481 lines
11 KiB
C++
481 lines
11 KiB
C++
//
|
|
// Build the dreamcast BIOS font table using the specified fonts with the help of FreeType2.
|
|
// Fonts should be listed in increasing priority order since glyphs are replaced by later fonts if found.
|
|
//
|
|
// TODO: extended half-width katakana chars (rows 5 and 6) aren't found
|
|
//
|
|
// biosfont 12x24rk.pcf jiskan24.pcf neep-iso8859-1-12x24.pcf
|
|
//
|
|
// 12x24rk.pcf: Copyright 1989 by Sony Corp.
|
|
// Attrib license
|
|
// jiskan24.pcf: Licensed under Public Domain
|
|
// neep-iso8859-1-12x24.pcf: Copyright Jim Knoble <jmknoble@pobox.com>
|
|
// GPL v2+
|
|
//
|
|
#include "fontutil.h"
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include <freetype/ftbdf.h>
|
|
|
|
#include <locale.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <wchar.h>
|
|
#include <stdint.h>
|
|
#include <vector>
|
|
#include <cassert>
|
|
|
|
// BIOS table
|
|
// 288 12x24 characters (unicode encoding)
|
|
const unsigned charcodes12[] = {
|
|
// from, to
|
|
// overbar
|
|
0xaf, 0xaf,
|
|
// ASCII 33-126
|
|
'!', '~',
|
|
// Yen
|
|
0xa5, 0xa5,
|
|
// ISO-8859-1 (chars 160-255)
|
|
0xa0, 0xff,
|
|
// JIS X 0201 (chars 160-255)
|
|
' ', ' ',
|
|
0xff61, 0xff9f, // regular JIS X 0201 range
|
|
0x30f0, 0x30f1, // Wi We
|
|
0x30ee, 0x30ee, // small Wa
|
|
0x30ab, 0x30ab, // Ka
|
|
0x30b1, 0x30b1, // Ke
|
|
0x30f4, 0x30f4, // Vu
|
|
0x30ac, 0x30ac, // Ga
|
|
0x30ae, 0x30ae, // Gi
|
|
0x30b0, 0x30b0, // Gu
|
|
0x30b2, 0x30b2, // Ge
|
|
0x30b4, 0x30b4, // Go
|
|
0x30b6, 0x30b6, // Za
|
|
0x30b8, 0x30b8, // Zi
|
|
0x30ba, 0x30ba, // Zu
|
|
0x30bc, 0x30bc, // Ze
|
|
0x30be, 0x30be, // Zo
|
|
0x30c0, 0x30c0, // Da
|
|
0x30c2, 0x30c2, // Di
|
|
0x30c5, 0x30c5, // Du
|
|
0x30c7, 0x30c7, // De
|
|
0x30c9, 0x30c9, // Do
|
|
0x30d0, 0x30d1, // Ba Pa
|
|
0x30d3, 0x30d4, // Bi Pi
|
|
0x30d6, 0x30d7, // Bu Pu
|
|
0x30d9, 0x30da, // Be Pe
|
|
0x30dc, 0x30dd, // Bo Po
|
|
' ', ' ',
|
|
};
|
|
|
|
// 7078 24x24 characters (JIS X0208 encoding)
|
|
const unsigned charcodes24[] = {
|
|
// prefix, from, to
|
|
// JIS X0208 row 33, Symbols
|
|
0x21, 0x21, 0x7e,
|
|
// JIS X0208 row 34, Symbols
|
|
0x22, 0x21, 0x7e,
|
|
// JIS X0208 row 35, Roman alphabet
|
|
0x23, 0x21, 0x7e,
|
|
// JIS X0208 row 36, Hiragana
|
|
0x24, 0x21, 0x7e,
|
|
// JIS X0208 row 37, Katakana
|
|
0x25, 0x21, 0x7e,
|
|
// JIS X0208 row 38, Greek
|
|
0x26, 0x21, 0x7e,
|
|
// JIS X0208 row 39, Cyrillic
|
|
0x27, 0x21, 0x7e,
|
|
// JIS X0208 row 48
|
|
0x30, 0x21, 0x7e,
|
|
// JIS X0208 row 49
|
|
0x31, 0x21, 0x7e,
|
|
// JIS X0208 row 50
|
|
0x32, 0x21, 0x7e,
|
|
// JIS X0208 row 51
|
|
0x33, 0x21, 0x7e,
|
|
// JIS X0208 row 52
|
|
0x34, 0x21, 0x7e,
|
|
// JIS X0208 row 53
|
|
0x35, 0x21, 0x7e,
|
|
// JIS X0208 row 54
|
|
0x36, 0x21, 0x7e,
|
|
// JIS X0208 row 55
|
|
0x37, 0x21, 0x7e,
|
|
// JIS X0208 row 56
|
|
0x38, 0x21, 0x7e,
|
|
// JIS X0208 row 57
|
|
0x39, 0x21, 0x7e,
|
|
// JIS X0208 row 58
|
|
0x3a, 0x21, 0x7e,
|
|
// JIS X0208 row 59
|
|
0x3b, 0x21, 0x7e,
|
|
// JIS X0208 row 60
|
|
0x3c, 0x21, 0x7e,
|
|
// JIS X0208 row 61
|
|
0x3d, 0x21, 0x7e,
|
|
// JIS X0208 row 62
|
|
0x3e, 0x21, 0x7e,
|
|
// JIS X0208 row 63
|
|
0x3f, 0x21, 0x7e,
|
|
// JIS X0208 row 64
|
|
0x40, 0x21, 0x7e,
|
|
// JIS X0208 row 65
|
|
0x41, 0x21, 0x7e,
|
|
// JIS X0208 row 66
|
|
0x42, 0x21, 0x7e,
|
|
// JIS X0208 row 67
|
|
0x43, 0x21, 0x7e,
|
|
// JIS X0208 row 68
|
|
0x44, 0x21, 0x7e,
|
|
// JIS X0208 row 69
|
|
0x45, 0x21, 0x7e,
|
|
// JIS X0208 row 70
|
|
0x46, 0x21, 0x7e,
|
|
// JIS X0208 row 71
|
|
0x47, 0x21, 0x7e,
|
|
// JIS X0208 row 72
|
|
0x48, 0x21, 0x7e,
|
|
// JIS X0208 row 73
|
|
0x49, 0x21, 0x7e,
|
|
// JIS X0208 row 74
|
|
0x4a, 0x21, 0x7e,
|
|
// JIS X0208 row 75
|
|
0x4b, 0x21, 0x7e,
|
|
// JIS X0208 row 76
|
|
0x4c, 0x21, 0x7e,
|
|
// JIS X0208 row 77
|
|
0x4d, 0x21, 0x7e,
|
|
// JIS X0208 row 78
|
|
0x4e, 0x21, 0x7e,
|
|
// JIS X0208 row 79
|
|
0x4f, 0x21, 0x7e,
|
|
|
|
// JIS X0208 row 80
|
|
0x50, 0x21, 0x7e,
|
|
// JIS X0208 row 81
|
|
0x51, 0x21, 0x7e,
|
|
// JIS X0208 row 82
|
|
0x52, 0x21, 0x7e,
|
|
// JIS X0208 row 83
|
|
0x53, 0x21, 0x7e,
|
|
// JIS X0208 row 84
|
|
0x54, 0x21, 0x7e,
|
|
// JIS X0208 row 85
|
|
0x55, 0x21, 0x7e,
|
|
// JIS X0208 row 86
|
|
0x56, 0x21, 0x7e,
|
|
// JIS X0208 row 87
|
|
0x57, 0x21, 0x7e,
|
|
// JIS X0208 row 88
|
|
0x58, 0x21, 0x7e,
|
|
// JIS X0208 row 89
|
|
0x59, 0x21, 0x7e,
|
|
// JIS X0208 row 90
|
|
0x5a, 0x21, 0x7e,
|
|
// JIS X0208 row 91
|
|
0x5b, 0x21, 0x7e,
|
|
// JIS X0208 row 92
|
|
0x5c, 0x21, 0x7e,
|
|
// JIS X0208 row 93
|
|
0x5d, 0x21, 0x7e,
|
|
// JIS X0208 row 94
|
|
0x5e, 0x21, 0x7e,
|
|
// JIS X0208 row 95
|
|
0x5f, 0x21, 0x7e,
|
|
// JIS X0208 row 96
|
|
0x60, 0x21, 0x7e,
|
|
// JIS X0208 row 97
|
|
0x61, 0x21, 0x7e,
|
|
// JIS X0208 row 98
|
|
0x62, 0x21, 0x7e,
|
|
// JIS X0208 row 99
|
|
0x63, 0x21, 0x7e,
|
|
// JIS X0208 row 100
|
|
0x64, 0x21, 0x7e,
|
|
// JIS X0208 row 101
|
|
0x65, 0x21, 0x7e,
|
|
// JIS X0208 row 102
|
|
0x66, 0x21, 0x7e,
|
|
// JIS X0208 row 103
|
|
0x67, 0x21, 0x7e,
|
|
// JIS X0208 row 104
|
|
0x68, 0x21, 0x7e,
|
|
// JIS X0208 row 105
|
|
0x69, 0x21, 0x7e,
|
|
// JIS X0208 row 106
|
|
0x6a, 0x21, 0x7e,
|
|
// JIS X0208 row 107
|
|
0x6b, 0x21, 0x7e,
|
|
// JIS X0208 row 108
|
|
0x6c, 0x21, 0x7e,
|
|
// JIS X0208 row 109
|
|
0x6d, 0x21, 0x7e,
|
|
// JIS X0208 row 110
|
|
0x6e, 0x21, 0x7e,
|
|
// JIS X0208 row 111
|
|
0x6f, 0x21, 0x7e,
|
|
// JIS X0208 row 112
|
|
0x70, 0x21, 0x7e,
|
|
// JIS X0208 row 113
|
|
0x71, 0x21, 0x7e,
|
|
// JIS X0208 row 114
|
|
0x72, 0x21, 0x7e,
|
|
// JIS X0208 row 115
|
|
0x73, 0x21, 0x7e,
|
|
// JIS X0208 row 116
|
|
0x74, 0x21, 0x26,
|
|
// TODO Dreamcast symbols (22 chars)
|
|
// copyright U+24B8
|
|
// register U+24C7
|
|
// trademark U+2122
|
|
// up arrow U+2B06
|
|
// down U+2B07
|
|
// left U+2B05
|
|
// right U+27A1 ???
|
|
// up+right U+2B08
|
|
// down+right U+2B0A
|
|
// down+left U+2B0B
|
|
// up+left U+2B09
|
|
// circled A U+24B6
|
|
// circled B U+24B7
|
|
// circled C U+24B8
|
|
// circled D U+24B8
|
|
// circled X U+24CD
|
|
// circled Y U+24CE
|
|
// circled Z U+24CF
|
|
// squared L U+1F13B (Supplementary Multilingual Plane)
|
|
// squared R U+1F141 (Supplementary Multilingual Plane)
|
|
// start button U+1F142 (Squared S, SMP)
|
|
// VMU U+1F4DF (pager) U+1F4F1 (mobile phone)
|
|
};
|
|
|
|
static uint8_t biosfont[288 * 36 + 7078 * 72];
|
|
|
|
static FT_Library library;
|
|
static FT_Face face;
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
setlocale(LC_ALL, "");
|
|
loadjisx208Table();
|
|
|
|
if (argc < 2)
|
|
{
|
|
fprintf(stderr, "Usage: %s <font>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
int error = FT_Init_FreeType(&library);
|
|
if (error)
|
|
{
|
|
fprintf(stderr, "FreeType init failed\n");
|
|
return 1;
|
|
}
|
|
for (int font = 1; font < argc; font++)
|
|
{
|
|
const char *fontname = argv[font];
|
|
long index = 0;
|
|
if (strlen(fontname) >= 4 && !strcmp(fontname + strlen(fontname) - 4, ".ttc"))
|
|
// 5: NotoSans Mono CJK.ttc
|
|
index = strtoul(argv[++font], nullptr, 10);
|
|
error = FT_New_Face(library, fontname, index, &face);
|
|
if (error)
|
|
{
|
|
fprintf(stderr, "Can't load %s\n", fontname);
|
|
return 1;
|
|
}
|
|
|
|
const char *registry;
|
|
const char *encoding;
|
|
FT_Get_BDF_Charset_ID(face, &encoding, ®istry);
|
|
printf("%s: %s %s\n%ld glyphs\n%d fixed sizes\n%d charmaps\n", fontname, encoding, registry, face->num_glyphs, face->num_fixed_sizes, face->num_charmaps);
|
|
FT_Bitmap_Size *size = face->available_sizes;
|
|
for (int i = 0; i < face->num_fixed_sizes; i++, size++)
|
|
printf("%d: %d x %d\n", i + 1, size->width, size->height);
|
|
if (face->num_fixed_sizes == 0)
|
|
FT_Set_Pixel_Sizes(face, 0, 24);
|
|
|
|
bool jisx0201Encoding = false;
|
|
bool jisx0208Encoding = false;
|
|
if (registry != nullptr)
|
|
{
|
|
if (!strcmp(registry, "JISX0208.1983"))
|
|
{
|
|
jisx0208Encoding = true;
|
|
FT_Set_Charmap(face, face->charmaps[0]);
|
|
}
|
|
else if (!strcmp(registry, "JISX0201.1976"))
|
|
{
|
|
jisx0201Encoding = true;
|
|
FT_Set_Charmap(face, face->charmaps[0]);
|
|
}
|
|
}
|
|
/* list chars
|
|
unsigned gindex;
|
|
long charcode = FT_Get_First_Char(face, &gindex);
|
|
while (gindex != 0)
|
|
{
|
|
printf("code %lx index %d\n", charcode, gindex);
|
|
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
|
}
|
|
*/
|
|
|
|
unsigned offset = 0;
|
|
if (jisx0208Encoding)
|
|
{
|
|
offset = 288 * 36;
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < std::size(charcodes12); i += 2)
|
|
{
|
|
int from = charcodes12[i];
|
|
int to = charcodes12[i + 1];
|
|
for (int j = from; j <= to; j++)
|
|
{
|
|
int code = j;
|
|
if (jisx0201Encoding)
|
|
{
|
|
if (code >= 0xff61 && code <= 0xff9f)
|
|
code = code - 0xff61 + 0xa1;
|
|
else if (code >= 0x80) {
|
|
offset += 36;
|
|
continue;
|
|
}
|
|
}
|
|
if (!loadGlyph(face, code, 12, 24)) {
|
|
offset += 36;
|
|
continue;
|
|
}
|
|
uint8_t *p = face->glyph->bitmap.buffer;
|
|
for (int r = 0; r < 24; r++)
|
|
{
|
|
if (r & 1)
|
|
{
|
|
biosfont[offset + 1] |= p[0] >> 4;
|
|
biosfont[offset + 2] = ((p[0] & 0xf) << 4) | (p[1] >> 4);
|
|
offset += 3;
|
|
}
|
|
else
|
|
{
|
|
biosfont[offset] = p[0];
|
|
biosfont[offset + 1] = p[1];
|
|
}
|
|
p += face->glyph->bitmap.pitch;
|
|
}
|
|
}
|
|
}
|
|
printf("Final offset(12) %d 288 * 36 = %d\n", offset, 288 * 36);
|
|
}
|
|
|
|
if (jisx0201Encoding)
|
|
{
|
|
offset += 7078 * 72;
|
|
}
|
|
else
|
|
{
|
|
for (size_t range = 0; range < std::size(charcodes24); range += 3)
|
|
{
|
|
int prefix = charcodes24[range];
|
|
int from = charcodes24[range + 1];
|
|
int to = charcodes24[range + 2];
|
|
for (int j = from; j <= to; j++)
|
|
{
|
|
uint16_t jis = (prefix << 8) | j;
|
|
wchar_t u;
|
|
if (jisx0208Encoding) {
|
|
u = jis;
|
|
}
|
|
else
|
|
{
|
|
u = jisx208[jis];
|
|
if (u == 0) {
|
|
printf("JISX208 conversion failed: [%02x %02x]\n", prefix, j);
|
|
offset += 72;
|
|
continue;
|
|
}
|
|
}
|
|
if (!loadGlyph(face, u, 24, 24)) {
|
|
offset += 72;
|
|
continue;
|
|
}
|
|
const FT_GlyphSlot glyph = face->glyph;
|
|
uint8_t *p = glyph->bitmap.buffer;
|
|
// left=0 top=22 is ok
|
|
// other values need to shift the bitmap
|
|
if (glyph->bitmap.pitch == 3 && glyph->bitmap_left == 0 && glyph->bitmap_top == 22) {
|
|
memcpy(&biosfont[offset], p, 72);
|
|
offset += 72;
|
|
}
|
|
else
|
|
{
|
|
const int topFill = std::max(0, 21 - glyph->bitmap_top);
|
|
memset(&biosfont[offset], 0, topFill * 3);
|
|
offset += topFill * 3;
|
|
const int rows = std::min((int)glyph->bitmap.rows, 24 - topFill);
|
|
const int width = glyph->bitmap.width;
|
|
for (int r = 0; r < rows; r++)
|
|
{
|
|
if (width == 24)
|
|
{
|
|
memcpy(&biosfont[offset], p, 3);
|
|
}
|
|
else
|
|
{
|
|
int left = glyph->bitmap_left;
|
|
unsigned o = offset;
|
|
while (left >= 8 && o - offset < 3)
|
|
{
|
|
left -= 8;
|
|
biosfont[o++] = 0;
|
|
}
|
|
if (o - offset < 3)
|
|
{
|
|
biosfont[o++] = p[0] >> left;
|
|
if (left > 0 && o - offset < 3)
|
|
biosfont[o] = p[0] << (8 - left);
|
|
if (width > 8 && o - offset < 3)
|
|
{
|
|
biosfont[o++] |= p[1] >> left;
|
|
if (o - offset < 3)
|
|
{
|
|
if (left > 0)
|
|
biosfont[o] = p[1] << (8 - left);
|
|
if (width > 16)
|
|
biosfont[o++] |= p[2] >> left;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
o++;
|
|
if (o - offset < 3)
|
|
biosfont[o] = 0;
|
|
}
|
|
}
|
|
}
|
|
offset += 3;
|
|
p += glyph->bitmap.pitch;
|
|
}
|
|
const int bottomFill = 24 - (topFill + rows);
|
|
assert(bottomFill >= 0);
|
|
memset(&biosfont[offset], 0, bottomFill * 3);
|
|
offset += bottomFill * 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
printf("Final offset(24) %d 288 * 36 + 7078 * 72 = %d\n", offset, 288 * 36 + 7078 * 72);
|
|
FT_Done_Face(face);
|
|
}
|
|
FT_Done_FreeType(library);
|
|
|
|
FILE *f = fopen("biosfont.bin", "wb");
|
|
if (f == nullptr) {
|
|
perror("biosfont.bin");
|
|
return 1;
|
|
}
|
|
fwrite(biosfont, 1, sizeof(biosfont), f);
|
|
fclose(f);
|
|
|
|
return 0;
|
|
}
|