fallout2-ce/src/text_font.cc

379 lines
9.6 KiB
C++

#include "text_font.h"
#include "color.h"
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
#include <stdio.h>
#include <string.h>
// 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
TextFontDescriptor gTextFontDescriptors[TEXT_FONT_MAX];
// 0x6ADBD0
FontManager gFontManagers[FONT_MANAGER_MAX];
// 0x6ADD88
TextFontDescriptor* gCurrentTextFontDescriptor;
// 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;
char path[COMPAT_MAX_PATH];
sprintf(path, "font%d.fon", font);
// 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");
if (stream == NULL) {
goto out;
}
if (fileRead(textFontDescriptor, sizeof(TextFontDescriptor), 1, stream) != 1) {
goto out;
}
textFontDescriptor->glyphs = (TextFontGlyph*)internal_malloc(textFontDescriptor->glyphCount * sizeof(TextFontGlyph));
if (textFontDescriptor->glyphs == NULL) {
goto out;
}
if (fileRead(textFontDescriptor->glyphs, sizeof(TextFontGlyph), textFontDescriptor->glyphCount, stream) != textFontDescriptor->glyphCount) {
goto out;
}
int dataSize = textFontDescriptor->lineHeight * ((textFontDescriptor->glyphs[textFontDescriptor->glyphCount - 1].width + 7) >> 3) + textFontDescriptor->glyphs[textFontDescriptor->glyphCount - 1].dataOffset;
textFontDescriptor->data = (unsigned char*)internal_malloc(dataSize);
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
void textFontSetCurrentImpl(int font)
{
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
bool fontManagerFind(int font, FontManager** fontManagerPtr)
{
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
void textFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color)
{
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
int textFontGetLineHeightImpl()
{
return gCurrentTextFontDescriptor->lineHeight;
}
// 0x4D5B60
int textFontGeStringWidthImpl(const char* string)
{
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
int textFontGetCharacterWidthImpl(int ch)
{
return gCurrentTextFontDescriptor->glyphs[ch & 0xFF].width;
}
// 0x4D5BB8
int textFontGetMonospacedStringWidthImpl(const char* string)
{
return fontGetMonospacedCharacterWidth() * strlen(string);
}
// 0x4D5BD8
int textFontGetLetterSpacingImpl()
{
return gCurrentTextFontDescriptor->letterSpacing;
}
// 0x4D5BE4
int textFontGetBufferSizeImpl(const char* string)
{
return fontGetStringWidth(string) * fontGetLineHeight();
}
// 0x4D5BF8
int textFontGetMonospacedCharacterWidthImpl()
{
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;
}