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