2022-05-19 01:51:26 -07:00
|
|
|
#include "font_manager.h"
|
|
|
|
|
2022-09-15 02:38:23 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
#include "color.h"
|
|
|
|
#include "db.h"
|
|
|
|
#include "memory_manager.h"
|
|
|
|
|
2022-06-18 06:45:28 -07:00
|
|
|
// The maximum number of interface fonts.
|
|
|
|
#define INTERFACE_FONT_MAX (16)
|
|
|
|
|
|
|
|
typedef struct InterfaceFontGlyph {
|
|
|
|
short width;
|
2022-08-06 03:48:16 -07:00
|
|
|
short height;
|
|
|
|
int offset;
|
2022-06-18 06:45:28 -07:00
|
|
|
} InterfaceFontGlyph;
|
|
|
|
|
|
|
|
typedef struct InterfaceFontDescriptor {
|
2022-08-06 03:48:16 -07:00
|
|
|
short maxHeight;
|
2022-06-18 06:45:28 -07:00
|
|
|
short letterSpacing;
|
|
|
|
short wordSpacing;
|
2022-08-06 03:48:16 -07:00
|
|
|
short lineSpacing;
|
2022-06-18 06:45:28 -07:00
|
|
|
short field_8;
|
|
|
|
short field_A;
|
|
|
|
InterfaceFontGlyph glyphs[256];
|
|
|
|
unsigned char* data;
|
|
|
|
} InterfaceFontDescriptor;
|
|
|
|
|
|
|
|
static int interfaceFontLoad(int font);
|
|
|
|
static void interfaceFontSetCurrentImpl(int font);
|
|
|
|
static int interfaceFontGetLineHeightImpl();
|
|
|
|
static int interfaceFontGetStringWidthImpl(const char* string);
|
|
|
|
static int interfaceFontGetCharacterWidthImpl(int ch);
|
|
|
|
static int interfaceFontGetMonospacedStringWidthImpl(const char* string);
|
|
|
|
static int interfaceFontGetLetterSpacingImpl();
|
|
|
|
static int interfaceFontGetBufferSizeImpl(const char* string);
|
|
|
|
static int interfaceFontGetMonospacedCharacterWidthImpl();
|
|
|
|
static void interfaceFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color);
|
|
|
|
static void interfaceFontByteSwapUInt32(unsigned int* value);
|
|
|
|
static void interfaceFontByteSwapInt32(int* value);
|
|
|
|
static void interfaceFontByteSwapUInt16(unsigned short* value);
|
|
|
|
static void interfaceFontByteSwapInt16(short* value);
|
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
// 0x518680
|
2022-06-18 06:45:28 -07:00
|
|
|
static bool gInterfaceFontsInitialized = false;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x518684
|
2022-06-18 06:45:28 -07:00
|
|
|
static int gInterfaceFontsLength = 0;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x518688
|
|
|
|
FontManager gModernFontManager = {
|
|
|
|
100,
|
|
|
|
110,
|
|
|
|
interfaceFontSetCurrentImpl,
|
|
|
|
interfaceFontDrawImpl,
|
|
|
|
interfaceFontGetLineHeightImpl,
|
|
|
|
interfaceFontGetStringWidthImpl,
|
|
|
|
interfaceFontGetCharacterWidthImpl,
|
|
|
|
interfaceFontGetMonospacedStringWidthImpl,
|
|
|
|
interfaceFontGetLetterSpacingImpl,
|
|
|
|
interfaceFontGetBufferSizeImpl,
|
|
|
|
interfaceFontGetMonospacedCharacterWidthImpl,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 0x586838
|
2022-06-18 06:45:28 -07:00
|
|
|
static InterfaceFontDescriptor gInterfaceFontDescriptors[INTERFACE_FONT_MAX];
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x58E938
|
2022-06-18 06:45:28 -07:00
|
|
|
static int gCurrentInterfaceFont;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x58E93C
|
2022-06-18 06:45:28 -07:00
|
|
|
static InterfaceFontDescriptor* gCurrentInterfaceFontDescriptor;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x441C80
|
|
|
|
int interfaceFontsInit()
|
|
|
|
{
|
|
|
|
int currentFont = -1;
|
|
|
|
|
|
|
|
for (int font = 0; font < INTERFACE_FONT_MAX; font++) {
|
|
|
|
if (interfaceFontLoad(font) == -1) {
|
2022-08-06 03:48:16 -07:00
|
|
|
gInterfaceFontDescriptors[font].maxHeight = 0;
|
2022-05-19 01:51:26 -07:00
|
|
|
gInterfaceFontDescriptors[font].data = NULL;
|
|
|
|
} else {
|
|
|
|
++gInterfaceFontsLength;
|
|
|
|
|
|
|
|
if (currentFont == -1) {
|
|
|
|
currentFont = font;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentFont == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
gInterfaceFontsInitialized = true;
|
|
|
|
|
|
|
|
interfaceFontSetCurrentImpl(currentFont + 100);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x441CEC
|
|
|
|
void interfaceFontsExit()
|
|
|
|
{
|
|
|
|
for (int font = 0; font < INTERFACE_FONT_MAX; font++) {
|
|
|
|
if (gInterfaceFontDescriptors[font].data != NULL) {
|
|
|
|
internal_free_safe(gInterfaceFontDescriptors[font].data, __FILE__, __LINE__); // FONTMGR.C, 124
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x441D20
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontLoad(int font_index)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
InterfaceFontDescriptor* fontDescriptor = &(gInterfaceFontDescriptors[font_index]);
|
|
|
|
|
|
|
|
char path[56];
|
|
|
|
sprintf(path, "font%d.aaf", font_index);
|
|
|
|
|
|
|
|
File* stream = fileOpen(path, "rb");
|
|
|
|
if (stream == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fileSize = fileGetSize(stream);
|
|
|
|
|
|
|
|
int sig;
|
|
|
|
if (fileRead(&sig, 4, 1, stream) != 1) goto err;
|
|
|
|
|
2022-05-28 11:39:47 -07:00
|
|
|
interfaceFontByteSwapInt32(&sig);
|
2022-05-19 01:51:26 -07:00
|
|
|
if (sig != 0x41414646) goto err;
|
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
if (fileRead(&(fontDescriptor->maxHeight), 2, 1, stream) != 1) goto err;
|
|
|
|
interfaceFontByteSwapInt16(&(fontDescriptor->maxHeight));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
if (fileRead(&(fontDescriptor->letterSpacing), 2, 1, stream) != 1) goto err;
|
2022-05-28 11:39:47 -07:00
|
|
|
interfaceFontByteSwapInt16(&(fontDescriptor->letterSpacing));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
if (fileRead(&(fontDescriptor->wordSpacing), 2, 1, stream) != 1) goto err;
|
2022-05-28 11:39:47 -07:00
|
|
|
interfaceFontByteSwapInt16(&(fontDescriptor->wordSpacing));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
if (fileRead(&(fontDescriptor->lineSpacing), 2, 1, stream) != 1) goto err;
|
|
|
|
interfaceFontByteSwapInt16(&(fontDescriptor->lineSpacing));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
for (int index = 0; index < 256; index++) {
|
|
|
|
InterfaceFontGlyph* glyph = &(fontDescriptor->glyphs[index]);
|
|
|
|
|
|
|
|
if (fileRead(&(glyph->width), 2, 1, stream) != 1) goto err;
|
2022-05-28 11:39:47 -07:00
|
|
|
interfaceFontByteSwapInt16(&(glyph->width));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
if (fileRead(&(glyph->height), 2, 1, stream) != 1) goto err;
|
|
|
|
interfaceFontByteSwapInt16(&(glyph->height));
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
if (fileRead(&(glyph->offset), 4, 1, stream) != 1) goto err;
|
|
|
|
interfaceFontByteSwapInt32(&(glyph->offset));
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fileSize -= sizeof(InterfaceFontDescriptor);
|
|
|
|
|
2022-05-21 08:22:03 -07:00
|
|
|
fontDescriptor->data = (unsigned char*)internal_malloc_safe(fileSize, __FILE__, __LINE__); // FONTMGR.C, 259
|
2022-05-19 01:51:26 -07:00
|
|
|
if (fontDescriptor->data == NULL) goto err;
|
|
|
|
|
|
|
|
if (fileRead(fontDescriptor->data, fileSize, 1, stream) != 1) {
|
|
|
|
internal_free_safe(fontDescriptor->data, __FILE__, __LINE__); // FONTMGR.C, 268
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
fileClose(stream);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
fileClose(stream);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442120
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontSetCurrentImpl(int font)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
font -= 100;
|
|
|
|
|
|
|
|
if (gInterfaceFontDescriptors[font].data != NULL) {
|
|
|
|
gCurrentInterfaceFont = font;
|
|
|
|
gCurrentInterfaceFontDescriptor = &(gInterfaceFontDescriptors[font]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442168
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetLineHeightImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
return gCurrentInterfaceFontDescriptor->lineSpacing + gCurrentInterfaceFontDescriptor->maxHeight;
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442188
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetStringWidthImpl(const char* string)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* pch = string;
|
|
|
|
int width = 0;
|
|
|
|
|
|
|
|
while (*pch != '\0') {
|
|
|
|
int v3;
|
|
|
|
int v4;
|
|
|
|
|
|
|
|
if (*pch == ' ') {
|
|
|
|
v3 = gCurrentInterfaceFontDescriptor->letterSpacing;
|
|
|
|
v4 = gCurrentInterfaceFontDescriptor->wordSpacing;
|
|
|
|
} else {
|
|
|
|
v3 = gCurrentInterfaceFontDescriptor->glyphs[*pch & 0xFF].width;
|
|
|
|
v4 = gCurrentInterfaceFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
width += v3 + v4;
|
|
|
|
|
|
|
|
pch++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4421DC
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetCharacterWidthImpl(int ch)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
int width;
|
|
|
|
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == ' ') {
|
|
|
|
width = gCurrentInterfaceFontDescriptor->wordSpacing;
|
|
|
|
} else {
|
|
|
|
width = gCurrentInterfaceFontDescriptor->glyphs[ch].width;
|
|
|
|
}
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442210
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetMonospacedStringWidthImpl(const char* str)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return interfaceFontGetMonospacedCharacterWidthImpl() * strlen(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442240
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetLetterSpacingImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gCurrentInterfaceFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442258
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetBufferSizeImpl(const char* str)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return interfaceFontGetStringWidthImpl(str) * interfaceFontGetLineHeightImpl();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442278
|
2022-06-18 06:45:28 -07:00
|
|
|
static int interfaceFontGetMonospacedCharacterWidthImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int v1;
|
|
|
|
if (gCurrentInterfaceFontDescriptor->wordSpacing <= gCurrentInterfaceFontDescriptor->field_8) {
|
2022-08-06 03:48:16 -07:00
|
|
|
v1 = gCurrentInterfaceFontDescriptor->lineSpacing;
|
2022-05-19 01:51:26 -07:00
|
|
|
} else {
|
|
|
|
v1 = gCurrentInterfaceFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
return v1 + gCurrentInterfaceFontDescriptor->maxHeight;
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4422B4
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (!gInterfaceFontsInitialized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((color & FONT_SHADOW) != 0) {
|
|
|
|
color &= ~FONT_SHADOW;
|
|
|
|
// NOTE: Other font options preserved. This is different from text font
|
|
|
|
// shadows.
|
|
|
|
interfaceFontDrawImpl(buf + pitch + 1, string, length, pitch, (color & ~0xFF) | _colorTable[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char* palette = _getColorBlendTable(color & 0xFF);
|
|
|
|
|
|
|
|
int monospacedCharacterWidth;
|
|
|
|
if ((color & FONT_MONO) != 0) {
|
|
|
|
// NOTE: Uninline.
|
|
|
|
monospacedCharacterWidth = interfaceFontGetMonospacedCharacterWidthImpl();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char* ptr = buf;
|
|
|
|
while (*string != '\0') {
|
|
|
|
char ch = *string++;
|
|
|
|
|
|
|
|
int characterWidth;
|
|
|
|
if (ch == ' ') {
|
|
|
|
characterWidth = gCurrentInterfaceFontDescriptor->wordSpacing;
|
|
|
|
} else {
|
|
|
|
characterWidth = gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF].width;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char* end;
|
|
|
|
if ((color & FONT_MONO) != 0) {
|
|
|
|
end = ptr + monospacedCharacterWidth;
|
|
|
|
ptr += (monospacedCharacterWidth - characterWidth - gCurrentInterfaceFontDescriptor->letterSpacing) / 2;
|
|
|
|
} else {
|
|
|
|
end = ptr + characterWidth + gCurrentInterfaceFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end - buf > length) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
InterfaceFontGlyph* glyph = &(gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF]);
|
2022-08-06 03:48:16 -07:00
|
|
|
unsigned char* glyphDataPtr = gCurrentInterfaceFontDescriptor->data + glyph->offset;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// Skip blank pixels (difference between font's line height and glyph height).
|
2022-08-06 03:48:16 -07:00
|
|
|
ptr += (gCurrentInterfaceFontDescriptor->maxHeight - glyph->height) * pitch;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-08-06 03:48:16 -07:00
|
|
|
for (int y = 0; y < glyph->height; y++) {
|
2022-05-19 01:51:26 -07:00
|
|
|
for (int x = 0; x < glyph->width; x++) {
|
|
|
|
unsigned char byte = *glyphDataPtr++;
|
|
|
|
|
|
|
|
*ptr++ = palette[(byte << 8) + *ptr];
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr += pitch - glyph->width;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((color & FONT_UNDERLINE) != 0) {
|
|
|
|
int length = ptr - buf;
|
2022-08-06 03:48:16 -07:00
|
|
|
unsigned char* underlinePtr = buf + pitch * (gCurrentInterfaceFontDescriptor->maxHeight - 1);
|
2022-05-19 01:51:26 -07:00
|
|
|
for (int index = 0; index < length; index++) {
|
|
|
|
*underlinePtr++ = color & 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_freeColorBlendTable(color & 0xFF);
|
|
|
|
}
|
2022-05-28 11:39:47 -07:00
|
|
|
|
|
|
|
// NOTE: Inlined.
|
|
|
|
//
|
|
|
|
// 0x442520
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontByteSwapUInt32(unsigned int* value)
|
2022-05-28 11:39:47 -07:00
|
|
|
{
|
|
|
|
unsigned int swapped = *value;
|
|
|
|
unsigned short high = swapped >> 16;
|
|
|
|
// NOTE: Uninline.
|
|
|
|
interfaceFontByteSwapUInt16(&high);
|
|
|
|
unsigned short low = swapped & 0xFFFF;
|
|
|
|
// NOTE: Uninline.
|
|
|
|
interfaceFontByteSwapUInt16(&low);
|
|
|
|
*value = (low << 16) | high;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: 0x442520 with different signature.
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontByteSwapInt32(int* value)
|
2022-05-28 11:39:47 -07:00
|
|
|
{
|
|
|
|
interfaceFontByteSwapUInt32((unsigned int*)value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x442568
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontByteSwapUInt16(unsigned short* value)
|
2022-05-28 11:39:47 -07:00
|
|
|
{
|
|
|
|
unsigned short swapped = *value;
|
|
|
|
swapped = (swapped >> 8) | (swapped << 8);
|
|
|
|
*value = swapped;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: 0x442568 with different signature.
|
2022-06-18 06:45:28 -07:00
|
|
|
static void interfaceFontByteSwapInt16(short* value)
|
2022-05-28 11:39:47 -07:00
|
|
|
{
|
|
|
|
interfaceFontByteSwapUInt16((unsigned short*)value);
|
|
|
|
}
|