/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_FONT_INVALID_PARAMETER;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_FAMILY_SANS_SERIF;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_FAMILY_SERIF;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_LANGUAGE_CHINESE;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_LANGUAGE_JAPANESE;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_LANGUAGE_KOREAN;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_LANGUAGE_LATIN;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_STYLE_BOLD;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_STYLE_BOLD_ITALIC;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_STYLE_DB;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_STYLE_ITALIC;
import static jpcsp.HLE.kernel.types.pspFontStyle.FONT_STYLE_REGULAR;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUidClass;
import jpcsp.HLE.HLEUidObjectMapping;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.PspString;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TErrorPointer32;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer16;
import jpcsp.HLE.TPointer32;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.HLE.Modules;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.kernel.types.SceIoStat;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceFontInfo;
import jpcsp.HLE.kernel.types.IAction;
import jpcsp.HLE.kernel.types.SceKernelThreadInfo;
import jpcsp.HLE.kernel.types.pspCharInfo;
import jpcsp.HLE.kernel.types.pspFontStyle;
import jpcsp.filesystems.SeekableDataInput;
import jpcsp.format.BWFont;
import jpcsp.format.PGF;
import jpcsp.graphics.GeCommands;
import jpcsp.graphics.capture.CaptureImage;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.MemoryReader;
import jpcsp.settings.AbstractBoolSettingsListener;
import jpcsp.settings.Settings;
import jpcsp.util.Debug;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
//
// The stackUsage values are based on tests performed using JpcspTrace
//
public class sceFont extends HLEModule {
public static Logger log = Modules.getLogger("sceFont");
private static final boolean dumpUserFont = false;
private class UseDebugFontSettingsListerner extends AbstractBoolSettingsListener {
@Override
protected void settingsValueChanged(boolean value) {
setUseDebugFont(value);
}
}
@Override
public int getMemoryUsage() {
return 0x7D00;
}
@Override
public void start() {
setSettingsListener("emu.useDebugFont", new UseDebugFontSettingsListerner());
internalFonts = new LinkedList<Font>();
fontLibsMap = new HashMap<Integer, FontLib>();
fontsMap = new HashMap<Integer, Font>();
loadFontRegistry();
loadDefaultSystemFont();
super.start();
}
@HLEUidClass(errorValueOnNotFound = SceKernelErrors.ERROR_FONT_INVALID_LIBID)
public class Font {
public PGF pgf;
public SceFontInfo fontInfo;
public FontLib fontLib;
private final int handle;
private int fontFileSize;
public int maxGlyphBaseYI;
public int maxBitmapWidth;
public int maxBitmapHeight;
public Font(PGF pgf, SceFontInfo fontInfo, int fontFileSize) {
this.pgf = pgf;
this.fontInfo = fontInfo;
fontLib = null;
this.handle = 0;
this.fontFileSize = fontFileSize;
if (pgf != null) {
maxGlyphBaseYI = pgf.getMaxBaseYAdjust();
maxBitmapWidth = pgf.getMaxGlyphWidth();
maxBitmapHeight = pgf.getMaxGlyphHeight();
}
}
public Font(Font font, FontLib fontLib, int handle, int fontFileSize) {
this.pgf = font.pgf;
this.fontInfo = font.fontInfo;
this.fontLib = fontLib;
this.handle = handle;
this.fontFileSize = fontFileSize;
maxGlyphBaseYI = font.maxGlyphBaseYI;
maxBitmapWidth = font.maxBitmapWidth;
maxBitmapHeight = font.maxBitmapHeight;
}
public pspFontStyle getFontStyle() {
pspFontStyle fontStyle = fontInfo.getFontStyle();
if (fontStyle == null) {
fontStyle = new pspFontStyle();
fontStyle.fontH = pgf.getHSize() / 64.f;
fontStyle.fontV = pgf.getVSize() / 64.f;
fontStyle.fontHRes = pgf.getHResolution() / 64.f;
fontStyle.fontVRes = pgf.getVResolution() / 64.f;
fontStyle.fontStyle = sceFont.getFontStyle(pgf.getFontType());
fontStyle.fontName = pgf.getFontName();
fontStyle.fontFileName = pgf.getFileNamez();
}
return fontStyle;
}
public int getHandle() {
return handle;
}
public boolean isClosed() {
return fontLib == null;
}
public void close() {
fontLib = null;
// Keep PGF and SceFontInfo information.
// A call to sceFontGetFontInfo is allowed on a closed font.
}
@Override
public String toString() {
if (isClosed()) {
return String.format("Font[handle=0x%X closed]", getHandle());
}
return String.format("Font[handle=0x%X, '%s' - '%s']", getHandle(), pgf.getFileNamez(), pgf.getFontName());
}
}
public static class FontRegistryEntry {
public int h_size;
public int v_size;
public int h_resolution;
public int v_resolution;
public int extra_attributes;
public int weight;
public int family_code;
public int style;
public int sub_style;
public int language_code;
public int region_code;
public int country_code;
public String file_name;
public String font_name;
public int expire_date;
public int shadow_option;
public int fontFileSize;
public int maxGlyphBaseYI;
public int maxBitmapWidth;
public int maxBitmapHeight;
public FontRegistryEntry(int h_size, int v_size, int h_resolution,
int v_resolution, int extra_attributes, int weight,
int family_code, int style, int sub_style, int language_code,
int region_code, int country_code, String file_name,
String font_name, int expire_date, int shadow_option,
int fontFileSize, int maxGlyphBaseYI, int maxBitmapWidth, int maxBitmapHeight) {
this.h_size = h_size;
this.v_size = v_size;
this.h_resolution = h_resolution;
this.v_resolution = v_resolution;
this.extra_attributes = extra_attributes;
this.weight = weight;
this.family_code = family_code;
this.style = style;
this.sub_style = sub_style;
this.language_code = language_code;
this.region_code = region_code;
this.country_code = country_code;
this.file_name = file_name;
this.font_name = font_name;
this.expire_date = expire_date;
this.shadow_option = shadow_option;
this.fontFileSize = fontFileSize;
this.maxGlyphBaseYI = maxGlyphBaseYI;
this.maxBitmapWidth = maxBitmapWidth;
this.maxBitmapHeight = maxBitmapHeight;
}
public FontRegistryEntry() {
}
}
public static final int PGF_MAGIC = 'P' << 24 | 'G' << 16 | 'F' << 8 | '0';
public static final String customFontFile = "debug.jpft";
public static final int PSP_FONT_PIXELFORMAT_4 = 0; // 2 pixels packed in 1 byte (natural order)
public static final int PSP_FONT_PIXELFORMAT_4_REV = 1; // 2 pixels packed in 1 byte (reversed order)
public static final int PSP_FONT_PIXELFORMAT_8 = 2; // 1 pixel in 1 byte
public static final int PSP_FONT_PIXELFORMAT_24 = 3; // 1 pixel in 3 bytes (RGB)
public static final int PSP_FONT_PIXELFORMAT_32 = 4; // 1 pixel in 4 bytes (RGBA)
public static final int PSP_FONT_MODE_FILE = 0;
public static final int PSP_FONT_MODE_MEMORY = 1;
private boolean useDebugFont = false;
private static final boolean dumpFonts = false;
private List<Font> internalFonts;
private HashMap<Integer, FontLib> fontLibsMap;
private HashMap<Integer, Font> fontsMap;
protected String uidPurpose = "sceFont";
private String fontDirPath = "flash0:/font";
private List<FontRegistryEntry> fontRegistry;
protected static final float pointDPI = 72.f;
protected boolean getUseDebugFont() {
return useDebugFont;
}
private void setUseDebugFont(boolean status) {
useDebugFont = status;
}
public List<FontRegistryEntry> getFontRegistry() {
return fontRegistry;
}
public String getFontDirPath() {
return fontDirPath;
}
public void setFontDirPath(String fontDirPath) {
this.fontDirPath = fontDirPath;
}
protected void loadFontRegistry() {
fontRegistry = new LinkedList<FontRegistryEntry>();
fontRegistry.add(new FontRegistryEntry(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, 0x4B4, 19, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x4B2, 23, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x482, 23, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x4B2, 25, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x482, 25, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x4F7, 24, 21));
fontRegistry.add(new FontRegistryEntry(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, 0x49C, 24, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x4F7, 27, 21));
fontRegistry.add(new FontRegistryEntry(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, 0x49C, 27, 20));
fontRegistry.add(new FontRegistryEntry(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, 0x321, 16, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x302, 16, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x321, 17, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x302, 17, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x34F, 17, 15));
fontRegistry.add(new FontRegistryEntry(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, 0x312, 17, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x34F, 18, 15));
fontRegistry.add(new FontRegistryEntry(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, 0x312, 18, 14));
fontRegistry.add(new FontRegistryEntry(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, 0x3CB, 21, 20));
// Add the Chinese fixed font file if it is present, i.e. if copied from a real PSP flash0:/font/gb3s1518.bwfon
if (Modules.IoFileMgrForUserModule.statFile(fontDirPath + "/gb3s1518.bwfon") != null) {
fontRegistry.add(new FontRegistryEntry(BWFont.charBitmapWidth << 6, BWFont.charBitmapHeight << 6, 0, 0, 0, 0, 0, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_CHINESE, 0, 0, "gb3s1518.bwfon", "gb3s1518", 0, 0, 1023372, 0, 0, 0));
}
}
protected void loadDefaultSystemFont() {
try {
SeekableDataInput fontFile = Modules.IoFileMgrForUserModule.getFile(fontDirPath + "/" + customFontFile, IoFileMgrForUser.PSP_O_RDONLY);
if (fontFile != null) {
fontFile.skipBytes(32); // Skip custom header.
byte[] c = new byte[(int) fontFile.length() - 32];
fontFile.readFully(c);
fontFile.close();
Debug.Font.setDebugFont(c); // Set the internal debug font.
Debug.Font.setDebugCharSize(8);
Debug.Font.setDebugCharHeight(8);
Debug.Font.setDebugCharWidth(8);
}
} catch (IOException e) {
// The file was removed from flash0.
log.error(e);
}
}
/**
* Dump a font as a .BMP image in the tmp directory for debugging purpose.
*
* @param font the font to be dumped
*/
protected void dumpFont(Font font) {
int addr = MemoryMap.START_VRAM;
int fontPixelFormat = PSP_FONT_PIXELFORMAT_32;
int bufferStorage = GeCommands.TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888;
int bufferWidth = 800;
int fontBufWidth = bufferWidth;
int fontBpl = bufferWidth * sceDisplay.getPixelFormatBytes(bufferStorage);
int fontBufHeight = MemoryMap.SIZE_VRAM / fontBpl;
SceFontInfo fontInfo = font.fontInfo;
PGF pgf = font.pgf;
int memoryLength = fontBpl * fontBufHeight * sceDisplay.getPixelFormatBytes(bufferStorage);
Memory mem = Memory.getInstance();
mem.memset(addr, (byte) 0, memoryLength);
Buffer memoryBuffer = Memory.getInstance().getBuffer(addr, memoryLength);
String fileNamePrefix = String.format("Font-%s-", pgf.getFileNamez());
int maxGlyphWidth = pgf.getMaxSize()[0] >> 6;
int maxGlyphHeight = pgf.getMaxSize()[1] >> 6;
int level = 0;
int x = 0;
int y = 0;
int firstCharCode = pgf.getFirstGlyphInCharMap();
int lastCharCode = pgf.getLastGlyphInCharMap();
for (int charCode = firstCharCode; charCode <= lastCharCode; charCode++) {
if (x == 0) {
String linePrefix = String.format("0x%04X: ", charCode);
Debug.printFramebuffer(addr, fontBufWidth, x, y, 0xFFFFFFFF, 0x00000000, bufferStorage, linePrefix);
x += linePrefix.length() * jpcsp.util.Debug.Font.charWidth;
}
fontInfo.printFont(addr, fontBpl, fontBufWidth, fontBufHeight, x, y, 0, 0, 0, 0, fontBufWidth, fontBufHeight, fontPixelFormat, charCode, ' ', SceFontInfo.FONT_PGF_GLYPH_TYPE_CHAR, true);
x += maxGlyphWidth;
if (x + maxGlyphWidth > fontBufWidth) {
x = 0;
y += maxGlyphHeight;
if (y + maxGlyphHeight > fontBufHeight) {
CaptureImage image = new CaptureImage(addr, level, memoryBuffer, fontBufWidth, fontBufHeight, bufferWidth, bufferStorage, false, 0, false, true, fileNamePrefix);
log.info(String.format("Dumping font %s from charCode 0x%04X to file %s", pgf.getFontName(), firstCharCode, image.getFileName()));
try {
image.write();
} catch (IOException e) {
log.error(e);
}
mem.memset(addr, (byte) 0, memoryLength);
level++;
firstCharCode = charCode + 1;
x = 0;
y = 0;
}
}
}
CaptureImage image = new CaptureImage(addr, level, memoryBuffer, fontBufWidth, fontBufHeight, bufferWidth, bufferStorage, false, 0, false, true, fileNamePrefix);
log.info(String.format("Dumping font %s from charCode 0x%04X to file %s", pgf.getFontName(), firstCharCode, image.getFileName()));
try {
image.write();
} catch (IOException e) {
log.error(e);
}
}
protected Font openFontFile(ByteBuffer pgfBuffer, String fileName) {
Font font = null;
try {
PGF pgfFile;
if (fileName != null && fileName.endsWith(".bwfon")) {
pgfFile = new BWFont(pgfBuffer, fileName);
} else {
pgfFile = new PGF(pgfBuffer);
}
if (fileName != null) {
pgfFile.setFileNamez(fileName);
}
SceFontInfo fontInfo = pgfFile.createFontInfo();
font = new Font(pgfFile, fontInfo, pgfBuffer.capacity());
if (dumpFonts) {
dumpFont(font);
}
} catch (Exception e) {
// Can't parse file.
log.error("openFontFile", e);
}
return font;
}
protected Font openFontFile(String fileName) {
Font font = null;
try {
SeekableDataInput fontFile = Modules.IoFileMgrForUserModule.getFile(fileName, IoFileMgrForUser.PSP_O_RDONLY);
if (fontFile != null) {
byte[] pgfBytes = new byte[(int) fontFile.length()];
fontFile.readFully(pgfBytes);
fontFile.close();
ByteBuffer pgfBuffer = ByteBuffer.wrap(pgfBytes);
font = openFontFile(pgfBuffer, new File(fileName).getName());
}
} catch (IOException e) {
// Can't open file.
log.warn(e);
}
return font;
}
protected Font openFontFile(int addr, int length) {
if (dumpUserFont) {
try {
OutputStream os = new FileOutputStream(String.format("%suserFont-0x%08X.pgf", Settings.getInstance().getTmpDirectory(), addr));
IMemoryReader memoryReader = MemoryReader.getMemoryReader(addr, length, 1);
for (int i = 0; i < length; i++) {
os.write(memoryReader.readNext());
}
os.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
ByteBuffer pgfBuffer = ByteBuffer.allocate(length);
Buffer memBuffer = Memory.getInstance().getBuffer(addr, length);
Utilities.putBuffer(pgfBuffer, memBuffer, ByteOrder.LITTLE_ENDIAN, length);
pgfBuffer.rewind();
Font font = openFontFile(pgfBuffer, null);
return font;
}
protected void setFontAttributesFromRegistry(Font font, FontRegistryEntry fontRegistryEntry) {
pspFontStyle fontStyle = new pspFontStyle();
fontStyle.fontH = fontRegistryEntry.h_size / 64.f;
fontStyle.fontV = fontRegistryEntry.v_size / 64.f;
fontStyle.fontHRes = fontRegistryEntry.h_resolution / 64.f;
fontStyle.fontVRes = fontRegistryEntry.v_resolution / 64.f;
fontStyle.fontWeight = fontRegistryEntry.weight;
fontStyle.fontFamily = (short) fontRegistryEntry.family_code;
fontStyle.fontStyle = (short) fontRegistryEntry.style;
fontStyle.fontStyleSub = (short) fontRegistryEntry.sub_style;
fontStyle.fontLanguage = (short) fontRegistryEntry.language_code;
fontStyle.fontRegion = (short) fontRegistryEntry.region_code;
fontStyle.fontCountry = (short) fontRegistryEntry.country_code;
fontStyle.fontName = fontRegistryEntry.font_name;
fontStyle.fontFileName = fontRegistryEntry.file_name;
fontStyle.fontAttributes = fontRegistryEntry.extra_attributes;
fontStyle.fontExpire = fontRegistryEntry.expire_date;
font.fontInfo.setFontStyle(fontStyle);
// The font file size is used during a sceOpenFont() call to allocate memory.
// The Jpcsp font files differ significantly in size from the real PSP files.
// So it is important to use the font file sizes from a real PSP, so that
// the correct memory is being allocated.
font.fontFileSize = fontRegistryEntry.fontFileSize;
// The following values are critical for some applications and need to match
// the values from a real PSP font file.
font.maxGlyphBaseYI = fontRegistryEntry.maxGlyphBaseYI;
font.maxBitmapWidth = fontRegistryEntry.maxBitmapWidth;
font.maxBitmapHeight = fontRegistryEntry.maxBitmapHeight;
}
protected void setFontAttributesFromRegistry(Font font) {
for (FontRegistryEntry fontRegistryEntry : fontRegistry) {
if (fontRegistryEntry.file_name.equals(font.pgf.getFileNamez())) {
if (fontRegistryEntry.font_name.equals(font.pgf.getFontName())) {
setFontAttributesFromRegistry(font, fontRegistryEntry);
break;
}
}
}
}
protected void loadAllFonts() {
internalFonts.clear();
// Load the fonts in the same order as on a PSP.
// Some applications are always using the first font returned by
// sceFontGetFontList.
for (FontRegistryEntry fontRegistryEntry : fontRegistry) {
String fontFileName = fontDirPath + "/" + fontRegistryEntry.file_name;
SceIoStat stat = Modules.IoFileMgrForUserModule.statFile(fontFileName);
if (stat != null) {
Font font = openFontFile(fontFileName);
if (font != null) {
setFontAttributesFromRegistry(font, fontRegistryEntry);
internalFonts.add(font);
log.info(String.format("Loading font file '%s'. Font='%s' Type='%s'", fontRegistryEntry.file_name, font.pgf.getFontName(), font.pgf.getFontType()));
}
}
}
}
protected static short getFontStyle(String styleString) {
if ("Regular".equals(styleString)) {
return FONT_STYLE_REGULAR;
}
if ("Italic".equals(styleString)) {
return FONT_STYLE_ITALIC;
}
if ("Bold".equals(styleString)) {
return FONT_STYLE_BOLD;
}
if ("Bold Italic".equals(styleString)) {
return FONT_STYLE_BOLD_ITALIC;
}
return 0;
}
@HLEUidClass(errorValueOnNotFound = SceKernelErrors.ERROR_FONT_INVALID_LIBID)
protected class FontLib {
private static final int FONT_IS_CLOSED = 0;
private static final int FONT_IS_OPEN = 1;
protected int userDataAddr;
protected int numFonts;
protected int cacheDataAddr;
protected int allocFuncAddr;
protected int freeFuncAddr;
protected int openFuncAddr;
protected int closeFuncAddr;
protected int readFuncAddr;
protected int seekFuncAddr;
protected int errorFuncAddr;
protected int ioFinishFuncAddr;
protected int fileFontHandle;
protected int altCharCode;
protected float fontHRes = 128.f;
protected float fontVRes = 128.f;
protected int handle;
protected int[] fonts;
protected int[] allocatedSizes = new int[] { 0x4C, 0x130, 0x8C0, 0xC78 };
protected int[] allocatedAddresses = new int[allocatedSizes.length];
protected int allocatedAddressIndex;
protected int[] openAllocatedAddresses;
protected int charInfoBitmapAddress;
public FontLib(TPointer32 params) {
read(params);
// On a PSP, FontLib handle and Font handles are addresses pointing to memory
// allocated by the "Alloc" callback.
// Here, we just fake theses addresses by allocating an area small enough to
// provide different addresses for the required FontLib and Font handles.
// E.g.
// addr = FontLib handle
// addr + 4 = Font handle for 1st font
// addr + 8 = Font handle for 2nd font
// ...
// Furthermore, the value stored at a Font handle address indicates if the
// font is closed (e.g. free to be opened) or open.
// mem.read32(fontHandle) == FONT_IS_OPEN: font is already open
// mem.read32(fontHandle) == FONT_IS_CLOSED: font is not open
//
openAllocatedAddresses = new int[numFonts];
allocateAddresses();
}
private void allocateAddresses() {
int minimumSize = numFonts * 4 + 4;
if (allocatedSizes[0] < minimumSize) {
allocatedSizes[0] = minimumSize;
}
allocatedAddressIndex = 0;
triggerAllocCallback(allocatedSizes[allocatedAddressIndex], new AfterCreateAllocCallback());
}
public int getNumFonts() {
return numFonts;
}
private Font openFont(Font font, int mode, boolean needAllocForFontFile) {
if (font == null) {
throw (new SceKernelErrorException(SceKernelErrors.ERROR_FONT_INVALID_PARAMETER));
}
Memory mem = Memory.getInstance();
int freeFontIndex = -1;
// Search for a free font slot
for (int i = 0; i < numFonts; i++) {
if (mem.read32(fonts[i]) == FONT_IS_CLOSED) {
freeFontIndex = i;
break;
}
}
if (freeFontIndex < 0) {
throw (new SceKernelErrorException(SceKernelErrors.ERROR_FONT_TOO_MANY_OPEN_FONTS));
}
font = new Font(font, this, fonts[freeFontIndex], font.fontFileSize);
mem.write32(fonts[freeFontIndex], FONT_IS_OPEN);
fontsMap.put(font.getHandle(), font);
int allocSize = 12;
if (needAllocForFontFile) {
if (mode == 0) {
// mode == 0: only parts of the font file are read.
// For jpn0.pgf, the alloc callback is called multiple times:
// 0x7F8, 0x7F8, 0x7F8, 0x350, 0x3FC, 0x0, 0x0, 0x1BFC8, 0x5AB8
// in total: 0x239B4
allocSize = 0x239B4;
} else if (mode == 1) {
// mode == 1: the whole font file is read in memory
allocSize += font.fontFileSize;
}
}
triggerAllocCallback(allocSize, new AfterOpenAllocCallback(freeFontIndex));
return font;
}
private void closeFont(Font font) {
HLEUidObjectMapping.removeObject(font);
for (int i = 0; i < numFonts; i++) {
if (fonts[i] == font.getHandle()) {
Memory mem = Memory.getInstance();
mem.write32(fonts[i], FONT_IS_CLOSED);
if (openAllocatedAddresses[i] != 0) {
triggerFreeCallback(openAllocatedAddresses[i], null);
openAllocatedAddresses[i] = 0;
}
break;
}
}
flushFont(font);
font.close();
}
public void flushFont(Font font) {
if (charInfoBitmapAddress != 0) {
triggerFreeCallback(charInfoBitmapAddress, null);
charInfoBitmapAddress = 0;
}
}
public void done() {
Memory mem = Memory.getInstance();
for (int i = 0; i < numFonts; i++) {
if (mem.read32(fonts[i]) == FONT_IS_OPEN) {
closeFont(fontsMap.get(fonts[i]));
}
fontsMap.remove(fonts[i]);
}
triggerFreeCallback(allocatedAddresses[--allocatedAddressIndex], new AfterFreeCallback());
fonts = null;
}
public int triggetGetCharInfo(pspCharInfo charInfo) {
int result = 0;
// The callbacks are not triggered by characters not present in the font
if (charInfo.sfp26AdvanceH != 0 || charInfo.sfp26AdvanceV != 0) {
SceKernelThreadInfo thread = Modules.ThreadManForUserModule.getCurrentThread();
if (charInfoBitmapAddress != 0) {
triggerFreeCallback(charInfoBitmapAddress, new AfterCharInfoFreeCallback(thread, charInfo));
} else {
triggerAllocCallback(charInfo.bitmapWidth * charInfo.bitmapHeight, new AfterCharInfoAllocCallback(thread));
}
if (charInfoBitmapAddress == 0) {
result = SceKernelErrors.ERROR_FONT_OUT_OF_MEMORY;
}
}
return result;
}
public int getHandle() {
return handle;
}
protected void triggerAllocCallback(int size, IAction afterAllocCallback) {
if (log.isDebugEnabled()) {
log.debug(String.format("triggerAllocCallback size=0x%X", size));
}
Modules.ThreadManForUserModule.executeCallback(null, allocFuncAddr, afterAllocCallback, true, userDataAddr, size);
}
protected void triggerFreeCallback(int addr, IAction afterFreeCallback) {
if (Memory.isAddressGood(addr)) {
if (log.isDebugEnabled()) {
log.debug(String.format("Calling free callback on 0x%08X", addr));
}
Modules.ThreadManForUserModule.executeCallback(null, freeFuncAddr, afterFreeCallback, true, userDataAddr, addr);
}
}
protected void triggerOpenCallback(int fileNameAddr, int errorCodeAddr) {
Modules.ThreadManForUserModule.executeCallback(null, openFuncAddr, new AfterOpenCallback(), true, userDataAddr, fileNameAddr, errorCodeAddr);
}
protected void triggerCloseCallback() {
if (fileFontHandle != 0) {
Modules.ThreadManForUserModule.executeCallback(null, closeFuncAddr, null, true, userDataAddr, fileFontHandle);
}
}
private void read(TPointer32 params) {
userDataAddr = params.getValue(0);
numFonts = params.getValue(4);
cacheDataAddr = params.getValue(8);
allocFuncAddr = params.getValue(12);
freeFuncAddr = params.getValue(16);
openFuncAddr = params.getValue(20);
closeFuncAddr = params.getValue(24);
readFuncAddr = params.getValue(28);
seekFuncAddr = params.getValue(32);
errorFuncAddr = params.getValue(36);
ioFinishFuncAddr = params.getValue(40);
if (log.isDebugEnabled()) {
log.debug(String.format("userDataAddr 0x%08X, numFonts=%d, cacheDataAddr=0x%08X, allocFuncAddr=0x%08X, freeFuncAddr=0x%08X, openFuncAddr=0x%08X, closeFuncAddr=0x%08X, readFuncAddr=0x%08X, seekFuncAddr=0x%08X, errorFuncAddr=0x%08X, ioFinishFuncAddr=0x%08X", userDataAddr, numFonts, cacheDataAddr, allocFuncAddr, freeFuncAddr, openFuncAddr, closeFuncAddr, readFuncAddr, seekFuncAddr, errorFuncAddr, ioFinishFuncAddr));
}
}
public int getAltCharCode() {
return altCharCode;
}
public void setAltCharCode(int altCharCode) {
this.altCharCode = altCharCode;
}
public int getFontHandle(int index) {
return fonts[index];
}
private class AfterCreateAllocCallback implements IAction {
@Override
public void execute() {
int allocatedAddr = Emulator.getProcessor().cpu._v0;
if (log.isDebugEnabled()) {
log.debug(String.format("FontLib's allocation callback#%d returned 0x%08X for size 0x%X", allocatedAddressIndex, allocatedAddr, allocatedSizes[allocatedAddressIndex]));
}
if (allocatedAddressIndex == 0) {
int addr = allocatedAddr;
handle = addr;
addr += 4;
fonts = new int[numFonts];
Memory mem = Memory.getInstance();
for (int i = 0; i < numFonts; i++) {
mem.write32(addr, FONT_IS_CLOSED);
fonts[i] = addr;
addr += 4;
}
}
allocatedAddresses[allocatedAddressIndex++] = allocatedAddr;
if (allocatedAddressIndex < allocatedSizes.length) {
triggerAllocCallback(allocatedSizes[allocatedAddressIndex], this);
}
}
}
private class AfterFreeCallback implements IAction {
@Override
public void execute() {
if (allocatedAddressIndex > 0) {
triggerFreeCallback(allocatedAddresses[--allocatedAddressIndex], this);
}
}
}
private class AfterOpenAllocCallback implements IAction {
private int fontIndex;
public AfterOpenAllocCallback(int fontIndex) {
this.fontIndex = fontIndex;
}
@Override
public void execute() {
int allocatedAddr = Emulator.getProcessor().cpu._v0;
if (log.isDebugEnabled()) {
log.debug(String.format("FontLib's allocation callback on open#%d returned 0x%08X", fontIndex, allocatedAddr));
}
openAllocatedAddresses[fontIndex] = allocatedAddr;
}
}
private class AfterOpenCallback implements IAction {
@Override
public void execute() {
fileFontHandle = Emulator.getProcessor().cpu._v0;
if (log.isDebugEnabled()) {
log.debug(String.format("FontLib's file open callback returned 0x%X", fileFontHandle));
}
}
}
private class AfterCharInfoFreeCallback implements IAction {
private SceKernelThreadInfo thread;
private pspCharInfo charInfo;
public AfterCharInfoFreeCallback(SceKernelThreadInfo thread, pspCharInfo charInfo) {
this.thread = thread;
this.charInfo = charInfo;
}
@Override
public void execute() {
charInfoBitmapAddress = 0;
triggerAllocCallback(charInfo.bitmapWidth * charInfo.bitmapHeight, new AfterCharInfoAllocCallback(thread));
}
}
private class AfterCharInfoAllocCallback implements IAction {
private SceKernelThreadInfo thread;
public AfterCharInfoAllocCallback(SceKernelThreadInfo thread) {
this.thread = thread;
}
@Override
public void execute() {
charInfoBitmapAddress = Emulator.getProcessor().cpu._v0;
if (log.isDebugEnabled()) {
log.debug(String.format("FontLib's allocation callback on getCharInfo returned 0x%08X", charInfoBitmapAddress));
}
if (charInfoBitmapAddress == 0) {
thread.cpuContext._v0 = SceKernelErrors.ERROR_FONT_OUT_OF_MEMORY;
}
}
}
@Override
public String toString() {
return String.format("FontLib - Handle: '0x%08X', Fonts: '%d'", getHandle(), getNumFonts());
}
}
private boolean isFontMatchingStyle(Font font, pspFontStyle fontStyle, boolean optimum) {
if (font != null && font.fontInfo != null && font.fontInfo.getFontStyle() != null) {
return font.fontInfo.getFontStyle().isMatching(fontStyle, optimum);
}
// Faking: always matching
return true;
}
/**
* Check if a given font is better matching the fontStyle than the currently best font.
* The check is based on the fontH and fontV.
*
* @param fontStyle the criteria for the optimum font
* @param optimumFont the currently optimum font
* @param matchingFont a candidate matching the fontStyle
* @return the matchingFont if it is better matching the fontStyle than the optimumFont,
* the optimumFont otherwise
*/
private Font getOptimiumFont(pspFontStyle fontStyle, Font optimumFont, Font matchingFont) {
if (optimumFont == null) {
return matchingFont;
}
pspFontStyle optimiumStyle = optimumFont.fontInfo.getFontStyle();
pspFontStyle matchingStyle = matchingFont.fontInfo.getFontStyle();
// Check the fontH if it is specified or both fontH and fontV are unspecified
boolean testH = fontStyle.fontH != 0f || fontStyle.fontV == 0f;
if (testH && Math.abs(fontStyle.fontH - optimiumStyle.fontH) > Math.abs(fontStyle.fontH - matchingStyle.fontH)) {
return matchingFont;
}
// Check the fontV if it is specified or both fontH and fontV are unspecified
boolean testV = fontStyle.fontV != 0f || fontStyle.fontH == 0f;
if (testV && Math.abs(fontStyle.fontV - optimiumStyle.fontV) > Math.abs(fontStyle.fontV - matchingStyle.fontV)) {
return matchingFont;
}
return optimumFont;
}
private Font getOptimumFont(pspFontStyle fontStyle) {
Font optimumFont = null;
for (int i = 0; i < internalFonts.size(); i++) {
Font font = internalFonts.get(i);
if (isFontMatchingStyle(font, fontStyle, true)) {
optimumFont = getOptimiumFont(fontStyle, optimumFont, font);
}
}
return optimumFont;
}
private int getFontIndex(Font font) {
if (font != null) {
for (int i = 0; i < internalFonts.size(); i++) {
if (internalFonts.get(i) == font) {
return i;
}
}
}
return -1;
}
protected FontLib getFontLib(int fontLibHandle) {
FontLib fontLib = fontLibsMap.get(fontLibHandle);
if (fontLib == null) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_FONT_INVALID_PARAMETER);
}
return fontLib;
}
protected Font getFont(int fontHandle, boolean allowClosedFont) {
Font font = fontsMap.get(fontHandle);
if (font == null || font.fontInfo == null) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_FONT_INVALID_PARAMETER);
}
if (!allowClosedFont && font.isClosed()) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_FONT_INVALID_PARAMETER);
}
return font;
}
public Font getFont(int index) {
if (internalFonts.size() == 0) {
loadAllFonts();
}
return internalFonts.get(index);
}
@HLEFunction(nid = 0x67F17ED7, version = 150, checkInsideInterrupt = true, stackUsage = 0x590)
public int sceFontNewLib(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=44, usage=Usage.in) TPointer32 paramsPtr, @CanBeNull TErrorPointer32 errorCodePtr) {
loadAllFonts();
errorCodePtr.setValue(0);
FontLib fontLib = new FontLib(paramsPtr);
fontLibsMap.put(fontLib.getHandle(), fontLib);
return fontLib.getHandle();
}
@HLEFunction(nid = 0x57FCB733, version = 150, checkInsideInterrupt = true)
public int sceFontOpenUserFile(int fontLibHandle, PspString fileName, int mode, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
// "open" callback is not called in this case. Tested on PSP.
Font font = openFontFile(fileName.getString());
if (font == null) {
errorCodePtr.setValue(SceKernelErrors.ERROR_FONT_FILE_NOT_FOUND);
return 0;
}
return fontLib.openFont(font, mode, true).getHandle();
}
@HLEFunction(nid = 0xBB8E7FE6, version = 150, checkInsideInterrupt = true, stackUsage = 0x440)
public int sceFontOpenUserMemory(int fontLibHandle, TPointer memoryFontPtr, int memoryFontLength, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
return fontLib.openFont(openFontFile(memoryFontPtr.getAddress(), memoryFontLength), 0, false).getHandle();
}
@HLEFunction(nid = 0x0DA7535E, version = 150, checkInsideInterrupt = true, stackUsage = 0x0)
public int sceFontGetFontInfo(int fontHandle, TPointer fontInfoPtr) {
// A call to sceFontGetFontInfo is allowed on a closed font.
Font font = getFont(fontHandle, true);
PGF currentPGF = font.pgf;
int maxGlyphWidthI = currentPGF.getMaxSize()[0];
int maxGlyphHeightI = currentPGF.getMaxSize()[1];
int maxGlyphAscenderI = currentPGF.getMaxAscender();
int maxGlyphDescenderI = currentPGF.getMaxDescender();
int maxGlyphLeftXI = currentPGF.getMaxLeftXAdjust();
int maxGlyphBaseYI = font.maxGlyphBaseYI;
int minGlyphCenterXI = currentPGF.getMinCenterXAdjust();
int maxGlyphTopYI = currentPGF.getMaxTopYAdjust();
int maxGlyphAdvanceXI = currentPGF.getMaxAdvance()[0];
int maxGlyphAdvanceYI = currentPGF.getMaxAdvance()[1];
int maxBitmapWidth = font.maxBitmapWidth;
int maxBitmapHeight = font.maxBitmapHeight;
pspFontStyle fontStyle = font.getFontStyle();
// Glyph metrics (in 26.6 signed fixed-point).
fontInfoPtr.setValue32(0, maxGlyphWidthI);
fontInfoPtr.setValue32(4, maxGlyphHeightI);
fontInfoPtr.setValue32(8, maxGlyphAscenderI);
fontInfoPtr.setValue32(12, maxGlyphDescenderI);
fontInfoPtr.setValue32(16, maxGlyphLeftXI);
fontInfoPtr.setValue32(20, maxGlyphBaseYI);
fontInfoPtr.setValue32(24, minGlyphCenterXI);
fontInfoPtr.setValue32(28, maxGlyphTopYI);
fontInfoPtr.setValue32(32, maxGlyphAdvanceXI);
fontInfoPtr.setValue32(36, maxGlyphAdvanceYI);
// Glyph metrics (replicated as float).
for (int i = 0; i < 40; i += 4) {
int intValue = fontInfoPtr.getValue32(i);
float floatValue = intValue / 64.f;
fontInfoPtr.setFloat(i + 40, floatValue);
}
// Bitmap dimensions.
fontInfoPtr.setValue16(80, (short) maxBitmapWidth);
fontInfoPtr.setValue16(82, (short) maxBitmapHeight);
fontInfoPtr.setValue32(84, currentPGF.getCharPointerLength()); // Number of elements in the font's charmap.
fontInfoPtr.setValue32(88, 0); // Number of elements in the font's shadow charmap.
// Font style (used by font comparison functions).
fontStyle.write(fontInfoPtr, 92);
fontInfoPtr.setValue8(260, (byte) currentPGF.getBpp()); // Font's BPP.
fontInfoPtr.setValue8(261, (byte) 0); // Padding.
fontInfoPtr.setValue8(262, (byte) 0); // Padding.
fontInfoPtr.setValue8(263, (byte) 0); // Padding.
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetFontInfo returning maxGlyphWidthI=%d, maxGlyphHeightI=%d, maxGlyphAscenderI=%d, maxGlyphDescenderI=%d, maxGlyphLeftXI=%d, maxGlyphBaseYI=%d, minGlyphCenterXI=%d, maxGlyphTopYI=%d, maxGlyphAdvanceXI=%d, maxGlyphAdvanceYI=%d, maxBitmapWidth=%d, maxBitmapHeight=%d, fontStyle=[%s]%s", maxGlyphWidthI, maxGlyphHeightI, maxGlyphAscenderI, maxGlyphDescenderI, maxGlyphLeftXI, maxGlyphBaseYI, minGlyphCenterXI, maxGlyphTopYI, maxGlyphAdvanceXI, maxGlyphAdvanceYI, maxBitmapWidth, maxBitmapHeight, fontStyle, Utilities.getMemoryDump(fontInfoPtr.getAddress(), 264)));
}
return 0;
}
@HLEFunction(nid = 0xDCC80C2F, version = 150, checkInsideInterrupt = true, stackUsage = 0x100)
public int sceFontGetCharInfo(int fontHandle, int charCode, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=60, usage=Usage.out) TPointer charInfoPtr) {
Font font = getFont(fontHandle, false);
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetCharInfo charCode=%04X (%c)", charCode, (charCode <= 0xFF ? (char) charCode : '?')));
}
charCode &= 0xFFFF;
pspCharInfo pspCharInfo = null;
if (!getUseDebugFont()) {
pspCharInfo = font.fontInfo.getCharInfo(charCode, SceFontInfo.FONT_PGF_GLYPH_TYPE_CHAR);
}
if (pspCharInfo == null) {
pspCharInfo = new pspCharInfo();
pspCharInfo.bitmapWidth = Debug.Font.charWidth * Debug.fontPixelSize;
pspCharInfo.bitmapHeight = Debug.Font.charHeight * Debug.fontPixelSize;
pspCharInfo.sfp26Width = pspCharInfo.bitmapWidth << 6;
pspCharInfo.sfp26Height = pspCharInfo.bitmapHeight << 6;
pspCharInfo.sfp26AdvanceH = pspCharInfo.bitmapWidth << 6;
pspCharInfo.sfp26AdvanceV = pspCharInfo.bitmapHeight << 6;
}
int result = font.fontLib.triggetGetCharInfo(pspCharInfo);
if (result == 0) {
pspCharInfo.write(charInfoPtr);
}
return result;
}
@HLEFunction(nid = 0x980F4895, version = 150, checkInsideInterrupt = true)
public int sceFontGetCharGlyphImage(int fontHandle, int charCode, TPointer glyphImagePtr) {
charCode&= 0xffff;
Font font = getFont(fontHandle, false);
// Read GlyphImage data.
int pixelFormat = glyphImagePtr.getValue32(0);
int xPos64 = glyphImagePtr.getValue32(4);
int yPos64 = glyphImagePtr.getValue32(8);
int bufWidth = glyphImagePtr.getValue16(12);
int bufHeight = glyphImagePtr.getValue16(14);
int bytesPerLine = glyphImagePtr.getValue16(16);
int buffer = glyphImagePtr.getValue32(20);
// 26.6 fixed-point.
int xPosI = xPos64 >> 6;
int yPosI = yPos64 >> 6;
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetCharGlyphImage charCode=%04X (%c), xPos=%d, yPos=%d, buffer=0x%08X, bufWidth=%d, bufHeight=%d, bytesPerLine=%d, pixelFormat=%d", charCode, (charCode <= 0xFF ? (char) charCode : '?'), xPosI, yPosI, buffer, bufWidth, bufHeight, bytesPerLine, pixelFormat));
}
// If there's an internal font loaded, use it to display the text.
// Otherwise, switch to our Debug font.
if (!getUseDebugFont()) {
font.fontInfo.printFont(
buffer, bytesPerLine, bufWidth, bufHeight,
xPosI, yPosI,
xPos64 % 64, yPos64 % 64,
0, 0, bufWidth, bufHeight,
pixelFormat, charCode, font.fontLib.getAltCharCode(), SceFontInfo.FONT_PGF_GLYPH_TYPE_CHAR, false);
} else {
// Font adjustment.
// TODO: Instead of using the loaded PGF, figure out
// the proper values for the Debug font.
yPosI -= font.pgf.getMaxBaseYAdjust() >> 6;
yPosI += font.pgf.getMaxTopYAdjust() >> 6;
Debug.printFontbuffer(
buffer, bytesPerLine, bufWidth, bufHeight,
xPosI, yPosI, pixelFormat, charCode, font.fontLib.getAltCharCode());
}
return 0;
}
@HLEFunction(nid = 0x099EF33C, version = 150, checkInsideInterrupt = false)
public int sceFontFindOptimumFont(int fontLibHandle, pspFontStyle fontStyle, @CanBeNull TErrorPointer32 errorCodePtr) {
if (fontStyle.isEmpty()) {
// Always return the first font entry if no criteria is specified for the fontStyle
return 0;
}
Font optimumFont = getOptimumFont(fontStyle);
// No font found for the given style, try to find a font without the given font style (bold, italic...)
if (optimumFont == null && fontStyle.fontStyle != 0) {
fontStyle.fontStyle = 0;
fontStyle.fontStyleSub = 0;
optimumFont = getOptimumFont(fontStyle);
}
// No font found for the given style, try to find a font without the given font size.
if (optimumFont == null && (fontStyle.fontH != 0f || fontStyle.fontV != 0f)) {
fontStyle.fontH = 0f;
fontStyle.fontV = 0f;
optimumFont = getOptimumFont(fontStyle);
}
// No font found for the given style, try to find a font without the given country.
if (optimumFont == null && (fontStyle.fontCountry != 0)) {
fontStyle.fontCountry = 0;
optimumFont = getOptimumFont(fontStyle);
}
int index = getFontIndex(optimumFont);
if (index < 0) {
// optimum font not found, assume font at index 0?
index = 0;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontFindOptimumFont found font at index %d: %s", index, optimumFont));
}
return index;
}
@HLEFunction(nid = 0x3AEA8CB6, version = 150, checkInsideInterrupt = true)
public int sceFontClose(int fontHandle) {
Font font = fontsMap.get(fontHandle);
if (font != null && font.fontLib != null) {
font.fontLib.closeFont(font);
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontClose font already closed font=%s", font));
}
}
return 0;
}
@HLEFunction(nid = 0x574B6FBC, version = 150, checkInsideInterrupt = true)
public int sceFontDoneLib(int fontLibHandle) {
FontLib fontLib = fontLibsMap.get(fontLibHandle);
if (fontLib != null) {
// Free all reserved font lib space and close all open font files.
fontLib.triggerCloseCallback();
fontLib.done();
fontLibsMap.remove(fontLibHandle);
HLEUidObjectMapping.removeObject(fontLib);
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontDoneLib font lib already done 0x%08X", fontLibHandle));
}
}
return 0;
}
@HLEFunction(nid = 0xA834319D, version = 150, checkInsideInterrupt = false)
public int sceFontOpen(int fontLibHandle, int index, int mode, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
if (index < 0) {
errorCodePtr.setValue(SceKernelErrors.ERROR_FONT_INVALID_PARAMETER);
return 0;
}
Font font = fontLib.openFont(internalFonts.get(index), mode, true);
if (log.isDebugEnabled()) {
log.debug(String.format("Opening '%s' - '%s', font=%s", font.pgf.getFontName(), font.pgf.getFontType(), font));
}
errorCodePtr.setValue(0);
return font.getHandle();
}
@HLEFunction(nid = 0xCA1E6945, version = 150, checkInsideInterrupt = true, stackUsage = 0x120)
public int sceFontGetCharGlyphImage_Clip(int fontHandle, int charCode, TPointer glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) {
charCode&= 0xffff;
Font font = getFont(fontHandle, false);
// Identical to sceFontGetCharGlyphImage, but uses a clipping
// rectangle over the char.
// Read GlyphImage data.
int pixelFormat = glyphImagePtr.getValue32(0);
int xPos64 = glyphImagePtr.getValue32(4);
int yPos64 = glyphImagePtr.getValue32(8);
int bufWidth = glyphImagePtr.getValue16(12);
int bufHeight = glyphImagePtr.getValue16(14);
int bytesPerLine = glyphImagePtr.getValue16(16);
int buffer = glyphImagePtr.getValue32(20);
// 26.6 fixed-point.
int xPosI = xPos64 >> 6;
int yPosI = yPos64 >> 6;
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetCharGlyphImage_Clip charCode=%04X (%c), xPos=%d(%d), yPos=%d(%d), buffer=0x%08X, bufWidth=%d, bufHeight=%d, bytesPerLine=%d, pixelFormat=%d", charCode, (charCode <= 0xFF ? (char) charCode : '?'), xPosI, xPos64, yPosI, yPos64, buffer, bufWidth, bufHeight, bytesPerLine, pixelFormat));
}
// If there's an internal font loaded, use it to display the text.
// Otherwise, switch to our Debug font.
if (!getUseDebugFont()) {
font.fontInfo.printFont(
buffer, bytesPerLine, bufWidth, bufHeight,
xPosI, yPosI,
xPos64 % 64, yPos64 % 64,
clipXPos, clipYPos, clipWidth, clipHeight,
pixelFormat, charCode, font.fontLib.getAltCharCode(), SceFontInfo.FONT_PGF_GLYPH_TYPE_CHAR, false);
} else {
// Font adjustment.
// TODO: Instead of using the loaded PGF, figure out
// the proper values for the Debug font.
yPosI -= font.pgf.getMaxBaseYAdjust() >> 6;
yPosI += font.pgf.getMaxTopYAdjust() >> 6;
if (yPosI < 0) {
yPosI = 0;
}
Debug.printFontbuffer(
buffer, bytesPerLine, bufWidth, bufHeight,
xPosI, yPosI, pixelFormat, charCode, font.fontLib.getAltCharCode());
}
return 0;
}
@HLEFunction(nid = 0x27F6E642, version = 150, checkInsideInterrupt = true)
public int sceFontGetNumFontList(int fontLibHandle, @CanBeNull TErrorPointer32 errorCodePtr) {
// Get all the available fonts
int numFonts = internalFonts.size();
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetNumFontList returning %d", numFonts));
}
errorCodePtr.setValue(0);
return numFonts;
}
@HLEFunction(nid = 0xBC75D85B, version = 150, checkInsideInterrupt = true)
public int sceFontGetFontList(int fontLibHandle, TPointer fontStylePtr, int numFonts) {
int fontsNum = Math.min(internalFonts.size(), numFonts);
for (int i = 0; i < fontsNum; i++) {
Font font = internalFonts.get(i);
pspFontStyle fontStyle = font.getFontStyle();
fontStyle.write(fontStylePtr, i * fontStyle.sizeof());
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetFontList returning font #%d at 0x%08X: %s", i, fontStylePtr.getAddress() + i * fontStyle.sizeof(), fontStyle));
}
}
return 0;
}
@HLEFunction(nid = 0xEE232411, version = 150, checkInsideInterrupt = true)
public int sceFontSetAltCharacterCode(int fontLibHandle, int charCode) {
FontLib fontLib = getFontLib(fontLibHandle);
charCode&= 0xffff;
fontLib.setAltCharCode(charCode);
return 0;
}
@HLEFunction(nid = 0x5C3E4A9E, version = 150, checkInsideInterrupt = true)
public int sceFontGetCharImageRect(int fontHandle, int charCode, TPointer16 charRectPtr) {
charCode&= 0xffff;
Font font = getFont(fontHandle, false);
pspCharInfo charInfo = font.fontInfo.getCharInfo(charCode, SceFontInfo.FONT_PGF_GLYPH_TYPE_CHAR);
// This function retrieves the dimensions of a specific char.
if (charInfo != null) {
charRectPtr.setValue(0, charInfo.bitmapWidth);
charRectPtr.setValue(2, charInfo.bitmapHeight);
} else {
charRectPtr.setValue(0, 0);
charRectPtr.setValue(2, 0);
}
return 0;
}
@HLEFunction(nid = 0x472694CD, version = 150)
public float sceFontPointToPixelH(int fontLibHandle, float fontPointsH, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
return fontPointsH * fontLib.fontHRes / pointDPI;
}
@HLEFunction(nid = 0x5333322D, version = 150, checkInsideInterrupt = true)
public int sceFontGetFontInfoByIndexNumber(int fontLibHandle, TPointer fontStylePtr, int fontIndex) {
// It says FontInfo but it means Style - this is like sceFontGetFontList().
getFontLib(fontLibHandle);
if (fontIndex < 0 || fontIndex >= internalFonts.size()) {
return ERROR_FONT_INVALID_PARAMETER;
}
Font font = internalFonts.get(fontIndex);
pspFontStyle fontStyle = font.getFontStyle();
fontStyle.write(fontStylePtr);
if (log.isDebugEnabled()) {
log.debug(String.format("sceFontGetFontInfoByIndexNumber returning font #%d at %s: %s", fontIndex, fontStylePtr, fontStyle));
}
return 0;
}
@HLEFunction(nid = 0x48293280, version = 150, checkInsideInterrupt = true)
public int sceFontSetResolution(int fontLibHandle, float hRes, float vRes) {
FontLib fontLib = getFontLib(fontLibHandle);
fontLib.fontHRes = hRes;
fontLib.fontVRes = vRes;
return 0;
}
@HLEFunction(nid = 0x02D7F94B, version = 150, checkInsideInterrupt = true)
public int sceFontFlush(int fontHandle) {
Font font = getFont(fontHandle, false);
font.fontLib.flushFont(font);
return 0;
}
@HLEFunction(nid = 0x681E61A7, version = 150, checkInsideInterrupt = true)
public int sceFontFindFont(int fontLibHandle, pspFontStyle fontStyle, @CanBeNull TErrorPointer32 errorCodePtr) {
errorCodePtr.setValue(0);
int fontsNum = internalFonts.size();
for (int i = 0; i < fontsNum; i++) {
if (isFontMatchingStyle(internalFonts.get(i), fontStyle, false)) {
return i;
}
}
return -1;
}
@HLEFunction(nid = 0x3C4B7E82, version = 150)
public float sceFontPointToPixelV(int fontLibHandle, float fontPointsV, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
return fontPointsV * fontLib.fontVRes / pointDPI;
}
@HLEFunction(nid = 0x74B21701, version = 150)
public float sceFontPixelToPointH(int fontLibHandle, float fontPixelsH, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
return fontPixelsH * pointDPI / fontLib.fontHRes;
}
@HLEFunction(nid = 0xF8F0752E, version = 150)
public float sceFontPixelToPointV(int fontLibHandle, float fontPixelsV, @CanBeNull TErrorPointer32 errorCodePtr) {
FontLib fontLib = getFontLib(fontLibHandle);
errorCodePtr.setValue(0);
// Convert vertical pixels to floating points (Pixels Per Inch to Points Per Inch).
// points = (pixels / dpiX) * 72.
return fontPixelsV * pointDPI / fontLib.fontVRes;
}
@HLEUnimplemented
@HLEFunction(nid = 0x2F67356A, version = 150)
public int sceFontCalcMemorySize() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x48B06520, version = 150)
public int sceFontGetShadowImageRect(int fontHandle, int charCode, TPointer charInfoPtr) {
charCode&= 0xffff;
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x568BE516, version = 150)
public int sceFontGetShadowGlyphImage(int fontHandle, int charCode, TPointer glyphImagePtr) {
charCode&= 0xffff;
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x5DCF6858, version = 150)
public int sceFontGetShadowGlyphImage_Clip(int fontHandle, int charCode, TPointer glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) {
charCode&= 0xffff;
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xAA3DE7B5, version = 150)
public int sceFontGetShadowInfo(int fontHandle, int charCode, TPointer charInfoPtr) {
charCode&= 0xffff;
return 0;
}
}