diff --git a/sys/msdos/font.c b/sys/msdos/font.c new file mode 100755 index 000000000..d7fd72046 --- /dev/null +++ b/sys/msdos/font.c @@ -0,0 +1,276 @@ +/* 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 flags; + 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; + flags = 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 (flags & 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; +} diff --git a/sys/msdos/font.h b/sys/msdos/font.h new file mode 100755 index 000000000..d5274fa6f --- /dev/null +++ b/sys/msdos/font.h @@ -0,0 +1,53 @@ +/* Maintain a data structure describing a monospaced bitmap font */ + +#ifndef FONT_H +#define FONT_H + +#include "integer.h" + +/* + * The file format is Linux PSF, version 2. Version 1 is not supported. + * Actual Linux fonts are restricted to 256 or 512 glyphs; for NetHack, the + * font can have any number of glyphs. The Unicode map is expected to be + * present, but combining sequences are not supported. + * The fonts supplied for use with this data structure have the first 256 + * glyphs arranged according to IBM code page 437, for simpler support of + * the IBM handling mode for the map. + */ + +/* For Unicode lookup, a three level tree provides constant-time access to + the glyphs without using an excessive amount of memory. + The root has seventeen entries, one for each plane of Unicode. Most fonts + will populate only plane 0, and Unicode defines only planes 0, 1, 2, 3, 15 + and 16. + The second level has 256 entries, each pointing to a third level node with + 256 entries. Each third level entry has type unsigned, and gives the index + of the glyph. + Given the Unicode code point, we can use bits 20 through 16 to index the + root, bits 15 through 8 for the second level and bits 7 through 0 for the + third level. */ + +struct BitmapFont { + /* Dimensions of a single glyph */ + unsigned width; + unsigned height; + + /* The glyphs, in the order that they appear in the font */ + /* IBM handling will index the glyphs this way */ + /* glyph points to an allocated array, each element of which points to + another allocated array */ + unsigned num_glyphs; + unsigned char **glyphs; + + /* The root node of the Unicode tree */ + unsigned **unicode[17]; +}; + +extern struct BitmapFont *load_font(const char *filename); +extern void free_font(struct BitmapFont *font); +extern const unsigned char *get_font_glyph( + struct BitmapFont *font, + uint32 ch, + boolean unicode); + +#endif diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 10877b7a6..5fce6e866 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -16,13 +16,14 @@ #include "vesa.h" #include "wintty.h" #include "tileset.h" +#include "font.h" #define FIRST_TEXT_COLOR 240 extern int total_tiles_used, Tile_corr, Tile_unexplored; /* from tile.c */ struct VesaCharacter { int colour; - int chr; + uint32 chr; }; static unsigned long vesa_SetWindow(int window, unsigned long offset); @@ -47,13 +48,13 @@ static boolean vesa_SetHardPalette(const struct Pixel *); static boolean vesa_SetSoftPalette(const struct Pixel *); static void vesa_DisplayCell(int, int, int); static unsigned vesa_FindMode(unsigned long mode_addr, unsigned bits); -static void vesa_WriteChar(int, int, int, int); -static void vesa_WriteCharXY(int, int, int, int); +static void vesa_WriteChar(uint32, int, int, int); +static void vesa_WriteCharXY(uint32, int, int, int); static void vesa_WriteCharTransparent(int, int, int, int); static void vesa_WriteTextRow(int pixx, int pixy, struct VesaCharacter const *t_row, unsigned t_row_width); static boolean vesa_GetCharPixel(int, unsigned, unsigned); -static unsigned char vesa_GetCharPixelRow(int, unsigned, unsigned); +static unsigned char vesa_GetCharPixelRow(uint32, unsigned, unsigned); static unsigned long vesa_DoublePixels(unsigned long); static unsigned long vesa_TriplePixels(unsigned long); static void vesa_WriteStr(const char *, int, int, int, int); @@ -81,7 +82,7 @@ static unsigned char __far *font; static struct map_struct { int glyph; - int ch; + uint32 ch; int attr; unsigned special; short int tileidx; @@ -151,6 +152,7 @@ static unsigned vesa_char_width = 8, vesa_char_height = 16; static unsigned vesa_oview_width, vesa_oview_height; static unsigned char **vesa_tiles; static unsigned char **vesa_oview_tiles; +static struct BitmapFont *vesa_font; #ifdef SIMULATE_CURSOR static unsigned long *undercursor; @@ -670,12 +672,17 @@ vesa_xputc(char ch, int attr) void vesa_xputg(const glyph_info *glyphinfo) { - int glyphnum = glyphinfo->glyph, ch = glyphinfo->ttychar; + int glyphnum = glyphinfo->glyph; + uint32 ch = (uchar) glyphinfo->ttychar; unsigned special = glyphinfo->gm.glyphflags; int col, row; int attr; int ry; + if (SYMHANDLING(H_UTF8) && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) { + ch = glyphinfo->gm.u->utf32ch; + } + row = currow; col = curcol; if ((col < 0 || col >= COLNO) @@ -689,7 +696,7 @@ vesa_xputg(const glyph_info *glyphinfo) attr = (g_attribute == 0) ? attrib_gr_normal : g_attribute; map[ry][col].attr = attr; if (iflags.traditional_view) { - vesa_WriteChar((unsigned char) ch, col, row, attr); + vesa_WriteChar(ch, col, row, attr); } else { if ((col >= clipx) && (col <= clipxmax) && (ry >= clipy) && (ry <= clipymax)) { @@ -1011,6 +1018,7 @@ vesa_Init(void) unsigned i; unsigned num_pixels, num_oview_pixels; const char *tile_file; + const char *font_name; int tilefailure = 0; if (inited) return; @@ -1090,22 +1098,48 @@ vesa_Init(void) vesa_oview_height = (unsigned) iflags.wc_tile_height; } - /* Use the map font size to set the font size */ - /* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */ - vesa_char_height = iflags.wc_fontsiz_map; - if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) { - vesa_char_height = vesa_y_res / 30; - } - if (vesa_char_height < 32) { - vesa_char_height = 16; - } else if (vesa_char_height < 48) { - vesa_char_height = 32; - } else if (vesa_char_height < 64) { - vesa_char_height = 48; + /* Load a font of size appropriate to the screen size */ + if (vesa_x_res >= 1280 && vesa_y_res >= 960) + font_name = "ter-u32b.psf"; + else if (vesa_x_res >= 1120 && vesa_y_res >= 840) + font_name = "ter-u28b.psf"; + else if (vesa_x_res >= 960 && vesa_y_res >= 720) + font_name = "ter-u24b.psf"; + else if (vesa_x_res >= 880 && vesa_y_res >= 660) + font_name = "ter-u22b.psf"; + else if (vesa_x_res >= 800 && vesa_y_res >= 600) + font_name = "ter-u20b.psf"; + else if (vesa_x_res >= 720 && vesa_y_res >= 540) + font_name = "ter-u18b.psf"; + else + font_name = "ter-u16v.psf"; + if (iflags.wc_font_map != NULL && iflags.wc_font_map[0] != '\0') + font_name = iflags.wc_font_map; + free_font(vesa_font); + vesa_font = load_font(font_name); + /* if load_font fails, vesa_font is NULL and we'll fall back to the font + defined in ROM */ + if (vesa_font != NULL) { + vesa_char_width = vesa_font->width; + vesa_char_height = vesa_font->height; } else { - vesa_char_height = 64; + /* Use the map font size to set the font size */ + /* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */ + vesa_char_height = iflags.wc_fontsiz_map; + if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) { + vesa_char_height = vesa_y_res / 30; + } + if (vesa_char_height < 32) { + vesa_char_height = 16; + } else if (vesa_char_height < 48) { + vesa_char_height = 32; + } else if (vesa_char_height < 64) { + vesa_char_height = 48; + } else { + vesa_char_height = 64; + } + vesa_char_width = vesa_char_height / 2; } - vesa_char_width = vesa_char_height / 2; /* Process tiles for the current video mode */ vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); @@ -1508,7 +1542,7 @@ vesa_FindMode(unsigned long mode_addr, unsigned bits) * */ static void -vesa_WriteChar(int chr, int col, int row, int colour) +vesa_WriteChar(uint32 chr, int col, int row, int colour) { int pixx, pixy; @@ -1527,7 +1561,7 @@ vesa_WriteChar(int chr, int col, int row, int colour) * transparency */ static void -vesa_WriteCharXY(int chr, int pixx, int pixy, int colour) +vesa_WriteCharXY(uint32 chr, int pixx, int pixy, int colour) { /* Flush if cache is full or if not contiguous to the last character */ if (chr_cache_size >= SIZE(chr_cache)) { @@ -1604,7 +1638,7 @@ vesa_WriteTextRow(int pixx, int pixy, struct VesaCharacter const *t_row, /* Second loop: draw one raster line of one character */ x = 0; for (i = 0; i < t_row_width; ++i) { - int chr = t_row[i].chr; + uint32 chr = t_row[i].chr; int colour = t_row[i].colour + FIRST_TEXT_COLOR; /* Preprocess the foreground color */ if (vesa_pixel_bytes == 1) { @@ -1655,8 +1689,9 @@ vesa_GetCharPixel(int ch, unsigned x, unsigned y) } static unsigned char -vesa_GetCharPixelRow(int ch, unsigned x, unsigned y) +vesa_GetCharPixelRow(uint32 ch, unsigned x, unsigned y) { + unsigned fnt_width; unsigned x1; unsigned char fnt; size_t offset; @@ -1664,30 +1699,38 @@ vesa_GetCharPixelRow(int ch, unsigned x, unsigned y) if (x >= vesa_char_width) return 0; if (y >= vesa_char_height) return 0; + fnt_width = (vesa_char_width + 7) / 8; x1 = x / 8; - const unsigned char __far *fp; + if (vesa_font != NULL) { + const unsigned char *fp; - if (ch < 0 || 255 < ch) return FALSE; - offset = ch * 16 + (y * 16 / vesa_char_height); - fp = font; - fnt = READ_ABSOLUTE((fp + offset)); + offset = y * fnt_width + x1; + fp = get_font_glyph(vesa_font, ch, SYMHANDLING(H_UTF8)); + fnt = fp[offset]; + } else { + const unsigned char __far *fp; - if (vesa_char_width != 8) { - unsigned long fnt2 = fnt; - unsigned width = vesa_char_width; - if (width % 3 == 0) { - fnt2 = vesa_TriplePixels(fnt2); - width /= 3; + if (255 < ch) return 0; + offset = (ch * vesa_char_height + y) * fnt_width + x1; + fp = font; + fnt = READ_ABSOLUTE((fp + offset)); + + if (vesa_char_width != 8) { + unsigned long fnt2 = fnt; + unsigned width = vesa_char_width; + if (width % 3 == 0) { + fnt2 = vesa_TriplePixels(fnt2); + width /= 3; + } + while (width > 8) { + fnt2 = vesa_DoublePixels(fnt2); + width /= 2; + } + fnt2 <<= 32 - vesa_char_width; + fnt = (unsigned char)(fnt2 >> (24 - 8 * x1)); } - while (width > 8) { - fnt2 = vesa_DoublePixels(fnt2); - width /= 2; - } - fnt2 <<= 32 - vesa_char_width; - fnt = (unsigned char)(fnt2 >> (24 - 8 * x1)); } - return fnt; } diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 4a056813f..c530bc070 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -6,6 +6,7 @@ ifdef CROSS_TO_MSDOS # $(TARGETPFX)msdos.o : ../sys/msdos/msdos.c $(HACK_H) +$(TARGETPFX)font.o : ../sys/msdos/font.c ../sys/msdos/font.h $(HACK_H) $(TARGETPFX)pckeys.o : ../sys/msdos/pckeys.c $(HACK_H) $(TARGETPFX)pctiles.o : ../sys/msdos/pctiles.c ../sys/msdos/portio.h $(HACK_H) $(TARGETPFX)video.o : ../sys/msdos/video.c ../sys/msdos/portio.h $(HACK_H) @@ -14,7 +15,7 @@ $(TARGETPFX)vidtxt.o : ../sys/msdos/vidtxt.c ../sys/msdos/portio.h \ $(TARGETPFX)vidvga.o : ../sys/msdos/vidvga.c ../sys/msdos/portio.h \ ../win/share/tile.h ../include/tileset.h $(HACK_H) $(TARGETPFX)vidvesa.o : ../sys/msdos/vidvesa.c ../sys/msdos/portio.h \ - ../win/share/tile.h ../include/tileset.h $(HACK_H) + ../win/share/tile.h ../include/tileset.h ../sys/msdos/font.h $(HACK_H) $(TARGETPFX)vidstub.o : ../sys/msdos/vidvesa.c ../sys/msdos/portio.h \ $(HACK_H) $(TARGETPFX)tile.o : tile.c diff --git a/sys/unix/hints/include/cross-pre.370 b/sys/unix/hints/include/cross-pre.370 index f3d11aa93..5acfdef11 100644 --- a/sys/unix/hints/include/cross-pre.370 +++ b/sys/unix/hints/include/cross-pre.370 @@ -185,6 +185,7 @@ override SYSSRC = ../sys/share/pcmain.c ../sys/msdos/msdos.c \ ../sys/share/pcunix.c ../sys/msdos/video.c \ ../sys/msdos/vidtxt.c ../sys/msdos/pckeys.c \ ../sys/msdos/vidvga.c ../sys/msdos/vidvesa.c \ + ../sys/msdos/font.c \ ../win/share/bmptiles.c ../win/share/giftiles.c \ ../win/share/tileset.c override SYSOBJ= $(TARGETPFX)pcmain.o $(TARGETPFX)msdos.o \ @@ -192,6 +193,7 @@ override SYSOBJ= $(TARGETPFX)pcmain.o $(TARGETPFX)msdos.o \ $(TARGETPFX)pcunix.o $(TARGETPFX)video.o \ $(TARGETPFX)vidtxt.o $(TARGETPFX)pckeys.o \ $(TARGETPFX)vidvga.o $(TARGETPFX)vidvesa.o \ + $(TARGETPFX)font.o \ $(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.o \ $(TARGETPFX)tileset.o $(TARGETPFX)tile.o override WINLIB=