// // 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 // GPL v2+ // #include "fontutil.h" #include #include FT_FREETYPE_H #include #include #include #include #include #include #include #include // 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 \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; }