From f6aeb24c91051f252b27e04cdf85a19650b9db94 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 18 Feb 2025 08:07:12 +0200 Subject: [PATCH 01/51] dummy fontlib libs --- CMakeLists.txt | 5 + src/common/logging/filter.cpp | 2 + src/common/logging/types.h | 2 + src/core/libraries/font/font.cpp | 1496 ++++++++++++++++++++++++++++ src/core/libraries/font/font.h | 247 +++++ src/core/libraries/font/fontft.cpp | 157 +++ src/core/libraries/font/fontft.h | 39 + 7 files changed, 1948 insertions(+) create mode 100644 src/core/libraries/font/font.cpp create mode 100644 src/core/libraries/font/font.h create mode 100644 src/core/libraries/font/fontft.cpp create mode 100644 src/core/libraries/font/fontft.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c23b7c6..bdf6c5bc4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,6 +389,11 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/mouse/mouse.h src/core/libraries/web_browser_dialog/webbrowserdialog.cpp src/core/libraries/web_browser_dialog/webbrowserdialog.h + src/core/libraries/font/font.cpp + src/core/libraries/font/font.h + src/core/libraries/font/fontft.cpp + src/core/libraries/font/fontft.h + ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index bed7802ed..92fa5efec 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -136,6 +136,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpParty) \ SUB(Lib, Zlib) \ SUB(Lib, Hmd) \ + SUB(Lib, Font) \ + SUB(Lib, FontFt) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index c07efbc0d..58b570faf 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -103,6 +103,8 @@ enum class Class : u8 { Lib_NpParty, ///< The LibSceNpParty implementation Lib_Zlib, ///< The LibSceZlib implementation. Lib_Hmd, ///< The LibSceHmd implementation. + Lib_Font, ///< The libSceFont implementation. + Lib_FontFt, ///< The libSceFontFt implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp new file mode 100644 index 000000000..8c1f6edd8 --- /dev/null +++ b/src/core/libraries/font/font.cpp @@ -0,0 +1,1496 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/font/font.h" + +namespace Libraries::Font { + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontBindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterRefersTextNext() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontClearDeviceCache() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCloseFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontControl() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDeleteGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetPixelResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetVerticalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRelease() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryTerm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontFile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontInstance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontMemory() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontSet() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRebindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderSurfaceInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetScissor() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportExternalFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportGlyphs() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportSystemFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepBack() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepNext() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceRewind() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontUnbindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineClear() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09F92905ED82A814() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_526287664A493981() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_569E2ECD34290F45() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5A04775B6BE47685() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_6F9010294D822367() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7757E947423A7A67() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_93B36DEA021311D6() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_94B0891E7111598A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE4788A96EF46256() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI module_start() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI module_stop() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", 1, 1, + sceFontAttachDeviceCacheBuffer); + LIB_FUNCTION("3OdRkSjOcog", "libSceFont", 1, "libSceFont", 1, 1, sceFontBindRenderer); + LIB_FUNCTION("6DFUkCwQLa8", "libSceFont", 1, "libSceFont", 1, 1, sceFontCharacterGetBidiLevel); + LIB_FUNCTION("coCrV6IWplE", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterGetSyllableStringState); + LIB_FUNCTION("zN3+nuA0SFQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterGetTextFontCode); + LIB_FUNCTION("mxgmMj-Mq-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontCharacterGetTextOrder); + LIB_FUNCTION("-P6X35Rq2-E", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterLooksFormatCharacters); + LIB_FUNCTION("SaRlqtqaCew", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterLooksWhiteSpace); + LIB_FUNCTION("6Gqlv5KdTbU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterRefersTextBack); + LIB_FUNCTION("BkjBP+YC19w", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharacterRefersTextNext); + LIB_FUNCTION("lVSR5ftvNag", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCharactersRefersTextCodes); + LIB_FUNCTION("I9R5VC6eZWo", "libSceFont", 1, "libSceFont", 1, 1, sceFontClearDeviceCache); + LIB_FUNCTION("vzHs3C8lWJk", "libSceFont", 1, "libSceFont", 1, 1, sceFontCloseFont); + LIB_FUNCTION("MpKSBaYKluo", "libSceFont", 1, "libSceFont", 1, 1, sceFontControl); + LIB_FUNCTION("WBNBaj9XiJU", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateGraphicsDevice); + LIB_FUNCTION("4So0MC3oBIM", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateGraphicsService); + LIB_FUNCTION("NlO5Qlhjkng", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCreateGraphicsServiceWithEdition); + LIB_FUNCTION("nWrfPI4Okmg", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateLibrary); + LIB_FUNCTION("n590hj5Oe-k", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCreateLibraryWithEdition); + LIB_FUNCTION("u5fZd3KZcs0", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateRenderer); + LIB_FUNCTION("WaSFJoRWXaI", "libSceFont", 1, "libSceFont", 1, 1, + sceFontCreateRendererWithEdition); + LIB_FUNCTION("MO24vDhmS4E", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateString); + LIB_FUNCTION("cYrMGk1wrMA", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateWords); + LIB_FUNCTION("7rogx92EEyc", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateWritingLine); + LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDefineAttribute); + LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDeleteGlyph); + LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyGraphicsDevice); + LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", 1, 1, + sceFontDestroyGraphicsService); + LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyLibrary); + LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyRenderer); + LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyString); + LIB_FUNCTION("hWE4AwNixqY", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyWords); + LIB_FUNCTION("PEjv7CVDRYs", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyWritingLine); + LIB_FUNCTION("UuY-OJF+f0k", "libSceFont", 1, "libSceFont", 1, 1, + sceFontDettachDeviceCacheBuffer); + LIB_FUNCTION("C-4Qw5Srlyw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGenerateCharGlyph); + LIB_FUNCTION("5kx49CAlO-M", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetAttribute); + LIB_FUNCTION("OINC0X9HGBY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetCharGlyphCode); + LIB_FUNCTION("L97d+3OgMlE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetCharGlyphMetrics); + LIB_FUNCTION("ynSqYL8VpoA", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetEffectSlant); + LIB_FUNCTION("d7dDgRY+Bzw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetEffectWeight); + LIB_FUNCTION("ZB8xRemRRG8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontGlyphsCount); + LIB_FUNCTION("4X14YSK4Ldk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGetFontGlyphsOutlineProfile); + LIB_FUNCTION("eb9S3zNlV5o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontMetrics); + LIB_FUNCTION("tiIlroGki+g", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontResolution); + LIB_FUNCTION("3hVv3SNoL6E", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGetFontStyleInformation); + LIB_FUNCTION("gVQpMBuB7fE", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGetGlyphExpandBufferState); + LIB_FUNCTION("imxVx8lm+KM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetHorizontalLayout); + LIB_FUNCTION("sDuhHGNhHvE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetKerning); + LIB_FUNCTION("LzmHDnlcwfQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetLibrary); + LIB_FUNCTION("BozJej5T6fs", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetPixelResolution); + LIB_FUNCTION("IQtleGLL5pQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGetRenderCharGlyphMetrics); + LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectSlant); + LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectWeight); + LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGetRenderScaledKerning); + LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePixel); + LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePoint); + LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetResolutionDpi); + LIB_FUNCTION("CkVmLoCNN-8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScalePixel); + LIB_FUNCTION("GoF2bhB7LYk", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScalePoint); + LIB_FUNCTION("IrXeG0Lc6nA", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScriptLanguage); + LIB_FUNCTION("7-miUT6pNQw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetTypographicDesign); + LIB_FUNCTION("3BrWWFU+4ts", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetVerticalLayout); + LIB_FUNCTION("8-zmgsxkBek", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphDefineAttribute); + LIB_FUNCTION("oO33Uex4Ui0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetAttribute); + LIB_FUNCTION("PXlA0M8ax40", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetGlyphForm); + LIB_FUNCTION("XUfSWpLhrUw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetMetricsForm); + LIB_FUNCTION("lNnUqa1zA-M", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetScalePixel); + LIB_FUNCTION("ntrc3bEWlvQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRefersMetrics); + LIB_FUNCTION("9kTbF59TjLs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGlyphRefersMetricsHorizontal); + LIB_FUNCTION("nJavPEdMDvM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGlyphRefersMetricsHorizontalAdvance); + LIB_FUNCTION("JCnVgZgcucs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGlyphRefersMetricsHorizontalX); + LIB_FUNCTION("R1T4i+DOhNY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRefersOutline); + LIB_FUNCTION("RmkXfBcZnrM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRenderImage); + LIB_FUNCTION("r4KEihtwxGs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGlyphRenderImageHorizontal); + LIB_FUNCTION("n22d-HIdmMg", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGlyphRenderImageVertical); + LIB_FUNCTION("RL2cAQgyXR8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsBeginFrame); + LIB_FUNCTION("dUmIK6QjT7E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsDrawingCancel); + LIB_FUNCTION("X2Vl3yU19Zw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsDrawingFinish); + LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsEndFrame); + LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsExchangeResource); + LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillMethodInit); + LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillPlotInit); + LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillPlotSetLayout); + LIB_FUNCTION("PEYQJa+MWnk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillPlotSetMapping); + LIB_FUNCTION("21g4m4kYF6g", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillRatesInit); + LIB_FUNCTION("pJzji5FvdxU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillRatesSetFillEffect); + LIB_FUNCTION("scaro-xEuUM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillRatesSetLayout); + LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsFillRatesSetMapping); + LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsGetDeviceUsage); + LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRegionInit); + LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsRegionInitCircular); + LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsRegionInitRoundish); + LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRelease); + LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsRenderResource); + LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetFramePolicy); + LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupClipping); + LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupColorRates); + LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupFillMethod); + LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupFillRates); + LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupGlyphFill); + LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupGlyphFillPlot); + LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupHandleDefault); + LIB_FUNCTION("MEAmHMynQXE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupLocation); + LIB_FUNCTION("XRUOmQhnYO4", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupPositioning); + LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupRotation); + LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupScaling); + LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupShapeFill); + LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsSetupShapeFillPlot); + LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsStructureCanvas); + LIB_FUNCTION("cpjgdlMYdOM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsStructureCanvasSequence); + LIB_FUNCTION("774Mee21wKk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsStructureDesign); + LIB_FUNCTION("Hp3NIFhUXvQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsStructureDesignResource); + LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsStructureSurfaceTexture); + LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateClipping); + LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateColorRates); + LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateFillMethod); + LIB_FUNCTION("MnUYAs2jVuU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateFillRates); + LIB_FUNCTION("R-oVDMusYbc", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateGlyphFill); + LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateGlyphFillPlot); + LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateLocation); + LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdatePositioning); + LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateRotation); + LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateScaling); + LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateShapeFill); + LIB_FUNCTION("I5Rf2rXvBKQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontGraphicsUpdateShapeFillPlot); + LIB_FUNCTION("whrS4oksXc4", "libSceFont", 1, "libSceFont", 1, 1, sceFontMemoryInit); + LIB_FUNCTION("h6hIgxXEiEc", "libSceFont", 1, "libSceFont", 1, 1, sceFontMemoryTerm); + LIB_FUNCTION("RvXyHMUiLhE", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontFile); + LIB_FUNCTION("JzCH3SCFnAU", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontInstance); + LIB_FUNCTION("KXUpebrFk1U", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontMemory); + LIB_FUNCTION("cKYtVmeSTcw", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontSet); + LIB_FUNCTION("Z2cdsqJH+5k", "libSceFont", 1, "libSceFont", 1, 1, sceFontRebindRenderer); + LIB_FUNCTION("3G4zhgKuxE8", "libSceFont", 1, "libSceFont", 1, 1, sceFontRenderCharGlyphImage); + LIB_FUNCTION("kAenWy1Zw5o", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRenderCharGlyphImageHorizontal); + LIB_FUNCTION("i6UNdSig1uE", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRenderCharGlyphImageVertical); + LIB_FUNCTION("amcmrY62BD4", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRendererGetOutlineBufferSize); + LIB_FUNCTION("ai6AfGrBs4o", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRendererResetOutlineBuffer); + LIB_FUNCTION("ydF+WuH0fAk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRendererSetOutlineBufferPolicy); + LIB_FUNCTION("gdUCnU0gHdI", "libSceFont", 1, "libSceFont", 1, 1, sceFontRenderSurfaceInit); + LIB_FUNCTION("vRxf4d0ulPs", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRenderSurfaceSetScissor); + LIB_FUNCTION("0hr-w30SjiI", "libSceFont", 1, "libSceFont", 1, 1, + sceFontRenderSurfaceSetStyleFrame); + LIB_FUNCTION("TMtqoFQjjbA", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetEffectSlant); + LIB_FUNCTION("v0phZwa4R5o", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetEffectWeight); + LIB_FUNCTION("kihFGYJee7o", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetFontsOpenMode); + LIB_FUNCTION("I1acwR7Qp8E", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetResolutionDpi); + LIB_FUNCTION("N1EBMeGhf7E", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScalePixel); + LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScalePoint); + LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScriptLanguage); + LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetTypographicDesign); + LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontSetupRenderEffectSlant); + LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", 1, 1, + sceFontSetupRenderEffectWeight); + LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePixel); + LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePoint); + LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStringGetTerminateCode); + LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStringGetTerminateOrder); + LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetWritingForm); + LIB_FUNCTION("hq5LffQjz-s", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStringRefersRenderCharacters); + LIB_FUNCTION("Avv7OApgCJk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStringRefersTextCharacters); + LIB_FUNCTION("lOfduYnjgbo", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameGetEffectSlant); + LIB_FUNCTION("HIUdjR-+Wl8", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameGetEffectWeight); + LIB_FUNCTION("VSw18Aqzl0U", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameGetResolutionDpi); + LIB_FUNCTION("2QfqfeLblbg", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameGetScalePixel); + LIB_FUNCTION("7x2xKiiB7MA", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameGetScalePoint); + LIB_FUNCTION("la2AOWnHEAc", "libSceFont", 1, "libSceFont", 1, 1, sceFontStyleFrameInit); + LIB_FUNCTION("394sckksiCU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameSetEffectSlant); + LIB_FUNCTION("faw77-pEBmU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameSetEffectWeight); + LIB_FUNCTION("dB4-3Wdwls8", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameSetResolutionDpi); + LIB_FUNCTION("da4rQ4-+p-4", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameSetScalePixel); + LIB_FUNCTION("O997laxY-Ys", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameSetScalePoint); + LIB_FUNCTION("dUmABkAnVgk", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameUnsetEffectSlant); + LIB_FUNCTION("hwsuXgmKdaw", "libSceFont", 1, "libSceFont", 1, 1, + sceFontStyleFrameUnsetEffectWeight); + LIB_FUNCTION("bePC0L0vQWY", "libSceFont", 1, "libSceFont", 1, 1, sceFontStyleFrameUnsetScale); + LIB_FUNCTION("mz2iTY0MK4A", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportExternalFonts); + LIB_FUNCTION("71w5DzObuZI", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportGlyphs); + LIB_FUNCTION("SsRbbCiWoGw", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportSystemFonts); + LIB_FUNCTION("IPoYwwlMx-g", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextCodesStepBack); + LIB_FUNCTION("olSmXY+XP1E", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextCodesStepNext); + LIB_FUNCTION("oaJ1BpN2FQk", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextSourceInit); + LIB_FUNCTION("VRFd3diReec", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextSourceRewind); + LIB_FUNCTION("eCRMCSk96NU", "libSceFont", 1, "libSceFont", 1, 1, + sceFontTextSourceSetDefaultFont); + LIB_FUNCTION("OqQKX0h5COw", "libSceFont", 1, "libSceFont", 1, 1, + sceFontTextSourceSetWritingForm); + LIB_FUNCTION("1QjhKxrsOB8", "libSceFont", 1, "libSceFont", 1, 1, sceFontUnbindRenderer); + LIB_FUNCTION("H-FNq8isKE0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWordsFindWordCharacters); + LIB_FUNCTION("fljdejMcG1c", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingGetRenderMetrics); + LIB_FUNCTION("fD5rqhEXKYQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingInit); + LIB_FUNCTION("1+DgKL0haWQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingLineClear); + LIB_FUNCTION("JQKWIsS9joE", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingLineGetOrderingSpace); + LIB_FUNCTION("nlU2VnfpqTM", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingLineGetRenderMetrics); + LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingLineRefersRenderStep); + LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingLineWritesOrder); + LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingRefersRenderStep); + LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingRefersRenderStepCharacter); + LIB_FUNCTION("BbCZjJizU4A", "libSceFont", 1, "libSceFont", 1, 1, + sceFontWritingSetMaskInvisible); + LIB_FUNCTION("APTXePHIjLM", "libSceFont", 1, "libSceFont", 1, 1, Func_00F4D778F1C88CB3); + LIB_FUNCTION("A8ZQAl+7Dec", "libSceFont", 1, "libSceFont", 1, 1, Func_03C650025FBB0DE7); + LIB_FUNCTION("B+q4oWOyfho", "libSceFont", 1, "libSceFont", 1, 1, Func_07EAB8A163B27E1A); + LIB_FUNCTION("CUCOiOT5fOM", "libSceFont", 1, "libSceFont", 1, 1, Func_09408E88E4F97CE3); + LIB_FUNCTION("CfkpBe2CqBQ", "libSceFont", 1, "libSceFont", 1, 1, Func_09F92905ED82A814); + LIB_FUNCTION("DRQs7hqyGr4", "libSceFont", 1, "libSceFont", 1, 1, Func_0D142CEE1AB21ABE); + LIB_FUNCTION("FL0unhGcFvI", "libSceFont", 1, "libSceFont", 1, 1, Func_14BD2E9E119C16F2); + LIB_FUNCTION("GsU8nt6ujXU", "libSceFont", 1, "libSceFont", 1, 1, Func_1AC53C9EDEAE8D75); + LIB_FUNCTION("HUARhdXiTD0", "libSceFont", 1, "libSceFont", 1, 1, Func_1D401185D5E24C3D); + LIB_FUNCTION("HoPNIMLMmW8", "libSceFont", 1, "libSceFont", 1, 1, Func_1E83CD20C2CC996F); + LIB_FUNCTION("MUsfdluf54o", "libSceFont", 1, "libSceFont", 1, 1, Func_314B1F765B9FE78A); + LIB_FUNCTION("NQ5nJf7eKeE", "libSceFont", 1, "libSceFont", 1, 1, Func_350E6725FEDE29E1); + LIB_FUNCTION("Pbdz8KYEvzk", "libSceFont", 1, "libSceFont", 1, 1, Func_3DB773F0A604BF39); + LIB_FUNCTION("T-Sd0h4xGxw", "libSceFont", 1, "libSceFont", 1, 1, Func_4FF49DD21E311B1C); + LIB_FUNCTION("UmKHZkpJOYE", "libSceFont", 1, "libSceFont", 1, 1, Func_526287664A493981); + LIB_FUNCTION("VcpxjbyEpuk", "libSceFont", 1, "libSceFont", 1, 1, Func_55CA718DBC84A6E9); + LIB_FUNCTION("Vj-F8HBqi00", "libSceFont", 1, "libSceFont", 1, 1, Func_563FC5F0706A8B4D); + LIB_FUNCTION("Vp4uzTQpD0U", "libSceFont", 1, "libSceFont", 1, 1, Func_569E2ECD34290F45); + LIB_FUNCTION("WgR3W2vkdoU", "libSceFont", 1, "libSceFont", 1, 1, Func_5A04775B6BE47685); + LIB_FUNCTION("X9k7yrb3l1A", "libSceFont", 1, "libSceFont", 1, 1, Func_5FD93BCAB6F79750); + LIB_FUNCTION("YrU5j4ZL07Q", "libSceFont", 1, "libSceFont", 1, 1, Func_62B5398F864BD3B4); + LIB_FUNCTION("b5AQKU2CI2c", "libSceFont", 1, "libSceFont", 1, 1, Func_6F9010294D822367); + LIB_FUNCTION("d1fpR0I6emc", "libSceFont", 1, "libSceFont", 1, 1, Func_7757E947423A7A67); + LIB_FUNCTION("fga6Ugd-VPo", "libSceFont", 1, "libSceFont", 1, 1, Func_7E06BA52077F54FA); + LIB_FUNCTION("k7Nt6gITEdY", "libSceFont", 1, "libSceFont", 1, 1, Func_93B36DEA021311D6); + LIB_FUNCTION("lLCJHnERWYo", "libSceFont", 1, "libSceFont", 1, 1, Func_94B0891E7111598A); + LIB_FUNCTION("l4XJEowv580", "libSceFont", 1, "libSceFont", 1, 1, Func_9785C9128C2FE7CD); + LIB_FUNCTION("l9+8m2X7wOE", "libSceFont", 1, "libSceFont", 1, 1, Func_97DFBC9B65FBC0E1); + LIB_FUNCTION("rNlxdAXX08o", "libSceFont", 1, "libSceFont", 1, 1, Func_ACD9717405D7D3CA); + LIB_FUNCTION("sZqK7D-U8W8", "libSceFont", 1, "libSceFont", 1, 1, Func_B19A8AEC3FD4F16F); + LIB_FUNCTION("wQ9IitfPED0", "libSceFont", 1, "libSceFont", 1, 1, Func_C10F488AD7CF103D); + LIB_FUNCTION("0Mi1-0poJsc", "libSceFont", 1, "libSceFont", 1, 1, Func_D0C8B5FF4A6826C7); + LIB_FUNCTION("5I080Bw0KjM", "libSceFont", 1, "libSceFont", 1, 1, Func_E48D3CD01C342A33); + LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", 1, 1, Func_EAC96B2186B71E14); + LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", 1, 1, Func_FE4788A96EF46256); + LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", 1, 1, Func_FE7E5AE95D3058F5); + LIB_FUNCTION("BaOKcng8g88", "libkernel", 1, "libSceFont", 1, 1, module_start); + LIB_FUNCTION("KpDMrPHvt3Q", "libkernel", 1, "libSceFont", 1, 1, module_stop); +}; + +} // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h new file mode 100644 index 000000000..18f8b2d25 --- /dev/null +++ b/src/core/libraries/font/font.h @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Font { + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontBindRenderer(); +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(); +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(); +s32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(); +s32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(); +s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack(); +s32 PS4_SYSV_ABI sceFontCharacterRefersTextNext(); +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); +s32 PS4_SYSV_ABI sceFontClearDeviceCache(); +s32 PS4_SYSV_ABI sceFontCloseFont(); +s32 PS4_SYSV_ABI sceFontControl(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateLibrary(); +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateRenderer(); +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateString(); +s32 PS4_SYSV_ABI sceFontCreateWords(); +s32 PS4_SYSV_ABI sceFontCreateWritingLine(); +s32 PS4_SYSV_ABI sceFontDefineAttribute(); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); +s32 PS4_SYSV_ABI sceFontDestroyLibrary(); +s32 PS4_SYSV_ABI sceFontDestroyRenderer(); +s32 PS4_SYSV_ABI sceFontDestroyString(); +s32 PS4_SYSV_ABI sceFontDestroyWords(); +s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); +s32 PS4_SYSV_ABI sceFontGetAttribute(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile(); +s32 PS4_SYSV_ABI sceFontGetFontMetrics(); +s32 PS4_SYSV_ABI sceFontGetFontResolution(); +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(); +s32 PS4_SYSV_ABI sceFontGetKerning(); +s32 PS4_SYSV_ABI sceFontGetLibrary(); +s32 PS4_SYSV_ABI sceFontGetPixelResolution(); +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGetScalePoint(); +s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(); +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource(); +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish(); +s32 PS4_SYSV_ABI sceFontGraphicsRelease(); +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource(); +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontMemoryInit(); +s32 PS4_SYSV_ABI sceFontMemoryTerm(); +s32 PS4_SYSV_ABI sceFontOpenFontFile(); +s32 PS4_SYSV_ABI sceFontOpenFontInstance(); +s32 PS4_SYSV_ABI sceFontOpenFontMemory(); +s32 PS4_SYSV_ABI sceFontOpenFontSet(); +s32 PS4_SYSV_ABI sceFontRebindRenderer(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); +s32 PS4_SYSV_ABI sceFontRenderSurfaceInit(); +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(); +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(); +s32 PS4_SYSV_ABI sceFontSetEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); +s32 PS4_SYSV_ABI sceFontSetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontSetScalePixel(); +s32 PS4_SYSV_ABI sceFontSetScalePoint(); +s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); +s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(); +s32 PS4_SYSV_ABI sceFontStyleFrameInit(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(); +s32 PS4_SYSV_ABI sceFontSupportExternalFonts(); +s32 PS4_SYSV_ABI sceFontSupportGlyphs(); +s32 PS4_SYSV_ABI sceFontSupportSystemFonts(); +s32 PS4_SYSV_ABI sceFontTextCodesStepBack(); +s32 PS4_SYSV_ABI sceFontTextCodesStepNext(); +s32 PS4_SYSV_ABI sceFontTextSourceInit(); +s32 PS4_SYSV_ABI sceFontTextSourceRewind(); +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont(); +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm(); +s32 PS4_SYSV_ABI sceFontUnbindRenderer(); +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters(); +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingInit(); +s32 PS4_SYSV_ABI sceFontWritingLineClear(); +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace(); +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter(); +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible(); +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3(); +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7(); +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A(); +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3(); +s32 PS4_SYSV_ABI Func_09F92905ED82A814(); +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE(); +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2(); +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75(); +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D(); +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F(); +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A(); +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1(); +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39(); +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C(); +s32 PS4_SYSV_ABI Func_526287664A493981(); +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9(); +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D(); +s32 PS4_SYSV_ABI Func_569E2ECD34290F45(); +s32 PS4_SYSV_ABI Func_5A04775B6BE47685(); +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750(); +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4(); +s32 PS4_SYSV_ABI Func_6F9010294D822367(); +s32 PS4_SYSV_ABI Func_7757E947423A7A67(); +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA(); +s32 PS4_SYSV_ABI Func_93B36DEA021311D6(); +s32 PS4_SYSV_ABI Func_94B0891E7111598A(); +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD(); +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1(); +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA(); +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F(); +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D(); +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7(); +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33(); +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14(); +s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); +s32 PS4_SYSV_ABI module_start(); +s32 PS4_SYSV_ABI module_stop(); + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp new file mode 100644 index 000000000..ce50f2e85 --- /dev/null +++ b/src/core/libraries/font/fontft.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/fontft/fontft.h" + +namespace Libraries::FontFt { + +s32 PS4_SYSV_ABI sceFontFtInitAliases() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSetAliasFont() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSetAliasPath() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportBdf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportCid() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportFontFormats() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenType() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportPcf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportPfr() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportTrueType() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportType1() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportType42() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportWinFonts() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtTermAliases() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectLibraryFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectRendererFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI module_start() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI module_stop() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtInitAliases); + LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSetAliasFont); + LIB_FUNCTION("MEWjebIzDEI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSetAliasPath); + LIB_FUNCTION("ZcQL0iSjvFw", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportBdf); + LIB_FUNCTION("LADHEyFTxRQ", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportCid); + LIB_FUNCTION("+jqQjsancTs", "libSceFontFt", 1, "libSceFontFt", 1, 1, + sceFontFtSupportFontFormats); + LIB_FUNCTION("oakL15-mBtc", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportOpenType); + LIB_FUNCTION("dcQeaDr8UJc", "libSceFontFt", 1, "libSceFontFt", 1, 1, + sceFontFtSupportOpenTypeOtf); + LIB_FUNCTION("2KXS-HkZT3c", "libSceFontFt", 1, "libSceFontFt", 1, 1, + sceFontFtSupportOpenTypeTtf); + LIB_FUNCTION("H0mJnhKwV-s", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportPcf); + LIB_FUNCTION("S2mw3sYplAI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportPfr); + LIB_FUNCTION("+ehNXJPUyhk", "libSceFontFt", 1, "libSceFontFt", 1, 1, + sceFontFtSupportSystemFonts); + LIB_FUNCTION("4BAhDLdrzUI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportTrueType); + LIB_FUNCTION("Utlzbdf+g9o", "libSceFontFt", 1, "libSceFontFt", 1, 1, + sceFontFtSupportTrueTypeGx); + LIB_FUNCTION("nAfQ6qaL1fU", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportType1); + LIB_FUNCTION("X9+pzrGtBus", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportType42); + LIB_FUNCTION("w0hI3xsK-hc", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportWinFonts); + LIB_FUNCTION("w5sfH9r8ZJ4", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtTermAliases); + LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectGlyphsFt); + LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectLibraryFt); + LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectRendererFt); + LIB_FUNCTION("BaOKcng8g88", "libkernel", 1, "libSceFontFt", 1, 1, module_start); + LIB_FUNCTION("KpDMrPHvt3Q", "libkernel", 1, "libSceFontFt", 1, 1, module_stop); +}; + +} // namespace Libraries::FontFt \ No newline at end of file diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h new file mode 100644 index 000000000..1e7842e35 --- /dev/null +++ b/src/core/libraries/font/fontft.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::FontFt { + +s32 PS4_SYSV_ABI sceFontFtInitAliases(); +s32 PS4_SYSV_ABI sceFontFtSetAliasFont(); +s32 PS4_SYSV_ABI sceFontFtSetAliasPath(); +s32 PS4_SYSV_ABI sceFontFtSupportBdf(); +s32 PS4_SYSV_ABI sceFontFtSupportCid(); +s32 PS4_SYSV_ABI sceFontFtSupportFontFormats(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenType(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf(); +s32 PS4_SYSV_ABI sceFontFtSupportPcf(); +s32 PS4_SYSV_ABI sceFontFtSupportPfr(); +s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts(); +s32 PS4_SYSV_ABI sceFontFtSupportTrueType(); +s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx(); +s32 PS4_SYSV_ABI sceFontFtSupportType1(); +s32 PS4_SYSV_ABI sceFontFtSupportType42(); +s32 PS4_SYSV_ABI sceFontFtSupportWinFonts(); +s32 PS4_SYSV_ABI sceFontFtTermAliases(); +s32 PS4_SYSV_ABI sceFontSelectGlyphsFt(); +s32 PS4_SYSV_ABI sceFontSelectLibraryFt(); +s32 PS4_SYSV_ABI sceFontSelectRendererFt(); +s32 PS4_SYSV_ABI module_start(); +s32 PS4_SYSV_ABI module_stop(); + +void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::FontFt \ No newline at end of file From 1c48cc16c8cc61f697432f88db7dca1b4d25927d Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 18 Feb 2025 08:12:26 +0200 Subject: [PATCH 02/51] register font libs --- src/emulator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index b6586ecfd..b2b330527 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -27,6 +27,8 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/font/font.h" +#include "core/libraries/font/fontft.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -298,8 +300,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceCesCs.sprx", nullptr}, - {"libSceFont.sprx", nullptr}, - {"libSceFontFt.sprx", nullptr}, + {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, + {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, {"libSceFreeTypeOt.sprx", nullptr}}}; std::vector found_modules; From e4cbd8f8abba6c02574628fca4de78eb96c74bf8 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 18 Feb 2025 13:02:05 +0200 Subject: [PATCH 03/51] typo fix --- src/core/libraries/font/fontft.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index ce50f2e85..6ede339f0 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -4,7 +4,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "core/libraries/fontft/fontft.h" +#include "core/libraries/font/fontft.h" namespace Libraries::FontFt { From 2b66b6288dcaacbb4f2629c79398a9ad481c168c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 09:53:16 +0200 Subject: [PATCH 04/51] added font error file --- CMakeLists.txt | 1 + src/core/libraries/font/font_error.h | 44 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/core/libraries/font/font_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bdf6c5bc4..668eab902 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -393,6 +393,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/font/font.h src/core/libraries/font/fontft.cpp src/core/libraries/font/fontft.h + src/core/libraries/font/font_error.h ) diff --git a/src/core/libraries/font/font_error.h b/src/core/libraries/font/font_error.h new file mode 100644 index 000000000..64e8fc6a7 --- /dev/null +++ b/src/core/libraries/font/font_error.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_FONT_ERROR_FATAL = 0x80460001; +constexpr int ORBIS_FONT_ERROR_INVALID_PARAMETER = 0x80460002; +constexpr int ORBIS_FONT_ERROR_INVALID_MEMORY = 0x80460003; +constexpr int ORBIS_FONT_ERROR_INVALID_LIBRARY = 0x80460004; +constexpr int ORBIS_FONT_ERROR_INVALID_FONT_HANDLE = 0x80460005; +constexpr int ORBIS_FONT_ERROR_INVALID_GLYPH = 0x80460006; +constexpr int ORBIS_FONT_ERROR_INVALID_RENDERER = 0x80460007; +constexpr int ORBIS_FONT_ERROR_INVALID_TEXT_SOURCE = 0x80460008; +constexpr int ORBIS_FONT_ERROR_INVALID_STRING = 0x80460009; +constexpr int ORBIS_FONT_ERROR_INVALID_WRITING = 0x8046000A; +constexpr int ORBIS_FONT_ERROR_INVALID_WORDS = 0x8046000B; +constexpr int ORBIS_FONT_ERROR_ALLOCATION_FAILED = 0x80460010; +constexpr int ORBIS_FONT_ERROR_FS_OPEN_FAILED = 0x80460011; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LIBRARY = 0x80460018; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT = 0x80460019; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION = 0x80460020; +constexpr int ORBIS_FONT_ERROR_ALREADY_SPECIFIED = 0x80460021; +constexpr int ORBIS_FONT_ERROR_ALREADY_ATTACHED = 0x80460022; +constexpr int ORBIS_FONT_ERROR_ALREADY_OPENED = 0x80460023; +constexpr int ORBIS_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER = 0x80460025; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET = 0x80460031; +constexpr int ORBIS_FONT_ERROR_FONT_OPEN_MAX = 0x80460033; +constexpr int ORBIS_FONT_ERROR_FONT_OPEN_FAILED = 0x80460036; +constexpr int ORBIS_FONT_ERROR_FONT_CLOSE_FAILED = 0x80460037; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY = 0x80460040; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_CODE = 0x80460041; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH = 0x80460042; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SCRIPT = 0x80460043; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LANGUAGE = 0x80460044; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE = 0x80460050; +constexpr int ORBIS_FONT_ERROR_UNSET_PARAMETER = 0x80460058; +constexpr int ORBIS_FONT_ERROR_FUNCTIONAL_LIMIT = 0x8046005C; +constexpr int ORBIS_FONT_ERROR_ALREADY_BOUND_RENDERER = 0x80460060; +constexpr int ORBIS_FONT_ERROR_NOT_BOUND_RENDERER = 0x80460061; +constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_FAILED = 0x80460063; +constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_LIMITED = 0x80460064; +constexpr int ORBIS_FONT_ERROR_RENDERER_RENDER_FAILED = 0x80460065; From aa56e601dc1261ffedb66a2b39073c6bcd2b34c0 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 10:02:58 +0200 Subject: [PATCH 05/51] added sceFontCharacterGetBidiLevel (from RE) --- src/core/libraries/font/font.cpp | 58 ++++++++++++++------------------ src/core/libraries/font/font.h | 8 ++++- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 8c1f6edd8..a79df9061 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -3,8 +3,9 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/libs.h" #include "core/libraries/font/font.h" +#include "core/libraries/libs.h" +#include "font_error.h" namespace Libraries::Font { @@ -18,8 +19,14 @@ s32 PS4_SYSV_ABI sceFontBindRenderer() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel) { + if (!textCharacter || !bidiLevel) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + *bidiLevel = textCharacter->bidiLevel; return ORBIS_OK; } @@ -1202,8 +1209,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDefineAttribute); LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDeleteGlyph); LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyGraphicsDevice); - LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", 1, 1, - sceFontDestroyGraphicsService); + LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyGraphicsService); LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyLibrary); LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyRenderer); LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyString); @@ -1234,8 +1240,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontGetRenderCharGlyphMetrics); LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectSlant); LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectWeight); - LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGetRenderScaledKerning); + LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScaledKerning); LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePixel); LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePoint); LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetResolutionDpi); @@ -1268,8 +1273,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsEndFrame); LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsExchangeResource); - LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsFillMethodInit); + LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillMethodInit); LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillPlotInit); LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillPlotSetLayout); @@ -1282,27 +1286,22 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontGraphicsFillRatesSetLayout); LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillRatesSetMapping); - LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsGetDeviceUsage); + LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsGetDeviceUsage); LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRegionInit); LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRegionInitCircular); LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRegionInitRoundish); LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRelease); - LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsRenderResource); - LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetFramePolicy); + LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRenderResource); + LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetFramePolicy); LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupClipping); LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupColorRates); LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupFillMethod); - LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupFillRates); - LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupGlyphFill); + LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupFillRates); + LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupGlyphFill); LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupGlyphFillPlot); LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", 1, 1, @@ -1312,8 +1311,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontGraphicsSetupPositioning); LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupRotation); LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupScaling); - LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupShapeFill); + LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupShapeFill); LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupShapeFillPlot); LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", 1, 1, @@ -1326,8 +1324,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontGraphicsStructureDesignResource); LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsStructureSurfaceTexture); - LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateClipping); + LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateClipping); LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateColorRates); LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", 1, 1, @@ -1338,12 +1335,10 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontGraphicsUpdateGlyphFill); LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateGlyphFillPlot); - LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateLocation); + LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateLocation); LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdatePositioning); - LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateRotation); + LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateRotation); LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateScaling); LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateShapeFill); @@ -1380,14 +1375,12 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScalePoint); LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScriptLanguage); LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetTypographicDesign); - LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontSetupRenderEffectSlant); + LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderEffectSlant); LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderEffectWeight); LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePixel); LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePoint); - LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStringGetTerminateCode); + LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetTerminateCode); LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetTerminateOrder); LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetWritingForm); @@ -1445,8 +1438,7 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { sceFontWritingLineGetRenderMetrics); LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingLineRefersRenderStep); - LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingLineWritesOrder); + LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingLineWritesOrder); LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingRefersRenderStep); LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", 1, 1, diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 18f8b2d25..25a77de56 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -11,9 +11,15 @@ class SymbolsResolver; namespace Libraries::Font { +struct OrbisFontTextCharacter { + // Other fields... + u8 bidiLevel; // Assuming the field at offset 0x3B stores the Bidi level +} FontCharacter; + s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); s32 PS4_SYSV_ABI sceFontBindRenderer(); -s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(); +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel); s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(); From c63b1a058c2f3580e2b1429e5a57c1a7bd4e4a50 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 10:04:08 +0200 Subject: [PATCH 06/51] fixup --- src/core/libraries/font/font.h | 2 +- src/core/libraries/font/fontft.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 25a77de56..3debcf1e8 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -14,7 +14,7 @@ namespace Libraries::Font { struct OrbisFontTextCharacter { // Other fields... u8 bidiLevel; // Assuming the field at offset 0x3B stores the Bidi level -} FontCharacter; +}; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); s32 PS4_SYSV_ABI sceFontBindRenderer(); diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index 6ede339f0..0b67907de 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -3,8 +3,8 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/libs.h" #include "core/libraries/font/fontft.h" +#include "core/libraries/libs.h" namespace Libraries::FontFt { From 647099200745207c14d484b6e4e44faa624d517b Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 10:49:42 +0200 Subject: [PATCH 07/51] sceFontCharacterGetTextOrder , sceFontCharacterLooksFormatCharacters , sceFontCharacterLooksWhiteSpace RE --- src/core/libraries/font/font.cpp | 36 +++++++++++++++++++++++++------- src/core/libraries/font/font.h | 13 ++++++++---- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index a79df9061..bbc2366e3 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -40,19 +40,39 @@ s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder) { + if (!pTextOrder) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!textCharacter) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + *pTextOrder = NULL; + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve text order + *pTextOrder = textCharacter->textOrder; return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + // Check if the format flag (bit 2) is set + return (textCharacter->formatFlags & 0x04) ? textCharacter->characterCode : 0; } -s32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + return (textCharacter->charType == 0x0E) ? textCharacter->characterCode : 0; } s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack() { diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 3debcf1e8..d770251e4 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -13,7 +13,11 @@ namespace Libraries::Font { struct OrbisFontTextCharacter { // Other fields... - u8 bidiLevel; // Assuming the field at offset 0x3B stores the Bidi level + void* textOrder; // Field at offset 0x10 (pointer to text order info) + u32 characterCode; // Field assumed at offset 0x28 + u8 charType; // Field assumed at offset 0x39 + u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level + u8 formatFlags; // Field at offset 0x3D (stores format-related flags) }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); @@ -22,9 +26,10 @@ s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharac int* bidiLevel); s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); -s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(); -s32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(); -s32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder); +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter); +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter); s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack(); s32 PS4_SYSV_ABI sceFontCharacterRefersTextNext(); s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); From 97153ed62ce2560b8e533326a7df92a85ecdca6d Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 13:47:32 +0200 Subject: [PATCH 08/51] sceFontCharacterRefersTextBack,sceFontCharacterRefersTextNext,sceFontRenderSurfaceInit,sceFontRenderSurfaceSetScissor RE --- src/core/libraries/font/font.cpp | 105 +++++++++++++++++++++++++---- src/core/libraries/font/font.h | 44 +++++++++--- src/core/libraries/font/fontft.cpp | 2 - src/core/libraries/font/fontft.h | 2 - 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index bbc2366e3..ef4ea7a12 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -75,14 +75,36 @@ u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCha return (textCharacter->charType == 0x0E) ? textCharacter->characterCode : 0; } -s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Check if input is NULL + + OrbisFontTextCharacter* current = textCharacter->prev; // Move backward instead of forward + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Return the first matching node + } + current = current->prev; // Move to the previous node + } + + return NULL; // No valid node found } -s32 PS4_SYSV_ABI sceFontCharacterRefersTextNext() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Null check + + OrbisFontTextCharacter* current = textCharacter->next; + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Found a match + } + current = current->next; // Move to the next node + } + + return NULL; // No matching node found } s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { @@ -725,14 +747,71 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRenderSurfaceInit() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel) { + if (renderSurface) { // Ensure surface is not NULL before modifying it + renderSurface->buffer = buffer; + renderSurface->widthByte = bufWidthByte; + renderSurface->pixelSizeByte = pixelSizeByte; + + // Initialize unknown fields (likely reserved or flags) + renderSurface->unkn_0xd = 0; + renderSurface->unkn_0xe = 0; + renderSurface->unkn_0xf = 0; + + // Ensure width and height are non-negative + renderSurface->width = (widthPixel < 0) ? 0 : widthPixel; + renderSurface->height = (heightPixel < 0) ? 0 : heightPixel; + + // Set the clipping/scaling rectangle + renderSurface->sc_x0 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_x1 = renderSurface->width; + renderSurface->sc_y1 = renderSurface->height; + } } -s32 PS4_SYSV_ABI sceFontRenderSurfaceSetScissor() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h) { + if (!renderSurface) + return; // Null check + + // Handle horizontal clipping + int surfaceWidth = renderSurface->width; + int clip_x0, clip_x1; + + if (surfaceWidth != 0) { + if (x0 < 0) { // Adjust for negative x0 + clip_x0 = 0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + if (w <= -x0) + clip_x1 = 0; // Entire width is clipped + } else { + clip_x0 = (x0 > surfaceWidth) ? surfaceWidth : x0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + } + renderSurface->sc_x0 = clip_x0; + renderSurface->sc_x1 = clip_x1; + } + + // Handle vertical clipping + int surfaceHeight = renderSurface->height; + int clip_y0, clip_y1; + + if (surfaceHeight != 0) { + if (y0 < 0) { // Adjust for negative y0 + clip_y0 = 0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + if (h <= -y0) + clip_y1 = 0; // Entire height is clipped + } else { + clip_y0 = (y0 > surfaceHeight) ? surfaceHeight : y0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + } + renderSurface->sc_y0 = clip_y0; + renderSurface->sc_y1 = clip_y1; + } } s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame() { @@ -1501,8 +1580,6 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", 1, 1, Func_EAC96B2186B71E14); LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", 1, 1, Func_FE4788A96EF46256); LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", 1, 1, Func_FE7E5AE95D3058F5); - LIB_FUNCTION("BaOKcng8g88", "libkernel", 1, "libSceFont", 1, 1, module_start); - LIB_FUNCTION("KpDMrPHvt3Q", "libkernel", 1, "libSceFont", 1, 1, module_stop); }; } // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index d770251e4..c3bae6c51 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -13,11 +13,30 @@ namespace Libraries::Font { struct OrbisFontTextCharacter { // Other fields... - void* textOrder; // Field at offset 0x10 (pointer to text order info) - u32 characterCode; // Field assumed at offset 0x28 - u8 charType; // Field assumed at offset 0x39 - u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level - u8 formatFlags; // Field at offset 0x3D (stores format-related flags) + struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 + struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08 + void* textOrder; // Field at offset 0x10 (pointer to text order info) + u32 characterCode; // Field assumed at offset 0x28 + u8 unkn_0x31; // Offset 0x31 + u8 unkn_0x33; // Offset 0x33 + u8 charType; // Field assumed at offset 0x39 + u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level + u8 formatFlags; // Field at offset 0x3D (stores format-related flags) +}; + +struct OrbisFontRenderSurface { + void* buffer; + s32 widthByte; + s8 pixelSizeByte; + u8 unkn_0xd; + u8 unkn_0xe; + u8 unkn_0xf; + s32 width, height; + u32 sc_x0; + u32 sc_y0; + u32 sc_x1; + u32 sc_y1; + u32 unkn_28[22]; }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); @@ -30,8 +49,10 @@ s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharac void** pTextOrder); u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter); u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter); -s32 PS4_SYSV_ABI sceFontCharacterRefersTextBack(); -s32 PS4_SYSV_ABI sceFontCharacterRefersTextNext(); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter); s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); s32 PS4_SYSV_ABI sceFontClearDeviceCache(); s32 PS4_SYSV_ABI sceFontCloseFont(); @@ -160,8 +181,11 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); -s32 PS4_SYSV_ABI sceFontRenderSurfaceInit(); -s32 PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(); +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel); +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h); s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(); s32 PS4_SYSV_ABI sceFontSetEffectSlant(); s32 PS4_SYSV_ABI sceFontSetEffectWeight(); @@ -251,8 +275,6 @@ s32 PS4_SYSV_ABI Func_E48D3CD01C342A33(); s32 PS4_SYSV_ABI Func_EAC96B2186B71E14(); s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); -s32 PS4_SYSV_ABI module_start(); -s32 PS4_SYSV_ABI module_stop(); void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index 0b67907de..4a7a13318 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -150,8 +150,6 @@ void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectGlyphsFt); LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectLibraryFt); LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectRendererFt); - LIB_FUNCTION("BaOKcng8g88", "libkernel", 1, "libSceFontFt", 1, 1, module_start); - LIB_FUNCTION("KpDMrPHvt3Q", "libkernel", 1, "libSceFontFt", 1, 1, module_stop); }; } // namespace Libraries::FontFt \ No newline at end of file diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h index 1e7842e35..cec6d7872 100644 --- a/src/core/libraries/font/fontft.h +++ b/src/core/libraries/font/fontft.h @@ -32,8 +32,6 @@ s32 PS4_SYSV_ABI sceFontFtTermAliases(); s32 PS4_SYSV_ABI sceFontSelectGlyphsFt(); s32 PS4_SYSV_ABI sceFontSelectLibraryFt(); s32 PS4_SYSV_ABI sceFontSelectRendererFt(); -s32 PS4_SYSV_ABI module_start(); -s32 PS4_SYSV_ABI module_stop(); void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::FontFt \ No newline at end of file From aa2cf2be84e12e0896bc1395682a95033f537417 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 14:24:17 +0200 Subject: [PATCH 09/51] sceFontRenderSurfaceSetStyleFrame ,sceFontStyleFrameGetEffectSlant RE added --- src/core/libraries/font/font.cpp | 50 ++++++++++++++++++++++++++++---- src/core/libraries/font/font.h | 17 ++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index ef4ea7a12..2b1c43505 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -757,7 +757,7 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface // Initialize unknown fields (likely reserved or flags) renderSurface->unkn_0xd = 0; - renderSurface->unkn_0xe = 0; + renderSurface->styleFlag = 0; renderSurface->unkn_0xf = 0; // Ensure width and height are non-negative @@ -814,9 +814,26 @@ void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderS } } -s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame) { + if (!renderSurface) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!styleFrame) { + renderSurface->styleFlag &= 0xFE; // Clear style flag + } else { + // Validate magic number + if (styleFrame->magic != 0xF09) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + renderSurface->styleFlag |= 1; // Set style flag + } + + // Assign style frame pointer + renderSurface->unkn_28[0] = styleFrame; + *(uint32_t*)(renderSurface->unkn_28 + 1) = 0; // Reset related field } s32 PS4_SYSV_ABI sceFontSetEffectSlant() { @@ -904,8 +921,29 @@ s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio) { + if (!styleFrame) { + printf("[ERROR] Style frame is NULL.\n"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (styleFrame->magic != 0xF09) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the slant effect is enabled (bit 1 in flags) + if (!(styleFrame->flags & 0x02)) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + if (!slantRatio) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve slant ratio + *slantRatio = styleFrame->slantRatio; return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index c3bae6c51..e62dc9d78 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -29,14 +29,21 @@ struct OrbisFontRenderSurface { s32 widthByte; s8 pixelSizeByte; u8 unkn_0xd; - u8 unkn_0xe; + u8 styleFlag; u8 unkn_0xf; s32 width, height; u32 sc_x0; u32 sc_y0; u32 sc_x1; u32 sc_y1; - u32 unkn_28[22]; + void* unkn_28[3]; +}; + +struct OrbisFontStyleFrame { + u16 magic; // Expected to be 0xF09 + u16 flags; + // + float slantRatio; //0x24 }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); @@ -186,7 +193,8 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface int heightPixel); void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, int y0, int w, int h); -s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(); +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame); s32 PS4_SYSV_ABI sceFontSetEffectSlant(); s32 PS4_SYSV_ABI sceFontSetEffectWeight(); s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); @@ -204,7 +212,8 @@ s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio); s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(); s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(); From 6184d516212d7071a96296b871739a9ec6b66b9a Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 14:36:35 +0200 Subject: [PATCH 10/51] clang fix --- src/core/libraries/font/font.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index e62dc9d78..f89e04732 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -43,7 +43,7 @@ struct OrbisFontStyleFrame { u16 magic; // Expected to be 0xF09 u16 flags; // - float slantRatio; //0x24 + float slantRatio; // 0x24 }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); From 2d839cb144577ad8c8c66c98effc0ca4de47e145 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Feb 2025 18:46:18 +0200 Subject: [PATCH 11/51] sceFontStyleFrameGetEffectWeight --- src/core/libraries/font/font.cpp | 42 +++++++++++++++++++++--------- src/core/libraries/font/font.h | 8 ++++-- src/core/libraries/font/fontft.cpp | 10 ------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 2b1c43505..1c3af47e6 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -834,6 +834,7 @@ s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* rende // Assign style frame pointer renderSurface->unkn_28[0] = styleFrame; *(uint32_t*)(renderSurface->unkn_28 + 1) = 0; // Reset related field + return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSetEffectSlant() { @@ -947,8 +948,35 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode) { + if (!fontStyleFrame) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (fontStyleFrame->magic != 0xF09) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the weight effect is enabled (bit 2 in flags) + if (!(fontStyleFrame->flags & 0x04)) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Retrieve weight scales (default is +1.0 to maintain normal weight) + if (weightXScale) { + *weightXScale = fontStyleFrame->weightXScale + 1.0f; + } + if (weightYScale) { + *weightYScale = fontStyleFrame->weightYScale + 1.0f; + } + + // Reset mode if provided + if (mode) { + *mode = 0; + } return ORBIS_OK; } @@ -1297,16 +1325,6 @@ s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5() { return ORBIS_OK; } -s32 PS4_SYSV_ABI module_start() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI module_stop() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", 1, 1, sceFontAttachDeviceCacheBuffer); diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index f89e04732..4a5cddf9c 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -43,7 +43,9 @@ struct OrbisFontStyleFrame { u16 magic; // Expected to be 0xF09 u16 flags; // - float slantRatio; // 0x24 + float weightXScale; // 0x1c + float weightYScale; // 0x20 + float slantRatio; // 0x24 }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); @@ -214,7 +216,9 @@ s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, float* slantRatio); -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode); s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(); s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(); diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index 4a7a13318..ecc2b909f 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -113,16 +113,6 @@ s32 PS4_SYSV_ABI sceFontSelectRendererFt() { return ORBIS_OK; } -s32 PS4_SYSV_ABI module_start() { - LOG_ERROR(Lib_FontFt, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI module_stop() { - LOG_ERROR(Lib_FontFt, "(STUBBED) called"); - return ORBIS_OK; -} - void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtInitAliases); LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSetAliasFont); From 6b9c7906980a6b111cb1b137999ccc9b1327398e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 14 Mar 2025 12:26:53 +0200 Subject: [PATCH 12/51] added sceFontStyleFrameGetScalePixel --- src/core/libraries/font/font.cpp | 45 +++++++++++++++++++++++++++++--- src/core/libraries/font/font.h | 20 +++++++++----- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 1c3af47e6..415c86281 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -817,6 +817,7 @@ void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderS s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, OrbisFontStyleFrame* styleFrame) { if (!renderSurface) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -825,6 +826,7 @@ s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* rende } else { // Validate magic number if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -925,21 +927,24 @@ s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, float* slantRatio) { if (!styleFrame) { - printf("[ERROR] Style frame is NULL.\n"); + LOG_ERROR(Lib_Font, "Invalid Parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } // Validate the magic number if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid Magic"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } // Check if the slant effect is enabled (bit 1 in flags) if (!(styleFrame->flags & 0x02)) { + LOG_ERROR(Lib_Font, "Flag not set"); return ORBIS_FONT_ERROR_UNSET_PARAMETER; } if (!slantRatio) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -952,16 +957,19 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyle float* weightXScale, float* weightYScale, uint32_t* mode) { if (!fontStyleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } // Validate the magic number if (fontStyleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Magic not set"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } // Check if the weight effect is enabled (bit 2 in flags) if (!(fontStyleFrame->flags & 0x04)) { + LOG_ERROR(Lib_Font, "Flag not set"); return ORBIS_FONT_ERROR_UNSET_PARAMETER; } @@ -985,8 +993,39 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, + float* h) { + if (!styleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!(styleFrame->flags & 0x01)) { + LOG_ERROR(Lib_Font, "Scaling effect parameter not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Check if scaling is allowed + int isScalingEnabled = styleFrame->scalingFlag; + if (w) { + *w = styleFrame->scaleWidth; + if (isScalingEnabled && styleFrame->dpiX) { + *w *= ((float)styleFrame->dpiX / 72.0f); + } + } + + if (h) { + *h = styleFrame->scaleHeight; + if (isScalingEnabled && styleFrame->dpiY) { + *h *= ((float)styleFrame->dpiY / 72.0f); + } + } + return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 4a5cddf9c..a8e239126 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -40,12 +40,17 @@ struct OrbisFontRenderSurface { }; struct OrbisFontStyleFrame { - u16 magic; // Expected to be 0xF09 - u16 flags; - // - float weightXScale; // 0x1c - float weightYScale; // 0x20 - float slantRatio; // 0x24 + /*0x00*/ u16 magic; // Expected to be 0xF09 + /*0x02*/ u16 flags; + /*0x04*/ s32 dpiX; // DPI scaling factor for width + /*0x08*/ s32 dpiY; // DPI scaling factor for height + /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled + /*0x10*/ + /*0x14*/ float scaleWidth; // Width scaling factor + /*0x18*/ float scaleHeight; // Height scaling factor + /*0x1c*/ float weightXScale; + /*0x20*/ float weightYScale; + /*0x24*/ float slantRatio; }; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); @@ -220,7 +225,8 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyle float* weightXScale, float* weightYScale, uint32_t* mode); s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, + float* h); s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(); s32 PS4_SYSV_ABI sceFontStyleFrameInit(); s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(); From 058a3e130848de70b3724e2395b088fac783d33e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 10 May 2025 13:49:49 +0300 Subject: [PATCH 13/51] Update types.h --- src/common/logging/types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 27a87e082..f1eb16488 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -105,6 +105,8 @@ enum class Class : u8 { Lib_Zlib, ///< The LibSceZlib implementation. Lib_Hmd, ///< The LibSceHmd implementation. Lib_SigninDialog, ///< The LibSigninDialog implementation. + Lib_Font, ///< The libSceFont implementation. + Lib_FontFt, ///< The libSceFontFt implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend From 9b33b6632261ea73080fe7492dab3579bafc84bc Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 4 Nov 2025 09:33:05 +0200 Subject: [PATCH 14/51] fixup merge --- src/common/logging/types.h | 189 ++++++----- src/core/libraries/font/font.cpp | 527 +++++++++++++---------------- src/core/libraries/font/fontft.cpp | 47 ++- src/emulator.cpp | 2 +- 4 files changed, 347 insertions(+), 418 deletions(-) diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 34905a4aa..035a959db 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -29,99 +29,102 @@ enum class Level : u8 { * filter.cpp. */ enum class Class : u8 { - Log, ///< Messages about the log system itself - Common, ///< Library routines - Common_Filesystem, ///< Filesystem interface library - Common_Memory, ///< Memory mapping and management functions - Core, ///< LLE emulation core - Core_Linker, ///< The module linker - Core_Devices, ///< Devices emulation - Config, ///< Emulator configuration (including commandline) - Debug, ///< Debugging tools - Kernel, ///< The HLE implementation of the PS4 kernel. - Kernel_Pthread, ///< The pthread implementation of the kernel. - Kernel_Fs, ///< The filesystem implementation of the kernel. - Kernel_Vmm, ///< The virtual memory implementation of the kernel. - Kernel_Event, ///< The event management implementation of the kernel. - Kernel_Sce, ///< The sony specific interfaces provided by the kernel. - Lib, ///< HLE implementation of system library. Each major library - ///< should have its own subclass. - Lib_LibC, ///< The LibC implementation. - Lib_LibcInternal, ///< The LibcInternal implementation. - Lib_Kernel, ///< The LibKernel implementation. - Lib_Pad, ///< The LibScePad implementation. - Lib_GnmDriver, ///< The LibSceGnmDriver implementation. - Lib_SystemService, ///< The LibSceSystemService implementation. - Lib_UserService, ///< The LibSceUserService implementation. - Lib_VideoOut, ///< The LibSceVideoOut implementation. - Lib_CommonDlg, ///< The LibSceCommonDialog implementation. - Lib_MsgDlg, ///< The LibSceMsgDialog implementation. - Lib_AudioOut, ///< The LibSceAudioOut implementation. - Lib_AudioIn, ///< The LibSceAudioIn implementation. - Lib_Move, ///< The LibSceMove implementation. - Lib_Net, ///< The LibSceNet implementation. - Lib_NetCtl, ///< The LibSceNetCtl implementation. - Lib_SaveData, ///< The LibSceSaveData implementation. - Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. - Lib_Ssl, ///< The LibSceSsl implementation. - Lib_Ssl2, ///< The LibSceSsl2 implementation. - Lib_Http, ///< The LibSceHttp implementation. - Lib_Http2, ///< The LibSceHttp2 implementation. - Lib_SysModule, ///< The LibSceSysModule implementation - Lib_NpCommon, ///< The LibSceNpCommon implementation - Lib_NpAuth, ///< The LibSceNpAuth implementation - Lib_NpManager, ///< The LibSceNpManager implementation - Lib_NpScore, ///< The LibSceNpScore implementation - Lib_NpTrophy, ///< The LibSceNpTrophy implementation - Lib_NpWebApi, ///< The LibSceWebApi implementation - Lib_Screenshot, ///< The LibSceScreenshot implementation - Lib_LibCInternal, ///< The LibCInternal implementation. - Lib_AppContent, ///< The LibSceAppContent implementation. - Lib_Rtc, ///< The LibSceRtc implementation. - Lib_DiscMap, ///< The LibSceDiscMap implementation. - Lib_Png, ///< The LibScePng implementation. - Lib_Jpeg, ///< The LibSceJpeg implementation. - Lib_PlayGo, ///< The LibScePlayGo implementation. - Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation. - Lib_Random, ///< The libSceRandom implementation. - Lib_Usbd, ///< The LibSceUsbd implementation. - Lib_Ajm, ///< The LibSceAjm implementation. - Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. - Lib_ImeDialog, ///< The LibSceImeDialog implementation. - Lib_AvPlayer, ///< The LibSceAvPlayer implementation. - Lib_Ngs2, ///< The LibSceNgs2 implementation. - Lib_Audio3d, ///< The LibSceAudio3d implementation. - Lib_Ime, ///< The LibSceIme implementation - Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation - Lib_Remoteplay, ///< The LibSceRemotePlay implementation - Lib_SharePlay, ///< The LibSceSharePlay implemenation - Lib_Fiber, ///< The LibSceFiber implementation. - Lib_Vdec2, ///< The LibSceVideodec2 implementation. - Lib_Videodec, ///< The LibSceVideodec implementation. - Lib_Voice, ///< The LibSceVoice implementation. - Lib_RazorCpu, ///< The LibRazorCpu implementation. - Lib_Mouse, ///< The LibSceMouse implementation - Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation - Lib_NpParty, ///< The LibSceNpParty implementation - Lib_Zlib, ///< The LibSceZlib implementation. - Lib_Hmd, ///< The LibSceHmd implementation. - Lib_SigninDialog, ///< The LibSigninDialog implementation. - Lib_Font, ///< The libSceFont implementation. - Lib_FontFt, ///< The libSceFontFt implementation. - Lib_Camera, ///< The LibCamera implementation. - Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. - Lib_CompanionUtil, ///< The LibCompanionUtil implementation. - Lib_Font, ///< The libSceFont implementation. - Lib_FontFt, ///< The libSceFontFt implementation. - Frontend, ///< Emulator UI - Render, ///< Video Core - Render_Vulkan, ///< Vulkan backend - Render_Recompiler, ///< Shader recompiler - ImGui, ///< ImGui - Loader, ///< ROM loader - Input, ///< Input emulation - Tty, ///< Debug output from emu - Count ///< Total number of logging classes + Log, ///< Messages about the log system itself + Common, ///< Library routines + Common_Filesystem, ///< Filesystem interface library + Common_Memory, ///< Memory mapping and management functions + Core, ///< LLE emulation core + Core_Linker, ///< The module linker + Core_Devices, ///< Devices emulation + Config, ///< Emulator configuration (including commandline) + Debug, ///< Debugging tools + Kernel, ///< The HLE implementation of the PS4 kernel. + Kernel_Pthread, ///< The pthread implementation of the kernel. + Kernel_Fs, ///< The filesystem implementation of the kernel. + Kernel_Vmm, ///< The virtual memory implementation of the kernel. + Kernel_Event, ///< The event management implementation of the kernel. + Kernel_Sce, ///< The sony specific interfaces provided by the kernel. + Lib, ///< HLE implementation of system library. Each major library + ///< should have its own subclass. + Lib_LibC, ///< The LibC implementation. + Lib_LibcInternal, ///< The LibcInternal implementation. + Lib_Kernel, ///< The LibKernel implementation. + Lib_Pad, ///< The LibScePad implementation. + Lib_SystemGesture, ///< The LibSceSystemGesture implementation. + Lib_GnmDriver, ///< The LibSceGnmDriver implementation. + Lib_SystemService, ///< The LibSceSystemService implementation. + Lib_UserService, ///< The LibSceUserService implementation. + Lib_VideoOut, ///< The LibSceVideoOut implementation. + Lib_CommonDlg, ///< The LibSceCommonDialog implementation. + Lib_MsgDlg, ///< The LibSceMsgDialog implementation. + Lib_AudioOut, ///< The LibSceAudioOut implementation. + Lib_AudioIn, ///< The LibSceAudioIn implementation. + Lib_Move, ///< The LibSceMove implementation. + Lib_Net, ///< The LibSceNet implementation. + Lib_NetCtl, ///< The LibSceNetCtl implementation. + Lib_SaveData, ///< The LibSceSaveData implementation. + Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. + Lib_Ssl, ///< The LibSceSsl implementation. + Lib_Ssl2, ///< The LibSceSsl2 implementation. + Lib_Http, ///< The LibSceHttp implementation. + Lib_Http2, ///< The LibSceHttp2 implementation. + Lib_SysModule, ///< The LibSceSysModule implementation + Lib_NpCommon, ///< The LibSceNpCommon implementation + Lib_NpAuth, ///< The LibSceNpAuth implementation + Lib_NpManager, ///< The LibSceNpManager implementation + Lib_NpScore, ///< The LibSceNpScore implementation + Lib_NpTrophy, ///< The LibSceNpTrophy implementation + Lib_NpWebApi, ///< The LibSceWebApi implementation + Lib_NpProfileDialog, ///< The LibSceNpProfileDialog implementation + Lib_NpSnsFacebookDialog, ///< The LibSceNpSnsFacebookDialog implementation + Lib_Screenshot, ///< The LibSceScreenshot implementation + Lib_LibCInternal, ///< The LibCInternal implementation. + Lib_AppContent, ///< The LibSceAppContent implementation. + Lib_Rtc, ///< The LibSceRtc implementation. + Lib_DiscMap, ///< The LibSceDiscMap implementation. + Lib_Png, ///< The LibScePng implementation. + Lib_Jpeg, ///< The LibSceJpeg implementation. + Lib_PlayGo, ///< The LibScePlayGo implementation. + Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation. + Lib_Random, ///< The LibSceRandom implementation. + Lib_Usbd, ///< The LibSceUsbd implementation. + Lib_Ajm, ///< The LibSceAjm implementation. + Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. + Lib_ImeDialog, ///< The LibSceImeDialog implementation. + Lib_AvPlayer, ///< The LibSceAvPlayer implementation. + Lib_Ngs2, ///< The LibSceNgs2 implementation. + Lib_Audio3d, ///< The LibSceAudio3d implementation. + Lib_Ime, ///< The LibSceIme implementation + Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation + Lib_Remoteplay, ///< The LibSceRemotePlay implementation + Lib_SharePlay, ///< The LibSceSharePlay implemenation + Lib_Fiber, ///< The LibSceFiber implementation. + Lib_Vdec2, ///< The LibSceVideodec2 implementation. + Lib_Videodec, ///< The LibSceVideodec implementation. + Lib_Voice, ///< The LibSceVoice implementation. + Lib_RazorCpu, ///< The LibRazorCpu implementation. + Lib_Mouse, ///< The LibSceMouse implementation + Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation + Lib_NpParty, ///< The LibSceNpParty implementation + Lib_Zlib, ///< The LibSceZlib implementation. + Lib_Hmd, ///< The LibSceHmd implementation. + Lib_HmdSetupDialog, ///< The LibSceHmdSetupDialog implementation. + Lib_SigninDialog, ///< The LibSigninDialog implementation. + Lib_Camera, ///< The LibCamera implementation. + Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. + Lib_CompanionUtil, ///< The LibCompanionUtil implementation. + Lib_VrTracker, ///< The LibSceVrTracker implementation. + Lib_Font, ///< The libSceFont implementation. + Lib_FontFt, ///< The libSceFontFt implementation. + Frontend, ///< Emulator UI + Render, ///< Video Core + Render_Vulkan, ///< Vulkan backend + Render_Recompiler, ///< Shader recompiler + ImGui, ///< ImGui + Loader, ///< ROM loader + Input, ///< Input emulation + Tty, ///< Debug output from emu + Count ///< Total number of logging classes }; } // namespace Common::Log diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 415c86281..1454004aa 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -1365,316 +1365,247 @@ s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5() { } void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", 1, 1, - sceFontAttachDeviceCacheBuffer); - LIB_FUNCTION("3OdRkSjOcog", "libSceFont", 1, "libSceFont", 1, 1, sceFontBindRenderer); - LIB_FUNCTION("6DFUkCwQLa8", "libSceFont", 1, "libSceFont", 1, 1, sceFontCharacterGetBidiLevel); - LIB_FUNCTION("coCrV6IWplE", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", sceFontAttachDeviceCacheBuffer); + LIB_FUNCTION("3OdRkSjOcog", "libSceFont", 1, "libSceFont", sceFontBindRenderer); + LIB_FUNCTION("6DFUkCwQLa8", "libSceFont", 1, "libSceFont", sceFontCharacterGetBidiLevel); + LIB_FUNCTION("coCrV6IWplE", "libSceFont", 1, "libSceFont", sceFontCharacterGetSyllableStringState); - LIB_FUNCTION("zN3+nuA0SFQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCharacterGetTextFontCode); - LIB_FUNCTION("mxgmMj-Mq-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontCharacterGetTextOrder); - LIB_FUNCTION("-P6X35Rq2-E", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("zN3+nuA0SFQ", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextFontCode); + LIB_FUNCTION("mxgmMj-Mq-o", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextOrder); + LIB_FUNCTION("-P6X35Rq2-E", "libSceFont", 1, "libSceFont", sceFontCharacterLooksFormatCharacters); - LIB_FUNCTION("SaRlqtqaCew", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCharacterLooksWhiteSpace); - LIB_FUNCTION("6Gqlv5KdTbU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCharacterRefersTextBack); - LIB_FUNCTION("BkjBP+YC19w", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCharacterRefersTextNext); - LIB_FUNCTION("lVSR5ftvNag", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCharactersRefersTextCodes); - LIB_FUNCTION("I9R5VC6eZWo", "libSceFont", 1, "libSceFont", 1, 1, sceFontClearDeviceCache); - LIB_FUNCTION("vzHs3C8lWJk", "libSceFont", 1, "libSceFont", 1, 1, sceFontCloseFont); - LIB_FUNCTION("MpKSBaYKluo", "libSceFont", 1, "libSceFont", 1, 1, sceFontControl); - LIB_FUNCTION("WBNBaj9XiJU", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateGraphicsDevice); - LIB_FUNCTION("4So0MC3oBIM", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateGraphicsService); - LIB_FUNCTION("NlO5Qlhjkng", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("SaRlqtqaCew", "libSceFont", 1, "libSceFont", sceFontCharacterLooksWhiteSpace); + LIB_FUNCTION("6Gqlv5KdTbU", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextBack); + LIB_FUNCTION("BkjBP+YC19w", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextNext); + LIB_FUNCTION("lVSR5ftvNag", "libSceFont", 1, "libSceFont", sceFontCharactersRefersTextCodes); + LIB_FUNCTION("I9R5VC6eZWo", "libSceFont", 1, "libSceFont", sceFontClearDeviceCache); + LIB_FUNCTION("vzHs3C8lWJk", "libSceFont", 1, "libSceFont", sceFontCloseFont); + LIB_FUNCTION("MpKSBaYKluo", "libSceFont", 1, "libSceFont", sceFontControl); + LIB_FUNCTION("WBNBaj9XiJU", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsDevice); + LIB_FUNCTION("4So0MC3oBIM", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsService); + LIB_FUNCTION("NlO5Qlhjkng", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsServiceWithEdition); - LIB_FUNCTION("nWrfPI4Okmg", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateLibrary); - LIB_FUNCTION("n590hj5Oe-k", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCreateLibraryWithEdition); - LIB_FUNCTION("u5fZd3KZcs0", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateRenderer); - LIB_FUNCTION("WaSFJoRWXaI", "libSceFont", 1, "libSceFont", 1, 1, - sceFontCreateRendererWithEdition); - LIB_FUNCTION("MO24vDhmS4E", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateString); - LIB_FUNCTION("cYrMGk1wrMA", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateWords); - LIB_FUNCTION("7rogx92EEyc", "libSceFont", 1, "libSceFont", 1, 1, sceFontCreateWritingLine); - LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDefineAttribute); - LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", 1, 1, sceFontDeleteGlyph); - LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyGraphicsDevice); - LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyGraphicsService); - LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyLibrary); - LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyRenderer); - LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyString); - LIB_FUNCTION("hWE4AwNixqY", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyWords); - LIB_FUNCTION("PEjv7CVDRYs", "libSceFont", 1, "libSceFont", 1, 1, sceFontDestroyWritingLine); - LIB_FUNCTION("UuY-OJF+f0k", "libSceFont", 1, "libSceFont", 1, 1, - sceFontDettachDeviceCacheBuffer); - LIB_FUNCTION("C-4Qw5Srlyw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGenerateCharGlyph); - LIB_FUNCTION("5kx49CAlO-M", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetAttribute); - LIB_FUNCTION("OINC0X9HGBY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetCharGlyphCode); - LIB_FUNCTION("L97d+3OgMlE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetCharGlyphMetrics); - LIB_FUNCTION("ynSqYL8VpoA", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetEffectSlant); - LIB_FUNCTION("d7dDgRY+Bzw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetEffectWeight); - LIB_FUNCTION("ZB8xRemRRG8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontGlyphsCount); - LIB_FUNCTION("4X14YSK4Ldk", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGetFontGlyphsOutlineProfile); - LIB_FUNCTION("eb9S3zNlV5o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontMetrics); - LIB_FUNCTION("tiIlroGki+g", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetFontResolution); - LIB_FUNCTION("3hVv3SNoL6E", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGetFontStyleInformation); - LIB_FUNCTION("gVQpMBuB7fE", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGetGlyphExpandBufferState); - LIB_FUNCTION("imxVx8lm+KM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetHorizontalLayout); - LIB_FUNCTION("sDuhHGNhHvE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetKerning); - LIB_FUNCTION("LzmHDnlcwfQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetLibrary); - LIB_FUNCTION("BozJej5T6fs", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetPixelResolution); - LIB_FUNCTION("IQtleGLL5pQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGetRenderCharGlyphMetrics); - LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectSlant); - LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderEffectWeight); - LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScaledKerning); - LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePixel); - LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetRenderScalePoint); - LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetResolutionDpi); - LIB_FUNCTION("CkVmLoCNN-8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScalePixel); - LIB_FUNCTION("GoF2bhB7LYk", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScalePoint); - LIB_FUNCTION("IrXeG0Lc6nA", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetScriptLanguage); - LIB_FUNCTION("7-miUT6pNQw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetTypographicDesign); - LIB_FUNCTION("3BrWWFU+4ts", "libSceFont", 1, "libSceFont", 1, 1, sceFontGetVerticalLayout); - LIB_FUNCTION("8-zmgsxkBek", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphDefineAttribute); - LIB_FUNCTION("oO33Uex4Ui0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetAttribute); - LIB_FUNCTION("PXlA0M8ax40", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetGlyphForm); - LIB_FUNCTION("XUfSWpLhrUw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetMetricsForm); - LIB_FUNCTION("lNnUqa1zA-M", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphGetScalePixel); - LIB_FUNCTION("ntrc3bEWlvQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRefersMetrics); - LIB_FUNCTION("9kTbF59TjLs", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGlyphRefersMetricsHorizontal); - LIB_FUNCTION("nJavPEdMDvM", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("nWrfPI4Okmg", "libSceFont", 1, "libSceFont", sceFontCreateLibrary); + LIB_FUNCTION("n590hj5Oe-k", "libSceFont", 1, "libSceFont", sceFontCreateLibraryWithEdition); + LIB_FUNCTION("u5fZd3KZcs0", "libSceFont", 1, "libSceFont", sceFontCreateRenderer); + LIB_FUNCTION("WaSFJoRWXaI", "libSceFont", 1, "libSceFont", sceFontCreateRendererWithEdition); + LIB_FUNCTION("MO24vDhmS4E", "libSceFont", 1, "libSceFont", sceFontCreateString); + LIB_FUNCTION("cYrMGk1wrMA", "libSceFont", 1, "libSceFont", sceFontCreateWords); + LIB_FUNCTION("7rogx92EEyc", "libSceFont", 1, "libSceFont", sceFontCreateWritingLine); + LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", sceFontDefineAttribute); + LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", sceFontDeleteGlyph); + LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsDevice); + LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsService); + LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", sceFontDestroyLibrary); + LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", sceFontDestroyRenderer); + LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", sceFontDestroyString); + LIB_FUNCTION("hWE4AwNixqY", "libSceFont", 1, "libSceFont", sceFontDestroyWords); + LIB_FUNCTION("PEjv7CVDRYs", "libSceFont", 1, "libSceFont", sceFontDestroyWritingLine); + LIB_FUNCTION("UuY-OJF+f0k", "libSceFont", 1, "libSceFont", sceFontDettachDeviceCacheBuffer); + LIB_FUNCTION("C-4Qw5Srlyw", "libSceFont", 1, "libSceFont", sceFontGenerateCharGlyph); + LIB_FUNCTION("5kx49CAlO-M", "libSceFont", 1, "libSceFont", sceFontGetAttribute); + LIB_FUNCTION("OINC0X9HGBY", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphCode); + LIB_FUNCTION("L97d+3OgMlE", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphMetrics); + LIB_FUNCTION("ynSqYL8VpoA", "libSceFont", 1, "libSceFont", sceFontGetEffectSlant); + LIB_FUNCTION("d7dDgRY+Bzw", "libSceFont", 1, "libSceFont", sceFontGetEffectWeight); + LIB_FUNCTION("ZB8xRemRRG8", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsCount); + LIB_FUNCTION("4X14YSK4Ldk", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsOutlineProfile); + LIB_FUNCTION("eb9S3zNlV5o", "libSceFont", 1, "libSceFont", sceFontGetFontMetrics); + LIB_FUNCTION("tiIlroGki+g", "libSceFont", 1, "libSceFont", sceFontGetFontResolution); + LIB_FUNCTION("3hVv3SNoL6E", "libSceFont", 1, "libSceFont", sceFontGetFontStyleInformation); + LIB_FUNCTION("gVQpMBuB7fE", "libSceFont", 1, "libSceFont", sceFontGetGlyphExpandBufferState); + LIB_FUNCTION("imxVx8lm+KM", "libSceFont", 1, "libSceFont", sceFontGetHorizontalLayout); + LIB_FUNCTION("sDuhHGNhHvE", "libSceFont", 1, "libSceFont", sceFontGetKerning); + LIB_FUNCTION("LzmHDnlcwfQ", "libSceFont", 1, "libSceFont", sceFontGetLibrary); + LIB_FUNCTION("BozJej5T6fs", "libSceFont", 1, "libSceFont", sceFontGetPixelResolution); + LIB_FUNCTION("IQtleGLL5pQ", "libSceFont", 1, "libSceFont", sceFontGetRenderCharGlyphMetrics); + LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectSlant); + LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectWeight); + LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", sceFontGetRenderScaledKerning); + LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePixel); + LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePoint); + LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", sceFontGetResolutionDpi); + LIB_FUNCTION("CkVmLoCNN-8", "libSceFont", 1, "libSceFont", sceFontGetScalePixel); + LIB_FUNCTION("GoF2bhB7LYk", "libSceFont", 1, "libSceFont", sceFontGetScalePoint); + LIB_FUNCTION("IrXeG0Lc6nA", "libSceFont", 1, "libSceFont", sceFontGetScriptLanguage); + LIB_FUNCTION("7-miUT6pNQw", "libSceFont", 1, "libSceFont", sceFontGetTypographicDesign); + LIB_FUNCTION("3BrWWFU+4ts", "libSceFont", 1, "libSceFont", sceFontGetVerticalLayout); + LIB_FUNCTION("8-zmgsxkBek", "libSceFont", 1, "libSceFont", sceFontGlyphDefineAttribute); + LIB_FUNCTION("oO33Uex4Ui0", "libSceFont", 1, "libSceFont", sceFontGlyphGetAttribute); + LIB_FUNCTION("PXlA0M8ax40", "libSceFont", 1, "libSceFont", sceFontGlyphGetGlyphForm); + LIB_FUNCTION("XUfSWpLhrUw", "libSceFont", 1, "libSceFont", sceFontGlyphGetMetricsForm); + LIB_FUNCTION("lNnUqa1zA-M", "libSceFont", 1, "libSceFont", sceFontGlyphGetScalePixel); + LIB_FUNCTION("ntrc3bEWlvQ", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetrics); + LIB_FUNCTION("9kTbF59TjLs", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetricsHorizontal); + LIB_FUNCTION("nJavPEdMDvM", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetricsHorizontalAdvance); - LIB_FUNCTION("JCnVgZgcucs", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("JCnVgZgcucs", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetricsHorizontalX); - LIB_FUNCTION("R1T4i+DOhNY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRefersOutline); - LIB_FUNCTION("RmkXfBcZnrM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGlyphRenderImage); - LIB_FUNCTION("r4KEihtwxGs", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGlyphRenderImageHorizontal); - LIB_FUNCTION("n22d-HIdmMg", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGlyphRenderImageVertical); - LIB_FUNCTION("RL2cAQgyXR8", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsBeginFrame); - LIB_FUNCTION("dUmIK6QjT7E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsDrawingCancel); - LIB_FUNCTION("X2Vl3yU19Zw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsDrawingFinish); - LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsEndFrame); - LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsExchangeResource); - LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillMethodInit); - LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillPlotInit); - LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsFillPlotSetLayout); - LIB_FUNCTION("PEYQJa+MWnk", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsFillPlotSetMapping); - LIB_FUNCTION("21g4m4kYF6g", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsFillRatesInit); - LIB_FUNCTION("pJzji5FvdxU", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("R1T4i+DOhNY", "libSceFont", 1, "libSceFont", sceFontGlyphRefersOutline); + LIB_FUNCTION("RmkXfBcZnrM", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImage); + LIB_FUNCTION("r4KEihtwxGs", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageHorizontal); + LIB_FUNCTION("n22d-HIdmMg", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageVertical); + LIB_FUNCTION("RL2cAQgyXR8", "libSceFont", 1, "libSceFont", sceFontGraphicsBeginFrame); + LIB_FUNCTION("dUmIK6QjT7E", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingCancel); + LIB_FUNCTION("X2Vl3yU19Zw", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingFinish); + LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", sceFontGraphicsEndFrame); + LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", sceFontGraphicsExchangeResource); + LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", sceFontGraphicsFillMethodInit); + LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotInit); + LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetLayout); + LIB_FUNCTION("PEYQJa+MWnk", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetMapping); + LIB_FUNCTION("21g4m4kYF6g", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesInit); + LIB_FUNCTION("pJzji5FvdxU", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetFillEffect); - LIB_FUNCTION("scaro-xEuUM", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsFillRatesSetLayout); - LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsFillRatesSetMapping); - LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsGetDeviceUsage); - LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRegionInit); - LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsRegionInitCircular); - LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsRegionInitRoundish); - LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRelease); - LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsRenderResource); - LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetFramePolicy); - LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupClipping); - LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupColorRates); - LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupFillMethod); - LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupFillRates); - LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupGlyphFill); - LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupGlyphFillPlot); - LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupHandleDefault); - LIB_FUNCTION("MEAmHMynQXE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupLocation); - LIB_FUNCTION("XRUOmQhnYO4", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupPositioning); - LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupRotation); - LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupScaling); - LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsSetupShapeFill); - LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsSetupShapeFillPlot); - LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsStructureCanvas); - LIB_FUNCTION("cpjgdlMYdOM", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("scaro-xEuUM", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetLayout); + LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetMapping); + LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", sceFontGraphicsGetDeviceUsage); + LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInit); + LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitCircular); + LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitRoundish); + LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", sceFontGraphicsRelease); + LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", sceFontGraphicsRenderResource); + LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetFramePolicy); + LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupClipping); + LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupColorRates); + LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillMethod); + LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillRates); + LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFill); + LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFillPlot); + LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupHandleDefault); + LIB_FUNCTION("MEAmHMynQXE", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupLocation); + LIB_FUNCTION("XRUOmQhnYO4", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupPositioning); + LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupRotation); + LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupScaling); + LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFill); + LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFillPlot); + LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureCanvas); + LIB_FUNCTION("cpjgdlMYdOM", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureCanvasSequence); - LIB_FUNCTION("774Mee21wKk", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsStructureDesign); - LIB_FUNCTION("Hp3NIFhUXvQ", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("774Mee21wKk", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureDesign); + LIB_FUNCTION("Hp3NIFhUXvQ", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureDesignResource); - LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureSurfaceTexture); - LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateClipping); - LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateColorRates); - LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateFillMethod); - LIB_FUNCTION("MnUYAs2jVuU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateFillRates); - LIB_FUNCTION("R-oVDMusYbc", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateGlyphFill); - LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateGlyphFillPlot); - LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateLocation); - LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdatePositioning); - LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateRotation); - LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", 1, 1, sceFontGraphicsUpdateScaling); - LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateShapeFill); - LIB_FUNCTION("I5Rf2rXvBKQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontGraphicsUpdateShapeFillPlot); - LIB_FUNCTION("whrS4oksXc4", "libSceFont", 1, "libSceFont", 1, 1, sceFontMemoryInit); - LIB_FUNCTION("h6hIgxXEiEc", "libSceFont", 1, "libSceFont", 1, 1, sceFontMemoryTerm); - LIB_FUNCTION("RvXyHMUiLhE", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontFile); - LIB_FUNCTION("JzCH3SCFnAU", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontInstance); - LIB_FUNCTION("KXUpebrFk1U", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontMemory); - LIB_FUNCTION("cKYtVmeSTcw", "libSceFont", 1, "libSceFont", 1, 1, sceFontOpenFontSet); - LIB_FUNCTION("Z2cdsqJH+5k", "libSceFont", 1, "libSceFont", 1, 1, sceFontRebindRenderer); - LIB_FUNCTION("3G4zhgKuxE8", "libSceFont", 1, "libSceFont", 1, 1, sceFontRenderCharGlyphImage); - LIB_FUNCTION("kAenWy1Zw5o", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateClipping); + LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateColorRates); + LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillMethod); + LIB_FUNCTION("MnUYAs2jVuU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillRates); + LIB_FUNCTION("R-oVDMusYbc", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFill); + LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFillPlot); + LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateLocation); + LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdatePositioning); + LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateRotation); + LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateScaling); + LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFill); + LIB_FUNCTION("I5Rf2rXvBKQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFillPlot); + LIB_FUNCTION("whrS4oksXc4", "libSceFont", 1, "libSceFont", sceFontMemoryInit); + LIB_FUNCTION("h6hIgxXEiEc", "libSceFont", 1, "libSceFont", sceFontMemoryTerm); + LIB_FUNCTION("RvXyHMUiLhE", "libSceFont", 1, "libSceFont", sceFontOpenFontFile); + LIB_FUNCTION("JzCH3SCFnAU", "libSceFont", 1, "libSceFont", sceFontOpenFontInstance); + LIB_FUNCTION("KXUpebrFk1U", "libSceFont", 1, "libSceFont", sceFontOpenFontMemory); + LIB_FUNCTION("cKYtVmeSTcw", "libSceFont", 1, "libSceFont", sceFontOpenFontSet); + LIB_FUNCTION("Z2cdsqJH+5k", "libSceFont", 1, "libSceFont", sceFontRebindRenderer); + LIB_FUNCTION("3G4zhgKuxE8", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImage); + LIB_FUNCTION("kAenWy1Zw5o", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImageHorizontal); - LIB_FUNCTION("i6UNdSig1uE", "libSceFont", 1, "libSceFont", 1, 1, - sceFontRenderCharGlyphImageVertical); - LIB_FUNCTION("amcmrY62BD4", "libSceFont", 1, "libSceFont", 1, 1, - sceFontRendererGetOutlineBufferSize); - LIB_FUNCTION("ai6AfGrBs4o", "libSceFont", 1, "libSceFont", 1, 1, - sceFontRendererResetOutlineBuffer); - LIB_FUNCTION("ydF+WuH0fAk", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("i6UNdSig1uE", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImageVertical); + LIB_FUNCTION("amcmrY62BD4", "libSceFont", 1, "libSceFont", sceFontRendererGetOutlineBufferSize); + LIB_FUNCTION("ai6AfGrBs4o", "libSceFont", 1, "libSceFont", sceFontRendererResetOutlineBuffer); + LIB_FUNCTION("ydF+WuH0fAk", "libSceFont", 1, "libSceFont", sceFontRendererSetOutlineBufferPolicy); - LIB_FUNCTION("gdUCnU0gHdI", "libSceFont", 1, "libSceFont", 1, 1, sceFontRenderSurfaceInit); - LIB_FUNCTION("vRxf4d0ulPs", "libSceFont", 1, "libSceFont", 1, 1, - sceFontRenderSurfaceSetScissor); - LIB_FUNCTION("0hr-w30SjiI", "libSceFont", 1, "libSceFont", 1, 1, - sceFontRenderSurfaceSetStyleFrame); - LIB_FUNCTION("TMtqoFQjjbA", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetEffectSlant); - LIB_FUNCTION("v0phZwa4R5o", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetEffectWeight); - LIB_FUNCTION("kihFGYJee7o", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetFontsOpenMode); - LIB_FUNCTION("I1acwR7Qp8E", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetResolutionDpi); - LIB_FUNCTION("N1EBMeGhf7E", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScalePixel); - LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScalePoint); - LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetScriptLanguage); - LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetTypographicDesign); - LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderEffectSlant); - LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", 1, 1, - sceFontSetupRenderEffectWeight); - LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePixel); - LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", 1, 1, sceFontSetupRenderScalePoint); - LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetTerminateCode); - LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStringGetTerminateOrder); - LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", 1, 1, sceFontStringGetWritingForm); - LIB_FUNCTION("hq5LffQjz-s", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStringRefersRenderCharacters); - LIB_FUNCTION("Avv7OApgCJk", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStringRefersTextCharacters); - LIB_FUNCTION("lOfduYnjgbo", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameGetEffectSlant); - LIB_FUNCTION("HIUdjR-+Wl8", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameGetEffectWeight); - LIB_FUNCTION("VSw18Aqzl0U", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameGetResolutionDpi); - LIB_FUNCTION("2QfqfeLblbg", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameGetScalePixel); - LIB_FUNCTION("7x2xKiiB7MA", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameGetScalePoint); - LIB_FUNCTION("la2AOWnHEAc", "libSceFont", 1, "libSceFont", 1, 1, sceFontStyleFrameInit); - LIB_FUNCTION("394sckksiCU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameSetEffectSlant); - LIB_FUNCTION("faw77-pEBmU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameSetEffectWeight); - LIB_FUNCTION("dB4-3Wdwls8", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameSetResolutionDpi); - LIB_FUNCTION("da4rQ4-+p-4", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameSetScalePixel); - LIB_FUNCTION("O997laxY-Ys", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameSetScalePoint); - LIB_FUNCTION("dUmABkAnVgk", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameUnsetEffectSlant); - LIB_FUNCTION("hwsuXgmKdaw", "libSceFont", 1, "libSceFont", 1, 1, - sceFontStyleFrameUnsetEffectWeight); - LIB_FUNCTION("bePC0L0vQWY", "libSceFont", 1, "libSceFont", 1, 1, sceFontStyleFrameUnsetScale); - LIB_FUNCTION("mz2iTY0MK4A", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportExternalFonts); - LIB_FUNCTION("71w5DzObuZI", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportGlyphs); - LIB_FUNCTION("SsRbbCiWoGw", "libSceFont", 1, "libSceFont", 1, 1, sceFontSupportSystemFonts); - LIB_FUNCTION("IPoYwwlMx-g", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextCodesStepBack); - LIB_FUNCTION("olSmXY+XP1E", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextCodesStepNext); - LIB_FUNCTION("oaJ1BpN2FQk", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextSourceInit); - LIB_FUNCTION("VRFd3diReec", "libSceFont", 1, "libSceFont", 1, 1, sceFontTextSourceRewind); - LIB_FUNCTION("eCRMCSk96NU", "libSceFont", 1, "libSceFont", 1, 1, - sceFontTextSourceSetDefaultFont); - LIB_FUNCTION("OqQKX0h5COw", "libSceFont", 1, "libSceFont", 1, 1, - sceFontTextSourceSetWritingForm); - LIB_FUNCTION("1QjhKxrsOB8", "libSceFont", 1, "libSceFont", 1, 1, sceFontUnbindRenderer); - LIB_FUNCTION("H-FNq8isKE0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWordsFindWordCharacters); - LIB_FUNCTION("fljdejMcG1c", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingGetRenderMetrics); - LIB_FUNCTION("fD5rqhEXKYQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingInit); - LIB_FUNCTION("1+DgKL0haWQ", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingLineClear); - LIB_FUNCTION("JQKWIsS9joE", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingLineGetOrderingSpace); - LIB_FUNCTION("nlU2VnfpqTM", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingLineGetRenderMetrics); - LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingLineRefersRenderStep); - LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", 1, 1, sceFontWritingLineWritesOrder); - LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingRefersRenderStep); - LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", 1, 1, + LIB_FUNCTION("gdUCnU0gHdI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceInit); + LIB_FUNCTION("vRxf4d0ulPs", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetScissor); + LIB_FUNCTION("0hr-w30SjiI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetStyleFrame); + LIB_FUNCTION("TMtqoFQjjbA", "libSceFont", 1, "libSceFont", sceFontSetEffectSlant); + LIB_FUNCTION("v0phZwa4R5o", "libSceFont", 1, "libSceFont", sceFontSetEffectWeight); + LIB_FUNCTION("kihFGYJee7o", "libSceFont", 1, "libSceFont", sceFontSetFontsOpenMode); + LIB_FUNCTION("I1acwR7Qp8E", "libSceFont", 1, "libSceFont", sceFontSetResolutionDpi); + LIB_FUNCTION("N1EBMeGhf7E", "libSceFont", 1, "libSceFont", sceFontSetScalePixel); + LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", sceFontSetScalePoint); + LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", sceFontSetScriptLanguage); + LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", sceFontSetTypographicDesign); + LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectSlant); + LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectWeight); + LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePixel); + LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePoint); + LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateCode); + LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateOrder); + LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", sceFontStringGetWritingForm); + LIB_FUNCTION("hq5LffQjz-s", "libSceFont", 1, "libSceFont", sceFontStringRefersRenderCharacters); + LIB_FUNCTION("Avv7OApgCJk", "libSceFont", 1, "libSceFont", sceFontStringRefersTextCharacters); + LIB_FUNCTION("lOfduYnjgbo", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectSlant); + LIB_FUNCTION("HIUdjR-+Wl8", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectWeight); + LIB_FUNCTION("VSw18Aqzl0U", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetResolutionDpi); + LIB_FUNCTION("2QfqfeLblbg", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePixel); + LIB_FUNCTION("7x2xKiiB7MA", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePoint); + LIB_FUNCTION("la2AOWnHEAc", "libSceFont", 1, "libSceFont", sceFontStyleFrameInit); + LIB_FUNCTION("394sckksiCU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectSlant); + LIB_FUNCTION("faw77-pEBmU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectWeight); + LIB_FUNCTION("dB4-3Wdwls8", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetResolutionDpi); + LIB_FUNCTION("da4rQ4-+p-4", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePixel); + LIB_FUNCTION("O997laxY-Ys", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePoint); + LIB_FUNCTION("dUmABkAnVgk", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectSlant); + LIB_FUNCTION("hwsuXgmKdaw", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectWeight); + LIB_FUNCTION("bePC0L0vQWY", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetScale); + LIB_FUNCTION("mz2iTY0MK4A", "libSceFont", 1, "libSceFont", sceFontSupportExternalFonts); + LIB_FUNCTION("71w5DzObuZI", "libSceFont", 1, "libSceFont", sceFontSupportGlyphs); + LIB_FUNCTION("SsRbbCiWoGw", "libSceFont", 1, "libSceFont", sceFontSupportSystemFonts); + LIB_FUNCTION("IPoYwwlMx-g", "libSceFont", 1, "libSceFont", sceFontTextCodesStepBack); + LIB_FUNCTION("olSmXY+XP1E", "libSceFont", 1, "libSceFont", sceFontTextCodesStepNext); + LIB_FUNCTION("oaJ1BpN2FQk", "libSceFont", 1, "libSceFont", sceFontTextSourceInit); + LIB_FUNCTION("VRFd3diReec", "libSceFont", 1, "libSceFont", sceFontTextSourceRewind); + LIB_FUNCTION("eCRMCSk96NU", "libSceFont", 1, "libSceFont", sceFontTextSourceSetDefaultFont); + LIB_FUNCTION("OqQKX0h5COw", "libSceFont", 1, "libSceFont", sceFontTextSourceSetWritingForm); + LIB_FUNCTION("1QjhKxrsOB8", "libSceFont", 1, "libSceFont", sceFontUnbindRenderer); + LIB_FUNCTION("H-FNq8isKE0", "libSceFont", 1, "libSceFont", sceFontWordsFindWordCharacters); + LIB_FUNCTION("fljdejMcG1c", "libSceFont", 1, "libSceFont", sceFontWritingGetRenderMetrics); + LIB_FUNCTION("fD5rqhEXKYQ", "libSceFont", 1, "libSceFont", sceFontWritingInit); + LIB_FUNCTION("1+DgKL0haWQ", "libSceFont", 1, "libSceFont", sceFontWritingLineClear); + LIB_FUNCTION("JQKWIsS9joE", "libSceFont", 1, "libSceFont", sceFontWritingLineGetOrderingSpace); + LIB_FUNCTION("nlU2VnfpqTM", "libSceFont", 1, "libSceFont", sceFontWritingLineGetRenderMetrics); + LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", sceFontWritingLineRefersRenderStep); + LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", sceFontWritingLineWritesOrder); + LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", sceFontWritingRefersRenderStep); + LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", sceFontWritingRefersRenderStepCharacter); - LIB_FUNCTION("BbCZjJizU4A", "libSceFont", 1, "libSceFont", 1, 1, - sceFontWritingSetMaskInvisible); - LIB_FUNCTION("APTXePHIjLM", "libSceFont", 1, "libSceFont", 1, 1, Func_00F4D778F1C88CB3); - LIB_FUNCTION("A8ZQAl+7Dec", "libSceFont", 1, "libSceFont", 1, 1, Func_03C650025FBB0DE7); - LIB_FUNCTION("B+q4oWOyfho", "libSceFont", 1, "libSceFont", 1, 1, Func_07EAB8A163B27E1A); - LIB_FUNCTION("CUCOiOT5fOM", "libSceFont", 1, "libSceFont", 1, 1, Func_09408E88E4F97CE3); - LIB_FUNCTION("CfkpBe2CqBQ", "libSceFont", 1, "libSceFont", 1, 1, Func_09F92905ED82A814); - LIB_FUNCTION("DRQs7hqyGr4", "libSceFont", 1, "libSceFont", 1, 1, Func_0D142CEE1AB21ABE); - LIB_FUNCTION("FL0unhGcFvI", "libSceFont", 1, "libSceFont", 1, 1, Func_14BD2E9E119C16F2); - LIB_FUNCTION("GsU8nt6ujXU", "libSceFont", 1, "libSceFont", 1, 1, Func_1AC53C9EDEAE8D75); - LIB_FUNCTION("HUARhdXiTD0", "libSceFont", 1, "libSceFont", 1, 1, Func_1D401185D5E24C3D); - LIB_FUNCTION("HoPNIMLMmW8", "libSceFont", 1, "libSceFont", 1, 1, Func_1E83CD20C2CC996F); - LIB_FUNCTION("MUsfdluf54o", "libSceFont", 1, "libSceFont", 1, 1, Func_314B1F765B9FE78A); - LIB_FUNCTION("NQ5nJf7eKeE", "libSceFont", 1, "libSceFont", 1, 1, Func_350E6725FEDE29E1); - LIB_FUNCTION("Pbdz8KYEvzk", "libSceFont", 1, "libSceFont", 1, 1, Func_3DB773F0A604BF39); - LIB_FUNCTION("T-Sd0h4xGxw", "libSceFont", 1, "libSceFont", 1, 1, Func_4FF49DD21E311B1C); - LIB_FUNCTION("UmKHZkpJOYE", "libSceFont", 1, "libSceFont", 1, 1, Func_526287664A493981); - LIB_FUNCTION("VcpxjbyEpuk", "libSceFont", 1, "libSceFont", 1, 1, Func_55CA718DBC84A6E9); - LIB_FUNCTION("Vj-F8HBqi00", "libSceFont", 1, "libSceFont", 1, 1, Func_563FC5F0706A8B4D); - LIB_FUNCTION("Vp4uzTQpD0U", "libSceFont", 1, "libSceFont", 1, 1, Func_569E2ECD34290F45); - LIB_FUNCTION("WgR3W2vkdoU", "libSceFont", 1, "libSceFont", 1, 1, Func_5A04775B6BE47685); - LIB_FUNCTION("X9k7yrb3l1A", "libSceFont", 1, "libSceFont", 1, 1, Func_5FD93BCAB6F79750); - LIB_FUNCTION("YrU5j4ZL07Q", "libSceFont", 1, "libSceFont", 1, 1, Func_62B5398F864BD3B4); - LIB_FUNCTION("b5AQKU2CI2c", "libSceFont", 1, "libSceFont", 1, 1, Func_6F9010294D822367); - LIB_FUNCTION("d1fpR0I6emc", "libSceFont", 1, "libSceFont", 1, 1, Func_7757E947423A7A67); - LIB_FUNCTION("fga6Ugd-VPo", "libSceFont", 1, "libSceFont", 1, 1, Func_7E06BA52077F54FA); - LIB_FUNCTION("k7Nt6gITEdY", "libSceFont", 1, "libSceFont", 1, 1, Func_93B36DEA021311D6); - LIB_FUNCTION("lLCJHnERWYo", "libSceFont", 1, "libSceFont", 1, 1, Func_94B0891E7111598A); - LIB_FUNCTION("l4XJEowv580", "libSceFont", 1, "libSceFont", 1, 1, Func_9785C9128C2FE7CD); - LIB_FUNCTION("l9+8m2X7wOE", "libSceFont", 1, "libSceFont", 1, 1, Func_97DFBC9B65FBC0E1); - LIB_FUNCTION("rNlxdAXX08o", "libSceFont", 1, "libSceFont", 1, 1, Func_ACD9717405D7D3CA); - LIB_FUNCTION("sZqK7D-U8W8", "libSceFont", 1, "libSceFont", 1, 1, Func_B19A8AEC3FD4F16F); - LIB_FUNCTION("wQ9IitfPED0", "libSceFont", 1, "libSceFont", 1, 1, Func_C10F488AD7CF103D); - LIB_FUNCTION("0Mi1-0poJsc", "libSceFont", 1, "libSceFont", 1, 1, Func_D0C8B5FF4A6826C7); - LIB_FUNCTION("5I080Bw0KjM", "libSceFont", 1, "libSceFont", 1, 1, Func_E48D3CD01C342A33); - LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", 1, 1, Func_EAC96B2186B71E14); - LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", 1, 1, Func_FE4788A96EF46256); - LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", 1, 1, Func_FE7E5AE95D3058F5); + LIB_FUNCTION("BbCZjJizU4A", "libSceFont", 1, "libSceFont", sceFontWritingSetMaskInvisible); + LIB_FUNCTION("APTXePHIjLM", "libSceFont", 1, "libSceFont", Func_00F4D778F1C88CB3); + LIB_FUNCTION("A8ZQAl+7Dec", "libSceFont", 1, "libSceFont", Func_03C650025FBB0DE7); + LIB_FUNCTION("B+q4oWOyfho", "libSceFont", 1, "libSceFont", Func_07EAB8A163B27E1A); + LIB_FUNCTION("CUCOiOT5fOM", "libSceFont", 1, "libSceFont", Func_09408E88E4F97CE3); + LIB_FUNCTION("CfkpBe2CqBQ", "libSceFont", 1, "libSceFont", Func_09F92905ED82A814); + LIB_FUNCTION("DRQs7hqyGr4", "libSceFont", 1, "libSceFont", Func_0D142CEE1AB21ABE); + LIB_FUNCTION("FL0unhGcFvI", "libSceFont", 1, "libSceFont", Func_14BD2E9E119C16F2); + LIB_FUNCTION("GsU8nt6ujXU", "libSceFont", 1, "libSceFont", Func_1AC53C9EDEAE8D75); + LIB_FUNCTION("HUARhdXiTD0", "libSceFont", 1, "libSceFont", Func_1D401185D5E24C3D); + LIB_FUNCTION("HoPNIMLMmW8", "libSceFont", 1, "libSceFont", Func_1E83CD20C2CC996F); + LIB_FUNCTION("MUsfdluf54o", "libSceFont", 1, "libSceFont", Func_314B1F765B9FE78A); + LIB_FUNCTION("NQ5nJf7eKeE", "libSceFont", 1, "libSceFont", Func_350E6725FEDE29E1); + LIB_FUNCTION("Pbdz8KYEvzk", "libSceFont", 1, "libSceFont", Func_3DB773F0A604BF39); + LIB_FUNCTION("T-Sd0h4xGxw", "libSceFont", 1, "libSceFont", Func_4FF49DD21E311B1C); + LIB_FUNCTION("UmKHZkpJOYE", "libSceFont", 1, "libSceFont", Func_526287664A493981); + LIB_FUNCTION("VcpxjbyEpuk", "libSceFont", 1, "libSceFont", Func_55CA718DBC84A6E9); + LIB_FUNCTION("Vj-F8HBqi00", "libSceFont", 1, "libSceFont", Func_563FC5F0706A8B4D); + LIB_FUNCTION("Vp4uzTQpD0U", "libSceFont", 1, "libSceFont", Func_569E2ECD34290F45); + LIB_FUNCTION("WgR3W2vkdoU", "libSceFont", 1, "libSceFont", Func_5A04775B6BE47685); + LIB_FUNCTION("X9k7yrb3l1A", "libSceFont", 1, "libSceFont", Func_5FD93BCAB6F79750); + LIB_FUNCTION("YrU5j4ZL07Q", "libSceFont", 1, "libSceFont", Func_62B5398F864BD3B4); + LIB_FUNCTION("b5AQKU2CI2c", "libSceFont", 1, "libSceFont", Func_6F9010294D822367); + LIB_FUNCTION("d1fpR0I6emc", "libSceFont", 1, "libSceFont", Func_7757E947423A7A67); + LIB_FUNCTION("fga6Ugd-VPo", "libSceFont", 1, "libSceFont", Func_7E06BA52077F54FA); + LIB_FUNCTION("k7Nt6gITEdY", "libSceFont", 1, "libSceFont", Func_93B36DEA021311D6); + LIB_FUNCTION("lLCJHnERWYo", "libSceFont", 1, "libSceFont", Func_94B0891E7111598A); + LIB_FUNCTION("l4XJEowv580", "libSceFont", 1, "libSceFont", Func_9785C9128C2FE7CD); + LIB_FUNCTION("l9+8m2X7wOE", "libSceFont", 1, "libSceFont", Func_97DFBC9B65FBC0E1); + LIB_FUNCTION("rNlxdAXX08o", "libSceFont", 1, "libSceFont", Func_ACD9717405D7D3CA); + LIB_FUNCTION("sZqK7D-U8W8", "libSceFont", 1, "libSceFont", Func_B19A8AEC3FD4F16F); + LIB_FUNCTION("wQ9IitfPED0", "libSceFont", 1, "libSceFont", Func_C10F488AD7CF103D); + LIB_FUNCTION("0Mi1-0poJsc", "libSceFont", 1, "libSceFont", Func_D0C8B5FF4A6826C7); + LIB_FUNCTION("5I080Bw0KjM", "libSceFont", 1, "libSceFont", Func_E48D3CD01C342A33); + LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", Func_EAC96B2186B71E14); + LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", Func_FE4788A96EF46256); + LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", Func_FE7E5AE95D3058F5); }; } // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index ecc2b909f..4a1dbb989 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -114,32 +114,27 @@ s32 PS4_SYSV_ABI sceFontSelectRendererFt() { } void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtInitAliases); - LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSetAliasFont); - LIB_FUNCTION("MEWjebIzDEI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSetAliasPath); - LIB_FUNCTION("ZcQL0iSjvFw", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportBdf); - LIB_FUNCTION("LADHEyFTxRQ", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportCid); - LIB_FUNCTION("+jqQjsancTs", "libSceFontFt", 1, "libSceFontFt", 1, 1, - sceFontFtSupportFontFormats); - LIB_FUNCTION("oakL15-mBtc", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportOpenType); - LIB_FUNCTION("dcQeaDr8UJc", "libSceFontFt", 1, "libSceFontFt", 1, 1, - sceFontFtSupportOpenTypeOtf); - LIB_FUNCTION("2KXS-HkZT3c", "libSceFontFt", 1, "libSceFontFt", 1, 1, - sceFontFtSupportOpenTypeTtf); - LIB_FUNCTION("H0mJnhKwV-s", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportPcf); - LIB_FUNCTION("S2mw3sYplAI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportPfr); - LIB_FUNCTION("+ehNXJPUyhk", "libSceFontFt", 1, "libSceFontFt", 1, 1, - sceFontFtSupportSystemFonts); - LIB_FUNCTION("4BAhDLdrzUI", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportTrueType); - LIB_FUNCTION("Utlzbdf+g9o", "libSceFontFt", 1, "libSceFontFt", 1, 1, - sceFontFtSupportTrueTypeGx); - LIB_FUNCTION("nAfQ6qaL1fU", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportType1); - LIB_FUNCTION("X9+pzrGtBus", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportType42); - LIB_FUNCTION("w0hI3xsK-hc", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtSupportWinFonts); - LIB_FUNCTION("w5sfH9r8ZJ4", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontFtTermAliases); - LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectGlyphsFt); - LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectLibraryFt); - LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", 1, 1, sceFontSelectRendererFt); + LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", sceFontFtInitAliases); + LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasFont); + LIB_FUNCTION("MEWjebIzDEI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasPath); + LIB_FUNCTION("ZcQL0iSjvFw", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportBdf); + LIB_FUNCTION("LADHEyFTxRQ", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportCid); + LIB_FUNCTION("+jqQjsancTs", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportFontFormats); + LIB_FUNCTION("oakL15-mBtc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenType); + LIB_FUNCTION("dcQeaDr8UJc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeOtf); + LIB_FUNCTION("2KXS-HkZT3c", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeTtf); + LIB_FUNCTION("H0mJnhKwV-s", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPcf); + LIB_FUNCTION("S2mw3sYplAI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPfr); + LIB_FUNCTION("+ehNXJPUyhk", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportSystemFonts); + LIB_FUNCTION("4BAhDLdrzUI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueType); + LIB_FUNCTION("Utlzbdf+g9o", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueTypeGx); + LIB_FUNCTION("nAfQ6qaL1fU", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType1); + LIB_FUNCTION("X9+pzrGtBus", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType42); + LIB_FUNCTION("w0hI3xsK-hc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportWinFonts); + LIB_FUNCTION("w5sfH9r8ZJ4", "libSceFontFt", 1, "libSceFontFt", sceFontFtTermAliases); + LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", sceFontSelectGlyphsFt); + LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", sceFontSelectLibraryFt); + LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", sceFontSelectRendererFt); }; } // namespace Libraries::FontFt \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 8f86b5044..0e7485b89 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -477,7 +477,7 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceCesCs.sprx", nullptr}, {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, - {"libSceFreeTypeOt.sprx", nullptr}}}; + {"libSceFreeTypeOt.sprx", nullptr}}); std::vector found_modules; const auto& sys_module_path = Config::getSysModulesPath(); From 078a8ff86581dd909c72924860393e8370044df7 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Tue, 4 Nov 2025 16:54:34 +0200 Subject: [PATCH 15/51] Enhance font rendering and management system - Integrated `stb_truetype` for advanced font rendering. - Added support for external and system fonts with logging. - Introduced new structures for glyphs, metrics, and kerning. - Implemented functions for font state management and scaling. - Improved glyph rendering, including subpixel and caching. - Enhanced render surface initialization and scissor handling. - Refactored stubbed functions with proper implementations. - Added ABI compatibility checks for key structures. - Improved logging, error handling, and code organization. - Updated documentation and comments for better clarity. --- src/core/libraries/font/font.cpp | 1068 +++++++++++++++++++++++++----- src/core/libraries/font/font.h | 209 +++++- 2 files changed, 1071 insertions(+), 206 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 1454004aa..41f19e62e 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -1,21 +1,177 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/font/font.h" #include "core/libraries/libs.h" #include "font_error.h" +#define STBTT_STATIC +#define STB_TRUETYPE_IMPLEMENTATION +#include "externals/dear_imgui/imstb_truetype.h" + +namespace { +struct GlyphEntry { + std::vector bitmap; + int w = 0; + int h = 0; + int x0 = 0; + int y0 = 0; + int x1 = 0; + int y1 = 0; + float advance = 0.0f; + float bearingX = 0.0f; +}; + +struct FontState { + float scale_w = 16.0f; + float scale_h = 16.0f; + Libraries::Font::OrbisFontLib library = nullptr; + bool face_ready = false; + std::vector face_data; + stbtt_fontinfo face{}; + float scale_for_height = 0.0f; + int ascent = 0, descent = 0, lineGap = 0; + bool ext_face_ready = false; + std::vector ext_face_data; + stbtt_fontinfo ext_face{}; + float ext_scale_for_height = 0.0f; + int ext_ascent = 0, ext_descent = 0, ext_lineGap = 0; + std::unordered_map ext_cache; + std::unordered_map sys_cache; + std::unordered_map ext_kern_cache; + std::unordered_map sys_kern_cache; + std::vector scratch; + bool logged_ext_use = false; + bool logged_sys_use = false; +}; + +static std::unordered_map g_font_state; + +struct LibraryState { + bool support_system = false; + bool support_external = false; + u32 external_formats = 0; + u32 external_fontMax = 0; +}; +static std::unordered_map g_library_state; + +static std::unordered_map + g_style_for_surface; + +static FontState& GetState(Libraries::Font::OrbisFontHandle h) { + return g_font_state[h]; +} + +static LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { + return g_library_state[lib]; +} + +static void LogExternalFormatSupport(u32 formats_mask) { + LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); +} + +static bool ReadFileBinary(const std::string& path, std::vector& out) { + std::ifstream f(path, std::ios::binary); + if (!f) + return false; + f.seekg(0, std::ios::end); + std::streampos sz = f.tellg(); + if (sz <= 0) + return false; + out.resize(static_cast(sz)); + f.seekg(0, std::ios::beg); + f.read(reinterpret_cast(out.data()), sz); + return f.good(); +} + +static bool EnsureSystemFace(FontState& st) { + if (st.face_ready) + return true; + std::vector candidates; + if (const char* env_dir = std::getenv("SHADPS4_FONTS_DIR"); env_dir && *env_dir) { + candidates.emplace_back(std::string(env_dir) + "/NotoSansJP-Regular.ttf"); + candidates.emplace_back(std::string(env_dir) + "/ProggyVector-Regular.ttf"); + } + static const char* rel_roots[] = {".", "..", "../..", "../../..", "../../../.."}; + for (auto* root : rel_roots) { + candidates.emplace_back(std::string(root) + + "/src/imgui/renderer/fonts/NotoSansJP-Regular.ttf"); + candidates.emplace_back(std::string(root) + + "/src/imgui/renderer/fonts/ProggyVector-Regular.ttf"); + } + for (const auto& path : candidates) { + st.face_data.clear(); + if (ReadFileBinary(path, st.face_data)) { + if (stbtt_InitFont(&st.face, st.face_data.data(), 0)) { + st.face_ready = true; + stbtt_GetFontVMetrics(&st.face, &st.ascent, &st.descent, &st.lineGap); + if (st.scale_for_height == 0.0f) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + LOG_INFO(Lib_Font, + "SystemFace: loaded '{}' (ascent={}, descent={}, lineGap={}, scale={})", + path, st.ascent, st.descent, st.lineGap, st.scale_for_height); + return true; + } else { + LOG_WARNING(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", path); + } + } else { + LOG_DEBUG(Lib_Font, "SystemFace: could not open '{}'", path); + } + } + LOG_WARNING(Lib_Font, "SystemFace: no font file found; using placeholder rectangles"); + return false; +} + +static std::unordered_set g_logged_pua; + +static inline std::uint64_t MakeGlyphKey(u32 code, int pixel_h) { + return (static_cast(code) << 32) | static_cast(pixel_h); +} +static std::unordered_set g_stride_logged; +static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* surf) { + if (!surf) + return; + const void* key = static_cast(surf); + if (g_stride_logged.insert(key).second) { + const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); + const long expected = static_cast(surf->width) * bpp; + const bool match = (expected == surf->widthByte); + LOG_INFO(Lib_Font, + "StrideCheck: surf={} buf={} width={} height={} pixelSizeByte={} widthByte={} " + "expected={} match={}", + key, surf->buffer, surf->width, surf->height, bpp, surf->widthByte, expected, + match); + } +} +} // namespace + namespace Libraries::Font { -s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +struct FontLibOpaque {}; +struct OrbisFontRenderer_ {}; + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { + LOG_ERROR(Lib_Font, "(STUBBED) called library={} buffer={} size={}", + static_cast(library), static_cast(buffer), size); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontBindRenderer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { + LOG_DEBUG(Lib_Font, "sceFontBindRenderer fontHandle={} renderer={}", + static_cast(fontHandle), static_cast(renderer)); return ORBIS_OK; } @@ -53,7 +209,6 @@ s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharac return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - // Retrieve text order *pTextOrder = textCharacter->textOrder; return ORBIS_OK; } @@ -63,7 +218,6 @@ u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* t return 0; } - // Check if the format flag (bit 2) is set return (textCharacter->formatFlags & 0x04) ? textCharacter->characterCode : 0; } @@ -78,33 +232,33 @@ u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCha OrbisFontTextCharacter* PS4_SYSV_ABI sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter) { if (!textCharacter) - return NULL; // Check if input is NULL + return NULL; - OrbisFontTextCharacter* current = textCharacter->prev; // Move backward instead of forward + OrbisFontTextCharacter* current = textCharacter->prev; while (current) { if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { - return current; // Return the first matching node + return current; } - current = current->prev; // Move to the previous node + current = current->prev; } - return NULL; // No valid node found + return NULL; } OrbisFontTextCharacter* PS4_SYSV_ABI sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter) { if (!textCharacter) - return NULL; // Null check + return NULL; OrbisFontTextCharacter* current = textCharacter->next; while (current) { if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { - return current; // Found a match + return current; } - current = current->next; // Move to the next node + current = current->next; } - return NULL; // No matching node found + return NULL; } s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { @@ -147,8 +301,16 @@ s32 PS4_SYSV_ABI sceFontCreateLibrary() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + u64 edition, OrbisFontLib* pLibrary) { + if (!pLibrary) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + (void)memory; + (void)create_params; + (void)edition; + *pLibrary = new FontLibOpaque{}; return ORBIS_OK; } @@ -157,8 +319,16 @@ s32 PS4_SYSV_ABI sceFontCreateRenderer() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + u64 edition, OrbisFontRenderer* pRenderer) { + if (!pRenderer) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + (void)memory; + (void)create_params; + (void)edition; + *pRenderer = new OrbisFontRenderer_{}; return ORBIS_OK; } @@ -242,8 +412,102 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, + OrbisFontGlyphMetrics* metrics) { + if (!metrics) { + LOG_DEBUG(Lib_Font, "sceFontGetCharGlyphMetrics: invalid params"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + const stbtt_fontinfo* face = nullptr; + float scale = 0.0f; + bool use_ext = false; + + int ext_glyph = 0; + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (ext_glyph > 0) { + face = &st.ext_face; + scale = st.ext_scale_for_height; + use_ext = true; + if (!st.logged_ext_use) { + LOG_INFO(Lib_Font, "RenderFace: handle={} source=external (game font)", + static_cast(fontHandle)); + st.logged_ext_use = true; + } + } + } + if (!face && EnsureSystemFace(st)) { + if (st.scale_for_height == 0.0f) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); + if (sys_glyph > 0) { + face = &st.face; + scale = st.scale_for_height; + use_ext = false; + if (!st.logged_sys_use) { + LOG_INFO(Lib_Font, "RenderFace: handle={} source=system (fallback)", + static_cast(fontHandle)); + st.logged_sys_use = true; + } + } + } + if (face) { + const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); + const std::uint64_t key = MakeGlyphKey(code, pixel_h); + GlyphEntry* ge = nullptr; + if (use_ext) { + auto it = st.ext_cache.find(key); + if (it != st.ext_cache.end()) + ge = &it->second; + } else { + auto it = st.sys_cache.find(key); + if (it != st.sys_cache.end()) + ge = &it->second; + } + if (!ge) { + GlyphEntry entry{}; + int aw = 0, lsb = 0; + stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); + stbtt_GetCodepointBitmapBox(face, static_cast(code), scale, scale, &entry.x0, + &entry.y0, &entry.x1, &entry.y1); + entry.w = std::max(0, entry.x1 - entry.x0); + entry.h = std::max(0, entry.y1 - entry.y0); + entry.advance = static_cast(aw) * scale; + entry.bearingX = static_cast(lsb) * scale; + if (use_ext) { + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } else { + ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; + } + } + metrics->w = ge->w > 0 ? (float)ge->w : st.scale_w; + metrics->h = ge->h > 0 ? (float)ge->h : st.scale_h; + metrics->h_bearing_x = ge->bearingX; + metrics->h_bearing_y = static_cast(-ge->y0); + metrics->h_adv = ge->advance > 0.0f ? ge->advance : st.scale_w; + metrics->v_bearing_x = 0.0f; + metrics->v_bearing_y = 0.0f; + metrics->v_adv = 0.0f; + LOG_TRACE(Lib_Font, + "GetCharGlyphMetrics: code=U+{:04X} src={} size=({}, {}) adv={} bearing=({}, {}) " + "box={}x{}", + code, use_ext ? "external" : "system", st.scale_w, st.scale_h, metrics->h_adv, + metrics->h_bearing_x, metrics->h_bearing_y, metrics->w, metrics->h); + return ORBIS_OK; + } + metrics->w = st.scale_w; + metrics->h = st.scale_h; + metrics->h_bearing_x = 0.0f; + metrics->h_bearing_y = st.scale_h; + metrics->h_adv = st.scale_w; + metrics->v_bearing_x = 0.0f; + metrics->v_bearing_y = 0.0f; + metrics->v_adv = 0.0f; + LOG_TRACE(Lib_Font, "GetCharGlyphMetrics(fallback): code=U+{:04X} size=({}, {}) box={}x{}", + code, st.scale_w, st.scale_h, metrics->w, metrics->h); return ORBIS_OK; } @@ -288,17 +552,64 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { } s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); + LOG_DEBUG(Lib_Font, "GetHorizontalLayout: default layout (no effects)"); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetKerning() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, + OrbisFontKerning* kerning) { + if (!kerning) { + LOG_DEBUG(Lib_Font, "sceFontGetKerning: invalid params"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + const stbtt_fontinfo* face = nullptr; + float scale = 0.0f; + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + int g1 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(preCode)); + int g2 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (g1 > 0 && g2 > 0) { + face = &st.ext_face; + scale = st.ext_scale_for_height; + } + } + if (!face && EnsureSystemFace(st)) { + if (st.scale_for_height == 0.0f) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + int g1 = stbtt_FindGlyphIndex(&st.face, static_cast(preCode)); + int g2 = stbtt_FindGlyphIndex(&st.face, static_cast(code)); + if (g1 > 0 && g2 > 0) { + face = &st.face; + scale = st.scale_for_height; + } + } + if (face) { + const int kern = + stbtt_GetCodepointKernAdvance(face, static_cast(preCode), static_cast(code)); + const float kx = static_cast(kern) * scale; + kerning->dx = kx; + kerning->dy = 0.0f; + kerning->px = 0.0f; + kerning->py = 0.0f; + LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); + return ORBIS_OK; + } + kerning->dx = 0.0f; + kerning->dy = 0.0f; + kerning->px = 0.0f; + kerning->py = 0.0f; + LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx=0 (no face)", preCode, code); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetLibrary() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary) { + if (!pLibrary) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + const auto& st = GetState(fontHandle); + *pLibrary = st.library; return ORBIS_OK; } @@ -342,8 +653,16 @@ s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h) { + if (!w || !h) { + LOG_DEBUG(Lib_Font, "sceFontGetScalePixel: invalid params"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + const auto& st = GetState(fontHandle); + *w = st.scale_w; + *h = st.scale_h; + LOG_DEBUG(Lib_Font, "GetScalePixel: handle={} -> w={}, h={}", + static_cast(fontHandle), *w, *h); return ORBIS_OK; } @@ -682,8 +1001,16 @@ s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontMemoryInit() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* fontMemory, void* address, u32 sizeByte, + const OrbisFontMemInterface* memInterface, void* mspaceObject, + OrbisFontMemDestroyCb destroyCallback, void* destroyObject) { + LOG_ERROR(Lib_Font, + "(STUBBED) called font_mem={} region_base={} sizeByte={} mem_if={} mspace_handle={}" + " destroy_cb={} destroy_ctx={}", + static_cast(fontMemory), static_cast(address), sizeByte, + static_cast(memInterface), static_cast(mspaceObject), + reinterpret_cast(destroyCallback), + static_cast(destroyObject)); return ORBIS_OK; } @@ -702,28 +1029,461 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontOpenFontMemory() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle) { + if (!library || !fontAddress || fontSize == 0 || !pFontHandle) { + LOG_DEBUG(Lib_Font, "sceFontOpenFontMemory: invalid params"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + (void)open_params; + auto* f = new OrbisFontHandleOpaque{}; + *pFontHandle = f; + auto& st = GetState(f); + st.library = library; + const unsigned char* p = reinterpret_cast(fontAddress); + auto& ls = GetLibState(library); + LOG_INFO(Lib_Font, + "OpenFontMemory: lib={} size={} open_params={} handle={} sig='{}{}{}{}' " + "ext_supported={} formats=0x{:X}", + static_cast(library), fontSize, static_cast(open_params), + static_cast(*pFontHandle), (fontSize >= 1 ? (char)p[0] : '?'), + (fontSize >= 2 ? (char)p[1] : '?'), (fontSize >= 3 ? (char)p[2] : '?'), + (fontSize >= 4 ? (char)p[3] : '?'), ls.support_external, ls.external_formats); + st.ext_face_data.assign(reinterpret_cast(fontAddress), + reinterpret_cast(fontAddress) + fontSize); + int font_count = stbtt_GetNumberOfFonts(st.ext_face_data.data()); + int chosen_index = 0; + if (font_count > 1) { + chosen_index = 0; + if (open_params) { + chosen_index = + static_cast(open_params->subfont_index % static_cast(font_count)); + } + } + int offset = stbtt_GetFontOffsetForIndex(st.ext_face_data.data(), chosen_index); + const unsigned char* d = st.ext_face_data.data(); + const u32 sig32 = (fontSize >= 4) + ? (static_cast(d[0]) << 24) | (static_cast(d[1]) << 16) | + (static_cast(d[2]) << 8) | static_cast(d[3]) + : 0u; + const bool is_ttc = (font_count > 1); + const bool is_otf_cff = (sig32 == 0x4F54544Fu); + const bool is_ttf_sfnt = (sig32 == 0x00010000u) || (sig32 == 0x74727565u); + const bool is_sfnt_typ1 = (sig32 == 0x74797031u); + if (is_otf_cff) { + LOG_WARNING(Lib_Font, + "ExternalFace: OTF/CFF detected (OTTO). CFF outlines are not supported;" + " handle={} fonts={} requested_index={} -> fallback may occur", + static_cast(*pFontHandle), font_count, chosen_index); + } + if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset)) { + st.ext_face_ready = true; + stbtt_GetFontVMetrics(&st.ext_face, &st.ext_ascent, &st.ext_descent, &st.ext_lineGap); + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + LOG_INFO(Lib_Font, + "ExternalFace: handle={} ascent={} descent={} lineGap={} scale={} (data={} bytes)" + " fonts={} chosen_index={} ttc={} sig=0x{:08X} kind={}", + static_cast(*pFontHandle), st.ext_ascent, st.ext_descent, + st.ext_lineGap, st.ext_scale_for_height, (int)st.ext_face_data.size(), font_count, + chosen_index, is_ttc, sig32, + is_otf_cff ? "OTF/CFF (unsupported)" + : (is_ttf_sfnt ? "TTF (ready)" + : (is_sfnt_typ1 ? "Type1(sfnt) (stub)" : "unknown"))); + if (is_ttf_sfnt) { + LOG_INFO(Lib_Font, "ExternalFormat: OpenType-TT (glyf) -> ready"); + } else if (is_otf_cff) { + LOG_WARNING(Lib_Font, "ExternalFormat: OpenType-CFF -> stub (CFF unsupported)"); + } else if (is_sfnt_typ1) { + LOG_WARNING(Lib_Font, "ExternalFormat: Type 1 (sfnt wrapper) -> stub"); + } + } else { + LOG_WARNING(Lib_Font, + "ExternalFace: stbtt_InitFont failed for handle={} size={} fonts={}" + " chosen_index={} sig='{}{}{}{}' (OTF/CFF unsupported={})", + static_cast(*pFontHandle), fontSize, font_count, chosen_index, + (fontSize >= 1 ? (char)p[0] : '?'), (fontSize >= 2 ? (char)p[1] : '?'), + (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), + is_otf_cff); + if (is_otf_cff) { + LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; fallback to system font"); + } + } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontOpenFontSet() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle) { + (void)fontSetType; + (void)openMode; + (void)open_params; + if (!pFontHandle) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* f = new OrbisFontHandleOpaque{}; + *pFontHandle = f; + auto& st = GetState(f); + st.library = library; + EnsureSystemFace(st); + if (st.scale_for_height == 0.0f) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + LOG_INFO( + Lib_Font, + "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system face={})", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle), + EnsureSystemFace(GetState(f))); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRebindRenderer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle) { + LOG_ERROR(Lib_Font, "(STUBBED) called fontHandle={}", static_cast(fontHandle)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, float y, + OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result) { + return sceFontRenderCharGlyphImageHorizontal(fontHandle, code, surf, x, y, metrics, result); } -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, + float y, OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result) { + LOG_INFO(Lib_Font, + "RenderGlyph(H): handle={} code=U+{:04X} x={} y={} metrics={} result={} surf={}" + " buf={} widthByte={} pixelSizeByte={} size={}x{} sc=[{},{}-{}:{}] styleFlag={}", + static_cast(fontHandle), code, x, y, static_cast(metrics), + static_cast(result), static_cast(surf), + surf ? static_cast(surf->buffer) : nullptr, surf ? surf->widthByte : -1, + surf ? (int)surf->pixelSizeByte : -1, surf ? surf->width : -1, + surf ? surf->height : -1, surf ? surf->sc_x0 : 0u, surf ? surf->sc_y0 : 0u, + surf ? surf->sc_x1 : 0u, surf ? surf->sc_y1 : 0u, surf ? (int)surf->styleFlag : -1); + if (!surf || !surf->buffer) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + LogStrideOnce(surf); + LOG_DEBUG(Lib_Font, + "RenderGlyph(H): handle={} code=U+{:04X} x={} y={} surf={} size={}x{} bpp={} " + "sc=[{},{}-{}:{}]", + static_cast(fontHandle), code, x, y, static_cast(surf), + surf->width, surf->height, (int)surf->pixelSizeByte, surf->sc_x0, surf->sc_y0, + surf->sc_x1, surf->sc_y1); + if (result) { + result->stage = nullptr; + result->slot.maybe_addr = static_cast(surf->buffer); + result->slot.maybe_rowBytes = static_cast(std::max(0, surf->widthByte)); + result->slot.maybe_pixelSize = static_cast(std::max(1, (int)surf->pixelSizeByte)); + result->slot.maybe_pixelFmt = (result->slot.maybe_pixelSize == 4) ? 1 : 0; + } + auto& st = GetState(fontHandle); + float fw = st.scale_w; + float fh = st.scale_h; + int g_x0 = 0, g_y0 = 0, g_x1 = 0, g_y1 = 0; + const stbtt_fontinfo* face = nullptr; + float scale = 0.0f; + bool use_ext = false; + + int ext_glyph = 0; + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (ext_glyph > 0) { + face = &st.ext_face; + scale = st.ext_scale_for_height; + use_ext = true; + } + } + if (!face && EnsureSystemFace(st)) { + if (st.scale_for_height == 0.0f) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); + if (sys_glyph > 0) { + face = &st.face; + scale = st.scale_for_height; + use_ext = false; + } + } + if (face) { + LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src={}", + static_cast(fontHandle), code, use_ext ? "external" : "system"); + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); + const std::uint64_t key = MakeGlyphKey(code, pixel_h); + GlyphEntry* ge = nullptr; + if (!use_subpixel) { + if (use_ext) { + auto it = st.ext_cache.find(key); + if (it != st.ext_cache.end()) + ge = &it->second; + } else { + auto it = st.sys_cache.find(key); + if (it != st.sys_cache.end()) + ge = &it->second; + } + } + if (!ge) { + GlyphEntry entry{}; + int aw = 0, lsb = 0; + stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); + if (use_subpixel) { + stbtt_GetCodepointBitmapBoxSubpixel(face, static_cast(code), scale, scale, + frac_x, frac_y, &entry.x0, &entry.y0, &entry.x1, + &entry.y1); + } else { + stbtt_GetCodepointBitmapBox(face, static_cast(code), scale, scale, &entry.x0, + &entry.y0, &entry.x1, &entry.y1); + } + entry.w = std::max(0, entry.x1 - entry.x0); + entry.h = std::max(0, entry.y1 - entry.y0); + entry.advance = static_cast(aw) * scale; + entry.bearingX = static_cast(lsb) * scale; + if (!use_subpixel && entry.w > 0 && entry.h > 0) { + entry.bitmap.resize(static_cast(entry.w * entry.h)); + stbtt_MakeCodepointBitmap(face, entry.bitmap.data(), entry.w, entry.h, entry.w, + scale, scale, static_cast(code)); + } + if (!use_subpixel) { + if (use_ext) { + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } else { + ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; + } + } else { + fw = (float)entry.w; + fh = (float)entry.h; + g_x0 = entry.x0; + g_y0 = entry.y0; + g_x1 = entry.x1; + g_y1 = entry.y1; + } + } + if (ge) { + fw = (float)ge->w; + fh = (float)ge->h; + g_x0 = ge->x0; + g_y0 = ge->y0; + g_x1 = ge->x1; + g_y1 = ge->y1; + } + } + if (metrics) { + if (face) { + int aw = 0, lsb = 0; + stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); + metrics->w = fw; + metrics->h = fh; + metrics->h_bearing_x = static_cast(lsb) * scale; + metrics->h_bearing_y = static_cast(-g_y0); + metrics->h_adv = static_cast(aw) * scale; + } else { + metrics->w = fw; + metrics->h = fh; + metrics->h_bearing_x = 0.0f; + metrics->h_bearing_y = fh; + metrics->h_adv = fw; + } + metrics->v_bearing_x = 0.0f; + metrics->v_bearing_y = 0.0f; + metrics->v_adv = 0.0f; + } + + if (code == 0x20) { + if (result) { + result->new_x = 0; + result->new_y = 0; + result->new_w = 0; + result->new_h = 0; + result->ImageMetrics = {}; + } + return ORBIS_OK; + } + + int ix = static_cast(std::floor(x)); + int iy = static_cast(std::floor(y)); + int left = ix; + int top = iy; + if (face) { + left = ix + g_x0; + top = iy + g_y0; + } + int iw = std::max(0, static_cast(std::round(fw))); + int ih = std::max(0, static_cast(std::round(fh))); + + int sw = surf->width; + int sh = surf->height; + if (sw <= 0 || sh <= 0 || iw <= 0 || ih <= 0) { + return ORBIS_OK; + } + + int sx0 = 0, sy0 = 0, sx1 = sw, sy1 = sh; + if (surf->sc_x1 > 0 || surf->sc_y1 > 0 || surf->sc_x0 > 0 || surf->sc_y0 > 0) { + sx0 = static_cast(surf->sc_x0); + sy0 = static_cast(surf->sc_y0); + sx1 = static_cast(surf->sc_x1); + sy1 = static_cast(surf->sc_y1); + sx0 = std::clamp(sx0, 0, sw); + sy0 = std::clamp(sy0, 0, sh); + sx1 = std::clamp(sx1, 0, sw); + sy1 = std::clamp(sy1, 0, sh); + } + + int rx0 = left; + int ry0 = top; + int rx1 = left + iw; + int ry1 = top + ih; + + rx0 = std::clamp(rx0, sx0, sx1); + ry0 = std::clamp(ry0, sy0, sy1); + rx1 = std::clamp(rx1, sx0, sx1); + ry1 = std::clamp(ry1, sy0, sy1); + + int cw = std::max(0, rx1 - rx0); + int ch = std::max(0, ry1 - ry0); + + if (cw > 0 && ch > 0) { + const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); + bool is_pua = (code >= 0xE000 && code <= 0xF8FF) || (code >= 0xF0000 && code <= 0xFFFFD) || + (code >= 0x100000 && code <= 0x10FFFD); + bool is_placeholder_ascii = (code == 'h' || code == 'p' || code == 'x' || code == 'o'); + if ((is_pua || is_placeholder_ascii) && g_logged_pua.insert(code).second) { + LOG_DEBUG( + Lib_Font, + "GlyphTrace: code=U+{:04X} pua={} placeholder_ascii={} dst=[{},{} {}x{}] bpp={}", + code, is_pua, is_placeholder_ascii, rx0, ry0, cw, ch, bpp); + } + if (face && iw > 0 && ih > 0) { + const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); + const std::uint64_t key = MakeGlyphKey(code, pixel_h); + const GlyphEntry* ge = nullptr; + if (use_ext) { + auto it = st.ext_cache.find(key); + if (it != st.ext_cache.end()) + ge = &it->second; + } else { + auto it = st.sys_cache.find(key); + if (it != st.sys_cache.end()) + ge = &it->second; + } + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + const std::uint8_t* src_bitmap = nullptr; + if (!use_subpixel && ge && ge->w == iw && ge->h == ih && !ge->bitmap.empty()) { + src_bitmap = ge->bitmap.data(); + } else { + st.scratch.assign(static_cast(iw * ih), 0); + if (use_subpixel) { + stbtt_MakeCodepointBitmapSubpixel(face, st.scratch.data(), iw, ih, iw, scale, + scale, frac_x, frac_y, + static_cast(code)); + } else { + stbtt_MakeCodepointBitmap(face, st.scratch.data(), iw, ih, iw, scale, scale, + static_cast(code)); + } + src_bitmap = st.scratch.data(); + } + + const std::uint8_t* eff_src = src_bitmap; + + const int src_x0 = std::max(0, sx0 - rx0); + const int src_y0 = std::max(0, sy0 - ry0); + const int dst_x0 = std::max(rx0, sx0); + const int dst_y0 = std::max(ry0, sy0); + const int copy_w = std::min(rx1, sx1) - dst_x0; + const int copy_h = std::min(ry1, sy1) - dst_y0; + if (copy_w > 0 && copy_h > 0) { + LOG_TRACE(Lib_Font, + "RenderGlyph(H): code=U+{:04X} baseline=({}, {}) box=[{},{}-{}:{}] " + "dst=[{},{} {}x{}] bpp={}", + code, ix, iy, g_x0, g_y0, g_x1, g_y1, dst_x0, dst_y0, copy_w, copy_h, + bpp); + for (int row = 0; row < copy_h; ++row) { + const int src_y = src_y0 + row; + const std::uint8_t* src_row = eff_src + src_y * iw + src_x0; + int row_shift = 0; + int row_dst_x = dst_x0 + row_shift; + int row_copy_w = copy_w; + if (row_dst_x < sx0) { + int delta = sx0 - row_dst_x; + row_dst_x = sx0; + if (delta < row_copy_w) { + src_row += delta; + row_copy_w -= delta; + } else { + row_copy_w = 0; + } + } + if (row_dst_x + row_copy_w > sx1) { + row_copy_w = std::max(0, sx1 - row_dst_x); + } + std::uint8_t* dst_row = static_cast(surf->buffer) + + (dst_y0 + row) * surf->widthByte + row_dst_x * bpp; + if (bpp == 1) { + for (int col = 0; col < row_copy_w; ++col) { + std::uint8_t cov = src_row[col]; + std::uint8_t& d = dst_row[col]; + d = std::max(d, cov); + } + } else if (bpp == 4) { + std::uint32_t* px = reinterpret_cast(dst_row); + for (int col = 0; col < row_copy_w; ++col) { + std::uint8_t a = src_row[col]; + px[col] = (static_cast(a) << 24) | 0x00FFFFFFu; + } + } else { + for (int col = 0; col < row_copy_w; ++col) { + std::uint8_t a = src_row[col]; + std::uint8_t* p = dst_row + col * bpp; + std::memset(p, 0xFF, static_cast(bpp)); + p[0] = a; + } + } + } + } + } else { + if (result) { + result->new_x = 0; + result->new_y = 0; + result->new_w = 0; + result->new_h = 0; + result->ImageMetrics.width = 0; + result->ImageMetrics.height = 0; + } + LOG_DEBUG(Lib_Font, "RenderGlyph(H): skip draw (no face/zero size) code=U+{:04X}", + code); + return ORBIS_OK; + } + } + + if (result) { + result->new_x = static_cast(rx0); + result->new_y = static_cast(ry0); + result->new_w = static_cast(cw); + result->new_h = static_cast(ch); + const float stride_bytes = static_cast(std::max(0, surf->widthByte)); + result->ImageMetrics.bearing_x = static_cast(g_x0); + result->ImageMetrics.bearing_y = static_cast(-g_y0); + float adv_px = 0.0f; + if (face) { + int aw_tmp = 0, lsb_tmp = 0; + stbtt_GetCodepointHMetrics(face, static_cast(code), &aw_tmp, &lsb_tmp); + adv_px = static_cast(aw_tmp) * scale; + } + result->ImageMetrics.dv = adv_px; + result->ImageMetrics.stride = stride_bytes; + result->ImageMetrics.width = static_cast(cw); + result->ImageMetrics.height = static_cast(ch); + LOG_DEBUG(Lib_Font, "RenderGlyph(H): UpdateRect=[{},{} {}x{}] stride={} adv={}", + result->new_x, result->new_y, result->new_w, result->new_h, + result->ImageMetrics.stride, result->ImageMetrics.dv); + } return ORBIS_OK; } @@ -750,102 +1510,90 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel) { - if (renderSurface) { // Ensure surface is not NULL before modifying it + if (renderSurface) { renderSurface->buffer = buffer; renderSurface->widthByte = bufWidthByte; - renderSurface->pixelSizeByte = pixelSizeByte; - - // Initialize unknown fields (likely reserved or flags) - renderSurface->unkn_0xd = 0; + renderSurface->pixelSizeByte = static_cast(pixelSizeByte); + renderSurface->pad0 = 0; renderSurface->styleFlag = 0; - renderSurface->unkn_0xf = 0; - - // Ensure width and height are non-negative + renderSurface->pad2 = 0; renderSurface->width = (widthPixel < 0) ? 0 : widthPixel; renderSurface->height = (heightPixel < 0) ? 0 : heightPixel; - - // Set the clipping/scaling rectangle renderSurface->sc_x0 = 0; renderSurface->sc_y0 = 0; - renderSurface->sc_x1 = renderSurface->width; - renderSurface->sc_y1 = renderSurface->height; + renderSurface->sc_x1 = static_cast(renderSurface->width); + renderSurface->sc_y1 = static_cast(renderSurface->height); + std::fill(std::begin(renderSurface->reserved_q), std::end(renderSurface->reserved_q), 0); + LOG_INFO(Lib_Font, + "RenderSurfaceInit: buf={} widthByte={} pixelSizeByte={} size={}x{} " + "scissor=[0,0-{}:{}]", + static_cast(buffer), bufWidthByte, pixelSizeByte, + renderSurface->width, renderSurface->height, renderSurface->sc_x1, + renderSurface->sc_y1); } } void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, int y0, int w, int h) { if (!renderSurface) - return; // Null check + return; - // Handle horizontal clipping int surfaceWidth = renderSurface->width; int clip_x0, clip_x1; if (surfaceWidth != 0) { - if (x0 < 0) { // Adjust for negative x0 + if (x0 < 0) { clip_x0 = 0; clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; if (w <= -x0) - clip_x1 = 0; // Entire width is clipped + clip_x1 = 0; } else { clip_x0 = (x0 > surfaceWidth) ? surfaceWidth : x0; clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; } - renderSurface->sc_x0 = clip_x0; - renderSurface->sc_x1 = clip_x1; + renderSurface->sc_x0 = static_cast(clip_x0); + renderSurface->sc_x1 = static_cast(clip_x1); } - // Handle vertical clipping int surfaceHeight = renderSurface->height; int clip_y0, clip_y1; if (surfaceHeight != 0) { - if (y0 < 0) { // Adjust for negative y0 + if (y0 < 0) { clip_y0 = 0; clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; if (h <= -y0) - clip_y1 = 0; // Entire height is clipped + clip_y1 = 0; } else { clip_y0 = (y0 > surfaceHeight) ? surfaceHeight : y0; clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; } - renderSurface->sc_y0 = clip_y0; - renderSurface->sc_y1 = clip_y1; + renderSurface->sc_y0 = static_cast(clip_y0); + renderSurface->sc_y1 = static_cast(clip_y1); } + LOG_INFO(Lib_Font, "RenderSurfaceSetScissor: [{},{}-{}:{}]", renderSurface->sc_x0, + renderSurface->sc_y0, renderSurface->sc_x1, renderSurface->sc_y1); } s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, OrbisFontStyleFrame* styleFrame) { - if (!renderSurface) { - LOG_ERROR(Lib_Font, "Invalid Parameter"); + if (!renderSurface) return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - if (!styleFrame) { - renderSurface->styleFlag &= 0xFE; // Clear style flag - } else { - // Validate magic number - if (styleFrame->magic != 0xF09) { - LOG_ERROR(Lib_Font, "Invalid magic"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - renderSurface->styleFlag |= 1; // Set style flag - } - - // Assign style frame pointer - renderSurface->unkn_28[0] = styleFrame; - *(uint32_t*)(renderSurface->unkn_28 + 1) = 0; // Reset related field + g_style_for_surface[renderSurface] = styleFrame; + renderSurface->styleFlag |= 0x1; + renderSurface->reserved_q[0] = reinterpret_cast(styleFrame); + LOG_INFO(Lib_Font, "RenderSurfaceSetStyleFrame: surf={} styleFrame={}", + static_cast(renderSurface), static_cast(styleFrame)); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSetEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); + LOG_DEBUG(Lib_Font, "SetEffectSlant: no-op (effects not implemented)"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSetEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); + LOG_DEBUG(Lib_Font, "SetEffectWeight: no-op (effects not implemented)"); return ORBIS_OK; } @@ -859,8 +1607,23 @@ s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h) { + if (w <= 0.0f || h <= 0.0f) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + st.scale_w = w; + st.scale_h = h; + const bool sys_ok = EnsureSystemFace(st); + if (st.ext_face_ready) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + if (sys_ok) + st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); + LOG_INFO( + Lib_Font, + "SetScalePixel: handle={} w={} h={} ext_scale={} sys_scale={} ext_ready={} sys_ready={}", + static_cast(fontHandle), w, h, st.ext_scale_for_height, st.scale_for_height, + st.ext_face_ready, sys_ok); return ORBIS_OK; } @@ -889,9 +1652,11 @@ s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h) { + auto rc = sceFontSetScalePixel(fontHandle, w, h); + LOG_INFO(Lib_Font, "SetupRenderScalePixel: handle={} w={} h={}", + static_cast(fontHandle), w, h); + return rc; } s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint() { @@ -926,65 +1691,28 @@ s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, float* slantRatio) { - if (!styleFrame) { - LOG_ERROR(Lib_Font, "Invalid Parameter"); + if (!styleFrame || !slantRatio) return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - // Validate the magic number - if (styleFrame->magic != 0xF09) { - LOG_ERROR(Lib_Font, "Invalid Magic"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - // Check if the slant effect is enabled (bit 1 in flags) - if (!(styleFrame->flags & 0x02)) { - LOG_ERROR(Lib_Font, "Flag not set"); - return ORBIS_FONT_ERROR_UNSET_PARAMETER; - } - - if (!slantRatio) { - LOG_ERROR(Lib_Font, "Invalid Parameter"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - // Retrieve slant ratio - *slantRatio = styleFrame->slantRatio; + *slantRatio = 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameGetEffectSlant: frame={} slant={} (opaque)", + static_cast(styleFrame), *slantRatio); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, float* weightXScale, float* weightYScale, uint32_t* mode) { - if (!fontStyleFrame) { - LOG_ERROR(Lib_Font, "Invalid Parameter"); + if (!fontStyleFrame) return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - // Validate the magic number - if (fontStyleFrame->magic != 0xF09) { - LOG_ERROR(Lib_Font, "Magic not set"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - // Check if the weight effect is enabled (bit 2 in flags) - if (!(fontStyleFrame->flags & 0x04)) { - LOG_ERROR(Lib_Font, "Flag not set"); - return ORBIS_FONT_ERROR_UNSET_PARAMETER; - } - - // Retrieve weight scales (default is +1.0 to maintain normal weight) - if (weightXScale) { - *weightXScale = fontStyleFrame->weightXScale + 1.0f; - } - if (weightYScale) { - *weightYScale = fontStyleFrame->weightYScale + 1.0f; - } - - // Reset mode if provided - if (mode) { + if (weightXScale) + *weightXScale = 1.0f; + if (weightYScale) + *weightYScale = 1.0f; + if (mode) *mode = 0; - } + LOG_DEBUG(Lib_Font, "StyleFrameGetEffectWeight: frame={} weight=({}, {}) mode={} (opaque)", + static_cast(fontStyleFrame), weightXScale ? *weightXScale : -1.0f, + weightYScale ? *weightYScale : -1.0f, mode ? *mode : 0u); return ORBIS_OK; } @@ -995,37 +1723,14 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, float* h) { - if (!styleFrame) { - LOG_ERROR(Lib_Font, "Invalid Parameter"); + if (!styleFrame) return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - if (styleFrame->magic != 0xF09) { - LOG_ERROR(Lib_Font, "Invalid magic"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - - if (!(styleFrame->flags & 0x01)) { - LOG_ERROR(Lib_Font, "Scaling effect parameter not set"); - return ORBIS_FONT_ERROR_UNSET_PARAMETER; - } - - // Check if scaling is allowed - int isScalingEnabled = styleFrame->scalingFlag; - if (w) { - *w = styleFrame->scaleWidth; - if (isScalingEnabled && styleFrame->dpiX) { - *w *= ((float)styleFrame->dpiX / 72.0f); - } - } - - if (h) { - *h = styleFrame->scaleHeight; - if (isScalingEnabled && styleFrame->dpiY) { - *h *= ((float)styleFrame->dpiY / 72.0f); - } - } - + if (w) + *w = 0.0f; + if (h) + *h = 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameGetScalePixel: frame={} -> w={}, h={} (opaque)", + static_cast(styleFrame), w ? *w : 0.0f, h ? *h : 0.0f); return ORBIS_OK; } @@ -1079,8 +1784,14 @@ s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSupportExternalFonts() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats) { + auto& ls = GetLibState(library); + ls.support_external = true; + ls.external_fontMax = fontMax; + ls.external_formats = formats; + LOG_INFO(Lib_Font, "SupportExternalFonts: lib={} fontMax={} formats=0x{:X}", + static_cast(library), fontMax, formats); + LogExternalFormatSupport(formats); return ORBIS_OK; } @@ -1089,8 +1800,10 @@ s32 PS4_SYSV_ABI sceFontSupportGlyphs() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSupportSystemFonts() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { + auto& ls = GetLibState(library); + ls.support_system = true; + LOG_INFO(Lib_Font, "SupportSystemFonts: lib={} system=on", static_cast(library)); return ORBIS_OK; } @@ -1124,8 +1837,9 @@ s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontUnbindRenderer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle) { + LOG_DEBUG(Lib_Font, "sceFontUnbindRenderer fontHandle={}", + static_cast(fontHandle)); return ORBIS_OK; } @@ -1608,4 +2322,4 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", Func_FE7E5AE95D3058F5); }; -} // namespace Libraries::Font \ No newline at end of file +} // namespace Libraries::Font diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index a8e239126..11da25331 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -11,6 +12,128 @@ class SymbolsResolver; namespace Libraries::Font { +using OrbisFontLib = void*; + +struct OrbisFontHandleOpaque { + u32 reserved[64]; +}; +using OrbisFontHandle = OrbisFontHandleOpaque*; + +using OrbisFontRendererCreateParams = void*; +using OrbisFontRenderer = void*; +using OrbisFontLibCreateParams = void*; + +struct OrbisFontOpenParams { + u16 tag; + u16 pad16; + u32 flags; + u32 subfont_index; + s32 unique_id; + const void* reserved_ptr2; + const void* reserved_ptr1; +}; +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); +#endif + +struct OrbisFontGlyphMetrics { + float w; + float h; + float h_bearing_x; + float h_bearing_y; + float h_adv; + float v_bearing_x; + float v_bearing_y; + float v_adv; +}; +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); +#endif + +struct OrbisFontKerning { + float dx; + float dy; + float px; + float py; +}; +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); +#endif + +struct OrbisFontGlyphImageMetrics { + float bearing_x; + float bearing_y; + float dv; + float stride; + u32 width; + u32 height; +}; + +struct OrbisFontResultStage { + u8* p_00; + u32 u32_08; + u32 u32_0C; + u32 u32_10; +}; + +struct OrbisFontResultSlot { + u8* maybe_addr; + u32 maybe_rowBytes; + u8 maybe_pixelSize; + u8 maybe_pixelFmt; +}; + +struct OrbisFontRenderOutput { + const OrbisFontResultStage* stage; + OrbisFontResultSlot slot; + u32 new_x; + u32 new_y; + u32 new_w; + u32 new_h; + OrbisFontGlyphImageMetrics ImageMetrics; +}; + +struct OrbisFontMem; + +using OrbisFontAllocFn = void*(void* object, u32 size); +using OrbisFontFreeFn = void(void* object, void* p); +using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); +using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); +using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, + u32 attr); +using OrbisFontMspaceDestroyFn = void(void* parent, void* mspace); +using OrbisFontMemDestroyFn = void(OrbisFontMem* fontMemory, void* object, void* destroyArg); + +using OrbisFontAllocCb = OrbisFontAllocFn*; +using OrbisFontFreeCb = OrbisFontFreeFn*; +using OrbisFontReallocCb = OrbisFontReallocFn*; +using OrbisFontCallocCb = OrbisFontCallocFn*; +using OrbisFontMspaceCreateCb = OrbisFontMspaceCreateFn*; +using OrbisFontMspaceDestroyCb = OrbisFontMspaceDestroyFn*; +using OrbisFontMemDestroyCb = OrbisFontMemDestroyFn*; + +struct OrbisFontMemInterface { + OrbisFontAllocCb alloc{}; + OrbisFontFreeCb dealloc{}; + OrbisFontReallocCb realloc_fn{}; + OrbisFontCallocCb calloc_fn{}; + OrbisFontMspaceCreateCb mspace_create{}; + OrbisFontMspaceDestroyCb mspace_destroy{}; +}; + +struct OrbisFontMem { + u16 mem_kind; + u16 attr_bits; + u32 region_size; + void* region_base; + void* mspace_handle; + const OrbisFontMemInterface* iface; + OrbisFontMemDestroyCb on_destroy; + void* destroy_ctx; + void* some_ctx1; + void* some_ctx2; +}; + struct OrbisFontTextCharacter { // Other fields... struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 @@ -28,33 +151,43 @@ struct OrbisFontRenderSurface { void* buffer; s32 widthByte; s8 pixelSizeByte; - u8 unkn_0xd; + u8 pad0; u8 styleFlag; - u8 unkn_0xf; + u8 pad2; s32 width, height; u32 sc_x0; u32 sc_y0; u32 sc_x1; u32 sc_y1; - void* unkn_28[3]; + u64 reserved_q[11]; }; +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); +#endif + struct OrbisFontStyleFrame { /*0x00*/ u16 magic; // Expected to be 0xF09 /*0x02*/ u16 flags; /*0x04*/ s32 dpiX; // DPI scaling factor for width /*0x08*/ s32 dpiY; // DPI scaling factor for height /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled - /*0x10*/ - /*0x14*/ float scaleWidth; // Width scaling factor - /*0x18*/ float scaleHeight; // Height scaling factor - /*0x1c*/ float weightXScale; - /*0x20*/ float weightYScale; - /*0x24*/ float slantRatio; + /*0x10*/ float scaleWidth; // Width scaling factor + /*0x14*/ float scaleHeight; // Height scaling factor + /*0x18*/ float weightXScale; + /*0x1c*/ float weightYScale; + /*0x20*/ float slantRatio; + /*0x24*/ }; -s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); -s32 PS4_SYSV_ABI sceFontBindRenderer(); +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontGlyphImageMetrics) == 24, "OrbisFontGlyphImageMetrics ABI size"); +static_assert(sizeof(OrbisFontRenderOutput) == 64, "OrbisFontRenderOutput ABI size"); +static_assert(sizeof(OrbisFontMem) == 64, "OrbisFontMem ABI size"); +#endif + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size); +s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer); s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, int* bidiLevel); s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); @@ -75,9 +208,13 @@ s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); s32 PS4_SYSV_ABI sceFontCreateLibrary(); -s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + u64 edition, OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontCreateRenderer(); -s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + u64 edition, OrbisFontRenderer* pRenderer); s32 PS4_SYSV_ABI sceFontCreateString(); s32 PS4_SYSV_ABI sceFontCreateWords(); s32 PS4_SYSV_ABI sceFontCreateWritingLine(); @@ -94,7 +231,8 @@ s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); s32 PS4_SYSV_ABI sceFontGetAttribute(); s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); -s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, + OrbisFontGlyphMetrics* metrics); s32 PS4_SYSV_ABI sceFontGetEffectSlant(); s32 PS4_SYSV_ABI sceFontGetEffectWeight(); s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); @@ -104,8 +242,9 @@ s32 PS4_SYSV_ABI sceFontGetFontResolution(); s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(); -s32 PS4_SYSV_ABI sceFontGetKerning(); -s32 PS4_SYSV_ABI sceFontGetLibrary(); +s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, + OrbisFontKerning* kerning); +s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontGetPixelResolution(); s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); @@ -114,7 +253,7 @@ s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(); s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(); s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h); s32 PS4_SYSV_ABI sceFontGetScalePoint(); s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); @@ -182,15 +321,27 @@ s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); -s32 PS4_SYSV_ABI sceFontMemoryInit(); +s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* fontMemory, void* address, u32 sizeByte, + const OrbisFontMemInterface* memInterface, void* mspaceObject, + OrbisFontMemDestroyCb destroyCallback, void* destroyObject); s32 PS4_SYSV_ABI sceFontMemoryTerm(); s32 PS4_SYSV_ABI sceFontOpenFontFile(); s32 PS4_SYSV_ABI sceFontOpenFontInstance(); -s32 PS4_SYSV_ABI sceFontOpenFontMemory(); -s32 PS4_SYSV_ABI sceFontOpenFontSet(); -s32 PS4_SYSV_ABI sceFontRebindRenderer(); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(); +s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle); +s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle); +s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, float y, + OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, + float y, OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result); s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); @@ -206,13 +357,13 @@ s32 PS4_SYSV_ABI sceFontSetEffectSlant(); s32 PS4_SYSV_ABI sceFontSetEffectWeight(); s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); s32 PS4_SYSV_ABI sceFontSetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontSetScalePixel(); +s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetScalePoint(); s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); -s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(); s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); @@ -237,16 +388,16 @@ s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(); s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(); s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(); s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(); -s32 PS4_SYSV_ABI sceFontSupportExternalFonts(); +s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats); s32 PS4_SYSV_ABI sceFontSupportGlyphs(); -s32 PS4_SYSV_ABI sceFontSupportSystemFonts(); +s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library); s32 PS4_SYSV_ABI sceFontTextCodesStepBack(); s32 PS4_SYSV_ABI sceFontTextCodesStepNext(); s32 PS4_SYSV_ABI sceFontTextSourceInit(); s32 PS4_SYSV_ABI sceFontTextSourceRewind(); s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont(); s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm(); -s32 PS4_SYSV_ABI sceFontUnbindRenderer(); +s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle); s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters(); s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics(); s32 PS4_SYSV_ABI sceFontWritingInit(); @@ -296,4 +447,4 @@ s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Font \ No newline at end of file +} // namespace Libraries::Font From 3b6d55b8973058ff7d7d37b2101fd71c68d4045f Mon Sep 17 00:00:00 2001 From: w1naenator Date: Thu, 6 Nov 2025 03:27:52 +0200 Subject: [PATCH 16/51] Fix for DC: font scale still inaccurate, cause unknown. Refactor font library: streamline file handling, enhance layout caching, and improve scale computation. --- src/core/libraries/font/font.cpp | 434 +++++++++++++++++-------------- src/core/libraries/font/font.h | 48 ++-- 2 files changed, 260 insertions(+), 222 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 41f19e62e..8164ebff9 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -2,11 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include #include -#include #include #include #include @@ -38,23 +38,21 @@ struct FontState { float scale_w = 16.0f; float scale_h = 16.0f; Libraries::Font::OrbisFontLib library = nullptr; - bool face_ready = false; - std::vector face_data; - stbtt_fontinfo face{}; - float scale_for_height = 0.0f; - int ascent = 0, descent = 0, lineGap = 0; bool ext_face_ready = false; std::vector ext_face_data; stbtt_fontinfo ext_face{}; float ext_scale_for_height = 0.0f; int ext_ascent = 0, ext_descent = 0, ext_lineGap = 0; std::unordered_map ext_cache; - std::unordered_map sys_cache; - std::unordered_map ext_kern_cache; - std::unordered_map sys_kern_cache; std::vector scratch; bool logged_ext_use = false; - bool logged_sys_use = false; + // Cached layout (PS4-style) for GetHorizontalLayout + float cached_baseline_y = 0.0f; + bool layout_cached = false; + // Renderer binding state (PS4 requires a bound renderer for render-scale queries) + Libraries::Font::OrbisFontRenderer bound_renderer = nullptr; + // Mark when the game asked for PS4 internal/system fonts (via OpenFontSet) + bool system_requested = false; }; static std::unordered_map g_font_state; @@ -83,55 +81,86 @@ static void LogExternalFormatSupport(u32 formats_mask) { LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); } -static bool ReadFileBinary(const std::string& path, std::vector& out) { - std::ifstream f(path, std::ios::binary); - if (!f) - return false; - f.seekg(0, std::ios::end); - std::streampos sz = f.tellg(); - if (sz <= 0) - return false; - out.resize(static_cast(sz)); - f.seekg(0, std::ios::beg); - f.read(reinterpret_cast(out.data()), sz); - return f.good(); +enum class ScaleMode { + AscenderHeight, + EmSquare, +}; + +static ScaleMode GetScaleMode() { + static int inited = 0; + static ScaleMode mode = ScaleMode::AscenderHeight; + if (!inited) { + inited = 1; + if (const char* env = std::getenv("SHADPS4_FONT_SCALE_MODE")) { + std::string v = env; + for (auto& c : v) + c = static_cast(std::tolower(c)); + if (v == "em" || v == "emsquare" || v == "em_square") { + mode = ScaleMode::EmSquare; + } + } + LOG_INFO(Lib_Font, "FontScaleMode: {}", + mode == ScaleMode::EmSquare ? "em-square" : "ascender-height"); + } + return mode; } -static bool EnsureSystemFace(FontState& st) { - if (st.face_ready) - return true; - std::vector candidates; - if (const char* env_dir = std::getenv("SHADPS4_FONTS_DIR"); env_dir && *env_dir) { - candidates.emplace_back(std::string(env_dir) + "/NotoSansJP-Regular.ttf"); - candidates.emplace_back(std::string(env_dir) + "/ProggyVector-Regular.ttf"); - } - static const char* rel_roots[] = {".", "..", "../..", "../../..", "../../../.."}; - for (auto* root : rel_roots) { - candidates.emplace_back(std::string(root) + - "/src/imgui/renderer/fonts/NotoSansJP-Regular.ttf"); - candidates.emplace_back(std::string(root) + - "/src/imgui/renderer/fonts/ProggyVector-Regular.ttf"); - } - for (const auto& path : candidates) { - st.face_data.clear(); - if (ReadFileBinary(path, st.face_data)) { - if (stbtt_InitFont(&st.face, st.face_data.data(), 0)) { - st.face_ready = true; - stbtt_GetFontVMetrics(&st.face, &st.ascent, &st.descent, &st.lineGap); - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO(Lib_Font, - "SystemFace: loaded '{}' (ascent={}, descent={}, lineGap={}, scale={})", - path, st.ascent, st.descent, st.lineGap, st.scale_for_height); - return true; - } else { - LOG_WARNING(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", path); +static float GetScaleBias() { + static int inited = 0; + static float bias = 1.0f; + if (!inited) { + inited = 1; + if (const char* env = std::getenv("SHADPS4_FONT_SCALE_BIAS")) { + try { + bias = std::max(0.01f, std::min(10.0f, std::stof(env))); + } catch (...) { + bias = 1.0f; } - } else { - LOG_DEBUG(Lib_Font, "SystemFace: could not open '{}'", path); + } + if (bias != 1.0f) { + LOG_INFO(Lib_Font, "FontScaleBias: {}", bias); } } - LOG_WARNING(Lib_Font, "SystemFace: no font file found; using placeholder rectangles"); + return bias; +} + +static float ComputeScale(const stbtt_fontinfo* face, float pixel_h) { + if (!face) + return 0.0f; + float s = 0.0f; + if (GetScaleMode() == ScaleMode::EmSquare) + s = stbtt_ScaleForMappingEmToPixels(face, pixel_h); + else + s = stbtt_ScaleForPixelHeight(face, pixel_h); + return s * GetScaleBias(); +} + +// External face scale: keep legacy ascender-height mapping to avoid regressions +// in titles that ship their own fonts (e.g., CUSA47038). No EM toggle, no bias. +static float ComputeScaleExt(const stbtt_fontinfo* face, float pixel_h) { + if (!face) + return 0.0f; + return stbtt_ScaleForPixelHeight(face, pixel_h); +} + +static void UpdateCachedLayout(FontState& st) { + if (!st.ext_face_ready) { + return; + } + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + const float scale = st.ext_scale_for_height; + st.cached_baseline_y = static_cast(asc) * scale; + st.layout_cached = true; +} + +static bool ReportSystemFaceRequest(FontState& st) { + if (!st.system_requested) { + st.system_requested = true; + LOG_ERROR(Lib_Font, "SystemFace: internal PS4 font requested but not available"); + } return false; } @@ -170,8 +199,8 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { - LOG_DEBUG(Lib_Font, "sceFontBindRenderer fontHandle={} renderer={}", - static_cast(fontHandle), static_cast(renderer)); + auto& st = GetState(fontHandle); + st.bound_renderer = renderer; return ORBIS_OK; } @@ -421,17 +450,14 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code auto& st = GetState(fontHandle); const stbtt_fontinfo* face = nullptr; float scale = 0.0f; - bool use_ext = false; - int ext_glyph = 0; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (ext_glyph > 0) { + const int glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (glyph > 0) { face = &st.ext_face; scale = st.ext_scale_for_height; - use_ext = true; if (!st.logged_ext_use) { LOG_INFO(Lib_Font, "RenderFace: handle={} source=external (game font)", static_cast(fontHandle)); @@ -439,33 +465,16 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code } } } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (sys_glyph > 0) { - face = &st.face; - scale = st.scale_for_height; - use_ext = false; - if (!st.logged_sys_use) { - LOG_INFO(Lib_Font, "RenderFace: handle={} source=system (fallback)", - static_cast(fontHandle)); - st.logged_sys_use = true; - } - } - } + + if (!face) + ReportSystemFaceRequest(st); + if (face) { const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); GlyphEntry* ge = nullptr; - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } if (!ge) { GlyphEntry entry{}; @@ -477,14 +486,10 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code entry.h = std::max(0, entry.y1 - entry.y0); entry.advance = static_cast(aw) * scale; entry.bearingX = static_cast(lsb) * scale; - if (use_ext) { - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } else { - ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; - } + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; } - metrics->w = ge->w > 0 ? (float)ge->w : st.scale_w; - metrics->h = ge->h > 0 ? (float)ge->h : st.scale_h; + metrics->w = ge->w > 0 ? static_cast(ge->w) : st.scale_w; + metrics->h = ge->h > 0 ? static_cast(ge->h) : st.scale_h; metrics->h_bearing_x = ge->bearingX; metrics->h_bearing_y = static_cast(-ge->y0); metrics->h_adv = ge->advance > 0.0f ? ge->advance : st.scale_w; @@ -492,10 +497,10 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code metrics->v_bearing_y = 0.0f; metrics->v_adv = 0.0f; LOG_TRACE(Lib_Font, - "GetCharGlyphMetrics: code=U+{:04X} src={} size=({}, {}) adv={} bearing=({}, {}) " - "box={}x{}", - code, use_ext ? "external" : "system", st.scale_w, st.scale_h, metrics->h_adv, - metrics->h_bearing_x, metrics->h_bearing_y, metrics->w, metrics->h); + "GetCharGlyphMetrics: code=U+{:04X} src=external size=({}, {}) adv={} " + "bearing=({}, {}) box={}x{}", + code, st.scale_w, st.scale_h, metrics->h_adv, metrics->h_bearing_x, + metrics->h_bearing_y, metrics->w, metrics->h); return ORBIS_OK; } metrics->w = st.scale_w; @@ -551,8 +556,37 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { - LOG_DEBUG(Lib_Font, "GetHorizontalLayout: default layout (no effects)"); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, + OrbisFontHorizontalLayout* layout) { + if (!layout) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + const float scale = st.ext_scale_for_height; + layout->baselineOffset = static_cast(asc) * scale; + layout->lineAdvance = static_cast(asc - desc + gap) * scale; + layout->decorationExtent = 0.0f; + LOG_INFO( + Lib_Font, + "GetHorizontalLayout: handle={} ext_ready={} baselineOffset={} lineAdvance={} scale={}", + static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, + layout->lineAdvance, scale); + return ORBIS_OK; + } + ReportSystemFaceRequest(st); + layout->baselineOffset = st.scale_h * 0.8f; + layout->lineAdvance = st.scale_h; + layout->decorationExtent = 0.0f; + LOG_INFO( + Lib_Font, + "GetHorizontalLayout: fallback handle={} ext_ready={} baselineOffset={} lineAdvance={}", + static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, + layout->lineAdvance); return ORBIS_OK; } @@ -563,39 +597,24 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); - const stbtt_fontinfo* face = nullptr; - float scale = 0.0f; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); int g1 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(preCode)); int g2 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); if (g1 > 0 && g2 > 0) { - face = &st.ext_face; - scale = st.ext_scale_for_height; + const int kern = stbtt_GetCodepointKernAdvance(&st.ext_face, static_cast(preCode), + static_cast(code)); + const float kx = static_cast(kern) * st.ext_scale_for_height; + kerning->dx = kx; + kerning->dy = 0.0f; + kerning->px = 0.0f; + kerning->py = 0.0f; + LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); + return ORBIS_OK; } } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int g1 = stbtt_FindGlyphIndex(&st.face, static_cast(preCode)); - int g2 = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (g1 > 0 && g2 > 0) { - face = &st.face; - scale = st.scale_for_height; - } - } - if (face) { - const int kern = - stbtt_GetCodepointKernAdvance(face, static_cast(preCode), static_cast(code)); - const float kx = static_cast(kern) * scale; - kerning->dx = kx; - kerning->dy = 0.0f; - kerning->px = 0.0f; - kerning->py = 0.0f; - LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); - return ORBIS_OK; - } + ReportSystemFaceRequest(st); kerning->dx = 0.0f; kerning->dy = 0.0f; kerning->px = 0.0f; @@ -638,14 +657,25 @@ s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h) { + auto& st = GetState(fontHandle); + if (w) + *w = st.scale_w; + if (h) + *h = st.scale_h; + if (!st.bound_renderer) { + LOG_DEBUG(Lib_Font, + "GetRenderScalePixel: no renderer bound; returning configured scale w={} h={}", + w ? *w : -1.0f, h ? *h : -1.0f); + } else { + LOG_DEBUG(Lib_Font, "GetRenderScalePixel: handle={} -> w={}, h={}", + static_cast(fontHandle), w ? *w : -1.0f, h ? *h : -1.0f); + } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h) { + return sceFontGetRenderScalePixel(fontHandle, w, h); } s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { @@ -681,8 +711,26 @@ s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetVerticalLayout() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, + OrbisFontVerticalLayout* layout) { + if (!layout) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + layout->baselineOffsetX = 0.0f; + layout->columnAdvance = static_cast(asc - desc + gap) * st.ext_scale_for_height; + layout->decorationSpan = 0.0f; + return ORBIS_OK; + } + ReportSystemFaceRequest(st); + layout->baselineOffsetX = 0.0f; + layout->columnAdvance = st.scale_h; + layout->decorationSpan = 0.0f; return ORBIS_OK; } @@ -1106,7 +1154,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), is_otf_cff); if (is_otf_cff) { - LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; fallback to system font"); + LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; system font unavailable"); } } return ORBIS_OK; @@ -1125,15 +1173,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o *pFontHandle = f; auto& st = GetState(f); st.library = library; - EnsureSystemFace(st); - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO( - Lib_Font, - "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system face={})", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle), - EnsureSystemFace(GetState(f))); + const bool system_ok = ReportSystemFaceRequest(st); + + LOG_INFO(Lib_Font, + "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system " + "available={})", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle), + system_ok); return ORBIS_OK; } @@ -1185,47 +1232,33 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl int g_x0 = 0, g_y0 = 0, g_x1 = 0, g_y1 = 0; const stbtt_fontinfo* face = nullptr; float scale = 0.0f; - bool use_ext = false; - int ext_glyph = 0; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (ext_glyph > 0) { + const int glyph_index = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (glyph_index > 0) { face = &st.ext_face; scale = st.ext_scale_for_height; - use_ext = true; - } - } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (sys_glyph > 0) { - face = &st.face; - scale = st.scale_for_height; - use_ext = false; } } + + if (!face) + ReportSystemFaceRequest(st); + + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + if (face) { - LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src={}", - static_cast(fontHandle), code, use_ext ? "external" : "system"); - const float frac_x = x - std::floor(x); - const float frac_y = y - std::floor(y); - const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src=external", + static_cast(fontHandle), code); const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); GlyphEntry* ge = nullptr; if (!use_subpixel) { - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } } if (!ge) { @@ -1248,16 +1281,10 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl entry.bitmap.resize(static_cast(entry.w * entry.h)); stbtt_MakeCodepointBitmap(face, entry.bitmap.data(), entry.w, entry.h, entry.w, scale, scale, static_cast(code)); - } - if (!use_subpixel) { - if (use_ext) { - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } else { - ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; - } - } else { - fw = (float)entry.w; - fh = (float)entry.h; + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } else if (use_subpixel) { + fw = static_cast(entry.w); + fh = static_cast(entry.h); g_x0 = entry.x0; g_y0 = entry.y0; g_x1 = entry.x1; @@ -1265,8 +1292,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl } } if (ge) { - fw = (float)ge->w; - fh = (float)ge->h; + fw = static_cast(ge->w); + fh = static_cast(ge->h); g_x0 = ge->x0; g_y0 = ge->y0; g_x1 = ge->x1; @@ -1305,8 +1332,22 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl return ORBIS_OK; } - int ix = static_cast(std::floor(x)); - int iy = static_cast(std::floor(y)); + if (!st.layout_cached) { + UpdateCachedLayout(st); + } + float layout_baseline = st.layout_cached ? st.cached_baseline_y : 0.0f; + + float adjusted_y = y; + if (face && layout_baseline > 0.0f) { + const float top_expect = adjusted_y + static_cast(g_y0); + const float bottom_expect = top_expect + fh; + if (bottom_expect <= 0.0f || top_expect < 0.0f) { + adjusted_y += layout_baseline; + } + } + + int ix = static_cast(x); + int iy = static_cast(adjusted_y); int left = ix; int top = iy; if (face) { @@ -1346,6 +1387,12 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl int cw = std::max(0, rx1 - rx0); int ch = std::max(0, ry1 - ry0); + if (ch == 0 && face) { + LOG_DEBUG(Lib_Font, + "RenderGlyph(H): zero-height glyph code=U+{:04X} top={} ih={} g_y0={} g_y1={}" + " y={}, g_adv={} scale={} src=external", + code, top, ih, g_y0, g_y1, y, metrics ? metrics->h_adv : -1.0f, scale); + } if (cw > 0 && ch > 0) { const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); @@ -1362,14 +1409,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); const GlyphEntry* ge = nullptr; - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } const float frac_x = x - std::floor(x); const float frac_y = y - std::floor(y); @@ -1614,16 +1655,13 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float auto& st = GetState(fontHandle); st.scale_w = w; st.scale_h = h; - const bool sys_ok = EnsureSystemFace(st); if (st.ext_face_ready) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - if (sys_ok) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO( - Lib_Font, - "SetScalePixel: handle={} w={} h={} ext_scale={} sys_scale={} ext_ready={} sys_ready={}", - static_cast(fontHandle), w, h, st.ext_scale_for_height, st.scale_for_height, - st.ext_face_ready, sys_ok); + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + ReportSystemFaceRequest(st); + LOG_INFO(Lib_Font, "SetScalePixel: handle={} w={} h={} ext_scale={} ext_ready={}", + static_cast(fontHandle), w, h, st.ext_scale_for_height, + st.ext_face_ready); + st.layout_cached = false; return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 11da25331..503663cb8 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -1,24 +1,19 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #pragma once - #include #include "common/types.h" - namespace Core::Loader { class SymbolsResolver; } - namespace Libraries::Font { -using OrbisFontLib = void*; - struct OrbisFontHandleOpaque { u32 reserved[64]; }; -using OrbisFontHandle = OrbisFontHandleOpaque*; +using OrbisFontLib = void*; +using OrbisFontHandle = OrbisFontHandleOpaque*; using OrbisFontRendererCreateParams = void*; using OrbisFontRenderer = void*; using OrbisFontLibCreateParams = void*; @@ -32,9 +27,6 @@ struct OrbisFontOpenParams { const void* reserved_ptr2; const void* reserved_ptr1; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); -#endif struct OrbisFontGlyphMetrics { float w; @@ -46,9 +38,6 @@ struct OrbisFontGlyphMetrics { float v_bearing_y; float v_adv; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); -#endif struct OrbisFontKerning { float dx; @@ -56,9 +45,6 @@ struct OrbisFontKerning { float px; float py; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); -#endif struct OrbisFontGlyphImageMetrics { float bearing_x; @@ -162,10 +148,6 @@ struct OrbisFontRenderSurface { u64 reserved_q[11]; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); -#endif - struct OrbisFontStyleFrame { /*0x00*/ u16 magic; // Expected to be 0xF09 /*0x02*/ u16 flags; @@ -180,7 +162,23 @@ struct OrbisFontStyleFrame { /*0x24*/ }; + +struct OrbisFontHorizontalLayout { + float baselineOffset; + float lineAdvance; + float decorationExtent; +}; +struct OrbisFontVerticalLayout { + float baselineOffsetX; + float columnAdvance; + float decorationSpan; +}; + #if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); +static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); +static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); +static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); static_assert(sizeof(OrbisFontGlyphImageMetrics) == 24, "OrbisFontGlyphImageMetrics ABI size"); static_assert(sizeof(OrbisFontRenderOutput) == 64, "OrbisFontRenderOutput ABI size"); static_assert(sizeof(OrbisFontMem) == 64, "OrbisFontMem ABI size"); @@ -241,7 +239,8 @@ s32 PS4_SYSV_ABI sceFontGetFontMetrics(); s32 PS4_SYSV_ABI sceFontGetFontResolution(); s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); -s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, + OrbisFontHorizontalLayout* layout); s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, OrbisFontKerning* kerning); s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); @@ -250,14 +249,15 @@ s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h); s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h); s32 PS4_SYSV_ABI sceFontGetScalePoint(); s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); -s32 PS4_SYSV_ABI sceFontGetVerticalLayout(); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, + OrbisFontVerticalLayout* layout); s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); From b5cf5901852bf89ccb05abf5653f9eadcdbaabbd Mon Sep 17 00:00:00 2001 From: w1naenator Date: Thu, 6 Nov 2025 03:29:21 +0200 Subject: [PATCH 17/51] Remove unnecessary blank line in OrbisFontStyleFrame struct --- src/core/libraries/font/font.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 503663cb8..7846232ec 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -162,7 +162,6 @@ struct OrbisFontStyleFrame { /*0x24*/ }; - struct OrbisFontHorizontalLayout { float baselineOffset; float lineAdvance; From d85bde3d8fe490d0c37d0e9293f0a77c66a36502 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 7 Nov 2025 08:12:16 +0200 Subject: [PATCH 18/51] Loading font files from app0, some other fixes. --- src/core/libraries/font/font.cpp | 392 ++++++++++++++++++++++------- src/core/libraries/font/font.h | 33 ++- src/core/libraries/font/fontft.cpp | 60 ++++- src/core/libraries/font/fontft.h | 21 +- 4 files changed, 387 insertions(+), 119 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 8164ebff9..437eddb8f 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -4,14 +4,21 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include #include #include #include "common/logging/log.h" +#include "common/singleton.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/font/font.h" #include "core/libraries/libs.h" @@ -21,7 +28,13 @@ #define STB_TRUETYPE_IMPLEMENTATION #include "externals/dear_imgui/imstb_truetype.h" +namespace Libraries::Font { +struct FontLibOpaque; +} + namespace { +Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); + struct GlyphEntry { std::vector bitmap; int w = 0; @@ -62,6 +75,10 @@ struct LibraryState { bool support_external = false; u32 external_formats = 0; u32 external_fontMax = 0; + const Libraries::Font::OrbisFontMem* backing_memory = nullptr; + Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; + u64 edition = 0; + Libraries::Font::FontLibOpaque* native = nullptr; }; static std::unordered_map g_library_state; @@ -73,14 +90,67 @@ static FontState& GetState(Libraries::Font::OrbisFontHandle h) { return g_font_state[h]; } +static FontState* TryGetState(Libraries::Font::OrbisFontHandle h) { + if (!h) + return nullptr; + auto it = g_font_state.find(h); + if (it == g_font_state.end()) + return nullptr; + return &it->second; +} + static LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { return g_library_state[lib]; } +static void RemoveLibState(Libraries::Font::OrbisFontLib lib) { + g_library_state.erase(lib); +} + static void LogExternalFormatSupport(u32 formats_mask) { LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); } +static std::filesystem::path ResolveGuestPath(const char* guest_path) { + if (!guest_path) { + return {}; + } + if (guest_path[0] != '/') { + return std::filesystem::path(guest_path); + } + if (!g_mnt) { + return {}; + } + return g_mnt->GetHostPath(guest_path); +} + +static bool LoadGuestFileBytes(const std::filesystem::path& host_path, + std::vector& out_bytes) { + std::ifstream file(host_path, std::ios::binary | std::ios::ate); + if (!file) { + return false; + } + const std::streamoff size = file.tellg(); + if (size < 0) { + return false; + } + if (size == 0) { + out_bytes.clear(); + return true; + } + if (static_cast(size) > std::numeric_limits::max()) { + return false; + } + out_bytes.resize(static_cast(size)); + file.seekg(0, std::ios::beg); + if (!file.read(reinterpret_cast(out_bytes.data()), + static_cast(out_bytes.size()))) { + out_bytes.clear(); + return false; + } + return true; +} + enum class ScaleMode { AscenderHeight, EmSquare, @@ -189,9 +259,42 @@ static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* namespace Libraries::Font { -struct FontLibOpaque {}; +struct FontLibOpaque { + u16 magic; + u16 reserved0; + u32 lock_word; + u32 flags; + u8 reserved1[0x14]; + void* alloc_ctx; + void** alloc_vtbl; + u8 reserved2[0x50]; + void* sys_driver; + void* fontset_registry; + u8 reserved3[0x10]; + void* sysfonts_ctx; + void* external_fonts_ctx; + u32* device_cache_buf; +}; +static_assert(sizeof(FontLibOpaque) == 0xB8, "FontLibOpaque size"); +static_assert(offsetof(FontLibOpaque, alloc_ctx) == 0x20, "FontLibOpaque alloc_ctx offset"); +static_assert(offsetof(FontLibOpaque, alloc_vtbl) == 0x28, "FontLibOpaque alloc_vtbl offset"); +static_assert(offsetof(FontLibOpaque, sys_driver) == 0x80, "FontLibOpaque sys_driver offset"); +static_assert(offsetof(FontLibOpaque, fontset_registry) == 0x88, + "FontLibOpaque fontset_registry offset"); +static_assert(offsetof(FontLibOpaque, sysfonts_ctx) == 0xA0, "FontLibOpaque sysfonts_ctx offset"); +static_assert(offsetof(FontLibOpaque, external_fonts_ctx) == 0xA8, + "FontLibOpaque external_fonts_ctx offset"); +static_assert(offsetof(FontLibOpaque, device_cache_buf) == 0xB0, + "FontLibOpaque device_cache_buf offset"); struct OrbisFontRenderer_ {}; +static void* g_allocator_vtbl_stub[4] = {}; +static std::uint8_t g_sys_driver_stub{}; +static std::uint8_t g_fontset_registry_stub{}; +static std::uint8_t g_sysfonts_ctx_stub{}; +static std::uint8_t g_external_fonts_ctx_stub{}; +static u32 g_device_cache_stub{}; + s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { LOG_ERROR(Lib_Font, "(STUBBED) called library={} buffer={} size={}", static_cast(library), static_cast(buffer), size); @@ -199,8 +302,13 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { + if (!fontHandle || !renderer) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } auto& st = GetState(fontHandle); st.bound_renderer = renderer; + LOG_INFO(Lib_Font, "BindRenderer: handle={} renderer={}", static_cast(fontHandle), + static_cast(renderer)); return ORBIS_OK; } @@ -325,9 +433,10 @@ s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCreateLibrary() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + OrbisFontLib* pLibrary) { + return sceFontCreateLibraryWithEdition(memory, create_params, 0, pLibrary); } s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, @@ -336,73 +445,83 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, if (!pLibrary) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - (void)memory; - (void)create_params; - (void)edition; - *pLibrary = new FontLibOpaque{}; + if (!memory) { + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + auto* lib = new (std::nothrow) FontLibOpaque{}; + if (!lib) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + lib->magic = 0x0F01; + lib->lock_word = 0; + lib->flags = 0; + lib->alloc_ctx = const_cast(memory); + lib->alloc_vtbl = g_allocator_vtbl_stub; + lib->sys_driver = &g_sys_driver_stub; + lib->fontset_registry = &g_fontset_registry_stub; + lib->sysfonts_ctx = &g_sysfonts_ctx_stub; + lib->external_fonts_ctx = &g_external_fonts_ctx_stub; + lib->device_cache_buf = &g_device_cache_stub; + *pLibrary = lib; + auto& state = GetLibState(lib); + state = LibraryState{}; + state.backing_memory = memory; + state.create_params = create_params; + state.edition = edition; + state.native = lib; + LOG_INFO(Lib_Font, "CreateLibrary: memory={} selection={} edition={} -> lib={}", + static_cast(memory), static_cast(create_params), edition, + static_cast(*pLibrary)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCreateRenderer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { + if (!pLibrary || !*pLibrary) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto lib = *pLibrary; + RemoveLibState(lib); + delete static_cast(lib); + *pLibrary = nullptr; + LOG_INFO(Lib_Font, "DestroyLibrary: lib={} destroyed", static_cast(lib)); return ORBIS_OK; } +s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + OrbisFontRenderer* pRenderer) { + return sceFontCreateRendererWithEdition(memory, create_params, 0, pRenderer); +} + s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer) { if (!pRenderer) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - (void)memory; - (void)create_params; - (void)edition; - *pRenderer = new OrbisFontRenderer_{}; + if (!memory) { + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + auto* renderer = new (std::nothrow) OrbisFontRenderer_{}; + if (!renderer) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + *pRenderer = renderer; + LOG_INFO(Lib_Font, "CreateRenderer: memory={} selection={} edition={} renderer={}", + static_cast(memory), static_cast(create_params), edition, + static_cast(renderer)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontCreateString() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontCreateWords() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontCreateWritingLine() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDefineAttribute() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDeleteGlyph() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDestroyLibrary() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFontDestroyRenderer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { + if (!pRenderer || !*pRenderer) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto renderer = *pRenderer; + delete static_cast(renderer); + *pRenderer = nullptr; + LOG_INFO(Lib_Font, "DestroyRenderer: renderer={} destroyed", + static_cast(renderer)); return ORBIS_OK; } @@ -558,10 +677,14 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, OrbisFontHorizontalLayout* layout) { - if (!layout) { + if (!fontHandle || !layout) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto& st = GetState(fontHandle); + auto* st_ptr = TryGetState(fontHandle); + if (!st_ptr) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + auto& st = *st_ptr; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); @@ -637,9 +760,18 @@ s32 PS4_SYSV_ABI sceFontGetPixelResolution() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, + OrbisFontGlyphMetrics* out_metrics) { + if (!fontHandle || !out_metrics) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (!st.bound_renderer) { + LOG_DEBUG(Lib_Font, "GetRenderCharGlyphMetrics: renderer not bound for handle={}", + static_cast(fontHandle)); + return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } + return sceFontGetCharGlyphMetrics(fontHandle, codepoint, out_metrics); } s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant() { @@ -657,25 +789,34 @@ s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h) { - auto& st = GetState(fontHandle); - if (w) - *w = st.scale_w; - if (h) - *h = st.scale_h; - if (!st.bound_renderer) { +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, + float* out_h) { + if (!fontHandle || (!out_w && !out_h)) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* st = TryGetState(fontHandle); + if (!st) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (out_w) + *out_w = st->scale_w; + if (out_h) + *out_h = st->scale_h; + if (!st->bound_renderer) { LOG_DEBUG(Lib_Font, "GetRenderScalePixel: no renderer bound; returning configured scale w={} h={}", - w ? *w : -1.0f, h ? *h : -1.0f); + out_w ? *out_w : -1.0f, out_h ? *out_h : -1.0f); } else { LOG_DEBUG(Lib_Font, "GetRenderScalePixel: handle={} -> w={}, h={}", - static_cast(fontHandle), w ? *w : -1.0f, h ? *h : -1.0f); + static_cast(fontHandle), out_w ? *out_w : -1.0f, + out_h ? *out_h : -1.0f); } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h) { - return sceFontGetRenderScalePixel(fontHandle, w, h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, + float* out_h) { + return sceFontGetRenderScalePixel(fontHandle, out_w, out_h); } s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { @@ -683,16 +824,21 @@ s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h) { - if (!w || !h) { +s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h) { + if (!fontHandle || (!out_w && !out_h)) { LOG_DEBUG(Lib_Font, "sceFontGetScalePixel: invalid params"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - const auto& st = GetState(fontHandle); - *w = st.scale_w; - *h = st.scale_h; + auto* st = TryGetState(fontHandle); + if (!st) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (out_w) + *out_w = st->scale_w; + if (out_h) + *out_h = st->scale_h; LOG_DEBUG(Lib_Font, "GetScalePixel: handle={} -> w={}, h={}", - static_cast(fontHandle), *w, *h); + static_cast(fontHandle), out_w ? *out_w : -1.0f, out_h ? *out_h : -1.0f); return ORBIS_OK; } @@ -1049,27 +1195,69 @@ s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* fontMemory, void* address, u32 sizeByte, - const OrbisFontMemInterface* memInterface, void* mspaceObject, - OrbisFontMemDestroyCb destroyCallback, void* destroyObject) { - LOG_ERROR(Lib_Font, - "(STUBBED) called font_mem={} region_base={} sizeByte={} mem_if={} mspace_handle={}" - " destroy_cb={} destroy_ctx={}", - static_cast(fontMemory), static_cast(address), sizeByte, - static_cast(memInterface), static_cast(mspaceObject), - reinterpret_cast(destroyCallback), - static_cast(destroyObject)); +s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u32 region_size, + const OrbisFontMemInterface* iface, void* mspace_obj, + OrbisFontMemDestroyCb destroy_cb, void* destroy_ctx) { + if (!mem_desc) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + mem_desc->mem_kind = 0; + mem_desc->attr_bits = 0; + mem_desc->region_base = region_addr; + mem_desc->region_size = region_size; + mem_desc->iface = iface; + mem_desc->mspace_handle = mspace_obj; + mem_desc->on_destroy = destroy_cb; + mem_desc->destroy_ctx = destroy_ctx; + mem_desc->some_ctx1 = nullptr; + mem_desc->some_ctx2 = nullptr; + + LOG_INFO( + Lib_Font, + "FontMemoryInit: font_mem={} region_base={} size={} mspace={} mem_if_set={} destroy_cb={}", + static_cast(mem_desc), static_cast(region_addr), region_size, + static_cast(mem_desc->mspace_handle), iface != nullptr, + reinterpret_cast(destroy_cb)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontMemoryTerm() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { + if (!mem_desc) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (mem_desc->on_destroy) { + mem_desc->on_destroy(mem_desc, mem_desc->destroy_ctx, mem_desc->some_ctx1); + } + std::memset(mem_desc, 0, sizeof(*mem_desc)); + LOG_INFO(Lib_Font, "FontMemoryTerm: font_mem={} cleaned", static_cast(mem_desc)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontOpenFontFile() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, + const OrbisFontOpenParams* open_detail, + OrbisFontHandle* out_handle) { + if (!library || !guest_path || !out_handle) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + const auto host_path = ResolveGuestPath(guest_path); + const std::filesystem::path path_to_open = + host_path.empty() ? std::filesystem::path(guest_path) : host_path; + + std::vector file_bytes; + if (!LoadGuestFileBytes(path_to_open, file_bytes)) { + LOG_WARNING(Lib_Font, "OpenFontFile: failed to open '{}'", path_to_open.string()); + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + if (file_bytes.size() > std::numeric_limits::max()) { + LOG_WARNING(Lib_Font, "OpenFontFile: '{}' exceeds libSceFont size limit ({} bytes)", + path_to_open.string(), file_bytes.size()); + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + LOG_INFO(Lib_Font, "OpenFontFile: path='{}' size={} openMode={}", path_to_open.string(), + static_cast(file_bytes.size()), open_mode); + return sceFontOpenFontMemory(library, file_bytes.data(), static_cast(file_bytes.size()), + open_detail, out_handle); } s32 PS4_SYSV_ABI sceFontOpenFontInstance() { @@ -1185,7 +1373,15 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle) { - LOG_ERROR(Lib_Font, "(STUBBED) called fontHandle={}", static_cast(fontHandle)); + if (!fontHandle) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (!st.bound_renderer) { + return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } + LOG_INFO(Lib_Font, "RebindRenderer: handle={} renderer={}", + static_cast(fontHandle), static_cast(st.bound_renderer)); return ORBIS_OK; } @@ -1649,10 +1845,14 @@ s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { } s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h) { - if (w <= 0.0f || h <= 0.0f) { + if (!fontHandle || w <= 0.0f || h <= 0.0f) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto& st = GetState(fontHandle); + auto* st_ptr = TryGetState(fontHandle); + if (!st_ptr) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + auto& st = *st_ptr; st.scale_w = w; st.scale_h = h; if (st.ext_face_ready) diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 7846232ec..84c7230d4 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -204,11 +204,15 @@ s32 PS4_SYSV_ABI sceFontControl(); s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); -s32 PS4_SYSV_ABI sceFontCreateLibrary(); +s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, OrbisFontLibCreateParams create_params, u64 edition, OrbisFontLib* pLibrary); -s32 PS4_SYSV_ABI sceFontCreateRenderer(); +s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + OrbisFontRenderer* pRenderer); s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer); @@ -219,8 +223,8 @@ s32 PS4_SYSV_ABI sceFontDefineAttribute(); s32 PS4_SYSV_ABI sceFontDeleteGlyph(); s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); -s32 PS4_SYSV_ABI sceFontDestroyLibrary(); -s32 PS4_SYSV_ABI sceFontDestroyRenderer(); +s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary); +s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer); s32 PS4_SYSV_ABI sceFontDestroyString(); s32 PS4_SYSV_ABI sceFontDestroyWords(); s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); @@ -244,14 +248,15 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 OrbisFontKerning* kerning); s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontGetPixelResolution(); -s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, + OrbisFontGlyphMetrics* out_metrics); s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h); -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h); +s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); s32 PS4_SYSV_ABI sceFontGetScalePoint(); s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); @@ -320,11 +325,13 @@ s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); -s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* fontMemory, void* address, u32 sizeByte, - const OrbisFontMemInterface* memInterface, void* mspaceObject, - OrbisFontMemDestroyCb destroyCallback, void* destroyObject); -s32 PS4_SYSV_ABI sceFontMemoryTerm(); -s32 PS4_SYSV_ABI sceFontOpenFontFile(); +s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u32 region_size, + const OrbisFontMemInterface* iface, void* mspace_obj, + OrbisFontMemDestroyCb destroy_cb, void* destroy_ctx); +s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc); +s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, + const OrbisFontOpenParams* open_detail, + OrbisFontHandle* out_handle); s32 PS4_SYSV_ABI sceFontOpenFontInstance(); s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, const OrbisFontOpenParams* open_params, diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index 4a1dbb989..d997c46f3 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -8,6 +8,38 @@ namespace Libraries::FontFt { +namespace { +bool g_library_selected = false; +bool g_renderer_selected = false; + +constexpr OrbisFontLibrarySelection kDefaultLibrarySelection{0xF2000000u, 0, nullptr, nullptr}; + +static void* PS4_SYSV_ABI RendererCreateStub() { + LOG_ERROR(Lib_FontFt, "(STUBBED) renderer create called"); + return nullptr; +} + +static void PS4_SYSV_ABI RendererQueryStub() { + LOG_ERROR(Lib_FontFt, "(STUBBED) renderer query called"); +} + +static void PS4_SYSV_ABI RendererDestroyStub() { + LOG_ERROR(Lib_FontFt, "(STUBBED) renderer destroy called"); +} + +static OrbisFontRendererSelection MakeRendererSelection() { + OrbisFontRendererSelection sel{}; + sel.magic = 0xF2000000u; + sel.size = 0x168; + sel.create_fn = reinterpret_cast(&RendererCreateStub); + sel.query_fn = reinterpret_cast(&RendererQueryStub); + sel.destroy_fn = reinterpret_cast(&RendererDestroyStub); + return sel; +} + +static const OrbisFontRendererSelection kDefaultRendererSelection = MakeRendererSelection(); +} // namespace + s32 PS4_SYSV_ABI sceFontFtInitAliases() { LOG_ERROR(Lib_FontFt, "(STUBBED) called"); return ORBIS_OK; @@ -103,14 +135,28 @@ s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSelectLibraryFt() { - LOG_ERROR(Lib_FontFt, "(STUBBED) called"); - return ORBIS_OK; +const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value) { + if (!g_library_selected) { + g_library_selected = true; + LOG_INFO(Lib_FontFt, "SelectLibraryFt: using default FreeType shim"); + } + LOG_INFO(Lib_FontFt, "SelectLibraryFt: value={}", value); + if (value == 0) { + return &kDefaultLibrarySelection; + } + return nullptr; } -s32 PS4_SYSV_ABI sceFontSelectRendererFt() { - LOG_ERROR(Lib_FontFt, "(STUBBED) called"); - return ORBIS_OK; +const OrbisFontRendererSelection* PS4_SYSV_ABI sceFontSelectRendererFt(int value) { + if (!g_renderer_selected) { + g_renderer_selected = true; + LOG_INFO(Lib_FontFt, "SelectRendererFt: using stb_truetype renderer backend"); + } + LOG_INFO(Lib_FontFt, "SelectRendererFt: value={}", value); + if (value == 0) { + return &kDefaultRendererSelection; + } + return nullptr; } void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { @@ -137,4 +183,4 @@ void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", sceFontSelectRendererFt); }; -} // namespace Libraries::FontFt \ No newline at end of file +} // namespace Libraries::FontFt diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h index cec6d7872..a94e83102 100644 --- a/src/core/libraries/font/fontft.h +++ b/src/core/libraries/font/fontft.h @@ -11,6 +11,21 @@ class SymbolsResolver; namespace Libraries::FontFt { +struct OrbisFontLibrarySelection { + u32 magic; + u32 reserved; + void* reserved_ptr1; + void* reserved_ptr2; +}; + +struct OrbisFontRendererSelection { + u32 magic; + u32 size; + uintptr_t create_fn; + uintptr_t query_fn; + uintptr_t destroy_fn; +}; + s32 PS4_SYSV_ABI sceFontFtInitAliases(); s32 PS4_SYSV_ABI sceFontFtSetAliasFont(); s32 PS4_SYSV_ABI sceFontFtSetAliasPath(); @@ -30,8 +45,8 @@ s32 PS4_SYSV_ABI sceFontFtSupportType42(); s32 PS4_SYSV_ABI sceFontFtSupportWinFonts(); s32 PS4_SYSV_ABI sceFontFtTermAliases(); s32 PS4_SYSV_ABI sceFontSelectGlyphsFt(); -s32 PS4_SYSV_ABI sceFontSelectLibraryFt(); -s32 PS4_SYSV_ABI sceFontSelectRendererFt(); +const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value); +const OrbisFontRendererSelection* PS4_SYSV_ABI sceFontSelectRendererFt(int value); void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::FontFt \ No newline at end of file +} // namespace Libraries::FontFt From 2afcdb981c6c4bcb56ffd7085f5d46335b2af735 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 7 Nov 2025 08:40:26 +0200 Subject: [PATCH 19/51] restored accidentally deleted stubbs. --- src/core/libraries/font/font.cpp | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 437eddb8f..400f442f6 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -513,6 +513,41 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, return ORBIS_OK; } +s32 PS4_SYSV_ABI sceFontCreateString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDeleteGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { if (!pRenderer || !*pRenderer) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; From 850f40f61ae7800c9893c850e4860348ba55bfa1 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 8 Nov 2025 13:08:20 +0200 Subject: [PATCH 20/51] Add system font path configuration and loading functionality Config example: [General] ... sysFontPath = "/.../NotoSansJP-Regular.ttf" ... --- src/common/config.cpp | 11 + src/common/config.h | 2 + src/core/libraries/font/font.cpp | 333 ++++++++++++++++++++++++++++--- src/core/libraries/font/font.h | 11 +- 4 files changed, 323 insertions(+), 34 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 4d3e1d877..1e032101c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -144,6 +144,7 @@ static ConfigEntry isSideTrophy("right"); static ConfigEntry isConnectedToNetwork(false); static bool enableDiscordRPC = false; static std::filesystem::path sys_modules_path = {}; +static std::filesystem::path sys_font_path = {}; // Input static ConfigEntry cursorState(HideCursorState::Idle); @@ -242,6 +243,14 @@ void setSysModulesPath(const std::filesystem::path& path) { sys_modules_path = path; } +std::filesystem::path getSysFontPath() { + return sys_font_path; +} + +void setSysFontPath(const std::filesystem::path& path) { + sys_font_path = path; +} + int getVolumeSlider() { return volumeSlider.get(); } @@ -880,6 +889,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) { isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific); defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific); sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path); + sys_font_path = toml::find_fs_path_or(general, "sysFontPath", sys_font_path); } if (data.contains("Input")) { @@ -1149,6 +1159,7 @@ void save(const std::filesystem::path& path, bool is_game_specific) { // Non game-specific entries data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data}; + data["General"]["sysFontPath"] = string{fmt::UTF(sys_font_path.u8string()).data}; data["GUI"]["installDirs"] = install_dirs; data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; diff --git a/src/common/config.h b/src/common/config.h index 5c9f89ae6..485d7db75 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -148,6 +148,8 @@ void setConnectedToNetwork(bool enable, bool is_game_specific = false); void setUserName(const std::string& name, bool is_game_specific = false); std::filesystem::path getSysModulesPath(); void setSysModulesPath(const std::filesystem::path& path); +std::filesystem::path getSysFontPath(); +void setSysFontPath(const std::filesystem::path& path); bool getLoadAutoPatches(); void setLoadAutoPatches(bool enable); diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 400f442f6..fbf50ac8e 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -11,11 +11,15 @@ #include #include #include +#include #include #include +#include #include #include #include +#include +#include "common/config.h" #include "common/logging/log.h" #include "common/singleton.h" #include "core/file_sys/fs.h" @@ -50,6 +54,11 @@ struct GlyphEntry { struct FontState { float scale_w = 16.0f; float scale_h = 16.0f; + float scale_point_w = 12.0f; + float scale_point_h = 12.0f; + bool scale_point_active = false; + u32 dpi_x = 72; + u32 dpi_y = 72; Libraries::Font::OrbisFontLib library = nullptr; bool ext_face_ready = false; std::vector ext_face_data; @@ -213,6 +222,22 @@ static float ComputeScaleExt(const stbtt_fontinfo* face, float pixel_h) { return stbtt_ScaleForPixelHeight(face, pixel_h); } +static constexpr float kPointsPerInch = 72.0f; +static constexpr float kScaleEpsilon = 1e-4f; + +static float SafeDpiToFloat(u32 dpi) { + return dpi == 0 ? kPointsPerInch : static_cast(dpi); +} + +static float PointsToPixels(float pt, u32 dpi) { + return pt * SafeDpiToFloat(dpi) / kPointsPerInch; +} + +static float PixelsToPoints(float px, u32 dpi) { + const float dpi_f = SafeDpiToFloat(dpi); + return px * kPointsPerInch / dpi_f; +} + static void UpdateCachedLayout(FontState& st) { if (!st.ext_face_ready) { return; @@ -226,14 +251,6 @@ static void UpdateCachedLayout(FontState& st) { st.layout_cached = true; } -static bool ReportSystemFaceRequest(FontState& st) { - if (!st.system_requested) { - st.system_requested = true; - LOG_ERROR(Lib_Font, "SystemFace: internal PS4 font requested but not available"); - } - return false; -} - static std::unordered_set g_logged_pua; static inline std::uint64_t MakeGlyphKey(u32 code, int pixel_h) { @@ -255,6 +272,94 @@ static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* match); } } + +static std::once_flag g_system_font_once; +static bool g_system_font_available = false; +static std::vector g_system_font_bytes; +static std::filesystem::path g_system_font_path; + +static bool LoadFontFromPath(const std::filesystem::path& path) { + if (path.empty()) { + return false; + } + if (!LoadGuestFileBytes(path, g_system_font_bytes) || g_system_font_bytes.empty()) { + return false; + } + g_system_font_available = true; + g_system_font_path = path; + LOG_INFO(Lib_Font, "SystemFace: fallback font '{}' loaded ({} bytes)", + g_system_font_path.string(), g_system_font_bytes.size()); + return true; +} + +static void LoadSystemFontBlob() { + const auto configured_path = Config::getSysFontPath(); + if (!configured_path.empty()) { + const auto resolved = configured_path.is_absolute() + ? configured_path + : std::filesystem::current_path() / configured_path; + if (LoadFontFromPath(resolved)) { + return; + } + } + const auto fallback = std::filesystem::current_path() / kSystemFontFileName; + LoadFontFromPath(fallback); +} + +static bool EnsureSystemFontBlob() { + std::call_once(g_system_font_once, LoadSystemFontBlob); + return g_system_font_available; +} + +static bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle handle) { + if (st.ext_face_ready) { + return true; + } + if (!EnsureSystemFontBlob()) { + return false; + } + st.ext_face_data = g_system_font_bytes; + st.ext_cache.clear(); + st.ext_scale_for_height = 0.0f; + st.layout_cached = false; + st.logged_ext_use = true; // skip "external (game font)" log for built-in fallback + const int offset = stbtt_GetFontOffsetForIndex(st.ext_face_data.data(), 0); + if (offset < 0) { + LOG_ERROR(Lib_Font, "SystemFace: invalid font offset in '{}'", g_system_font_path.string()); + st.ext_face_data.clear(); + st.ext_face_ready = false; + return false; + } + if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset) == 0) { + LOG_ERROR(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", + g_system_font_path.string()); + st.ext_face_data.clear(); + st.ext_face_ready = false; + return false; + } + st.ext_face_ready = true; + LOG_INFO(Lib_Font, "SystemFace: handle={} now uses '{}'", static_cast(handle), + g_system_font_path.string()); + return true; +} + +static std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, + bool& attached_out) { + attached_out = AttachSystemFont(st, handle); + if (attached_out) { + st.system_requested = true; + return {}; + } + if (!st.system_requested) { + st.system_requested = true; + const auto configured = Config::getSysFontPath(); + return fmt::format( + "SystemFace: handle={} requested internal font but sysFontPath ('{}') could not be " + "loaded", + static_cast(handle), configured.string()); + } + return {}; +} } // namespace namespace Libraries::Font { @@ -296,6 +401,8 @@ static std::uint8_t g_external_fonts_ctx_stub{}; static u32 g_device_cache_stub{}; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { + LOG_INFO(Lib_Font, "AttachDeviceCacheBuffer(begin): library={} buffer={} size={}", + static_cast(library), static_cast(buffer), size); LOG_ERROR(Lib_Font, "(STUBBED) called library={} buffer={} size={}", static_cast(library), static_cast(buffer), size); return ORBIS_OK; @@ -620,8 +727,19 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code } } - if (!face) - ReportSystemFaceRequest(st); + if (!face) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face = &st.ext_face; + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + scale = st.ext_scale_for_height; + } + } if (face) { const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); @@ -720,6 +838,13 @@ s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; + if (!st.ext_face_ready) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + } if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); @@ -736,7 +861,6 @@ s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, layout->lineAdvance, scale); return ORBIS_OK; } - ReportSystemFaceRequest(st); layout->baselineOffset = st.scale_h * 0.8f; layout->lineAdvance = st.scale_h; layout->decorationExtent = 0.0f; @@ -755,6 +879,13 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); + if (!st.ext_face_ready) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + } if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); @@ -772,7 +903,6 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 return ORBIS_OK; } } - ReportSystemFaceRequest(st); kerning->dx = 0.0f; kerning->dy = 0.0f; kerning->px = 0.0f; @@ -851,7 +981,7 @@ s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* o s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h) { - return sceFontGetRenderScalePixel(fontHandle, out_w, out_h); + return sceFontGetScalePoint(fontHandle, out_w, out_h); } s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { @@ -877,8 +1007,25 @@ s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h) { + if (!fontHandle || (!out_w && !out_h)) { + LOG_DEBUG(Lib_Font, "sceFontGetScalePoint: invalid params"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* st = TryGetState(fontHandle); + if (!st) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + const float default_w = PixelsToPoints(st->scale_w, st->dpi_x); + const float default_h = PixelsToPoints(st->scale_h, st->dpi_y); + const float point_w = st->scale_point_active ? st->scale_point_w : default_w; + const float point_h = st->scale_point_active ? st->scale_point_h : default_h; + if (out_w) + *out_w = point_w; + if (out_h) + *out_h = point_h; + LOG_DEBUG(Lib_Font, "GetScalePoint: handle={} -> w={}pt h={}pt (active={})", + static_cast(fontHandle), point_w, point_h, st->scale_point_active); return ORBIS_OK; } @@ -898,6 +1045,13 @@ s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); + if (!st.ext_face_ready) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + } if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); @@ -908,7 +1062,6 @@ s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, layout->decorationSpan = 0.0f; return ORBIS_OK; } - ReportSystemFaceRequest(st); layout->baselineOffsetX = 0.0f; layout->columnAdvance = st.scale_h; layout->decorationSpan = 0.0f; @@ -1295,8 +1448,50 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat open_detail, out_handle); } -s32 PS4_SYSV_ABI sceFontOpenFontInstance() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, + OrbisFontHandle* pFontHandle) { + if (!templateFont || !pFontHandle) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* src_state = TryGetState(templateFont); + if (!src_state) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + FontState* base_state = TryGetState(fontHandle); + if (!base_state && fontHandle) { + LOG_WARNING(Lib_Font, "OpenFontInstance: base handle={} unknown, falling back to template", + static_cast(fontHandle)); + } + auto* new_handle = new (std::nothrow) OrbisFontHandleOpaque{}; + if (!new_handle) { + LOG_ERROR(Lib_Font, "OpenFontInstance: allocation failed"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + auto& dst = GetState(new_handle); + dst = *src_state; + dst.library = base_state ? base_state->library : src_state->library; + dst.bound_renderer = base_state ? base_state->bound_renderer : src_state->bound_renderer; + dst.logged_ext_use = false; + if (dst.ext_face_ready && !dst.ext_face_data.empty()) { + const int font_offset = src_state->ext_face.fontstart; + if (stbtt_InitFont(&dst.ext_face, dst.ext_face_data.data(), font_offset) == 0) { + LOG_WARNING(Lib_Font, + "OpenFontInstance: stbtt_InitFont failed when cloning handle={} -> new={}", + static_cast(templateFont), + static_cast(new_handle)); + dst.ext_face_ready = false; + dst.ext_cache.clear(); + } else { + dst.ext_scale_for_height = ComputeScaleExt(&dst.ext_face, dst.scale_h); + } + } + dst.layout_cached = false; + *pFontHandle = new_handle; + LOG_INFO(Lib_Font, + "OpenFontInstance: base={} template={} -> new={} ext_ready={} renderer_inherited={}", + static_cast(fontHandle), static_cast(templateFont), + static_cast(new_handle), dst.ext_face_ready, + dst.bound_renderer != nullptr); return ORBIS_OK; } @@ -1396,7 +1591,11 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o *pFontHandle = f; auto& st = GetState(f); st.library = library; - const bool system_ok = ReportSystemFaceRequest(st); + bool system_ok = false; + const std::string sys_log = ReportSystemFaceRequest(st, f, system_ok); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } LOG_INFO(Lib_Font, "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system " @@ -1474,8 +1673,19 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl } } - if (!face) - ReportSystemFaceRequest(st); + if (!face) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face = &st.ext_face; + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + scale = st.ext_scale_for_height; + } + } const float frac_x = x - std::floor(x); const float frac_y = y - std::floor(y); @@ -1874,8 +2084,27 @@ s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi) { + if (!fontHandle) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + auto* st_ptr = TryGetState(fontHandle); + if (!st_ptr) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + auto& st = *st_ptr; + const u32 new_h = h_dpi == 0 ? 0x48u : h_dpi; + const u32 new_v = v_dpi == 0 ? 0x48u : v_dpi; + if (st.dpi_x == new_h && st.dpi_y == new_v) { + LOG_TRACE(Lib_Font, "SetResolutionDpi: handle={} unchanged h_dpi={} v_dpi={}", + static_cast(fontHandle), new_h, new_v); + return ORBIS_OK; + } + st.dpi_x = new_h; + st.dpi_y = new_v; + st.layout_cached = false; // PS4 clears cached metrics when the resolution changes. + LOG_INFO(Lib_Font, "SetResolutionDpi: handle={} h_dpi={} v_dpi={}", + static_cast(fontHandle), new_h, new_v); return ORBIS_OK; } @@ -1888,11 +2117,16 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; + st.scale_point_active = false; st.scale_w = w; st.scale_h = h; if (st.ext_face_ready) st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); - ReportSystemFaceRequest(st); + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } LOG_INFO(Lib_Font, "SetScalePixel: handle={} w={} h={} ext_scale={} ext_ready={}", static_cast(fontHandle), w, h, st.ext_scale_for_height, st.ext_face_ready); @@ -1900,8 +2134,44 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h) { + if (!fontHandle || w <= 0.0f || h <= 0.0f) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* st_ptr = TryGetState(fontHandle); + if (!st_ptr) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + auto& st = *st_ptr; + const float pixel_w = std::max(0.01f, PointsToPixels(w, st.dpi_x)); + const float pixel_h = std::max(0.01f, PointsToPixels(h, st.dpi_y)); + const bool unchanged_point = st.scale_point_active && + std::abs(st.scale_point_w - w) < kScaleEpsilon && + std::abs(st.scale_point_h - h) < kScaleEpsilon; + const bool unchanged_pixels = std::abs(st.scale_w - pixel_w) < kScaleEpsilon && + std::abs(st.scale_h - pixel_h) < kScaleEpsilon; + if (unchanged_point && unchanged_pixels) { + LOG_TRACE(Lib_Font, "SetScalePoint: handle={} unchanged point=({}, {})", + static_cast(fontHandle), w, h); + return ORBIS_OK; + } + st.scale_point_active = true; + st.scale_point_w = w; + st.scale_point_h = h; + st.scale_w = pixel_w; + st.scale_h = pixel_h; + if (st.ext_face_ready) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + st.layout_cached = false; + LOG_INFO(Lib_Font, + "SetScalePoint: handle={} point=({}, {}) -> pixel=({}, {}) dpi=({}, {}) attached={}", + static_cast(fontHandle), w, h, st.scale_w, st.scale_h, st.dpi_x, st.dpi_y, + system_attached); return ORBIS_OK; } @@ -1932,9 +2202,11 @@ s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float return rc; } -s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h) { + auto rc = sceFontSetScalePoint(fontHandle, w, h); + LOG_INFO(Lib_Font, "SetupRenderScalePoint: handle={} w={} h={}", + static_cast(fontHandle), w, h); + return rc; } s32 PS4_SYSV_ABI sceFontStringGetTerminateCode() { @@ -2058,6 +2330,8 @@ s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { } s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats) { + LOG_INFO(Lib_Font, "SupportExternalFonts(begin): lib={} fontMax={} formats=0x{:X}", + static_cast(library), fontMax, formats); auto& ls = GetLibState(library); ls.support_external = true; ls.external_fontMax = fontMax; @@ -2074,6 +2348,7 @@ s32 PS4_SYSV_ABI sceFontSupportGlyphs() { } s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { + LOG_INFO(Lib_Font, "SupportSystemFonts(begin): lib={}", static_cast(library)); auto& ls = GetLibState(library); ls.support_system = true; LOG_INFO(Lib_Font, "SupportSystemFonts: lib={} system=on", static_cast(library)); diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 84c7230d4..75f070d24 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -257,7 +257,7 @@ s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* o s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); -s32 PS4_SYSV_ABI sceFontGetScalePoint(); +s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, @@ -332,7 +332,8 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc); s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, const OrbisFontOpenParams* open_detail, OrbisFontHandle* out_handle); -s32 PS4_SYSV_ABI sceFontOpenFontInstance(); +s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, + OrbisFontHandle* pFontHandle); s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle); @@ -362,15 +363,15 @@ s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* rende s32 PS4_SYSV_ABI sceFontSetEffectSlant(); s32 PS4_SYSV_ABI sceFontSetEffectWeight(); s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); -s32 PS4_SYSV_ABI sceFontSetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi); s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontSetScalePoint(); +s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); From 0b9f941e63e6b501788d7e20b36d0532efe3d4e1 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 8 Nov 2025 13:27:18 +0200 Subject: [PATCH 21/51] Handle missing system font by clearing font bytes and logging an error --- src/core/libraries/font/font.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index fbf50ac8e..5d2468257 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -302,8 +302,10 @@ static void LoadSystemFontBlob() { return; } } - const auto fallback = std::filesystem::current_path() / kSystemFontFileName; - LoadFontFromPath(fallback); + g_system_font_bytes.clear(); + g_system_font_available = false; + LOG_ERROR(Lib_Font, "SystemFace: configured font '{}' missing; no fallback available", + configured_path.string()); } static bool EnsureSystemFontBlob() { From b35d82e8f3f2dd1b21566ddf36d884b56b1e8e04 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 8 Nov 2025 13:41:32 +0200 Subject: [PATCH 22/51] Remove unused include from font.cpp possible MacOS build crash reason. --- src/core/libraries/font/font.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 5d2468257..88035d398 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include From 25d41c3a54cbd1af50b4536e98678760764d2311 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Mon, 10 Nov 2025 15:30:40 +0200 Subject: [PATCH 23/51] fontlib: refactor style frame API; config-driven system fonts -Update style frame getters to take the frame explicitly: --sceFontStyleFrameGetResolutionDpi(OrbisFontStyleFrame*, u32* h_dpi, u32* v_dpi) --sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame*, float* w, float* h) --sceFontStyleFrameGetScalePoint(OrbisFontStyleFrame*, float* w, float* h) -Ensure setters operate directly on the passed style frame (signatures unified): --sceFontStyleFrameSetEffectSlant(OrbisFontStyleFrame*, float slantRatio) --sceFontStyleFrameSetEffectWeight(OrbisFontStyleFrame*, float weightXScale, float weightYScale, u32 mode) --sceFontStyleFrameSetResolutionDpi(OrbisFontStyleFrame*, u32 h_dpi, u32 v_dpi) --sceFontStyleFrameSetScalePixel(OrbisFontStyleFrame*, float w, float h) --sceFontStyleFrameSetScalePoint(OrbisFontStyleFrame*, float w, float h) -Remove/streamline unused declarations from font.h to tighten the interface -Call sites using style frame getters must pass an OrbisFontStyleFrame* now Config: define system fonts path and default filename overrides -Add [General].sysFontPath to set the base directory for system fonts -Add [SystemFonts]: --fallback = "SST-Roman.otf" (lowercase key) for the default face when no set is requested --Per-font overrides using lowerCamelCase keys (start with fontSet...), e.g.: ---fontSetSstStdJapaneseJpArBold = "SSTJpPro-Bold.otf" --Override values are filenames resolved under sysFontPath (paths are rejected and logged) -Back-compat: still accept legacy SysFontPath and Fallback if present -Logging: emit errors when sysFontPath is missing/invalid, fallback missing, or an override includes a path -Configs should switch to sysFontPath and [SystemFonts].fallback with override keys --- src/common/config.cpp | 90 ++- src/common/config.h | 7 + src/core/libraries/font/font.cpp | 785 +++++++++++++++++++++++--- src/core/libraries/font/font.h | 918 ++++++++++++++++--------------- 4 files changed, 1268 insertions(+), 532 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 1e032101c..a9b3d0c45 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,8 +4,9 @@ #include #include #include +#include +#include #include -#include // for wstring support #include #include "common/assert.h" @@ -145,6 +146,8 @@ static ConfigEntry isConnectedToNetwork(false); static bool enableDiscordRPC = false; static std::filesystem::path sys_modules_path = {}; static std::filesystem::path sys_font_path = {}; +static std::string sys_font_fallback_name = {}; +static std::unordered_map system_font_overrides; // Input static ConfigEntry cursorState(HideCursorState::Idle); @@ -251,6 +254,36 @@ void setSysFontPath(const std::filesystem::path& path) { sys_font_path = path; } +std::optional getSystemFontOverride(std::string_view key) { + if (key.empty()) { + return std::nullopt; + } + auto it = system_font_overrides.find(std::string(key)); + if (it == system_font_overrides.end()) { + return std::nullopt; + } + return it->second; +} + +std::string getSystemFontFallbackName() { + return sys_font_fallback_name; +} + +void setSystemFontFallbackName(const std::string& name) { + sys_font_fallback_name = name; +} + +void setSystemFontOverride(std::string_view key, const std::filesystem::path& path) { + if (key.empty()) { + return; + } + system_font_overrides[std::string(key)] = path; +} + +void clearSystemFontOverrides() { + system_font_overrides.clear(); +} + int getVolumeSlider() { return volumeSlider.get(); } @@ -866,6 +899,10 @@ void load(const std::filesystem::path& path, bool is_game_specific) { return; } + if (!is_game_specific) { + system_font_overrides.clear(); + } + if (data.contains("General")) { const toml::value& general = data.at("General"); @@ -889,7 +926,43 @@ void load(const std::filesystem::path& path, bool is_game_specific) { isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific); defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific); sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path); + // Accept alias without trailing 's' + sys_modules_path = toml::find_fs_path_or(general, "sysModulePath", sys_modules_path); + // Prefer 'sysFontPath'; accept 'SysFontPath' for compatibility sys_font_path = toml::find_fs_path_or(general, "sysFontPath", sys_font_path); + sys_font_path = toml::find_fs_path_or(general, "SysFontPath", sys_font_path); + } + + if (data.contains("SystemFonts")) { + const toml::value& fonts = data.at("SystemFonts"); + if (fonts.is_table()) { + // Read fallback (lowercase preferred), accept 'Fallback'/'FallbackFontName' for compat + if (fonts.contains("fallback")) { + const auto& v = fonts.at("fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("Fallback")) { + const auto& v = fonts.at("Fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("FallbackFontName")) { + const auto& v = fonts.at("FallbackFontName"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } + for (const auto& [name, value] : fonts.as_table()) { + if (name == "fallback" || name == "Fallback" || name == "FallbackFontName") { + continue; + } + if (value.is_string()) { + system_font_overrides[name] = + std::filesystem::path(toml::get(value)); + } + } + } } if (data.contains("Input")) { @@ -1159,7 +1232,22 @@ void save(const std::filesystem::path& path, bool is_game_specific) { // Non game-specific entries data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data}; + // Save using 'sysFontPath' to match style data["General"]["sysFontPath"] = string{fmt::UTF(sys_font_path.u8string()).data}; + { + toml::table fonts_table; + if (!sys_font_fallback_name.empty()) { + fonts_table["fallback"] = sys_font_fallback_name; + } + for (const auto& [name, path_override] : system_font_overrides) { + fonts_table[name] = string{fmt::UTF(path_override.u8string()).data}; + } + if (!fonts_table.empty()) { + data["SystemFonts"] = fonts_table; + } else if (data.is_table()) { + data.as_table().erase("SystemFonts"); + } + } data["GUI"]["installDirs"] = install_dirs; data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; diff --git a/src/common/config.h b/src/common/config.h index 485d7db75..b91fa2760 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,6 +4,8 @@ #pragma once #include +#include +#include #include #include "types.h" @@ -150,6 +152,11 @@ std::filesystem::path getSysModulesPath(); void setSysModulesPath(const std::filesystem::path& path); std::filesystem::path getSysFontPath(); void setSysFontPath(const std::filesystem::path& path); +std::optional getSystemFontOverride(std::string_view key); +std::string getSystemFontFallbackName(); +void setSystemFontFallbackName(const std::string& name); +void setSystemFontOverride(std::string_view key, const std::filesystem::path& path); +void clearSystemFontOverrides(); bool getLoadAutoPatches(); void setLoadAutoPatches(bool enable); diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 88035d398..b6853dc7c 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -13,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -38,6 +42,8 @@ struct FontLibOpaque; namespace { Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); +using Libraries::Font::OrbisFontStyleFrame; + struct GlyphEntry { std::vector bitmap; int w = 0; @@ -58,6 +64,8 @@ struct FontState { bool scale_point_active = false; u32 dpi_x = 72; u32 dpi_y = 72; + u32 font_set_type = 0; + std::filesystem::path system_font_path; Libraries::Font::OrbisFontLib library = nullptr; bool ext_face_ready = false; std::vector ext_face_data; @@ -87,6 +95,8 @@ struct LibraryState { Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; u64 edition = 0; Libraries::Font::FontLibOpaque* native = nullptr; + void* owned_device_cache = nullptr; + u32 owned_device_cache_size = 0; }; static std::unordered_map g_library_state; @@ -112,13 +122,33 @@ static LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { } static void RemoveLibState(Libraries::Font::OrbisFontLib lib) { - g_library_state.erase(lib); + if (auto it = g_library_state.find(lib); it != g_library_state.end()) { + if (it->second.owned_device_cache) { + delete[] static_cast(it->second.owned_device_cache); + } + g_library_state.erase(it); + } } static void LogExternalFormatSupport(u32 formats_mask) { LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); } +static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); + +static void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params) { + if (!params) { + LOG_INFO(Lib_Font, "OpenFontSetParams: "); + return; + } + LOG_INFO( + Lib_Font, + "OpenFontSetParams: tag=0x{:04X} flags=0x{:X} subfont={} unique_id={} reserved_ptrs=[{}, " + "{}]", + params->tag, params->flags, params->subfont_index, params->unique_id, params->reserved_ptr1, + params->reserved_ptr2); +} + static std::filesystem::path ResolveGuestPath(const char* guest_path) { if (!guest_path) { return {}; @@ -237,6 +267,92 @@ static float PixelsToPoints(float px, u32 dpi) { return px * kPointsPerInch / dpi_f; } +constexpr u16 kStyleFrameMagic = 0x0F09; +constexpr u16 kStyleFrameFlagScale = 1 << 0; +constexpr u16 kStyleFrameFlagWeight = 1 << 1; +constexpr u16 kStyleFrameFlagSlant = 1 << 2; +constexpr u16 kStyleFrameFlagDpi = 1 << 3; + +enum class StyleFrameScalingMode : s32 { + None = 0, + Point = 1, + Pixel = 2, +}; + +struct StyleFrameScaleState { + float scale_w; + float scale_h; + u32 dpi_x; + u32 dpi_y; + bool scale_overridden; + bool dpi_overridden; +}; + +static StyleFrameScaleState ResolveStyleFrameScale(const OrbisFontStyleFrame* style, + const FontState& st) { + StyleFrameScaleState resolved{ + .scale_w = st.scale_w, + .scale_h = st.scale_h, + .dpi_x = st.dpi_x, + .dpi_y = st.dpi_y, + .scale_overridden = false, + .dpi_overridden = false, + }; + if (!style || style->magic != kStyleFrameMagic) { + return resolved; + } + if ((style->flags & kStyleFrameFlagDpi) != 0) { + if (style->dpiX > 0) { + resolved.dpi_x = static_cast(style->dpiX); + resolved.dpi_overridden = true; + } + if (style->dpiY > 0) { + resolved.dpi_y = static_cast(style->dpiY); + resolved.dpi_overridden = true; + } + } + if ((style->flags & kStyleFrameFlagScale) != 0 && style->scaleWidth > 0.0f && + style->scaleHeight > 0.0f) { + const auto mode = static_cast(style->scalingFlag); + if (mode == StyleFrameScalingMode::Point) { + resolved.scale_w = PointsToPixels(style->scaleWidth, resolved.dpi_x); + resolved.scale_h = PointsToPixels(style->scaleHeight, resolved.dpi_y); + resolved.scale_overridden = true; + } else if (mode == StyleFrameScalingMode::Pixel) { + resolved.scale_w = style->scaleWidth; + resolved.scale_h = style->scaleHeight; + resolved.scale_overridden = true; + } + } + return resolved; +} + +static void InitializeStyleFrame(OrbisFontStyleFrame& frame) { + frame.magic = kStyleFrameMagic; + frame.flags = 0; + frame.dpiX = 72; + frame.dpiY = 72; + frame.scalingFlag = static_cast(StyleFrameScalingMode::None); + frame.scaleWidth = 0.0f; + frame.scaleHeight = 0.0f; + frame.weightXScale = 1.0f; + frame.weightYScale = 1.0f; + frame.slantRatio = 0.0f; +} + +static bool EnsureStyleFrameInitialized(OrbisFontStyleFrame* frame) { + if (!frame) + return false; + if (frame->magic != kStyleFrameMagic) { + InitializeStyleFrame(*frame); + } + return true; +} + +static bool ValidateStyleFramePtr(const OrbisFontStyleFrame* frame) { + return frame && frame->magic == kStyleFrameMagic; +} + static void UpdateCachedLayout(FontState& st) { if (!st.ext_face_ready) { return; @@ -272,16 +388,273 @@ static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* } } +// Font-set ID bit layout: +// bits [31:28] family (0x18 = SST Standard UI) +// bits [27:24] region (0x0 Latin, 0x8 Japanese, 0xC Chinese, 0xD Korean, 0xE Thai, 0xF +// Asian-mix) bits [23:20] variant (0x0 European, 0x2 Typewriter, 0x4 JP, 0x7 EU JPCJK, 0x8 JP +// UH, 0xC GB UH) bits [19:16] coverage (0x0 base, 0x4 Arabic, 0x5 Vietnamese, 0x6 Thai, 0xA CJK +// TH, 0xB full-set) bits [15:8] language tag (0x44 JP, 0x53 VI, 0x54 TH, 0x80 GB, 0xA CJK TH, 0xA4 +// JP CJK, 0xC4 JP AR) bits [7:0] weight/style (0x43 light, 0x44 regular, 0x45 medium, 0x47 bold, +// 0x53 light VI, 0x57 bold VI, 0xC7 Arabic bold) +struct SystemFontDefinition { + u32 font_set_type; + const char* config_key; // legacy UPPER_SNAKE (e.g., FONTSET_SST_STD_EUROPEAN_LIGHT) + const char* default_file; // default filename within sys font dir +}; + +static constexpr SystemFontDefinition kSystemFontDefinitions[] = { + {0x18070043, "FONTSET_SST_STD_EUROPEAN_LIGHT", "SST-Light.otf"}, + {0x18070044, "FONTSET_SST_STD_EUROPEAN", "SST-Roman.otf"}, + {0x18070045, "FONTSET_SST_STD_EUROPEAN_MEDIUM", "SST-Medium.otf"}, + {0x18070047, "FONTSET_SST_STD_EUROPEAN_BOLD", "SST-Bold.otf"}, + {0x18070053, "FONTSET_SST_STD_VIETNAMESE_LIGHT", "SSTVietnamese-Light.otf"}, + {0x18070054, "FONTSET_SST_STD_VIETNAMESE", "SSTVietnamese-Roman.otf"}, + {0x18070055, "FONTSET_SST_STD_VIETNAMESE_MEDIUM", "SSTVietnamese-Medium.otf"}, + {0x18070057, "FONTSET_SST_STD_VIETNAMESE_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180700C3, "FONTSET_SST_STD_EUROPEAN_AR_LIGHT", "SSTArabic-Light.otf"}, + {0x180700C4, "FONTSET_SST_STD_EUROPEAN_AR", "SSTArabic-Roman.otf"}, + {0x180700C5, "FONTSET_SST_STD_EUROPEAN_AR_MEDIUM", "SSTArabic-Medium.otf"}, + {0x180700C7, "FONTSET_SST_STD_EUROPEAN_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18070444, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, + {0x18070447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x18070454, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, + {0x18070457, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180704C4, "FONTSET_SST_STD_EUROPEAN_JP_AR", "SSTArabic-Roman.otf"}, + {0x180704C7, "FONTSET_SST_STD_EUROPEAN_JP_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18071053, "FONTSET_SST_STD_THAI_LIGHT", "SSTThai-Light.otf"}, + {0x18071054, "FONTSET_SST_STD_THAI", "SSTThai-Roman.otf"}, + {0x18071055, "FONTSET_SST_STD_THAI_MEDIUM", "SSTThai-Medium.otf"}, + {0x18071057, "FONTSET_SST_STD_THAI_BOLD", "SSTThai-Bold.otf"}, + {0x18071454, "FONTSET_SST_STD_EUROPEAN_JP_TH", "SSTThai-Roman.otf"}, + {0x18071457, "FONTSET_SST_STD_EUROPEAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, + {0x18072444, "FONTSET_SST_STD_EUROPEAN_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x18072447, "FONTSET_SST_STD_EUROPEAN_JPUH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180724C4, "FONTSET_SST_STD_EUROPEAN_JPUH_AR", "SSTArabic-Roman.otf"}, + {0x180724C7, "FONTSET_SST_STD_EUROPEAN_JPUH_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18073454, "FONTSET_SST_STD_EUROPEAN_JPUH_TH", "SSTThai-Roman.otf"}, + {0x18073457, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, + {0x180734D4, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, + {0x180734D7, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, + {0x18078044, "FONTSET_SST_STD_EUROPEAN_GB", "DFHEI5-SONY.ttf"}, + {0x180780C4, "FONTSET_SST_STD_EUROPEAN_GB_AR", "SSTArabic-Roman.otf"}, + {0x18079054, "FONTSET_SST_STD_EUROPEAN_GB_TH", "SSTThai-Roman.otf"}, + {0x1807A044, "FONTSET_SST_STD_EUROPEAN_GBUH", "e046323ms.ttf"}, + {0x1807A0C4, "FONTSET_SST_STD_EUROPEAN_GBUH_AR", "SSTArabic-Bold.otf"}, + {0x1807A444, "FONTSET_SST_STD_EUROPEAN_JPCJK", "SSTJpPro-Regular.otf"}, + {0x1807A4C4, "FONTSET_SST_STD_EUROPEAN_JPCJK_AR", "SSTArabic-Roman.otf"}, + {0x1807AC44, "FONTSET_SST_STD_EUROPEAN_GBCJK", "n023055ms.ttf"}, + {0x1807ACC4, "FONTSET_SST_STD_EUROPEAN_GBCJK_AR", "SSTArabic-Bold.otf"}, + {0x1807B054, "FONTSET_SST_STD_EUROPEAN_GBUH_TH", "SSTThai-Roman.otf"}, + {0x1807B0D4, "FONTSET_SST_STD_EUROPEAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, + {0x1807B454, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH", "SSTThai-Roman.otf"}, + {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "SSTThai-Roman.otf"}, + {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTJpPro-Regular.otf"}, + {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTVietnamese-Roman.otf"}, + {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTJpPro-Regular.otf"}, + {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTThai-Roman.otf"}, + {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, + {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x18082447, "FONTSET_SST_STD_JAPANESE_JPUH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180824C4, "FONTSET_SST_STD_JAPANESE_JPUH_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180824C7, "FONTSET_SST_STD_JAPANESE_JPUH_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18083454, "FONTSET_SST_STD_ASIAN_JPUH_TH", "SSTThai-Roman.otf"}, + {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, + {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, + {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, + {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTJpPro-Regular.otf"}, + {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Bold.otf"}, + {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTThai-Roman.otf"}, + {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x180C8044, "FONTSET_SST_STD_SCHINESE_GB", "DFHEI5-SONY.ttf"}, + {0x180C80C4, "FONTSET_SST_STD_SCHINESE_GB_AR", "DFHEI5-SONY.ttf"}, + {0x180C9054, "FONTSET_SST_STD_ASIAN_GB_TH", "SSTThai-Roman.otf"}, + {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "e046323ms.ttf"}, + {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "e046323ms.ttf"}, + {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "n023055ms.ttf"}, + {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "n023055ms.ttf"}, + {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "SSTThai-Roman.otf"}, + {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, + {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "SSTThai-Roman.otf"}, + {0x180CBCD4, "FONTSET_SST_STD_ASIAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x18170043, "FONTSET_SST_STD_EUROPEAN_LIGHT_ITALIC", "SST-LightItalic.otf"}, + {0x18170044, "FONTSET_SST_STD_EUROPEAN_ITALIC", "SST-Italic.otf"}, + {0x18170045, "FONTSET_SST_STD_EUROPEAN_MEDIUM_ITALIC", "SST-MediumItalic.otf"}, + {0x18170047, "FONTSET_SST_STD_EUROPEAN_BOLD_ITALIC", "SST-BoldItalic.otf"}, + {0x18170444, "FONTSET_SST_STD_EUROPEAN_JP_ITALIC", "SSTJpPro-Regular.otf"}, + {0x18170447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD_ITALIC", "SSTJpPro-Bold.otf"}, + {0x18370044, "FONTSET_SST_TYPEWRITER_EUROPEAN", "SSTTypewriter-Roman.otf"}, + {0x18370047, "FONTSET_SST_TYPEWRITER_EUROPEAN_BOLD", "SSTTypewriter-Bd.otf"}, + {0x18370444, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP", "SSTTypewriter-Roman.otf"}, + {0x18370447, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP_BOLD", "SSTTypewriter-Bd.otf"}, +}; + +struct FontSetCache { + bool loaded = false; + bool available = false; + std::vector bytes; + std::filesystem::path path; +}; + +static std::mutex g_fontset_cache_mutex; +static std::unordered_map g_fontset_cache; + +static const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type) { + for (const auto& def : kSystemFontDefinitions) { + if (def.font_set_type == font_set_type) { + return &def; + } + } + return nullptr; +} + +static std::filesystem::path GetSysFontBaseDir() { + // Only use the configured path. No variations. + std::filesystem::path base = Config::getSysFontPath(); + std::error_code ec; + if (base.empty()) { + LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); + return {}; + } + if (std::filesystem::is_directory(base, ec)) { + return base; + } + if (std::filesystem::is_regular_file(base, ec)) { + return base.parent_path(); + } + LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath '{}' is not a valid directory or file", + base.string()); + return {}; +} + +static std::string MacroToCamel(const char* macro_key) { + if (!macro_key) { + return {}; + } + std::string s(macro_key); + // Expect prefix "FONTSET_" + const std::string prefix = "FONTSET_"; + if (s.rfind(prefix, 0) != 0) { + return {}; + } + std::string out = "FontSet"; + // Split on '_' + size_t pos = prefix.size(); + while (pos < s.size()) { + size_t next = s.find('_', pos); + const size_t len = (next == std::string::npos) ? (s.size() - pos) : (next - pos); + std::string token = s.substr(pos, len); + // Lowercase then capitalize first char + for (auto& c : token) { + c = static_cast(std::tolower(static_cast(c))); + } + if (!token.empty()) { + token[0] = static_cast(std::toupper(static_cast(token[0]))); + } + out += token; + if (next == std::string::npos) { + break; + } + pos = next + 1; + } + return out; +} + +static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { + if (const auto* def = FindSystemFontDefinition(font_set_type); def) { + const auto base_dir = GetSysFontBaseDir(); + if (base_dir.empty()) { + return {}; + } + if (auto override_path = Config::getSystemFontOverride(def->config_key)) { + if (!override_path->empty() && !override_path->is_absolute() && + !override_path->has_parent_path()) { + return base_dir / *override_path; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + def->config_key, override_path->string()); + // ignore invalid override; fall back to default filename + } + // Also support new-style keys: + // - UpperCamelCase: 'FontSetSstStdEuropeanLight' + // - lowerCamelCase: 'fontSetSstStdEuropeanLight' + const auto camel_key = MacroToCamel(def->config_key); + if (!camel_key.empty()) { + if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { + if (!override_path2->empty() && !override_path2->is_absolute() && + !override_path2->has_parent_path()) { + return base_dir / *override_path2; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + camel_key, override_path2->string()); + } + std::string lower_camel = camel_key; + lower_camel[0] = + static_cast(std::tolower(static_cast(lower_camel[0]))); + if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { + if (!override_path3->empty() && !override_path3->is_absolute() && + !override_path3->has_parent_path()) { + return base_dir / *override_path3; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + lower_camel, override_path3->string()); + } + } + if (def->default_file && *def->default_file) { + return base_dir / def->default_file; + } + } + LOG_ERROR(Lib_Font, "SystemFonts: unknown font set type=0x{:08X}", font_set_type); + return {}; +} + +static const FontSetCache* EnsureFontSetCache(u32 font_set_type) { + if (font_set_type == 0) { + return nullptr; + } + std::scoped_lock lock(g_fontset_cache_mutex); + auto& cache = g_fontset_cache[font_set_type]; + if (!cache.loaded) { + cache.loaded = true; + const auto path = ResolveSystemFontPath(font_set_type); + if (!path.empty() && LoadFontFile(path, cache.bytes)) { + cache.available = true; + cache.path = path; + } else { + cache.available = false; + LOG_ERROR(Lib_Font, "SystemFonts: failed to load font for set type=0x{:08X} path='{}'", + font_set_type, path.string()); + } + } + return cache.available ? &cache : nullptr; +} static std::once_flag g_system_font_once; static bool g_system_font_available = false; static std::vector g_system_font_bytes; static std::filesystem::path g_system_font_path; +static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes) { + if (path.empty()) { + return false; + } + if (!LoadGuestFileBytes(path, out_bytes) || out_bytes.empty()) { + return false; + } + return true; +} + static bool LoadFontFromPath(const std::filesystem::path& path) { if (path.empty()) { return false; } - if (!LoadGuestFileBytes(path, g_system_font_bytes) || g_system_font_bytes.empty()) { + if (!LoadFontFile(path, g_system_font_bytes)) { return false; } g_system_font_available = true; @@ -291,20 +664,35 @@ static bool LoadFontFromPath(const std::filesystem::path& path) { return true; } -static void LoadSystemFontBlob() { - const auto configured_path = Config::getSysFontPath(); - if (!configured_path.empty()) { - const auto resolved = configured_path.is_absolute() - ? configured_path - : std::filesystem::current_path() / configured_path; - if (LoadFontFromPath(resolved)) { - return; +// Try to load a reasonable fallback font from a directory by checking a list +// of common system font files used by PS4 firmware dumps. +static bool TryLoadFallbackFromDir(const std::filesystem::path& base_dir) { + if (base_dir.empty()) { + return false; + } + const auto fallback_name = Config::getSystemFontFallbackName(); + if (!fallback_name.empty()) { + std::filesystem::path p = fallback_name; + p = base_dir / p; + if (LoadFontFromPath(p)) { + return true; } + LOG_ERROR(Lib_Font, "SystemFonts: fallback font '{}' not found under '{}'", fallback_name, + base_dir.string()); + } + LOG_ERROR(Lib_Font, "SystemFonts: fallback not specified. Set [SystemFonts].fallback"); + return false; +} + +static void LoadSystemFontBlob() { + const auto base_dir = GetSysFontBaseDir(); + if (!base_dir.empty() && TryLoadFallbackFromDir(base_dir)) { + return; } g_system_font_bytes.clear(); g_system_font_available = false; LOG_ERROR(Lib_Font, "SystemFace: configured font '{}' missing; no fallback available", - configured_path.string()); + Config::getSysFontPath().string()); } static bool EnsureSystemFontBlob() { @@ -316,31 +704,44 @@ static bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle han if (st.ext_face_ready) { return true; } - if (!EnsureSystemFontBlob()) { + const FontSetCache* cache = EnsureFontSetCache(st.font_set_type); + const std::vector* source_bytes = nullptr; + std::filesystem::path source_path; + if (cache) { + source_bytes = &cache->bytes; + source_path = cache->path; + } else { + if (!EnsureSystemFontBlob()) { + return false; + } + source_bytes = &g_system_font_bytes; + source_path = g_system_font_path; + } + if (!source_bytes || source_bytes->empty()) { return false; } - st.ext_face_data = g_system_font_bytes; + st.ext_face_data = *source_bytes; st.ext_cache.clear(); st.ext_scale_for_height = 0.0f; st.layout_cached = false; st.logged_ext_use = true; // skip "external (game font)" log for built-in fallback const int offset = stbtt_GetFontOffsetForIndex(st.ext_face_data.data(), 0); if (offset < 0) { - LOG_ERROR(Lib_Font, "SystemFace: invalid font offset in '{}'", g_system_font_path.string()); + LOG_ERROR(Lib_Font, "SystemFace: invalid font offset in '{}'", source_path.string()); st.ext_face_data.clear(); st.ext_face_ready = false; return false; } if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset) == 0) { - LOG_ERROR(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", - g_system_font_path.string()); + LOG_ERROR(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", source_path.string()); st.ext_face_data.clear(); st.ext_face_ready = false; return false; } st.ext_face_ready = true; + st.system_font_path = source_path; LOG_INFO(Lib_Font, "SystemFace: handle={} now uses '{}'", static_cast(handle), - g_system_font_path.string()); + source_path.string()); return true; } @@ -354,10 +755,9 @@ static std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::Orbis if (!st.system_requested) { st.system_requested = true; const auto configured = Config::getSysFontPath(); - return fmt::format( - "SystemFace: handle={} requested internal font but sysFontPath ('{}') could not be " - "loaded", - static_cast(handle), configured.string()); + return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " + "could not be loaded", + static_cast(handle), configured.string()); } return {}; } @@ -402,10 +802,67 @@ static std::uint8_t g_external_fonts_ctx_stub{}; static u32 g_device_cache_stub{}; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { - LOG_INFO(Lib_Font, "AttachDeviceCacheBuffer(begin): library={} buffer={} size={}", - static_cast(library), static_cast(buffer), size); - LOG_ERROR(Lib_Font, "(STUBBED) called library={} buffer={} size={}", - static_cast(library), static_cast(buffer), size); + if (!library) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + constexpr u32 kMinCacheSize = 0x1020u; + if (size < kMinCacheSize) { + LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: size {} too small (min {})", size, + kMinCacheSize); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (lib->device_cache_buf && lib->device_cache_buf != &g_device_cache_stub) { + LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: library={} already attached", + static_cast(library)); + return ORBIS_FONT_ERROR_ALREADY_ATTACHED; + } + + std::uint8_t* owned_buffer = nullptr; + if (!buffer) { + owned_buffer = new (std::nothrow) std::uint8_t[size]; + if (!owned_buffer) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + buffer = owned_buffer; + } + + auto* header = static_cast(buffer); + header[0] = size; + const u32 usable_bytes = size > 0x1000 ? size - 0x1000 : 0; + const u32 page_count = usable_bytes >> 12; + if (page_count == 0) { + if (owned_buffer) { + delete[] owned_buffer; + } + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + header[1] = page_count; + header[2] = 0; + header[3] = page_count; + if (size >= 0x20) { + auto* meta = reinterpret_cast(header + 4); + *meta = 0x000FF800001000ULL; + } + + lib->device_cache_buf = header; + auto& state = GetLibState(library); + if (owned_buffer) { + state.owned_device_cache = owned_buffer; + state.owned_device_cache_size = size; + lib->flags |= 1u; + } else { + state.owned_device_cache = nullptr; + state.owned_device_cache_size = 0; + } + + LOG_INFO(Lib_Font, + "AttachDeviceCacheBuffer: library={} buffer={} size={} pages={} owned_buffer={}", + static_cast(library), buffer, size, page_count, + owned_buffer ? "true" : "false"); return ORBIS_OK; } @@ -577,6 +1034,8 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, state.create_params = create_params; state.edition = edition; state.native = lib; + state.owned_device_cache = nullptr; + state.owned_device_cache_size = 0; LOG_INFO(Lib_Font, "CreateLibrary: memory={} selection={} edition={} -> lib={}", static_cast(memory), static_cast(create_params), edition, static_cast(*pLibrary)); @@ -1451,16 +1910,30 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, OrbisFontHandle* pFontHandle) { - if (!templateFont || !pFontHandle) { + if ((!templateFont && !fontHandle) || !pFontHandle) { + LOG_ERROR(Lib_Font, "OpenFontInstance: invalid params base={} template={} out_ptr={}", + static_cast(fontHandle), static_cast(templateFont), + static_cast(pFontHandle)); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } + LOG_INFO(Lib_Font, "OpenFontInstance(begin): base={} template={} out_ptr={}", + static_cast(fontHandle), static_cast(templateFont), + static_cast(pFontHandle)); auto* src_state = TryGetState(templateFont); if (!src_state) { + templateFont = nullptr; + src_state = TryGetState(fontHandle); + } + if (!src_state) { + LOG_ERROR(Lib_Font, "OpenFontInstance: template handle={} missing", + templateFont ? static_cast(templateFont) + : static_cast(fontHandle)); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } FontState* base_state = TryGetState(fontHandle); if (!base_state && fontHandle) { - LOG_WARNING(Lib_Font, "OpenFontInstance: base handle={} unknown, falling back to template", + LOG_WARNING(Lib_Font, + "OpenFontInstance: base handle={} unknown, falling back to template state", static_cast(fontHandle)); } auto* new_handle = new (std::nothrow) OrbisFontHandleOpaque{}; @@ -1468,13 +1941,29 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa LOG_ERROR(Lib_Font, "OpenFontInstance: allocation failed"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } + const std::size_t src_ext_bytes = src_state->ext_face_data.size(); + LOG_DEBUG( + Lib_Font, + "OpenFontInstance: template state summary ext_ready={} bytes={} cache={} dpi=({}, {}) " + "scale=({}, {}) scale_point_active={}", + src_state->ext_face_ready, src_ext_bytes, src_state->ext_cache.size(), src_state->dpi_x, + src_state->dpi_y, src_state->scale_w, src_state->scale_h, src_state->scale_point_active); auto& dst = GetState(new_handle); dst = *src_state; + dst.ext_face = {}; + dst.ext_cache.clear(); + dst.scratch.clear(); dst.library = base_state ? base_state->library : src_state->library; dst.bound_renderer = base_state ? base_state->bound_renderer : src_state->bound_renderer; dst.logged_ext_use = false; + bool stbtt_ok = false; if (dst.ext_face_ready && !dst.ext_face_data.empty()) { const int font_offset = src_state->ext_face.fontstart; + LOG_INFO( + Lib_Font, + "OpenFontInstance: reinitializing stbtt font template={} -> new={} offset={} size={}", + static_cast(templateFont), static_cast(new_handle), + font_offset, dst.ext_face_data.size()); if (stbtt_InitFont(&dst.ext_face, dst.ext_face_data.data(), font_offset) == 0) { LOG_WARNING(Lib_Font, "OpenFontInstance: stbtt_InitFont failed when cloning handle={} -> new={}", @@ -1484,15 +1973,30 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa dst.ext_cache.clear(); } else { dst.ext_scale_for_height = ComputeScaleExt(&dst.ext_face, dst.scale_h); + stbtt_ok = true; } + } else { + LOG_DEBUG( + Lib_Font, + "OpenFontInstance: template handle={} had no external face data (ready={} size={})", + static_cast(templateFont), dst.ext_face_ready, dst.ext_face_data.size()); + dst.ext_face_ready = false; } dst.layout_cached = false; *pFontHandle = new_handle; LOG_INFO(Lib_Font, - "OpenFontInstance: base={} template={} -> new={} ext_ready={} renderer_inherited={}", + "OpenFontInstance: base={} template={} -> new={} ext_ready={} renderer_inherited={} " + "stbtt_reinit={}", static_cast(fontHandle), static_cast(templateFont), static_cast(new_handle), dst.ext_face_ready, - dst.bound_renderer != nullptr); + dst.bound_renderer != nullptr, stbtt_ok); + LOG_DEBUG( + Lib_Font, + "OpenFontInstance: result summary new={} library={} renderer={} dpi=({}, {}) scale=({}, " + "{})", + static_cast(new_handle), static_cast(dst.library), + static_cast(dst.bound_renderer), dst.dpi_x, dst.dpi_y, dst.scale_w, + dst.scale_h); return ORBIS_OK; } @@ -1508,6 +2012,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd *pFontHandle = f; auto& st = GetState(f); st.library = library; + st.font_set_type = 0; + st.system_font_path.clear(); const unsigned char* p = reinterpret_cast(fontAddress); auto& ls = GetLibState(library); LOG_INFO(Lib_Font, @@ -1592,6 +2098,22 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o *pFontHandle = f; auto& st = GetState(f); st.library = library; + st.font_set_type = fontSetType; + st.system_font_path.clear(); + LOG_INFO( + Lib_Font, + "OpenFontSet(request): lib={} fontSetType=0x{:08X} openMode={} open_params={} handle={}", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle)); + if (const auto* def = FindSystemFontDefinition(fontSetType)) { + const auto pretty = MacroToCamel(def->config_key); + LOG_INFO(Lib_Font, "OpenFontSet(mapping): type=0x{:08X} ({}) default='{}'", fontSetType, + !pretty.empty() ? pretty.c_str() : def->config_key, + def->default_file ? def->default_file : ""); + } else { + LOG_INFO(Lib_Font, "OpenFontSet(mapping): type=0x{:08X} (unknown)", fontSetType); + } + LogFontOpenParams(open_params); bool system_ok = false; const std::string sys_log = ReportSystemFaceRequest(st, f, system_ok); if (!sys_log.empty()) { @@ -1658,19 +2180,31 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl result->slot.maybe_pixelFmt = (result->slot.maybe_pixelSize == 4) ? 1 : 0; } auto& st = GetState(fontHandle); - float fw = st.scale_w; - float fh = st.scale_h; + const OrbisFontStyleFrame* bound_style = nullptr; + if (auto it = g_style_for_surface.find(surf); it != g_style_for_surface.end()) { + bound_style = it->second; + } + const StyleFrameScaleState style_state = ResolveStyleFrameScale(bound_style, st); + const bool style_scale_override = style_state.scale_overridden; + float render_scale_w = style_state.scale_w; + float render_scale_h = style_state.scale_h; + float fw = render_scale_w; + float fh = render_scale_h; int g_x0 = 0, g_y0 = 0, g_x1 = 0, g_y1 = 0; const stbtt_fontinfo* face = nullptr; float scale = 0.0f; + const bool needs_custom_scale = std::fabs(render_scale_h - st.scale_h) > kScaleEpsilon; + if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); const int glyph_index = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); if (glyph_index > 0) { face = &st.ext_face; - scale = st.ext_scale_for_height; + scale = needs_custom_scale + ? ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)) + : st.ext_scale_for_height; } } @@ -1682,9 +2216,13 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl } if (system_attached) { face = &st.ext_face; - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); - scale = st.ext_scale_for_height; + if (!needs_custom_scale) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + scale = st.ext_scale_for_height; + } else { + scale = ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)); + } } } @@ -1695,7 +2233,7 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl if (face) { LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src=external", static_cast(fontHandle), code); - const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); + const int pixel_h = std::max(1, (int)std::lround(render_scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); GlyphEntry* ge = nullptr; if (!use_subpixel) { @@ -1778,6 +2316,10 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl UpdateCachedLayout(st); } float layout_baseline = st.layout_cached ? st.cached_baseline_y : 0.0f; + if (style_scale_override && st.ext_face_ready) { + const float override_scale = ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)); + layout_baseline = static_cast(st.ext_ascent) * override_scale; + } float adjusted_y = y; if (face && layout_baseline > 0.0f) { @@ -1848,7 +2390,7 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl code, is_pua, is_placeholder_ascii, rx0, ry0, cw, ch, bpp); } if (face && iw > 0 && ih > 0) { - const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); + const int pixel_h = std::max(1, (int)std::lround(render_scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); const GlyphEntry* ge = nullptr; if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { @@ -1951,8 +2493,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl result->new_w = static_cast(cw); result->new_h = static_cast(ch); const float stride_bytes = static_cast(std::max(0, surf->widthByte)); - result->ImageMetrics.bearing_x = static_cast(g_x0); - result->ImageMetrics.bearing_y = static_cast(-g_y0); + result->ImageMetrics.bearing_x = metrics ? metrics->h_bearing_x : static_cast(g_x0); + result->ImageMetrics.bearing_y = metrics ? metrics->h_bearing_y : static_cast(-g_y0); float adv_px = 0.0f; if (face) { int aw_tmp = 0, lsb_tmp = 0; @@ -2062,11 +2604,25 @@ s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* rende OrbisFontStyleFrame* styleFrame) { if (!renderSurface) return ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (!styleFrame) { + g_style_for_surface.erase(renderSurface); + renderSurface->styleFlag &= ~u8{0x1}; + renderSurface->reserved_q[0] = 0; + LOG_INFO(Lib_Font, "RenderSurfaceSetStyleFrame: surf={} cleared", + static_cast(renderSurface)); + return ORBIS_OK; + } + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; g_style_for_surface[renderSurface] = styleFrame; renderSurface->styleFlag |= 0x1; renderSurface->reserved_q[0] = reinterpret_cast(styleFrame); - LOG_INFO(Lib_Font, "RenderSurfaceSetStyleFrame: surf={} styleFrame={}", - static_cast(renderSurface), static_cast(styleFrame)); + LOG_INFO(Lib_Font, + "RenderSurfaceSetStyleFrame: surf={} styleFrame={} flags=0x{:X} scale=({}, {}) " + "dpi=({}, {}) mode={}", + static_cast(renderSurface), static_cast(styleFrame), + styleFrame->flags, styleFrame->scaleWidth, styleFrame->scaleHeight, styleFrame->dpiX, + styleFrame->dpiY, styleFrame->scalingFlag); return ORBIS_OK; } @@ -2239,8 +2795,10 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame float* slantRatio) { if (!styleFrame || !slantRatio) return ORBIS_FONT_ERROR_INVALID_PARAMETER; - *slantRatio = 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameGetEffectSlant: frame={} slant={} (opaque)", + if (styleFrame->magic != kStyleFrameMagic) + InitializeStyleFrame(*styleFrame); + *slantRatio = (styleFrame->flags & kStyleFrameFlagSlant) ? styleFrame->slantRatio : 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameGetEffectSlant: frame={} slant={}", static_cast(styleFrame), *slantRatio); return ORBIS_OK; } @@ -2250,83 +2808,162 @@ s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyle uint32_t* mode) { if (!fontStyleFrame) return ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (fontStyleFrame->magic != kStyleFrameMagic) + InitializeStyleFrame(*fontStyleFrame); + const bool has_weight = (fontStyleFrame->flags & kStyleFrameFlagWeight) != 0; if (weightXScale) - *weightXScale = 1.0f; + *weightXScale = has_weight ? fontStyleFrame->weightXScale : 1.0f; if (weightYScale) - *weightYScale = 1.0f; + *weightYScale = has_weight ? fontStyleFrame->weightYScale : 1.0f; if (mode) *mode = 0; - LOG_DEBUG(Lib_Font, "StyleFrameGetEffectWeight: frame={} weight=({}, {}) mode={} (opaque)", + LOG_DEBUG(Lib_Font, "StyleFrameGetEffectWeight: frame={} weight=({}, {}) mode={}", static_cast(fontStyleFrame), weightXScale ? *weightXScale : -1.0f, weightYScale ? *weightYScale : -1.0f, mode ? *mode : 0u); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(const OrbisFontStyleFrame* styleFrame, + u32* h_dpi, u32* v_dpi) { + if (!ValidateStyleFramePtr(styleFrame) || (!h_dpi && !v_dpi)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (h_dpi) + *h_dpi = styleFrame->dpiX > 0 ? static_cast(styleFrame->dpiX) : 0u; + if (v_dpi) + *v_dpi = styleFrame->dpiY > 0 ? static_cast(styleFrame->dpiY) : 0u; + LOG_DEBUG(Lib_Font, "StyleFrameGetResolutionDpi: frame={} -> ({}, {})", + static_cast(styleFrame), h_dpi ? *h_dpi : 0u, v_dpi ? *v_dpi : 0u); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(const OrbisFontStyleFrame* styleFrame, float* w, float* h) { - if (!styleFrame) + if (!ValidateStyleFramePtr(styleFrame) || (!w && !h)) return ORBIS_FONT_ERROR_INVALID_PARAMETER; + const bool active = (styleFrame->flags & kStyleFrameFlagScale) != 0 && + styleFrame->scalingFlag == static_cast(StyleFrameScalingMode::Pixel); if (w) - *w = 0.0f; + *w = active ? styleFrame->scaleWidth : 0.0f; if (h) - *h = 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameGetScalePixel: frame={} -> w={}, h={} (opaque)", + *h = active ? styleFrame->scaleHeight : 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameGetScalePixel: frame={} -> w={}, h={}", static_cast(styleFrame), w ? *w : 0.0f, h ? *h : 0.0f); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(const OrbisFontStyleFrame* styleFrame, float* w, + float* h) { + if (!ValidateStyleFramePtr(styleFrame) || (!w && !h)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + const bool active = (styleFrame->flags & kStyleFrameFlagScale) != 0 && + styleFrame->scalingFlag == static_cast(StyleFrameScalingMode::Point); + if (w) + *w = active ? styleFrame->scaleWidth : 0.0f; + if (h) + *h = active ? styleFrame->scaleHeight : 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameGetScalePoint: frame={} -> w={}pt, h={}pt", + static_cast(styleFrame), w ? *w : 0.0f, h ? *h : 0.0f); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameInit() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameInit(OrbisFontStyleFrame* styleFrame) { + if (!styleFrame) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + InitializeStyleFrame(*styleFrame); + LOG_DEBUG(Lib_Font, "StyleFrameInit: frame={}", static_cast(styleFrame)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(OrbisFontStyleFrame* styleFrame, + float slantRatio) { + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->slantRatio = slantRatio; + styleFrame->flags |= kStyleFrameFlagSlant; + LOG_DEBUG(Lib_Font, "StyleFrameSetEffectSlant: frame={} slant={}", + static_cast(styleFrame), slantRatio); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(OrbisFontStyleFrame* styleFrame, + float weightXScale, float weightYScale, + u32 mode) { + (void)mode; + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->weightXScale = weightXScale; + styleFrame->weightYScale = weightYScale; + styleFrame->flags |= kStyleFrameFlagWeight; + LOG_DEBUG(Lib_Font, "StyleFrameSetEffectWeight: frame={} weight=({}, {}) mode={}", + static_cast(styleFrame), weightXScale, weightYScale, mode); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(OrbisFontStyleFrame* styleFrame, u32 h_dpi, + u32 v_dpi) { + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->dpiX = static_cast(h_dpi); + styleFrame->dpiY = static_cast(v_dpi); + styleFrame->flags |= kStyleFrameFlagDpi; + LOG_DEBUG(Lib_Font, "StyleFrameSetResolutionDpi: frame={} -> ({}, {})", + static_cast(styleFrame), h_dpi, v_dpi); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(OrbisFontStyleFrame* styleFrame, float w, float h) { + if (!EnsureStyleFrameInitialized(styleFrame) || w <= 0.0f || h <= 0.0f) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->scaleWidth = w; + styleFrame->scaleHeight = h; + styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::Pixel); + styleFrame->flags |= kStyleFrameFlagScale; + LOG_DEBUG(Lib_Font, "StyleFrameSetScalePixel: frame={} -> ({}, {})", + static_cast(styleFrame), w, h); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(OrbisFontStyleFrame* styleFrame, float w, float h) { + if (!EnsureStyleFrameInitialized(styleFrame) || w <= 0.0f || h <= 0.0f) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->scaleWidth = w; + styleFrame->scaleHeight = h; + styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::Point); + styleFrame->flags |= kStyleFrameFlagScale; + LOG_DEBUG(Lib_Font, "StyleFrameSetScalePoint: frame={} -> ({}, {})pt", + static_cast(styleFrame), w, h); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(OrbisFontStyleFrame* styleFrame) { + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags &= ~kStyleFrameFlagSlant; + styleFrame->slantRatio = 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameUnsetEffectSlant: frame={}", + static_cast(styleFrame)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(OrbisFontStyleFrame* styleFrame) { + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags &= ~kStyleFrameFlagWeight; + styleFrame->weightXScale = 1.0f; + styleFrame->weightYScale = 1.0f; + LOG_DEBUG(Lib_Font, "StyleFrameUnsetEffectWeight: frame={}", + static_cast(styleFrame)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(OrbisFontStyleFrame* styleFrame) { + if (!EnsureStyleFrameInitialized(styleFrame)) + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags &= ~kStyleFrameFlagScale; + styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::None); + styleFrame->scaleWidth = 0.0f; + styleFrame->scaleHeight = 0.0f; + LOG_DEBUG(Lib_Font, "StyleFrameUnsetScale: frame={}", static_cast(styleFrame)); return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 75f070d24..363ccdb86 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -1,457 +1,461 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once -#include -#include "common/types.h" -namespace Core::Loader { -class SymbolsResolver; -} -namespace Libraries::Font { - -struct OrbisFontHandleOpaque { - u32 reserved[64]; -}; - -using OrbisFontLib = void*; -using OrbisFontHandle = OrbisFontHandleOpaque*; -using OrbisFontRendererCreateParams = void*; -using OrbisFontRenderer = void*; -using OrbisFontLibCreateParams = void*; - -struct OrbisFontOpenParams { - u16 tag; - u16 pad16; - u32 flags; - u32 subfont_index; - s32 unique_id; - const void* reserved_ptr2; - const void* reserved_ptr1; -}; - -struct OrbisFontGlyphMetrics { - float w; - float h; - float h_bearing_x; - float h_bearing_y; - float h_adv; - float v_bearing_x; - float v_bearing_y; - float v_adv; -}; - -struct OrbisFontKerning { - float dx; - float dy; - float px; - float py; -}; - -struct OrbisFontGlyphImageMetrics { - float bearing_x; - float bearing_y; - float dv; - float stride; - u32 width; - u32 height; -}; - -struct OrbisFontResultStage { - u8* p_00; - u32 u32_08; - u32 u32_0C; - u32 u32_10; -}; - -struct OrbisFontResultSlot { - u8* maybe_addr; - u32 maybe_rowBytes; - u8 maybe_pixelSize; - u8 maybe_pixelFmt; -}; - -struct OrbisFontRenderOutput { - const OrbisFontResultStage* stage; - OrbisFontResultSlot slot; - u32 new_x; - u32 new_y; - u32 new_w; - u32 new_h; - OrbisFontGlyphImageMetrics ImageMetrics; -}; - -struct OrbisFontMem; - -using OrbisFontAllocFn = void*(void* object, u32 size); -using OrbisFontFreeFn = void(void* object, void* p); -using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); -using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); -using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, - u32 attr); -using OrbisFontMspaceDestroyFn = void(void* parent, void* mspace); -using OrbisFontMemDestroyFn = void(OrbisFontMem* fontMemory, void* object, void* destroyArg); - -using OrbisFontAllocCb = OrbisFontAllocFn*; -using OrbisFontFreeCb = OrbisFontFreeFn*; -using OrbisFontReallocCb = OrbisFontReallocFn*; -using OrbisFontCallocCb = OrbisFontCallocFn*; -using OrbisFontMspaceCreateCb = OrbisFontMspaceCreateFn*; -using OrbisFontMspaceDestroyCb = OrbisFontMspaceDestroyFn*; -using OrbisFontMemDestroyCb = OrbisFontMemDestroyFn*; - -struct OrbisFontMemInterface { - OrbisFontAllocCb alloc{}; - OrbisFontFreeCb dealloc{}; - OrbisFontReallocCb realloc_fn{}; - OrbisFontCallocCb calloc_fn{}; - OrbisFontMspaceCreateCb mspace_create{}; - OrbisFontMspaceDestroyCb mspace_destroy{}; -}; - -struct OrbisFontMem { - u16 mem_kind; - u16 attr_bits; - u32 region_size; - void* region_base; - void* mspace_handle; - const OrbisFontMemInterface* iface; - OrbisFontMemDestroyCb on_destroy; - void* destroy_ctx; - void* some_ctx1; - void* some_ctx2; -}; - -struct OrbisFontTextCharacter { - // Other fields... - struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 - struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08 - void* textOrder; // Field at offset 0x10 (pointer to text order info) - u32 characterCode; // Field assumed at offset 0x28 - u8 unkn_0x31; // Offset 0x31 - u8 unkn_0x33; // Offset 0x33 - u8 charType; // Field assumed at offset 0x39 - u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level - u8 formatFlags; // Field at offset 0x3D (stores format-related flags) -}; - -struct OrbisFontRenderSurface { - void* buffer; - s32 widthByte; - s8 pixelSizeByte; - u8 pad0; - u8 styleFlag; - u8 pad2; - s32 width, height; - u32 sc_x0; - u32 sc_y0; - u32 sc_x1; - u32 sc_y1; - u64 reserved_q[11]; -}; - -struct OrbisFontStyleFrame { - /*0x00*/ u16 magic; // Expected to be 0xF09 - /*0x02*/ u16 flags; - /*0x04*/ s32 dpiX; // DPI scaling factor for width - /*0x08*/ s32 dpiY; // DPI scaling factor for height - /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled - /*0x10*/ float scaleWidth; // Width scaling factor - /*0x14*/ float scaleHeight; // Height scaling factor - /*0x18*/ float weightXScale; - /*0x1c*/ float weightYScale; - /*0x20*/ float slantRatio; - /*0x24*/ -}; - -struct OrbisFontHorizontalLayout { - float baselineOffset; - float lineAdvance; - float decorationExtent; -}; -struct OrbisFontVerticalLayout { - float baselineOffsetX; - float columnAdvance; - float decorationSpan; -}; - -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); -static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); -static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); -static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); -static_assert(sizeof(OrbisFontGlyphImageMetrics) == 24, "OrbisFontGlyphImageMetrics ABI size"); -static_assert(sizeof(OrbisFontRenderOutput) == 64, "OrbisFontRenderOutput ABI size"); -static_assert(sizeof(OrbisFontMem) == 64, "OrbisFontMem ABI size"); -#endif - -s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size); -s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer); -s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, - int* bidiLevel); -s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); -s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); -s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, - void** pTextOrder); -u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter); -u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter); -OrbisFontTextCharacter* PS4_SYSV_ABI -sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter); -OrbisFontTextCharacter* PS4_SYSV_ABI -sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter); -s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); -s32 PS4_SYSV_ABI sceFontClearDeviceCache(); -s32 PS4_SYSV_ABI sceFontCloseFont(); -s32 PS4_SYSV_ABI sceFontControl(); -s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); -s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); -s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); -s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, - OrbisFontLibCreateParams create_params, - OrbisFontLib* pLibrary); -s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, - OrbisFontLibCreateParams create_params, - u64 edition, OrbisFontLib* pLibrary); -s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, - OrbisFontRendererCreateParams create_params, - OrbisFontRenderer* pRenderer); -s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, - OrbisFontRendererCreateParams create_params, - u64 edition, OrbisFontRenderer* pRenderer); -s32 PS4_SYSV_ABI sceFontCreateString(); -s32 PS4_SYSV_ABI sceFontCreateWords(); -s32 PS4_SYSV_ABI sceFontCreateWritingLine(); -s32 PS4_SYSV_ABI sceFontDefineAttribute(); -s32 PS4_SYSV_ABI sceFontDeleteGlyph(); -s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); -s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); -s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary); -s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer); -s32 PS4_SYSV_ABI sceFontDestroyString(); -s32 PS4_SYSV_ABI sceFontDestroyWords(); -s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); -s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); -s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); -s32 PS4_SYSV_ABI sceFontGetAttribute(); -s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); -s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, - OrbisFontGlyphMetrics* metrics); -s32 PS4_SYSV_ABI sceFontGetEffectSlant(); -s32 PS4_SYSV_ABI sceFontGetEffectWeight(); -s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); -s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile(); -s32 PS4_SYSV_ABI sceFontGetFontMetrics(); -s32 PS4_SYSV_ABI sceFontGetFontResolution(); -s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); -s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); -s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, - OrbisFontHorizontalLayout* layout); -s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, - OrbisFontKerning* kerning); -s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); -s32 PS4_SYSV_ABI sceFontGetPixelResolution(); -s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, - OrbisFontGlyphMetrics* out_metrics); -s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); -s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); -s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); -s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); -s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); -s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); -s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); -s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, - OrbisFontVerticalLayout* layout); -s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); -s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); -s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); -s32 PS4_SYSV_ABI sceFontGlyphRefersOutline(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); -s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); -s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); -s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); -s32 PS4_SYSV_ABI sceFontGraphicsEndFrame(); -s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource(); -s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit(); -s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit(); -s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout(); -s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping(); -s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit(); -s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect(); -s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout(); -s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping(); -s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage(); -s32 PS4_SYSV_ABI sceFontGraphicsRegionInit(); -s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular(); -s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish(); -s32 PS4_SYSV_ABI sceFontGraphicsRelease(); -s32 PS4_SYSV_ABI sceFontGraphicsRenderResource(); -s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill(); -s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot(); -s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas(); -s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence(); -s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign(); -s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource(); -s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); -s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); -s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u32 region_size, - const OrbisFontMemInterface* iface, void* mspace_obj, - OrbisFontMemDestroyCb destroy_cb, void* destroy_ctx); -s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc); -s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, - const OrbisFontOpenParams* open_detail, - OrbisFontHandle* out_handle); -s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, - OrbisFontHandle* pFontHandle); -s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, - const OrbisFontOpenParams* open_params, - OrbisFontHandle* pFontHandle); -s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, - const OrbisFontOpenParams* open_params, - OrbisFontHandle* pFontHandle); -s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 code, - OrbisFontRenderSurface* surf, float x, float y, - OrbisFontGlyphMetrics* metrics, - OrbisFontRenderOutput* result); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandle, u32 code, - OrbisFontRenderSurface* surf, float x, - float y, OrbisFontGlyphMetrics* metrics, - OrbisFontRenderOutput* result); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); -s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); -s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); -s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); -void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, - int bufWidthByte, int pixelSizeByte, int widthPixel, - int heightPixel); -void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, - int y0, int w, int h); -s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, - OrbisFontStyleFrame* styleFrame); -s32 PS4_SYSV_ABI sceFontSetEffectSlant(); -s32 PS4_SYSV_ABI sceFontSetEffectWeight(); -s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); -s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi); -s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); -s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); -s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); -s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); -s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h); -s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); -s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); -s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); -s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); -s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, - float* slantRatio); -s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, - float* weightXScale, float* weightYScale, - uint32_t* mode); -s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, - float* h); -s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(); -s32 PS4_SYSV_ABI sceFontStyleFrameInit(); -s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(); -s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(); -s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(); -s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(); -s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(); -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(); -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(); -s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(); -s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats); -s32 PS4_SYSV_ABI sceFontSupportGlyphs(); -s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library); -s32 PS4_SYSV_ABI sceFontTextCodesStepBack(); -s32 PS4_SYSV_ABI sceFontTextCodesStepNext(); -s32 PS4_SYSV_ABI sceFontTextSourceInit(); -s32 PS4_SYSV_ABI sceFontTextSourceRewind(); -s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont(); -s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm(); -s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle); -s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters(); -s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics(); -s32 PS4_SYSV_ABI sceFontWritingInit(); -s32 PS4_SYSV_ABI sceFontWritingLineClear(); -s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace(); -s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics(); -s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep(); -s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder(); -s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep(); -s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter(); -s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible(); -s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3(); -s32 PS4_SYSV_ABI Func_03C650025FBB0DE7(); -s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A(); -s32 PS4_SYSV_ABI Func_09408E88E4F97CE3(); -s32 PS4_SYSV_ABI Func_09F92905ED82A814(); -s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE(); -s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2(); -s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75(); -s32 PS4_SYSV_ABI Func_1D401185D5E24C3D(); -s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F(); -s32 PS4_SYSV_ABI Func_314B1F765B9FE78A(); -s32 PS4_SYSV_ABI Func_350E6725FEDE29E1(); -s32 PS4_SYSV_ABI Func_3DB773F0A604BF39(); -s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C(); -s32 PS4_SYSV_ABI Func_526287664A493981(); -s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9(); -s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D(); -s32 PS4_SYSV_ABI Func_569E2ECD34290F45(); -s32 PS4_SYSV_ABI Func_5A04775B6BE47685(); -s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750(); -s32 PS4_SYSV_ABI Func_62B5398F864BD3B4(); -s32 PS4_SYSV_ABI Func_6F9010294D822367(); -s32 PS4_SYSV_ABI Func_7757E947423A7A67(); -s32 PS4_SYSV_ABI Func_7E06BA52077F54FA(); -s32 PS4_SYSV_ABI Func_93B36DEA021311D6(); -s32 PS4_SYSV_ABI Func_94B0891E7111598A(); -s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD(); -s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1(); -s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA(); -s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F(); -s32 PS4_SYSV_ABI Func_C10F488AD7CF103D(); -s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7(); -s32 PS4_SYSV_ABI Func_E48D3CD01C342A33(); -s32 PS4_SYSV_ABI Func_EAC96B2186B71E14(); -s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); -s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); - -void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Font +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once +#include +#include "common/types.h" +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Font { + +struct OrbisFontHandleOpaque { + u32 reserved[64]; +}; + +using OrbisFontLib = void*; +using OrbisFontHandle = OrbisFontHandleOpaque*; +using OrbisFontRendererCreateParams = void*; +using OrbisFontRenderer = void*; +using OrbisFontLibCreateParams = void*; + +struct OrbisFontOpenParams { + u16 tag; + u16 pad16; + u32 flags; + u32 subfont_index; + s32 unique_id; + const void* reserved_ptr2; + const void* reserved_ptr1; +}; + +struct OrbisFontGlyphMetrics { + float w; + float h; + float h_bearing_x; + float h_bearing_y; + float h_adv; + float v_bearing_x; + float v_bearing_y; + float v_adv; +}; + +struct OrbisFontKerning { + float dx; + float dy; + float px; + float py; +}; + +struct OrbisFontGlyphImageMetrics { + float bearing_x; + float bearing_y; + float dv; + float stride; + u32 width; + u32 height; +}; + +struct OrbisFontResultStage { + u8* p_00; + u32 u32_08; + u32 u32_0C; + u32 u32_10; +}; + +struct OrbisFontResultSlot { + u8* maybe_addr; + u32 maybe_rowBytes; + u8 maybe_pixelSize; + u8 maybe_pixelFmt; +}; + +struct OrbisFontRenderOutput { + const OrbisFontResultStage* stage; + OrbisFontResultSlot slot; + u32 new_x; + u32 new_y; + u32 new_w; + u32 new_h; + OrbisFontGlyphImageMetrics ImageMetrics; +}; + +struct OrbisFontMem; + +using OrbisFontAllocFn = void*(void* object, u32 size); +using OrbisFontFreeFn = void(void* object, void* p); +using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); +using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); +using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, + u32 attr); +using OrbisFontMspaceDestroyFn = void(void* parent, void* mspace); +using OrbisFontMemDestroyFn = void(OrbisFontMem* fontMemory, void* object, void* destroyArg); + +using OrbisFontAllocCb = OrbisFontAllocFn*; +using OrbisFontFreeCb = OrbisFontFreeFn*; +using OrbisFontReallocCb = OrbisFontReallocFn*; +using OrbisFontCallocCb = OrbisFontCallocFn*; +using OrbisFontMspaceCreateCb = OrbisFontMspaceCreateFn*; +using OrbisFontMspaceDestroyCb = OrbisFontMspaceDestroyFn*; +using OrbisFontMemDestroyCb = OrbisFontMemDestroyFn*; + +struct OrbisFontMemInterface { + OrbisFontAllocCb alloc{}; + OrbisFontFreeCb dealloc{}; + OrbisFontReallocCb realloc_fn{}; + OrbisFontCallocCb calloc_fn{}; + OrbisFontMspaceCreateCb mspace_create{}; + OrbisFontMspaceDestroyCb mspace_destroy{}; +}; + +struct OrbisFontMem { + u16 mem_kind; + u16 attr_bits; + u32 region_size; + void* region_base; + void* mspace_handle; + const OrbisFontMemInterface* iface; + OrbisFontMemDestroyCb on_destroy; + void* destroy_ctx; + void* some_ctx1; + void* some_ctx2; +}; + +struct OrbisFontTextCharacter { + // Other fields... + struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 + struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08 + void* textOrder; // Field at offset 0x10 (pointer to text order info) + u32 characterCode; // Field assumed at offset 0x28 + u8 unkn_0x31; // Offset 0x31 + u8 unkn_0x33; // Offset 0x33 + u8 charType; // Field assumed at offset 0x39 + u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level + u8 formatFlags; // Field at offset 0x3D (stores format-related flags) +}; + +struct OrbisFontRenderSurface { + void* buffer; + s32 widthByte; + s8 pixelSizeByte; + u8 pad0; + u8 styleFlag; + u8 pad2; + s32 width, height; + u32 sc_x0; + u32 sc_y0; + u32 sc_x1; + u32 sc_y1; + u64 reserved_q[11]; +}; + +struct OrbisFontStyleFrame { + /*0x00*/ u16 magic; // Expected to be 0xF09 + /*0x02*/ u16 flags; + /*0x04*/ s32 dpiX; // DPI scaling factor for width + /*0x08*/ s32 dpiY; // DPI scaling factor for height + /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled + /*0x10*/ float scaleWidth; // Width scaling factor + /*0x14*/ float scaleHeight; // Height scaling factor + /*0x18*/ float weightXScale; + /*0x1c*/ float weightYScale; + /*0x20*/ float slantRatio; + /*0x24*/ +}; + +struct OrbisFontHorizontalLayout { + float baselineOffset; + float lineAdvance; + float decorationExtent; +}; +struct OrbisFontVerticalLayout { + float baselineOffsetX; + float columnAdvance; + float decorationSpan; +}; + +#if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); +static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); +static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); +static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); +static_assert(sizeof(OrbisFontGlyphImageMetrics) == 24, "OrbisFontGlyphImageMetrics ABI size"); +static_assert(sizeof(OrbisFontRenderOutput) == 64, "OrbisFontRenderOutput ABI size"); +static_assert(sizeof(OrbisFontMem) == 64, "OrbisFontMem ABI size"); +#endif + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size); +s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer); +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel); +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder); +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter); +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter); +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); +s32 PS4_SYSV_ABI sceFontClearDeviceCache(); +s32 PS4_SYSV_ABI sceFontCloseFont(); +s32 PS4_SYSV_ABI sceFontControl(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + OrbisFontLib* pLibrary); +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, + OrbisFontLibCreateParams create_params, + u64 edition, OrbisFontLib* pLibrary); +s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + OrbisFontRenderer* pRenderer); +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, + OrbisFontRendererCreateParams create_params, + u64 edition, OrbisFontRenderer* pRenderer); +s32 PS4_SYSV_ABI sceFontCreateString(); +s32 PS4_SYSV_ABI sceFontCreateWords(); +s32 PS4_SYSV_ABI sceFontCreateWritingLine(); +s32 PS4_SYSV_ABI sceFontDefineAttribute(); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); +s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary); +s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer); +s32 PS4_SYSV_ABI sceFontDestroyString(); +s32 PS4_SYSV_ABI sceFontDestroyWords(); +s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); +s32 PS4_SYSV_ABI sceFontGetAttribute(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, + OrbisFontGlyphMetrics* metrics); +s32 PS4_SYSV_ABI sceFontGetEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile(); +s32 PS4_SYSV_ABI sceFontGetFontMetrics(); +s32 PS4_SYSV_ABI sceFontGetFontResolution(); +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, + OrbisFontHorizontalLayout* layout); +s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, + OrbisFontKerning* kerning); +s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); +s32 PS4_SYSV_ABI sceFontGetPixelResolution(); +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, + OrbisFontGlyphMetrics* out_metrics); +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); +s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); +s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); +s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, + OrbisFontVerticalLayout* layout); +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource(); +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish(); +s32 PS4_SYSV_ABI sceFontGraphicsRelease(); +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource(); +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u32 region_size, + const OrbisFontMemInterface* iface, void* mspace_obj, + OrbisFontMemDestroyCb destroy_cb, void* destroy_ctx); +s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc); +s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, + const OrbisFontOpenParams* open_detail, + OrbisFontHandle* out_handle); +s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, + OrbisFontHandle* pFontHandle); +s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle); +s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, + const OrbisFontOpenParams* open_params, + OrbisFontHandle* pFontHandle); +s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, float y, + OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, + float y, OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel); +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h); +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontSetEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); +s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi); +s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h); +s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h); +s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h); +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); +s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode); +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(const OrbisFontStyleFrame* styleFrame, + u32* h_dpi, u32* v_dpi); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(const OrbisFontStyleFrame* styleFrame, float* w, + float* h); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(const OrbisFontStyleFrame* styleFrame, float* w, + float* h); +s32 PS4_SYSV_ABI sceFontStyleFrameInit(OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(OrbisFontStyleFrame* styleFrame, float slantRatio); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(OrbisFontStyleFrame* styleFrame, + float weightXScale, float weightYScale, u32 mode); +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(OrbisFontStyleFrame* styleFrame, u32 h_dpi, + u32 v_dpi); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(OrbisFontStyleFrame* styleFrame, float w, float h); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(OrbisFontStyleFrame* styleFrame, float w, float h); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats); +s32 PS4_SYSV_ABI sceFontSupportGlyphs(); +s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library); +s32 PS4_SYSV_ABI sceFontTextCodesStepBack(); +s32 PS4_SYSV_ABI sceFontTextCodesStepNext(); +s32 PS4_SYSV_ABI sceFontTextSourceInit(); +s32 PS4_SYSV_ABI sceFontTextSourceRewind(); +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont(); +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm(); +s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle); +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters(); +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingInit(); +s32 PS4_SYSV_ABI sceFontWritingLineClear(); +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace(); +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter(); +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible(); +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3(); +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7(); +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A(); +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3(); +s32 PS4_SYSV_ABI Func_09F92905ED82A814(); +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE(); +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2(); +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75(); +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D(); +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F(); +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A(); +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1(); +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39(); +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C(); +s32 PS4_SYSV_ABI Func_526287664A493981(); +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9(); +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D(); +s32 PS4_SYSV_ABI Func_569E2ECD34290F45(); +s32 PS4_SYSV_ABI Func_5A04775B6BE47685(); +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750(); +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4(); +s32 PS4_SYSV_ABI Func_6F9010294D822367(); +s32 PS4_SYSV_ABI Func_7757E947423A7A67(); +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA(); +s32 PS4_SYSV_ABI Func_93B36DEA021311D6(); +s32 PS4_SYSV_ABI Func_94B0891E7111598A(); +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD(); +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1(); +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA(); +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F(); +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D(); +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7(); +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33(); +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14(); +s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Font From e3e8561d58f6da0e487cf81e6ca177c11dccb142 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 22 Nov 2025 01:14:32 +0200 Subject: [PATCH 24/51] font.cpp Added full glyph tracking/outline plumbing plus mutex-protected generated-glyph tracking and helpers (BuildTrueOutline, true-outline extraction, glyf detection, system-font cache/fallback logic, etc.). Swapped OTF defaults for several JP/JP_AR sets (and CJK sets used by Death Stranding / Anywhere VR) to SSTAribStdB24-Regular.ttf or other TTFs so we load glyf faces. Wrapped every LOG_* call in INFO/DEBUG pairs: INFO just announces the call, DEBUG lists each parameter on its own line with an extra trailing newline; warnings/errors remain for validation/fallback. Implemented TTF detection for cache loading and fallback with logging. Renamed glyph param struct/members, moved glyph handle struct into the header, and renamed the glyph API parameters/fields accordingly. font.h Added the new glyph outline/glyph handle definitions and renamed OrbisFontGenerateGlyphParams members (id, res0, form_options, mem, res1/2). Updated function prototypes to use the renamed types and pointers (e.g., sceFontGenerateCharGlyph signature now matches the new names). fontft.cpp Reworked sceFontSelectLibraryFt/sceFontSelectRendererFt logging to follow the same INFO/DEBUG pattern (INFO announces that selection is requested, DEBUG dumps all params). --- src/core/libraries/font/font.cpp | 786 ++++++++++++++++++++++++----- src/core/libraries/font/font.h | 166 +++--- src/core/libraries/font/fontft.cpp | 14 +- 3 files changed, 764 insertions(+), 202 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index b6853dc7c..2ae21fdbd 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,13 @@ struct FontLibOpaque; namespace { Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); +using Libraries::Font::OrbisFontGenerateGlyphParams; +using Libraries::Font::OrbisFontGlyph; +using Libraries::Font::OrbisFontGlyphMetrics; +using Libraries::Font::OrbisFontGlyphOpaque; +using Libraries::Font::OrbisFontGlyphOutline; +using Libraries::Font::OrbisFontGlyphOutlinePoint; +using Libraries::Font::OrbisFontMem; using Libraries::Font::OrbisFontStyleFrame; struct GlyphEntry { @@ -104,6 +112,151 @@ static std::unordered_map g_style_for_surface; +static std::once_flag g_system_font_once; +static bool g_system_font_available = false; +static std::vector g_system_font_bytes; +static std::filesystem::path g_system_font_path; + +static bool EnsureSystemFontBlob(); +static bool HasTrueTypeGlyf(const std::vector& bytes); +static FontState* TryGetState(Libraries::Font::OrbisFontHandle h); + +struct GeneratedGlyph { + OrbisFontGlyphOpaque glyph{}; + OrbisFontGlyphMetrics metrics{}; + u32 codepoint = 0; + Libraries::Font::OrbisFontHandle owner_handle{}; + OrbisFontGlyphOutline outline{}; + std::vector outline_points; + std::vector outline_tags; + std::vector outline_contours; + bool outline_initialized = false; +}; + +static std::mutex g_generated_glyph_mutex; +static std::unordered_set g_generated_glyphs; + +static void TrackGeneratedGlyph(OrbisFontGlyph glyph) { + std::scoped_lock lock(g_generated_glyph_mutex); + g_generated_glyphs.insert(glyph); +} + +static bool ForgetGeneratedGlyph(OrbisFontGlyph glyph) { + std::scoped_lock lock(g_generated_glyph_mutex); + return g_generated_glyphs.erase(glyph) > 0; +} + +static void BuildBoundingOutline(GeneratedGlyph& gg) { + const float left = gg.metrics.h_bearing_x; + const float top = gg.metrics.h_bearing_y; + const float right = gg.metrics.h_bearing_x + gg.metrics.w; + const float bottom = gg.metrics.h_bearing_y - gg.metrics.h; + + gg.outline_points.clear(); + gg.outline_tags.clear(); + gg.outline_contours.clear(); + + gg.outline_points.push_back({left, top}); + gg.outline_points.push_back({right, top}); + gg.outline_points.push_back({right, bottom}); + gg.outline_points.push_back({left, bottom}); + + gg.outline_tags.resize(gg.outline_points.size(), 1); // on-curve flags + gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); + + gg.outline.points_ptr = gg.outline_points.data(); + gg.outline.tags_ptr = gg.outline_tags.data(); + gg.outline.contour_end_idx = gg.outline_contours.data(); + gg.outline.points_cnt = static_cast(gg.outline_points.size()); + gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); + gg.outline_initialized = true; +} + +static bool BuildTrueOutline(GeneratedGlyph& gg) { + const auto* st = TryGetState(gg.owner_handle); + if (!st || !st->ext_face_ready) { + return false; + } + auto* face = &st->ext_face; + const int glyph_index = stbtt_FindGlyphIndex(face, static_cast(gg.codepoint)); + if (glyph_index <= 0) { + return false; + } + + stbtt_vertex* verts = nullptr; + const int count = stbtt_GetGlyphShape(face, glyph_index, &verts); + if (count <= 0 || !verts) { + return false; + } + + gg.outline_points.clear(); + gg.outline_tags.clear(); + gg.outline_contours.clear(); + + const float scale = st->ext_scale_for_height == 0.0f + ? stbtt_ScaleForPixelHeight(face, st->scale_h) + : st->ext_scale_for_height; + + auto push_point = [&](float x, float y, u8 tag) { + gg.outline_points.push_back({x * scale, y * scale}); + gg.outline_tags.push_back(tag); + }; + + int last_start = -1; + for (int i = 0; i < count; ++i) { + const stbtt_vertex& v = verts[i]; + switch (v.type) { + case STBTT_vmove: + if (last_start >= 0 && !gg.outline_points.empty()) { + gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); + } + last_start = static_cast(gg.outline_points.size()); + push_point(static_cast(v.x), static_cast(v.y), 1); + break; + case STBTT_vline: + push_point(static_cast(v.x), static_cast(v.y), 1); + break; + case STBTT_vcurve: + push_point(static_cast(v.cx), static_cast(v.cy), 0); + push_point(static_cast(v.x), static_cast(v.y), 1); + break; + case STBTT_vcubic: + // Approximate cubic with two off-curve controls then on-curve end. + push_point(static_cast(v.cx), static_cast(v.cy), 0); + push_point(static_cast(v.cx1), static_cast(v.cy1), 0); + push_point(static_cast(v.x), static_cast(v.y), 1); + break; + default: + break; + } + } + + if (last_start >= 0 && !gg.outline_points.empty()) { + gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); + } + + const bool ok = !gg.outline_points.empty() && !gg.outline_contours.empty(); + if (ok) { + gg.outline.points_ptr = gg.outline_points.data(); + gg.outline.tags_ptr = gg.outline_tags.data(); + gg.outline.contour_end_idx = gg.outline_contours.data(); + gg.outline.points_cnt = static_cast(gg.outline_points.size()); + gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); + gg.outline_initialized = true; + } + + stbtt_FreeShape(face, verts); + return ok; +} + +static u16 ClampToU16(float value) { + if (value <= 0.0f) { + return 0; + } + const float clamped = std::min(value, static_cast(std::numeric_limits::max())); + return static_cast(std::lround(clamped)); +} + static FontState& GetState(Libraries::Font::OrbisFontHandle h) { return g_font_state[h]; } @@ -207,8 +360,11 @@ static ScaleMode GetScaleMode() { mode = ScaleMode::EmSquare; } } - LOG_INFO(Lib_Font, "FontScaleMode: {}", - mode == ScaleMode::EmSquare ? "em-square" : "ascender-height"); + LOG_INFO(Lib_Font, "scale mode initialized"); + LOG_DEBUG(Lib_Font, + "scale mode params:\n" + " mode={}\n", + mode == ScaleMode::EmSquare ? "em-square" : "ascender-height"); } return mode; } @@ -226,7 +382,11 @@ static float GetScaleBias() { } } if (bias != 1.0f) { - LOG_INFO(Lib_Font, "FontScaleBias: {}", bias); + LOG_INFO(Lib_Font, "scale bias initialized"); + LOG_DEBUG(Lib_Font, + "scale bias params:\n" + " bias={}\n", + bias); } } return bias; @@ -450,12 +610,12 @@ static constexpr SystemFontDefinition kSystemFontDefinitions[] = { {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "SSTThai-Roman.otf"}, {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTJpPro-Regular.otf"}, - {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTAribStdB24-Regular.ttf"}, + {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTAribStdB24-Regular.ttf"}, {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTVietnamese-Roman.otf"}, {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTJpPro-Regular.otf"}, - {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTAribStdB24-Regular.ttf"}, {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTThai-Roman.otf"}, {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTAribStdB24-Regular.ttf"}, @@ -466,7 +626,7 @@ static constexpr SystemFontDefinition kSystemFontDefinitions[] = { {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, - {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTJpPro-Regular.otf"}, + {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTAribStdB24-Regular.ttf"}, {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Bold.otf"}, {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTThai-Roman.otf"}, {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, @@ -476,7 +636,7 @@ static constexpr SystemFontDefinition kSystemFontDefinitions[] = { {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "e046323ms.ttf"}, {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "e046323ms.ttf"}, {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "n023055ms.ttf"}, - {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "n023055ms.ttf"}, + {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "SSTAribStdB24-Regular.ttf"}, {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "SSTThai-Roman.otf"}, {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "SSTThai-Roman.otf"}, @@ -625,20 +785,54 @@ static const FontSetCache* EnsureFontSetCache(u32 font_set_type) { cache.loaded = true; const auto path = ResolveSystemFontPath(font_set_type); if (!path.empty() && LoadFontFile(path, cache.bytes)) { - cache.available = true; - cache.path = path; + if (!HasTrueTypeGlyf(cache.bytes)) { + LOG_WARNING(Lib_Font, + "SystemFonts: '{}' lacks 'glyf' table (likely CFF) -> falling back", + path.string()); + cache.bytes.clear(); + cache.available = false; + } else { + cache.available = true; + cache.path = path; + } } else { cache.available = false; LOG_ERROR(Lib_Font, "SystemFonts: failed to load font for set type=0x{:08X} path='{}'", font_set_type, path.string()); } + if (!cache.available) { + if (EnsureSystemFontBlob()) { + cache.bytes = g_system_font_bytes; + cache.path = g_system_font_path; + cache.available = true; + LOG_WARNING(Lib_Font, "SystemFonts: using fallback font '{}' for type 0x{:08X}", + cache.path.string(), font_set_type); + } + } } return cache.available ? &cache : nullptr; } -static std::once_flag g_system_font_once; -static bool g_system_font_available = false; -static std::vector g_system_font_bytes; -static std::filesystem::path g_system_font_path; + +static bool HasTrueTypeGlyf(const std::vector& bytes) { + if (bytes.size() < 12) { + return false; + } + const u8* ptr = bytes.data(); + const u32 num_tables = (ptr[4] << 8) | ptr[5]; + constexpr u32 kDirEntrySize = 16; + if (bytes.size() < 12 + static_cast(num_tables) * kDirEntrySize) { + return false; + } + const u8* dir = ptr + 12; + for (u32 i = 0; i < num_tables; ++i) { + const u8* entry = dir + i * kDirEntrySize; + const u32 tag = (entry[0] << 24) | (entry[1] << 16) | (entry[2] << 8) | entry[3]; + if (tag == (('g' << 24) | ('l' << 16) | ('y' << 8) | 'f')) { + return true; + } + } + return false; +} static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes) { if (path.empty()) { @@ -659,8 +853,12 @@ static bool LoadFontFromPath(const std::filesystem::path& path) { } g_system_font_available = true; g_system_font_path = path; - LOG_INFO(Lib_Font, "SystemFace: fallback font '{}' loaded ({} bytes)", - g_system_font_path.string(), g_system_font_bytes.size()); + LOG_INFO(Lib_Font, "system font fallback loaded"); + LOG_DEBUG(Lib_Font, + "system font fallback params:\n" + " path={}\n" + " size_bytes={}\n", + g_system_font_path.string(), g_system_font_bytes.size()); return true; } @@ -740,8 +938,12 @@ static bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle han } st.ext_face_ready = true; st.system_font_path = source_path; - LOG_INFO(Lib_Font, "SystemFace: handle={} now uses '{}'", static_cast(handle), - source_path.string()); + LOG_INFO(Lib_Font, "system font attached"); + LOG_DEBUG(Lib_Font, + "system font attach params:\n" + " handle={}\n" + " path={}\n", + static_cast(handle), source_path.string()); return true; } @@ -859,10 +1061,16 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff state.owned_device_cache_size = 0; } - LOG_INFO(Lib_Font, - "AttachDeviceCacheBuffer: library={} buffer={} size={} pages={} owned_buffer={}", - static_cast(library), buffer, size, page_count, - owned_buffer ? "true" : "false"); + LOG_INFO(Lib_Font, "device cache attach requested"); + LOG_DEBUG(Lib_Font, + "device cache attach params:\n" + " library={}\n" + " buffer={}\n" + " size={}\n" + " pages={}\n" + " owned_buffer={}\n", + static_cast(library), buffer, size, page_count, + owned_buffer ? "true" : "false"); return ORBIS_OK; } @@ -872,8 +1080,12 @@ s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRender } auto& st = GetState(fontHandle); st.bound_renderer = renderer; - LOG_INFO(Lib_Font, "BindRenderer: handle={} renderer={}", static_cast(fontHandle), - static_cast(renderer)); + LOG_INFO(Lib_Font, "renderer bind requested"); + LOG_DEBUG(Lib_Font, + "renderer bind params:\n" + " handle={}\n" + " renderer={}\n", + static_cast(fontHandle), static_cast(renderer)); return ORBIS_OK; } @@ -1007,10 +1219,24 @@ s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, OrbisFontLibCreateParams create_params, u64 edition, OrbisFontLib* pLibrary) { + LOG_INFO(Lib_Font, "library create requested"); + LOG_DEBUG(Lib_Font, + "library create params:\n" + " memory={}\n" + " create_params={}\n" + " edition={}\n" + " out_library_ptr={}\n", + static_cast(memory), static_cast(create_params), edition, + static_cast(pLibrary)); + if (!pLibrary) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } if (!memory) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (memory->mem_kind != 0x0F00 || !memory->iface || !memory->iface->alloc || + !memory->iface->dealloc) { return ORBIS_FONT_ERROR_INVALID_MEMORY; } auto* lib = new (std::nothrow) FontLibOpaque{}; @@ -1024,9 +1250,9 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, lib->alloc_vtbl = g_allocator_vtbl_stub; lib->sys_driver = &g_sys_driver_stub; lib->fontset_registry = &g_fontset_registry_stub; - lib->sysfonts_ctx = &g_sysfonts_ctx_stub; - lib->external_fonts_ctx = &g_external_fonts_ctx_stub; - lib->device_cache_buf = &g_device_cache_stub; + lib->sysfonts_ctx = nullptr; + lib->external_fonts_ctx = nullptr; + lib->device_cache_buf = nullptr; *pLibrary = lib; auto& state = GetLibState(lib); state = LibraryState{}; @@ -1036,9 +1262,6 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, state.native = lib; state.owned_device_cache = nullptr; state.owned_device_cache_size = 0; - LOG_INFO(Lib_Font, "CreateLibrary: memory={} selection={} edition={} -> lib={}", - static_cast(memory), static_cast(create_params), edition, - static_cast(*pLibrary)); return ORBIS_OK; } @@ -1047,10 +1270,15 @@ s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto lib = *pLibrary; + LOG_INFO(Lib_Font, "library destroy requested"); + LOG_DEBUG(Lib_Font, + "library destroy params:\n" + " library_handle_ptr={}\n" + " library_handle={}\n", + static_cast(pLibrary), static_cast(lib)); RemoveLibState(lib); delete static_cast(lib); *pLibrary = nullptr; - LOG_INFO(Lib_Font, "DestroyLibrary: lib={} destroyed", static_cast(lib)); return ORBIS_OK; } @@ -1063,6 +1291,16 @@ s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer) { + LOG_INFO(Lib_Font, "renderer create requested"); + LOG_DEBUG(Lib_Font, + "renderer create params:\n" + " memory={}\n" + " create_params={}\n" + " edition={}\n" + " out_renderer_ptr={}\n", + static_cast(memory), static_cast(create_params), edition, + static_cast(pRenderer)); + if (!pRenderer) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -1100,8 +1338,36 @@ s32 PS4_SYSV_ABI sceFontDefineAttribute() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontDeleteGlyph() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(const OrbisFontMem* memory, OrbisFontGlyph* glyph) { + LOG_INFO(Lib_Font, "glyph delete requested"); + LOG_DEBUG(Lib_Font, + "glyph delete params:\n" + " memory={}\n" + " glyph_ptr={}\n", + static_cast(memory), + glyph ? static_cast(*glyph) : static_cast(nullptr)); + + if (!glyph) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + OrbisFontGlyph handle = *glyph; + if (!handle) { + return ORBIS_FONT_ERROR_INVALID_GLYPH; + } + + if (handle->magic != 0x0F03) { + return ORBIS_FONT_ERROR_INVALID_GLYPH; + } + + // We currently own the allocation; the memory pointer is only recorded for ABI compatibility. + (void)memory; + if (!ForgetGeneratedGlyph(handle)) { + return ORBIS_FONT_ERROR_INVALID_GLYPH; + } + + auto* boxed = reinterpret_cast(handle); + delete boxed; + *glyph = nullptr; return ORBIS_OK; } @@ -1120,10 +1386,14 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto renderer = *pRenderer; + LOG_INFO(Lib_Font, "renderer destroy requested"); + LOG_DEBUG(Lib_Font, + "renderer destroy params:\n" + " renderer_ptr={}\n" + " renderer={}\n", + static_cast(pRenderer), static_cast(renderer)); delete static_cast(renderer); *pRenderer = nullptr; - LOG_INFO(Lib_Font, "DestroyRenderer: renderer={} destroyed", - static_cast(renderer)); return ORBIS_OK; } @@ -1147,8 +1417,102 @@ s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGenerateCharGlyph() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 codepoint, + const OrbisFontGenerateGlyphParams* gen_params, + OrbisFontGlyph* glyph_out) { + LOG_INFO(Lib_Font, "glyph generation requested"); + LOG_DEBUG(Lib_Font, + "glyph generation params:\n" + " handle={}\n" + " codepoint={}\n" + " id=0x{:04X}\n" + " glyph_form={}\n" + " metrics_form={}\n" + " form_options=0x{:X}\n" + " mem={}\n", + static_cast(glyph_handle), codepoint, + gen_params ? static_cast(gen_params->id) : 0u, + gen_params ? static_cast(gen_params->glyph_form) : 0u, + gen_params ? static_cast(gen_params->metrics_form) : 0u, + gen_params ? static_cast(gen_params->form_options) : 0u, + gen_params ? static_cast(gen_params->mem) + : static_cast(nullptr)); + + if (!glyph_out) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + *glyph_out = nullptr; + + if (!glyph_handle) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (codepoint == 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + + auto* st = TryGetState(glyph_handle); + if (!st) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const u8 glyph_form = gen_params ? gen_params->glyph_form : 0; + const u8 metrics_form = gen_params ? gen_params->metrics_form : 0; + const u16 form_options = gen_params ? gen_params->form_options : 0; + const OrbisFontMem* glyph_memory = gen_params ? gen_params->mem : nullptr; + + if (gen_params && gen_params->id != 0x0FD3) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if ((form_options & static_cast(~0x11)) != 0) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (glyph_form == 0 && metrics_form != 0) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if ((glyph_form != 0 && metrics_form == 0) || glyph_form > 1 || metrics_form > 4) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (glyph_memory && glyph_memory->mem_kind != 0xF00) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + OrbisFontGlyphMetrics metrics{}; + const s32 metrics_res = sceFontGetCharGlyphMetrics(glyph_handle, codepoint, &metrics); + if (metrics_res != ORBIS_OK) { + return metrics_res; + } + + auto* boxed = new (std::nothrow) GeneratedGlyph(); + if (!boxed) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + + boxed->codepoint = codepoint; + boxed->metrics = metrics; + boxed->glyph.magic = 0x0F03; + boxed->glyph.flags = form_options; + boxed->glyph.glyph_form = glyph_form; + boxed->glyph.metrics_form = metrics_form; + boxed->glyph.em_size = ClampToU16(st->scale_h); + boxed->glyph.baseline = ClampToU16(metrics.h_bearing_y); + boxed->glyph.height_px = ClampToU16(metrics.h); + boxed->glyph.origin_x = 0; + boxed->glyph.origin_y = boxed->glyph.baseline; + boxed->glyph.scale_x = st->scale_w; + boxed->glyph.base_scale = st->scale_h; + boxed->glyph.memory = glyph_memory; + boxed->owner_handle = glyph_handle; + boxed->outline = {}; + boxed->outline.outline_flags = boxed->glyph.flags; + boxed->outline.points_ptr = nullptr; + boxed->outline.tags_ptr = nullptr; + boxed->outline.contour_end_idx = nullptr; + boxed->outline.contours_cnt = 0; + boxed->outline.points_cnt = 0; + boxed->outline_initialized = false; + + TrackGeneratedGlyph(&boxed->glyph); + *glyph_out = &boxed->glyph; return ORBIS_OK; } @@ -1573,9 +1937,23 @@ s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGlyphRefersOutline() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +OrbisFontGlyphOutline* PS4_SYSV_ABI sceFontGlyphRefersOutline(OrbisFontGlyph glyph) { + if (!glyph || glyph->magic != 0x0F03 || glyph->glyph_form != 1) { + return nullptr; + } + { + std::scoped_lock lock(g_generated_glyph_mutex); + if (g_generated_glyphs.find(glyph) == g_generated_glyphs.end()) { + return nullptr; + } + } + auto* boxed = reinterpret_cast(glyph); + if (!boxed->outline_initialized) { + if (!BuildTrueOutline(*boxed)) { + BuildBoundingOutline(*boxed); + } + } + return &boxed->outline; } s32 PS4_SYSV_ABI sceFontGlyphRenderImage() { @@ -1849,8 +2227,11 @@ s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u3 if (!mem_desc) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } + if (!iface && (!region_addr || region_size == 0)) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } - mem_desc->mem_kind = 0; + mem_desc->mem_kind = 0x0F00; mem_desc->attr_bits = 0; mem_desc->region_base = region_addr; mem_desc->region_size = region_size; @@ -1861,12 +2242,18 @@ s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u3 mem_desc->some_ctx1 = nullptr; mem_desc->some_ctx2 = nullptr; - LOG_INFO( - Lib_Font, - "FontMemoryInit: font_mem={} region_base={} size={} mspace={} mem_if_set={} destroy_cb={}", - static_cast(mem_desc), static_cast(region_addr), region_size, - static_cast(mem_desc->mspace_handle), iface != nullptr, - reinterpret_cast(destroy_cb)); + LOG_INFO(Lib_Font, "font memory init requested"); + LOG_DEBUG(Lib_Font, + "font memory init params:\n" + " mem_desc={}\n" + " region_addr={}\n" + " region_size={}\n" + " mspace_obj={}\n" + " has_iface={}\n" + " destroy_cb={}\n", + static_cast(mem_desc), static_cast(region_addr), + region_size, static_cast(mspace_obj), iface != nullptr, + reinterpret_cast(destroy_cb)); return ORBIS_OK; } @@ -1874,11 +2261,18 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { if (!mem_desc) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } + if (mem_desc->mem_kind != 0x0F00) { + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } if (mem_desc->on_destroy) { mem_desc->on_destroy(mem_desc, mem_desc->destroy_ctx, mem_desc->some_ctx1); } std::memset(mem_desc, 0, sizeof(*mem_desc)); - LOG_INFO(Lib_Font, "FontMemoryTerm: font_mem={} cleaned", static_cast(mem_desc)); + LOG_INFO(Lib_Font, "font memory term requested"); + LOG_DEBUG(Lib_Font, + "font memory term params:\n" + " mem_desc={}\n", + static_cast(mem_desc)); return ORBIS_OK; } @@ -1942,12 +2336,19 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } const std::size_t src_ext_bytes = src_state->ext_face_data.size(); - LOG_DEBUG( - Lib_Font, - "OpenFontInstance: template state summary ext_ready={} bytes={} cache={} dpi=({}, {}) " - "scale=({}, {}) scale_point_active={}", - src_state->ext_face_ready, src_ext_bytes, src_state->ext_cache.size(), src_state->dpi_x, - src_state->dpi_y, src_state->scale_w, src_state->scale_h, src_state->scale_point_active); + LOG_DEBUG(Lib_Font, + "font instance clone template state:\n" + " ext_ready={}\n" + " bytes={}\n" + " cache_size={}\n" + " dpi_x={}\n" + " dpi_y={}\n" + " scale_w={}\n" + " scale_h={}\n" + " scale_point_active={}\n", + src_state->ext_face_ready, src_ext_bytes, src_state->ext_cache.size(), + src_state->dpi_x, src_state->dpi_y, src_state->scale_w, src_state->scale_h, + src_state->scale_point_active); auto& dst = GetState(new_handle); dst = *src_state; dst.ext_face = {}; @@ -1959,16 +2360,17 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa bool stbtt_ok = false; if (dst.ext_face_ready && !dst.ext_face_data.empty()) { const int font_offset = src_state->ext_face.fontstart; - LOG_INFO( - Lib_Font, - "OpenFontInstance: reinitializing stbtt font template={} -> new={} offset={} size={}", - static_cast(templateFont), static_cast(new_handle), - font_offset, dst.ext_face_data.size()); + LOG_INFO(Lib_Font, "reinitializing stbtt font for clone"); + LOG_DEBUG(Lib_Font, + "stbtt reinit params:\n" + " template_handle={}\n" + " new_handle={}\n" + " offset={}\n" + " data_size={}\n", + static_cast(templateFont), static_cast(new_handle), + font_offset, dst.ext_face_data.size()); if (stbtt_InitFont(&dst.ext_face, dst.ext_face_data.data(), font_offset) == 0) { - LOG_WARNING(Lib_Font, - "OpenFontInstance: stbtt_InitFont failed when cloning handle={} -> new={}", - static_cast(templateFont), - static_cast(new_handle)); + LOG_WARNING(Lib_Font, "stbtt_InitFont failed during clone"); dst.ext_face_ready = false; dst.ext_cache.clear(); } else { @@ -1976,27 +2378,37 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa stbtt_ok = true; } } else { - LOG_DEBUG( - Lib_Font, - "OpenFontInstance: template handle={} had no external face data (ready={} size={})", - static_cast(templateFont), dst.ext_face_ready, dst.ext_face_data.size()); + LOG_DEBUG(Lib_Font, + "font instance clone missing external face:\n" + " template_handle={}\n" + " ready={}\n" + " data_size={}\n", + static_cast(templateFont), dst.ext_face_ready, + dst.ext_face_data.size()); dst.ext_face_ready = false; } dst.layout_cached = false; *pFontHandle = new_handle; - LOG_INFO(Lib_Font, - "OpenFontInstance: base={} template={} -> new={} ext_ready={} renderer_inherited={} " - "stbtt_reinit={}", - static_cast(fontHandle), static_cast(templateFont), - static_cast(new_handle), dst.ext_face_ready, - dst.bound_renderer != nullptr, stbtt_ok); - LOG_DEBUG( - Lib_Font, - "OpenFontInstance: result summary new={} library={} renderer={} dpi=({}, {}) scale=({}, " - "{})", - static_cast(new_handle), static_cast(dst.library), - static_cast(dst.bound_renderer), dst.dpi_x, dst.dpi_y, dst.scale_w, - dst.scale_h); + LOG_INFO(Lib_Font, "font instance clone requested"); + LOG_DEBUG(Lib_Font, + "font instance clone result:\n" + " base_handle={}\n" + " template_handle={}\n" + " new_handle={}\n" + " ext_ready={}\n" + " renderer_inherited={}\n" + " stbtt_reinit={}\n" + " library={}\n" + " renderer={}\n" + " dpi_x={}\n" + " dpi_y={}\n" + " scale_w={}\n" + " scale_h={}\n", + static_cast(fontHandle), static_cast(templateFont), + static_cast(new_handle), dst.ext_face_ready, + dst.bound_renderer != nullptr, stbtt_ok, static_cast(dst.library), + static_cast(dst.bound_renderer), dst.dpi_x, dst.dpi_y, dst.scale_w, + dst.scale_h); return ORBIS_OK; } @@ -2016,13 +2428,22 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd st.system_font_path.clear(); const unsigned char* p = reinterpret_cast(fontAddress); auto& ls = GetLibState(library); - LOG_INFO(Lib_Font, - "OpenFontMemory: lib={} size={} open_params={} handle={} sig='{}{}{}{}' " - "ext_supported={} formats=0x{:X}", - static_cast(library), fontSize, static_cast(open_params), - static_cast(*pFontHandle), (fontSize >= 1 ? (char)p[0] : '?'), - (fontSize >= 2 ? (char)p[1] : '?'), (fontSize >= 3 ? (char)p[2] : '?'), - (fontSize >= 4 ? (char)p[3] : '?'), ls.support_external, ls.external_formats); + LOG_INFO(Lib_Font, "font memory open requested"); + LOG_DEBUG(Lib_Font, + "font memory open params:\n" + " library={}\n" + " font_address={}\n" + " font_size={}\n" + " open_params={}\n" + " out_handle={}\n" + " sig='{}{}{}{}'\n" + " external_supported={}\n" + " external_formats=0x{:X}", + static_cast(library), fontAddress, fontSize, + static_cast(open_params), static_cast(*pFontHandle), + (fontSize >= 1 ? (char)p[0] : '?'), (fontSize >= 2 ? (char)p[1] : '?'), + (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), + ls.support_external, ls.external_formats); st.ext_face_data.assign(reinterpret_cast(fontAddress), reinterpret_cast(fontAddress) + fontSize); int font_count = stbtt_GetNumberOfFonts(st.ext_face_data.data()); @@ -2054,21 +2475,32 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd st.ext_face_ready = true; stbtt_GetFontVMetrics(&st.ext_face, &st.ext_ascent, &st.ext_descent, &st.ext_lineGap); st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - LOG_INFO(Lib_Font, - "ExternalFace: handle={} ascent={} descent={} lineGap={} scale={} (data={} bytes)" - " fonts={} chosen_index={} ttc={} sig=0x{:08X} kind={}", - static_cast(*pFontHandle), st.ext_ascent, st.ext_descent, - st.ext_lineGap, st.ext_scale_for_height, (int)st.ext_face_data.size(), font_count, - chosen_index, is_ttc, sig32, - is_otf_cff ? "OTF/CFF (unsupported)" - : (is_ttf_sfnt ? "TTF (ready)" - : (is_sfnt_typ1 ? "Type1(sfnt) (stub)" : "unknown"))); + LOG_INFO(Lib_Font, "external face ready"); + LOG_DEBUG(Lib_Font, + "external face params:\n" + " handle={}\n" + " ascent={}\n" + " descent={}\n" + " line_gap={}\n" + " scale={}\n" + " data_bytes={}\n" + " font_count={}\n" + " chosen_index={}\n" + " ttc={}\n" + " sig=0x{:08X}\n" + " kind={}\n", + static_cast(*pFontHandle), st.ext_ascent, st.ext_descent, + st.ext_lineGap, st.ext_scale_for_height, (int)st.ext_face_data.size(), font_count, + chosen_index, is_ttc, sig32, + is_otf_cff ? "OTF/CFF (unsupported)" + : (is_ttf_sfnt ? "TTF (ready)" + : (is_sfnt_typ1 ? "Type1(sfnt) (stub)" : "unknown"))); if (is_ttf_sfnt) { - LOG_INFO(Lib_Font, "ExternalFormat: OpenType-TT (glyf) -> ready"); + LOG_INFO(Lib_Font, "external format TTF ready"); } else if (is_otf_cff) { - LOG_WARNING(Lib_Font, "ExternalFormat: OpenType-CFF -> stub (CFF unsupported)"); + LOG_WARNING(Lib_Font, "external format OpenType-CFF unsupported"); } else if (is_sfnt_typ1) { - LOG_WARNING(Lib_Font, "ExternalFormat: Type 1 (sfnt wrapper) -> stub"); + LOG_WARNING(Lib_Font, "external format Type1(sfnt) stub"); } } else { LOG_WARNING(Lib_Font, @@ -2088,30 +2520,53 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle) { - (void)fontSetType; - (void)openMode; - (void)open_params; + if (!library) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } if (!pFontHandle) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } + if (!lib->sysfonts_ctx) { + return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; + } auto* f = new OrbisFontHandleOpaque{}; *pFontHandle = f; auto& st = GetState(f); st.library = library; st.font_set_type = fontSetType; st.system_font_path.clear(); - LOG_INFO( - Lib_Font, - "OpenFontSet(request): lib={} fontSetType=0x{:08X} openMode={} open_params={} handle={}", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle)); + + LOG_INFO(Lib_Font, "font set open requested"); + LOG_DEBUG(Lib_Font, + "font set open params:\n" + " library={}\n" + " font_set_type=0x{:08X}\n" + " open_mode={}\n" + " open_params={}\n" + " out_handle={}\n", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle)); + if (const auto* def = FindSystemFontDefinition(fontSetType)) { const auto pretty = MacroToCamel(def->config_key); - LOG_INFO(Lib_Font, "OpenFontSet(mapping): type=0x{:08X} ({}) default='{}'", fontSetType, - !pretty.empty() ? pretty.c_str() : def->config_key, - def->default_file ? def->default_file : ""); + LOG_DEBUG(Lib_Font, + "font set mapping:\n" + " type=0x{:08X}\n" + " key={}\n" + " default_file='{}'\n", + fontSetType, !pretty.empty() ? pretty.c_str() : def->config_key, + def->default_file ? def->default_file : ""); } else { - LOG_INFO(Lib_Font, "OpenFontSet(mapping): type=0x{:08X} (unknown)", fontSetType); + LOG_DEBUG(Lib_Font, + "font set mapping:\n" + " type=0x{:08X}\n" + " key=(unknown)\n" + " default_file=''\n", + fontSetType); } LogFontOpenParams(open_params); bool system_ok = false; @@ -2120,12 +2575,17 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o LOG_ERROR(Lib_Font, "{}", sys_log); } - LOG_INFO(Lib_Font, - "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system " - "available={})", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle), - system_ok); + LOG_DEBUG(Lib_Font, + "font set open result:\n" + " library={}\n" + " font_set_type=0x{:08X}\n" + " open_mode={}\n" + " open_params={}\n" + " handle={}\n" + " system_available={}\n", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle), + system_ok); return ORBIS_OK; } @@ -2660,8 +3120,13 @@ s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, st.dpi_x = new_h; st.dpi_y = new_v; st.layout_cached = false; // PS4 clears cached metrics when the resolution changes. - LOG_INFO(Lib_Font, "SetResolutionDpi: handle={} h_dpi={} v_dpi={}", - static_cast(fontHandle), new_h, new_v); + LOG_INFO(Lib_Font, "resolution dpi set requested"); + LOG_DEBUG(Lib_Font, + "resolution dpi params:\n" + " handle={}\n" + " h_dpi={}\n" + " v_dpi={}\n", + static_cast(fontHandle), new_h, new_v); return ORBIS_OK; } @@ -2684,9 +3149,16 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float if (!sys_log.empty()) { LOG_ERROR(Lib_Font, "{}", sys_log); } - LOG_INFO(Lib_Font, "SetScalePixel: handle={} w={} h={} ext_scale={} ext_ready={}", - static_cast(fontHandle), w, h, st.ext_scale_for_height, - st.ext_face_ready); + LOG_INFO(Lib_Font, "scale pixel set requested"); + LOG_DEBUG(Lib_Font, + "scale pixel params:\n" + " handle={}\n" + " w={}\n" + " h={}\n" + " ext_scale={}\n" + " ext_ready={}\n", + static_cast(fontHandle), w, h, st.ext_scale_for_height, + st.ext_face_ready); st.layout_cached = false; return ORBIS_OK; } @@ -2725,10 +3197,19 @@ s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float LOG_ERROR(Lib_Font, "{}", sys_log); } st.layout_cached = false; - LOG_INFO(Lib_Font, - "SetScalePoint: handle={} point=({}, {}) -> pixel=({}, {}) dpi=({}, {}) attached={}", - static_cast(fontHandle), w, h, st.scale_w, st.scale_h, st.dpi_x, st.dpi_y, - system_attached); + LOG_INFO(Lib_Font, "scale point set requested"); + LOG_DEBUG(Lib_Font, + "scale point params:\n" + " handle={}\n" + " point_w={}\n" + " point_h={}\n" + " pixel_w={}\n" + " pixel_h={}\n" + " dpi_x={}\n" + " dpi_y={}\n" + " system_attached={}\n", + static_cast(fontHandle), w, h, st.scale_w, st.scale_h, st.dpi_x, + st.dpi_y, system_attached); return ORBIS_OK; } @@ -2754,15 +3235,25 @@ s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h) { auto rc = sceFontSetScalePixel(fontHandle, w, h); - LOG_INFO(Lib_Font, "SetupRenderScalePixel: handle={} w={} h={}", - static_cast(fontHandle), w, h); + LOG_INFO(Lib_Font, "render scale pixel setup requested"); + LOG_DEBUG(Lib_Font, + "render scale pixel setup params:\n" + " handle={}\n" + " w={}\n" + " h={}\n", + static_cast(fontHandle), w, h); return rc; } s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h) { auto rc = sceFontSetScalePoint(fontHandle, w, h); - LOG_INFO(Lib_Font, "SetupRenderScalePoint: handle={} w={} h={}", - static_cast(fontHandle), w, h); + LOG_INFO(Lib_Font, "render scale point setup requested"); + LOG_DEBUG(Lib_Font, + "render scale point setup params:\n" + " handle={}\n" + " w={}\n" + " h={}\n", + static_cast(fontHandle), w, h); return rc; } @@ -2968,14 +3459,17 @@ s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(OrbisFontStyleFrame* styleFrame) { } s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats) { - LOG_INFO(Lib_Font, "SupportExternalFonts(begin): lib={} fontMax={} formats=0x{:X}", - static_cast(library), fontMax, formats); + LOG_INFO(Lib_Font, "external font support requested"); + LOG_DEBUG(Lib_Font, + "external font support params:\n" + " library={}\n" + " font_max={}\n" + " formats_mask=0x{:X}\n", + static_cast(library), fontMax, formats); auto& ls = GetLibState(library); ls.support_external = true; ls.external_fontMax = fontMax; ls.external_formats = formats; - LOG_INFO(Lib_Font, "SupportExternalFonts: lib={} fontMax={} formats=0x{:X}", - static_cast(library), fontMax, formats); LogExternalFormatSupport(formats); return ORBIS_OK; } @@ -2986,10 +3480,28 @@ s32 PS4_SYSV_ABI sceFontSupportGlyphs() { } s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { - LOG_INFO(Lib_Font, "SupportSystemFonts(begin): lib={}", static_cast(library)); + LOG_INFO(Lib_Font, "system font support requested"); + LOG_DEBUG(Lib_Font, + "system font support params:\n" + " library={}\n", + static_cast(library)); + + if (!library) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + auto& ls = GetLibState(library); + if (ls.support_system) { + return ORBIS_FONT_ERROR_ALREADY_SPECIFIED; + } + + // Real implementation allocates a small system-font context; we just mark it available. + lib->sysfonts_ctx = lib->sysfonts_ctx ? lib->sysfonts_ctx : &g_sysfonts_ctx_stub; ls.support_system = true; - LOG_INFO(Lib_Font, "SupportSystemFonts: lib={} system=on", static_cast(library)); return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 363ccdb86..e7dc31982 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -12,17 +12,19 @@ struct OrbisFontHandleOpaque { u32 reserved[64]; }; -using OrbisFontLib = void*; -using OrbisFontHandle = OrbisFontHandleOpaque*; -using OrbisFontRendererCreateParams = void*; -using OrbisFontRenderer = void*; -using OrbisFontLibCreateParams = void*; - -struct OrbisFontOpenParams { - u16 tag; - u16 pad16; - u32 flags; - u32 subfont_index; +using OrbisFontLib = void*; +using OrbisFontHandle = OrbisFontHandleOpaque*; +using OrbisFontRendererCreateParams = void*; +using OrbisFontRenderer = void*; +using OrbisFontLibCreateParams = void*; + +struct OrbisFontMem; + +struct OrbisFontOpenParams { + u16 tag; + u16 pad16; + u32 flags; + u32 subfont_index; s32 unique_id; const void* reserved_ptr2; const void* reserved_ptr1; @@ -46,20 +48,62 @@ struct OrbisFontKerning { float py; }; -struct OrbisFontGlyphImageMetrics { - float bearing_x; - float bearing_y; - float dv; - float stride; - u32 width; - u32 height; -}; - -struct OrbisFontResultStage { - u8* p_00; - u32 u32_08; - u32 u32_0C; - u32 u32_10; +struct OrbisFontGlyphImageMetrics { + float bearing_x; + float bearing_y; + float dv; + float stride; + u32 width; + u32 height; +}; + +struct OrbisFontGenerateGlyphParams { + u16 id; + u16 res0; + u16 form_options; + u8 glyph_form; + u8 metrics_form; + const OrbisFontMem* mem; + void* res1; + void* res2; +}; + +struct OrbisFontGlyphOutlinePoint { + float x; + float y; +}; + +struct OrbisFontGlyphOutline { + s16 contours_cnt; + s16 points_cnt; + u32 outline_flags; + OrbisFontGlyphOutlinePoint* points_ptr; + u8* tags_ptr; + u16* contour_end_idx; +}; + +struct OrbisFontGlyphOpaque { + u16 magic; + u16 flags; + u8 glyph_form; + u8 metrics_form; + u16 em_size; + u16 baseline; + u16 height_px; + u16 origin_x; + u16 origin_y; + float scale_x; + float base_scale; + const OrbisFontMem* memory; +}; + +using OrbisFontGlyph = OrbisFontGlyphOpaque*; + +struct OrbisFontResultStage { + u8* p_00; + u32 u32_08; + u32 u32_0C; + u32 u32_10; }; struct OrbisFontResultSlot { @@ -69,23 +113,21 @@ struct OrbisFontResultSlot { u8 maybe_pixelFmt; }; -struct OrbisFontRenderOutput { - const OrbisFontResultStage* stage; - OrbisFontResultSlot slot; - u32 new_x; - u32 new_y; - u32 new_w; - u32 new_h; - OrbisFontGlyphImageMetrics ImageMetrics; -}; - -struct OrbisFontMem; - -using OrbisFontAllocFn = void*(void* object, u32 size); -using OrbisFontFreeFn = void(void* object, void* p); -using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); -using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); -using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, +struct OrbisFontRenderOutput { + const OrbisFontResultStage* stage; + OrbisFontResultSlot slot; + u32 new_x; + u32 new_y; + u32 new_w; + u32 new_h; + OrbisFontGlyphImageMetrics ImageMetrics; +}; + +using OrbisFontAllocFn = void*(void* object, u32 size); +using OrbisFontFreeFn = void(void* object, void* p); +using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); +using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); +using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, u32 attr); using OrbisFontMspaceDestroyFn = void(void* parent, void* mspace); using OrbisFontMemDestroyFn = void(OrbisFontMem* fontMemory, void* object, void* destroyArg); @@ -216,20 +258,22 @@ s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer); -s32 PS4_SYSV_ABI sceFontCreateString(); -s32 PS4_SYSV_ABI sceFontCreateWords(); -s32 PS4_SYSV_ABI sceFontCreateWritingLine(); -s32 PS4_SYSV_ABI sceFontDefineAttribute(); -s32 PS4_SYSV_ABI sceFontDeleteGlyph(); +s32 PS4_SYSV_ABI sceFontCreateString(); +s32 PS4_SYSV_ABI sceFontCreateWords(); +s32 PS4_SYSV_ABI sceFontCreateWritingLine(); +s32 PS4_SYSV_ABI sceFontDefineAttribute(); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(const OrbisFontMem* memory, OrbisFontGlyph* glyph); s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer); s32 PS4_SYSV_ABI sceFontDestroyString(); -s32 PS4_SYSV_ABI sceFontDestroyWords(); -s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); -s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); -s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); +s32 PS4_SYSV_ABI sceFontDestroyWords(); +s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 codepoint, + const OrbisFontGenerateGlyphParams* gen_params, + OrbisFontGlyph* glyph_out); s32 PS4_SYSV_ABI sceFontGetAttribute(); s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, @@ -265,16 +309,16 @@ s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); -s32 PS4_SYSV_ABI sceFontGlyphRefersOutline(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); +OrbisFontGlyphOutline* PS4_SYSV_ABI sceFontGlyphRefersOutline(OrbisFontGlyph glyph); +s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index d997c46f3..9bd43054b 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -138,9 +138,12 @@ s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() { const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value) { if (!g_library_selected) { g_library_selected = true; - LOG_INFO(Lib_FontFt, "SelectLibraryFt: using default FreeType shim"); + LOG_INFO(Lib_FontFt, "library selection requested (FreeType shim)"); } - LOG_INFO(Lib_FontFt, "SelectLibraryFt: value={}", value); + LOG_DEBUG(Lib_FontFt, + "library selection params:\n" + " value={}\n", + value); if (value == 0) { return &kDefaultLibrarySelection; } @@ -150,9 +153,12 @@ const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value) const OrbisFontRendererSelection* PS4_SYSV_ABI sceFontSelectRendererFt(int value) { if (!g_renderer_selected) { g_renderer_selected = true; - LOG_INFO(Lib_FontFt, "SelectRendererFt: using stb_truetype renderer backend"); + LOG_INFO(Lib_FontFt, "renderer selection requested (stb_truetype backend)"); } - LOG_INFO(Lib_FontFt, "SelectRendererFt: value={}", value); + LOG_DEBUG(Lib_FontFt, + "renderer selection params:\n" + " value={}\n", + value); if (value == 0) { return &kDefaultRendererSelection; } From 7600090b9cd9cf64eb797545d417db7d83e8f9a5 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 22 Nov 2025 01:21:16 +0200 Subject: [PATCH 25/51] clang --- src/core/libraries/font/font.h | 210 ++++++++++++++++----------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index e7dc31982..2525e7c2b 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -12,19 +12,19 @@ struct OrbisFontHandleOpaque { u32 reserved[64]; }; -using OrbisFontLib = void*; -using OrbisFontHandle = OrbisFontHandleOpaque*; -using OrbisFontRendererCreateParams = void*; -using OrbisFontRenderer = void*; -using OrbisFontLibCreateParams = void*; - -struct OrbisFontMem; - -struct OrbisFontOpenParams { - u16 tag; - u16 pad16; - u32 flags; - u32 subfont_index; +using OrbisFontLib = void*; +using OrbisFontHandle = OrbisFontHandleOpaque*; +using OrbisFontRendererCreateParams = void*; +using OrbisFontRenderer = void*; +using OrbisFontLibCreateParams = void*; + +struct OrbisFontMem; + +struct OrbisFontOpenParams { + u16 tag; + u16 pad16; + u32 flags; + u32 subfont_index; s32 unique_id; const void* reserved_ptr2; const void* reserved_ptr1; @@ -48,62 +48,62 @@ struct OrbisFontKerning { float py; }; -struct OrbisFontGlyphImageMetrics { - float bearing_x; - float bearing_y; - float dv; - float stride; - u32 width; - u32 height; -}; - -struct OrbisFontGenerateGlyphParams { - u16 id; - u16 res0; - u16 form_options; - u8 glyph_form; - u8 metrics_form; - const OrbisFontMem* mem; - void* res1; - void* res2; -}; - -struct OrbisFontGlyphOutlinePoint { - float x; - float y; -}; - -struct OrbisFontGlyphOutline { - s16 contours_cnt; - s16 points_cnt; - u32 outline_flags; - OrbisFontGlyphOutlinePoint* points_ptr; - u8* tags_ptr; - u16* contour_end_idx; -}; - -struct OrbisFontGlyphOpaque { - u16 magic; - u16 flags; - u8 glyph_form; - u8 metrics_form; - u16 em_size; - u16 baseline; - u16 height_px; - u16 origin_x; - u16 origin_y; - float scale_x; - float base_scale; - const OrbisFontMem* memory; -}; - -using OrbisFontGlyph = OrbisFontGlyphOpaque*; - -struct OrbisFontResultStage { - u8* p_00; - u32 u32_08; - u32 u32_0C; - u32 u32_10; +struct OrbisFontGlyphImageMetrics { + float bearing_x; + float bearing_y; + float dv; + float stride; + u32 width; + u32 height; +}; + +struct OrbisFontGenerateGlyphParams { + u16 id; + u16 res0; + u16 form_options; + u8 glyph_form; + u8 metrics_form; + const OrbisFontMem* mem; + void* res1; + void* res2; +}; + +struct OrbisFontGlyphOutlinePoint { + float x; + float y; +}; + +struct OrbisFontGlyphOutline { + s16 contours_cnt; + s16 points_cnt; + u32 outline_flags; + OrbisFontGlyphOutlinePoint* points_ptr; + u8* tags_ptr; + u16* contour_end_idx; +}; + +struct OrbisFontGlyphOpaque { + u16 magic; + u16 flags; + u8 glyph_form; + u8 metrics_form; + u16 em_size; + u16 baseline; + u16 height_px; + u16 origin_x; + u16 origin_y; + float scale_x; + float base_scale; + const OrbisFontMem* memory; +}; + +using OrbisFontGlyph = OrbisFontGlyphOpaque*; + +struct OrbisFontResultStage { + u8* p_00; + u32 u32_08; + u32 u32_0C; + u32 u32_10; }; struct OrbisFontResultSlot { @@ -113,21 +113,21 @@ struct OrbisFontResultSlot { u8 maybe_pixelFmt; }; -struct OrbisFontRenderOutput { - const OrbisFontResultStage* stage; - OrbisFontResultSlot slot; - u32 new_x; - u32 new_y; - u32 new_w; - u32 new_h; - OrbisFontGlyphImageMetrics ImageMetrics; -}; - -using OrbisFontAllocFn = void*(void* object, u32 size); -using OrbisFontFreeFn = void(void* object, void* p); -using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); -using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); -using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, +struct OrbisFontRenderOutput { + const OrbisFontResultStage* stage; + OrbisFontResultSlot slot; + u32 new_x; + u32 new_y; + u32 new_w; + u32 new_h; + OrbisFontGlyphImageMetrics ImageMetrics; +}; + +using OrbisFontAllocFn = void*(void* object, u32 size); +using OrbisFontFreeFn = void(void* object, void* p); +using OrbisFontReallocFn = void*(void* object, void* p, u32 newSize); +using OrbisFontCallocFn = void*(void* object, u32 nBlock, u32 size); +using OrbisFontMspaceCreateFn = void*(void* parent, const char* name, void* address, u32 size, u32 attr); using OrbisFontMspaceDestroyFn = void(void* parent, void* mspace); using OrbisFontMemDestroyFn = void(OrbisFontMem* fontMemory, void* object, void* destroyArg); @@ -258,22 +258,22 @@ s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer); -s32 PS4_SYSV_ABI sceFontCreateString(); -s32 PS4_SYSV_ABI sceFontCreateWords(); -s32 PS4_SYSV_ABI sceFontCreateWritingLine(); -s32 PS4_SYSV_ABI sceFontDefineAttribute(); -s32 PS4_SYSV_ABI sceFontDeleteGlyph(const OrbisFontMem* memory, OrbisFontGlyph* glyph); +s32 PS4_SYSV_ABI sceFontCreateString(); +s32 PS4_SYSV_ABI sceFontCreateWords(); +s32 PS4_SYSV_ABI sceFontCreateWritingLine(); +s32 PS4_SYSV_ABI sceFontDefineAttribute(); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(const OrbisFontMem* memory, OrbisFontGlyph* glyph); s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary); s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer); s32 PS4_SYSV_ABI sceFontDestroyString(); -s32 PS4_SYSV_ABI sceFontDestroyWords(); -s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); -s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); -s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 codepoint, - const OrbisFontGenerateGlyphParams* gen_params, - OrbisFontGlyph* glyph_out); +s32 PS4_SYSV_ABI sceFontDestroyWords(); +s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 codepoint, + const OrbisFontGenerateGlyphParams* gen_params, + OrbisFontGlyph* glyph_out); s32 PS4_SYSV_ABI sceFontGetAttribute(); s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, @@ -309,16 +309,16 @@ s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); -s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); -s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); -OrbisFontGlyphOutline* PS4_SYSV_ABI sceFontGlyphRefersOutline(OrbisFontGlyph glyph); -s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); -s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); +OrbisFontGlyphOutline* PS4_SYSV_ABI sceFontGlyphRefersOutline(OrbisFontGlyph glyph); +s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); From 59785a020da0c4ab9ff8e7f53756e825d0bc56d1 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Mon, 24 Nov 2025 22:05:34 +0200 Subject: [PATCH 26/51] Scaling and font state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed env‑driven debug scaling (ScaleMode, GetScaleMode, GetScaleBias, SHADPS4_FONT_SCALE_*). Added FontState::is_otf_cff and storing of external vmetrics (ext_ascent, ext_descent, ext_lineGap). Introduced ComputeScaleExtPoint, ComputeScaleExtPixel, IsEmProfileExternalFont and ComputeScaleExtForState to choose ascender‑ vs EM‑based scaling per font (system fonts + CFF → ascender; selected TTF profiles → EM). Updated callers (sceFontGetCharGlyphMetrics, glyph cache, layout, renderer, clones, scale setters) to use ComputeScaleExtForState instead of ComputeScale / ComputeScaleExt. System font handling Added ReportSystemFaceRequest helper to centralize “attach system font or log why not” and avoid repeating the logic at each call site. Slightly adjusted when system_requested is set and how fallback/system attach errors are reported. Horizontal layout sceFontGetHorizontalLayout now logs invalid parameter/handle and, when an external face is present, uses that face’s vmetrics and the per‑state scale to compute baselineOffset and lineAdvance. (Fallback still uses the simple scale_h‑based approximation.) Glyph rendering (horizontal) Reworked sceFontRenderCharGlyphImageHorizontal: Resolves effective pixel scale from the attached style frame + point/pixel API via ResolveStyleFrameScale. Uses ComputeScaleExtForState to compute scale_y (and derived scale_x), rather than a single global scale. Always computes metrics from stbtt_GetCodepointHMetrics + GetCodepointBitmapBoxSubpixel, with metrics->h_bearing_* and h_adv in sync with those scales. Introduces a clearer origin/gravity model: System fonts: (x,y) treated as raw top‑left. Certain external TTFs (EM‑profile) : (x,y) treated as baseline. Other external fonts (CFF/point etc.): (x,y) treated as line‑top with baseline offset. Validates surfaces more strictly and returns NO_SUPPORT_SURFACE when buffer/size/bpp are invalid; supports only 1‑bpp and 4‑bpp. Uses a local glyph_bitmap and a straightforward blit with scissor/clipping, instead of the older mix of cached bitmaps/PUA logging. Removed PUA/placeholder glyph debug tracing and the more ad‑hoc baseline “adjusted_y” heuristic. Logging clean‑up Standardized LOG_INFO/LOG_DEBUG in sceFont* functions: Info: usually just "called" or a short description (“scale pixel set requested”, etc.). Debug: "parameters:\n"/"result:\n"/"template state:\n" followed by values on separate lines. LOG_ERROR messages no longer repeat the function name (logger already prints it); they now say "invalid parameter", "invalid font handle", "no support glyph (face/scale)", "no support surface (buffer)", etc. Added missing error logs before some early returns (allocation failures, page_count == 0, invalid parameters), and upgraded a few previous LOG_DEBUG “invalid params” to LOG_ERROR where they correspond to an error return. --- src/core/libraries/font/font.cpp | 840 +++++++++++++++---------------- 1 file changed, 419 insertions(+), 421 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 2ae21fdbd..00a6ebd35 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -76,6 +76,7 @@ struct FontState { std::filesystem::path system_font_path; Libraries::Font::OrbisFontLib library = nullptr; bool ext_face_ready = false; + bool is_otf_cff = false; std::vector ext_face_data; stbtt_fontinfo ext_face{}; float ext_scale_for_height = 0.0f; @@ -120,6 +121,8 @@ static std::filesystem::path g_system_font_path; static bool EnsureSystemFontBlob(); static bool HasTrueTypeGlyf(const std::vector& bytes); static FontState* TryGetState(Libraries::Font::OrbisFontHandle h); +static std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, + bool& attached_out); struct GeneratedGlyph { OrbisFontGlyphOpaque glyph{}; @@ -342,75 +345,76 @@ static bool LoadGuestFileBytes(const std::filesystem::path& host_path, return true; } -enum class ScaleMode { - AscenderHeight, - EmSquare, -}; - -static ScaleMode GetScaleMode() { - static int inited = 0; - static ScaleMode mode = ScaleMode::AscenderHeight; - if (!inited) { - inited = 1; - if (const char* env = std::getenv("SHADPS4_FONT_SCALE_MODE")) { - std::string v = env; - for (auto& c : v) - c = static_cast(std::tolower(c)); - if (v == "em" || v == "emsquare" || v == "em_square") { - mode = ScaleMode::EmSquare; - } - } - LOG_INFO(Lib_Font, "scale mode initialized"); - LOG_DEBUG(Lib_Font, - "scale mode params:\n" - " mode={}\n", - mode == ScaleMode::EmSquare ? "em-square" : "ascender-height"); - } - return mode; -} - -static float GetScaleBias() { - static int inited = 0; - static float bias = 1.0f; - if (!inited) { - inited = 1; - if (const char* env = std::getenv("SHADPS4_FONT_SCALE_BIAS")) { - try { - bias = std::max(0.01f, std::min(10.0f, std::stof(env))); - } catch (...) { - bias = 1.0f; - } - } - if (bias != 1.0f) { - LOG_INFO(Lib_Font, "scale bias initialized"); - LOG_DEBUG(Lib_Font, - "scale bias params:\n" - " bias={}\n", - bias); - } - } - return bias; -} - -static float ComputeScale(const stbtt_fontinfo* face, float pixel_h) { - if (!face) - return 0.0f; - float s = 0.0f; - if (GetScaleMode() == ScaleMode::EmSquare) - s = stbtt_ScaleForMappingEmToPixels(face, pixel_h); - else - s = stbtt_ScaleForPixelHeight(face, pixel_h); - return s * GetScaleBias(); -} - -// External face scale: keep legacy ascender-height mapping to avoid regressions -// in titles that ship their own fonts (e.g., CUSA47038). No EM toggle, no bias. -static float ComputeScaleExt(const stbtt_fontinfo* face, float pixel_h) { +// External face scale helpers. +// - Point scaling (sceFontSetScalePoint): match stbtt_ScaleForPixelHeight semantics. +// - Pixel scaling (sceFontSetScalePixel): match stbtt_ScaleForMappingEmToPixels / FreeType EM. +static float ComputeScaleExtPoint(const stbtt_fontinfo* face, float pixel_h) { if (!face) return 0.0f; return stbtt_ScaleForPixelHeight(face, pixel_h); } +static float ComputeScaleExtPixel(const stbtt_fontinfo* face, float pixel_h) { + if (!face) + return 0.0f; + return stbtt_ScaleForMappingEmToPixels(face, pixel_h); +} + +// Heuristic: identify external TrueType fonts that expect EM-based mapping +// (stbtt_ScaleForMappingEmToPixels) rather than ascender-height mapping. +// This is derived from observed ascent/descent/lineGap and blob sizes, but +// does not depend on the calling title. +static bool IsEmProfileExternalFont(const FontState& st) { + if (!st.ext_face_ready || st.ext_face_data.empty()) { + return false; + } + const std::size_t bytes = st.ext_face_data.size(); + const int asc = st.ext_ascent; + const int desc = st.ext_descent; + const int gap = st.ext_lineGap; + if (gap != 0) { + return false; + } + // Fonts observed in logs: + // - 81968 bytes, ascent=1989, descent=-469 + // - 3534360 bytes, ascent=901, descent=-123 + // - 1868660 bytes, ascent=800, descent=-200 + if (bytes == 81968 && asc == 1989 && desc == -469) { + return true; + } + if (bytes == 3534360 && asc == 901 && desc == -123) { + return true; + } + if (bytes == 1868660 && asc == 800 && desc == -200) { + return true; + } + return false; +} + +// Per-font-state external scale: +// - System fonts (font sets / fallbacks): ascender-height mapping (PixelHeight). +// - External CFF / point APIs: ascender-height mapping. +// - External TrueType + pixel scaling for certain profiles: EM-based mapping. +static float ComputeScaleExtForState(const FontState& st, const stbtt_fontinfo* face, + float pixel_h) { + if (!face) + return 0.0f; + const bool is_system_font = (st.font_set_type != 0) || !st.system_font_path.empty(); + if (is_system_font) { + return ComputeScaleExtPoint(face, pixel_h); + } + if (st.is_otf_cff || st.scale_point_active) { + return ComputeScaleExtPoint(face, pixel_h); + } + // Only a small subset of external TrueType fonts (Driveclub) require + // EM-based mapping; all others stay on ascender-height mapping to + // avoid regressions (Metaphor, etc.). + if (IsEmProfileExternalFont(st)) { + return ComputeScaleExtPixel(face, pixel_h); + } + return ComputeScaleExtPoint(face, pixel_h); +} + static constexpr float kPointsPerInch = 72.0f; static constexpr float kScaleEpsilon = 1e-4f; @@ -518,7 +522,7 @@ static void UpdateCachedLayout(FontState& st) { return; } if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); int asc = 0, desc = 0, gap = 0; stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); const float scale = st.ext_scale_for_height; @@ -526,8 +530,6 @@ static void UpdateCachedLayout(FontState& st) { st.layout_cached = true; } -static std::unordered_set g_logged_pua; - static inline std::uint64_t MakeGlyphKey(u32 code, int pixel_h) { return (static_cast(code) << 32) | static_cast(pixel_h); } @@ -548,6 +550,62 @@ static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* } } +static void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result) { + if (metrics) { + *metrics = {}; + } + if (result) { + *result = {}; + } +} + +static const GlyphEntry* GetGlyphEntry(FontState& st, Libraries::Font::OrbisFontHandle handle, + u32 code, const stbtt_fontinfo*& face_out, + float& scale_out) { + face_out = nullptr; + scale_out = 0.0f; + if (st.ext_face_ready) { + face_out = &st.ext_face; + } + if (!face_out) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, handle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face_out = &st.ext_face; + } + } + if (!face_out) { + return nullptr; + } + if (st.ext_scale_for_height == 0.0f) { + st.ext_scale_for_height = ComputeScaleExtForState(st, face_out, st.scale_h); + } + scale_out = st.ext_scale_for_height; + const int pixel_h = std::max(1, static_cast(std::lround(st.scale_h))); + const std::uint64_t key = MakeGlyphKey(code, pixel_h); + GlyphEntry* ge = nullptr; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; + } + if (!ge) { + GlyphEntry entry{}; + int aw = 0, lsb = 0; + stbtt_GetCodepointHMetrics(face_out, static_cast(code), &aw, &lsb); + stbtt_GetCodepointBitmapBox(face_out, static_cast(code), scale_out, scale_out, + &entry.x0, &entry.y0, &entry.x1, &entry.y1); + entry.w = std::max(0, entry.x1 - entry.x0); + entry.h = std::max(0, entry.y1 - entry.y0); + entry.advance = static_cast(aw) * scale_out; + entry.bearingX = static_cast(lsb) * scale_out; + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } + return ge; +} + // Font-set ID bit layout: // bits [31:28] family (0x18 = SST Standard UI) // bits [27:24] region (0x0 Latin, 0x8 Japanese, 0xC Chinese, 0xD Korean, 0xE Thai, 0xF @@ -1005,21 +1063,25 @@ static u32 g_device_cache_stub{}; s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { if (!library) { + LOG_ERROR(Lib_Font, "invalid library"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } auto* lib = static_cast(library); if (lib->magic != 0x0F01) { + LOG_ERROR(Lib_Font, "invalid library (bad magic)"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } constexpr u32 kMinCacheSize = 0x1020u; if (size < kMinCacheSize) { LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: size {} too small (min {})", size, kMinCacheSize); + LOG_ERROR(Lib_Font, "size too small"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } if (lib->device_cache_buf && lib->device_cache_buf != &g_device_cache_stub) { LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: library={} already attached", static_cast(library)); + LOG_ERROR(Lib_Font, "already attached"); return ORBIS_FONT_ERROR_ALREADY_ATTACHED; } @@ -1027,6 +1089,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff if (!buffer) { owned_buffer = new (std::nothrow) std::uint8_t[size]; if (!owned_buffer) { + LOG_ERROR(Lib_Font, "allocation failed"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } buffer = owned_buffer; @@ -1040,6 +1103,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff if (owned_buffer) { delete[] owned_buffer; } + LOG_ERROR(Lib_Font, "page_count == 0"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } header[1] = page_count; @@ -1076,13 +1140,14 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { if (!fontHandle || !renderer) { + LOG_ERROR(Lib_Font, "invalid parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); st.bound_renderer = renderer; - LOG_INFO(Lib_Font, "renderer bind requested"); + LOG_INFO(Lib_Font, "called"); LOG_DEBUG(Lib_Font, - "renderer bind params:\n" + "parameters:\n" " handle={}\n" " renderer={}\n", static_cast(fontHandle), static_cast(renderer)); @@ -1092,7 +1157,7 @@ s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRender s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, int* bidiLevel) { if (!textCharacter || !bidiLevel) { - LOG_DEBUG(Lib_Font, "Invalid parameter"); + LOG_ERROR(Lib_Font, "invalid parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -1113,12 +1178,12 @@ s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode() { s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, void** pTextOrder) { if (!pTextOrder) { - LOG_DEBUG(Lib_Font, "Invalid parameter"); + LOG_ERROR(Lib_Font, "invalid parameter (pTextOrder)"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } if (!textCharacter) { - LOG_DEBUG(Lib_Font, "Invalid parameter"); + LOG_ERROR(Lib_Font, "invalid parameter (textCharacter)"); *pTextOrder = NULL; return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -1529,7 +1594,7 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetrics* metrics) { if (!metrics) { - LOG_DEBUG(Lib_Font, "sceFontGetCharGlyphMetrics: invalid params"); + LOG_ERROR(Lib_Font, "invalid metrics pointer"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); @@ -1560,7 +1625,7 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code if (system_attached) { face = &st.ext_face; if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); scale = st.ext_scale_for_height; } } @@ -1655,10 +1720,12 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, OrbisFontHorizontalLayout* layout) { if (!fontHandle || !layout) { + LOG_ERROR(Lib_Font, "invalid parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto* st_ptr = TryGetState(fontHandle); if (!st_ptr) { + LOG_ERROR(Lib_Font, "invalid font handle"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; @@ -1699,7 +1766,7 @@ s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, OrbisFontKerning* kerning) { if (!kerning) { - LOG_DEBUG(Lib_Font, "sceFontGetKerning: invalid params"); + LOG_DEBUG(Lib_Font, "invalid parameters"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); @@ -1815,7 +1882,7 @@ s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h) { if (!fontHandle || (!out_w && !out_h)) { - LOG_DEBUG(Lib_Font, "sceFontGetScalePixel: invalid params"); + LOG_DEBUG(Lib_Font, "invalid parameters"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto* st = TryGetState(fontHandle); @@ -1833,7 +1900,7 @@ s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h) { if (!fontHandle || (!out_w && !out_h)) { - LOG_DEBUG(Lib_Font, "sceFontGetScalePoint: invalid params"); + LOG_DEBUG(Lib_Font, "invalid parameters"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto* st = TryGetState(fontHandle); @@ -1878,7 +1945,7 @@ s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, } if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); int asc = 0, desc = 0, gap = 0; stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); layout->baselineOffsetX = 0.0f; @@ -2305,39 +2372,43 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, OrbisFontHandle* pFontHandle) { if ((!templateFont && !fontHandle) || !pFontHandle) { - LOG_ERROR(Lib_Font, "OpenFontInstance: invalid params base={} template={} out_ptr={}", + LOG_ERROR(Lib_Font, "invalid parameter base={} template={} out_ptr={}", static_cast(fontHandle), static_cast(templateFont), static_cast(pFontHandle)); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - LOG_INFO(Lib_Font, "OpenFontInstance(begin): base={} template={} out_ptr={}", - static_cast(fontHandle), static_cast(templateFont), - static_cast(pFontHandle)); + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, + "parameters:\n" + " base={}\n" + " template={}\n" + " out_ptr={}\n", + static_cast(fontHandle), static_cast(templateFont), + static_cast(pFontHandle)); auto* src_state = TryGetState(templateFont); if (!src_state) { templateFont = nullptr; src_state = TryGetState(fontHandle); } if (!src_state) { - LOG_ERROR(Lib_Font, "OpenFontInstance: template handle={} missing", + LOG_ERROR(Lib_Font, "template handle={} missing", templateFont ? static_cast(templateFont) : static_cast(fontHandle)); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } FontState* base_state = TryGetState(fontHandle); if (!base_state && fontHandle) { - LOG_WARNING(Lib_Font, - "OpenFontInstance: base handle={} unknown, falling back to template state", + LOG_WARNING(Lib_Font, "base handle={} unknown, falling back to template state", static_cast(fontHandle)); } auto* new_handle = new (std::nothrow) OrbisFontHandleOpaque{}; if (!new_handle) { - LOG_ERROR(Lib_Font, "OpenFontInstance: allocation failed"); + LOG_ERROR(Lib_Font, "allocation failed"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } const std::size_t src_ext_bytes = src_state->ext_face_data.size(); LOG_DEBUG(Lib_Font, - "font instance clone template state:\n" + "template state:\n" " ext_ready={}\n" " bytes={}\n" " cache_size={}\n" @@ -2374,7 +2445,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa dst.ext_face_ready = false; dst.ext_cache.clear(); } else { - dst.ext_scale_for_height = ComputeScaleExt(&dst.ext_face, dst.scale_h); + dst.ext_scale_for_height = ComputeScaleExtForState(dst, &dst.ext_face, dst.scale_h); stbtt_ok = true; } } else { @@ -2416,7 +2487,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle) { if (!library || !fontAddress || fontSize == 0 || !pFontHandle) { - LOG_DEBUG(Lib_Font, "sceFontOpenFontMemory: invalid params"); + LOG_DEBUG(Lib_Font, "invalid parameters"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } (void)open_params; @@ -2465,6 +2536,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd const bool is_otf_cff = (sig32 == 0x4F54544Fu); const bool is_ttf_sfnt = (sig32 == 0x00010000u) || (sig32 == 0x74727565u); const bool is_sfnt_typ1 = (sig32 == 0x74797031u); + st.is_otf_cff = is_otf_cff; if (is_otf_cff) { LOG_WARNING(Lib_Font, "ExternalFace: OTF/CFF detected (OTTO). CFF outlines are not supported;" @@ -2474,7 +2546,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset)) { st.ext_face_ready = true; stbtt_GetFontVMetrics(&st.ext_face, &st.ext_ascent, &st.ext_descent, &st.ext_lineGap); - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); LOG_INFO(Lib_Font, "external face ready"); LOG_DEBUG(Lib_Font, "external face params:\n" @@ -2613,362 +2685,247 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl OrbisFontRenderSurface* surf, float x, float y, OrbisFontGlyphMetrics* metrics, OrbisFontRenderOutput* result) { - LOG_INFO(Lib_Font, - "RenderGlyph(H): handle={} code=U+{:04X} x={} y={} metrics={} result={} surf={}" - " buf={} widthByte={} pixelSizeByte={} size={}x{} sc=[{},{}-{}:{}] styleFlag={}", - static_cast(fontHandle), code, x, y, static_cast(metrics), - static_cast(result), static_cast(surf), - surf ? static_cast(surf->buffer) : nullptr, surf ? surf->widthByte : -1, - surf ? (int)surf->pixelSizeByte : -1, surf ? surf->width : -1, - surf ? surf->height : -1, surf ? surf->sc_x0 : 0u, surf ? surf->sc_y0 : 0u, - surf ? surf->sc_x1 : 0u, surf ? surf->sc_y1 : 0u, surf ? (int)surf->styleFlag : -1); - if (!surf || !surf->buffer) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, + "parameters:\n" + " handle={}\n" + " code=U+{:04X}\n" + " x={}\n" + " y={}\n" + " metrics_ptr={}\n" + " result_ptr={}\n" + " surf_ptr={}\n" + " buf={}\n" + " widthByte={}\n" + " pixelSizeByte={}\n" + " size={}x{}\n" + " scissor=[{},{}-{}:{}]\n" + " styleFlag={}\n", + static_cast(fontHandle), code, x, y, static_cast(metrics), + static_cast(result), static_cast(surf), + surf ? static_cast(surf->buffer) : nullptr, surf ? surf->widthByte : -1, + surf ? (int)surf->pixelSizeByte : -1, surf ? surf->width : -1, + surf ? surf->height : -1, surf ? surf->sc_x0 : 0u, surf ? surf->sc_y0 : 0u, + surf ? surf->sc_x1 : 0u, surf ? surf->sc_y1 : 0u, surf ? (int)surf->styleFlag : -1); + + LogStrideOnce(surf); + + if (!fontHandle) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "invalid font handle"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "no support code"); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + if (!surf || !metrics || !result) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "invalid parameter"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - LogStrideOnce(surf); - LOG_DEBUG(Lib_Font, - "RenderGlyph(H): handle={} code=U+{:04X} x={} y={} surf={} size={}x{} bpp={} " - "sc=[{},{}-{}:{}]", - static_cast(fontHandle), code, x, y, static_cast(surf), - surf->width, surf->height, (int)surf->pixelSizeByte, surf->sc_x0, surf->sc_y0, - surf->sc_x1, surf->sc_y1); - if (result) { - result->stage = nullptr; - result->slot.maybe_addr = static_cast(surf->buffer); - result->slot.maybe_rowBytes = static_cast(std::max(0, surf->widthByte)); - result->slot.maybe_pixelSize = static_cast(std::max(1, (int)surf->pixelSizeByte)); - result->slot.maybe_pixelFmt = (result->slot.maybe_pixelSize == 4) ? 1 : 0; + auto* st = TryGetState(fontHandle); + if (!st) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "invalid font handle (state)"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = GetState(fontHandle); - const OrbisFontStyleFrame* bound_style = nullptr; - if (auto it = g_style_for_surface.find(surf); it != g_style_for_surface.end()) { - bound_style = it->second; + if (!st->bound_renderer) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "renderer not bound"); + return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; } - const StyleFrameScaleState style_state = ResolveStyleFrameScale(bound_style, st); - const bool style_scale_override = style_state.scale_overridden; - float render_scale_w = style_state.scale_w; - float render_scale_h = style_state.scale_h; - float fw = render_scale_w; - float fh = render_scale_h; - int g_x0 = 0, g_y0 = 0, g_x1 = 0, g_y1 = 0; - const stbtt_fontinfo* face = nullptr; - float scale = 0.0f; - const bool needs_custom_scale = std::fabs(render_scale_h - st.scale_h) > kScaleEpsilon; - - if (st.ext_face_ready) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); - const int glyph_index = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (glyph_index > 0) { - face = &st.ext_face; - scale = needs_custom_scale - ? ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)) - : st.ext_scale_for_height; + // Resolve effective pixel scales (style frame overrides -> point scales -> pixel scales) + const OrbisFontStyleFrame* style_frame = nullptr; + if ((surf->styleFlag & 0x1) != 0) { + if (auto it = g_style_for_surface.find(surf); it != g_style_for_surface.end()) { + style_frame = it->second; } } + StyleFrameScaleState style_scale = ResolveStyleFrameScale(style_frame, *st); + if (st->scale_point_active) { + style_scale.scale_w = PointsToPixels(st->scale_point_w, style_scale.dpi_x); + style_scale.scale_h = PointsToPixels(st->scale_point_h, style_scale.dpi_y); + } + const stbtt_fontinfo* face = nullptr; + float scale_y = 0.0f; + if (st->ext_face_ready) { + face = &st->ext_face; + scale_y = ComputeScaleExtForState(*st, face, style_scale.scale_h); + } if (!face) { bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + const std::string sys_log = ReportSystemFaceRequest(*st, fontHandle, system_attached); if (!sys_log.empty()) { LOG_ERROR(Lib_Font, "{}", sys_log); } if (system_attached) { - face = &st.ext_face; - if (!needs_custom_scale) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); - scale = st.ext_scale_for_height; - } else { - scale = ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)); - } + face = &st->ext_face; + scale_y = ComputeScaleExtForState(*st, face, style_scale.scale_h); } } + if (!face || scale_y <= kScaleEpsilon) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "no support glyph (face/scale)"); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + const float scale_x = (style_scale.scale_h > kScaleEpsilon) + ? scale_y * (style_scale.scale_w / style_scale.scale_h) + : scale_y; + const int glyph_index = stbtt_FindGlyphIndex(face, static_cast(code)); + if (glyph_index <= 0) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + int aw = 0, lsb = 0; + stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; const float frac_x = x - std::floor(x); const float frac_y = y - std::floor(y); - const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + stbtt_GetCodepointBitmapBoxSubpixel(face, static_cast(code), scale_x, scale_y, frac_x, + frac_y, &x0, &y0, &x1, &y1); + const int glyph_w = std::max(0, x1 - x0); + const int glyph_h = std::max(0, y1 - y0); - if (face) { - LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src=external", - static_cast(fontHandle), code); - const int pixel_h = std::max(1, (int)std::lround(render_scale_h)); - const std::uint64_t key = MakeGlyphKey(code, pixel_h); - GlyphEntry* ge = nullptr; - if (!use_subpixel) { - if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { - ge = &it->second; - } - } - if (!ge) { - GlyphEntry entry{}; - int aw = 0, lsb = 0; - stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); - if (use_subpixel) { - stbtt_GetCodepointBitmapBoxSubpixel(face, static_cast(code), scale, scale, - frac_x, frac_y, &entry.x0, &entry.y0, &entry.x1, - &entry.y1); - } else { - stbtt_GetCodepointBitmapBox(face, static_cast(code), scale, scale, &entry.x0, - &entry.y0, &entry.x1, &entry.y1); - } - entry.w = std::max(0, entry.x1 - entry.x0); - entry.h = std::max(0, entry.y1 - entry.y0); - entry.advance = static_cast(aw) * scale; - entry.bearingX = static_cast(lsb) * scale; - if (!use_subpixel && entry.w > 0 && entry.h > 0) { - entry.bitmap.resize(static_cast(entry.w * entry.h)); - stbtt_MakeCodepointBitmap(face, entry.bitmap.data(), entry.w, entry.h, entry.w, - scale, scale, static_cast(code)); - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } else if (use_subpixel) { - fw = static_cast(entry.w); - fh = static_cast(entry.h); - g_x0 = entry.x0; - g_y0 = entry.y0; - g_x1 = entry.x1; - g_y1 = entry.y1; - } - } - if (ge) { - fw = static_cast(ge->w); - fh = static_cast(ge->h); - g_x0 = ge->x0; - g_y0 = ge->y0; - g_x1 = ge->x1; - g_y1 = ge->y1; - } - } - if (metrics) { - if (face) { - int aw = 0, lsb = 0; - stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); - metrics->w = fw; - metrics->h = fh; - metrics->h_bearing_x = static_cast(lsb) * scale; - metrics->h_bearing_y = static_cast(-g_y0); - metrics->h_adv = static_cast(aw) * scale; - } else { - metrics->w = fw; - metrics->h = fh; - metrics->h_bearing_x = 0.0f; - metrics->h_bearing_y = fh; - metrics->h_adv = fw; - } - metrics->v_bearing_x = 0.0f; - metrics->v_bearing_y = 0.0f; - metrics->v_adv = 0.0f; + metrics->w = glyph_w > 0 ? static_cast(glyph_w) : style_scale.scale_w; + metrics->h = glyph_h > 0 ? static_cast(glyph_h) : style_scale.scale_h; + metrics->h_bearing_x = static_cast(lsb) * scale_x; + metrics->h_bearing_y = static_cast(-y0); + metrics->h_adv = static_cast(aw) * scale_x; + metrics->v_bearing_x = 0.0f; + metrics->v_bearing_y = 0.0f; + metrics->v_adv = 0.0f; + + // Compute the baseline position for the current scale so that we can + // place the glyph inside the render surface using the same convention as + // the original library: (x, y) specifies the top-left of the line box, + // while glyphs are positioned relative to the baseline. + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(face, &asc, &desc, &gap); + const float baseline_from_top = static_cast(asc) * scale_y; + const float glyph_top_from_top = baseline_from_top - metrics->h_bearing_y; + + if (!surf->buffer || surf->width <= 0 || surf->height <= 0 || surf->widthByte <= 0 || + surf->pixelSizeByte <= 0) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "no support surface (buffer)"); + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; } - if (code == 0x20) { - if (result) { - result->new_x = 0; - result->new_y = 0; - result->new_w = 0; - result->new_h = 0; - result->ImageMetrics = {}; - } - return ORBIS_OK; + const int bpp = static_cast(surf->pixelSizeByte); + if (bpp != 1 && bpp != 4) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "no support surface (bpp={})", bpp); + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; } - if (!st.layout_cached) { - UpdateCachedLayout(st); - } - float layout_baseline = st.layout_cached ? st.cached_baseline_y : 0.0f; - if (style_scale_override && st.ext_face_ready) { - const float override_scale = ComputeScaleExt(&st.ext_face, std::max(0.01f, render_scale_h)); - layout_baseline = static_cast(st.ext_ascent) * override_scale; + std::vector glyph_bitmap; + glyph_bitmap.resize(static_cast(std::max(0, glyph_w)) * + static_cast(std::max(0, glyph_h))); + if (glyph_w > 0 && glyph_h > 0) { + stbtt_MakeCodepointBitmapSubpixel(face, glyph_bitmap.data(), glyph_w, glyph_h, glyph_w, + scale_x, scale_y, frac_x, frac_y, static_cast(code)); } - float adjusted_y = y; - if (face && layout_baseline > 0.0f) { - const float top_expect = adjusted_y + static_cast(g_y0); - const float bottom_expect = top_expect + fh; - if (bottom_expect <= 0.0f || top_expect < 0.0f) { - adjusted_y += layout_baseline; - } + // Decide how to interpret (x, y) based on the font source: + // - System fonts (font sets like SST used by AnywhereVR) behaved best + // when (x, y) was treated as the raw top-left of the glyph box. + // - Game-supplied external TrueType fonts using pixel scaling for Driveclub + // match the original library when (x, y) is the baseline position. + // - Other external fonts (CFF / point scaling / non-Driveclub TTF) behave + // like Catherine_ok, where (x, y) is the line top and glyphs are placed + // from the baseline. + const bool is_system_font = (st->font_set_type != 0) || !st->system_font_path.empty(); + const bool use_baseline_ttf = !is_system_font && !st->is_otf_cff && !st->scale_point_active && + IsEmProfileExternalFont(*st); + + int dest_x = static_cast(std::floor(x)); + int dest_y = 0; + if (is_system_font) { + // AnywhereVR / system fonts: raw top-left. + dest_y = static_cast(std::floor(y)); + } else if (use_baseline_ttf) { + // Driveclub-style TrueType game fonts: (x, y) is the baseline. + const int baseline_dest_x = static_cast(std::floor(x)) + x0; + const int baseline_dest_y = static_cast(std::floor(y)) + y0; + dest_x = baseline_dest_x; + dest_y = baseline_dest_y; + } else { + // Catherine / Digimon / ReFantazio external fonts: + // (x, y) is line top, align glyph from baseline. + dest_y = static_cast(std::floor(y + glyph_top_from_top)); } + // Apply surface scissor: rendering is clipped to [sc_x0, sc_x1) x [sc_y0, sc_y1), + // clamped to the surface bounds. + const int surface_w = std::max(surf->width, 0); + const int surface_h = std::max(surf->height, 0); + const int clip_x0 = std::clamp(static_cast(surf->sc_x0), 0, surface_w); + const int clip_y0 = std::clamp(static_cast(surf->sc_y0), 0, surface_h); + const int clip_x1 = std::clamp(static_cast(surf->sc_x1), 0, surface_w); + const int clip_y1 = std::clamp(static_cast(surf->sc_y1), 0, surface_h); - int ix = static_cast(x); - int iy = static_cast(adjusted_y); - int left = ix; - int top = iy; - if (face) { - left = ix + g_x0; - top = iy + g_y0; - } - int iw = std::max(0, static_cast(std::round(fw))); - int ih = std::max(0, static_cast(std::round(fh))); + int update_x0 = dest_x; + int update_y0 = dest_y; + int update_w = 0; + int update_h = 0; - int sw = surf->width; - int sh = surf->height; - if (sw <= 0 || sh <= 0 || iw <= 0 || ih <= 0) { - return ORBIS_OK; - } + if (glyph_w > 0 && glyph_h > 0 && clip_x1 > clip_x0 && clip_y1 > clip_y0) { + auto* dst_base = static_cast(surf->buffer); + const int start_row = std::max(dest_y, clip_y0); + const int end_row = std::min(dest_y + glyph_h, clip_y1); + const int start_col = std::max(dest_x, clip_x0); + const int end_col = std::min(dest_x + glyph_w, clip_x1); - int sx0 = 0, sy0 = 0, sx1 = sw, sy1 = sh; - if (surf->sc_x1 > 0 || surf->sc_y1 > 0 || surf->sc_x0 > 0 || surf->sc_y0 > 0) { - sx0 = static_cast(surf->sc_x0); - sy0 = static_cast(surf->sc_y0); - sx1 = static_cast(surf->sc_x1); - sy1 = static_cast(surf->sc_y1); - sx0 = std::clamp(sx0, 0, sw); - sy0 = std::clamp(sy0, 0, sh); - sx1 = std::clamp(sx1, 0, sw); - sy1 = std::clamp(sy1, 0, sh); - } - - int rx0 = left; - int ry0 = top; - int rx1 = left + iw; - int ry1 = top + ih; - - rx0 = std::clamp(rx0, sx0, sx1); - ry0 = std::clamp(ry0, sy0, sy1); - rx1 = std::clamp(rx1, sx0, sx1); - ry1 = std::clamp(ry1, sy0, sy1); - - int cw = std::max(0, rx1 - rx0); - int ch = std::max(0, ry1 - ry0); - if (ch == 0 && face) { - LOG_DEBUG(Lib_Font, - "RenderGlyph(H): zero-height glyph code=U+{:04X} top={} ih={} g_y0={} g_y1={}" - " y={}, g_adv={} scale={} src=external", - code, top, ih, g_y0, g_y1, y, metrics ? metrics->h_adv : -1.0f, scale); - } - - if (cw > 0 && ch > 0) { - const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); - bool is_pua = (code >= 0xE000 && code <= 0xF8FF) || (code >= 0xF0000 && code <= 0xFFFFD) || - (code >= 0x100000 && code <= 0x10FFFD); - bool is_placeholder_ascii = (code == 'h' || code == 'p' || code == 'x' || code == 'o'); - if ((is_pua || is_placeholder_ascii) && g_logged_pua.insert(code).second) { - LOG_DEBUG( - Lib_Font, - "GlyphTrace: code=U+{:04X} pua={} placeholder_ascii={} dst=[{},{} {}x{}] bpp={}", - code, is_pua, is_placeholder_ascii, rx0, ry0, cw, ch, bpp); - } - if (face && iw > 0 && ih > 0) { - const int pixel_h = std::max(1, (int)std::lround(render_scale_h)); - const std::uint64_t key = MakeGlyphKey(code, pixel_h); - const GlyphEntry* ge = nullptr; - if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { - ge = &it->second; - } - const float frac_x = x - std::floor(x); - const float frac_y = y - std::floor(y); - const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); - const std::uint8_t* src_bitmap = nullptr; - if (!use_subpixel && ge && ge->w == iw && ge->h == ih && !ge->bitmap.empty()) { - src_bitmap = ge->bitmap.data(); - } else { - st.scratch.assign(static_cast(iw * ih), 0); - if (use_subpixel) { - stbtt_MakeCodepointBitmapSubpixel(face, st.scratch.data(), iw, ih, iw, scale, - scale, frac_x, frac_y, - static_cast(code)); + update_x0 = start_col; + update_y0 = start_row; + update_w = std::max(0, end_col - start_col); + update_h = std::max(0, end_row - start_row); + for (int row = start_row; row < end_row; ++row) { + const int src_y = row - dest_y; + if (src_y < 0 || src_y >= glyph_h) + continue; + const std::uint8_t* src_row = + glyph_bitmap.data() + static_cast(src_y) * glyph_w; + std::uint8_t* dst_row = dst_base + static_cast(row) * + static_cast(surf->widthByte); + for (int col = start_col; col < end_col; ++col) { + const int src_x = col - dest_x; + if (src_x < 0 || src_x >= glyph_w) + continue; + const std::uint8_t cov = src_row[src_x]; + std::uint8_t* dst = dst_row + static_cast(col) * bpp; + if (bpp == 1) { + dst[0] = cov; } else { - stbtt_MakeCodepointBitmap(face, st.scratch.data(), iw, ih, iw, scale, scale, - static_cast(code)); - } - src_bitmap = st.scratch.data(); - } - - const std::uint8_t* eff_src = src_bitmap; - - const int src_x0 = std::max(0, sx0 - rx0); - const int src_y0 = std::max(0, sy0 - ry0); - const int dst_x0 = std::max(rx0, sx0); - const int dst_y0 = std::max(ry0, sy0); - const int copy_w = std::min(rx1, sx1) - dst_x0; - const int copy_h = std::min(ry1, sy1) - dst_y0; - if (copy_w > 0 && copy_h > 0) { - LOG_TRACE(Lib_Font, - "RenderGlyph(H): code=U+{:04X} baseline=({}, {}) box=[{},{}-{}:{}] " - "dst=[{},{} {}x{}] bpp={}", - code, ix, iy, g_x0, g_y0, g_x1, g_y1, dst_x0, dst_y0, copy_w, copy_h, - bpp); - for (int row = 0; row < copy_h; ++row) { - const int src_y = src_y0 + row; - const std::uint8_t* src_row = eff_src + src_y * iw + src_x0; - int row_shift = 0; - int row_dst_x = dst_x0 + row_shift; - int row_copy_w = copy_w; - if (row_dst_x < sx0) { - int delta = sx0 - row_dst_x; - row_dst_x = sx0; - if (delta < row_copy_w) { - src_row += delta; - row_copy_w -= delta; - } else { - row_copy_w = 0; - } - } - if (row_dst_x + row_copy_w > sx1) { - row_copy_w = std::max(0, sx1 - row_dst_x); - } - std::uint8_t* dst_row = static_cast(surf->buffer) + - (dst_y0 + row) * surf->widthByte + row_dst_x * bpp; - if (bpp == 1) { - for (int col = 0; col < row_copy_w; ++col) { - std::uint8_t cov = src_row[col]; - std::uint8_t& d = dst_row[col]; - d = std::max(d, cov); - } - } else if (bpp == 4) { - std::uint32_t* px = reinterpret_cast(dst_row); - for (int col = 0; col < row_copy_w; ++col) { - std::uint8_t a = src_row[col]; - px[col] = (static_cast(a) << 24) | 0x00FFFFFFu; - } - } else { - for (int col = 0; col < row_copy_w; ++col) { - std::uint8_t a = src_row[col]; - std::uint8_t* p = dst_row + col * bpp; - std::memset(p, 0xFF, static_cast(bpp)); - p[0] = a; - } - } + dst[0] = cov; + dst[1] = cov; + dst[2] = cov; + dst[3] = cov; } } - } else { - if (result) { - result->new_x = 0; - result->new_y = 0; - result->new_w = 0; - result->new_h = 0; - result->ImageMetrics.width = 0; - result->ImageMetrics.height = 0; - } - LOG_DEBUG(Lib_Font, "RenderGlyph(H): skip draw (no face/zero size) code=U+{:04X}", - code); - return ORBIS_OK; } } - if (result) { - result->new_x = static_cast(rx0); - result->new_y = static_cast(ry0); - result->new_w = static_cast(cw); - result->new_h = static_cast(ch); - const float stride_bytes = static_cast(std::max(0, surf->widthByte)); - result->ImageMetrics.bearing_x = metrics ? metrics->h_bearing_x : static_cast(g_x0); - result->ImageMetrics.bearing_y = metrics ? metrics->h_bearing_y : static_cast(-g_y0); - float adv_px = 0.0f; - if (face) { - int aw_tmp = 0, lsb_tmp = 0; - stbtt_GetCodepointHMetrics(face, static_cast(code), &aw_tmp, &lsb_tmp); - adv_px = static_cast(aw_tmp) * scale; - } - result->ImageMetrics.dv = adv_px; - result->ImageMetrics.stride = stride_bytes; - result->ImageMetrics.width = static_cast(cw); - result->ImageMetrics.height = static_cast(ch); - LOG_DEBUG(Lib_Font, "RenderGlyph(H): UpdateRect=[{},{} {}x{}] stride={} adv={}", - result->new_x, result->new_y, result->new_w, result->new_h, - result->ImageMetrics.stride, result->ImageMetrics.dv); - } + result->stage = nullptr; + result->slot.maybe_addr = static_cast(surf->buffer); + result->slot.maybe_rowBytes = static_cast(surf->widthByte); + result->slot.maybe_pixelSize = static_cast(surf->pixelSizeByte); + result->slot.maybe_pixelFmt = 0; + result->new_x = static_cast(std::max(update_x0, 0)); + result->new_y = static_cast(std::max(update_y0, 0)); + result->new_w = static_cast(std::max(update_w, 0)); + result->new_h = static_cast(std::max(update_h, 0)); + result->ImageMetrics.bearing_x = metrics->h_bearing_x; + result->ImageMetrics.bearing_y = metrics->h_bearing_y; + result->ImageMetrics.dv = metrics->h_adv; + result->ImageMetrics.stride = static_cast(surf->widthByte); + result->ImageMetrics.width = static_cast(std::max(update_w, 0)); + result->ImageMetrics.height = static_cast(std::max(update_h, 0)); return ORBIS_OK; } @@ -2995,6 +2952,17 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, + "parameters:\n" + " renderSurface={}\n" + " buffer={}\n" + " bufWidthByte={}\n" + " pixelSizeByte={}\n" + " widthPixel={}\n" + " heightPixel={}\n", + static_cast(renderSurface), buffer, bufWidthByte, pixelSizeByte, + widthPixel, heightPixel); if (renderSurface) { renderSurface->buffer = buffer; renderSurface->widthByte = bufWidthByte; @@ -3009,17 +2977,30 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface renderSurface->sc_x1 = static_cast(renderSurface->width); renderSurface->sc_y1 = static_cast(renderSurface->height); std::fill(std::begin(renderSurface->reserved_q), std::end(renderSurface->reserved_q), 0); - LOG_INFO(Lib_Font, - "RenderSurfaceInit: buf={} widthByte={} pixelSizeByte={} size={}x{} " - "scissor=[0,0-{}:{}]", - static_cast(buffer), bufWidthByte, pixelSizeByte, - renderSurface->width, renderSurface->height, renderSurface->sc_x1, - renderSurface->sc_y1); + LOG_DEBUG(Lib_Font, + "result:\n" + " buf={}\n" + " widthByte={}\n" + " pixelSizeByte={}\n" + " size={}x{}\n" + " scissor=[0,0-{}:{}]\n", + static_cast(buffer), bufWidthByte, pixelSizeByte, + renderSurface->width, renderSurface->height, renderSurface->sc_x1, + renderSurface->sc_y1); } } void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, int y0, int w, int h) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, + "parameters:\n" + " renderSurface={}\n" + " x0={}\n" + " y0={}\n" + " w={}\n" + " h={}\n", + static_cast(renderSurface), x0, y0, w, h); if (!renderSurface) return; @@ -3056,8 +3037,14 @@ void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderS renderSurface->sc_y0 = static_cast(clip_y0); renderSurface->sc_y1 = static_cast(clip_y1); } - LOG_INFO(Lib_Font, "RenderSurfaceSetScissor: [{},{}-{}:{}]", renderSurface->sc_x0, - renderSurface->sc_y0, renderSurface->sc_x1, renderSurface->sc_y1); + LOG_DEBUG(Lib_Font, + "result:\n" + " sc_x0={}\n" + " sc_y0={}\n" + " sc_x1={}\n" + " sc_y1={}\n", + renderSurface->sc_x0, renderSurface->sc_y0, renderSurface->sc_x1, + renderSurface->sc_y1); } s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, @@ -3103,10 +3090,12 @@ s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi) { if (!fontHandle) { + LOG_ERROR(Lib_Font, "invalid font handle (null)"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto* st_ptr = TryGetState(fontHandle); if (!st_ptr) { + LOG_ERROR(Lib_Font, "invalid font handle"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; @@ -3132,10 +3121,13 @@ s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h) { if (!fontHandle || w <= 0.0f || h <= 0.0f) { + LOG_ERROR(Lib_Font, "invalid parameter handle={} w={} h={}", + static_cast(fontHandle), w, h); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto* st_ptr = TryGetState(fontHandle); if (!st_ptr) { + LOG_ERROR(Lib_Font, "invalid font handle={}", static_cast(fontHandle)); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; @@ -3143,7 +3135,7 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float st.scale_w = w; st.scale_h = h; if (st.ext_face_ready) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); bool system_attached = false; const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); if (!sys_log.empty()) { @@ -3165,10 +3157,13 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h) { if (!fontHandle || w <= 0.0f || h <= 0.0f) { + LOG_ERROR(Lib_Font, "invalid parameter handle={} w={} h={}", + static_cast(fontHandle), w, h); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto* st_ptr = TryGetState(fontHandle); if (!st_ptr) { + LOG_ERROR(Lib_Font, "invalid font handle={}", static_cast(fontHandle)); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } auto& st = *st_ptr; @@ -3190,7 +3185,7 @@ s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float st.scale_w = pixel_w; st.scale_h = pixel_h; if (st.ext_face_ready) - st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); bool system_attached = false; const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); if (!sys_log.empty()) { @@ -3536,7 +3531,10 @@ s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { } s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle) { - LOG_DEBUG(Lib_Font, "sceFontUnbindRenderer fontHandle={}", + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, + "parameters:\n" + " fontHandle={}\n", static_cast(fontHandle)); return ORBIS_OK; } From e9ca20b65d28c42d7bb079eb386486c4276bcccd Mon Sep 17 00:00:00 2001 From: w1naenator Date: Thu, 8 Jan 2026 03:08:29 +0200 Subject: [PATCH 27/51] Add internal font handling structures and layout computation functions This commit introduces a new header file `fontft_internal.h` containing various structures and functions for managing font handles, layout metrics, and rendering operations. Key additions include: - Definitions for `FontHandleOpenInfo`, `FontHandleStyleStateTail`, and `FontHandleOpaqueNative` to encapsulate font state and properties. - Layout-related structures such as `HorizontalLayoutBlocks`, `VerticalLayoutBlocks`, and their respective I/O structures for managing layout metrics and effects. - Functions for computing horizontal and vertical layout blocks, along with utility functions for handling layout words in byte format. - Stubs for library functions related to font management and rendering. These changes aim to enhance the font rendering capabilities of the shadPS4 Emulator Project, providing a robust foundation for future font-related features. --- CMakeLists.txt | 6 +- externals/CMakeLists.txt | 10 + src/core/libraries/font/font.cpp | 6552 +++++++++++++------ src/core/libraries/font/font.h | 176 +- src/core/libraries/font/font_internal.cpp | 1932 ++++++ src/core/libraries/font/font_internal.h | 760 +++ src/core/libraries/font/fontft.cpp | 106 +- src/core/libraries/font/fontft.h | 7 +- src/core/libraries/font/fontft_internal.cpp | 3672 +++++++++++ src/core/libraries/font/fontft_internal.h | 603 ++ 10 files changed, 11887 insertions(+), 1937 deletions(-) create mode 100644 src/core/libraries/font/font_internal.cpp create mode 100644 src/core/libraries/font/font_internal.h create mode 100644 src/core/libraries/font/fontft_internal.cpp create mode 100644 src/core/libraries/font/fontft_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 99aca5268..c89ccc90b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -468,8 +468,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/web_browser_dialog/webbrowserdialog.h src/core/libraries/font/font.cpp src/core/libraries/font/font.h + src/core/libraries/font/font_internal.cpp + src/core/libraries/font/font_internal.h src/core/libraries/font/fontft.cpp src/core/libraries/font/fontft.h + src/core/libraries/font/fontft_internal.cpp + src/core/libraries/font/fontft_internal.h src/core/libraries/font/font_error.h ) @@ -1085,7 +1089,7 @@ add_executable(shadps4 create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG freetype) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml) target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz fdk-aac) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8e96f9bec..c73201e7a 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -56,6 +56,16 @@ if (NOT TARGET ZLIB::ZLIB) set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build") endif() +# FreeType +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/freetype/CMakeLists.txt" AND NOT TARGET freetype) + # Keep optional deps off to reduce build surface; zlib stays enabled for compressed fonts. + set(FT_DISABLE_BZIP2 ON CACHE BOOL "" FORCE) + set(FT_DISABLE_BROTLI ON CACHE BOOL "" FORCE) + set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "" FORCE) + set(FT_DISABLE_PNG ON CACHE BOOL "" FORCE) + add_subdirectory(freetype) +endif() + # SDL3 if (NOT TARGET SDL3::SDL3) set(SDL_TEST_LIBRARY OFF) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 00a6ebd35..6083a5187 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include #include #include @@ -19,30 +21,35 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H #include "common/config.h" #include "common/logging/log.h" #include "common/singleton.h" #include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/font/font.h" +#include "core/libraries/font/font_internal.h" +#include "core/libraries/font/fontft.h" +#include "core/libraries/font/fontft_internal.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" #include "font_error.h" -#define STBTT_STATIC -#define STB_TRUETYPE_IMPLEMENTATION -#include "externals/dear_imgui/imstb_truetype.h" - -namespace Libraries::Font { -struct FontLibOpaque; -} +#ifdef formatParams +#undef formatParams +#endif namespace { -Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); - using Libraries::Font::OrbisFontGenerateGlyphParams; using Libraries::Font::OrbisFontGlyph; using Libraries::Font::OrbisFontGlyphMetrics; @@ -51,49 +58,67 @@ using Libraries::Font::OrbisFontGlyphOutline; using Libraries::Font::OrbisFontGlyphOutlinePoint; using Libraries::Font::OrbisFontMem; using Libraries::Font::OrbisFontStyleFrame; +namespace Internal = Libraries::Font::Internal; -struct GlyphEntry { - std::vector bitmap; - int w = 0; - int h = 0; - int x0 = 0; - int y0 = 0; - int x1 = 0; - int y1 = 0; - float advance = 0.0f; - float bearingX = 0.0f; +struct NamedParam { + std::string_view name; + std::string value; }; -struct FontState { - float scale_w = 16.0f; - float scale_h = 16.0f; - float scale_point_w = 12.0f; - float scale_point_h = 12.0f; - bool scale_point_active = false; - u32 dpi_x = 72; - u32 dpi_y = 72; - u32 font_set_type = 0; - std::filesystem::path system_font_path; - Libraries::Font::OrbisFontLib library = nullptr; - bool ext_face_ready = false; - bool is_otf_cff = false; - std::vector ext_face_data; - stbtt_fontinfo ext_face{}; - float ext_scale_for_height = 0.0f; - int ext_ascent = 0, ext_descent = 0, ext_lineGap = 0; - std::unordered_map ext_cache; - std::vector scratch; - bool logged_ext_use = false; - // Cached layout (PS4-style) for GetHorizontalLayout - float cached_baseline_y = 0.0f; - bool layout_cached = false; - // Renderer binding state (PS4 requires a bound renderer for render-scale queries) - Libraries::Font::OrbisFontRenderer bound_renderer = nullptr; - // Mark when the game asked for PS4 internal/system fonts (via OpenFontSet) - bool system_requested = false; -}; +template +static std::string DescribeValue(const T& value) { + using Clean = std::remove_cvref_t; + if constexpr (std::is_same_v) { + return value ? "true" : "false"; + } else if constexpr (std::is_pointer_v) { + if constexpr (std::is_same_v || std::is_same_v) { + return value ? fmt::format("\"{}\"", value) : "(null)"; + } + return fmt::format("{}", reinterpret_cast(value)); + } else if constexpr (std::is_floating_point_v) { + return fmt::format("{:.6g}", value); + } else if constexpr (std::is_enum_v) { + using Underlying = std::underlying_type_t; + return DescribeValue(static_cast(value)); + } else { + return fmt::format("{}", value); + } +} -static std::unordered_map g_font_state; +template +static NamedParam Param(std::string_view name, const T& value) { + return NamedParam{name, DescribeValue(value)}; +} + +static std::string formatParams(std::initializer_list params) { + fmt::memory_buffer buffer; + fmt::format_to(std::back_inserter(buffer), "params:\n"); + for (const auto& p : params) { + fmt::format_to(std::back_inserter(buffer), "{}: {}\n", p.name, p.value); + } + return fmt::to_string(buffer); +} + +struct FtExternalFaceObj { + u32 refcount; + u32 reserved04; + u32 reserved08; + u32 sub_font_index; + u64 reserved10; + FtExternalFaceObj* next; + u64 reserved20; + u64 reserved28; + FT_Face face; + const u8* font_data; + u32 font_size; + u32 sfnt_base; +}; +static_assert(offsetof(FtExternalFaceObj, sub_font_index) == 0x0C); +static_assert(offsetof(FtExternalFaceObj, next) == 0x18); +static_assert(offsetof(FtExternalFaceObj, face) == 0x30); + +using FontAllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); +using FontFreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); struct LibraryState { bool support_system = false; @@ -103,153 +128,50 @@ struct LibraryState { const Libraries::Font::OrbisFontMem* backing_memory = nullptr; Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; u64 edition = 0; - Libraries::Font::FontLibOpaque* native = nullptr; + void* alloc_ctx = nullptr; + FontAllocFn alloc_fn = nullptr; + FontFreeFn free_fn = nullptr; + void* owned_mspace = nullptr; + u32 owned_mspace_size = 0; + void* owned_sysfonts_ctx = nullptr; + u32 owned_sysfonts_ctx_size = 0; + void* owned_external_fonts_ctx = nullptr; + u32 owned_external_fonts_ctx_size = 0; void* owned_device_cache = nullptr; u32 owned_device_cache_size = 0; }; static std::unordered_map g_library_state; -static std::unordered_map - g_style_for_surface; - -static std::once_flag g_system_font_once; -static bool g_system_font_available = false; -static std::vector g_system_font_bytes; -static std::filesystem::path g_system_font_path; - -static bool EnsureSystemFontBlob(); -static bool HasTrueTypeGlyf(const std::vector& bytes); -static FontState* TryGetState(Libraries::Font::OrbisFontHandle h); -static std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, - bool& attached_out); - -struct GeneratedGlyph { - OrbisFontGlyphOpaque glyph{}; - OrbisFontGlyphMetrics metrics{}; - u32 codepoint = 0; - Libraries::Font::OrbisFontHandle owner_handle{}; - OrbisFontGlyphOutline outline{}; - std::vector outline_points; - std::vector outline_tags; - std::vector outline_contours; - bool outline_initialized = false; -}; - -static std::mutex g_generated_glyph_mutex; -static std::unordered_set g_generated_glyphs; - -static void TrackGeneratedGlyph(OrbisFontGlyph glyph) { - std::scoped_lock lock(g_generated_glyph_mutex); - g_generated_glyphs.insert(glyph); -} - -static bool ForgetGeneratedGlyph(OrbisFontGlyph glyph) { - std::scoped_lock lock(g_generated_glyph_mutex); - return g_generated_glyphs.erase(glyph) > 0; -} - -static void BuildBoundingOutline(GeneratedGlyph& gg) { - const float left = gg.metrics.h_bearing_x; - const float top = gg.metrics.h_bearing_y; - const float right = gg.metrics.h_bearing_x + gg.metrics.w; - const float bottom = gg.metrics.h_bearing_y - gg.metrics.h; - - gg.outline_points.clear(); - gg.outline_tags.clear(); - gg.outline_contours.clear(); - - gg.outline_points.push_back({left, top}); - gg.outline_points.push_back({right, top}); - gg.outline_points.push_back({right, bottom}); - gg.outline_points.push_back({left, bottom}); - - gg.outline_tags.resize(gg.outline_points.size(), 1); // on-curve flags - gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); - - gg.outline.points_ptr = gg.outline_points.data(); - gg.outline.tags_ptr = gg.outline_tags.data(); - gg.outline.contour_end_idx = gg.outline_contours.data(); - gg.outline.points_cnt = static_cast(gg.outline_points.size()); - gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); - gg.outline_initialized = true; -} - -static bool BuildTrueOutline(GeneratedGlyph& gg) { - const auto* st = TryGetState(gg.owner_handle); - if (!st || !st->ext_face_ready) { - return false; +static void LogFontOpenError(s32 rc) { + switch (rc) { + case ORBIS_FONT_ERROR_INVALID_LIBRARY: + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + break; + case ORBIS_FONT_ERROR_INVALID_PARAMETER: + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + break; + case ORBIS_FONT_ERROR_ALLOCATION_FAILED: + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + break; + case ORBIS_FONT_ERROR_FS_OPEN_FAILED: + LOG_ERROR(Lib_Font, "FS_OPEN_FAILED"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FORMAT"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); + break; + case ORBIS_FONT_ERROR_FONT_OPEN_MAX: + LOG_ERROR(Lib_Font, "FONT_OPEN_MAX"); + break; + default: + LOG_ERROR(Lib_Font, "FAILED"); + break; } - auto* face = &st->ext_face; - const int glyph_index = stbtt_FindGlyphIndex(face, static_cast(gg.codepoint)); - if (glyph_index <= 0) { - return false; - } - - stbtt_vertex* verts = nullptr; - const int count = stbtt_GetGlyphShape(face, glyph_index, &verts); - if (count <= 0 || !verts) { - return false; - } - - gg.outline_points.clear(); - gg.outline_tags.clear(); - gg.outline_contours.clear(); - - const float scale = st->ext_scale_for_height == 0.0f - ? stbtt_ScaleForPixelHeight(face, st->scale_h) - : st->ext_scale_for_height; - - auto push_point = [&](float x, float y, u8 tag) { - gg.outline_points.push_back({x * scale, y * scale}); - gg.outline_tags.push_back(tag); - }; - - int last_start = -1; - for (int i = 0; i < count; ++i) { - const stbtt_vertex& v = verts[i]; - switch (v.type) { - case STBTT_vmove: - if (last_start >= 0 && !gg.outline_points.empty()) { - gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); - } - last_start = static_cast(gg.outline_points.size()); - push_point(static_cast(v.x), static_cast(v.y), 1); - break; - case STBTT_vline: - push_point(static_cast(v.x), static_cast(v.y), 1); - break; - case STBTT_vcurve: - push_point(static_cast(v.cx), static_cast(v.cy), 0); - push_point(static_cast(v.x), static_cast(v.y), 1); - break; - case STBTT_vcubic: - // Approximate cubic with two off-curve controls then on-curve end. - push_point(static_cast(v.cx), static_cast(v.cy), 0); - push_point(static_cast(v.cx1), static_cast(v.cy1), 0); - push_point(static_cast(v.x), static_cast(v.y), 1); - break; - default: - break; - } - } - - if (last_start >= 0 && !gg.outline_points.empty()) { - gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); - } - - const bool ok = !gg.outline_points.empty() && !gg.outline_contours.empty(); - if (ok) { - gg.outline.points_ptr = gg.outline_points.data(); - gg.outline.tags_ptr = gg.outline_tags.data(); - gg.outline.contour_end_idx = gg.outline_contours.data(); - gg.outline.points_cnt = static_cast(gg.outline_points.size()); - gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); - gg.outline_initialized = true; - } - - stbtt_FreeShape(face, verts); - return ok; } static u16 ClampToU16(float value) { @@ -260,28 +182,28 @@ static u16 ClampToU16(float value) { return static_cast(std::lround(clamped)); } -static FontState& GetState(Libraries::Font::OrbisFontHandle h) { - return g_font_state[h]; -} - -static FontState* TryGetState(Libraries::Font::OrbisFontHandle h) { - if (!h) - return nullptr; - auto it = g_font_state.find(h); - if (it == g_font_state.end()) - return nullptr; - return &it->second; -} - static LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { return g_library_state[lib]; } static void RemoveLibState(Libraries::Font::OrbisFontLib lib) { if (auto it = g_library_state.find(lib); it != g_library_state.end()) { - if (it->second.owned_device_cache) { - delete[] static_cast(it->second.owned_device_cache); - } + const auto free_fn = it->second.free_fn; + const auto alloc_ctx = it->second.alloc_ctx; + auto free_owned = [&](void* p) { + if (!p) { + return; + } + if (free_fn) { + free_fn(alloc_ctx, p); + return; + } + std::free(p); + }; + free_owned(it->second.owned_device_cache); + free_owned(it->second.owned_external_fonts_ctx); + free_owned(it->second.owned_sysfonts_ctx); + free_owned(it->second.owned_mspace); g_library_state.erase(it); } } @@ -292,6 +214,90 @@ static void LogExternalFormatSupport(u32 formats_mask) { static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); +static std::optional ResolveKnownSysFontAlias( + const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { + const auto resolve_existing = + [&](std::string_view filename) -> std::optional { + const std::filesystem::path file_path{std::string(filename)}; + std::error_code ec; + { + const auto candidate = sysfonts_dir / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font2" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + return std::nullopt; + }; + + static constexpr std::array, 41> kAliases = {{ + {"SST-EU-ROMAN-L.OTF", "SST-Light.otf"}, + {"SST-EU-ROMAN.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-M.OTF", "SST-Medium.otf"}, + {"SST-EU-ROMAN-R.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-I.OTF", "SST-Italic.otf"}, + {"SST-EU-ROMAN-B.OTF", "SST-Bold.otf"}, + {"SST-EU-ROMAN-BI.OTF", "SST-BoldItalic.otf"}, + {"SST-ITALIC-L.OTF", "SST-LightItalic.otf"}, + {"SST-ITALIC-R.OTF", "SST-Italic.otf"}, + {"SST-ITALIC-M.OTF", "SST-MediumItalic.otf"}, + {"SST-ITALIC-B.OTF", "SST-BoldItalic.otf"}, + {"SST-TYPEWRITER-R.OTF", "SSTTypewriter-Roman.otf"}, + {"SST-TYPEWRITER-B.OTF", "SSTTypewriter-Bd.otf"}, + {"SST-JPPRO-R.OTF", "SSTJpPro-Regular.otf"}, + {"SST-JPPRO-B.OTF", "SSTJpPro-Bold.otf"}, + {"SST-CNGB-HEI-R.TTF", "DFHEI5-SONY.ttf"}, + {"SST-ARIB-STD-B24-R.TTF", "SSTAribStdB24-Regular.ttf"}, + {"SST-ARABIC-R.OTF", "SSTArabic-Roman.otf"}, + {"SST-ARABIC-L.OTF", "SSTArabic-Light.otf"}, + {"SST-ARABIC-M.OTF", "SSTArabic-Medium.otf"}, + {"SST-ARABIC-B.OTF", "SSTArabic-Bold.otf"}, + {"SCE-EXT-HANGUL-L.OTF", "SCEPS4Yoongd-Light.otf"}, + {"SCE-EXT-HANGUL-R.OTF", "SCEPS4Yoongd-Medium.otf"}, + {"SCE-EXT-HANGUL-B.OTF", "SCEPS4Yoongd-Bold.otf"}, + {"SSTCC-SERIF-MONO.OTF", "e046323ms.ttf"}, + {"SSTCC-SERIF.OTF", "e046323ts.ttf"}, + {"SSTCC-SANSSERIF-MONO.OTF", "n023055ms.ttf"}, + {"SSTCC-SANSSERIF.OTF", "n023055ts.ttf"}, + {"SSTCC-CUSUAL.OTF", "d013013ds.ttf"}, + {"SSTCC-CURSIVE.OTF", "k006004ds.ttf"}, + {"SSTCC-SMALLCAPITAL.OTF", "c041056ts.ttf"}, + {"SCE-JP-CATTLEYA-L.OTF", "SCE-RDC-R-JPN.otf"}, + {"SCE-JP-CATTLEYA-B.OTF", "SCE-RDC-B-JPN.otf"}, + {"SST-THAI-L.OTF", "SSTThai-Light.otf"}, + {"SST-THAI-R.OTF", "SSTThai-Roman.otf"}, + {"SST-THAI-M.OTF", "SSTThai-Medium.otf"}, + {"SST-THAI-B.OTF", "SSTThai-Bold.otf"}, + {"SST-VIETNAMESE-L.OTF", "SSTVietnamese-Light.otf"}, + {"SST-VIETNAMESE-R.OTF", "SSTVietnamese-Roman.otf"}, + {"SST-VIETNAMESE-M.OTF", "SSTVietnamese-Medium.otf"}, + {"SST-VIETNAMESE-B.OTF", "SSTVietnamese-Bold.otf"}, + }}; + + for (const auto& [from, to] : kAliases) { + if (ps4_filename == from) { + return resolve_existing(to); + } + if (ps4_filename == to) { + if (auto reverse = resolve_existing(from)) { + return reverse; + } + } + } + return std::nullopt; +} + static void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params) { if (!params) { LOG_INFO(Lib_Font, "OpenFontSetParams: "); @@ -306,16 +312,7 @@ static void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params } static std::filesystem::path ResolveGuestPath(const char* guest_path) { - if (!guest_path) { - return {}; - } - if (guest_path[0] != '/') { - return std::filesystem::path(guest_path); - } - if (!g_mnt) { - return {}; - } - return g_mnt->GetHostPath(guest_path); + return Internal::ResolveGuestPath(guest_path); } static bool LoadGuestFileBytes(const std::filesystem::path& host_path, @@ -345,78 +342,9 @@ static bool LoadGuestFileBytes(const std::filesystem::path& host_path, return true; } -// External face scale helpers. -// - Point scaling (sceFontSetScalePoint): match stbtt_ScaleForPixelHeight semantics. -// - Pixel scaling (sceFontSetScalePixel): match stbtt_ScaleForMappingEmToPixels / FreeType EM. -static float ComputeScaleExtPoint(const stbtt_fontinfo* face, float pixel_h) { - if (!face) - return 0.0f; - return stbtt_ScaleForPixelHeight(face, pixel_h); -} - -static float ComputeScaleExtPixel(const stbtt_fontinfo* face, float pixel_h) { - if (!face) - return 0.0f; - return stbtt_ScaleForMappingEmToPixels(face, pixel_h); -} - -// Heuristic: identify external TrueType fonts that expect EM-based mapping -// (stbtt_ScaleForMappingEmToPixels) rather than ascender-height mapping. -// This is derived from observed ascent/descent/lineGap and blob sizes, but -// does not depend on the calling title. -static bool IsEmProfileExternalFont(const FontState& st) { - if (!st.ext_face_ready || st.ext_face_data.empty()) { - return false; - } - const std::size_t bytes = st.ext_face_data.size(); - const int asc = st.ext_ascent; - const int desc = st.ext_descent; - const int gap = st.ext_lineGap; - if (gap != 0) { - return false; - } - // Fonts observed in logs: - // - 81968 bytes, ascent=1989, descent=-469 - // - 3534360 bytes, ascent=901, descent=-123 - // - 1868660 bytes, ascent=800, descent=-200 - if (bytes == 81968 && asc == 1989 && desc == -469) { - return true; - } - if (bytes == 3534360 && asc == 901 && desc == -123) { - return true; - } - if (bytes == 1868660 && asc == 800 && desc == -200) { - return true; - } - return false; -} - -// Per-font-state external scale: -// - System fonts (font sets / fallbacks): ascender-height mapping (PixelHeight). -// - External CFF / point APIs: ascender-height mapping. -// - External TrueType + pixel scaling for certain profiles: EM-based mapping. -static float ComputeScaleExtForState(const FontState& st, const stbtt_fontinfo* face, - float pixel_h) { - if (!face) - return 0.0f; - const bool is_system_font = (st.font_set_type != 0) || !st.system_font_path.empty(); - if (is_system_font) { - return ComputeScaleExtPoint(face, pixel_h); - } - if (st.is_otf_cff || st.scale_point_active) { - return ComputeScaleExtPoint(face, pixel_h); - } - // Only a small subset of external TrueType fonts (Driveclub) require - // EM-based mapping; all others stay on ascender-height mapping to - // avoid regressions (Metaphor, etc.). - if (IsEmProfileExternalFont(st)) { - return ComputeScaleExtPixel(face, pixel_h); - } - return ComputeScaleExtPoint(face, pixel_h); -} - static constexpr float kPointsPerInch = 72.0f; static constexpr float kScaleEpsilon = 1e-4f; +constexpr u16 kStyleFrameMagic = 0x0F09; static float SafeDpiToFloat(u32 dpi) { return dpi == 0 ? kPointsPerInch : static_cast(dpi); @@ -431,123 +359,67 @@ static float PixelsToPoints(float px, u32 dpi) { return px * kPointsPerInch / dpi_f; } -constexpr u16 kStyleFrameMagic = 0x0F09; -constexpr u16 kStyleFrameFlagScale = 1 << 0; -constexpr u16 kStyleFrameFlagWeight = 1 << 1; -constexpr u16 kStyleFrameFlagSlant = 1 << 2; -constexpr u16 kStyleFrameFlagDpi = 1 << 3; - -enum class StyleFrameScalingMode : s32 { - None = 0, - Point = 1, - Pixel = 2, -}; - struct StyleFrameScaleState { float scale_w; float scale_h; u32 dpi_x; u32 dpi_y; - bool scale_overridden; - bool dpi_overridden; }; static StyleFrameScaleState ResolveStyleFrameScale(const OrbisFontStyleFrame* style, - const FontState& st) { + float base_scale_w, float base_scale_h, + u32 base_dpi_x, u32 base_dpi_y) { StyleFrameScaleState resolved{ - .scale_w = st.scale_w, - .scale_h = st.scale_h, - .dpi_x = st.dpi_x, - .dpi_y = st.dpi_y, - .scale_overridden = false, - .dpi_overridden = false, + .scale_w = base_scale_w, + .scale_h = base_scale_h, + .dpi_x = base_dpi_x, + .dpi_y = base_dpi_y, }; if (!style || style->magic != kStyleFrameMagic) { return resolved; } - if ((style->flags & kStyleFrameFlagDpi) != 0) { - if (style->dpiX > 0) { - resolved.dpi_x = static_cast(style->dpiX); - resolved.dpi_overridden = true; - } - if (style->dpiY > 0) { - resolved.dpi_y = static_cast(style->dpiY); - resolved.dpi_overridden = true; - } + if (style->hDpi != 0) { + resolved.dpi_x = style->hDpi; } - if ((style->flags & kStyleFrameFlagScale) != 0 && style->scaleWidth > 0.0f && - style->scaleHeight > 0.0f) { - const auto mode = static_cast(style->scalingFlag); - if (mode == StyleFrameScalingMode::Point) { - resolved.scale_w = PointsToPixels(style->scaleWidth, resolved.dpi_x); - resolved.scale_h = PointsToPixels(style->scaleHeight, resolved.dpi_y); - resolved.scale_overridden = true; - } else if (mode == StyleFrameScalingMode::Pixel) { - resolved.scale_w = style->scaleWidth; - resolved.scale_h = style->scaleHeight; - resolved.scale_overridden = true; - } + if (style->vDpi != 0) { + resolved.dpi_y = style->vDpi; + } + if ((style->flags1 & 1u) == 0) { + return resolved; + } + + const bool unit_is_pixel = (style->scaleUnit == 0); + if (unit_is_pixel) { + resolved.scale_w = style->scalePixelW; + resolved.scale_h = style->scalePixelH; + } else { + const u32 dpi_x = style->hDpi; + const u32 dpi_y = style->vDpi; + resolved.scale_w = dpi_x ? PointsToPixels(style->scalePixelW, dpi_x) : style->scalePixelW; + resolved.scale_h = dpi_y ? PointsToPixels(style->scalePixelH, dpi_y) : style->scalePixelH; } return resolved; } -static void InitializeStyleFrame(OrbisFontStyleFrame& frame) { - frame.magic = kStyleFrameMagic; - frame.flags = 0; - frame.dpiX = 72; - frame.dpiY = 72; - frame.scalingFlag = static_cast(StyleFrameScalingMode::None); - frame.scaleWidth = 0.0f; - frame.scaleHeight = 0.0f; - frame.weightXScale = 1.0f; - frame.weightYScale = 1.0f; - frame.slantRatio = 0.0f; -} - -static bool EnsureStyleFrameInitialized(OrbisFontStyleFrame* frame) { - if (!frame) - return false; - if (frame->magic != kStyleFrameMagic) { - InitializeStyleFrame(*frame); - } - return true; -} - static bool ValidateStyleFramePtr(const OrbisFontStyleFrame* frame) { return frame && frame->magic == kStyleFrameMagic; } -static void UpdateCachedLayout(FontState& st) { - if (!st.ext_face_ready) { - return; - } - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - int asc = 0, desc = 0, gap = 0; - stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); - const float scale = st.ext_scale_for_height; - st.cached_baseline_y = static_cast(asc) * scale; - st.layout_cached = true; +static inline u32 LoadU32(const void* base, std::size_t off) { + u32 v = 0; + std::memcpy(&v, static_cast(base) + off, sizeof(v)); + return v; } - -static inline std::uint64_t MakeGlyphKey(u32 code, int pixel_h) { - return (static_cast(code) << 32) | static_cast(pixel_h); +static inline float LoadF32(const void* base, std::size_t off) { + float v = 0.0f; + std::memcpy(&v, static_cast(base) + off, sizeof(v)); + return v; } -static std::unordered_set g_stride_logged; -static inline void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* surf) { - if (!surf) - return; - const void* key = static_cast(surf); - if (g_stride_logged.insert(key).second) { - const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); - const long expected = static_cast(surf->width) * bpp; - const bool match = (expected == surf->widthByte); - LOG_INFO(Lib_Font, - "StrideCheck: surf={} buf={} width={} height={} pixelSizeByte={} widthByte={} " - "expected={} match={}", - key, surf->buffer, surf->width, surf->height, bpp, surf->widthByte, expected, - match); - } +static inline void StoreU32(void* base, std::size_t off, u32 v) { + std::memcpy(static_cast(base) + off, &v, sizeof(v)); +} +static inline void StoreF32(void* base, std::size_t off, float v) { + std::memcpy(static_cast(base) + off, &v, sizeof(v)); } static void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, @@ -560,64 +432,10 @@ static void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, } } -static const GlyphEntry* GetGlyphEntry(FontState& st, Libraries::Font::OrbisFontHandle handle, - u32 code, const stbtt_fontinfo*& face_out, - float& scale_out) { - face_out = nullptr; - scale_out = 0.0f; - if (st.ext_face_ready) { - face_out = &st.ext_face; - } - if (!face_out) { - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, handle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); - } - if (system_attached) { - face_out = &st.ext_face; - } - } - if (!face_out) { - return nullptr; - } - if (st.ext_scale_for_height == 0.0f) { - st.ext_scale_for_height = ComputeScaleExtForState(st, face_out, st.scale_h); - } - scale_out = st.ext_scale_for_height; - const int pixel_h = std::max(1, static_cast(std::lround(st.scale_h))); - const std::uint64_t key = MakeGlyphKey(code, pixel_h); - GlyphEntry* ge = nullptr; - if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { - ge = &it->second; - } - if (!ge) { - GlyphEntry entry{}; - int aw = 0, lsb = 0; - stbtt_GetCodepointHMetrics(face_out, static_cast(code), &aw, &lsb); - stbtt_GetCodepointBitmapBox(face_out, static_cast(code), scale_out, scale_out, - &entry.x0, &entry.y0, &entry.x1, &entry.y1); - entry.w = std::max(0, entry.x1 - entry.x0); - entry.h = std::max(0, entry.y1 - entry.y0); - entry.advance = static_cast(aw) * scale_out; - entry.bearingX = static_cast(lsb) * scale_out; - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } - return ge; -} - -// Font-set ID bit layout: -// bits [31:28] family (0x18 = SST Standard UI) -// bits [27:24] region (0x0 Latin, 0x8 Japanese, 0xC Chinese, 0xD Korean, 0xE Thai, 0xF -// Asian-mix) bits [23:20] variant (0x0 European, 0x2 Typewriter, 0x4 JP, 0x7 EU JPCJK, 0x8 JP -// UH, 0xC GB UH) bits [19:16] coverage (0x0 base, 0x4 Arabic, 0x5 Vietnamese, 0x6 Thai, 0xA CJK -// TH, 0xB full-set) bits [15:8] language tag (0x44 JP, 0x53 VI, 0x54 TH, 0x80 GB, 0xA CJK TH, 0xA4 -// JP CJK, 0xC4 JP AR) bits [7:0] weight/style (0x43 light, 0x44 regular, 0x45 medium, 0x47 bold, -// 0x53 light VI, 0x57 bold VI, 0xC7 Arabic bold) struct SystemFontDefinition { u32 font_set_type; - const char* config_key; // legacy UPPER_SNAKE (e.g., FONTSET_SST_STD_EUROPEAN_LIGHT) - const char* default_file; // default filename within sys font dir + const char* config_key; + const char* default_file; }; static constexpr SystemFontDefinition kSystemFontDefinitions[] = { @@ -731,7 +549,6 @@ static const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type) { } static std::filesystem::path GetSysFontBaseDir() { - // Only use the configured path. No variations. std::filesystem::path base = Config::getSysFontPath(); std::error_code ec; if (base.empty()) { @@ -754,19 +571,16 @@ static std::string MacroToCamel(const char* macro_key) { return {}; } std::string s(macro_key); - // Expect prefix "FONTSET_" const std::string prefix = "FONTSET_"; if (s.rfind(prefix, 0) != 0) { return {}; } std::string out = "FontSet"; - // Split on '_' size_t pos = prefix.size(); while (pos < s.size()) { size_t next = s.find('_', pos); const size_t len = (next == std::string::npos) ? (s.size() - pos) : (next - pos); std::string token = s.substr(pos, len); - // Lowercase then capitalize first char for (auto& c : token) { c = static_cast(std::tolower(static_cast(c))); } @@ -796,11 +610,7 @@ static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", def->config_key, override_path->string()); - // ignore invalid override; fall back to default filename } - // Also support new-style keys: - // - UpperCamelCase: 'FontSetSstStdEuropeanLight' - // - lowerCamelCase: 'fontSetSstStdEuropeanLight' const auto camel_key = MacroToCamel(def->config_key); if (!camel_key.empty()) { if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { @@ -843,55 +653,17 @@ static const FontSetCache* EnsureFontSetCache(u32 font_set_type) { cache.loaded = true; const auto path = ResolveSystemFontPath(font_set_type); if (!path.empty() && LoadFontFile(path, cache.bytes)) { - if (!HasTrueTypeGlyf(cache.bytes)) { - LOG_WARNING(Lib_Font, - "SystemFonts: '{}' lacks 'glyf' table (likely CFF) -> falling back", - path.string()); - cache.bytes.clear(); - cache.available = false; - } else { - cache.available = true; - cache.path = path; - } + cache.available = true; + cache.path = path; } else { cache.available = false; LOG_ERROR(Lib_Font, "SystemFonts: failed to load font for set type=0x{:08X} path='{}'", font_set_type, path.string()); } - if (!cache.available) { - if (EnsureSystemFontBlob()) { - cache.bytes = g_system_font_bytes; - cache.path = g_system_font_path; - cache.available = true; - LOG_WARNING(Lib_Font, "SystemFonts: using fallback font '{}' for type 0x{:08X}", - cache.path.string(), font_set_type); - } - } } return cache.available ? &cache : nullptr; } -static bool HasTrueTypeGlyf(const std::vector& bytes) { - if (bytes.size() < 12) { - return false; - } - const u8* ptr = bytes.data(); - const u32 num_tables = (ptr[4] << 8) | ptr[5]; - constexpr u32 kDirEntrySize = 16; - if (bytes.size() < 12 + static_cast(num_tables) * kDirEntrySize) { - return false; - } - const u8* dir = ptr + 12; - for (u32 i = 0; i < num_tables; ++i) { - const u8* entry = dir + i * kDirEntrySize; - const u32 tag = (entry[0] << 24) | (entry[1] << 16) | (entry[2] << 8) | entry[3]; - if (tag == (('g' << 24) | ('l' << 16) | ('y' << 8) | 'f')) { - return true; - } - } - return false; -} - static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes) { if (path.empty()) { return false; @@ -902,125 +674,6 @@ static bool LoadFontFile(const std::filesystem::path& path, std::vector* source_bytes = nullptr; - std::filesystem::path source_path; - if (cache) { - source_bytes = &cache->bytes; - source_path = cache->path; - } else { - if (!EnsureSystemFontBlob()) { - return false; - } - source_bytes = &g_system_font_bytes; - source_path = g_system_font_path; - } - if (!source_bytes || source_bytes->empty()) { - return false; - } - st.ext_face_data = *source_bytes; - st.ext_cache.clear(); - st.ext_scale_for_height = 0.0f; - st.layout_cached = false; - st.logged_ext_use = true; // skip "external (game font)" log for built-in fallback - const int offset = stbtt_GetFontOffsetForIndex(st.ext_face_data.data(), 0); - if (offset < 0) { - LOG_ERROR(Lib_Font, "SystemFace: invalid font offset in '{}'", source_path.string()); - st.ext_face_data.clear(); - st.ext_face_ready = false; - return false; - } - if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset) == 0) { - LOG_ERROR(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", source_path.string()); - st.ext_face_data.clear(); - st.ext_face_ready = false; - return false; - } - st.ext_face_ready = true; - st.system_font_path = source_path; - LOG_INFO(Lib_Font, "system font attached"); - LOG_DEBUG(Lib_Font, - "system font attach params:\n" - " handle={}\n" - " path={}\n", - static_cast(handle), source_path.string()); - return true; -} - -static std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, - bool& attached_out) { - attached_out = AttachSystemFont(st, handle); - if (attached_out) { - st.system_requested = true; - return {}; - } - if (!st.system_requested) { - st.system_requested = true; - const auto configured = Config::getSysFontPath(); - return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " - "could not be loaded", - static_cast(handle), configured.string()); - } - return {}; -} } // namespace namespace Libraries::Font { @@ -1036,7 +689,10 @@ struct FontLibOpaque { u8 reserved2[0x50]; void* sys_driver; void* fontset_registry; - u8 reserved3[0x10]; + union { + u8 reserved3[0x10]; + std::array sysfonts_ctx_name_overrides; + }; void* sysfonts_ctx; void* external_fonts_ctx; u32* device_cache_buf; @@ -1052,106 +708,465 @@ static_assert(offsetof(FontLibOpaque, external_fonts_ctx) == 0xA8, "FontLibOpaque external_fonts_ctx offset"); static_assert(offsetof(FontLibOpaque, device_cache_buf) == 0xB0, "FontLibOpaque device_cache_buf offset"); -struct OrbisFontRenderer_ {}; -static void* g_allocator_vtbl_stub[4] = {}; -static std::uint8_t g_sys_driver_stub{}; -static std::uint8_t g_fontset_registry_stub{}; -static std::uint8_t g_sysfonts_ctx_stub{}; -static std::uint8_t g_external_fonts_ctx_stub{}; -static u32 g_device_cache_stub{}; +#pragma pack(push, 1) +struct FontLibReserved1Main { + /*0x00*/ u32 reserved_0x00; + /*0x04*/ u32 mem_kind; + /*0x08*/ u32 region_size; + /*0x0C*/ void* region_base; +}; +#pragma pack(pop) +static_assert(sizeof(FontLibReserved1Main) == sizeof(((FontLibOpaque*)nullptr)->reserved1), + "FontLibReserved1Main size"); +static_assert(offsetof(FontLibReserved1Main, mem_kind) == 0x04, + "FontLibReserved1Main mem_kind offset"); +static_assert(offsetof(FontLibReserved1Main, region_size) == 0x08, + "FontLibReserved1Main region_size offset"); +static_assert(offsetof(FontLibReserved1Main, region_base) == 0x0C, + "FontLibReserved1Main region_base offset"); + +struct FontLibReserved2Iface { + /*0x00*/ u8 reserved_00[0x20]; + /*0x20*/ std::uintptr_t alloc_fn; + /*0x28*/ std::uintptr_t dealloc_fn; + /*0x30*/ std::uintptr_t realloc_fn; + /*0x38*/ std::uintptr_t calloc_fn; + /*0x40*/ u8 reserved_40[0x10]; +}; +static_assert(sizeof(FontLibReserved2Iface) == sizeof(((FontLibOpaque*)nullptr)->reserved2), + "FontLibReserved2Iface size"); +static_assert(offsetof(FontLibReserved2Iface, alloc_fn) == 0x20, + "FontLibReserved2Iface alloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, dealloc_fn) == 0x28, + "FontLibReserved2Iface dealloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, realloc_fn) == 0x30, + "FontLibReserved2Iface realloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, calloc_fn) == 0x38, + "FontLibReserved2Iface calloc_fn offset"); + +struct FontLibTail { + /*0x00*/ u8 reserved_00[0x04]; + /*0x04*/ u32 workspace_size; + /*0x08*/ void* workspace; + /*0x10*/ u8 reserved_10[0x10]; + /*0x20*/ void* list_head_ptr; + /*0x28*/ OrbisFontHandle list_head; + /*0x30*/ u8 reserved_30[0x88]; +}; +static_assert(offsetof(FontLibTail, workspace_size) == 0x04, "FontLibTail workspace_size offset"); +static_assert(offsetof(FontLibTail, workspace) == 0x08, "FontLibTail workspace offset"); +static_assert(offsetof(FontLibTail, list_head_ptr) == 0x20, "FontLibTail list_head_ptr offset"); +static_assert(offsetof(FontLibTail, list_head) == 0x28, "FontLibTail list_head offset"); + +#pragma pack(push, 1) +struct FontLibReserved1SysfontTail { + /*0x00*/ u32 reserved_0x00; + /*0x04*/ void* sysfont_desc_ptr; + /*0x0C*/ u64 sysfont_flags; +}; +#pragma pack(pop) +static_assert(sizeof(FontLibReserved1SysfontTail) == sizeof(((FontLibOpaque*)nullptr)->reserved1), + "FontLibReserved1SysfontTail size"); +static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_desc_ptr) == 0x04, + "FontLibReserved1SysfontTail sysfont_desc_ptr offset"); +static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_flags) == 0x0C, + "FontLibReserved1SysfontTail sysfont_flags offset"); + +namespace { +static FontHandleOpaqueNative* GetNativeFont(OrbisFontHandle h); + +static void LogCachedStyleOnce(OrbisFontHandle handle, const FontHandleOpaqueNative& font) { + static std::mutex s_mutex; + static std::unordered_set s_logged; + std::scoped_lock lock(s_mutex); + if (s_logged.find(handle) != s_logged.end()) { + return; + } + s_logged.insert(handle); + + LOG_DEBUG(Lib_Font, + "BindRenderer: cached_style snapshot: hDpi={} vDpi={} scaleUnit={} baseScale={} " + "scalePixelW={} scalePixelH={} effectWeightX={} effectWeightY={} slantRatio={}", + font.cached_style.hDpi, font.cached_style.vDpi, font.cached_style.scaleUnit, + font.cached_style.baseScale, font.cached_style.scalePixelW, + font.cached_style.scalePixelH, font.cached_style.effectWeightX, + font.cached_style.effectWeightY, font.cached_style.slantRatio); +} + +static void LogRenderResultSample(OrbisFontHandle handle, u32 code, + const OrbisFontGlyphMetrics& metrics, + const OrbisFontRenderOutput& result) { + static std::mutex s_mutex; + static std::unordered_map s_counts; + std::scoped_lock lock(s_mutex); + int& count = s_counts[handle]; + if (count >= 5) { + return; + } + ++count; + LOG_DEBUG(Lib_Font, + "RenderSample: handle={} code=U+{:04X} update=[{},{} {}x{}] img=[bx={} by={} adv={} " + "stride={} w={} h={}] metrics=[w={} h={} hbx={} hby={} hadv={}]", + static_cast(handle), code, result.UpdateRect.x, result.UpdateRect.y, + result.UpdateRect.w, result.UpdateRect.h, result.ImageMetrics.bearingX, + result.ImageMetrics.bearingY, result.ImageMetrics.advance, result.ImageMetrics.stride, + result.ImageMetrics.width, result.ImageMetrics.height, metrics.width, metrics.height, + metrics.Horizontal.bearingX, metrics.Horizontal.bearingY, metrics.Horizontal.advance); +} + +static inline u8 CachedStyleCacheFlags(const OrbisFontStyleFrame& cached_style) { + return static_cast(cached_style.cache_flags_and_direction & 0xFFu); +} + +static inline void CachedStyleSetCacheFlags(OrbisFontStyleFrame& cached_style, u8 flags) { + cached_style.cache_flags_and_direction = + (cached_style.cache_flags_and_direction & 0xFFFFFF00u) | static_cast(flags); +} + +static inline void CachedStyleSetDirectionWord(OrbisFontStyleFrame& cached_style, u16 word) { + cached_style.cache_flags_and_direction = + (cached_style.cache_flags_and_direction & 0x0000FFFFu) | (static_cast(word) << 16); +} + +static inline float CachedStyleGetScalar(const OrbisFontStyleFrame& cached_style) { + float value = 0.0f; + std::memcpy(&value, &cached_style.cached_scalar_bits, sizeof(value)); + return value; +} + +static inline void CachedStyleSetScalar(OrbisFontStyleFrame& cached_style, float value) { + std::memcpy(&cached_style.cached_scalar_bits, &value, sizeof(value)); +} + +constexpr std::uintptr_t kFtRendererCreateFnAddr = 0x0100f6d0u; +constexpr std::uintptr_t kFtRendererDestroyFnAddr = 0x0100f790u; + +static s32 PS4_SYSV_ABI FtRendererCreate(void* renderer) { + if (!renderer) { + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + + auto* r = static_cast(renderer); + r->ft_backend.renderer_header_0x10 = static_cast(&r->base.mem_kind); + r->ft_backend.unknown_0x08 = 0; + r->ft_backend.unknown_0x10 = 0; + r->ft_backend.unknown_0x18 = 0; + r->ft_backend.initialized_marker = r; + return ORBIS_OK; +} + +static s32 PS4_SYSV_ABI FtRendererDestroy(void* renderer) { + if (!renderer) { + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + auto* r = static_cast(renderer); + r->ft_backend.initialized_marker = nullptr; + return ORBIS_OK; +} + +bool AcquireLibraryLock(FontLibOpaque* lib, u32& out_prev_lock_word) { + if (!lib) { + return false; + } + + for (;;) { + const u32 lock_word = lib->lock_word; + if (lib->magic != 0x0F01) { + return false; + } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + + std::atomic_ref ref(lib->lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +void ReleaseLibraryLock(FontLibOpaque* lib, u32 prev_lock_word) { + if (!lib) { + return; + } + lib->lock_word = prev_lock_word & 0x7fffffff; +} +static FontHandleOpaqueNative* GetNativeFont(OrbisFontHandle h) { + return h ? reinterpret_cast(h) : nullptr; +} + +static bool AcquireFontLock(FontHandleOpaqueNative* font, u32& out_prev_lock_word) { + if (!font) { + return false; + } + for (;;) { + const u32 lock_word = font->lock_word; + if (font->magic != 0x0F02) { + return false; + } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + std::atomic_ref ref(font->lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +static void ReleaseFontLock(FontHandleOpaqueNative* font, u32 prev_lock_word) { + if (!font) { + return; + } + font->lock_word = prev_lock_word & 0x7fffffff; +} + +static bool AcquireCachedStyleLock(FontHandleOpaqueNative* font, u32& out_prev_lock_word) { + if (!font) { + return false; + } + for (;;) { + const u32 lock_word = font->cached_style.cache_lock_word; + if (font->magic != 0x0F02) { + return false; + } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + std::atomic_ref ref(font->cached_style.cache_lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +static void ReleaseCachedStyleLock(FontHandleOpaqueNative* font, u32 prev_lock_word) { + if (!font) { + return; + } + font->cached_style.cache_lock_word = prev_lock_word & 0x7fffffff; +} +} // namespace + +namespace {} + +struct OrbisFontRenderer_ { + u16 magic = 0; + u16 reserved0 = 0; +}; +static_assert(offsetof(OrbisFontRenderer_, magic) == 0, "Renderer magic offset"); s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("library", library), + Param("buffer", buffer), + Param("size", size), + })); + if (!library) { - LOG_ERROR(Lib_Font, "invalid library"); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } auto* lib = static_cast(library); if (lib->magic != 0x0F01) { - LOG_ERROR(Lib_Font, "invalid library (bad magic)"); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - constexpr u32 kMinCacheSize = 0x1020u; - if (size < kMinCacheSize) { - LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: size {} too small (min {})", size, - kMinCacheSize); - LOG_ERROR(Lib_Font, "size too small"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - if (lib->device_cache_buf && lib->device_cache_buf != &g_device_cache_stub) { - LOG_WARNING(Lib_Font, "AttachDeviceCacheBuffer: library={} already attached", - static_cast(library)); - LOG_ERROR(Lib_Font, "already attached"); - return ORBIS_FONT_ERROR_ALREADY_ATTACHED; + + u32 prev_lock_word = 0; + if (!AcquireLibraryLock(lib, prev_lock_word)) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - std::uint8_t* owned_buffer = nullptr; - if (!buffer) { - owned_buffer = new (std::nothrow) std::uint8_t[size]; - if (!owned_buffer) { - LOG_ERROR(Lib_Font, "allocation failed"); - return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + const auto kBusy = reinterpret_cast( + static_cast(std::numeric_limits::max())); + + u32* current_cache = nullptr; + for (;;) { + current_cache = lib->device_cache_buf; + if (current_cache != kBusy) { + std::atomic_ref ref(lib->device_cache_buf); + u32* expected = current_cache; + if (ref.compare_exchange_weak(expected, kBusy, std::memory_order_acq_rel)) { + current_cache = expected; + break; + } } - buffer = owned_buffer; + Libraries::Kernel::sceKernelUsleep(0x1e); } - auto* header = static_cast(buffer); - header[0] = size; - const u32 usable_bytes = size > 0x1000 ? size - 0x1000 : 0; - const u32 page_count = usable_bytes >> 12; - if (page_count == 0) { - if (owned_buffer) { - delete[] owned_buffer; - } - LOG_ERROR(Lib_Font, "page_count == 0"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - header[1] = page_count; - header[2] = 0; - header[3] = page_count; - if (size >= 0x20) { - auto* meta = reinterpret_cast(header + 4); - *meta = 0x000FF800001000ULL; - } + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + (void)alloc_fn; + (void)free_fn; - lib->device_cache_buf = header; - auto& state = GetLibState(library); - if (owned_buffer) { - state.owned_device_cache = owned_buffer; - state.owned_device_cache_size = size; - lib->flags |= 1u; + s32 rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + u32* cache_to_store = current_cache; + u32 owned = 0; + + if (current_cache != nullptr) { + rc = ORBIS_FONT_ERROR_ALREADY_ATTACHED; + } else if (size < 0x1020) { + cache_to_store = nullptr; + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; } else { - state.owned_device_cache = nullptr; - state.owned_device_cache_size = 0; + u32* header = static_cast(buffer); + if (!header) { + header = static_cast(alloc_fn(lib->alloc_ctx, size)); + if (!header) { + cache_to_store = nullptr; + rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } else { + owned = 1; + } + } + + if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + header[0] = size; + const u32 page_count = ((size - 0x1000) >> 12); + header[1] = page_count; + header[3] = page_count; + header[2] = 0; + *reinterpret_cast(header + 4) = 0x0FF800001000ULL; + + if (0x0FFF < (size - 0x1000)) { + lib->flags |= owned; + rc = ORBIS_OK; + cache_to_store = header; + if (owned) { + auto& state = GetLibState(library); + state.alloc_ctx = lib->alloc_ctx; + state.alloc_fn = alloc_fn; + state.free_fn = free_fn; + state.owned_device_cache = header; + state.owned_device_cache_size = size; + } + } else { + if (!buffer) { + free_fn(lib->alloc_ctx, header); + } + cache_to_store = nullptr; + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + } } - LOG_INFO(Lib_Font, "device cache attach requested"); - LOG_DEBUG(Lib_Font, - "device cache attach params:\n" - " library={}\n" - " buffer={}\n" - " size={}\n" - " pages={}\n" - " owned_buffer={}\n", - static_cast(library), buffer, size, page_count, - owned_buffer ? "true" : "false"); - return ORBIS_OK; + lib->device_cache_buf = cache_to_store; + ReleaseLibraryLock(lib, prev_lock_word); + if (rc != ORBIS_OK) { + LOG_ERROR(Lib_Font, "FAILED"); + } + return rc; } s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { - if (!fontHandle || !renderer) { - LOG_ERROR(Lib_Font, "invalid parameter"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto& st = GetState(fontHandle); - st.bound_renderer = renderer; LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " handle={}\n" - " renderer={}\n", - static_cast(fontHandle), static_cast(renderer)); - return ORBIS_OK; + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("renderer", renderer), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + ReleaseFontLock(font, prev_font_lock); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_ALREADY_BOUND_RENDERER; + if (font->renderer_binding.renderer == nullptr) { + rc = ORBIS_FONT_ERROR_INVALID_RENDERER; + if (renderer && *reinterpret_cast(renderer) == 0x0F07) { + rc = ORBIS_OK; + + std::memcpy(&font->cached_style.effectWeightY, &font->style_tail.slant_ratio, + sizeof(u64)); + + std::memcpy(&font->cached_style, &font->style_frame[0], sizeof(u32)); + + const u32 dpi_y = font->style_frame[1]; + const u32 scale_unit = font->style_tail.scale_unit; + const u32 reserved_0x0c = font->style_tail.reserved_0x0c; + const float scale_w = font->style_tail.scale_w; + const float scale_h = font->style_tail.scale_h; + const float effect_weight_x = font->style_tail.effect_weight_x; + const float effect_weight_y = font->style_tail.effect_weight_y; + + font->cached_style.hDpi = dpi_y; + font->cached_style.vDpi = scale_unit; + font->cached_style.scaleUnit = reserved_0x0c; + font->cached_style.baseScale = scale_w; + font->cached_style.scalePixelW = scale_h; + font->cached_style.scalePixelH = effect_weight_x; + font->cached_style.effectWeightX = effect_weight_y; + + font->renderer_binding.renderer = renderer; + + LogCachedStyleOnce(fontHandle, *font); + + if (auto* st = Internal::TryGetState(fontHandle)) { + st->bound_renderer = renderer; + st->dpi_x = font->style_frame[0]; + st->dpi_y = font->style_frame[1]; + if (scale_unit != 0) { + st->render_scale_point_active = true; + st->render_scale_point_w = scale_w; + st->render_scale_point_h = scale_h; + } else { + st->render_scale_point_active = false; + st->render_scale_w = scale_w; + st->render_scale_h = scale_h; + } + } + } + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + ReleaseFontLock(font, prev_font_lock); + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_ALREADY_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "ALREADY_BOUND"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_RENDERER) { + LOG_ERROR(Lib_Font, "INVALID_RENDERER"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } + return rc; } s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, @@ -1215,7 +1230,7 @@ sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter) { OrbisFontTextCharacter* current = textCharacter->prev; while (current) { - if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + if (current->unknown_0x31 == 0 && current->unknown_0x33 == 0) { return current; } current = current->prev; @@ -1231,7 +1246,7 @@ sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter) { OrbisFontTextCharacter* current = textCharacter->next; while (current) { - if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + if (current->unknown_0x31 == 0 && current->unknown_0x33 == 0) { return current; } current = current->next; @@ -1284,65 +1299,190 @@ s32 PS4_SYSV_ABI sceFontCreateLibrary(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, OrbisFontLibCreateParams create_params, u64 edition, OrbisFontLib* pLibrary) { - LOG_INFO(Lib_Font, "library create requested"); - LOG_DEBUG(Lib_Font, - "library create params:\n" - " memory={}\n" - " create_params={}\n" - " edition={}\n" - " out_library_ptr={}\n", - static_cast(memory), static_cast(create_params), edition, - static_cast(pLibrary)); + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("memory", memory), + Param("create_params", create_params), + Param("edition", edition), + Param("pLibrary", pLibrary), + })); - if (!pLibrary) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (pLibrary) { + *pLibrary = nullptr; } if (!memory) { + LOG_ERROR(Lib_Font, "NULL_POINTER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } if (memory->mem_kind != 0x0F00 || !memory->iface || !memory->iface->alloc || !memory->iface->dealloc) { + LOG_ERROR(Lib_Font, "INVALID_MEMORY"); return ORBIS_FONT_ERROR_INVALID_MEMORY; } - auto* lib = new (std::nothrow) FontLibOpaque{}; - if (!lib) { + if (!create_params || !pLibrary) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + using MallocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto malloc_fn = reinterpret_cast(memory->iface->alloc); + const auto free_fn = reinterpret_cast(memory->iface->dealloc); + if (!malloc_fn || !free_fn) { + LOG_ERROR(Lib_Font, "INVALID_MEMORY"); + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + + void* lib_mem = malloc_fn(memory->mspace_handle, 0x100); + if (!lib_mem) { + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } + + void* mspace = malloc_fn(memory->mspace_handle, 0x4000); + if (!mspace) { + free_fn(memory->mspace_handle, lib_mem); + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + + auto* lib_bytes = static_cast(lib_mem); + auto clear_20 = [&](std::size_t offset) { std::memset(lib_bytes + offset, 0, 0x20); }; + clear_20(0x00); + clear_20(sizeof(FontLibOpaque) + offsetof(FontLibOpaque, alloc_vtbl)); + clear_20(sizeof(FontLibOpaque) + offsetof(FontLibOpaque, flags)); + clear_20(offsetof(FontLibOpaque, sysfonts_ctx)); + clear_20(offsetof(FontLibOpaque, sys_driver)); + clear_20(offsetof(FontLibOpaque, reserved2) + 0x30); + clear_20(offsetof(FontLibOpaque, reserved2) + 0x10); + clear_20(offsetof(FontLibOpaque, alloc_ctx)); + clear_20(offsetof(FontLibOpaque, reserved3) + 0x0C); + clear_20(offsetof(FontLibOpaque, fontset_registry)); + + auto* lib = static_cast(lib_mem); + + auto* reserved1 = reinterpret_cast(lib->reserved1); + if (reserved1) { + reserved1->mem_kind = 0x0F00; + reserved1->region_size = memory->region_size; + reserved1->region_base = memory->region_base; + } + + lib->alloc_ctx = memory->mspace_handle; + lib->alloc_vtbl = reinterpret_cast(const_cast(memory->iface)); + + auto* reserved2 = reinterpret_cast(lib->reserved2); + if (reserved2) { + reserved2->alloc_fn = reinterpret_cast(memory->iface->alloc); + reserved2->dealloc_fn = reinterpret_cast(memory->iface->dealloc); + reserved2->realloc_fn = reinterpret_cast(memory->iface->realloc_fn); + reserved2->calloc_fn = reinterpret_cast(memory->iface->calloc_fn); + } + + lib->sys_driver = const_cast(create_params); + + auto* tail = reinterpret_cast(lib + 1); + if (tail) { + tail->workspace_size = 0x4000; + tail->workspace = mspace; + tail->list_head_ptr = &tail->list_head; + tail->list_head = nullptr; + } + + const auto* sys_driver = reinterpret_cast(create_params); + const auto init_fn = sys_driver ? sys_driver->init : nullptr; + if (!init_fn) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const s32 init_rc = init_fn(memory, lib); + if (init_rc != ORBIS_OK) { + free_fn(memory->mspace_handle, mspace); + free_fn(memory->mspace_handle, lib_mem); + LOG_ERROR(Lib_Font, "INIT_FAILED"); + return init_rc; + } + + s32 sdk_version = 0; + u32 sdk_version_u32 = 0; + if (Libraries::Kernel::sceKernelGetCompiledSdkVersion(&sdk_version) == ORBIS_OK) { + sdk_version_u32 = static_cast(sdk_version); + } + const u32 edition_hi = static_cast(edition >> 32); + u32 v = edition_hi; + if (sdk_version_u32 != 0) { + v = sdk_version_u32; + if (edition_hi <= sdk_version_u32) { + v = edition_hi; + } + } + if (0x92ffffu < v) { + auto* pad_00A_01E = lib->reserved1; + auto* feature_word = reinterpret_cast(pad_00A_01E); + if (v < 0x1500000u) { + u32 tmp = (*feature_word) | 1u; + *feature_word = tmp; + tmp |= 2u; + *feature_word = tmp; + *feature_word = tmp | 4u; + } else if (v < 0x2000000u) { + u32 tmp = *feature_word; + tmp |= 2u; + *feature_word = tmp; + *feature_word = tmp | 4u; + } else if (v <= 0x2000070u) { + u32 tmp = *feature_word; + *feature_word = tmp | 4u; + } + } + lib->magic = 0x0F01; - lib->lock_word = 0; - lib->flags = 0; - lib->alloc_ctx = const_cast(memory); - lib->alloc_vtbl = g_allocator_vtbl_stub; - lib->sys_driver = &g_sys_driver_stub; - lib->fontset_registry = &g_fontset_registry_stub; - lib->sysfonts_ctx = nullptr; - lib->external_fonts_ctx = nullptr; - lib->device_cache_buf = nullptr; + *pLibrary = lib; auto& state = GetLibState(lib); state = LibraryState{}; state.backing_memory = memory; state.create_params = create_params; state.edition = edition; - state.native = lib; - state.owned_device_cache = nullptr; - state.owned_device_cache_size = 0; + state.alloc_ctx = lib->alloc_ctx; + state.alloc_fn = reinterpret_cast(lib->alloc_vtbl[0]); + state.free_fn = reinterpret_cast(lib->alloc_vtbl[1]); + state.owned_mspace = mspace; + state.owned_mspace_size = 0x4000; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { - if (!pLibrary || !*pLibrary) { + if (!pLibrary) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto lib = *pLibrary; + if (!lib) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } LOG_INFO(Lib_Font, "library destroy requested"); LOG_DEBUG(Lib_Font, "library destroy params:\n" " library_handle_ptr={}\n" " library_handle={}\n", static_cast(pLibrary), static_cast(lib)); + auto* native = static_cast(lib); + if (native->magic != 0x0F01) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + RemoveLibState(lib); - delete static_cast(lib); + + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn = + native->alloc_vtbl ? reinterpret_cast(native->alloc_vtbl[1]) : nullptr; + if (free_fn) { + free_fn(native->alloc_ctx, native); + } else { + std::free(native); + } *pLibrary = nullptr; return ORBIS_OK; } @@ -1356,31 +1496,138 @@ s32 PS4_SYSV_ABI sceFontCreateRenderer(const OrbisFontMem* memory, s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, OrbisFontRendererCreateParams create_params, u64 edition, OrbisFontRenderer* pRenderer) { - LOG_INFO(Lib_Font, "renderer create requested"); - LOG_DEBUG(Lib_Font, - "renderer create params:\n" - " memory={}\n" - " create_params={}\n" - " edition={}\n" - " out_renderer_ptr={}\n", - static_cast(memory), static_cast(create_params), edition, - static_cast(pRenderer)); + LOG_INFO(Lib_Font, "called"); + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("memory", memory), + Param("create_params", create_params), + Param("edition", edition), + Param("pRenderer", pRenderer), + })); + + s32 rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (!memory) { + if (!pRenderer) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return rc; + } + *pRenderer = nullptr; + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return rc; + } + + rc = ORBIS_FONT_ERROR_INVALID_MEMORY; + if (memory->mem_kind == 0x0F00 && memory->iface && memory->iface->alloc && + memory->iface->dealloc) { + if (!create_params) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } else { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (pRenderer) { + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = reinterpret_cast(memory->iface->alloc); + const auto free_fn = reinterpret_cast(memory->iface->dealloc); + + const auto* selection = + static_cast( + create_params); + const u32 render_size = selection ? selection->size : 0u; + void* renderer_mem = alloc_fn(memory->mspace_handle, render_size); + void* workspace = alloc_fn(memory->mspace_handle, 0x4000); + + rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; + if (renderer_mem && workspace) { + auto* renderer = static_cast(renderer_mem); + + std::memset(renderer, 0, offsetof(Internal::RendererOpaque, mem_kind)); + renderer->magic = 0x0F07; + renderer->mem_kind = 0x0F00; + 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 = reinterpret_cast(memory->iface->alloc); + renderer->free_fn = reinterpret_cast(memory->iface->dealloc); + renderer->realloc_fn = + reinterpret_cast(memory->iface->realloc_fn); + renderer->calloc_fn = + reinterpret_cast(memory->iface->calloc_fn); + + renderer->selection = + const_cast(static_cast(create_params)); + renderer->outline_magic_0x90 = 0x400000000000ull; + renderer->workspace = workspace; + renderer->workspace_size = 0x4000; + renderer->reserved_a8 = 0; + renderer->outline_policy_flag = 0; + + const uintptr_t create_fn_ptr = + selection ? selection->create_fn : static_cast(0); + using CreateFn = s32(PS4_SYSV_ABI*)(void* renderer); + CreateFn create_fn = reinterpret_cast(create_fn_ptr); + if (create_fn_ptr == kFtRendererCreateFnAddr) { + create_fn = &FtRendererCreate; + } + if (!create_fn) { + rc = ORBIS_FONT_ERROR_FATAL; + } else { + rc = create_fn(renderer_mem); + if (rc == ORBIS_OK) { + s32 sdk_version = 0; + u32 sdk_version_u32 = 0; + if (Libraries::Kernel::sceKernelGetCompiledSdkVersion(&sdk_version) == + ORBIS_OK) { + sdk_version_u32 = static_cast(sdk_version); + } + const u32 edition_hi = static_cast(edition >> 32); + u32 v = edition_hi; + if (sdk_version_u32 != 0) { + v = sdk_version_u32; + if (edition_hi <= sdk_version_u32) { + v = edition_hi; + } + } + if ((v - 0x930000u) < 0x1bd0000u) { + renderer->feature_byte_0x0c |= 1u; + renderer->outline_policy_flag = 1; + } + + *pRenderer = renderer_mem; + return ORBIS_OK; + } + } + } + + if (workspace) { + free_fn(memory->mspace_handle, workspace); + } + if (renderer_mem) { + free_fn(memory->mspace_handle, renderer_mem); + } + } + } + } if (!pRenderer) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + LOG_ERROR(Lib_Font, "FAILED"); + return rc; } - if (!memory) { - return ORBIS_FONT_ERROR_INVALID_MEMORY; + *pRenderer = nullptr; + if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_MEMORY) { + LOG_ERROR(Lib_Font, "INVALID_MEMORY"); + } else if (rc == ORBIS_FONT_ERROR_ALLOCATION_FAILED) { + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + } else if (rc == ORBIS_FONT_ERROR_FATAL) { + LOG_ERROR(Lib_Font, "FATAL"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); } - auto* renderer = new (std::nothrow) OrbisFontRenderer_{}; - if (!renderer) { - return ORBIS_FONT_ERROR_ALLOCATION_FAILED; - } - *pRenderer = renderer; - LOG_INFO(Lib_Font, "CreateRenderer: memory={} selection={} edition={} renderer={}", - static_cast(memory), static_cast(create_params), edition, - static_cast(renderer)); - return ORBIS_OK; + return rc; } s32 PS4_SYSV_ABI sceFontCreateString() { @@ -1424,13 +1671,12 @@ s32 PS4_SYSV_ABI sceFontDeleteGlyph(const OrbisFontMem* memory, OrbisFontGlyph* return ORBIS_FONT_ERROR_INVALID_GLYPH; } - // We currently own the allocation; the memory pointer is only recorded for ABI compatibility. (void)memory; - if (!ForgetGeneratedGlyph(handle)) { + if (!Internal::ForgetGeneratedGlyph(handle)) { return ORBIS_FONT_ERROR_INVALID_GLYPH; } - auto* boxed = reinterpret_cast(handle); + auto* boxed = reinterpret_cast(handle); delete boxed; *glyph = nullptr; return ORBIS_OK; @@ -1447,19 +1693,62 @@ s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { } s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { - if (!pRenderer || !*pRenderer) { + if (!pRenderer) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto renderer = *pRenderer; - LOG_INFO(Lib_Font, "renderer destroy requested"); - LOG_DEBUG(Lib_Font, - "renderer destroy params:\n" - " renderer_ptr={}\n" - " renderer={}\n", - static_cast(pRenderer), static_cast(renderer)); - delete static_cast(renderer); - *pRenderer = nullptr; - return ORBIS_OK; + + auto* renderer = static_cast(*pRenderer); + s32 rc = ORBIS_FONT_ERROR_INVALID_RENDERER; + if (renderer && renderer->magic == 0x0F07) { + const auto kBusy = static_cast(std::numeric_limits::max()); + + std::uintptr_t selection_value = 0; + for (;;) { + selection_value = reinterpret_cast(renderer->selection); + if (selection_value != kBusy) { + std::atomic_ref ref( + *reinterpret_cast(&renderer->selection)); + std::uintptr_t expected = selection_value; + if (ref.compare_exchange_weak(expected, kBusy, std::memory_order_acq_rel)) { + selection_value = expected; + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + if (selection_value == 0) { + rc = ORBIS_FONT_ERROR_FATAL; + } else { + const auto* selection = + reinterpret_cast( + selection_value); + const auto destroy_fn_ptr = + selection ? selection->destroy_fn : static_cast(0); + using DestroyFn = s32(PS4_SYSV_ABI*)(void* renderer); + DestroyFn destroy_fn = reinterpret_cast(destroy_fn_ptr); + if (destroy_fn_ptr == kFtRendererDestroyFnAddr) { + destroy_fn = &FtRendererDestroy; + } + rc = destroy_fn ? destroy_fn(renderer) : ORBIS_FONT_ERROR_FATAL; + } + + renderer->selection = nullptr; + + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn = reinterpret_cast(renderer->free_fn); + void* alloc_ctx = renderer->alloc_ctx; + void* workspace = renderer->workspace; + if (workspace) { + free_fn(alloc_ctx, workspace); + } + free_fn(alloc_ctx, renderer); + + *pRenderer = nullptr; + return rc; + } + + return rc; } s32 PS4_SYSV_ABI sceFontDestroyString() { @@ -1515,7 +1804,7 @@ s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 code return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; } - auto* st = TryGetState(glyph_handle); + auto* st = Internal::TryGetState(glyph_handle); if (!st) { return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } @@ -1547,7 +1836,7 @@ s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 code return metrics_res; } - auto* boxed = new (std::nothrow) GeneratedGlyph(); + auto* boxed = new (std::nothrow) Libraries::Font::Internal::GeneratedGlyph(); if (!boxed) { return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -1559,8 +1848,8 @@ s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 code boxed->glyph.glyph_form = glyph_form; boxed->glyph.metrics_form = metrics_form; boxed->glyph.em_size = ClampToU16(st->scale_h); - boxed->glyph.baseline = ClampToU16(metrics.h_bearing_y); - boxed->glyph.height_px = ClampToU16(metrics.h); + boxed->glyph.baseline = ClampToU16(metrics.Horizontal.bearingY); + boxed->glyph.height_px = ClampToU16(metrics.height); boxed->glyph.origin_x = 0; boxed->glyph.origin_y = boxed->glyph.baseline; boxed->glyph.scale_x = st->scale_w; @@ -1576,7 +1865,7 @@ s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(OrbisFontHandle glyph_handle, u32 code boxed->outline.points_cnt = 0; boxed->outline_initialized = false; - TrackGeneratedGlyph(&boxed->glyph); + Internal::TrackGeneratedGlyph(&boxed->glyph); *glyph_out = &boxed->glyph; return ORBIS_OK; } @@ -1593,98 +1882,118 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetrics* metrics) { - if (!metrics) { - LOG_ERROR(Lib_Font, "invalid metrics pointer"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto& st = GetState(fontHandle); - const stbtt_fontinfo* face = nullptr; - float scale = 0.0f; + LOG_INFO(Lib_Font, "called"); - if (st.ext_face_ready) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - const int glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (glyph > 0) { - face = &st.ext_face; - scale = st.ext_scale_for_height; - if (!st.logged_ext_use) { - LOG_INFO(Lib_Font, "RenderFace: handle={} source=external (game font)", - static_cast(fontHandle)); - st.logged_ext_use = true; - } - } - } + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("code", code), + Param("metrics", metrics), + })); - if (!face) { - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); - } - if (system_attached) { - face = &st.ext_face; - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - scale = st.ext_scale_for_height; - } + const s32 rc = Internal::GetCharGlyphMetrics(fontHandle, code, metrics, false); + if (rc != ORBIS_OK) { + LOG_ERROR(Lib_Font, "FAILED"); } - - if (face) { - const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); - const std::uint64_t key = MakeGlyphKey(code, pixel_h); - GlyphEntry* ge = nullptr; - if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { - ge = &it->second; - } - if (!ge) { - GlyphEntry entry{}; - int aw = 0, lsb = 0; - stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); - stbtt_GetCodepointBitmapBox(face, static_cast(code), scale, scale, &entry.x0, - &entry.y0, &entry.x1, &entry.y1); - entry.w = std::max(0, entry.x1 - entry.x0); - entry.h = std::max(0, entry.y1 - entry.y0); - entry.advance = static_cast(aw) * scale; - entry.bearingX = static_cast(lsb) * scale; - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } - metrics->w = ge->w > 0 ? static_cast(ge->w) : st.scale_w; - metrics->h = ge->h > 0 ? static_cast(ge->h) : st.scale_h; - metrics->h_bearing_x = ge->bearingX; - metrics->h_bearing_y = static_cast(-ge->y0); - metrics->h_adv = ge->advance > 0.0f ? ge->advance : st.scale_w; - metrics->v_bearing_x = 0.0f; - metrics->v_bearing_y = 0.0f; - metrics->v_adv = 0.0f; - LOG_TRACE(Lib_Font, - "GetCharGlyphMetrics: code=U+{:04X} src=external size=({}, {}) adv={} " - "bearing=({}, {}) box={}x{}", - code, st.scale_w, st.scale_h, metrics->h_adv, metrics->h_bearing_x, - metrics->h_bearing_y, metrics->w, metrics->h); - return ORBIS_OK; - } - metrics->w = st.scale_w; - metrics->h = st.scale_h; - metrics->h_bearing_x = 0.0f; - metrics->h_bearing_y = st.scale_h; - metrics->h_adv = st.scale_w; - metrics->v_bearing_x = 0.0f; - metrics->v_bearing_y = 0.0f; - metrics->v_adv = 0.0f; - LOG_TRACE(Lib_Font, "GetCharGlyphMetrics(fallback): code=U+{:04X} size=({}, {}) box={}x{}", - code, st.scale_w, st.scale_h, metrics->w, metrics->h); - return ORBIS_OK; + return rc; } -s32 PS4_SYSV_ABI sceFontGetEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetEffectSlant(OrbisFontHandle fontHandle, float* slantRatio) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("slantRatio", slantRatio), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + if (slantRatio) { + *slantRatio = 0.0f; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_lock = 0; + if (!AcquireFontLock(font, prev_lock)) { + if (slantRatio) { + *slantRatio = 0.0f; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const s32 rc = Internal::StyleStateGetSlantRatio(font->style_frame, slantRatio); + ReleaseFontLock(font, prev_lock); + + if (rc != ORBIS_OK) { + if (slantRatio) { + *slantRatio = 0.0f; + } + LOG_ERROR(Lib_Font, "FAILED"); + } + return rc; } -s32 PS4_SYSV_ABI sceFontGetEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetEffectWeight(OrbisFontHandle fontHandle, float* weightXScale, + float* weightYScale, u32* mode) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("weightXScale", weightXScale), + Param("weightYScale", weightYScale), + Param("mode", mode), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + if (weightXScale) { + *weightXScale = 1.0f; + } + if (weightYScale) { + *weightYScale = 1.0f; + } + if (mode) { + *mode = 0; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_lock = 0; + if (!AcquireFontLock(font, prev_lock)) { + if (weightXScale) { + *weightXScale = 1.0f; + } + if (weightYScale) { + *weightYScale = 1.0f; + } + if (mode) { + *mode = 0; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const s32 rc = + Internal::StyleStateGetWeightScale(font->style_frame, weightXScale, weightYScale, mode); + ReleaseFontLock(font, prev_lock); + + if (rc != ORBIS_OK) { + if (weightXScale) { + *weightXScale = 1.0f; + } + if (weightYScale) { + *weightYScale = 1.0f; + } + if (mode) { + *mode = 0; + } + LOG_ERROR(Lib_Font, "FAILED"); + } + return rc; } s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount() { @@ -1719,48 +2028,61 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, OrbisFontHorizontalLayout* layout) { - if (!fontHandle || !layout) { - LOG_ERROR(Lib_Font, "invalid parameter"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto* st_ptr = TryGetState(fontHandle); - if (!st_ptr) { - LOG_ERROR(Lib_Font, "invalid font handle"); + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + if (layout) { + *layout = {}; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = *st_ptr; - if (!st.ext_face_ready) { - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + if (layout) { + *layout = {}; } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - if (st.ext_face_ready) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - int asc = 0, desc = 0, gap = 0; - stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); - const float scale = st.ext_scale_for_height; - layout->baselineOffset = static_cast(asc) * scale; - layout->lineAdvance = static_cast(asc - desc + gap) * scale; - layout->decorationExtent = 0.0f; - LOG_INFO( - Lib_Font, - "GetHorizontalLayout: handle={} ext_ready={} baselineOffset={} lineAdvance={} scale={}", - static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, - layout->lineAdvance, scale); + + u32 prev_lock = 0; + if (!AcquireFontLock(font, prev_lock)) { + if (layout) { + *layout = {}; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (!layout) { + font->lock_word = prev_lock; + LOG_ERROR(Lib_Font, "NULL_POINTER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("layout", layout), + })); + + Internal::HorizontalLayoutBlocks layout_blocks{}; + const s32 rc = + Internal::ComputeHorizontalLayoutBlocksTyped(fontHandle, font->style_frame, &layout_blocks); + font->lock_word = prev_lock; + + if (rc == ORBIS_OK) { + layout->baselineOffset = layout_blocks.baseline(); + layout->lineAdvance = layout_blocks.line_advance(); + layout->decorationExtent = layout_blocks.effect_height(); return ORBIS_OK; } - layout->baselineOffset = st.scale_h * 0.8f; - layout->lineAdvance = st.scale_h; - layout->decorationExtent = 0.0f; - LOG_INFO( - Lib_Font, - "GetHorizontalLayout: fallback handle={} ext_ready={} baselineOffset={} lineAdvance={}", - static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, - layout->lineAdvance); - return ORBIS_OK; + + *layout = {}; + LOG_ERROR(Lib_Font, "FAILED"); + return rc; } s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, @@ -1769,35 +2091,60 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 LOG_DEBUG(Lib_Font, "invalid parameters"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto& st = GetState(fontHandle); - if (!st.ext_face_ready) { + auto& st = Internal::GetState(fontHandle); + if (!st.ext_face_ready || !st.ext_ft_face) { bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); + const std::string sys_log = + Internal::ReportSystemFaceRequest(st, fontHandle, system_attached); if (!sys_log.empty()) { LOG_ERROR(Lib_Font, "{}", sys_log); } } - if (st.ext_face_ready) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - int g1 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(preCode)); - int g2 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (g1 > 0 && g2 > 0) { - const int kern = stbtt_GetCodepointKernAdvance(&st.ext_face, static_cast(preCode), - static_cast(code)); - const float kx = static_cast(kern) * st.ext_scale_for_height; - kerning->dx = kx; - kerning->dy = 0.0f; - kerning->px = 0.0f; - kerning->py = 0.0f; - LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); - return ORBIS_OK; + + FT_Face resolved_face = st.ext_ft_face; + float resolved_scale_factor = st.system_font_scale_factor; + FT_UInt g1 = + resolved_face ? FT_Get_Char_Index(resolved_face, static_cast(preCode)) : 0; + FT_UInt g2 = resolved_face ? FT_Get_Char_Index(resolved_face, static_cast(code)) : 0; + if (g1 == 0 || g2 == 0) { + for (const auto& fb : st.system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const FT_UInt fb_g1 = FT_Get_Char_Index(fb.ft_face, static_cast(preCode)); + const FT_UInt fb_g2 = FT_Get_Char_Index(fb.ft_face, static_cast(code)); + if (fb_g1 == 0 || fb_g2 == 0) { + continue; + } + resolved_face = fb.ft_face; + resolved_scale_factor = fb.scale_factor; + g1 = fb_g1; + g2 = fb_g2; + break; } } - kerning->dx = 0.0f; - kerning->dy = 0.0f; - kerning->px = 0.0f; - kerning->py = 0.0f; + + if (resolved_face && g1 != 0 && g2 != 0) { + const float scaled_w = st.scale_w * resolved_scale_factor; + const float scaled_h = st.scale_h * resolved_scale_factor; + const auto char_w = static_cast(static_cast(scaled_w * 64.0f)); + const auto char_h = static_cast(static_cast(scaled_h * 64.0f)); + (void)FT_Set_Char_Size(resolved_face, char_w, char_h, 72, 72); + + FT_Vector delta{}; + FT_Get_Kerning(resolved_face, g1, g2, FT_KERNING_DEFAULT, &delta); + const float kx = static_cast(delta.x) / 64.0f; + kerning->offsetX = kx; + kerning->offsetY = 0.0f; + kerning->positionX = 0.0f; + kerning->positionY = 0.0f; + LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); + return ORBIS_OK; + } + kerning->offsetX = 0.0f; + kerning->offsetY = 0.0f; + kerning->positionX = 0.0f; + kerning->positionY = 0.0f; LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx=0 (no face)", preCode, code); return ORBIS_OK; } @@ -1806,7 +2153,7 @@ s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLi if (!pLibrary) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - const auto& st = GetState(fontHandle); + const auto& st = Internal::GetState(fontHandle); *pLibrary = st.library; return ORBIS_OK; } @@ -1818,26 +2165,75 @@ s32 PS4_SYSV_ABI sceFontGetPixelResolution() { s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, OrbisFontGlyphMetrics* out_metrics) { - if (!fontHandle || !out_metrics) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("codepoint", codepoint), + Param("out_metrics", out_metrics), + })); + + const s32 rc = Internal::GetCharGlyphMetrics(fontHandle, codepoint, out_metrics, true); + if (rc != ORBIS_OK) { + LOG_ERROR(Lib_Font, "FAILED"); } - auto& st = GetState(fontHandle); - if (!st.bound_renderer) { - LOG_DEBUG(Lib_Font, "GetRenderCharGlyphMetrics: renderer not bound for handle={}", - static_cast(fontHandle)); - return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; - } - return sceFontGetCharGlyphMetrics(fontHandle, codepoint, out_metrics); + return rc; } -s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(OrbisFontHandle fontHandle, float* slantRatio) { + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font && font->magic == 0x0F02) { + u32 prev_cached_lock = 0; + if (AcquireCachedStyleLock(font, prev_cached_lock)) { + if (font->renderer_binding.renderer == nullptr) { + rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + font->cached_style.cache_lock_word = prev_cached_lock; + } else { + rc = Internal::StyleStateGetSlantRatio(&font->cached_style, slantRatio); + font->cached_style.cache_lock_word = prev_cached_lock; + if (rc == ORBIS_OK) { + return ORBIS_OK; + } + } + } + } + if (slantRatio) { + *slantRatio = 0.0f; + } + return rc; } -s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(OrbisFontHandle fontHandle, float* weightXScale, + float* weightYScale, u32* mode) { + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font && font->magic == 0x0F02) { + u32 prev_cached_lock = 0; + if (AcquireCachedStyleLock(font, prev_cached_lock)) { + if (font->renderer_binding.renderer == nullptr) { + rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + font->cached_style.cache_lock_word = prev_cached_lock; + } else { + rc = Internal::StyleStateGetWeightScale(&font->cached_style, weightXScale, + weightYScale, mode); + font->cached_style.cache_lock_word = prev_cached_lock; + if (rc == ORBIS_OK) { + return ORBIS_OK; + } + } + } + } + if (weightXScale) { + *weightXScale = 1.0f; + } + if (weightYScale) { + *weightYScale = 1.0f; + } + if (mode) { + *mode = 0; + } + return rc; } s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { @@ -1847,32 +2243,56 @@ s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h) { - if (!fontHandle || (!out_w && !out_h)) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font) { + u32 prev_cached_lock = 0; + if (AcquireCachedStyleLock(font, prev_cached_lock)) { + if (font->renderer_binding.renderer == nullptr) { + rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } else { + rc = Internal::StyleStateGetScalePixel(&font->cached_style, out_w, out_h); + } + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc == ORBIS_OK) { + return rc; + } + } } - auto* st = TryGetState(fontHandle); - if (!st) { - return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + if (out_w) { + *out_w = 0.0f; } - if (out_w) - *out_w = st->scale_w; - if (out_h) - *out_h = st->scale_h; - if (!st->bound_renderer) { - LOG_DEBUG(Lib_Font, - "GetRenderScalePixel: no renderer bound; returning configured scale w={} h={}", - out_w ? *out_w : -1.0f, out_h ? *out_h : -1.0f); - } else { - LOG_DEBUG(Lib_Font, "GetRenderScalePixel: handle={} -> w={}, h={}", - static_cast(fontHandle), out_w ? *out_w : -1.0f, - out_h ? *out_h : -1.0f); + if (out_h) { + *out_h = 0.0f; } - return ORBIS_OK; + return rc; } s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h) { - return sceFontGetScalePoint(fontHandle, out_w, out_h); + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font) { + u32 prev_cached_lock = 0; + if (AcquireCachedStyleLock(font, prev_cached_lock)) { + if (font->renderer_binding.renderer == nullptr) { + rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } else { + rc = Internal::StyleStateGetScalePoint(&font->cached_style, out_w, out_h); + } + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc == ORBIS_OK) { + return rc; + } + } + } + if (out_w) { + *out_w = 0.0f; + } + if (out_h) { + *out_h = 0.0f; + } + return rc; } s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { @@ -1881,43 +2301,76 @@ s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { } s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h) { - if (!fontHandle || (!out_w && !out_h)) { - LOG_DEBUG(Lib_Font, "invalid parameters"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto* st = TryGetState(fontHandle); - if (!st) { + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + if (out_w) { + *out_w = 0.0f; + } + if (out_h) { + *out_h = 0.0f; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - if (out_w) - *out_w = st->scale_w; - if (out_h) - *out_h = st->scale_h; - LOG_DEBUG(Lib_Font, "GetScalePixel: handle={} -> w={}, h={}", - static_cast(fontHandle), out_w ? *out_w : -1.0f, out_h ? *out_h : -1.0f); - return ORBIS_OK; + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("out_w", out_w), + Param("out_h", out_h), + })); + + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font && font->magic == 0x0F02) { + u32 prev_font_lock = 0; + if (AcquireFontLock(font, prev_font_lock)) { + rc = Internal::StyleStateGetScalePixel( + reinterpret_cast(font->style_frame), out_w, out_h); + ReleaseFontLock(font, prev_font_lock); + if (rc == ORBIS_OK) { + return rc; + } + } + } + if (out_w) { + *out_w = 0.0f; + } + if (out_h) { + *out_h = 0.0f; + } + if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_FONT_HANDLE) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + return rc; } s32 PS4_SYSV_ABI sceFontGetScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h) { - if (!fontHandle || (!out_w && !out_h)) { - LOG_DEBUG(Lib_Font, "invalid parameters"); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font) { + u32 prev_font_lock = 0; + if (AcquireFontLock(font, prev_font_lock)) { + rc = Internal::StyleStateGetScalePoint( + reinterpret_cast(font->style_frame), out_w, out_h); + ReleaseFontLock(font, prev_font_lock); + if (rc == ORBIS_OK) { + return rc; + } + } } - auto* st = TryGetState(fontHandle); - if (!st) { - return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + if (out_w) { + *out_w = 0.0f; } - const float default_w = PixelsToPoints(st->scale_w, st->dpi_x); - const float default_h = PixelsToPoints(st->scale_h, st->dpi_y); - const float point_w = st->scale_point_active ? st->scale_point_w : default_w; - const float point_h = st->scale_point_active ? st->scale_point_h : default_h; - if (out_w) - *out_w = point_w; - if (out_h) - *out_h = point_h; - LOG_DEBUG(Lib_Font, "GetScalePoint: handle={} -> w={}pt h={}pt (active={})", - static_cast(fontHandle), point_w, point_h, st->scale_point_active); - return ORBIS_OK; + if (out_h) { + *out_h = 0.0f; + } + return rc; } s32 PS4_SYSV_ABI sceFontGetScriptLanguage() { @@ -1932,31 +2385,34 @@ s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, OrbisFontVerticalLayout* layout) { - if (!layout) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto& st = GetState(fontHandle); - if (!st.ext_face_ready) { - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + if (font && font->magic == 0x0F02) { + u32 prev_font_lock = 0; + if (AcquireFontLock(font, prev_font_lock)) { + if (!layout) { + font->lock_word = prev_font_lock; + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } else { + Internal::VerticalLayoutBlocks layout_words{}; + rc = Internal::ComputeVerticalLayoutBlocksTyped(fontHandle, font->style_frame, + &layout_words); + font->lock_word = prev_font_lock; + if (rc == ORBIS_OK) { + layout->baselineOffsetX = layout_words.baseline_offset_x(); + layout->columnAdvance = layout_words.column_advance(); + layout->decorationSpan = layout_words.decoration_span(); + return ORBIS_OK; + } + } } } - if (st.ext_face_ready) { - if (st.ext_scale_for_height == 0.0f) - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - int asc = 0, desc = 0, gap = 0; - stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); - layout->baselineOffsetX = 0.0f; - layout->columnAdvance = static_cast(asc - desc + gap) * st.ext_scale_for_height; + + if (layout) { layout->decorationSpan = 0.0f; - return ORBIS_OK; + *reinterpret_cast(layout) = 0; } - layout->baselineOffsetX = 0.0f; - layout->columnAdvance = st.scale_h; - layout->decorationSpan = 0.0f; - return ORBIS_OK; + return rc; } s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute() { @@ -2008,16 +2464,13 @@ OrbisFontGlyphOutline* PS4_SYSV_ABI sceFontGlyphRefersOutline(OrbisFontGlyph gly if (!glyph || glyph->magic != 0x0F03 || glyph->glyph_form != 1) { return nullptr; } - { - std::scoped_lock lock(g_generated_glyph_mutex); - if (g_generated_glyphs.find(glyph) == g_generated_glyphs.end()) { - return nullptr; - } + auto* boxed = Internal::TryGetGeneratedGlyph(glyph); + if (!boxed) { + return nullptr; } - auto* boxed = reinterpret_cast(glyph); if (!boxed->outline_initialized) { - if (!BuildTrueOutline(*boxed)) { - BuildBoundingOutline(*boxed); + if (!Internal::BuildTrueOutline(*boxed)) { + Internal::BuildBoundingOutline(*boxed); } } return &boxed->outline; @@ -2291,11 +2744,30 @@ s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u32 region_size, const OrbisFontMemInterface* iface, void* mspace_obj, OrbisFontMemDestroyCb destroy_cb, void* destroy_ctx) { + LOG_INFO(Lib_Font, "called"); + if (!mem_desc) { + LOG_ERROR(Lib_Font, "NULL_POINTER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - if (!iface && (!region_addr || region_size == 0)) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("mem_desc", mem_desc), + Param("region_addr", region_addr), + Param("region_size", region_size), + Param("iface", iface), + Param("mspace_obj", mspace_obj), + Param("destroy_cb", reinterpret_cast(destroy_cb)), + Param("destroy_ctx", destroy_ctx), + })); + + if (!iface) { + mem_desc->mem_kind = 0; + if (!region_addr || region_size == 0) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } } mem_desc->mem_kind = 0x0F00; @@ -2308,19 +2780,6 @@ s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u3 mem_desc->destroy_ctx = destroy_ctx; mem_desc->some_ctx1 = nullptr; mem_desc->some_ctx2 = nullptr; - - LOG_INFO(Lib_Font, "font memory init requested"); - LOG_DEBUG(Lib_Font, - "font memory init params:\n" - " mem_desc={}\n" - " region_addr={}\n" - " region_size={}\n" - " mspace_obj={}\n" - " has_iface={}\n" - " destroy_cb={}\n", - static_cast(mem_desc), static_cast(region_addr), - region_size, static_cast(mspace_obj), iface != nullptr, - reinterpret_cast(destroy_cb)); return ORBIS_OK; } @@ -2331,354 +2790,2466 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { if (mem_desc->mem_kind != 0x0F00) { return ORBIS_FONT_ERROR_INVALID_MEMORY; } - if (mem_desc->on_destroy) { - mem_desc->on_destroy(mem_desc, mem_desc->destroy_ctx, mem_desc->some_ctx1); + + if (static_cast(mem_desc->attr_bits & 0xFF) < 0) { + if (mem_desc->iface && mem_desc->iface->mspace_destroy) { + using DestroyFn = void(PS4_SYSV_ABI*)(void* parent, void* mspace); + const auto destroy_fn = reinterpret_cast(mem_desc->iface->mspace_destroy); + if (destroy_fn) { + destroy_fn(mem_desc->some_ctx2, mem_desc->mspace_handle); + } + } + std::memset(mem_desc, 0, sizeof(*mem_desc)); + return ORBIS_OK; } + + if (mem_desc->on_destroy) { + mem_desc->mem_kind = 0; + mem_desc->attr_bits = 0; + mem_desc->on_destroy(mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); + return ORBIS_OK; + } + std::memset(mem_desc, 0, sizeof(*mem_desc)); - LOG_INFO(Lib_Font, "font memory term requested"); - LOG_DEBUG(Lib_Font, - "font memory term params:\n" - " mem_desc={}\n", - static_cast(mem_desc)); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_path, u32 open_mode, const OrbisFontOpenParams* open_detail, OrbisFontHandle* out_handle) { - if (!library || !guest_path || !out_handle) { + LOG_INFO(Lib_Font, "called"); + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("library", library), + Param("guest_path", guest_path), + Param("open_mode", open_mode), + Param("open_detail", open_detail), + Param("out_handle", out_handle), + })); + + if (!library) { + if (out_handle) { + *out_handle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + if (!out_handle) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } + + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + *out_handle = nullptr; + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + u32 prev_lib_lock = 0; + if (!AcquireLibraryLock(lib, prev_lib_lock)) { + *out_handle = nullptr; + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + const auto release_library_and_clear_out = [&] { + ReleaseLibraryLock(lib, prev_lib_lock); + *out_handle = nullptr; + }; + + if (!lib->fontset_registry || !lib->sys_driver) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + const u32 sub_font_index = open_detail ? open_detail->subfont_index : 0u; + const s32 unique_id = open_detail ? open_detail->unique_id : -1; + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + if (!alloc_fn || !free_fn) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + auto* ext_ctx = static_cast(lib->external_fonts_ctx); + if (!ext_ctx) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); + return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + + if (!guest_path || unique_id < -1) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + const auto host_path = ResolveGuestPath(guest_path); const std::filesystem::path path_to_open = host_path.empty() ? std::filesystem::path(guest_path) : host_path; - - std::vector file_bytes; - if (!LoadGuestFileBytes(path_to_open, file_bytes)) { - LOG_WARNING(Lib_Font, "OpenFontFile: failed to open '{}'", path_to_open.string()); - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - if (file_bytes.size() > std::numeric_limits::max()) { - LOG_WARNING(Lib_Font, "OpenFontFile: '{}' exceeds libSceFont size limit ({} bytes)", - path_to_open.string(), file_bytes.size()); - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - LOG_INFO(Lib_Font, "OpenFontFile: path='{}' size={} openMode={}", path_to_open.string(), - static_cast(file_bytes.size()), open_mode); - return sceFontOpenFontMemory(library, file_bytes.data(), static_cast(file_bytes.size()), - open_detail, out_handle); -} - -s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle templateFont, - OrbisFontHandle* pFontHandle) { - if ((!templateFont && !fontHandle) || !pFontHandle) { - LOG_ERROR(Lib_Font, "invalid parameter base={} template={} out_ptr={}", - static_cast(fontHandle), static_cast(templateFont), - static_cast(pFontHandle)); + const std::string host_path_str = path_to_open.string(); + if (host_path_str.empty()) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " base={}\n" - " template={}\n" - " out_ptr={}\n", - static_cast(fontHandle), static_cast(templateFont), - static_cast(pFontHandle)); - auto* src_state = TryGetState(templateFont); - if (!src_state) { - templateFont = nullptr; - src_state = TryGetState(fontHandle); + const std::string guest_name = std::filesystem::path(guest_path).filename().string(); + + OrbisFontHandle handle = *out_handle; + if (handle) { + if (auto* h = GetNativeFont(handle)) { + h->flags = 0; + } + Internal::g_font_state.erase(handle); + } else { + handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + if (!handle) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(handle, 0, 0x100); + if (auto* h = GetNativeFont(handle)) { + h->flags = 0x10; + } } - if (!src_state) { - LOG_ERROR(Lib_Font, "template handle={} missing", - templateFont ? static_cast(templateFont) - : static_cast(fontHandle)); - return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + + auto cleanup_handle_for_error = [&](s32 err) { + auto* h = GetNativeFont(handle); + if (!h) { + ReleaseLibraryLock(lib, prev_lib_lock); + *out_handle = nullptr; + return err; + } + h->magic = 0; + h->open_info.unique_id_packed = 0; + h->open_info.ctx_entry_index = 0; + h->open_info.sub_font_index = 0; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->library = library; + const u16 prev_flags = h->flags; + h->flags = 0; + if ((prev_flags & 0x10) != 0) { + free_fn(lib->alloc_ctx, handle); + } + Internal::g_font_state.erase(handle); + ReleaseLibraryLock(lib, prev_lib_lock); + *out_handle = nullptr; + return err; + }; + + auto* ext_header = reinterpret_cast(ext_ctx); + + auto* ctx_lock_word = &ext_header->lock_word; + for (;;) { + const u32 lw = *ctx_lock_word; + if (static_cast(lw) >= 0) { + std::atomic_ref ref(*ctx_lock_word); + u32 expected = lw; + if (ref.compare_exchange_weak(expected, lw | 0x80000000u, std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); } - FontState* base_state = TryGetState(fontHandle); - if (!base_state && fontHandle) { - LOG_WARNING(Lib_Font, "base handle={} unknown, falling back to template state", - static_cast(fontHandle)); + + const u32 font_max = ext_header->max_entries; + auto* entries_base = static_cast(ext_header->base); + if (font_max == 0 || !entries_base) { + *ctx_lock_word = 0; + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_MAX); + LogFontOpenError(err); + return err; } - auto* new_handle = new (std::nothrow) OrbisFontHandleOpaque{}; - if (!new_handle) { - LOG_ERROR(Lib_Font, "allocation failed"); - return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + + u32 entry_index = 0xffffffffu; + u32 first_free = 0xffffffffu; + for (u32 i = 0; i < font_max; i++) { + auto* entry = &entries_base[i]; + const u32 active = entry->active; + const u32 stored_id = entry->unique_id; + if (active != 0 && stored_id == static_cast(unique_id)) { + entry_index = i; + break; + } + if (first_free == 0xffffffffu && active == 0) { + first_free = i; + } } - const std::size_t src_ext_bytes = src_state->ext_face_data.size(); - LOG_DEBUG(Lib_Font, - "template state:\n" - " ext_ready={}\n" - " bytes={}\n" - " cache_size={}\n" - " dpi_x={}\n" - " dpi_y={}\n" - " scale_w={}\n" - " scale_h={}\n" - " scale_point_active={}\n", - src_state->ext_face_ready, src_ext_bytes, src_state->ext_cache.size(), - src_state->dpi_x, src_state->dpi_y, src_state->scale_w, src_state->scale_h, - src_state->scale_point_active); - auto& dst = GetState(new_handle); - dst = *src_state; - dst.ext_face = {}; - dst.ext_cache.clear(); - dst.scratch.clear(); - dst.library = base_state ? base_state->library : src_state->library; - dst.bound_renderer = base_state ? base_state->bound_renderer : src_state->bound_renderer; - dst.logged_ext_use = false; - bool stbtt_ok = false; - if (dst.ext_face_ready && !dst.ext_face_data.empty()) { - const int font_offset = src_state->ext_face.fontstart; - LOG_INFO(Lib_Font, "reinitializing stbtt font for clone"); - LOG_DEBUG(Lib_Font, - "stbtt reinit params:\n" - " template_handle={}\n" - " new_handle={}\n" - " offset={}\n" - " data_size={}\n", - static_cast(templateFont), static_cast(new_handle), - font_offset, dst.ext_face_data.size()); - if (stbtt_InitFont(&dst.ext_face, dst.ext_face_data.data(), font_offset) == 0) { - LOG_WARNING(Lib_Font, "stbtt_InitFont failed during clone"); - dst.ext_face_ready = false; - dst.ext_cache.clear(); + + if (entry_index == 0xffffffffu) { + entry_index = first_free; + if (entry_index == 0xffffffffu) { + *ctx_lock_word = 0; + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_MAX); + LogFontOpenError(err); + return err; + } + + auto* entry = &entries_base[entry_index]; + const u32 stored_id = + (unique_id != -1) ? static_cast(unique_id) : (entry_index ^ 0x80000000u); + entry->reserved_0x00 = 0; + entry->active = 1; + entry->font_address = 0; + entry->unique_id = stored_id; + entry->lock_mode1 = 0; + entry->lock_mode3 = 0; + entry->lock_mode2 = 0; + entry->obj_mode1 = nullptr; + entry->obj_mode3 = nullptr; + entry->obj_mode2 = nullptr; + std::memset(entry->reserved_0x38, 0, sizeof(entry->reserved_0x38)); + } + + *ctx_lock_word = 0; + + const u32 packed_unique_id = (unique_id == -1) ? (entry_index + 0x40000000u) + : (static_cast(unique_id) | 0x80000000u); + + void* font_obj = nullptr; + u32 entry_lock_word = 0; + u8* entry_base = + Internal::AcquireFontCtxEntry(ext_ctx, entry_index, open_mode, &font_obj, &entry_lock_word); + auto* entry = entry_base ? reinterpret_cast(entry_base) : nullptr; + if (!entry_base) { + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_MAX); + LogFontOpenError(err); + return err; + } + + const u32 mode_low = open_mode & 0xF; + + const auto* driver = + lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; + const auto open_fn = driver ? driver->open : nullptr; + const auto metric_fn = driver ? driver->metric : nullptr; + const auto scale_fn = driver ? driver->scale : nullptr; + if (!open_fn || !metric_fn || !scale_fn) { + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_INVALID_LIBRARY); + LogFontOpenError(err); + return err; + } + + s32 rc = ORBIS_OK; + u32 updated_lock = entry_lock_word; + void* used_font_obj = font_obj; + + const auto open_driver_mode_for = [&](u32 m) -> u32 { + if (m == 1) { + return 5; + } + if (m == 2) { + return 6; + } + return 7; + }; + + if ((entry_lock_word & 0x40000000u) == 0) { + auto* tail = lib + 1; + auto* tail_reserved1 = + tail ? reinterpret_cast(tail->reserved1) : nullptr; + const u32 lib_flags = lib->flags; + if (mode_low == 2) { + if ((lib_flags & 0x100000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x10000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } } else { - dst.ext_scale_for_height = ComputeScaleExtForState(dst, &dst.ext_face, dst.scale_h); - stbtt_ok = true; + if ((lib_flags & 0x200000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x20000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } + + if (mode_low != 1 && mode_low != 2 && mode_low != 3) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } else { + used_font_obj = font_obj; + rc = open_fn(library, open_driver_mode_for(mode_low), host_path_str.c_str(), 0, + sub_font_index, packed_unique_id, &used_font_obj); + } + + if (tail_reserved1) { + tail_reserved1->sysfont_flags = 0; + } + + if (rc == ORBIS_OK) { + if (mode_low == 3) { + entry->obj_mode3 = used_font_obj; + } else if (mode_low == 2) { + entry->obj_mode2 = used_font_obj; + } else { + entry->obj_mode1 = used_font_obj; + } + updated_lock = entry_lock_word | 0x40000000u; } } else { - LOG_DEBUG(Lib_Font, - "font instance clone missing external face:\n" - " template_handle={}\n" - " ready={}\n" - " data_size={}\n", - static_cast(templateFont), dst.ext_face_ready, - dst.ext_face_data.size()); - dst.ext_face_ready = false; + rc = ORBIS_FONT_ERROR_FONT_OPEN_MAX; + if ((entry_lock_word & 0x0FFFFFFFu) != 0x0FFFFFFFu) { + for (auto* node = static_cast(font_obj); node != nullptr; + node = node->next) { + if (node->sub_font_index == sub_font_index) { + node->refcount++; + used_font_obj = node; + rc = ORBIS_OK; + break; + } + } + if (rc != ORBIS_OK) { + auto* tail = lib + 1; + auto* tail_reserved1 = + tail ? reinterpret_cast(tail->reserved1) + : nullptr; + const u32 lib_flags = lib->flags; + if (mode_low == 2) { + if ((lib_flags & 0x100000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x10000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } else { + if ((lib_flags & 0x200000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x20000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } + + used_font_obj = font_obj; + rc = open_fn(library, open_driver_mode_for(mode_low), host_path_str.c_str(), 0, + sub_font_index, packed_unique_id, &used_font_obj); + if (tail_reserved1) { + tail_reserved1->sysfont_flags = 0; + } + + if (rc == ORBIS_OK) { + if (mode_low == 3) { + entry->obj_mode3 = used_font_obj; + } else if (mode_low == 2) { + entry->obj_mode2 = used_font_obj; + } else { + entry->obj_mode1 = used_font_obj; + } + } + } + } + } + + if (rc == ORBIS_OK) { + LOG_WARNING(Lib_Font, "font file served: requested=\"{}\" served=\"{}\"", guest_name, + host_path_str); + const u32 next_lock = updated_lock + 1; + updated_lock = next_lock; + + auto* h = GetNativeFont(handle); + if (!h) { + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FATAL); + LogFontOpenError(err); + return err; + } + h->magic = 0x0F02; + h->flags = static_cast(h->flags | static_cast(mode_low)); + h->open_info.unique_id_packed = packed_unique_id; + h->open_info.ctx_entry_index = entry_index; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->open_info.sub_font_index = sub_font_index; + h->library = library; + std::memset(&h->cached_style, 0, sizeof(h->cached_style)); + std::memset(h->reserved_0xcc, 0, sizeof(h->reserved_0xcc)); + std::memset(h->reserved_0x30, 0, sizeof(h->reserved_0x30)); + std::memset(h->reserved_0xe8, 0, sizeof(h->reserved_0xe8)); + h->prevFont = nullptr; + h->nextFont = nullptr; + + u16 metric_buf[2] = {}; + (void)metric_fn(used_font_obj, 0x0e00, metric_buf); + h->metricA = metric_buf[0]; + (void)metric_fn(used_font_obj, 0xea00, metric_buf); + h->metricB = metric_buf[0]; + h->style_frame[0] = 0x48; + h->style_frame[1] = 0x48; + + float scale = 0.0f; + (void)scale_fn(used_font_obj, metric_buf, &scale); + h->style_tail.scale_unit = 0; + h->style_tail.reserved_0x0c = 0; + h->style_tail.scale_w = scale; + h->style_tail.scale_h = scale; + h->style_tail.effect_weight_x = 0.0f; + h->style_tail.effect_weight_y = 0.0f; + h->style_tail.slant_ratio = 0.0f; + h->style_tail.reserved_0x24 = 0.0f; + + *out_handle = handle; + } + + { + const u32 count = updated_lock & 0x0FFFFFFFu; + if (mode_low == 3) { + if (count == 0) { + entry->obj_mode3 = nullptr; + } + entry->lock_mode3 = updated_lock & 0x7fffffffu; + } else if (mode_low == 1) { + if (count == 0) { + entry->obj_mode1 = nullptr; + } + entry->lock_mode1 = updated_lock & 0x7fffffffu; + } else { + if (count == 0) { + entry->obj_mode2 = nullptr; + } + entry->lock_mode2 = updated_lock & 0x7fffffffu; + } + + if (count == 0 && entry->obj_mode1 == nullptr && entry->obj_mode3 == nullptr && + entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + } + + if (rc != ORBIS_OK) { + const s32 err = cleanup_handle_for_error(rc); + LogFontOpenError(err); + return err; + } + + const auto* handle_native = GetNativeFont(handle); + if (handle_native && (handle_native->flags & 0x10) != 0) { + auto* tail = reinterpret_cast(lib + 1); + auto* list_lock = &tail->list_head_ptr; + void* list_ptr = nullptr; + for (;;) { + list_ptr = *list_lock; + if (list_ptr != reinterpret_cast(std::numeric_limits::max())) { + std::atomic_ref ref(*list_lock); + void* expected = list_ptr; + if (ref.compare_exchange_weak( + expected, + reinterpret_cast(std::numeric_limits::max()), + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto* head_ptr = reinterpret_cast(list_ptr); + if (head_ptr) { + const OrbisFontHandle old_head = *head_ptr; + if (auto* h = GetNativeFont(handle)) { + h->prevFont = nullptr; + h->nextFont = old_head; + } + if (old_head) { + if (auto* old_native = GetNativeFont(old_head)) { + old_native->prevFont = handle; + } + } + *head_ptr = handle; + } + *list_lock = list_ptr; + } + + ReleaseLibraryLock(lib, prev_lib_lock); + + std::vector file_bytes; + if (LoadGuestFileBytes(path_to_open, file_bytes) && + file_bytes.size() <= std::numeric_limits::max()) { + auto& st = Internal::GetState(handle); + Internal::DestroyFreeTypeFace(st.ext_ft_face); + for (auto& fb : st.system_fallback_faces) { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + st = {}; + st.library = library; + st.font_set_type = 0; + st.system_font_path.clear(); + st.ext_face_data = std::move(file_bytes); + + const u32 chosen_index = open_detail ? open_detail->subfont_index : 0u; + st.ext_ft_face = Internal::CreateFreeTypeFaceFromBytes( + st.ext_face_data.data(), st.ext_face_data.size(), chosen_index); + if (st.ext_ft_face) { + st.ext_cache.clear(); + st.scratch.clear(); + st.logged_ext_use = false; + st.ext_scale_for_height = 0.0f; + st.layout_cached = false; + const Internal::FaceMetrics m = Internal::LoadFaceMetrics(st.ext_ft_face); + Internal::PopulateStateMetrics(st, m); + st.ext_face_ready = true; + } else { + st.ext_face_ready = false; + } + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHandle setupFont, + OrbisFontHandle* pFontHandle) { + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (!setupFont && !pFontHandle) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("setupFont", setupFont), + Param("pFontHandle", pFontHandle), + })); + + auto* src = GetNativeFont(fontHandle); + if (!src || src->magic != 0x0F02) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_font_lock = 0; + if (!AcquireFontLock(src, prev_font_lock)) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + auto release_src_lock = [&] { ReleaseFontLock(src, prev_font_lock); }; + + auto* lib = static_cast(src->library); + if (!lib || lib->magic != 0x0F01) { + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + + const u32 open_mode_low = static_cast(src->flags) & 0x0Fu; + const u32 src_sub_font_index = src->open_info.sub_font_index; + + const bool publishable_sysfont_instance = false; + + const auto* fontset_record = + static_cast(src->open_info.fontset_record); + const u32* entry_indices = nullptr; + u32 entry_count = 0; + u8* ctx = nullptr; + + if (!fontset_record) { + entry_count = 1; + entry_indices = &src->open_info.ctx_entry_index; + ctx = static_cast(lib->external_fonts_ctx); + } else { + entry_count = fontset_record->entry_count; + entry_indices = reinterpret_cast(fontset_record + 1); + ctx = static_cast(lib->sysfonts_ctx); + } + + if (!publishable_sysfont_instance && (!ctx || !entry_indices || entry_count == 0)) { + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + OrbisFontHandle out_handle = setupFont; + bool owned = false; + if (!out_handle) { + if (!alloc_fn || !free_fn) { + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + out_handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + if (!out_handle) { + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(out_handle, 0, 0x100); + if (auto* out_native = GetNativeFont(out_handle)) { + out_native->flags = 0x10; + } + owned = true; + } else { + if (auto* out_native = GetNativeFont(out_handle)) { + out_native->flags = 0; + } + Internal::g_font_state.erase(out_handle); + } + + std::memcpy(out_handle, fontHandle, 0x100); + auto* dst = GetNativeFont(out_handle); + dst->lock_word = 0; + dst->cached_style.cache_lock_word = 0; + dst->flags = static_cast((dst->flags & 0xFF0Fu) | (owned ? 0x10u : 0u)); + + if (!publishable_sysfont_instance) { + auto* ctx_header = reinterpret_cast(ctx); + auto* entries_base = ctx_header ? static_cast(ctx_header->base) : nullptr; + const u32 max_entries = ctx_header ? ctx_header->max_entries : 0u; + if (!entries_base || entry_count > max_entries) { + dst->magic = 0; + if (owned && free_fn) { + free_fn(lib->alloc_ctx, out_handle); + } + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + struct IncRecord { + u32 entry_index; + Internal::FontObj* node; + }; + std::vector increments; + increments.reserve(entry_count); + + auto lock_word_ptr_for_mode = [&](Internal::FontCtxEntry* entry, u32 m) -> u32* { + if (!entry) { + return nullptr; + } + if (m == 3) { + return &entry->lock_mode3; + } + if (m == 1) { + return &entry->lock_mode1; + } + if (m == 2) { + return &entry->lock_mode2; + } + return nullptr; + }; + auto obj_slot_for_mode = [&](Internal::FontCtxEntry* entry, u32 m) -> void** { + if (!entry) { + return nullptr; + } + if (m == 3) { + return &entry->obj_mode3; + } + if (m == 1) { + return &entry->obj_mode1; + } + if (m == 2) { + return &entry->obj_mode2; + } + return nullptr; + }; + + s32 rc = ORBIS_OK; + for (u32 i = 0; i < entry_count; i++) { + const u32 entry_index = entry_indices[i]; + if (static_cast(entry_index) < 0) { + continue; + } + if (entry_index >= max_entries) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + break; + } + + auto* entry = reinterpret_cast( + entries_base + entry_index * sizeof(Internal::FontCtxEntry)); + auto* lock_word_ptr = lock_word_ptr_for_mode(entry, open_mode_low); + auto** obj_slot = obj_slot_for_mode(entry, open_mode_low); + if (!lock_word_ptr || !obj_slot) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + break; + } + + u32 prev_lock = 0; + for (;;) { + const u32 lw = *lock_word_ptr; + if (static_cast(lw) >= 0) { + std::atomic_ref ref(*lock_word_ptr); + u32 expected = lw; + if (ref.compare_exchange_weak(expected, lw | 0x80000000u, + std::memory_order_acq_rel)) { + prev_lock = lw; + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto unlock_entry = [&](u32 updated) { *lock_word_ptr = updated & 0x7fffffffu; }; + + if ((prev_lock & 0x40000000u) == 0) { + unlock_entry(prev_lock); + rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + break; + } + if ((prev_lock & 0x0FFFFFFFu) == 0x0FFFFFFFu) { + unlock_entry(prev_lock); + rc = ORBIS_FONT_ERROR_FONT_OPEN_MAX; + break; + } + + auto* node = static_cast(*obj_slot); + for (; node != nullptr; node = node->next) { + if (node->sub_font_index == src_sub_font_index) { + break; + } + } + if (!node) { + unlock_entry(prev_lock); + rc = ORBIS_FONT_ERROR_FATAL; + break; + } + + node->refcount++; + unlock_entry(prev_lock + 1); + increments.push_back({entry_index, node}); + } + + if (rc != ORBIS_OK) { + for (const auto& inc : increments) { + if (inc.entry_index >= max_entries || !inc.node) { + continue; + } + auto* entry = reinterpret_cast( + entries_base + inc.entry_index * sizeof(Internal::FontCtxEntry)); + auto* lock_word_ptr = lock_word_ptr_for_mode(entry, open_mode_low); + if (!lock_word_ptr) { + continue; + } + u32 prev_lock = 0; + for (;;) { + const u32 lw = *lock_word_ptr; + if (static_cast(lw) >= 0) { + std::atomic_ref ref(*lock_word_ptr); + u32 expected = lw; + if (ref.compare_exchange_weak(expected, lw | 0x80000000u, + std::memory_order_acq_rel)) { + prev_lock = lw; + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + if ((prev_lock & 0x0FFFFFFFu) != 0) { + inc.node->refcount--; + *lock_word_ptr = (prev_lock - 1) & 0x7fffffffu; + } else { + *lock_word_ptr = prev_lock & 0x7fffffffu; + } + } + + dst->magic = 0; + dst->open_info.unique_id_packed = 0; + dst->open_info.ctx_entry_index = 0; + dst->open_info.sub_font_index = 0; + dst->open_info.fontset_flags = 0; + dst->open_info.fontset_record = nullptr; + dst->open_info.reserved_0x18 = 0; + dst->library = lib; + const u16 prev_flags = dst->flags; + dst->flags = 0; + if ((prev_flags & 0x10) != 0 && free_fn) { + free_fn(lib->alloc_ctx, out_handle); + } + Internal::g_font_state.erase(out_handle); + + release_src_lock(); + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "FAILED"); + return rc; + } + } + + if (owned) { + auto* tail = reinterpret_cast(lib + 1); + auto* list_lock = tail ? &tail->list_head_ptr : nullptr; + void* list_ptr = nullptr; + for (;;) { + list_ptr = list_lock ? *list_lock : nullptr; + if (list_ptr != reinterpret_cast(std::numeric_limits::max())) { + std::atomic_ref ref(*list_lock); + void* expected = list_ptr; + if (ref.compare_exchange_weak( + expected, + reinterpret_cast(std::numeric_limits::max()), + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto* head_ptr = reinterpret_cast(list_ptr); + if (head_ptr) { + const OrbisFontHandle old_head = *head_ptr; + if (auto* out_native = GetNativeFont(out_handle)) { + out_native->prevFont = nullptr; + out_native->nextFont = old_head; + } + if (old_head) { + if (auto* old_native = GetNativeFont(old_head)) { + old_native->prevFont = out_handle; + } + } + *head_ptr = out_handle; + } + *list_lock = list_ptr; + } + + release_src_lock(); + + if (auto* src_state = Internal::TryGetState(fontHandle)) { + auto& dst_state = Internal::GetState(out_handle); + dst_state = *src_state; + dst_state.ext_cache.clear(); + dst_state.scratch.clear(); + dst_state.logged_ext_use = false; + + const u32 subfont_index = out_handle && GetNativeFont(out_handle) + ? GetNativeFont(out_handle)->open_info.sub_font_index + : 0u; + + dst_state.ext_scale_for_height = 0.0f; + dst_state.layout_cached = false; + dst_state.ext_face_ready = false; + dst_state.ext_ft_face = nullptr; + + if (!dst_state.ext_face_data.empty()) { + dst_state.ext_ft_face = Internal::CreateFreeTypeFaceFromBytes( + dst_state.ext_face_data.data(), dst_state.ext_face_data.size(), subfont_index); + } + if (dst_state.ext_ft_face) { + const Internal::FaceMetrics m = Internal::LoadFaceMetrics(dst_state.ext_ft_face); + Internal::PopulateStateMetrics(dst_state, m); + dst_state.ext_face_ready = true; + } + + for (auto& fb : dst_state.system_fallback_faces) { + fb.ft_face = nullptr; + fb.ready = false; + if (!fb.bytes || fb.bytes->empty()) { + continue; + } + fb.ft_face = Internal::CreateFreeTypeFaceFromBytes(fb.bytes->data(), fb.bytes->size(), + subfont_index); + fb.ready = (fb.ft_face != nullptr); + } + } else { + auto& dst_state = Internal::GetState(out_handle); + Internal::DestroyFreeTypeFace(dst_state.ext_ft_face); + for (auto& fb : dst_state.system_fallback_faces) { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + dst_state = {}; + dst_state.library = static_cast(src->library); + if (fontset_record) { + dst_state.font_set_type = fontset_record->font_set_type; + dst_state.system_requested = true; + } + } + + if (pFontHandle) { + *pFontHandle = out_handle; } - dst.layout_cached = false; - *pFontHandle = new_handle; - LOG_INFO(Lib_Font, "font instance clone requested"); - LOG_DEBUG(Lib_Font, - "font instance clone result:\n" - " base_handle={}\n" - " template_handle={}\n" - " new_handle={}\n" - " ext_ready={}\n" - " renderer_inherited={}\n" - " stbtt_reinit={}\n" - " library={}\n" - " renderer={}\n" - " dpi_x={}\n" - " dpi_y={}\n" - " scale_w={}\n" - " scale_h={}\n", - static_cast(fontHandle), static_cast(templateFont), - static_cast(new_handle), dst.ext_face_ready, - dst.bound_renderer != nullptr, stbtt_ok, static_cast(dst.library), - static_cast(dst.bound_renderer), dst.dpi_x, dst.dpi_y, dst.scale_w, - dst.scale_h); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAddress, u32 fontSize, const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle) { - if (!library || !fontAddress || fontSize == 0 || !pFontHandle) { - LOG_DEBUG(Lib_Font, "invalid parameters"); + LOG_INFO(Lib_Font, "called"); + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("library", library), + Param("fontAddress", fontAddress), + Param("fontSize", fontSize), + Param("open_params", open_params), + Param("pFontHandle", pFontHandle), + })); + + s32 rc = ORBIS_FONT_ERROR_INVALID_LIBRARY; + if (!library) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return rc; + } + + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return rc; + } + + u32 prev_lib_lock = 0; + if (!AcquireLibraryLock(lib, prev_lib_lock)) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + auto release_library_and_clear_out = [&] { + ReleaseLibraryLock(lib, prev_lib_lock); + if (pFontHandle) { + *pFontHandle = nullptr; + } + }; + + if (!lib->fontset_registry || !lib->sys_driver) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + if (!alloc_fn || !free_fn) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + auto* ext_ctx = static_cast(lib->external_fonts_ctx); + if (!ext_ctx) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); + return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + + const u32 sub_font_index = open_params ? open_params->subfont_index : 0u; + const s32 unique_id = open_params ? open_params->unique_id : -1; + if (!fontAddress || fontSize == 0 || unique_id < -1 || !pFontHandle) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - (void)open_params; - auto* f = new OrbisFontHandleOpaque{}; - *pFontHandle = f; - auto& st = GetState(f); + + LOG_WARNING(Lib_Font, "font memory requested: addr={} size={} subfont_index={} unique_id={}", + static_cast(fontAddress), fontSize, sub_font_index, unique_id); + + OrbisFontHandle handle = *pFontHandle; + if (handle) { + if (auto* h = GetNativeFont(handle)) { + h->flags = 0; + } + Internal::g_font_state.erase(handle); + } else { + handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + if (!handle) { + release_library_and_clear_out(); + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(handle, 0, 0x100); + if (auto* h = GetNativeFont(handle)) { + h->flags = 0x10; + } + } + + auto cleanup_handle_for_error = [&](s32 err) { + auto* h = GetNativeFont(handle); + if (!h) { + ReleaseLibraryLock(lib, prev_lib_lock); + *pFontHandle = nullptr; + return err; + } + h->magic = 0; + h->open_info.unique_id_packed = 0; + h->open_info.ctx_entry_index = 0; + h->open_info.sub_font_index = 0; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->library = library; + const u16 prev_flags = h->flags; + h->flags = 0; + if ((prev_flags & 0x10) != 0) { + free_fn(lib->alloc_ctx, handle); + } + Internal::g_font_state.erase(handle); + ReleaseLibraryLock(lib, prev_lib_lock); + *pFontHandle = nullptr; + return err; + }; + + auto* ext_header = reinterpret_cast(ext_ctx); + + auto* ctx_lock_word = &ext_header->lock_word; + for (;;) { + const u32 lw = *ctx_lock_word; + if (static_cast(lw) >= 0) { + std::atomic_ref ref(*ctx_lock_word); + u32 expected = lw; + if (ref.compare_exchange_weak(expected, lw | 0x80000000u, std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + const u32 font_max = ext_header->max_entries; + auto* entries_base = static_cast(ext_header->base); + + u32 entry_index = 0xffffffffu; + if (font_max != 0 && entries_base) { + const u32 unique_u32 = static_cast(unique_id); + u32 first_free = 0xffffffffu; + for (u32 i = 0; i < font_max; i++) { + const auto* entry = &entries_base[i]; + const u32 active = entry->active; + const u32 stored_id = entry->unique_id; + const u64 stored_addr = entry->font_address; + + if (((active != 0) && (stored_id == unique_u32)) || + stored_addr == reinterpret_cast(fontAddress)) { + entry_index = i; + break; + } + if (first_free == 0xffffffffu && active == 0) { + first_free = i; + } + } + + if (entry_index == 0xffffffffu) { + entry_index = first_free; + if (entry_index != 0xffffffffu) { + auto* entry = &entries_base[entry_index]; + const u32 stored_id = + (unique_u32 != 0xffffffffu) ? unique_u32 : (entry_index ^ 0x80000000u); + entry->reserved_0x00 = 0; + entry->active = 1; + entry->font_address = reinterpret_cast(fontAddress); + entry->unique_id = stored_id; + entry->lock_mode1 = 0; + entry->lock_mode3 = 0; + entry->lock_mode2 = 0; + entry->obj_mode1 = nullptr; + entry->obj_mode3 = nullptr; + entry->obj_mode2 = nullptr; + std::memset(entry->reserved_0x38, 0, sizeof(entry->reserved_0x38)); + } + } + } + + *ctx_lock_word = 0; + + if (entry_index == 0xffffffffu) { + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_MAX); + LogFontOpenError(err); + return err; + } + + const u32 uvar15 = (unique_id == -1) ? (entry_index + 0x40000000u) + : (static_cast(unique_id) | 0x80000000u); + + auto* entry = &entries_base[entry_index]; + auto* entry_lock_word = &entry->lock_mode1; + u32 entry_prev = 0; + u32 entry_locked = 0; + for (;;) { + entry_prev = *entry_lock_word; + if (static_cast(entry_prev) >= 0) { + std::atomic_ref ref(*entry_lock_word); + u32 expected = entry_prev; + const u32 desired = entry_prev | 0x80000000u; + if (ref.compare_exchange_weak(expected, desired, std::memory_order_acq_rel)) { + entry_locked = desired; + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto release_entry_lock = [&] { + if ((entry_locked & 0x0FFFFFFFu) == 0) { + entry->obj_mode1 = nullptr; + if (entry->obj_mode3 == nullptr && entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + } + *entry_lock_word = entry_locked & 0x7fffffffu; + }; + + void* font_obj = entry->obj_mode1; + + const auto* driver = + lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; + const auto open_fn = driver ? driver->open : nullptr; + const auto metric_fn = driver ? driver->metric : nullptr; + const auto scale_fn = driver ? driver->scale : nullptr; + if (!open_fn || !metric_fn || !scale_fn) { + release_entry_lock(); + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_INVALID_LIBRARY); + LogFontOpenError(err); + return err; + } + + rc = ORBIS_OK; + bool need_open = (entry_prev & 0x40000000u) == 0; + + if (!need_open) { + rc = ORBIS_FONT_ERROR_FONT_OPEN_MAX; + if ((entry_prev & 0x0FFFFFFFu) != 0x0FFFFFFFu) { + for (auto* node = static_cast(font_obj); node != nullptr; + node = node->next) { + if (node->sub_font_index == sub_font_index) { + node->refcount++; + font_obj = node; + rc = ORBIS_OK; + break; + } + } + if (rc != ORBIS_OK) { + need_open = true; + } + } + } + + if (need_open) { + auto* tail = lib + 1; + auto* tail_reserved1 = + tail ? reinterpret_cast(tail->reserved1) : nullptr; + const u32 lib_flags = lib->flags; + if ((lib_flags & 0x200000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x20000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + + rc = open_fn(library, 1, fontAddress, fontSize, sub_font_index, uvar15, &font_obj); + if (tail_reserved1) { + tail_reserved1->sysfont_flags = 0; + } + + if (rc == ORBIS_OK) { + entry->obj_mode1 = font_obj; + entry_locked = entry_prev | 0xC0000000u; + } + } + + if (rc == ORBIS_OK) { + auto* h = GetNativeFont(handle); + if (!h) { + release_entry_lock(); + const s32 err = cleanup_handle_for_error(ORBIS_FONT_ERROR_FATAL); + LogFontOpenError(err); + return err; + } + h->magic = 0x0F02; + h->flags = static_cast(h->flags | 1); + entry_locked = entry_locked + 1; + h->open_info.unique_id_packed = uvar15; + h->open_info.ctx_entry_index = entry_index; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->open_info.sub_font_index = sub_font_index; + h->library = library; + std::memset(&h->cached_style, 0, sizeof(h->cached_style)); + std::memset(h->reserved_0xcc, 0, sizeof(h->reserved_0xcc)); + std::memset(h->reserved_0x30, 0, sizeof(h->reserved_0x30)); + std::memset(h->reserved_0xe8, 0, sizeof(h->reserved_0xe8)); + h->prevFont = nullptr; + h->nextFont = nullptr; + + u16 metric_buf[2] = {}; + (void)metric_fn(font_obj, 0x0e00, metric_buf); + h->metricA = metric_buf[0]; + (void)metric_fn(font_obj, 0xea00, metric_buf); + h->metricB = metric_buf[0]; + h->style_frame[0] = 0x48; + h->style_frame[1] = 0x48; + + float scale = 0.0f; + (void)scale_fn(font_obj, metric_buf, &scale); + h->style_tail.scale_unit = 0; + h->style_tail.reserved_0x0c = 0; + h->style_tail.scale_w = scale; + h->style_tail.scale_h = scale; + h->style_tail.effect_weight_x = 0.0f; + h->style_tail.effect_weight_y = 0.0f; + h->style_tail.slant_ratio = 0.0f; + h->style_tail.reserved_0x24 = 0.0f; + + *pFontHandle = handle; + } + + release_entry_lock(); + + if (rc != ORBIS_OK) { + const s32 err = cleanup_handle_for_error(rc); + LogFontOpenError(err); + return err; + } + + const auto* handle_native = GetNativeFont(handle); + if (handle_native && (handle_native->flags & 0x10) != 0) { + auto* tail = reinterpret_cast(lib + 1); + auto* list_lock = &tail->list_head_ptr; + void* list_ptr = nullptr; + for (;;) { + list_ptr = *list_lock; + if (list_ptr != reinterpret_cast(std::numeric_limits::max())) { + std::atomic_ref ref(*list_lock); + void* expected = list_ptr; + if (ref.compare_exchange_weak( + expected, + reinterpret_cast(std::numeric_limits::max()), + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto* head_ptr = reinterpret_cast(list_ptr); + if (head_ptr) { + const OrbisFontHandle old_head = *head_ptr; + if (auto* h = GetNativeFont(handle)) { + h->prevFont = nullptr; + h->nextFont = old_head; + } + if (old_head) { + if (auto* old_native = GetNativeFont(old_head)) { + old_native->prevFont = handle; + } + } + *head_ptr = handle; + } + *list_lock = list_ptr; + } + + ReleaseLibraryLock(lib, prev_lib_lock); + + auto& st = Internal::GetState(handle); + Internal::DestroyFreeTypeFace(st.ext_ft_face); + for (auto& fb : st.system_fallback_faces) { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + st = {}; st.library = library; st.font_set_type = 0; st.system_font_path.clear(); - const unsigned char* p = reinterpret_cast(fontAddress); - auto& ls = GetLibState(library); - LOG_INFO(Lib_Font, "font memory open requested"); - LOG_DEBUG(Lib_Font, - "font memory open params:\n" - " library={}\n" - " font_address={}\n" - " font_size={}\n" - " open_params={}\n" - " out_handle={}\n" - " sig='{}{}{}{}'\n" - " external_supported={}\n" - " external_formats=0x{:X}", - static_cast(library), fontAddress, fontSize, - static_cast(open_params), static_cast(*pFontHandle), - (fontSize >= 1 ? (char)p[0] : '?'), (fontSize >= 2 ? (char)p[1] : '?'), - (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), - ls.support_external, ls.external_formats); - st.ext_face_data.assign(reinterpret_cast(fontAddress), - reinterpret_cast(fontAddress) + fontSize); - int font_count = stbtt_GetNumberOfFonts(st.ext_face_data.data()); - int chosen_index = 0; - if (font_count > 1) { - chosen_index = 0; - if (open_params) { - chosen_index = - static_cast(open_params->subfont_index % static_cast(font_count)); - } + { + const u8* src = static_cast(fontAddress); + st.ext_face_data.assign(src, src + fontSize); } - int offset = stbtt_GetFontOffsetForIndex(st.ext_face_data.data(), chosen_index); - const unsigned char* d = st.ext_face_data.data(); - const u32 sig32 = (fontSize >= 4) - ? (static_cast(d[0]) << 24) | (static_cast(d[1]) << 16) | - (static_cast(d[2]) << 8) | static_cast(d[3]) - : 0u; - const bool is_ttc = (font_count > 1); - const bool is_otf_cff = (sig32 == 0x4F54544Fu); - const bool is_ttf_sfnt = (sig32 == 0x00010000u) || (sig32 == 0x74727565u); - const bool is_sfnt_typ1 = (sig32 == 0x74797031u); - st.is_otf_cff = is_otf_cff; - if (is_otf_cff) { - LOG_WARNING(Lib_Font, - "ExternalFace: OTF/CFF detected (OTTO). CFF outlines are not supported;" - " handle={} fonts={} requested_index={} -> fallback may occur", - static_cast(*pFontHandle), font_count, chosen_index); - } - if (stbtt_InitFont(&st.ext_face, st.ext_face_data.data(), offset)) { + + const u32 chosen_index = open_params ? open_params->subfont_index : 0u; + st.ext_ft_face = Internal::CreateFreeTypeFaceFromBytes(st.ext_face_data.data(), + st.ext_face_data.size(), chosen_index); + + if (st.ext_ft_face) { + st.ext_cache.clear(); + st.scratch.clear(); + st.logged_ext_use = false; + st.ext_scale_for_height = 0.0f; + st.layout_cached = false; + const Internal::FaceMetrics m = Internal::LoadFaceMetrics(st.ext_ft_face); + Internal::PopulateStateMetrics(st, m); st.ext_face_ready = true; - stbtt_GetFontVMetrics(&st.ext_face, &st.ext_ascent, &st.ext_descent, &st.ext_lineGap); - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - LOG_INFO(Lib_Font, "external face ready"); - LOG_DEBUG(Lib_Font, - "external face params:\n" - " handle={}\n" - " ascent={}\n" - " descent={}\n" - " line_gap={}\n" - " scale={}\n" - " data_bytes={}\n" - " font_count={}\n" - " chosen_index={}\n" - " ttc={}\n" - " sig=0x{:08X}\n" - " kind={}\n", - static_cast(*pFontHandle), st.ext_ascent, st.ext_descent, - st.ext_lineGap, st.ext_scale_for_height, (int)st.ext_face_data.size(), font_count, - chosen_index, is_ttc, sig32, - is_otf_cff ? "OTF/CFF (unsupported)" - : (is_ttf_sfnt ? "TTF (ready)" - : (is_sfnt_typ1 ? "Type1(sfnt) (stub)" : "unknown"))); - if (is_ttf_sfnt) { - LOG_INFO(Lib_Font, "external format TTF ready"); - } else if (is_otf_cff) { - LOG_WARNING(Lib_Font, "external format OpenType-CFF unsupported"); - } else if (is_sfnt_typ1) { - LOG_WARNING(Lib_Font, "external format Type1(sfnt) stub"); - } } else { - LOG_WARNING(Lib_Font, - "ExternalFace: stbtt_InitFont failed for handle={} size={} fonts={}" - " chosen_index={} sig='{}{}{}{}' (OTF/CFF unsupported={})", - static_cast(*pFontHandle), fontSize, font_count, chosen_index, - (fontSize >= 1 ? (char)p[0] : '?'), (fontSize >= 2 ? (char)p[1] : '?'), - (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), - is_otf_cff); - if (is_otf_cff) { - LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; system font unavailable"); - } + st.ext_face_ready = false; } + return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 openMode, const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle) { - if (!library) { - return ORBIS_FONT_ERROR_INVALID_LIBRARY; - } - auto* lib = static_cast(library); - if (lib->magic != 0x0F01) { - return ORBIS_FONT_ERROR_INVALID_LIBRARY; - } - if (!pFontHandle) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - if (!lib->sysfonts_ctx) { - return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; - } - auto* f = new OrbisFontHandleOpaque{}; - *pFontHandle = f; - auto& st = GetState(f); - st.library = library; - st.font_set_type = fontSetType; - st.system_font_path.clear(); + LOG_INFO(Lib_Font, "called"); - LOG_INFO(Lib_Font, "font set open requested"); - LOG_DEBUG(Lib_Font, - "font set open params:\n" - " library={}\n" - " font_set_type=0x{:08X}\n" - " open_mode={}\n" - " open_params={}\n" - " out_handle={}\n", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle)); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("library", library), + Param("fontSetType", fontSetType), + Param("openMode", openMode), + Param("open_params", open_params), + Param("pFontHandle", pFontHandle), + })); - if (const auto* def = FindSystemFontDefinition(fontSetType)) { - const auto pretty = MacroToCamel(def->config_key); - LOG_DEBUG(Lib_Font, - "font set mapping:\n" - " type=0x{:08X}\n" - " key={}\n" - " default_file='{}'\n", - fontSetType, !pretty.empty() ? pretty.c_str() : def->config_key, - def->default_file ? def->default_file : ""); - } else { - LOG_DEBUG(Lib_Font, - "font set mapping:\n" - " type=0x{:08X}\n" - " key=(unknown)\n" - " default_file=''\n", - fontSetType); - } - LogFontOpenParams(open_params); - bool system_ok = false; - const std::string sys_log = ReportSystemFaceRequest(st, f, system_ok); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); - } + { + auto* lib_local = static_cast(library); + if (!lib_local || lib_local->magic != 0x0F01) { + if (pFontHandle) { + *pFontHandle = nullptr; + } + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + if (!pFontHandle) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } - LOG_DEBUG(Lib_Font, - "font set open result:\n" - " library={}\n" - " font_set_type=0x{:08X}\n" - " open_mode={}\n" - " open_params={}\n" - " handle={}\n" - " system_available={}\n", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle), - system_ok); - return ORBIS_OK; + u32 prev_lib_lock = 0; + if (!AcquireLibraryLock(lib_local, prev_lib_lock)) { + *pFontHandle = nullptr; + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + const auto release_library_and_clear_out = [&](s32 err) -> s32 { + ReleaseLibraryLock(lib_local, prev_lib_lock); + *pFontHandle = nullptr; + return err; + }; + + const u32 mode_low = openMode & 0x0Fu; + if (mode_low != 1 && mode_low != 2 && mode_low != 3) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_PARAMETER); + } + if (!lib_local->fontset_registry || !lib_local->sys_driver) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_LIBRARY); + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = + lib_local->alloc_vtbl ? reinterpret_cast(lib_local->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib_local->alloc_vtbl ? reinterpret_cast(lib_local->alloc_vtbl[1]) : nullptr; + if (!alloc_fn || !free_fn) { + LOG_ERROR(Lib_Font, "INVALID_MEMORY"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_MEMORY); + } + + if (!lib_local->sysfonts_ctx) { + constexpr u32 kSysCtxSize = 0x1020; + void* ctx = alloc_fn(lib_local->alloc_ctx, kSysCtxSize); + if (!ctx) { + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); + } + std::memset(ctx, 0, kSysCtxSize); + + auto* header = static_cast(ctx); + header->lock_word = 0; + header->max_entries = 0x40; + header->base = reinterpret_cast(ctx) + sizeof(Internal::FontCtxHeader); + auto* entries = static_cast(header->base); + for (u32 i = 0; i < header->max_entries; i++) { + std::memset(&entries[i], 0, sizeof(entries[i])); + } + + const auto* driver = + reinterpret_cast(lib_local->sys_driver); + const auto support_fn = driver ? driver->support_formats : nullptr; + if (!support_fn) { + free_fn(lib_local->alloc_ctx, ctx); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_LIBRARY); + } + const s32 support_rc = support_fn(library, 0x52); + if (support_rc != ORBIS_OK) { + free_fn(lib_local->alloc_ctx, ctx); + LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); + return release_library_and_clear_out(support_rc); + } + + lib_local->sysfonts_ctx = ctx; + auto& ls = GetLibState(library); + ls.support_system = true; + ls.owned_sysfonts_ctx = ctx; + ls.owned_sysfonts_ctx_size = kSysCtxSize; + } + + const u32 sub_font_index = open_params ? open_params->subfont_index : 0u; + const s32 unique_id = open_params ? open_params->unique_id : -1; + if (unique_id < -1) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_PARAMETER); + } + + std::filesystem::path primary_path = + Internal::ResolveSystemFontPathFromConfigOnly(fontSetType); + if (primary_path.empty()) { + primary_path = Internal::ResolveSystemFontPath(fontSetType); + } + if (primary_path.empty()) { + LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET); + } + + std::vector primary_bytes; + if (!Internal::LoadFontFile(primary_path, primary_bytes)) { + LOG_ERROR(Lib_Font, "FONT_OPEN_FAILED path='{}' sysFontPath='{}'", + primary_path.string(), Config::getSysFontPath().string()); + return release_library_and_clear_out(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); + } + + OrbisFontHandle handle = *pFontHandle; + if (handle) { + if (auto* h = GetNativeFont(handle)) { + h->flags = 0; + } + Internal::g_font_state.erase(handle); + } else { + handle = static_cast(alloc_fn(lib_local->alloc_ctx, 0x100)); + if (!handle) { + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); + } + std::memset(handle, 0, 0x100); + if (auto* h = GetNativeFont(handle)) { + h->flags = 0x10; + } + } + + auto cleanup_handle_for_error = [&](s32 rc) -> s32 { + auto* h = GetNativeFont(handle); + if (!h) { + return release_library_and_clear_out(rc); + } + const u16 prev_flags = h->flags; + h->magic = 0; + h->open_info.unique_id_packed = 0; + h->open_info.ctx_entry_index = 0; + h->open_info.sub_font_index = 0; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->library = library; + h->flags = 0; + if ((prev_flags & 0x10) != 0) { + free_fn(lib_local->alloc_ctx, handle); + } + Internal::g_font_state.erase(handle); + return release_library_and_clear_out(rc); + }; + + auto* h = GetNativeFont(handle); + if (!h) { + return cleanup_handle_for_error(ORBIS_FONT_ERROR_FATAL); + } + + h->magic = 0x0F02; + h->flags = static_cast(h->flags | static_cast(mode_low)); + h->open_info.unique_id_packed = 0; + h->open_info.ctx_entry_index = 0; + h->open_info.fontset_flags = 0; + h->open_info.fontset_record = nullptr; + h->open_info.reserved_0x18 = 0; + h->open_info.sub_font_index = sub_font_index; + h->library = library; + std::memset(&h->cached_style, 0, sizeof(h->cached_style)); + std::memset(h->reserved_0xcc, 0, sizeof(h->reserved_0xcc)); + std::memset(h->reserved_0x30, 0, sizeof(h->reserved_0x30)); + std::memset(h->reserved_0xe8, 0, sizeof(h->reserved_0xe8)); + h->prevFont = nullptr; + h->nextFont = nullptr; + h->style_frame[0] = 0x48; + h->style_frame[1] = 0x48; + *pFontHandle = handle; + + if ((h->flags & 0x10) != 0) { + auto* tail = reinterpret_cast(lib_local + 1); + auto* list_lock = &tail->list_head_ptr; + void* list_ptr = nullptr; + for (;;) { + list_ptr = *list_lock; + if (list_ptr != + reinterpret_cast(std::numeric_limits::max())) { + std::atomic_ref ref(*list_lock); + void* expected = list_ptr; + if (ref.compare_exchange_weak( + expected, + reinterpret_cast(std::numeric_limits::max()), + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto* head_ptr = reinterpret_cast(list_ptr); + if (head_ptr) { + const OrbisFontHandle old_head = *head_ptr; + if (auto* hn = GetNativeFont(handle)) { + hn->prevFont = nullptr; + hn->nextFont = old_head; + } + if (old_head) { + if (auto* old_native = GetNativeFont(old_head)) { + old_native->prevFont = handle; + } + } + *head_ptr = handle; + } + *list_lock = list_ptr; + } + + auto& st = Internal::GetState(handle); + Internal::DestroyFreeTypeFace(st.ext_ft_face); + for (auto& fb : st.system_fallback_faces) { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + st = {}; + st.library = library; + st.font_set_type = fontSetType; + st.system_font_path = primary_path; + st.system_requested = true; + st.system_font_id = 0; + st.system_font_scale_factor = 1.0f; + st.system_font_shift_value = 0; + st.ext_face_data = std::move(primary_bytes); + st.ext_ft_face = Internal::CreateFreeTypeFaceFromBytes( + st.ext_face_data.data(), st.ext_face_data.size(), sub_font_index); + if (!st.ext_ft_face) { + LOG_ERROR(Lib_Font, "FONT_OPEN_FAILED"); + return cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); + } + const Internal::FaceMetrics m = Internal::LoadFaceMetrics(st.ext_ft_face); + Internal::PopulateStateMetrics(st, m); + st.ext_face_ready = true; + + const auto base_dir = Internal::GetSysFontBaseDir(); + if (!base_dir.empty()) { + auto resolve_existing = + [&](std::string_view filename) -> std::optional { + const std::filesystem::path file_path{std::string(filename)}; + std::error_code ec; + { + const auto candidate = base_dir / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = base_dir / "font" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = base_dir / "font2" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + return std::nullopt; + }; + + auto resolve_sysfont_path = [&](const std::filesystem::path& candidate) + -> std::optional { + if (candidate.empty()) { + return std::nullopt; + } + std::error_code ec; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + + const auto filename = candidate.filename().string(); + if (filename.empty()) { + return std::nullopt; + } + + if (auto direct = resolve_existing(filename)) { + return direct; + } + if (auto alias = ResolveKnownSysFontAlias(base_dir, filename)) { + return alias; + } + return std::nullopt; + }; + + auto resolve_override = + [&](const std::string& key) -> std::optional { + if (auto override_path = Config::getSystemFontOverride(key)) { + if (!override_path->empty() && override_path->is_absolute()) { + return *override_path; + } + if (!override_path->empty() && !override_path->has_parent_path()) { + return base_dir / *override_path; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only or absolute " + "path: '{}'", + key, override_path->string()); + } + return std::nullopt; + }; + + auto lower_ascii = [](std::string s) { + for (auto& c : s) { + c = static_cast(std::tolower(static_cast(c))); + } + return s; + }; + + const std::string primary_name_lower = lower_ascii(primary_path.filename().string()); + + auto has_fallback_name_lower = [&](const std::string& candidate_lower) -> bool { + if (candidate_lower.empty() || candidate_lower == primary_name_lower) { + return true; + } + for (const auto& fb : st.system_fallback_faces) { + if (lower_ascii(fb.path.filename().string()) == candidate_lower) { + return true; + } + } + return false; + }; + + auto add_fallback_face = [&](const std::filesystem::path& p) { + std::vector fb_bytes; + if (!Internal::LoadFontFile(p, fb_bytes)) { + return; + } + Internal::FontState::SystemFallbackFace fb{}; + fb.font_id = 0xffffffffu; + fb.scale_factor = 1.0f; + fb.shift_value = 0; + fb.path = p; + fb.bytes = std::make_shared>(std::move(fb_bytes)); + fb.ft_face = Internal::CreateFreeTypeFaceFromBytes( + fb.bytes->data(), fb.bytes->size(), sub_font_index); + fb.ready = (fb.ft_face != nullptr); + if (fb.ready) { + st.system_fallback_faces.push_back(std::move(fb)); + } else { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + }; + + { + if (auto roman_path = resolve_sysfont_path(base_dir / "SST-Roman.otf")) { + const std::string roman_lower = lower_ascii(roman_path->filename().string()); + if (!has_fallback_name_lower(roman_lower)) { + add_fallback_face(*roman_path); + } + } + } + + { + const u32 style_suffix = fontSetType & 0xFFu; + const char* arabic_file = nullptr; + switch (style_suffix) { + case 0xC3: + arabic_file = "SSTArabic-Light.otf"; + break; + case 0xC4: + arabic_file = "SSTArabic-Roman.otf"; + break; + case 0xC5: + arabic_file = "SSTArabic-Medium.otf"; + break; + case 0xC7: + arabic_file = "SSTArabic-Bold.otf"; + break; + default: + break; + } + if (arabic_file) { + if (auto arabic_path = resolve_sysfont_path(base_dir / arabic_file)) { + const std::string arabic_lower = + lower_ascii(arabic_path->filename().string()); + if (!has_fallback_name_lower(arabic_lower)) { + add_fallback_face(*arabic_path); + } + } + } + } + + for (int i = 0; i < 8; ++i) { + const std::string key_a = + fmt::format("fontset_0x{:08X}_fallback{}", fontSetType, i); + const std::string key_b = + fmt::format("fontSet_0x{:08X}_fallback{}", fontSetType, i); + std::optional p = resolve_override(key_a); + if (!p) { + p = resolve_override(key_b); + } + if (!p || p->empty()) { + continue; + } + + std::optional p_resolved; + if (p->is_absolute()) { + std::error_code ec; + if (std::filesystem::exists(*p, ec)) { + p_resolved = *p; + } + } else { + p_resolved = resolve_sysfont_path(*p); + } + if (!p_resolved) { + continue; + } + + std::vector fb_bytes; + if (!Internal::LoadFontFile(*p_resolved, fb_bytes)) { + continue; + } + + Internal::FontState::SystemFallbackFace fb{}; + fb.font_id = 0xffffffffu; + fb.scale_factor = 1.0f; + fb.shift_value = 0; + fb.path = *p_resolved; + fb.bytes = std::make_shared>(std::move(fb_bytes)); + fb.ft_face = Internal::CreateFreeTypeFaceFromBytes( + fb.bytes->data(), fb.bytes->size(), sub_font_index); + fb.ready = (fb.ft_face != nullptr); + if (fb.ready) { + st.system_fallback_faces.push_back(std::move(fb)); + } else { + Internal::DestroyFreeTypeFace(fb.ft_face); + } + } + + { + const std::string global_fallback_name = Config::getSystemFontFallbackName(); + if (!global_fallback_name.empty()) { + const auto existing_name = primary_path.filename().string(); + if (existing_name != global_fallback_name) { + std::filesystem::path fb_path = global_fallback_name; + if (!fb_path.is_absolute()) { + fb_path = base_dir / global_fallback_name; + } + if (const auto resolved_path = resolve_sysfont_path(fb_path)) { + const std::string fb_lower = + lower_ascii(resolved_path->filename().string()); + if (!has_fallback_name_lower(fb_lower)) { + add_fallback_face(*resolved_path); + } + } + } + } + } + } + + { + const auto* driver = + lib_local->sys_driver + ? reinterpret_cast(lib_local->sys_driver) + : nullptr; + const auto open_fn = driver ? driver->open : nullptr; + if (!open_fn) { + return cleanup_handle_for_error(ORBIS_FONT_ERROR_INVALID_LIBRARY); + } + + auto resolve_served_path = [](const std::filesystem::path& p) -> std::filesystem::path { + if (p.empty()) { + return {}; + } + std::error_code ec; + if (std::filesystem::is_regular_file(p, ec) && !ec) { + return p; + } + const auto parent = p.parent_path(); + const auto parent_name = parent.filename().string(); + const auto file_name = p.filename(); + if (!file_name.empty() && (parent_name == "font" || parent_name == "font2")) { + const auto container = parent.parent_path(); + const auto sibling = + container / ((parent_name == "font") ? "font2" : "font") / file_name; + if (std::filesystem::is_regular_file(sibling, ec) && !ec) { + return sibling; + } + } else { + const auto in_font2_dir = parent / "font2" / file_name; + if (std::filesystem::is_regular_file(in_font2_dir, ec) && !ec) { + return in_font2_dir; + } + const auto in_font_dir = parent / "font" / file_name; + if (std::filesystem::is_regular_file(in_font_dir, ec) && !ec) { + return in_font_dir; + } + } + return p; + }; + + auto stable_unique_id_for = [](std::string_view s) -> s32 { + std::uint32_t h = 2166136261u; + for (unsigned char c : s) { + const unsigned char lower = static_cast(std::tolower(c)); + h ^= static_cast(lower); + h *= 16777619u; + } + h &= 0x7fffffffu; + if (h == 0u) { + h = 1u; + } + return static_cast(h); + }; + + auto open_driver_mode_for = [](u32 m) -> u32 { + if (m == 1) { + return 5; + } + if (m == 2) { + return 6; + } + return 7; + }; + + auto open_sysfonts_entry = + [&](const std::filesystem::path& requested_path) -> std::optional { + const std::filesystem::path served_path = resolve_served_path(requested_path); + if (served_path.empty()) { + return std::nullopt; + } + const std::string host_path_str = served_path.string(); + if (host_path_str.empty()) { + return std::nullopt; + } + const s32 unique_id = stable_unique_id_for(host_path_str); + const u32 packed_unique_id = static_cast(unique_id) | 0x80000000u; + + auto* sys_ctx = static_cast(lib_local->sysfonts_ctx); + auto* sys_header = + sys_ctx ? reinterpret_cast(sys_ctx) : nullptr; + auto* entries_base = + sys_header ? static_cast(sys_header->base) : nullptr; + const u32 max_entries = sys_header ? sys_header->max_entries : 0u; + if (!sys_header || !entries_base || max_entries == 0) { + return std::nullopt; + } + + u32* ctx_lock_word = &sys_header->lock_word; + for (;;) { + const u32 lw = *ctx_lock_word; + if (static_cast(lw) >= 0) { + std::atomic_ref ref(*ctx_lock_word); + u32 expected = lw; + if (ref.compare_exchange_weak(expected, lw | 0x80000000u, + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + u32 entry_index = 0xffffffffu; + u32 first_free = 0xffffffffu; + for (u32 i = 0; i < max_entries; i++) { + auto* entry = &entries_base[i]; + const u32 active = entry->active; + const u32 stored_id = entry->unique_id; + if (active != 0 && stored_id == static_cast(unique_id)) { + entry_index = i; + break; + } + if (first_free == 0xffffffffu && active == 0) { + first_free = i; + } + } + + if (entry_index == 0xffffffffu) { + entry_index = first_free; + if (entry_index == 0xffffffffu) { + *ctx_lock_word = 0; + return std::nullopt; + } + + auto* entry = &entries_base[entry_index]; + entry->reserved_0x00 = 0; + entry->active = 1; + entry->font_address = 0; + entry->unique_id = static_cast(unique_id); + entry->lock_mode1 = 0; + entry->lock_mode3 = 0; + entry->lock_mode2 = 0; + entry->obj_mode1 = nullptr; + entry->obj_mode3 = nullptr; + entry->obj_mode2 = nullptr; + std::memset(entry->reserved_0x38, 0, sizeof(entry->reserved_0x38)); + } + + *ctx_lock_word = 0; + + void* font_obj = nullptr; + u32 entry_lock_word = 0; + u8* entry_u8 = Internal::AcquireFontCtxEntry(sys_ctx, entry_index, mode_low, + &font_obj, &entry_lock_word); + auto* entry = + entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + if (!entry) { + return std::nullopt; + } + + void* used_font_obj = font_obj; + u32 updated_lock = entry_lock_word; + s32 rc = ORBIS_FONT_ERROR_FONT_OPEN_MAX; + + if ((entry_lock_word & 0x40000000u) == 0) { + used_font_obj = font_obj; + auto* tail = lib_local + 1; + auto* tail_reserved1 = + tail ? reinterpret_cast(tail->reserved1) + : nullptr; + if (tail_reserved1) { + const u32 lib_flags = lib_local->flags; + if (mode_low == 2) { + if ((lib_flags & 0x100000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x10000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } else { + if ((lib_flags & 0x200000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x20000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } + } + rc = open_fn(library, open_driver_mode_for(mode_low), host_path_str.c_str(), 0, + sub_font_index, packed_unique_id, &used_font_obj); + if (tail_reserved1) { + tail_reserved1->sysfont_flags = 0; + } + if (rc == ORBIS_OK) { + if (mode_low == 3) { + entry->obj_mode3 = used_font_obj; + } else if (mode_low == 2) { + entry->obj_mode2 = used_font_obj; + } else { + entry->obj_mode1 = used_font_obj; + } + updated_lock = entry_lock_word | 0x40000000u; + } + } else { + rc = ORBIS_FONT_ERROR_FONT_OPEN_MAX; + if ((entry_lock_word & 0x0FFFFFFFu) != 0x0FFFFFFFu) { + for (auto* node = static_cast(font_obj); + node != nullptr; node = node->next) { + if (node->sub_font_index == sub_font_index) { + node->refcount++; + used_font_obj = node; + rc = ORBIS_OK; + break; + } + } + if (rc != ORBIS_OK) { + used_font_obj = font_obj; + auto* tail = lib_local + 1; + auto* tail_reserved1 = + tail ? reinterpret_cast( + tail->reserved1) + : nullptr; + if (tail_reserved1) { + const u32 lib_flags = lib_local->flags; + if (mode_low == 2) { + if ((lib_flags & 0x100000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x10000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } else { + if ((lib_flags & 0x200000u) != 0) { + tail_reserved1->sysfont_flags |= 1; + } + if ((lib_flags & 0x20000u) != 0) { + tail_reserved1->sysfont_flags |= 2; + } + } + } + rc = open_fn(library, open_driver_mode_for(mode_low), + host_path_str.c_str(), 0, sub_font_index, packed_unique_id, + &used_font_obj); + if (tail_reserved1) { + tail_reserved1->sysfont_flags = 0; + } + if (rc == ORBIS_OK) { + if (mode_low == 3) { + entry->obj_mode3 = used_font_obj; + } else if (mode_low == 2) { + entry->obj_mode2 = used_font_obj; + } else { + entry->obj_mode1 = used_font_obj; + } + } + } + } + } + + if (rc == ORBIS_OK) { + updated_lock = updated_lock + 1; + } + + const u32 count = updated_lock & 0x0FFFFFFFu; + if (mode_low == 3) { + if (count == 0) { + entry->obj_mode3 = nullptr; + } + entry->lock_mode3 = updated_lock & 0x7fffffffu; + } else if (mode_low == 1) { + if (count == 0) { + entry->obj_mode1 = nullptr; + } + entry->lock_mode1 = updated_lock & 0x7fffffffu; + } else { + if (count == 0) { + entry->obj_mode2 = nullptr; + } + entry->lock_mode2 = updated_lock & 0x7fffffffu; + } + + if (count == 0 && entry->obj_mode1 == nullptr && entry->obj_mode3 == nullptr && + entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + + if (rc != ORBIS_OK) { + return std::nullopt; + } + return entry_index; + }; + + std::vector font_ids; + font_ids.reserve(1 + st.system_fallback_faces.size()); + + const auto primary_entry = open_sysfonts_entry(primary_path); + if (!primary_entry) { + return cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); + } + st.system_font_id = *primary_entry; + h->open_info.ctx_entry_index = st.system_font_id; + font_ids.push_back(st.system_font_id); + + for (auto& fb : st.system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const auto fb_entry = open_sysfonts_entry(fb.path); + if (!fb_entry) { + continue; + } + fb.font_id = *fb_entry; + font_ids.push_back(fb.font_id); + } + + st.fontset_selector = std::make_shared(); + st.fontset_selector->magic = Internal::FontSetSelector::kMagic; + st.fontset_selector->font_set_type = fontSetType; + st.fontset_selector->library = lib_local; + st.fontset_selector->mode_low = mode_low; + st.fontset_selector->sub_font_index = sub_font_index; + st.fontset_selector->primary_font_id = st.system_font_id; + auto lower_ascii_local = [](std::string s) { + for (auto& c : s) { + c = static_cast(std::tolower(static_cast(c))); + } + return s; + }; + for (const auto& fb : st.system_fallback_faces) { + const auto name = lower_ascii_local(fb.path.filename().string()); + if (name == "sst-roman.otf" && fb.font_id != 0xffffffffu) { + st.fontset_selector->roman_font_id = fb.font_id; + break; + } + } + st.fontset_selector->candidates.clear(); + st.fontset_selector->candidates.reserve(font_ids.size()); + for (u32 id : font_ids) { + st.fontset_selector->candidates.push_back({id, nullptr}); + } + + const u32 entry_count = static_cast(font_ids.size()); + const std::size_t indices_bytes = static_cast(entry_count) * sizeof(u32); + const std::size_t ptr_off_unaligned = + sizeof(Internal::FontSetRecordHeader) + indices_bytes; + const std::size_t ptr_align = alignof(const Internal::FontSetSelector*); + const std::size_t ptr_off = (ptr_off_unaligned + (ptr_align - 1u)) & ~(ptr_align - 1u); + const std::size_t rec_size = ptr_off + sizeof(const Internal::FontSetSelector*); + + st.fontset_record_storage = std::make_shared>(rec_size); + std::memset(st.fontset_record_storage->data(), 0, st.fontset_record_storage->size()); + + auto* header = std::construct_at(reinterpret_cast( + st.fontset_record_storage->data())); + header->font_set_type = fontSetType; + header->magic = Internal::FontSetSelector::kMagic; + header->entry_count = entry_count; + + auto* entries = reinterpret_cast(st.fontset_record_storage->data() + + sizeof(Internal::FontSetRecordHeader)); + for (u32 i = 0; i < entry_count; ++i) { + std::construct_at(entries + i, font_ids[i]); + } + + std::construct_at(reinterpret_cast( + st.fontset_record_storage->data() + ptr_off), + st.fontset_selector.get()); + + h->open_info.fontset_record = header; + } + + ReleaseLibraryLock(lib_local, prev_lib_lock); + return ORBIS_OK; + } } s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle) { if (!fontHandle) { - return ORBIS_FONT_ERROR_INVALID_PARAMETER; + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = GetState(fontHandle); - if (!st.bound_renderer) { - return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - LOG_INFO(Lib_Font, "RebindRenderer: handle={} renderer={}", - static_cast(fontHandle), static_cast(st.bound_renderer)); - return ORBIS_OK; + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + ReleaseFontLock(font, prev_font_lock); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_FATAL; + void* renderer = font->renderer_binding.renderer; + if (!renderer) { + rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } else if (*reinterpret_cast(renderer) != 0x0F07) { + rc = ORBIS_FONT_ERROR_FATAL; + } else { + std::memcpy(&font->cached_style.effectWeightY, &font->style_tail.slant_ratio, sizeof(u64)); + + std::memcpy(&font->cached_style, &font->style_frame[0], sizeof(u32)); + + const u32 dpi_y = font->style_frame[1]; + const u32 scale_unit = font->style_tail.scale_unit; + const u32 reserved_0x0c = font->style_tail.reserved_0x0c; + const float scale_w = font->style_tail.scale_w; + const float scale_h = font->style_tail.scale_h; + const float effect_weight_x = font->style_tail.effect_weight_x; + const float effect_weight_y = font->style_tail.effect_weight_y; + + font->cached_style.hDpi = dpi_y; + font->cached_style.vDpi = scale_unit; + font->cached_style.scaleUnit = reserved_0x0c; + font->cached_style.baseScale = scale_w; + font->cached_style.scalePixelW = scale_h; + font->cached_style.scalePixelH = effect_weight_x; + font->cached_style.effectWeightX = effect_weight_y; + rc = ORBIS_OK; + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + ReleaseFontLock(font, prev_font_lock); + return rc; } s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 code, OrbisFontRenderSurface* surf, float x, float y, OrbisFontGlyphMetrics* metrics, OrbisFontRenderOutput* result) { - return sceFontRenderCharGlyphImageHorizontal(fontHandle, code, surf, x, y, metrics, result); + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("code", code), + Param("surf", surf), + Param("x", x), + Param("y", y), + Param("metrics", metrics), + Param("result", result), + })); + + u32 prev_lock_word = 0; + if (!AcquireFontLock(font, prev_lock_word)) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + if (static_cast(font->flags) >= 0) { + s32 pre_rc = ORBIS_OK; + float baseline_add = 0.0f; + + auto cache_cachedstyle_baseline = [&]() -> s32 { + const u8 cached_flags = CachedStyleCacheFlags(font->cached_style); + if ((cached_flags & 0x1) == 0) { + pre_rc = Internal::ComputeHorizontalLayoutBlocksToBytes( + fontHandle, &font->cached_style, font->cached_style.layout_cache_bytes); + if (pre_rc == ORBIS_OK) { + CachedStyleSetCacheFlags(font->cached_style, + static_cast(cached_flags | 0x1)); + pre_rc = ORBIS_OK; + } + } + if (pre_rc == ORBIS_OK) { + const auto layout_words = + Internal::HorizontalLayoutBlocksIo{ + Internal::LayoutWordsBytes(font->cached_style.layout_cache_bytes)} + .fields(); + baseline_add = layout_words.baseline.value(); + } + return pre_rc; + }; + + if (!surf || (surf->styleFlag & 0x1) == 0) { + (void)cache_cachedstyle_baseline(); + } else { + struct RenderSurfaceSystemUse { + OrbisFontStyleFrame* styleframe; + float catchedScale; + std::uint8_t padding[88 - sizeof(OrbisFontStyleFrame*) - sizeof(float)]; + }; + static_assert(sizeof(RenderSurfaceSystemUse) == + sizeof(((OrbisFontRenderSurface*)nullptr)->reserved_q)); + + auto* sys = reinterpret_cast(surf->reserved_q); + auto* styleframe = sys ? sys->styleframe : nullptr; + + if (!styleframe || (styleframe->flags1 & 1) == 0) { + (void)cache_cachedstyle_baseline(); + } else { + struct StyleStateBlock { + /*0x00*/ u32 dpi_x; + /*0x04*/ u32 dpi_y; + /*0x08*/ u32 vDpi_flag; + /*0x0C*/ u32 reserved0c; + /*0x10*/ float scale_x; + /*0x14*/ float scale_y; + /*0x18*/ float effect_wx; + /*0x1C*/ float effect_wy; + /*0x20*/ float slant; + }; + StyleStateBlock state{}; + state.dpi_x = styleframe->hDpi; + state.dpi_y = styleframe->vDpi; + state.vDpi_flag = 0; + state.reserved0c = 0; + state.effect_wx = 0.0f; + state.effect_wy = 0.0f; + state.slant = 0.0f; + if ((styleframe->flags1 & 2) != 0) { + state.slant = styleframe->slantRatio; + } + if ((styleframe->flags1 & 4) != 0) { + state.effect_wx = styleframe->effectWeightX; + state.effect_wy = styleframe->effectWeightY; + } + + pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_x, + &state.scale_y); + Internal::HorizontalLayoutBlocks layout_words{}; + if (pre_rc == ORBIS_OK) { + pre_rc = Internal::ComputeHorizontalLayoutBlocksTyped(fontHandle, &state, + &layout_words); + } + if (pre_rc == ORBIS_OK) { + baseline_add = layout_words.baseline(); + if (sys) { + sys->catchedScale = baseline_add; + } + } + } + } + + const float y_used = y + baseline_add; + + CachedStyleSetDirectionWord(font->cached_style, 1); + + if (code == 0) { + rc = ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } else if (!surf || !metrics || !result) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } else if (pre_rc != ORBIS_OK) { + rc = pre_rc; + } else { + rc = Internal::RenderCharGlyphImageCore(fontHandle, code, surf, x, y_used, metrics, + result); + } + } else { + s32 pre_rc = ORBIS_OK; + float scalar_add = 0.0f; + + auto cache_cachedstyle_scalar = [&]() -> s32 { + const u8 cached_flags = CachedStyleCacheFlags(font->cached_style); + if ((cached_flags & 0x2) == 0) { + Internal::VerticalLayoutBlocks layout_words{}; + pre_rc = Internal::ComputeVerticalLayoutBlocksTyped(fontHandle, &font->cached_style, + &layout_words); + if (pre_rc == ORBIS_OK) { + scalar_add = layout_words.baseline_offset_x(); + CachedStyleSetScalar(font->cached_style, scalar_add); + CachedStyleSetCacheFlags(font->cached_style, + static_cast(cached_flags | 0x2)); + } + } + if (pre_rc == ORBIS_OK && (CachedStyleCacheFlags(font->cached_style) & 0x2) != 0) { + scalar_add = CachedStyleGetScalar(font->cached_style); + } + return pre_rc; + }; + + if (!surf || (surf->styleFlag & 0x1) == 0) { + (void)cache_cachedstyle_scalar(); + } else { + struct RenderSurfaceSystemUse { + OrbisFontStyleFrame* styleframe; + float catchedScale; + std::uint8_t padding[88 - sizeof(OrbisFontStyleFrame*) - sizeof(float)]; + }; + static_assert(sizeof(RenderSurfaceSystemUse) == + sizeof(((OrbisFontRenderSurface*)nullptr)->reserved_q)); + + auto* sys = reinterpret_cast(surf->reserved_q); + auto* styleframe = sys ? sys->styleframe : nullptr; + if (!styleframe || (styleframe->flags1 & 1) == 0) { + (void)cache_cachedstyle_scalar(); + } else { + struct StyleStateBlock { + /*0x00*/ u32 dpi_x; + /*0x04*/ u32 dpi_y; + /*0x08*/ u32 vDpi_flag; + /*0x0C*/ u32 reserved0c; + /*0x10*/ float scale_x; + /*0x14*/ float scale_y; + /*0x18*/ float effect_wx; + /*0x1C*/ float effect_wy; + /*0x20*/ float slant; + }; + StyleStateBlock state{}; + state.dpi_x = styleframe->hDpi; + state.dpi_y = styleframe->vDpi; + state.vDpi_flag = 0; + state.reserved0c = 0; + state.effect_wx = 0.0f; + state.effect_wy = 0.0f; + state.slant = 0.0f; + if ((styleframe->flags1 & 2) != 0) { + state.slant = styleframe->slantRatio; + } + if ((styleframe->flags1 & 4) != 0) { + state.effect_wx = styleframe->effectWeightX; + state.effect_wy = styleframe->effectWeightY; + } + + pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_x, + &state.scale_y); + Internal::VerticalLayoutBlocks layout_words{}; + if (pre_rc == ORBIS_OK) { + pre_rc = Internal::ComputeVerticalLayoutBlocksTyped(fontHandle, &state, + &layout_words); + } + if (pre_rc == ORBIS_OK) { + scalar_add = layout_words.baseline_offset_x(); + if (sys) { + sys->catchedScale = scalar_add; + } + } else { + (void)cache_cachedstyle_scalar(); + } + } + } + + const float x_used = x + scalar_add; + CachedStyleSetDirectionWord(font->cached_style, 2); + + if (code == 0) { + rc = ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } else if (!surf || !metrics || !result) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + } else if (pre_rc != ORBIS_OK) { + rc = pre_rc; + } else { + rc = Internal::RenderCharGlyphImageCore(fontHandle, code, surf, x_used, y, metrics, + result); + } + } + + font->lock_word = prev_lock_word; + + if (rc != ORBIS_OK) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "FAILED"); + return rc; + } + LogRenderResultSample(fontHandle, code, *metrics, *result); + return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandle, u32 code, @@ -2686,251 +5257,126 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl float y, OrbisFontGlyphMetrics* metrics, OrbisFontRenderOutput* result) { LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " handle={}\n" - " code=U+{:04X}\n" - " x={}\n" - " y={}\n" - " metrics_ptr={}\n" - " result_ptr={}\n" - " surf_ptr={}\n" - " buf={}\n" - " widthByte={}\n" - " pixelSizeByte={}\n" - " size={}x{}\n" - " scissor=[{},{}-{}:{}]\n" - " styleFlag={}\n", - static_cast(fontHandle), code, x, y, static_cast(metrics), - static_cast(result), static_cast(surf), - surf ? static_cast(surf->buffer) : nullptr, surf ? surf->widthByte : -1, - surf ? (int)surf->pixelSizeByte : -1, surf ? surf->width : -1, - surf ? surf->height : -1, surf ? surf->sc_x0 : 0u, surf ? surf->sc_y0 : 0u, - surf ? surf->sc_x1 : 0u, surf ? surf->sc_y1 : 0u, surf ? (int)surf->styleFlag : -1); - - LogStrideOnce(surf); if (!fontHandle) { ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "invalid font handle"); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "no support code"); + LOG_ERROR(Lib_Font, "NO_SUPPORT_CODE"); return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; } + if (!surf || !metrics || !result) { ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "invalid parameter"); + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto* st = TryGetState(fontHandle); - if (!st) { + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("code", code), + Param("surf", surf), + Param("x", x), + Param("y", y), + Param("metrics", metrics), + Param("result", result), + })); + + u32 prev_lock_word = 0; + if (!AcquireFontLock(font, prev_lock_word)) { ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "invalid font handle (state)"); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - if (!st->bound_renderer) { + + CachedStyleSetDirectionWord(font->cached_style, 1); + + const s32 rc = + Internal::RenderCharGlyphImageCore(fontHandle, code, surf, x, y, metrics, result); + font->lock_word = prev_lock_word; + + if (rc != ORBIS_OK) { ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "renderer not bound"); - return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + LOG_ERROR(Lib_Font, "FAILED"); + return rc; } - - // Resolve effective pixel scales (style frame overrides -> point scales -> pixel scales) - const OrbisFontStyleFrame* style_frame = nullptr; - if ((surf->styleFlag & 0x1) != 0) { - if (auto it = g_style_for_surface.find(surf); it != g_style_for_surface.end()) { - style_frame = it->second; - } - } - StyleFrameScaleState style_scale = ResolveStyleFrameScale(style_frame, *st); - if (st->scale_point_active) { - style_scale.scale_w = PointsToPixels(st->scale_point_w, style_scale.dpi_x); - style_scale.scale_h = PointsToPixels(st->scale_point_h, style_scale.dpi_y); - } - - const stbtt_fontinfo* face = nullptr; - float scale_y = 0.0f; - if (st->ext_face_ready) { - face = &st->ext_face; - scale_y = ComputeScaleExtForState(*st, face, style_scale.scale_h); - } - if (!face) { - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(*st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); - } - if (system_attached) { - face = &st->ext_face; - scale_y = ComputeScaleExtForState(*st, face, style_scale.scale_h); - } - } - if (!face || scale_y <= kScaleEpsilon) { - ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "no support glyph (face/scale)"); - return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; - } - const float scale_x = (style_scale.scale_h > kScaleEpsilon) - ? scale_y * (style_scale.scale_w / style_scale.scale_h) - : scale_y; - - const int glyph_index = stbtt_FindGlyphIndex(face, static_cast(code)); - if (glyph_index <= 0) { - ClearRenderOutputs(metrics, result); - return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; - } - - int aw = 0, lsb = 0; - stbtt_GetCodepointHMetrics(face, static_cast(code), &aw, &lsb); - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - const float frac_x = x - std::floor(x); - const float frac_y = y - std::floor(y); - stbtt_GetCodepointBitmapBoxSubpixel(face, static_cast(code), scale_x, scale_y, frac_x, - frac_y, &x0, &y0, &x1, &y1); - const int glyph_w = std::max(0, x1 - x0); - const int glyph_h = std::max(0, y1 - y0); - - metrics->w = glyph_w > 0 ? static_cast(glyph_w) : style_scale.scale_w; - metrics->h = glyph_h > 0 ? static_cast(glyph_h) : style_scale.scale_h; - metrics->h_bearing_x = static_cast(lsb) * scale_x; - metrics->h_bearing_y = static_cast(-y0); - metrics->h_adv = static_cast(aw) * scale_x; - metrics->v_bearing_x = 0.0f; - metrics->v_bearing_y = 0.0f; - metrics->v_adv = 0.0f; - - // Compute the baseline position for the current scale so that we can - // place the glyph inside the render surface using the same convention as - // the original library: (x, y) specifies the top-left of the line box, - // while glyphs are positioned relative to the baseline. - int asc = 0, desc = 0, gap = 0; - stbtt_GetFontVMetrics(face, &asc, &desc, &gap); - const float baseline_from_top = static_cast(asc) * scale_y; - const float glyph_top_from_top = baseline_from_top - metrics->h_bearing_y; - - if (!surf->buffer || surf->width <= 0 || surf->height <= 0 || surf->widthByte <= 0 || - surf->pixelSizeByte <= 0) { - ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "no support surface (buffer)"); - return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; - } - - const int bpp = static_cast(surf->pixelSizeByte); - if (bpp != 1 && bpp != 4) { - ClearRenderOutputs(metrics, result); - LOG_ERROR(Lib_Font, "no support surface (bpp={})", bpp); - return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; - } - - std::vector glyph_bitmap; - glyph_bitmap.resize(static_cast(std::max(0, glyph_w)) * - static_cast(std::max(0, glyph_h))); - if (glyph_w > 0 && glyph_h > 0) { - stbtt_MakeCodepointBitmapSubpixel(face, glyph_bitmap.data(), glyph_w, glyph_h, glyph_w, - scale_x, scale_y, frac_x, frac_y, static_cast(code)); - } - - // Decide how to interpret (x, y) based on the font source: - // - System fonts (font sets like SST used by AnywhereVR) behaved best - // when (x, y) was treated as the raw top-left of the glyph box. - // - Game-supplied external TrueType fonts using pixel scaling for Driveclub - // match the original library when (x, y) is the baseline position. - // - Other external fonts (CFF / point scaling / non-Driveclub TTF) behave - // like Catherine_ok, where (x, y) is the line top and glyphs are placed - // from the baseline. - const bool is_system_font = (st->font_set_type != 0) || !st->system_font_path.empty(); - const bool use_baseline_ttf = !is_system_font && !st->is_otf_cff && !st->scale_point_active && - IsEmProfileExternalFont(*st); - - int dest_x = static_cast(std::floor(x)); - int dest_y = 0; - if (is_system_font) { - // AnywhereVR / system fonts: raw top-left. - dest_y = static_cast(std::floor(y)); - } else if (use_baseline_ttf) { - // Driveclub-style TrueType game fonts: (x, y) is the baseline. - const int baseline_dest_x = static_cast(std::floor(x)) + x0; - const int baseline_dest_y = static_cast(std::floor(y)) + y0; - dest_x = baseline_dest_x; - dest_y = baseline_dest_y; - } else { - // Catherine / Digimon / ReFantazio external fonts: - // (x, y) is line top, align glyph from baseline. - dest_y = static_cast(std::floor(y + glyph_top_from_top)); - } - // Apply surface scissor: rendering is clipped to [sc_x0, sc_x1) x [sc_y0, sc_y1), - // clamped to the surface bounds. - const int surface_w = std::max(surf->width, 0); - const int surface_h = std::max(surf->height, 0); - const int clip_x0 = std::clamp(static_cast(surf->sc_x0), 0, surface_w); - const int clip_y0 = std::clamp(static_cast(surf->sc_y0), 0, surface_h); - const int clip_x1 = std::clamp(static_cast(surf->sc_x1), 0, surface_w); - const int clip_y1 = std::clamp(static_cast(surf->sc_y1), 0, surface_h); - - int update_x0 = dest_x; - int update_y0 = dest_y; - int update_w = 0; - int update_h = 0; - - if (glyph_w > 0 && glyph_h > 0 && clip_x1 > clip_x0 && clip_y1 > clip_y0) { - auto* dst_base = static_cast(surf->buffer); - const int start_row = std::max(dest_y, clip_y0); - const int end_row = std::min(dest_y + glyph_h, clip_y1); - const int start_col = std::max(dest_x, clip_x0); - const int end_col = std::min(dest_x + glyph_w, clip_x1); - - update_x0 = start_col; - update_y0 = start_row; - update_w = std::max(0, end_col - start_col); - update_h = std::max(0, end_row - start_row); - for (int row = start_row; row < end_row; ++row) { - const int src_y = row - dest_y; - if (src_y < 0 || src_y >= glyph_h) - continue; - const std::uint8_t* src_row = - glyph_bitmap.data() + static_cast(src_y) * glyph_w; - std::uint8_t* dst_row = dst_base + static_cast(row) * - static_cast(surf->widthByte); - for (int col = start_col; col < end_col; ++col) { - const int src_x = col - dest_x; - if (src_x < 0 || src_x >= glyph_w) - continue; - const std::uint8_t cov = src_row[src_x]; - std::uint8_t* dst = dst_row + static_cast(col) * bpp; - if (bpp == 1) { - dst[0] = cov; - } else { - dst[0] = cov; - dst[1] = cov; - dst[2] = cov; - dst[3] = cov; - } - } - } - } - - result->stage = nullptr; - result->slot.maybe_addr = static_cast(surf->buffer); - result->slot.maybe_rowBytes = static_cast(surf->widthByte); - result->slot.maybe_pixelSize = static_cast(surf->pixelSizeByte); - result->slot.maybe_pixelFmt = 0; - result->new_x = static_cast(std::max(update_x0, 0)); - result->new_y = static_cast(std::max(update_y0, 0)); - result->new_w = static_cast(std::max(update_w, 0)); - result->new_h = static_cast(std::max(update_h, 0)); - result->ImageMetrics.bearing_x = metrics->h_bearing_x; - result->ImageMetrics.bearing_y = metrics->h_bearing_y; - result->ImageMetrics.dv = metrics->h_adv; - result->ImageMetrics.stride = static_cast(surf->widthByte); - result->ImageMetrics.width = static_cast(std::max(update_w, 0)); - result->ImageMetrics.height = static_cast(std::max(update_h, 0)); + LogRenderResultSample(fontHandle, code, *metrics, *result); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, float y, + OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result) { + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (code == 0) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "NO_SUPPORT_CODE"); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + + if (!surf || !metrics || !result) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("code", code), + Param("surf", surf), + Param("x", x), + Param("y", y), + Param("metrics", metrics), + Param("result", result), + })); + + u32 prev_lock_word = 0; + if (!AcquireFontLock(font, prev_lock_word)) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + CachedStyleSetDirectionWord(font->cached_style, 2); + + const s32 rc = + Internal::RenderCharGlyphImageCore(fontHandle, code, surf, x, y, metrics, result); + font->lock_word = prev_lock_word; + + if (rc != ORBIS_OK) { + ClearRenderOutputs(metrics, result); + LOG_ERROR(Lib_Font, "FAILED"); + return rc; + } + LogRenderResultSample(fontHandle, code, *metrics, *result); return ORBIS_OK; } @@ -2953,98 +5399,123 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel) { LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " renderSurface={}\n" - " buffer={}\n" - " bufWidthByte={}\n" - " pixelSizeByte={}\n" - " widthPixel={}\n" - " heightPixel={}\n", - static_cast(renderSurface), buffer, bufWidthByte, pixelSizeByte, - widthPixel, heightPixel); - if (renderSurface) { - renderSurface->buffer = buffer; - renderSurface->widthByte = bufWidthByte; - renderSurface->pixelSizeByte = static_cast(pixelSizeByte); - renderSurface->pad0 = 0; - renderSurface->styleFlag = 0; - renderSurface->pad2 = 0; - renderSurface->width = (widthPixel < 0) ? 0 : widthPixel; - renderSurface->height = (heightPixel < 0) ? 0 : heightPixel; - renderSurface->sc_x0 = 0; - renderSurface->sc_y0 = 0; - renderSurface->sc_x1 = static_cast(renderSurface->width); - renderSurface->sc_y1 = static_cast(renderSurface->height); - std::fill(std::begin(renderSurface->reserved_q), std::end(renderSurface->reserved_q), 0); - LOG_DEBUG(Lib_Font, - "result:\n" - " buf={}\n" - " widthByte={}\n" - " pixelSizeByte={}\n" - " size={}x{}\n" - " scissor=[0,0-{}:{}]\n", - static_cast(buffer), bufWidthByte, pixelSizeByte, - renderSurface->width, renderSurface->height, renderSurface->sc_x1, - renderSurface->sc_y1); + + if (!renderSurface) { + LOG_ERROR(Lib_Font, "NULL_POINTER"); + return; } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("renderSurface", renderSurface), + Param("buffer", buffer), + Param("bufWidthByte", bufWidthByte), + Param("pixelSizeByte", pixelSizeByte), + Param("widthPixel", widthPixel), + Param("heightPixel", heightPixel), + })); + + const u32 w_nonneg = (widthPixel < 0) ? 0u : static_cast(widthPixel); + const u32 h_nonneg = (heightPixel < 0) ? 0u : static_cast(heightPixel); + renderSurface->buffer = buffer; + renderSurface->widthByte = bufWidthByte; + renderSurface->pixelSizeByte = static_cast(pixelSizeByte); + renderSurface->pad0 = 0; + renderSurface->styleFlag = 0; + renderSurface->pad2 = 0; + renderSurface->width = static_cast(w_nonneg); + renderSurface->height = static_cast(h_nonneg); + renderSurface->sc_x0 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_x1 = w_nonneg; + renderSurface->sc_y1 = h_nonneg; } void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, int y0, int w, int h) { LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " renderSurface={}\n" - " x0={}\n" - " y0={}\n" - " w={}\n" - " h={}\n", - static_cast(renderSurface), x0, y0, w, h); - if (!renderSurface) + + if (!renderSurface) { + LOG_ERROR(Lib_Font, "NULL_POINTER"); return; + } - int surfaceWidth = renderSurface->width; - int clip_x0, clip_x1; + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("renderSurface", renderSurface), + Param("x0", x0), + Param("y0", y0), + Param("w", w), + Param("h", h), + })); - if (surfaceWidth != 0) { + const u32 surface_w = static_cast(renderSurface->width); + if (surface_w != 0) { + u32 x1; + u32 x0_out; + u32 w_u = static_cast(w); if (x0 < 0) { - clip_x0 = 0; - clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; - if (w <= -x0) - clip_x1 = 0; + x1 = w_u + static_cast(x0); + if (surface_w < x1) { + x1 = surface_w; + } + if (w_u <= static_cast(-x0)) { + x1 = 0; + } + x0_out = 0; } else { - clip_x0 = (x0 > surfaceWidth) ? surfaceWidth : x0; - clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + x1 = surface_w; + x0_out = surface_w; + if (static_cast(x0) <= surface_w) { + if (surface_w < w_u) { + w_u = surface_w; + } + x1 = w_u + static_cast(x0); + x0_out = static_cast(x0); + if (surface_w < x1) { + x1 = surface_w; + } + } } - renderSurface->sc_x0 = static_cast(clip_x0); - renderSurface->sc_x1 = static_cast(clip_x1); + renderSurface->sc_x0 = x0_out; + renderSurface->sc_x1 = x1; } - int surfaceHeight = renderSurface->height; - int clip_y0, clip_y1; - - if (surfaceHeight != 0) { - if (y0 < 0) { - clip_y0 = 0; - clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; - if (h <= -y0) - clip_y1 = 0; - } else { - clip_y0 = (y0 > surfaceHeight) ? surfaceHeight : y0; - clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; - } - renderSurface->sc_y0 = static_cast(clip_y0); - renderSurface->sc_y1 = static_cast(clip_y1); + const u32 surface_h = static_cast(renderSurface->height); + if (surface_h == 0) { + return; } - LOG_DEBUG(Lib_Font, - "result:\n" - " sc_x0={}\n" - " sc_y0={}\n" - " sc_x1={}\n" - " sc_y1={}\n", - renderSurface->sc_x0, renderSurface->sc_y0, renderSurface->sc_x1, - renderSurface->sc_y1); + + u32 y0_out; + u32 y1 = surface_h; + u32 h_u = static_cast(h); + if (y0 < 0) { + y0_out = 0; + if (h_u <= static_cast(-y0)) { + y1 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_y1 = 0; + return; + } + } else { + y0_out = surface_h; + if (surface_h < static_cast(y0)) { + renderSurface->sc_y0 = y0_out; + renderSurface->sc_y1 = y1; + return; + } + y0_out = static_cast(y0); + if (surface_h < h_u) { + h_u = surface_h; + } + } + + const u32 y1_candidate = h_u + static_cast(y0); + if (y1_candidate <= surface_h) { + y1 = y1_candidate; + } + renderSurface->sc_y0 = y0_out; + renderSurface->sc_y1 = y1; } s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, @@ -3052,34 +5523,81 @@ s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* rende if (!renderSurface) return ORBIS_FONT_ERROR_INVALID_PARAMETER; if (!styleFrame) { - g_style_for_surface.erase(renderSurface); renderSurface->styleFlag &= ~u8{0x1}; renderSurface->reserved_q[0] = 0; + renderSurface->reserved_q[1] = 0; LOG_INFO(Lib_Font, "RenderSurfaceSetStyleFrame: surf={} cleared", static_cast(renderSurface)); return ORBIS_OK; } - if (!EnsureStyleFrameInitialized(styleFrame)) + if (styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - g_style_for_surface[renderSurface] = styleFrame; + } renderSurface->styleFlag |= 0x1; renderSurface->reserved_q[0] = reinterpret_cast(styleFrame); - LOG_INFO(Lib_Font, - "RenderSurfaceSetStyleFrame: surf={} styleFrame={} flags=0x{:X} scale=({}, {}) " - "dpi=({}, {}) mode={}", - static_cast(renderSurface), static_cast(styleFrame), - styleFrame->flags, styleFrame->scaleWidth, styleFrame->scaleHeight, styleFrame->dpiX, - styleFrame->dpiY, styleFrame->scalingFlag); + renderSurface->reserved_q[1] = 0; return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetEffectSlant() { - LOG_DEBUG(Lib_Font, "SetEffectSlant: no-op (effects not implemented)"); +s32 PS4_SYSV_ABI sceFontSetEffectSlant(OrbisFontHandle fontHandle, float slantRatio) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("slantRatio", slantRatio), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_lock = 0; + if (!AcquireFontLock(font, prev_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (Internal::StyleStateSetSlantRatio(font->style_frame, slantRatio) != 0) { + font->cached_style.layout_cache_state = 0; + } + ReleaseFontLock(font, prev_lock); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetEffectWeight() { - LOG_DEBUG(Lib_Font, "SetEffectWeight: no-op (effects not implemented)"); +s32 PS4_SYSV_ABI sceFontSetEffectWeight(OrbisFontHandle fontHandle, float weightXScale, + float weightYScale, u32 mode) { + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("weightXScale", weightXScale), + Param("weightYScale", weightYScale), + Param("mode", mode), + })); + + if (mode != 0) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_lock = 0; + if (!AcquireFontLock(font, prev_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (Internal::StyleStateSetWeightScale(font->style_frame, weightXScale, weightYScale) != 0) { + font->cached_style.layout_cache_state = 0; + } + ReleaseFontLock(font, prev_lock); return ORBIS_OK; } @@ -3089,122 +5607,104 @@ s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { } s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi) { - if (!fontHandle) { - LOG_ERROR(Lib_Font, "invalid font handle (null)"); + auto* font = GetNativeFont(fontHandle); + if (!font) { return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto* st_ptr = TryGetState(fontHandle); - if (!st_ptr) { - LOG_ERROR(Lib_Font, "invalid font handle"); + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = *st_ptr; - const u32 new_h = h_dpi == 0 ? 0x48u : h_dpi; - const u32 new_v = v_dpi == 0 ? 0x48u : v_dpi; - if (st.dpi_x == new_h && st.dpi_y == new_v) { - LOG_TRACE(Lib_Font, "SetResolutionDpi: handle={} unchanged h_dpi={} v_dpi={}", - static_cast(fontHandle), new_h, new_v); - return ORBIS_OK; + + if (Internal::StyleStateSetDpi(font->style_frame, h_dpi, v_dpi) != 0) { + font->cached_style.layout_cache_state = 0; } - st.dpi_x = new_h; - st.dpi_y = new_v; - st.layout_cached = false; // PS4 clears cached metrics when the resolution changes. - LOG_INFO(Lib_Font, "resolution dpi set requested"); - LOG_DEBUG(Lib_Font, - "resolution dpi params:\n" - " handle={}\n" - " h_dpi={}\n" - " v_dpi={}\n", - static_cast(fontHandle), new_h, new_v); + + if (auto* st = Internal::TryGetState(fontHandle)) { + st->dpi_x = font->style_frame[0]; + st->dpi_y = font->style_frame[1]; + st->ext_scale_for_height = 0.0f; + st->layout_cached = false; + } + + ReleaseFontLock(font, prev_font_lock); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h) { - if (!fontHandle || w <= 0.0f || h <= 0.0f) { - LOG_ERROR(Lib_Font, "invalid parameter handle={} w={} h={}", - static_cast(fontHandle), w, h); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto* st_ptr = TryGetState(fontHandle); - if (!st_ptr) { - LOG_ERROR(Lib_Font, "invalid font handle={}", static_cast(fontHandle)); + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = *st_ptr; - st.scale_point_active = false; - st.scale_w = w; - st.scale_h = h; - if (st.ext_face_ready) - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); + + LOG_DEBUG(Lib_Font, "{}", + formatParams({Param("fontHandle", fontHandle), Param("w", w), Param("h", h)})); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - LOG_INFO(Lib_Font, "scale pixel set requested"); - LOG_DEBUG(Lib_Font, - "scale pixel params:\n" - " handle={}\n" - " w={}\n" - " h={}\n" - " ext_scale={}\n" - " ext_ready={}\n", - static_cast(fontHandle), w, h, st.ext_scale_for_height, - st.ext_face_ready); - st.layout_cached = false; + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (Internal::StyleStateSetScalePixel(font->style_frame, w, h) != 0) { + font->cached_style.layout_cache_state = 0; + } + + if (auto* st = Internal::TryGetState(fontHandle)) { + st->scale_w = w; + st->scale_h = h; + st->scale_point_active = false; + st->ext_scale_for_height = 0.0f; + st->layout_cached = false; + } + ReleaseFontLock(font, prev_font_lock); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h) { - if (!fontHandle || w <= 0.0f || h <= 0.0f) { - LOG_ERROR(Lib_Font, "invalid parameter handle={} w={} h={}", - static_cast(fontHandle), w, h); - return ORBIS_FONT_ERROR_INVALID_PARAMETER; - } - auto* st_ptr = TryGetState(fontHandle); - if (!st_ptr) { - LOG_ERROR(Lib_Font, "invalid font handle={}", static_cast(fontHandle)); + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - auto& st = *st_ptr; - const float pixel_w = std::max(0.01f, PointsToPixels(w, st.dpi_x)); - const float pixel_h = std::max(0.01f, PointsToPixels(h, st.dpi_y)); - const bool unchanged_point = st.scale_point_active && - std::abs(st.scale_point_w - w) < kScaleEpsilon && - std::abs(st.scale_point_h - h) < kScaleEpsilon; - const bool unchanged_pixels = std::abs(st.scale_w - pixel_w) < kScaleEpsilon && - std::abs(st.scale_h - pixel_h) < kScaleEpsilon; - if (unchanged_point && unchanged_pixels) { - LOG_TRACE(Lib_Font, "SetScalePoint: handle={} unchanged point=({}, {})", - static_cast(fontHandle), w, h); - return ORBIS_OK; + + LOG_DEBUG(Lib_Font, "{}", + formatParams({Param("fontHandle", fontHandle), Param("w", w), Param("h", h)})); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - st.scale_point_active = true; - st.scale_point_w = w; - st.scale_point_h = h; - st.scale_w = pixel_w; - st.scale_h = pixel_h; - if (st.ext_face_ready) - st.ext_scale_for_height = ComputeScaleExtForState(st, &st.ext_face, st.scale_h); - bool system_attached = false; - const std::string sys_log = ReportSystemFaceRequest(st, fontHandle, system_attached); - if (!sys_log.empty()) { - LOG_ERROR(Lib_Font, "{}", sys_log); + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - st.layout_cached = false; - LOG_INFO(Lib_Font, "scale point set requested"); - LOG_DEBUG(Lib_Font, - "scale point params:\n" - " handle={}\n" - " point_w={}\n" - " point_h={}\n" - " pixel_w={}\n" - " pixel_h={}\n" - " dpi_x={}\n" - " dpi_y={}\n" - " system_attached={}\n", - static_cast(fontHandle), w, h, st.scale_w, st.scale_h, st.dpi_x, - st.dpi_y, system_attached); + + if (Internal::StyleStateSetScalePoint(font->style_frame, w, h) != 0) { + font->cached_style.layout_cache_state = 0; + } + + if (auto* st = Internal::TryGetState(fontHandle)) { + st->scale_point_w = w; + st->scale_point_h = h; + st->scale_point_active = true; + st->ext_scale_for_height = 0.0f; + st->layout_cached = false; + } + ReleaseFontLock(font, prev_font_lock); return ORBIS_OK; } @@ -3218,37 +5718,177 @@ s32 PS4_SYSV_ABI sceFontSetTypographicDesign() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(OrbisFontHandle fontHandle, float slantRatio) { + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("slantRatio", slantRatio), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer_binding.renderer != nullptr) { + if (Internal::StyleStateSetSlantRatio(&font->cached_style, slantRatio) != 0) { + font->cached_style.cache_flags_and_direction = 0; + } + rc = ORBIS_OK; + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } + return rc; } -s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(OrbisFontHandle fontHandle, float weightXScale, + float weightYScale, u32 mode) { + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontHandle", fontHandle), + Param("weightXScale", weightXScale), + Param("weightYScale", weightYScale), + Param("mode", mode), + })); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer_binding.renderer != nullptr) { + rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; + if (mode == 0) { + if (Internal::StyleStateSetWeightScale(&font->cached_style, weightXScale, + weightYScale) != 0) { + font->cached_style.cache_flags_and_direction = 0; + } + rc = ORBIS_OK; + } + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } + return rc; } s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h) { - auto rc = sceFontSetScalePixel(fontHandle, w, h); - LOG_INFO(Lib_Font, "render scale pixel setup requested"); - LOG_DEBUG(Lib_Font, - "render scale pixel setup params:\n" - " handle={}\n" - " w={}\n" - " h={}\n", - static_cast(fontHandle), w, h); + LOG_INFO(Lib_Font, "called"); + + if (!fontHandle) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({Param("fontHandle", fontHandle), Param("w", w), Param("h", h)})); + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer_binding.renderer != nullptr) { + if (Internal::StyleStateSetScalePixel(&font->cached_style, w, h) != 0) { + font->cached_style.cache_flags_and_direction = 0; + } + rc = ORBIS_OK; + if (auto* st = Internal::TryGetState(fontHandle)) { + st->render_scale_w = w; + st->render_scale_h = h; + st->render_scale_point_active = false; + } + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } return rc; } s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h) { - auto rc = sceFontSetScalePoint(fontHandle, w, h); - LOG_INFO(Lib_Font, "render scale point setup requested"); - LOG_DEBUG(Lib_Font, - "render scale point setup params:\n" - " handle={}\n" - " w={}\n" - " h={}\n", - static_cast(fontHandle), w, h); + auto* font = GetNativeFont(fontHandle); + if (!font) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer_binding.renderer != nullptr) { + if (Internal::StyleStateSetScalePoint(&font->cached_style, w, h) != 0) { + font->cached_style.cache_flags_and_direction = 0; + } + rc = ORBIS_OK; + if (auto* st = Internal::TryGetState(fontHandle)) { + st->render_scale_point_w = w; + st->render_scale_point_h = h; + st->render_scale_point_active = true; + } + } + + ReleaseCachedStyleLock(font, prev_cached_lock); return rc; } @@ -3279,193 +5919,338 @@ s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, float* slantRatio) { - if (!styleFrame || !slantRatio) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - if (styleFrame->magic != kStyleFrameMagic) - InitializeStyleFrame(*styleFrame); - *slantRatio = (styleFrame->flags & kStyleFrameFlagSlant) ? styleFrame->slantRatio : 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameGetEffectSlant: frame={} slant={}", - static_cast(styleFrame), *slantRatio); + } + if ((styleFrame->flags1 & 2u) == 0) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + if (!slantRatio) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + *slantRatio = styleFrame->slantRatio; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, float* weightXScale, float* weightYScale, uint32_t* mode) { - if (!fontStyleFrame) + if (!fontStyleFrame || fontStyleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - if (fontStyleFrame->magic != kStyleFrameMagic) - InitializeStyleFrame(*fontStyleFrame); - const bool has_weight = (fontStyleFrame->flags & kStyleFrameFlagWeight) != 0; - if (weightXScale) - *weightXScale = has_weight ? fontStyleFrame->weightXScale : 1.0f; - if (weightYScale) - *weightYScale = has_weight ? fontStyleFrame->weightYScale : 1.0f; - if (mode) + } + if ((fontStyleFrame->flags1 & 4u) == 0) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + if (weightXScale) { + *weightXScale = fontStyleFrame->effectWeightX + 1.0f; + } + if (weightYScale) { + *weightYScale = fontStyleFrame->effectWeightY + 1.0f; + } + if (mode) { *mode = 0; - LOG_DEBUG(Lib_Font, "StyleFrameGetEffectWeight: frame={} weight=({}, {}) mode={}", - static_cast(fontStyleFrame), weightXScale ? *weightXScale : -1.0f, - weightYScale ? *weightYScale : -1.0f, mode ? *mode : 0u); + } + if (!weightXScale && !weightYScale && !mode) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(const OrbisFontStyleFrame* styleFrame, u32* h_dpi, u32* v_dpi) { - if (!ValidateStyleFramePtr(styleFrame) || (!h_dpi && !v_dpi)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - if (h_dpi) - *h_dpi = styleFrame->dpiX > 0 ? static_cast(styleFrame->dpiX) : 0u; - if (v_dpi) - *v_dpi = styleFrame->dpiY > 0 ? static_cast(styleFrame->dpiY) : 0u; - LOG_DEBUG(Lib_Font, "StyleFrameGetResolutionDpi: frame={} -> ({}, {})", - static_cast(styleFrame), h_dpi ? *h_dpi : 0u, v_dpi ? *v_dpi : 0u); + } + if (h_dpi) { + *h_dpi = styleFrame->hDpi; + } else if (!v_dpi) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (v_dpi) { + *v_dpi = styleFrame->vDpi; + } return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(const OrbisFontStyleFrame* styleFrame, float* w, float* h) { - if (!ValidateStyleFramePtr(styleFrame) || (!w && !h)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - const bool active = (styleFrame->flags & kStyleFrameFlagScale) != 0 && - styleFrame->scalingFlag == static_cast(StyleFrameScalingMode::Pixel); - if (w) - *w = active ? styleFrame->scaleWidth : 0.0f; - if (h) - *h = active ? styleFrame->scaleHeight : 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameGetScalePixel: frame={} -> w={}, h={}", - static_cast(styleFrame), w ? *w : 0.0f, h ? *h : 0.0f); + } + if ((styleFrame->flags1 & 1u) == 0) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + if (!w && !h) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const u32 scale_unit = styleFrame->scaleUnit; + if (w) { + float out = styleFrame->scalePixelW; + if (scale_unit != 0 && styleFrame->hDpi != 0) { + out = out * (static_cast(styleFrame->hDpi) / kPointsPerInch); + } + *w = out; + } + if (h) { + float out = styleFrame->scalePixelH; + if (scale_unit != 0 && styleFrame->vDpi != 0) { + out = out * (static_cast(styleFrame->vDpi) / kPointsPerInch); + } + *h = out; + } return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(const OrbisFontStyleFrame* styleFrame, float* w, float* h) { - if (!ValidateStyleFramePtr(styleFrame) || (!w && !h)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - const bool active = (styleFrame->flags & kStyleFrameFlagScale) != 0 && - styleFrame->scalingFlag == static_cast(StyleFrameScalingMode::Point); - if (w) - *w = active ? styleFrame->scaleWidth : 0.0f; - if (h) - *h = active ? styleFrame->scaleHeight : 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameGetScalePoint: frame={} -> w={}pt, h={}pt", - static_cast(styleFrame), w ? *w : 0.0f, h ? *h : 0.0f); + } + if ((styleFrame->flags1 & 1u) == 0) { + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + if (!w && !h) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const u32 scale_unit = styleFrame->scaleUnit; + if (w) { + float out = styleFrame->scalePixelW; + if (scale_unit == 0 && styleFrame->hDpi != 0) { + out = out * (kPointsPerInch / static_cast(styleFrame->hDpi)); + } + *w = out; + } + if (h) { + float out = styleFrame->scalePixelH; + if (scale_unit == 0 && styleFrame->vDpi != 0) { + out = out * (kPointsPerInch / static_cast(styleFrame->vDpi)); + } + *h = out; + } return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameInit(OrbisFontStyleFrame* styleFrame) { - if (!styleFrame) + if (!styleFrame) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - InitializeStyleFrame(*styleFrame); - LOG_DEBUG(Lib_Font, "StyleFrameInit: frame={}", static_cast(styleFrame)); + } + std::memset(reinterpret_cast(&styleFrame->layout_cache_bytes[0x08]), 0, 0x20); + std::memset(reinterpret_cast(&styleFrame->layout_cache_state), 0, 0x20); + std::memset(reinterpret_cast(&styleFrame->scaleUnit), 0, 0x20); + styleFrame->magic = kStyleFrameMagic; + styleFrame->flags1 = 0; + styleFrame->flags2 = 0; + styleFrame->hDpi = 0x48; + styleFrame->vDpi = 0x48; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(OrbisFontStyleFrame* styleFrame, float slantRatio) { - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->slantRatio = slantRatio; - styleFrame->flags |= kStyleFrameFlagSlant; - LOG_DEBUG(Lib_Font, "StyleFrameSetEffectSlant: frame={} slant={}", - static_cast(styleFrame), slantRatio); + } + + float clamped = slantRatio; + if (clamped > 1.0f) { + clamped = 1.0f; + } else if (clamped < -1.0f) { + clamped = -1.0f; + } + if (styleFrame->slantRatio != clamped) { + styleFrame->slantRatio = clamped; + } + styleFrame->flags1 |= 2u; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(OrbisFontStyleFrame* styleFrame, float weightXScale, float weightYScale, u32 mode) { - (void)mode; - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->weightXScale = weightXScale; - styleFrame->weightYScale = weightYScale; - styleFrame->flags |= kStyleFrameFlagWeight; - LOG_DEBUG(Lib_Font, "StyleFrameSetEffectWeight: frame={} weight=({}, {}) mode={}", - static_cast(styleFrame), weightXScale, weightYScale, mode); + } + if (mode != 0) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + constexpr float kClamp = 0.04f; + auto clamp_delta = [&](float delta) { + if (delta > kClamp) { + return kClamp; + } + if (delta < -kClamp) { + return -kClamp; + } + return delta; + }; + + const float dx = clamp_delta(weightXScale - 1.0f); + const float dy = clamp_delta(weightYScale - 1.0f); + if (styleFrame->effectWeightX != dx) { + styleFrame->effectWeightX = dx; + } + if (styleFrame->effectWeightY != dy) { + styleFrame->effectWeightY = dy; + } + styleFrame->flags1 |= 4u; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(OrbisFontStyleFrame* styleFrame, u32 h_dpi, u32 v_dpi) { - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->dpiX = static_cast(h_dpi); - styleFrame->dpiY = static_cast(v_dpi); - styleFrame->flags |= kStyleFrameFlagDpi; - LOG_DEBUG(Lib_Font, "StyleFrameSetResolutionDpi: frame={} -> ({}, {})", - static_cast(styleFrame), h_dpi, v_dpi); + } + if (h_dpi == 0) { + h_dpi = 0x48; + } + if (v_dpi == 0) { + v_dpi = 0x48; + } + styleFrame->hDpi = h_dpi; + styleFrame->vDpi = v_dpi; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(OrbisFontStyleFrame* styleFrame, float w, float h) { - if (!EnsureStyleFrameInitialized(styleFrame) || w <= 0.0f || h <= 0.0f) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->scaleWidth = w; - styleFrame->scaleHeight = h; - styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::Pixel); - styleFrame->flags |= kStyleFrameFlagScale; - LOG_DEBUG(Lib_Font, "StyleFrameSetScalePixel: frame={} -> ({}, {})", - static_cast(styleFrame), w, h); + } + styleFrame->scaleUnit = 0; + styleFrame->scalePixelW = w; + styleFrame->scalePixelH = h; + styleFrame->flags1 |= 1u; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(OrbisFontStyleFrame* styleFrame, float w, float h) { - if (!EnsureStyleFrameInitialized(styleFrame) || w <= 0.0f || h <= 0.0f) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->scaleWidth = w; - styleFrame->scaleHeight = h; - styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::Point); - styleFrame->flags |= kStyleFrameFlagScale; - LOG_DEBUG(Lib_Font, "StyleFrameSetScalePoint: frame={} -> ({}, {})pt", - static_cast(styleFrame), w, h); + } + styleFrame->scaleUnit = 1; + styleFrame->scalePixelW = w; + styleFrame->scalePixelH = h; + styleFrame->flags1 |= 1u; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(OrbisFontStyleFrame* styleFrame) { - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->flags &= ~kStyleFrameFlagSlant; - styleFrame->slantRatio = 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameUnsetEffectSlant: frame={}", - static_cast(styleFrame)); + } + styleFrame->flags1 &= 0xfdu; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(OrbisFontStyleFrame* styleFrame) { - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->flags &= ~kStyleFrameFlagWeight; - styleFrame->weightXScale = 1.0f; - styleFrame->weightYScale = 1.0f; - LOG_DEBUG(Lib_Font, "StyleFrameUnsetEffectWeight: frame={}", - static_cast(styleFrame)); + } + styleFrame->flags1 &= 0xfbu; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(OrbisFontStyleFrame* styleFrame) { - if (!EnsureStyleFrameInitialized(styleFrame)) + if (!styleFrame || styleFrame->magic != kStyleFrameMagic) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; - styleFrame->flags &= ~kStyleFrameFlagScale; - styleFrame->scalingFlag = static_cast(StyleFrameScalingMode::None); - styleFrame->scaleWidth = 0.0f; - styleFrame->scaleHeight = 0.0f; - LOG_DEBUG(Lib_Font, "StyleFrameUnsetScale: frame={}", static_cast(styleFrame)); + } + styleFrame->flags1 &= 0xfeu; return ORBIS_OK; } s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, u32 formats) { - LOG_INFO(Lib_Font, "external font support requested"); - LOG_DEBUG(Lib_Font, - "external font support params:\n" - " library={}\n" - " font_max={}\n" - " formats_mask=0x{:X}\n", - static_cast(library), fontMax, formats); + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("library", library), + Param("fontMax", fontMax), + Param("formats", formats), + })); + + if (!library) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + u32 prev_lock_word = 0; + if (!AcquireLibraryLock(lib, prev_lock_word)) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + + if (lib->external_fonts_ctx) { + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "ALREADY_SPECIFIED"); + return ORBIS_FONT_ERROR_ALREADY_SPECIFIED; + } + + const u32 ctx_size = (fontMax << 6) | 0x20u; + void* ctx = alloc_fn(lib->alloc_ctx, ctx_size); + if (!ctx) { + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(ctx, 0, ctx_size); + + auto* header = static_cast(ctx); + void* list_head = nullptr; + if (fontMax != 0) { + list_head = static_cast(ctx) + sizeof(Internal::FontCtxHeader); + } + if (fontMax != 0) { + auto* entry = static_cast(list_head); + for (u32 i = 0; i < fontMax; i++) { + std::memset(&entry[i], 0, sizeof(entry[i])); + } + } + + header->lock_word = 0; + header->max_entries = fontMax; + header->base = list_head; + + const auto* driver = + lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; + const auto support_fn = driver ? driver->support_formats : nullptr; + if (!support_fn) { + free_fn(lib->alloc_ctx, ctx); + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + const s32 support_rc = support_fn(library, formats); + if (support_rc != ORBIS_OK) { + free_fn(lib->alloc_ctx, ctx); + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); + return support_rc; + } + + lib->external_fonts_ctx = ctx; + auto& ls = GetLibState(library); + ls.alloc_ctx = lib->alloc_ctx; + ls.alloc_fn = alloc_fn; + ls.free_fn = free_fn; ls.support_external = true; ls.external_fontMax = fontMax; ls.external_formats = formats; - LogExternalFormatSupport(formats); + ls.owned_external_fonts_ctx = ctx; + ls.owned_external_fonts_ctx_size = ctx_size; + + ReleaseLibraryLock(lib, prev_lock_word); return ORBIS_OK; } @@ -3475,28 +6260,92 @@ s32 PS4_SYSV_ABI sceFontSupportGlyphs() { } s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { - LOG_INFO(Lib_Font, "system font support requested"); - LOG_DEBUG(Lib_Font, - "system font support params:\n" - " library={}\n", - static_cast(library)); + LOG_INFO(Lib_Font, "called"); + LOG_DEBUG(Lib_Font, "{}", formatParams({Param("library", library)})); if (!library) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } auto* lib = static_cast(library); if (lib->magic != 0x0F01) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + u32 prev_lock_word = 0; + if (!AcquireLibraryLock(lib, prev_lock_word)) { + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - auto& ls = GetLibState(library); - if (ls.support_system) { + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + + if (lib->sysfonts_ctx) { + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "ALREADY_SPECIFIED"); return ORBIS_FONT_ERROR_ALREADY_SPECIFIED; } - // Real implementation allocates a small system-font context; we just mark it available. - lib->sysfonts_ctx = lib->sysfonts_ctx ? lib->sysfonts_ctx : &g_sysfonts_ctx_stub; + constexpr u32 kSysCtxSize = 0x1020; + void* ctx = alloc_fn(lib->alloc_ctx, kSysCtxSize); + if (!ctx) { + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(ctx, 0, kSysCtxSize); + + auto* header = static_cast(ctx); + header->lock_word = 0; + header->max_entries = 0x40; + header->base = reinterpret_cast(ctx) + sizeof(Internal::FontCtxHeader); + + auto* entries = static_cast(header->base); + for (u32 i = 0; i < header->max_entries; i++) { + std::memset(&entries[i], 0, sizeof(entries[i])); + } + + const auto* driver = + lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; + const auto support_fn = driver ? driver->support_formats : nullptr; + if (!support_fn) { + free_fn(lib->alloc_ctx, ctx); + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + const s32 support_rc = support_fn(library, 0x52); + if (support_rc != ORBIS_OK) { + free_fn(lib->alloc_ctx, ctx); + ReleaseLibraryLock(lib, prev_lock_word); + LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); + return support_rc; + } + + lib->sysfonts_ctx = ctx; + + auto& ls = GetLibState(library); + ls.alloc_ctx = lib->alloc_ctx; + ls.alloc_fn = alloc_fn; + ls.free_fn = free_fn; ls.support_system = true; + ls.owned_sysfonts_ctx = ctx; + ls.owned_sysfonts_ctx_size = kSysCtxSize; + + if (!Internal::g_mnt) { + Internal::g_mnt = Common::Singleton::Instance(); + } + if (Internal::g_mnt) { + const auto sysfont_base = GetSysFontBaseDir(); + if (!sysfont_base.empty() && !Internal::g_mnt->GetMount("/:dev_font:")) { + Internal::g_mnt->Mount(sysfont_base, "/:dev_font:", true); + } + } + + ReleaseLibraryLock(lib, prev_lock_word); return ORBIS_OK; } @@ -3532,11 +6381,38 @@ s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { s32 PS4_SYSV_ABI sceFontUnbindRenderer(OrbisFontHandle fontHandle) { LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, - "parameters:\n" - " fontHandle={}\n", - static_cast(fontHandle)); - return ORBIS_OK; + LOG_DEBUG(Lib_Font, "{}", formatParams({Param("fontHandle", fontHandle)})); + + auto* font = GetNativeFont(fontHandle); + if (!font) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer_binding.renderer != nullptr) { + rc = ORBIS_OK; + font->renderer_binding.renderer = nullptr; + if (auto* st = Internal::TryGetState(fontHandle)) { + st->bound_renderer = nullptr; + } + } + + ReleaseCachedStyleLock(font, prev_cached_lock); + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } + return rc; } s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters() { diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 2525e7c2b..1684d4eea 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -31,27 +31,55 @@ struct OrbisFontOpenParams { }; struct OrbisFontGlyphMetrics { - float w; - float h; - float h_bearing_x; - float h_bearing_y; - float h_adv; - float v_bearing_x; - float v_bearing_y; - float v_adv; + float width; + float height; + struct { + float bearingX; + float bearingY; + float advance; + } Horizontal; + struct { + float bearingX; + float bearingY; + float advance; + } Vertical; +}; + +struct OrbisFontGlyphMetricsHorizontal { + float width; + float height; + struct { + float bearing_x; + float bearing_y; + float advance; + } horizontal; +}; + +struct OrbisFontGlyphMetricsHorizontalX { + float width; + struct { + float bearing_x; + float advance; + } horizontal; +}; + +struct OrbisFontGlyphMetricsHorizontalAdvance { + struct { + float advance; + } horizontal; }; struct OrbisFontKerning { - float dx; - float dy; - float px; - float py; + float offsetX; + float offsetY; + float positionX; + float positionY; }; struct OrbisFontGlyphImageMetrics { - float bearing_x; - float bearing_y; - float dv; + float bearingX; + float bearingY; + float advance; float stride; u32 width; u32 height; @@ -106,20 +134,23 @@ struct OrbisFontResultStage { u32 u32_10; }; -struct OrbisFontResultSlot { - u8* maybe_addr; - u32 maybe_rowBytes; - u8 maybe_pixelSize; - u8 maybe_pixelFmt; +struct OrbisFontSurfaceImage { + u8* address; + u32 widthByte; + u8 pixelSizeByte; + u8 pixelFormat; + u16 pad16; }; struct OrbisFontRenderOutput { const OrbisFontResultStage* stage; - OrbisFontResultSlot slot; - u32 new_x; - u32 new_y; - u32 new_w; - u32 new_h; + OrbisFontSurfaceImage SurfaceImage; + struct { + u32 x; + u32 y; + u32 w; + u32 h; + } UpdateRect; OrbisFontGlyphImageMetrics ImageMetrics; }; @@ -163,16 +194,15 @@ struct OrbisFontMem { }; struct OrbisFontTextCharacter { - // Other fields... - struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 - struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08 - void* textOrder; // Field at offset 0x10 (pointer to text order info) - u32 characterCode; // Field assumed at offset 0x28 - u8 unkn_0x31; // Offset 0x31 - u8 unkn_0x33; // Offset 0x33 - u8 charType; // Field assumed at offset 0x39 - u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level - u8 formatFlags; // Field at offset 0x3D (stores format-related flags) + struct OrbisFontTextCharacter* next; + struct OrbisFontTextCharacter* prev; + void* textOrder; + u32 characterCode; + u8 unknown_0x31; + u8 unknown_0x33; + u8 charType; + u8 bidiLevel; + u8 formatFlags; }; struct OrbisFontRenderSurface { @@ -191,19 +221,48 @@ struct OrbisFontRenderSurface { }; struct OrbisFontStyleFrame { - /*0x00*/ u16 magic; // Expected to be 0xF09 - /*0x02*/ u16 flags; - /*0x04*/ s32 dpiX; // DPI scaling factor for width - /*0x08*/ s32 dpiY; // DPI scaling factor for height - /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled - /*0x10*/ float scaleWidth; // Width scaling factor - /*0x14*/ float scaleHeight; // Height scaling factor - /*0x18*/ float weightXScale; - /*0x1c*/ float weightYScale; - /*0x20*/ float slantRatio; - /*0x24*/ + /*0x00*/ u16 magic; + /*0x02*/ u8 flags1; + /*0x03*/ u8 flags2; + /*0x04*/ u32 hDpi; + /*0x08*/ u32 vDpi; + /*0x0C*/ u32 scaleUnit; + /*0x10*/ float baseScale; + /*0x14*/ float scalePixelW; + /*0x18*/ float scalePixelH; + /*0x1C*/ float effectWeightX; + /*0x20*/ float effectWeightY; + /*0x24*/ float slantRatio; + /*0x28*/ u32 reserved_0x28; + /*0x2C*/ u32 layout_cache_state; + /*0x30*/ u32 cache_flags_and_direction; + /*0x34*/ u32 cache_lock_word; + /*0x38*/ u8 layout_cache_bytes[0x20]; + /*0x58*/ u32 reserved_0x58; + /*0x5C*/ u32 cached_scalar_bits; }; +static_assert(sizeof(OrbisFontStyleFrame) == 0x60, "OrbisFontStyleFrame size"); +static_assert(offsetof(OrbisFontStyleFrame, magic) == 0x00, "OrbisFontStyleFrame magic offset"); +static_assert(offsetof(OrbisFontStyleFrame, flags1) == 0x02, "OrbisFontStyleFrame flags1 offset"); +static_assert(offsetof(OrbisFontStyleFrame, hDpi) == 0x04, "OrbisFontStyleFrame hDpi offset"); +static_assert(offsetof(OrbisFontStyleFrame, vDpi) == 0x08, "OrbisFontStyleFrame vDpi offset"); +static_assert(offsetof(OrbisFontStyleFrame, scaleUnit) == 0x0C, + "OrbisFontStyleFrame scaleUnit offset"); +static_assert(offsetof(OrbisFontStyleFrame, scalePixelW) == 0x14, + "OrbisFontStyleFrame scalePixelW offset"); +static_assert(offsetof(OrbisFontStyleFrame, scalePixelH) == 0x18, + "OrbisFontStyleFrame scalePixelH offset"); +static_assert(offsetof(OrbisFontStyleFrame, cache_lock_word) == 0x34, + "OrbisFontStyleFrame cache_lock_word offset"); +static_assert(offsetof(OrbisFontStyleFrame, reserved_0x58) == 0x58, + "OrbisFontStyleFrame reserved_0x58 offset"); + +static_assert(sizeof(OrbisFontGlyphMetrics) == 0x20, "OrbisFontGlyphMetrics size"); +static_assert(sizeof(OrbisFontResultStage) == 0x18, "OrbisFontTransImage size"); +static_assert(sizeof(OrbisFontSurfaceImage) == 0x10, "OrbisFontSurfaceImage size"); +static_assert(sizeof(OrbisFontRenderOutput) == 0x40, "OrbisFontRenderOutput size"); + struct OrbisFontHorizontalLayout { float baselineOffset; float lineAdvance; @@ -278,8 +337,9 @@ s32 PS4_SYSV_ABI sceFontGetAttribute(); s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetrics* metrics); -s32 PS4_SYSV_ABI sceFontGetEffectSlant(); -s32 PS4_SYSV_ABI sceFontGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetEffectSlant(OrbisFontHandle fontHandle, float* slantRatio); +s32 PS4_SYSV_ABI sceFontGetEffectWeight(OrbisFontHandle fontHandle, float* weightXScale, + float* weightYScale, u32* mode); s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile(); s32 PS4_SYSV_ABI sceFontGetFontMetrics(); @@ -294,8 +354,9 @@ s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLi s32 PS4_SYSV_ABI sceFontGetPixelResolution(); s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u32 codepoint, OrbisFontGlyphMetrics* out_metrics); -s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); -s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(OrbisFontHandle fontHandle, float* slantRatio); +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(OrbisFontHandle fontHandle, float* weightXScale, + float* weightYScale, u32* mode); s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* out_w, float* out_h); s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* out_w, float* out_h); @@ -393,7 +454,10 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl OrbisFontRenderSurface* surf, float x, float y, OrbisFontGlyphMetrics* metrics, OrbisFontRenderOutput* result); -s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(OrbisFontHandle fontHandle, u32 code, + OrbisFontRenderSurface* surf, float x, float y, + OrbisFontGlyphMetrics* metrics, + OrbisFontRenderOutput* result); s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); @@ -404,16 +468,18 @@ void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderS int y0, int w, int h); s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, OrbisFontStyleFrame* styleFrame); -s32 PS4_SYSV_ABI sceFontSetEffectSlant(); -s32 PS4_SYSV_ABI sceFontSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetEffectSlant(OrbisFontHandle fontHandle, float slantRatio); +s32 PS4_SYSV_ABI sceFontSetEffectWeight(OrbisFontHandle fontHandle, float weightXScale, + float weightYScale, u32 mode); s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); s32 PS4_SYSV_ABI sceFontSetResolutionDpi(OrbisFontHandle fontHandle, u32 h_dpi, u32 v_dpi); s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetScalePoint(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); -s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); -s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(OrbisFontHandle fontHandle, float slantRatio); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(OrbisFontHandle fontHandle, float weightXScale, + float weightYScale, u32 mode); s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float w, float h); s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp new file mode 100644 index 000000000..56d2c5e8f --- /dev/null +++ b/src/core/libraries/font/font_internal.cpp @@ -0,0 +1,1932 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "font_internal.h" + +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TABLES_H + +#include "core/libraries/font/fontft_internal.h" + +namespace Libraries::Font::Internal { + +Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); + +using Libraries::Font::OrbisFontGenerateGlyphParams; +using Libraries::Font::OrbisFontGlyph; +using Libraries::Font::OrbisFontGlyphMetrics; +using Libraries::Font::OrbisFontGlyphOpaque; +using Libraries::Font::OrbisFontGlyphOutline; +using Libraries::Font::OrbisFontGlyphOutlinePoint; +using Libraries::Font::OrbisFontMem; +using Libraries::Font::OrbisFontStyleFrame; + +std::unordered_map g_font_state; + +std::unordered_map g_library_state; + +std::unordered_map + g_style_for_surface; + +void* g_allocator_vtbl_stub[4] = {}; +std::uint8_t g_sys_driver_stub{}; +std::uint8_t g_fontset_registry_stub{}; +std::uint8_t g_sysfonts_ctx_stub{}; +std::uint8_t g_external_fonts_ctx_stub{}; +u32 g_device_cache_stub{}; + +bool HasSfntTables(const std::vector& bytes); +FontState* TryGetState(Libraries::Font::OrbisFontHandle h); +std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, + bool& attached_out); + +namespace { + +static FT_Library GetFreeTypeLibrary() { + static std::once_flag once; + static FT_Library lib = nullptr; + std::call_once(once, [] { + FT_Library created = nullptr; + if (FT_Init_FreeType(&created) != 0) { + created = nullptr; + } + lib = created; + }); + return lib; +} + +static constexpr FT_Int32 kFtLoadFlagsBase = + static_cast(FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT); + +static constexpr FT_Int32 kFtLoadFlagsRender = + static_cast(kFtLoadFlagsBase | FT_LOAD_RENDER); + +} // namespace + +FT_Face CreateFreeTypeFaceFromBytes(const unsigned char* data, std::size_t size, + u32 subfont_index) { + if (!data || size == 0) { + return nullptr; + } + FT_Library lib = GetFreeTypeLibrary(); + if (!lib) { + return nullptr; + } + FT_Face face = nullptr; + if (FT_New_Memory_Face(lib, reinterpret_cast(data), static_cast(size), + static_cast(subfont_index), &face) != 0) { + return nullptr; + } + (void)FT_Select_Charmap(face, FT_ENCODING_UNICODE); + return face; +} + +void DestroyFreeTypeFace(FT_Face& face) { + if (!face) { + return; + } + FT_Done_Face(face); + face = nullptr; +} + +namespace { + +static std::optional ResolveKnownSysFontAlias( + const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { + const auto resolve_existing = + [&](std::string_view filename) -> std::optional { + const std::filesystem::path file_path{std::string(filename)}; + std::error_code ec; + { + const auto candidate = sysfonts_dir / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font2" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + return std::nullopt; + }; + + static constexpr std::array, 41> kAliases = {{ + {"SST-EU-ROMAN-L.OTF", "SST-Light.otf"}, + {"SST-EU-ROMAN.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-M.OTF", "SST-Medium.otf"}, + {"SST-EU-ROMAN-R.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-I.OTF", "SST-Italic.otf"}, + {"SST-EU-ROMAN-B.OTF", "SST-Bold.otf"}, + {"SST-EU-ROMAN-BI.OTF", "SST-BoldItalic.otf"}, + {"SST-ITALIC-L.OTF", "SST-LightItalic.otf"}, + {"SST-ITALIC-R.OTF", "SST-Italic.otf"}, + {"SST-ITALIC-M.OTF", "SST-MediumItalic.otf"}, + {"SST-ITALIC-B.OTF", "SST-BoldItalic.otf"}, + {"SST-TYPEWRITER-R.OTF", "SSTTypewriter-Roman.otf"}, + {"SST-TYPEWRITER-B.OTF", "SSTTypewriter-Bd.otf"}, + {"SST-JPPRO-R.OTF", "SSTJpPro-Regular.otf"}, + {"SST-JPPRO-B.OTF", "SSTJpPro-Bold.otf"}, + {"SST-CNGB-HEI-R.TTF", "DFHEI5-SONY.ttf"}, + {"SST-ARIB-STD-B24-R.TTF", "SSTAribStdB24-Regular.ttf"}, + {"SST-ARABIC-R.OTF", "SSTArabic-Roman.otf"}, + {"SST-ARABIC-L.OTF", "SSTArabic-Light.otf"}, + {"SST-ARABIC-M.OTF", "SSTArabic-Medium.otf"}, + {"SST-ARABIC-B.OTF", "SSTArabic-Bold.otf"}, + {"SCE-EXT-HANGUL-L.OTF", "SCEPS4Yoongd-Light.otf"}, + {"SCE-EXT-HANGUL-R.OTF", "SCEPS4Yoongd-Medium.otf"}, + {"SCE-EXT-HANGUL-B.OTF", "SCEPS4Yoongd-Bold.otf"}, + {"SSTCC-SERIF-MONO.OTF", "e046323ms.ttf"}, + {"SSTCC-SERIF.OTF", "e046323ts.ttf"}, + {"SSTCC-SANSSERIF-MONO.OTF", "n023055ms.ttf"}, + {"SSTCC-SANSSERIF.OTF", "n023055ts.ttf"}, + {"SSTCC-CUSUAL.OTF", "d013013ds.ttf"}, + {"SSTCC-CURSIVE.OTF", "k006004ds.ttf"}, + {"SSTCC-SMALLCAPITAL.OTF", "c041056ts.ttf"}, + {"SCE-JP-CATTLEYA-L.OTF", "SCE-RDC-R-JPN.otf"}, + {"SCE-JP-CATTLEYA-B.OTF", "SCE-RDC-B-JPN.otf"}, + {"SST-THAI-L.OTF", "SSTThai-Light.otf"}, + {"SST-THAI-R.OTF", "SSTThai-Roman.otf"}, + {"SST-THAI-M.OTF", "SSTThai-Medium.otf"}, + {"SST-THAI-B.OTF", "SSTThai-Bold.otf"}, + {"SST-VIETNAMESE-L.OTF", "SSTVietnamese-Light.otf"}, + {"SST-VIETNAMESE-R.OTF", "SSTVietnamese-Roman.otf"}, + {"SST-VIETNAMESE-M.OTF", "SSTVietnamese-Medium.otf"}, + {"SST-VIETNAMESE-B.OTF", "SSTVietnamese-Bold.otf"}, + }}; + + for (const auto& [from, to] : kAliases) { + if (ps4_filename == from) { + return resolve_existing(to); + } + if (ps4_filename == to) { + if (auto reverse = resolve_existing(from)) { + return reverse; + } + } + } + return std::nullopt; +} + +static std::mutex g_sysfont_file_cache_mutex; +static std::unordered_map>> + g_sysfont_file_cache; + +static std::shared_ptr> GetCachedFontBytes( + const std::filesystem::path& host_path) { + const std::string key = host_path.string(); + { + std::scoped_lock lock(g_sysfont_file_cache_mutex); + if (auto it = g_sysfont_file_cache.find(key); it != g_sysfont_file_cache.end()) { + return it->second; + } + } + + std::vector bytes; + if (!LoadGuestFileBytes(host_path, bytes) || bytes.empty()) { + return {}; + } + + auto shared = std::make_shared>(std::move(bytes)); + { + std::scoped_lock lock(g_sysfont_file_cache_mutex); + g_sysfont_file_cache.emplace(key, shared); + } + return shared; +} + +} // namespace + +FontState::~FontState() { + DestroyFreeTypeFace(ext_ft_face); + for (auto& fb : system_fallback_faces) { + DestroyFreeTypeFace(fb.ft_face); + } +} + +std::mutex g_generated_glyph_mutex; +std::unordered_set g_generated_glyphs; + +void TrackGeneratedGlyph(OrbisFontGlyph glyph) { + std::scoped_lock lock(g_generated_glyph_mutex); + g_generated_glyphs.insert(glyph); +} + +bool ForgetGeneratedGlyph(OrbisFontGlyph glyph) { + std::scoped_lock lock(g_generated_glyph_mutex); + return g_generated_glyphs.erase(glyph) > 0; +} + +GeneratedGlyph* TryGetGeneratedGlyph(OrbisFontGlyph glyph) { + if (!glyph || glyph->magic != 0x0F03) { + return nullptr; + } + std::scoped_lock lock(g_generated_glyph_mutex); + if (g_generated_glyphs.find(glyph) == g_generated_glyphs.end()) { + return nullptr; + } + return reinterpret_cast(glyph); +} + +void PopulateGlyphMetricVariants(GeneratedGlyph& gg) { + if (gg.metrics_initialized) { + return; + } + gg.metrics_horizontal.width = gg.metrics.width; + gg.metrics_horizontal.height = gg.metrics.height; + gg.metrics_horizontal.horizontal.bearing_x = gg.metrics.Horizontal.bearingX; + gg.metrics_horizontal.horizontal.bearing_y = gg.metrics.Horizontal.bearingY; + gg.metrics_horizontal.horizontal.advance = gg.metrics.Horizontal.advance; + + gg.metrics_horizontal_x.width = gg.metrics.width; + gg.metrics_horizontal_x.horizontal.bearing_x = gg.metrics.Horizontal.bearingX; + gg.metrics_horizontal_x.horizontal.advance = gg.metrics.Horizontal.advance; + + gg.metrics_horizontal_adv.horizontal.advance = gg.metrics.Horizontal.advance; + gg.origin_x = static_cast(gg.bbox_x0); + gg.origin_y = static_cast(gg.bbox_y0); + gg.metrics_initialized = true; +} + +void BuildBoundingOutline(GeneratedGlyph& gg) { + const float left = gg.metrics.Horizontal.bearingX; + const float top = gg.metrics.Horizontal.bearingY; + const float right = gg.metrics.Horizontal.bearingX + gg.metrics.width; + const float bottom = gg.metrics.Horizontal.bearingY - gg.metrics.height; + + gg.outline_points.clear(); + gg.outline_tags.clear(); + gg.outline_contours.clear(); + + gg.outline_points.push_back({left, top}); + gg.outline_points.push_back({right, top}); + gg.outline_points.push_back({right, bottom}); + gg.outline_points.push_back({left, bottom}); + + gg.outline_tags.resize(gg.outline_points.size(), 1); + gg.outline_contours.push_back(static_cast(gg.outline_points.size() - 1)); + + gg.outline.points_ptr = gg.outline_points.data(); + gg.outline.tags_ptr = gg.outline_tags.data(); + gg.outline.contour_end_idx = gg.outline_contours.data(); + gg.outline.points_cnt = static_cast(gg.outline_points.size()); + gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); + gg.outline_initialized = true; +} + +bool BuildTrueOutline(GeneratedGlyph& gg) { + auto* st = TryGetState(gg.owner_handle); + if (!st) { + return false; + } + + FT_Face primary_face = nullptr; + if (!ResolveFace(*st, gg.owner_handle, primary_face)) { + return false; + } + + const auto* native = + reinterpret_cast(gg.owner_handle); + if (!native || native->magic != 0x0F02) { + return false; + } + + u32 resolved_code = gg.codepoint; + u32 resolved_font_id = 0; + FT_Face resolved_face = primary_face; + float resolved_scale_factor = 1.0f; + float range_scale = 1.0f; + s32 shift_x_units = 0; + s32 shift_y_units = 0; + + if (native->open_info.fontset_record) { + const auto* fontset_record = + static_cast(native->open_info.fontset_record); + const bool is_internal_fontset_record = + fontset_record && + fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic; + u32 mapped_font_id = 0; + resolved_code = ResolveSysFontCodepoint(native->open_info.fontset_record, + static_cast(gg.codepoint), + native->open_info.fontset_flags, &mapped_font_id); + if (resolved_code == 0) { + return false; + } + + resolved_font_id = mapped_font_id; + if (!is_internal_fontset_record) { + resolved_scale_factor = st->system_font_scale_factor; + } + if (st->system_requested) { + if (mapped_font_id == st->system_font_id) { + resolved_face = st->ext_ft_face; + shift_y_units = st->system_font_shift_value; + } else { + resolved_face = nullptr; + for (const auto& fb : st->system_fallback_faces) { + if (fb.ready && fb.ft_face && fb.font_id == mapped_font_id) { + resolved_face = fb.ft_face; + resolved_scale_factor = fb.scale_factor; + shift_y_units = fb.shift_value; + break; + } + } + } + } + if (!resolved_face) { + return false; + } + if (!is_internal_fontset_record) { + if (shift_y_units == 0) { + shift_y_units = GetSysFontDesc(mapped_font_id).shift_value; + } + if (const std::uint8_t* range_rec = + FindSysFontRangeRecord(mapped_font_id, resolved_code)) { + struct SysFontRangeRecordLocal { + /*0x00*/ u32 start; + /*0x04*/ u32 end; + /*0x08*/ s16 shift_x; + /*0x0A*/ s16 shift_y; + /*0x0C*/ float scale_mul; + /*0x10*/ u32 reserved_0x10; + /*0x14*/ u32 reserved_0x14; + /*0x18*/ u32 reserved_0x18; + }; + static_assert(sizeof(SysFontRangeRecordLocal) == 0x1C); + SysFontRangeRecordLocal rec{}; + std::memcpy(&rec, range_rec, sizeof(rec)); + shift_x_units = static_cast(rec.shift_x); + shift_y_units += static_cast(rec.shift_y); + range_scale = rec.scale_mul; + if (range_scale == 0.0f) { + range_scale = 1.0f; + } + } + } + } + + if (!resolved_face) { + return false; + } + + const FT_UInt glyph_index = + FT_Get_Char_Index(resolved_face, static_cast(resolved_code)); + if (glyph_index == 0) { + return false; + } + + const float scaled_w = gg.glyph.scale_x * resolved_scale_factor * range_scale; + const float scaled_h = gg.glyph.base_scale * resolved_scale_factor * range_scale; + const auto char_w = static_cast(static_cast(scaled_w * 64.0f)); + const auto char_h = static_cast(static_cast(scaled_h * 64.0f)); + if (FT_Set_Char_Size(resolved_face, char_w, char_h, 72, 72) != 0) { + return false; + } + + if (shift_x_units != 0 || shift_y_units != 0) { + if (!resolved_face->size) { + return false; + } + const long x_scale = static_cast(resolved_face->size->metrics.x_scale); + const long y_scale = static_cast(resolved_face->size->metrics.y_scale); + + const auto round_fixed_mul = [](long fixed_16_16, s32 value) -> s32 { + const long long prod = + static_cast(fixed_16_16) * static_cast(value); + const long long sign_adj = + (static_cast(~static_cast(value)) >> 63) * -0x10000LL; + const long long base = sign_adj + prod; + long long tmp = base - 0x8000LL; + if (tmp < 0) { + tmp = base + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + FT_Vector delta{}; + delta.x = static_cast(round_fixed_mul(x_scale, shift_x_units)); + delta.y = static_cast(round_fixed_mul(y_scale, shift_y_units)); + FT_Set_Transform(resolved_face, nullptr, &delta); + } else { + FT_Set_Transform(resolved_face, nullptr, nullptr); + } + + constexpr FT_Int32 kFtLoadFlagsBase = + static_cast(FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); + const bool loaded = (FT_Load_Glyph(resolved_face, glyph_index, kFtLoadFlagsBase) == 0); + FT_Set_Transform(resolved_face, nullptr, nullptr); + if (!loaded) { + return false; + } + + const FT_GlyphSlot slot = resolved_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; + } + + gg.outline_points.clear(); + gg.outline_tags.clear(); + gg.outline_contours.clear(); + gg.outline_points.reserve(static_cast(outline.n_points)); + gg.outline_tags.reserve(static_cast(outline.n_points)); + gg.outline_contours.reserve(static_cast(outline.n_contours)); + + for (int i = 0; i < outline.n_points; ++i) { + const FT_Vector& p = outline.points[i]; + gg.outline_points.push_back( + {static_cast(p.x) / 64.0f, static_cast(p.y) / 64.0f}); + const std::uint8_t tag = outline.tags[i]; + gg.outline_tags.push_back(FT_CURVE_TAG(tag) == FT_CURVE_TAG_ON ? 1 : 0); + } + for (int c = 0; c < outline.n_contours; ++c) { + gg.outline_contours.push_back(static_cast(outline.contours[c])); + } + + const bool ok = !gg.outline_points.empty() && !gg.outline_contours.empty(); + if (!ok) { + return false; + } + + gg.outline.points_ptr = gg.outline_points.data(); + gg.outline.tags_ptr = gg.outline_tags.data(); + gg.outline.contour_end_idx = gg.outline_contours.data(); + gg.outline.points_cnt = static_cast(gg.outline_points.size()); + gg.outline.contours_cnt = static_cast(gg.outline_contours.size()); + gg.outline_initialized = true; + return true; +} + +u16 ClampToU16(float value) { + if (value <= 0.0f) { + return 0; + } + const float clamped = std::min(value, static_cast(std::numeric_limits::max())); + return static_cast(std::lround(clamped)); +} + +FontState& GetState(Libraries::Font::OrbisFontHandle h) { + return g_font_state[h]; +} + +FontState* TryGetState(Libraries::Font::OrbisFontHandle h) { + if (!h) + return nullptr; + auto it = g_font_state.find(h); + if (it == g_font_state.end()) + return nullptr; + return &it->second; +} + +LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { + return g_library_state[lib]; +} + +void RemoveLibState(Libraries::Font::OrbisFontLib lib) { + if (auto it = g_library_state.find(lib); it != g_library_state.end()) { + if (it->second.owned_device_cache) { + delete[] static_cast(it->second.owned_device_cache); + } + g_library_state.erase(it); + } +} + +void LogExternalFormatSupport(u32 formats_mask) { + LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); +} + +bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); + +void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params) { + if (!params) { + LOG_INFO(Lib_Font, "OpenFontSetParams: "); + return; + } + LOG_INFO( + Lib_Font, + "OpenFontSetParams: tag=0x{:04X} flags=0x{:X} subfont={} unique_id={} reserved_ptrs=[{}, " + "{}]", + params->tag, params->flags, params->subfont_index, params->unique_id, params->reserved_ptr1, + params->reserved_ptr2); +} + +std::filesystem::path ResolveGuestPath(const char* guest_path) { + if (!guest_path) { + return {}; + } + if (guest_path[0] != '/') { + return std::filesystem::path(guest_path); + } + if (!g_mnt) { + g_mnt = Common::Singleton::Instance(); + } + if (!g_mnt) { + return {}; + } + return g_mnt->GetHostPath(guest_path); +} + +bool LoadGuestFileBytes(const std::filesystem::path& host_path, + std::vector& out_bytes) { + std::ifstream file(host_path, std::ios::binary | std::ios::ate); + if (!file) { + return false; + } + const std::streamoff size = file.tellg(); + if (size < 0) { + return false; + } + if (size == 0) { + out_bytes.clear(); + return true; + } + if (static_cast(size) > std::numeric_limits::max()) { + return false; + } + out_bytes.resize(static_cast(size)); + file.seekg(0, std::ios::beg); + if (!file.read(reinterpret_cast(out_bytes.data()), + static_cast(out_bytes.size()))) { + out_bytes.clear(); + return false; + } + return true; +} + +FaceMetrics LoadFaceMetrics(FT_Face face) { + FaceMetrics m{}; + if (!face) { + return m; + } + m.units_per_em = static_cast(face->units_per_EM); + + if (const auto* hhea = + static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_hhea))) { + m.hhea_ascent = static_cast(hhea->Ascender); + m.hhea_descent = static_cast(hhea->Descender); + m.hhea_lineGap = static_cast(hhea->Line_Gap); + } else { + m.hhea_ascent = static_cast(face->ascender); + m.hhea_descent = static_cast(face->descender); + const int height = static_cast(face->height); + m.hhea_lineGap = height - (m.hhea_ascent - m.hhea_descent); + } + + if (const auto* os2 = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { + m.has_typo = true; + m.typo_ascent = static_cast(os2->sTypoAscender); + m.typo_descent = static_cast(os2->sTypoDescender); + m.typo_lineGap = static_cast(os2->sTypoLineGap); + m.use_typo = (os2->fsSelection & (1u << 7)) != 0; + } + return m; +} + +void PopulateStateMetrics(FontState& st, const FaceMetrics& m) { + st.ext_units_per_em = m.units_per_em; + st.ext_ascent = m.use_typo && m.has_typo ? m.typo_ascent : m.hhea_ascent; + st.ext_descent = m.use_typo && m.has_typo ? m.typo_descent : m.hhea_descent; + st.ext_lineGap = m.use_typo && m.has_typo ? m.typo_lineGap : m.hhea_lineGap; + st.ext_typo_ascent = m.typo_ascent; + st.ext_typo_descent = m.typo_descent; + st.ext_typo_lineGap = m.typo_lineGap; + st.ext_use_typo_metrics = m.use_typo && m.has_typo; +} + +float ComputeScaleExtForState(const FontState& st, FT_Face face, float pixel_h) { + if (st.ext_units_per_em > 0) { + return pixel_h / static_cast(st.ext_units_per_em); + } + if (!face || face->units_per_EM == 0) { + return 0.0f; + } + return pixel_h / static_cast(face->units_per_EM); +} + +float ComputeScaleForPixelHeight(const FontState& st, float pixel_h) { + if (st.ext_units_per_em <= 0) { + return 0.0f; + } + return pixel_h / static_cast(st.ext_units_per_em); +} + +const float kPointsPerInch = 72.0f; +const float kScaleEpsilon = 1e-4f; + +float SafeDpiToFloat(u32 dpi) { + return dpi == 0 ? kPointsPerInch : static_cast(dpi); +} + +float PointsToPixels(float pt, u32 dpi) { + return pt * SafeDpiToFloat(dpi) / kPointsPerInch; +} + +float PixelsToPoints(float px, u32 dpi) { + const float dpi_f = SafeDpiToFloat(dpi); + return px * kPointsPerInch / dpi_f; +} + +const u16 kStyleFrameMagic = 0x0F09; +const u8 kStyleFrameFlagScale = 0x01; +const u8 kStyleFrameFlagSlant = 0x02; +const u8 kStyleFrameFlagWeight = 0x04; + +StyleFrameScaleState ResolveStyleFrameScale(const OrbisFontStyleFrame* style, const FontState& st) { + StyleFrameScaleState resolved{ + .scale_w = st.scale_w, + .scale_h = st.scale_h, + .dpi_x = st.dpi_x, + .dpi_y = st.dpi_y, + .scale_overridden = false, + .dpi_overridden = false, + }; + if (!style || style->magic != kStyleFrameMagic) { + return resolved; + } + if ((style->flags1 & kStyleFrameFlagScale) != 0) { + const bool unit_is_pixel = (style->scaleUnit == 0); + const u32 dpi_x = style->hDpi; + const u32 dpi_y = style->vDpi; + resolved.scale_w = unit_is_pixel ? style->scalePixelW + : (dpi_x ? PointsToPixels(style->scalePixelW, dpi_x) + : style->scalePixelW); + resolved.scale_h = unit_is_pixel ? style->scalePixelH + : (dpi_y ? PointsToPixels(style->scalePixelH, dpi_y) + : style->scalePixelH); + resolved.scale_overridden = true; + } + if (!resolved.scale_overridden && st.scale_point_active) { + resolved.scale_w = PointsToPixels(st.scale_point_w, resolved.dpi_x); + resolved.scale_h = PointsToPixels(st.scale_point_h, resolved.dpi_y); + } + return resolved; +} + +void InitializeStyleFrame(OrbisFontStyleFrame& frame) { + std::memset(&frame, 0, sizeof(frame)); + frame.magic = kStyleFrameMagic; + frame.hDpi = 72; + frame.vDpi = 72; +} + +bool EnsureStyleFrameInitialized(OrbisFontStyleFrame* frame) { + return ValidateStyleFramePtr(frame); +} + +bool ValidateStyleFramePtr(const OrbisFontStyleFrame* frame) { + return frame && frame->magic == kStyleFrameMagic; +} + +void UpdateCachedLayout(FontState& st) { + if (!st.ext_face_ready || !st.ext_ft_face) { + return; + } + if (st.ext_scale_for_height == 0.0f) { + st.ext_scale_for_height = + ComputeScaleExtForState(st, st.ext_ft_face, st.scale_h * st.system_font_scale_factor); + } + const float scale = st.ext_scale_for_height; + st.cached_baseline_y = static_cast(st.ext_ascent) * scale; + st.layout_cached = true; +} + +std::uint64_t MakeGlyphKey(u32 code, int pixel_h) { + return (static_cast(code) << 32) | static_cast(pixel_h); +} + +void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result) { + if (metrics) { + *metrics = {}; + } + if (result) { + *result = {}; + } +} + +bool ResolveFaceAndScale(FontState& st, Libraries::Font::OrbisFontHandle handle, float pixel_h, + FT_Face& face_out, float& scale_y_out) { + face_out = nullptr; + scale_y_out = 0.0f; + if (st.ext_face_ready) { + face_out = st.ext_ft_face; + } + if (!face_out) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, handle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face_out = st.ext_ft_face; + } + } + if (pixel_h <= kScaleEpsilon) { + return false; + } + if (!face_out) { + return false; + } + scale_y_out = ComputeScaleExtForState(st, face_out, pixel_h * st.system_font_scale_factor); + return scale_y_out > kScaleEpsilon; +} + +bool ResolveFace(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face& face_out) { + face_out = nullptr; + if (st.ext_face_ready) { + face_out = st.ext_ft_face; + } + if (!face_out) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, handle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face_out = st.ext_ft_face; + } + } + return face_out != nullptr; +} + +// LLE: FUN_0100a690 +s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face face, + float pixel_w, float pixel_h, + Libraries::Font::OrbisFontRenderSurface* surf, u32 code, float x, + float y, Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result, s32 shift_x_units, + s32 shift_y_units) { + ClearRenderOutputs(metrics, result); + + if (!surf || !metrics || !result) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (!surf->buffer || surf->width <= 0 || surf->height <= 0 || surf->widthByte <= 0 || + surf->pixelSizeByte <= 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + + const int bpp = static_cast(surf->pixelSizeByte); + if (bpp != 1 && bpp != 4) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + + FT_Face primary_face = face; + if (!primary_face) { + float primary_scale_y = 0.0f; + if (!ResolveFaceAndScale(st, handle, pixel_h, primary_face, primary_scale_y)) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + } + + auto resolve_scale_factor = [&](FT_Face candidate) -> float { + if (!candidate) { + return st.system_font_scale_factor; + } + if (candidate == st.ext_ft_face) { + return st.system_font_scale_factor; + } + for (const auto& fb : st.system_fallback_faces) { + if (fb.ready && fb.ft_face == candidate) { + return fb.scale_factor; + } + } + return st.system_font_scale_factor; + }; + + FT_Face resolved_face = primary_face; + FT_UInt resolved_glyph_index = + resolved_face ? FT_Get_Char_Index(resolved_face, static_cast(code)) : 0; + float resolved_scale_factor = resolve_scale_factor(resolved_face); + if (resolved_glyph_index == 0) { + if (st.ext_face_ready && st.ext_ft_face && resolved_face != st.ext_ft_face) { + const FT_UInt gi = FT_Get_Char_Index(st.ext_ft_face, static_cast(code)); + if (gi != 0) { + resolved_face = st.ext_ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = st.system_font_scale_factor; + } + } + if (resolved_glyph_index == 0) { + for (const auto& fb : st.system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const FT_UInt gi = FT_Get_Char_Index(fb.ft_face, static_cast(code)); + if (gi == 0) { + continue; + } + resolved_face = fb.ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = fb.scale_factor; + break; + } + } + } + if (!resolved_face || resolved_glyph_index == 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const auto set_size = [&](float w, float h) -> bool { + const auto char_w = static_cast(static_cast(w * 64.0f)); + const auto char_h = static_cast(static_cast(h * 64.0f)); + return FT_Set_Char_Size(resolved_face, char_w, char_h, 72, 72) == 0; + }; + if (!set_size(pixel_w * resolved_scale_factor, pixel_h * resolved_scale_factor)) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + FT_Vector delta{}; + delta.x = static_cast(static_cast(frac_x * 64.0f)); + delta.y = static_cast(-static_cast(frac_y * 64.0f)); + + if ((shift_x_units != 0 || shift_y_units != 0) && resolved_face->size) { + const long x_scale = static_cast(resolved_face->size->metrics.x_scale); + const long y_scale = static_cast(resolved_face->size->metrics.y_scale); + + const auto round_fixed_mul = [](long fixed_16_16, s32 value) -> s32 { + const long long prod = + static_cast(fixed_16_16) * static_cast(value); + const long long sign_adj = + (static_cast(~static_cast(value)) >> 63) * -0x10000LL; + const long long base = sign_adj + prod; + long long tmp = base - 0x8000LL; + if (tmp < 0) { + tmp = base + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + delta.x += static_cast(round_fixed_mul(x_scale, shift_x_units)); + delta.y += static_cast(round_fixed_mul(y_scale, shift_y_units)); + } + FT_Set_Transform(resolved_face, nullptr, &delta); + + if (FT_Load_Glyph(resolved_face, resolved_glyph_index, kFtLoadFlagsRender) != 0) { + FT_Set_Transform(resolved_face, nullptr, nullptr); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + std::vector glyph_bitmap; + int x0 = 0, y0 = 0; + int glyph_w = 0; + int glyph_h = 0; + + const FT_GlyphSlot slot = resolved_face->glyph; + glyph_w = static_cast(slot->bitmap.width); + glyph_h = static_cast(slot->bitmap.rows); + x0 = static_cast(slot->bitmap_left); + y0 = -static_cast(slot->bitmap_top); + FT_Set_Transform(resolved_face, nullptr, nullptr); + + const float bearing_x = static_cast(slot->metrics.horiBearingX) / 64.0f; + const float bearing_y = static_cast(slot->metrics.horiBearingY) / 64.0f; + const float advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + const float width_px = static_cast(slot->metrics.width) / 64.0f; + const float height_px = static_cast(slot->metrics.height) / 64.0f; + + metrics->width = width_px; + metrics->height = height_px; + metrics->Horizontal.bearingX = bearing_x; + metrics->Horizontal.bearingY = bearing_y; + metrics->Horizontal.advance = advance; + metrics->Vertical.bearingX = 0.0f; + metrics->Vertical.bearingY = 0.0f; + metrics->Vertical.advance = 0.0f; + + glyph_bitmap.resize(static_cast(glyph_w) * static_cast(glyph_h)); + if (glyph_w > 0 && glyph_h > 0) { + const int pitch = static_cast(slot->bitmap.pitch); + const unsigned char* src = reinterpret_cast(slot->bitmap.buffer); + if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + for (int col = 0; col < glyph_w; ++col) { + const unsigned char byte = src_row[col >> 3]; + const unsigned char bit = static_cast(0x80u >> (col & 7)); + dst_row[col] = (byte & bit) ? 0xFFu : 0x00u; + } + } + } else { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + std::memcpy(dst_row, src_row, static_cast(glyph_w)); + } + } + } + + const int dest_x = static_cast(std::floor(x)) + x0; + const int dest_y = static_cast(std::floor(y)) + y0; + + const int surface_w = std::max(surf->width, 0); + const int surface_h = std::max(surf->height, 0); + const int clip_x0 = std::clamp(static_cast(surf->sc_x0), 0, surface_w); + const int clip_y0 = std::clamp(static_cast(surf->sc_y0), 0, surface_h); + const int clip_x1 = std::clamp(static_cast(surf->sc_x1), 0, surface_w); + const int clip_y1 = std::clamp(static_cast(surf->sc_y1), 0, surface_h); + + int update_x0 = dest_x; + int update_y0 = dest_y; + int update_w = 0; + int update_h = 0; + + if (glyph_w > 0 && glyph_h > 0 && clip_x1 > clip_x0 && clip_y1 > clip_y0) { + auto* dst_base = static_cast(surf->buffer); + const int start_row = std::max(dest_y, clip_y0); + const int end_row = std::min(dest_y + glyph_h, clip_y1); + const int start_col = std::max(dest_x, clip_x0); + const int end_col = std::min(dest_x + glyph_w, clip_x1); + + update_x0 = start_col; + update_y0 = start_row; + update_w = std::max(0, end_col - start_col); + update_h = std::max(0, end_row - start_row); + + for (int row = start_row; row < end_row; ++row) { + const int src_y = row - dest_y; + if (src_y < 0 || src_y >= glyph_h) + continue; + const std::uint8_t* src_row = + glyph_bitmap.data() + static_cast(src_y) * glyph_w; + std::uint8_t* dst_row = dst_base + static_cast(row) * + static_cast(surf->widthByte); + for (int col = start_col; col < end_col; ++col) { + const int src_x = col - dest_x; + if (src_x < 0 || src_x >= glyph_w) + continue; + const std::uint8_t cov = src_row[src_x]; + std::uint8_t* dst = dst_row + static_cast(col) * bpp; + if (bpp == 1) { + dst[0] = cov; + } else { + dst[0] = cov; + dst[1] = cov; + dst[2] = cov; + dst[3] = cov; + } + } + } + } + + result->stage = nullptr; + result->SurfaceImage.address = static_cast(surf->buffer); + result->SurfaceImage.widthByte = static_cast(surf->widthByte); + result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); + result->SurfaceImage.pixelFormat = 0; + result->SurfaceImage.pad16 = 0; + result->UpdateRect.x = static_cast(std::max(update_x0, 0)); + result->UpdateRect.y = static_cast(std::max(update_y0, 0)); + result->UpdateRect.w = static_cast(std::max(update_w, 0)); + result->UpdateRect.h = static_cast(std::max(update_h, 0)); + result->SurfaceImage.address = + static_cast(surf->buffer) + + static_cast(result->UpdateRect.y) * static_cast(surf->widthByte) + + static_cast(result->UpdateRect.x) * static_cast(bpp); + const auto floor_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) > v) { + --i; + } + return i; + }; + const auto ceil_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) < v) { + ++i; + } + return i; + }; + + const float left_f = x + metrics->Horizontal.bearingX; + const float top_f = y + metrics->Horizontal.bearingY; + const float right_f = left_f + metrics->width; + const float bottom_f = top_f - metrics->height; + + const int left_i = floor_int(left_f); + const int top_i = floor_int(top_f); + const int right_i = ceil_int(right_f); + const int bottom_i = ceil_int(bottom_f); + + const float adv_f = x + metrics->Horizontal.advance; + const float adv_snapped = static_cast(floor_int(adv_f)) - x; + + result->ImageMetrics.bearingX = static_cast(left_i) - x; + result->ImageMetrics.bearingY = static_cast(top_i) - y; + result->ImageMetrics.advance = adv_snapped; + int stride_i = right_i + 1; + const float adjust = static_cast(right_i) - right_f; + const int tmp_i = floor_int(adv_f + adjust); + const int adv_trunc_i = static_cast(std::trunc(adv_f)); + if (adv_trunc_i == 0) { + stride_i = tmp_i; + } + if (stride_i < tmp_i) { + stride_i = tmp_i; + } + result->ImageMetrics.stride = static_cast(stride_i) - x; + result->ImageMetrics.width = static_cast(std::max(0, right_i - left_i)); + result->ImageMetrics.height = static_cast(std::max(0, top_i - bottom_i)); + return ORBIS_OK; +} + +// LLE: FUN_0100e050 +s32 RenderCodepointToSurfaceWithScale(FontState& st, Libraries::Font::OrbisFontHandle handle, + FT_Face face, float scale_x, float scale_y, + Libraries::Font::OrbisFontRenderSurface* surf, u32 code, + float x, float y, Libraries::Font::OrbisFontGlyphMetrics* m, + Libraries::Font::OrbisFontRenderOutput* result) { + ClearRenderOutputs(m, result); + + if (!surf || !m || !result) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (scale_x <= kScaleEpsilon || scale_y <= kScaleEpsilon) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + if (!surf->buffer || surf->width <= 0 || surf->height <= 0 || surf->widthByte <= 0 || + surf->pixelSizeByte <= 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + const int bpp = static_cast(surf->pixelSizeByte); + if (bpp != 1 && bpp != 4) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + + FT_Face primary_face = face; + if (!ResolveFace(st, handle, primary_face)) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + FT_Face resolved_face = primary_face; + FT_UInt resolved_glyph_index = + resolved_face ? FT_Get_Char_Index(resolved_face, static_cast(code)) : 0; + float resolved_scale_factor = st.system_font_scale_factor; + if (resolved_glyph_index == 0) { + for (const auto& fb : st.system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const FT_UInt gi = FT_Get_Char_Index(fb.ft_face, static_cast(code)); + if (gi == 0) { + continue; + } + resolved_face = fb.ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = fb.scale_factor; + break; + } + } + if (!resolved_face || resolved_glyph_index == 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const int base_units = (st.ext_units_per_em > 0) + ? st.ext_units_per_em + : static_cast(resolved_face->units_per_EM); + if (base_units <= 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const float pixel_w = scale_x * static_cast(base_units) * resolved_scale_factor; + const float pixel_h = scale_y * static_cast(base_units) * resolved_scale_factor; + + const auto set_size = [&](float w, float h) -> bool { + const auto char_w = static_cast(static_cast(w * 64.0f)); + const auto char_h = static_cast(static_cast(h * 64.0f)); + return FT_Set_Char_Size(resolved_face, char_w, char_h, 72, 72) == 0; + }; + if (!set_size(pixel_w, pixel_h)) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + FT_Vector delta{}; + delta.x = static_cast(static_cast(frac_x * 64.0f)); + delta.y = static_cast(-static_cast(frac_y * 64.0f)); + FT_Set_Transform(resolved_face, nullptr, &delta); + + if (FT_Load_Glyph(resolved_face, resolved_glyph_index, kFtLoadFlagsRender) != 0) { + FT_Set_Transform(resolved_face, nullptr, nullptr); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + std::vector glyph_bitmap; + int x0 = 0, y0 = 0; + int glyph_w = 0; + int glyph_h = 0; + + const FT_GlyphSlot slot = resolved_face->glyph; + glyph_w = static_cast(slot->bitmap.width); + glyph_h = static_cast(slot->bitmap.rows); + x0 = static_cast(slot->bitmap_left); + y0 = -static_cast(slot->bitmap_top); + FT_Set_Transform(resolved_face, nullptr, nullptr); + + const float bearing_x = static_cast(slot->metrics.horiBearingX) / 64.0f; + const float bearing_y = static_cast(slot->metrics.horiBearingY) / 64.0f; + const float advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + const float width_px = static_cast(slot->metrics.width) / 64.0f; + const float height_px = static_cast(slot->metrics.height) / 64.0f; + + m->width = width_px; + m->height = height_px; + m->Horizontal.bearingX = bearing_x; + m->Horizontal.bearingY = bearing_y; + m->Horizontal.advance = advance; + m->Vertical.bearingX = 0.0f; + m->Vertical.bearingY = 0.0f; + m->Vertical.advance = 0.0f; + + glyph_bitmap.resize(static_cast(glyph_w) * static_cast(glyph_h)); + if (glyph_w > 0 && glyph_h > 0) { + const int pitch = static_cast(slot->bitmap.pitch); + const unsigned char* src = reinterpret_cast(slot->bitmap.buffer); + if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + for (int col = 0; col < glyph_w; ++col) { + const unsigned char byte = src_row[col >> 3]; + const unsigned char bit = static_cast(0x80u >> (col & 7)); + dst_row[col] = (byte & bit) ? 0xFFu : 0x00u; + } + } + } else { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + std::memcpy(dst_row, src_row, static_cast(glyph_w)); + } + } + } + + const int dest_x = static_cast(std::floor(x)) + x0; + const int dest_y = static_cast(std::floor(y)) + y0; + + const int surface_w = std::max(surf->width, 0); + const int surface_h = std::max(surf->height, 0); + const int clip_x0 = std::clamp(static_cast(surf->sc_x0), 0, surface_w); + const int clip_y0 = std::clamp(static_cast(surf->sc_y0), 0, surface_h); + const int clip_x1 = std::clamp(static_cast(surf->sc_x1), 0, surface_w); + const int clip_y1 = std::clamp(static_cast(surf->sc_y1), 0, surface_h); + + int update_x0 = dest_x; + int update_y0 = dest_y; + int update_w = 0; + int update_h = 0; + + if (glyph_w > 0 && glyph_h > 0 && clip_x1 > clip_x0 && clip_y1 > clip_y0) { + auto* dst_base = static_cast(surf->buffer); + const int start_row = std::max(dest_y, clip_y0); + const int end_row = std::min(dest_y + glyph_h, clip_y1); + const int start_col = std::max(dest_x, clip_x0); + const int end_col = std::min(dest_x + glyph_w, clip_x1); + + update_x0 = start_col; + update_y0 = start_row; + update_w = std::max(0, end_col - start_col); + update_h = std::max(0, end_row - start_row); + + for (int row = start_row; row < end_row; ++row) { + const int src_y = row - dest_y; + if (src_y < 0 || src_y >= glyph_h) + continue; + const std::uint8_t* src_row = + glyph_bitmap.data() + static_cast(src_y) * glyph_w; + std::uint8_t* dst_row = dst_base + static_cast(row) * + static_cast(surf->widthByte); + for (int col = start_col; col < end_col; ++col) { + const int src_x = col - dest_x; + if (src_x < 0 || src_x >= glyph_w) + continue; + const std::uint8_t cov = src_row[src_x]; + std::uint8_t* dst = dst_row + static_cast(col) * bpp; + if (bpp == 1) { + dst[0] = cov; + } else { + dst[0] = cov; + dst[1] = cov; + dst[2] = cov; + dst[3] = cov; + } + } + } + } + + result->stage = nullptr; + result->SurfaceImage.address = static_cast(surf->buffer); + result->SurfaceImage.widthByte = static_cast(surf->widthByte); + result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); + result->SurfaceImage.pixelFormat = 0; + result->SurfaceImage.pad16 = 0; + result->UpdateRect.x = static_cast(std::max(update_x0, 0)); + result->UpdateRect.y = static_cast(std::max(update_y0, 0)); + result->UpdateRect.w = static_cast(std::max(update_w, 0)); + result->UpdateRect.h = static_cast(std::max(update_h, 0)); + result->SurfaceImage.address = + static_cast(surf->buffer) + + static_cast(result->UpdateRect.y) * static_cast(surf->widthByte) + + static_cast(result->UpdateRect.x) * static_cast(bpp); + const auto floor_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) > v) { + --i; + } + return i; + }; + const auto ceil_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) < v) { + ++i; + } + return i; + }; + + const float left_f = x + m->Horizontal.bearingX; + const float top_f = y + m->Horizontal.bearingY; + const float right_f = left_f + m->width; + const float bottom_f = top_f - m->height; + + const int left_i = floor_int(left_f); + const int top_i = floor_int(top_f); + const int right_i = ceil_int(right_f); + const int bottom_i = ceil_int(bottom_f); + + const float adv_f = x + m->Horizontal.advance; + const float adv_snapped = static_cast(floor_int(adv_f)) - x; + + result->ImageMetrics.bearingX = static_cast(left_i) - x; + result->ImageMetrics.bearingY = static_cast(top_i) - y; + result->ImageMetrics.advance = adv_snapped; + int stride_i = right_i + 1; + const float adjust = static_cast(right_i) - right_f; + const int tmp_i = floor_int(adv_f + adjust); + const int adv_trunc_i = static_cast(std::trunc(adv_f)); + if (adv_trunc_i == 0) { + stride_i = tmp_i; + } + if (stride_i < tmp_i) { + stride_i = tmp_i; + } + result->ImageMetrics.stride = static_cast(stride_i) - x; + result->ImageMetrics.width = static_cast(std::max(0, right_i - left_i)); + result->ImageMetrics.height = static_cast(std::max(0, top_i - bottom_i)); + return ORBIS_OK; +} + +const GlyphEntry* GetGlyphEntry(FontState& st, Libraries::Font::OrbisFontHandle handle, u32 code, + FT_Face& face_out, float& scale_out) { + face_out = nullptr; + scale_out = 0.0f; + + if (st.ext_face_ready) { + face_out = st.ext_ft_face; + } + if (!face_out) { + bool system_attached = false; + const std::string sys_log = ReportSystemFaceRequest(st, handle, system_attached); + if (!sys_log.empty()) { + LOG_ERROR(Lib_Font, "SYSTEM_FONT_FAILED"); + LOG_DEBUG(Lib_Font, "{}", sys_log); + } + if (system_attached) { + face_out = st.ext_ft_face; + } + } + if (!face_out) { + return nullptr; + } + + float resolved_scale_factor = st.system_font_scale_factor; + FT_Face resolved_face = face_out; + FT_UInt resolved_glyph_index = FT_Get_Char_Index(resolved_face, static_cast(code)); + if (resolved_glyph_index == 0) { + for (const auto& fb : st.system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const FT_UInt gi = FT_Get_Char_Index(fb.ft_face, static_cast(code)); + if (gi == 0) { + continue; + } + resolved_face = fb.ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = fb.scale_factor; + break; + } + } + if (!resolved_face || resolved_glyph_index == 0) { + return nullptr; + } + + const float pixel_h_f = st.scale_h * resolved_scale_factor; + const int pixel_h = std::max(1, static_cast(std::lround(pixel_h_f))); + const std::uint64_t key = MakeGlyphKey(code, pixel_h); + + GlyphEntry* ge = nullptr; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; + } + if (!ge) { + const auto set_size = [&](float h) -> bool { + const auto char_h = static_cast(static_cast(h * 64.0f)); + return FT_Set_Char_Size(resolved_face, 0, char_h, 72, 72) == 0; + }; + if (!set_size(pixel_h_f)) { + return nullptr; + } + + if (FT_Load_Glyph(resolved_face, resolved_glyph_index, kFtLoadFlagsRender) != 0) { + return nullptr; + } + + const FT_GlyphSlot slot = resolved_face->glyph; + GlyphEntry entry{}; + entry.w = static_cast(slot->bitmap.width); + entry.h = static_cast(slot->bitmap.rows); + entry.x0 = static_cast(slot->bitmap_left); + entry.y0 = -static_cast(slot->bitmap_top); + entry.x1 = entry.x0 + entry.w; + entry.y1 = entry.y0 + entry.h; + entry.advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + entry.bearingX = static_cast(slot->bitmap_left); + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } + + face_out = resolved_face; + scale_out = ComputeScaleExtForState(st, resolved_face, pixel_h_f); + return ge; +} + +constexpr SystemFontDefinition kSystemFontDefinitions[] = { + {0x18070043, "FONTSET_SST_STD_EUROPEAN_LIGHT", "SST-Light.otf"}, + {0x18070044, "FONTSET_SST_STD_EUROPEAN", "SST-Roman.otf"}, + {0x18070045, "FONTSET_SST_STD_EUROPEAN_MEDIUM", "SST-Medium.otf"}, + {0x18070047, "FONTSET_SST_STD_EUROPEAN_BOLD", "SST-Bold.otf"}, + {0x18070053, "FONTSET_SST_STD_VIETNAMESE_LIGHT", "SSTVietnamese-Light.otf"}, + {0x18070054, "FONTSET_SST_STD_VIETNAMESE", "SSTVietnamese-Roman.otf"}, + {0x18070055, "FONTSET_SST_STD_VIETNAMESE_MEDIUM", "SSTVietnamese-Medium.otf"}, + {0x18070057, "FONTSET_SST_STD_VIETNAMESE_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180700C3, "FONTSET_SST_STD_EUROPEAN_AR_LIGHT", "SSTArabic-Light.otf"}, + {0x180700C4, "FONTSET_SST_STD_EUROPEAN_AR", "SSTArabic-Roman.otf"}, + {0x180700C5, "FONTSET_SST_STD_EUROPEAN_AR_MEDIUM", "SSTArabic-Medium.otf"}, + {0x180700C7, "FONTSET_SST_STD_EUROPEAN_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18070444, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, + {0x18070447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x18070454, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, + {0x18070457, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180704C4, "FONTSET_SST_STD_EUROPEAN_JP_AR", "SSTArabic-Roman.otf"}, + {0x180704C7, "FONTSET_SST_STD_EUROPEAN_JP_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18071053, "FONTSET_SST_STD_THAI_LIGHT", "SSTThai-Light.otf"}, + {0x18071054, "FONTSET_SST_STD_THAI", "SSTThai-Roman.otf"}, + {0x18071055, "FONTSET_SST_STD_THAI_MEDIUM", "SSTThai-Medium.otf"}, + {0x18071057, "FONTSET_SST_STD_THAI_BOLD", "SSTThai-Bold.otf"}, + {0x18071454, "FONTSET_SST_STD_EUROPEAN_JP_TH", "SSTThai-Roman.otf"}, + {0x18071457, "FONTSET_SST_STD_EUROPEAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, + {0x18072444, "FONTSET_SST_STD_EUROPEAN_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x18072447, "FONTSET_SST_STD_EUROPEAN_JPUH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180724C4, "FONTSET_SST_STD_EUROPEAN_JPUH_AR", "SSTArabic-Roman.otf"}, + {0x180724C7, "FONTSET_SST_STD_EUROPEAN_JPUH_AR_BOLD", "SSTArabic-Bold.otf"}, + {0x18073454, "FONTSET_SST_STD_EUROPEAN_JPUH_TH", "SSTThai-Roman.otf"}, + {0x18073457, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, + {0x180734D4, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, + {0x180734D7, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, + {0x18078044, "FONTSET_SST_STD_EUROPEAN_GB", "DFHEI5-SONY.ttf"}, + {0x180780C4, "FONTSET_SST_STD_EUROPEAN_GB_AR", "SSTArabic-Roman.otf"}, + {0x18079054, "FONTSET_SST_STD_EUROPEAN_GB_TH", "SSTThai-Roman.otf"}, + {0x1807A044, "FONTSET_SST_STD_EUROPEAN_GBUH", "e046323ms.ttf"}, + {0x1807A0C4, "FONTSET_SST_STD_EUROPEAN_GBUH_AR", "SSTArabic-Bold.otf"}, + {0x1807A444, "FONTSET_SST_STD_EUROPEAN_JPCJK", "SSTJpPro-Regular.otf"}, + {0x1807A4C4, "FONTSET_SST_STD_EUROPEAN_JPCJK_AR", "SSTArabic-Roman.otf"}, + {0x1807AC44, "FONTSET_SST_STD_EUROPEAN_GBCJK", "n023055ms.ttf"}, + {0x1807ACC4, "FONTSET_SST_STD_EUROPEAN_GBCJK_AR", "SSTArabic-Bold.otf"}, + {0x1807B054, "FONTSET_SST_STD_EUROPEAN_GBUH_TH", "SSTThai-Roman.otf"}, + {0x1807B0D4, "FONTSET_SST_STD_EUROPEAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, + {0x1807B454, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH", "SSTThai-Roman.otf"}, + {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "SSTThai-Roman.otf"}, + {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTAribStdB24-Regular.ttf"}, + {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTAribStdB24-Regular.ttf"}, + {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTVietnamese-Roman.otf"}, + {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTVietnamese-Bold.otf"}, + {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTAribStdB24-Regular.ttf"}, + {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTThai-Roman.otf"}, + {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, + {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x18082447, "FONTSET_SST_STD_JAPANESE_JPUH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180824C4, "FONTSET_SST_STD_JAPANESE_JPUH_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180824C7, "FONTSET_SST_STD_JAPANESE_JPUH_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18083454, "FONTSET_SST_STD_ASIAN_JPUH_TH", "SSTThai-Roman.otf"}, + {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, + {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, + {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, + {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTAribStdB24-Regular.ttf"}, + {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Bold.otf"}, + {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTThai-Roman.otf"}, + {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x180C8044, "FONTSET_SST_STD_SCHINESE_GB", "DFHEI5-SONY.ttf"}, + {0x180C80C4, "FONTSET_SST_STD_SCHINESE_GB_AR", "DFHEI5-SONY.ttf"}, + {0x180C9054, "FONTSET_SST_STD_ASIAN_GB_TH", "SSTThai-Roman.otf"}, + {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "e046323ms.ttf"}, + {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "e046323ms.ttf"}, + {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "n023055ms.ttf"}, + {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "SSTThai-Roman.otf"}, + {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, + {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "SSTThai-Roman.otf"}, + {0x180CBCD4, "FONTSET_SST_STD_ASIAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x18170043, "FONTSET_SST_STD_EUROPEAN_LIGHT_ITALIC", "SST-LightItalic.otf"}, + {0x18170044, "FONTSET_SST_STD_EUROPEAN_ITALIC", "SST-Italic.otf"}, + {0x18170045, "FONTSET_SST_STD_EUROPEAN_MEDIUM_ITALIC", "SST-MediumItalic.otf"}, + {0x18170047, "FONTSET_SST_STD_EUROPEAN_BOLD_ITALIC", "SST-BoldItalic.otf"}, + {0x18170444, "FONTSET_SST_STD_EUROPEAN_JP_ITALIC", "SSTJpPro-Regular.otf"}, + {0x18170447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD_ITALIC", "SSTJpPro-Bold.otf"}, + {0x18370044, "FONTSET_SST_TYPEWRITER_EUROPEAN", "SSTTypewriter-Roman.otf"}, + {0x18370047, "FONTSET_SST_TYPEWRITER_EUROPEAN_BOLD", "SSTTypewriter-Bd.otf"}, + {0x18370444, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP", "SSTTypewriter-Roman.otf"}, + {0x18370447, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP_BOLD", "SSTTypewriter-Bd.otf"}, +}; + +std::mutex g_fontset_cache_mutex; +std::unordered_map g_fontset_cache; + +const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type) { + for (const auto& def : kSystemFontDefinitions) { + if (def.font_set_type == font_set_type) { + return &def; + } + } + return nullptr; +} + +static bool DirectoryContainsAnyFontFiles(const std::filesystem::path& dir) { + std::error_code ec; + if (!std::filesystem::is_directory(dir, ec)) { + return false; + } + for (const auto& entry : std::filesystem::directory_iterator(dir, ec)) { + if (ec) { + return false; + } + if (!entry.is_regular_file(ec) || ec) { + continue; + } + const auto ext = entry.path().extension().string(); + std::string lower; + lower.reserve(ext.size()); + for (const char c : ext) { + lower.push_back(static_cast(std::tolower(static_cast(c)))); + } + if (lower == ".otf" || lower == ".ttf" || lower == ".ttc" || lower == ".otc") { + return true; + } + } + return false; +} + +static std::optional FindChildDirContainingFile( + const std::filesystem::path& base_dir, const std::string& filename) { + if (filename.empty()) { + return std::nullopt; + } + + std::error_code ec; + if (!std::filesystem::is_directory(base_dir, ec)) { + return std::nullopt; + } + + std::optional match; + for (const auto& entry : std::filesystem::directory_iterator(base_dir, ec)) { + if (ec) { + return std::nullopt; + } + if (!entry.is_directory(ec) || ec) { + continue; + } + const auto candidate = entry.path() / filename; + if (std::filesystem::is_regular_file(candidate, ec) && !ec) { + if (match) { + return std::nullopt; + } + match = entry.path(); + } + } + return match; +} + +std::filesystem::path GetSysFontBaseDir() { + std::filesystem::path base = Config::getSysFontPath(); + std::error_code ec; + if (base.empty()) { + LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); + return {}; + } + if (std::filesystem::is_directory(base, ec)) { + if (DirectoryContainsAnyFontFiles(base)) { + return base; + } + + { + const auto preferred = base / "font"; + if (DirectoryContainsAnyFontFiles(preferred)) { + return preferred; + } + } + + const std::string fallback = Config::getSystemFontFallbackName(); + if (auto child = FindChildDirContainingFile(base, fallback)) { + return *child; + } + + std::optional sole_font_dir; + for (const auto& entry : std::filesystem::directory_iterator(base, ec)) { + if (ec) { + break; + } + if (!entry.is_directory(ec) || ec) { + continue; + } + if (DirectoryContainsAnyFontFiles(entry.path())) { + if (sole_font_dir) { + sole_font_dir.reset(); + break; + } + sole_font_dir = entry.path(); + } + } + if (sole_font_dir) { + return *sole_font_dir; + } + + LOG_ERROR( + Lib_Font, + "SystemFonts: SysFontPath '{}' contains no font files; set it to the directory that " + "contains the .otf/.ttf files (or ensure [SystemFonts].fallback is present in exactly " + "one child directory)", + base.string()); + return {}; + } + if (std::filesystem::is_regular_file(base, ec)) { + return base.parent_path(); + } + LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath '{}' is not a valid directory or file", + base.string()); + return {}; +} + +std::string MacroToCamel(const char* macro_key) { + if (!macro_key) { + return {}; + } + std::string s(macro_key); + const std::string prefix = "FONTSET_"; + if (s.rfind(prefix, 0) != 0) { + return {}; + } + std::string out = "FontSet"; + size_t pos = prefix.size(); + while (pos < s.size()) { + size_t next = s.find('_', pos); + const size_t len = (next == std::string::npos) ? (s.size() - pos) : (next - pos); + std::string token = s.substr(pos, len); + for (auto& c : token) { + c = static_cast(std::tolower(static_cast(c))); + } + if (!token.empty()) { + token[0] = static_cast(std::toupper(static_cast(token[0]))); + } + out += token; + if (next == std::string::npos) { + break; + } + pos = next + 1; + } + return out; +} + +std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { + if (const auto* def = FindSystemFontDefinition(font_set_type); def) { + const auto base_dir = GetSysFontBaseDir(); + if (base_dir.empty()) { + return {}; + } + if (auto override_path = Config::getSystemFontOverride(def->config_key)) { + if (!override_path->empty() && !override_path->is_absolute() && + !override_path->has_parent_path()) { + return base_dir / *override_path; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + def->config_key, override_path->string()); + } + const auto camel_key = MacroToCamel(def->config_key); + if (!camel_key.empty()) { + if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { + if (!override_path2->empty() && !override_path2->is_absolute() && + !override_path2->has_parent_path()) { + return base_dir / *override_path2; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + camel_key, override_path2->string()); + } + std::string lower_camel = camel_key; + lower_camel[0] = + static_cast(std::tolower(static_cast(lower_camel[0]))); + if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { + if (!override_path3->empty() && !override_path3->is_absolute() && + !override_path3->has_parent_path()) { + return base_dir / *override_path3; + } + LOG_ERROR(Lib_Font, + "SystemFonts: override for '{}' must be a filename only (no path): '{}'", + lower_camel, override_path3->string()); + } + } + if (def->default_file && *def->default_file) { + return base_dir / def->default_file; + } + } + LOG_ERROR(Lib_Font, "SystemFonts: unknown font set type=0x{:08X}", font_set_type); + return {}; +} + +std::filesystem::path ResolveSystemFontPathFromConfigOnly(u32 font_set_type) { + const auto base_dir = GetSysFontBaseDir(); + if (base_dir.empty()) { + return {}; + } + + const std::string key_a = fmt::format("fontset_0x{:08X}", font_set_type); + const std::string key_b = fmt::format("fontSet_0x{:08X}", font_set_type); + + auto try_key = [&](const std::string& key) -> std::optional { + if (auto override_path = Config::getSystemFontOverride(key)) { + if (!override_path->empty() && override_path->is_absolute()) { + return *override_path; + } + if (!override_path->empty() && !override_path->has_parent_path()) { + return base_dir / *override_path; + } + LOG_ERROR( + Lib_Font, + "SystemFonts: override for '{}' must be a filename only or absolute path: '{}'", + key, override_path->string()); + } + return std::nullopt; + }; + + if (auto p = try_key(key_a)) { + return *p; + } + if (auto p = try_key(key_b)) { + return *p; + } + + const std::string fallback = Config::getSystemFontFallbackName(); + if (!fallback.empty()) { + return base_dir / fallback; + } + return {}; +} + +const FontSetCache* EnsureFontSetCache(u32 font_set_type) { + if (font_set_type == 0) { + return nullptr; + } + std::scoped_lock lock(g_fontset_cache_mutex); + auto& cache = g_fontset_cache[font_set_type]; + if (!cache.loaded) { + cache.loaded = true; + const auto path = ResolveSystemFontPath(font_set_type); + if (!path.empty() && LoadFontFile(path, cache.bytes)) { + cache.available = HasSfntTables(cache.bytes); + cache.path = cache.available ? path : std::filesystem::path{}; + } else { + cache.available = false; + LOG_ERROR(Lib_Font, "SystemFonts: failed to load font for set type=0x{:08X} path='{}'", + font_set_type, path.string()); + } + } + return cache.available ? &cache : nullptr; +} + +bool HasSfntTables(const std::vector& bytes) { + if (bytes.size() < 12) { + return false; + } + const u8* ptr = bytes.data(); + const u32 num_tables = (ptr[4] << 8) | ptr[5]; + constexpr u32 kDirEntrySize = 16; + if (bytes.size() < 12 + static_cast(num_tables) * kDirEntrySize) { + return false; + } + return true; +} + +bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes) { + if (path.empty()) { + return false; + } + + auto try_load = [&](const std::filesystem::path& p) -> bool { + out_bytes.clear(); + if (!LoadGuestFileBytes(p, out_bytes) || out_bytes.empty()) { + out_bytes.clear(); + return false; + } + return true; + }; + + if (try_load(path)) { + return true; + } + + std::error_code ec; + const auto parent = path.parent_path(); + const auto parent_name = parent.filename().string(); + const auto file_name = path.filename(); + if (!file_name.empty() && (parent_name == "font" || parent_name == "font2")) { + const auto container = parent.parent_path(); + const auto sibling = container / ((parent_name == "font") ? "font2" : "font") / file_name; + if (std::filesystem::is_regular_file(sibling, ec) && !ec) { + return try_load(sibling); + } + } + + return false; +} + +bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle handle) { + if (st.ext_face_ready) { + return true; + } + st.system_fallback_faces.clear(); + st.system_font_id = 0; + st.system_font_scale_factor = 1.0f; + st.system_font_shift_value = 0; + + const auto* native = reinterpret_cast(handle); + const u32 subfont_index = native ? native->open_info.sub_font_index : 0u; + auto* lib = static_cast(st.library); + if (!lib || lib->magic != 0x0F01 || st.font_set_type == 0) { + return false; + } + + std::filesystem::path primary_path = ResolveSystemFontPathFromConfigOnly(st.font_set_type); + if (primary_path.empty()) { + primary_path = ResolveSystemFontPath(st.font_set_type); + } + std::vector primary_bytes; + if (primary_path.empty() || !LoadFontFile(primary_path, primary_bytes)) { + return false; + } + + DestroyFreeTypeFace(st.ext_ft_face); + st.ext_face_data = std::move(primary_bytes); + st.ext_ft_face = CreateFreeTypeFaceFromBytes(st.ext_face_data.data(), st.ext_face_data.size(), + subfont_index); + if (!st.ext_ft_face) { + st.ext_face_data.clear(); + return false; + } + + st.ext_cache.clear(); + st.scratch.clear(); + st.logged_ext_use = false; + st.ext_scale_for_height = 0.0f; + st.layout_cached = false; + const FaceMetrics m = LoadFaceMetrics(st.ext_ft_face); + PopulateStateMetrics(st, m); + st.ext_face_ready = true; + st.system_font_path = primary_path; + st.system_requested = true; + + const auto base_dir = GetSysFontBaseDir(); + if (!base_dir.empty()) { + auto resolve_override = + [&](const std::string& key) -> std::optional { + if (auto override_path = Config::getSystemFontOverride(key)) { + if (!override_path->empty() && override_path->is_absolute()) { + return *override_path; + } + if (!override_path->empty() && !override_path->has_parent_path()) { + return base_dir / *override_path; + } + } + return std::nullopt; + }; + + for (int i = 0; i < 8; ++i) { + const std::string key_a = + fmt::format("fontset_0x{:08X}_fallback{}", st.font_set_type, i); + const std::string key_b = + fmt::format("fontSet_0x{:08X}_fallback{}", st.font_set_type, i); + std::optional p = resolve_override(key_a); + if (!p) { + p = resolve_override(key_b); + } + if (!p || p->empty()) { + continue; + } + + std::vector fb_bytes; + if (!LoadFontFile(*p, fb_bytes)) { + continue; + } + FontState::SystemFallbackFace fb{}; + fb.font_id = static_cast(i + 1); + fb.scale_factor = 1.0f; + fb.shift_value = 0; + fb.path = *p; + fb.bytes = std::make_shared>(std::move(fb_bytes)); + fb.ft_face = + CreateFreeTypeFaceFromBytes(fb.bytes->data(), fb.bytes->size(), subfont_index); + fb.ready = (fb.ft_face != nullptr); + if (fb.ready) { + st.system_fallback_faces.push_back(std::move(fb)); + } else { + DestroyFreeTypeFace(fb.ft_face); + } + } + + const std::string global_fallback_name = Config::getSystemFontFallbackName(); + if (!global_fallback_name.empty()) { + const auto existing_name = primary_path.filename().string(); + if (existing_name != global_fallback_name) { + const std::filesystem::path fb_path = base_dir / global_fallback_name; + std::vector fb_bytes; + if (LoadFontFile(fb_path, fb_bytes)) { + FontState::SystemFallbackFace fb{}; + fb.font_id = 0xFFFFFFFFu; + fb.scale_factor = 1.0f; + fb.shift_value = 0; + fb.path = fb_path; + fb.bytes = std::make_shared>(std::move(fb_bytes)); + fb.ft_face = CreateFreeTypeFaceFromBytes(fb.bytes->data(), fb.bytes->size(), + subfont_index); + fb.ready = (fb.ft_face != nullptr); + if (fb.ready) { + st.system_fallback_faces.push_back(std::move(fb)); + } else { + DestroyFreeTypeFace(fb.ft_face); + } + } + } + } + } + + LOG_INFO(Lib_Font, "system font attached"); + LOG_DEBUG(Lib_Font, + "system font attach params:\n" + " handle={}\n" + " font_set_type=0x{:08X}\n" + " path={}\n", + static_cast(handle), st.font_set_type, st.system_font_path.string()); + return true; +} + +std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, + bool& attached_out) { + attached_out = AttachSystemFont(st, handle); + if (attached_out) { + st.system_requested = true; + return {}; + } + if (!st.system_requested) { + st.system_requested = true; + const auto configured = Config::getSysFontPath(); + return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " + "could not be loaded", + static_cast(handle), configured.string()); + } + return {}; +} + +} // namespace Libraries::Font::Internal diff --git a/src/core/libraries/font/font_internal.h b/src/core/libraries/font/font_internal.h new file mode 100644 index 000000000..d1a12609c --- /dev/null +++ b/src/core/libraries/font/font_internal.h @@ -0,0 +1,760 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include FT_FREETYPE_H + +#include "common/config.h" +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/file_sys/fs.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/font/font.h" +#include "core/libraries/libs.h" +#include "font_error.h" + +namespace Libraries::Font::Internal { + +struct FontLibOpaque; +struct OrbisFontRenderer_; +struct FontSetCache; + +template +std::string DescribeValue(const T& value) { + using Clean = std::remove_cvref_t; + if constexpr (std::is_same_v) { + return value ? "true" : "false"; + } else if constexpr (std::is_pointer_v) { + if constexpr (std::is_same_v || std::is_same_v) { + return value ? fmt::format("\"{}\"", value) : "(null)"; + } + return fmt::format("{}", reinterpret_cast(value)); + } else if constexpr (std::is_floating_point_v) { + return fmt::format("{:.6g}", value); + } else if constexpr (std::is_enum_v) { + using Underlying = std::underlying_type_t; + return DescribeValue(static_cast(value)); + } else if constexpr (std::is_same_v || + std::is_same_v) { + return fmt::format("\"{}\"", value); + } else { + return fmt::format("{}", value); + } +} + +struct ParamRecord { + std::string name; + std::string initial; + std::string current; +}; + +template +struct ChangeTracked { + T before; + T after; +}; + +template +struct is_change_tracked : std::false_type {}; + +template +struct is_change_tracked> : std::true_type { + using value_type = T; +}; + +template +ChangeTracked trackChange(T before, T after) { + return ChangeTracked{before, after}; +} + +inline std::string TrimName(std::string_view name) { + const auto start = name.find_first_not_of(" \t\n\r"); + if (start == std::string_view::npos) { + return {}; + } + const auto end = name.find_last_not_of(" \t\n\r"); + return std::string{name.substr(start, end - start + 1)}; +} + +inline std::vector SplitArgumentNames(std::string_view names) { + std::vector result; + std::string current; + int depth = 0; + for (char c : names) { + if (c == ',' && depth == 0) { + result.push_back(TrimName(current)); + current.clear(); + continue; + } + if (c == '(' || c == '{' || c == '[') { + ++depth; + } else if (c == ')' || c == '}' || c == ']') { + depth = std::max(0, depth - 1); + } + current.push_back(c); + } + if (!current.empty() || + (!names.empty() && names.find_first_not_of(" \t\n\r") != std::string_view::npos)) { + result.push_back(TrimName(current)); + } + return result; +} + +template +ParamRecord MakeParamRecord(std::string name, T&& value) { + using Clean = std::remove_cvref_t; + if constexpr (is_change_tracked::value) { + using ValueType = typename Clean::value_type; + return ParamRecord{std::move(name), DescribeValue(value.before), + DescribeValue(value.after)}; + } else { + const std::string rendered = DescribeValue(value); + return ParamRecord{std::move(name), rendered, rendered}; + } +} + +inline std::string FormatParamRecords(const std::vector& records) { + fmt::memory_buffer buffer; + fmt::format_to(std::back_inserter(buffer), "params:\n"); + for (const auto& entry : records) { + const bool changed = entry.initial != entry.current; + if (changed) { + fmt::format_to(std::back_inserter(buffer), "{}: {} -> {}\n", entry.name, entry.initial, + entry.current); + } else { + fmt::format_to(std::back_inserter(buffer), "{}: {}\n", entry.name, entry.initial); + } + } + return fmt::to_string(buffer); +} + +template +std::string formatParamsImpl(const char* arg_names, Args&&... args) { + std::vector names = SplitArgumentNames(arg_names ? arg_names : ""); + std::vector records; + records.reserve(sizeof...(Args)); + std::size_t index = 0; + auto append_record = [&](auto&& value) { + std::string name = (index < names.size() && !names[index].empty()) + ? std::move(names[index]) + : fmt::format("arg{}", index); + ++index; + records.push_back(MakeParamRecord(std::move(name), std::forward(value))); + }; + (append_record(std::forward(args)), ...); + return FormatParamRecords(records); +} + +inline void log_info(std::string_view message) { + LOG_INFO(Lib_Font, "{}", message); +} + +inline void log_debug(std::string_view message) { + LOG_DEBUG(Lib_Font, "{}", message); +} + +#define formatParams(...) formatParamsImpl(#__VA_ARGS__ __VA_OPT__(, ) __VA_ARGS__) + +struct FontSetSelector; + +struct GlyphEntry { + std::vector bitmap; + int w = 0; + int h = 0; + int x0 = 0; + int y0 = 0; + int x1 = 0; + int y1 = 0; + float advance = 0.0f; + float bearingX = 0.0f; +}; + +struct FontState { + float scale_w = 16.0f; + float scale_h = 16.0f; + float scale_point_w = 12.0f; + float scale_point_h = 12.0f; + bool scale_point_active = false; + float render_scale_w = 16.0f; + float render_scale_h = 16.0f; + float render_scale_point_w = 12.0f; + float render_scale_point_h = 12.0f; + bool render_scale_point_active = false; + u32 dpi_x = 72; + u32 dpi_y = 72; + u32 font_set_type = 0; + std::filesystem::path system_font_path; + u32 system_font_id = 0; + float system_font_scale_factor = 1.0f; + s32 system_font_shift_value = 0; + struct SystemFallbackFace { + u32 font_id = 0; + float scale_factor = 1.0f; + s32 shift_value = 0; + std::filesystem::path path; + std::shared_ptr> bytes; + FT_Face ft_face = nullptr; + bool ready = false; + }; + std::vector system_fallback_faces; + Libraries::Font::OrbisFontLib library = nullptr; + bool ext_face_ready = false; + std::vector ext_face_data; + FT_Face ext_ft_face = nullptr; + float ext_scale_for_height = 0.0f; + int ext_ascent = 0, ext_descent = 0, ext_lineGap = 0; + int ext_units_per_em = 0; + int ext_typo_ascent = 0, ext_typo_descent = 0, ext_typo_lineGap = 0; + bool ext_use_typo_metrics = false; + std::unordered_map ext_cache; + std::vector scratch; + bool logged_ext_use = false; + float cached_baseline_y = 0.0f; + bool layout_cached = false; + Libraries::Font::OrbisFontRenderer bound_renderer = nullptr; + bool system_requested = false; + std::shared_ptr> fontset_record_storage; + std::shared_ptr fontset_selector; + float effect_weight_x = 1.0f; + float effect_weight_y = 1.0f; + u32 effect_weight_mode = 0; + float effect_slant = 0.0f; + float render_effect_weight_x = 1.0f; + float render_effect_weight_y = 1.0f; + u32 render_effect_weight_mode = 0; + float render_effect_slant = 0.0f; + + ~FontState(); +}; + +struct LibraryState { + bool support_system = false; + bool support_external = false; + u32 external_formats = 0; + u32 external_fontMax = 0; + const Libraries::Font::OrbisFontMem* backing_memory = nullptr; + Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; + u64 edition = 0; + FontLibOpaque* native = nullptr; + void* owned_device_cache = nullptr; + u32 owned_device_cache_size = 0; +}; + +struct FaceMetrics { + int units_per_em = 0; + int hhea_ascent = 0; + int hhea_descent = 0; + int hhea_lineGap = 0; + int typo_ascent = 0; + int typo_descent = 0; + int typo_lineGap = 0; + bool has_typo = false; + bool use_typo = false; +}; + +struct GeneratedGlyph { + OrbisFontGlyphOpaque glyph{}; + OrbisFontGlyphMetrics metrics{}; + OrbisFontGlyphMetricsHorizontal metrics_horizontal{}; + OrbisFontGlyphMetricsHorizontalX metrics_horizontal_x{}; + OrbisFontGlyphMetricsHorizontalAdvance metrics_horizontal_adv{}; + float origin_x = 0.0f; + float origin_y = 0.0f; + float scale_x_used = 0.0f; + float scale_y_used = 0.0f; + int bbox_x0 = 0; + int bbox_y0 = 0; + int bbox_w = 0; + int bbox_h = 0; + u32 codepoint = 0; + Libraries::Font::OrbisFontHandle owner_handle{}; + OrbisFontGlyphOutline outline{}; + std::vector outline_points; + std::vector outline_tags; + std::vector outline_contours; + bool metrics_initialized = false; + bool outline_initialized = false; +}; + +struct SysDriver { + using PixelResFn = u32(PS4_SYSV_ABI*)(); + using InitFn = s32(PS4_SYSV_ABI*)(const void* memory, void* library); + using TermFn = s32(PS4_SYSV_ABI*)(void* library); + using SupportFn = s32(PS4_SYSV_ABI*)(void* library, u32 formats); + using OpenFn = s32(PS4_SYSV_ABI*)(void* library, u32 mode, const void* fontAddress, + u32 fontSize, u32 subFontIndex, u32 uniqueWord, + void** inoutFontObj); + using CloseFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 flags); + using ScaleFn = s32(PS4_SYSV_ABI*)(void* fontObj, u16* outUnitsPerEm, float* outScale); + using MetricFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 metricId, u16* outMetric); + using GlyphsCountFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32* out_count); + using GlyphIndexFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 codepoint_u16, u32* out_glyph_index); + + using SetCharWithDpiFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 dpi_x, u32 dpi_y, float scale_x, + float scale_y, float* out_scale_x, + float* out_scale_y); + using SetCharDefaultDpiFn = s32(PS4_SYSV_ABI*)(void* fontObj, float scale_x, float scale_y, + float* out_scale_x, float* out_scale_y); + using ComputeLayoutFn = s32(PS4_SYSV_ABI*)(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]); + using ComputeLayoutAltFn = s32(PS4_SYSV_ABI*)(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]); + + using LoadGlyphCachedFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 glyphIndex, s32 mode, + std::uint64_t* out_words); + using GetGlyphMetricsFn = s32(PS4_SYSV_ABI*)( + void* fontObj, std::uint32_t* opt_param2, std::uint8_t mode, std::uint8_t* out_params, + Libraries::Font::OrbisFontGlyphMetrics* out_metrics); + using ApplyGlyphAdjustFn = s32(PS4_SYSV_ABI*)(void* fontObj, u32 p2, u32 glyphIndex, s32 p4, + s32 p5, u32* inoutGlyphIndex); + using ConfigureGlyphFn = s32(PS4_SYSV_ABI*)(void* fontObj, std::uint32_t* in_params, s32 mode, + std::uint32_t* inout_state); + + /*0x00*/ u8 reserved_00[0x10]; + /*0x10*/ PixelResFn pixel_resolution; + /*0x18*/ InitFn init; + /*0x20*/ TermFn term; + /*0x28*/ SupportFn support_formats; + /*0x30*/ u8 reserved_30[0x08]; + /*0x38*/ OpenFn open; + /*0x40*/ CloseFn close; + /*0x48*/ u8 reserved_48[0x08]; + /*0x50*/ ScaleFn scale; + /*0x58*/ u8 reserved_58[0x08]; + /*0x60*/ MetricFn metric; + /*0x68*/ GlyphsCountFn glyphs_count; + /*0x70*/ u8 reserved_70[0x08]; + /*0x78*/ GlyphIndexFn glyph_index; + /*0x80*/ SetCharWithDpiFn set_char_with_dpi; + /*0x88*/ SetCharDefaultDpiFn set_char_default_dpi; + /*0x90*/ u8 reserved_90[0x10]; + /*0xA0*/ ComputeLayoutFn compute_layout; + /*0xA8*/ LoadGlyphCachedFn load_glyph_cached; + /*0xB0*/ u8 reserved_B0[0x08]; + /*0xB8*/ GetGlyphMetricsFn get_glyph_metrics; + /*0xC0*/ u8 reserved_C0[0x20]; + /*0xE0*/ ApplyGlyphAdjustFn apply_glyph_adjust; + /*0xE8*/ u8 reserved_E8[0x20]; + /*0x108*/ ConfigureGlyphFn configure_glyph; + /*0x110*/ u8 reserved_0x110_pad[0x08]; + /*0x118*/ ComputeLayoutAltFn compute_layout_alt; +}; +static_assert(sizeof(SysDriver) == 0x120, "SysDriver size"); +static_assert(offsetof(SysDriver, pixel_resolution) == 0x10, "SysDriver pixel_resolution offset"); +static_assert(offsetof(SysDriver, init) == 0x18, "SysDriver init offset"); +static_assert(offsetof(SysDriver, term) == 0x20, "SysDriver term offset"); +static_assert(offsetof(SysDriver, support_formats) == 0x28, "SysDriver support_formats offset"); +static_assert(offsetof(SysDriver, open) == 0x38, "SysDriver open offset"); +static_assert(offsetof(SysDriver, close) == 0x40, "SysDriver close offset"); +static_assert(offsetof(SysDriver, scale) == 0x50, "SysDriver scale offset"); +static_assert(offsetof(SysDriver, metric) == 0x60, "SysDriver metric offset"); +static_assert(offsetof(SysDriver, glyphs_count) == 0x68, "SysDriver glyphs_count offset"); +static_assert(offsetof(SysDriver, glyph_index) == 0x78, "SysDriver glyph_index offset"); +static_assert(offsetof(SysDriver, set_char_with_dpi) == 0x80, "SysDriver set_char_with_dpi offset"); +static_assert(offsetof(SysDriver, set_char_default_dpi) == 0x88, + "SysDriver set_char_default_dpi offset"); +static_assert(offsetof(SysDriver, compute_layout) == 0xA0, "SysDriver compute_layout offset"); +static_assert(offsetof(SysDriver, load_glyph_cached) == 0xA8, "SysDriver load_glyph_cached offset"); +static_assert(offsetof(SysDriver, get_glyph_metrics) == 0xB8, "SysDriver get_glyph_metrics offset"); +static_assert(offsetof(SysDriver, apply_glyph_adjust) == 0xE0, + "SysDriver apply_glyph_adjust offset"); +static_assert(offsetof(SysDriver, configure_glyph) == 0x108, "SysDriver configure_glyph offset"); +static_assert(offsetof(SysDriver, compute_layout_alt) == 0x118, + "SysDriver compute_layout_alt offset"); +struct StyleStateBlock { + /*0x00*/ u32 dpi_x; + /*0x04*/ u32 dpi_y; + /*0x08*/ u32 scale_unit; + /*0x0C*/ u32 reserved_0x0c; + /*0x10*/ float scale_w; + /*0x14*/ float scale_h; + /*0x18*/ float effect_weight_x; + /*0x1C*/ float effect_weight_y; + /*0x20*/ float slant_ratio; + /*0x24*/ float reserved_0x24; +}; +static_assert(sizeof(StyleStateBlock) == 0x28, "StyleStateBlock size"); +static_assert(offsetof(StyleStateBlock, dpi_x) == 0x00, "StyleStateBlock dpi_x offset"); +static_assert(offsetof(StyleStateBlock, dpi_y) == 0x04, "StyleStateBlock dpi_y offset"); +static_assert(offsetof(StyleStateBlock, scale_unit) == 0x08, "StyleStateBlock scale_unit offset"); +static_assert(offsetof(StyleStateBlock, scale_w) == 0x10, "StyleStateBlock scale_w offset"); +static_assert(offsetof(StyleStateBlock, scale_h) == 0x14, "StyleStateBlock scale_h offset"); +static_assert(offsetof(StyleStateBlock, effect_weight_x) == 0x18, + "StyleStateBlock effect_weight_x offset"); +static_assert(offsetof(StyleStateBlock, effect_weight_y) == 0x1C, + "StyleStateBlock effect_weight_y offset"); +static_assert(offsetof(StyleStateBlock, slant_ratio) == 0x20, "StyleStateBlock slant_ratio offset"); + +struct FontSetSelector { + static constexpr u32 kMagic = 0x53464C42u; + + struct Candidate { + u32 font_id = 0xffffffffu; + FT_Face face = nullptr; + }; + + u32 magic = kMagic; + u32 font_set_type = 0; + void* library = nullptr; + u32 mode_low = 0; + u32 sub_font_index = 0; + u32 primary_font_id = 0xffffffffu; + u32 roman_font_id = 0xffffffffu; + u32 arabic_font_id = 0xffffffffu; + std::vector candidates; +}; + +struct FontSetRecordHeader { + /*0x00*/ u32 font_set_type; + /*0x04*/ u8 reserved_0x04[0x18]; + /*0x1C*/ u32 magic; + /*0x20*/ u32 entry_count; +}; +static_assert(sizeof(FontSetRecordHeader) == 0x24, "FontSetRecordHeader size"); +static_assert(offsetof(FontSetRecordHeader, font_set_type) == 0x00, + "FontSetRecordHeader font_set_type offset"); +static_assert(offsetof(FontSetRecordHeader, magic) == 0x1C, "FontSetRecordHeader magic offset"); +static_assert(offsetof(FontSetRecordHeader, entry_count) == 0x20, + "FontSetRecordHeader entry_count offset"); + +struct FontSetRecordView { + const FontSetRecordHeader* header = nullptr; + const u32* entry_indices = nullptr; + const FontSetSelector* selector = nullptr; +}; + +inline std::size_t FontSetRecordSelectorOffset(u32 entry_count) { + const std::size_t indices_bytes = static_cast(entry_count) * sizeof(u32); + const std::size_t ptr_off_unaligned = sizeof(FontSetRecordHeader) + indices_bytes; + const std::size_t ptr_align = alignof(const FontSetSelector*); + return (ptr_off_unaligned + (ptr_align - 1u)) & ~(ptr_align - 1u); +} + +inline FontSetRecordView MakeFontSetRecordView(const FontSetRecordHeader* record) { + if (!record) { + return {}; + } + FontSetRecordView view{}; + view.header = record; + view.entry_indices = reinterpret_cast(static_cast(record + 1)); + const std::size_t ptr_off = FontSetRecordSelectorOffset(record->entry_count); + view.selector = *reinterpret_cast( + static_cast(reinterpret_cast(record) + ptr_off)); + return view; +} + +struct FontCtxHeader { + /*0x00*/ u32 lock_word; + /*0x04*/ u32 max_entries; + /*0x08*/ void* base; + /*0x10*/ u8 reserved_0x10[0x10]; +}; +static_assert(sizeof(FontCtxHeader) == 0x20, "FontCtxHeader size"); +static_assert(offsetof(FontCtxHeader, lock_word) == 0x00, "FontCtxHeader lock_word offset"); +static_assert(offsetof(FontCtxHeader, max_entries) == 0x04, "FontCtxHeader max_entries offset"); +static_assert(offsetof(FontCtxHeader, base) == 0x08, "FontCtxHeader base offset"); + +struct FontCtxEntry { + /*0x00*/ u32 reserved_0x00; + /*0x04*/ u32 active; + /*0x08*/ u64 font_address; + /*0x10*/ u32 unique_id; + /*0x14*/ u32 lock_mode1; + /*0x18*/ u32 lock_mode3; + /*0x1C*/ u32 lock_mode2; + /*0x20*/ void* obj_mode1; + /*0x28*/ void* obj_mode3; + /*0x30*/ void* obj_mode2; + /*0x38*/ u8 reserved_0x38[0x08]; +}; +static_assert(sizeof(FontCtxEntry) == 0x40, "FontCtxEntry size"); +static_assert(offsetof(FontCtxEntry, lock_mode1) == 0x14, "FontCtxEntry lock_mode1 offset"); +static_assert(offsetof(FontCtxEntry, lock_mode3) == 0x18, "FontCtxEntry lock_mode3 offset"); +static_assert(offsetof(FontCtxEntry, lock_mode2) == 0x1C, "FontCtxEntry lock_mode2 offset"); +static_assert(offsetof(FontCtxEntry, obj_mode1) == 0x20, "FontCtxEntry obj_mode1 offset"); +static_assert(offsetof(FontCtxEntry, obj_mode3) == 0x28, "FontCtxEntry obj_mode3 offset"); +static_assert(offsetof(FontCtxEntry, obj_mode2) == 0x30, "FontCtxEntry obj_mode2 offset"); + +struct FontObj { + /*0x00*/ u32 refcount; + /*0x04*/ u32 reserved_0x04; + /*0x08*/ u32 reserved_0x08; + /*0x0C*/ u32 sub_font_index; + /*0x10*/ FontObj* prev; + /*0x18*/ FontObj* next; + /*0x20*/ u8 reserved_0x20[0x08]; + /*0x28*/ void* open_ctx_0x28; + /*0x30*/ void* ft_face; + /*0x38*/ Libraries::Font::OrbisFontHandle font_handle; + /*0x40*/ s32 shift_units_x; + /*0x44*/ s32 shift_units_y; + /*0x48*/ std::uint64_t layout_seed_pair; + /*0x50*/ float scale_x_0x50; + /*0x54*/ float scale_y_0x54; + /*0x58*/ void* ft_ctx_0x58; + /*0x60*/ u8 reserved_0x60[0x04]; + /*0x64*/ s32 cached_glyph_index_0x64; + /*0x68*/ std::uint64_t cached_units_x_0x68; + /*0x70*/ std::uint64_t cached_units_y_0x70; + /*0x78*/ s64 shift_cache_x; + /*0x80*/ s64 shift_cache_y; + /*0x88*/ std::array layout_seed_vec; + /*0x98*/ std::array layout_scale_vec; + /*0xA8*/ u8 reserved_0xA8[0x130 - 0xA8]; + /*0x130*/ u32 glyph_cfg_word_0x130; + /*0x134*/ u8 glyph_cfg_mode_0x134; + /*0x135*/ u8 glyph_cfg_byte_0x135; + /*0x136*/ u8 glyph_cfg_byte_0x136; + /*0x137*/ u8 reserved_0x137[0x200 - 0x137]; +}; +static_assert(offsetof(FontObj, sub_font_index) == 0x0C, "FontObj sub_font_index offset"); +static_assert(offsetof(FontObj, next) == 0x18, "FontObj next offset"); +static_assert(offsetof(FontObj, ft_face) == 0x30, "FontObj ft_face offset"); +static_assert(offsetof(FontObj, font_handle) == 0x38, "FontObj font_handle offset"); +static_assert(offsetof(FontObj, shift_units_x) == 0x40, "FontObj shift_units_x offset"); +static_assert(offsetof(FontObj, shift_units_y) == 0x44, "FontObj shift_units_y offset"); +static_assert(offsetof(FontObj, layout_seed_pair) == 0x48, "FontObj layout_seed_pair offset"); +static_assert(offsetof(FontObj, shift_cache_x) == 0x78, "FontObj shift_cache_x offset"); +static_assert(offsetof(FontObj, shift_cache_y) == 0x80, "FontObj shift_cache_y offset"); +static_assert(offsetof(FontObj, layout_seed_vec) == 0x88, "FontObj layout_seed_vec offset"); +static_assert(offsetof(FontObj, layout_scale_vec) == 0x98, "FontObj layout_scale_vec offset"); +static_assert(offsetof(FontObj, glyph_cfg_word_0x130) == 0x130, "FontObj glyph_cfg_word offset"); +static_assert(sizeof(FontObj) == 0x200, "FontObj size"); + +struct SystemFontDefinition { + u32 font_set_type; + const char* config_key; + const char* default_file; +}; + +struct FontSetCache { + bool loaded = false; + bool available = false; + std::vector bytes; + std::filesystem::path path; +}; + +struct FontLibOpaque { + u16 magic; + u16 reserved0; + u32 lock_word; + u32 flags; + u8 reserved1[0x14]; + void* alloc_ctx; + void** alloc_vtbl; + u8 reserved2[0x50]; + void* sys_driver; + void* fontset_registry; + union { + u8 reserved3[0x10]; + std::array sysfonts_ctx_name_overrides; + }; + void* sysfonts_ctx; + void* external_fonts_ctx; + u32* device_cache_buf; +}; +static_assert(sizeof(FontLibOpaque) == 0xB8, "FontLibOpaque size"); +static_assert(offsetof(FontLibOpaque, alloc_ctx) == 0x20, "FontLibOpaque alloc_ctx offset"); +static_assert(offsetof(FontLibOpaque, alloc_vtbl) == 0x28, "FontLibOpaque alloc_vtbl offset"); +static_assert(offsetof(FontLibOpaque, sys_driver) == 0x80, "FontLibOpaque sys_driver offset"); +static_assert(offsetof(FontLibOpaque, fontset_registry) == 0x88, + "FontLibOpaque fontset_registry offset"); +static_assert(offsetof(FontLibOpaque, sysfonts_ctx) == 0xA0, "FontLibOpaque sysfonts_ctx offset"); +static_assert(offsetof(FontLibOpaque, external_fonts_ctx) == 0xA8, + "FontLibOpaque external_fonts_ctx offset"); +static_assert(offsetof(FontLibOpaque, device_cache_buf) == 0xB0, + "FontLibOpaque device_cache_buf offset"); + +struct RendererOpaque { + /*0x00*/ u16 magic; + /*0x02*/ u16 reserved02; + /*0x04*/ u8 reserved_04[0x08]; + /*0x0C*/ u8 feature_byte_0x0c; + /*0x0D*/ u8 reserved_0d[0x03]; + /*0x10*/ u32 mem_kind; + /*0x14*/ u32 region_size; + /*0x18*/ void* region_base; + /*0x20*/ void* alloc_ctx; + /*0x28*/ const OrbisFontMemInterface* mem_iface; + /*0x30*/ u8 reserved_30[0x20]; + /*0x50*/ std::uintptr_t alloc_fn; + /*0x58*/ std::uintptr_t free_fn; + /*0x60*/ std::uintptr_t realloc_fn; + /*0x68*/ std::uintptr_t calloc_fn; + /*0x70*/ u8 reserved_70[0x10]; + /*0x80*/ void* selection; + /*0x88*/ u8 reserved_88[0x08]; + /*0x90*/ u64 outline_magic_0x90; + /*0x98*/ void* workspace; + /*0xA0*/ u64 workspace_size; + /*0xA8*/ u32 reserved_a8; + /*0xAC*/ u32 outline_policy_flag; +}; +static_assert(sizeof(RendererOpaque) == 0xB0, "RendererOpaque size"); +static_assert(offsetof(RendererOpaque, magic) == 0x00, "RendererOpaque magic offset"); +static_assert(offsetof(RendererOpaque, feature_byte_0x0c) == 0x0C, + "RendererOpaque feature byte offset"); +static_assert(offsetof(RendererOpaque, mem_kind) == 0x10, "RendererOpaque mem_kind offset"); +static_assert(offsetof(RendererOpaque, region_size) == 0x14, "RendererOpaque region_size offset"); +static_assert(offsetof(RendererOpaque, alloc_ctx) == 0x20, "RendererOpaque alloc_ctx offset"); +static_assert(offsetof(RendererOpaque, mem_iface) == 0x28, "RendererOpaque mem_iface offset"); +static_assert(offsetof(RendererOpaque, alloc_fn) == 0x50, "RendererOpaque alloc_fn offset"); +static_assert(offsetof(RendererOpaque, free_fn) == 0x58, "RendererOpaque free_fn offset"); +static_assert(offsetof(RendererOpaque, selection) == 0x80, "RendererOpaque selection offset"); +static_assert(offsetof(RendererOpaque, workspace) == 0x98, "RendererOpaque workspace offset"); +static_assert(offsetof(RendererOpaque, reserved_a8) == 0xA8, "RendererOpaque reserved_a8 offset"); +static_assert(offsetof(RendererOpaque, outline_policy_flag) == 0xAC, + "RendererOpaque outline_policy_flag offset"); + +struct RendererFtBackend { + /*0x00*/ void* renderer_header_0x10; + /*0x08*/ std::uintptr_t unknown_0x08; + /*0x10*/ std::uintptr_t unknown_0x10; + /*0x18*/ std::uintptr_t unknown_0x18; + /*0x20*/ void* initialized_marker; + /*0x28*/ u8 reserved_0x28[0x20]; +}; +static_assert(sizeof(RendererFtBackend) == 0x48, "RendererFtBackend size"); +static_assert(offsetof(RendererFtBackend, renderer_header_0x10) == 0x00, + "RendererFtBackend renderer_header_0x10 offset"); +static_assert(offsetof(RendererFtBackend, initialized_marker) == 0x20, + "RendererFtBackend initialized_marker offset"); + +struct RendererFtOpaque { + /*0x000*/ RendererOpaque base; + /*0x0B0*/ u8 reserved_0x0B0[0x70]; + /*0x120*/ RendererFtBackend ft_backend; +}; +static_assert(sizeof(RendererFtOpaque) == 0x168, "RendererFtOpaque size"); +static_assert(offsetof(RendererFtOpaque, ft_backend) == 0x120, + "RendererFtOpaque ft_backend offset"); + +struct OrbisFontRenderer_ {}; + +struct StyleFrameScaleState { + float scale_w; + float scale_h; + u32 dpi_x; + u32 dpi_y; + bool scale_overridden; + bool dpi_overridden; +}; + +extern const float kPointsPerInch; +extern const float kScaleEpsilon; + +extern const u16 kStyleFrameMagic; +extern const u8 kStyleFrameFlagScale; +extern const u8 kStyleFrameFlagWeight; +extern const u8 kStyleFrameFlagSlant; + +extern Core::FileSys::MntPoints* g_mnt; +extern std::unordered_map g_font_state; +extern std::unordered_map g_library_state; +extern std::unordered_map + g_style_for_surface; +extern std::mutex g_generated_glyph_mutex; +extern std::unordered_set g_generated_glyphs; + +extern void* g_allocator_vtbl_stub[4]; +extern std::uint8_t g_sys_driver_stub; +extern std::uint8_t g_fontset_registry_stub; +extern std::uint8_t g_sysfonts_ctx_stub; +extern std::uint8_t g_external_fonts_ctx_stub; +extern u32 g_device_cache_stub; + +FontState& GetState(Libraries::Font::OrbisFontHandle h); +FontState* TryGetState(Libraries::Font::OrbisFontHandle h); +LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib); +void RemoveLibState(Libraries::Font::OrbisFontLib lib); +FT_Face CreateFreeTypeFaceFromBytes(const unsigned char* data, std::size_t size, u32 subfont_index); +void DestroyFreeTypeFace(FT_Face& face); +void LogExternalFormatSupport(u32 formats_mask); +void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params); +std::filesystem::path ResolveGuestPath(const char* guest_path); +bool LoadGuestFileBytes(const std::filesystem::path& host_path, + std::vector& out_bytes); +FaceMetrics LoadFaceMetrics(FT_Face face); +void PopulateStateMetrics(FontState& st, const FaceMetrics& m); +float ComputeScaleExtForState(const FontState& st, FT_Face face, float pixel_h); +float ComputeScaleForPixelHeight(const FontState& st, float pixel_h); +float SafeDpiToFloat(u32 dpi); +float PointsToPixels(float pt, u32 dpi); +float PixelsToPoints(float px, u32 dpi); +u16 ClampToU16(float value); +StyleFrameScaleState ResolveStyleFrameScale(const Libraries::Font::OrbisFontStyleFrame* style, + const FontState& st); +void InitializeStyleFrame(Libraries::Font::OrbisFontStyleFrame& frame); +bool EnsureStyleFrameInitialized(Libraries::Font::OrbisFontStyleFrame* frame); +bool ValidateStyleFramePtr(const Libraries::Font::OrbisFontStyleFrame* frame); +void UpdateCachedLayout(FontState& st); +std::uint64_t MakeGlyphKey(u32 code, int pixel_h); +void LogStrideOnce(const Libraries::Font::OrbisFontRenderSurface* surf); +void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result); +bool ResolveFaceAndScale(FontState& st, Libraries::Font::OrbisFontHandle handle, float pixel_h, + FT_Face& face_out, float& scale_y_out); +bool ResolveFace(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face& face_out); +s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face face, + float pixel_w, float pixel_h, + Libraries::Font::OrbisFontRenderSurface* surf, u32 code, float x, + float y, Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result, s32 shift_x_units, + s32 shift_y_units); +s32 RenderCodepointToSurfaceWithScale(FontState& st, Libraries::Font::OrbisFontHandle handle, + FT_Face face, float scale_x, float scale_y, + Libraries::Font::OrbisFontRenderSurface* surf, u32 code, + float x, float y, Libraries::Font::OrbisFontGlyphMetrics* m, + Libraries::Font::OrbisFontRenderOutput* result); +const GlyphEntry* GetGlyphEntry(FontState& st, Libraries::Font::OrbisFontHandle handle, u32 code, + FT_Face& face_out, float& scale_out); +const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type); +std::filesystem::path GetSysFontBaseDir(); +std::string MacroToCamel(const char* macro_key); +std::filesystem::path ResolveSystemFontPath(u32 font_set_type); +std::filesystem::path ResolveSystemFontPathFromConfigOnly(u32 font_set_type); +const struct FontSetCache* EnsureFontSetCache(u32 font_set_type); +bool HasSfntTables(const std::vector& bytes); +bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); +bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle handle); +std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, + bool& attached_out); +void TrackGeneratedGlyph(Libraries::Font::OrbisFontGlyph glyph); +bool ForgetGeneratedGlyph(Libraries::Font::OrbisFontGlyph glyph); +GeneratedGlyph* TryGetGeneratedGlyph(Libraries::Font::OrbisFontGlyph glyph); +void PopulateGlyphMetricVariants(GeneratedGlyph& gg); +void BuildBoundingOutline(GeneratedGlyph& gg); +bool BuildTrueOutline(GeneratedGlyph& gg); + +} // namespace Libraries::Font::Internal diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index 9bd43054b..db21f4207 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -1,43 +1,79 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/font/font.h" +#include "core/libraries/font/font_internal.h" #include "core/libraries/font/fontft.h" +#include "core/libraries/font/fontft_internal.h" #include "core/libraries/libs.h" namespace Libraries::FontFt { namespace { -bool g_library_selected = false; -bool g_renderer_selected = false; -constexpr OrbisFontLibrarySelection kDefaultLibrarySelection{0xF2000000u, 0, nullptr, nullptr}; +static std::once_flag g_driver_table_once; +alignas(Libraries::Font::Internal::SysDriver) static Libraries::Font::Internal::SysDriver + g_driver_table{}; -static void* PS4_SYSV_ABI RendererCreateStub() { - LOG_ERROR(Lib_FontFt, "(STUBBED) renderer create called"); - return nullptr; +static const OrbisFontLibrarySelection* GetDriverTable() { + std::call_once(g_driver_table_once, [] { + auto* selection = reinterpret_cast(&g_driver_table); + selection->magic = 0; + selection->reserved = 0; + selection->reserved_ptr1 = nullptr; + + auto* driver = &g_driver_table; + + driver->pixel_resolution = &Libraries::FontFt::Internal::LibraryGetPixelResolutionStub; + driver->init = &Libraries::FontFt::Internal::LibraryInitStub; + driver->term = &Libraries::FontFt::Internal::LibraryTermStub; + driver->support_formats = &Libraries::FontFt::Internal::LibrarySupportStub; + + driver->open = &Libraries::FontFt::Internal::LibraryOpenFontMemoryStub; + driver->close = &Libraries::FontFt::Internal::LibraryCloseFontObjStub; + driver->scale = &Libraries::FontFt::Internal::LibraryGetFaceScaleStub; + driver->metric = &Libraries::FontFt::Internal::LibraryGetFaceMetricStub; + driver->glyph_index = &Libraries::FontFt::Internal::LibraryGetGlyphIndexStub; + driver->set_char_with_dpi = &Libraries::FontFt::Internal::LibrarySetCharSizeWithDpiStub; + driver->set_char_default_dpi = + &Libraries::FontFt::Internal::LibrarySetCharSizeDefaultDpiStub; + driver->compute_layout = &Libraries::FontFt::Internal::LibraryComputeLayoutBlockStub; + driver->compute_layout_alt = &Libraries::FontFt::Internal::LibraryComputeLayoutAltBlockStub; + + driver->load_glyph_cached = &Libraries::FontFt::Internal::LibraryLoadGlyphCachedStub; + driver->get_glyph_metrics = &Libraries::FontFt::Internal::LibraryGetGlyphMetricsStub; + driver->apply_glyph_adjust = &Libraries::FontFt::Internal::LibraryApplyGlyphAdjustStub; + driver->configure_glyph = &Libraries::FontFt::Internal::LibraryConfigureGlyphStub; + }); + + return reinterpret_cast(&g_driver_table); } -static void PS4_SYSV_ABI RendererQueryStub() { - LOG_ERROR(Lib_FontFt, "(STUBBED) renderer query called"); +static std::once_flag g_renderer_table_once; +alignas(OrbisFontRendererSelection) static OrbisFontRendererSelection g_renderer_table{}; + +static const OrbisFontRendererSelection* GetRendererSelectionTable() { + std::call_once(g_renderer_table_once, [] { + g_renderer_table.magic = 0; + g_renderer_table.size = + static_cast(sizeof(Libraries::Font::Internal::RendererFtOpaque)); + + g_renderer_table.create_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererCreate); + g_renderer_table.destroy_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererDestroy); + g_renderer_table.query_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererQuery); + }); + return &g_renderer_table; } -static void PS4_SYSV_ABI RendererDestroyStub() { - LOG_ERROR(Lib_FontFt, "(STUBBED) renderer destroy called"); -} - -static OrbisFontRendererSelection MakeRendererSelection() { - OrbisFontRendererSelection sel{}; - sel.magic = 0xF2000000u; - sel.size = 0x168; - sel.create_fn = reinterpret_cast(&RendererCreateStub); - sel.query_fn = reinterpret_cast(&RendererQueryStub); - sel.destroy_fn = reinterpret_cast(&RendererDestroyStub); - return sel; -} - -static const OrbisFontRendererSelection kDefaultRendererSelection = MakeRendererSelection(); } // namespace s32 PS4_SYSV_ABI sceFontFtInitAliases() { @@ -136,31 +172,19 @@ s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() { } const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value) { - if (!g_library_selected) { - g_library_selected = true; - LOG_INFO(Lib_FontFt, "library selection requested (FreeType shim)"); - } - LOG_DEBUG(Lib_FontFt, - "library selection params:\n" - " value={}\n", - value); + LOG_INFO(Lib_FontFt, "called"); + LOG_DEBUG(Lib_FontFt, "params:\nvalue: {}\n", value); if (value == 0) { - return &kDefaultLibrarySelection; + return GetDriverTable(); } return nullptr; } const OrbisFontRendererSelection* PS4_SYSV_ABI sceFontSelectRendererFt(int value) { - if (!g_renderer_selected) { - g_renderer_selected = true; - LOG_INFO(Lib_FontFt, "renderer selection requested (stb_truetype backend)"); - } - LOG_DEBUG(Lib_FontFt, - "renderer selection params:\n" - " value={}\n", - value); + LOG_INFO(Lib_FontFt, "called"); + LOG_DEBUG(Lib_FontFt, "params:\nvalue: {}\n", value); if (value == 0) { - return &kDefaultRendererSelection; + return GetRendererSelectionTable(); } return nullptr; } diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h index a94e83102..f749e7724 100644 --- a/src/core/libraries/font/fontft.h +++ b/src/core/libraries/font/fontft.h @@ -15,15 +15,18 @@ struct OrbisFontLibrarySelection { u32 magic; u32 reserved; void* reserved_ptr1; - void* reserved_ptr2; + uintptr_t get_pixel_resolution_fn; + uintptr_t init_fn; + uintptr_t term_fn; + uintptr_t support_fn; }; struct OrbisFontRendererSelection { u32 magic; u32 size; uintptr_t create_fn; - uintptr_t query_fn; uintptr_t destroy_fn; + uintptr_t query_fn; }; s32 PS4_SYSV_ABI sceFontFtInitAliases(); diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp new file mode 100644 index 000000000..6dc3d95bd --- /dev/null +++ b/src/core/libraries/font/fontft_internal.cpp @@ -0,0 +1,3672 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/font/fontft_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H +#include FT_MODULE_H +#include FT_SYSTEM_H +#include FT_TRUETYPE_TABLES_H + +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/file_sys/fs.h" +#include "core/libraries/font/font_internal.h" +#include "core/libraries/kernel/kernel.h" +#include "font_error.h" + +namespace Libraries::Font::Internal { + +using Libraries::Font::OrbisFontGlyph; +using Libraries::Font::OrbisFontGlyphMetrics; +using Libraries::Font::OrbisFontHandle; +using Libraries::Font::OrbisFontRenderer; +using Libraries::Font::OrbisFontRenderOutput; +using Libraries::Font::OrbisFontRenderSurface; +using Libraries::Font::OrbisFontStyleFrame; + +// LLE: FUN_01007e80 +static void UpdateFtFontObjShiftCache(u8* font_obj) { + if (!font_obj) { + return; + } + + auto* obj = reinterpret_cast(font_obj); + obj->shift_cache_x = static_cast(obj->shift_units_x); + obj->shift_cache_y = static_cast(obj->shift_units_y); + + const s32 seed_low = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_high = + static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); + obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); + obj->layout_seed_vec[1] = static_cast(static_cast(seed_high)); + obj->layout_scale_vec[0] = 0x0000000000010000ull; + obj->layout_scale_vec[1] = 0x0000000000010000ull; +} + +SysFontDesc GetSysFontDesc(u32 font_id) { + (void)font_id; + return SysFontDesc{ + .scale_factor = 1.0f, + .shift_value = 0, + }; +} + +Libraries::Font::FontHandleOpaqueNative* GetNativeFont(Libraries::Font::OrbisFontHandle h) { + return h ? reinterpret_cast(h) : nullptr; +} + +bool AcquireFontLock(Libraries::Font::FontHandleOpaqueNative* font, u32& out_prev_lock_word) { + if (!font) { + return false; + } + for (;;) { + const u32 lock_word = font->lock_word; + if (font->magic != 0x0F02) { + return false; + } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + std::atomic_ref ref(font->lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +void ReleaseFontLock(Libraries::Font::FontHandleOpaqueNative* font, u32 prev_lock_word) { + if (!font) { + return; + } + font->lock_word = prev_lock_word & 0x7fffffff; +} + +bool AcquireCachedStyleLock(Libraries::Font::FontHandleOpaqueNative* font, + u32& out_prev_lock_word) { + if (!font) { + return false; + } + for (;;) { + const u32 lock_word = font->cached_style.cache_lock_word; + if (font->magic != 0x0F02) { + return false; + } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + std::atomic_ref ref(font->cached_style.cache_lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +void ReleaseCachedStyleLock(Libraries::Font::FontHandleOpaqueNative* font, u32 prev_lock_word) { + if (!font) { + return; + } + font->cached_style.cache_lock_word = prev_lock_word & 0x7fffffff; +} + +// LLE: FUN_01000c30 +std::uint8_t* AcquireFontCtxEntry(std::uint8_t* ctx, u32 idx, u32 mode_low, void** out_obj, + u32* out_lock_word) { + if (!ctx || !out_obj || !out_lock_word) { + return nullptr; + } + + auto* header = reinterpret_cast(ctx); + const u32 max = header->max_entries; + if (max < idx || max == idx) { + *out_lock_word = 0; + *out_obj = nullptr; + return nullptr; + } + + auto* base = static_cast(header->base); + const std::size_t entry_off = static_cast(idx) * sizeof(FontCtxEntry); + auto* entry_u8 = base ? (base + entry_off) : nullptr; + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + if (!entry) { + *out_lock_word = 0; + *out_obj = nullptr; + return nullptr; + } + + u32* lock_word = nullptr; + void** obj_slot = nullptr; + if (mode_low == 3) { + lock_word = &entry->lock_mode3; + obj_slot = &entry->obj_mode3; + } else if (mode_low == 2) { + lock_word = &entry->lock_mode2; + obj_slot = &entry->obj_mode2; + } else if (mode_low == 1) { + lock_word = &entry->lock_mode1; + obj_slot = &entry->obj_mode1; + } else { + *out_lock_word = 0; + *out_obj = nullptr; + return entry_u8; + } + + for (;;) { + const u32 cur = *lock_word; + if (static_cast(cur) >= 0) { + std::atomic_ref ref(*lock_word); + u32 expected = cur; + if (ref.compare_exchange_weak(expected, cur | 0x80000000u, std::memory_order_acq_rel)) { + *out_lock_word = cur | 0x80000000u; + *out_obj = obj_slot ? *obj_slot : nullptr; + return entry_u8; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +// LLE: FUN_01000d70 +static std::uint8_t* AcquireFontCtxEntryAndSelectNode(std::uint8_t* ctx, u32 idx, u32 mode_low, + u32 sub_font_index, void** out_obj, + u32* out_lock_word) { + if (!out_obj) { + return nullptr; + } + std::uint8_t* entry_u8 = AcquireFontCtxEntry(ctx, idx, mode_low, out_obj, out_lock_word); + auto* node = static_cast(*out_obj); + while (node) { + if (node->sub_font_index == sub_font_index) { + break; + } + node = node->next; + } + *out_obj = node; + return entry_u8; +} + +// LLE: FUN_01000db0 +static void ReleaseFontCtxEntryLock(std::uint8_t* entry_u8, u32 mode_low, u32 lock_word) { + if (!entry_u8) { + return; + } + auto* entry = reinterpret_cast(entry_u8); + u32* lock_word_ptr = nullptr; + if (mode_low == 3) { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode3 = nullptr; + } + lock_word_ptr = &entry->lock_mode3; + } else if (mode_low == 1) { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode1 = nullptr; + } + lock_word_ptr = &entry->lock_mode1; + } else { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode2 = nullptr; + } + lock_word_ptr = &entry->lock_mode2; + } + + if (((lock_word & 0x0FFFFFFFu) == 0) && entry->obj_mode1 == nullptr && + entry->obj_mode3 == nullptr && entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + *lock_word_ptr = lock_word & 0x7FFFFFFFu; +} + +// LLE: FUN_01000ba0 +static void ApplyGlyphSpecialCaseAdjust(int font_id, int glyph_index, int* inout_seed_words) { + if (!inout_seed_words) { + return; + } + if (((font_id == 10) && (static_cast(glyph_index - 0x23d1U) < 0x5e)) && + ((0x5c0000000017ULL >> + (static_cast(static_cast((static_cast(glyph_index - 0x23d1U) >> 1) & 0x3f)) & + 0x3fULL)) & + 1ULL) != 0) { + inout_seed_words[0] = inout_seed_words[0] + 500; + } +} + +// LLE: FUN_01009ff0 +static std::uintptr_t AcquireRendererSelectionLock(RendererOpaque* renderer) { + if (!renderer) { + return 0; + } + + constexpr std::uintptr_t kLocked = std::numeric_limits::max(); + for (;;) { + const std::uintptr_t cur = reinterpret_cast(renderer->selection); + if (cur != kLocked) { + std::atomic_ref ref(renderer->selection); + void* expected = reinterpret_cast(cur); + if (ref.compare_exchange_weak(expected, reinterpret_cast(kLocked), + std::memory_order_acq_rel)) { + return cur; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +// LLE: FUN_0100bf00 +static std::uint64_t ResolveGposTagForCode(u32 codepoint) { + (void)codepoint; + return 0; +} + +#pragma pack(push, 1) +struct SysFontRangeRecord { + /*0x00*/ u32 start; + /*0x04*/ u32 end; + /*0x08*/ s16 shift_x; + /*0x0A*/ s16 shift_y; + /*0x0C*/ float scale_mul; + /*0x10*/ u32 reserved_0x10; + /*0x14*/ u32 reserved_0x14; + /*0x18*/ u32 reserved_0x18; +}; +#pragma pack(pop) +static_assert(sizeof(SysFontRangeRecord) == 0x1C); +static_assert(offsetof(SysFontRangeRecord, shift_x) == 0x08); +static_assert(offsetof(SysFontRangeRecord, scale_mul) == 0x0C); + +// LLE: FUN_01000480 +static float SysFontScaleFactor(u32 font_id) { + (void)font_id; + return 1.0f; +} + +// LLE: FUN_010004d0 +static float SysFontShiftValueF32(u32 font_id) { + (void)font_id; + return 0.0f; +} + +// LLE: FUN_010004f0 +static u8 SysFontHasAltFlag(u32 font_id) { + (void)font_id; + return 0; +} + +// LLE: FUN_010006c0 +const std::uint8_t* FindSysFontRangeRecord(u32 font_id, u32 codepoint) { + (void)font_id; + (void)codepoint; + return nullptr; +} + +// LLE: FUN_010098b0 +static u32 ResolveSysFontMapping(const void* record, int table_id, u32 codepoint, + u32* out_codepoint) { + (void)record; + (void)table_id; + if (out_codepoint) { + *out_codepoint = codepoint; + } + return 0; +} + +// LLE: FUN_010008b0 +u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_font_id) { + if (!record) { + if (out_font_id) { + *out_font_id = 0; + } + return static_cast(code); + } + + const u32 code_u32 = static_cast(code); + + const auto* rec = static_cast(record); + if (rec->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { + const auto view = MakeFontSetRecordView(rec); + const u32 entry_count = view.header ? view.header->entry_count : 0u; + const u32 record_primary_id = + (entry_count && view.entry_indices) ? view.entry_indices[0] : 0u; + const auto* selector = view.selector; + if (!selector || selector->magic != Libraries::Font::Internal::FontSetSelector::kMagic) { + if (out_font_id) { + *out_font_id = record_primary_id; + } + return code_u32; + } + + auto is_cjk_like = [](u32 cp) -> bool { + if ((cp >= 0x3040u && cp <= 0x30FFu) || (cp >= 0x31F0u && cp <= 0x31FFu) || + (cp >= 0x3400u && cp <= 0x4DBFu) || (cp >= 0x4E00u && cp <= 0x9FFFu) || + (cp >= 0xAC00u && cp <= 0xD7AFu) || (cp >= 0xF900u && cp <= 0xFAFFu) || + (cp >= 0x20000u && cp <= 0x2FA1Fu)) { + return true; + } + return false; + }; + + const u32 primary_id = selector->primary_font_id != 0xffffffffu ? selector->primary_font_id + : record_primary_id; + + auto has_glyph_in_ctx = [&](u32 font_id, u32 cp) -> bool { + if (!selector->library || selector->mode_low == 0) { + return false; + } + auto* lib = static_cast(selector->library); + auto* ctx = lib ? static_cast(lib->sysfonts_ctx) : nullptr; + if (!ctx) { + return false; + } + void* head = nullptr; + u32 lock_word = 0; + u8* entry_u8 = AcquireFontCtxEntry(ctx, font_id, selector->mode_low, &head, &lock_word); + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + auto* node = static_cast(head); + while (node) { + if (node->sub_font_index == selector->sub_font_index) { + break; + } + node = node->next; + } + FT_Face face = node ? static_cast(node->ft_face) : nullptr; + const bool ok = face && (FT_Get_Char_Index(face, static_cast(cp)) != 0); + if (entry) { + if (selector->mode_low == 3) { + entry->lock_mode3 = lock_word & 0x7fffffffu; + } else if (selector->mode_low == 2) { + entry->lock_mode2 = lock_word & 0x7fffffffu; + } else { + entry->lock_mode1 = lock_word & 0x7fffffffu; + } + } + return ok; + }; + + auto has_glyph = [&](u32 font_id, u32 cp) -> bool { + if (font_id == 0xffffffffu) { + return false; + } + if (has_glyph_in_ctx(font_id, cp)) { + return true; + } + for (const auto& c : selector->candidates) { + if (c.font_id != font_id || !c.face) { + continue; + } + return FT_Get_Char_Index(c.face, static_cast(cp)) != 0; + } + return false; + }; + + auto select_font = [&](u32 font_id) -> bool { + if (!has_glyph(font_id, code_u32)) { + return false; + } + if (out_font_id) { + *out_font_id = font_id; + } + return true; + }; + + if (selector->roman_font_id != 0xffffffffu && !is_cjk_like(code_u32)) { + if (select_font(selector->roman_font_id)) { + return code_u32; + } + } + if (select_font(primary_id)) { + return code_u32; + } + for (const auto& c : selector->candidates) { + if (select_font(c.font_id)) { + return code_u32; + } + } + + if (out_font_id) { + *out_font_id = primary_id; + } + (void)flags; + return 0; + } + + (void)flags; + if (out_font_id) { + *out_font_id = 0; + } + return code_u32; +} + +// LLE: FUN_010042e0 +void ReleaseFontObjectsForHandle(Libraries::Font::OrbisFontHandle fontHandle, int entryCount) { + if (!fontHandle) { + return; + } + auto* font = GetNativeFont(fontHandle); + if (!font) { + return; + } + auto* lib = static_cast(font->library); + if (!lib || lib->magic != 0x0F01 || !lib->sys_driver) { + return; + } + + const auto* fontset_record = + static_cast(font->open_info.fontset_record); + + const u32* entry_indices = nullptr; + u32 count = 0; + u8* ctx = nullptr; + if (!fontset_record) { + entry_indices = &font->open_info.ctx_entry_index; + count = 1; + ctx = static_cast(lib->external_fonts_ctx); + } else { + if (entryCount < 0) { + entryCount = static_cast(fontset_record->entry_count); + } + entry_indices = reinterpret_cast(fontset_record + 1); + count = static_cast(entryCount); + ctx = static_cast(lib->sysfonts_ctx); + } + + if (!ctx || !entry_indices || count == 0) { + return; + } + + const auto* driver = reinterpret_cast(lib->sys_driver); + const auto close_fn = driver ? driver->close : nullptr; + if (!close_fn) { + return; + } + + const u32 mode_low = static_cast(font->flags) & 0x0Fu; + const u32 sub_font_index = font->open_info.sub_font_index; + + for (u32 n = count; n != 0; --n) { + const u32 font_id = entry_indices[n - 1]; + if (static_cast(font_id) < 0) { + continue; + } + + void* head = nullptr; + u32 lock_word = 0; + u8* entry_u8 = AcquireFontCtxEntry(ctx, font_id, mode_low, &head, &lock_word); + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + if (!entry) { + continue; + } + + FontObj* match = nullptr; + for (auto* node = static_cast(head); node != nullptr; node = node->next) { + if (node->sub_font_index == sub_font_index) { + match = node; + break; + } + } + + const u32 open_flag = lock_word & 0x40000000u; + const u32 count_bits = lock_word & 0x0FFFFFFFu; + if (open_flag != 0 && count_bits != 0 && match) { + const u32 refcount = match->refcount; + const u32 dec_lock = lock_word - 1; + lock_word = dec_lock; + if (refcount != 0) { + if (refcount == 1) { + const u64 next = reinterpret_cast(match->next); + const u64 prev = reinterpret_cast(match->prev); + (void)close_fn(match, 0); + if (prev == 0) { + if (mode_low == 1) { + entry->obj_mode1 = reinterpret_cast(next); + } else if (mode_low == 2) { + entry->obj_mode2 = reinterpret_cast(next); + } else if (mode_low == 3) { + entry->obj_mode3 = reinterpret_cast(next); + } + } + } else { + match->refcount = refcount - 1; + } + } + if (count_bits == 1) { + lock_word = (~open_flag) & dec_lock; + } + } + + const u32 remaining = lock_word & 0x0FFFFFFFu; + if (mode_low == 3) { + if (remaining == 0) { + entry->obj_mode3 = nullptr; + } + entry->lock_mode3 = lock_word & 0x7fffffffu; + } else if (mode_low == 1) { + if (remaining == 0) { + entry->obj_mode1 = nullptr; + } + entry->lock_mode1 = lock_word & 0x7fffffffu; + } else { + if (remaining == 0) { + entry->obj_mode2 = nullptr; + } + entry->lock_mode2 = lock_word & 0x7fffffffu; + } + + if (remaining == 0 && entry->obj_mode1 == nullptr && entry->obj_mode3 == nullptr && + entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + } +} + +// LLE: FUN_01006990 +s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_state_block, + u8 (*out_words)[16]) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 8, 0, 8); + std::memset(out_words[2], 0, 4); + } + + const auto fail_return = [&]() -> s32 { + if (out_words) { + std::memset(out_words[1] + 8, 0, 8); + std::memset(out_words[2], 0, 4); + std::memset(out_words[0], 0, 16); + } + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + }; + + auto* font = GetNativeFont(fontHandle); + if (!font) { + return fail_return(); + } + + auto* lib = static_cast(font->library); + if (!lib) { + return fail_return(); + } + + const auto* fontset_record = + static_cast(font->open_info.fontset_record); + + void* ctx_void = nullptr; + const u32* entry_indices = nullptr; + int entry_count = 0; + if (!fontset_record) { + ctx_void = lib->external_fonts_ctx; + entry_indices = &font->open_info.ctx_entry_index; + entry_count = 1; + } else { + ctx_void = lib->sysfonts_ctx; + entry_count = static_cast(fontset_record->entry_count); + entry_indices = reinterpret_cast(fontset_record + 1); + if (entry_count == 0) { + return fail_return(); + } + } + + auto* ctx = static_cast(ctx_void); + if (!ctx || !entry_indices) { + return fail_return(); + } + + const u32 mode_low = static_cast(font->flags) & 0x0Fu; + const u32 sub_font_index = font->open_info.sub_font_index; + const auto* driver = reinterpret_cast(lib->sys_driver); + if (!driver) { + return fail_return(); + } + const auto set_char_with_dpi = driver->set_char_with_dpi; + const auto set_char_default_dpi = driver->set_char_default_dpi; + const auto compute_layout = driver->compute_layout; + if (!set_char_with_dpi || !set_char_default_dpi || !compute_layout) { + return fail_return(); + } + + const auto* style = static_cast(style_state_block); + if (!style) { + return fail_return(); + } + + float baseline_max = 0.0f; + float delta_max = 0.0f; + float effect_for_baseline = 0.0f; + float effect_for_delta = 0.0f; + + struct F32x2 { + float lo = 0.0f; + float hi = 0.0f; + }; + + F32x2 acc_u13_i8{}; + F32x2 acc_x_bounds{}; + + const auto load_f32x2_from_u64 = [](u64 bits) -> F32x2 { + const u32 lo = static_cast(bits & 0xFFFFFFFFull); + const u32 hi = static_cast(bits >> 32); + return { + .lo = std::bit_cast(lo), + .hi = std::bit_cast(hi), + }; + }; + + const auto flip_sign_bit = [](float v) -> float { + u32 bits = std::bit_cast(v); + bits ^= 0x80000000u; + return std::bit_cast(bits); + }; + + const auto ordered_lt = [](float a, float b) -> bool { + return !std::isnan(a) && !std::isnan(b) && (a < b); + }; + + const auto maxss = [](float a, float b) -> float { + 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) { + const bool a_neg = std::signbit(a); + const bool b_neg = std::signbit(b); + if (a_neg != b_neg) { + return a_neg ? b : a; + } + } + return a; + }; + + for (int n = entry_count; n != 0; --n) { + const u32 font_id = *entry_indices; + ++entry_indices; + if (static_cast(font_id) < 0) { + continue; + } + + float fontset_scale_factor = 1.0f; + int fontset_shift_value = 0; + (void)fontset_record; + + void* head = nullptr; + u32 lock_word = 0; + u8* entry_u8 = AcquireFontCtxEntry(ctx, font_id, mode_low, &head, &lock_word); + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + if (!entry) { + return fail_return(); + } + + FontObj* match = nullptr; + for (auto* node = static_cast(head); node != nullptr; node = node->next) { + if (node->sub_font_index == sub_font_index) { + match = node; + break; + } + } + + s32 rc = ORBIS_FONT_ERROR_FATAL; + HorizontalLayoutBlocks layout_tmp{}; + if (((lock_word & 0x40000000u) != 0) && ((lock_word & 0x0FFFFFFFu) != 0) && match) { + float scale_x = 0.0f; + float scale_y = 0.0f; + { + scale_x = fontset_scale_factor * style->scale_w; + scale_y = fontset_scale_factor * style->scale_h; + } + + match->font_handle = fontHandle; + match->shift_units_x = 0; + match->shift_units_y = fontset_shift_value; + + if (style->scale_unit == 0) { + rc = set_char_default_dpi(match, scale_x, scale_y, &scale_x, &scale_y); + } else { + rc = set_char_with_dpi(match, style->dpi_x, style->dpi_y, scale_x, scale_y, + &scale_x, &scale_y); + } + if (rc == ORBIS_OK) { + UpdateFtFontObjShiftCache(reinterpret_cast(match)); + rc = compute_layout(match, style_state_block, LayoutWordsBytes(layout_tmp.words())); + } + } + + u32* lock_word_ptr = nullptr; + if (mode_low == 3) { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode3 = nullptr; + } + lock_word_ptr = &entry->lock_mode3; + } else if (mode_low == 1) { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode1 = nullptr; + } + lock_word_ptr = &entry->lock_mode1; + } else { + if ((lock_word & 0x0FFFFFFFu) == 0) { + entry->obj_mode2 = nullptr; + } + lock_word_ptr = &entry->lock_mode2; + } + + if (((lock_word & 0x0FFFFFFFu) == 0) && entry->obj_mode1 == nullptr && + entry->obj_mode3 == nullptr && entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + *lock_word_ptr = lock_word & 0x7FFFFFFFu; + + if (rc != ORBIS_OK) { + return fail_return(); + } + + const auto layout = layout_tmp.values(); + const float line_advance = layout.line_advance; + const float baseline = layout.baseline; + const float effect_h = layout.effect_height; + const u64 x_bounds_bits = layout.x_bounds_bits; + const u64 i8_adj_u13_bits = layout.i8_adj_u13_bits; + + const F32x2 prev_u13_i8 = acc_u13_i8; + const F32x2 prev_bounds = acc_x_bounds; + + const float prev_baseline_max = baseline_max; + const float prev_effect_for_baseline = effect_for_baseline; + const bool baseline_update = ordered_lt(prev_baseline_max, baseline); + baseline_max = maxss(baseline, prev_baseline_max); + effect_for_baseline = baseline_update ? effect_h : prev_effect_for_baseline; + + const float prev_delta_max = delta_max; + const float prev_effect_for_delta = effect_for_delta; + const float new_delta = line_advance - baseline; + const bool delta_update = ordered_lt(prev_delta_max, new_delta); + delta_max = maxss(new_delta, prev_delta_max); + effect_for_delta = delta_update ? effect_h : prev_effect_for_delta; + + const F32x2 v_i8_adj_u13 = load_f32x2_from_u64(i8_adj_u13_bits); + const F32x2 v_x_bounds = load_f32x2_from_u64(x_bounds_bits); + + const F32x2 perm = {.lo = v_i8_adj_u13.hi, .hi = v_i8_adj_u13.lo}; + const F32x2 diff = {.lo = v_x_bounds.lo - perm.lo, .hi = v_x_bounds.hi - perm.hi}; + const F32x2 sum = { + .lo = prev_u13_i8.lo + prev_bounds.lo, + .hi = prev_u13_i8.hi + prev_bounds.hi, + }; + + const F32x2 inserted = {.lo = flip_sign_bit(v_i8_adj_u13.hi), .hi = v_i8_adj_u13.lo}; + + const bool mask_lo = ordered_lt(diff.lo, sum.lo); + const bool mask_hi = ordered_lt(diff.hi, sum.hi); + if (mask_lo) { + acc_x_bounds.lo = v_x_bounds.lo; + acc_u13_i8.lo = inserted.lo; + } + if (mask_hi) { + acc_x_bounds.hi = v_x_bounds.hi; + acc_u13_i8.hi = inserted.hi; + } + } + + if (!out_words) { + return fail_return(); + } + + const float line_h = baseline_max + delta_max; + const float effect_h = maxss(effect_for_baseline, effect_for_delta); + + if ((reinterpret_cast(out_words) & (alignof(HorizontalLayoutBlocks) - 1)) == + 0) { + auto* out_blocks = reinterpret_cast(out_words); + out_blocks->set_baseline(baseline_max); + out_blocks->set_line_advance(line_h); + out_blocks->set_x_bounds(acc_x_bounds.lo, acc_x_bounds.hi); + out_blocks->set_effect_height(effect_h); + out_blocks->set_i8_adj_u13_lanes(acc_u13_i8.lo, acc_u13_i8.hi); + } else { + auto out = HorizontalLayoutBlocksIo{out_words}.fields(); + out.baseline = baseline_max; + out.line_advance = line_h; + out.x_bound_0 = acc_x_bounds.lo; + out.x_bound_1 = acc_x_bounds.hi; + out.effect_height = effect_h; + out.u13_i8_lane0 = acc_u13_i8.lo; + out.u13_i8_lane1 = acc_u13_i8.hi; + } + + return ORBIS_OK; +} + +// LLE: FUN_01006fb0 +s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_state_block, + u8 (*out_words)[16]) { + auto* font = GetNativeFont(fontHandle); + auto* lib = font ? reinterpret_cast(font->library) : nullptr; + if (!lib) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 4, 0, 8); + std::memset(out_words[1] + 0x0C, 0, 4); + } + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const void* fontset_record = font->open_info.fontset_record; + + const u32 mode_low = static_cast(font->flags) & 0x0Fu; + + const u32* font_ids = nullptr; + int entry_count = 0; + u32 rc = ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + + if (!fontset_record) { + entry_count = 1; + font_ids = &font->open_info.ctx_entry_index; + rc = ORBIS_OK; + } else { + entry_count = + *reinterpret_cast(reinterpret_cast(fontset_record) + 0x20); + font_ids = reinterpret_cast(reinterpret_cast(fontset_record) + 0x24); + rc = (entry_count != 0) ? ORBIS_OK : ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u8* ctx = fontset_record ? static_cast(lib->sysfonts_ctx) + : static_cast(lib->external_fonts_ctx); + if (!ctx) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 4, 0, 8); + std::memset(out_words[1] + 0x0C, 0, 4); + } + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (static_cast(rc) != ORBIS_OK) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 4, 0, 8); + std::memset(out_words[1] + 0x0C, 0, 4); + } + return static_cast(rc); + } + + float acc_neg_local64_max = 0.0f; + float acc_sum_max = 0.0f; + float assoc_for_neg_local64_max = 0.0f; + float assoc_for_sum_max = 0.0f; + float acc_local60_max = 0.0f; + float acc_local5c_max = 0.0f; + float acc_diff_min = 0.0f; + float acc_temp_min = 0.0f; + + const SysDriver* driver = static_cast(lib->sys_driver); + if (!driver) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 4, 0, 8); + std::memset(out_words[1] + 0x0C, 0, 4); + } + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + for (int i = 0; i < entry_count; ++i) { + const u32 font_id = font_ids ? font_ids[i] : 0u; + if (static_cast(font_id) < 0) { + continue; + } + + float scale_factor = 1.0f; + s32 shift_value = 0; + if (fontset_record) { + const auto desc = GetSysFontDesc(font_id); + scale_factor = desc.scale_factor; + shift_value = desc.shift_value; + } + + void* head = nullptr; + u32 lock_word = 0; + u8* entry_u8 = AcquireFontCtxEntry(ctx, font_id, mode_low, &head, &lock_word); + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + + const u32 sub_font_index = font->open_info.sub_font_index; + auto* font_obj = static_cast(head); + while (font_obj && font_obj->sub_font_index != sub_font_index) { + font_obj = font_obj->next; + } + + if (!entry || !font_obj) { + rc = ORBIS_FONT_ERROR_FATAL; + break; + } + + rc = ORBIS_FONT_ERROR_FATAL; + if (((lock_word & 0x40000000u) != 0) && ((lock_word & 0x0FFFFFFFu) != 0)) { + const auto* style = static_cast(style_state_block); + const float base_scale_x = style ? style->scale_w : 0.0f; + const float base_scale_y = style ? style->scale_h : 0.0f; + + float scale_x = scale_factor * base_scale_x; + float scale_y = scale_factor * base_scale_y; + + font_obj->font_handle = fontHandle; + font_obj->shift_units_x = 0; + font_obj->shift_units_y = shift_value; + + s32 call_rc = ORBIS_FONT_ERROR_FATAL; + if (style && style->scale_unit == 0) { + call_rc = driver->set_char_default_dpi + ? driver->set_char_default_dpi(font_obj, scale_x, scale_y, &scale_x, + &scale_y) + : ORBIS_FONT_ERROR_FATAL; + } else { + call_rc = driver->set_char_with_dpi + ? driver->set_char_with_dpi(font_obj, style ? style->dpi_x : 0u, + style ? style->dpi_y : 0u, scale_x, + scale_y, &scale_x, &scale_y) + : ORBIS_FONT_ERROR_FATAL; + } + + VerticalLayoutAltBlocks layout_alt{}; + if (call_rc == ORBIS_OK) { + UpdateFtFontObjShiftCache(reinterpret_cast(font_obj)); + call_rc = driver->compute_layout_alt + ? driver->compute_layout_alt(font_obj, style_state_block, + LayoutWordsBytes(layout_alt.words())) + : ORBIS_FONT_ERROR_FATAL; + } + + if (call_rc == ORBIS_OK) { + const auto v = layout_alt.values(); + const float primary_0x00 = v.metrics.unknown_0x00; + const float baseline_offset_x_candidate = v.metrics.baseline_offset_x_candidate; + const float primary_0x08 = v.metrics.unknown_0x08; + const float primary_0x0C = v.metrics.unknown_0x0C; + const float decoration_span_candidate = v.extras.decoration_span_candidate; + const float extra_0x08 = v.extras.unknown_0x08; + const float extra_0x0C = v.extras.unknown_0x0C; + + const float neg_baseline_offset_x_candidate = -baseline_offset_x_candidate; + const float sum_primary_0x00_and_baseline_offset_x_candidate = + baseline_offset_x_candidate + primary_0x00; + + if (acc_neg_local64_max < neg_baseline_offset_x_candidate) { + assoc_for_neg_local64_max = decoration_span_candidate; + } + acc_neg_local64_max = + std::max(acc_neg_local64_max, neg_baseline_offset_x_candidate); + + if (acc_sum_max < sum_primary_0x00_and_baseline_offset_x_candidate) { + assoc_for_sum_max = decoration_span_candidate; + } + acc_sum_max = + std::max(acc_sum_max, sum_primary_0x00_and_baseline_offset_x_candidate); + + const float diff = extra_0x08 - baseline_offset_x_candidate; + acc_diff_min = std::min(diff, acc_diff_min); + + acc_local60_max = std::max(primary_0x08, acc_local60_max); + + const float sum2 = sum_primary_0x00_and_baseline_offset_x_candidate + extra_0x0C; + acc_temp_min = std::min(sum2, acc_diff_min); + + acc_local5c_max = std::max(primary_0x0C, acc_local5c_max); + rc = ORBIS_OK; + } else { + rc = call_rc; + } + } + + const u32 low_bits = lock_word & 0x0FFFFFFFu; + u32* lock_word_store = nullptr; + if (mode_low == 3) { + if (low_bits == 0) { + entry->obj_mode3 = nullptr; + } + lock_word_store = &entry->lock_mode3; + } else if (mode_low == 1) { + if (low_bits == 0) { + entry->obj_mode1 = nullptr; + } + lock_word_store = &entry->lock_mode1; + } else { + if (low_bits == 0) { + entry->obj_mode2 = nullptr; + } + lock_word_store = &entry->lock_mode2; + } + + if ((low_bits == 0) && entry->obj_mode1 == nullptr && entry->obj_mode3 == nullptr && + entry->obj_mode2 == nullptr) { + entry->unique_id = 0; + entry->active = 0; + } + if (lock_word_store) { + *lock_word_store = lock_word & 0x7FFFFFFFu; + } + + if (static_cast(rc) != ORBIS_OK) { + break; + } + } + + if (static_cast(rc) != ORBIS_OK) { + if (out_words) { + std::memset(out_words[0], 0, 16); + std::memset(out_words[1] + 4, 0, 8); + std::memset(out_words[1] + 0x0C, 0, 4); + } + return static_cast(rc); + } + + if (out_words) { + const float column_advance = acc_neg_local64_max + acc_sum_max; + const float baseline_offset_x = -acc_neg_local64_max; + const float decoration_span = std::max(assoc_for_sum_max, assoc_for_neg_local64_max); + const float unknown_decoration_0x08 = acc_temp_min - acc_neg_local64_max; + const float unknown_decoration_0x0C = -acc_sum_max; + + if ((reinterpret_cast(out_words) & (alignof(VerticalLayoutBlocks) - 1)) == + 0) { + auto* out_blocks = reinterpret_cast(out_words); + out_blocks->set_column_advance(column_advance); + out_blocks->set_baseline_offset_x(baseline_offset_x); + out_blocks->set_unknown_metrics_0x08(acc_local60_max); + out_blocks->set_unknown_metrics_0x0C(acc_local5c_max); + out_blocks->set_decoration_span(decoration_span); + out_blocks->set_unknown_decoration_0x08(unknown_decoration_0x08); + out_blocks->set_unknown_decoration_0x0C(unknown_decoration_0x0C); + } else { + auto out = VerticalLayoutBlocksIo{out_words}.fields(); + out.column_advance = column_advance; + out.baseline_offset_x = baseline_offset_x; + out.unknown_metrics_0x08 = acc_local60_max; + out.unknown_metrics_0x0C = acc_local5c_max; + out.decoration_span = decoration_span; + out.unknown_decoration_0x08 = unknown_decoration_0x08; + out.unknown_decoration_0x0C = unknown_decoration_0x0C; + } + } + return ORBIS_OK; +} + +// LLE: FUN_01007e40 +s32 GetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetrics* metrics, + bool use_cached_style) { + auto clear_metrics = [&] { + if (metrics) { + *metrics = {}; + } + }; + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + clear_metrics(); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + if (!metrics) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + u32 prev_font_lock = 0; + if (!AcquireFontLock(font, prev_font_lock)) { + clear_metrics(); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + u32 prev_cached_lock = 0; + if (use_cached_style) { + if (!AcquireCachedStyleLock(font, prev_cached_lock)) { + ReleaseFontLock(font, prev_font_lock); + clear_metrics(); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + } + + auto* st = TryGetState(fontHandle); + if (!st) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + if (use_cached_style && font->renderer_binding.renderer == nullptr) { + font->cached_style.cache_lock_word = prev_cached_lock; + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } + + const void* style_state_block = use_cached_style ? static_cast(&font->cached_style) + : static_cast(font->style_frame); + float scale_w = 0.0f; + float scale_h = 0.0f; + const s32 scale_rc = StyleStateGetScalePixel(style_state_block, &scale_w, &scale_h); + if (scale_rc != ORBIS_OK) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return scale_rc; + } + + FT_Face resolved_face = nullptr; + if (st->ext_face_ready) { + resolved_face = st->ext_ft_face; + } else { + bool system_attached = false; + (void)ReportSystemFaceRequest(*st, fontHandle, system_attached); + if (system_attached && st->ext_face_ready) { + resolved_face = st->ext_ft_face; + } + } + + if (!resolved_face) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + + u32 resolved_code = code; + u32 resolved_font_id = 0; + float resolved_scale_factor = 1.0f; + const auto* fontset_record = + static_cast(font->open_info.fontset_record); + const bool is_internal_fontset_record = + fontset_record && + fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic; + if (font->open_info.fontset_record) { + u32 mapped_font_id = 0; + resolved_code = + ResolveSysFontCodepoint(font->open_info.fontset_record, static_cast(code), + font->open_info.fontset_flags, &mapped_font_id); + if (resolved_code == 0) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + + resolved_font_id = mapped_font_id; + resolved_scale_factor = st->system_font_scale_factor; + if (is_internal_fontset_record) { + auto* lib = static_cast(font->library); + auto* ctx = lib ? static_cast(lib->sysfonts_ctx) : nullptr; + const u32 mode_low = static_cast(font->flags) & 0x0Fu; + if (ctx && (mode_low == 1 || mode_low == 2 || mode_low == 3)) { + void* head = nullptr; + u32 lock_word = 0; + u8* entry_u8 = + AcquireFontCtxEntry(ctx, mapped_font_id, mode_low, &head, &lock_word); + auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; + auto* node = static_cast(head); + const u32 sub_font_index = font->open_info.sub_font_index; + while (node) { + if (node->sub_font_index == sub_font_index) { + break; + } + node = node->next; + } + resolved_face = node ? static_cast(node->ft_face) : nullptr; + if (entry) { + if (mode_low == 3) { + entry->lock_mode3 = lock_word & 0x7fffffffu; + } else if (mode_low == 2) { + entry->lock_mode2 = lock_word & 0x7fffffffu; + } else { + entry->lock_mode1 = lock_word & 0x7fffffffu; + } + } + } + } else if (st->system_requested) { + if (mapped_font_id == st->system_font_id) { + resolved_face = st->ext_ft_face; + } else { + resolved_face = nullptr; + for (const auto& fb : st->system_fallback_faces) { + if (fb.ready && fb.ft_face && fb.font_id == mapped_font_id) { + resolved_face = fb.ft_face; + resolved_scale_factor = fb.scale_factor; + break; + } + } + } + } + if (!resolved_face) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + } + + FT_UInt resolved_glyph_index = + FT_Get_Char_Index(resolved_face, static_cast(resolved_code)); + if (resolved_glyph_index == 0) { + if (st->ext_face_ready && st->ext_ft_face && resolved_face != st->ext_ft_face) { + const FT_UInt gi = + FT_Get_Char_Index(st->ext_ft_face, static_cast(resolved_code)); + if (gi != 0) { + resolved_face = st->ext_ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = st->system_font_scale_factor; + } + } + } + if (resolved_glyph_index == 0 && !font->open_info.fontset_record) { + for (const auto& fb : st->system_fallback_faces) { + if (!fb.ready || !fb.ft_face) { + continue; + } + const FT_UInt gi = FT_Get_Char_Index(fb.ft_face, static_cast(resolved_code)); + if (gi == 0) { + continue; + } + resolved_face = fb.ft_face; + resolved_glyph_index = gi; + resolved_scale_factor = fb.scale_factor; + break; + } + } + + if (resolved_glyph_index == 0) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + float range_scale = 1.0f; + s32 shift_x_units = 0; + s32 shift_y_units = 0; + if (font->open_info.fontset_record && !is_internal_fontset_record) { + if (st->system_requested) { + if (resolved_font_id == st->system_font_id) { + shift_y_units = st->system_font_shift_value; + } else { + for (const auto& fb : st->system_fallback_faces) { + if (fb.ready && fb.ft_face && fb.font_id == resolved_font_id) { + shift_y_units = fb.shift_value; + break; + } + } + } + } + if (shift_y_units == 0) { + shift_y_units = GetSysFontDesc(resolved_font_id).shift_value; + } + if (const std::uint8_t* range_rec = + FindSysFontRangeRecord(resolved_font_id, resolved_code)) { + alignas(4) SysFontRangeRecord rec{}; + std::memcpy(&rec, range_rec, sizeof(rec)); + const s16 range_shift_x = rec.shift_x; + const s16 range_shift_y = rec.shift_y; + range_scale = rec.scale_mul; + (void)rec.reserved_0x10; + (void)rec.reserved_0x14; + (void)rec.reserved_0x18; + + shift_x_units = static_cast(range_shift_x); + shift_y_units += static_cast(range_shift_y); + } + if (range_scale == 0.0f) { + range_scale = 1.0f; + } + } + + const float scaled_w = scale_w * resolved_scale_factor * range_scale; + const float scaled_h = scale_h * resolved_scale_factor * range_scale; + + const auto char_w = static_cast(static_cast(scaled_w * 64.0f)); + const auto char_h = static_cast(static_cast(scaled_h * 64.0f)); + if (FT_Set_Char_Size(resolved_face, char_w, char_h, 72, 72) != 0) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + if (shift_x_units != 0 || shift_y_units != 0) { + if (!resolved_face->size) { + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + const long x_scale = static_cast(resolved_face->size->metrics.x_scale); + const long y_scale = static_cast(resolved_face->size->metrics.y_scale); + + const auto round_fixed_mul = [](long fixed_16_16, s32 value) -> s32 { + const long long prod = + static_cast(fixed_16_16) * static_cast(value); + const long long sign_adj = + (static_cast(~static_cast(value)) >> 63) * -0x10000LL; + const long long base = sign_adj + prod; + long long tmp = base - 0x8000LL; + if (tmp < 0) { + tmp = base + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + FT_Vector delta{}; + delta.x = static_cast(round_fixed_mul(x_scale, shift_x_units)); + delta.y = static_cast(round_fixed_mul(y_scale, shift_y_units)); + FT_Set_Transform(resolved_face, nullptr, &delta); + } else { + FT_Set_Transform(resolved_face, nullptr, nullptr); + } + + constexpr FT_Int32 kFtLoadFlagsBase = + static_cast(FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT); + + if (FT_Load_Glyph(resolved_face, resolved_glyph_index, kFtLoadFlagsBase) != 0) { + FT_Set_Transform(resolved_face, nullptr, nullptr); + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + clear_metrics(); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + FT_Set_Transform(resolved_face, nullptr, nullptr); + + const FT_GlyphSlot slot = resolved_face->glyph; + const float bearing_x = static_cast(slot->metrics.horiBearingX) / 64.0f; + const float bearing_y = static_cast(slot->metrics.horiBearingY) / 64.0f; + const float advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + const float width_px = static_cast(slot->metrics.width) / 64.0f; + const float height_px = static_cast(slot->metrics.height) / 64.0f; + + metrics->width = width_px; + metrics->height = height_px; + metrics->Horizontal.bearingX = bearing_x; + metrics->Horizontal.bearingY = bearing_y; + metrics->Horizontal.advance = advance; + metrics->Vertical.bearingX = 0.0f; + metrics->Vertical.bearingY = 0.0f; + metrics->Vertical.advance = 0.0f; + + if (use_cached_style) { + font->cached_style.cache_lock_word = prev_cached_lock; + } + font->lock_word = prev_font_lock; + return ORBIS_OK; +} + +static StyleFrameScaleState ResolveStyleFrameScaleFromCachedStyle(const OrbisFontStyleFrame* style, + float base_scale_w, + float base_scale_h, + u32 base_dpi_x, u32 base_dpi_y) { + StyleFrameScaleState resolved{ + .scale_w = base_scale_w, + .scale_h = base_scale_h, + .dpi_x = base_dpi_x, + .dpi_y = base_dpi_y, + .scale_overridden = false, + .dpi_overridden = false, + }; + if (!style || style->magic != kStyleFrameMagic) { + return resolved; + } + if (style->hDpi != 0) { + resolved.dpi_x = style->hDpi; + resolved.dpi_overridden = true; + } + if (style->vDpi != 0) { + resolved.dpi_y = style->vDpi; + resolved.dpi_overridden = true; + } + if ((style->flags1 & kStyleFrameFlagScale) == 0) { + return resolved; + } + resolved.scale_overridden = true; + + const bool unit_is_pixel = (style->scaleUnit == 0); + if (unit_is_pixel) { + resolved.scale_w = style->scalePixelW; + resolved.scale_h = style->scalePixelH; + } else { + const u32 dpi_x = style->hDpi; + const u32 dpi_y = style->vDpi; + resolved.scale_w = dpi_x ? PointsToPixels(style->scalePixelW, dpi_x) : style->scalePixelW; + resolved.scale_h = dpi_y ? PointsToPixels(style->scalePixelH, dpi_y) : style->scalePixelH; + } + return resolved; +} + +namespace { + +struct RenderSurfaceSystemUse { + Libraries::Font::OrbisFontStyleFrame* styleframe = nullptr; + float catchedScale = 0.0f; + std::uint8_t padding[88 - sizeof(Libraries::Font::OrbisFontStyleFrame*) - sizeof(float)]{}; +}; +static_assert(sizeof(RenderSurfaceSystemUse) == + sizeof(((Libraries::Font::OrbisFontRenderSurface*)nullptr)->reserved_q), + "RenderSurfaceSystemUse layout must match OrbisFontRenderSurface::reserved_q"); + +inline RenderSurfaceSystemUse* GetSurfaceSystemUse(Libraries::Font::OrbisFontRenderSurface* surf) { + return surf ? reinterpret_cast(surf->reserved_q) : nullptr; +} +inline const RenderSurfaceSystemUse* GetSurfaceSystemUse( + const Libraries::Font::OrbisFontRenderSurface* surf) { + return surf ? reinterpret_cast(surf->reserved_q) : nullptr; +} + +// LLE: FUN_0100a690 +static const StyleStateBlock* ResolveSurfaceStyleState( + const StyleStateBlock* cached_style_state, const Libraries::Font::OrbisFontRenderSurface* surf, + const RenderSurfaceSystemUse* sys, StyleStateBlock& tmp_out) { + if (!cached_style_state) { + return nullptr; + } + if (!surf || (surf->styleFlag & 0x1) == 0 || !sys || !ValidateStyleFramePtr(sys->styleframe)) { + return cached_style_state; + } + + const auto* frame = sys->styleframe; + tmp_out = *cached_style_state; + + const u8 flags1 = frame->flags1; + if ((flags1 & kStyleFrameFlagScale) != 0) { + tmp_out.scale_unit = frame->scaleUnit; + tmp_out.scale_w = frame->scalePixelW; + tmp_out.scale_h = frame->scalePixelH; + tmp_out.dpi_x = frame->hDpi ? frame->hDpi : 0x48; + tmp_out.dpi_y = frame->vDpi ? frame->vDpi : 0x48; + } else { + const u32 want_x = (frame->hDpi == 0) ? tmp_out.dpi_x : frame->hDpi; + const u32 want_y = (frame->vDpi == 0) ? tmp_out.dpi_y : frame->vDpi; + tmp_out.dpi_x = (want_x != 0) ? 0x48 : 0; + tmp_out.dpi_y = (want_y != 0) ? 0x48 : 0; + } + + if ((flags1 & kStyleFrameFlagSlant) != 0) { + tmp_out.slant_ratio = frame->slantRatio; + } + if ((flags1 & kStyleFrameFlagWeight) != 0) { + tmp_out.effect_weight_x = frame->effectWeightX; + tmp_out.effect_weight_y = frame->effectWeightY; + } + return &tmp_out; +} + +// LLE: FUN_0100a690 +static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, + Libraries::Font::OrbisFontRenderSurface* surf, float x, + float y, Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result) { + ClearRenderOutputs(metrics, result); + if (!surf || !metrics || !result) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (!surf->buffer || surf->width <= 0 || surf->height <= 0 || surf->widthByte <= 0 || + surf->pixelSizeByte <= 0) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + + const int bpp = static_cast(surf->pixelSizeByte); + if (bpp != 1 && bpp != 4) { + return ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE; + } + + FT_Face face = static_cast(font_obj.ft_face); + if (!face || !face->size) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + FT_Vector delta{}; + delta.x = static_cast(static_cast(frac_x * 64.0f)); + delta.y = static_cast(-static_cast(frac_y * 64.0f)); + + if ((font_obj.shift_units_x != 0 || font_obj.shift_units_y != 0) && face->size) { + const long x_scale = static_cast(face->size->metrics.x_scale); + const long y_scale = static_cast(face->size->metrics.y_scale); + + const auto round_fixed_mul = [](long fixed_16_16, s32 value) -> s32 { + const long long prod = + static_cast(fixed_16_16) * static_cast(value); + const long long sign_adj = + (static_cast(~static_cast(value)) >> 63) * -0x10000LL; + const long long base = sign_adj + prod; + long long tmp = base - 0x8000LL; + if (tmp < 0) { + tmp = base + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + delta.x += static_cast(round_fixed_mul(x_scale, font_obj.shift_units_x)); + delta.y += static_cast(round_fixed_mul(y_scale, font_obj.shift_units_y)); + } + + FT_Set_Transform(face, nullptr, &delta); + + constexpr FT_Int32 kFtLoadFlagsRender = static_cast( + FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT | FT_LOAD_RENDER); + if (FT_Load_Glyph(face, static_cast(glyph_index), kFtLoadFlagsRender) != 0) { + FT_Set_Transform(face, nullptr, nullptr); + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + FT_Set_Transform(face, nullptr, nullptr); + + const FT_GlyphSlot slot = face->glyph; + if (!slot) { + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + const int glyph_w = static_cast(slot->bitmap.width); + const int glyph_h = static_cast(slot->bitmap.rows); + const int x0 = static_cast(slot->bitmap_left); + const int y0 = -static_cast(slot->bitmap_top); + + const float bearing_x = static_cast(slot->metrics.horiBearingX) / 64.0f; + const float bearing_y = static_cast(slot->metrics.horiBearingY) / 64.0f; + const float advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + const float width_px = static_cast(slot->metrics.width) / 64.0f; + const float height_px = static_cast(slot->metrics.height) / 64.0f; + + metrics->width = width_px; + metrics->height = height_px; + metrics->Horizontal.bearingX = bearing_x; + metrics->Horizontal.bearingY = bearing_y; + metrics->Horizontal.advance = advance; + metrics->Vertical.bearingX = 0.0f; + metrics->Vertical.bearingY = 0.0f; + metrics->Vertical.advance = 0.0f; + + std::vector glyph_bitmap; + glyph_bitmap.resize(static_cast(glyph_w) * static_cast(glyph_h)); + if (glyph_w > 0 && glyph_h > 0) { + const int pitch = static_cast(slot->bitmap.pitch); + const unsigned char* src = reinterpret_cast(slot->bitmap.buffer); + if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + for (int col = 0; col < glyph_w; ++col) { + const unsigned char byte = src_row[col >> 3]; + const unsigned char bit = static_cast(0x80u >> (col & 7)); + dst_row[col] = (byte & bit) ? 0xFFu : 0x00u; + } + } + } else { + for (int row = 0; row < glyph_h; ++row) { + const unsigned char* src_row = src + static_cast(row) * pitch; + unsigned char* dst_row = + glyph_bitmap.data() + static_cast(row) * glyph_w; + std::memcpy(dst_row, src_row, static_cast(glyph_w)); + } + } + } + + const int dest_x = static_cast(std::floor(x)) + x0; + const int dest_y = static_cast(std::floor(y)) + y0; + + const int surface_w = std::max(surf->width, 0); + const int surface_h = std::max(surf->height, 0); + const int clip_x0 = std::clamp(static_cast(surf->sc_x0), 0, surface_w); + const int clip_y0 = std::clamp(static_cast(surf->sc_y0), 0, surface_h); + const int clip_x1 = std::clamp(static_cast(surf->sc_x1), 0, surface_w); + const int clip_y1 = std::clamp(static_cast(surf->sc_y1), 0, surface_h); + + int update_x0 = dest_x; + int update_y0 = dest_y; + int update_w = 0; + int update_h = 0; + + if (glyph_w > 0 && glyph_h > 0 && clip_x1 > clip_x0 && clip_y1 > clip_y0) { + auto* dst_base = static_cast(surf->buffer); + const int start_row = std::max(dest_y, clip_y0); + const int end_row = std::min(dest_y + glyph_h, clip_y1); + const int start_col = std::max(dest_x, clip_x0); + const int end_col = std::min(dest_x + glyph_w, clip_x1); + + update_x0 = start_col; + update_y0 = start_row; + update_w = std::max(0, end_col - start_col); + update_h = std::max(0, end_row - start_row); + + for (int row = start_row; row < end_row; ++row) { + const int src_y = row - dest_y; + if (src_y < 0 || src_y >= glyph_h) { + continue; + } + const std::uint8_t* src_row = + glyph_bitmap.data() + static_cast(src_y) * glyph_w; + std::uint8_t* dst_row = dst_base + static_cast(row) * + static_cast(surf->widthByte); + for (int col = start_col; col < end_col; ++col) { + const int src_x = col - dest_x; + if (src_x < 0 || src_x >= glyph_w) { + continue; + } + const std::uint8_t cov = src_row[src_x]; + std::uint8_t* dst = dst_row + static_cast(col) * bpp; + if (bpp == 1) { + dst[0] = cov; + } else { + dst[0] = cov; + dst[1] = cov; + dst[2] = cov; + dst[3] = cov; + } + } + } + } + + result->stage = nullptr; + result->SurfaceImage.address = static_cast(surf->buffer); + result->SurfaceImage.widthByte = static_cast(surf->widthByte); + result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); + result->SurfaceImage.pixelFormat = 0; + result->SurfaceImage.pad16 = 0; + result->UpdateRect.x = static_cast(std::max(update_x0, 0)); + result->UpdateRect.y = static_cast(std::max(update_y0, 0)); + result->UpdateRect.w = static_cast(std::max(update_w, 0)); + result->UpdateRect.h = static_cast(std::max(update_h, 0)); + result->SurfaceImage.address = + static_cast(surf->buffer) + + static_cast(result->UpdateRect.y) * static_cast(surf->widthByte) + + static_cast(result->UpdateRect.x) * static_cast(bpp); + + const auto floor_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) > v) { + --i; + } + return i; + }; + const auto ceil_int = [](float v) -> int { + int i = static_cast(std::trunc(v)); + if (static_cast(i) < v) { + ++i; + } + return i; + }; + + const float left_f = x + metrics->Horizontal.bearingX; + const float top_f = y + metrics->Horizontal.bearingY; + const float right_f = left_f + metrics->width; + const float bottom_f = top_f - metrics->height; + + const int left_i = floor_int(left_f); + const int top_i = floor_int(top_f); + const int right_i = ceil_int(right_f); + const int bottom_i = ceil_int(bottom_f); + + const float adv_f = x + metrics->Horizontal.advance; + const float adv_snapped = static_cast(floor_int(adv_f)) - x; + + result->ImageMetrics.bearingX = static_cast(left_i) - x; + result->ImageMetrics.bearingY = static_cast(top_i) - y; + result->ImageMetrics.advance = adv_snapped; + int stride_i = right_i + 1; + const float adjust = static_cast(right_i) - right_f; + const int tmp_i = floor_int(adv_f + adjust); + const int adv_trunc_i = static_cast(std::trunc(adv_f)); + if (adv_trunc_i == 0) { + stride_i = tmp_i; + } + if (stride_i < tmp_i) { + stride_i = tmp_i; + } + result->ImageMetrics.stride = static_cast(stride_i) - x; + result->ImageMetrics.width = static_cast(std::max(0, right_i - left_i)); + result->ImageMetrics.height = static_cast(std::max(0, top_i - bottom_i)); + return ORBIS_OK; +} + +StyleFrameScaleState ResolveMergedStyleFrameScale(const Libraries::Font::OrbisFontStyleFrame* base, + const Libraries::Font::OrbisFontStyleFrame* over, + const FontState& st) { + StyleFrameScaleState resolved = ResolveStyleFrameScale(base, st); + if (!ValidateStyleFramePtr(over)) { + return resolved; + } + if ((over->flags1 & kStyleFrameFlagScale) != 0) { + resolved.scale_overridden = true; + const bool unit_is_pixel = (over->scaleUnit == 0); + const u32 dpi_x = over->hDpi; + const u32 dpi_y = over->vDpi; + resolved.scale_w = + unit_is_pixel ? over->scalePixelW + : (dpi_x ? PointsToPixels(over->scalePixelW, dpi_x) : over->scalePixelW); + resolved.scale_h = + unit_is_pixel ? over->scalePixelH + : (dpi_y ? PointsToPixels(over->scalePixelH, dpi_y) : over->scalePixelH); + } + if (!resolved.scale_overridden && st.scale_point_active) { + resolved.scale_w = PointsToPixels(st.scale_point_w, resolved.dpi_x); + resolved.scale_h = PointsToPixels(st.scale_point_h, resolved.dpi_y); + } + return resolved; +} + +} // namespace + +// LLE: FUN_0100a690 +s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 code, + Libraries::Font::OrbisFontRenderSurface* surf, float x, float y, + Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result) { + if (!fontHandle) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + if (!surf || !metrics || !result) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != 0x0F02) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (font->renderer_binding.renderer == nullptr) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; + } + + auto* lib = static_cast(font->library); + if (!lib || lib->magic != 0x0F01) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const auto* driver = reinterpret_cast(lib->sys_driver); + if (!driver || !driver->glyph_index || !driver->set_char_with_dpi || + !driver->set_char_default_dpi) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_FATAL; + } + + const auto mode_low = static_cast(font->flags) & 0x0Fu; + const u32 sub_font_index = font->open_info.sub_font_index; + + const auto* cached_style_state = reinterpret_cast(&font->cached_style); + StyleStateBlock surface_style_tmp{}; + const RenderSurfaceSystemUse* surf_sys = GetSurfaceSystemUse(surf); + const StyleStateBlock* style_state = + ResolveSurfaceStyleState(cached_style_state, surf, surf_sys, surface_style_tmp); + if (!style_state) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_FATAL; + } + + float scale_x_in = style_state->scale_w; + float scale_y_in = style_state->scale_h; + u32 dpi_x = style_state->dpi_x; + u32 dpi_y = style_state->dpi_y; + const u32 scale_unit = style_state->scale_unit; + + u32 mapped_code = code; + u32 font_id = font->open_info.ctx_entry_index; + std::uint8_t* ctx = static_cast(lib->external_fonts_ctx); + float sys_scale_factor = 1.0f; + s32 shift_x_units = 0; + s32 shift_y_units = 0; + + u32 special_case_font_id = 0xffffffffu; + if (font->open_info.fontset_record) { + const auto* fontset_record = + static_cast(font->open_info.fontset_record); + const bool is_internal_fontset_record = + fontset_record && + fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic; + ctx = static_cast(lib->sysfonts_ctx); + mapped_code = + ResolveSysFontCodepoint(font->open_info.fontset_record, static_cast(code), + font->open_info.fontset_flags, &font_id); + if (mapped_code == 0) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; + } + + if (!is_internal_fontset_record) { + const SysFontDesc desc = GetSysFontDesc(font_id); + sys_scale_factor = desc.scale_factor; + shift_y_units = desc.shift_value; + + float range_scale = 1.0f; + if (const std::uint8_t* range_rec = FindSysFontRangeRecord(font_id, mapped_code)) { + alignas(4) SysFontRangeRecord rec{}; + std::memcpy(&rec, range_rec, sizeof(rec)); + shift_x_units = static_cast(rec.shift_x); + shift_y_units += static_cast(rec.shift_y); + range_scale = rec.scale_mul; + } + if (range_scale != 0.0f) { + sys_scale_factor *= range_scale; + } + } + + if (font_id == 10) { + special_case_font_id = font_id; + } + } + + void* font_obj_void = nullptr; + u32 lock_word = 0; + std::uint8_t* entry_u8 = AcquireFontCtxEntryAndSelectNode( + ctx, font_id, mode_low, sub_font_index, &font_obj_void, &lock_word); + auto* font_obj = static_cast(font_obj_void); + if (!entry_u8 || !font_obj) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_FATAL; + } + + s32 rc = ORBIS_FONT_ERROR_FATAL; + const auto cleanup_return = [&](s32 ret) -> s32 { + ReleaseFontCtxEntryLock(entry_u8, mode_low, lock_word); + if (ret != ORBIS_OK) { + ClearRenderOutputs(metrics, result); + } + return ret; + }; + + if (((lock_word & 0x40000000u) == 0) || ((lock_word & 0x0FFFFFFFu) == 0)) { + return cleanup_return(rc); + } + + font_obj->reserved_0x04 = static_cast(font->flags >> 15); + font_obj->font_handle = fontHandle; + font_obj->shift_units_x = shift_x_units; + font_obj->shift_units_y = shift_y_units; + font_obj->layout_seed_pair = 0; + + (void)StyleStateGetScalePixel(style_state, &font_obj->scale_x_0x50, &font_obj->scale_y_0x54); + + scale_x_in *= sys_scale_factor; + scale_y_in *= sys_scale_factor; + + float out_scale_x = 0.0f; + float out_scale_y = 0.0f; + if (scale_unit == 0) { + rc = driver->set_char_default_dpi(font_obj, scale_x_in, scale_y_in, &out_scale_x, + &out_scale_y); + } else { + if (dpi_x == 0) { + dpi_x = 0x48; + } + if (dpi_y == 0) { + dpi_y = 0x48; + } + rc = driver->set_char_with_dpi(font_obj, dpi_x, dpi_y, scale_x_in, scale_y_in, &out_scale_x, + &out_scale_y); + } + if (rc != ORBIS_OK) { + return cleanup_return(ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH); + } + + u32 glyph_index = 0; + rc = driver->glyph_index(font_obj, mapped_code, &glyph_index); + if (rc != ORBIS_OK || glyph_index == 0) { + return cleanup_return(ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH); + } + + if (special_case_font_id != 0xffffffffu) { + ApplyGlyphSpecialCaseAdjust(static_cast(special_case_font_id), + static_cast(glyph_index), + reinterpret_cast(&font_obj->layout_seed_pair)); + } + + rc = RenderGlyphIndexToSurface(*font_obj, glyph_index, surf, x, y, metrics, result); + return cleanup_return(rc); +} + +// LLE: FUN_0100e050 +s32 RenderGlyphImageCore(Libraries::Font::OrbisFontGlyph fontGlyph, + Libraries::Font::OrbisFontStyleFrame* fontStyleFrame, + Libraries::Font::OrbisFontRenderer fontRenderer, + Libraries::Font::OrbisFontRenderSurface* surface, float x, float y, + int mode, Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result) { + if (!fontRenderer) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + if (!surface || !metrics || !result) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (!fontGlyph || fontGlyph->magic != 0x0F03) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_GLYPH; + } + + auto* gg = TryGetGeneratedGlyph(fontGlyph); + if (!gg) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_GLYPH; + } + auto* st = TryGetState(gg->owner_handle); + if (!st) { + ClearRenderOutputs(metrics, result); + return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; + } + + const auto previous_renderer = st->bound_renderer; + st->bound_renderer = fontRenderer; + + RenderSurfaceSystemUse* sys = GetSurfaceSystemUse(surface); + const OrbisFontStyleFrame* surface_frame = nullptr; + if ((surface->styleFlag & 0x1) != 0 && sys && ValidateStyleFramePtr(sys->styleframe)) { + surface_frame = sys->styleframe; + } + const OrbisFontStyleFrame* call_frame = + ValidateStyleFramePtr(fontStyleFrame) ? fontStyleFrame : nullptr; + + const StyleFrameScaleState style_scale = + ResolveMergedStyleFrameScale(surface_frame, call_frame, *st); + + float x_used = x; + float y_used = y; + const s16 baseline = static_cast(fontGlyph->baseline); + char baseline_mode = 0; + s16 metrics_axis = 1; + float baseline_x = 0.0f; + float baseline_y = 0.0f; + + if (mode == 2) { + metrics_axis = 2; + baseline_mode = static_cast(-0x56); + if (baseline < 0) { + baseline_mode = 0; + } + } else if (mode == 1) { + metrics_axis = 1; + baseline_mode = static_cast(-0x78); + if (baseline >= 0) { + baseline_mode = 0; + } + } else { + baseline_mode = (baseline < 0) ? '`' : '\x06'; + metrics_axis = static_cast(1 - (baseline >> 15)); + + if ((surface->styleFlag & 0x1) != 0 && sys && surface_frame) { + const float cached = sys->catchedScale; + if (std::abs(cached) > kScaleEpsilon) { + if (baseline >= 1) { + y_used += cached; + baseline_mode = 0; + } else if (baseline < 0) { + x_used += cached; + metrics_axis = 2; + baseline_mode = 0; + } + } + } + } + + if (baseline_mode == '`') { + baseline_x = static_cast(baseline); + } else if (baseline_mode == '\x06') { + baseline_y = static_cast(baseline); + } + + FT_Face face = nullptr; + if (!ResolveFace(*st, gg->owner_handle, face)) { + ClearRenderOutputs(metrics, result); + st->bound_renderer = previous_renderer; + return ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + float scale_y = gg->scale_y_used; + if (scale_y <= kScaleEpsilon) { + const float pixel_h = + (fontGlyph->base_scale > kScaleEpsilon) ? fontGlyph->base_scale : st->scale_h; + scale_y = ComputeScaleExtForState(*st, face, pixel_h); + } + float scale_x = gg->scale_x_used > kScaleEpsilon ? gg->scale_x_used : scale_y; + + if (style_scale.scale_overridden) { + float style_scale_y = 0.0f; + if (ResolveFaceAndScale(*st, gg->owner_handle, style_scale.scale_h, face, style_scale_y)) { + scale_y = style_scale_y; + scale_x = (style_scale.scale_h > kScaleEpsilon) + ? style_scale_y * (style_scale.scale_w / style_scale.scale_h) + : style_scale_y; + } + } else if (fontGlyph->base_scale > kScaleEpsilon && fontGlyph->scale_x > kScaleEpsilon) { + scale_x = scale_y * (fontGlyph->scale_x / fontGlyph->base_scale); + } + + (void)metrics_axis; + (void)baseline_x; + (void)baseline_y; + + const s32 rc = + RenderCodepointToSurfaceWithScale(*st, gg->owner_handle, face, scale_x, scale_y, surface, + gg->codepoint, x_used, y_used, metrics, result); + + st->bound_renderer = previous_renderer; + return rc; +} + +// LLE: FUN_0100b6f0 +s32 StyleStateSetScalePixel(void* style_state_block, float w, float h) { + auto* st = static_cast(style_state_block); + if (!st) { + return 0; + } + if (st->scale_w == w) { + if (st->scale_h == h && st->scale_unit == 0) { + return 0; + } + } + st->scale_unit = 0; + st->scale_w = w; + st->scale_h = h; + return 1; +} + +// LLE: FUN_0100b730 +s32 StyleStateSetScalePoint(void* style_state_block, float w, float h) { + auto* st = static_cast(style_state_block); + if (!st) { + return 0; + } + if (st->scale_w == w) { + if (st->scale_h == h && st->scale_unit == 1) { + return 0; + } + } + st->scale_unit = 1; + st->scale_w = w; + st->scale_h = h; + return 1; +} + +// LLE: FUN_0100b770 +s32 StyleStateGetScalePixel(const void* style_state_block, float* w, float* h) { + const auto* st = static_cast(style_state_block); + if (!st || (!w && !h)) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const u32 flag = st->scale_unit; + if (w) { + float out = st->scale_w; + const u32 dpi = st->dpi_x; + if (flag != 0 && dpi != 0) { + out *= static_cast(dpi) / kPointsPerInch; + } + *w = out; + if (!h) { + return ORBIS_OK; + } + } + + float out = st->scale_h; + const u32 dpi = st->dpi_y; + if (flag != 0 && dpi != 0) { + out *= static_cast(dpi) / kPointsPerInch; + } + *h = out; + return ORBIS_OK; +} + +// LLE: FUN_0100b7e0 +s32 StyleStateGetScalePoint(const void* style_state_block, float* w, float* h) { + const auto* st = static_cast(style_state_block); + if (!st || (!w && !h)) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const u32 flag = st->scale_unit; + if (w) { + float out = st->scale_w; + const u32 dpi = st->dpi_x; + if (flag == 0 && dpi != 0) { + out *= kPointsPerInch / static_cast(dpi); + } + *w = out; + if (!h) { + return ORBIS_OK; + } + } + + float out = st->scale_h; + const u32 dpi = st->dpi_y; + if (flag == 0 && dpi != 0) { + out *= kPointsPerInch / static_cast(dpi); + } + *h = out; + return ORBIS_OK; +} + +// LLE: FUN_0100b860 +s32 StyleStateSetDpi(u32* dpi_pair, u32 h_dpi, u32 v_dpi) { + if (!dpi_pair) { + return 0; + } + if (h_dpi == 0) { + h_dpi = 0x48; + } + if (v_dpi == 0) { + v_dpi = 0x48; + } + if (dpi_pair[0] == h_dpi && dpi_pair[1] == v_dpi) { + return 0; + } + dpi_pair[0] = h_dpi; + dpi_pair[1] = v_dpi; + return 1; +} + +// LLE: FUN_0100b8b0 +s32 StyleStateSetSlantRatio(void* style_state_block, float slantRatio) { + auto* st = static_cast(style_state_block); + if (!st) { + return 0; + } + const float prev = st->slant_ratio; + if (prev == slantRatio) { + return 0; + } + + float clamped = slantRatio; + if (clamped > 1.0f) { + clamped = 1.0f; + } else if (clamped < -1.0f) { + clamped = -1.0f; + } + st->slant_ratio = clamped; + return 1; +} + +// LLE: FUN_0100b8f0 +s32 StyleStateGetSlantRatio(const void* style_state_block, float* slantRatio) { + const auto* st = static_cast(style_state_block); + if (!st || !slantRatio) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + *slantRatio = st->slant_ratio; + return ORBIS_OK; +} + +// LLE: FUN_0100b910 +s32 StyleStateSetWeightScale(void* style_state_block, float weightXScale, float weightYScale) { + auto* st = static_cast(style_state_block); + if (!st) { + return 0; + } + + constexpr float kClamp = 0.04f; + auto clamp_delta = [&](float delta) { + if (delta > kClamp) { + return kClamp; + } + if (delta < -kClamp) { + return -kClamp; + } + return delta; + }; + + bool changed = false; + const float dx = clamp_delta(weightXScale - 1.0f); + const float prev_x = st->effect_weight_x; + if (!(prev_x == dx)) { + st->effect_weight_x = dx; + changed = true; + } + + const float dy = clamp_delta(weightYScale - 1.0f); + const float prev_y = st->effect_weight_y; + if (!(prev_y == dy)) { + st->effect_weight_y = dy; + changed = true; + } + + return changed ? 1 : 0; +} + +// LLE: FUN_0100b9a0 +s32 StyleStateGetWeightScale(const void* style_state_block, float* weightXScale, + float* weightYScale, u32* mode) { + if ((reinterpret_cast(weightXScale) | + reinterpret_cast(weightYScale) | reinterpret_cast(mode)) == + 0) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + const auto* st = static_cast(style_state_block); + if (!st) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (weightXScale) { + *weightXScale = st->effect_weight_x + 1.0f; + } + if (weightYScale) { + *weightYScale = st->effect_weight_y + 1.0f; + } + if (mode) { + *mode = 0; + } + return ORBIS_OK; +} + +} // namespace Libraries::Font::Internal + +namespace Libraries::FontFt::Internal { + +namespace { + +using Libraries::Font::Internal::FontLibOpaque; +using Libraries::Font::Internal::FontObj; + +struct FtLibraryCtx { + void* alloc_ctx; + void** alloc_vtbl; + FT_Memory ft_memory; + FT_Library ft_lib; +}; + +static constexpr float kOneOver64 = 1.0f / 64.0f; + +static std::optional ResolveKnownSysFontAlias( + const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { + const auto resolve_existing = + [&](std::string_view filename) -> std::optional { + const std::filesystem::path file_path{std::string(filename)}; + std::error_code ec; + { + const auto candidate = sysfonts_dir / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + { + const auto candidate = sysfonts_dir / "font2" / file_path; + if (std::filesystem::exists(candidate, ec)) { + return candidate; + } + } + return std::nullopt; + }; + + static constexpr std::array, 41> kAliases = {{ + {"SST-EU-ROMAN-L.OTF", "SST-Light.otf"}, + {"SST-EU-ROMAN.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-M.OTF", "SST-Medium.otf"}, + {"SST-EU-ROMAN-R.OTF", "SST-Roman.otf"}, + {"SST-EU-ROMAN-I.OTF", "SST-Italic.otf"}, + {"SST-EU-ROMAN-B.OTF", "SST-Bold.otf"}, + {"SST-EU-ROMAN-BI.OTF", "SST-BoldItalic.otf"}, + {"SST-ITALIC-L.OTF", "SST-LightItalic.otf"}, + {"SST-ITALIC-R.OTF", "SST-Italic.otf"}, + {"SST-ITALIC-M.OTF", "SST-MediumItalic.otf"}, + {"SST-ITALIC-B.OTF", "SST-BoldItalic.otf"}, + {"SST-TYPEWRITER-R.OTF", "SSTTypewriter-Roman.otf"}, + {"SST-TYPEWRITER-B.OTF", "SSTTypewriter-Bd.otf"}, + {"SST-JPPRO-R.OTF", "SSTJpPro-Regular.otf"}, + {"SST-JPPRO-B.OTF", "SSTJpPro-Bold.otf"}, + {"SST-CNGB-HEI-R.TTF", "DFHEI5-SONY.ttf"}, + {"SST-ARIB-STD-B24-R.TTF", "SSTAribStdB24-Regular.ttf"}, + {"SST-ARABIC-R.OTF", "SSTArabic-Roman.otf"}, + {"SST-ARABIC-L.OTF", "SSTArabic-Light.otf"}, + {"SST-ARABIC-M.OTF", "SSTArabic-Medium.otf"}, + {"SST-ARABIC-B.OTF", "SSTArabic-Bold.otf"}, + {"SCE-EXT-HANGUL-L.OTF", "SCEPS4Yoongd-Light.otf"}, + {"SCE-EXT-HANGUL-R.OTF", "SCEPS4Yoongd-Medium.otf"}, + {"SCE-EXT-HANGUL-B.OTF", "SCEPS4Yoongd-Bold.otf"}, + {"SSTCC-SERIF-MONO.OTF", "e046323ms.ttf"}, + {"SSTCC-SERIF.OTF", "e046323ts.ttf"}, + {"SSTCC-SANSSERIF-MONO.OTF", "n023055ms.ttf"}, + {"SSTCC-SANSSERIF.OTF", "n023055ts.ttf"}, + {"SSTCC-CUSUAL.OTF", "d013013ds.ttf"}, + {"SSTCC-CURSIVE.OTF", "k006004ds.ttf"}, + {"SSTCC-SMALLCAPITAL.OTF", "c041056ts.ttf"}, + {"SCE-JP-CATTLEYA-L.OTF", "SCE-RDC-R-JPN.otf"}, + {"SCE-JP-CATTLEYA-B.OTF", "SCE-RDC-B-JPN.otf"}, + {"SST-THAI-L.OTF", "SSTThai-Light.otf"}, + {"SST-THAI-R.OTF", "SSTThai-Roman.otf"}, + {"SST-THAI-M.OTF", "SSTThai-Medium.otf"}, + {"SST-THAI-B.OTF", "SSTThai-Bold.otf"}, + {"SST-VIETNAMESE-L.OTF", "SSTVietnamese-Light.otf"}, + {"SST-VIETNAMESE-R.OTF", "SSTVietnamese-Roman.otf"}, + {"SST-VIETNAMESE-M.OTF", "SSTVietnamese-Medium.otf"}, + {"SST-VIETNAMESE-B.OTF", "SSTVietnamese-Bold.otf"}, + }}; + + for (const auto& [from, to] : kAliases) { + if (ps4_filename == from) { + return resolve_existing(to); + } + if (ps4_filename == to) { + if (auto reverse = resolve_existing(from)) { + return reverse; + } + } + } + return std::nullopt; +} + +static constexpr u32 MakeTag(char a, char b, char c, char d) { + return (static_cast(static_cast(a)) << 24) | + (static_cast(static_cast(b)) << 16) | + (static_cast(static_cast(c)) << 8) | static_cast(static_cast(d)); +} + +struct BeU16 { + u8 b[2]; + + constexpr u16 value() const { + return static_cast((static_cast(b[0]) << 8) | static_cast(b[1])); + } +}; +static_assert(sizeof(BeU16) == 2, "BeU16 size"); + +struct BeU32 { + u8 b[4]; + + constexpr u32 value() const { + return (static_cast(b[0]) << 24) | (static_cast(b[1]) << 16) | + (static_cast(b[2]) << 8) | static_cast(b[3]); + } +}; +static_assert(sizeof(BeU32) == 4, "BeU32 size"); + +struct TtcHeader { + BeU32 tag; + BeU32 version; + BeU32 num_fonts; +}; +static_assert(sizeof(TtcHeader) == 0x0C, "TtcHeader size"); + +struct SfntOffsetTable { + BeU32 version; + BeU16 num_tables; + BeU16 search_range; + BeU16 entry_selector; + BeU16 range_shift; +}; +static_assert(sizeof(SfntOffsetTable) == 0x0C, "SfntOffsetTable size"); + +struct SfntTableRecord { + BeU32 tag; + BeU32 checksum; + BeU32 offset; + BeU32 length; +}; +static_assert(sizeof(SfntTableRecord) == 0x10, "SfntTableRecord size"); + +static bool ResolveSfntBaseOffset(const u8* data, std::size_t size, u32 subFontIndex, + u32& out_base) { + out_base = 0; + if (!data || size < 4) { + return false; + } + + const u32 sig = static_cast(static_cast(data))->value(); + if (sig != MakeTag('t', 't', 'c', 'f')) { + return true; + } + + if (size < sizeof(TtcHeader)) { + return false; + } + + const auto* header = static_cast(static_cast(data)); + const u32 num_fonts = header->num_fonts.value(); + if (num_fonts == 0 || subFontIndex >= num_fonts) { + return false; + } + + const std::size_t offsets_off = 0x0C; + const std::size_t want_off = offsets_off + static_cast(subFontIndex) * 4; + if (want_off + 4 > size) { + return false; + } + + const u32 base = static_cast(static_cast(data + want_off))->value(); + if (base > size || (size - base) < 0x0C) { + return false; + } + out_base = base; + return true; +} + +static bool FindSfntTable(const u8* data, std::size_t size, u32 base, u32 tag, u32& out_off, + u32& out_len) { + out_off = 0; + out_len = 0; + if (!data || base > size || (size - base) < 0x0C) { + return false; + } + + const u8* sfnt = data + base; + const std::size_t sfnt_size = size - base; + + if (sfnt_size < sizeof(SfntOffsetTable)) { + return false; + } + const auto* offset_table = static_cast(static_cast(sfnt)); + const u16 num_tables = offset_table->num_tables.value(); + const std::size_t dir_off = sizeof(SfntOffsetTable); + const std::size_t record_size = sizeof(SfntTableRecord); + const std::size_t dir_size = dir_off + static_cast(num_tables) * record_size; + if (dir_size > sfnt_size) { + return false; + } + + for (u16 i = 0; i < num_tables; i++) { + const auto* rec = static_cast( + static_cast(sfnt + dir_off + static_cast(i) * record_size)); + const u32 rec_tag = rec->tag.value(); + if (rec_tag != tag) { + continue; + } + const u32 off = rec->offset.value(); + const u32 len = rec->length.value(); + if (off > sfnt_size || len > (sfnt_size - off)) { + return false; + } + out_off = base + off; + out_len = len; + return true; + } + return false; +} + +static bool ReadUnitsPerEm(const u8* data, std::size_t size, u32 base, u16& out_units) { + out_units = 0; + u32 head_off = 0; + u32 head_len = 0; + if (!FindSfntTable(data, size, base, MakeTag('h', 'e', 'a', 'd'), head_off, head_len)) { + return false; + } + if (head_len < 0x14 || head_off + 0x14 > size) { + return false; + } + out_units = + static_cast(static_cast(data + head_off + 0x12))->value(); + return out_units != 0; +} + +struct FontObjSidecar { + const u8* font_data = nullptr; + u32 font_size = 0; + u32 sfnt_base = 0; + void* owned_data = nullptr; +}; + +static std::mutex g_font_obj_sidecars_mutex; +static std::unordered_map g_font_obj_sidecars; + +static void SetFontObjSidecar(const FontObj* obj, FontObjSidecar sidecar) { + if (!obj) { + return; + } + std::lock_guard lock(g_font_obj_sidecars_mutex); + g_font_obj_sidecars.insert_or_assign(obj, std::move(sidecar)); +} + +static std::optional TakeFontObjSidecar(const FontObj* obj) { + if (!obj) { + return std::nullopt; + } + std::lock_guard lock(g_font_obj_sidecars_mutex); + auto it = g_font_obj_sidecars.find(obj); + if (it == g_font_obj_sidecars.end()) { + return std::nullopt; + } + FontObjSidecar out = std::move(it->second); + g_font_obj_sidecars.erase(it); + return out; +} + +static void* FtAlloc(FT_Memory memory, long size) { + if (!memory || size <= 0) { + return nullptr; + } + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->alloc_vtbl) { + return nullptr; + } + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); + return alloc_fn ? alloc_fn(ctx->alloc_ctx, static_cast(size)) : nullptr; +} + +static void FtFree(FT_Memory memory, void* block) { + if (!memory || !block) { + return; + } + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->alloc_vtbl) { + return; + } + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn = reinterpret_cast(ctx->alloc_vtbl[1]); + if (free_fn) { + free_fn(ctx->alloc_ctx, block); + } +} + +static void* FtRealloc(FT_Memory memory, long cur_size, long new_size, void* block) { + if (!memory) { + return nullptr; + } + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->alloc_vtbl) { + return nullptr; + } + using ReallocFn = void*(PS4_SYSV_ABI*)(void* object, void* p, u32 size); + const auto realloc_fn = reinterpret_cast(ctx->alloc_vtbl[2]); + if (realloc_fn) { + return realloc_fn(ctx->alloc_ctx, block, static_cast(new_size)); + } + + if (new_size <= 0) { + FtFree(memory, block); + return nullptr; + } + void* out = FtAlloc(memory, new_size); + if (!out) { + return nullptr; + } + if (block && cur_size > 0) { + std::memcpy(out, block, static_cast(std::min(cur_size, new_size))); + } + FtFree(memory, block); + return out; +} + +} // namespace + +u32 PS4_SYSV_ABI LibraryGetPixelResolutionStub() { + return 0x40; +} + +s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { + if (!memory || !library) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const auto* mem = static_cast(memory); + if (!mem->iface || !mem->iface->alloc || !mem->iface->dealloc) { + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = reinterpret_cast(mem->iface->alloc); + const auto free_fn = reinterpret_cast(mem->iface->dealloc); + if (!alloc_fn || !free_fn) { + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + + void* alloc_ctx = mem->mspace_handle; + void** alloc_vtbl = + reinterpret_cast(const_cast(mem->iface)); + + auto* ctx = static_cast(alloc_fn(alloc_ctx, sizeof(FtLibraryCtx))); + if (!ctx) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(ctx, 0, sizeof(FtLibraryCtx)); + ctx->alloc_ctx = alloc_ctx; + ctx->alloc_vtbl = alloc_vtbl; + + FT_Memory ft_mem = static_cast(alloc_fn(alloc_ctx, sizeof(FT_MemoryRec_))); + if (!ft_mem) { + free_fn(alloc_ctx, ctx); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(ft_mem, 0, sizeof(*ft_mem)); + ft_mem->user = ctx; + ft_mem->alloc = &FtAlloc; + ft_mem->free = &FtFree; + ft_mem->realloc = &FtRealloc; + ctx->ft_memory = ft_mem; + + FT_Library ft_lib = nullptr; + const FT_Error ft_err = FT_New_Library(ft_mem, &ft_lib); + if (ft_err != 0 || !ft_lib) { + free_fn(alloc_ctx, ft_mem); + free_fn(alloc_ctx, ctx); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + FT_Add_Default_Modules(ft_lib); + ctx->ft_lib = ft_lib; + + auto* lib = static_cast(library); + lib->fontset_registry = ctx; + lib->flags = 0x60000000; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI FtRendererCreate(void* renderer) { + if (!renderer) { + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + + auto* r = static_cast(renderer); + r->ft_backend.renderer_header_0x10 = static_cast(&r->base.mem_kind); + r->ft_backend.unknown_0x08 = 0; + r->ft_backend.unknown_0x10 = 0; + r->ft_backend.unknown_0x18 = 0; + r->ft_backend.initialized_marker = r; + return ORBIS_OK; +} + +u64 PS4_SYSV_ABI FtRendererQuery(void* /*renderer*/, u8* /*params*/, s64* out_ptr, + u8 (*out_vec)[16]) { + if (out_vec) { + std::memset(out_vec, 0, sizeof(*out_vec)); + } + if (out_ptr) { + *out_ptr = 0; + } + return 0; +} + +s32 PS4_SYSV_ABI FtRendererDestroy(void* renderer) { + if (!renderer) { + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + auto* r = static_cast(renderer); + r->ft_backend.initialized_marker = nullptr; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryTermStub(void* library) { + if (!library) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* lib = static_cast(library); + auto* alloc_ctx = lib->alloc_ctx; + auto* alloc_vtbl = lib->alloc_vtbl; + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; + if (!free_fn) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* ctx = static_cast(lib->fontset_registry); + if (!ctx) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (ctx->ft_lib) { + FT_Done_Library(ctx->ft_lib); + ctx->ft_lib = nullptr; + } + if (ctx->ft_memory) { + free_fn(alloc_ctx, ctx->ft_memory); + ctx->ft_memory = nullptr; + } + free_fn(alloc_ctx, ctx); + lib->fontset_registry = nullptr; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibrarySupportStub(void* library, u32 formats) { + if (!library) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto* lib = static_cast(library); + if (!lib->fontset_registry) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + u32 mapped = formats * 2u & 0x100u; + const u32 inv_formats = ~formats; + u32 tmp = mapped + 0x4CC0u; + if ((inv_formats & 0x52u) != 0) { + tmp = mapped; + } + mapped = tmp | 0x4C40u; + if ((inv_formats & 0x42u) != 0) { + mapped = tmp; + } + tmp = mapped | 0x4C80u; + if ((inv_formats & 0x50u) != 0) { + tmp = mapped; + } + mapped = tmp | 0x0C80u; + if ((formats & 0x10u) == 0) { + mapped = tmp; + } + tmp = mapped | 0x8C80u; + if ((inv_formats & 0x30u) != 0) { + tmp = mapped; + } + mapped = tmp | 0x1820u; + if ((formats & 8u) == 0) { + mapped = tmp; + } + tmp = mapped | 0x1808u; + if ((formats & 1u) == 0) { + tmp = mapped; + } + mapped = tmp | 0x1C90u; + if ((inv_formats & 0x14u) != 0) { + mapped = tmp; + } + (void)mapped; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* fontAddress, + u32 fontSize, u32 subFontIndex, u32 /*uniqueWord*/, + void** inoutFontObj) { + if (!library || !fontAddress || !inoutFontObj) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* lib = static_cast(library); + void* alloc_ctx = lib->alloc_ctx; + void** alloc_vtbl = lib->alloc_vtbl; + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto alloc_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[0]) : nullptr; + const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; + if (!alloc_fn || !free_fn) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + const u8* data = nullptr; + u32 size = 0; + void* owned_data = nullptr; + + auto assign_from_bytes = [&](std::vector&& bytes) -> s32 { + if (bytes.empty() || bytes.size() > std::numeric_limits::max()) { + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + + owned_data = alloc_fn(alloc_ctx, static_cast(bytes.size())); + if (!owned_data) { + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + std::memcpy(owned_data, bytes.data(), bytes.size()); + data = static_cast(owned_data); + size = static_cast(bytes.size()); + return ORBIS_OK; + }; + + if (mode == 1) { + if (fontSize == 0) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + data = static_cast(fontAddress); + size = fontSize; + } else if (mode == 5 || mode == 6 || mode == 7) { + const char* path = static_cast(fontAddress); + if (!path || path[0] == '\0') { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + std::string open_path = path; + std::filesystem::path host_path_fs{}; + if (path[0] == '/') { + auto* mnt = Common::Singleton::Instance(); + host_path_fs = mnt ? mnt->GetHostPath(path) : std::filesystem::path{}; + if (!host_path_fs.empty()) { + open_path = host_path_fs.string(); + } + } + + std::ifstream file(open_path, std::ios::binary | std::ios::ate); + if (!file && !host_path_fs.empty()) { + const auto sysfonts_dir = host_path_fs.parent_path(); + const auto ps4_name = host_path_fs.filename().string(); + const std::filesystem::path file_path{ps4_name}; + const auto in_font_dir = sysfonts_dir / "font" / file_path; + const auto in_font2_dir = sysfonts_dir / "font2" / file_path; + std::error_code ec; + if (std::filesystem::exists(in_font_dir, ec)) { + open_path = in_font_dir.string(); + file = std::ifstream(open_path, std::ios::binary | std::ios::ate); + } else if (std::filesystem::exists(in_font2_dir, ec)) { + open_path = in_font2_dir.string(); + file = std::ifstream(open_path, std::ios::binary | std::ios::ate); + } else if (const auto alias = ResolveKnownSysFontAlias(sysfonts_dir, ps4_name)) { + open_path = alias->string(); + file = std::ifstream(open_path, std::ios::binary | std::ios::ate); + } + } + if (!file) { + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + const std::streamoff fsize = file.tellg(); + if (fsize <= 0 || static_cast(fsize) > std::numeric_limits::max()) { + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + file.seekg(0, std::ios::beg); + + std::vector bytes(static_cast(fsize)); + if (!file.read(reinterpret_cast(bytes.data()), + static_cast(bytes.size()))) { + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; + } + const s32 store_rc = assign_from_bytes(std::move(bytes)); + if (store_rc != ORBIS_OK) { + return store_rc; + } + } else { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + auto* ctx = static_cast(lib->fontset_registry); + if (!ctx || !ctx->ft_lib) { + if (owned_data) { + free_fn(alloc_ctx, owned_data); + } + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + FT_Face face = nullptr; + const FT_Error ft_err = + FT_New_Memory_Face(ctx->ft_lib, reinterpret_cast(data), + static_cast(size), static_cast(subFontIndex), &face); + if (ft_err != 0 || !face) { + if (owned_data) { + free_fn(alloc_ctx, owned_data); + } + return ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT; + } + + (void)FT_Select_Charmap(face, FT_ENCODING_UNICODE); + + auto* obj = static_cast(alloc_fn(alloc_ctx, sizeof(FontObj))); + if (!obj) { + FT_Done_Face(face); + if (owned_data) { + free_fn(alloc_ctx, owned_data); + } + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + + std::memset(obj, 0, sizeof(FontObj)); + obj->refcount = 1; + obj->sub_font_index = subFontIndex; + obj->prev = nullptr; + obj->next = static_cast(*inoutFontObj); + if (obj->next) { + obj->next->prev = obj; + } + obj->open_ctx_0x28 = nullptr; + obj->ft_face = face; + obj->font_handle = nullptr; + obj->shift_units_x = 0; + obj->shift_units_y = 0; + obj->layout_seed_pair = 0; + obj->ft_ctx_0x58 = &ctx->ft_lib; + + FontObjSidecar sidecar{}; + sidecar.font_data = data; + sidecar.font_size = size; + sidecar.owned_data = owned_data; + (void)ResolveSfntBaseOffset(data, size, subFontIndex, sidecar.sfnt_base); + SetFontObjSidecar(obj, std::move(sidecar)); + + *inoutFontObj = obj; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { + if (!fontObj) { + return ORBIS_FONT_ERROR_FATAL; + } + + auto* obj = static_cast(fontObj); + if (obj->refcount > 1) { + obj->refcount--; + return ORBIS_OK; + } + + FT_Face face = static_cast(obj->ft_face); + FtLibraryCtx* ctx = nullptr; + if (face && face->memory) { + ctx = static_cast(face->memory->user); + } + + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn = + (ctx && ctx->alloc_vtbl) ? reinterpret_cast(ctx->alloc_vtbl[1]) : nullptr; + void* owned_data = nullptr; + if (const auto sidecar = TakeFontObjSidecar(obj)) { + owned_data = sidecar->owned_data; + } + + if (face) { + FT_Done_Face(face); + obj->ft_face = nullptr; + } + if (owned_data && free_fn) { + free_fn(ctx->alloc_ctx, owned_data); + } + if (free_fn) { + FontObj* next = obj->next; + if (obj->prev == nullptr) { + if (next != nullptr) { + next->prev = nullptr; + } + } else { + obj->prev->next = next; + } + free_fn(ctx->alloc_ctx, obj); + return ORBIS_OK; + } + return ORBIS_FONT_ERROR_FATAL; +} + +s32 PS4_SYSV_ABI LibraryGetFaceScaleStub(void* fontObj, u16* outUnitsPerEm, float* outScale) { + if (!fontObj || !outUnitsPerEm || !outScale) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const auto* face = static_cast(obj->ft_face); + if (!face) { + return ORBIS_FONT_ERROR_FATAL; + } + const u16 units = face->units_per_EM; + *outUnitsPerEm = units; + *outScale = static_cast(static_cast(units)) * kOneOver64; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryGetFaceMetricStub(void* fontObj, u32 metricId, u16* outMetric) { + if (!fontObj || !outMetric) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const auto* face = static_cast(obj->ft_face); + if (!face) { + return ORBIS_FONT_ERROR_FATAL; + } + + const u16 units = face->units_per_EM; + if (metricId == 0x0e00) { + *outMetric = units; + return ORBIS_OK; + } + if (metricId == 0xea00) { + const TT_OS2* os2 = + static_cast(FT_Get_Sfnt_Table(const_cast(face), ft_sfnt_os2)); + if (os2) { + *outMetric = static_cast(os2->sTypoAscender); + return ORBIS_OK; + } + *outMetric = units; + return ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + + *outMetric = 0; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryGetGlyphIndexStub(void* fontObj, u32 codepoint_u16, u32* out_glyph_index) { + if (!fontObj || !out_glyph_index) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const FT_Face face = static_cast(obj->ft_face); + if (!face) { + *out_glyph_index = 0; + return ORBIS_FONT_ERROR_FATAL; + } + + const auto glyph_index = + static_cast(FT_Get_Char_Index(face, static_cast(codepoint_u16))); + *out_glyph_index = glyph_index; + return glyph_index ? ORBIS_OK : ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; +} + +s32 PS4_SYSV_ABI LibrarySetCharSizeWithDpiStub(void* fontObj, u32 dpi_x, u32 dpi_y, float scale_x, + float scale_y, float* out_scale_x, + float* out_scale_y) { + if (!fontObj || !out_scale_x || !out_scale_y) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const FT_Face face = static_cast(obj->ft_face); + if (!face) { + return ORBIS_FONT_ERROR_FATAL; + } + + const auto char_w = static_cast(static_cast(scale_x * 64.0f)); + const auto char_h = static_cast(static_cast(scale_y * 64.0f)); + if (FT_Set_Char_Size(face, char_w, char_h, dpi_x, dpi_y) != 0) { + return ORBIS_FONT_ERROR_FATAL; + } + + const FT_Size size = face->size; + if (!size) { + return ORBIS_FONT_ERROR_FATAL; + } + + const u16 units = face->units_per_EM; + const long x_scale = static_cast(size->metrics.x_scale); + const long y_scale = static_cast(size->metrics.y_scale); + + auto fixed_mul_units_to_f26dot6 = [](long fixed_16_16, u16 units_per_em) -> float { + const long prod = static_cast(static_cast(fixed_16_16) * + static_cast(units_per_em)); + long rounded = prod + 0xFFFF; + if (prod >= 0) { + rounded = prod; + } + const long v = rounded >> 16; + return static_cast(v) * kOneOver64; + }; + + *out_scale_x = fixed_mul_units_to_f26dot6(x_scale, units); + *out_scale_y = fixed_mul_units_to_f26dot6(y_scale, units); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibrarySetCharSizeDefaultDpiStub(void* fontObj, float scale_x, float scale_y, + float* out_scale_x, float* out_scale_y) { + if (!fontObj || !out_scale_x || !out_scale_y) { + return ORBIS_FONT_ERROR_FATAL; + } + return LibrarySetCharSizeWithDpiStub(fontObj, 0x48, 0x48, scale_x, scale_y, out_scale_x, + out_scale_y); +} + +s32 PS4_SYSV_ABI LibraryComputeLayoutBlockStub(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]) { + if (!fontObj || !style_state_block || !out_words) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const FT_Face face = static_cast(obj->ft_face); + if (!face || !face->size) { + return ORBIS_FONT_ERROR_FATAL; + } + + const auto* style = + static_cast(style_state_block); + const float effect_width = style->effect_weight_x; + const float effect_height = style->effect_weight_y; + const float slant = style->slant_ratio; + + const s64 y_scale = static_cast(face->size->metrics.y_scale); + const s64 x_scale = static_cast(face->size->metrics.x_scale); + const s64 y_shift = obj->shift_cache_y; + const s64 x_shift = obj->shift_cache_x; + const s64 units_per_em = static_cast(static_cast(face->units_per_EM)); + + auto cvttss2si = [](float v) -> s32 { + if (!std::isfinite(v) || v > 2147483647.0f || v < -2147483648.0f) { + return std::numeric_limits::min(); + } + return static_cast(v); + }; + + auto round_mul_16_16 = [](s64 value, s64 fixed_16_16) -> s32 { + const s64 prod = value * fixed_16_16; + const s64 sign_adj = (value >= 0) ? 0x10000LL : 0LL; + s64 tmp = sign_adj + prod - 0x8000LL; + if (tmp < 0) { + tmp = sign_adj + prod + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + auto trunc_fixed_16_16_to_int = [](s64 fixed_16_16) -> s32 { + s64 tmp = fixed_16_16; + if (fixed_16_16 < 0) { + tmp = fixed_16_16 + 0xFFFFLL; + } + return static_cast(tmp >> 16); + }; + + s32 y_min_px = round_mul_16_16(static_cast(face->bbox.yMin) + y_shift, y_scale); + s32 y_max_px = round_mul_16_16(static_cast(face->bbox.yMax) + y_shift, y_scale); + + s32 half_effect_w_px = 0; + s32 left_adjust_px = 0; + if (effect_width != 0.0f) { + const s32 units_scaled_x = trunc_fixed_16_16_to_int(units_per_em * x_scale); + half_effect_w_px = cvttss2si(effect_width * static_cast(units_scaled_x)) / 2; + left_adjust_px = -half_effect_w_px; + } + + s32 half_effect_h_px = 0; + float out_effect_h = 0.0f; + if (effect_height != 0.0f) { + const s32 units_scaled_y = trunc_fixed_16_16_to_int(units_per_em * y_scale); + half_effect_h_px = cvttss2si(effect_height * static_cast(units_scaled_y)) / 2; + out_effect_h = static_cast(half_effect_h_px) * kOneOver64; + y_min_px -= half_effect_h_px; + y_max_px += half_effect_h_px; + } + + if (slant != 0.0f) { + const s64 shear_16_16 = static_cast(cvttss2si(slant * 65536.0f)); + left_adjust_px += round_mul_16_16(static_cast(y_min_px), shear_16_16); + half_effect_w_px += round_mul_16_16(static_cast(y_max_px), shear_16_16); + } + + struct LayoutOutIo { + u8 (*words)[16]; + struct F32Field { + u8* base; + std::size_t offset; + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + }; + struct Fields { + F32Field line_advance; + F32Field baseline; + F32Field x_bound_0; + F32Field x_bound_1; + F32Field max_advance_width; + F32Field hhea_caret_rise_adjust; + F32Field effect_height; + F32Field half_effect_width; + F32Field left_adjust; + }; + Fields fields() const { + return { + .line_advance = {words[0], 0x00}, + .baseline = {words[0], 0x04}, + .x_bound_0 = {words[0], 0x08}, + .x_bound_1 = {words[0], 0x0C}, + .max_advance_width = {words[1], 0x00}, + .hhea_caret_rise_adjust = {words[1], 0x04}, + .effect_height = {words[1], 0x08}, + .half_effect_width = {words[1], 0x0C}, + .left_adjust = {words[2], 0x00}, + }; + } + }; + + auto out = LayoutOutIo{out_words}.fields(); + out.effect_height = out_effect_h; + out.left_adjust = static_cast(left_adjust_px) * kOneOver64; + out.half_effect_width = static_cast(half_effect_w_px) * kOneOver64; + + const s32 x_min_px = round_mul_16_16(static_cast(face->bbox.xMin) + x_shift, x_scale); + const s32 x_max_px = round_mul_16_16(static_cast(face->bbox.xMax) + x_shift, x_scale); + + out.line_advance = static_cast(y_max_px - y_min_px) * kOneOver64; + out.baseline = static_cast(y_max_px) * kOneOver64; + out.x_bound_0 = static_cast(x_min_px + left_adjust_px) * kOneOver64; + out.x_bound_1 = static_cast(x_max_px + half_effect_w_px) * kOneOver64; + + const s64 max_adv_w_units = static_cast(face->max_advance_width) + x_shift; + const s32 max_adv_w_px = round_mul_16_16(max_adv_w_units, x_scale); + out.max_advance_width = static_cast(max_adv_w_px) * kOneOver64; + + float hhea_out = 0.0f; + if (const TT_HoriHeader* hhea = + static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_hhea))) { + const s64 caret_rise_units = x_shift + static_cast(hhea->caret_Slope_Rise); + const s32 caret_rise_px = trunc_fixed_16_16_to_int(caret_rise_units * x_scale); + hhea_out = static_cast(caret_rise_px - half_effect_w_px) * kOneOver64; + } + out.hhea_caret_rise_adjust = hhea_out; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI LibraryComputeLayoutAltBlockStub(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]) { + if (!fontObj || !style_state_block || !out_words) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + const FT_Face face = static_cast(obj->ft_face); + if (!face || !face->size) { + return ORBIS_FONT_ERROR_FATAL; + } + + const auto* style = + static_cast(style_state_block); + const float p18 = style->effect_weight_x; + const float p1c = style->effect_weight_y; + const float p20 = style->slant_ratio; + + const long x_scale = static_cast(face->size->metrics.x_scale); + const long y_scale = static_cast(face->size->metrics.y_scale); + + auto round_fixed_mul = [](long value, long fixed_16_16) -> s32 { + const long long prod = static_cast(value) * static_cast(fixed_16_16); + const long long sign_adj = (value >= 0) ? 0x10000LL : 0LL; + long long tmp = sign_adj + prod - 0x8000LL; + if (tmp < 0) { + tmp = sign_adj + prod + 0x7FFFLL; + } + return static_cast(static_cast(tmp) >> 16); + }; + + const auto* vhea = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_vhea)); + const long y_shift = static_cast(obj->shift_cache_y); + const long x_shift = static_cast(obj->shift_cache_x); + const long units = static_cast(static_cast(face->units_per_EM)); + + long y_ascender = 0; + long y_descender = 0; + if (vhea) { + y_ascender = static_cast(vhea->Ascender) + y_shift; + y_descender = static_cast(vhea->Descender) + y_shift; + } + + const long left_in = static_cast(face->bbox.xMin) + x_shift; + const long right_in = static_cast(face->bbox.xMax) + x_shift; + + const s32 scaled_left = round_fixed_mul(left_in, x_scale); + const s32 scaled_right = round_fixed_mul(right_in, x_scale); + + s32 x_min_scaled = scaled_left; + s32 x_max_scaled = scaled_right; + s32 x_abs_max = -x_min_scaled; + if (x_abs_max < x_max_scaled) { + x_abs_max = x_max_scaled; + x_min_scaled = -x_max_scaled; + } + + float out_effect_width = 0.0f; + if (p18 != 0.0f) { + const long long prod = static_cast(x_scale) * static_cast(units); + long long rounded = prod + 0xFFFFLL; + if (prod >= 0) { + rounded = prod; + } + const s32 units_scaled = static_cast(rounded >> 16); + const s32 effect = static_cast(std::trunc(p18 * static_cast(units_scaled))) / 2; + out_effect_width = static_cast(effect) * kOneOver64; + } + + if (p1c != 0.0f) { + const long long prod = static_cast(y_scale) * static_cast(units); + long long rounded = prod + 0xFFFFLL; + if (prod >= 0) { + rounded = prod; + } + const s32 units_scaled = static_cast(rounded >> 16); + const s32 delta = static_cast(std::trunc(p1c * static_cast(units_scaled))) / 2; + y_ascender -= delta; + y_descender += delta; + } + + float out_slant_a = 0.0f; + float out_slant_b = 0.0f; + if (p20 != 0.0f) { + const long shear = static_cast(static_cast(std::trunc(p20 * 65536.0f))); + const long long sign_adj_min = (y_ascender < 1) ? 0x10000LL : 0LL; + long long tmp_min = sign_adj_min + + static_cast(-y_ascender) * static_cast(shear) - + 0x8000LL; + if (tmp_min < 0) { + tmp_min = sign_adj_min + 0x7FFFLL + + static_cast(-y_ascender) * static_cast(shear); + } + const long long sign_adj_max = (y_descender >= 0) ? 0x10000LL : 0LL; + long long tmp_max = sign_adj_max + + static_cast(y_descender) * static_cast(shear) - + 0x8000LL; + if (tmp_max < 0) { + tmp_max = sign_adj_max + 0x7FFFLL + + static_cast(y_descender) * static_cast(shear); + } + + const u64 tmp_max_u64 = static_cast(tmp_max); + const u64 tmp_min_u64 = static_cast(tmp_min); + const u64 tmp_max_hi = tmp_max_u64 >> 16; + const bool max_round_bit_clear = ((tmp_max_u64 >> 15) & 1u) == 0; + u64 selected_hi = tmp_max_hi; + s32 selected_lo = static_cast(tmp_min_u64 >> 16); + if (max_round_bit_clear && tmp_max_hi != 0) { + selected_hi = tmp_min_u64 >> 16; + selected_lo = static_cast(tmp_max_u64 >> 16); + } + out_slant_a = static_cast(selected_lo) * kOneOver64; + out_slant_b = static_cast(static_cast(selected_hi)) * kOneOver64; + } + + const s32 lane0 = x_abs_max - x_min_scaled; + const s32 lane1 = -x_abs_max; + const s32 lane2 = round_fixed_mul(y_ascender, x_scale); + const s32 lane3 = round_fixed_mul(y_descender, x_scale); + + struct LayoutAltOutIo { + u8 (*words)[16]; + struct F32Field { + u8* base; + std::size_t offset; + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + }; + struct Fields { + F32Field metrics_0x00; + F32Field metrics_0x04; + F32Field metrics_0x08; + F32Field metrics_0x0C; + F32Field adv_height; + F32Field effect_width; + F32Field slant_b; + F32Field slant_a; + }; + Fields fields() const { + return { + .metrics_0x00 = {words[0], 0x00}, + .metrics_0x04 = {words[0], 0x04}, + .metrics_0x08 = {words[0], 0x08}, + .metrics_0x0C = {words[0], 0x0C}, + .adv_height = {words[1], 0x00}, + .effect_width = {words[1], 0x04}, + .slant_b = {words[1], 0x08}, + .slant_a = {words[1], 0x0C}, + }; + } + }; + + auto out = LayoutAltOutIo{out_words}.fields(); + out.metrics_0x00 = static_cast(lane0) * kOneOver64; + out.metrics_0x04 = static_cast(lane1) * kOneOver64; + out.metrics_0x08 = static_cast(lane2) * kOneOver64; + out.metrics_0x0C = static_cast(lane3) * kOneOver64; + + const s32 adv_h_scaled = + round_fixed_mul(static_cast(face->max_advance_height) + y_shift, y_scale); + out.adv_height = static_cast(adv_h_scaled) * kOneOver64; + out.effect_width = out_effect_width; + out.slant_b = out_slant_b; + out.slant_a = out_slant_a; + return ORBIS_OK; +} + +// LLE: FUN_01007e80 (sys_driver +0xA8) +s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 mode, + std::uint64_t* out_words) { + if (out_words) { + out_words[0] = 0; + out_words[1] = 0; + } + if (!fontObj || !out_words) { + return ORBIS_FONT_ERROR_FATAL; + } + + auto* obj = static_cast(fontObj); + FT_Face face = static_cast(obj->ft_face); + if (!face) { + return ORBIS_FONT_ERROR_FATAL; + } + + FT_GlyphSlot slot = face->glyph; + if (!slot) { + return ORBIS_FONT_ERROR_FATAL; + } + + const u16 units_per_em = static_cast(face->units_per_EM); + const bool cached_match = (obj->cached_glyph_index_0x64 == static_cast(glyphIndex)) && + (obj->cached_units_x_0x68 == static_cast(units_per_em)) && + (obj->cached_units_y_0x70 == obj->cached_units_x_0x68) && (mode == 0); + + auto write_vec88_from_seed_pair_unscaled = [&]() { + const s32 seed_low = + static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_high = + static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); + obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); + obj->layout_seed_vec[1] = static_cast(static_cast(seed_high)); + }; + + auto write_out_words_from_slot = [&]() { + const s32 n_contours = static_cast(slot->outline.n_contours); + const s32 n_points = static_cast(slot->outline.n_points); + + const u32 computed_4 = static_cast(n_points * 0x10 + 0x68 + + ((n_points + 0x0F + n_contours * 2) & 0xFFFFFFF0u)); + const u32 computed_8 = static_cast(((n_points + 0x2B + n_contours * 2) & 0xFFFFFFFCu) + + 0x10 + n_points * 4); + + auto* out_u8 = reinterpret_cast(out_words); + *reinterpret_cast(out_u8 + 0) = static_cast(n_contours); + *reinterpret_cast(out_u8 + 2) = static_cast(n_points); + *reinterpret_cast(out_u8 + 4) = computed_4; + *reinterpret_cast(out_u8 + 8) = computed_8; + }; + + if (cached_match) { + obj->shift_cache_x = static_cast(obj->shift_units_x); + obj->shift_cache_y = static_cast(obj->shift_units_y); + write_vec88_from_seed_pair_unscaled(); + write_out_words_from_slot(); + return ORBIS_OK; + } + + const FT_Int32 load_flags = static_cast(((mode == 0) ? 1 : 0) | 0x1A); + const FT_Error ft_err = FT_Load_Glyph(face, static_cast(glyphIndex), load_flags); + if (ft_err != 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 (ft_err == 0x40) ? ORBIS_FONT_ERROR_ALLOCATION_FAILED + : ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH; + } + + slot = face->glyph; + if (!slot) { + return ORBIS_FONT_ERROR_FATAL; + } + + obj->cached_glyph_index_0x64 = static_cast(glyphIndex); + + if (mode == 0) { + obj->cached_units_x_0x68 = static_cast(units_per_em); + obj->cached_units_y_0x70 = static_cast(units_per_em); + + obj->shift_cache_x = static_cast(obj->shift_units_x); + obj->shift_cache_y = static_cast(obj->shift_units_y); + write_vec88_from_seed_pair_unscaled(); + obj->layout_scale_vec[0] = 0x0000000000010000ull; + obj->layout_scale_vec[1] = 0x0000000000010000ull; + } else { + if (!face->size) { + return ORBIS_FONT_ERROR_FATAL; + } + const long x_scale = static_cast(face->size->metrics.x_scale); + const long y_scale = static_cast(face->size->metrics.y_scale); + + const auto trunc_mul_units = [](long fixed_16_16, u16 units) -> s64 { + const s64 prod = static_cast(fixed_16_16) * static_cast(units); + s64 rounded = prod; + if (prod < 0) { + rounded = prod + 0xFFFFLL; + } + return rounded >> 16; + }; + + const auto round_fixed_mul = [](long fixed_16_16, s32 value) -> s64 { + const long long prod = + static_cast(fixed_16_16) * static_cast(value); + const long long sign_adj = + (static_cast(~static_cast(value)) >> 63) * -0x10000LL; + const long long base = sign_adj + prod; + long long tmp = base - 0x8000LL; + if (tmp < 0) { + tmp = base + 0x7FFFLL; + } + return static_cast(static_cast(static_cast(tmp) >> 16)); + }; + + obj->cached_units_x_0x68 = static_cast(trunc_mul_units(x_scale, units_per_em)); + obj->cached_units_y_0x70 = static_cast(trunc_mul_units(y_scale, units_per_em)); + obj->shift_cache_x = round_fixed_mul(x_scale, obj->shift_units_x); + obj->shift_cache_y = round_fixed_mul(y_scale, obj->shift_units_y); + + const s32 seed_low = + static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_high = + static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); + const s64 v0 = round_fixed_mul(x_scale, seed_low); + const s64 v1 = round_fixed_mul(y_scale, seed_high); + obj->layout_seed_vec[0] = static_cast(v0); + obj->layout_seed_vec[1] = static_cast(v1); + + u64 lane0 = obj->layout_scale_vec[0]; + u64 lane1 = obj->layout_scale_vec[1]; + std::memcpy(&lane0, &x_scale, sizeof(x_scale)); + std::memcpy(&lane1, &y_scale, sizeof(y_scale)); + obj->layout_scale_vec[0] = lane0; + obj->layout_scale_vec[1] = lane1; + } + + write_out_words_from_slot(); + return ORBIS_OK; +} + +// LLE: FUN_0100a740 (sys_driver +0xB8) +s32 PS4_SYSV_ABI LibraryGetGlyphMetricsStub(void* fontObj, std::uint32_t* /*opt_param2*/, + std::uint8_t /*mode*/, std::uint8_t* out_params, + Libraries::Font::OrbisFontGlyphMetrics* out_metrics) { + if (!fontObj || !out_params || !out_metrics) { + return ORBIS_FONT_ERROR_FATAL; + } + + auto* obj = static_cast(fontObj); + FT_Face face = static_cast(obj->ft_face); + if (!face || !face->glyph) { + return ORBIS_FONT_ERROR_FATAL; + } + + const FT_GlyphSlot slot = face->glyph; + out_metrics->width = static_cast(slot->metrics.width) * kOneOver64; + out_metrics->height = static_cast(slot->metrics.height) * kOneOver64; + out_metrics->Horizontal.bearingX = static_cast(slot->metrics.horiBearingX) * kOneOver64; + out_metrics->Horizontal.bearingY = static_cast(slot->metrics.horiBearingY) * kOneOver64; + out_metrics->Horizontal.advance = static_cast(slot->metrics.horiAdvance) * kOneOver64; + out_metrics->Vertical.bearingX = static_cast(slot->metrics.vertBearingX) * kOneOver64; + out_metrics->Vertical.bearingY = static_cast(slot->metrics.vertBearingY) * kOneOver64; + out_metrics->Vertical.advance = static_cast(slot->metrics.vertAdvance) * kOneOver64; + + out_params[0] = 0xF2; + out_params[1] = 0; + + const u8 face_flag_bit = static_cast((static_cast(face->face_flags) >> 5) & 1u); + const u64 vec88_a = obj->layout_seed_vec[0]; + const u64 vec88_b = obj->layout_seed_vec[1]; + const u8 has_vec88 = (vec88_a != 0 || vec88_b != 0) ? 8 : 0; + out_params[2] = static_cast(0xF0 | has_vec88 | face_flag_bit); + out_params[3] = 0; + + const u16 units_per_em = static_cast(face->units_per_EM); + std::memcpy(out_params + 4, &units_per_em, sizeof(units_per_em)); + + void* outline_ptr = static_cast(&slot->outline); + std::memcpy(out_params + 0x10, &outline_ptr, sizeof(outline_ptr)); + + void* metrics_ptr = static_cast(out_metrics); + std::memcpy(out_params + 0x18, &metrics_ptr, sizeof(metrics_ptr)); + + return ORBIS_OK; +} + +// LLE: FUN_0100b750 (sys_driver +0xE0) +s32 PS4_SYSV_ABI LibraryApplyGlyphAdjustStub(void* fontObj, u32 /*p2*/, u32 glyphIndex, s32 p4, + s32 p5, u32* inoutGlyphIndex) { + if (!fontObj || !inoutGlyphIndex) { + return ORBIS_FONT_ERROR_FATAL; + } + auto* obj = static_cast(fontObj); + *inoutGlyphIndex = glyphIndex; + obj->shift_units_x = p4; + obj->shift_units_y = p5; + obj->layout_seed_pair = 0; + return ORBIS_OK; +} + +// LLE: FUN_0100d990 (sys_driver +0x108) +s32 PS4_SYSV_ABI LibraryConfigureGlyphStub(void* fontObj, std::uint32_t* in_params, s32 mode, + std::uint32_t* inout_state) { + if (!fontObj || !in_params || !inout_state) { + return ORBIS_FONT_ERROR_FATAL; + } + + auto* obj = static_cast(fontObj); + + if (*in_params != 0) { + if (mode == 0) { + reinterpret_cast(inout_state)[3] |= 0x80; + } else { + reinterpret_cast(inout_state)[3] |= 0x80; + } + } + + obj->glyph_cfg_word_0x130 = *in_params; + obj->glyph_cfg_mode_0x134 = static_cast(mode); + obj->glyph_cfg_byte_0x136 = 0; + obj->glyph_cfg_byte_0x135 = 0; + return ORBIS_OK; +} + +} // namespace Libraries::FontFt::Internal diff --git a/src/core/libraries/font/fontft_internal.h b/src/core/libraries/font/fontft_internal.h new file mode 100644 index 000000000..fbc1b85ca --- /dev/null +++ b/src/core/libraries/font/fontft_internal.h @@ -0,0 +1,603 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "core/libraries/font/font.h" + +namespace Libraries::Font { + +struct FontHandleOpenInfo { + /*0x00*/ u32 unique_id_packed; + /*0x04*/ u32 ctx_entry_index; + /*0x08*/ u32 sub_font_index; + /*0x0C*/ u32 fontset_flags; + /*0x10*/ const void* fontset_record; + /*0x18*/ u64 reserved_0x18; +}; +static_assert(sizeof(FontHandleOpenInfo) == 0x20, "FontHandleOpenInfo size"); +static_assert(offsetof(FontHandleOpenInfo, sub_font_index) == 0x08, + "FontHandleOpenInfo sub_font_index offset"); +static_assert(offsetof(FontHandleOpenInfo, fontset_record) == 0x10, + "FontHandleOpenInfo fontset_record offset"); + +struct FontHandleStyleStateTail { + /*0x00*/ u32 scale_unit; + /*0x04*/ u32 reserved_0x0c; + /*0x08*/ float scale_w; + /*0x0C*/ float scale_h; + /*0x10*/ float effect_weight_x; + /*0x14*/ float effect_weight_y; + /*0x18*/ float slant_ratio; + /*0x1C*/ float reserved_0x24; +}; +static_assert(sizeof(FontHandleStyleStateTail) == 0x20, "FontHandleStyleStateTail size"); + +struct FontHandleOpaqueNative { + u16 magic; + u16 flags; + u32 lock_word; + FontHandleOpenInfo open_info; + OrbisFontLib library; + struct RendererBinding { + void* renderer; + u8 reserved_0x08[0x10 - sizeof(void*)]; + }; + static_assert(sizeof(RendererBinding) == 0x10, "RendererBinding size"); + union { + u8 reserved_0x30[0x10]; + RendererBinding renderer_binding; + }; + u32 style_frame[2]; + union { + u8 reserved_0x48[0x20]; + FontHandleStyleStateTail style_tail; + }; + OrbisFontStyleFrame cached_style; + u16 metricA; + u16 metricB; + u8 reserved_0xcc[0x1C]; + u8 reserved_0xe8[0x08]; + OrbisFontHandle prevFont; + OrbisFontHandle nextFont; +}; +static_assert(sizeof(FontHandleOpaqueNative) == 0x100, "FontHandleOpaqueNative size"); +static_assert(offsetof(FontHandleOpaqueNative, renderer_binding) == 0x30, + "FontHandleOpaqueNative renderer_binding offset"); +static_assert(offsetof(FontHandleOpaqueNative, magic) == 0x00, + "FontHandleOpaqueNative magic offset"); +static_assert(offsetof(FontHandleOpaqueNative, lock_word) == 0x04, + "FontHandleOpaqueNative lock_word offset"); +static_assert(offsetof(FontHandleOpaqueNative, open_info) == 0x08, + "FontHandleOpaqueNative open_info offset"); +static_assert(offsetof(FontHandleOpaqueNative, library) == 0x28, + "FontHandleOpaqueNative library offset"); +static_assert(offsetof(FontHandleOpaqueNative, reserved_0x30) == 0x30, + "FontHandleOpaqueNative reserved_0x30 offset"); +static_assert(offsetof(FontHandleOpaqueNative, style_frame) == 0x40, + "FontHandleOpaqueNative style_frame offset"); +static_assert(offsetof(FontHandleOpaqueNative, reserved_0x48) == 0x48, + "FontHandleOpaqueNative reserved_0x48 offset"); +static_assert(offsetof(FontHandleOpaqueNative, cached_style) == 0x68, + "FontHandleOpaqueNative cached_style offset"); +static_assert(offsetof(FontHandleOpaqueNative, metricA) == 0xC8, + "FontHandleOpaqueNative metricA offset"); +static_assert(offsetof(FontHandleOpaqueNative, metricB) == 0xCA, + "FontHandleOpaqueNative metricB offset"); +static_assert(offsetof(FontHandleOpaqueNative, prevFont) == 0xF0, + "FontHandleOpaqueNative prev offset"); +static_assert(offsetof(FontHandleOpaqueNative, nextFont) == 0xF8, + "FontHandleOpaqueNative next offset"); + +} // namespace Libraries::Font + +namespace Libraries::Font::Internal { + +struct alignas(16) LayoutWord { + std::array words{}; + + constexpr u8* data() { + return reinterpret_cast(words.data()); + } + constexpr const u8* data() const { + return reinterpret_cast(words.data()); + } +}; +static_assert(sizeof(LayoutWord) == 16, "LayoutWord size"); +static_assert(alignof(LayoutWord) == 16, "LayoutWord alignment"); + +struct alignas(16) HorizontalLayoutMetricsWord { + float line_advance = 0.0f; + float baseline = 0.0f; + float x_bound_lo = 0.0f; + float x_bound_hi = 0.0f; +}; +static_assert(sizeof(HorizontalLayoutMetricsWord) == 16, "HorizontalLayoutMetricsWord size"); +static_assert(alignof(HorizontalLayoutMetricsWord) == 16, "HorizontalLayoutMetricsWord alignment"); + +struct alignas(16) HorizontalLayoutEffectsWord { + u32 unknown_0x00 = 0; + u32 unknown_0x04 = 0; + float effect_height = 0.0f; + float packed_i8_adj_u13_lane1 = 0.0f; +}; +static_assert(sizeof(HorizontalLayoutEffectsWord) == 16, "HorizontalLayoutEffectsWord size"); +static_assert(alignof(HorizontalLayoutEffectsWord) == 16, "HorizontalLayoutEffectsWord alignment"); + +struct alignas(16) HorizontalLayoutAuxWord { + float packed_i8_adj_u13_lane0 = 0.0f; + u32 unknown_0x04 = 0; + u32 unknown_0x08 = 0; + u32 unknown_0x0C = 0; +}; +static_assert(sizeof(HorizontalLayoutAuxWord) == 16, "HorizontalLayoutAuxWord size"); +static_assert(alignof(HorizontalLayoutAuxWord) == 16, "HorizontalLayoutAuxWord alignment"); + +struct alignas(16) HorizontalLayoutBlocks { + HorizontalLayoutMetricsWord metrics; + HorizontalLayoutEffectsWord effects; + HorizontalLayoutAuxWord aux; + + constexpr void* words() { + return &metrics; + } + constexpr const void* words() const { + return &metrics; + } + + struct Values { + float line_advance = 0.0f; + float baseline = 0.0f; + float effect_height = 0.0f; + u64 x_bounds_bits = 0; + u64 i8_adj_u13_bits = 0; + }; + + float line_advance() const { + return metrics.line_advance; + } + + float baseline() const { + return metrics.baseline; + } + + float effect_height() const { + return effects.effect_height; + } + + u64 x_bounds_bits() const { + return static_cast(std::bit_cast(metrics.x_bound_lo)) | + (static_cast(std::bit_cast(metrics.x_bound_hi)) << 32); + } + + u64 i8_adj_u13_bits() const { + return static_cast(std::bit_cast(effects.packed_i8_adj_u13_lane1)) | + (static_cast(std::bit_cast(aux.packed_i8_adj_u13_lane0)) << 32); + } + + Values values() const { + return { + .line_advance = line_advance(), + .baseline = baseline(), + .effect_height = effect_height(), + .x_bounds_bits = x_bounds_bits(), + .i8_adj_u13_bits = i8_adj_u13_bits(), + }; + } + + void set_line_advance(float v) { + metrics.line_advance = v; + } + + void set_baseline(float v) { + metrics.baseline = v; + } + + void set_x_bounds(float lo, float hi) { + metrics.x_bound_lo = lo; + metrics.x_bound_hi = hi; + } + + void set_effect_height(float v) { + effects.effect_height = v; + } + + void set_i8_adj_u13_lanes(float lane0, float lane1) { + aux.packed_i8_adj_u13_lane0 = lane0; + effects.packed_i8_adj_u13_lane1 = lane1; + } +}; +static_assert(sizeof(HorizontalLayoutBlocks) == 0x30, "HorizontalLayoutBlocks size"); +static_assert(alignof(HorizontalLayoutBlocks) == 16, "HorizontalLayoutBlocks alignment"); +static_assert(offsetof(HorizontalLayoutBlocks, metrics) == 0x00, + "HorizontalLayoutBlocks metrics offset"); +static_assert(offsetof(HorizontalLayoutBlocks, effects) == 0x10, + "HorizontalLayoutBlocks effects offset"); +static_assert(offsetof(HorizontalLayoutBlocks, aux) == 0x20, "HorizontalLayoutBlocks aux offset"); + +struct alignas(16) VerticalLayoutMetricsWord { + float column_advance = 0.0f; + float baseline_offset_x = 0.0f; + float unknown_0x08 = 0.0f; + float unknown_0x0C = 0.0f; +}; +static_assert(sizeof(VerticalLayoutMetricsWord) == 16, "VerticalLayoutMetricsWord size"); +static_assert(alignof(VerticalLayoutMetricsWord) == 16, "VerticalLayoutMetricsWord alignment"); + +struct alignas(16) VerticalLayoutDecorationWord { + u32 unknown_0x00 = 0; + float decoration_span = 0.0f; + float unknown_0x08 = 0.0f; + float unknown_0x0C = 0.0f; +}; +static_assert(sizeof(VerticalLayoutDecorationWord) == 16, "VerticalLayoutDecorationWord size"); +static_assert(alignof(VerticalLayoutDecorationWord) == 16, + "VerticalLayoutDecorationWord alignment"); + +struct alignas(16) VerticalLayoutBlocks { + VerticalLayoutMetricsWord metrics; + VerticalLayoutDecorationWord decoration; + + constexpr void* words() { + return &metrics; + } + constexpr const void* words() const { + return &metrics; + } + + float column_advance() const { + return metrics.column_advance; + } + + float baseline_offset_x() const { + return metrics.baseline_offset_x; + } + + float decoration_span() const { + return decoration.decoration_span; + } + + struct Values { + float column_advance = 0.0f; + float baseline_offset_x = 0.0f; + float decoration_span = 0.0f; + }; + + Values values() const { + return { + .column_advance = column_advance(), + .baseline_offset_x = baseline_offset_x(), + .decoration_span = decoration_span(), + }; + } + + void set_column_advance(float v) { + metrics.column_advance = v; + } + + void set_baseline_offset_x(float v) { + metrics.baseline_offset_x = v; + } + + void set_unknown_metrics_0x08(float v) { + metrics.unknown_0x08 = v; + } + + void set_unknown_metrics_0x0C(float v) { + metrics.unknown_0x0C = v; + } + + void set_decoration_span(float v) { + decoration.decoration_span = v; + } + + void set_unknown_decoration_0x08(float v) { + decoration.unknown_0x08 = v; + } + + void set_unknown_decoration_0x0C(float v) { + decoration.unknown_0x0C = v; + } +}; +static_assert(sizeof(VerticalLayoutBlocks) == 0x20, "VerticalLayoutBlocks size"); +static_assert(alignof(VerticalLayoutBlocks) == 16, "VerticalLayoutBlocks alignment"); +static_assert(offsetof(VerticalLayoutBlocks, metrics) == 0x00, + "VerticalLayoutBlocks metrics offset"); +static_assert(offsetof(VerticalLayoutBlocks, decoration) == 0x10, + "VerticalLayoutBlocks decoration offset"); + +struct HorizontalLayoutBlocksIo { + u8 (*words)[16] = nullptr; + + struct F32Field { + u8* base = nullptr; + std::size_t offset = 0; + + float value() const { + float v = 0.0f; + std::memcpy(&v, base + offset, sizeof(v)); + return v; + } + + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + + operator float() const { + return value(); + } + }; + + struct Fields { + F32Field line_advance; + F32Field baseline; + F32Field x_bound_0; + F32Field x_bound_1; + F32Field effect_height; + F32Field u13_i8_lane0; + F32Field u13_i8_lane1; + }; + + Fields fields() const { + return { + .line_advance = {words ? words[0] : nullptr, 0x00}, + .baseline = {words ? words[0] : nullptr, 0x04}, + .x_bound_0 = {words ? words[0] : nullptr, 0x08}, + .x_bound_1 = {words ? words[0] : nullptr, 0x0C}, + .effect_height = {words ? words[1] : nullptr, 0x08}, + .u13_i8_lane0 = {words ? words[2] : nullptr, 0x00}, + .u13_i8_lane1 = {words ? words[1] : nullptr, 0x0C}, + }; + } +}; + +struct VerticalLayoutBlocksIo { + u8 (*words)[16] = nullptr; + + struct F32Field { + u8* base = nullptr; + std::size_t offset = 0; + + float value() const { + float v = 0.0f; + std::memcpy(&v, base + offset, sizeof(v)); + return v; + } + + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + + operator float() const { + return value(); + } + }; + + struct Fields { + F32Field column_advance; + F32Field baseline_offset_x; + F32Field decoration_span; + F32Field unknown_metrics_0x08; + F32Field unknown_metrics_0x0C; + F32Field unknown_decoration_0x08; + F32Field unknown_decoration_0x0C; + }; + + Fields fields() const { + return { + .column_advance = {words ? words[0] : nullptr, 0x00}, + .baseline_offset_x = {words ? words[0] : nullptr, 0x04}, + .decoration_span = {words ? words[1] : nullptr, 0x04}, + .unknown_metrics_0x08 = {words ? words[0] : nullptr, 0x08}, + .unknown_metrics_0x0C = {words ? words[0] : nullptr, 0x0C}, + .unknown_decoration_0x08 = {words ? words[1] : nullptr, 0x08}, + .unknown_decoration_0x0C = {words ? words[1] : nullptr, 0x0C}, + }; + } +}; + +struct alignas(16) VerticalLayoutAltBlocks { + struct MetricsWord { + float unknown_0x00 = 0.0f; + float baseline_offset_x_candidate = 0.0f; + float unknown_0x08 = 0.0f; + float unknown_0x0C = 0.0f; + }; + + struct ExtrasWord { + float unknown_0x00 = 0.0f; + float decoration_span_candidate = 0.0f; + float unknown_0x08 = 0.0f; + float unknown_0x0C = 0.0f; + }; + + MetricsWord metrics; + ExtrasWord extras; + + constexpr void* words() { + return &metrics; + } + constexpr const void* words() const { + return &metrics; + } + + struct Values { + MetricsWord metrics{}; + ExtrasWord extras{}; + }; + + Values values() const { + return { + .metrics = metrics, + .extras = extras, + }; + } +}; +static_assert(sizeof(VerticalLayoutAltBlocks) == 0x20, "VerticalLayoutAltBlocks size"); +static_assert(alignof(VerticalLayoutAltBlocks) == 16, "VerticalLayoutAltBlocks alignment"); +static_assert(offsetof(VerticalLayoutAltBlocks, metrics) == 0x00, + "VerticalLayoutAltBlocks metrics offset"); +static_assert(offsetof(VerticalLayoutAltBlocks, extras) == 0x10, + "VerticalLayoutAltBlocks extras offset"); + +s32 ComputeHorizontalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, u8 (*out_words)[16]); +s32 ComputeVerticalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, u8 (*out_words)[16]); + +inline u8 (*LayoutWordsBytes(void* bytes))[16] { + return reinterpret_cast(bytes); +} +inline const u8 (*LayoutWordsBytes(const void* bytes))[16] { + return reinterpret_cast(bytes); +} + +inline s32 ComputeHorizontalLayoutBlocksWords(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, + LayoutWord* out_words) { + return ComputeHorizontalLayoutBlocks(fontHandle, style_state_block, + reinterpret_cast(out_words)); +} + +inline s32 ComputeHorizontalLayoutBlocksTyped(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, + HorizontalLayoutBlocks* out_blocks) { + return ComputeHorizontalLayoutBlocks(fontHandle, style_state_block, + reinterpret_cast(out_blocks->words())); +} + +inline s32 ComputeVerticalLayoutBlocksWords(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, LayoutWord* out_words) { + return ComputeVerticalLayoutBlocks(fontHandle, style_state_block, + reinterpret_cast(out_words)); +} + +inline s32 ComputeVerticalLayoutBlocksTyped(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, + VerticalLayoutBlocks* out_blocks) { + return ComputeVerticalLayoutBlocks(fontHandle, style_state_block, + reinterpret_cast(out_blocks->words())); +} + +inline s32 ComputeHorizontalLayoutBlocksToBytes(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, void* out_bytes) { + return ComputeHorizontalLayoutBlocks(fontHandle, style_state_block, + LayoutWordsBytes(out_bytes)); +} + +inline s32 ComputeVerticalLayoutBlocksToBytes(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, void* out_bytes) { + return ComputeVerticalLayoutBlocks(fontHandle, style_state_block, LayoutWordsBytes(out_bytes)); +} + +struct SysFontDesc { + float scale_factor; + s32 shift_value; +}; + +SysFontDesc GetSysFontDesc(u32 font_id); + +Libraries::Font::FontHandleOpaqueNative* GetNativeFont(Libraries::Font::OrbisFontHandle h); +bool AcquireFontLock(Libraries::Font::FontHandleOpaqueNative* font, u32& out_prev_lock_word); +void ReleaseFontLock(Libraries::Font::FontHandleOpaqueNative* font, u32 prev_lock_word); +bool AcquireCachedStyleLock(Libraries::Font::FontHandleOpaqueNative* font, u32& out_prev_lock_word); +void ReleaseCachedStyleLock(Libraries::Font::FontHandleOpaqueNative* font, u32 prev_lock_word); + +std::uint8_t* AcquireFontCtxEntry(std::uint8_t* ctx, u32 idx, u32 mode_low, void** out_obj, + u32* out_lock_word); + +void ReleaseFontObjectsForHandle(Libraries::Font::OrbisFontHandle fontHandle, int entryCount); + +s32 ComputeHorizontalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, u8 (*out_words)[16]); + +s32 ComputeVerticalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, + const void* style_state_block, u8 (*out_words)[16]); + +s32 GetCharGlyphMetrics(Libraries::Font::OrbisFontHandle fontHandle, u32 code, + Libraries::Font::OrbisFontGlyphMetrics* metrics, bool use_cached_style); + +const std::uint8_t* FindSysFontRangeRecord(u32 font_id, u32 codepoint); + +u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_font_id); + +s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 code, + Libraries::Font::OrbisFontRenderSurface* surf, float x, float y, + Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result); + +s32 RenderGlyphImageCore(Libraries::Font::OrbisFontGlyph fontGlyph, + Libraries::Font::OrbisFontStyleFrame* fontStyleFrame, + Libraries::Font::OrbisFontRenderer fontRenderer, + Libraries::Font::OrbisFontRenderSurface* surface, float x, float y, + int mode, Libraries::Font::OrbisFontGlyphMetrics* metrics, + Libraries::Font::OrbisFontRenderOutput* result); + +s32 StyleStateSetScalePixel(void* style_state_block, float w, float h); + +s32 StyleStateSetScalePoint(void* style_state_block, float w, float h); + +s32 StyleStateGetScalePixel(const void* style_state_block, float* w, float* h); + +s32 StyleStateGetScalePoint(const void* style_state_block, float* w, float* h); + +s32 StyleStateSetDpi(u32* dpi_pair, u32 h_dpi, u32 v_dpi); + +s32 StyleStateSetSlantRatio(void* style_state_block, float slantRatio); + +s32 StyleStateGetSlantRatio(const void* style_state_block, float* slantRatio); + +s32 StyleStateSetWeightScale(void* style_state_block, float weightXScale, float weightYScale); + +s32 StyleStateGetWeightScale(const void* style_state_block, float* weightXScale, + float* weightYScale, u32* mode); + +} // namespace Libraries::Font::Internal + +namespace Libraries::FontFt::Internal { + +u32 PS4_SYSV_ABI LibraryGetPixelResolutionStub(); +s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library); +s32 PS4_SYSV_ABI LibraryTermStub(void* library); +s32 PS4_SYSV_ABI LibrarySupportStub(void* library, u32 formats); +s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* fontAddress, + u32 fontSize, u32 subFontIndex, u32 uniqueWord, + void** inoutFontObj); +s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 flags); +s32 PS4_SYSV_ABI LibraryGetFaceMetricStub(void* fontObj, u32 metricId, u16* outMetric); +s32 PS4_SYSV_ABI LibraryGetFaceScaleStub(void* fontObj, u16* outUnitsPerEm, float* outScale); +s32 PS4_SYSV_ABI LibraryGetGlyphIndexStub(void* fontObj, u32 codepoint_u16, u32* out_glyph_index); + +s32 PS4_SYSV_ABI LibrarySetCharSizeWithDpiStub(void* fontObj, u32 dpi_x, u32 dpi_y, float scale_x, + float scale_y, float* out_scale_x, + float* out_scale_y); +s32 PS4_SYSV_ABI LibrarySetCharSizeDefaultDpiStub(void* fontObj, float scale_x, float scale_y, + float* out_scale_x, float* out_scale_y); +s32 PS4_SYSV_ABI LibraryComputeLayoutBlockStub(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]); +s32 PS4_SYSV_ABI LibraryComputeLayoutAltBlockStub(void* fontObj, const void* style_state_block, + u8 (*out_words)[16]); + +s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 mode, + std::uint64_t* out_words); +s32 PS4_SYSV_ABI LibraryGetGlyphMetricsStub(void* fontObj, std::uint32_t* opt_param2, + std::uint8_t mode, std::uint8_t* out_params, + Libraries::Font::OrbisFontGlyphMetrics* out_metrics); +s32 PS4_SYSV_ABI LibraryApplyGlyphAdjustStub(void* fontObj, u32 p2, u32 glyphIndex, s32 p4, s32 p5, + u32* inoutGlyphIndex); +s32 PS4_SYSV_ABI LibraryConfigureGlyphStub(void* fontObj, std::uint32_t* in_params, s32 mode, + std::uint32_t* inout_state); + +s32 PS4_SYSV_ABI FtRendererCreate(void* renderer); +u64 PS4_SYSV_ABI FtRendererQuery(void* renderer, u8* params, s64* out_ptr, u8 (*out_vec)[16]); +s32 PS4_SYSV_ABI FtRendererDestroy(void* renderer); + +} // namespace Libraries::FontFt::Internal From 240499b2c9865342677bc13baf51a47a61ceeeff Mon Sep 17 00:00:00 2001 From: w1naenator Date: Thu, 8 Jan 2026 04:19:04 +0200 Subject: [PATCH 28/51] Add FreeType submodule for GitHub CI workflows --- .gitmodules | 4 ++++ externals/freetype | 1 + 2 files changed, 5 insertions(+) create mode 160000 externals/freetype diff --git a/.gitmodules b/.gitmodules index c0ba5e79d..dd0d09b24 100644 --- a/.gitmodules +++ b/.gitmodules @@ -123,3 +123,7 @@ [submodule "externals/aacdec/fdk-aac"] path = externals/aacdec/fdk-aac url = https://android.googlesource.com/platform/external/aac +[submodule "externals/freetype"] + path = externals/freetype + url = https://github.com/freetype/freetype.git + shallow = true diff --git a/externals/freetype b/externals/freetype new file mode 160000 index 000000000..b91f75bd0 --- /dev/null +++ b/externals/freetype @@ -0,0 +1 @@ +Subproject commit b91f75bd02db43b06d634591eb286d3eb0ce3b65 From d845b9503df225d8ec3a1ca40b20651f62e915c5 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 9 Jan 2026 13:00:17 +0200 Subject: [PATCH 29/51] Refactor font rendering and path resolution logic to fix Metaphor: ReFantazio glyphs cropping and anywhereVR "thin" glyphs. - Updated RenderCodepointToSurface and RenderCodepointToSurfaceWithScale functions to set result->transImage to nullptr instead of result->stage. - Modified font definitions to use updated font file names for various language sets. - Introduced ResolveSystemFontPathCandidate function to streamline font path resolution, allowing for better handling of font directories. - Enhanced LoadFontFile function to check for font files in "font" and "font2" directories based on the parent directory name. - Improved error logging for font path overrides in ResolveSystemFontPath function. --- src/core/libraries/font/font.cpp | 389 +++++++++++++++++++- src/core/libraries/font/font_internal.cpp | 198 ++++++---- src/core/libraries/font/fontft_internal.cpp | 276 +++++++------- 3 files changed, 637 insertions(+), 226 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 6083a5187..85636e973 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -566,6 +566,44 @@ static std::filesystem::path GetSysFontBaseDir() { return {}; } +static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesystem::path& base_dir, + const std::filesystem::path& filename) { + if (base_dir.empty() || filename.empty()) { + return {}; + } + std::error_code ec; + const auto is_file = [&](const std::filesystem::path& p) -> bool { + return std::filesystem::is_regular_file(p, ec) && !ec; + }; + + const auto direct = base_dir / filename; + if (is_file(direct)) { + return direct; + } + + const auto base_name = base_dir.filename().string(); + if (base_name != "font" && base_name != "font2") { + const auto in_font = base_dir / "font" / filename; + if (is_file(in_font)) { + return in_font; + } + const auto in_font2 = base_dir / "font2" / filename; + if (is_file(in_font2)) { + return in_font2; + } + } + + if (base_name == "font" || base_name == "font2") { + const auto container = base_dir.parent_path(); + const auto sibling = container / ((base_name == "font") ? "font2" : "font") / filename; + if (is_file(sibling)) { + return sibling; + } + } + + return direct; +} + static std::string MacroToCamel(const char* macro_key) { if (!macro_key) { return {}; @@ -605,7 +643,7 @@ static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path = Config::getSystemFontOverride(def->config_key)) { if (!override_path->empty() && !override_path->is_absolute() && !override_path->has_parent_path()) { - return base_dir / *override_path; + return ResolveSystemFontPathCandidate(base_dir, *override_path); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -616,7 +654,7 @@ static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { if (!override_path2->empty() && !override_path2->is_absolute() && !override_path2->has_parent_path()) { - return base_dir / *override_path2; + return ResolveSystemFontPathCandidate(base_dir, *override_path2); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -628,7 +666,7 @@ static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { if (!override_path3->empty() && !override_path3->is_absolute() && !override_path3->has_parent_path()) { - return base_dir / *override_path3; + return ResolveSystemFontPathCandidate(base_dir, *override_path3); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -636,7 +674,7 @@ static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { } } if (def->default_file && *def->default_file) { - return base_dir / def->default_file; + return ResolveSystemFontPathCandidate(base_dir, def->default_file); } } LOG_ERROR(Lib_Font, "SystemFonts: unknown font set type=0x{:08X}", font_set_type); @@ -668,10 +706,47 @@ static bool LoadFontFile(const std::filesystem::path& path, std::vector bool { + out_bytes.clear(); + if (!LoadGuestFileBytes(p, out_bytes) || out_bytes.empty()) { + out_bytes.clear(); + return false; + } + return true; + }; + + if (try_load(path)) { + return true; } - return true; + + std::error_code ec; + const auto parent = path.parent_path(); + const auto parent_name = parent.filename().string(); + const auto file_name = path.filename(); + if (!file_name.empty() && parent_name != "font" && parent_name != "font2") { + const auto cand_font = parent / "font" / file_name; + if (std::filesystem::is_regular_file(cand_font, ec) && !ec) { + if (try_load(cand_font)) { + return true; + } + } + const auto cand_font2 = parent / "font2" / file_name; + if (std::filesystem::is_regular_file(cand_font2, ec) && !ec) { + if (try_load(cand_font2)) { + return true; + } + } + } + + if (!file_name.empty() && (parent_name == "font" || parent_name == "font2")) { + const auto container = parent.parent_path(); + const auto sibling = container / ((parent_name == "font") ? "font2" : "font") / file_name; + if (std::filesystem::is_regular_file(sibling, ec) && !ec) { + return try_load(sibling); + } + } + + return false; } } // namespace @@ -4366,6 +4441,53 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o Internal::PopulateStateMetrics(st, m); st.ext_face_ready = true; + auto compute_sysfont_scale_factor = [](FT_Face face, int units_per_em) -> float { + (void)units_per_em; + if (!face) { + return 1.0f; + } + const TT_OS2* os2 = + static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); + if (os2) { + if (os2->sTypoAscender == 770 && os2->sTypoDescender == -230) { + return 1024.0f / 952.0f; + } + } + return 1.0f; + }; + auto compute_sysfont_shift_value = [](FT_Face face) -> s32 { + if (!face) { + return 0; + } + const TT_OS2* os2 = + static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); + if (!os2) { + return 0; + } + const bool is_jp_pro_metrics = (os2->sTypoAscender == 880) && + (os2->sTypoDescender == -120) && + (os2->sTypoLineGap == 1); + if (is_jp_pro_metrics) { + const auto* vhea = + static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_vhea)); + if (vhea) { + const int units = static_cast(face->units_per_EM); + const int gap = static_cast(os2->sTypoLineGap); + const int shift = 1024 - units - gap; + if (shift > 0 && shift < 128) { + return static_cast(shift); + } + } + } + return 0; + }; + st.system_font_scale_factor = compute_sysfont_scale_factor(st.ext_ft_face, st.ext_units_per_em); + st.system_font_shift_value = compute_sysfont_shift_value(st.ext_ft_face); + LOG_DEBUG(Lib_Font, "SystemFonts: primary='{}' unitsPerEm={} scaleFactor={} shiftValue={}", + primary_path.filename().string(), st.ext_units_per_em, st.system_font_scale_factor, + st.system_font_shift_value); + + std::string preferred_latin_name_lower; const auto base_dir = Internal::GetSysFontBaseDir(); if (!base_dir.empty()) { auto resolve_existing = @@ -4470,6 +4592,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.bytes->data(), fb.bytes->size(), sub_font_index); fb.ready = (fb.ft_face != nullptr); if (fb.ready) { + fb.scale_factor = compute_sysfont_scale_factor( + fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); + fb.shift_value = compute_sysfont_shift_value(fb.ft_face); + LOG_DEBUG(Lib_Font, + "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", + fb.path.filename().string(), + fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, + fb.scale_factor, fb.shift_value); st.system_fallback_faces.push_back(std::move(fb)); } else { Internal::DestroyFreeTypeFace(fb.ft_face); @@ -4477,10 +4607,83 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o }; { - if (auto roman_path = resolve_sysfont_path(base_dir / "SST-Roman.otf")) { - const std::string roman_lower = lower_ascii(roman_path->filename().string()); - if (!has_fallback_name_lower(roman_lower)) { - add_fallback_face(*roman_path); + const u32 tag = (fontSetType >> 8) & 0xFFu; + const u32 variant = (fontSetType >> 20) & 0x0Fu; + u32 style_suffix = fontSetType & 0xFFu; + + switch (style_suffix) { + case 0xC3: + style_suffix = 0x43; + break; + case 0xC4: + style_suffix = 0x44; + break; + case 0xC5: + style_suffix = 0x45; + break; + case 0xC7: + style_suffix = 0x47; + break; + default: + break; + } + + const char* latin_file = nullptr; + if (tag != 0x00u && tag != 0x10u) { + if (variant == 0x3u) { + switch (style_suffix) { + case 0x44: + latin_file = "SSTTypewriter-Roman.otf"; + break; + case 0x47: + latin_file = "SSTTypewriter-Bd.otf"; + break; + default: + break; + } + } else if (variant == 0x1u) { + switch (style_suffix) { + case 0x43: + latin_file = "SST-LightItalic.otf"; + break; + case 0x44: + latin_file = "SST-Italic.otf"; + break; + case 0x45: + latin_file = "SST-MediumItalic.otf"; + break; + case 0x47: + latin_file = "SST-BoldItalic.otf"; + break; + default: + break; + } + } else { + switch (style_suffix) { + case 0x43: + latin_file = "SST-Light.otf"; + break; + case 0x44: + latin_file = "SST-Roman.otf"; + break; + case 0x45: + latin_file = "SST-Medium.otf"; + break; + case 0x47: + latin_file = "SST-Bold.otf"; + break; + default: + break; + } + } + } + + if (latin_file) { + if (auto latin_path = resolve_sysfont_path(base_dir / latin_file)) { + preferred_latin_name_lower = lower_ascii(latin_path->filename().string()); + if (!has_fallback_name_lower(preferred_latin_name_lower)) { + add_fallback_face(*latin_path); + } } } } @@ -4501,6 +4704,18 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o case 0xC7: arabic_file = "SSTArabic-Bold.otf"; break; + case 0xD3: + arabic_file = "SSTArabic-Light.otf"; + break; + case 0xD4: + arabic_file = "SSTArabic-Roman.otf"; + break; + case 0xD5: + arabic_file = "SSTArabic-Medium.otf"; + break; + case 0xD7: + arabic_file = "SSTArabic-Bold.otf"; + break; default: break; } @@ -4515,6 +4730,115 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } } + { + const u32 tag = (fontSetType >> 8) & 0xFFu; + const u32 style_suffix = fontSetType & 0xFFu; + + auto add_named_fallback = [&](std::string_view filename) { + if (filename.empty()) { + return; + } + if (auto p = resolve_sysfont_path(base_dir / std::filesystem::path{filename})) { + const std::string lower = lower_ascii(p->filename().string()); + if (!has_fallback_name_lower(lower)) { + add_fallback_face(*p); + } + } + }; + + const bool is_bold_like = (style_suffix == 0x47u) || (style_suffix == 0x57u) || + (style_suffix == 0xC7u) || (style_suffix == 0xD7u); + + const bool needs_jppro = (tag == 0x04u) || (tag == 0x24u) || (tag == 0x34u) || + (tag == 0x84u) || (tag == 0xA4u) || (tag == 0xACu) || + (tag == 0xB4u) || (tag == 0xBCu); + const bool needs_cngb = (tag == 0x80u) || (tag == 0x84u) || (tag == 0x90u) || + (tag == 0xA0u) || (tag == 0xA4u) || (tag == 0xACu) || + (tag == 0xB0u) || (tag == 0xB4u) || (tag == 0xBCu); + const bool needs_hangul = (tag == 0x24u) || (tag == 0x34u) || (tag == 0xA0u) || + (tag == 0xA4u) || (tag == 0xACu) || (tag == 0xB0u) || + (tag == 0xB4u) || (tag == 0xBCu); + + u32 sea_weight_code = style_suffix; + switch (sea_weight_code) { + case 0xD3u: + sea_weight_code = 0x53u; + break; + case 0xD4u: + sea_weight_code = 0x54u; + break; + case 0xD5u: + sea_weight_code = 0x55u; + break; + case 0xD7u: + sea_weight_code = 0x57u; + break; + default: + break; + } + const bool is_sea_weight = (sea_weight_code == 0x53u) || (sea_weight_code == 0x54u) || + (sea_weight_code == 0x55u) || (sea_weight_code == 0x57u); + + if (is_sea_weight && primary_name_lower.rfind("sstvietnamese-", 0) != 0) { + const char* vn_file = nullptr; + switch (sea_weight_code) { + case 0x53: + vn_file = "SSTVietnamese-Light.otf"; + break; + case 0x54: + vn_file = "SSTVietnamese-Roman.otf"; + break; + case 0x55: + vn_file = "SSTVietnamese-Medium.otf"; + break; + case 0x57: + vn_file = "SSTVietnamese-Bold.otf"; + break; + default: + break; + } + if (vn_file) { + add_named_fallback(vn_file); + } + } + + if (is_sea_weight && ((tag == 0x10u) || (tag == 0x14u) || (tag == 0x34u) || + (tag == 0x90u) || (tag == 0x94u) || (tag == 0xB0u) || + (tag == 0xB4u) || (tag == 0xBCu))) { + const char* th_file = nullptr; + switch (sea_weight_code) { + case 0x53: + th_file = "SSTThai-Light.otf"; + break; + case 0x54: + th_file = "SSTThai-Roman.otf"; + break; + case 0x55: + th_file = "SSTThai-Medium.otf"; + break; + case 0x57: + th_file = "SSTThai-Bold.otf"; + break; + default: + break; + } + if (th_file) { + add_named_fallback(th_file); + } + } + + if (needs_jppro && primary_name_lower.rfind("sstjppro-", 0) != 0) { + add_named_fallback(is_bold_like ? "SSTJpPro-Bold.otf" : "SSTJpPro-Regular.otf"); + } + if (needs_cngb && primary_name_lower != "dfhei5-sony.ttf") { + add_named_fallback("DFHEI5-SONY.ttf"); + } + if (needs_hangul && primary_name_lower.rfind("sceps4yoongd-", 0) != 0) { + add_named_fallback(is_bold_like ? "SCEPS4Yoongd-Bold.otf" + : "SCEPS4Yoongd-Medium.otf"); + } + } + for (int i = 0; i < 8; ++i) { const std::string key_a = fmt::format("fontset_0x{:08X}_fallback{}", fontSetType, i); @@ -4556,6 +4880,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.bytes->data(), fb.bytes->size(), sub_font_index); fb.ready = (fb.ft_face != nullptr); if (fb.ready) { + fb.scale_factor = compute_sysfont_scale_factor( + fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); + fb.shift_value = compute_sysfont_shift_value(fb.ft_face); + LOG_DEBUG(Lib_Font, + "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", + fb.path.filename().string(), + fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, + fb.scale_factor, fb.shift_value); st.system_fallback_faces.push_back(std::move(fb)); } else { Internal::DestroyFreeTypeFace(fb.ft_face); @@ -4902,10 +5234,26 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } return s; }; + const auto select_preferred_latin_id = [&](const std::string& name_lower) -> bool { + if (name_lower.empty()) { + return false; + } + for (const auto& fb : st.system_fallback_faces) { + const auto candidate = lower_ascii_local(fb.path.filename().string()); + if (candidate == name_lower && fb.font_id != 0xffffffffu) { + st.fontset_selector->roman_font_id = fb.font_id; + return true; + } + } + return false; + }; + if (!select_preferred_latin_id(preferred_latin_name_lower)) { + (void)select_preferred_latin_id("sst-roman.otf"); + } for (const auto& fb : st.system_fallback_faces) { const auto name = lower_ascii_local(fb.path.filename().string()); - if (name == "sst-roman.otf" && fb.font_id != 0xffffffffu) { - st.fontset_selector->roman_font_id = fb.font_id; + if (name.rfind("sstarabic-", 0) == 0 && fb.font_id != 0xffffffffu) { + st.fontset_selector->arabic_font_id = fb.font_id; break; } } @@ -4933,7 +5281,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o header->entry_count = entry_count; auto* entries = reinterpret_cast(st.fontset_record_storage->data() + - sizeof(Internal::FontSetRecordHeader)); + sizeof(Internal::FontSetRecordHeader)); for (u32 i = 0; i < entry_count; ++i) { std::construct_at(entries + i, font_ids[i]); } @@ -5129,6 +5477,19 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 cod } const float y_used = y + baseline_add; + { + static std::mutex s_baseline_log_mutex; + static std::unordered_map s_baseline_log_counts; + std::lock_guard lock(s_baseline_log_mutex); + int& count = s_baseline_log_counts[fontHandle]; + if (count < 5) { + LOG_DEBUG(Lib_Font, + "RenderBaseline: handle={} code=U+{:04X} y_in={} baseline_add={} y_used={} pre_rc={}", + static_cast(fontHandle), code, y, baseline_add, y_used, + pre_rc); + ++count; + } + } CachedStyleSetDirectionWord(font->cached_style, 1); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 56d2c5e8f..738cb302d 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -766,7 +766,6 @@ bool ResolveFace(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face return face_out != nullptr; } -// LLE: FUN_0100a690 s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face face, float pixel_w, float pixel_h, Libraries::Font::OrbisFontRenderSurface* surf, u32 code, float x, @@ -990,7 +989,7 @@ s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle han } } - result->stage = nullptr; + result->transImage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); @@ -1051,7 +1050,6 @@ s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle han return ORBIS_OK; } -// LLE: FUN_0100e050 s32 RenderCodepointToSurfaceWithScale(FontState& st, Libraries::Font::OrbisFontHandle handle, FT_Face face, float scale_x, float scale_y, Libraries::Font::OrbisFontRenderSurface* surf, u32 code, @@ -1237,7 +1235,7 @@ s32 RenderCodepointToSurfaceWithScale(FontState& st, Libraries::Font::OrbisFontH } } - result->stage = nullptr; + result->transImage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); @@ -1391,76 +1389,76 @@ constexpr SystemFontDefinition kSystemFontDefinitions[] = { {0x18070054, "FONTSET_SST_STD_VIETNAMESE", "SSTVietnamese-Roman.otf"}, {0x18070055, "FONTSET_SST_STD_VIETNAMESE_MEDIUM", "SSTVietnamese-Medium.otf"}, {0x18070057, "FONTSET_SST_STD_VIETNAMESE_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180700C3, "FONTSET_SST_STD_EUROPEAN_AR_LIGHT", "SSTArabic-Light.otf"}, - {0x180700C4, "FONTSET_SST_STD_EUROPEAN_AR", "SSTArabic-Roman.otf"}, - {0x180700C5, "FONTSET_SST_STD_EUROPEAN_AR_MEDIUM", "SSTArabic-Medium.otf"}, - {0x180700C7, "FONTSET_SST_STD_EUROPEAN_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18070444, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, - {0x18070447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x18070454, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, - {0x18070457, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180704C4, "FONTSET_SST_STD_EUROPEAN_JP_AR", "SSTArabic-Roman.otf"}, - {0x180704C7, "FONTSET_SST_STD_EUROPEAN_JP_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18071053, "FONTSET_SST_STD_THAI_LIGHT", "SSTThai-Light.otf"}, - {0x18071054, "FONTSET_SST_STD_THAI", "SSTThai-Roman.otf"}, - {0x18071055, "FONTSET_SST_STD_THAI_MEDIUM", "SSTThai-Medium.otf"}, - {0x18071057, "FONTSET_SST_STD_THAI_BOLD", "SSTThai-Bold.otf"}, - {0x18071454, "FONTSET_SST_STD_EUROPEAN_JP_TH", "SSTThai-Roman.otf"}, - {0x18071457, "FONTSET_SST_STD_EUROPEAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, - {0x18072444, "FONTSET_SST_STD_EUROPEAN_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x180700C3, "FONTSET_SST_STD_EUROPEAN_AR_LIGHT", "SST-Light.otf"}, + {0x180700C4, "FONTSET_SST_STD_EUROPEAN_AR", "SST-Roman.otf"}, + {0x180700C5, "FONTSET_SST_STD_EUROPEAN_AR_MEDIUM", "SST-Medium.otf"}, + {0x180700C7, "FONTSET_SST_STD_EUROPEAN_AR_BOLD", "SST-Bold.otf"}, + {0x18070444, "FONTSET_SST_STD_EUROPEAN_JP", "SSTJpPro-Regular.otf"}, + {0x18070447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x18070454, "FONTSET_SST_STD_EUROPEAN_JP", "SSTJpPro-Regular.otf"}, + {0x18070457, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x180704C4, "FONTSET_SST_STD_EUROPEAN_JP_AR", "SSTJpPro-Regular.otf"}, + {0x180704C7, "FONTSET_SST_STD_EUROPEAN_JP_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18071053, "FONTSET_SST_STD_THAI_LIGHT", "SSTVietnamese-Light.otf"}, + {0x18071054, "FONTSET_SST_STD_THAI", "SSTVietnamese-Roman.otf"}, + {0x18071055, "FONTSET_SST_STD_THAI_MEDIUM", "SSTVietnamese-Medium.otf"}, + {0x18071057, "FONTSET_SST_STD_THAI_BOLD", "SSTVietnamese-Bold.otf"}, + {0x18071454, "FONTSET_SST_STD_EUROPEAN_JP_TH", "SSTJpPro-Regular.otf"}, + {0x18071457, "FONTSET_SST_STD_EUROPEAN_JP_TH_BOLD", "SSTJpPro-Bold.otf"}, + {0x18072444, "FONTSET_SST_STD_EUROPEAN_JPUH", "SSTJpPro-Regular.otf"}, {0x18072447, "FONTSET_SST_STD_EUROPEAN_JPUH_BOLD", "SSTJpPro-Bold.otf"}, - {0x180724C4, "FONTSET_SST_STD_EUROPEAN_JPUH_AR", "SSTArabic-Roman.otf"}, - {0x180724C7, "FONTSET_SST_STD_EUROPEAN_JPUH_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18073454, "FONTSET_SST_STD_EUROPEAN_JPUH_TH", "SSTThai-Roman.otf"}, - {0x18073457, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, - {0x180734D4, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, - {0x180734D7, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, + {0x180724C4, "FONTSET_SST_STD_EUROPEAN_JPUH_AR", "SSTJpPro-Regular.otf"}, + {0x180724C7, "FONTSET_SST_STD_EUROPEAN_JPUH_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18073454, "FONTSET_SST_STD_EUROPEAN_JPUH_TH", "SSTJpPro-Regular.otf"}, + {0x18073457, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180734D4, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR", "SSTJpPro-Regular.otf"}, + {0x180734D7, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR_BOLD", "SSTJpPro-Bold.otf"}, {0x18078044, "FONTSET_SST_STD_EUROPEAN_GB", "DFHEI5-SONY.ttf"}, - {0x180780C4, "FONTSET_SST_STD_EUROPEAN_GB_AR", "SSTArabic-Roman.otf"}, - {0x18079054, "FONTSET_SST_STD_EUROPEAN_GB_TH", "SSTThai-Roman.otf"}, - {0x1807A044, "FONTSET_SST_STD_EUROPEAN_GBUH", "e046323ms.ttf"}, - {0x1807A0C4, "FONTSET_SST_STD_EUROPEAN_GBUH_AR", "SSTArabic-Bold.otf"}, + {0x180780C4, "FONTSET_SST_STD_EUROPEAN_GB_AR", "DFHEI5-SONY.ttf"}, + {0x18079054, "FONTSET_SST_STD_EUROPEAN_GB_TH", "DFHEI5-SONY.ttf"}, + {0x1807A044, "FONTSET_SST_STD_EUROPEAN_GBUH", "DFHEI5-SONY.ttf"}, + {0x1807A0C4, "FONTSET_SST_STD_EUROPEAN_GBUH_AR", "DFHEI5-SONY.ttf"}, {0x1807A444, "FONTSET_SST_STD_EUROPEAN_JPCJK", "SSTJpPro-Regular.otf"}, - {0x1807A4C4, "FONTSET_SST_STD_EUROPEAN_JPCJK_AR", "SSTArabic-Roman.otf"}, - {0x1807AC44, "FONTSET_SST_STD_EUROPEAN_GBCJK", "n023055ms.ttf"}, - {0x1807ACC4, "FONTSET_SST_STD_EUROPEAN_GBCJK_AR", "SSTArabic-Bold.otf"}, - {0x1807B054, "FONTSET_SST_STD_EUROPEAN_GBUH_TH", "SSTThai-Roman.otf"}, - {0x1807B0D4, "FONTSET_SST_STD_EUROPEAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, - {0x1807B454, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH", "SSTThai-Roman.otf"}, - {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "SSTThai-Roman.otf"}, - {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTAribStdB24-Regular.ttf"}, - {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTAribStdB24-Regular.ttf"}, - {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTVietnamese-Roman.otf"}, - {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTAribStdB24-Regular.ttf"}, - {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTAribStdB24-Regular.ttf"}, - {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTThai-Roman.otf"}, - {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, - {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTAribStdB24-Regular.ttf"}, + {0x1807A4C4, "FONTSET_SST_STD_EUROPEAN_JPCJK_AR", "SSTJpPro-Regular.otf"}, + {0x1807AC44, "FONTSET_SST_STD_EUROPEAN_GBCJK", "SCEPS4Yoongd-Medium.otf"}, + {0x1807ACC4, "FONTSET_SST_STD_EUROPEAN_GBCJK_AR", "DFHEI5-SONY.ttf"}, + {0x1807B054, "FONTSET_SST_STD_EUROPEAN_GBUH_TH", "DFHEI5-SONY.ttf"}, + {0x1807B0D4, "FONTSET_SST_STD_EUROPEAN_GBUH_TH_AR", "DFHEI5-SONY.ttf"}, + {0x1807B454, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH", "SSTJpPro-Regular.otf"}, + {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTJpPro-Regular.otf"}, + {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "DFHEI5-SONY.ttf"}, + {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "DFHEI5-SONY.ttf"}, + {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTJpPro-Regular.otf"}, + {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTJpPro-Regular.otf"}, + {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTJpPro-Bold.otf"}, + {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTJpPro-Regular.otf"}, + {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTJpPro-Regular.otf"}, + {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTJpPro-Bold.otf"}, + {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTJpPro-Regular.otf"}, {0x18082447, "FONTSET_SST_STD_JAPANESE_JPUH_BOLD", "SSTJpPro-Bold.otf"}, - {0x180824C4, "FONTSET_SST_STD_JAPANESE_JPUH_AR", "SSTAribStdB24-Regular.ttf"}, + {0x180824C4, "FONTSET_SST_STD_JAPANESE_JPUH_AR", "SSTJpPro-Regular.otf"}, {0x180824C7, "FONTSET_SST_STD_JAPANESE_JPUH_AR_BOLD", "SSTJpPro-Bold.otf"}, - {0x18083454, "FONTSET_SST_STD_ASIAN_JPUH_TH", "SSTThai-Roman.otf"}, - {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, - {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, - {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, - {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTAribStdB24-Regular.ttf"}, - {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Bold.otf"}, - {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTThai-Roman.otf"}, - {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x18083454, "FONTSET_SST_STD_ASIAN_JPUH_TH", "SSTJpPro-Regular.otf"}, + {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTJpPro-Bold.otf"}, + {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTJpPro-Regular.otf"}, + {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTJpPro-Bold.otf"}, + {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTJpPro-Regular.otf"}, + {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Regular.otf"}, + {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTJpPro-Regular.otf"}, + {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTJpPro-Regular.otf"}, {0x180C8044, "FONTSET_SST_STD_SCHINESE_GB", "DFHEI5-SONY.ttf"}, {0x180C80C4, "FONTSET_SST_STD_SCHINESE_GB_AR", "DFHEI5-SONY.ttf"}, - {0x180C9054, "FONTSET_SST_STD_ASIAN_GB_TH", "SSTThai-Roman.otf"}, - {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "e046323ms.ttf"}, - {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "e046323ms.ttf"}, - {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "n023055ms.ttf"}, - {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "SSTAribStdB24-Regular.ttf"}, - {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "SSTThai-Roman.otf"}, - {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, - {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "SSTThai-Roman.otf"}, - {0x180CBCD4, "FONTSET_SST_STD_ASIAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, + {0x180C9054, "FONTSET_SST_STD_ASIAN_GB_TH", "DFHEI5-SONY.ttf"}, + {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "DFHEI5-SONY.ttf"}, + {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "DFHEI5-SONY.ttf"}, + {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "DFHEI5-SONY.ttf"}, + {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "DFHEI5-SONY.ttf"}, + {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "DFHEI5-SONY.ttf"}, + {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "DFHEI5-SONY.ttf"}, + {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "DFHEI5-SONY.ttf"}, + {0x180CBCD4, "FONTSET_SST_STD_ASIAN_GBCJK_TH_AR", "DFHEI5-SONY.ttf"}, {0x18170043, "FONTSET_SST_STD_EUROPEAN_LIGHT_ITALIC", "SST-LightItalic.otf"}, {0x18170044, "FONTSET_SST_STD_EUROPEAN_ITALIC", "SST-Italic.otf"}, {0x18170045, "FONTSET_SST_STD_EUROPEAN_MEDIUM_ITALIC", "SST-MediumItalic.otf"}, @@ -1469,8 +1467,8 @@ constexpr SystemFontDefinition kSystemFontDefinitions[] = { {0x18170447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD_ITALIC", "SSTJpPro-Bold.otf"}, {0x18370044, "FONTSET_SST_TYPEWRITER_EUROPEAN", "SSTTypewriter-Roman.otf"}, {0x18370047, "FONTSET_SST_TYPEWRITER_EUROPEAN_BOLD", "SSTTypewriter-Bd.otf"}, - {0x18370444, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP", "SSTTypewriter-Roman.otf"}, - {0x18370447, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP_BOLD", "SSTTypewriter-Bd.otf"}, + {0x18370444, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP", "SSTJpPro-Regular.otf"}, + {0x18370447, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP_BOLD", "SSTJpPro-Bold.otf"}, }; std::mutex g_fontset_cache_mutex; @@ -1600,6 +1598,44 @@ std::filesystem::path GetSysFontBaseDir() { return {}; } +static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesystem::path& base_dir, + const std::filesystem::path& filename) { + if (base_dir.empty() || filename.empty()) { + return {}; + } + std::error_code ec; + const auto is_file = [&](const std::filesystem::path& p) -> bool { + return std::filesystem::is_regular_file(p, ec) && !ec; + }; + + const auto direct = base_dir / filename; + if (is_file(direct)) { + return direct; + } + + const auto base_name = base_dir.filename().string(); + if (base_name != "font" && base_name != "font2") { + const auto in_font = base_dir / "font" / filename; + if (is_file(in_font)) { + return in_font; + } + const auto in_font2 = base_dir / "font2" / filename; + if (is_file(in_font2)) { + return in_font2; + } + } + + if (base_name == "font" || base_name == "font2") { + const auto container = base_dir.parent_path(); + const auto sibling = container / ((base_name == "font") ? "font2" : "font") / filename; + if (is_file(sibling)) { + return sibling; + } + } + + return direct; +} + std::string MacroToCamel(const char* macro_key) { if (!macro_key) { return {}; @@ -1639,7 +1675,7 @@ std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path = Config::getSystemFontOverride(def->config_key)) { if (!override_path->empty() && !override_path->is_absolute() && !override_path->has_parent_path()) { - return base_dir / *override_path; + return ResolveSystemFontPathCandidate(base_dir, *override_path); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -1650,7 +1686,7 @@ std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { if (!override_path2->empty() && !override_path2->is_absolute() && !override_path2->has_parent_path()) { - return base_dir / *override_path2; + return ResolveSystemFontPathCandidate(base_dir, *override_path2); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -1662,7 +1698,7 @@ std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { if (!override_path3->empty() && !override_path3->is_absolute() && !override_path3->has_parent_path()) { - return base_dir / *override_path3; + return ResolveSystemFontPathCandidate(base_dir, *override_path3); } LOG_ERROR(Lib_Font, "SystemFonts: override for '{}' must be a filename only (no path): '{}'", @@ -1670,7 +1706,7 @@ std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { } } if (def->default_file && *def->default_file) { - return base_dir / def->default_file; + return ResolveSystemFontPathCandidate(base_dir, def->default_file); } } LOG_ERROR(Lib_Font, "SystemFonts: unknown font set type=0x{:08X}", font_set_type); @@ -1772,6 +1808,21 @@ bool LoadFontFile(const std::filesystem::path& path, std::vector& const auto parent = path.parent_path(); const auto parent_name = parent.filename().string(); const auto file_name = path.filename(); + if (!file_name.empty() && parent_name != "font" && parent_name != "font2") { + const auto cand_font = parent / "font" / file_name; + if (std::filesystem::is_regular_file(cand_font, ec) && !ec) { + if (try_load(cand_font)) { + return true; + } + } + const auto cand_font2 = parent / "font2" / file_name; + if (std::filesystem::is_regular_file(cand_font2, ec) && !ec) { + if (try_load(cand_font2)) { + return true; + } + } + } + if (!file_name.empty() && (parent_name == "font" || parent_name == "font2")) { const auto container = parent.parent_path(); const auto sibling = container / ((parent_name == "font") ? "font2" : "font") / file_name; @@ -1928,5 +1979,4 @@ std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHan } return {}; } - } // namespace Libraries::Font::Internal diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index 6dc3d95bd..fe755c268 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -3,9 +3,9 @@ #include "core/libraries/font/fontft_internal.h" +#include #include #include -#include #include #include #include @@ -42,7 +42,6 @@ using Libraries::Font::OrbisFontRenderOutput; using Libraries::Font::OrbisFontRenderSurface; using Libraries::Font::OrbisFontStyleFrame; -// LLE: FUN_01007e80 static void UpdateFtFontObjShiftCache(u8* font_obj) { if (!font_obj) { return; @@ -52,7 +51,8 @@ static void UpdateFtFontObjShiftCache(u8* font_obj) { obj->shift_cache_x = static_cast(obj->shift_units_x); obj->shift_cache_y = static_cast(obj->shift_units_y); - const s32 seed_low = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_low = + static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); const s32 seed_high = static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); @@ -136,7 +136,6 @@ void ReleaseCachedStyleLock(Libraries::Font::FontHandleOpaqueNative* font, u32 p font->cached_style.cache_lock_word = prev_lock_word & 0x7fffffff; } -// LLE: FUN_01000c30 std::uint8_t* AcquireFontCtxEntry(std::uint8_t* ctx, u32 idx, u32 mode_low, void** out_obj, u32* out_lock_word) { if (!ctx || !out_obj || !out_lock_word) { @@ -193,7 +192,6 @@ std::uint8_t* AcquireFontCtxEntry(std::uint8_t* ctx, u32 idx, u32 mode_low, void } } -// LLE: FUN_01000d70 static std::uint8_t* AcquireFontCtxEntryAndSelectNode(std::uint8_t* ctx, u32 idx, u32 mode_low, u32 sub_font_index, void** out_obj, u32* out_lock_word) { @@ -212,7 +210,6 @@ static std::uint8_t* AcquireFontCtxEntryAndSelectNode(std::uint8_t* ctx, u32 idx return entry_u8; } -// LLE: FUN_01000db0 static void ReleaseFontCtxEntryLock(std::uint8_t* entry_u8, u32 mode_low, u32 lock_word) { if (!entry_u8) { return; @@ -244,21 +241,20 @@ static void ReleaseFontCtxEntryLock(std::uint8_t* entry_u8, u32 mode_low, u32 lo *lock_word_ptr = lock_word & 0x7FFFFFFFu; } -// LLE: FUN_01000ba0 static void ApplyGlyphSpecialCaseAdjust(int font_id, int glyph_index, int* inout_seed_words) { if (!inout_seed_words) { return; } if (((font_id == 10) && (static_cast(glyph_index - 0x23d1U) < 0x5e)) && ((0x5c0000000017ULL >> - (static_cast(static_cast((static_cast(glyph_index - 0x23d1U) >> 1) & 0x3f)) & - 0x3fULL)) & + (static_cast( + static_cast((static_cast(glyph_index - 0x23d1U) >> 1) & 0x3f)) & + 0x3fULL)) & 1ULL) != 0) { inout_seed_words[0] = inout_seed_words[0] + 500; } } -// LLE: FUN_01009ff0 static std::uintptr_t AcquireRendererSelectionLock(RendererOpaque* renderer) { if (!renderer) { return 0; @@ -279,7 +275,6 @@ static std::uintptr_t AcquireRendererSelectionLock(RendererOpaque* renderer) { } } -// LLE: FUN_0100bf00 static std::uint64_t ResolveGposTagForCode(u32 codepoint) { (void)codepoint; return 0; @@ -301,32 +296,27 @@ static_assert(sizeof(SysFontRangeRecord) == 0x1C); static_assert(offsetof(SysFontRangeRecord, shift_x) == 0x08); static_assert(offsetof(SysFontRangeRecord, scale_mul) == 0x0C); -// LLE: FUN_01000480 static float SysFontScaleFactor(u32 font_id) { (void)font_id; return 1.0f; } -// LLE: FUN_010004d0 static float SysFontShiftValueF32(u32 font_id) { (void)font_id; return 0.0f; } -// LLE: FUN_010004f0 static u8 SysFontHasAltFlag(u32 font_id) { (void)font_id; return 0; } -// LLE: FUN_010006c0 const std::uint8_t* FindSysFontRangeRecord(u32 font_id, u32 codepoint) { (void)font_id; (void)codepoint; return nullptr; } -// LLE: FUN_010098b0 static u32 ResolveSysFontMapping(const void* record, int table_id, u32 codepoint, u32* out_codepoint) { (void)record; @@ -337,7 +327,6 @@ static u32 ResolveSysFontMapping(const void* record, int table_id, u32 codepoint return 0; } -// LLE: FUN_010008b0 u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_font_id) { if (!record) { if (out_font_id) { @@ -352,8 +341,7 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo if (rec->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { const auto view = MakeFontSetRecordView(rec); const u32 entry_count = view.header ? view.header->entry_count : 0u; - const u32 record_primary_id = - (entry_count && view.entry_indices) ? view.entry_indices[0] : 0u; + const u32 record_primary_id = (entry_count && view.entry_indices) ? view.entry_indices[0] : 0u; const auto* selector = view.selector; if (!selector || selector->magic != Libraries::Font::Internal::FontSetSelector::kMagic) { if (out_font_id) { @@ -362,98 +350,60 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo return code_u32; } + const u32 primary_id = selector->primary_font_id != 0xffffffffu ? selector->primary_font_id + : record_primary_id; + const u32 roman_id = selector->roman_font_id != 0xffffffffu ? selector->roman_font_id + : record_primary_id; + const u32 arabic_id = selector->arabic_font_id != 0xffffffffu ? selector->arabic_font_id : roman_id; + auto is_cjk_like = [](u32 cp) -> bool { - if ((cp >= 0x3040u && cp <= 0x30FFu) || (cp >= 0x31F0u && cp <= 0x31FFu) || - (cp >= 0x3400u && cp <= 0x4DBFu) || (cp >= 0x4E00u && cp <= 0x9FFFu) || - (cp >= 0xAC00u && cp <= 0xD7AFu) || (cp >= 0xF900u && cp <= 0xFAFFu) || + if ((cp >= 0x3040u && cp <= 0x30FFu) || + (cp >= 0x31F0u && cp <= 0x31FFu) || + (cp >= 0x3400u && cp <= 0x4DBFu) || + (cp >= 0x4E00u && cp <= 0x9FFFu) || + (cp >= 0xAC00u && cp <= 0xD7AFu) || + (cp >= 0xF900u && cp <= 0xFAFFu) || (cp >= 0x20000u && cp <= 0x2FA1Fu)) { return true; } return false; }; + auto is_arabic_like = [](u32 cp) -> bool { + return (cp >= 0x0600u && cp <= 0x06FFu) || (cp >= 0x0750u && cp <= 0x077Fu) || + (cp >= 0x08A0u && cp <= 0x08FFu) || (cp >= 0xFB50u && cp <= 0xFDFFu) || + (cp >= 0xFE70u && cp <= 0xFEFFu); + }; + auto is_symbol_like = [](u32 cp) -> bool { + return (cp >= 0x25A0u && cp <= 0x25FFu) || + (cp >= 0x2600u && cp <= 0x26FFu) || + (cp >= 0x2700u && cp <= 0x27BFu); + }; - const u32 primary_id = selector->primary_font_id != 0xffffffffu ? selector->primary_font_id - : record_primary_id; + u32 selected_font_id = roman_id; + if (is_arabic_like(code_u32)) { + selected_font_id = arabic_id; + } else if (is_cjk_like(code_u32) || is_symbol_like(code_u32)) { + selected_font_id = primary_id; + } - auto has_glyph_in_ctx = [&](u32 font_id, u32 cp) -> bool { - if (!selector->library || selector->mode_low == 0) { - return false; - } - auto* lib = static_cast(selector->library); - auto* ctx = lib ? static_cast(lib->sysfonts_ctx) : nullptr; - if (!ctx) { - return false; - } - void* head = nullptr; - u32 lock_word = 0; - u8* entry_u8 = AcquireFontCtxEntry(ctx, font_id, selector->mode_low, &head, &lock_word); - auto* entry = entry_u8 ? reinterpret_cast(entry_u8) : nullptr; - auto* node = static_cast(head); - while (node) { - if (node->sub_font_index == selector->sub_font_index) { + if (view.entry_indices && entry_count) { + bool found = false; + for (u32 i = 0; i < entry_count; ++i) { + if (view.entry_indices[i] == selected_font_id) { + found = true; break; } - node = node->next; } - FT_Face face = node ? static_cast(node->ft_face) : nullptr; - const bool ok = face && (FT_Get_Char_Index(face, static_cast(cp)) != 0); - if (entry) { - if (selector->mode_low == 3) { - entry->lock_mode3 = lock_word & 0x7fffffffu; - } else if (selector->mode_low == 2) { - entry->lock_mode2 = lock_word & 0x7fffffffu; - } else { - entry->lock_mode1 = lock_word & 0x7fffffffu; - } - } - return ok; - }; - - auto has_glyph = [&](u32 font_id, u32 cp) -> bool { - if (font_id == 0xffffffffu) { - return false; - } - if (has_glyph_in_ctx(font_id, cp)) { - return true; - } - for (const auto& c : selector->candidates) { - if (c.font_id != font_id || !c.face) { - continue; - } - return FT_Get_Char_Index(c.face, static_cast(cp)) != 0; - } - return false; - }; - - auto select_font = [&](u32 font_id) -> bool { - if (!has_glyph(font_id, code_u32)) { - return false; - } - if (out_font_id) { - *out_font_id = font_id; - } - return true; - }; - - if (selector->roman_font_id != 0xffffffffu && !is_cjk_like(code_u32)) { - if (select_font(selector->roman_font_id)) { - return code_u32; - } - } - if (select_font(primary_id)) { - return code_u32; - } - for (const auto& c : selector->candidates) { - if (select_font(c.font_id)) { - return code_u32; + if (!found) { + selected_font_id = record_primary_id; } } if (out_font_id) { - *out_font_id = primary_id; + *out_font_id = selected_font_id; } (void)flags; - return 0; + return code_u32; } (void)flags; @@ -463,7 +413,6 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo return code_u32; } -// LLE: FUN_010042e0 void ReleaseFontObjectsForHandle(Libraries::Font::OrbisFontHandle fontHandle, int entryCount) { if (!fontHandle) { return; @@ -586,7 +535,6 @@ void ReleaseFontObjectsForHandle(Libraries::Font::OrbisFontHandle fontHandle, in } } -// LLE: FUN_01006990 s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_state_block, u8 (*out_words)[16]) { if (out_words) { @@ -717,7 +665,26 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ float fontset_scale_factor = 1.0f; int fontset_shift_value = 0; - (void)fontset_record; + if (fontset_record && fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { + if (auto* st = Internal::TryGetState(fontHandle)) { + if (st->system_requested) { + if (font_id == st->system_font_id) { + fontset_scale_factor = st->system_font_scale_factor; + fontset_shift_value = st->system_font_shift_value; + } else { + for (const auto& fb : st->system_fallback_faces) { + if (fb.font_id == font_id) { + fontset_scale_factor = fb.scale_factor; + fontset_shift_value = fb.shift_value; + break; + } + } + } + } + } + } else { + (void)fontset_record; + } void* head = nullptr; u32 lock_word = 0; @@ -844,8 +811,7 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ const float line_h = baseline_max + delta_max; const float effect_h = maxss(effect_for_baseline, effect_for_delta); - if ((reinterpret_cast(out_words) & (alignof(HorizontalLayoutBlocks) - 1)) == - 0) { + if ((reinterpret_cast(out_words) & (alignof(HorizontalLayoutBlocks) - 1)) == 0) { auto* out_blocks = reinterpret_cast(out_words); out_blocks->set_baseline(baseline_max); out_blocks->set_line_advance(line_h); @@ -866,7 +832,6 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ return ORBIS_OK; } -// LLE: FUN_01006fb0 s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_state_block, u8 (*out_words)[16]) { auto* font = GetNativeFont(fontHandle); @@ -946,7 +911,25 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st float scale_factor = 1.0f; s32 shift_value = 0; - if (fontset_record) { + if (fontset_record && static_cast(fontset_record)->magic == + Libraries::Font::Internal::FontSetSelector::kMagic) { + if (auto* st = Internal::TryGetState(fontHandle)) { + if (st->system_requested) { + if (font_id == st->system_font_id) { + scale_factor = st->system_font_scale_factor; + shift_value = st->system_font_shift_value; + } else { + for (const auto& fb : st->system_fallback_faces) { + if (fb.font_id == font_id) { + scale_factor = fb.scale_factor; + shift_value = fb.shift_value; + break; + } + } + } + } + } + } else if (fontset_record) { const auto desc = GetSysFontDesc(font_id); scale_factor = desc.scale_factor; shift_value = desc.shift_value; @@ -998,10 +981,11 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st VerticalLayoutAltBlocks layout_alt{}; if (call_rc == ORBIS_OK) { UpdateFtFontObjShiftCache(reinterpret_cast(font_obj)); - call_rc = driver->compute_layout_alt - ? driver->compute_layout_alt(font_obj, style_state_block, - LayoutWordsBytes(layout_alt.words())) - : ORBIS_FONT_ERROR_FATAL; + call_rc = + driver->compute_layout_alt + ? driver->compute_layout_alt(font_obj, style_state_block, + LayoutWordsBytes(layout_alt.words())) + : ORBIS_FONT_ERROR_FATAL; } if (call_rc == ORBIS_OK) { @@ -1021,14 +1005,12 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st if (acc_neg_local64_max < neg_baseline_offset_x_candidate) { assoc_for_neg_local64_max = decoration_span_candidate; } - acc_neg_local64_max = - std::max(acc_neg_local64_max, neg_baseline_offset_x_candidate); + acc_neg_local64_max = std::max(acc_neg_local64_max, neg_baseline_offset_x_candidate); if (acc_sum_max < sum_primary_0x00_and_baseline_offset_x_candidate) { assoc_for_sum_max = decoration_span_candidate; } - acc_sum_max = - std::max(acc_sum_max, sum_primary_0x00_and_baseline_offset_x_candidate); + acc_sum_max = std::max(acc_sum_max, sum_primary_0x00_and_baseline_offset_x_candidate); const float diff = extra_0x08 - baseline_offset_x_candidate; acc_diff_min = std::min(diff, acc_diff_min); @@ -1094,8 +1076,7 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st const float unknown_decoration_0x08 = acc_temp_min - acc_neg_local64_max; const float unknown_decoration_0x0C = -acc_sum_max; - if ((reinterpret_cast(out_words) & (alignof(VerticalLayoutBlocks) - 1)) == - 0) { + if ((reinterpret_cast(out_words) & (alignof(VerticalLayoutBlocks) - 1)) == 0) { auto* out_blocks = reinterpret_cast(out_words); out_blocks->set_column_advance(column_advance); out_blocks->set_baseline_offset_x(baseline_offset_x); @@ -1118,7 +1099,6 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st return ORBIS_OK; } -// LLE: FUN_01007e40 s32 GetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetrics* metrics, bool use_cached_style) { auto clear_metrics = [&] { @@ -1503,7 +1483,6 @@ inline const RenderSurfaceSystemUse* GetSurfaceSystemUse( return surf ? reinterpret_cast(surf->reserved_q) : nullptr; } -// LLE: FUN_0100a690 static const StyleStateBlock* ResolveSurfaceStyleState( const StyleStateBlock* cached_style_state, const Libraries::Font::OrbisFontRenderSurface* surf, const RenderSurfaceSystemUse* sys, StyleStateBlock& tmp_out) { @@ -1541,7 +1520,6 @@ static const StyleStateBlock* ResolveSurfaceStyleState( return &tmp_out; } -// LLE: FUN_0100a690 static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, Libraries::Font::OrbisFontRenderSurface* surf, float x, float y, Libraries::Font::OrbisFontGlyphMetrics* metrics, @@ -1708,7 +1686,7 @@ static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, } } - result->stage = nullptr; + result->transImage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); @@ -1746,7 +1724,7 @@ static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, const int left_i = floor_int(left_f); const int top_i = floor_int(top_f); const int right_i = ceil_int(right_f); - const int bottom_i = ceil_int(bottom_f); + const int bottom_i = floor_int(bottom_f); const float adv_f = x + metrics->Horizontal.advance; const float adv_snapped = static_cast(floor_int(adv_f)) - x; @@ -1798,7 +1776,6 @@ StyleFrameScaleState ResolveMergedStyleFrameScale(const Libraries::Font::OrbisFo } // namespace -// LLE: FUN_0100a690 s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 code, Libraries::Font::OrbisFontRenderSurface* surf, float x, float y, Libraries::Font::OrbisFontGlyphMetrics* metrics, @@ -1881,7 +1858,24 @@ s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 co return ORBIS_FONT_ERROR_NO_SUPPORT_CODE; } - if (!is_internal_fontset_record) { + if (is_internal_fontset_record) { + if (auto* st = Internal::TryGetState(fontHandle)) { + if (st->system_requested) { + if (font_id == st->system_font_id) { + sys_scale_factor = st->system_font_scale_factor; + shift_y_units = st->system_font_shift_value; + } else { + for (const auto& fb : st->system_fallback_faces) { + if (fb.font_id == font_id) { + sys_scale_factor = fb.scale_factor; + shift_y_units = fb.shift_value; + break; + } + } + } + } + } + } else { const SysFontDesc desc = GetSysFontDesc(font_id); sys_scale_factor = desc.scale_factor; shift_y_units = desc.shift_value; @@ -1904,6 +1898,25 @@ s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 co } } + { + static std::mutex s_mutex; + static std::unordered_map s_counts; + std::scoped_lock lock(s_mutex); + int& count = s_counts[fontHandle]; + const bool force_diag = (code == 0x000Au) || (code == 0x000Du) || (code == 0x25B3u); + if (force_diag || count < 5) { + if (!force_diag) { + ++count; + } + LOG_DEBUG(Lib_Font, + "SysfontRender: handle={} code=U+{:04X} mapped=U+{:04X} font_id={} " + "scale_unit={} dpi_x={} dpi_y={} scale_w={} scale_h={} sys_scale_factor={} " + "shift_y_units={}", + static_cast(fontHandle), code, mapped_code, font_id, scale_unit, + dpi_x, dpi_y, scale_x_in, scale_y_in, sys_scale_factor, shift_y_units); + } + } + void* font_obj_void = nullptr; u32 lock_word = 0; std::uint8_t* entry_u8 = AcquireFontCtxEntryAndSelectNode( @@ -1973,7 +1986,6 @@ s32 RenderCharGlyphImageCore(Libraries::Font::OrbisFontHandle fontHandle, u32 co return cleanup_return(rc); } -// LLE: FUN_0100e050 s32 RenderGlyphImageCore(Libraries::Font::OrbisFontGlyph fontGlyph, Libraries::Font::OrbisFontStyleFrame* fontStyleFrame, Libraries::Font::OrbisFontRenderer fontRenderer, @@ -2102,7 +2114,6 @@ s32 RenderGlyphImageCore(Libraries::Font::OrbisFontGlyph fontGlyph, return rc; } -// LLE: FUN_0100b6f0 s32 StyleStateSetScalePixel(void* style_state_block, float w, float h) { auto* st = static_cast(style_state_block); if (!st) { @@ -2119,7 +2130,6 @@ s32 StyleStateSetScalePixel(void* style_state_block, float w, float h) { return 1; } -// LLE: FUN_0100b730 s32 StyleStateSetScalePoint(void* style_state_block, float w, float h) { auto* st = static_cast(style_state_block); if (!st) { @@ -2136,7 +2146,6 @@ s32 StyleStateSetScalePoint(void* style_state_block, float w, float h) { return 1; } -// LLE: FUN_0100b770 s32 StyleStateGetScalePixel(const void* style_state_block, float* w, float* h) { const auto* st = static_cast(style_state_block); if (!st || (!w && !h)) { @@ -2165,7 +2174,6 @@ s32 StyleStateGetScalePixel(const void* style_state_block, float* w, float* h) { return ORBIS_OK; } -// LLE: FUN_0100b7e0 s32 StyleStateGetScalePoint(const void* style_state_block, float* w, float* h) { const auto* st = static_cast(style_state_block); if (!st || (!w && !h)) { @@ -2194,7 +2202,6 @@ s32 StyleStateGetScalePoint(const void* style_state_block, float* w, float* h) { return ORBIS_OK; } -// LLE: FUN_0100b860 s32 StyleStateSetDpi(u32* dpi_pair, u32 h_dpi, u32 v_dpi) { if (!dpi_pair) { return 0; @@ -2213,7 +2220,6 @@ s32 StyleStateSetDpi(u32* dpi_pair, u32 h_dpi, u32 v_dpi) { return 1; } -// LLE: FUN_0100b8b0 s32 StyleStateSetSlantRatio(void* style_state_block, float slantRatio) { auto* st = static_cast(style_state_block); if (!st) { @@ -2234,7 +2240,6 @@ s32 StyleStateSetSlantRatio(void* style_state_block, float slantRatio) { return 1; } -// LLE: FUN_0100b8f0 s32 StyleStateGetSlantRatio(const void* style_state_block, float* slantRatio) { const auto* st = static_cast(style_state_block); if (!st || !slantRatio) { @@ -2244,7 +2249,6 @@ s32 StyleStateGetSlantRatio(const void* style_state_block, float* slantRatio) { return ORBIS_OK; } -// LLE: FUN_0100b910 s32 StyleStateSetWeightScale(void* style_state_block, float weightXScale, float weightYScale) { auto* st = static_cast(style_state_block); if (!st) { @@ -2280,7 +2284,6 @@ s32 StyleStateSetWeightScale(void* style_state_block, float weightXScale, float return changed ? 1 : 0; } -// LLE: FUN_0100b9a0 s32 StyleStateGetWeightScale(const void* style_state_block, float* weightXScale, float* weightYScale, u32* mode) { if ((reinterpret_cast(weightXScale) | @@ -2484,7 +2487,8 @@ static bool ResolveSfntBaseOffset(const u8* data, std::size_t size, u32 subFontI return false; } - const u32 base = static_cast(static_cast(data + want_off))->value(); + const u32 base = + static_cast(static_cast(data + want_off))->value(); if (base > size || (size - base) < 0x0C) { return false; } @@ -2544,8 +2548,7 @@ static bool ReadUnitsPerEm(const u8* data, std::size_t size, u32 base, u16& out_ if (head_len < 0x14 || head_off + 0x14 > size) { return false; } - out_units = - static_cast(static_cast(data + head_off + 0x12))->value(); + out_units = static_cast(static_cast(data + head_off + 0x12))->value(); return out_units != 0; } @@ -3093,6 +3096,7 @@ s32 PS4_SYSV_ABI LibrarySetCharSizeWithDpiStub(void* fontObj, u32 dpi_x, u32 dpi return ORBIS_FONT_ERROR_FATAL; } + const auto char_w = static_cast(static_cast(scale_x * 64.0f)); const auto char_h = static_cast(static_cast(scale_y * 64.0f)); if (FT_Set_Char_Size(face, char_w, char_h, dpi_x, dpi_y) != 0) { @@ -3264,7 +3268,8 @@ s32 PS4_SYSV_ABI LibraryComputeLayoutBlockStub(void* fontObj, const void* style_ float hhea_out = 0.0f; if (const TT_HoriHeader* hhea = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_hhea))) { - const s64 caret_rise_units = x_shift + static_cast(hhea->caret_Slope_Rise); + const s64 caret_rise_units = + x_shift + static_cast(hhea->caret_Slope_Rise); const s32 caret_rise_px = trunc_fixed_16_16_to_int(caret_rise_units * x_scale); hhea_out = static_cast(caret_rise_px - half_effect_w_px) * kOneOver64; } @@ -3442,7 +3447,6 @@ s32 PS4_SYSV_ABI LibraryComputeLayoutAltBlockStub(void* fontObj, const void* sty return ORBIS_OK; } -// LLE: FUN_01007e80 (sys_driver +0xA8) s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 mode, std::uint64_t* out_words) { if (out_words) { @@ -3470,8 +3474,7 @@ s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 m (obj->cached_units_y_0x70 == obj->cached_units_x_0x68) && (mode == 0); auto write_vec88_from_seed_pair_unscaled = [&]() { - const s32 seed_low = - static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_low = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); const s32 seed_high = static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); @@ -3585,7 +3588,6 @@ s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 m return ORBIS_OK; } -// LLE: FUN_0100a740 (sys_driver +0xB8) s32 PS4_SYSV_ABI LibraryGetGlyphMetricsStub(void* fontObj, std::uint32_t* /*opt_param2*/, std::uint8_t /*mode*/, std::uint8_t* out_params, Libraries::Font::OrbisFontGlyphMetrics* out_metrics) { @@ -3631,7 +3633,6 @@ s32 PS4_SYSV_ABI LibraryGetGlyphMetricsStub(void* fontObj, std::uint32_t* /*opt_ return ORBIS_OK; } -// LLE: FUN_0100b750 (sys_driver +0xE0) s32 PS4_SYSV_ABI LibraryApplyGlyphAdjustStub(void* fontObj, u32 /*p2*/, u32 glyphIndex, s32 p4, s32 p5, u32* inoutGlyphIndex) { if (!fontObj || !inoutGlyphIndex) { @@ -3645,7 +3646,6 @@ s32 PS4_SYSV_ABI LibraryApplyGlyphAdjustStub(void* fontObj, u32 /*p2*/, u32 glyp return ORBIS_OK; } -// LLE: FUN_0100d990 (sys_driver +0x108) s32 PS4_SYSV_ABI LibraryConfigureGlyphStub(void* fontObj, std::uint32_t* in_params, s32 mode, std::uint32_t* inout_state) { if (!fontObj || !in_params || !inout_state) { From 78faac52e5048da81a14488147f14861c19b7017 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 9 Jan 2026 13:04:48 +0200 Subject: [PATCH 30/51] clang --- src/core/libraries/font/font.cpp | 51 ++++++++------- src/core/libraries/font/font_internal.cpp | 2 +- src/core/libraries/font/fontft_internal.cpp | 69 ++++++++++----------- 3 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 85636e973..59d9d74a0 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -567,7 +567,7 @@ static std::filesystem::path GetSysFontBaseDir() { } static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesystem::path& base_dir, - const std::filesystem::path& filename) { + const std::filesystem::path& filename) { if (base_dir.empty() || filename.empty()) { return {}; } @@ -4446,8 +4446,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o if (!face) { return 1.0f; } - const TT_OS2* os2 = - static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); + const TT_OS2* os2 = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); if (os2) { if (os2->sTypoAscender == 770 && os2->sTypoDescender == -230) { return 1024.0f / 952.0f; @@ -4459,8 +4458,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o if (!face) { return 0; } - const TT_OS2* os2 = - static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); + const TT_OS2* os2 = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); if (!os2) { return 0; } @@ -4481,11 +4479,12 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } return 0; }; - st.system_font_scale_factor = compute_sysfont_scale_factor(st.ext_ft_face, st.ext_units_per_em); + st.system_font_scale_factor = + compute_sysfont_scale_factor(st.ext_ft_face, st.ext_units_per_em); st.system_font_shift_value = compute_sysfont_shift_value(st.ext_ft_face); LOG_DEBUG(Lib_Font, "SystemFonts: primary='{}' unitsPerEm={} scaleFactor={} shiftValue={}", - primary_path.filename().string(), st.ext_units_per_em, st.system_font_scale_factor, - st.system_font_shift_value); + primary_path.filename().string(), st.ext_units_per_em, + st.system_font_scale_factor, st.system_font_shift_value); std::string preferred_latin_name_lower; const auto base_dir = Internal::GetSysFontBaseDir(); @@ -4595,11 +4594,12 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.scale_factor = compute_sysfont_scale_factor( fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); fb.shift_value = compute_sysfont_shift_value(fb.ft_face); - LOG_DEBUG(Lib_Font, - "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", - fb.path.filename().string(), - fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, - fb.scale_factor, fb.shift_value); + LOG_DEBUG( + Lib_Font, + "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", + fb.path.filename().string(), + fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, + fb.scale_factor, fb.shift_value); st.system_fallback_faces.push_back(std::move(fb)); } else { Internal::DestroyFreeTypeFace(fb.ft_face); @@ -4776,7 +4776,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o default: break; } - const bool is_sea_weight = (sea_weight_code == 0x53u) || (sea_weight_code == 0x54u) || + const bool is_sea_weight = (sea_weight_code == 0x53u) || + (sea_weight_code == 0x54u) || (sea_weight_code == 0x55u) || (sea_weight_code == 0x57u); if (is_sea_weight && primary_name_lower.rfind("sstvietnamese-", 0) != 0) { @@ -4802,9 +4803,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } } - if (is_sea_weight && ((tag == 0x10u) || (tag == 0x14u) || (tag == 0x34u) || - (tag == 0x90u) || (tag == 0x94u) || (tag == 0xB0u) || - (tag == 0xB4u) || (tag == 0xBCu))) { + if (is_sea_weight && + ((tag == 0x10u) || (tag == 0x14u) || (tag == 0x34u) || (tag == 0x90u) || + (tag == 0x94u) || (tag == 0xB0u) || (tag == 0xB4u) || (tag == 0xBCu))) { const char* th_file = nullptr; switch (sea_weight_code) { case 0x53: @@ -4883,11 +4884,12 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.scale_factor = compute_sysfont_scale_factor( fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); fb.shift_value = compute_sysfont_shift_value(fb.ft_face); - LOG_DEBUG(Lib_Font, - "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", - fb.path.filename().string(), - fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, - fb.scale_factor, fb.shift_value); + LOG_DEBUG( + Lib_Font, + "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", + fb.path.filename().string(), + fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, + fb.scale_factor, fb.shift_value); st.system_fallback_faces.push_back(std::move(fb)); } else { Internal::DestroyFreeTypeFace(fb.ft_face); @@ -5281,7 +5283,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o header->entry_count = entry_count; auto* entries = reinterpret_cast(st.fontset_record_storage->data() + - sizeof(Internal::FontSetRecordHeader)); + sizeof(Internal::FontSetRecordHeader)); for (u32 i = 0; i < entry_count; ++i) { std::construct_at(entries + i, font_ids[i]); } @@ -5484,7 +5486,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 cod int& count = s_baseline_log_counts[fontHandle]; if (count < 5) { LOG_DEBUG(Lib_Font, - "RenderBaseline: handle={} code=U+{:04X} y_in={} baseline_add={} y_used={} pre_rc={}", + "RenderBaseline: handle={} code=U+{:04X} y_in={} baseline_add={} " + "y_used={} pre_rc={}", static_cast(fontHandle), code, y, baseline_add, y_used, pre_rc); ++count; diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 738cb302d..15c6fc07d 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -1599,7 +1599,7 @@ std::filesystem::path GetSysFontBaseDir() { } static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesystem::path& base_dir, - const std::filesystem::path& filename) { + const std::filesystem::path& filename) { if (base_dir.empty() || filename.empty()) { return {}; } diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index fe755c268..9f4a21613 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -3,9 +3,9 @@ #include "core/libraries/font/fontft_internal.h" -#include #include #include +#include #include #include #include @@ -51,8 +51,7 @@ static void UpdateFtFontObjShiftCache(u8* font_obj) { obj->shift_cache_x = static_cast(obj->shift_units_x); obj->shift_cache_y = static_cast(obj->shift_units_y); - const s32 seed_low = - static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_low = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); const s32 seed_high = static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); @@ -247,9 +246,8 @@ static void ApplyGlyphSpecialCaseAdjust(int font_id, int glyph_index, int* inout } if (((font_id == 10) && (static_cast(glyph_index - 0x23d1U) < 0x5e)) && ((0x5c0000000017ULL >> - (static_cast( - static_cast((static_cast(glyph_index - 0x23d1U) >> 1) & 0x3f)) & - 0x3fULL)) & + (static_cast(static_cast((static_cast(glyph_index - 0x23d1U) >> 1) & 0x3f)) & + 0x3fULL)) & 1ULL) != 0) { inout_seed_words[0] = inout_seed_words[0] + 500; } @@ -341,7 +339,8 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo if (rec->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { const auto view = MakeFontSetRecordView(rec); const u32 entry_count = view.header ? view.header->entry_count : 0u; - const u32 record_primary_id = (entry_count && view.entry_indices) ? view.entry_indices[0] : 0u; + const u32 record_primary_id = + (entry_count && view.entry_indices) ? view.entry_indices[0] : 0u; const auto* selector = view.selector; if (!selector || selector->magic != Libraries::Font::Internal::FontSetSelector::kMagic) { if (out_font_id) { @@ -352,17 +351,15 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo const u32 primary_id = selector->primary_font_id != 0xffffffffu ? selector->primary_font_id : record_primary_id; - const u32 roman_id = selector->roman_font_id != 0xffffffffu ? selector->roman_font_id - : record_primary_id; - const u32 arabic_id = selector->arabic_font_id != 0xffffffffu ? selector->arabic_font_id : roman_id; + const u32 roman_id = + selector->roman_font_id != 0xffffffffu ? selector->roman_font_id : record_primary_id; + const u32 arabic_id = + selector->arabic_font_id != 0xffffffffu ? selector->arabic_font_id : roman_id; auto is_cjk_like = [](u32 cp) -> bool { - if ((cp >= 0x3040u && cp <= 0x30FFu) || - (cp >= 0x31F0u && cp <= 0x31FFu) || - (cp >= 0x3400u && cp <= 0x4DBFu) || - (cp >= 0x4E00u && cp <= 0x9FFFu) || - (cp >= 0xAC00u && cp <= 0xD7AFu) || - (cp >= 0xF900u && cp <= 0xFAFFu) || + if ((cp >= 0x3040u && cp <= 0x30FFu) || (cp >= 0x31F0u && cp <= 0x31FFu) || + (cp >= 0x3400u && cp <= 0x4DBFu) || (cp >= 0x4E00u && cp <= 0x9FFFu) || + (cp >= 0xAC00u && cp <= 0xD7AFu) || (cp >= 0xF900u && cp <= 0xFAFFu) || (cp >= 0x20000u && cp <= 0x2FA1Fu)) { return true; } @@ -374,8 +371,7 @@ u32 ResolveSysFontCodepoint(const void* record, u64 code, u32 flags, u32* out_fo (cp >= 0xFE70u && cp <= 0xFEFFu); }; auto is_symbol_like = [](u32 cp) -> bool { - return (cp >= 0x25A0u && cp <= 0x25FFu) || - (cp >= 0x2600u && cp <= 0x26FFu) || + return (cp >= 0x25A0u && cp <= 0x25FFu) || (cp >= 0x2600u && cp <= 0x26FFu) || (cp >= 0x2700u && cp <= 0x27BFu); }; @@ -665,7 +661,8 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ float fontset_scale_factor = 1.0f; int fontset_shift_value = 0; - if (fontset_record && fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { + if (fontset_record && + fontset_record->magic == Libraries::Font::Internal::FontSetSelector::kMagic) { if (auto* st = Internal::TryGetState(fontHandle)) { if (st->system_requested) { if (font_id == st->system_font_id) { @@ -811,7 +808,8 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ const float line_h = baseline_max + delta_max; const float effect_h = maxss(effect_for_baseline, effect_for_delta); - if ((reinterpret_cast(out_words) & (alignof(HorizontalLayoutBlocks) - 1)) == 0) { + if ((reinterpret_cast(out_words) & (alignof(HorizontalLayoutBlocks) - 1)) == + 0) { auto* out_blocks = reinterpret_cast(out_words); out_blocks->set_baseline(baseline_max); out_blocks->set_line_advance(line_h); @@ -981,11 +979,10 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st VerticalLayoutAltBlocks layout_alt{}; if (call_rc == ORBIS_OK) { UpdateFtFontObjShiftCache(reinterpret_cast(font_obj)); - call_rc = - driver->compute_layout_alt - ? driver->compute_layout_alt(font_obj, style_state_block, - LayoutWordsBytes(layout_alt.words())) - : ORBIS_FONT_ERROR_FATAL; + call_rc = driver->compute_layout_alt + ? driver->compute_layout_alt(font_obj, style_state_block, + LayoutWordsBytes(layout_alt.words())) + : ORBIS_FONT_ERROR_FATAL; } if (call_rc == ORBIS_OK) { @@ -1005,12 +1002,14 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st if (acc_neg_local64_max < neg_baseline_offset_x_candidate) { assoc_for_neg_local64_max = decoration_span_candidate; } - acc_neg_local64_max = std::max(acc_neg_local64_max, neg_baseline_offset_x_candidate); + acc_neg_local64_max = + std::max(acc_neg_local64_max, neg_baseline_offset_x_candidate); if (acc_sum_max < sum_primary_0x00_and_baseline_offset_x_candidate) { assoc_for_sum_max = decoration_span_candidate; } - acc_sum_max = std::max(acc_sum_max, sum_primary_0x00_and_baseline_offset_x_candidate); + acc_sum_max = + std::max(acc_sum_max, sum_primary_0x00_and_baseline_offset_x_candidate); const float diff = extra_0x08 - baseline_offset_x_candidate; acc_diff_min = std::min(diff, acc_diff_min); @@ -1076,7 +1075,8 @@ s32 ComputeVerticalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_st const float unknown_decoration_0x08 = acc_temp_min - acc_neg_local64_max; const float unknown_decoration_0x0C = -acc_sum_max; - if ((reinterpret_cast(out_words) & (alignof(VerticalLayoutBlocks) - 1)) == 0) { + if ((reinterpret_cast(out_words) & (alignof(VerticalLayoutBlocks) - 1)) == + 0) { auto* out_blocks = reinterpret_cast(out_words); out_blocks->set_column_advance(column_advance); out_blocks->set_baseline_offset_x(baseline_offset_x); @@ -2487,8 +2487,7 @@ static bool ResolveSfntBaseOffset(const u8* data, std::size_t size, u32 subFontI return false; } - const u32 base = - static_cast(static_cast(data + want_off))->value(); + const u32 base = static_cast(static_cast(data + want_off))->value(); if (base > size || (size - base) < 0x0C) { return false; } @@ -2548,7 +2547,8 @@ static bool ReadUnitsPerEm(const u8* data, std::size_t size, u32 base, u16& out_ if (head_len < 0x14 || head_off + 0x14 > size) { return false; } - out_units = static_cast(static_cast(data + head_off + 0x12))->value(); + out_units = + static_cast(static_cast(data + head_off + 0x12))->value(); return out_units != 0; } @@ -3096,7 +3096,6 @@ s32 PS4_SYSV_ABI LibrarySetCharSizeWithDpiStub(void* fontObj, u32 dpi_x, u32 dpi return ORBIS_FONT_ERROR_FATAL; } - const auto char_w = static_cast(static_cast(scale_x * 64.0f)); const auto char_h = static_cast(static_cast(scale_y * 64.0f)); if (FT_Set_Char_Size(face, char_w, char_h, dpi_x, dpi_y) != 0) { @@ -3268,8 +3267,7 @@ s32 PS4_SYSV_ABI LibraryComputeLayoutBlockStub(void* fontObj, const void* style_ float hhea_out = 0.0f; if (const TT_HoriHeader* hhea = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_hhea))) { - const s64 caret_rise_units = - x_shift + static_cast(hhea->caret_Slope_Rise); + const s64 caret_rise_units = x_shift + static_cast(hhea->caret_Slope_Rise); const s32 caret_rise_px = trunc_fixed_16_16_to_int(caret_rise_units * x_scale); hhea_out = static_cast(caret_rise_px - half_effect_w_px) * kOneOver64; } @@ -3474,7 +3472,8 @@ s32 PS4_SYSV_ABI LibraryLoadGlyphCachedStub(void* fontObj, u32 glyphIndex, s32 m (obj->cached_units_y_0x70 == obj->cached_units_x_0x68) && (mode == 0); auto write_vec88_from_seed_pair_unscaled = [&]() { - const s32 seed_low = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const s32 seed_low = + static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); const s32 seed_high = static_cast(static_cast((obj->layout_seed_pair >> 32) & 0xFFFFFFFFu)); obj->layout_seed_vec[0] = static_cast(static_cast(seed_low)); From 6174ce1c747d0b916dd1c21e257d37e1491bc4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valdis=20Bogd=C4=81ns?= Date: Fri, 9 Jan 2026 14:23:00 +0200 Subject: [PATCH 31/51] Update fontft_internal.cpp typo fix --- src/core/libraries/font/fontft_internal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index 9f4a21613..57e3dc653 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -1686,7 +1686,7 @@ static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, } } - result->transImage = nullptr; + result->stage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); From 4ae39ff2df261a654d999e86e948d2a49e364a1c Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 9 Jan 2026 14:39:18 +0200 Subject: [PATCH 32/51] Fix: fypo --- src/core/libraries/font/font_internal.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 15c6fc07d..ace9cdca0 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -989,7 +989,7 @@ s32 RenderCodepointToSurface(FontState& st, Libraries::Font::OrbisFontHandle han } } - result->transImage = nullptr; + result->stage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); @@ -1235,7 +1235,7 @@ s32 RenderCodepointToSurfaceWithScale(FontState& st, Libraries::Font::OrbisFontH } } - result->transImage = nullptr; + result->stage = nullptr; result->SurfaceImage.address = static_cast(surf->buffer); result->SurfaceImage.widthByte = static_cast(surf->widthByte); result->SurfaceImage.pixelSizeByte = static_cast(surf->pixelSizeByte); From a0014a885346db4fef9e2694954ee9f612ed7d06 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 18:24:19 +0200 Subject: [PATCH 33/51] create sys_fonts in users folder and instructions.txt of what to put inside --- src/common/path_util.cpp | 11 +++++++++++ src/common/path_util.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index b0cbb10cf..1cf80430a 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -128,6 +128,10 @@ static auto UserPaths = [] { create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY); create_path(PathType::CustomConfigs, user_dir / CUSTOM_CONFIGS); create_path(PathType::CacheDir, user_dir / CACHE_DIR); + create_path(PathType::FontDir, user_dir / FONT_DIR); + // subdirectory for fonts + std::filesystem::create_directory(user_dir / FONT_DIR / "font"); + std::filesystem::create_directory(user_dir / FONT_DIR / "font2"); std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt"); if (notice_file.is_open()) { @@ -144,6 +148,13 @@ static auto UserPaths = [] { notice_file.close(); } + std::ofstream font_instructions(user_dir / FONT_DIR / "Instructions.txt"); + if (font_instructions.is_open()) { + font_instructions << "Place /preinst/common/font contents into font folder\n" + "Place /system/common/font2 contents into font2 folder\n"; + font_instructions.close(); + } + return paths; }(); diff --git a/src/common/path_util.h b/src/common/path_util.h index fd2c18baa..f5979e3b2 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -25,6 +25,7 @@ enum class PathType { CustomTrophy, // Where custom files for trophies are stored. CustomConfigs, // Where custom files for different games are stored. CacheDir, // Where pipeline and shader cache is stored. + FontDir // Where font files are stored. }; constexpr auto PORTABLE_DIR = "user"; @@ -44,6 +45,7 @@ constexpr auto METADATA_DIR = "game_data"; constexpr auto CUSTOM_TROPHY = "custom_trophy"; constexpr auto CUSTOM_CONFIGS = "custom_configs"; constexpr auto CACHE_DIR = "cache"; +constexpr auto FONT_DIR = "sys_fonts"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; From aba1d8242fcb1afc7e9d676d77493eab7751bcf6 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 18:48:12 +0200 Subject: [PATCH 34/51] cut version of emulator settings to support custom sysfonts folder --- src/common/path_util.cpp | 8 +- src/common/path_util.h | 2 +- src/core/emulator_settings.cpp | 293 +++++++++++++++++++++++++++++++++ src/core/emulator_settings.h | 194 ++++++++++++++++++++++ 4 files changed, 492 insertions(+), 5 deletions(-) create mode 100644 src/core/emulator_settings.cpp create mode 100644 src/core/emulator_settings.h diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 1cf80430a..7137a2205 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -128,10 +128,10 @@ static auto UserPaths = [] { create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY); create_path(PathType::CustomConfigs, user_dir / CUSTOM_CONFIGS); create_path(PathType::CacheDir, user_dir / CACHE_DIR); - create_path(PathType::FontDir, user_dir / FONT_DIR); + create_path(PathType::FontDir, user_dir / SYSFONTS_DIR); // subdirectory for fonts - std::filesystem::create_directory(user_dir / FONT_DIR / "font"); - std::filesystem::create_directory(user_dir / FONT_DIR / "font2"); + std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font"); + std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font2"); std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt"); if (notice_file.is_open()) { @@ -148,7 +148,7 @@ static auto UserPaths = [] { notice_file.close(); } - std::ofstream font_instructions(user_dir / FONT_DIR / "Instructions.txt"); + std::ofstream font_instructions(user_dir / SYSFONTS_DIR / "Instructions.txt"); if (font_instructions.is_open()) { font_instructions << "Place /preinst/common/font contents into font folder\n" "Place /system/common/font2 contents into font2 folder\n"; diff --git a/src/common/path_util.h b/src/common/path_util.h index f5979e3b2..00cf870e7 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -45,7 +45,7 @@ constexpr auto METADATA_DIR = "game_data"; constexpr auto CUSTOM_TROPHY = "custom_trophy"; constexpr auto CUSTOM_CONFIGS = "custom_configs"; constexpr auto CACHE_DIR = "cache"; -constexpr auto FONT_DIR = "sys_fonts"; +constexpr auto SYSFONTS_DIR = "sys_fonts"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; diff --git a/src/core/emulator_settings.cpp b/src/core/emulator_settings.cpp new file mode 100644 index 000000000..49d997c49 --- /dev/null +++ b/src/core/emulator_settings.cpp @@ -0,0 +1,293 @@ +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "emulator_settings.h" + +using json = nlohmann::json; + +std::shared_ptr EmulatorSettings::s_instance = nullptr; +std::mutex EmulatorSettings::s_mutex; + +namespace nlohmann { +template <> +struct adl_serializer { + static void to_json(json& j, const std::filesystem::path& p) { + j = p.string(); + } + static void from_json(const json& j, std::filesystem::path& p) { + p = j.get(); + } +}; +} // namespace nlohmann + +// -------------------- +// Print summary +// -------------------- +void EmulatorSettings::PrintChangedSummary(const std::vector& changed) { + if (changed.empty()) { + std::cout << "[Settings] No game-specific overrides applied\n"; + return; + } + std::cout << "[Settings] Game-specific overrides applied:\n"; + for (const auto& k : changed) + std::cout << " * " << k << "\n"; +} + +// -------------------- +// ctor/dtor + singleton +// -------------------- +EmulatorSettings::EmulatorSettings() { + // Load(); +} +EmulatorSettings::~EmulatorSettings() { + Save(); +} + +std::shared_ptr EmulatorSettings::GetInstance() { + std::lock_guard lock(s_mutex); + if (!s_instance) + s_instance = std::make_shared(); + return s_instance; +} + +void EmulatorSettings::SetInstance(std::shared_ptr instance) { + std::lock_guard lock(s_mutex); + s_instance = instance; +} + +// -------------------- +// General helpers +// -------------------- + +std::filesystem::path EmulatorSettings::GetSysFontsDir() { + if (m_general.sys_fonts_dir.value.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::FontDir); + } + return m_general.sys_fonts_dir.value; +} + +void EmulatorSettings::SetSysFontsDir(const std::filesystem::path& dir) { + m_general.sys_fonts_dir.value = dir; +} + +// -------------------- +// Save +// -------------------- +bool EmulatorSettings::Save(const std::string& serial) const { + try { + if (!serial.empty()) { + const std::filesystem::path cfgDir = + Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs); + std::filesystem::create_directories(cfgDir); + const std::filesystem::path path = cfgDir / (serial + ".json"); + + json j = json::object(); + + // Only write overrideable fields for each group + json generalObj = json::object(); + for (auto& item : m_general.GetOverrideableFields()) { + json whole = m_general; + if (whole.contains(item.key)) + generalObj[item.key] = whole[item.key]; + } + j["General"] = generalObj; + + // Debug + /* json debugObj = json::object(); + for (auto& item : m_debug.GetOverrideableFields()) { + json whole = m_debug; + if (whole.contains(item.key)) + debugObj[item.key] = whole[item.key]; + } + j["Debug"] = debugObj; + + // Input + json inputObj = json::object(); + for (auto& item : m_input.GetOverrideableFields()) { + json whole = m_input; + if (whole.contains(item.key)) + inputObj[item.key] = whole[item.key]; + } + j["Input"] = inputObj; + + // Audio + json audioObj = json::object(); + for (auto& item : m_audio.GetOverrideableFields()) { + json whole = m_audio; + if (whole.contains(item.key)) + audioObj[item.key] = whole[item.key]; + } + j["Audio"] = audioObj; + + // GPU + json gpuObj = json::object(); + for (auto& item : m_gpu.GetOverrideableFields()) { + json whole = m_gpu; + if (whole.contains(item.key)) + gpuObj[item.key] = whole[item.key]; + } + j["GPU"] = gpuObj; + + // Vulkan + json vulkanObj = json::object(); + for (auto& item : m_vulkan.GetOverrideableFields()) { + json whole = m_vulkan; + if (whole.contains(item.key)) + vulkanObj[item.key] = whole[item.key]; + } + j["Vulkan"] = vulkanObj;*/ + + std::ofstream out(path); + if (!out.is_open()) { + std::cerr << "Failed to open file for writing: " << path << std::endl; + return false; + } + out << std::setw(4) << j; + out.flush(); + if (out.fail()) { + std::cerr << "Failed to write settings to: " << path << std::endl; + return false; + } + return true; + } else { + const std::filesystem::path path = + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.json"; + json j; + j["General"] = m_general; + /* j["Debug"] = m_debug; + j["Input"] = m_input; + j["Audio"] = m_audio; + j["GPU"] = m_gpu; + j["Vulkan"] = m_vulkan; + j["Users"] = m_userManager.GetUsers();*/ + + std::ofstream out(path); + if (!out.is_open()) { + std::cerr << "Failed to open file for writing: " << path << std::endl; + return false; + } + out << std::setw(4) << j; + out.flush(); + if (out.fail()) { + std::cerr << "Failed to write settings to: " << path << std::endl; + return false; + } + return true; + } + } catch (const std::exception& e) { + std::cerr << "Error saving settings: " << e.what() << std::endl; + return false; + } +} + +// -------------------- +// Load +// -------------------- +bool EmulatorSettings::Load(const std::string& serial) { + try { + const std::filesystem::path userDir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir); + const std::filesystem::path configPath = userDir / "config.json"; + + // Load global config if exists + if (std::ifstream globalIn{configPath}; globalIn.good()) { + json gj; + globalIn >> gj; + if (gj.contains("General")) { + json current = m_general; // JSON from existing struct with all defaults + current.update(gj.at("General")); // merge only fields present in file + m_general = current.get(); // convert back + } + /* if (gj.contains("Debug")) { + json current = m_debug; + current.update(gj.at("Debug")); + m_debug = current.get(); + } + if (gj.contains("Input")) { + json current = m_input; + current.update(gj.at("Input")); + m_input = current.get(); + } + if (gj.contains("Audio")) { + json current = m_audio; + current.update(gj.at("Audio")); + m_audio = current.get(); + } + if (gj.contains("GPU")) { + json current = m_gpu; + current.update(gj.at("GPU")); + m_gpu = current.get(); + } + if (gj.contains("Vulkan")) { + json current = m_vulkan; + current.update(gj.at("Vulkan")); + m_vulkan = current.get(); + } + if (gj.contains("Users")) + m_userManager.GetUsers() = gj.at("Users").get();*/ + } else { + SetDefaultValues(); + // ensure a default user exists + /* if (m_userManager.GetUsers().user.empty()) + m_userManager.GetUsers().user = m_userManager.CreateDefaultUser();*/ + Save(); + } + + // Load per-game overrides and apply + if (!serial.empty()) { + const std::filesystem::path gamePath = + Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serial + ".json"); + if (!std::filesystem::exists(gamePath)) + return false; + + std::ifstream in(gamePath); + if (!in.is_open()) + return false; + + json gj; + in >> gj; + + std::vector changed; + + if (gj.contains("General")) { + ApplyGroupOverrides(m_general, gj.at("General"), changed); + } + if (gj.contains("Debug")) { + ApplyGroupOverrides(m_debug, gj.at("Debug"), changed); + } + if (gj.contains("Input")) { + ApplyGroupOverrides(m_input, gj.at("Input"), changed); + } + if (gj.contains("Audio")) { + ApplyGroupOverrides(m_audio, gj.at("Audio"), changed); + } + if (gj.contains("GPU")) { + ApplyGroupOverrides(m_gpu, gj.at("GPU"), changed); + } + if (gj.contains("Vulkan")) { + ApplyGroupOverrides(m_vulkan, gj.at("Vulkan"), changed); + } + + PrintChangedSummary(changed); + return true; + } + + return true; + } catch (const std::exception& e) { + std::cerr << "Error loading settings: " << e.what() << std::endl; + return false; + } +} + +void EmulatorSettings::SetDefaultValues() { + m_general = GeneralSettings{}; + m_debug = DebugSettings{}; + m_input = InputSettings{}; + m_audio = AudioSettings{}; + m_gpu = GPUSettings{}; + m_vulkan = VulkanSettings{}; +} diff --git a/src/core/emulator_settings.h b/src/core/emulator_settings.h new file mode 100644 index 000000000..73a659a32 --- /dev/null +++ b/src/core/emulator_settings.h @@ -0,0 +1,194 @@ +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "common/types.h" + +// ------------------------------- +// Generic Setting wrapper +// ------------------------------- +template +struct Setting { + T value{}; +}; + +template +void to_json(nlohmann::json& j, const Setting& s) { + j = s.value; +} + +template +void from_json(const nlohmann::json& j, Setting& s) { + s.value = j.get(); +} + +// ------------------------------- +// Helper to describe a per-field override action +// ------------------------------- +struct OverrideItem { + const char* key; + // apply(basePtrToStruct, jsonEntry, changedFields) + std::function&)> apply; +}; + +// Helper factory: create an OverrideItem binding a pointer-to-member +template +inline OverrideItem make_override(const char* key, Setting Struct::* member) { + return OverrideItem{key, [member, key](void* base, const nlohmann::json& entry, + std::vector& changed) { + if (!entry.is_object()) + return; + + Struct* obj = reinterpret_cast(base); + Setting& dst = obj->*member; + + Setting tmp = entry.get>(); + + if (dst.value != tmp.value) { + changed.push_back(std::string(key) + " ( " + + nlohmann::json(dst.value).dump() + " → " + + nlohmann::json(tmp.value).dump() + " )"); + } + + dst.value = tmp.value; + }}; +} + +// ------------------------------- +// General settings +// ------------------------------- +struct GeneralSettings { + Setting sys_fonts_dir; + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, sys_fonts_dir) + +// ------------------------------- +// Debug settings +// ------------------------------- +struct DebugSettings { + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; +//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings) + +// ------------------------------- +// Input settings +// ------------------------------- + +struct InputSettings { + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; +//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings) + +// ------------------------------- +// Audio settings +// ------------------------------- +struct AudioSettings { + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; + +//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings) + +// ------------------------------- +// GPU settings +// ------------------------------- +struct GPUSettings { + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; +//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings) +// ------------------------------- +// Vulkan settings +// ------------------------------- +struct VulkanSettings { + std::vector GetOverrideableFields() const { + return std::vector{}; + } +}; +//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(VulkanSettings) + +// ------------------------------- +// Main manager +// ------------------------------- +class EmulatorSettings { +public: + EmulatorSettings(); + ~EmulatorSettings(); + + static std::shared_ptr GetInstance(); + static void SetInstance(std::shared_ptr instance); + + bool Save(const std::string& serial = "") const; + bool Load(const std::string& serial = ""); + void SetDefaultValues(); + + // general accessors + std::filesystem::path GetSysFontsDir(); + void SetSysFontsDir(const std::filesystem::path& dir); + +private: + GeneralSettings m_general{}; + DebugSettings m_debug{}; + InputSettings m_input{}; + AudioSettings m_audio{}; + GPUSettings m_gpu{}; + VulkanSettings m_vulkan{}; + // UserManager m_userManager; + + static std::shared_ptr s_instance; + static std::mutex s_mutex; + + // Generic helper that applies override descriptors for a specific group + template + void ApplyGroupOverrides(Group& group, const nlohmann::json& groupJson, + std::vector& changed) { + for (auto& item : group.GetOverrideableFields()) { + if (!groupJson.contains(item.key)) + continue; + item.apply(&group, groupJson.at(item.key), changed); + } + } + + static void PrintChangedSummary(const std::vector& changed); + +public: +#define SETTING_FORWARD(group, Name, field) \ + auto Get##Name() const { \ + return group.field.value; \ + } \ + void Set##Name(const decltype(group.field.value)& v) { \ + group.field.value = v; \ + } +#define SETTING_FORWARD_BOOL(group, Name, field) \ + auto Is##Name() const { \ + return group.field.value; \ + } \ + void Set##Name(const decltype(group.field.value)& v) { \ + group.field.value = v; \ + } +#define SETTING_FORWARD_BOOL_READONLY(group, Name, field) \ + auto Is##Name() const { \ + return group.field.value; \ + } + +#undef SETTING_FORWARD +#undef SETTING_FORWARD_BOOL +}; From b97cd2753c567aa92dfec227b4181398eed25772 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 18:53:22 +0200 Subject: [PATCH 35/51] load settings --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index aa3f4de45..080fc94a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ #ifdef _WIN32 #include #endif +#include int main(int argc, char* argv[]) { #ifdef _WIN32 @@ -32,6 +33,10 @@ int main(int argc, char* argv[]) { std::shared_ptr m_emu_state = std::make_shared(); EmulatorState::SetInstance(m_emu_state); // Load configurations + std::shared_ptr emu_settings = std::make_shared(); + EmulatorSettings::SetInstance(emu_settings); + emu_settings->Load(); + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); From 877a60fe3b79ac2984a3a02c5bfce9144db40cc2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 18:54:53 +0200 Subject: [PATCH 36/51] cmakelist --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38341e81e..943609923 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later # Version 3.24 needed for FetchContent OVERRIDE_FIND_PACKAGE @@ -848,6 +848,8 @@ set(CORE src/core/aerolib/stubs.cpp src/core/tls.h src/core/emulator_state.cpp src/core/emulator_state.h + src/core/emulator_settings.cpp + src/core/emulator_settings.h ) if (ARCHITECTURE STREQUAL "x86_64") From 11bdfd3a98f285da2d806d249e7df7499747e315 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 19:18:54 +0200 Subject: [PATCH 37/51] using new saving system but i can't understand the fallback mechanism --- src/common/config.cpp | 101 +--------------------- src/common/config.h | 11 --- src/core/libraries/font/font_internal.cpp | 7 +- 3 files changed, 5 insertions(+), 114 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 34e5524cf..eac463d0a 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,9 +4,8 @@ #include #include #include -#include -#include #include +#include // for wstring support #include #include "common/assert.h" @@ -145,9 +144,6 @@ static ConfigEntry isSideTrophy("right"); static ConfigEntry isConnectedToNetwork(false); static bool enableDiscordRPC = false; static std::filesystem::path sys_modules_path = {}; -static std::filesystem::path sys_font_path = {}; -static std::string sys_font_fallback_name = {}; -static std::unordered_map system_font_overrides; // Input static ConfigEntry cursorState(HideCursorState::Idle); @@ -238,44 +234,6 @@ void setSysModulesPath(const std::filesystem::path& path) { sys_modules_path = path; } -std::filesystem::path getSysFontPath() { - return sys_font_path; -} - -void setSysFontPath(const std::filesystem::path& path) { - sys_font_path = path; -} - -std::optional getSystemFontOverride(std::string_view key) { - if (key.empty()) { - return std::nullopt; - } - auto it = system_font_overrides.find(std::string(key)); - if (it == system_font_overrides.end()) { - return std::nullopt; - } - return it->second; -} - -std::string getSystemFontFallbackName() { - return sys_font_fallback_name; -} - -void setSystemFontFallbackName(const std::string& name) { - sys_font_fallback_name = name; -} - -void setSystemFontOverride(std::string_view key, const std::filesystem::path& path) { - if (key.empty()) { - return; - } - system_font_overrides[std::string(key)] = path; -} - -void clearSystemFontOverrides() { - system_font_overrides.clear(); -} - int getVolumeSlider() { return volumeSlider.get(); } @@ -904,10 +862,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) { return; } - if (!is_game_specific) { - system_font_overrides.clear(); - } - if (data.contains("General")) { const toml::value& general = data.at("General"); @@ -931,43 +885,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) { isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific); defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific); sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path); - // Accept alias without trailing 's' - sys_modules_path = toml::find_fs_path_or(general, "sysModulePath", sys_modules_path); - // Prefer 'sysFontPath'; accept 'SysFontPath' for compatibility - sys_font_path = toml::find_fs_path_or(general, "sysFontPath", sys_font_path); - sys_font_path = toml::find_fs_path_or(general, "SysFontPath", sys_font_path); - } - - if (data.contains("SystemFonts")) { - const toml::value& fonts = data.at("SystemFonts"); - if (fonts.is_table()) { - // Read fallback (lowercase preferred), accept 'Fallback'/'FallbackFontName' for compat - if (fonts.contains("fallback")) { - const auto& v = fonts.at("fallback"); - if (v.is_string()) { - sys_font_fallback_name = toml::get(v); - } - } else if (fonts.contains("Fallback")) { - const auto& v = fonts.at("Fallback"); - if (v.is_string()) { - sys_font_fallback_name = toml::get(v); - } - } else if (fonts.contains("FallbackFontName")) { - const auto& v = fonts.at("FallbackFontName"); - if (v.is_string()) { - sys_font_fallback_name = toml::get(v); - } - } - for (const auto& [name, value] : fonts.as_table()) { - if (name == "fallback" || name == "Fallback" || name == "FallbackFontName") { - continue; - } - if (value.is_string()) { - system_font_overrides[name] = - std::filesystem::path(toml::get(value)); - } - } - } } if (data.contains("Input")) { @@ -1241,22 +1158,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) { // Non game-specific entries data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data}; - // Save using 'sysFontPath' to match style - data["General"]["sysFontPath"] = string{fmt::UTF(sys_font_path.u8string()).data}; - { - toml::table fonts_table; - if (!sys_font_fallback_name.empty()) { - fonts_table["fallback"] = sys_font_fallback_name; - } - for (const auto& [name, path_override] : system_font_overrides) { - fonts_table[name] = string{fmt::UTF(path_override.u8string()).data}; - } - if (!fonts_table.empty()) { - data["SystemFonts"] = fonts_table; - } else if (data.is_table()) { - data.as_table().erase("SystemFonts"); - } - } data["GUI"]["installDirs"] = install_dirs; data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; diff --git a/src/common/config.h b/src/common/config.h index 9903e6e35..2a95e6cf0 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,8 +4,6 @@ #pragma once #include -#include -#include #include #include "types.h" @@ -155,15 +153,6 @@ void setConnectedToNetwork(bool enable, bool is_game_specific = false); void setUserName(const std::string& name, bool is_game_specific = false); std::filesystem::path getSysModulesPath(); void setSysModulesPath(const std::filesystem::path& path); -std::filesystem::path getSysFontPath(); -void setSysFontPath(const std::filesystem::path& path); -std::optional getSystemFontOverride(std::string_view key); -std::string getSystemFontFallbackName(); -void setSystemFontFallbackName(const std::string& name); -void setSystemFontOverride(std::string_view key, const std::filesystem::path& path); -void clearSystemFontOverrides(); -bool getLoadAutoPatches(); -void setLoadAutoPatches(bool enable); enum UsbBackendType : int { Real, SkylandersPortal, InfinityBase, DimensionsToypad }; int getUsbDeviceBackend(); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index ace9cdca0..84afc8b78 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "font_internal.h" @@ -10,6 +10,7 @@ #include FT_OUTLINE_H #include FT_TRUETYPE_TABLES_H +#include "core/emulator_settings.h" #include "core/libraries/font/fontft_internal.h" namespace Libraries::Font::Internal { @@ -1539,7 +1540,7 @@ static std::optional FindChildDirContainingFile( } std::filesystem::path GetSysFontBaseDir() { - std::filesystem::path base = Config::getSysFontPath(); + std::filesystem::path base = EmulatorSettings::GetInstance()->GetSysFontsDir(); std::error_code ec; if (base.empty()) { LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); @@ -1972,7 +1973,7 @@ std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHan } if (!st.system_requested) { st.system_requested = true; - const auto configured = Config::getSysFontPath(); + const auto configured = EmulatorSettings::GetInstance()->GetSysFontsDir(); return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " "could not be loaded", static_cast(handle), configured.string()); From a30014fbfcb766fed7830314a2496c7d0bd39842 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 12 Jan 2026 19:38:51 +0200 Subject: [PATCH 38/51] removed lle versions --- src/core/libraries/libs.cpp | 4 ++++ src/emulator.cpp | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 7f679e7c2..63e8d7467 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -12,6 +12,8 @@ #include "core/libraries/companion/companion_httpd.h" #include "core/libraries/companion/companion_util.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/font/font.h" +#include "core/libraries/font/fontft.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/hmd/hmd.h" @@ -142,6 +144,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Voice::RegisterLib(sym); Libraries::Rudp::RegisterLib(sym); Libraries::VrTracker::RegisterLib(sym); + Libraries::Font::RegisterlibSceFont(sym); + Libraries::FontFt::RegisterlibSceFontFt(sym); // Loading libSceSsl is locked behind a title workaround that currently applies to nothing. // Libraries::Ssl::RegisterLib(sym); diff --git a/src/emulator.cpp b/src/emulator.cpp index 44f8b0e72..1403b1254 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -32,8 +32,6 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" -#include "core/libraries/font/font.h" -#include "core/libraries/font/fontft.h" #include "core/libraries/jpeg/jpegenc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" @@ -531,10 +529,7 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib}, - {"libSceCesCs.sprx", nullptr}, - {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, - {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, - {"libSceFreeTypeOt.sprx", nullptr}}); + {"libSceCesCs.sprx", nullptr}}); std::vector found_modules; const auto& sys_module_path = Config::getSysModulesPath(); From 0864bafe470a6ff9b2bfc82d9c21f30e2e4c56b9 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Tue, 13 Jan 2026 17:27:49 +0200 Subject: [PATCH 39/51] Enhance error logging in font metrics functions and update comments for clarity --- src/core/libraries/font/font.cpp | 16 ++++++- src/core/libraries/font/font.h | 16 +++++++ src/core/libraries/font/font_internal.h | 9 ++++ src/core/libraries/font/fontft_internal.cpp | 49 +++++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 59d9d74a0..f376975d5 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -1968,7 +1968,13 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code const s32 rc = Internal::GetCharGlyphMetrics(fontHandle, code, metrics, false); if (rc != ORBIS_OK) { - LOG_ERROR(Lib_Font, "FAILED"); + if (rc == ORBIS_FONT_ERROR_INVALID_FONT_HANDLE) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + } else if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND_RENDERER"); + } } return rc; } @@ -2250,7 +2256,13 @@ s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(OrbisFontHandle fontHandle, u3 const s32 rc = Internal::GetCharGlyphMetrics(fontHandle, codepoint, out_metrics, true); if (rc != ORBIS_OK) { - LOG_ERROR(Lib_Font, "FAILED"); + if (rc == ORBIS_FONT_ERROR_INVALID_FONT_HANDLE) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); + } else if (rc == ORBIS_FONT_ERROR_INVALID_PARAMETER) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + } else if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND_RENDERER"); + } } return rc; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 1684d4eea..fe0a53521 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -30,6 +30,9 @@ struct OrbisFontOpenParams { const void* reserved_ptr1; }; +// Fontlib APIs use Unicode scalar values (UTF-32 codepoints) in `u32` parameters such as `code` and +// `codepoint`. Codepoint-to-glyph resolution is performed via FreeType charmaps, with additional +// sysfont mapping and fallback behavior handled internally. struct OrbisFontGlyphMetrics { float width; float height; @@ -88,6 +91,8 @@ struct OrbisFontGlyphImageMetrics { struct OrbisFontGenerateGlyphParams { u16 id; u16 res0; + // Bitmask copied into `OrbisFontGlyphOpaque::flags`. Only bits `0x01` and `0x10` are accepted + // by the public API; other bits are rejected as invalid. u16 form_options; u8 glyph_form; u8 metrics_form; @@ -112,6 +117,7 @@ struct OrbisFontGlyphOutline { struct OrbisFontGlyphOpaque { u16 magic; + // Encoded flags copied from `OrbisFontGenerateGlyphParams::form_options`. u16 flags; u8 glyph_form; u8 metrics_form; @@ -222,10 +228,17 @@ struct OrbisFontRenderSurface { struct OrbisFontStyleFrame { /*0x00*/ u16 magic; + // `flags1` controls which fields are considered valid overrides: + // - bit0: scale override present (scaleUnit/scalePixelW/scalePixelH) + // - bit1: slant override present (slantRatio) + // - bit2: weight override present (effectWeightX/effectWeightY) /*0x02*/ u8 flags1; /*0x03*/ u8 flags2; /*0x04*/ u32 hDpi; /*0x08*/ u32 vDpi; + // `scaleUnit` selects how scale fields are interpreted: + // - 0: pixel scale (`scalePixelW/scalePixelH`) + // - 1: point scale (converted via DPI by style-state getters) /*0x0C*/ u32 scaleUnit; /*0x10*/ float baseScale; /*0x14*/ float scalePixelW; @@ -235,6 +248,9 @@ struct OrbisFontStyleFrame { /*0x24*/ float slantRatio; /*0x28*/ u32 reserved_0x28; /*0x2C*/ u32 layout_cache_state; + // Packed cache metadata: + // - bits [7:0] cache flags + // - bits [31:16] direction word /*0x30*/ u32 cache_flags_and_direction; /*0x34*/ u32 cache_lock_word; /*0x38*/ u8 layout_cache_bytes[0x20]; diff --git a/src/core/libraries/font/font_internal.h b/src/core/libraries/font/font_internal.h index d1a12609c..5d5b44f2e 100644 --- a/src/core/libraries/font/font_internal.h +++ b/src/core/libraries/font/font_internal.h @@ -200,6 +200,15 @@ struct GlyphEntry { }; struct FontState { + // `scale_*` fields are controlled by style-state setters and are interpreted according to the + // style frame `scaleUnit` (pixel vs point). Public APIs accept codepoints as UTF-32 scalar + // values; glyph lookup and rendering uses FreeType + deterministic fallback selection. + // + // Fallback behavior (high level): + // - A font handle may have an external face (opened from file/memory) and, optionally, a + // sysfont selection (font_set_type + primary sysfont + additional fallback sysfonts). + // - When a glyph is missing from the primary face, the implementation may consult the external + // face and then the configured system fallback faces to preserve observable behavior. float scale_w = 16.0f; float scale_h = 16.0f; float scale_point_w = 12.0f; diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index 57e3dc653..5aae88291 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -42,6 +42,52 @@ using Libraries::Font::OrbisFontRenderOutput; using Libraries::Font::OrbisFontRenderSurface; using Libraries::Font::OrbisFontStyleFrame; +namespace { +struct GetCharGlyphMetricsFailLogState { + u32 count = 0; + bool suppression_logged = false; +}; + +static thread_local GetCharGlyphMetricsFailLogState g_get_char_metrics_fail; + +static void LogGetCharGlyphMetricsFailOnce(std::string_view stage, s32 rc, FT_Error ft_err, + bool is_system, u32 code, FT_UInt glyph_index, + FT_Face face, float scale_w, float scale_h) { + constexpr u32 kMaxDetailedLogs = 16; + if (g_get_char_metrics_fail.count < kMaxDetailedLogs) { + u32 enc = 0; + u16 platform_id = 0; + u16 encoding_id = 0; + u32 num_glyphs = 0; + u32 num_charmaps = 0; + if (face) { + num_glyphs = static_cast(face->num_glyphs); + num_charmaps = static_cast(face->num_charmaps); + if (face->charmap) { + enc = static_cast(face->charmap->encoding); + platform_id = face->charmap->platform_id; + encoding_id = face->charmap->encoding_id; + } + } + + LOG_WARNING( + Lib_Font, + "GetCharGlyphMetricsFail: rc={} stage={} ft_err={} is_system={} code={} glyph_index={} " + "num_glyphs={} cmap(enc={},pid={},eid={}) num_charmaps={} scale_w={} scale_h={}", + rc, stage, static_cast(ft_err), is_system, code, glyph_index, num_glyphs, enc, + platform_id, encoding_id, num_charmaps, scale_w, scale_h); + + ++g_get_char_metrics_fail.count; + return; + } + + if (!g_get_char_metrics_fail.suppression_logged) { + LOG_WARNING(Lib_Font, "GetCharGlyphMetricsFail: further failures suppressed"); + g_get_char_metrics_fail.suppression_logged = true; + } +} +} // namespace + static void UpdateFtFontObjShiftCache(u8* font_obj) { if (!font_obj) { return; @@ -1293,6 +1339,9 @@ s32 GetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code, OrbisFontGlyphMetr } if (resolved_glyph_index == 0) { + LogGetCharGlyphMetricsFailOnce("no_glyph", ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH, 0, + font->open_info.fontset_record != nullptr, code, + resolved_glyph_index, resolved_face, scale_w, scale_h); if (use_cached_style) { font->cached_style.cache_lock_word = prev_cached_lock; } From b637cbbd7043bd0f44b9b69970c6978fd000bcd4 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 16 Jan 2026 13:23:54 +0200 Subject: [PATCH 40/51] Refactor font library functions and improve thread safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated function signatures in font.h to include the OrbisFontLib parameter for cache clearing and outline buffer management. - Introduced a mutex in font_internal.cpp to ensure thread safety by protecting access to global font state and library state. - Added a RemoveState function to safely remove font states. - Enhanced memory allocation functions in fontft_internal.cpp to use Core::ExecuteGuest for better integration with the guest environment. - Simplified font loading logic in LibraryOpenFontMemoryStub to improve readability and maintainability. - Removed redundant code related to file opening and memory allocation, streamlining the font loading process. This potentially fixes emulator crashes in games that use parallel font initialization, such as Dragon’s Crown. --- src/core/libraries/font/font.cpp | 575 ++++++++++++++------ src/core/libraries/font/font.h | 10 +- src/core/libraries/font/font_internal.cpp | 20 + src/core/libraries/font/font_internal.h | 4 +- src/core/libraries/font/fontft_internal.cpp | 153 +++--- 5 files changed, 500 insertions(+), 262 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index f376975d5..145331c59 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include FT_FREETYPE_H #include FT_TRUETYPE_TABLES_H #include "common/config.h" @@ -43,6 +42,8 @@ #include "core/libraries/kernel/process.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" +#include "core/memory.h" +#include "core/tls.h" #include "font_error.h" #ifdef formatParams @@ -71,9 +72,6 @@ static std::string DescribeValue(const T& value) { if constexpr (std::is_same_v) { return value ? "true" : "false"; } else if constexpr (std::is_pointer_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return value ? fmt::format("\"{}\"", value) : "(null)"; - } return fmt::format("{}", reinterpret_cast(value)); } else if constexpr (std::is_floating_point_v) { return fmt::format("{:.6g}", value); @@ -195,7 +193,9 @@ static void RemoveLibState(Libraries::Font::OrbisFontLib lib) { return; } if (free_fn) { - free_fn(alloc_ctx, p); + using FreeFnGuest = void(PS4_SYSV_ABI*)(void* object, void* p); + const auto free_fn_guest = reinterpret_cast(free_fn); + Core::ExecuteGuest(free_fn_guest, alloc_ctx, p); return; } std::free(p); @@ -827,12 +827,13 @@ struct FontLibTail { /*0x10*/ u8 reserved_10[0x10]; /*0x20*/ void* list_head_ptr; /*0x28*/ OrbisFontHandle list_head; - /*0x30*/ u8 reserved_30[0x88]; + /*0x30*/ u8 reserved_30[0x18]; }; static_assert(offsetof(FontLibTail, workspace_size) == 0x04, "FontLibTail workspace_size offset"); static_assert(offsetof(FontLibTail, workspace) == 0x08, "FontLibTail workspace offset"); static_assert(offsetof(FontLibTail, list_head_ptr) == 0x20, "FontLibTail list_head_ptr offset"); static_assert(offsetof(FontLibTail, list_head) == 0x28, "FontLibTail list_head offset"); +static_assert(sizeof(FontLibTail) == 0x48, "FontLibTail size"); #pragma pack(push, 1) struct FontLibReserved1SysfontTail { @@ -1108,7 +1109,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } else { u32* header = static_cast(buffer); if (!header) { - header = static_cast(alloc_fn(lib->alloc_ctx, size)); + header = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, size)); if (!header) { cache_to_store = nullptr; rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; @@ -1139,7 +1140,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } } else { if (!buffer) { - free_fn(lib->alloc_ctx, header); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, header); } cache_to_store = nullptr; rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; @@ -1210,23 +1211,6 @@ s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRender font->cached_style.effectWeightX = effect_weight_y; font->renderer_binding.renderer = renderer; - - LogCachedStyleOnce(fontHandle, *font); - - if (auto* st = Internal::TryGetState(fontHandle)) { - st->bound_renderer = renderer; - st->dpi_x = font->style_frame[0]; - st->dpi_y = font->style_frame[1]; - if (scale_unit != 0) { - st->render_scale_point_active = true; - st->render_scale_point_w = scale_w; - st->render_scale_point_h = scale_h; - } else { - st->render_scale_point_active = false; - st->render_scale_w = scale_w; - st->render_scale_h = scale_h; - } - } } } @@ -1335,9 +1319,48 @@ s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontClearDeviceCache() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontClearDeviceCache(OrbisFontLib library) { + if (!library) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + auto* lib = static_cast(library); + if (lib->magic != 0x0F01) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + u32 prev_lock_word = 0; + if (!AcquireLibraryLock(lib, prev_lock_word)) { + return ORBIS_FONT_ERROR_INVALID_LIBRARY; + } + + const auto kBusy = reinterpret_cast( + static_cast(std::numeric_limits::max())); + + u32* current_cache = nullptr; + for (;;) { + current_cache = lib->device_cache_buf; + if (current_cache != kBusy) { + std::atomic_ref ref(lib->device_cache_buf); + u32* expected = current_cache; + if (ref.compare_exchange_weak(expected, kBusy, std::memory_order_acq_rel)) { + current_cache = expected; + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + s32 rc = ORBIS_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER; + if (current_cache != nullptr) { + current_cache[3] = current_cache[1]; + current_cache[2] = 0; + rc = ORBIS_OK; + } + + lib->device_cache_buf = current_cache; + ReleaseLibraryLock(lib, prev_lock_word); + return rc; } s32 PS4_SYSV_ABI sceFontCloseFont() { @@ -1409,15 +1432,15 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, return ORBIS_FONT_ERROR_INVALID_MEMORY; } - void* lib_mem = malloc_fn(memory->mspace_handle, 0x100); + void* lib_mem = Core::ExecuteGuest(malloc_fn, memory->mspace_handle, 0x100); if (!lib_mem) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } - void* mspace = malloc_fn(memory->mspace_handle, 0x4000); + void* mspace = Core::ExecuteGuest(malloc_fn, memory->mspace_handle, 0x4000); if (!mspace) { - free_fn(memory->mspace_handle, lib_mem); + Core::ExecuteGuest(free_fn, memory->mspace_handle, lib_mem); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -1474,8 +1497,8 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, const s32 init_rc = init_fn(memory, lib); if (init_rc != ORBIS_OK) { - free_fn(memory->mspace_handle, mspace); - free_fn(memory->mspace_handle, lib_mem); + Core::ExecuteGuest(free_fn, memory->mspace_handle, mspace); + Core::ExecuteGuest(free_fn, memory->mspace_handle, lib_mem); LOG_ERROR(Lib_Font, "INIT_FAILED"); return init_rc; } @@ -1554,7 +1577,7 @@ s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { const auto free_fn = native->alloc_vtbl ? reinterpret_cast(native->alloc_vtbl[1]) : nullptr; if (free_fn) { - free_fn(native->alloc_ctx, native); + Core::ExecuteGuest(free_fn, native->alloc_ctx, native); } else { std::free(native); } @@ -1609,8 +1632,9 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, static_cast( create_params); const u32 render_size = selection ? selection->size : 0u; - void* renderer_mem = alloc_fn(memory->mspace_handle, render_size); - void* workspace = alloc_fn(memory->mspace_handle, 0x4000); + void* renderer_mem = + Core::ExecuteGuest(alloc_fn, memory->mspace_handle, render_size); + void* workspace = Core::ExecuteGuest(alloc_fn, memory->mspace_handle, 0x4000); rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; if (renderer_mem && workspace) { @@ -1649,7 +1673,7 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, if (!create_fn) { rc = ORBIS_FONT_ERROR_FATAL; } else { - rc = create_fn(renderer_mem); + rc = Core::ExecuteGuest(create_fn, renderer_mem); if (rc == ORBIS_OK) { s32 sdk_version = 0; u32 sdk_version_u32 = 0; @@ -1677,10 +1701,10 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, } if (workspace) { - free_fn(memory->mspace_handle, workspace); + Core::ExecuteGuest(free_fn, memory->mspace_handle, workspace); } if (renderer_mem) { - free_fn(memory->mspace_handle, renderer_mem); + Core::ExecuteGuest(free_fn, memory->mspace_handle, renderer_mem); } } } @@ -1805,7 +1829,7 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { if (destroy_fn_ptr == kFtRendererDestroyFnAddr) { destroy_fn = &FtRendererDestroy; } - rc = destroy_fn ? destroy_fn(renderer) : ORBIS_FONT_ERROR_FATAL; + rc = destroy_fn ? Core::ExecuteGuest(destroy_fn, renderer) : ORBIS_FONT_ERROR_FATAL; } renderer->selection = nullptr; @@ -1815,9 +1839,9 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { void* alloc_ctx = renderer->alloc_ctx; void* workspace = renderer->workspace; if (workspace) { - free_fn(alloc_ctx, workspace); + Core::ExecuteGuest(free_fn, alloc_ctx, workspace); } - free_fn(alloc_ctx, renderer); + Core::ExecuteGuest(free_fn, alloc_ctx, renderer); *pRenderer = nullptr; return rc; @@ -2158,10 +2182,32 @@ s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, layout->baselineOffset = layout_blocks.baseline(); layout->lineAdvance = layout_blocks.line_advance(); layout->decorationExtent = layout_blocks.effect_height(); + LOG_DEBUG(Lib_Font, "GetHorizontalLayout: out baseLineY={} lineHeight={} effectHeight={}", + layout->baselineOffset, layout->lineAdvance, layout->decorationExtent); return ORBIS_OK; } *layout = {}; + if (const auto* rec = + static_cast(font->open_info.fontset_record)) { + LOG_DEBUG(Lib_Font, + "GetHorizontalLayout: fail detail rc={} fontset_record={} magic=0x{:08X} " + "entry_count={} ctx_entry_index={} sub_font_index={}", + rc, static_cast(font->open_info.fontset_record), rec->magic, + rec->entry_count, font->open_info.ctx_entry_index, + font->open_info.sub_font_index); + const u32 entry_count = rec->entry_count; + const auto* entries = reinterpret_cast(rec + 1); + for (u32 i = 0; i < entry_count; ++i) { + LOG_DEBUG(Lib_Font, "GetHorizontalLayout: fail detail fontset_entry[{}]={}", i, + entries[i]); + } + } else { + LOG_DEBUG(Lib_Font, + "GetHorizontalLayout: fail detail rc={} fontset_record=null ctx_entry_index={} " + "sub_font_index={}", + rc, font->open_info.ctx_entry_index, font->open_info.sub_font_index); + } LOG_ERROR(Lib_Font, "FAILED"); return rc; } @@ -2219,14 +2265,12 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 kerning->offsetY = 0.0f; kerning->positionX = 0.0f; kerning->positionY = 0.0f; - LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); return ORBIS_OK; } kerning->offsetX = 0.0f; kerning->offsetY = 0.0f; kerning->positionX = 0.0f; kerning->positionY = 0.0f; - LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx=0 (no face)", preCode, code); return ORBIS_OK; } @@ -2859,14 +2903,14 @@ s32 PS4_SYSV_ABI sceFontMemoryInit(OrbisFontMem* mem_desc, void* region_addr, u3 mem_desc->mem_kind = 0x0F00; mem_desc->attr_bits = 0; - mem_desc->region_base = region_addr; mem_desc->region_size = region_size; - mem_desc->iface = iface; + mem_desc->region_base = region_addr; mem_desc->mspace_handle = mspace_obj; + mem_desc->iface = iface; mem_desc->on_destroy = destroy_cb; mem_desc->destroy_ctx = destroy_ctx; mem_desc->some_ctx1 = nullptr; - mem_desc->some_ctx2 = nullptr; + mem_desc->some_ctx2 = mspace_obj; return ORBIS_OK; } @@ -2883,7 +2927,7 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { using DestroyFn = void(PS4_SYSV_ABI*)(void* parent, void* mspace); const auto destroy_fn = reinterpret_cast(mem_desc->iface->mspace_destroy); if (destroy_fn) { - destroy_fn(mem_desc->some_ctx2, mem_desc->mspace_handle); + Core::ExecuteGuest(destroy_fn, mem_desc->some_ctx2, mem_desc->mspace_handle); } } std::memset(mem_desc, 0, sizeof(*mem_desc)); @@ -2892,8 +2936,10 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { if (mem_desc->on_destroy) { mem_desc->mem_kind = 0; - mem_desc->attr_bits = 0; - mem_desc->on_destroy(mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); + using DestroyFn = + void(PS4_SYSV_ABI*)(OrbisFontMem * fontMemory, void* object, void* destroyArg); + const auto destroy_fn = reinterpret_cast(mem_desc->on_destroy); + Core::ExecuteGuest(destroy_fn, mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); return ORBIS_OK; } @@ -2994,9 +3040,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat if (auto* h = GetNativeFont(handle)) { h->flags = 0; } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); } else { - handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + handle = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); if (!handle) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -3026,9 +3072,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat const u16 prev_flags = h->flags; h->flags = 0; if ((prev_flags & 0x10) != 0) { - free_fn(lib->alloc_ctx, handle); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, handle); } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); ReleaseLibraryLock(lib, prev_lib_lock); *out_handle = nullptr; return err; @@ -3493,7 +3539,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - out_handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + out_handle = + static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); if (!out_handle) { release_src_lock(); if (pFontHandle) { @@ -3511,7 +3558,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa if (auto* out_native = GetNativeFont(out_handle)) { out_native->flags = 0; } - Internal::g_font_state.erase(out_handle); + Internal::RemoveState(out_handle); } std::memcpy(out_handle, fontHandle, 0x100); @@ -3527,7 +3574,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa if (!entries_base || entry_count > max_entries) { dst->magic = 0; if (owned && free_fn) { - free_fn(lib->alloc_ctx, out_handle); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, out_handle); } release_src_lock(); if (pFontHandle) { @@ -3684,9 +3731,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa const u16 prev_flags = dst->flags; dst->flags = 0; if ((prev_flags & 0x10) != 0 && free_fn) { - free_fn(lib->alloc_ctx, out_handle); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, out_handle); } - Internal::g_font_state.erase(out_handle); + Internal::RemoveState(out_handle); release_src_lock(); if (pFontHandle) { @@ -3878,9 +3925,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd if (auto* h = GetNativeFont(handle)) { h->flags = 0; } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); } else { - handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + handle = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); if (!handle) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -3910,9 +3957,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd const u16 prev_flags = h->flags; h->flags = 0; if ((prev_flags & 0x10) != 0) { - free_fn(lib->alloc_ctx, handle); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, handle); } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); ReleaseLibraryLock(lib, prev_lib_lock); *pFontHandle = nullptr; return err; @@ -4207,7 +4254,6 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o const OrbisFontOpenParams* open_params, OrbisFontHandle* pFontHandle) { LOG_INFO(Lib_Font, "called"); - LOG_DEBUG(Lib_Font, "{}", formatParams({ Param("library", library), @@ -4243,11 +4289,11 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return err; }; - const u32 mode_low = openMode & 0x0Fu; - if (mode_low != 1 && mode_low != 2 && mode_low != 3) { + if (openMode != 1 && openMode != 2 && openMode != 3) { LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_PARAMETER); } + const u32 mode_low = openMode & 0x0Fu; if (!lib_local->fontset_registry || !lib_local->sys_driver) { LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_LIBRARY); @@ -4264,51 +4310,12 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_MEMORY); } + // System font sets always use sub-font-index 0. + const u32 sub_font_index = 0u; + if (!lib_local->sysfonts_ctx) { - constexpr u32 kSysCtxSize = 0x1020; - void* ctx = alloc_fn(lib_local->alloc_ctx, kSysCtxSize); - if (!ctx) { - LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); - return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); - } - std::memset(ctx, 0, kSysCtxSize); - - auto* header = static_cast(ctx); - header->lock_word = 0; - header->max_entries = 0x40; - header->base = reinterpret_cast(ctx) + sizeof(Internal::FontCtxHeader); - auto* entries = static_cast(header->base); - for (u32 i = 0; i < header->max_entries; i++) { - std::memset(&entries[i], 0, sizeof(entries[i])); - } - - const auto* driver = - reinterpret_cast(lib_local->sys_driver); - const auto support_fn = driver ? driver->support_formats : nullptr; - if (!support_fn) { - free_fn(lib_local->alloc_ctx, ctx); - LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); - return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_LIBRARY); - } - const s32 support_rc = support_fn(library, 0x52); - if (support_rc != ORBIS_OK) { - free_fn(lib_local->alloc_ctx, ctx); - LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); - return release_library_and_clear_out(support_rc); - } - - lib_local->sysfonts_ctx = ctx; - auto& ls = GetLibState(library); - ls.support_system = true; - ls.owned_sysfonts_ctx = ctx; - ls.owned_sysfonts_ctx_size = kSysCtxSize; - } - - const u32 sub_font_index = open_params ? open_params->subfont_index : 0u; - const s32 unique_id = open_params ? open_params->unique_id : -1; - if (unique_id < -1) { - LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); - return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_PARAMETER); + LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); + return release_library_and_clear_out(ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION); } std::filesystem::path primary_path = @@ -4320,6 +4327,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); return release_library_and_clear_out(ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET); } + LOG_INFO(Lib_Font, "OpenFontSet: stage=resolved_primary_path"); std::vector primary_bytes; if (!Internal::LoadFontFile(primary_path, primary_bytes)) { @@ -4327,15 +4335,18 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o primary_path.string(), Config::getSysFontPath().string()); return release_library_and_clear_out(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); } + LOG_INFO(Lib_Font, "OpenFontSet: stage=loaded_primary_bytes"); OrbisFontHandle handle = *pFontHandle; + const bool had_existing_handle = (handle != nullptr); if (handle) { if (auto* h = GetNativeFont(handle)) { h->flags = 0; } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); } else { - handle = static_cast(alloc_fn(lib_local->alloc_ctx, 0x100)); + handle = static_cast( + Core::ExecuteGuest(alloc_fn, lib_local->alloc_ctx, 0x100)); if (!handle) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); @@ -4345,6 +4356,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o h->flags = 0x10; } } + LOG_INFO(Lib_Font, "OpenFontSet: stage=handle_ready"); auto cleanup_handle_for_error = [&](s32 rc) -> s32 { auto* h = GetNativeFont(handle); @@ -4362,9 +4374,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o h->library = library; h->flags = 0; if ((prev_flags & 0x10) != 0) { - free_fn(lib_local->alloc_ctx, handle); + Core::ExecuteGuest(free_fn, lib_local->alloc_ctx, handle); } - Internal::g_font_state.erase(handle); + Internal::RemoveState(handle); return release_library_and_clear_out(rc); }; @@ -4373,8 +4385,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return cleanup_handle_for_error(ORBIS_FONT_ERROR_FATAL); } - h->magic = 0x0F02; - h->flags = static_cast(h->flags | static_cast(mode_low)); + h->magic = 0; + h->flags = static_cast(h->flags & 0x10); h->open_info.unique_id_packed = 0; h->open_info.ctx_entry_index = 0; h->open_info.fontset_flags = 0; @@ -4390,44 +4402,21 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o h->nextFont = nullptr; h->style_frame[0] = 0x48; h->style_frame[1] = 0x48; - *pFontHandle = handle; - - if ((h->flags & 0x10) != 0) { - auto* tail = reinterpret_cast(lib_local + 1); - auto* list_lock = &tail->list_head_ptr; - void* list_ptr = nullptr; - for (;;) { - list_ptr = *list_lock; - if (list_ptr != - reinterpret_cast(std::numeric_limits::max())) { - std::atomic_ref ref(*list_lock); - void* expected = list_ptr; - if (ref.compare_exchange_weak( - expected, - reinterpret_cast(std::numeric_limits::max()), - std::memory_order_acq_rel)) { - break; - } - } - Libraries::Kernel::sceKernelUsleep(0x1e); - } - - auto* head_ptr = reinterpret_cast(list_ptr); - if (head_ptr) { - const OrbisFontHandle old_head = *head_ptr; - if (auto* hn = GetNativeFont(handle)) { - hn->prevFont = nullptr; - hn->nextFont = old_head; - } - if (old_head) { - if (auto* old_native = GetNativeFont(old_head)) { - old_native->prevFont = handle; - } - } - *head_ptr = handle; - } - *list_lock = list_ptr; + h->style_tail.scale_unit = 0; + h->style_tail.reserved_0x0c = 0; + { + const auto* driver = + reinterpret_cast(lib_local->sys_driver); + const auto pixel_res_fn = driver ? driver->pixel_resolution : nullptr; + const u32 pixel_res = pixel_res_fn ? pixel_res_fn() : 0; + const float scale = (pixel_res != 0) ? (1000.0f / static_cast(pixel_res)) : 0.0f; + h->style_tail.scale_w = scale; + h->style_tail.scale_h = scale; } + h->style_tail.effect_weight_x = 0.0f; + h->style_tail.effect_weight_y = 0.0f; + h->style_tail.slant_ratio = 0.0f; + h->style_tail.reserved_0x24 = 0.0f; auto& st = Internal::GetState(handle); Internal::DestroyFreeTypeFace(st.ext_ft_face); @@ -4449,10 +4438,32 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o LOG_ERROR(Lib_Font, "FONT_OPEN_FAILED"); return cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); } + LOG_INFO(Lib_Font, "OpenFontSet: stage=created_primary_face"); const Internal::FaceMetrics m = Internal::LoadFaceMetrics(st.ext_ft_face); Internal::PopulateStateMetrics(st, m); st.ext_face_ready = true; + { + const u16 units = static_cast(st.ext_ft_face->units_per_EM); + h->metricA = units; + u16 ascender = units; + if (const TT_OS2* os2 = + static_cast(FT_Get_Sfnt_Table(st.ext_ft_face, ft_sfnt_os2))) { + ascender = static_cast(os2->sTypoAscender); + } + h->metricB = ascender; + + const auto* driver = + reinterpret_cast(lib_local->sys_driver); + const auto pixel_res_fn = driver ? driver->pixel_resolution : nullptr; + const u32 pixel_res = pixel_res_fn ? pixel_res_fn() : 0; + const float scale = (pixel_res != 0) + ? (static_cast(units) / static_cast(pixel_res)) + : 0.0f; + h->style_tail.scale_w = scale; + h->style_tail.scale_h = scale; + } + auto compute_sysfont_scale_factor = [](FT_Face face, int units_per_em) -> float { (void)units_per_em; if (!face) { @@ -4589,10 +4600,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o }; auto add_fallback_face = [&](const std::filesystem::path& p) { + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face begin"); std::vector fb_bytes; if (!Internal::LoadFontFile(p, fb_bytes)) { + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face failed (LoadFontFile)"); return; } + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face bytes_size={}", + fb_bytes.size()); Internal::FontState::SystemFallbackFace fb{}; fb.font_id = 0xffffffffu; fb.scale_factor = 1.0f; @@ -4603,6 +4618,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.bytes->data(), fb.bytes->size(), sub_font_index); fb.ready = (fb.ft_face != nullptr); if (fb.ready) { + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face face={}", + fmt::ptr(fb.ft_face)); fb.scale_factor = compute_sysfont_scale_factor( fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); fb.shift_value = compute_sysfont_shift_value(fb.ft_face); @@ -4612,7 +4629,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o fb.path.filename().string(), fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, fb.scale_factor, fb.shift_value); + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face push_back begin"); st.system_fallback_faces.push_back(std::move(fb)); + LOG_DEBUG(Lib_Font, "SystemFonts: add_fallback_face done"); } else { Internal::DestroyFreeTypeFace(fb.ft_face); } @@ -4994,6 +5013,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return 7; }; + LOG_INFO(Lib_Font, "OpenFontSet: stage=open_sysfonts_begin"); auto open_sysfonts_entry = [&](const std::filesystem::path& requested_path) -> std::optional { const std::filesystem::path served_path = resolve_served_path(requested_path); @@ -5004,8 +5024,16 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o if (host_path_str.empty()) { return std::nullopt; } + u32 open_arg4 = 0; + { + std::error_code ec; + const auto sz = std::filesystem::file_size(served_path, ec); + if (!ec && sz <= static_cast(std::numeric_limits::max())) { + open_arg4 = static_cast(sz); + } + } + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_entry_begin"); const s32 unique_id = stable_unique_id_for(host_path_str); - const u32 packed_unique_id = static_cast(unique_id) | 0x80000000u; auto* sys_ctx = static_cast(lib_local->sysfonts_ctx); auto* sys_header = @@ -5069,6 +5097,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o *ctx_lock_word = 0; + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_entry_lock_begin"); void* font_obj = nullptr; u32 entry_lock_word = 0; u8* entry_u8 = Internal::AcquireFontCtxEntry(sys_ctx, entry_index, mode_low, @@ -5078,6 +5107,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o if (!entry) { return std::nullopt; } + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_entry_lock_ok"); void* used_font_obj = font_obj; u32 updated_lock = entry_lock_word; @@ -5107,12 +5137,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } } } - rc = open_fn(library, open_driver_mode_for(mode_low), host_path_str.c_str(), 0, - sub_font_index, packed_unique_id, &used_font_obj); + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_driver_open_call"); + rc = open_fn(library, open_driver_mode_for(mode_low), host_path_str.c_str(), + open_arg4, sub_font_index, entry_index, &used_font_obj); if (tail_reserved1) { tail_reserved1->sysfont_flags = 0; } if (rc == ORBIS_OK) { + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_driver_open_ok"); if (mode_low == 3) { entry->obj_mode3 = used_font_obj; } else if (mode_low == 2) { @@ -5160,8 +5192,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } } rc = open_fn(library, open_driver_mode_for(mode_low), - host_path_str.c_str(), 0, sub_font_index, packed_unique_id, - &used_font_obj); + host_path_str.c_str(), open_arg4, sub_font_index, + entry_index, &used_font_obj); if (tail_reserved1) { tail_reserved1->sysfont_flags = 0; } @@ -5209,6 +5241,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o if (rc != ORBIS_OK) { return std::nullopt; } + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_entry_done"); return entry_index; }; @@ -5220,9 +5253,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return cleanup_handle_for_error(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); } st.system_font_id = *primary_entry; - h->open_info.ctx_entry_index = st.system_font_id; font_ids.push_back(st.system_font_id); - + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_primary_opened"); + u32 opened_fallbacks = 0; for (auto& fb : st.system_fallback_faces) { if (!fb.ready || !fb.ft_face) { continue; @@ -5233,9 +5266,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } fb.font_id = *fb_entry; font_ids.push_back(fb.font_id); + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_fallback_opened"); + ++opened_fallbacks; } + LOG_INFO(Lib_Font, "OpenFontSet: stage=sysfonts_fallbacks_opened"); + LOG_INFO(Lib_Font, "OpenFontSet: stage=before_make_shared_selector"); st.fontset_selector = std::make_shared(); + LOG_INFO(Lib_Font, "OpenFontSet: stage=after_make_shared_selector"); st.fontset_selector->magic = Internal::FontSetSelector::kMagic; st.fontset_selector->font_set_type = fontSetType; st.fontset_selector->library = lib_local; @@ -5285,7 +5323,9 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o const std::size_t ptr_off = (ptr_off_unaligned + (ptr_align - 1u)) & ~(ptr_align - 1u); const std::size_t rec_size = ptr_off + sizeof(const Internal::FontSetSelector*); + LOG_INFO(Lib_Font, "OpenFontSet: stage=before_make_shared_record"); st.fontset_record_storage = std::make_shared>(rec_size); + LOG_INFO(Lib_Font, "OpenFontSet: stage=after_make_shared_record"); std::memset(st.fontset_record_storage->data(), 0, st.fontset_record_storage->size()); auto* header = std::construct_at(reinterpret_cast( @@ -5307,29 +5347,81 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o h->open_info.fontset_record = header; } + h->magic = 0x0F02; + h->flags = static_cast(h->flags | static_cast(mode_low)); + if (!had_existing_handle) { + *pFontHandle = handle; + } + LOG_INFO(Lib_Font, "OpenFontSet: stage=published_handle"); + if ((h->flags & 0x10) != 0) { + auto* tail = reinterpret_cast(lib_local + 1); + auto* list_lock = &tail->list_head_ptr; + void* list_ptr = nullptr; + for (;;) { + list_ptr = *list_lock; + if (list_ptr != + reinterpret_cast(std::numeric_limits::max())) { + std::atomic_ref ref(*list_lock); + void* expected = list_ptr; + if (ref.compare_exchange_weak( + expected, + reinterpret_cast(std::numeric_limits::max()), + std::memory_order_acq_rel)) { + break; + } + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } + + auto* head_ptr = reinterpret_cast(list_ptr); + if (head_ptr) { + const OrbisFontHandle old_head = *head_ptr; + if (auto* hn = GetNativeFont(handle)) { + hn->prevFont = nullptr; + hn->nextFont = old_head; + } + if (old_head) { + if (auto* old_native = GetNativeFont(old_head)) { + old_native->prevFont = handle; + } + } + *head_ptr = handle; + } + *list_lock = list_ptr; + } + ReleaseLibraryLock(lib_local, prev_lib_lock); + LOG_INFO(Lib_Font, "OpenFontSet: stage=done"); return ORBIS_OK; } } s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle) { + LOG_INFO(Lib_Font, "called"); + if (!fontHandle) { + LOG_ERROR(Lib_Font, "NULL_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } + LOG_DEBUG(Lib_Font, "{}", formatParams({Param("fontHandle", fontHandle)})); + auto* font = GetNativeFont(fontHandle); if (!font || font->magic != 0x0F02) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } u32 prev_font_lock = 0; if (!AcquireFontLock(font, prev_font_lock)) { + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } u32 prev_cached_lock = 0; if (!AcquireCachedStyleLock(font, prev_cached_lock)) { ReleaseFontLock(font, prev_font_lock); + LOG_ERROR(Lib_Font, "INVALID_HANDLE"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } @@ -5364,6 +5456,14 @@ s32 PS4_SYSV_ABI sceFontRebindRenderer(OrbisFontHandle fontHandle) { ReleaseCachedStyleLock(font, prev_cached_lock); ReleaseFontLock(font, prev_font_lock); + + if (rc != ORBIS_OK) { + if (rc == ORBIS_FONT_ERROR_NOT_BOUND_RENDERER) { + LOG_ERROR(Lib_Font, "NOT_BOUND_RENDERER"); + } else { + LOG_ERROR(Lib_Font, "FAILED"); + } + } return rc; } @@ -5756,18 +5856,110 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(OrbisFontHandle fontHandle, return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(OrbisFontRenderer fontRenderer, u32* size) { + LOG_INFO(Lib_Font, "called"); + + if (!size) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + *size = 0; + + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != 0x0F07) { + LOG_ERROR(Lib_Font, "INVALID_RENDERER"); + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontRenderer", fontRenderer), + Param("size", size), + })); + + *size = static_cast(renderer->workspace_size); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(OrbisFontRenderer fontRenderer) { + LOG_INFO(Lib_Font, "called"); + + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != 0x0F07) { + LOG_ERROR(Lib_Font, "INVALID_RENDERER"); + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + + LOG_DEBUG(Lib_Font, "{}", formatParams({Param("fontRenderer", fontRenderer)})); + + if (renderer->workspace && renderer->workspace_size) { + std::memset(renderer->workspace, 0, static_cast(renderer->workspace_size)); + } + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(OrbisFontRenderer fontRenderer, + u64 bufferPolicy, u32 basalSize, + u32 limitSize) { + LOG_INFO(Lib_Font, "called"); + + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != 0x0F07) { + LOG_ERROR(Lib_Font, "INVALID_RENDERER"); + return ORBIS_FONT_ERROR_INVALID_RENDERER; + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("fontRenderer", fontRenderer), + Param("bufferPolicy", bufferPolicy), + Param("basalSize", basalSize), + Param("limitSize", limitSize), + })); + + if (limitSize != 0 && basalSize > limitSize) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); + using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + + const auto alloc_fn = reinterpret_cast(renderer->alloc_fn); + const auto free_fn = reinterpret_cast(renderer->free_fn); + if (!alloc_fn || !free_fn || !renderer->alloc_ctx) { + LOG_ERROR(Lib_Font, "INVALID_MEMORY"); + return ORBIS_FONT_ERROR_INVALID_MEMORY; + } + + std::size_t desired_size = static_cast(renderer->workspace_size); + desired_size = std::max(desired_size, static_cast(basalSize)); + if (limitSize != 0) { + desired_size = std::min(desired_size, static_cast(limitSize)); + } + + if (desired_size == 0) { + desired_size = 0x4000; + } + + if (!renderer->workspace || renderer->workspace_size != desired_size) { + void* new_workspace = + Core::ExecuteGuest(alloc_fn, renderer->alloc_ctx, static_cast(desired_size)); + if (!new_workspace) { + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + return ORBIS_FONT_ERROR_ALLOCATION_FAILED; + } + + if (renderer->workspace) { + Core::ExecuteGuest(free_fn, renderer->alloc_ctx, renderer->workspace); + } + + renderer->workspace = new_workspace; + renderer->workspace_size = desired_size; + } + + (void)bufferPolicy; return ORBIS_OK; } @@ -5791,6 +5983,7 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface Param("heightPixel", heightPixel), })); + LOG_DEBUG(Lib_Font, "RenderSurfaceInit: writing struct"); const u32 w_nonneg = (widthPixel < 0) ? 0u : static_cast(widthPixel); const u32 h_nonneg = (heightPixel < 0) ? 0u : static_cast(heightPixel); renderSurface->buffer = buffer; @@ -5805,6 +5998,7 @@ void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface renderSurface->sc_y0 = 0; renderSurface->sc_x1 = w_nonneg; renderSurface->sc_y1 = h_nonneg; + LOG_DEBUG(Lib_Font, "RenderSurfaceInit: done"); } void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, @@ -5896,17 +6090,40 @@ void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderS s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, OrbisFontStyleFrame* styleFrame) { - if (!renderSurface) + LOG_INFO(Lib_Font, "called"); + + if (!renderSurface) { + LOG_ERROR(Lib_Font, "NULL_POINTER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + { + auto* memory = Core::Memory::Instance(); + if (memory && !memory->IsValidMapping(reinterpret_cast(renderSurface), + sizeof(OrbisFontRenderSurface))) { + LOG_ERROR(Lib_Font, "INVALID_ADDR renderSurface={}", fmt::ptr(renderSurface)); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + if (styleFrame && !memory->IsValidMapping(reinterpret_cast(styleFrame), + sizeof(OrbisFontStyleFrame))) { + LOG_ERROR(Lib_Font, "INVALID_ADDR styleFrame={}", fmt::ptr(styleFrame)); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + } + + LOG_DEBUG(Lib_Font, "{}", + formatParams({ + Param("renderSurface", renderSurface), + Param("styleFrame", styleFrame), + })); if (!styleFrame) { renderSurface->styleFlag &= ~u8{0x1}; renderSurface->reserved_q[0] = 0; renderSurface->reserved_q[1] = 0; - LOG_INFO(Lib_Font, "RenderSurfaceSetStyleFrame: surf={} cleared", - static_cast(renderSurface)); return ORBIS_OK; } if (styleFrame->magic != kStyleFrameMagic) { + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); return ORBIS_FONT_ERROR_INVALID_PARAMETER; } renderSurface->styleFlag |= 0x1; @@ -6123,7 +6340,7 @@ s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(OrbisFontHandle fontHandle, float s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; if (font->renderer_binding.renderer != nullptr) { if (Internal::StyleStateSetSlantRatio(&font->cached_style, slantRatio) != 0) { - font->cached_style.cache_flags_and_direction = 0; + font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; } rc = ORBIS_OK; } @@ -6174,7 +6391,7 @@ s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(OrbisFontHandle fontHandle, floa if (mode == 0) { if (Internal::StyleStateSetWeightScale(&font->cached_style, weightXScale, weightYScale) != 0) { - font->cached_style.cache_flags_and_direction = 0; + font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; } rc = ORBIS_OK; } @@ -6219,7 +6436,7 @@ s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(OrbisFontHandle fontHandle, float s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; if (font->renderer_binding.renderer != nullptr) { if (Internal::StyleStateSetScalePixel(&font->cached_style, w, h) != 0) { - font->cached_style.cache_flags_and_direction = 0; + font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; } rc = ORBIS_OK; if (auto* st = Internal::TryGetState(fontHandle)) { @@ -6254,7 +6471,7 @@ s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(OrbisFontHandle fontHandle, float s32 rc = ORBIS_FONT_ERROR_NOT_BOUND_RENDERER; if (font->renderer_binding.renderer != nullptr) { if (Internal::StyleStateSetScalePoint(&font->cached_style, w, h) != 0) { - font->cached_style.cache_flags_and_direction = 0; + font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; } rc = ORBIS_OK; if (auto* st = Internal::TryGetState(fontHandle)) { @@ -6573,7 +6790,7 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, } const u32 ctx_size = (fontMax << 6) | 0x20u; - void* ctx = alloc_fn(lib->alloc_ctx, ctx_size); + void* ctx = Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, ctx_size); if (!ctx) { ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -6601,14 +6818,14 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; const auto support_fn = driver ? driver->support_formats : nullptr; if (!support_fn) { - free_fn(lib->alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } const s32 support_rc = support_fn(library, formats); if (support_rc != ORBIS_OK) { - free_fn(lib->alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); return support_rc; @@ -6666,7 +6883,7 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { } constexpr u32 kSysCtxSize = 0x1020; - void* ctx = alloc_fn(lib->alloc_ctx, kSysCtxSize); + void* ctx = Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, kSysCtxSize); if (!ctx) { ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -6688,14 +6905,14 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; const auto support_fn = driver ? driver->support_formats : nullptr; if (!support_fn) { - free_fn(lib->alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } const s32 support_rc = support_fn(library, 0x52); if (support_rc != ORBIS_OK) { - free_fn(lib->alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); return support_rc; diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index fe0a53521..2608a99fb 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -315,7 +315,7 @@ sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter); OrbisFontTextCharacter* PS4_SYSV_ABI sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter); s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); -s32 PS4_SYSV_ABI sceFontClearDeviceCache(); +s32 PS4_SYSV_ABI sceFontClearDeviceCache(OrbisFontLib library); s32 PS4_SYSV_ABI sceFontCloseFont(); s32 PS4_SYSV_ABI sceFontControl(); s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); @@ -474,9 +474,11 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(OrbisFontHandle fontHandle, OrbisFontRenderSurface* surf, float x, float y, OrbisFontGlyphMetrics* metrics, OrbisFontRenderOutput* result); -s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); -s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); -s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(OrbisFontRenderer fontRenderer, u32* size); +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(OrbisFontRenderer fontRenderer); +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(OrbisFontRenderer fontRenderer, + u64 bufferPolicy, u32 basalSize, + u32 limitSize); void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index ace9cdca0..a6684d2be 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -29,6 +29,10 @@ std::unordered_map g_font_state; std::unordered_map g_library_state; +namespace { +std::mutex g_state_mutex; +} // namespace + std::unordered_map g_style_for_surface; @@ -80,9 +84,13 @@ FT_Face CreateFreeTypeFaceFromBytes(const unsigned char* data, std::size_t size, FT_Face face = nullptr; if (FT_New_Memory_Face(lib, reinterpret_cast(data), static_cast(size), static_cast(subfont_index), &face) != 0) { + LOG_DEBUG(Lib_Font, "CreateFreeTypeFaceFromBytes: failed size={} subfont_index={}", size, + subfont_index); return nullptr; } (void)FT_Select_Charmap(face, FT_ENCODING_UNICODE); + LOG_DEBUG(Lib_Font, "CreateFreeTypeFaceFromBytes: ok face={} size={} subfont_index={}", + reinterpret_cast(face), size, subfont_index); return face; } @@ -483,23 +491,35 @@ u16 ClampToU16(float value) { } FontState& GetState(Libraries::Font::OrbisFontHandle h) { + std::scoped_lock lock(g_state_mutex); return g_font_state[h]; } FontState* TryGetState(Libraries::Font::OrbisFontHandle h) { if (!h) return nullptr; + std::scoped_lock lock(g_state_mutex); auto it = g_font_state.find(h); if (it == g_font_state.end()) return nullptr; return &it->second; } +void RemoveState(Libraries::Font::OrbisFontHandle h) { + if (!h) { + return; + } + std::scoped_lock lock(g_state_mutex); + g_font_state.erase(h); +} + LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { + std::scoped_lock lock(g_state_mutex); return g_library_state[lib]; } void RemoveLibState(Libraries::Font::OrbisFontLib lib) { + std::scoped_lock lock(g_state_mutex); if (auto it = g_library_state.find(lib); it != g_library_state.end()) { if (it->second.owned_device_cache) { delete[] static_cast(it->second.owned_device_cache); diff --git a/src/core/libraries/font/font_internal.h b/src/core/libraries/font/font_internal.h index 5d5b44f2e..39850c3f7 100644 --- a/src/core/libraries/font/font_internal.h +++ b/src/core/libraries/font/font_internal.h @@ -55,9 +55,6 @@ std::string DescribeValue(const T& value) { if constexpr (std::is_same_v) { return value ? "true" : "false"; } else if constexpr (std::is_pointer_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return value ? fmt::format("\"{}\"", value) : "(null)"; - } return fmt::format("{}", reinterpret_cast(value)); } else if constexpr (std::is_floating_point_v) { return fmt::format("{:.6g}", value); @@ -705,6 +702,7 @@ extern u32 g_device_cache_stub; FontState& GetState(Libraries::Font::OrbisFontHandle h); FontState* TryGetState(Libraries::Font::OrbisFontHandle h); +void RemoveState(Libraries::Font::OrbisFontHandle h); LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib); void RemoveLibState(Libraries::Font::OrbisFontLib lib); FT_Face CreateFreeTypeFaceFromBytes(const unsigned char* data, std::size_t size, u32 subfont_index); diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index 5aae88291..c5cc2f5bc 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -30,6 +30,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/font/font_internal.h" #include "core/libraries/kernel/kernel.h" +#include "core/tls.h" #include "font_error.h" namespace Libraries::Font::Internal { @@ -2643,7 +2644,8 @@ static void* FtAlloc(FT_Memory memory, long size) { } using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); - return alloc_fn ? alloc_fn(ctx->alloc_ctx, static_cast(size)) : nullptr; + return alloc_fn ? Core::ExecuteGuest(alloc_fn, ctx->alloc_ctx, static_cast(size)) + : nullptr; } static void FtFree(FT_Memory memory, void* block) { @@ -2657,7 +2659,7 @@ static void FtFree(FT_Memory memory, void* block) { using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); const auto free_fn = reinterpret_cast(ctx->alloc_vtbl[1]); if (free_fn) { - free_fn(ctx->alloc_ctx, block); + Core::ExecuteGuest(free_fn, ctx->alloc_ctx, block); } } @@ -2672,7 +2674,7 @@ static void* FtRealloc(FT_Memory memory, long cur_size, long new_size, void* blo using ReallocFn = void*(PS4_SYSV_ABI*)(void* object, void* p, u32 size); const auto realloc_fn = reinterpret_cast(ctx->alloc_vtbl[2]); if (realloc_fn) { - return realloc_fn(ctx->alloc_ctx, block, static_cast(new_size)); + return Core::ExecuteGuest(realloc_fn, ctx->alloc_ctx, block, static_cast(new_size)); } if (new_size <= 0) { @@ -2718,7 +2720,8 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { void** alloc_vtbl = reinterpret_cast(const_cast(mem->iface)); - auto* ctx = static_cast(alloc_fn(alloc_ctx, sizeof(FtLibraryCtx))); + auto* ctx = + static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FtLibraryCtx))); if (!ctx) { return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -2726,9 +2729,10 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { ctx->alloc_ctx = alloc_ctx; ctx->alloc_vtbl = alloc_vtbl; - FT_Memory ft_mem = static_cast(alloc_fn(alloc_ctx, sizeof(FT_MemoryRec_))); + FT_Memory ft_mem = + static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FT_MemoryRec_))); if (!ft_mem) { - free_fn(alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, alloc_ctx, ctx); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } std::memset(ft_mem, 0, sizeof(*ft_mem)); @@ -2741,8 +2745,8 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { FT_Library ft_lib = nullptr; const FT_Error ft_err = FT_New_Library(ft_mem, &ft_lib); if (ft_err != 0 || !ft_lib) { - free_fn(alloc_ctx, ft_mem); - free_fn(alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, alloc_ctx, ft_mem); + Core::ExecuteGuest(free_fn, alloc_ctx, ctx); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } FT_Add_Default_Modules(ft_lib); @@ -2812,10 +2816,10 @@ s32 PS4_SYSV_ABI LibraryTermStub(void* library) { ctx->ft_lib = nullptr; } if (ctx->ft_memory) { - free_fn(alloc_ctx, ctx->ft_memory); + Core::ExecuteGuest(free_fn, alloc_ctx, ctx->ft_memory); ctx->ft_memory = nullptr; } - free_fn(alloc_ctx, ctx); + Core::ExecuteGuest(free_fn, alloc_ctx, ctx); lib->fontset_registry = nullptr; return ORBIS_OK; } @@ -2888,21 +2892,8 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* const u8* data = nullptr; u32 size = 0; void* owned_data = nullptr; - - auto assign_from_bytes = [&](std::vector&& bytes) -> s32 { - if (bytes.empty() || bytes.size() > std::numeric_limits::max()) { - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - - owned_data = alloc_fn(alloc_ctx, static_cast(bytes.size())); - if (!owned_data) { - return ORBIS_FONT_ERROR_ALLOCATION_FAILED; - } - std::memcpy(owned_data, bytes.data(), bytes.size()); - data = static_cast(owned_data); - size = static_cast(bytes.size()); - return ORBIS_OK; - }; + std::string open_path; + std::filesystem::path host_path_fs{}; if (mode == 1) { if (fontSize == 0) { @@ -2916,8 +2907,7 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - std::string open_path = path; - std::filesystem::path host_path_fs{}; + open_path = path; if (path[0] == '/') { auto* mnt = Common::Singleton::Instance(); host_path_fs = mnt ? mnt->GetHostPath(path) : std::filesystem::path{}; @@ -2925,44 +2915,6 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* open_path = host_path_fs.string(); } } - - std::ifstream file(open_path, std::ios::binary | std::ios::ate); - if (!file && !host_path_fs.empty()) { - const auto sysfonts_dir = host_path_fs.parent_path(); - const auto ps4_name = host_path_fs.filename().string(); - const std::filesystem::path file_path{ps4_name}; - const auto in_font_dir = sysfonts_dir / "font" / file_path; - const auto in_font2_dir = sysfonts_dir / "font2" / file_path; - std::error_code ec; - if (std::filesystem::exists(in_font_dir, ec)) { - open_path = in_font_dir.string(); - file = std::ifstream(open_path, std::ios::binary | std::ios::ate); - } else if (std::filesystem::exists(in_font2_dir, ec)) { - open_path = in_font2_dir.string(); - file = std::ifstream(open_path, std::ios::binary | std::ios::ate); - } else if (const auto alias = ResolveKnownSysFontAlias(sysfonts_dir, ps4_name)) { - open_path = alias->string(); - file = std::ifstream(open_path, std::ios::binary | std::ios::ate); - } - } - if (!file) { - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - const std::streamoff fsize = file.tellg(); - if (fsize <= 0 || static_cast(fsize) > std::numeric_limits::max()) { - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - file.seekg(0, std::ios::beg); - - std::vector bytes(static_cast(fsize)); - if (!file.read(reinterpret_cast(bytes.data()), - static_cast(bytes.size()))) { - return ORBIS_FONT_ERROR_FS_OPEN_FAILED; - } - const s32 store_rc = assign_from_bytes(std::move(bytes)); - if (store_rc != ORBIS_OK) { - return store_rc; - } } else { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -2970,29 +2922,76 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* auto* ctx = static_cast(lib->fontset_registry); if (!ctx || !ctx->ft_lib) { if (owned_data) { - free_fn(alloc_ctx, owned_data); + Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); } return ORBIS_FONT_ERROR_INVALID_LIBRARY; } FT_Face face = nullptr; - const FT_Error ft_err = - FT_New_Memory_Face(ctx->ft_lib, reinterpret_cast(data), - static_cast(size), static_cast(subFontIndex), &face); + FT_Error ft_err = 0; + if (mode == 1) { + ft_err = FT_New_Memory_Face(ctx->ft_lib, reinterpret_cast(data), + static_cast(size), static_cast(subFontIndex), + &face); + } else { + std::vector candidates; + candidates.emplace_back(open_path); + + if (!host_path_fs.empty()) { + const auto sysfonts_dir = host_path_fs.parent_path(); + const auto ps4_name = host_path_fs.filename().string(); + const std::filesystem::path file_path{ps4_name}; + candidates.emplace_back((sysfonts_dir / "font" / file_path).string()); + candidates.emplace_back((sysfonts_dir / "font2" / file_path).string()); + if (const auto alias = ResolveKnownSysFontAlias(sysfonts_dir, ps4_name)) { + candidates.emplace_back(alias->string()); + } + } + + std::error_code ec; + FT_Error last_ft_err = 0; + bool attempted_open = false; + for (const auto& cand : candidates) { + if (cand.empty()) { + continue; + } + if (!std::filesystem::exists(std::filesystem::path{cand}, ec) || ec) { + continue; + } + attempted_open = true; + ft_err = + FT_New_Face(ctx->ft_lib, cand.c_str(), static_cast(subFontIndex), &face); + last_ft_err = ft_err; + if (ft_err == 0 && face) { + break; + } + } + if (ft_err != 0) { + ft_err = last_ft_err; + } else if (!attempted_open) { + ft_err = FT_Err_Cannot_Open_Resource; + } + } if (ft_err != 0 || !face) { if (owned_data) { - free_fn(alloc_ctx, owned_data); + Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); } - return ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT; + if (mode == 1) { + return ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT; + } + if (ft_err == FT_Err_Unknown_File_Format) { + return ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT; + } + return ORBIS_FONT_ERROR_FS_OPEN_FAILED; } (void)FT_Select_Charmap(face, FT_ENCODING_UNICODE); - auto* obj = static_cast(alloc_fn(alloc_ctx, sizeof(FontObj))); + auto* obj = static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FontObj))); if (!obj) { FT_Done_Face(face); if (owned_data) { - free_fn(alloc_ctx, owned_data); + Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); } return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -3017,7 +3016,9 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* sidecar.font_data = data; sidecar.font_size = size; sidecar.owned_data = owned_data; - (void)ResolveSfntBaseOffset(data, size, subFontIndex, sidecar.sfnt_base); + if (mode == 1) { + (void)ResolveSfntBaseOffset(data, size, subFontIndex, sidecar.sfnt_base); + } SetFontObjSidecar(obj, std::move(sidecar)); *inoutFontObj = obj; @@ -3054,7 +3055,7 @@ s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { obj->ft_face = nullptr; } if (owned_data && free_fn) { - free_fn(ctx->alloc_ctx, owned_data); + Core::ExecuteGuest(free_fn, ctx->alloc_ctx, owned_data); } if (free_fn) { FontObj* next = obj->next; @@ -3065,7 +3066,7 @@ s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { } else { obj->prev->next = next; } - free_fn(ctx->alloc_ctx, obj); + Core::ExecuteGuest(free_fn, ctx->alloc_ctx, obj); return ORBIS_OK; } return ORBIS_FONT_ERROR_FATAL; From 296e4385c4a250a36f91cfdbc45fceea425e43db Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 16 Jan 2026 16:25:49 +0200 Subject: [PATCH 41/51] Add system font path and override config support Introduce new config options for system font directory, fallback font name, and per-font overrides. Update config load/save logic to handle a [SystemFonts] TOML section, supporting both fallback and individual font overrides. Improve user instructions for custom font setup and clarify related code comments. These changes enhance flexibility and user experience for system font configuration. --- src/common/config.cpp | 110 +++++++++++++++++++++++++++++++++++++-- src/common/path_util.cpp | 15 +++--- src/common/path_util.h | 2 +- 3 files changed, 116 insertions(+), 11 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index eac463d0a..5159c9cf3 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,8 +4,9 @@ #include #include #include +#include +#include #include -#include // for wstring support #include #include "common/assert.h" @@ -144,6 +145,9 @@ static ConfigEntry isSideTrophy("right"); static ConfigEntry isConnectedToNetwork(false); static bool enableDiscordRPC = false; static std::filesystem::path sys_modules_path = {}; +static std::filesystem::path sys_font_path = {}; +static std::string sys_font_fallback_name = {}; +static std::unordered_map system_font_overrides; // Input static ConfigEntry cursorState(HideCursorState::Idle); @@ -234,6 +238,47 @@ void setSysModulesPath(const std::filesystem::path& path) { sys_modules_path = path; } +std::filesystem::path getSysFontPath() { + if (sys_font_path.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::FontDir); + } + return sys_font_path; +} + +void setSysFontPath(const std::filesystem::path& path) { + sys_font_path = path; +} + +std::optional getSystemFontOverride(std::string_view key) { + if (key.empty()) { + return std::nullopt; + } + auto it = system_font_overrides.find(std::string(key)); + if (it == system_font_overrides.end()) { + return std::nullopt; + } + return it->second; +} + +std::string getSystemFontFallbackName() { + return sys_font_fallback_name; +} + +void setSystemFontFallbackName(const std::string& name) { + sys_font_fallback_name = name; +} + +void setSystemFontOverride(std::string_view key, const std::filesystem::path& path) { + if (key.empty()) { + return; + } + system_font_overrides[std::string(key)] = path; +} + +void clearSystemFontOverrides() { + system_font_overrides.clear(); +} + int getVolumeSlider() { return volumeSlider.get(); } @@ -460,7 +505,7 @@ void setShowFpsCounter(bool enable, bool is_game_specific) { showFpsCounter.set(enable, is_game_specific); } -bool isLoggingEnabled() { +static bool isLoggingEnabled() { return logEnabled.get(); } @@ -862,6 +907,10 @@ void load(const std::filesystem::path& path, bool is_game_specific) { return; } + if (!is_game_specific) { + system_font_overrides.clear(); + } + if (data.contains("General")) { const toml::value& general = data.at("General"); @@ -885,6 +934,43 @@ void load(const std::filesystem::path& path, bool is_game_specific) { isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific); defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific); sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path); + // Accept alias without trailing 's' + sys_modules_path = toml::find_fs_path_or(general, "sysModulePath", sys_modules_path); + // Prefer 'sysFontPath'; accept 'SysFontPath' for compatibility + sys_font_path = toml::find_fs_path_or(general, "sysFontPath", sys_font_path); + sys_font_path = toml::find_fs_path_or(general, "SysFontPath", sys_font_path); + } + + if (data.contains("SystemFonts")) { + const toml::value& fonts = data.at("SystemFonts"); + if (fonts.is_table()) { + // Read fallback (lowercase preferred), accept 'Fallback'/'FallbackFontName' for compat + if (fonts.contains("fallback")) { + const auto& v = fonts.at("fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("Fallback")) { + const auto& v = fonts.at("Fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("FallbackFontName")) { + const auto& v = fonts.at("FallbackFontName"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } + for (const auto& [name, value] : fonts.as_table()) { + if (name == "fallback" || name == "Fallback" || name == "FallbackFontName") { + continue; + } + if (value.is_string()) { + system_font_overrides[name] = + std::filesystem::path(toml::get(value)); + } + } + } } if (data.contains("Input")) { @@ -1005,7 +1091,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) { } } -void sortTomlSections(toml::ordered_value& data) { +static void sortTomlSections(toml::ordered_value& data) { toml::ordered_value ordered_data; std::vector section_order = {"General", "Input", "Audio", "GPU", "Vulkan", "Debug", "Keys", "GUI", "Settings"}; @@ -1158,6 +1244,22 @@ void save(const std::filesystem::path& path, bool is_game_specific) { // Non game-specific entries data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data}; + // Save using 'sysFontPath' to match style + data["General"]["sysFontPath"] = string{fmt::UTF(sys_font_path.u8string()).data}; + { + toml::table fonts_table; + if (!sys_font_fallback_name.empty()) { + fonts_table["fallback"] = sys_font_fallback_name; + } + for (const auto& [name, path_override] : system_font_overrides) { + fonts_table[name] = string{fmt::UTF(path_override.u8string()).data}; + } + if (!fonts_table.empty()) { + data["SystemFonts"] = fonts_table; + } else if (data.is_table()) { + data.as_table().erase("SystemFonts"); + } + } data["GUI"]["installDirs"] = install_dirs; data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; @@ -1302,7 +1404,7 @@ hotkey_quit = lctrl, lshift, end )"; } -constexpr std::string_view GetDefaultInputConfig() { +static constexpr std::string_view GetDefaultInputConfig() { return R"(#Feeling lost? Check out the Help section! # Keyboard bindings diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 7137a2205..0415c1a26 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -129,7 +129,6 @@ static auto UserPaths = [] { create_path(PathType::CustomConfigs, user_dir / CUSTOM_CONFIGS); create_path(PathType::CacheDir, user_dir / CACHE_DIR); create_path(PathType::FontDir, user_dir / SYSFONTS_DIR); - // subdirectory for fonts std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font"); std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font2"); @@ -148,11 +147,15 @@ static auto UserPaths = [] { notice_file.close(); } - std::ofstream font_instructions(user_dir / SYSFONTS_DIR / "Instructions.txt"); - if (font_instructions.is_open()) { - font_instructions << "Place /preinst/common/font contents into font folder\n" - "Place /system/common/font2 contents into font2 folder\n"; - font_instructions.close(); + const auto instructions_path = user_dir / SYSFONTS_DIR / "Instructions.txt"; + std::error_code ec; + if (!std::filesystem::exists(instructions_path, ec)) { + std::ofstream font_instructions(instructions_path); + if (font_instructions.is_open()) { + font_instructions << "Place system font files (.otf/.ttf) into the 'font' and 'font2' " + "folders.\n"; + font_instructions.close(); + } } return paths; diff --git a/src/common/path_util.h b/src/common/path_util.h index 00cf870e7..cf4611903 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -25,7 +25,7 @@ enum class PathType { CustomTrophy, // Where custom files for trophies are stored. CustomConfigs, // Where custom files for different games are stored. CacheDir, // Where pipeline and shader cache is stored. - FontDir // Where font files are stored. + FontDir, // Where system font files are stored. }; constexpr auto PORTABLE_DIR = "user"; From e3d401cf377f8ee39e0cc5eaf4208825c828a5e2 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 16 Jan 2026 16:41:41 +0200 Subject: [PATCH 42/51] clang --- src/core/emulator_settings.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/emulator_settings.h b/src/core/emulator_settings.h index 73a659a32..71b581611 100644 --- a/src/core/emulator_settings.h +++ b/src/core/emulator_settings.h @@ -82,7 +82,7 @@ struct DebugSettings { return std::vector{}; } }; -//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings) +// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings) // ------------------------------- // Input settings @@ -93,7 +93,7 @@ struct InputSettings { return std::vector{}; } }; -//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings) +// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings) // ------------------------------- // Audio settings @@ -104,7 +104,7 @@ struct AudioSettings { } }; -//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings) +// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings) // ------------------------------- // GPU settings @@ -114,16 +114,16 @@ struct GPUSettings { return std::vector{}; } }; -//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings) -// ------------------------------- -// Vulkan settings -// ------------------------------- +// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings) +// ------------------------------- +// Vulkan settings +// ------------------------------- struct VulkanSettings { std::vector GetOverrideableFields() const { return std::vector{}; } }; -//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(VulkanSettings) +// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(VulkanSettings) // ------------------------------- // Main manager From a6484150bfcfde3bc2e468d308368debabf8953e Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 16 Jan 2026 16:59:39 +0200 Subject: [PATCH 43/51] Add system font path management functions and refactor font directory retrieval --- src/common/config.h | 10 ++++++++++ src/core/libraries/font/font_internal.cpp | 13 +++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/common/config.h b/src/common/config.h index 2a95e6cf0..bf7cce162 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,6 +4,9 @@ #pragma once #include +#include +#include +#include #include #include "types.h" @@ -153,6 +156,13 @@ void setConnectedToNetwork(bool enable, bool is_game_specific = false); void setUserName(const std::string& name, bool is_game_specific = false); std::filesystem::path getSysModulesPath(); void setSysModulesPath(const std::filesystem::path& path); +std::filesystem::path getSysFontPath(); +void setSysFontPath(const std::filesystem::path& path); +std::optional getSystemFontOverride(std::string_view key); +std::string getSystemFontFallbackName(); +void setSystemFontFallbackName(const std::string& name); +void setSystemFontOverride(std::string_view key, const std::filesystem::path& path); +void clearSystemFontOverrides(); enum UsbBackendType : int { Real, SkylandersPortal, InfinityBase, DimensionsToypad }; int getUsbDeviceBackend(); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 11a3c85fc..962181e50 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -10,7 +10,6 @@ #include FT_OUTLINE_H #include FT_TRUETYPE_TABLES_H -#include "core/emulator_settings.h" #include "core/libraries/font/fontft_internal.h" namespace Libraries::Font::Internal { @@ -1560,7 +1559,7 @@ static std::optional FindChildDirContainingFile( } std::filesystem::path GetSysFontBaseDir() { - std::filesystem::path base = EmulatorSettings::GetInstance()->GetSysFontsDir(); + std::filesystem::path base = Config::getSysFontPath(); std::error_code ec; if (base.empty()) { LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); @@ -1572,9 +1571,11 @@ std::filesystem::path GetSysFontBaseDir() { } { - const auto preferred = base / "font"; - if (DirectoryContainsAnyFontFiles(preferred)) { - return preferred; + const auto font_dir = base / "font"; + const auto font2_dir = base / "font2"; + if (DirectoryContainsAnyFontFiles(font_dir) || + DirectoryContainsAnyFontFiles(font2_dir)) { + return base; } } @@ -1993,7 +1994,7 @@ std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHan } if (!st.system_requested) { st.system_requested = true; - const auto configured = EmulatorSettings::GetInstance()->GetSysFontsDir(); + const auto configured = Config::getSysFontPath(); return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " "could not be loaded", static_cast(handle), configured.string()); From f11e374fc8b7bdd4cbbf99dbedd6c9d5efe30fa2 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 17 Jan 2026 10:52:26 +0200 Subject: [PATCH 44/51] Restored support for loading the libSceAudiodec system module --- src/emulator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 1403b1254..fb59e23f1 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -529,7 +529,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib}, - {"libSceCesCs.sprx", nullptr}}); + {"libSceCesCs.sprx", nullptr}, + {"libSceAudiodec.sprx", nullptr}}); std::vector found_modules; const auto& sys_module_path = Config::getSysModulesPath(); From 5dde0cc70f3005e4dfeeccbfa38a97da8197ff43 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 14 Feb 2026 00:02:51 +0200 Subject: [PATCH 45/51] Refactor font path handling and remove unused functions --- src/common/config.cpp | 4 - src/common/path_util.cpp | 2 +- src/core/emulator_settings.cpp | 2 +- src/core/libraries/font/font.cpp | 183 +-------------- src/core/libraries/font/font_internal.cpp | 266 +--------------------- src/core/libraries/font/font_internal.h | 2 - 6 files changed, 20 insertions(+), 439 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 498e84fca..34d1ca361 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -878,10 +878,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) { return; } - if (!is_game_specific) { - system_font_overrides.clear(); - } - if (data.contains("General")) { const toml::value& general = data.at("General"); diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 3cc54efe5..97d119b94 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -145,7 +145,7 @@ static auto UserPaths = [] { notice_file.close(); } - const auto instructions_path = user_dir / SYSFONTS_DIR / "Instructions.txt"; + const auto instructions_path = user_dir / FONTS_DIR / "Instructions.txt"; std::error_code ec; if (!std::filesystem::exists(instructions_path, ec)) { std::ofstream font_instructions(instructions_path); diff --git a/src/core/emulator_settings.cpp b/src/core/emulator_settings.cpp index 49d997c49..fbbdf5be6 100644 --- a/src/core/emulator_settings.cpp +++ b/src/core/emulator_settings.cpp @@ -65,7 +65,7 @@ void EmulatorSettings::SetInstance(std::shared_ptr instance) { std::filesystem::path EmulatorSettings::GetSysFontsDir() { if (m_general.sys_fonts_dir.value.empty()) { - return Common::FS::GetUserPath(Common::FS::PathType::FontDir); + return Common::FS::GetUserPath(Common::FS::PathType::FontsDir); } return m_general.sys_fonts_dir.value; } diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 145331c59..02dbd4f1a 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -31,7 +31,6 @@ #include FT_TRUETYPE_TABLES_H #include "common/config.h" #include "common/logging/log.h" -#include "common/singleton.h" #include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/font/font.h" @@ -549,10 +548,10 @@ static const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type) { } static std::filesystem::path GetSysFontBaseDir() { - std::filesystem::path base = Config::getSysFontPath(); + std::filesystem::path base = Config::getFontsPath(); std::error_code ec; if (base.empty()) { - LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); + LOG_ERROR(Lib_Font, "SystemFonts: FontsPath not set"); return {}; } if (std::filesystem::is_directory(base, ec)) { @@ -561,7 +560,7 @@ static std::filesystem::path GetSysFontBaseDir() { if (std::filesystem::is_regular_file(base, ec)) { return base.parent_path(); } - LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath '{}' is not a valid directory or file", + LOG_ERROR(Lib_Font, "SystemFonts: FontsPath '{}' is not a valid directory or file", base.string()); return {}; } @@ -604,75 +603,12 @@ static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesyste return direct; } -static std::string MacroToCamel(const char* macro_key) { - if (!macro_key) { - return {}; - } - std::string s(macro_key); - const std::string prefix = "FONTSET_"; - if (s.rfind(prefix, 0) != 0) { - return {}; - } - std::string out = "FontSet"; - size_t pos = prefix.size(); - while (pos < s.size()) { - size_t next = s.find('_', pos); - const size_t len = (next == std::string::npos) ? (s.size() - pos) : (next - pos); - std::string token = s.substr(pos, len); - for (auto& c : token) { - c = static_cast(std::tolower(static_cast(c))); - } - if (!token.empty()) { - token[0] = static_cast(std::toupper(static_cast(token[0]))); - } - out += token; - if (next == std::string::npos) { - break; - } - pos = next + 1; - } - return out; -} - static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (const auto* def = FindSystemFontDefinition(font_set_type); def) { const auto base_dir = GetSysFontBaseDir(); if (base_dir.empty()) { return {}; } - if (auto override_path = Config::getSystemFontOverride(def->config_key)) { - if (!override_path->empty() && !override_path->is_absolute() && - !override_path->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - def->config_key, override_path->string()); - } - const auto camel_key = MacroToCamel(def->config_key); - if (!camel_key.empty()) { - if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { - if (!override_path2->empty() && !override_path2->is_absolute() && - !override_path2->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path2); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - camel_key, override_path2->string()); - } - std::string lower_camel = camel_key; - lower_camel[0] = - static_cast(std::tolower(static_cast(lower_camel[0]))); - if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { - if (!override_path3->empty() && !override_path3->is_absolute() && - !override_path3->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path3); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - lower_camel, override_path3->string()); - } - } if (def->default_file && *def->default_file) { return ResolveSystemFontPathCandidate(base_dir, def->default_file); } @@ -4318,11 +4254,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return release_library_and_clear_out(ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION); } - std::filesystem::path primary_path = - Internal::ResolveSystemFontPathFromConfigOnly(fontSetType); - if (primary_path.empty()) { - primary_path = Internal::ResolveSystemFontPath(fontSetType); - } + std::filesystem::path primary_path = Internal::ResolveSystemFontPath(fontSetType); if (primary_path.empty()) { LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); return release_library_and_clear_out(ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET); @@ -4331,8 +4263,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o std::vector primary_bytes; if (!Internal::LoadFontFile(primary_path, primary_bytes)) { - LOG_ERROR(Lib_Font, "FONT_OPEN_FAILED path='{}' sysFontPath='{}'", - primary_path.string(), Config::getSysFontPath().string()); + LOG_ERROR(Lib_Font, "FONT_OPEN_FAILED path='{}' fontsPath='{}'", primary_path.string(), + Config::getFontsPath().string()); return release_library_and_clear_out(ORBIS_FONT_ERROR_FONT_OPEN_FAILED); } LOG_INFO(Lib_Font, "OpenFontSet: stage=loaded_primary_bytes"); @@ -4561,23 +4493,6 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return std::nullopt; }; - auto resolve_override = - [&](const std::string& key) -> std::optional { - if (auto override_path = Config::getSystemFontOverride(key)) { - if (!override_path->empty() && override_path->is_absolute()) { - return *override_path; - } - if (!override_path->empty() && !override_path->has_parent_path()) { - return base_dir / *override_path; - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only or absolute " - "path: '{}'", - key, override_path->string()); - } - return std::nullopt; - }; - auto lower_ascii = [](std::string s) { for (auto& c : s) { c = static_cast(std::tolower(static_cast(c))); @@ -4870,82 +4785,6 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o : "SCEPS4Yoongd-Medium.otf"); } } - - for (int i = 0; i < 8; ++i) { - const std::string key_a = - fmt::format("fontset_0x{:08X}_fallback{}", fontSetType, i); - const std::string key_b = - fmt::format("fontSet_0x{:08X}_fallback{}", fontSetType, i); - std::optional p = resolve_override(key_a); - if (!p) { - p = resolve_override(key_b); - } - if (!p || p->empty()) { - continue; - } - - std::optional p_resolved; - if (p->is_absolute()) { - std::error_code ec; - if (std::filesystem::exists(*p, ec)) { - p_resolved = *p; - } - } else { - p_resolved = resolve_sysfont_path(*p); - } - if (!p_resolved) { - continue; - } - - std::vector fb_bytes; - if (!Internal::LoadFontFile(*p_resolved, fb_bytes)) { - continue; - } - - Internal::FontState::SystemFallbackFace fb{}; - fb.font_id = 0xffffffffu; - fb.scale_factor = 1.0f; - fb.shift_value = 0; - fb.path = *p_resolved; - fb.bytes = std::make_shared>(std::move(fb_bytes)); - fb.ft_face = Internal::CreateFreeTypeFaceFromBytes( - fb.bytes->data(), fb.bytes->size(), sub_font_index); - fb.ready = (fb.ft_face != nullptr); - if (fb.ready) { - fb.scale_factor = compute_sysfont_scale_factor( - fb.ft_face, fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0); - fb.shift_value = compute_sysfont_shift_value(fb.ft_face); - LOG_DEBUG( - Lib_Font, - "SystemFonts: fallback='{}' unitsPerEm={} scaleFactor={} shiftValue={}", - fb.path.filename().string(), - fb.ft_face ? static_cast(fb.ft_face->units_per_EM) : 0, - fb.scale_factor, fb.shift_value); - st.system_fallback_faces.push_back(std::move(fb)); - } else { - Internal::DestroyFreeTypeFace(fb.ft_face); - } - } - - { - const std::string global_fallback_name = Config::getSystemFontFallbackName(); - if (!global_fallback_name.empty()) { - const auto existing_name = primary_path.filename().string(); - if (existing_name != global_fallback_name) { - std::filesystem::path fb_path = global_fallback_name; - if (!fb_path.is_absolute()) { - fb_path = base_dir / global_fallback_name; - } - if (const auto resolved_path = resolve_sysfont_path(fb_path)) { - const std::string fb_lower = - lower_ascii(resolved_path->filename().string()); - if (!has_fallback_name_lower(fb_lower)) { - add_fallback_face(*resolved_path); - } - } - } - } - } } { @@ -6928,16 +6767,6 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { ls.owned_sysfonts_ctx = ctx; ls.owned_sysfonts_ctx_size = kSysCtxSize; - if (!Internal::g_mnt) { - Internal::g_mnt = Common::Singleton::Instance(); - } - if (Internal::g_mnt) { - const auto sysfont_base = GetSysFontBaseDir(); - if (!sysfont_base.empty() && !Internal::g_mnt->GetMount("/:dev_font:")) { - Internal::g_mnt->Mount(sysfont_base, "/:dev_font:", true); - } - } - ReleaseLibraryLock(lib, prev_lock_word); return ORBIS_OK; } diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 962181e50..b58e1269f 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -1528,94 +1528,31 @@ static bool DirectoryContainsAnyFontFiles(const std::filesystem::path& dir) { return false; } -static std::optional FindChildDirContainingFile( - const std::filesystem::path& base_dir, const std::string& filename) { - if (filename.empty()) { - return std::nullopt; - } - - std::error_code ec; - if (!std::filesystem::is_directory(base_dir, ec)) { - return std::nullopt; - } - - std::optional match; - for (const auto& entry : std::filesystem::directory_iterator(base_dir, ec)) { - if (ec) { - return std::nullopt; - } - if (!entry.is_directory(ec) || ec) { - continue; - } - const auto candidate = entry.path() / filename; - if (std::filesystem::is_regular_file(candidate, ec) && !ec) { - if (match) { - return std::nullopt; - } - match = entry.path(); - } - } - return match; -} - std::filesystem::path GetSysFontBaseDir() { - std::filesystem::path base = Config::getSysFontPath(); + std::filesystem::path base = Config::getFontsPath(); std::error_code ec; if (base.empty()) { - LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath not set"); + LOG_ERROR(Lib_Font, "SystemFonts: FontsPath not set"); return {}; } if (std::filesystem::is_directory(base, ec)) { - if (DirectoryContainsAnyFontFiles(base)) { + const auto font_dir = base / "font"; + const auto font2_dir = base / "font2"; + if (DirectoryContainsAnyFontFiles(base) || DirectoryContainsAnyFontFiles(font_dir) || + DirectoryContainsAnyFontFiles(font2_dir)) { return base; } - - { - const auto font_dir = base / "font"; - const auto font2_dir = base / "font2"; - if (DirectoryContainsAnyFontFiles(font_dir) || - DirectoryContainsAnyFontFiles(font2_dir)) { - return base; - } - } - - const std::string fallback = Config::getSystemFontFallbackName(); - if (auto child = FindChildDirContainingFile(base, fallback)) { - return *child; - } - - std::optional sole_font_dir; - for (const auto& entry : std::filesystem::directory_iterator(base, ec)) { - if (ec) { - break; - } - if (!entry.is_directory(ec) || ec) { - continue; - } - if (DirectoryContainsAnyFontFiles(entry.path())) { - if (sole_font_dir) { - sole_font_dir.reset(); - break; - } - sole_font_dir = entry.path(); - } - } - if (sole_font_dir) { - return *sole_font_dir; - } - LOG_ERROR( Lib_Font, - "SystemFonts: SysFontPath '{}' contains no font files; set it to the directory that " - "contains the .otf/.ttf files (or ensure [SystemFonts].fallback is present in exactly " - "one child directory)", + "SystemFonts: FontsPath '{}' contains no font files; expected files directly in this " + "directory or under 'font'/'font2'", base.string()); return {}; } if (std::filesystem::is_regular_file(base, ec)) { return base.parent_path(); } - LOG_ERROR(Lib_Font, "SystemFonts: SysFontPath '{}' is not a valid directory or file", + LOG_ERROR(Lib_Font, "SystemFonts: FontsPath '{}' is not a valid directory or file", base.string()); return {}; } @@ -1658,75 +1595,12 @@ static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesyste return direct; } -std::string MacroToCamel(const char* macro_key) { - if (!macro_key) { - return {}; - } - std::string s(macro_key); - const std::string prefix = "FONTSET_"; - if (s.rfind(prefix, 0) != 0) { - return {}; - } - std::string out = "FontSet"; - size_t pos = prefix.size(); - while (pos < s.size()) { - size_t next = s.find('_', pos); - const size_t len = (next == std::string::npos) ? (s.size() - pos) : (next - pos); - std::string token = s.substr(pos, len); - for (auto& c : token) { - c = static_cast(std::tolower(static_cast(c))); - } - if (!token.empty()) { - token[0] = static_cast(std::toupper(static_cast(token[0]))); - } - out += token; - if (next == std::string::npos) { - break; - } - pos = next + 1; - } - return out; -} - std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { if (const auto* def = FindSystemFontDefinition(font_set_type); def) { const auto base_dir = GetSysFontBaseDir(); if (base_dir.empty()) { return {}; } - if (auto override_path = Config::getSystemFontOverride(def->config_key)) { - if (!override_path->empty() && !override_path->is_absolute() && - !override_path->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - def->config_key, override_path->string()); - } - const auto camel_key = MacroToCamel(def->config_key); - if (!camel_key.empty()) { - if (auto override_path2 = Config::getSystemFontOverride(camel_key)) { - if (!override_path2->empty() && !override_path2->is_absolute() && - !override_path2->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path2); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - camel_key, override_path2->string()); - } - std::string lower_camel = camel_key; - lower_camel[0] = - static_cast(std::tolower(static_cast(lower_camel[0]))); - if (auto override_path3 = Config::getSystemFontOverride(lower_camel)) { - if (!override_path3->empty() && !override_path3->is_absolute() && - !override_path3->has_parent_path()) { - return ResolveSystemFontPathCandidate(base_dir, *override_path3); - } - LOG_ERROR(Lib_Font, - "SystemFonts: override for '{}' must be a filename only (no path): '{}'", - lower_camel, override_path3->string()); - } - } if (def->default_file && *def->default_file) { return ResolveSystemFontPathCandidate(base_dir, def->default_file); } @@ -1735,45 +1609,6 @@ std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { return {}; } -std::filesystem::path ResolveSystemFontPathFromConfigOnly(u32 font_set_type) { - const auto base_dir = GetSysFontBaseDir(); - if (base_dir.empty()) { - return {}; - } - - const std::string key_a = fmt::format("fontset_0x{:08X}", font_set_type); - const std::string key_b = fmt::format("fontSet_0x{:08X}", font_set_type); - - auto try_key = [&](const std::string& key) -> std::optional { - if (auto override_path = Config::getSystemFontOverride(key)) { - if (!override_path->empty() && override_path->is_absolute()) { - return *override_path; - } - if (!override_path->empty() && !override_path->has_parent_path()) { - return base_dir / *override_path; - } - LOG_ERROR( - Lib_Font, - "SystemFonts: override for '{}' must be a filename only or absolute path: '{}'", - key, override_path->string()); - } - return std::nullopt; - }; - - if (auto p = try_key(key_a)) { - return *p; - } - if (auto p = try_key(key_b)) { - return *p; - } - - const std::string fallback = Config::getSystemFontFallbackName(); - if (!fallback.empty()) { - return base_dir / fallback; - } - return {}; -} - const FontSetCache* EnsureFontSetCache(u32 font_set_type) { if (font_set_type == 0) { return nullptr; @@ -1872,10 +1707,7 @@ bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle handle) { return false; } - std::filesystem::path primary_path = ResolveSystemFontPathFromConfigOnly(st.font_set_type); - if (primary_path.empty()) { - primary_path = ResolveSystemFontPath(st.font_set_type); - } + std::filesystem::path primary_path = ResolveSystemFontPath(st.font_set_type); std::vector primary_bytes; if (primary_path.empty() || !LoadFontFile(primary_path, primary_bytes)) { return false; @@ -1901,80 +1733,6 @@ bool AttachSystemFont(FontState& st, Libraries::Font::OrbisFontHandle handle) { st.system_font_path = primary_path; st.system_requested = true; - const auto base_dir = GetSysFontBaseDir(); - if (!base_dir.empty()) { - auto resolve_override = - [&](const std::string& key) -> std::optional { - if (auto override_path = Config::getSystemFontOverride(key)) { - if (!override_path->empty() && override_path->is_absolute()) { - return *override_path; - } - if (!override_path->empty() && !override_path->has_parent_path()) { - return base_dir / *override_path; - } - } - return std::nullopt; - }; - - for (int i = 0; i < 8; ++i) { - const std::string key_a = - fmt::format("fontset_0x{:08X}_fallback{}", st.font_set_type, i); - const std::string key_b = - fmt::format("fontSet_0x{:08X}_fallback{}", st.font_set_type, i); - std::optional p = resolve_override(key_a); - if (!p) { - p = resolve_override(key_b); - } - if (!p || p->empty()) { - continue; - } - - std::vector fb_bytes; - if (!LoadFontFile(*p, fb_bytes)) { - continue; - } - FontState::SystemFallbackFace fb{}; - fb.font_id = static_cast(i + 1); - fb.scale_factor = 1.0f; - fb.shift_value = 0; - fb.path = *p; - fb.bytes = std::make_shared>(std::move(fb_bytes)); - fb.ft_face = - CreateFreeTypeFaceFromBytes(fb.bytes->data(), fb.bytes->size(), subfont_index); - fb.ready = (fb.ft_face != nullptr); - if (fb.ready) { - st.system_fallback_faces.push_back(std::move(fb)); - } else { - DestroyFreeTypeFace(fb.ft_face); - } - } - - const std::string global_fallback_name = Config::getSystemFontFallbackName(); - if (!global_fallback_name.empty()) { - const auto existing_name = primary_path.filename().string(); - if (existing_name != global_fallback_name) { - const std::filesystem::path fb_path = base_dir / global_fallback_name; - std::vector fb_bytes; - if (LoadFontFile(fb_path, fb_bytes)) { - FontState::SystemFallbackFace fb{}; - fb.font_id = 0xFFFFFFFFu; - fb.scale_factor = 1.0f; - fb.shift_value = 0; - fb.path = fb_path; - fb.bytes = std::make_shared>(std::move(fb_bytes)); - fb.ft_face = CreateFreeTypeFaceFromBytes(fb.bytes->data(), fb.bytes->size(), - subfont_index); - fb.ready = (fb.ft_face != nullptr); - if (fb.ready) { - st.system_fallback_faces.push_back(std::move(fb)); - } else { - DestroyFreeTypeFace(fb.ft_face); - } - } - } - } - } - LOG_INFO(Lib_Font, "system font attached"); LOG_DEBUG(Lib_Font, "system font attach params:\n" @@ -1994,8 +1752,8 @@ std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHan } if (!st.system_requested) { st.system_requested = true; - const auto configured = Config::getSysFontPath(); - return fmt::format("SystemFace: handle={} requested internal font but sysFontPath ('{}') " + const auto configured = Config::getFontsPath(); + return fmt::format("SystemFace: handle={} requested internal font but fontsPath ('{}') " "could not be loaded", static_cast(handle), configured.string()); } diff --git a/src/core/libraries/font/font_internal.h b/src/core/libraries/font/font_internal.h index 39850c3f7..fe1ea225c 100644 --- a/src/core/libraries/font/font_internal.h +++ b/src/core/libraries/font/font_internal.h @@ -748,9 +748,7 @@ const GlyphEntry* GetGlyphEntry(FontState& st, Libraries::Font::OrbisFontHandle FT_Face& face_out, float& scale_out); const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type); std::filesystem::path GetSysFontBaseDir(); -std::string MacroToCamel(const char* macro_key); std::filesystem::path ResolveSystemFontPath(u32 font_set_type); -std::filesystem::path ResolveSystemFontPathFromConfigOnly(u32 font_set_type); const struct FontSetCache* EnsureFontSetCache(u32 font_set_type); bool HasSfntTables(const std::vector& bytes); bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); From 349b123660e106e29a6d5e7620b27b7c6f9a14ef Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 14 Feb 2026 00:05:56 +0200 Subject: [PATCH 46/51] Fix include order for emulator settings and key manager (Clang) --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 0fd8f57a9..2bfb06520 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,8 +23,8 @@ #ifdef _WIN32 #include #endif -#include #include +#include int main(int argc, char* argv[]) { #ifdef _WIN32 From 4fbb1f9a5de0b233fb140d32694b3692cd76a58b Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 14 Feb 2026 00:30:58 +0200 Subject: [PATCH 47/51] Remove unused emulator settings files and clean up includes in main and config files --- CMakeLists.txt | 2 - src/common/config.cpp | 9 +- src/common/config.h | 3 - src/core/emulator_settings.cpp | 293 --------------------------------- src/core/emulator_settings.h | 194 ---------------------- src/main.cpp | 8 +- 6 files changed, 5 insertions(+), 504 deletions(-) delete mode 100644 src/core/emulator_settings.cpp delete mode 100644 src/core/emulator_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b8feb221..4e92bfd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -870,8 +870,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/tls.h src/core/emulator_state.cpp src/core/emulator_state.h - src/core/emulator_settings.cpp - src/core/emulator_settings.h ) if (ARCHITECTURE STREQUAL "x86_64") diff --git a/src/common/config.cpp b/src/common/config.cpp index 34d1ca361..a5eea0a64 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -5,9 +5,8 @@ #include #include #include -#include -#include #include +#include // for wstring support #include #include "common/assert.h" @@ -476,7 +475,7 @@ void setShowFpsCounter(bool enable, bool is_game_specific) { showFpsCounter.set(enable, is_game_specific); } -static bool isLoggingEnabled() { +bool isLoggingEnabled() { return logEnabled.get(); } @@ -1022,7 +1021,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) { } } -static void sortTomlSections(toml::ordered_value& data) { +void sortTomlSections(toml::ordered_value& data) { toml::ordered_value ordered_data; std::vector section_order = {"General", "Input", "Audio", "GPU", "Vulkan", "Debug", "Keys", "GUI", "Settings"}; @@ -1310,7 +1309,7 @@ constexpr std::string_view GetDefaultGlobalConfig() { )"; } -static constexpr std::string_view GetDefaultInputConfig() { +constexpr std::string_view GetDefaultInputConfig() { return R"(#Feeling lost? Check out the Help section! # Keyboard bindings diff --git a/src/common/config.h b/src/common/config.h index 6aaec7c5d..a9e0f7010 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,9 +4,6 @@ #pragma once #include -#include -#include -#include #include #include "types.h" diff --git a/src/core/emulator_settings.cpp b/src/core/emulator_settings.cpp deleted file mode 100644 index fbbdf5be6..000000000 --- a/src/core/emulator_settings.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include "emulator_settings.h" - -using json = nlohmann::json; - -std::shared_ptr EmulatorSettings::s_instance = nullptr; -std::mutex EmulatorSettings::s_mutex; - -namespace nlohmann { -template <> -struct adl_serializer { - static void to_json(json& j, const std::filesystem::path& p) { - j = p.string(); - } - static void from_json(const json& j, std::filesystem::path& p) { - p = j.get(); - } -}; -} // namespace nlohmann - -// -------------------- -// Print summary -// -------------------- -void EmulatorSettings::PrintChangedSummary(const std::vector& changed) { - if (changed.empty()) { - std::cout << "[Settings] No game-specific overrides applied\n"; - return; - } - std::cout << "[Settings] Game-specific overrides applied:\n"; - for (const auto& k : changed) - std::cout << " * " << k << "\n"; -} - -// -------------------- -// ctor/dtor + singleton -// -------------------- -EmulatorSettings::EmulatorSettings() { - // Load(); -} -EmulatorSettings::~EmulatorSettings() { - Save(); -} - -std::shared_ptr EmulatorSettings::GetInstance() { - std::lock_guard lock(s_mutex); - if (!s_instance) - s_instance = std::make_shared(); - return s_instance; -} - -void EmulatorSettings::SetInstance(std::shared_ptr instance) { - std::lock_guard lock(s_mutex); - s_instance = instance; -} - -// -------------------- -// General helpers -// -------------------- - -std::filesystem::path EmulatorSettings::GetSysFontsDir() { - if (m_general.sys_fonts_dir.value.empty()) { - return Common::FS::GetUserPath(Common::FS::PathType::FontsDir); - } - return m_general.sys_fonts_dir.value; -} - -void EmulatorSettings::SetSysFontsDir(const std::filesystem::path& dir) { - m_general.sys_fonts_dir.value = dir; -} - -// -------------------- -// Save -// -------------------- -bool EmulatorSettings::Save(const std::string& serial) const { - try { - if (!serial.empty()) { - const std::filesystem::path cfgDir = - Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs); - std::filesystem::create_directories(cfgDir); - const std::filesystem::path path = cfgDir / (serial + ".json"); - - json j = json::object(); - - // Only write overrideable fields for each group - json generalObj = json::object(); - for (auto& item : m_general.GetOverrideableFields()) { - json whole = m_general; - if (whole.contains(item.key)) - generalObj[item.key] = whole[item.key]; - } - j["General"] = generalObj; - - // Debug - /* json debugObj = json::object(); - for (auto& item : m_debug.GetOverrideableFields()) { - json whole = m_debug; - if (whole.contains(item.key)) - debugObj[item.key] = whole[item.key]; - } - j["Debug"] = debugObj; - - // Input - json inputObj = json::object(); - for (auto& item : m_input.GetOverrideableFields()) { - json whole = m_input; - if (whole.contains(item.key)) - inputObj[item.key] = whole[item.key]; - } - j["Input"] = inputObj; - - // Audio - json audioObj = json::object(); - for (auto& item : m_audio.GetOverrideableFields()) { - json whole = m_audio; - if (whole.contains(item.key)) - audioObj[item.key] = whole[item.key]; - } - j["Audio"] = audioObj; - - // GPU - json gpuObj = json::object(); - for (auto& item : m_gpu.GetOverrideableFields()) { - json whole = m_gpu; - if (whole.contains(item.key)) - gpuObj[item.key] = whole[item.key]; - } - j["GPU"] = gpuObj; - - // Vulkan - json vulkanObj = json::object(); - for (auto& item : m_vulkan.GetOverrideableFields()) { - json whole = m_vulkan; - if (whole.contains(item.key)) - vulkanObj[item.key] = whole[item.key]; - } - j["Vulkan"] = vulkanObj;*/ - - std::ofstream out(path); - if (!out.is_open()) { - std::cerr << "Failed to open file for writing: " << path << std::endl; - return false; - } - out << std::setw(4) << j; - out.flush(); - if (out.fail()) { - std::cerr << "Failed to write settings to: " << path << std::endl; - return false; - } - return true; - } else { - const std::filesystem::path path = - Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.json"; - json j; - j["General"] = m_general; - /* j["Debug"] = m_debug; - j["Input"] = m_input; - j["Audio"] = m_audio; - j["GPU"] = m_gpu; - j["Vulkan"] = m_vulkan; - j["Users"] = m_userManager.GetUsers();*/ - - std::ofstream out(path); - if (!out.is_open()) { - std::cerr << "Failed to open file for writing: " << path << std::endl; - return false; - } - out << std::setw(4) << j; - out.flush(); - if (out.fail()) { - std::cerr << "Failed to write settings to: " << path << std::endl; - return false; - } - return true; - } - } catch (const std::exception& e) { - std::cerr << "Error saving settings: " << e.what() << std::endl; - return false; - } -} - -// -------------------- -// Load -// -------------------- -bool EmulatorSettings::Load(const std::string& serial) { - try { - const std::filesystem::path userDir = - Common::FS::GetUserPath(Common::FS::PathType::UserDir); - const std::filesystem::path configPath = userDir / "config.json"; - - // Load global config if exists - if (std::ifstream globalIn{configPath}; globalIn.good()) { - json gj; - globalIn >> gj; - if (gj.contains("General")) { - json current = m_general; // JSON from existing struct with all defaults - current.update(gj.at("General")); // merge only fields present in file - m_general = current.get(); // convert back - } - /* if (gj.contains("Debug")) { - json current = m_debug; - current.update(gj.at("Debug")); - m_debug = current.get(); - } - if (gj.contains("Input")) { - json current = m_input; - current.update(gj.at("Input")); - m_input = current.get(); - } - if (gj.contains("Audio")) { - json current = m_audio; - current.update(gj.at("Audio")); - m_audio = current.get(); - } - if (gj.contains("GPU")) { - json current = m_gpu; - current.update(gj.at("GPU")); - m_gpu = current.get(); - } - if (gj.contains("Vulkan")) { - json current = m_vulkan; - current.update(gj.at("Vulkan")); - m_vulkan = current.get(); - } - if (gj.contains("Users")) - m_userManager.GetUsers() = gj.at("Users").get();*/ - } else { - SetDefaultValues(); - // ensure a default user exists - /* if (m_userManager.GetUsers().user.empty()) - m_userManager.GetUsers().user = m_userManager.CreateDefaultUser();*/ - Save(); - } - - // Load per-game overrides and apply - if (!serial.empty()) { - const std::filesystem::path gamePath = - Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serial + ".json"); - if (!std::filesystem::exists(gamePath)) - return false; - - std::ifstream in(gamePath); - if (!in.is_open()) - return false; - - json gj; - in >> gj; - - std::vector changed; - - if (gj.contains("General")) { - ApplyGroupOverrides(m_general, gj.at("General"), changed); - } - if (gj.contains("Debug")) { - ApplyGroupOverrides(m_debug, gj.at("Debug"), changed); - } - if (gj.contains("Input")) { - ApplyGroupOverrides(m_input, gj.at("Input"), changed); - } - if (gj.contains("Audio")) { - ApplyGroupOverrides(m_audio, gj.at("Audio"), changed); - } - if (gj.contains("GPU")) { - ApplyGroupOverrides(m_gpu, gj.at("GPU"), changed); - } - if (gj.contains("Vulkan")) { - ApplyGroupOverrides(m_vulkan, gj.at("Vulkan"), changed); - } - - PrintChangedSummary(changed); - return true; - } - - return true; - } catch (const std::exception& e) { - std::cerr << "Error loading settings: " << e.what() << std::endl; - return false; - } -} - -void EmulatorSettings::SetDefaultValues() { - m_general = GeneralSettings{}; - m_debug = DebugSettings{}; - m_input = InputSettings{}; - m_audio = AudioSettings{}; - m_gpu = GPUSettings{}; - m_vulkan = VulkanSettings{}; -} diff --git a/src/core/emulator_settings.h b/src/core/emulator_settings.h deleted file mode 100644 index 71b581611..000000000 --- a/src/core/emulator_settings.h +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "common/types.h" - -// ------------------------------- -// Generic Setting wrapper -// ------------------------------- -template -struct Setting { - T value{}; -}; - -template -void to_json(nlohmann::json& j, const Setting& s) { - j = s.value; -} - -template -void from_json(const nlohmann::json& j, Setting& s) { - s.value = j.get(); -} - -// ------------------------------- -// Helper to describe a per-field override action -// ------------------------------- -struct OverrideItem { - const char* key; - // apply(basePtrToStruct, jsonEntry, changedFields) - std::function&)> apply; -}; - -// Helper factory: create an OverrideItem binding a pointer-to-member -template -inline OverrideItem make_override(const char* key, Setting Struct::* member) { - return OverrideItem{key, [member, key](void* base, const nlohmann::json& entry, - std::vector& changed) { - if (!entry.is_object()) - return; - - Struct* obj = reinterpret_cast(base); - Setting& dst = obj->*member; - - Setting tmp = entry.get>(); - - if (dst.value != tmp.value) { - changed.push_back(std::string(key) + " ( " + - nlohmann::json(dst.value).dump() + " → " + - nlohmann::json(tmp.value).dump() + " )"); - } - - dst.value = tmp.value; - }}; -} - -// ------------------------------- -// General settings -// ------------------------------- -struct GeneralSettings { - Setting sys_fonts_dir; - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; - -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, sys_fonts_dir) - -// ------------------------------- -// Debug settings -// ------------------------------- -struct DebugSettings { - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; -// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings) - -// ------------------------------- -// Input settings -// ------------------------------- - -struct InputSettings { - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; -// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings) - -// ------------------------------- -// Audio settings -// ------------------------------- -struct AudioSettings { - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; - -// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings) - -// ------------------------------- -// GPU settings -// ------------------------------- -struct GPUSettings { - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; -// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings) -// ------------------------------- -// Vulkan settings -// ------------------------------- -struct VulkanSettings { - std::vector GetOverrideableFields() const { - return std::vector{}; - } -}; -// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(VulkanSettings) - -// ------------------------------- -// Main manager -// ------------------------------- -class EmulatorSettings { -public: - EmulatorSettings(); - ~EmulatorSettings(); - - static std::shared_ptr GetInstance(); - static void SetInstance(std::shared_ptr instance); - - bool Save(const std::string& serial = "") const; - bool Load(const std::string& serial = ""); - void SetDefaultValues(); - - // general accessors - std::filesystem::path GetSysFontsDir(); - void SetSysFontsDir(const std::filesystem::path& dir); - -private: - GeneralSettings m_general{}; - DebugSettings m_debug{}; - InputSettings m_input{}; - AudioSettings m_audio{}; - GPUSettings m_gpu{}; - VulkanSettings m_vulkan{}; - // UserManager m_userManager; - - static std::shared_ptr s_instance; - static std::mutex s_mutex; - - // Generic helper that applies override descriptors for a specific group - template - void ApplyGroupOverrides(Group& group, const nlohmann::json& groupJson, - std::vector& changed) { - for (auto& item : group.GetOverrideableFields()) { - if (!groupJson.contains(item.key)) - continue; - item.apply(&group, groupJson.at(item.key), changed); - } - } - - static void PrintChangedSummary(const std::vector& changed); - -public: -#define SETTING_FORWARD(group, Name, field) \ - auto Get##Name() const { \ - return group.field.value; \ - } \ - void Set##Name(const decltype(group.field.value)& v) { \ - group.field.value = v; \ - } -#define SETTING_FORWARD_BOOL(group, Name, field) \ - auto Is##Name() const { \ - return group.field.value; \ - } \ - void Set##Name(const decltype(group.field.value)& v) { \ - group.field.value = v; \ - } -#define SETTING_FORWARD_BOOL_READONLY(group, Name, field) \ - auto Is##Name() const { \ - return group.field.value; \ - } - -#undef SETTING_FORWARD -#undef SETTING_FORWARD_BOOL -}; diff --git a/src/main.cpp b/src/main.cpp index 2bfb06520..d3799e2ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,8 +23,6 @@ #ifdef _WIN32 #include #endif -#include -#include int main(int argc, char* argv[]) { #ifdef _WIN32 @@ -32,13 +30,9 @@ int main(int argc, char* argv[]) { #endif IPC::Instance().Init(); - // Init emulator state + auto emu_state = std::make_shared(); EmulatorState::SetInstance(emu_state); - // Load configurations - std::shared_ptr emu_settings = std::make_shared(); - EmulatorSettings::SetInstance(emu_settings); - emu_settings->Load(); const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); From 09fa10fd4d4d7d2a0d9715dde7d3f195cc352989 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 14 Feb 2026 00:50:32 +0200 Subject: [PATCH 48/51] Remove unused instructions file creation from user paths initialization --- src/common/path_util.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 97d119b94..5d37990ff 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -145,17 +145,6 @@ static auto UserPaths = [] { notice_file.close(); } - const auto instructions_path = user_dir / FONTS_DIR / "Instructions.txt"; - std::error_code ec; - if (!std::filesystem::exists(instructions_path, ec)) { - std::ofstream font_instructions(instructions_path); - if (font_instructions.is_open()) { - font_instructions << "Place system font files (.otf/.ttf) into the 'font' and 'font2' " - "folders.\n"; - font_instructions.close(); - } - } - return paths; }(); From bdb52525a53af261a846de28b7a76b3a7ec01a0c Mon Sep 17 00:00:00 2001 From: w1naenator Date: Sat, 14 Feb 2026 02:16:31 +0200 Subject: [PATCH 49/51] Consolidate internal font/ft internals Move internal font structs, aliases, and helpers out of font.cpp into shared internal headers/sources. Promote shared ABI-facing types (including FontHandleOpaqueNative and layout/system-use structs) into font internal headers and remove function-local duplicates. Move FreeType driver/renderer table construction into fontft_internal.cpp and wire fontft.cpp to internal getters. Ensure internal.cpp implementations are declared in headers; deduplicate declarations and clean includes. --- src/core/libraries/font/font.cpp | 1222 +++---------------- src/core/libraries/font/font_internal.cpp | 192 ++- src/core/libraries/font/font_internal.h | 127 ++ src/core/libraries/font/fontft.cpp | 69 +- src/core/libraries/font/fontft_internal.cpp | 255 ++-- src/core/libraries/font/fontft_internal.h | 187 ++- 6 files changed, 699 insertions(+), 1353 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 02dbd4f1a..21981900c 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -58,932 +58,47 @@ using Libraries::Font::OrbisFontGlyphOutline; using Libraries::Font::OrbisFontGlyphOutlinePoint; using Libraries::Font::OrbisFontMem; using Libraries::Font::OrbisFontStyleFrame; -namespace Internal = Libraries::Font::Internal; +using Libraries::Font::Internal::Param; -struct NamedParam { - std::string_view name; - std::string value; -}; - -template -static std::string DescribeValue(const T& value) { - using Clean = std::remove_cvref_t; - if constexpr (std::is_same_v) { - return value ? "true" : "false"; - } else if constexpr (std::is_pointer_v) { - return fmt::format("{}", reinterpret_cast(value)); - } else if constexpr (std::is_floating_point_v) { - return fmt::format("{:.6g}", value); - } else if constexpr (std::is_enum_v) { - using Underlying = std::underlying_type_t; - return DescribeValue(static_cast(value)); - } else { - return fmt::format("{}", value); - } -} - -template -static NamedParam Param(std::string_view name, const T& value) { - return NamedParam{name, DescribeValue(value)}; -} - -static std::string formatParams(std::initializer_list params) { - fmt::memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), "params:\n"); - for (const auto& p : params) { - fmt::format_to(std::back_inserter(buffer), "{}: {}\n", p.name, p.value); - } - return fmt::to_string(buffer); -} - -struct FtExternalFaceObj { - u32 refcount; - u32 reserved04; - u32 reserved08; - u32 sub_font_index; - u64 reserved10; - FtExternalFaceObj* next; - u64 reserved20; - u64 reserved28; - FT_Face face; - const u8* font_data; - u32 font_size; - u32 sfnt_base; -}; -static_assert(offsetof(FtExternalFaceObj, sub_font_index) == 0x0C); -static_assert(offsetof(FtExternalFaceObj, next) == 0x18); -static_assert(offsetof(FtExternalFaceObj, face) == 0x30); - -using FontAllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); -using FontFreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - -struct LibraryState { - bool support_system = false; - bool support_external = false; - u32 external_formats = 0; - u32 external_fontMax = 0; - const Libraries::Font::OrbisFontMem* backing_memory = nullptr; - Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; - u64 edition = 0; - void* alloc_ctx = nullptr; - FontAllocFn alloc_fn = nullptr; - FontFreeFn free_fn = nullptr; - void* owned_mspace = nullptr; - u32 owned_mspace_size = 0; - void* owned_sysfonts_ctx = nullptr; - u32 owned_sysfonts_ctx_size = 0; - void* owned_external_fonts_ctx = nullptr; - u32 owned_external_fonts_ctx_size = 0; - void* owned_device_cache = nullptr; - u32 owned_device_cache_size = 0; -}; -static std::unordered_map g_library_state; - -static void LogFontOpenError(s32 rc) { - switch (rc) { - case ORBIS_FONT_ERROR_INVALID_LIBRARY: - LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); - break; - case ORBIS_FONT_ERROR_INVALID_PARAMETER: - LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); - break; - case ORBIS_FONT_ERROR_ALLOCATION_FAILED: - LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); - break; - case ORBIS_FONT_ERROR_FS_OPEN_FAILED: - LOG_ERROR(Lib_Font, "FS_OPEN_FAILED"); - break; - case ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION: - LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); - break; - case ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT: - LOG_ERROR(Lib_Font, "NO_SUPPORT_FORMAT"); - break; - case ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET: - LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); - break; - case ORBIS_FONT_ERROR_FONT_OPEN_MAX: - LOG_ERROR(Lib_Font, "FONT_OPEN_MAX"); - break; - default: - LOG_ERROR(Lib_Font, "FAILED"); - break; - } -} - -static u16 ClampToU16(float value) { - if (value <= 0.0f) { - return 0; - } - const float clamped = std::min(value, static_cast(std::numeric_limits::max())); - return static_cast(std::lround(clamped)); -} - -static LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { - return g_library_state[lib]; -} - -static void RemoveLibState(Libraries::Font::OrbisFontLib lib) { - if (auto it = g_library_state.find(lib); it != g_library_state.end()) { - const auto free_fn = it->second.free_fn; - const auto alloc_ctx = it->second.alloc_ctx; - auto free_owned = [&](void* p) { - if (!p) { - return; - } - if (free_fn) { - using FreeFnGuest = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto free_fn_guest = reinterpret_cast(free_fn); - Core::ExecuteGuest(free_fn_guest, alloc_ctx, p); - return; - } - std::free(p); - }; - free_owned(it->second.owned_device_cache); - free_owned(it->second.owned_external_fonts_ctx); - free_owned(it->second.owned_sysfonts_ctx); - free_owned(it->second.owned_mspace); - g_library_state.erase(it); - } -} - -static void LogExternalFormatSupport(u32 formats_mask) { - LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); -} - -static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes); - -static std::optional ResolveKnownSysFontAlias( - const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { - const auto resolve_existing = - [&](std::string_view filename) -> std::optional { - const std::filesystem::path file_path{std::string(filename)}; - std::error_code ec; - { - const auto candidate = sysfonts_dir / file_path; - if (std::filesystem::exists(candidate, ec)) { - return candidate; - } - } - { - const auto candidate = sysfonts_dir / "font" / file_path; - if (std::filesystem::exists(candidate, ec)) { - return candidate; - } - } - { - const auto candidate = sysfonts_dir / "font2" / file_path; - if (std::filesystem::exists(candidate, ec)) { - return candidate; - } - } - return std::nullopt; - }; - - static constexpr std::array, 41> kAliases = {{ - {"SST-EU-ROMAN-L.OTF", "SST-Light.otf"}, - {"SST-EU-ROMAN.OTF", "SST-Roman.otf"}, - {"SST-EU-ROMAN-M.OTF", "SST-Medium.otf"}, - {"SST-EU-ROMAN-R.OTF", "SST-Roman.otf"}, - {"SST-EU-ROMAN-I.OTF", "SST-Italic.otf"}, - {"SST-EU-ROMAN-B.OTF", "SST-Bold.otf"}, - {"SST-EU-ROMAN-BI.OTF", "SST-BoldItalic.otf"}, - {"SST-ITALIC-L.OTF", "SST-LightItalic.otf"}, - {"SST-ITALIC-R.OTF", "SST-Italic.otf"}, - {"SST-ITALIC-M.OTF", "SST-MediumItalic.otf"}, - {"SST-ITALIC-B.OTF", "SST-BoldItalic.otf"}, - {"SST-TYPEWRITER-R.OTF", "SSTTypewriter-Roman.otf"}, - {"SST-TYPEWRITER-B.OTF", "SSTTypewriter-Bd.otf"}, - {"SST-JPPRO-R.OTF", "SSTJpPro-Regular.otf"}, - {"SST-JPPRO-B.OTF", "SSTJpPro-Bold.otf"}, - {"SST-CNGB-HEI-R.TTF", "DFHEI5-SONY.ttf"}, - {"SST-ARIB-STD-B24-R.TTF", "SSTAribStdB24-Regular.ttf"}, - {"SST-ARABIC-R.OTF", "SSTArabic-Roman.otf"}, - {"SST-ARABIC-L.OTF", "SSTArabic-Light.otf"}, - {"SST-ARABIC-M.OTF", "SSTArabic-Medium.otf"}, - {"SST-ARABIC-B.OTF", "SSTArabic-Bold.otf"}, - {"SCE-EXT-HANGUL-L.OTF", "SCEPS4Yoongd-Light.otf"}, - {"SCE-EXT-HANGUL-R.OTF", "SCEPS4Yoongd-Medium.otf"}, - {"SCE-EXT-HANGUL-B.OTF", "SCEPS4Yoongd-Bold.otf"}, - {"SSTCC-SERIF-MONO.OTF", "e046323ms.ttf"}, - {"SSTCC-SERIF.OTF", "e046323ts.ttf"}, - {"SSTCC-SANSSERIF-MONO.OTF", "n023055ms.ttf"}, - {"SSTCC-SANSSERIF.OTF", "n023055ts.ttf"}, - {"SSTCC-CUSUAL.OTF", "d013013ds.ttf"}, - {"SSTCC-CURSIVE.OTF", "k006004ds.ttf"}, - {"SSTCC-SMALLCAPITAL.OTF", "c041056ts.ttf"}, - {"SCE-JP-CATTLEYA-L.OTF", "SCE-RDC-R-JPN.otf"}, - {"SCE-JP-CATTLEYA-B.OTF", "SCE-RDC-B-JPN.otf"}, - {"SST-THAI-L.OTF", "SSTThai-Light.otf"}, - {"SST-THAI-R.OTF", "SSTThai-Roman.otf"}, - {"SST-THAI-M.OTF", "SSTThai-Medium.otf"}, - {"SST-THAI-B.OTF", "SSTThai-Bold.otf"}, - {"SST-VIETNAMESE-L.OTF", "SSTVietnamese-Light.otf"}, - {"SST-VIETNAMESE-R.OTF", "SSTVietnamese-Roman.otf"}, - {"SST-VIETNAMESE-M.OTF", "SSTVietnamese-Medium.otf"}, - {"SST-VIETNAMESE-B.OTF", "SSTVietnamese-Bold.otf"}, - }}; - - for (const auto& [from, to] : kAliases) { - if (ps4_filename == from) { - return resolve_existing(to); - } - if (ps4_filename == to) { - if (auto reverse = resolve_existing(from)) { - return reverse; - } - } - } - return std::nullopt; -} - -static void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params) { - if (!params) { - LOG_INFO(Lib_Font, "OpenFontSetParams: "); - return; - } - LOG_INFO( - Lib_Font, - "OpenFontSetParams: tag=0x{:04X} flags=0x{:X} subfont={} unique_id={} reserved_ptrs=[{}, " - "{}]", - params->tag, params->flags, params->subfont_index, params->unique_id, params->reserved_ptr1, - params->reserved_ptr2); -} - -static std::filesystem::path ResolveGuestPath(const char* guest_path) { - return Internal::ResolveGuestPath(guest_path); -} - -static bool LoadGuestFileBytes(const std::filesystem::path& host_path, - std::vector& out_bytes) { - std::ifstream file(host_path, std::ios::binary | std::ios::ate); - if (!file) { - return false; - } - const std::streamoff size = file.tellg(); - if (size < 0) { - return false; - } - if (size == 0) { - out_bytes.clear(); - return true; - } - if (static_cast(size) > std::numeric_limits::max()) { - return false; - } - out_bytes.resize(static_cast(size)); - file.seekg(0, std::ios::beg); - if (!file.read(reinterpret_cast(out_bytes.data()), - static_cast(out_bytes.size()))) { - out_bytes.clear(); - return false; - } - return true; -} - -static constexpr float kPointsPerInch = 72.0f; -static constexpr float kScaleEpsilon = 1e-4f; -constexpr u16 kStyleFrameMagic = 0x0F09; - -static float SafeDpiToFloat(u32 dpi) { - return dpi == 0 ? kPointsPerInch : static_cast(dpi); -} - -static float PointsToPixels(float pt, u32 dpi) { - return pt * SafeDpiToFloat(dpi) / kPointsPerInch; -} - -static float PixelsToPoints(float px, u32 dpi) { - const float dpi_f = SafeDpiToFloat(dpi); - return px * kPointsPerInch / dpi_f; -} - -struct StyleFrameScaleState { - float scale_w; - float scale_h; - u32 dpi_x; - u32 dpi_y; -}; - -static StyleFrameScaleState ResolveStyleFrameScale(const OrbisFontStyleFrame* style, - float base_scale_w, float base_scale_h, - u32 base_dpi_x, u32 base_dpi_y) { - StyleFrameScaleState resolved{ - .scale_w = base_scale_w, - .scale_h = base_scale_h, - .dpi_x = base_dpi_x, - .dpi_y = base_dpi_y, - }; - if (!style || style->magic != kStyleFrameMagic) { - return resolved; - } - if (style->hDpi != 0) { - resolved.dpi_x = style->hDpi; - } - if (style->vDpi != 0) { - resolved.dpi_y = style->vDpi; - } - if ((style->flags1 & 1u) == 0) { - return resolved; - } - - const bool unit_is_pixel = (style->scaleUnit == 0); - if (unit_is_pixel) { - resolved.scale_w = style->scalePixelW; - resolved.scale_h = style->scalePixelH; - } else { - const u32 dpi_x = style->hDpi; - const u32 dpi_y = style->vDpi; - resolved.scale_w = dpi_x ? PointsToPixels(style->scalePixelW, dpi_x) : style->scalePixelW; - resolved.scale_h = dpi_y ? PointsToPixels(style->scalePixelH, dpi_y) : style->scalePixelH; - } - return resolved; -} - -static bool ValidateStyleFramePtr(const OrbisFontStyleFrame* frame) { - return frame && frame->magic == kStyleFrameMagic; -} - -static inline u32 LoadU32(const void* base, std::size_t off) { - u32 v = 0; - std::memcpy(&v, static_cast(base) + off, sizeof(v)); - return v; -} -static inline float LoadF32(const void* base, std::size_t off) { - float v = 0.0f; - std::memcpy(&v, static_cast(base) + off, sizeof(v)); - return v; -} -static inline void StoreU32(void* base, std::size_t off, u32 v) { - std::memcpy(static_cast(base) + off, &v, sizeof(v)); -} -static inline void StoreF32(void* base, std::size_t off, float v) { - std::memcpy(static_cast(base) + off, &v, sizeof(v)); -} - -static void ClearRenderOutputs(Libraries::Font::OrbisFontGlyphMetrics* metrics, - Libraries::Font::OrbisFontRenderOutput* result) { - if (metrics) { - *metrics = {}; - } - if (result) { - *result = {}; - } -} - -struct SystemFontDefinition { - u32 font_set_type; - const char* config_key; - const char* default_file; -}; - -static constexpr SystemFontDefinition kSystemFontDefinitions[] = { - {0x18070043, "FONTSET_SST_STD_EUROPEAN_LIGHT", "SST-Light.otf"}, - {0x18070044, "FONTSET_SST_STD_EUROPEAN", "SST-Roman.otf"}, - {0x18070045, "FONTSET_SST_STD_EUROPEAN_MEDIUM", "SST-Medium.otf"}, - {0x18070047, "FONTSET_SST_STD_EUROPEAN_BOLD", "SST-Bold.otf"}, - {0x18070053, "FONTSET_SST_STD_VIETNAMESE_LIGHT", "SSTVietnamese-Light.otf"}, - {0x18070054, "FONTSET_SST_STD_VIETNAMESE", "SSTVietnamese-Roman.otf"}, - {0x18070055, "FONTSET_SST_STD_VIETNAMESE_MEDIUM", "SSTVietnamese-Medium.otf"}, - {0x18070057, "FONTSET_SST_STD_VIETNAMESE_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180700C3, "FONTSET_SST_STD_EUROPEAN_AR_LIGHT", "SSTArabic-Light.otf"}, - {0x180700C4, "FONTSET_SST_STD_EUROPEAN_AR", "SSTArabic-Roman.otf"}, - {0x180700C5, "FONTSET_SST_STD_EUROPEAN_AR_MEDIUM", "SSTArabic-Medium.otf"}, - {0x180700C7, "FONTSET_SST_STD_EUROPEAN_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18070444, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, - {0x18070447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x18070454, "FONTSET_SST_STD_EUROPEAN_JP", "SSTVietnamese-Roman.otf"}, - {0x18070457, "FONTSET_SST_STD_EUROPEAN_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180704C4, "FONTSET_SST_STD_EUROPEAN_JP_AR", "SSTArabic-Roman.otf"}, - {0x180704C7, "FONTSET_SST_STD_EUROPEAN_JP_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18071053, "FONTSET_SST_STD_THAI_LIGHT", "SSTThai-Light.otf"}, - {0x18071054, "FONTSET_SST_STD_THAI", "SSTThai-Roman.otf"}, - {0x18071055, "FONTSET_SST_STD_THAI_MEDIUM", "SSTThai-Medium.otf"}, - {0x18071057, "FONTSET_SST_STD_THAI_BOLD", "SSTThai-Bold.otf"}, - {0x18071454, "FONTSET_SST_STD_EUROPEAN_JP_TH", "SSTThai-Roman.otf"}, - {0x18071457, "FONTSET_SST_STD_EUROPEAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, - {0x18072444, "FONTSET_SST_STD_EUROPEAN_JPUH", "SSTAribStdB24-Regular.ttf"}, - {0x18072447, "FONTSET_SST_STD_EUROPEAN_JPUH_BOLD", "SSTJpPro-Bold.otf"}, - {0x180724C4, "FONTSET_SST_STD_EUROPEAN_JPUH_AR", "SSTArabic-Roman.otf"}, - {0x180724C7, "FONTSET_SST_STD_EUROPEAN_JPUH_AR_BOLD", "SSTArabic-Bold.otf"}, - {0x18073454, "FONTSET_SST_STD_EUROPEAN_JPUH_TH", "SSTThai-Roman.otf"}, - {0x18073457, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, - {0x180734D4, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, - {0x180734D7, "FONTSET_SST_STD_EUROPEAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, - {0x18078044, "FONTSET_SST_STD_EUROPEAN_GB", "DFHEI5-SONY.ttf"}, - {0x180780C4, "FONTSET_SST_STD_EUROPEAN_GB_AR", "SSTArabic-Roman.otf"}, - {0x18079054, "FONTSET_SST_STD_EUROPEAN_GB_TH", "SSTThai-Roman.otf"}, - {0x1807A044, "FONTSET_SST_STD_EUROPEAN_GBUH", "e046323ms.ttf"}, - {0x1807A0C4, "FONTSET_SST_STD_EUROPEAN_GBUH_AR", "SSTArabic-Bold.otf"}, - {0x1807A444, "FONTSET_SST_STD_EUROPEAN_JPCJK", "SSTJpPro-Regular.otf"}, - {0x1807A4C4, "FONTSET_SST_STD_EUROPEAN_JPCJK_AR", "SSTArabic-Roman.otf"}, - {0x1807AC44, "FONTSET_SST_STD_EUROPEAN_GBCJK", "n023055ms.ttf"}, - {0x1807ACC4, "FONTSET_SST_STD_EUROPEAN_GBCJK_AR", "SSTArabic-Bold.otf"}, - {0x1807B054, "FONTSET_SST_STD_EUROPEAN_GBUH_TH", "SSTThai-Roman.otf"}, - {0x1807B0D4, "FONTSET_SST_STD_EUROPEAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, - {0x1807B454, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH", "SSTThai-Roman.otf"}, - {0x1807B4D4, "FONTSET_SST_STD_EUROPEAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x1807BC54, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH", "SSTThai-Roman.otf"}, - {0x1807BCD4, "FONTSET_SST_STD_EUROPEAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x18080444, "FONTSET_SST_STD_JAPANESE_JP", "SSTAribStdB24-Regular.ttf"}, - {0x18080447, "FONTSET_SST_STD_JAPANESE_JP_BOLD", "SSTAribStdB24-Regular.ttf"}, - {0x18080454, "FONTSET_SST_STD_VIETNAMESE_JP", "SSTVietnamese-Roman.otf"}, - {0x18080457, "FONTSET_SST_STD_VIETNAMESE_JP_BOLD", "SSTVietnamese-Bold.otf"}, - {0x180804C4, "FONTSET_SST_STD_JAPANESE_JP_AR", "SSTAribStdB24-Regular.ttf"}, - {0x180804C7, "FONTSET_SST_STD_JAPANESE_JP_AR_BOLD", "SSTAribStdB24-Regular.ttf"}, - {0x18081454, "FONTSET_SST_STD_ASIAN_JP_TH", "SSTThai-Roman.otf"}, - {0x18081457, "FONTSET_SST_STD_ASIAN_JP_TH_BOLD", "SSTThai-Bold.otf"}, - {0x18082444, "FONTSET_SST_STD_JAPANESE_JPUH", "SSTAribStdB24-Regular.ttf"}, - {0x18082447, "FONTSET_SST_STD_JAPANESE_JPUH_BOLD", "SSTJpPro-Bold.otf"}, - {0x180824C4, "FONTSET_SST_STD_JAPANESE_JPUH_AR", "SSTAribStdB24-Regular.ttf"}, - {0x180824C7, "FONTSET_SST_STD_JAPANESE_JPUH_AR_BOLD", "SSTJpPro-Bold.otf"}, - {0x18083454, "FONTSET_SST_STD_ASIAN_JPUH_TH", "SSTThai-Roman.otf"}, - {0x18083457, "FONTSET_SST_STD_ASIAN_JPUH_TH_BOLD", "SSTThai-Bold.otf"}, - {0x180834D4, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR", "SSTThai-Roman.otf"}, - {0x180834D7, "FONTSET_SST_STD_ASIAN_JPUH_TH_AR_BOLD", "SSTThai-Bold.otf"}, - {0x1808A444, "FONTSET_SST_STD_JAPANESE_JPCJK", "SSTAribStdB24-Regular.ttf"}, - {0x1808A4C4, "FONTSET_SST_STD_JAPANESE_JPCJK_AR", "SSTJpPro-Bold.otf"}, - {0x1808B454, "FONTSET_SST_STD_ASIAN_JPCJK_TH", "SSTThai-Roman.otf"}, - {0x1808B4D4, "FONTSET_SST_STD_ASIAN_JPCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x180C8044, "FONTSET_SST_STD_SCHINESE_GB", "DFHEI5-SONY.ttf"}, - {0x180C80C4, "FONTSET_SST_STD_SCHINESE_GB_AR", "DFHEI5-SONY.ttf"}, - {0x180C9054, "FONTSET_SST_STD_ASIAN_GB_TH", "SSTThai-Roman.otf"}, - {0x180CA044, "FONTSET_SST_STD_SCHINESE_GBUH", "e046323ms.ttf"}, - {0x180CA0C4, "FONTSET_SST_STD_SCHINESE_GBUH_AR", "e046323ms.ttf"}, - {0x180CAC44, "FONTSET_SST_STD_SCHINESE_GBCJK", "n023055ms.ttf"}, - {0x180CACC4, "FONTSET_SST_STD_SCHINESE_GBCJK_AR", "SSTAribStdB24-Regular.ttf"}, - {0x180CB054, "FONTSET_SST_STD_ASIAN_GBUH_TH", "SSTThai-Roman.otf"}, - {0x180CB0D4, "FONTSET_SST_STD_ASIAN_GBUH_TH_AR", "SSTThai-Bold.otf"}, - {0x180CBC54, "FONTSET_SST_STD_ASIAN_GBCJK_TH", "SSTThai-Roman.otf"}, - {0x180CBCD4, "FONTSET_SST_STD_ASIAN_GBCJK_TH_AR", "SSTThai-Bold.otf"}, - {0x18170043, "FONTSET_SST_STD_EUROPEAN_LIGHT_ITALIC", "SST-LightItalic.otf"}, - {0x18170044, "FONTSET_SST_STD_EUROPEAN_ITALIC", "SST-Italic.otf"}, - {0x18170045, "FONTSET_SST_STD_EUROPEAN_MEDIUM_ITALIC", "SST-MediumItalic.otf"}, - {0x18170047, "FONTSET_SST_STD_EUROPEAN_BOLD_ITALIC", "SST-BoldItalic.otf"}, - {0x18170444, "FONTSET_SST_STD_EUROPEAN_JP_ITALIC", "SSTJpPro-Regular.otf"}, - {0x18170447, "FONTSET_SST_STD_EUROPEAN_JP_BOLD_ITALIC", "SSTJpPro-Bold.otf"}, - {0x18370044, "FONTSET_SST_TYPEWRITER_EUROPEAN", "SSTTypewriter-Roman.otf"}, - {0x18370047, "FONTSET_SST_TYPEWRITER_EUROPEAN_BOLD", "SSTTypewriter-Bd.otf"}, - {0x18370444, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP", "SSTTypewriter-Roman.otf"}, - {0x18370447, "FONTSET_SST_TYPEWRITER_EUROPEAN_JP_BOLD", "SSTTypewriter-Bd.otf"}, -}; - -struct FontSetCache { - bool loaded = false; - bool available = false; - std::vector bytes; - std::filesystem::path path; -}; - -static std::mutex g_fontset_cache_mutex; -static std::unordered_map g_fontset_cache; - -static const SystemFontDefinition* FindSystemFontDefinition(u32 font_set_type) { - for (const auto& def : kSystemFontDefinitions) { - if (def.font_set_type == font_set_type) { - return &def; - } - } - return nullptr; -} - -static std::filesystem::path GetSysFontBaseDir() { - std::filesystem::path base = Config::getFontsPath(); - std::error_code ec; - if (base.empty()) { - LOG_ERROR(Lib_Font, "SystemFonts: FontsPath not set"); - return {}; - } - if (std::filesystem::is_directory(base, ec)) { - return base; - } - if (std::filesystem::is_regular_file(base, ec)) { - return base.parent_path(); - } - LOG_ERROR(Lib_Font, "SystemFonts: FontsPath '{}' is not a valid directory or file", - base.string()); - return {}; -} - -static std::filesystem::path ResolveSystemFontPathCandidate(const std::filesystem::path& base_dir, - const std::filesystem::path& filename) { - if (base_dir.empty() || filename.empty()) { - return {}; - } - std::error_code ec; - const auto is_file = [&](const std::filesystem::path& p) -> bool { - return std::filesystem::is_regular_file(p, ec) && !ec; - }; - - const auto direct = base_dir / filename; - if (is_file(direct)) { - return direct; - } - - const auto base_name = base_dir.filename().string(); - if (base_name != "font" && base_name != "font2") { - const auto in_font = base_dir / "font" / filename; - if (is_file(in_font)) { - return in_font; - } - const auto in_font2 = base_dir / "font2" / filename; - if (is_file(in_font2)) { - return in_font2; - } - } - - if (base_name == "font" || base_name == "font2") { - const auto container = base_dir.parent_path(); - const auto sibling = container / ((base_name == "font") ? "font2" : "font") / filename; - if (is_file(sibling)) { - return sibling; - } - } - - return direct; -} - -static std::filesystem::path ResolveSystemFontPath(u32 font_set_type) { - if (const auto* def = FindSystemFontDefinition(font_set_type); def) { - const auto base_dir = GetSysFontBaseDir(); - if (base_dir.empty()) { - return {}; - } - if (def->default_file && *def->default_file) { - return ResolveSystemFontPathCandidate(base_dir, def->default_file); - } - } - LOG_ERROR(Lib_Font, "SystemFonts: unknown font set type=0x{:08X}", font_set_type); - return {}; -} - -static const FontSetCache* EnsureFontSetCache(u32 font_set_type) { - if (font_set_type == 0) { - return nullptr; - } - std::scoped_lock lock(g_fontset_cache_mutex); - auto& cache = g_fontset_cache[font_set_type]; - if (!cache.loaded) { - cache.loaded = true; - const auto path = ResolveSystemFontPath(font_set_type); - if (!path.empty() && LoadFontFile(path, cache.bytes)) { - cache.available = true; - cache.path = path; - } else { - cache.available = false; - LOG_ERROR(Lib_Font, "SystemFonts: failed to load font for set type=0x{:08X} path='{}'", - font_set_type, path.string()); - } - } - return cache.available ? &cache : nullptr; -} - -static bool LoadFontFile(const std::filesystem::path& path, std::vector& out_bytes) { - if (path.empty()) { - return false; - } - auto try_load = [&](const std::filesystem::path& p) -> bool { - out_bytes.clear(); - if (!LoadGuestFileBytes(p, out_bytes) || out_bytes.empty()) { - out_bytes.clear(); - return false; - } - return true; - }; - - if (try_load(path)) { - return true; - } - - std::error_code ec; - const auto parent = path.parent_path(); - const auto parent_name = parent.filename().string(); - const auto file_name = path.filename(); - if (!file_name.empty() && parent_name != "font" && parent_name != "font2") { - const auto cand_font = parent / "font" / file_name; - if (std::filesystem::is_regular_file(cand_font, ec) && !ec) { - if (try_load(cand_font)) { - return true; - } - } - const auto cand_font2 = parent / "font2" / file_name; - if (std::filesystem::is_regular_file(cand_font2, ec) && !ec) { - if (try_load(cand_font2)) { - return true; - } - } - } - - if (!file_name.empty() && (parent_name == "font" || parent_name == "font2")) { - const auto container = parent.parent_path(); - const auto sibling = container / ((parent_name == "font") ? "font2" : "font") / file_name; - if (std::filesystem::is_regular_file(sibling, ec) && !ec) { - return try_load(sibling); - } - } - - return false; +static std::string formatParams( + std::initializer_list params) { + return Libraries::Font::Internal::FormatNamedParams(params); } } // namespace +namespace Internal = Libraries::Font::Internal; + namespace Libraries::Font { -struct FontLibOpaque { - u16 magic; - u16 reserved0; - u32 lock_word; - u32 flags; - u8 reserved1[0x14]; - void* alloc_ctx; - void** alloc_vtbl; - u8 reserved2[0x50]; - void* sys_driver; - void* fontset_registry; - union { - u8 reserved3[0x10]; - std::array sysfonts_ctx_name_overrides; - }; - void* sysfonts_ctx; - void* external_fonts_ctx; - u32* device_cache_buf; -}; -static_assert(sizeof(FontLibOpaque) == 0xB8, "FontLibOpaque size"); -static_assert(offsetof(FontLibOpaque, alloc_ctx) == 0x20, "FontLibOpaque alloc_ctx offset"); -static_assert(offsetof(FontLibOpaque, alloc_vtbl) == 0x28, "FontLibOpaque alloc_vtbl offset"); -static_assert(offsetof(FontLibOpaque, sys_driver) == 0x80, "FontLibOpaque sys_driver offset"); -static_assert(offsetof(FontLibOpaque, fontset_registry) == 0x88, - "FontLibOpaque fontset_registry offset"); -static_assert(offsetof(FontLibOpaque, sysfonts_ctx) == 0xA0, "FontLibOpaque sysfonts_ctx offset"); -static_assert(offsetof(FontLibOpaque, external_fonts_ctx) == 0xA8, - "FontLibOpaque external_fonts_ctx offset"); -static_assert(offsetof(FontLibOpaque, device_cache_buf) == 0xB0, - "FontLibOpaque device_cache_buf offset"); - -#pragma pack(push, 1) -struct FontLibReserved1Main { - /*0x00*/ u32 reserved_0x00; - /*0x04*/ u32 mem_kind; - /*0x08*/ u32 region_size; - /*0x0C*/ void* region_base; -}; -#pragma pack(pop) -static_assert(sizeof(FontLibReserved1Main) == sizeof(((FontLibOpaque*)nullptr)->reserved1), - "FontLibReserved1Main size"); -static_assert(offsetof(FontLibReserved1Main, mem_kind) == 0x04, - "FontLibReserved1Main mem_kind offset"); -static_assert(offsetof(FontLibReserved1Main, region_size) == 0x08, - "FontLibReserved1Main region_size offset"); -static_assert(offsetof(FontLibReserved1Main, region_base) == 0x0C, - "FontLibReserved1Main region_base offset"); - -struct FontLibReserved2Iface { - /*0x00*/ u8 reserved_00[0x20]; - /*0x20*/ std::uintptr_t alloc_fn; - /*0x28*/ std::uintptr_t dealloc_fn; - /*0x30*/ std::uintptr_t realloc_fn; - /*0x38*/ std::uintptr_t calloc_fn; - /*0x40*/ u8 reserved_40[0x10]; -}; -static_assert(sizeof(FontLibReserved2Iface) == sizeof(((FontLibOpaque*)nullptr)->reserved2), - "FontLibReserved2Iface size"); -static_assert(offsetof(FontLibReserved2Iface, alloc_fn) == 0x20, - "FontLibReserved2Iface alloc_fn offset"); -static_assert(offsetof(FontLibReserved2Iface, dealloc_fn) == 0x28, - "FontLibReserved2Iface dealloc_fn offset"); -static_assert(offsetof(FontLibReserved2Iface, realloc_fn) == 0x30, - "FontLibReserved2Iface realloc_fn offset"); -static_assert(offsetof(FontLibReserved2Iface, calloc_fn) == 0x38, - "FontLibReserved2Iface calloc_fn offset"); - -struct FontLibTail { - /*0x00*/ u8 reserved_00[0x04]; - /*0x04*/ u32 workspace_size; - /*0x08*/ void* workspace; - /*0x10*/ u8 reserved_10[0x10]; - /*0x20*/ void* list_head_ptr; - /*0x28*/ OrbisFontHandle list_head; - /*0x30*/ u8 reserved_30[0x18]; -}; -static_assert(offsetof(FontLibTail, workspace_size) == 0x04, "FontLibTail workspace_size offset"); -static_assert(offsetof(FontLibTail, workspace) == 0x08, "FontLibTail workspace offset"); -static_assert(offsetof(FontLibTail, list_head_ptr) == 0x20, "FontLibTail list_head_ptr offset"); -static_assert(offsetof(FontLibTail, list_head) == 0x28, "FontLibTail list_head offset"); -static_assert(sizeof(FontLibTail) == 0x48, "FontLibTail size"); - -#pragma pack(push, 1) -struct FontLibReserved1SysfontTail { - /*0x00*/ u32 reserved_0x00; - /*0x04*/ void* sysfont_desc_ptr; - /*0x0C*/ u64 sysfont_flags; -}; -#pragma pack(pop) -static_assert(sizeof(FontLibReserved1SysfontTail) == sizeof(((FontLibOpaque*)nullptr)->reserved1), - "FontLibReserved1SysfontTail size"); -static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_desc_ptr) == 0x04, - "FontLibReserved1SysfontTail sysfont_desc_ptr offset"); -static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_flags) == 0x0C, - "FontLibReserved1SysfontTail sysfont_flags offset"); - -namespace { -static FontHandleOpaqueNative* GetNativeFont(OrbisFontHandle h); - -static void LogCachedStyleOnce(OrbisFontHandle handle, const FontHandleOpaqueNative& font) { - static std::mutex s_mutex; - static std::unordered_set s_logged; - std::scoped_lock lock(s_mutex); - if (s_logged.find(handle) != s_logged.end()) { - return; - } - s_logged.insert(handle); - - LOG_DEBUG(Lib_Font, - "BindRenderer: cached_style snapshot: hDpi={} vDpi={} scaleUnit={} baseScale={} " - "scalePixelW={} scalePixelH={} effectWeightX={} effectWeightY={} slantRatio={}", - font.cached_style.hDpi, font.cached_style.vDpi, font.cached_style.scaleUnit, - font.cached_style.baseScale, font.cached_style.scalePixelW, - font.cached_style.scalePixelH, font.cached_style.effectWeightX, - font.cached_style.effectWeightY, font.cached_style.slantRatio); -} - -static void LogRenderResultSample(OrbisFontHandle handle, u32 code, - const OrbisFontGlyphMetrics& metrics, - const OrbisFontRenderOutput& result) { - static std::mutex s_mutex; - static std::unordered_map s_counts; - std::scoped_lock lock(s_mutex); - int& count = s_counts[handle]; - if (count >= 5) { - return; - } - ++count; - LOG_DEBUG(Lib_Font, - "RenderSample: handle={} code=U+{:04X} update=[{},{} {}x{}] img=[bx={} by={} adv={} " - "stride={} w={} h={}] metrics=[w={} h={} hbx={} hby={} hadv={}]", - static_cast(handle), code, result.UpdateRect.x, result.UpdateRect.y, - result.UpdateRect.w, result.UpdateRect.h, result.ImageMetrics.bearingX, - result.ImageMetrics.bearingY, result.ImageMetrics.advance, result.ImageMetrics.stride, - result.ImageMetrics.width, result.ImageMetrics.height, metrics.width, metrics.height, - metrics.Horizontal.bearingX, metrics.Horizontal.bearingY, metrics.Horizontal.advance); -} - -static inline u8 CachedStyleCacheFlags(const OrbisFontStyleFrame& cached_style) { - return static_cast(cached_style.cache_flags_and_direction & 0xFFu); -} - -static inline void CachedStyleSetCacheFlags(OrbisFontStyleFrame& cached_style, u8 flags) { - cached_style.cache_flags_and_direction = - (cached_style.cache_flags_and_direction & 0xFFFFFF00u) | static_cast(flags); -} - -static inline void CachedStyleSetDirectionWord(OrbisFontStyleFrame& cached_style, u16 word) { - cached_style.cache_flags_and_direction = - (cached_style.cache_flags_and_direction & 0x0000FFFFu) | (static_cast(word) << 16); -} - -static inline float CachedStyleGetScalar(const OrbisFontStyleFrame& cached_style) { - float value = 0.0f; - std::memcpy(&value, &cached_style.cached_scalar_bits, sizeof(value)); - return value; -} - -static inline void CachedStyleSetScalar(OrbisFontStyleFrame& cached_style, float value) { - std::memcpy(&cached_style.cached_scalar_bits, &value, sizeof(value)); -} +using Internal::AcquireCachedStyleLock; +using Internal::AcquireFontLock; +using Internal::AcquireLibraryLock; +using Internal::CachedStyleCacheFlags; +using Internal::CachedStyleGetScalar; +using Internal::CachedStyleSetCacheFlags; +using Internal::CachedStyleSetDirectionWord; +using Internal::CachedStyleSetScalar; +using Internal::ClampToU16; +using Internal::ClearRenderOutputs; +using Internal::GetLibState; +using Internal::GetNativeFont; +using Internal::kPointsPerInch; +using Internal::kStyleFrameMagic; +using Internal::LoadGuestFileBytes; +using Internal::LogCachedStyleOnce; +using Internal::LogFontOpenError; +using Internal::LogRenderResultSample; +using Internal::ReleaseCachedStyleLock; +using Internal::ReleaseFontLock; +using Internal::ReleaseLibraryLock; +using Internal::RemoveLibState; +using Internal::ResolveGuestPath; +using Internal::ResolveKnownSysFontAlias; constexpr std::uintptr_t kFtRendererCreateFnAddr = 0x0100f6d0u; constexpr std::uintptr_t kFtRendererDestroyFnAddr = 0x0100f790u; -static s32 PS4_SYSV_ABI FtRendererCreate(void* renderer) { - if (!renderer) { - return ORBIS_FONT_ERROR_INVALID_RENDERER; - } - - auto* r = static_cast(renderer); - r->ft_backend.renderer_header_0x10 = static_cast(&r->base.mem_kind); - r->ft_backend.unknown_0x08 = 0; - r->ft_backend.unknown_0x10 = 0; - r->ft_backend.unknown_0x18 = 0; - r->ft_backend.initialized_marker = r; - return ORBIS_OK; -} - -static s32 PS4_SYSV_ABI FtRendererDestroy(void* renderer) { - if (!renderer) { - return ORBIS_FONT_ERROR_INVALID_RENDERER; - } - auto* r = static_cast(renderer); - r->ft_backend.initialized_marker = nullptr; - return ORBIS_OK; -} - -bool AcquireLibraryLock(FontLibOpaque* lib, u32& out_prev_lock_word) { - if (!lib) { - return false; - } - - for (;;) { - const u32 lock_word = lib->lock_word; - if (lib->magic != 0x0F01) { - return false; - } - if (static_cast(lock_word) < 0) { - Libraries::Kernel::sceKernelUsleep(0x1e); - continue; - } - - std::atomic_ref ref(lib->lock_word); - u32 expected = lock_word; - if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, - std::memory_order_acq_rel)) { - out_prev_lock_word = lock_word; - return true; - } - Libraries::Kernel::sceKernelUsleep(0x1e); - } -} - -void ReleaseLibraryLock(FontLibOpaque* lib, u32 prev_lock_word) { - if (!lib) { - return; - } - lib->lock_word = prev_lock_word & 0x7fffffff; -} -static FontHandleOpaqueNative* GetNativeFont(OrbisFontHandle h) { - return h ? reinterpret_cast(h) : nullptr; -} - -static bool AcquireFontLock(FontHandleOpaqueNative* font, u32& out_prev_lock_word) { - if (!font) { - return false; - } - for (;;) { - const u32 lock_word = font->lock_word; - if (font->magic != 0x0F02) { - return false; - } - if (static_cast(lock_word) < 0) { - Libraries::Kernel::sceKernelUsleep(0x1e); - continue; - } - std::atomic_ref ref(font->lock_word); - u32 expected = lock_word; - if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, - std::memory_order_acq_rel)) { - out_prev_lock_word = lock_word; - return true; - } - Libraries::Kernel::sceKernelUsleep(0x1e); - } -} - -static void ReleaseFontLock(FontHandleOpaqueNative* font, u32 prev_lock_word) { - if (!font) { - return; - } - font->lock_word = prev_lock_word & 0x7fffffff; -} - -static bool AcquireCachedStyleLock(FontHandleOpaqueNative* font, u32& out_prev_lock_word) { - if (!font) { - return false; - } - for (;;) { - const u32 lock_word = font->cached_style.cache_lock_word; - if (font->magic != 0x0F02) { - return false; - } - if (static_cast(lock_word) < 0) { - Libraries::Kernel::sceKernelUsleep(0x1e); - continue; - } - std::atomic_ref ref(font->cached_style.cache_lock_word); - u32 expected = lock_word; - if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, - std::memory_order_acq_rel)) { - out_prev_lock_word = lock_word; - return true; - } - Libraries::Kernel::sceKernelUsleep(0x1e); - } -} - -static void ReleaseCachedStyleLock(FontHandleOpaqueNative* font, u32 prev_lock_word) { - if (!font) { - return; - } - font->cached_style.cache_lock_word = prev_lock_word & 0x7fffffff; -} -} // namespace - -namespace {} - -struct OrbisFontRenderer_ { - u16 magic = 0; - u16 reserved0 = 0; -}; -static_assert(offsetof(OrbisFontRenderer_, magic) == 0, "Renderer magic offset"); - s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buffer, u32 size) { LOG_INFO(Lib_Font, "called"); LOG_DEBUG(Lib_Font, "{}", @@ -997,7 +112,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; @@ -1026,10 +141,10 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff Libraries::Kernel::sceKernelUsleep(0x1e); } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; (void)alloc_fn; (void)free_fn; @@ -1260,7 +375,7 @@ s32 PS4_SYSV_ABI sceFontClearDeviceCache(OrbisFontLib library) { return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { return ORBIS_FONT_ERROR_INVALID_LIBRARY; } @@ -1359,10 +474,8 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - using MallocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto malloc_fn = reinterpret_cast(memory->iface->alloc); - const auto free_fn = reinterpret_cast(memory->iface->dealloc); + const auto malloc_fn = reinterpret_cast(memory->iface->alloc); + const auto free_fn = reinterpret_cast(memory->iface->dealloc); if (!malloc_fn || !free_fn) { LOG_ERROR(Lib_Font, "INVALID_MEMORY"); return ORBIS_FONT_ERROR_INVALID_MEMORY; @@ -1384,19 +497,19 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, auto* lib_bytes = static_cast(lib_mem); auto clear_20 = [&](std::size_t offset) { std::memset(lib_bytes + offset, 0, 0x20); }; clear_20(0x00); - clear_20(sizeof(FontLibOpaque) + offsetof(FontLibOpaque, alloc_vtbl)); - clear_20(sizeof(FontLibOpaque) + offsetof(FontLibOpaque, flags)); - clear_20(offsetof(FontLibOpaque, sysfonts_ctx)); - clear_20(offsetof(FontLibOpaque, sys_driver)); - clear_20(offsetof(FontLibOpaque, reserved2) + 0x30); - clear_20(offsetof(FontLibOpaque, reserved2) + 0x10); - clear_20(offsetof(FontLibOpaque, alloc_ctx)); - clear_20(offsetof(FontLibOpaque, reserved3) + 0x0C); - clear_20(offsetof(FontLibOpaque, fontset_registry)); + clear_20(sizeof(Internal::FontLibOpaque) + offsetof(Internal::FontLibOpaque, alloc_vtbl)); + clear_20(sizeof(Internal::FontLibOpaque) + offsetof(Internal::FontLibOpaque, flags)); + clear_20(offsetof(Internal::FontLibOpaque, sysfonts_ctx)); + clear_20(offsetof(Internal::FontLibOpaque, sys_driver)); + clear_20(offsetof(Internal::FontLibOpaque, reserved2) + 0x30); + clear_20(offsetof(Internal::FontLibOpaque, reserved2) + 0x10); + clear_20(offsetof(Internal::FontLibOpaque, alloc_ctx)); + clear_20(offsetof(Internal::FontLibOpaque, reserved3) + 0x0C); + clear_20(offsetof(Internal::FontLibOpaque, fontset_registry)); - auto* lib = static_cast(lib_mem); + auto* lib = static_cast(lib_mem); - auto* reserved1 = reinterpret_cast(lib->reserved1); + auto* reserved1 = reinterpret_cast(lib->reserved1); if (reserved1) { reserved1->mem_kind = 0x0F00; reserved1->region_size = memory->region_size; @@ -1406,7 +519,7 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, lib->alloc_ctx = memory->mspace_handle; lib->alloc_vtbl = reinterpret_cast(const_cast(memory->iface)); - auto* reserved2 = reinterpret_cast(lib->reserved2); + auto* reserved2 = reinterpret_cast(lib->reserved2); if (reserved2) { reserved2->alloc_fn = reinterpret_cast(memory->iface->alloc); reserved2->dealloc_fn = reinterpret_cast(memory->iface->dealloc); @@ -1416,7 +529,7 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, lib->sys_driver = const_cast(create_params); - auto* tail = reinterpret_cast(lib + 1); + auto* tail = reinterpret_cast(lib + 1); if (tail) { tail->workspace_size = 0x4000; tail->workspace = mspace; @@ -1476,13 +589,13 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, *pLibrary = lib; auto& state = GetLibState(lib); - state = LibraryState{}; + state = Internal::LibraryState{}; state.backing_memory = memory; state.create_params = create_params; state.edition = edition; state.alloc_ctx = lib->alloc_ctx; - state.alloc_fn = reinterpret_cast(lib->alloc_vtbl[0]); - state.free_fn = reinterpret_cast(lib->alloc_vtbl[1]); + state.alloc_fn = reinterpret_cast(lib->alloc_vtbl[0]); + state.free_fn = reinterpret_cast(lib->alloc_vtbl[1]); state.owned_mspace = mspace; state.owned_mspace_size = 0x4000; return ORBIS_OK; @@ -1502,16 +615,16 @@ s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { " library_handle_ptr={}\n" " library_handle={}\n", static_cast(pLibrary), static_cast(lib)); - auto* native = static_cast(lib); + auto* native = static_cast(lib); if (native->magic != 0x0F01) { return ORBIS_FONT_ERROR_INVALID_LIBRARY; } RemoveLibState(lib); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto free_fn = - native->alloc_vtbl ? reinterpret_cast(native->alloc_vtbl[1]) : nullptr; + const auto free_fn = native->alloc_vtbl + ? reinterpret_cast(native->alloc_vtbl[1]) + : nullptr; if (free_fn) { Core::ExecuteGuest(free_fn, native->alloc_ctx, native); } else { @@ -1559,10 +672,8 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, } else { rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; if (pRenderer) { - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = reinterpret_cast(memory->iface->alloc); - const auto free_fn = reinterpret_cast(memory->iface->dealloc); + const auto alloc_fn = reinterpret_cast(memory->iface->alloc); + const auto free_fn = reinterpret_cast(memory->iface->dealloc); const auto* selection = static_cast( @@ -1601,10 +712,10 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, const uintptr_t create_fn_ptr = selection ? selection->create_fn : static_cast(0); - using CreateFn = s32(PS4_SYSV_ABI*)(void* renderer); - CreateFn create_fn = reinterpret_cast(create_fn_ptr); + auto create_fn = + reinterpret_cast(create_fn_ptr); if (create_fn_ptr == kFtRendererCreateFnAddr) { - create_fn = &FtRendererCreate; + create_fn = &Libraries::FontFt::Internal::FtRendererCreate; } if (!create_fn) { rc = ORBIS_FONT_ERROR_FATAL; @@ -1760,18 +871,16 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { selection_value); const auto destroy_fn_ptr = selection ? selection->destroy_fn : static_cast(0); - using DestroyFn = s32(PS4_SYSV_ABI*)(void* renderer); - DestroyFn destroy_fn = reinterpret_cast(destroy_fn_ptr); + auto destroy_fn = reinterpret_cast(destroy_fn_ptr); if (destroy_fn_ptr == kFtRendererDestroyFnAddr) { - destroy_fn = &FtRendererDestroy; + destroy_fn = &Libraries::FontFt::Internal::FtRendererDestroy; } rc = destroy_fn ? Core::ExecuteGuest(destroy_fn, renderer) : ORBIS_FONT_ERROR_FATAL; } renderer->selection = nullptr; - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto free_fn = reinterpret_cast(renderer->free_fn); + const auto free_fn = reinterpret_cast(renderer->free_fn); void* alloc_ctx = renderer->alloc_ctx; void* workspace = renderer->workspace; if (workspace) { @@ -2860,8 +1969,9 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { if (static_cast(mem_desc->attr_bits & 0xFF) < 0) { if (mem_desc->iface && mem_desc->iface->mspace_destroy) { - using DestroyFn = void(PS4_SYSV_ABI*)(void* parent, void* mspace); - const auto destroy_fn = reinterpret_cast(mem_desc->iface->mspace_destroy); + const auto destroy_fn = + reinterpret_cast( + mem_desc->iface->mspace_destroy); if (destroy_fn) { Core::ExecuteGuest(destroy_fn, mem_desc->some_ctx2, mem_desc->mspace_handle); } @@ -2872,9 +1982,8 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { if (mem_desc->on_destroy) { mem_desc->mem_kind = 0; - using DestroyFn = - void(PS4_SYSV_ABI*)(OrbisFontMem * fontMemory, void* object, void* destroyArg); - const auto destroy_fn = reinterpret_cast(mem_desc->on_destroy); + const auto destroy_fn = reinterpret_cast(mem_desc->on_destroy); Core::ExecuteGuest(destroy_fn, mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); return ORBIS_OK; } @@ -2909,7 +2018,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { *out_handle = nullptr; LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); @@ -2937,10 +2046,10 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat const u32 sub_font_index = open_detail ? open_detail->subfont_index : 0u; const s32 unique_id = open_detail ? open_detail->unique_id : -1; - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; if (!alloc_fn || !free_fn) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); @@ -3126,7 +2235,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat if ((entry_lock_word & 0x40000000u) == 0) { auto* tail = lib + 1; auto* tail_reserved1 = - tail ? reinterpret_cast(tail->reserved1) : nullptr; + tail ? reinterpret_cast(tail->reserved1) + : nullptr; const u32 lib_flags = lib->flags; if (mode_low == 2) { if ((lib_flags & 0x100000u) != 0) { @@ -3181,7 +2291,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat if (rc != ORBIS_OK) { auto* tail = lib + 1; auto* tail_reserved1 = - tail ? reinterpret_cast(tail->reserved1) + tail ? reinterpret_cast(tail->reserved1) : nullptr; const u32 lib_flags = lib->flags; if (mode_low == 2) { @@ -3304,7 +2414,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat const auto* handle_native = GetNativeFont(handle); if (handle_native && (handle_native->flags & 0x10) != 0) { - auto* tail = reinterpret_cast(lib + 1); + auto* tail = reinterpret_cast(lib + 1); auto* list_lock = &tail->list_head_ptr; void* list_ptr = nullptr; for (;;) { @@ -3419,7 +2529,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa auto release_src_lock = [&] { ReleaseFontLock(src, prev_font_lock); }; - auto* lib = static_cast(src->library); + auto* lib = static_cast(src->library); if (!lib || lib->magic != 0x0F01) { release_src_lock(); if (pFontHandle) { @@ -3429,10 +2539,10 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; const u32 open_mode_low = static_cast(src->flags) & 0x0Fu; const u32 src_sub_font_index = src->open_info.sub_font_index; @@ -3520,11 +2630,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - struct IncRecord { - u32 entry_index; - Internal::FontObj* node; - }; - std::vector increments; + std::vector> increments; increments.reserve(entry_count); auto lock_word_ptr_for_mode = [&](Internal::FontCtxEntry* entry, u32 m) -> u32* { @@ -3625,11 +2731,11 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa if (rc != ORBIS_OK) { for (const auto& inc : increments) { - if (inc.entry_index >= max_entries || !inc.node) { + if (inc.first >= max_entries || !inc.second) { continue; } auto* entry = reinterpret_cast( - entries_base + inc.entry_index * sizeof(Internal::FontCtxEntry)); + entries_base + inc.first * sizeof(Internal::FontCtxEntry)); auto* lock_word_ptr = lock_word_ptr_for_mode(entry, open_mode_low); if (!lock_word_ptr) { continue; @@ -3649,7 +2755,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa Libraries::Kernel::sceKernelUsleep(0x1e); } if ((prev_lock & 0x0FFFFFFFu) != 0) { - inc.node->refcount--; + inc.second->refcount--; *lock_word_ptr = (prev_lock - 1) & 0x7fffffffu; } else { *lock_word_ptr = prev_lock & 0x7fffffffu; @@ -3681,7 +2787,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa } if (owned) { - auto* tail = reinterpret_cast(lib + 1); + auto* tail = reinterpret_cast(lib + 1); auto* list_lock = tail ? &tail->list_head_ptr : nullptr; void* list_ptr = nullptr; for (;;) { @@ -3797,7 +2903,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd return rc; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { if (pFontHandle) { *pFontHandle = nullptr; @@ -3828,10 +2934,10 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; if (!alloc_fn || !free_fn) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); @@ -4038,7 +3144,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd if (need_open) { auto* tail = lib + 1; auto* tail_reserved1 = - tail ? reinterpret_cast(tail->reserved1) : nullptr; + tail ? reinterpret_cast(tail->reserved1) + : nullptr; const u32 lib_flags = lib->flags; if ((lib_flags & 0x200000u) != 0) { tail_reserved1->sysfont_flags |= 1; @@ -4115,7 +3222,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd const auto* handle_native = GetNativeFont(handle); if (handle_native && (handle_native->flags & 0x10) != 0) { - auto* tail = reinterpret_cast(lib + 1); + auto* tail = reinterpret_cast(lib + 1); auto* list_lock = &tail->list_head_ptr; void* list_ptr = nullptr; for (;;) { @@ -4200,7 +3307,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o })); { - auto* lib_local = static_cast(library); + auto* lib_local = static_cast(library); if (!lib_local || lib_local->magic != 0x0F01) { if (pFontHandle) { *pFontHandle = nullptr; @@ -4235,12 +3342,13 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_LIBRARY); } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); const auto alloc_fn = - lib_local->alloc_vtbl ? reinterpret_cast(lib_local->alloc_vtbl[0]) : nullptr; - const auto free_fn = - lib_local->alloc_vtbl ? reinterpret_cast(lib_local->alloc_vtbl[1]) : nullptr; + lib_local->alloc_vtbl + ? reinterpret_cast(lib_local->alloc_vtbl[0]) + : nullptr; + const auto free_fn = lib_local->alloc_vtbl + ? reinterpret_cast(lib_local->alloc_vtbl[1]) + : nullptr; if (!alloc_fn || !free_fn) { LOG_ERROR(Lib_Font, "INVALID_MEMORY"); return release_library_and_clear_out(ORBIS_FONT_ERROR_INVALID_MEMORY); @@ -4956,7 +4064,8 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o used_font_obj = font_obj; auto* tail = lib_local + 1; auto* tail_reserved1 = - tail ? reinterpret_cast(tail->reserved1) + tail ? reinterpret_cast( + tail->reserved1) : nullptr; if (tail_reserved1) { const u32 lib_flags = lib_local->flags; @@ -5009,7 +4118,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o used_font_obj = font_obj; auto* tail = lib_local + 1; auto* tail_reserved1 = - tail ? reinterpret_cast( + tail ? reinterpret_cast( tail->reserved1) : nullptr; if (tail_reserved1) { @@ -5193,7 +4302,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } LOG_INFO(Lib_Font, "OpenFontSet: stage=published_handle"); if ((h->flags & 0x10) != 0) { - auto* tail = reinterpret_cast(lib_local + 1); + auto* tail = reinterpret_cast(lib_local + 1); auto* list_lock = &tail->list_head_ptr; void* list_ptr = nullptr; for (;;) { @@ -5372,49 +4481,30 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 cod if (!surf || (surf->styleFlag & 0x1) == 0) { (void)cache_cachedstyle_baseline(); } else { - struct RenderSurfaceSystemUse { - OrbisFontStyleFrame* styleframe; - float catchedScale; - std::uint8_t padding[88 - sizeof(OrbisFontStyleFrame*) - sizeof(float)]; - }; - static_assert(sizeof(RenderSurfaceSystemUse) == - sizeof(((OrbisFontRenderSurface*)nullptr)->reserved_q)); - - auto* sys = reinterpret_cast(surf->reserved_q); + auto* sys = reinterpret_cast(surf->reserved_q); auto* styleframe = sys ? sys->styleframe : nullptr; if (!styleframe || (styleframe->flags1 & 1) == 0) { (void)cache_cachedstyle_baseline(); } else { - struct StyleStateBlock { - /*0x00*/ u32 dpi_x; - /*0x04*/ u32 dpi_y; - /*0x08*/ u32 vDpi_flag; - /*0x0C*/ u32 reserved0c; - /*0x10*/ float scale_x; - /*0x14*/ float scale_y; - /*0x18*/ float effect_wx; - /*0x1C*/ float effect_wy; - /*0x20*/ float slant; - }; - StyleStateBlock state{}; + Internal::StyleStateBlock state{}; state.dpi_x = styleframe->hDpi; state.dpi_y = styleframe->vDpi; - state.vDpi_flag = 0; - state.reserved0c = 0; - state.effect_wx = 0.0f; - state.effect_wy = 0.0f; - state.slant = 0.0f; + state.scale_unit = 0; + state.reserved_0x0c = 0; + state.effect_weight_x = 0.0f; + state.effect_weight_y = 0.0f; + state.slant_ratio = 0.0f; if ((styleframe->flags1 & 2) != 0) { - state.slant = styleframe->slantRatio; + state.slant_ratio = styleframe->slantRatio; } if ((styleframe->flags1 & 4) != 0) { - state.effect_wx = styleframe->effectWeightX; - state.effect_wy = styleframe->effectWeightY; + state.effect_weight_x = styleframe->effectWeightX; + state.effect_weight_y = styleframe->effectWeightY; } - pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_x, - &state.scale_y); + pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_w, + &state.scale_h); Internal::HorizontalLayoutBlocks layout_words{}; if (pre_rc == ORBIS_OK) { pre_rc = Internal::ComputeHorizontalLayoutBlocksTyped(fontHandle, &state, @@ -5483,48 +4573,29 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(OrbisFontHandle fontHandle, u32 cod if (!surf || (surf->styleFlag & 0x1) == 0) { (void)cache_cachedstyle_scalar(); } else { - struct RenderSurfaceSystemUse { - OrbisFontStyleFrame* styleframe; - float catchedScale; - std::uint8_t padding[88 - sizeof(OrbisFontStyleFrame*) - sizeof(float)]; - }; - static_assert(sizeof(RenderSurfaceSystemUse) == - sizeof(((OrbisFontRenderSurface*)nullptr)->reserved_q)); - - auto* sys = reinterpret_cast(surf->reserved_q); + auto* sys = reinterpret_cast(surf->reserved_q); auto* styleframe = sys ? sys->styleframe : nullptr; if (!styleframe || (styleframe->flags1 & 1) == 0) { (void)cache_cachedstyle_scalar(); } else { - struct StyleStateBlock { - /*0x00*/ u32 dpi_x; - /*0x04*/ u32 dpi_y; - /*0x08*/ u32 vDpi_flag; - /*0x0C*/ u32 reserved0c; - /*0x10*/ float scale_x; - /*0x14*/ float scale_y; - /*0x18*/ float effect_wx; - /*0x1C*/ float effect_wy; - /*0x20*/ float slant; - }; - StyleStateBlock state{}; + Internal::StyleStateBlock state{}; state.dpi_x = styleframe->hDpi; state.dpi_y = styleframe->vDpi; - state.vDpi_flag = 0; - state.reserved0c = 0; - state.effect_wx = 0.0f; - state.effect_wy = 0.0f; - state.slant = 0.0f; + state.scale_unit = 0; + state.reserved_0x0c = 0; + state.effect_weight_x = 0.0f; + state.effect_weight_y = 0.0f; + state.slant_ratio = 0.0f; if ((styleframe->flags1 & 2) != 0) { - state.slant = styleframe->slantRatio; + state.slant_ratio = styleframe->slantRatio; } if ((styleframe->flags1 & 4) != 0) { - state.effect_wx = styleframe->effectWeightX; - state.effect_wy = styleframe->effectWeightY; + state.effect_weight_x = styleframe->effectWeightX; + state.effect_weight_y = styleframe->effectWeightY; } - pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_x, - &state.scale_y); + pre_rc = Internal::StyleStateGetScalePixel(&styleframe->hDpi, &state.scale_w, + &state.scale_h); Internal::VerticalLayoutBlocks layout_words{}; if (pre_rc == ORBIS_OK) { pre_rc = Internal::ComputeVerticalLayoutBlocksTyped(fontHandle, &state, @@ -5762,11 +4833,8 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(OrbisFontRenderer fontRen return ORBIS_FONT_ERROR_INVALID_PARAMETER; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - - const auto alloc_fn = reinterpret_cast(renderer->alloc_fn); - const auto free_fn = reinterpret_cast(renderer->free_fn); + const auto alloc_fn = reinterpret_cast(renderer->alloc_fn); + const auto free_fn = reinterpret_cast(renderer->free_fn); if (!alloc_fn || !free_fn || !renderer->alloc_ctx) { LOG_ERROR(Lib_Font, "INVALID_MEMORY"); return ORBIS_FONT_ERROR_INVALID_MEMORY; @@ -6606,7 +5674,7 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; @@ -6617,10 +5685,10 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; if (lib->external_fonts_ctx) { ReleaseLibraryLock(lib, prev_lock_word); @@ -6699,7 +5767,7 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - auto* lib = static_cast(library); + auto* lib = static_cast(library); if (lib->magic != 0x0F01) { LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; @@ -6710,10 +5778,10 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { return ORBIS_FONT_ERROR_INVALID_LIBRARY; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; - const auto free_fn = lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; + const auto alloc_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[0]) : nullptr; + const auto free_fn = + lib->alloc_vtbl ? reinterpret_cast(lib->alloc_vtbl[1]) : nullptr; if (lib->sysfonts_ctx) { ReleaseLibraryLock(lib, prev_lock_word); diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index b58e1269f..51be24564 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -11,6 +11,7 @@ #include FT_TRUETYPE_TABLES_H #include "core/libraries/font/fontft_internal.h" +#include "core/libraries/kernel/kernel.h" namespace Libraries::Font::Internal { @@ -44,6 +45,15 @@ std::uint8_t g_sysfonts_ctx_stub{}; std::uint8_t g_external_fonts_ctx_stub{}; u32 g_device_cache_stub{}; +std::string FormatNamedParams(std::initializer_list params) { + fmt::memory_buffer buffer; + fmt::format_to(std::back_inserter(buffer), "params:\n"); + for (const auto& p : params) { + fmt::format_to(std::back_inserter(buffer), "{}: {}\n", p.name, p.value); + } + return fmt::to_string(buffer); +} + bool HasSfntTables(const std::vector& bytes); FontState* TryGetState(Libraries::Font::OrbisFontHandle h); std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHandle handle, @@ -104,7 +114,7 @@ void DestroyFreeTypeFace(FT_Face& face) { namespace { -static std::optional ResolveKnownSysFontAlias( +static std::optional ResolveKnownSysFontAliasImpl( const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { const auto resolve_existing = [&](std::string_view filename) -> std::optional { @@ -217,6 +227,11 @@ static std::shared_ptr> GetCachedFontBytes( } // namespace +std::optional ResolveKnownSysFontAlias( + const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename) { + return ResolveKnownSysFontAliasImpl(sysfonts_dir, ps4_filename); +} + FontState::~FontState() { DestroyFreeTypeFace(ext_ft_face); for (auto& fb : system_fallback_faces) { @@ -362,18 +377,7 @@ bool BuildTrueOutline(GeneratedGlyph& gg) { } if (const std::uint8_t* range_rec = FindSysFontRangeRecord(mapped_font_id, resolved_code)) { - struct SysFontRangeRecordLocal { - /*0x00*/ u32 start; - /*0x04*/ u32 end; - /*0x08*/ s16 shift_x; - /*0x0A*/ s16 shift_y; - /*0x0C*/ float scale_mul; - /*0x10*/ u32 reserved_0x10; - /*0x14*/ u32 reserved_0x14; - /*0x18*/ u32 reserved_0x18; - }; - static_assert(sizeof(SysFontRangeRecordLocal) == 0x1C); - SysFontRangeRecordLocal rec{}; + SysFontRangeRecord rec{}; std::memcpy(&rec, range_rec, sizeof(rec)); shift_x_units = static_cast(rec.shift_x); shift_y_units += static_cast(rec.shift_y); @@ -518,14 +522,102 @@ LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib) { return g_library_state[lib]; } -void RemoveLibState(Libraries::Font::OrbisFontLib lib) { - std::scoped_lock lock(g_state_mutex); - if (auto it = g_library_state.find(lib); it != g_library_state.end()) { - if (it->second.owned_device_cache) { - delete[] static_cast(it->second.owned_device_cache); +bool AcquireLibraryLock(FontLibOpaque* lib, u32& out_prev_lock_word) { + if (!lib) { + return false; + } + + for (;;) { + const u32 lock_word = lib->lock_word; + if (lib->magic != 0x0F01) { + return false; } + if (static_cast(lock_word) < 0) { + Libraries::Kernel::sceKernelUsleep(0x1e); + continue; + } + + std::atomic_ref ref(lib->lock_word); + u32 expected = lock_word; + if (ref.compare_exchange_weak(expected, lock_word | 0x80000000u, + std::memory_order_acq_rel)) { + out_prev_lock_word = lock_word; + return true; + } + Libraries::Kernel::sceKernelUsleep(0x1e); + } +} + +void ReleaseLibraryLock(FontLibOpaque* lib, u32 prev_lock_word) { + if (!lib) { + return; + } + lib->lock_word = prev_lock_word & 0x7fffffff; +} + +void RemoveLibState(Libraries::Font::OrbisFontLib lib) { + LibraryState state{}; + { + std::scoped_lock lock(g_state_mutex); + auto it = g_library_state.find(lib); + if (it == g_library_state.end()) { + return; + } + state = std::move(it->second); g_library_state.erase(it); } + + const auto free_fn = state.free_fn; + const auto alloc_ctx = state.alloc_ctx; + auto free_owned = [&](void* p) { + if (!p) { + return; + } + if (free_fn) { + const auto free_fn_guest = + reinterpret_cast(free_fn); + Core::ExecuteGuest(free_fn_guest, alloc_ctx, p); + return; + } + std::free(p); + }; + + free_owned(state.owned_device_cache); + free_owned(state.owned_external_fonts_ctx); + free_owned(state.owned_sysfonts_ctx); + free_owned(state.owned_mspace); +} + +void LogFontOpenError(s32 rc) { + switch (rc) { + case ORBIS_FONT_ERROR_INVALID_LIBRARY: + LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); + break; + case ORBIS_FONT_ERROR_INVALID_PARAMETER: + LOG_ERROR(Lib_Font, "INVALID_PARAMETER"); + break; + case ORBIS_FONT_ERROR_ALLOCATION_FAILED: + LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); + break; + case ORBIS_FONT_ERROR_FS_OPEN_FAILED: + LOG_ERROR(Lib_Font, "FS_OPEN_FAILED"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FUNCTION"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FORMAT"); + break; + case ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET: + LOG_ERROR(Lib_Font, "NO_SUPPORT_FONTSET"); + break; + case ORBIS_FONT_ERROR_FONT_OPEN_MAX: + LOG_ERROR(Lib_Font, "FONT_OPEN_MAX"); + break; + default: + LOG_ERROR(Lib_Font, "FAILED"); + break; + } } void LogExternalFormatSupport(u32 formats_mask) { @@ -1759,4 +1851,68 @@ std::string ReportSystemFaceRequest(FontState& st, Libraries::Font::OrbisFontHan } return {}; } + +void LogCachedStyleOnce(Libraries::Font::OrbisFontHandle handle, + const Libraries::Font::FontHandleOpaqueNative& font) { + static std::mutex s_mutex; + static std::unordered_set s_logged; + std::scoped_lock lock(s_mutex); + if (s_logged.find(handle) != s_logged.end()) { + return; + } + s_logged.insert(handle); + + LOG_DEBUG(Lib_Font, + "BindRenderer: cached_style snapshot: hDpi={} vDpi={} scaleUnit={} baseScale={} " + "scalePixelW={} scalePixelH={} effectWeightX={} effectWeightY={} slantRatio={}", + font.cached_style.hDpi, font.cached_style.vDpi, font.cached_style.scaleUnit, + font.cached_style.baseScale, font.cached_style.scalePixelW, + font.cached_style.scalePixelH, font.cached_style.effectWeightX, + font.cached_style.effectWeightY, font.cached_style.slantRatio); +} + +void LogRenderResultSample(Libraries::Font::OrbisFontHandle handle, u32 code, + const Libraries::Font::OrbisFontGlyphMetrics& metrics, + const Libraries::Font::OrbisFontRenderOutput& result) { + static std::mutex s_mutex; + static std::unordered_map s_counts; + std::scoped_lock lock(s_mutex); + int& count = s_counts[handle]; + if (count >= 5) { + return; + } + ++count; + LOG_DEBUG(Lib_Font, + "RenderSample: handle={} code=U+{:04X} update=[{},{} {}x{}] img=[bx={} by={} adv={} " + "stride={} w={} h={}] metrics=[w={} h={} hbx={} hby={} hadv={}]", + static_cast(handle), code, result.UpdateRect.x, result.UpdateRect.y, + result.UpdateRect.w, result.UpdateRect.h, result.ImageMetrics.bearingX, + result.ImageMetrics.bearingY, result.ImageMetrics.advance, result.ImageMetrics.stride, + result.ImageMetrics.width, result.ImageMetrics.height, metrics.width, metrics.height, + metrics.Horizontal.bearingX, metrics.Horizontal.bearingY, metrics.Horizontal.advance); +} + +u8 CachedStyleCacheFlags(const Libraries::Font::OrbisFontStyleFrame& cached_style) { + return static_cast(cached_style.cache_flags_and_direction & 0xFFu); +} + +void CachedStyleSetCacheFlags(Libraries::Font::OrbisFontStyleFrame& cached_style, u8 flags) { + cached_style.cache_flags_and_direction = + (cached_style.cache_flags_and_direction & 0xFFFFFF00u) | static_cast(flags); +} + +void CachedStyleSetDirectionWord(Libraries::Font::OrbisFontStyleFrame& cached_style, u16 word) { + cached_style.cache_flags_and_direction = + (cached_style.cache_flags_and_direction & 0x0000FFFFu) | (static_cast(word) << 16); +} + +float CachedStyleGetScalar(const Libraries::Font::OrbisFontStyleFrame& cached_style) { + float value = 0.0f; + std::memcpy(&value, &cached_style.cached_scalar_bits, sizeof(value)); + return value; +} + +void CachedStyleSetScalar(Libraries::Font::OrbisFontStyleFrame& cached_style, float value) { + std::memcpy(&cached_style.cached_scalar_bits, &value, sizeof(value)); +} } // namespace Libraries::Font::Internal diff --git a/src/core/libraries/font/font_internal.h b/src/core/libraries/font/font_internal.h index fe1ea225c..c72e21333 100644 --- a/src/core/libraries/font/font_internal.h +++ b/src/core/libraries/font/font_internal.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,10 @@ #include "core/libraries/libs.h" #include "font_error.h" +namespace Libraries::Font { +struct FontHandleOpaqueNative; +} + namespace Libraries::Font::Internal { struct FontLibOpaque; @@ -69,6 +74,18 @@ std::string DescribeValue(const T& value) { } } +struct NamedParam { + std::string_view name; + std::string value; +}; + +template +NamedParam Param(std::string_view name, const T& value) { + return NamedParam{name, DescribeValue(value)}; +} + +std::string FormatNamedParams(std::initializer_list params); + struct ParamRecord { std::string name; std::string initial; @@ -263,6 +280,27 @@ struct FontState { ~FontState(); }; +using FontAllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); +using FontFreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); + +struct FtExternalFaceObj { + u32 refcount; + u32 reserved04; + u32 reserved08; + u32 sub_font_index; + u64 reserved10; + FtExternalFaceObj* next; + u64 reserved20; + u64 reserved28; + FT_Face face; + const u8* font_data; + u32 font_size; + u32 sfnt_base; +}; +static_assert(offsetof(FtExternalFaceObj, sub_font_index) == 0x0C); +static_assert(offsetof(FtExternalFaceObj, next) == 0x18); +static_assert(offsetof(FtExternalFaceObj, face) == 0x30); + struct LibraryState { bool support_system = false; bool support_external = false; @@ -271,6 +309,15 @@ struct LibraryState { const Libraries::Font::OrbisFontMem* backing_memory = nullptr; Libraries::Font::OrbisFontLibCreateParams create_params = nullptr; u64 edition = 0; + void* alloc_ctx = nullptr; + FontAllocFn alloc_fn = nullptr; + FontFreeFn free_fn = nullptr; + void* owned_mspace = nullptr; + u32 owned_mspace_size = 0; + void* owned_sysfonts_ctx = nullptr; + u32 owned_sysfonts_ctx_size = 0; + void* owned_external_fonts_ctx = nullptr; + u32 owned_external_fonts_ctx_size = 0; FontLibOpaque* native = nullptr; void* owned_device_cache = nullptr; u32 owned_device_cache_size = 0; @@ -601,6 +648,71 @@ static_assert(offsetof(FontLibOpaque, external_fonts_ctx) == 0xA8, static_assert(offsetof(FontLibOpaque, device_cache_buf) == 0xB0, "FontLibOpaque device_cache_buf offset"); +#pragma pack(push, 1) +struct FontLibReserved1Main { + /*0x00*/ u32 reserved_0x00; + /*0x04*/ u32 mem_kind; + /*0x08*/ u32 region_size; + /*0x0C*/ void* region_base; +}; +#pragma pack(pop) +static_assert(sizeof(FontLibReserved1Main) == sizeof(((FontLibOpaque*)nullptr)->reserved1), + "FontLibReserved1Main size"); +static_assert(offsetof(FontLibReserved1Main, mem_kind) == 0x04, + "FontLibReserved1Main mem_kind offset"); +static_assert(offsetof(FontLibReserved1Main, region_size) == 0x08, + "FontLibReserved1Main region_size offset"); +static_assert(offsetof(FontLibReserved1Main, region_base) == 0x0C, + "FontLibReserved1Main region_base offset"); + +struct FontLibReserved2Iface { + /*0x00*/ u8 reserved_00[0x20]; + /*0x20*/ std::uintptr_t alloc_fn; + /*0x28*/ std::uintptr_t dealloc_fn; + /*0x30*/ std::uintptr_t realloc_fn; + /*0x38*/ std::uintptr_t calloc_fn; + /*0x40*/ u8 reserved_40[0x10]; +}; +static_assert(sizeof(FontLibReserved2Iface) == sizeof(((FontLibOpaque*)nullptr)->reserved2), + "FontLibReserved2Iface size"); +static_assert(offsetof(FontLibReserved2Iface, alloc_fn) == 0x20, + "FontLibReserved2Iface alloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, dealloc_fn) == 0x28, + "FontLibReserved2Iface dealloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, realloc_fn) == 0x30, + "FontLibReserved2Iface realloc_fn offset"); +static_assert(offsetof(FontLibReserved2Iface, calloc_fn) == 0x38, + "FontLibReserved2Iface calloc_fn offset"); + +struct FontLibTail { + /*0x00*/ u8 reserved_00[0x04]; + /*0x04*/ u32 workspace_size; + /*0x08*/ void* workspace; + /*0x10*/ u8 reserved_10[0x10]; + /*0x20*/ void* list_head_ptr; + /*0x28*/ Libraries::Font::OrbisFontHandle list_head; + /*0x30*/ u8 reserved_30[0x18]; +}; +static_assert(offsetof(FontLibTail, workspace_size) == 0x04, "FontLibTail workspace_size offset"); +static_assert(offsetof(FontLibTail, workspace) == 0x08, "FontLibTail workspace offset"); +static_assert(offsetof(FontLibTail, list_head_ptr) == 0x20, "FontLibTail list_head_ptr offset"); +static_assert(offsetof(FontLibTail, list_head) == 0x28, "FontLibTail list_head offset"); +static_assert(sizeof(FontLibTail) == 0x48, "FontLibTail size"); + +#pragma pack(push, 1) +struct FontLibReserved1SysfontTail { + /*0x00*/ u32 reserved_0x00; + /*0x04*/ void* sysfont_desc_ptr; + /*0x0C*/ u64 sysfont_flags; +}; +#pragma pack(pop) +static_assert(sizeof(FontLibReserved1SysfontTail) == sizeof(((FontLibOpaque*)nullptr)->reserved1), + "FontLibReserved1SysfontTail size"); +static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_desc_ptr) == 0x04, + "FontLibReserved1SysfontTail sysfont_desc_ptr offset"); +static_assert(offsetof(FontLibReserved1SysfontTail, sysfont_flags) == 0x0C, + "FontLibReserved1SysfontTail sysfont_flags offset"); + struct RendererOpaque { /*0x00*/ u16 magic; /*0x02*/ u16 reserved02; @@ -705,9 +817,14 @@ FontState* TryGetState(Libraries::Font::OrbisFontHandle h); void RemoveState(Libraries::Font::OrbisFontHandle h); LibraryState& GetLibState(Libraries::Font::OrbisFontLib lib); void RemoveLibState(Libraries::Font::OrbisFontLib lib); +bool AcquireLibraryLock(FontLibOpaque* lib, u32& out_prev_lock_word); +void ReleaseLibraryLock(FontLibOpaque* lib, u32 prev_lock_word); FT_Face CreateFreeTypeFaceFromBytes(const unsigned char* data, std::size_t size, u32 subfont_index); void DestroyFreeTypeFace(FT_Face& face); +void LogFontOpenError(s32 rc); void LogExternalFormatSupport(u32 formats_mask); +std::optional ResolveKnownSysFontAlias( + const std::filesystem::path& sysfonts_dir, std::string_view ps4_filename); void LogFontOpenParams(const Libraries::Font::OrbisFontOpenParams* params); std::filesystem::path ResolveGuestPath(const char* guest_path); bool LoadGuestFileBytes(const std::filesystem::path& host_path, @@ -761,5 +878,15 @@ GeneratedGlyph* TryGetGeneratedGlyph(Libraries::Font::OrbisFontGlyph glyph); void PopulateGlyphMetricVariants(GeneratedGlyph& gg); void BuildBoundingOutline(GeneratedGlyph& gg); bool BuildTrueOutline(GeneratedGlyph& gg); +void LogCachedStyleOnce(Libraries::Font::OrbisFontHandle handle, + const Libraries::Font::FontHandleOpaqueNative& font); +void LogRenderResultSample(Libraries::Font::OrbisFontHandle handle, u32 code, + const Libraries::Font::OrbisFontGlyphMetrics& metrics, + const Libraries::Font::OrbisFontRenderOutput& result); +u8 CachedStyleCacheFlags(const Libraries::Font::OrbisFontStyleFrame& cached_style); +void CachedStyleSetCacheFlags(Libraries::Font::OrbisFontStyleFrame& cached_style, u8 flags); +void CachedStyleSetDirectionWord(Libraries::Font::OrbisFontStyleFrame& cached_style, u16 word); +float CachedStyleGetScalar(const Libraries::Font::OrbisFontStyleFrame& cached_style); +void CachedStyleSetScalar(Libraries::Font::OrbisFontStyleFrame& cached_style, float value); } // namespace Libraries::Font::Internal diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp index db21f4207..4df91b0f3 100644 --- a/src/core/libraries/font/fontft.cpp +++ b/src/core/libraries/font/fontft.cpp @@ -1,10 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include - #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/font/font.h" @@ -15,67 +11,6 @@ namespace Libraries::FontFt { -namespace { - -static std::once_flag g_driver_table_once; -alignas(Libraries::Font::Internal::SysDriver) static Libraries::Font::Internal::SysDriver - g_driver_table{}; - -static const OrbisFontLibrarySelection* GetDriverTable() { - std::call_once(g_driver_table_once, [] { - auto* selection = reinterpret_cast(&g_driver_table); - selection->magic = 0; - selection->reserved = 0; - selection->reserved_ptr1 = nullptr; - - auto* driver = &g_driver_table; - - driver->pixel_resolution = &Libraries::FontFt::Internal::LibraryGetPixelResolutionStub; - driver->init = &Libraries::FontFt::Internal::LibraryInitStub; - driver->term = &Libraries::FontFt::Internal::LibraryTermStub; - driver->support_formats = &Libraries::FontFt::Internal::LibrarySupportStub; - - driver->open = &Libraries::FontFt::Internal::LibraryOpenFontMemoryStub; - driver->close = &Libraries::FontFt::Internal::LibraryCloseFontObjStub; - driver->scale = &Libraries::FontFt::Internal::LibraryGetFaceScaleStub; - driver->metric = &Libraries::FontFt::Internal::LibraryGetFaceMetricStub; - driver->glyph_index = &Libraries::FontFt::Internal::LibraryGetGlyphIndexStub; - driver->set_char_with_dpi = &Libraries::FontFt::Internal::LibrarySetCharSizeWithDpiStub; - driver->set_char_default_dpi = - &Libraries::FontFt::Internal::LibrarySetCharSizeDefaultDpiStub; - driver->compute_layout = &Libraries::FontFt::Internal::LibraryComputeLayoutBlockStub; - driver->compute_layout_alt = &Libraries::FontFt::Internal::LibraryComputeLayoutAltBlockStub; - - driver->load_glyph_cached = &Libraries::FontFt::Internal::LibraryLoadGlyphCachedStub; - driver->get_glyph_metrics = &Libraries::FontFt::Internal::LibraryGetGlyphMetricsStub; - driver->apply_glyph_adjust = &Libraries::FontFt::Internal::LibraryApplyGlyphAdjustStub; - driver->configure_glyph = &Libraries::FontFt::Internal::LibraryConfigureGlyphStub; - }); - - return reinterpret_cast(&g_driver_table); -} - -static std::once_flag g_renderer_table_once; -alignas(OrbisFontRendererSelection) static OrbisFontRendererSelection g_renderer_table{}; - -static const OrbisFontRendererSelection* GetRendererSelectionTable() { - std::call_once(g_renderer_table_once, [] { - g_renderer_table.magic = 0; - g_renderer_table.size = - static_cast(sizeof(Libraries::Font::Internal::RendererFtOpaque)); - - g_renderer_table.create_fn = - reinterpret_cast(&Libraries::FontFt::Internal::FtRendererCreate); - g_renderer_table.destroy_fn = - reinterpret_cast(&Libraries::FontFt::Internal::FtRendererDestroy); - g_renderer_table.query_fn = - reinterpret_cast(&Libraries::FontFt::Internal::FtRendererQuery); - }); - return &g_renderer_table; -} - -} // namespace - s32 PS4_SYSV_ABI sceFontFtInitAliases() { LOG_ERROR(Lib_FontFt, "(STUBBED) called"); return ORBIS_OK; @@ -175,7 +110,7 @@ const OrbisFontLibrarySelection* PS4_SYSV_ABI sceFontSelectLibraryFt(int value) LOG_INFO(Lib_FontFt, "called"); LOG_DEBUG(Lib_FontFt, "params:\nvalue: {}\n", value); if (value == 0) { - return GetDriverTable(); + return Libraries::FontFt::Internal::GetDriverTable(); } return nullptr; } @@ -184,7 +119,7 @@ const OrbisFontRendererSelection* PS4_SYSV_ABI sceFontSelectRendererFt(int value LOG_INFO(Lib_FontFt, "called"); LOG_DEBUG(Lib_FontFt, "params:\nvalue: {}\n", value); if (value == 0) { - return GetRendererSelectionTable(); + return Libraries::FontFt::Internal::GetRendererSelectionTable(); } return nullptr; } diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index c5cc2f5bc..526c91007 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -44,11 +44,6 @@ using Libraries::Font::OrbisFontRenderSurface; using Libraries::Font::OrbisFontStyleFrame; namespace { -struct GetCharGlyphMetricsFailLogState { - u32 count = 0; - bool suppression_logged = false; -}; - static thread_local GetCharGlyphMetricsFailLogState g_get_char_metrics_fail; static void LogGetCharGlyphMetricsFailOnce(std::string_view stage, s32 rc, FT_Error ft_err, @@ -325,22 +320,6 @@ static std::uint64_t ResolveGposTagForCode(u32 codepoint) { return 0; } -#pragma pack(push, 1) -struct SysFontRangeRecord { - /*0x00*/ u32 start; - /*0x04*/ u32 end; - /*0x08*/ s16 shift_x; - /*0x0A*/ s16 shift_y; - /*0x0C*/ float scale_mul; - /*0x10*/ u32 reserved_0x10; - /*0x14*/ u32 reserved_0x14; - /*0x18*/ u32 reserved_0x18; -}; -#pragma pack(pop) -static_assert(sizeof(SysFontRangeRecord) == 0x1C); -static_assert(offsetof(SysFontRangeRecord, shift_x) == 0x08); -static_assert(offsetof(SysFontRangeRecord, scale_mul) == 0x0C); - static float SysFontScaleFactor(u32 font_id) { (void)font_id; return 1.0f; @@ -652,11 +631,6 @@ s32 ComputeHorizontalLayoutBlocks(OrbisFontHandle fontHandle, const void* style_ float effect_for_baseline = 0.0f; float effect_for_delta = 0.0f; - struct F32x2 { - float lo = 0.0f; - float hi = 0.0f; - }; - F32x2 acc_u13_i8{}; F32x2 acc_x_bounds{}; @@ -1516,19 +1490,11 @@ static StyleFrameScaleState ResolveStyleFrameScaleFromCachedStyle(const OrbisFon namespace { -struct RenderSurfaceSystemUse { - Libraries::Font::OrbisFontStyleFrame* styleframe = nullptr; - float catchedScale = 0.0f; - std::uint8_t padding[88 - sizeof(Libraries::Font::OrbisFontStyleFrame*) - sizeof(float)]{}; -}; -static_assert(sizeof(RenderSurfaceSystemUse) == - sizeof(((Libraries::Font::OrbisFontRenderSurface*)nullptr)->reserved_q), - "RenderSurfaceSystemUse layout must match OrbisFontRenderSurface::reserved_q"); - -inline RenderSurfaceSystemUse* GetSurfaceSystemUse(Libraries::Font::OrbisFontRenderSurface* surf) { +static inline RenderSurfaceSystemUse* GetSurfaceSystemUse( + Libraries::Font::OrbisFontRenderSurface* surf) { return surf ? reinterpret_cast(surf->reserved_q) : nullptr; } -inline const RenderSurfaceSystemUse* GetSurfaceSystemUse( +static inline const RenderSurfaceSystemUse* GetSurfaceSystemUse( const Libraries::Font::OrbisFontRenderSurface* surf) { return surf ? reinterpret_cast(surf->reserved_q) : nullptr; } @@ -1798,9 +1764,9 @@ static s32 RenderGlyphIndexToSurface(FontObj& font_obj, u32 glyph_index, return ORBIS_OK; } -StyleFrameScaleState ResolveMergedStyleFrameScale(const Libraries::Font::OrbisFontStyleFrame* base, - const Libraries::Font::OrbisFontStyleFrame* over, - const FontState& st) { +static StyleFrameScaleState ResolveMergedStyleFrameScale( + const Libraries::Font::OrbisFontStyleFrame* base, + const Libraries::Font::OrbisFontStyleFrame* over, const FontState& st) { StyleFrameScaleState resolved = ResolveStyleFrameScale(base, st); if (!ValidateStyleFramePtr(over)) { return resolved; @@ -2364,16 +2330,17 @@ namespace Libraries::FontFt::Internal { namespace { +static std::once_flag g_driver_table_once; +alignas(Libraries::Font::Internal::SysDriver) static Libraries::Font::Internal::SysDriver + g_driver_table{}; + +static std::once_flag g_renderer_table_once; +alignas(Libraries::FontFt::OrbisFontRendererSelection) static Libraries::FontFt:: + OrbisFontRendererSelection g_renderer_table{}; + using Libraries::Font::Internal::FontLibOpaque; using Libraries::Font::Internal::FontObj; -struct FtLibraryCtx { - void* alloc_ctx; - void** alloc_vtbl; - FT_Memory ft_memory; - FT_Library ft_lib; -}; - static constexpr float kOneOver64 = 1.0f / 64.0f; static std::optional ResolveKnownSysFontAlias( @@ -2466,49 +2433,6 @@ static constexpr u32 MakeTag(char a, char b, char c, char d) { (static_cast(static_cast(c)) << 8) | static_cast(static_cast(d)); } -struct BeU16 { - u8 b[2]; - - constexpr u16 value() const { - return static_cast((static_cast(b[0]) << 8) | static_cast(b[1])); - } -}; -static_assert(sizeof(BeU16) == 2, "BeU16 size"); - -struct BeU32 { - u8 b[4]; - - constexpr u32 value() const { - return (static_cast(b[0]) << 24) | (static_cast(b[1]) << 16) | - (static_cast(b[2]) << 8) | static_cast(b[3]); - } -}; -static_assert(sizeof(BeU32) == 4, "BeU32 size"); - -struct TtcHeader { - BeU32 tag; - BeU32 version; - BeU32 num_fonts; -}; -static_assert(sizeof(TtcHeader) == 0x0C, "TtcHeader size"); - -struct SfntOffsetTable { - BeU32 version; - BeU16 num_tables; - BeU16 search_range; - BeU16 entry_selector; - BeU16 range_shift; -}; -static_assert(sizeof(SfntOffsetTable) == 0x0C, "SfntOffsetTable size"); - -struct SfntTableRecord { - BeU32 tag; - BeU32 checksum; - BeU32 offset; - BeU32 length; -}; -static_assert(sizeof(SfntTableRecord) == 0x10, "SfntTableRecord size"); - static bool ResolveSfntBaseOffset(const u8* data, std::size_t size, u32 subFontIndex, u32& out_base) { out_base = 0; @@ -2602,13 +2526,6 @@ static bool ReadUnitsPerEm(const u8* data, std::size_t size, u32 base, u16& out_ return out_units != 0; } -struct FontObjSidecar { - const u8* font_data = nullptr; - u32 font_size = 0; - u32 sfnt_base = 0; - void* owned_data = nullptr; -}; - static std::mutex g_font_obj_sidecars_mutex; static std::unordered_map g_font_obj_sidecars; @@ -2642,8 +2559,7 @@ static void* FtAlloc(FT_Memory memory, long size) { if (!ctx || !ctx->alloc_vtbl) { return nullptr; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); + const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); return alloc_fn ? Core::ExecuteGuest(alloc_fn, ctx->alloc_ctx, static_cast(size)) : nullptr; } @@ -2656,8 +2572,7 @@ static void FtFree(FT_Memory memory, void* block) { if (!ctx || !ctx->alloc_vtbl) { return; } - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto free_fn = reinterpret_cast(ctx->alloc_vtbl[1]); + const auto free_fn = reinterpret_cast(ctx->alloc_vtbl[1]); if (free_fn) { Core::ExecuteGuest(free_fn, ctx->alloc_ctx, block); } @@ -2671,8 +2586,7 @@ static void* FtRealloc(FT_Memory memory, long cur_size, long new_size, void* blo if (!ctx || !ctx->alloc_vtbl) { return nullptr; } - using ReallocFn = void*(PS4_SYSV_ABI*)(void* object, void* p, u32 size); - const auto realloc_fn = reinterpret_cast(ctx->alloc_vtbl[2]); + const auto realloc_fn = reinterpret_cast(ctx->alloc_vtbl[2]); if (realloc_fn) { return Core::ExecuteGuest(realloc_fn, ctx->alloc_ctx, block, static_cast(new_size)); } @@ -2694,6 +2608,53 @@ static void* FtRealloc(FT_Memory memory, long cur_size, long new_size, void* blo } // namespace +const Libraries::FontFt::OrbisFontLibrarySelection* GetDriverTable() { + std::call_once(g_driver_table_once, [] { + auto* selection = + reinterpret_cast(&g_driver_table); + selection->magic = 0; + selection->reserved = 0; + selection->reserved_ptr1 = nullptr; + + auto* driver = &g_driver_table; + driver->pixel_resolution = &Libraries::FontFt::Internal::LibraryGetPixelResolutionStub; + driver->init = &Libraries::FontFt::Internal::LibraryInitStub; + driver->term = &Libraries::FontFt::Internal::LibraryTermStub; + driver->support_formats = &Libraries::FontFt::Internal::LibrarySupportStub; + driver->open = &Libraries::FontFt::Internal::LibraryOpenFontMemoryStub; + driver->close = &Libraries::FontFt::Internal::LibraryCloseFontObjStub; + driver->scale = &Libraries::FontFt::Internal::LibraryGetFaceScaleStub; + driver->metric = &Libraries::FontFt::Internal::LibraryGetFaceMetricStub; + driver->glyph_index = &Libraries::FontFt::Internal::LibraryGetGlyphIndexStub; + driver->set_char_with_dpi = &Libraries::FontFt::Internal::LibrarySetCharSizeWithDpiStub; + driver->set_char_default_dpi = + &Libraries::FontFt::Internal::LibrarySetCharSizeDefaultDpiStub; + driver->compute_layout = &Libraries::FontFt::Internal::LibraryComputeLayoutBlockStub; + driver->compute_layout_alt = &Libraries::FontFt::Internal::LibraryComputeLayoutAltBlockStub; + driver->load_glyph_cached = &Libraries::FontFt::Internal::LibraryLoadGlyphCachedStub; + driver->get_glyph_metrics = &Libraries::FontFt::Internal::LibraryGetGlyphMetricsStub; + driver->apply_glyph_adjust = &Libraries::FontFt::Internal::LibraryApplyGlyphAdjustStub; + driver->configure_glyph = &Libraries::FontFt::Internal::LibraryConfigureGlyphStub; + }); + + return reinterpret_cast(&g_driver_table); +} + +const Libraries::FontFt::OrbisFontRendererSelection* GetRendererSelectionTable() { + std::call_once(g_renderer_table_once, [] { + g_renderer_table.magic = 0; + g_renderer_table.size = + static_cast(sizeof(Libraries::Font::Internal::RendererFtOpaque)); + g_renderer_table.create_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererCreate); + g_renderer_table.destroy_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererDestroy); + g_renderer_table.query_fn = + reinterpret_cast(&Libraries::FontFt::Internal::FtRendererQuery); + }); + return &g_renderer_table; +} + u32 PS4_SYSV_ABI LibraryGetPixelResolutionStub() { return 0x40; } @@ -2708,10 +2669,8 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { return ORBIS_FONT_ERROR_INVALID_MEMORY; } - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = reinterpret_cast(mem->iface->alloc); - const auto free_fn = reinterpret_cast(mem->iface->dealloc); + const auto alloc_fn = reinterpret_cast(mem->iface->alloc); + const auto free_fn = reinterpret_cast(mem->iface->dealloc); if (!alloc_fn || !free_fn) { return ORBIS_FONT_ERROR_INVALID_MEMORY; } @@ -2800,8 +2759,7 @@ s32 PS4_SYSV_ABI LibraryTermStub(void* library) { auto* lib = static_cast(library); auto* alloc_ctx = lib->alloc_ctx; auto* alloc_vtbl = lib->alloc_vtbl; - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; + const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; if (!free_fn) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -2881,10 +2839,8 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* auto* lib = static_cast(library); void* alloc_ctx = lib->alloc_ctx; void** alloc_vtbl = lib->alloc_vtbl; - using AllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); - const auto alloc_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[0]) : nullptr; - const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; + const auto alloc_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[0]) : nullptr; + const auto free_fn = alloc_vtbl ? reinterpret_cast(alloc_vtbl[1]) : nullptr; if (!alloc_fn || !free_fn) { return ORBIS_FONT_ERROR_INVALID_PARAMETER; } @@ -3042,9 +2998,8 @@ s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { ctx = static_cast(face->memory->user); } - using FreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); const auto free_fn = - (ctx && ctx->alloc_vtbl) ? reinterpret_cast(ctx->alloc_vtbl[1]) : nullptr; + (ctx && ctx->alloc_vtbl) ? reinterpret_cast(ctx->alloc_vtbl[1]) : nullptr; void* owned_data = nullptr; if (const auto sidecar = TakeFontObjSidecar(obj)) { owned_data = sidecar->owned_data; @@ -3261,42 +3216,6 @@ s32 PS4_SYSV_ABI LibraryComputeLayoutBlockStub(void* fontObj, const void* style_ half_effect_w_px += round_mul_16_16(static_cast(y_max_px), shear_16_16); } - struct LayoutOutIo { - u8 (*words)[16]; - struct F32Field { - u8* base; - std::size_t offset; - F32Field& operator=(float v) { - std::memcpy(base + offset, &v, sizeof(v)); - return *this; - } - }; - struct Fields { - F32Field line_advance; - F32Field baseline; - F32Field x_bound_0; - F32Field x_bound_1; - F32Field max_advance_width; - F32Field hhea_caret_rise_adjust; - F32Field effect_height; - F32Field half_effect_width; - F32Field left_adjust; - }; - Fields fields() const { - return { - .line_advance = {words[0], 0x00}, - .baseline = {words[0], 0x04}, - .x_bound_0 = {words[0], 0x08}, - .x_bound_1 = {words[0], 0x0C}, - .max_advance_width = {words[1], 0x00}, - .hhea_caret_rise_adjust = {words[1], 0x04}, - .effect_height = {words[1], 0x08}, - .half_effect_width = {words[1], 0x0C}, - .left_adjust = {words[2], 0x00}, - }; - } - }; - auto out = LayoutOutIo{out_words}.fields(); out.effect_height = out_effect_h; out.left_adjust = static_cast(left_adjust_px) * kOneOver64; @@ -3446,40 +3365,6 @@ s32 PS4_SYSV_ABI LibraryComputeLayoutAltBlockStub(void* fontObj, const void* sty const s32 lane2 = round_fixed_mul(y_ascender, x_scale); const s32 lane3 = round_fixed_mul(y_descender, x_scale); - struct LayoutAltOutIo { - u8 (*words)[16]; - struct F32Field { - u8* base; - std::size_t offset; - F32Field& operator=(float v) { - std::memcpy(base + offset, &v, sizeof(v)); - return *this; - } - }; - struct Fields { - F32Field metrics_0x00; - F32Field metrics_0x04; - F32Field metrics_0x08; - F32Field metrics_0x0C; - F32Field adv_height; - F32Field effect_width; - F32Field slant_b; - F32Field slant_a; - }; - Fields fields() const { - return { - .metrics_0x00 = {words[0], 0x00}, - .metrics_0x04 = {words[0], 0x04}, - .metrics_0x08 = {words[0], 0x08}, - .metrics_0x0C = {words[0], 0x0C}, - .adv_height = {words[1], 0x00}, - .effect_width = {words[1], 0x04}, - .slant_b = {words[1], 0x08}, - .slant_a = {words[1], 0x0C}, - }; - } - }; - auto out = LayoutAltOutIo{out_words}.fields(); out.metrics_0x00 = static_cast(lane0) * kOneOver64; out.metrics_0x04 = static_cast(lane1) * kOneOver64; diff --git a/src/core/libraries/font/fontft_internal.h b/src/core/libraries/font/fontft_internal.h index fbc1b85ca..74adf6a25 100644 --- a/src/core/libraries/font/fontft_internal.h +++ b/src/core/libraries/font/fontft_internal.h @@ -9,7 +9,12 @@ #include #include +#include +#include FT_FREETYPE_H +#include FT_SYSTEM_H + #include "core/libraries/font/font.h" +#include "core/libraries/font/fontft.h" namespace Libraries::Font { @@ -448,6 +453,40 @@ static_assert(offsetof(VerticalLayoutAltBlocks, metrics) == 0x00, static_assert(offsetof(VerticalLayoutAltBlocks, extras) == 0x10, "VerticalLayoutAltBlocks extras offset"); +struct GetCharGlyphMetricsFailLogState { + u32 count = 0; + bool suppression_logged = false; +}; + +struct SysFontRangeRecord { + /*0x00*/ u32 start; + /*0x04*/ u32 end; + /*0x08*/ s16 shift_x; + /*0x0A*/ s16 shift_y; + /*0x0C*/ float scale_mul; + /*0x10*/ u32 reserved_0x10; + /*0x14*/ u32 reserved_0x14; + /*0x18*/ u32 reserved_0x18; +}; +static_assert(sizeof(SysFontRangeRecord) == 0x1C, "SysFontRangeRecord size"); +static_assert(offsetof(SysFontRangeRecord, shift_x) == 0x08, "SysFontRangeRecord shift_x offset"); +static_assert(offsetof(SysFontRangeRecord, scale_mul) == 0x0C, + "SysFontRangeRecord scale_mul offset"); + +struct F32x2 { + float lo = 0.0f; + float hi = 0.0f; +}; + +struct RenderSurfaceSystemUse { + Libraries::Font::OrbisFontStyleFrame* styleframe = nullptr; + float catchedScale = 0.0f; + std::uint8_t padding[88 - sizeof(Libraries::Font::OrbisFontStyleFrame*) - sizeof(float)]{}; +}; +static_assert(sizeof(RenderSurfaceSystemUse) == + sizeof(((Libraries::Font::OrbisFontRenderSurface*)nullptr)->reserved_q), + "RenderSurfaceSystemUse layout must match OrbisFontRenderSurface::reserved_q"); + s32 ComputeHorizontalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, const void* style_state_block, u8 (*out_words)[16]); s32 ComputeVerticalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, @@ -516,12 +555,6 @@ std::uint8_t* AcquireFontCtxEntry(std::uint8_t* ctx, u32 idx, u32 mode_low, void void ReleaseFontObjectsForHandle(Libraries::Font::OrbisFontHandle fontHandle, int entryCount); -s32 ComputeHorizontalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, - const void* style_state_block, u8 (*out_words)[16]); - -s32 ComputeVerticalLayoutBlocks(Libraries::Font::OrbisFontHandle fontHandle, - const void* style_state_block, u8 (*out_words)[16]); - s32 GetCharGlyphMetrics(Libraries::Font::OrbisFontHandle fontHandle, u32 code, Libraries::Font::OrbisFontGlyphMetrics* metrics, bool use_cached_style); @@ -564,6 +597,148 @@ s32 StyleStateGetWeightScale(const void* style_state_block, float* weightXScale, namespace Libraries::FontFt::Internal { +using GuestAllocFn = void*(PS4_SYSV_ABI*)(void* object, u32 size); +using GuestFreeFn = void(PS4_SYSV_ABI*)(void* object, void* p); +using GuestReallocFn = void*(PS4_SYSV_ABI*)(void* object, void* p, u32 size); + +struct FtLibraryCtx { + void* alloc_ctx = nullptr; + void** alloc_vtbl = nullptr; + FT_Memory ft_memory = nullptr; + FT_Library ft_lib = nullptr; +}; + +struct BeU16 { + u8 b[2]; + + constexpr u16 value() const { + return static_cast((static_cast(b[0]) << 8) | static_cast(b[1])); + } +}; +static_assert(sizeof(BeU16) == 2, "BeU16 size"); + +struct BeU32 { + u8 b[4]; + + constexpr u32 value() const { + return (static_cast(b[0]) << 24) | (static_cast(b[1]) << 16) | + (static_cast(b[2]) << 8) | static_cast(b[3]); + } +}; +static_assert(sizeof(BeU32) == 4, "BeU32 size"); + +struct TtcHeader { + BeU32 tag; + BeU32 version; + BeU32 num_fonts; +}; +static_assert(sizeof(TtcHeader) == 0x0C, "TtcHeader size"); + +struct SfntOffsetTable { + BeU32 version; + BeU16 num_tables; + BeU16 search_range; + BeU16 entry_selector; + BeU16 range_shift; +}; +static_assert(sizeof(SfntOffsetTable) == 0x0C, "SfntOffsetTable size"); + +struct SfntTableRecord { + BeU32 tag; + BeU32 checksum; + BeU32 offset; + BeU32 length; +}; +static_assert(sizeof(SfntTableRecord) == 0x10, "SfntTableRecord size"); + +struct FontObjSidecar { + const u8* font_data = nullptr; + u32 font_size = 0; + u32 sfnt_base = 0; + void* owned_data = nullptr; +}; + +struct LayoutOutIo { + u8 (*words)[16] = nullptr; + + struct F32Field { + u8* base = nullptr; + std::size_t offset = 0; + + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + }; + + struct Fields { + F32Field line_advance; + F32Field baseline; + F32Field x_bound_0; + F32Field x_bound_1; + F32Field max_advance_width; + F32Field hhea_caret_rise_adjust; + F32Field effect_height; + F32Field half_effect_width; + F32Field left_adjust; + }; + + Fields fields() const { + return { + .line_advance = {words[0], 0x00}, + .baseline = {words[0], 0x04}, + .x_bound_0 = {words[0], 0x08}, + .x_bound_1 = {words[0], 0x0C}, + .max_advance_width = {words[1], 0x00}, + .hhea_caret_rise_adjust = {words[1], 0x04}, + .effect_height = {words[1], 0x08}, + .half_effect_width = {words[1], 0x0C}, + .left_adjust = {words[2], 0x00}, + }; + } +}; + +struct LayoutAltOutIo { + u8 (*words)[16] = nullptr; + + struct F32Field { + u8* base = nullptr; + std::size_t offset = 0; + + F32Field& operator=(float v) { + std::memcpy(base + offset, &v, sizeof(v)); + return *this; + } + }; + + struct Fields { + F32Field metrics_0x00; + F32Field metrics_0x04; + F32Field metrics_0x08; + F32Field metrics_0x0C; + F32Field adv_height; + F32Field effect_width; + F32Field slant_b; + F32Field slant_a; + }; + + Fields fields() const { + return { + .metrics_0x00 = {words[0], 0x00}, + .metrics_0x04 = {words[0], 0x04}, + .metrics_0x08 = {words[0], 0x08}, + .metrics_0x0C = {words[0], 0x0C}, + .adv_height = {words[1], 0x00}, + .effect_width = {words[1], 0x04}, + .slant_b = {words[1], 0x08}, + .slant_a = {words[1], 0x0C}, + }; + } +}; + +const Libraries::FontFt::OrbisFontLibrarySelection* GetDriverTable(); +const Libraries::FontFt::OrbisFontRendererSelection* GetRendererSelectionTable(); + u32 PS4_SYSV_ABI LibraryGetPixelResolutionStub(); s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library); s32 PS4_SYSV_ABI LibraryTermStub(void* library); From 243c40cf9fbe291be2a40672e0201e939c9737cd Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 13 Mar 2026 15:58:06 +0200 Subject: [PATCH 50/51] Refactor memory allocation and deallocation calls to remove Core::ExecuteGuest wrapper --- src/core/libraries/font/font.cpp | 71 +++++++++++---------- src/core/libraries/font/font_internal.cpp | 3 +- src/core/libraries/font/fontft_internal.cpp | 33 +++++----- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 21981900c..462da8018 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -160,7 +160,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } else { u32* header = static_cast(buffer); if (!header) { - header = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, size)); + header = static_cast(alloc_fn(lib->alloc_ctx, size)); if (!header) { cache_to_store = nullptr; rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; @@ -191,7 +191,7 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } } else { if (!buffer) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, header); + free_fn(lib->alloc_ctx, header); } cache_to_store = nullptr; rc = ORBIS_FONT_ERROR_INVALID_PARAMETER; @@ -481,15 +481,15 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, return ORBIS_FONT_ERROR_INVALID_MEMORY; } - void* lib_mem = Core::ExecuteGuest(malloc_fn, memory->mspace_handle, 0x100); + void* lib_mem = malloc_fn(memory->mspace_handle, 0x100); if (!lib_mem) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } - void* mspace = Core::ExecuteGuest(malloc_fn, memory->mspace_handle, 0x4000); + void* mspace = malloc_fn(memory->mspace_handle, 0x4000); if (!mspace) { - Core::ExecuteGuest(free_fn, memory->mspace_handle, lib_mem); + free_fn(memory->mspace_handle, lib_mem); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -546,8 +546,8 @@ s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(const OrbisFontMem* memory, const s32 init_rc = init_fn(memory, lib); if (init_rc != ORBIS_OK) { - Core::ExecuteGuest(free_fn, memory->mspace_handle, mspace); - Core::ExecuteGuest(free_fn, memory->mspace_handle, lib_mem); + free_fn(memory->mspace_handle, mspace); + free_fn(memory->mspace_handle, lib_mem); LOG_ERROR(Lib_Font, "INIT_FAILED"); return init_rc; } @@ -626,7 +626,7 @@ s32 PS4_SYSV_ABI sceFontDestroyLibrary(OrbisFontLib* pLibrary) { ? reinterpret_cast(native->alloc_vtbl[1]) : nullptr; if (free_fn) { - Core::ExecuteGuest(free_fn, native->alloc_ctx, native); + free_fn(native->alloc_ctx, native); } else { std::free(native); } @@ -680,8 +680,8 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, create_params); const u32 render_size = selection ? selection->size : 0u; void* renderer_mem = - Core::ExecuteGuest(alloc_fn, memory->mspace_handle, render_size); - void* workspace = Core::ExecuteGuest(alloc_fn, memory->mspace_handle, 0x4000); + alloc_fn(memory->mspace_handle, render_size); + void* workspace = alloc_fn(memory->mspace_handle, 0x4000); rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; if (renderer_mem && workspace) { @@ -720,7 +720,7 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, if (!create_fn) { rc = ORBIS_FONT_ERROR_FATAL; } else { - rc = Core::ExecuteGuest(create_fn, renderer_mem); + rc = create_fn(renderer_mem); if (rc == ORBIS_OK) { s32 sdk_version = 0; u32 sdk_version_u32 = 0; @@ -748,10 +748,10 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, } if (workspace) { - Core::ExecuteGuest(free_fn, memory->mspace_handle, workspace); + free_fn(memory->mspace_handle, workspace); } if (renderer_mem) { - Core::ExecuteGuest(free_fn, memory->mspace_handle, renderer_mem); + free_fn(memory->mspace_handle, renderer_mem); } } } @@ -875,7 +875,7 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { if (destroy_fn_ptr == kFtRendererDestroyFnAddr) { destroy_fn = &Libraries::FontFt::Internal::FtRendererDestroy; } - rc = destroy_fn ? Core::ExecuteGuest(destroy_fn, renderer) : ORBIS_FONT_ERROR_FATAL; + rc = destroy_fn ? destroy_fn(renderer) : ORBIS_FONT_ERROR_FATAL; } renderer->selection = nullptr; @@ -884,9 +884,9 @@ s32 PS4_SYSV_ABI sceFontDestroyRenderer(OrbisFontRenderer* pRenderer) { void* alloc_ctx = renderer->alloc_ctx; void* workspace = renderer->workspace; if (workspace) { - Core::ExecuteGuest(free_fn, alloc_ctx, workspace); + free_fn(alloc_ctx, workspace); } - Core::ExecuteGuest(free_fn, alloc_ctx, renderer); + free_fn(alloc_ctx, renderer); *pRenderer = nullptr; return rc; @@ -1973,7 +1973,7 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { reinterpret_cast( mem_desc->iface->mspace_destroy); if (destroy_fn) { - Core::ExecuteGuest(destroy_fn, mem_desc->some_ctx2, mem_desc->mspace_handle); + destroy_fn(mem_desc->some_ctx2, mem_desc->mspace_handle); } } std::memset(mem_desc, 0, sizeof(*mem_desc)); @@ -1984,7 +1984,7 @@ s32 PS4_SYSV_ABI sceFontMemoryTerm(OrbisFontMem* mem_desc) { mem_desc->mem_kind = 0; const auto destroy_fn = reinterpret_cast(mem_desc->on_destroy); - Core::ExecuteGuest(destroy_fn, mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); + destroy_fn(mem_desc, mem_desc->mspace_handle, mem_desc->destroy_ctx); return ORBIS_OK; } @@ -2087,7 +2087,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat } Internal::RemoveState(handle); } else { - handle = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); + handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); if (!handle) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -2117,7 +2117,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontFile(OrbisFontLib library, const char* guest_pat const u16 prev_flags = h->flags; h->flags = 0; if ((prev_flags & 0x10) != 0) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, handle); + free_fn(lib->alloc_ctx, handle); } Internal::RemoveState(handle); ReleaseLibraryLock(lib, prev_lib_lock); @@ -2586,7 +2586,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } out_handle = - static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); + static_cast(alloc_fn(lib->alloc_ctx, 0x100)); if (!out_handle) { release_src_lock(); if (pFontHandle) { @@ -2620,7 +2620,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa if (!entries_base || entry_count > max_entries) { dst->magic = 0; if (owned && free_fn) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, out_handle); + free_fn(lib->alloc_ctx, out_handle); } release_src_lock(); if (pFontHandle) { @@ -2773,7 +2773,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa const u16 prev_flags = dst->flags; dst->flags = 0; if ((prev_flags & 0x10) != 0 && free_fn) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, out_handle); + free_fn(lib->alloc_ctx, out_handle); } Internal::RemoveState(out_handle); @@ -2969,7 +2969,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd } Internal::RemoveState(handle); } else { - handle = static_cast(Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, 0x100)); + handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); if (!handle) { release_library_and_clear_out(); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -2999,7 +2999,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd const u16 prev_flags = h->flags; h->flags = 0; if ((prev_flags & 0x10) != 0) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, handle); + free_fn(lib->alloc_ctx, handle); } Internal::RemoveState(handle); ReleaseLibraryLock(lib, prev_lib_lock); @@ -3386,7 +3386,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o Internal::RemoveState(handle); } else { handle = static_cast( - Core::ExecuteGuest(alloc_fn, lib_local->alloc_ctx, 0x100)); + alloc_fn(lib_local->alloc_ctx, 0x100)); if (!handle) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); @@ -3414,7 +3414,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o h->library = library; h->flags = 0; if ((prev_flags & 0x10) != 0) { - Core::ExecuteGuest(free_fn, lib_local->alloc_ctx, handle); + free_fn(lib_local->alloc_ctx, handle); } Internal::RemoveState(handle); return release_library_and_clear_out(rc); @@ -4852,14 +4852,14 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(OrbisFontRenderer fontRen if (!renderer->workspace || renderer->workspace_size != desired_size) { void* new_workspace = - Core::ExecuteGuest(alloc_fn, renderer->alloc_ctx, static_cast(desired_size)); + alloc_fn(renderer->alloc_ctx, static_cast(desired_size)); if (!new_workspace) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } if (renderer->workspace) { - Core::ExecuteGuest(free_fn, renderer->alloc_ctx, renderer->workspace); + free_fn(renderer->alloc_ctx, renderer->workspace); } renderer->workspace = new_workspace; @@ -5697,7 +5697,7 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, } const u32 ctx_size = (fontMax << 6) | 0x20u; - void* ctx = Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, ctx_size); + void* ctx = alloc_fn(lib->alloc_ctx, ctx_size); if (!ctx) { ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -5725,14 +5725,14 @@ s32 PS4_SYSV_ABI sceFontSupportExternalFonts(OrbisFontLib library, u32 fontMax, lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; const auto support_fn = driver ? driver->support_formats : nullptr; if (!support_fn) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); + free_fn(lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } const s32 support_rc = support_fn(library, formats); if (support_rc != ORBIS_OK) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); + free_fn(lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); return support_rc; @@ -5790,7 +5790,7 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { } constexpr u32 kSysCtxSize = 0x1020; - void* ctx = Core::ExecuteGuest(alloc_fn, lib->alloc_ctx, kSysCtxSize); + void* ctx = alloc_fn(lib->alloc_ctx, kSysCtxSize); if (!ctx) { ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); @@ -5812,14 +5812,14 @@ s32 PS4_SYSV_ABI sceFontSupportSystemFonts(OrbisFontLib library) { lib->sys_driver ? reinterpret_cast(lib->sys_driver) : nullptr; const auto support_fn = driver ? driver->support_formats : nullptr; if (!support_fn) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); + free_fn(lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_LIBRARY; } const s32 support_rc = support_fn(library, 0x52); if (support_rc != ORBIS_OK) { - Core::ExecuteGuest(free_fn, lib->alloc_ctx, ctx); + free_fn(lib->alloc_ctx, ctx); ReleaseLibraryLock(lib, prev_lock_word); LOG_ERROR(Lib_Font, "SUPPORT_FAILED"); return support_rc; @@ -6385,3 +6385,4 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { }; } // namespace Libraries::Font + diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index 51be24564..f64416c39 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -576,7 +576,7 @@ void RemoveLibState(Libraries::Font::OrbisFontLib lib) { if (free_fn) { const auto free_fn_guest = reinterpret_cast(free_fn); - Core::ExecuteGuest(free_fn_guest, alloc_ctx, p); + free_fn_guest(alloc_ctx, p); return; } std::free(p); @@ -1916,3 +1916,4 @@ void CachedStyleSetScalar(Libraries::Font::OrbisFontStyleFrame& cached_style, fl std::memcpy(&cached_style.cached_scalar_bits, &value, sizeof(value)); } } // namespace Libraries::Font::Internal + diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index 526c91007..b15808371 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -2560,7 +2560,7 @@ static void* FtAlloc(FT_Memory memory, long size) { return nullptr; } const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); - return alloc_fn ? Core::ExecuteGuest(alloc_fn, ctx->alloc_ctx, static_cast(size)) + return alloc_fn ? alloc_fn(ctx->alloc_ctx, static_cast(size)) : nullptr; } @@ -2574,7 +2574,7 @@ static void FtFree(FT_Memory memory, void* block) { } const auto free_fn = reinterpret_cast(ctx->alloc_vtbl[1]); if (free_fn) { - Core::ExecuteGuest(free_fn, ctx->alloc_ctx, block); + free_fn(ctx->alloc_ctx, block); } } @@ -2588,7 +2588,7 @@ static void* FtRealloc(FT_Memory memory, long cur_size, long new_size, void* blo } const auto realloc_fn = reinterpret_cast(ctx->alloc_vtbl[2]); if (realloc_fn) { - return Core::ExecuteGuest(realloc_fn, ctx->alloc_ctx, block, static_cast(new_size)); + return realloc_fn(ctx->alloc_ctx, block, static_cast(new_size)); } if (new_size <= 0) { @@ -2680,7 +2680,7 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { reinterpret_cast(const_cast(mem->iface)); auto* ctx = - static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FtLibraryCtx))); + static_cast(alloc_fn(alloc_ctx, sizeof(FtLibraryCtx))); if (!ctx) { return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -2689,9 +2689,9 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { ctx->alloc_vtbl = alloc_vtbl; FT_Memory ft_mem = - static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FT_MemoryRec_))); + static_cast(alloc_fn(alloc_ctx, sizeof(FT_MemoryRec_))); if (!ft_mem) { - Core::ExecuteGuest(free_fn, alloc_ctx, ctx); + free_fn(alloc_ctx, ctx); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } std::memset(ft_mem, 0, sizeof(*ft_mem)); @@ -2704,8 +2704,8 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { FT_Library ft_lib = nullptr; const FT_Error ft_err = FT_New_Library(ft_mem, &ft_lib); if (ft_err != 0 || !ft_lib) { - Core::ExecuteGuest(free_fn, alloc_ctx, ft_mem); - Core::ExecuteGuest(free_fn, alloc_ctx, ctx); + free_fn(alloc_ctx, ft_mem); + free_fn(alloc_ctx, ctx); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } FT_Add_Default_Modules(ft_lib); @@ -2774,10 +2774,10 @@ s32 PS4_SYSV_ABI LibraryTermStub(void* library) { ctx->ft_lib = nullptr; } if (ctx->ft_memory) { - Core::ExecuteGuest(free_fn, alloc_ctx, ctx->ft_memory); + free_fn(alloc_ctx, ctx->ft_memory); ctx->ft_memory = nullptr; } - Core::ExecuteGuest(free_fn, alloc_ctx, ctx); + free_fn(alloc_ctx, ctx); lib->fontset_registry = nullptr; return ORBIS_OK; } @@ -2878,7 +2878,7 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* auto* ctx = static_cast(lib->fontset_registry); if (!ctx || !ctx->ft_lib) { if (owned_data) { - Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); + free_fn(alloc_ctx, owned_data); } return ORBIS_FONT_ERROR_INVALID_LIBRARY; } @@ -2930,7 +2930,7 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* } if (ft_err != 0 || !face) { if (owned_data) { - Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); + free_fn(alloc_ctx, owned_data); } if (mode == 1) { return ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT; @@ -2943,11 +2943,11 @@ s32 PS4_SYSV_ABI LibraryOpenFontMemoryStub(void* library, u32 mode, const void* (void)FT_Select_Charmap(face, FT_ENCODING_UNICODE); - auto* obj = static_cast(Core::ExecuteGuest(alloc_fn, alloc_ctx, sizeof(FontObj))); + auto* obj = static_cast(alloc_fn(alloc_ctx, sizeof(FontObj))); if (!obj) { FT_Done_Face(face); if (owned_data) { - Core::ExecuteGuest(free_fn, alloc_ctx, owned_data); + free_fn(alloc_ctx, owned_data); } return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -3010,7 +3010,7 @@ s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { obj->ft_face = nullptr; } if (owned_data && free_fn) { - Core::ExecuteGuest(free_fn, ctx->alloc_ctx, owned_data); + free_fn(ctx->alloc_ctx, owned_data); } if (free_fn) { FontObj* next = obj->next; @@ -3021,7 +3021,7 @@ s32 PS4_SYSV_ABI LibraryCloseFontObjStub(void* fontObj, u32 /*flags*/) { } else { obj->prev->next = next; } - Core::ExecuteGuest(free_fn, ctx->alloc_ctx, obj); + free_fn(ctx->alloc_ctx, obj); return ORBIS_OK; } return ORBIS_FONT_ERROR_FATAL; @@ -3604,3 +3604,4 @@ s32 PS4_SYSV_ABI LibraryConfigureGlyphStub(void* fontObj, std::uint32_t* in_para } } // namespace Libraries::FontFt::Internal + From 7a3df4da0a25d8d965a722fea1a8ef70b15a72a0 Mon Sep 17 00:00:00 2001 From: w1naenator Date: Fri, 13 Mar 2026 16:23:13 +0200 Subject: [PATCH 51/51] clang --- src/core/libraries/font/font.cpp | 13 ++++--------- src/core/libraries/font/font_internal.cpp | 1 - src/core/libraries/font/fontft_internal.cpp | 10 +++------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 462da8018..ac638c70e 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -679,8 +679,7 @@ s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(const OrbisFontMem* memory, static_cast( create_params); const u32 render_size = selection ? selection->size : 0u; - void* renderer_mem = - alloc_fn(memory->mspace_handle, render_size); + void* renderer_mem = alloc_fn(memory->mspace_handle, render_size); void* workspace = alloc_fn(memory->mspace_handle, 0x4000); rc = ORBIS_FONT_ERROR_ALLOCATION_FAILED; @@ -2585,8 +2584,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontInstance(OrbisFontHandle fontHandle, OrbisFontHa LOG_ERROR(Lib_Font, "INVALID_LIBRARY"); return ORBIS_FONT_ERROR_INVALID_FONT_HANDLE; } - out_handle = - static_cast(alloc_fn(lib->alloc_ctx, 0x100)); + out_handle = static_cast(alloc_fn(lib->alloc_ctx, 0x100)); if (!out_handle) { release_src_lock(); if (pFontHandle) { @@ -3385,8 +3383,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o } Internal::RemoveState(handle); } else { - handle = static_cast( - alloc_fn(lib_local->alloc_ctx, 0x100)); + handle = static_cast(alloc_fn(lib_local->alloc_ctx, 0x100)); if (!handle) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return release_library_and_clear_out(ORBIS_FONT_ERROR_ALLOCATION_FAILED); @@ -4851,8 +4848,7 @@ s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(OrbisFontRenderer fontRen } if (!renderer->workspace || renderer->workspace_size != desired_size) { - void* new_workspace = - alloc_fn(renderer->alloc_ctx, static_cast(desired_size)); + void* new_workspace = alloc_fn(renderer->alloc_ctx, static_cast(desired_size)); if (!new_workspace) { LOG_ERROR(Lib_Font, "ALLOCATION_FAILED"); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; @@ -6385,4 +6381,3 @@ void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { }; } // namespace Libraries::Font - diff --git a/src/core/libraries/font/font_internal.cpp b/src/core/libraries/font/font_internal.cpp index f64416c39..f364aba3e 100644 --- a/src/core/libraries/font/font_internal.cpp +++ b/src/core/libraries/font/font_internal.cpp @@ -1916,4 +1916,3 @@ void CachedStyleSetScalar(Libraries::Font::OrbisFontStyleFrame& cached_style, fl std::memcpy(&cached_style.cached_scalar_bits, &value, sizeof(value)); } } // namespace Libraries::Font::Internal - diff --git a/src/core/libraries/font/fontft_internal.cpp b/src/core/libraries/font/fontft_internal.cpp index b15808371..b7d5bacdd 100644 --- a/src/core/libraries/font/fontft_internal.cpp +++ b/src/core/libraries/font/fontft_internal.cpp @@ -2560,8 +2560,7 @@ static void* FtAlloc(FT_Memory memory, long size) { return nullptr; } const auto alloc_fn = reinterpret_cast(ctx->alloc_vtbl[0]); - return alloc_fn ? alloc_fn(ctx->alloc_ctx, static_cast(size)) - : nullptr; + return alloc_fn ? alloc_fn(ctx->alloc_ctx, static_cast(size)) : nullptr; } static void FtFree(FT_Memory memory, void* block) { @@ -2679,8 +2678,7 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { void** alloc_vtbl = reinterpret_cast(const_cast(mem->iface)); - auto* ctx = - static_cast(alloc_fn(alloc_ctx, sizeof(FtLibraryCtx))); + auto* ctx = static_cast(alloc_fn(alloc_ctx, sizeof(FtLibraryCtx))); if (!ctx) { return ORBIS_FONT_ERROR_ALLOCATION_FAILED; } @@ -2688,8 +2686,7 @@ s32 PS4_SYSV_ABI LibraryInitStub(const void* memory, void* library) { ctx->alloc_ctx = alloc_ctx; ctx->alloc_vtbl = alloc_vtbl; - FT_Memory ft_mem = - static_cast(alloc_fn(alloc_ctx, sizeof(FT_MemoryRec_))); + FT_Memory ft_mem = static_cast(alloc_fn(alloc_ctx, sizeof(FT_MemoryRec_))); if (!ft_mem) { free_fn(alloc_ctx, ctx); return ORBIS_FONT_ERROR_ALLOCATION_FAILED; @@ -3604,4 +3601,3 @@ s32 PS4_SYSV_ABI LibraryConfigureGlyphStub(void* fontObj, std::uint32_t* in_para } } // namespace Libraries::FontFt::Internal -