sceFont: use the fonts in NAND, and take them off the disc if we have none

sceFont never looked at flash0:, so a firmware the user installed was
ignored and we always fell back to our bundled substitutes - despite
the "ignoring NAND" warning suggesting otherwise. It reads flash0:/font
now, after the game's own fonts and the classic ms0 override.

And if there's nothing in NAND, we unpack just flash0:/font out of the
firmware updater on the running disc, which most UMDs carry. That turns
"install a firmware first" into something that happens by itself for
anyone playing a retail game.

EmulatedModelGeneration moves from InstallUpdateScreen into PSARUnpack
so both callers pick the same firmware file list.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JvJR8oJNSCimCM9KXVLjfq
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-04 18:35:07 -06:00
1 parent f929a74780
commit a641305c75
4 files changed
+57 -7

No files matched your search

+48
View File
@@ -22,6 +22,10 @@
#include "Core/Core.h"
#include "Core/System.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;
@@ -872,6 +876,44 @@ static LoadedFont *GetLoadedFont(u32 handle, bool allowClosed) {
}
}
// 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/";
// Most UMDs carry a firmware updater, so if we have no real fonts yet 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.
@@ -883,6 +925,8 @@ static void __LoadInternalFonts() {
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 (!pspFileSystem.GetFileInfo(std::string(g_nandFontPath) + "ltn0.pgf").exists) {
InstallFontsFromDiscUpdater();
}
if ((pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/zh_gb.pgf").exists) && (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/oldfont.prx").exists)) {
@@ -913,6 +957,10 @@ static void __LoadInternalFonts() {
// 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) {
+5
View File
@@ -26,6 +26,7 @@
#include "Common/File/Path.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Core/Config.h"
#include "Core/ELF/ParamSFO.h"
#include "Core/ELF/PBPReader.h"
#include "Core/ELF/PrxDecrypter.h"
@@ -334,6 +335,10 @@ const char *PSPModelGenerationToString(PSPModelGeneration generation) {
}
}
PSPModelGeneration EmulatedModelGeneration() {
return g_Config.iPSPModel == PSP_MODEL_FAT ? PSPModelGeneration::PSP_1000 : PSPModelGeneration::PSP_2000;
}
bool PSPModelGenerationFromString(std::string_view name, PSPModelGeneration *generation) {
if (equalsNoCase(name, "any")) {
*generation = PSPModelGeneration::Any;
+4
View File
@@ -67,6 +67,10 @@ enum class PSPModelGeneration {
};
const char *PSPModelGenerationToString(PSPModelGeneration generation);
// The model we're claiming to be. An updater carries one file list per hardware revision, and
// anything the chosen model's list doesn't name isn't part of its firmware, so unpacking this
// one keeps flash0 consistent with what the emulator reports to games.
PSPModelGeneration EmulatedModelGeneration();
// Accepts "01g".."12g", a bare number, or "any". Returns false if it's none of those.
bool PSPModelGenerationFromString(std::string_view name, PSPModelGeneration *generation);
-7
View File
@@ -36,13 +36,6 @@
#include "UI/MiscViews.h"
#include "UI/EmuScreen.h"
// An updater carries one file list per hardware revision, and anything the chosen model's list
// doesn't name isn't part of its firmware. Unpacking the model we claim to be keeps flash0
// consistent with what the emulator reports to games.
static PSPModelGeneration EmulatedModelGeneration() {
return g_Config.iPSPModel == PSP_MODEL_FAT ? PSPModelGeneration::PSP_1000 : PSPModelGeneration::PSP_2000;
}
InstallUpdateScreen::InstallUpdateScreen(const Path &path, std::string_view title)
: UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::ContentsCanScroll), path_(path), title_(title) {
destination_ = GetSysDirectory(DIRECTORY_NAND);