mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-07 21:23:29 +02:00
The disc-updater install ran when ltn0.pgf was missing, so a font set unpacked from an old UMD looked complete forever, and a later game wanting a font its own firmware added silently got a bundled substitute instead. Requiring the whole registry doesn't work either: firmwares older than a font can never satisfy it, so we'd unpack the same updater on every launch and announce it each time. What settles it is that a game can't ask for a font that didn't exist when it was made. Record the earliest firmware known to ship each font in the registry, and only require the ones the running game's firmware would have had. The version comes from PARAM.SFO's PSP_SYSTEM_VER, with the bundled updater's version as a fallback. That keeps the whole thing stateless - nothing recorded that could go stale when flash0 or the ini gets moved around. Survey of a large library, unpacking flash0:/font from each disc's updater across firmware 1.50 to 6.60: jpn0 and ltn0..ltn15 are in every one of them, and kr0.pgf is the only registry font that arrived later - absent in 1.50, present from 1.52. Uses one directory listing rather than a stat per font, since on Android's scoped storage the individual checks are slow.
1792 lines
65 KiB
C++
1792 lines
65 KiB
C++
#include "Common/TimeUtil.h"
|
|
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
#include "Common/Serialize/Serializer.h"
|
|
#include "Common/Serialize/SerializeFuncs.h"
|
|
#include "Common/Serialize/SerializeMap.h"
|
|
#include "Common/StringUtils.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/ELF/ParamSFO.h"
|
|
#include "Core/Font/PGF.h"
|
|
#include "Core/Util/PathUtil.h"
|
|
#include "Core/Util/PSARUnpack.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Common/System/OSD.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;
|
|
// The earliest firmware known to ship this font, as major * 100 + minor, or 0 if every
|
|
// firmware has it. A game can't want a font that didn't exist when it was made, so this is
|
|
// what keeps us from forever chasing a file its own disc could never provide.
|
|
int minFirmware;
|
|
};
|
|
|
|
// The minFirmware values come from unpacking flash0:/font out of the updater on every disc in a
|
|
// large library, covering firmware 1.50 through 6.60. jpn0 and ltn0..ltn15 are in all of them;
|
|
// kr0.pgf is the only one that ever appeared later, absent in 1.50 and present from 1.52 on.
|
|
// (The updaters carry more than we register: shadow.pgf in 2.00-2.60, gb3s1518.bwfon from 2.60,
|
|
// arib.pgf and imagefont.bin from 3.71.)
|
|
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, false, 152 },
|
|
};
|
|
|
|
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<Font *> internalFonts;
|
|
// However, these we must save - but we could take a shortcut
|
|
// for LoadedFonts that point to internal fonts.
|
|
static std::map<u32, LoadedFont *> fontMap;
|
|
static std::map<u32, int> fontLibMap;
|
|
// We keep this list to avoid ptr references, even before alloc is called.
|
|
static std::vector<FontLib *> 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<u8> &data) {
|
|
Init(&data[0], data.size());
|
|
}
|
|
|
|
Font(const std::vector<u8> &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<PGFCharInfo> charInfo) { charInfo_ = charInfo; }
|
|
|
|
private:
|
|
int fontLibID_ = -1;
|
|
PSPPointer<PGFCharInfo> 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 (const 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<u32> fonts_;
|
|
std::vector<u32> fontRefCount_;
|
|
|
|
FontNewLibParams params_;
|
|
float fontHRes_ = 128.0f;
|
|
float fontVRes_ = 128.0f;
|
|
int fileFontHandle_ = -1;
|
|
int handle_ = -1;
|
|
int altCharCode_ = 0x5F;
|
|
std::vector<u32> openAllocatedAddresses_;
|
|
u32 charInfoBitmapAddress_ = 0;
|
|
PSPPointer<NativeFontLib> 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::WriteOrException_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;
|
|
}
|
|
}
|
|
|
|
// The real PSP fonts live in flash0, which we only have if the user installed a firmware.
|
|
static const char *const g_nandFontPath = "flash0:/font/";
|
|
|
|
// The firmware this game was built against, which bounds the fonts it can ask for. Practically
|
|
// all discs declare it in PARAM.SFO, and the version of the updater they carry stands in for the
|
|
// rest. Zero when we can't tell, which asks for nothing beyond the fonts every firmware has.
|
|
static int GameFirmwareVersion() {
|
|
int version = g_paramSFO.GetSystemVersion();
|
|
if (version == 0) {
|
|
version = ParamSFOData::VersionToInt(ReadMountedDiscUpdaterVersion());
|
|
}
|
|
return version;
|
|
}
|
|
|
|
// Whether NAND has the fonts this game could actually ask for. Not every font we know of - an
|
|
// older game's firmware never had the later ones, so requiring those could never be satisfied and
|
|
// we'd unpack the same updater on every launch.
|
|
//
|
|
// One directory listing rather than a stat per font, since on Android's scoped storage the
|
|
// individual checks are slow.
|
|
static bool NandFontsComplete() {
|
|
const int firmware = GameFirmwareVersion();
|
|
|
|
std::string_view fontDir(g_nandFontPath);
|
|
fontDir.remove_suffix(1); // GetDirListing doesn't want the trailing slash.
|
|
|
|
bool dirExists = false;
|
|
const std::vector<PSPFileInfo> listing = pspFileSystem.GetDirListing(fontDir, &dirExists);
|
|
if (!dirExists) {
|
|
return false;
|
|
}
|
|
|
|
for (const FontRegistryEntry &entry : fontRegistry) {
|
|
if (entry.ignoreIfMissing) {
|
|
// zh_gb.pgf, which we added for Chinese translations. No firmware ships it, so
|
|
// waiting for one to show up would mean never being satisfied.
|
|
continue;
|
|
}
|
|
if (entry.minFirmware > firmware) {
|
|
// Didn't exist yet when this game was made.
|
|
continue;
|
|
}
|
|
bool found = false;
|
|
for (const PSPFileInfo &file : listing) {
|
|
if (equalsNoCase(file.name, entry.fileName)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
DEBUG_LOG(Log::sceFont, "%s missing from NAND (game wants firmware %d)", entry.fileName, firmware);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Most UMDs carry a firmware updater, so if we're missing real fonts we can pull them out of
|
|
// whatever game is running rather than making the user find an updater themselves. Our bundled
|
|
// substitutes are a good deal worse - some homebrew even trips over them.
|
|
static bool InstallFontsFromDiscUpdater() {
|
|
if (!MountedDiscHasUpdater()) {
|
|
return false;
|
|
}
|
|
|
|
INFO_LOG(Log::sceFont, "No fonts in NAND, trying the firmware updater on the disc (%s)",
|
|
ReadMountedDiscUpdaterVersion().c_str());
|
|
|
|
PSARUnpackOptions options;
|
|
options.model = EmulatedModelGeneration();
|
|
// Just the fonts - the rest of a firmware is none of our business here.
|
|
options.prefixFilter = g_nandFontPath;
|
|
|
|
PSARUnpackStats stats;
|
|
std::string error;
|
|
if (!UnpackUpdaterFromMountedDisc(GetSysDirectory(DIRECTORY_NAND), options, &stats, &error)) {
|
|
WARN_LOG(Log::sceFont, "Couldn't unpack fonts from the disc's updater: %s", error.c_str());
|
|
return false;
|
|
}
|
|
if (stats.written == 0) {
|
|
// The updater has no font list for the model we're emulating.
|
|
WARN_LOG(Log::sceFont, "The disc's updater had no fonts for this PSP model");
|
|
return false;
|
|
}
|
|
|
|
INFO_LOG(Log::sceFont, "Installed %d font files from the disc's %s updater", stats.written,
|
|
stats.firmwareVersion.c_str());
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
g_OSD.Show(OSDType::MESSAGE_SUCCESS, sy->T("Installed fonts"), stats.firmwareVersion, 3.0f);
|
|
return true;
|
|
}
|
|
|
|
static void __LoadInternalFonts() {
|
|
if (internalFonts.size()) {
|
|
// Fonts already loaded.
|
|
return;
|
|
}
|
|
const std::string fontOverridePath = "ms0:/PSP/flash0/font/";
|
|
const std::string gameFontPath = "disc0:/PSP_GAME/USRDIR/";
|
|
|
|
const bool checkClassicOverrides = pspFileSystem.GetFileInfo(fontOverridePath).exists;
|
|
if (checkClassicOverrides) {
|
|
WARN_LOG(Log::sceFont, "Classic font overrides active, ignoring NAND: %s", fontOverridePath.c_str());
|
|
} else if (!NandFontsComplete()) {
|
|
InstallFontsFromDiscUpdater();
|
|
}
|
|
|
|
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<u8> 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<u8> buffer;
|
|
bool bufferRead = false;
|
|
|
|
std::string fontFilename = gameFontPath + entry.fileName;
|
|
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0;
|
|
|
|
if (checkClassicOverrides && !bufferRead) {
|
|
// No game font, let's try classic override path. NOTE: This is not recommended - use flash0.
|
|
fontFilename = fontOverridePath + entry.fileName;
|
|
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0;
|
|
} else if (!bufferRead) {
|
|
// The real fonts, from a firmware the user installed or that we took off a disc.
|
|
fontFilename = std::string(g_nandFontPath) + entry.fileName;
|
|
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0;
|
|
}
|
|
|
|
if (!bufferRead) {
|
|
// No override, let's read fonts from assets from VFS.
|
|
fontFilename = std::string("flash0/font/") + entry.fileName;
|
|
bufferRead = g_VFS.ReadFileInto(fontFilename, &buffer);
|
|
}
|
|
|
|
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<FontNewLibParams>::Create(paramPtr);
|
|
auto errorCode = PSPPointer<s32_le>::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<s32_le>::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<s32_le>::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::GetPointerOrException(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<s32_le>::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<u8> 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<s32_le>::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<const PGFFontStyle>::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<float>::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<s32_le>::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<const PGFFontStyle>::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<PGFFontInfo>::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<PGFFontStyle>::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<PGFCharInfo>::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<PGFCharInfo>::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<FontImageRect>::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<FontImageRect>::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<const GlyphImage>::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<const GlyphImage>::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<PGFFontStyle>::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<s32_le>::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<s32_le>::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<s32_le>::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<s32_le>::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<s32_le>::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<const GlyphImage>::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<const GlyphImage>::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>, "sceFontNewLib", 'x', "xx", HLE_CLEAR_STACK_BYTES, 0x5A0 },
|
|
{0X574B6FBC, &WrapI_U<sceFontDoneLib>, "sceFontDoneLib", 'i', "x", HLE_CLEAR_STACK_BYTES, 0x2C },
|
|
{0X48293280, &WrapI_UFF<sceFontSetResolution>, "sceFontSetResolution", 'i', "xff" },
|
|
{0X27F6E642, &WrapI_UU<sceFontGetNumFontList>, "sceFontGetNumFontList", 'i', "xx" },
|
|
{0XBC75D85B, &WrapI_UUI<sceFontGetFontList>, "sceFontGetFontList", 'i', "xxi", HLE_CLEAR_STACK_BYTES, 0x31C },
|
|
{0X099EF33C, &WrapI_UUU<sceFontFindOptimumFont>, "sceFontFindOptimumFont", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0xF0 },
|
|
{0X681E61A7, &WrapI_UUU<sceFontFindFont>, "sceFontFindFont", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x40 },
|
|
{0X2F67356A, &WrapI_V<sceFontCalcMemorySize>, "sceFontCalcMemorySize", 'i', "" },
|
|
{0X5333322D, &WrapI_UUU<sceFontGetFontInfoByIndexNumber>, "sceFontGetFontInfoByIndexNumber", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x20 },
|
|
{0XA834319D, &WrapU_UUUU<sceFontOpen>, "sceFontOpen", 'x', "xxxp", HLE_CLEAR_STACK_BYTES, 0x460 },
|
|
{0X57FCB733, &WrapU_UCUU<sceFontOpenUserFile>, "sceFontOpenUserFile", 'x', "xsxp" },
|
|
{0XBB8E7FE6, &WrapU_UUUU<sceFontOpenUserMemory>, "sceFontOpenUserMemory", 'x', "xxxp", HLE_CLEAR_STACK_BYTES, 0x440 /*from JPCSP*/ },
|
|
{0X3AEA8CB6, &WrapI_U<sceFontClose>, "sceFontClose", 'i', "x", HLE_CLEAR_STACK_BYTES, 0x54 },
|
|
{0X0DA7535E, &WrapI_UU<sceFontGetFontInfo>, "sceFontGetFontInfo", 'i', "xx" },
|
|
{0XDCC80C2F, &WrapI_UUU<sceFontGetCharInfo>, "sceFontGetCharInfo", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x110 },
|
|
{0XAA3DE7B5, &WrapI_UUU<sceFontGetShadowInfo>, "sceFontGetShadowInfo", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x150 },
|
|
{0X5C3E4A9E, &WrapI_UUU<sceFontGetCharImageRect>, "sceFontGetCharImageRect", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x120 },
|
|
{0X48B06520, &WrapI_UUU<sceFontGetShadowImageRect>, "sceFontGetShadowImageRect", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x150 },
|
|
{0X980F4895, &WrapI_UUU<sceFontGetCharGlyphImage>, "sceFontGetCharGlyphImage", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x120 },
|
|
{0XCA1E6945, &WrapI_UUUIIII<sceFontGetCharGlyphImage_Clip>, "sceFontGetCharGlyphImage_Clip", 'i', "xxxiiii", HLE_CLEAR_STACK_BYTES, 0x130 },
|
|
{0X74B21701, &WrapF_IFU<sceFontPixelToPointH>, "sceFontPixelToPointH", 'f', "ifx", HLE_CLEAR_STACK_BYTES, 0x10 },
|
|
{0XF8F0752E, &WrapF_IFU<sceFontPixelToPointV>, "sceFontPixelToPointV", 'f', "ifx", HLE_CLEAR_STACK_BYTES, 0x10 },
|
|
{0X472694CD, &WrapF_IFU<sceFontPointToPixelH>, "sceFontPointToPixelH", 'f', "ifx" },
|
|
{0X3C4B7E82, &WrapF_IFU<sceFontPointToPixelV>, "sceFontPointToPixelV", 'f', "ifx" },
|
|
{0XEE232411, &WrapI_UU<sceFontSetAltCharacterCode>, "sceFontSetAltCharacterCode", 'i', "xx" },
|
|
{0X568BE516, &WrapI_UUU<sceFontGetShadowGlyphImage>, "sceFontGetShadowGlyphImage", 'i', "xxx", HLE_CLEAR_STACK_BYTES, 0x160 },
|
|
{0X5DCF6858, &WrapI_UUUIIII<sceFontGetShadowGlyphImage_Clip>, "sceFontGetShadowGlyphImage_Clip", 'i', "xxxiiii", HLE_CLEAR_STACK_BYTES, 0x170 },
|
|
{0X02D7F94B, &WrapI_U<sceFontFlush>, "sceFontFlush", 'i', "x" },
|
|
};
|
|
|
|
void Register_sceFont() {
|
|
RegisterHLEModule("sceLibFont", ARRAY_SIZE(sceLibFont), sceLibFont);
|
|
}
|
|
|
|
void Register_sceLibFttt() {
|
|
RegisterHLEModule("sceLibFttt", ARRAY_SIZE(sceLibFont), sceLibFont);
|
|
}
|