../sys/msdos/font.c:24:12: warning: declaration of 'flags' shadows a global declaration [-Wshadow]
24 | uint32 flags;
| ^~~~~
In file included from ../include/hack.h:285,
from ../sys/msdos/font.c:3:
../include/flag.h:422:29: note: shadowed declaration is here
422 | extern NEARDATA struct flag flags;
| ^~~~~
make[2]: Entering directory '/home/nhmall/git/NHsource/util'
277 lines
7.3 KiB
C
Executable File
277 lines
7.3 KiB
C
Executable File
/* Maintain a data structure describing a monospaced bitmap font */
|
|
|
|
#include "hack.h"
|
|
#include "integer.h"
|
|
#include "font.h"
|
|
|
|
static uint32 read_u32(const unsigned char *);
|
|
static void add_unicode_index(
|
|
struct BitmapFont *font,
|
|
uint32 ch,
|
|
unsigned index);
|
|
static uint32 *uni_8to32(const char *);
|
|
|
|
struct BitmapFont *
|
|
load_font(const char *filename)
|
|
{
|
|
FILE *fp;
|
|
struct BitmapFont *font = NULL;
|
|
unsigned char header[32];
|
|
size_t size;
|
|
uint32 magic;
|
|
uint32 version;
|
|
uint32 headersize;
|
|
uint32 fontflags;
|
|
uint32 length;
|
|
uint32 charsize;
|
|
uint32 height;
|
|
uint32 width;
|
|
uint32 bwidth, memsize;
|
|
uint32 i;
|
|
|
|
fp = fopen(filename, "rb");
|
|
if (fp == NULL) goto error;
|
|
|
|
/* Read the PSF header */
|
|
size = fread(header, 1, sizeof(header), fp);
|
|
if (size != sizeof(header)) goto error;
|
|
|
|
/* Convert from little endian order */
|
|
magic = read_u32(header + 0);
|
|
if (magic != 0x864AB572) goto error;
|
|
version = read_u32(header + 4);
|
|
if (version != 0) goto error;
|
|
headersize = read_u32(header + 8);
|
|
if (headersize < sizeof(header)) goto error;
|
|
fontflags = read_u32(header + 12);
|
|
length = read_u32(header + 16);
|
|
charsize = read_u32(header + 20);
|
|
height = read_u32(header + 24);
|
|
width = read_u32(header + 28);
|
|
|
|
/* Check that the declared character size can hold the declared width
|
|
and height */
|
|
bwidth = (width + 7) / 8;
|
|
memsize = bwidth * height;
|
|
if (memsize > charsize) goto error;
|
|
|
|
/* Allocate a font structure */
|
|
font = (struct BitmapFont *) alloc(sizeof(struct BitmapFont));
|
|
memset(font, 0, sizeof(struct BitmapFont));
|
|
|
|
/* Dimensions of the font */
|
|
font->width = width;
|
|
font->height = height;
|
|
font->num_glyphs = length;
|
|
|
|
/* The glyph array */
|
|
font->glyphs = (unsigned char **) alloc(length * sizeof(unsigned char *));
|
|
memset(font->glyphs, 0, length * sizeof(unsigned char *));
|
|
|
|
/* Read the glyphs */
|
|
fseek(fp, headersize, SEEK_SET);
|
|
for (i = 0; i < length; ++i) {
|
|
font->glyphs[i] = (unsigned char *) alloc(memsize);
|
|
size = fread(font->glyphs[i], 1, memsize, fp);
|
|
if (size != memsize) goto error;
|
|
fseek(fp, charsize - memsize, SEEK_CUR);
|
|
}
|
|
|
|
if (fontflags & 0x01) {
|
|
/* Read the Unicode table */
|
|
char buf[128], buf2[128+1];
|
|
unsigned bufsize, strsize;
|
|
char *p;
|
|
uint32 *codepoints;
|
|
|
|
bufsize = 0;
|
|
i = 0;
|
|
while (i < length) {
|
|
unsigned j;
|
|
|
|
size = fread(buf + bufsize, 1, sizeof(buf) - bufsize, fp);
|
|
if (ferror(fp)) goto error;
|
|
bufsize += size;
|
|
if (bufsize == 0) goto error; /* unexpected EOF */
|
|
|
|
p = memchr(buf, 0xFF, bufsize);
|
|
if (p != NULL) { /* end marker found */
|
|
strsize = p - buf;
|
|
memcpy(buf2, buf, strsize);
|
|
buf2[strsize] = '\0';
|
|
bufsize -= strsize + 1;
|
|
memmove(buf, buf + strsize + 1, bufsize);
|
|
} else { /* partial string */
|
|
strsize = bufsize - 1;
|
|
/* Roll back to character boundary in case of partial character */
|
|
while (strsize != 0 && (buf[strsize] & 0xC0) == 0x80)
|
|
--strsize;
|
|
if (strsize == 0) /* avoid infinite loop */
|
|
strsize = (bufsize < 4) ? bufsize : 4;
|
|
memcpy(buf2, buf, strsize);
|
|
buf2[strsize] = '\0';
|
|
bufsize -= strsize;
|
|
memmove(buf, buf + strsize, bufsize);
|
|
}
|
|
codepoints = uni_8to32(buf2);
|
|
for (j = 0; codepoints[j] != 0; ++j) {
|
|
add_unicode_index(font, codepoints[j], i);
|
|
}
|
|
free(codepoints);
|
|
if (p != NULL)
|
|
++i;
|
|
}
|
|
} else {
|
|
/* Fake a Unicode table, assuming that ASCII glyphs are in the
|
|
expected places */
|
|
for (i = 0x20; i <= 0x7E; ++i) {
|
|
add_unicode_index(font, i, i);
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
return font;
|
|
|
|
error:
|
|
if (fp != NULL) fclose(fp);
|
|
free_font(font);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
free_font(struct BitmapFont *font)
|
|
{
|
|
unsigned i, j;
|
|
|
|
if (font == NULL) return;
|
|
|
|
if (font->glyphs != NULL) {
|
|
for (i = 0; i < font->num_glyphs; ++i)
|
|
free(font->glyphs[i]);
|
|
free(font->glyphs);
|
|
}
|
|
|
|
for (i = 0; i < SIZE(font->unicode); ++i) {
|
|
if (font->unicode[i] == NULL) continue;
|
|
for (j = 0; j < 256; ++j)
|
|
free(font->unicode[i][j]);
|
|
free(font->unicode[i]);
|
|
}
|
|
|
|
free(font);
|
|
}
|
|
|
|
const unsigned char *
|
|
get_font_glyph(struct BitmapFont *font, uint32 ch, boolean unicode)
|
|
{
|
|
unsigned index;
|
|
|
|
if (unicode) {
|
|
index = 0;
|
|
if (ch <= 0x10FFFF && (ch & 0xFFFFD800) != 0xD800) {
|
|
unsigned i, j, k;
|
|
|
|
i = (unsigned) (ch >> 16);
|
|
j = (unsigned) ((ch >> 8) & 0xFF);
|
|
k = (unsigned) (ch & 0xFF);
|
|
if (font->unicode[i] != NULL
|
|
&& font->unicode[i][j] != NULL) {
|
|
index = font->unicode[i][j][k];
|
|
}
|
|
}
|
|
} else {
|
|
index = ch;
|
|
}
|
|
|
|
if (index >= font->num_glyphs)
|
|
index = 0;
|
|
|
|
return font->glyphs[index];
|
|
}
|
|
|
|
static void
|
|
add_unicode_index(struct BitmapFont *font, uint32 ch, unsigned index)
|
|
{
|
|
unsigned i, j, k;
|
|
|
|
i = (unsigned) (ch >> 16);
|
|
j = (unsigned) ((ch >> 8) & 0xFF);
|
|
k = (unsigned) (ch & 0xFF);
|
|
|
|
if (font->unicode[i] == NULL) {
|
|
/* Create the second level node */
|
|
font->unicode[i] = (unsigned **) alloc(256 * sizeof(unsigned *));
|
|
memset(font->unicode[i], 0, 256 * sizeof(unsigned *));
|
|
}
|
|
if (font->unicode[i][j] == NULL) {
|
|
/* Create the third level node */
|
|
font->unicode[i][j] = (unsigned *) alloc(256 * sizeof(unsigned));
|
|
memset(font->unicode[i][j], 0, 256 * sizeof(unsigned));
|
|
}
|
|
font->unicode[i][j][k] = index;
|
|
}
|
|
|
|
static uint32
|
|
read_u32(const unsigned char *buf)
|
|
{
|
|
return ((uint32) buf[0] << 0)
|
|
| ((uint32) buf[1] << 8)
|
|
| ((uint32) buf[2] << 16)
|
|
| ((uint32) buf[3] << 24);
|
|
}
|
|
|
|
static uint32 *
|
|
uni_8to32(const char *inp)
|
|
{
|
|
size_t i, j;
|
|
uint32 *out;
|
|
|
|
/* Output string */
|
|
out = (uint32 *) alloc((strlen(inp) + 1) * sizeof(out[0]));
|
|
|
|
i = 0;
|
|
j = 0;
|
|
while (inp[i] != 0) {
|
|
unsigned char byte = inp[i++];
|
|
uint32 ch32;
|
|
uint32 min = 0;
|
|
unsigned count = 0;
|
|
|
|
if (byte < 0x80) {
|
|
ch32 = byte;
|
|
} else if (byte < 0xC0) {
|
|
ch32 = 0xFFFD;
|
|
} else if (byte < 0xE0) {
|
|
ch32 = byte & 0x1F;
|
|
min = 0x80;
|
|
count = 1;
|
|
} else if (byte < 0xF0) {
|
|
ch32 = byte & 0x0F;
|
|
min = 0x800;
|
|
count = 2;
|
|
} else if (byte < 0xF5) {
|
|
ch32 = byte & 0x07;
|
|
min = 0x10000;
|
|
count = 3;
|
|
} else {
|
|
ch32 = 0xFFFD;
|
|
}
|
|
|
|
for (; count != 0; --count) {
|
|
byte = inp[i];
|
|
if ((byte & 0xC0) != 0x80) {
|
|
break;
|
|
}
|
|
++i;
|
|
ch32 = (ch32 << 6) | (byte & 0x3F);
|
|
}
|
|
if (count != 0 || ch32 < min || ((ch32 & 0xFFFFF800) == 0xD800)) {
|
|
ch32 = 0xFFFD;
|
|
}
|
|
out[j++] = ch32;
|
|
}
|
|
|
|
out[j] = 0;
|
|
return out;
|
|
}
|