Added missing file.

This commit is contained in:
Justin Marshall
2026-05-28 20:06:52 -07:00
parent 2aa8ee62cb
commit 3b222499ca
+508
View File
@@ -0,0 +1,508 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code 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.
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "tr_local.h"
const char* DEFAULT_FONT = "Gothic821_Cn_BT";
static const float old_scale2 = 0.6f;
static const float old_scale1 = 0.3f;
/*
==============================
Old_SelectValueForScale
==============================
*/
ID_INLINE float Old_SelectValueForScale(float scale, float v0, float v1, float v2) {
return (scale >= old_scale2) ? v2 : (scale >= old_scale1) ? v1 : v0;
}
static void Font_ReadBigInt(idFile *fd, int &value) {
fd->Read(&value, sizeof(value));
value = BigLong(value);
}
static void Font_ReadBigShort(idFile *fd, short &value) {
fd->Read(&value, sizeof(value));
value = BigShort(value);
}
static bool Font_ReadTGAHeader(const char* filename, int &width, int &height) {
width = 0;
height = 0;
idFile* fd = fileSystem->OpenFileRead(filename);
if (fd == NULL) {
return false;
}
byte header[18];
const int bytesRead = fd->Read(header, sizeof(header));
fileSystem->CloseFile(fd);
if (bytesRead != sizeof(header)) {
return false;
}
width = header[12] | (header[13] << 8);
height = header[14] | (header[15] << 8);
return width > 0 && height > 0;
}
/*
==============================
R_NormalizeFontName
==============================
*/
static idStr R_NormalizeFontName(const char* fontName) {
idStr cleanName = fontName ? fontName : "";
cleanName.StripQuotes();
cleanName.BackSlashesToSlashes();
cleanName.StripFileExtension();
if (cleanName.IsEmpty() || cleanName.Icmp("fonts") == 0 || cleanName.Icmp("default") == 0) {
return DEFAULT_FONT;
}
const char* newFontPrefix = "newfonts/";
if (idStr::Icmpn(cleanName.c_str(), newFontPrefix, strlen(newFontPrefix)) == 0) {
idStr rest = cleanName.c_str() + strlen(newFontPrefix);
int slash = rest.Find('/');
if (slash >= 0) {
rest = rest.Left(slash);
}
return rest.IsEmpty() ? DEFAULT_FONT : rest;
}
cleanName.StripPath();
return cleanName.IsEmpty() ? DEFAULT_FONT : cleanName;
}
/*
==============================
idRenderSystemLocal::RegisterFont
==============================
*/
idFont* idRenderSystemLocal::RegisterFont(const char* fontName) {
idStr cleanName = R_NormalizeFontName(fontName);
for (int i = 0; i < fonts.Num(); i++) {
if (idStr::Icmp(fonts[i]->GetName(), cleanName.c_str()) == 0) {
fonts[i]->Touch();
return fonts[i];
}
}
idFont* font = new idFont(cleanName.c_str());
fonts.Append(font);
return font;
}
/*
==============================
idRenderSystemLocal::FreeFonts
==============================
*/
void idRenderSystemLocal::FreeFonts() {
fonts.DeleteContents(true);
}
/*
==============================
idFont::RemapFont
==============================
*/
idFont* idFont::RemapFont(const char* baseName) {
idStr cleanName = R_NormalizeFontName(baseName);
if (cleanName.Icmp(DEFAULT_FONT) == 0) {
return NULL;
}
return NULL;
}
/*
==============================
idFont::~idFont
==============================
*/
idFont::~idFont() {
if (fontInfo != NULL) {
Mem_Free(fontInfo->glyphData);
Mem_Free(fontInfo->charIndex);
delete fontInfo;
}
}
/*
==============================
idFont::idFont
==============================
*/
idFont::idFont(const char* n) : name(R_NormalizeFontName(n)) {
fontInfo = NULL;
alias = RemapFont(name.c_str());
if (alias != NULL) {
for (idFont* f = alias; f != NULL; f = f->alias) {
if (f == this) {
common->FatalError("Font alias \"%s\" is a circular reference!", n);
}
}
return;
}
if (!LoadFont()) {
if (name.Icmp(DEFAULT_FONT) == 0) {
common->FatalError("Could not load default font \"%s\"", DEFAULT_FONT);
}
else {
common->Warning("Could not load font %s", n);
alias = renderSystem->RegisterFont(DEFAULT_FONT);
}
}
}
struct oldGlyphInfo_t {
int height;
int top;
int bottom;
int pitch;
int xSkip;
int imageWidth;
int imageHeight;
float s;
float t;
float s2;
float t2;
int junk;
char materialName[32];
};
static const int GLYPHS_PER_FONT = 256;
/*
==============================
LoadOldGlyphData
==============================
*/
bool LoadOldGlyphData(const char* filename, oldGlyphInfo_t glyphInfo[GLYPHS_PER_FONT]) {
idFile* fd = fileSystem->OpenFileRead(filename);
if (fd == NULL) {
return false;
}
fd->Read(glyphInfo, GLYPHS_PER_FONT * sizeof(oldGlyphInfo_t));
for (int i = 0; i < GLYPHS_PER_FONT; i++) {
glyphInfo[i].height = LittleLong(glyphInfo[i].height);
glyphInfo[i].top = LittleLong(glyphInfo[i].top);
glyphInfo[i].bottom = LittleLong(glyphInfo[i].bottom);
glyphInfo[i].pitch = LittleLong(glyphInfo[i].pitch);
glyphInfo[i].xSkip = LittleLong(glyphInfo[i].xSkip);
glyphInfo[i].imageWidth = LittleLong(glyphInfo[i].imageWidth);
glyphInfo[i].imageHeight = LittleLong(glyphInfo[i].imageHeight);
glyphInfo[i].s = LittleFloat(glyphInfo[i].s);
glyphInfo[i].t = LittleFloat(glyphInfo[i].t);
glyphInfo[i].s2 = LittleFloat(glyphInfo[i].s2);
glyphInfo[i].t2 = LittleFloat(glyphInfo[i].t2);
assert(glyphInfo[i].imageWidth == glyphInfo[i].pitch);
assert(glyphInfo[i].imageHeight == glyphInfo[i].height);
assert(glyphInfo[i].imageWidth == (glyphInfo[i].s2 - glyphInfo[i].s) * 256);
assert(glyphInfo[i].imageHeight == (glyphInfo[i].t2 - glyphInfo[i].t) * 256);
assert(glyphInfo[i].junk == 0);
}
fileSystem->CloseFile(fd);
return true;
}
/*
==============================
idFont::LoadFont
==============================
*/
bool idFont::LoadFont() {
idStr fontName = va("newfonts/%s/48.dat", GetName());
idFile* fd = fileSystem->OpenFileRead(fontName);
if (fd == NULL) {
return false;
}
const int FONT_INFO_VERSION = 42;
const int FONT_INFO_MAGIC = (FONT_INFO_VERSION | ('i' << 24) | ('d' << 16) | ('f' << 8));
int version = 0;
Font_ReadBigInt(fd, version);
if (version != FONT_INFO_MAGIC) {
idLib::Warning("Wrong version in %s", GetName());
fileSystem->CloseFile(fd);
return false;
}
fontInfo = new fontInfo_t;
memset(fontInfo, 0, sizeof(*fontInfo));
short pointSize = 0;
Font_ReadBigShort(fd, pointSize);
assert(pointSize == 48);
Font_ReadBigShort(fd, fontInfo->ascender);
Font_ReadBigShort(fd, fontInfo->descender);
Font_ReadBigShort(fd, fontInfo->numGlyphs);
fontInfo->glyphData = (glyphInfo_t*)Mem_Alloc(sizeof(glyphInfo_t) * fontInfo->numGlyphs);
fontInfo->charIndex = (uint32_t*)Mem_Alloc(sizeof(uint32_t) * fontInfo->numGlyphs);
const int GLYPH_RECORD_SIZE = 10;
for (int i = 0; i < fontInfo->numGlyphs; i++) {
byte glyphBytes[GLYPH_RECORD_SIZE];
if (fd->Read(glyphBytes, sizeof(glyphBytes)) != sizeof(glyphBytes)) {
common->Warning("Short glyph data in %s", fontName.c_str());
fileSystem->CloseFile(fd);
return false;
}
fontInfo->glyphData[i].width = glyphBytes[0];
fontInfo->glyphData[i].height = glyphBytes[1];
fontInfo->glyphData[i].top = (char)glyphBytes[2];
fontInfo->glyphData[i].left = (char)glyphBytes[3];
fontInfo->glyphData[i].xSkip = glyphBytes[4];
fontInfo->glyphData[i].s = glyphBytes[6] | (glyphBytes[7] << 8);
fontInfo->glyphData[i].t = glyphBytes[8] | (glyphBytes[9] << 8);
}
fd->Read(fontInfo->charIndex, fontInfo->numGlyphs * sizeof(uint32_t));
for (int i = 0; i < fontInfo->numGlyphs; i++) {
fontInfo->charIndex[i] = LittleLong(fontInfo->charIndex[i]);
}
memset(fontInfo->ascii, -1, sizeof(fontInfo->ascii));
for (int i = 0; i < fontInfo->numGlyphs; i++) {
if (fontInfo->charIndex[i] < 128) {
fontInfo->ascii[fontInfo->charIndex[i]] = i;
}
else {
break;
}
}
idStr fontTextureName = fontName;
fontTextureName.SetFileExtension("tga");
fontInfo->material = declManager->FindMaterial(fontTextureName);
fontInfo->material->SetSort(SS_GUI);
if (!Font_ReadTGAHeader(fontTextureName.c_str(), fontInfo->materialWidth, fontInfo->materialHeight)) {
fontInfo->materialWidth = fontInfo->material->GetImageWidth();
fontInfo->materialHeight = fontInfo->material->GetImageHeight();
}
int pointSizes[3] = { 12, 24, 48 };
float scales[3] = { 4.0f, 2.0f, 1.0f };
for (int i = 0; i < 3; i++) {
oldGlyphInfo_t oldGlyphInfo[GLYPHS_PER_FONT];
const char* oldFileName = va("newfonts/%s/old_%d.dat", GetName(), pointSizes[i]);
if (LoadOldGlyphData(oldFileName, oldGlyphInfo)) {
int mh = 0;
int mw = 0;
for (int g = 0; g < GLYPHS_PER_FONT; g++) {
if (mh < oldGlyphInfo[g].height) {
mh = oldGlyphInfo[g].height;
}
if (mw < oldGlyphInfo[g].xSkip) {
mw = oldGlyphInfo[g].xSkip;
}
}
fontInfo->oldInfo[i].maxWidth = scales[i] * mw;
fontInfo->oldInfo[i].maxHeight = scales[i] * mh;
}
else {
int mh = 0;
int mw = 0;
for (int g = 0; g < fontInfo->numGlyphs; g++) {
if (mh < fontInfo->glyphData[g].height) {
mh = fontInfo->glyphData[g].height;
}
if (mw < fontInfo->glyphData[g].xSkip) {
mw = fontInfo->glyphData[g].xSkip;
}
}
fontInfo->oldInfo[i].maxWidth = mw;
fontInfo->oldInfo[i].maxHeight = mh;
}
}
fileSystem->CloseFile(fd);
return true;
}
/*
==============================
idFont::GetGlyphIndex
==============================
*/
int idFont::GetGlyphIndex(uint32_t idx) const {
if (idx < 128) {
return fontInfo->ascii[idx];
}
if (fontInfo->numGlyphs == 0) {
return -1;
}
if (fontInfo->charIndex == NULL) {
return idx;
}
int len = fontInfo->numGlyphs;
int mid = fontInfo->numGlyphs;
int offset = 0;
while (mid > 0) {
mid = len >> 1;
if (fontInfo->charIndex[offset + mid] <= idx) {
offset += mid;
}
len -= mid;
}
return (fontInfo->charIndex[offset] == idx) ? offset : -1;
}
/*
==============================
idFont::GetLineHeight
==============================
*/
float idFont::GetLineHeight(float scale) const {
if (alias != NULL) {
return alias->GetLineHeight(scale);
}
if (fontInfo != NULL) {
return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxHeight, fontInfo->oldInfo[1].maxHeight, fontInfo->oldInfo[2].maxHeight);
}
return 0.0f;
}
/*
==============================
idFont::GetAscender
==============================
*/
float idFont::GetAscender(float scale) const {
if (alias != NULL) {
return alias->GetAscender(scale);
}
if (fontInfo != NULL) {
return scale * fontInfo->ascender;
}
return 0.0f;
}
/*
==============================
idFont::GetMaxCharWidth
==============================
*/
float idFont::GetMaxCharWidth(float scale) const {
if (alias != NULL) {
return alias->GetMaxCharWidth(scale);
}
if (fontInfo != NULL) {
return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxWidth, fontInfo->oldInfo[1].maxWidth, fontInfo->oldInfo[2].maxWidth);
}
return 0.0f;
}
/*
==============================
idFont::GetGlyphWidth
==============================
*/
float idFont::GetGlyphWidth(float scale, uint32_t idx) const {
if (alias != NULL) {
return alias->GetGlyphWidth(scale, idx);
}
if (fontInfo != NULL) {
int i = GetGlyphIndex(idx);
const int asterisk = 42;
if (i == -1 && idx != asterisk) {
i = GetGlyphIndex(asterisk);
}
if (i >= 0) {
return scale * fontInfo->glyphData[i].xSkip;
}
}
return 0.0f;
}
/*
==============================
idFont::GetScaledGlyph
==============================
*/
void idFont::GetScaledGlyph(float scale, uint32_t idx, scaledGlyphInfo_t& glyphInfo) const {
if (alias != NULL) {
return alias->GetScaledGlyph(scale, idx, glyphInfo);
}
if (fontInfo != NULL) {
int i = GetGlyphIndex(idx);
const int asterisk = 42;
if (i == -1 && idx != asterisk) {
i = GetGlyphIndex(asterisk);
}
if (i >= 0) {
float invMaterialWidth = 1.0f / fontInfo->materialWidth;
float invMaterialHeight = 1.0f / fontInfo->materialHeight;
glyphInfo_t& gi = fontInfo->glyphData[i];
glyphInfo.xSkip = scale * gi.xSkip;
glyphInfo.top = scale * gi.top;
glyphInfo.left = scale * gi.left;
glyphInfo.width = scale * gi.width;
glyphInfo.height = scale * gi.height;
glyphInfo.s1 = (gi.s - 0.5f) * invMaterialWidth;
glyphInfo.t1 = (gi.t - 0.5f) * invMaterialHeight;
glyphInfo.s2 = (gi.s + gi.width + 0.5f) * invMaterialWidth;
glyphInfo.t2 = (gi.t + gi.height + 0.5f) * invMaterialHeight;
glyphInfo.material = fontInfo->material;
return;
}
}
memset(&glyphInfo, 0, sizeof(glyphInfo));
}
/*
==============================
idFont::Touch
==============================
*/
void idFont::Touch() {
if (alias != NULL) {
alias->Touch();
}
if (fontInfo != NULL) {
const_cast<idMaterial*>(fontInfo->material)->EnsureNotPurged();
fontInfo->material->SetSort(SS_GUI);
}
}