2022-05-19 01:51:26 -07:00
|
|
|
#include "text_font.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.h"
|
2022-05-28 02:34:49 -07:00
|
|
|
#include "platform_compat.h"
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-09-23 05:43:44 -07:00
|
|
|
namespace fallout {
|
|
|
|
|
2022-06-18 09:15:19 -07:00
|
|
|
// The maximum number of text fonts.
|
|
|
|
#define TEXT_FONT_MAX (10)
|
|
|
|
|
|
|
|
// The maximum number of font managers.
|
|
|
|
#define FONT_MANAGER_MAX (10)
|
|
|
|
|
|
|
|
typedef struct TextFontGlyph {
|
|
|
|
// The width of the glyph in pixels.
|
|
|
|
int width;
|
|
|
|
|
|
|
|
// Data offset into [TextFont.data].
|
|
|
|
int dataOffset;
|
|
|
|
} TextFontGlyph;
|
|
|
|
|
|
|
|
typedef struct TextFontDescriptor {
|
|
|
|
// The number of glyphs in the font.
|
|
|
|
int glyphCount;
|
|
|
|
|
|
|
|
// The height of the font.
|
|
|
|
int lineHeight;
|
|
|
|
|
|
|
|
// Horizontal spacing between characters in pixels.
|
|
|
|
int letterSpacing;
|
|
|
|
|
|
|
|
TextFontGlyph* glyphs;
|
|
|
|
unsigned char* data;
|
|
|
|
} TextFontDescriptor;
|
|
|
|
|
|
|
|
static void textFontSetCurrentImpl(int font);
|
|
|
|
static bool fontManagerFind(int font, FontManager** fontManagerPtr);
|
|
|
|
static void textFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color);
|
|
|
|
static int textFontGetLineHeightImpl();
|
|
|
|
static int textFontGeStringWidthImpl(const char* string);
|
|
|
|
static int textFontGetCharacterWidthImpl(int ch);
|
|
|
|
static int textFontGetMonospacedStringWidthImpl(const char* string);
|
|
|
|
static int textFontGetLetterSpacingImpl();
|
|
|
|
static int textFontGetBufferSizeImpl(const char* string);
|
|
|
|
static int textFontGetMonospacedCharacterWidthImpl();
|
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
// 0x4D5530
|
|
|
|
FontManager gTextFontManager = {
|
|
|
|
0,
|
|
|
|
9,
|
|
|
|
textFontSetCurrentImpl,
|
|
|
|
textFontDrawImpl,
|
|
|
|
textFontGetLineHeightImpl,
|
|
|
|
textFontGeStringWidthImpl,
|
|
|
|
textFontGetCharacterWidthImpl,
|
|
|
|
textFontGetMonospacedStringWidthImpl,
|
|
|
|
textFontGetLetterSpacingImpl,
|
|
|
|
textFontGetBufferSizeImpl,
|
|
|
|
textFontGetMonospacedCharacterWidthImpl,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 0x51E3B0
|
|
|
|
int gCurrentFont = -1;
|
|
|
|
|
|
|
|
// 0x51E3B4
|
|
|
|
int gFontManagersCount = 0;
|
|
|
|
|
|
|
|
// 0x51E3B8
|
|
|
|
FontManagerDrawTextProc* fontDrawText = NULL;
|
|
|
|
|
|
|
|
// 0x51E3BC
|
|
|
|
FontManagerGetLineHeightProc* fontGetLineHeight = NULL;
|
|
|
|
|
|
|
|
// 0x51E3C0
|
|
|
|
FontManagerGetStringWidthProc* fontGetStringWidth = NULL;
|
|
|
|
|
|
|
|
// 0x51E3C4
|
|
|
|
FontManagerGetCharacterWidthProc* fontGetCharacterWidth = NULL;
|
|
|
|
|
|
|
|
// 0x51E3C8
|
|
|
|
FontManagerGetMonospacedStringWidthProc* fontGetMonospacedStringWidth = NULL;
|
|
|
|
|
|
|
|
// 0x51E3CC
|
|
|
|
FontManagerGetLetterSpacingProc* fontGetLetterSpacing = NULL;
|
|
|
|
|
|
|
|
// 0x51E3D0
|
|
|
|
FontManagerGetBufferSizeProc* fontGetBufferSize = NULL;
|
|
|
|
|
|
|
|
// 0x51E3D4
|
|
|
|
FontManagerGetMonospacedCharacterWidth* fontGetMonospacedCharacterWidth = NULL;
|
|
|
|
|
|
|
|
// 0x6ADB08
|
2022-06-18 09:15:19 -07:00
|
|
|
static TextFontDescriptor gTextFontDescriptors[TEXT_FONT_MAX];
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x6ADBD0
|
2022-06-18 09:15:19 -07:00
|
|
|
static FontManager gFontManagers[FONT_MANAGER_MAX];
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x6ADD88
|
2022-06-18 09:15:19 -07:00
|
|
|
static TextFontDescriptor* gCurrentTextFontDescriptor;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x4D555C
|
|
|
|
int textFontsInit()
|
|
|
|
{
|
|
|
|
int currentFont = -1;
|
|
|
|
|
|
|
|
FontManager fontManager;
|
|
|
|
memcpy(&fontManager, &gTextFontManager, sizeof(fontManager));
|
|
|
|
|
|
|
|
for (int font = 0; font < TEXT_FONT_MAX; font++) {
|
|
|
|
if (textFontLoad(font) == -1) {
|
|
|
|
gTextFontDescriptors[font].glyphCount = 0;
|
|
|
|
} else {
|
|
|
|
if (currentFont == -1) {
|
|
|
|
currentFont = font;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentFont == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fontManagerAdd(&fontManager) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fontSetCurrent(currentFont);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D55CC
|
|
|
|
void textFontsExit()
|
|
|
|
{
|
|
|
|
for (int index = 0; index < TEXT_FONT_MAX; index++) {
|
|
|
|
TextFontDescriptor* textFontDescriptor = &(gTextFontDescriptors[index]);
|
|
|
|
if (textFontDescriptor->glyphCount != 0) {
|
|
|
|
internal_free(textFontDescriptor->glyphs);
|
|
|
|
internal_free(textFontDescriptor->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D55FC
|
|
|
|
int textFontLoad(int font)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
|
2022-05-28 02:34:49 -07:00
|
|
|
char path[COMPAT_MAX_PATH];
|
2022-12-08 12:05:50 -08:00
|
|
|
snprintf(path, sizeof(path), "font%d.fon", font);
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// NOTE: Original code is slightly different. It uses deep nesting and
|
|
|
|
// unwinds everything from the point of failure.
|
|
|
|
TextFontDescriptor* textFontDescriptor = &(gTextFontDescriptors[font]);
|
|
|
|
textFontDescriptor->data = NULL;
|
|
|
|
textFontDescriptor->glyphs = NULL;
|
|
|
|
|
|
|
|
File* stream = fileOpen(path, "rb");
|
2022-05-28 14:32:50 -07:00
|
|
|
int dataSize;
|
2022-05-19 01:51:26 -07:00
|
|
|
if (stream == NULL) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-06-11 14:02:54 -07:00
|
|
|
// NOTE: Original code reads entire descriptor in one go. This does not work
|
|
|
|
// in x64 because of the two pointers.
|
|
|
|
|
|
|
|
if (fileRead(&(textFontDescriptor->glyphCount), 4, 1, stream) != 1) goto out;
|
|
|
|
if (fileRead(&(textFontDescriptor->lineHeight), 4, 1, stream) != 1) goto out;
|
|
|
|
if (fileRead(&(textFontDescriptor->letterSpacing), 4, 1, stream) != 1) goto out;
|
|
|
|
|
|
|
|
int glyphsPtr;
|
|
|
|
if (fileRead(&glyphsPtr, 4, 1, stream) != 1) goto out;
|
|
|
|
|
|
|
|
int dataPtr;
|
|
|
|
if (fileRead(&dataPtr, 4, 1, stream) != 1) goto out;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-05-21 08:22:03 -07:00
|
|
|
textFontDescriptor->glyphs = (TextFontGlyph*)internal_malloc(textFontDescriptor->glyphCount * sizeof(TextFontGlyph));
|
2022-05-19 01:51:26 -07:00
|
|
|
if (textFontDescriptor->glyphs == NULL) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fileRead(textFontDescriptor->glyphs, sizeof(TextFontGlyph), textFontDescriptor->glyphCount, stream) != textFontDescriptor->glyphCount) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-05-28 14:32:50 -07:00
|
|
|
dataSize = textFontDescriptor->lineHeight * ((textFontDescriptor->glyphs[textFontDescriptor->glyphCount - 1].width + 7) >> 3) + textFontDescriptor->glyphs[textFontDescriptor->glyphCount - 1].dataOffset;
|
2022-05-21 08:22:03 -07:00
|
|
|
textFontDescriptor->data = (unsigned char*)internal_malloc(dataSize);
|
2022-05-19 01:51:26 -07:00
|
|
|
if (textFontDescriptor->data == NULL) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fileRead(textFontDescriptor->data, 1, dataSize, stream) != dataSize) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
if (textFontDescriptor->data != NULL) {
|
|
|
|
internal_free(textFontDescriptor->data);
|
|
|
|
textFontDescriptor->data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (textFontDescriptor->glyphs != NULL) {
|
|
|
|
internal_free(textFontDescriptor->glyphs);
|
|
|
|
textFontDescriptor->glyphs = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream != NULL) {
|
|
|
|
fileClose(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5780
|
|
|
|
int fontManagerAdd(FontManager* fontManager)
|
|
|
|
{
|
|
|
|
if (fontManager == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gFontManagersCount >= FONT_MANAGER_MAX) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if a font manager exists for any font in the specified range.
|
|
|
|
for (int index = fontManager->minFont; index < fontManager->maxFont; index++) {
|
|
|
|
FontManager* existingFontManager;
|
|
|
|
if (fontManagerFind(index, &existingFontManager)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&(gFontManagers[gFontManagersCount]), fontManager, sizeof(*fontManager));
|
|
|
|
gFontManagersCount++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D58AC
|
2022-06-18 09:15:19 -07:00
|
|
|
static void textFontSetCurrentImpl(int font)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if (font >= TEXT_FONT_MAX) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextFontDescriptor* textFontDescriptor = &(gTextFontDescriptors[font]);
|
|
|
|
if (textFontDescriptor->glyphCount == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gCurrentTextFontDescriptor = textFontDescriptor;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D58D4
|
|
|
|
int fontGetCurrent()
|
|
|
|
{
|
|
|
|
return gCurrentFont;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D58DC
|
|
|
|
void fontSetCurrent(int font)
|
|
|
|
{
|
|
|
|
FontManager* fontManager;
|
|
|
|
|
|
|
|
if (fontManagerFind(font, &fontManager)) {
|
|
|
|
fontDrawText = fontManager->drawTextProc;
|
|
|
|
fontGetLineHeight = fontManager->getLineHeightProc;
|
|
|
|
fontGetStringWidth = fontManager->getStringWidthProc;
|
|
|
|
fontGetCharacterWidth = fontManager->getCharacterWidthProc;
|
|
|
|
fontGetMonospacedStringWidth = fontManager->getMonospacedStringWidthProc;
|
|
|
|
fontGetLetterSpacing = fontManager->getLetterSpacingProc;
|
|
|
|
fontGetBufferSize = fontManager->getBufferSizeProc;
|
|
|
|
fontGetMonospacedCharacterWidth = fontManager->getMonospacedCharacterWidthProc;
|
|
|
|
|
|
|
|
gCurrentFont = font;
|
|
|
|
|
|
|
|
fontManager->setCurrentProc(font);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D595C
|
2022-06-18 09:15:19 -07:00
|
|
|
static bool fontManagerFind(int font, FontManager** fontManagerPtr)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
for (int index = 0; index < gFontManagersCount; index++) {
|
|
|
|
FontManager* fontManager = &(gFontManagers[index]);
|
|
|
|
if (font >= fontManager->minFont && font <= fontManager->maxFont) {
|
|
|
|
*fontManagerPtr = fontManager;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D59B0
|
2022-06-18 09:15:19 -07:00
|
|
|
static void textFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
if ((color & FONT_SHADOW) != 0) {
|
|
|
|
color &= ~FONT_SHADOW;
|
|
|
|
fontDrawText(buf + pitch + 1, string, length, pitch, _colorTable[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int monospacedCharacterWidth;
|
|
|
|
if ((color & FONT_MONO) != 0) {
|
|
|
|
monospacedCharacterWidth = fontGetMonospacedCharacterWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char* ptr = buf;
|
|
|
|
while (*string != '\0') {
|
|
|
|
char ch = *string++;
|
|
|
|
if (ch < gCurrentTextFontDescriptor->glyphCount) {
|
|
|
|
TextFontGlyph* glyph = &(gCurrentTextFontDescriptor->glyphs[ch & 0xFF]);
|
|
|
|
|
|
|
|
unsigned char* end;
|
|
|
|
if ((color & FONT_MONO) != 0) {
|
|
|
|
end = ptr + monospacedCharacterWidth;
|
|
|
|
ptr += (monospacedCharacterWidth - gCurrentTextFontDescriptor->letterSpacing - glyph->width) / 2;
|
|
|
|
} else {
|
|
|
|
end = ptr + glyph->width + gCurrentTextFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end - buf > length) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char* glyphData = gCurrentTextFontDescriptor->data + glyph->dataOffset;
|
|
|
|
for (int y = 0; y < gCurrentTextFontDescriptor->lineHeight; y++) {
|
|
|
|
int bits = 0x80;
|
|
|
|
for (int x = 0; x < glyph->width; x++) {
|
|
|
|
if (bits == 0) {
|
|
|
|
bits = 0x80;
|
|
|
|
glyphData++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*glyphData & bits) != 0) {
|
|
|
|
*ptr = color & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
bits >>= 1;
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
glyphData++;
|
|
|
|
ptr += pitch - glyph->width;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((color & FONT_UNDERLINE) != 0) {
|
|
|
|
// TODO: Probably additional -1 present, check.
|
|
|
|
int length = ptr - buf;
|
|
|
|
unsigned char* underlinePtr = buf + pitch * (gCurrentTextFontDescriptor->lineHeight - 1);
|
|
|
|
for (int pix = 0; pix < length; pix++) {
|
|
|
|
*underlinePtr++ = color & 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5B54
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetLineHeightImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
return gCurrentTextFontDescriptor->lineHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5B60
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGeStringWidthImpl(const char* string)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
|
|
|
|
const char* ch = string;
|
|
|
|
while (*ch != '\0') {
|
|
|
|
if (*ch < gCurrentTextFontDescriptor->glyphCount) {
|
|
|
|
width += gCurrentTextFontDescriptor->letterSpacing + gCurrentTextFontDescriptor->glyphs[*ch & 0xFF].width;
|
|
|
|
}
|
|
|
|
ch++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5BA4
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetCharacterWidthImpl(int ch)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
return gCurrentTextFontDescriptor->glyphs[ch & 0xFF].width;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5BB8
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetMonospacedStringWidthImpl(const char* string)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
return fontGetMonospacedCharacterWidth() * strlen(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5BD8
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetLetterSpacingImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
return gCurrentTextFontDescriptor->letterSpacing;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5BE4
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetBufferSizeImpl(const char* string)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
return fontGetStringWidth(string) * fontGetLineHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4D5BF8
|
2022-06-18 09:15:19 -07:00
|
|
|
static int textFontGetMonospacedCharacterWidthImpl()
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
|
|
|
|
for (int index = 0; index < gCurrentTextFontDescriptor->glyphCount; index++) {
|
|
|
|
TextFontGlyph* glyph = &(gCurrentTextFontDescriptor->glyphs[index]);
|
|
|
|
if (width < glyph->width) {
|
|
|
|
width = glyph->width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return width + gCurrentTextFontDescriptor->letterSpacing;
|
|
|
|
}
|
2022-09-23 05:43:44 -07:00
|
|
|
|
|
|
|
} // namespace fallout
|