#include "Common/TimeUtil.h" #include #include #include #include #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" #include "Core/HLE/HLE.h" #include "Core/HLE/ErrorCodes.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceFont.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelThread.h" #include "Core/MIPS/MIPS.h" #include "Core/FileSystems/FileSystem.h" #include "Core/FileSystems/MetaFileSystem.h" #include "Core/MemMapHelpers.h" #include "Core/Reporting.h" #include "Core/Core.h" #include "Core/System.h" #include "Core/Font/PGF.h" constexpr int MAX_FONT_REFS = 4; // For the save states. static bool useAllocCallbacks = true; // Actions static int actionPostAllocCallback; static int actionPostOpenCallback; static int actionPostOpenAllocCallback; static int actionPostCharInfoAllocCallback; static int actionPostCharInfoFreeCallback; // Monster Hunter sequence: // 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:469 E[HLE]: sceFontNewLib 89ad4a0, 9fff5cc // 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:699 E[HLE]: UNIMPL sceFontGetNumFontList 1, 9fff5cc // 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:526 E[HLE]: sceFontFindOptimumFont 1, 9fff524, 9fff5cc // 36:46:999 c:\dev\ppsspp\core\hle\scefont.cpp:490 E[HLE]: sceFontOpenFont 1, 1, 0, 9fff5cc // 36:46:999 c:\dev\ppsspp\core\hle\scefont.cpp:542 E[HLE]: sceFontGetFontInfo 1, 997140c typedef u32 FontLibraryHandle; typedef u32 FontHandle; struct FontNewLibParams { u32_le userDataAddr; u32_le numFonts; u32_le cacheDataAddr; // Driver callbacks. u32_le allocFuncAddr; u32_le freeFuncAddr; u32_le openFuncAddr; u32_le closeFuncAddr; u32_le readFuncAddr; u32_le seekFuncAddr; u32_le errorFuncAddr; u32_le ioFinishFuncAddr; }; struct FontRegistryEntry { int hSize; int vSize; int hResolution; int vResolution; int extraAttributes; int weight; int familyCode; int style; int styleSub; int languageCode; int regionCode; int countryCode; const char *fileName; const char *fontName; int expireDate; int shadow_option; u32 fontFileSize; u32 stingySize; // for the FONT_OPEN_INTERNAL_STINGY mode, from pspautotests. bool ignoreIfMissing; }; static const FontRegistryEntry fontRegistry[] = { // This was added for Chinese translations and is not normally loaded on a PSP. { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_DB, 0, FONT_LANGUAGE_CHINESE, 0, 1, "zh_gb.pgf", "FTT-NewRodin Pro DB", 0, 0, 1581700, 145844, true }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_DB, 0, FONT_LANGUAGE_JAPANESE, 0, 1, "jpn0.pgf", "FTT-NewRodin Pro DB", 0, 0, 1581700, 145844 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn0.pgf", "FTT-NewRodin Pro Latin", 0, 0, 69108, 16680 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn1.pgf", "FTT-Matisse Pro Latin", 0, 0, 65124, 16920 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn2.pgf", "FTT-NewRodin Pro Latin", 0, 0, 72948, 16872 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn3.pgf", "FTT-Matisse Pro Latin", 0, 0, 67700, 17112 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn4.pgf", "FTT-NewRodin Pro Latin", 0, 0, 72828, 16648 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn5.pgf", "FTT-Matisse Pro Latin", 0, 0, 68220, 16928 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn6.pgf", "FTT-NewRodin Pro Latin", 0, 0, 77032, 16792 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn7.pgf", "FTT-Matisse Pro Latin", 0, 0, 71144, 17160 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn8.pgf", "FTT-NewRodin Pro Latin", 0, 0, 41000, 16192 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn9.pgf", "FTT-Matisse Pro Latin", 0, 0, 40164, 16476 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn10.pgf", "FTT-NewRodin Pro Latin", 0, 0, 42692, 16300 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn11.pgf", "FTT-Matisse Pro Latin", 0, 0, 41488, 16656 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn12.pgf", "FTT-NewRodin Pro Latin", 0, 0, 43136, 16176 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn13.pgf", "FTT-Matisse Pro Latin", 0, 0, 41772, 16436 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn14.pgf", "FTT-NewRodin Pro Latin", 0, 0, 45184, 16272 }, { 0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn15.pgf", "FTT-Matisse Pro Latin", 0, 0, 43044, 16704 }, { 0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_KOREAN, 0, 3, "kr0.pgf", "AsiaNHH(512Johab)", 0, 0, 394192, 51856 }, }; static const float pointDPI = 72.f; class LoadedFont; class FontLib; class Font; int GetInternalFontIndex(Font *font); // These should not need to be state saved. static std::vector internalFonts; // However, these we must save - but we could take a shortcut // for LoadedFonts that point to internal fonts. static std::map fontMap; static std::map fontLibMap; // We keep this list to avoid ptr references, even before alloc is called. static std::vector fontLibList; enum MatchQuality { MATCH_UNKNOWN, MATCH_NONE, MATCH_GOOD, }; enum FontOpenMode { FONT_OPEN_INTERNAL_STINGY = 0, FONT_OPEN_INTERNAL_FULL = 1, // Calls open/seek/read/close handlers to read the file partially. FONT_OPEN_USERFILE_HANDLERS = 2, // Reads directly from filesystem. FONT_OPEN_USERFILE_FULL = 3, FONT_OPEN_USERBUFFER = 4, }; // TODO: Merge this class with PGF? That'd make it harder to support .bwfon // fonts though, unless that's added directly to PGF. class Font { public: // For savestates only. Font() : valid_(false) { } Font(const u8 *data, size_t dataSize) { Init(data, dataSize); } Font(const u8 *data, size_t dataSize, const FontRegistryEntry &entry) { Init(data, dataSize, entry); } Font(const std::vector &data) { Init(&data[0], data.size()); } Font(const std::vector &data, const FontRegistryEntry &entry) { Init(&data[0], data.size(), entry); } const PGFFontStyle &GetFontStyle() const { return style_; } MatchQuality MatchesStyle(const PGFFontStyle &style) const { // If no field matches, it doesn't match. MatchQuality match = MATCH_UNKNOWN; #define CHECK_FIELD(f, m) \ if (style.f != 0) { \ if (style.f != style_.f) { \ return MATCH_NONE; \ } \ if (match < m) { \ match = m; \ } \ } #define CHECK_FIELD_STR(f, m) \ if (style.f[0] != '\0') { \ if (strcmp(style.f, style_.f) != 0) { \ return MATCH_NONE; \ } \ if (match < m) { \ match = m; \ } \ } CHECK_FIELD(fontFamily, MATCH_GOOD); CHECK_FIELD(fontStyle, MATCH_GOOD); CHECK_FIELD(fontLanguage, MATCH_GOOD); CHECK_FIELD(fontCountry, MATCH_GOOD); CHECK_FIELD_STR(fontName, MATCH_GOOD); CHECK_FIELD_STR(fontFileName, MATCH_GOOD); #undef CHECK_FIELD_STR #undef CHECK_FIELD return match; } PGF *GetPGF() { return &pgf_; } const PGF *GetPGF() const { return &pgf_; } u32 getSize() const { return dataSize_; } u32 getStingySize() const { return stingySize_; } bool IsValid() const { return valid_; } void DoState(PointerWrap &p) { auto s = p.Section("Font", 1, 2); if (!s) return; Do(p, pgf_); Do(p, style_); if (s < 2) { valid_ = true; } else { Do(p, valid_); } } private: void Init(const u8 *data, size_t dataSize) { valid_ = pgf_.ReadPtr(data, dataSize); memset(&style_, 0, sizeof(style_)); style_.fontH = (float)pgf_.header.hSize / 64.0f; style_.fontV = (float)pgf_.header.vSize / 64.0f; style_.fontHRes = (float)pgf_.header.hResolution / 64.0f; style_.fontVRes = (float)pgf_.header.vResolution / 64.0f; this->dataSize_ = (u32)dataSize; this->stingySize_ = 0; // Unused } void Init(const u8 *data, size_t dataSize, const FontRegistryEntry &entry) { valid_ = pgf_.ReadPtr(data, dataSize); style_.fontH = entry.hSize / 64.f; style_.fontV = entry.vSize / 64.f; style_.fontHRes = entry.hResolution / 64.f; style_.fontVRes = entry.vResolution / 64.f; style_.fontWeight = (float)entry.weight; style_.fontFamily = (u16)entry.familyCode; style_.fontStyle = (u16)entry.style; style_.fontStyleSub = (u16)entry.styleSub; style_.fontLanguage = (u16)entry.languageCode; style_.fontRegion = (u16)entry.regionCode; style_.fontCountry = (u16)entry.countryCode; strncpy(style_.fontName, entry.fontName, sizeof(style_.fontName)); strncpy(style_.fontFileName, entry.fileName, sizeof(style_.fontFileName)); style_.fontAttributes = entry.extraAttributes; style_.fontExpire = entry.expireDate; this->dataSize_ = entry.fontFileSize; this->stingySize_ = entry.stingySize; } PGF pgf_; PGFFontStyle style_; bool valid_; u32 dataSize_; u32 stingySize_; DISALLOW_COPY_AND_ASSIGN(Font); }; class LoadedFont { public: // For savestates only. LoadedFont() {} LoadedFont(Font *font, FontOpenMode mode, u32 fontLibID, u32 handle) : fontLibID_(fontLibID), font_(font), handle_(handle), mode_(mode), open_(true) {} ~LoadedFont() { switch (mode_) { case FONT_OPEN_USERBUFFER: case FONT_OPEN_USERFILE_FULL: case FONT_OPEN_USERFILE_HANDLERS: // For these types, it's our responsibility to delete. delete font_; font_ = NULL; break; default: // Otherwise, it's an internal font, we keep those. break; } } const Font *GetFont() const { return font_; } const PGF *GetPGF() const { return font_->GetPGF(); } const FontLib *GetFontLib() const { _dbg_assert_(IsValid()); return fontLibList[fontLibID_]; } FontLib *GetFontLib() { _dbg_assert_(IsValid()); return fontLibList[fontLibID_]; } u32 Handle() const { return handle_; } bool IsValid() const { return fontLibID_ >= 0 && fontLibID_ < (int)fontLibList.size(); } bool GetCharInfo(int charCode, PGFCharInfo *charInfo, int glyphType = FONT_PGF_CHARGLYPH) const; void DrawCharacter(const GlyphImage *image, int clipX, int clipY, int clipWidth, int clipHeight, int charCode, int glyphType) const; bool IsOpen() const { return open_; } void Close() { open_ = false; // We keep the rest around until deleted, as some queries are allowed // on closed fonts (which is rather strange). } void DoState(PointerWrap &p) { auto s = p.Section("LoadedFont", 1, 3); if (!s) return; int numInternalFonts = (int)internalFonts.size(); Do(p, numInternalFonts); // It's okay if numInternalFonts was zero and we've now loaded them. if (numInternalFonts != (int)internalFonts.size() && numInternalFonts != 0) { ERROR_LOG(Log::sceFont, "Unable to load state: different internal font count (%d in save, %d in memory)", numInternalFonts, (int)internalFonts.size()); p.SetError(p.ERROR_FAILURE); return; } Do(p, fontLibID_); int internalFont = GetInternalFontIndex(font_); Do(p, internalFont); if (internalFont == -1) { Do(p, font_); } else if (p.mode == p.MODE_READ) { if (internalFont < 0 || internalFont >= (int)internalFonts.size()) { ERROR_LOG(Log::sceFont, "Unable to load state: unexpected internal font index."); p.SetError(p.ERROR_FAILURE); return; } font_ = internalFonts[internalFont]; } Do(p, handle_); if (s >= 2) { Do(p, open_); } else { open_ = fontLibID_ != (u32)-1; } if (s >= 3) { Do(p, mode_); } else { mode_ = FONT_OPEN_INTERNAL_FULL; } } private: int fontLibID_ = -1; Font *font_ = nullptr; u32 handle_ = 0; FontOpenMode mode_ = FONT_OPEN_INTERNAL_STINGY; bool open_ = false; DISALLOW_COPY_AND_ASSIGN(LoadedFont); }; class PostAllocCallback : public PSPAction { public: PostAllocCallback() {} static PSPAction *Create() { return new PostAllocCallback(); } void DoState(PointerWrap &p) override { auto s = p.Section("PostAllocCallback", 1, 2); if (!s) return; Do(p, fontLibID_); if (s >= 2) { Do(p, errorCodePtr_); } else { errorCodePtr_ = 0; } } void run(MipsCall &call) override; void SetFontLib(u32 fontLibID, u32 errorCodePtr) { fontLibID_ = fontLibID; errorCodePtr_ = errorCodePtr; } private: int fontLibID_ = -1; u32 errorCodePtr_ = 0; DISALLOW_COPY_AND_ASSIGN(PostAllocCallback); }; class PostOpenCallback : public PSPAction { public: PostOpenCallback() {} static PSPAction *Create() { return new PostOpenCallback(); } void DoState(PointerWrap &p) override { auto s = p.Section("PostOpenCallback", 1); if (!s) return; Do(p, fontLibID_); } void run(MipsCall &call) override; void SetFontLib(int fontLibID) { fontLibID_ = fontLibID; } private: int fontLibID_ = -1; DISALLOW_COPY_AND_ASSIGN(PostOpenCallback); }; class PostOpenAllocCallback : public PSPAction { public: PostOpenAllocCallback() {} static PSPAction *Create() { return new PostOpenAllocCallback(); } void DoState(PointerWrap &p) override { auto s = p.Section("PostOpenAllocCallback", 1); if (!s) return; Do(p, fontLibID_); Do(p, fontHandle_); Do(p, fontIndex_); } void run(MipsCall &call) override; void SetFontLib(int fontLibID) { fontLibID_ = fontLibID; } void SetFont(u32 handle, int index) { fontHandle_ = handle; fontIndex_ = index; } private: int fontLibID_ = -1; u32 fontHandle_ = 0; int fontIndex_ = -1; DISALLOW_COPY_AND_ASSIGN(PostOpenAllocCallback); }; class PostCharInfoAllocCallback : public PSPAction { public: PostCharInfoAllocCallback() {} static PSPAction *Create() { return new PostCharInfoAllocCallback(); } void DoState(PointerWrap &p) override { auto s = p.Section("PostCharInfoAllocCallback", 1); if (!s) return; Do(p, fontLibID_); } void run(MipsCall &call) override; void SetFontLib(int fontLibID) { fontLibID_ = fontLibID; } private: int fontLibID_ = -1; DISALLOW_COPY_AND_ASSIGN(PostCharInfoAllocCallback); }; class PostCharInfoFreeCallback : public PSPAction { public: PostCharInfoFreeCallback() {} static PSPAction *Create() { return new PostCharInfoFreeCallback(); } void DoState(PointerWrap &p) override { auto s = p.Section("PostCharInfoFreeCallback", 1); if (!s) return; Do(p, fontLibID_); Do(p, charInfo_); } void run(MipsCall &call) override; void SetFontLib(int fontLibID) { fontLibID_ = fontLibID; } void SetCharInfo(PSPPointer charInfo) { charInfo_ = charInfo; } private: int fontLibID_ = -1; PSPPointer charInfo_; DISALLOW_COPY_AND_ASSIGN(PostCharInfoFreeCallback); }; struct NativeFontLib { FontNewLibParams params; // TODO (what?) u32_le fontInfo1; u32_le fontInfo2; u16_le unk1; u16_le unk2; float_le hRes; float_le vRes; u32_le internalFontCount; u32_le internalFontInfo; u16_le altCharCode; u16_le unk5; }; struct FontImageRect { s16_le width; s16_le height; }; // A "fontLib" is a container of loaded fonts. // One can open either "internal" fonts or load custom fonts into a fontlib. class FontLib { public: FontLib() { // For save states only. } FontLib(FontNewLibParams *params, u32 errorCodePtr) { params_ = *params; if (params_.numFonts > 9) { params_.numFonts = 9; } // Technically, this should be four separate allocations. u32 allocSize = 0x4C + params_.numFonts * 0x4C + params_.numFonts * 0x230 + (u32)internalFonts.size() * 0xA8; PostAllocCallback *action = (PostAllocCallback *) __KernelCreateAction(actionPostAllocCallback); action->SetFontLib(GetListID(), errorCodePtr); u32 args[2] = { userDataAddr(), allocSize }; hleEnqueueCall(allocFuncAddr(), 2, args, action); } int GetListID() { return (int)(std::find(fontLibList.begin(), fontLibList.end(), this) - fontLibList.begin()); } void Done() { for (size_t i = 0; i < fonts_.size(); i++) { if (fontRefCount_[i] > 0) { CloseFont(fontMap[fonts_[i]], true); delete fontMap[fonts_[i]]; fontMap.erase(fonts_[i]); } } // TODO: The return value of this is leaking. if (handle_) { // Avoid calling free-callback on double-free if (coreState != CORE_POWERDOWN) { u32 args[2] = { userDataAddr(), (u32)handle_ }; hleEnqueueCall(freeFuncAddr(), 2, args); } } handle_ = 0; fonts_.clear(); fontRefCount_.clear(); openAllocatedAddresses_.clear(); } void AllocDone(u32 allocatedAddr) { handle_ = allocatedAddr; fonts_.resize(params_.numFonts); fontRefCount_.resize(params_.numFonts); openAllocatedAddresses_.resize(params_.numFonts); for (size_t i = 0; i < fonts_.size(); i++) { u32 addr = allocatedAddr + 0x4C + (u32)i * 0x4C; fontRefCount_[i] = 0; fonts_[i] = addr; } // Let's write out the native struct to make tests easier. // It's possible games may depend on this staying in ram, e.g. copying it, we may move to that. nfl_ = allocatedAddr; nfl_->params = params_; nfl_->fontInfo1 = allocatedAddr + 0x4C; nfl_->fontInfo2 = allocatedAddr + 0x4C + params_.numFonts * 0x4C; nfl_->unk1 = 0; nfl_->unk2 = 0; nfl_->hRes = fontHRes_; nfl_->vRes = fontVRes_; nfl_->internalFontCount = (u32)internalFonts.size(); nfl_->internalFontInfo = allocatedAddr + 0x4C + params_.numFonts * 0x4C + params_.numFonts * 0x230; nfl_->altCharCode = altCharCode_; } u32 handle() const { return handle_; } int numFonts() const { return params_.numFonts; } u32_le userDataAddr() const{ return params_.userDataAddr; } u32_le allocFuncAddr() const { return params_.allocFuncAddr; } u32_le freeFuncAddr() const { return params_.freeFuncAddr; } void SetResolution(float hres, float vres) { fontHRes_ = hres; fontVRes_ = vres; if (nfl_.IsValid()) { nfl_->hRes = hres; nfl_->vRes = vres; } } float FontHRes() const { return fontHRes_; } float FontVRes() const { return fontVRes_; } void SetAltCharCode(int charCode) { altCharCode_ = charCode; if (nfl_.IsValid()) nfl_->altCharCode = charCode; } int GetFontHandle(int index) const { return fonts_[index]; } int GetFontRefCount(Font *font) const { int foundFontIndex = FindExistingIndex(font); if (foundFontIndex >= 0) return fontRefCount_.at(foundFontIndex); return 0; } // For FONT_OPEN_USER* modes, the font will automatically be freed. LoadedFont *OpenFont(Font *font, FontOpenMode mode, int &error) { // TODO: Do something with mode, possibly save it where the PSP does in the struct. // Maybe needed in Font, though? Handlers seem... difficult to emulate. // First, check if the font is already open. We need to refcount, see font/open test. int foundFontIndex = FindExistingIndex(font); if (foundFontIndex < 0) foundFontIndex = FindFreeIndex(); if (foundFontIndex < 0 || fontRefCount_[foundFontIndex] >= MAX_FONT_REFS) { error = SCE_FONT_ERROR_TOO_MANY_OPEN_FONTS; ERROR_LOG(Log::sceFont, "OpenFont: Too many fonts opened in FontLib"); return nullptr; } if (!font->IsValid()) { error = SCE_FONT_ERROR_INVALID_FONT_DATA; ERROR_LOG(Log::sceFont, "OpenFont: Invalid font data"); return nullptr; } LoadedFont *loadedFont = nullptr; if (fontRefCount_[foundFontIndex] == 0) { loadedFont = new LoadedFont(font, mode, GetListID(), fonts_[foundFontIndex]); auto prevFont = fontMap.find(loadedFont->Handle()); if (prevFont != fontMap.end()) { // Before replacing it and forgetting about it, let's free it. delete prevFont->second; } fontMap.insert_or_assign(loadedFont->Handle(), loadedFont); } else { loadedFont = fontMap[fonts_[foundFontIndex]]; } fontRefCount_[foundFontIndex]++; // Only need to allocate the first time. if (!useAllocCallbacks || fontRefCount_[foundFontIndex] > 1) return loadedFont; u32 allocSize = 12; if (mode == FONT_OPEN_INTERNAL_STINGY) { allocSize = loadedFont->GetFont()->getStingySize(); } else if (mode == FONT_OPEN_INTERNAL_FULL) { allocSize += loadedFont->GetFont()->getSize(); } PostOpenAllocCallback *action = (PostOpenAllocCallback *)__KernelCreateAction(actionPostOpenAllocCallback); action->SetFontLib(GetListID()); action->SetFont(loadedFont->Handle(), foundFontIndex); u32 args[2] = { userDataAddr(), allocSize }; hleEnqueueCall(allocFuncAddr(), 2, args, action); return loadedFont; } void CloseFont(LoadedFont *font, bool releaseAll) { bool allowClose = true; for (size_t i = 0; i < fonts_.size(); i++) { if (fonts_[i] == font->Handle() && fontRefCount_[i] > 0) { if (releaseAll) fontRefCount_[i] = 0; else fontRefCount_[i]--; allowClose = fontRefCount_[i] == 0; bool deallocate = allowClose && openAllocatedAddresses_[i] != 0; if (deallocate && coreState != CORE_POWERDOWN) { u32 args[2] = { userDataAddr(), openAllocatedAddresses_[i] }; hleEnqueueCall(freeFuncAddr(), 2, args); openAllocatedAddresses_[i] = 0; } break; } } flushFont(); if (allowClose) font->Close(); } void flushFont() { if (charInfoBitmapAddress_ != 0 && coreState != CORE_POWERDOWN) { u32 args[2] = { userDataAddr(), charInfoBitmapAddress_ }; hleEnqueueCall(freeFuncAddr(), 2, args); charInfoBitmapAddress_ = 0; } } void DoState(PointerWrap &p) { auto s = p.Section("FontLib", 1, 3); if (!s) return; Do(p, fonts_); Do(p, fontRefCount_); Do(p, params_); Do(p, fontHRes_); Do(p, fontVRes_); Do(p, fileFontHandle_); Do(p, handle_); Do(p, altCharCode_); if (s >= 2) { Do(p, nfl_); } else { nfl_ = 0; } if (s >= 3) { Do(p, openAllocatedAddresses_); Do(p, charInfoBitmapAddress_); } else { openAllocatedAddresses_.resize(params_.numFonts); charInfoBitmapAddress_ = 0; } } void SetFileFontHandle(u32 handle) { fileFontHandle_ = handle; } u32 GetAltCharCode() const { return altCharCode_; } u32 GetOpenAllocatedAddress(int index) const { if(index < numFonts()) return openAllocatedAddresses_[index]; return 0; } void SetOpenAllocatedAddress(int index, u32 addr) { if (index < numFonts()) openAllocatedAddresses_[index] = addr; } u32 GetCharInfoBitmapAddress() const { return charInfoBitmapAddress_; } void SetCharInfoBitmapAddress(u32 addr) { charInfoBitmapAddress_ = addr; } private: int FindExistingIndex(Font *font) const { // TODO: Should this also match for memory fonts, or only internal fonts? for (auto it : fontMap) { if (it.second->GetFont() != font || it.second->GetFontLib() != this) continue; for (size_t i = 0; i < fonts_.size(); i++) { if (fonts_[i] == it.first) { return (int)i; } } } return -1; } int FindFreeIndex() const { for (size_t i = 0; i < fonts_.size(); i++) { if (fontRefCount_[i] == 0) { return (int)i; } } return -1; } std::vector fonts_; std::vector fontRefCount_; FontNewLibParams params_; float fontHRes_ = 128.0f; float fontVRes_ = 128.0f; int fileFontHandle_ = -1; int handle_ = -1; int altCharCode_ = 0x5F; std::vector openAllocatedAddresses_; u32 charInfoBitmapAddress_ = 0; PSPPointer nfl_{}; DISALLOW_COPY_AND_ASSIGN(FontLib); }; void PostAllocCallback::run(MipsCall &call) { INFO_LOG(Log::sceFont, "Entering PostAllocCallback::run"); u32 v0 = currentMIPS->r[MIPS_REG_V0]; if (v0 == 0) { // TODO: Who deletes fontLib? if (errorCodePtr_) Memory::Write_U32(SCE_FONT_ERROR_OUT_OF_MEMORY, errorCodePtr_); call.setReturnValue(0); } else { _dbg_assert_(fontLibID_ >= 0); FontLib *fontLib = fontLibList[fontLibID_]; fontLib->AllocDone(v0); fontLibMap[fontLib->handle()] = fontLibID_; // This is the same as v0 above. call.setReturnValue(fontLib->handle()); } INFO_LOG(Log::sceFont, "Leaving PostAllocCallback::run"); } void PostOpenCallback::run(MipsCall &call) { _dbg_assert_(fontLibID_ >= 0); FontLib *fontLib = fontLibList[fontLibID_]; u32 v0 = currentMIPS->r[MIPS_REG_V0]; fontLib->SetFileFontHandle(v0); } void PostOpenAllocCallback::run(MipsCall &call) { _dbg_assert_(fontLibID_ >= 0); FontLib *fontLib = fontLibList[fontLibID_]; u32 v0 = currentMIPS->r[MIPS_REG_V0]; fontLib->SetOpenAllocatedAddress(fontIndex_, v0); } void PostCharInfoAllocCallback::run(MipsCall &call) { _dbg_assert_(fontLibID_ >= 0); FontLib *fontLib = fontLibList[fontLibID_]; u32 v0 = currentMIPS->r[MIPS_REG_V0]; if (v0 == 0) { call.setReturnValue(SCE_FONT_ERROR_OUT_OF_MEMORY); // From JPCSP, if alloc size is 0, still this error value? } else { fontLib->SetCharInfoBitmapAddress(v0); } } void PostCharInfoFreeCallback::run(MipsCall &call) { FontLib *fontLib = fontLibList[fontLibID_]; fontLib->SetCharInfoBitmapAddress(0); u32 allocSize = charInfo_->bitmapWidth * charInfo_->bitmapHeight; PostCharInfoAllocCallback *action = (PostCharInfoAllocCallback *)__KernelCreateAction(actionPostCharInfoAllocCallback); action->SetFontLib(fontLibID_); u32 args[2] = { fontLib->userDataAddr(), allocSize }; hleEnqueueCall(fontLib->allocFuncAddr(), 2, args, action); } inline bool LoadedFont::GetCharInfo(int charCode, PGFCharInfo *charInfo, int glyphType) const { _dbg_assert_(IsValid()); auto fontLib = GetFontLib(); int altCharCode = fontLib == NULL ? -1 : fontLib->GetAltCharCode(); return GetPGF()->GetCharInfo(charCode, charInfo, altCharCode, glyphType); } inline void LoadedFont::DrawCharacter(const GlyphImage *image, int clipX, int clipY, int clipWidth, int clipHeight, int charCode, int glyphType) const { _dbg_assert_(IsValid()); auto fontLib = GetFontLib(); int altCharCode = fontLib == NULL ? -1 : fontLib->GetAltCharCode(); GetPGF()->DrawCharacter(image, clipX, clipY, clipWidth, clipHeight, charCode, altCharCode, glyphType); } static FontLib *GetFontLib(u32 handle) { auto iter = fontLibMap.find(handle); if (iter != fontLibMap.end()) { return fontLibList[iter->second]; } return nullptr; } static LoadedFont *GetLoadedFont(u32 handle, bool allowClosed) { auto iter = fontMap.find(handle); if (iter != fontMap.end()) { if (iter->second->IsOpen() || allowClosed) { return fontMap[handle]; } else { ERROR_LOG(Log::sceFont, "Font exists but is closed, which was not allowed in this call."); return nullptr; } } else { ERROR_LOG(Log::sceFont, "No font with handle %08x", handle); return nullptr; } } static void __LoadInternalFonts() { if (internalFonts.size()) { // Fonts already loaded. return; } const std::string fontPath = "flash0:/font/"; const std::string fontOverridePath = "ms0:/PSP/flash0/font/"; const std::string gameFontPath = "disc0:/PSP_GAME/USRDIR/"; if (!pspFileSystem.GetFileInfo(fontPath).exists) { pspFileSystem.MkDir(fontPath); } if ((pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/zh_gb.pgf").exists) && (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/oldfont.prx").exists)) { for (size_t i = 0; i < ARRAY_SIZE(fontRegistry); i++) { const FontRegistryEntry &entry = fontRegistry[i]; std::string fontFilename = gameFontPath + entry.fileName; std::vector buffer; if (pspFileSystem.ReadEntireFile(fontFilename, buffer) < 0) { ERROR_LOG(Log::sceFont, "Failed opening font %s", fontFilename.c_str()); continue; } DEBUG_LOG(Log::sceFont, "Loading internal font %s (%i bytes)", fontFilename.c_str(), (int)buffer.size()); internalFonts.push_back(new Font(buffer, entry)); DEBUG_LOG(Log::sceFont, "Loaded font %s", fontFilename.c_str()); return; } } for (size_t i = 0; i < ARRAY_SIZE(fontRegistry); i++) { const FontRegistryEntry &entry = fontRegistry[i]; std::vector buffer; bool bufferRead = false; std::string fontFilename = gameFontPath + entry.fileName; bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0; if (!bufferRead) { // No game font, let's try override path. fontFilename = fontOverridePath + entry.fileName; bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0; } if (!bufferRead) { // No override, let's use the default path. fontFilename = fontPath + entry.fileName; bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer) >= 0; } if (bufferRead) { DEBUG_LOG(Log::sceFont, "Loading internal font %s (%i bytes)", fontFilename.c_str(), (int)buffer.size()); internalFonts.push_back(new Font(buffer, entry)); DEBUG_LOG(Log::sceFont, "Loaded font %s", fontFilename.c_str()); } else if (!entry.ignoreIfMissing) { WARN_LOG(Log::sceFont, "Font file not found: %s", fontFilename.c_str()); } } } int GetInternalFontIndex(Font *font) { for (size_t i = 0; i < internalFonts.size(); i++) { if (internalFonts[i] == font) return (int)i; } return -1; } void __FontInit() { useAllocCallbacks = true; actionPostAllocCallback = __KernelRegisterActionType(PostAllocCallback::Create); actionPostOpenCallback = __KernelRegisterActionType(PostOpenCallback::Create); actionPostOpenAllocCallback = __KernelRegisterActionType(PostOpenAllocCallback::Create); actionPostCharInfoAllocCallback = __KernelRegisterActionType(PostCharInfoAllocCallback::Create); actionPostCharInfoFreeCallback = __KernelRegisterActionType(PostCharInfoFreeCallback::Create); } void __FontShutdown() { for (const auto &[_, font] : fontMap) { if (font->IsValid()) { FontLib *fontLib = font->GetFontLib(); if (fontLib) { fontLib->CloseFont(font, true); } } else { ERROR_LOG(Log::HLE, "__FontShutdown: Bad entry in fontMap"); } delete font; } fontMap.clear(); for (auto *fontlib : fontLibList) { delete fontlib; } fontLibList.clear(); fontLibMap.clear(); for (auto *font : internalFonts) { delete font; } internalFonts.clear(); } void __FontDoState(PointerWrap &p) { auto s = p.Section("sceFont", 1, 3); if (!s) return; bool needInternalFonts = true; if (s >= 3) { // If we loaded internal fonts, we need to load them when loading the state. needInternalFonts = !internalFonts.empty(); Do(p, needInternalFonts); } if (needInternalFonts && p.mode == PointerWrap::MODE_READ) __LoadInternalFonts(); Do(p, fontLibList); Do(p, fontLibMap); Do(p, fontMap); Do(p, actionPostAllocCallback); __KernelRestoreActionType(actionPostAllocCallback, PostAllocCallback::Create); Do(p, actionPostOpenCallback); __KernelRestoreActionType(actionPostOpenCallback, PostOpenCallback::Create); if (s >= 2) { Do(p, actionPostOpenAllocCallback); __KernelRestoreActionType(actionPostOpenAllocCallback, PostOpenAllocCallback::Create); Do(p, actionPostCharInfoAllocCallback); __KernelRestoreActionType(actionPostCharInfoAllocCallback, PostCharInfoAllocCallback::Create); Do(p, actionPostCharInfoFreeCallback); __KernelRestoreActionType(actionPostCharInfoFreeCallback, PostCharInfoFreeCallback::Create); } else { useAllocCallbacks = false; } } static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) { // Lazy load internal fonts, only when font library first inited. __LoadInternalFonts(); auto params = PSPPointer::Create(paramPtr); auto errorCode = PSPPointer::Create(errorCodePtr); if (!params.IsValid() || !errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontNewLib(%08x, %08x): invalid addresses", paramPtr, errorCodePtr); // The PSP would crash in this situation, not a real error code. return hleLogError(Log::sceFont, SCE_KERNEL_ERROR_ILLEGAL_ADDR); } if (!Memory::IsValidAddress(params->allocFuncAddr) || !Memory::IsValidAddress(params->freeFuncAddr)) { ERROR_LOG_REPORT(Log::sceFont, "sceFontNewLib(%08x, %08x): missing alloc func", paramPtr, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleLogError(Log::sceFont, 0); } *errorCode = 0; FontLib *newLib = new FontLib(params, errorCodePtr); fontLibList.push_back(newLib); // The game should never see this value, the return value is replaced // by the action. Except if we disable the alloc, in this case we return // the handle correctly here. return hleDelayResult(hleLogInfo(Log::sceFont, newLib->handle()), "new fontlib", 30000); } static int sceFontDoneLib(u32 fontLibHandle) { FontLib *fl = GetFontLib(fontLibHandle); if (fl) { fl->Done(); return hleLogInfo(Log::sceFont, 0); } return hleLogWarning(Log::sceFont, 0, "invalid font lib"); } // Open internal font into a FontLib static u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { // Would crash on the PSP. return hleLogError(Log::sceFont, -1, "invalid error address"); } FontLib *fontLib = GetFontLib(libHandle); if (!fontLib) { *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleLogWarning(Log::sceFont, 0, "invalid font lib"); } if (index >= internalFonts.size()) { *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleLogWarning(Log::sceFont, 0, "invalid font index"); } FontOpenMode openMode = mode != 1 ? FONT_OPEN_INTERNAL_STINGY : FONT_OPEN_INTERNAL_FULL; LoadedFont *font = fontLib->OpenFont(internalFonts[index], openMode, *errorCode); if (font) { *errorCode = 0; // Delay only on the first open. if (fontLib->GetFontRefCount(internalFonts[index]) == 1) return hleDelayResult(hleLogDebug(Log::sceFont, font->Handle()), "font open", 10000); return hleLogDebug(Log::sceFont, font->Handle()); } return 0; } // Open a user font in RAM into a FontLib static u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontPtr, u32 memoryFontLength, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { return hleReportError(Log::sceFont, -1, "invalid error address"); } if (!Memory::IsValidAddress(memoryFontPtr)) { *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleReportError(Log::sceFont, 0, "invalid address"); } FontLib *fontLib = GetFontLib(libHandle); if (!fontLib) { *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleReportError(Log::sceFont, 0, "invalid font lib"); } if (memoryFontLength == 0) { *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleReportError(Log::sceFont, 0, "invalid size"); } const u8 *fontData = Memory::GetPointer(memoryFontPtr); // Games are able to overstate the size of a font. Let's avoid crashing when we memcpy() it. // Unsigned 0xFFFFFFFF is treated as max, but that's impossible, so let's clamp to 64MB. if (memoryFontLength > 0x03FFFFFF) memoryFontLength = 0x03FFFFFF; while (!Memory::IsValidAddress(memoryFontPtr + memoryFontLength - 1)) { --memoryFontLength; } Font *f = new Font(fontData, memoryFontLength); LoadedFont *font = fontLib->OpenFont(f, FONT_OPEN_USERBUFFER, *errorCode); if (font) { *errorCode = 0; return hleLogDebug(Log::sceFont, font->Handle()); } delete f; return hleNoLog(0); } // Open a user font in a file into a FontLib static u32 sceFontOpenUserFile(u32 libHandle, const char *fileName, u32 mode, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { return hleReportError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid error address"); } if (!fileName) { *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleReportError(Log::sceFont, 0, "invalid filename"); } FontLib *fontLib = GetFontLib(libHandle); if (!fontLib) { *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleReportError(Log::sceFont, 0, "invalid font lib"); } // TODO: Technically, we only do this if mode = 1. Mode 0 uses the handlers. if (mode != 1) { WARN_LOG_REPORT(Log::sceFont, "Loading file directly instead of using handlers: %s", fileName); } std::vector buffer; if (pspFileSystem.ReadEntireFile(fileName, buffer) != 0) { *errorCode = SCE_FONT_ERROR_HANDLER_OPEN_FAILED; return hleLogError(Log::sceFont, 0, "file does not exist"); } Font *f = new Font(buffer); FontOpenMode openMode = mode == 0 ? FONT_OPEN_USERFILE_HANDLERS : FONT_OPEN_USERFILE_FULL; LoadedFont *font = fontLib->OpenFont(f, openMode, *errorCode); if (font) { *errorCode = 0; return hleLogInfo(Log::sceFont, font->Handle()); } delete f; // Message was already logged (or was it?) return hleNoLog(0); } static int sceFontClose(u32 fontHandle) { LoadedFont *font = GetLoadedFont(fontHandle, false); if (font) { FontLib *fontLib = font->GetFontLib(); if (fontLib) { fontLib->CloseFont(font, false); } return hleLogDebug(Log::sceFont, 0); } else { return hleLogError(Log::sceFont, 0, "sceFontClose(%x) - font not open?", fontHandle); } } static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { return hleReportError(Log::sceFont, SCE_KERNEL_ERROR_INVALID_ARGUMENT, "invalid error address"); } FontLib *fontLib = GetFontLib(libHandle); if (!fontLib) { *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleReportError(Log::sceFont, 0, "invalid font lib"); } if (!Memory::IsValidAddress(fontStylePtr)) { // Yes, actually. Must've been a typo in the library. *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleReportError(Log::sceFont, 0, "invalid style address"); } auto requestedStyle = PSPPointer::Create(fontStylePtr); DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight); // Find the first nearest match for H/V, OR the last exact match for others. float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes(); float vRes = requestedStyle->fontVRes > 0.0f ? (float)requestedStyle->fontVRes : fontLib->FontVRes(); Font *optimumFont = 0; Font *nearestFont = 0; float nearestDist = std::numeric_limits::infinity(); if (PSP_CoreParameter().compat.flags().Fontltn12Hack && requestedStyle->fontLanguage == 2) { for (size_t j = 0; j < internalFonts.size(); j++) { const auto &tempmatchStyle = internalFonts[j]->GetFontStyle(); const std::string str(tempmatchStyle.fontFileName); if (str == "ltn12.pgf") { optimumFont = internalFonts[j]; *errorCode = 0; return hleLogInfo(Log::sceFont, GetInternalFontIndex(optimumFont)); } } } for (size_t i = 0; i < internalFonts.size(); i++) { MatchQuality q = internalFonts[i]->MatchesStyle(*requestedStyle); if (q != MATCH_NONE) { const auto &matchStyle = internalFonts[i]->GetFontStyle(); if (requestedStyle->fontH > 0.0f) { float hDist = fabs(matchStyle.fontHRes * matchStyle.fontH - hRes * requestedStyle->fontH); if (hDist < nearestDist) { nearestDist = hDist; nearestFont = internalFonts[i]; } } if (requestedStyle->fontV > 0.0f) { // Appears to be a bug? It seems to match H instead of V. float vDist = fabs(matchStyle.fontVRes * matchStyle.fontV - vRes * requestedStyle->fontH); if (vDist < nearestDist) { nearestDist = vDist; nearestFont = internalFonts[i]; } } } if (q == MATCH_GOOD) { optimumFont = internalFonts[i]; } } if (nearestFont) { optimumFont = nearestFont; } if (optimumFont) { *errorCode = 0; return hleLogInfo(Log::sceFont, GetInternalFontIndex(optimumFont)); } else { *errorCode = 0; return hleLogInfo(Log::sceFont, 0); } } // Returns the font index, not handle static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontFindFont(%x, %x, %x): invalid error address", libHandle, fontStylePtr, errorCodePtr); return hleNoLog(SCE_KERNEL_ERROR_INVALID_ARGUMENT); } FontLib *fontLib = GetFontLib(libHandle); if (!fontLib) { ERROR_LOG_REPORT(Log::sceFont, "sceFontFindFont(%08x, %08x, %08x): invalid font lib", libHandle, fontStylePtr, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleNoLog(0); } if (!Memory::IsValidAddress(fontStylePtr)) { ERROR_LOG_REPORT(Log::sceFont, "sceFontFindFont(%08x, %08x, %08x): invalid style address", libHandle, fontStylePtr, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_PARAMETER; return hleNoLog(0); } DEBUG_LOG(Log::sceFont, "sceFontFindFont(%x, %x, %x)", libHandle, fontStylePtr, errorCodePtr); auto requestedStyle = PSPPointer::Create(fontStylePtr); DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight); // Find the closest exact match for the fields specified. float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes(); for (size_t i = 0; i < internalFonts.size(); i++) { if (internalFonts[i]->MatchesStyle(*requestedStyle) != MATCH_NONE) { const auto &matchStyle = internalFonts[i]->GetFontStyle(); if (requestedStyle->fontH > 0.0f) { float hDist = fabs(matchStyle.fontHRes * matchStyle.fontH - hRes * requestedStyle->fontH); if (hDist > 0.001f) { continue; } } else if (requestedStyle->fontV > 0.0f) { // V seems to be ignored, unless H isn't specified. // If V is specified alone, the match always fails. continue; } *errorCode = 0; return hleNoLog((int)i); } } *errorCode = 0; return hleLogWarning(Log::sceFont, -1); } static int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) { if (!Memory::IsValidAddress(fontInfoPtr)) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad fontInfo pointer"); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } auto fi = PSPPointer::Create(fontInfoPtr); font->GetPGF()->GetFontInfo(fi); fi->fontStyle = font->GetFont()->GetFontStyle(); return hleLogDebug(Log::sceFont, 0); } // It says FontInfo but it means Style - this is like sceFontGetFontList(). static int sceFontGetFontInfoByIndexNumber(u32 libHandle, u32 fontInfoPtr, u32 index) { auto fontStyle = PSPPointer::Create(fontInfoPtr); FontLib *fl = GetFontLib(libHandle); if (!fl || fl->handle() == 0) { int error = !fl ? SCE_FONT_ERROR_INVALID_LIBID : SCE_FONT_ERROR_INVALID_PARAMETER; return hleLogError(Log::sceFont, error, "invalid font lib"); } if (index >= internalFonts.size()) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid font index"); } if (!fontStyle.IsValid()) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid info pointer"); } auto font = internalFonts[index]; *fontStyle = font->GetFontStyle(); return hleLogDebug(Log::sceFont, 0); } static int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) { charCode &= 0xffff; if (!Memory::IsValidAddress(charInfoPtr)) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad charInfo pointer"); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { // The PSP crashes, but we assume it'd work like sceFontGetFontInfo(), and not touch charInfo. return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } DEBUG_LOG(Log::sceFont, "sceFontGetCharInfo(%08x, %i, %08x)", fontHandle, charCode, charInfoPtr); auto charInfo = PSPPointer::Create(charInfoPtr); font->GetCharInfo(charCode, charInfo); if (!useAllocCallbacks) return hleLogDebug(Log::sceFont, 0); u32 allocSize = charInfo->bitmapWidth * charInfo->bitmapHeight; if (font->GetFontLib() && (charInfo->sfp26AdvanceH != 0 || charInfo->sfp26AdvanceV != 0)) { if (font->GetFontLib()->GetCharInfoBitmapAddress() != 0) { PostCharInfoFreeCallback *action = (PostCharInfoFreeCallback *)__KernelCreateAction(actionPostCharInfoFreeCallback); action->SetFontLib(font->GetFontLib()->GetListID()); action->SetCharInfo(charInfo); u32 args[2] = { font->GetFontLib()->userDataAddr(), font->GetFontLib()->GetCharInfoBitmapAddress() }; hleEnqueueCall(font->GetFontLib()->freeFuncAddr(), 2, args, action); } else { PostCharInfoAllocCallback *action = (PostCharInfoAllocCallback *)__KernelCreateAction(actionPostCharInfoAllocCallback); action->SetFontLib(font->GetFontLib()->GetListID()); u32 args[2] = { font->GetFontLib()->userDataAddr(), allocSize }; hleEnqueueCall(font->GetFontLib()->allocFuncAddr(), 2, args, action); } } return hleLogDebug(Log::sceFont, 0); } static int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) { charCode &= 0xffff; if (!Memory::IsValidAddress(charInfoPtr)) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad charInfo pointer"); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } DEBUG_LOG(Log::sceFont, "sceFontGetShadowInfo(%08x, %i, %08x)", fontHandle, charCode, charInfoPtr); auto charInfo = PSPPointer::Create(charInfoPtr); font->GetCharInfo(charCode, charInfo, FONT_PGF_SHADOWGLYPH); return hleLogDebug(Log::sceFont, 0); } static int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) { charCode &= 0xffff; auto charRect = PSPPointer::Create(charRectPtr); LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } if (!charRect.IsValid()) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid rect pointer"); } PGFCharInfo charInfo; font->GetCharInfo(charCode, &charInfo); charRect->width = charInfo.bitmapWidth; charRect->height = charInfo.bitmapHeight; return hleLogDebug(Log::sceFont, 0, "w: %d h: %d", charInfo.bitmapWidth, charInfo.bitmapHeight); } static int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) { charCode &= 0xffff; auto charRect = PSPPointer::Create(charRectPtr); LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } if (!charRect.IsValid()) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid rect pointer"); } PGFCharInfo charInfo; font->GetCharInfo(charCode, &charInfo, FONT_PGF_SHADOWGLYPH); charRect->width = charInfo.bitmapWidth; charRect->height = charInfo.bitmapHeight; return hleLogDebug(Log::sceFont, 0); } static int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) { charCode &= 0xffff; if (!Memory::IsValidAddress(glyphImagePtr)) { ERROR_LOG(Log::sceFont, "sceFontGetCharGlyphImage(%x, %x, %x): bad glyphImage pointer", fontHandle, charCode, glyphImagePtr); return SCE_FONT_ERROR_INVALID_PARAMETER; } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } auto glyph = PSPPointer::Create(glyphImagePtr); font->DrawCharacter(glyph, -1, -1, -1, -1, charCode, FONT_PGF_CHARGLYPH); return hleLogDebug(Log::sceFont, 0); } static int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) { charCode &= 0xffff; if (!Memory::IsValidAddress(glyphImagePtr)) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad glyphImage pointer"); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad font"); } auto glyph = PSPPointer::Create(glyphImagePtr); font->DrawCharacter(glyph, clipXPos, clipYPos, clipWidth, clipHeight, charCode, FONT_PGF_CHARGLYPH); return hleLogDebug(Log::sceFont, 0); } static int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode) { charCode &= 0xffff; FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontSetAltCharacterCode(%08x, %08x): invalid font lib", fontLibHandle, charCode); return hleNoLog(SCE_FONT_ERROR_INVALID_LIBID); } fl->SetAltCharCode(charCode & 0xFFFF); return hleLogInfo(Log::sceFont, 0); } static int sceFontFlush(u32 fontHandle) { LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { ERROR_LOG_REPORT(Log::sceFont, "sceFontFlush(%08x): bad font", fontHandle); return hleNoLog(SCE_FONT_ERROR_INVALID_PARAMETER); } if (font->GetFontLib()) { font->GetFontLib()->flushFont(); } return hleLogDebug(Log::sceFont, 0); } // One would think that this should loop through the fonts loaded in the fontLibHandle, // but it seems not. static int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, int numFonts) { auto fontStyles = PSPPointer::Create(fontStylePtr); FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontGetFontList(%08x, %08x, %i): invalid font lib", fontLibHandle, fontStylePtr, numFonts); return SCE_FONT_ERROR_INVALID_LIBID; } if (!fontStyles.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontGetFontList(%08x, %08x, %i): invalid style pointer", fontLibHandle, fontStylePtr, numFonts); return SCE_FONT_ERROR_INVALID_PARAMETER; } DEBUG_LOG(Log::sceFont, "sceFontGetFontList(%08x, %08x, %i)", fontLibHandle, fontStylePtr, numFonts); if (fl->handle() != 0) { numFonts = std::min(numFonts, (int)internalFonts.size()); for (int i = 0; i < numFonts; i++) fontStyles[i] = internalFonts[i]->GetFontStyle(); } return hleDelayResult(hleLogDebug(Log::sceFont, 0), "font list read", 100); } static int sceFontGetNumFontList(u32 fontLibHandle, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "invalid error address"); } FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return hleLogError(Log::sceFont, 0, "invalid font lib"); } *errorCode = 0; return hleLogDebug(Log::sceFont, fl->handle() == 0 ? 0 : (int)internalFonts.size()); } static int sceFontSetResolution(u32 fontLibHandle, float hRes, float vRes) { FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_LIBID, "invalid font lib"); } if (hRes <= 0.0f || vRes <= 0.0f) { return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "negative value in hRes %f or vRes %f", hRes, vRes); } fl->SetResolution(hRes, vRes); return hleLogInfo(Log::sceFont, 0); } static float sceFontPixelToPointH(int fontLibHandle, float fontPixelsH, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPixelToPointH(%08x, %f, %08x): invalid error address", fontLibHandle, fontPixelsH, errorCodePtr); return 0.0f; } FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPixelToPointH(%08x, %f, %08x): invalid font lib", fontLibHandle, fontPixelsH, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return 0.0f; } DEBUG_LOG(Log::sceFont, "sceFontPixelToPointH(%08x, %f, %08x)", fontLibHandle, fontPixelsH, errorCodePtr); *errorCode = 0; return fontPixelsH * pointDPI / fl->FontHRes(); } static float sceFontPixelToPointV(int fontLibHandle, float fontPixelsV, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPixelToPointV(%08x, %f, %08x): invalid error address", fontLibHandle, fontPixelsV, errorCodePtr); return 0.0f; } FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPixelToPointV(%08x, %f, %08x): invalid font lib", fontLibHandle, fontPixelsV, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return 0.0f; } DEBUG_LOG(Log::sceFont, "sceFontPixelToPointV(%08x, %f, %08x)", fontLibHandle, fontPixelsV, errorCodePtr); *errorCode = 0; return fontPixelsV * pointDPI / fl->FontVRes(); } static float sceFontPointToPixelH(int fontLibHandle, float fontPointsH, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPointToPixelH(%08x, %f, %08x): invalid error address", fontLibHandle, fontPointsH, errorCodePtr); return 0.0f; } FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPointToPixelH(%08x, %f, %08x): invalid font lib", fontLibHandle, fontPointsH, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return 0.0f; } DEBUG_LOG(Log::sceFont, "sceFontPointToPixelH(%08x, %f, %08x)", fontLibHandle, fontPointsH, errorCodePtr); *errorCode = 0; return fontPointsH * fl->FontHRes() / pointDPI; } static float sceFontPointToPixelV(int fontLibHandle, float fontPointsV, u32 errorCodePtr) { auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPointToPixelV(%08x, %f, %08x): invalid error address", fontLibHandle, fontPointsV, errorCodePtr); return 0.0f; } FontLib *fl = GetFontLib(fontLibHandle); if (!fl) { ERROR_LOG_REPORT(Log::sceFont, "sceFontPointToPixelV(%08x, %f, %08x): invalid font lib", fontLibHandle, fontPointsV, errorCodePtr); *errorCode = SCE_FONT_ERROR_INVALID_LIBID; return 0.0f; } DEBUG_LOG(Log::sceFont, "sceFontPointToPixelV(%08x, %f, %08x)", fontLibHandle, fontPointsV, errorCodePtr); *errorCode = 0; return fontPointsV * fl->FontVRes() / pointDPI; } static int sceFontCalcMemorySize() { ERROR_LOG_REPORT(Log::sceFont, "UNIMPL sceFontCalcMemorySize()"); return hleNoLog(0); } static int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) { charCode &= 0xffff; if (!Memory::IsValidAddress(glyphImagePtr)) { ERROR_LOG(Log::sceFont, "sceFontGetShadowGlyphImage(%x, %x, %x): bad glyphImage pointer", fontHandle, charCode, glyphImagePtr); return hleNoLog(SCE_FONT_ERROR_INVALID_PARAMETER); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { ERROR_LOG_REPORT(Log::sceFont, "sceFontGetShadowGlyphImage(%x, %x, %x): bad font", fontHandle, charCode, glyphImagePtr); return hleNoLog(SCE_FONT_ERROR_INVALID_PARAMETER); } auto glyph = PSPPointer::Create(glyphImagePtr); font->DrawCharacter(glyph, -1, -1, -1, -1, charCode, FONT_PGF_SHADOWGLYPH); return hleLogDebug(Log::sceFont, 0); } static int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) { charCode &= 0xffff; if (!Memory::IsValidAddress(glyphImagePtr)) { ERROR_LOG(Log::sceFont, "sceFontGetShadowGlyphImage_Clip(%08x, %i, %08x, %i, %i, %i, %i): bad glyphImage pointer", fontHandle, charCode, glyphImagePtr, clipXPos, clipYPos, clipWidth, clipHeight); return hleNoLog(SCE_FONT_ERROR_INVALID_PARAMETER); } LoadedFont *font = GetLoadedFont(fontHandle, true); if (!font) { ERROR_LOG_REPORT(Log::sceFont, "sceFontGetShadowGlyphImage_Clip(%08x, %i, %08x, %i, %i, %i, %i): bad font", fontHandle, charCode, glyphImagePtr, clipXPos, clipYPos, clipWidth, clipHeight); return hleNoLog(SCE_FONT_ERROR_INVALID_PARAMETER); } auto glyph = PSPPointer::Create(glyphImagePtr); font->DrawCharacter(glyph, clipXPos, clipYPos, clipWidth, clipHeight, charCode, FONT_PGF_SHADOWGLYPH); return hleLogDebug(Log::sceFont, 0); } // sceLibFont is a user level library so it can touch the stack. Some games appear to rely a bit of stack // being wiped - although in reality, it won't be wiped with just zeroes.. const HLEFunction sceLibFont[] = { {0X67F17ED7, &WrapU_UU, "sceFontNewLib", 'x', "xx", HLE_CLEAR_STACK_BYTES, 0x5A0 }, {0X574B6FBC, &WrapI_U, "sceFontDoneLib", 'i', "x", HLE_CLEAR_STACK_BYTES, 0x2C }, {0X48293280, &WrapI_UFF, "sceFontSetResolution", 'i', "xff" }, {0X27F6E642, &WrapI_UU, "sceFontGetNumFontList", 'i', "xx" }, {0XBC75D85B, &WrapI_UUI, "sceFontGetFontList", 'i', "xxi", HLE_CLEAR_STACK_BYTES, 0x31C }, {0X099EF33C, &WrapI_UUU, "sceFontFindOptimumFont", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0xF0 }, {0X681E61A7, &WrapI_UUU, "sceFontFindFont", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x40 }, {0X2F67356A, &WrapI_V, "sceFontCalcMemorySize", 'i', "" }, {0X5333322D, &WrapI_UUU, "sceFontGetFontInfoByIndexNumber", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x20 }, {0XA834319D, &WrapU_UUUU, "sceFontOpen", 'x', "xxxp", HLE_CLEAR_STACK_BYTES, 0x460 }, {0X57FCB733, &WrapU_UCUU, "sceFontOpenUserFile", 'x', "xsxp" }, {0XBB8E7FE6, &WrapU_UUUU, "sceFontOpenUserMemory", 'x', "xxxp", HLE_CLEAR_STACK_BYTES, 0x440 /*from JPCSP*/ }, {0X3AEA8CB6, &WrapI_U, "sceFontClose", 'i', "x", HLE_CLEAR_STACK_BYTES, 0x54 }, {0X0DA7535E, &WrapI_UU, "sceFontGetFontInfo", 'i', "xx" }, {0XDCC80C2F, &WrapI_UUU, "sceFontGetCharInfo", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x110 }, {0XAA3DE7B5, &WrapI_UUU, "sceFontGetShadowInfo", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x150 }, {0X5C3E4A9E, &WrapI_UUU, "sceFontGetCharImageRect", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x120 }, {0X48B06520, &WrapI_UUU, "sceFontGetShadowImageRect", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x150 }, {0X980F4895, &WrapI_UUU, "sceFontGetCharGlyphImage", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x120 }, {0XCA1E6945, &WrapI_UUUIIII, "sceFontGetCharGlyphImage_Clip", 'i', "xxxiiii", HLE_CLEAR_STACK_BYTES, 0x130 }, {0X74B21701, &WrapF_IFU, "sceFontPixelToPointH", 'f', "ifx", HLE_CLEAR_STACK_BYTES, 0x10 }, {0XF8F0752E, &WrapF_IFU, "sceFontPixelToPointV", 'f', "ifx", HLE_CLEAR_STACK_BYTES, 0x10 }, {0X472694CD, &WrapF_IFU, "sceFontPointToPixelH", 'f', "ifx" }, {0X3C4B7E82, &WrapF_IFU, "sceFontPointToPixelV", 'f', "ifx" }, {0XEE232411, &WrapI_UU, "sceFontSetAltCharacterCode", 'i', "xx" }, {0X568BE516, &WrapI_UUU, "sceFontGetShadowGlyphImage", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x160 }, {0X5DCF6858, &WrapI_UUUIIII, "sceFontGetShadowGlyphImage_Clip", 'i', "xxxiiii", HLE_CLEAR_STACK_BYTES, 0x170 }, {0X02D7F94B, &WrapI_U, "sceFontFlush", 'i', "x" }, }; void Register_sceFont() { RegisterHLEModule("sceLibFont", ARRAY_SIZE(sceLibFont), sceLibFont); } void Register_sceLibFttt() { RegisterHLEModule("sceLibFttt", ARRAY_SIZE(sceLibFont), sceLibFont); }