Switch to a RIFF font format

Fix height of ラ character

I accidentally made it 1px too tall before

Add Cyrillic to default font

Make Я more like latin R

Right after I commit, looking at my screenshot I notice I forgot to tweak the Я to be more angled like this font's latin R...

Improve the default font's Kana

derp fix

Properly handle invalid UTF-8

Fix conversion PBMs with non-byte aligned rows

Rename font extension to .frf

For Font RiFf

Re-add PBM font support

Default converting to CP-437 and try guess size

Revert "Default converting to CP-437 and try guess size"

Reverts 2c9a47d224b28cbb51a3ee335fd9970265201b72 as I think the old behaviour works better given PBM font support being kept

Re-add mapping file for CP-437

Automatically use mapping file with same name as image

ex. for "font_6x10.pbm" it will use "font_6x10.txt" in the same directory
This commit is contained in:
Pk11 2021-08-01 23:24:16 -05:00 committed by d0k3
parent 13eb4f8869
commit b366200d4b
13 changed files with 489 additions and 88 deletions

View File

@ -39,7 +39,7 @@ GodMode9 is designed to be intuitive, buttons leading to the results you'd expec
## How to build this / developer info
Build `GodMode9.firm` via `make firm`. This requires [firmtool](https://github.com/TuxSH/firmtool), [Python 3.5+](https://www.python.org/downloads/) and [devkitARM](https://sourceforge.net/projects/devkitpro/) installed).
You may run `make release` to get a nice, release-ready package of all required files. To build __SafeMode9__ (a bricksafe variant of GodMode9, with limited write permissions) instead of GodMode9, compile with `make FLAVOR=SafeMode9`. To switch screens, compile with `make SWITCH_SCREENS=1`. For additional customization, you may choose the internal font by replacing `font_default.pbm` inside the `data` directory. You may also hardcode the brightness via `make FIXED_BRIGHTNESS=x`, whereas `x` is a value between 0...15.
You may run `make release` to get a nice, release-ready package of all required files. To build __SafeMode9__ (a bricksafe variant of GodMode9, with limited write permissions) instead of GodMode9, compile with `make FLAVOR=SafeMode9`. To switch screens, compile with `make SWITCH_SCREENS=1`. For additional customization, you may choose the internal font by replacing `font_default.frf` inside the `data` directory. You may also hardcode the brightness via `make FIXED_BRIGHTNESS=x`, whereas `x` is a value between 0...15.
Further customization is possible by hardcoding `aeskeydb.bin` (just put the file into the `data` folder when compiling). All files put into the `data` folder will turn up in the `V:` drive, but keep in mind there's a hard 3MB limit for all files inside, including overhead. A standalone script runner is compiled by providing `autorun.gm9` (again, in the `data` folder) and building with `make SCRIPT_RUNNER=1`. There's more possibility for customization, read the Makefiles to learn more.
@ -88,7 +88,7 @@ GodMode9 provides access to system data via drives, a listing of what each drive
* __`K: AESKEYDB IMAGE`__: An `aeskeydb.bin` image can be mounted and accessed via this drive. The drive shows all keys inside the aeskeydb.bin. This is read-only.
* __`T: TICKET.DB IMAGE / BDRI IMAGE`__: Ticket database files can be mounted and accessed via this drive. This provides easy and quick access to all tickets inside the `ticket.db`. This drive also provides access to other BDRI images, such as the Title database (`title.db`).
* __`M: MEMORY VIRTUAL`__: This provides access to various memory regions. This is protected by a special write permission, and caution is advised when doing modifications inside this drive. This drive also gives access to `boot9.bin`, `boot11.bin` (boot9strap only) and `otp.mem` (sighaxed systems only).
* __`V: VRAM VIRTUAL`__: This drive resides in the first VRAM bank and contains files essential to GodMode9. The font (in PBM format), the splash logo (in PNG format) and the readme file are found there, as well as any file that is provided inside the `data` folder at build time. This is read-only.
* __`V: VRAM VIRTUAL`__: This drive resides in the first VRAM bank and contains files essential to GodMode9. The font (in FRF format), the splash logo (in PNG format) and the readme file are found there, as well as any file that is provided inside the `data` folder at build time. This is read-only.
* __`Y: TITLE MANAGER`__: The title manager is accessed via the HOME menu and provides easy access to all installed titles.
* __`Z: LAST SEARCH`__: After a search operation, search results are found inside this drive. The drive can be accessed at a later point to return to the former search results.

View File

@ -23,11 +23,103 @@
static u32 font_width = 0;
static u32 font_height = 0;
static u32 font_count = 0;
static u32 line_height = 0;
static u8 font_bin[FONT_MAX_HEIGHT * 256];
static u16 question_mark_index = 0;
static u8* font_bin = NULL;
static u16* font_map = NULL;
// lookup table to sort CP-437 so it can be binary searched with Unicode codepoints
static const u8 cp437_sorted[0x100] = {
0x00, 0xF5, 0xF6, 0xFC, 0xFD, 0xFB, 0xFA, 0xA4, 0xF3, 0xF2, 0xF4, 0xF9, 0xF8, 0xFE, 0xFF, 0xF7,
0xEF, 0xF1, 0xAD, 0xA5, 0x6D, 0x65, 0xED, 0xAE, 0xA9, 0xAB, 0xAA, 0xA8, 0xB2, 0xAC, 0xEE, 0xF0,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0xB8,
0x77, 0x95, 0x85, 0x7F, 0x80, 0x7D, 0x81, 0x83, 0x86, 0x87, 0x84, 0x8B, 0x8A, 0x88, 0x74, 0x75,
0x78, 0x82, 0x76, 0x8F, 0x90, 0x8D, 0x94, 0x92, 0x96, 0x7A, 0x7B, 0x62, 0x63, 0x64, 0xA7, 0x97,
0x7E, 0x89, 0x8E, 0x93, 0x8C, 0x79, 0x66, 0x6F, 0x73, 0xB9, 0x68, 0x72, 0x71, 0x61, 0x67, 0x70,
0xE9, 0xEA, 0xEB, 0xBD, 0xC3, 0xD8, 0xD9, 0xCD, 0xCC, 0xDA, 0xC8, 0xCE, 0xD4, 0xD3, 0xD2, 0xBF,
0xC0, 0xC5, 0xC4, 0xC2, 0xBC, 0xC6, 0xD5, 0xD6, 0xD1, 0xCB, 0xE0, 0xDD, 0xD7, 0xC7, 0xE3, 0xDE,
0xDF, 0xDB, 0xDC, 0xD0, 0xCF, 0xC9, 0xCA, 0xE2, 0xE1, 0xC1, 0xBE, 0xE6, 0xE5, 0xE7, 0xE8, 0xE4,
0x9D, 0x7C, 0x98, 0xA0, 0x9A, 0xA1, 0x6C, 0xA2, 0x9B, 0x99, 0x9C, 0x9E, 0xB1, 0xA3, 0x9F, 0xB3,
0xB5, 0x6A, 0xB7, 0xB6, 0xBA, 0xBB, 0x91, 0xB4, 0x69, 0xAF, 0x6E, 0xB0, 0xA6, 0x6B, 0xEC, 0x60
};
// Unicode font mapping for sorted CP-437
static const u16 cp437_sorted_map[0x100] = {
0x0000, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E,
0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E,
0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E,
0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E,
0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E,
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A5, 0x00A7, 0x00AA, 0x00AB, 0x00AC, 0x00B0, 0x00B1, 0x00B2, 0x00B5, 0x00B6, 0x00B7, 0x00BA,
0x00BB, 0x00BC, 0x00BD, 0x00BF, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00DF, 0x00E0, 0x00E1, 0x00E2,
0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F1, 0x00F2, 0x00F3, 0x00F4,
0x00F6, 0x00F7, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FF, 0x0192, 0x0393, 0x0398, 0x03A3, 0x03A6, 0x03A9, 0x03B1, 0x03B4, 0x03B5,
0x03C0, 0x03C3, 0x03C4, 0x03C6, 0x2022, 0x203C, 0x207F, 0x20A7, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x21A8, 0x2219,
0x221A, 0x221E, 0x221F, 0x2229, 0x2248, 0x2261, 0x2264, 0x2265, 0x2302, 0x2310, 0x2320, 0x2321, 0x2500, 0x2502, 0x250C, 0x2510,
0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2550, 0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558,
0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568,
0x2569, 0x256A, 0x256B, 0x256C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x25A0, 0x25AC, 0x25B2, 0x25BA,
0x25BC, 0x25C4, 0x25CB, 0x25D8, 0x25D9, 0x263A, 0x263B, 0x263C, 0x2640, 0x2642, 0x2660, 0x2663, 0x2665, 0x2666, 0x266A, 0x266B
};
#define PIXEL_OFFSET(x, y) (((x) * SCREEN_HEIGHT) + (SCREEN_HEIGHT - (y) - 1))
u16 GetFontIndex(u16 c)
{
int left = 0;
int right = font_count;
while (left <= right) {
int mid = left + ((right - left) / 2);
if (font_map[mid] == c)
return mid;
if (font_map[mid] < c)
left = mid + 1;
else
right = mid - 1;
}
// if not found in font, return a '?'
return question_mark_index;
}
// gets a u32 codepoint from a UTF-8 string and moves the pointer to the next character
u32 GetCharacter(const char** str)
{
u32 c;
if ((**str & 0x80) == 0) {
c = *(*str)++;
} else if ((**str & 0xE0) == 0xC0) {
c = (*(*str)++ & 0x1F) << 6;
c |= *(*str)++ & 0x3F;
} else if ((**str & 0xF0) == 0xE0) {
c = (*(*str)++ & 0x0F) << 12;
c |= (*(*str)++ & 0x3F) << 6;
c |= *(*str)++ & 0x3F;
} else if ((**str & 0xF8) == 0xF0) {
c = (*(*str)++ & 0x07) << 18;
c |= (*(*str)++ & 0x3F) << 12;
c |= (*(*str)++ & 0x3F) << 6;
c |= *(*str)++ & 0x3F;
} else {
// invalid UTF-8, skip to next character
(*str)++;
c = '?';
}
return c;
}
u8* GetFontFromPbm(const void* pbm, const u32 pbm_size, u32* w, u32* h) {
char* hdr = (char*) pbm;
u32 hdr_max_size = min(512, pbm_size);
@ -90,46 +182,130 @@ u8* GetFontFromPbm(const void* pbm, const u32 pbm_size, u32* w, u32* h) {
return (u8*) pbm + p;
}
// sets the font from a given PBM
// if no PBM is given, the PBM is fetched from the default VRAM0 location
bool SetFontFromPbm(const void* pbm, u32 pbm_size) {
u8* GetFontFromRiff(const void* riff, const u32 riff_size, u32* w, u32* h, u16* count) {
u8 *ptr = (u8*) riff;
u8 riff_w = 0;
u8 riff_h = 0;
u16 riff_count = 0;
// check header magic, then skip over
if (memcmp(ptr, "RIFF", 4) != 0) return NULL;
// ensure enough space is allocated
u32 data_size;
memcpy(&data_size, ptr + 4, sizeof(u32));
if (data_size > riff_size) return NULL;
ptr += 8;
// check for and load META section
if (memcmp(ptr, "META", 4) == 0) {
riff_w = ptr[8];
riff_h = ptr[9];
memcpy(&riff_count, ptr + 10, sizeof(u16));
u32 section_size;
memcpy(&section_size, ptr + 4, sizeof(u32));
ptr += 8 + section_size;
if (riff_w > FONT_MAX_WIDTH || riff_h > FONT_MAX_HEIGHT) return NULL;
} else return NULL;
// all good
if (w) *w = riff_w;
if (h) *h = riff_h;
if (count) *count = riff_count;
return ptr;
}
// sets the font from a given RIFF or PBM
// if no font is given, the font is fetched from the default VRAM0 location
bool SetFont(const void* font, u32 font_size) {
u32 w, h;
u16 count;
u8* ptr = NULL;
if (!pbm) {
u64 pbm_size64 = 0;
pbm = FindVTarFileInfo(VRAM0_FONT_PBM, &pbm_size64);
pbm_size = (u32) pbm_size64;
if (!font) {
u64 font_size64 = 0;
font = FindVTarFileInfo(VRAM0_FONT, &font_size64);
font_size = (u32) font_size64;
}
if (pbm)
ptr = GetFontFromPbm(pbm, pbm_size, &w, &h);
if (!ptr) {
if (!font)
return false;
} else if (w > 8) {
font_width = w / 16;
font_height = h / 16;
memset(font_bin, 0x00, w * h / 8);
for (u32 cy = 0; cy < 16; cy++) {
for (u32 row = 0; row < font_height; row++) {
for (u32 cx = 0; cx < 16; cx++) {
u32 bp0 = (cx * font_width) >> 3;
u32 bm0 = (cx * font_width) % 8;
u8 byte = ((ptr[bp0] << bm0) | (ptr[bp0+1] >> (8 - bm0))) & (0xFF << (8 - font_width));
font_bin[(((cy << 4) + cx) * font_height) + row] = byte;
}
ptr += font_width << 1;
}
}
} else {
if ((ptr = GetFontFromRiff(font, font_size, &w, &h, &count))) { // RIFF font
font_width = w;
font_height = h / 256;
memcpy(font_bin, ptr, h);
font_height = h;
font_count = count;
// character data
if (memcmp(ptr, "CDAT", 4) == 0) {
u32 section_size;
memcpy(&section_size, ptr + 4, sizeof(u32));
if (font_bin) free(font_bin);
font_bin = malloc(font_height * font_count);
if (!font_bin) return NULL;
memcpy(font_bin, ptr + 8, font_height * font_count);
ptr += 8 + section_size;
} else return NULL;
// character map
if (memcmp(ptr, "CMAP", 4) == 0) {
u32 section_size;
memcpy(&section_size, ptr + 4, sizeof(u32));
if (font_map) free(font_map);
font_map = malloc(sizeof(u16) * font_count);
if (!font_map) return NULL;
memcpy(font_map, ptr + 8, sizeof(u16) * font_count);
ptr += 8 + section_size;
} else return NULL;
} else if ((ptr = GetFontFromPbm(font, font_size, &w, &h))) {
font_count = 0x100;
if (w > 8) {
font_width = w / 16;
font_height = h / 16;
if (font_bin) free(font_bin);
font_bin = malloc(font_height * font_count);
if (!font_bin) return NULL;
for (u32 cy = 0; cy < 16; cy++) {
for (u32 row = 0; row < font_height; row++) {
for (u32 cx = 0; cx < 16; cx++) {
u32 bp0 = (cx * font_width) >> 3;
u32 bm0 = (cx * font_width) % 8;
u8 byte = ((ptr[bp0] << bm0) | (ptr[bp0+1] >> (8 - bm0))) & (0xFF << (8 - font_width));
font_bin[(cp437_sorted[(cy << 4) + cx] * font_height) + row] = byte;
}
ptr += font_width << 1;
}
}
} else {
font_width = w;
font_height = h / 256;
for (u32 i = 0; i < font_count; i++)
memcpy(font_bin + cp437_sorted[i] * font_height, ptr + i * font_height, font_height);
}
if (font_map) free(font_map);
font_map = malloc(sizeof(u16) * font_count);
if (!font_map) return NULL;
memcpy(font_map, cp437_sorted_map, sizeof(cp437_sorted_map));
} else {
return false;
}
line_height = min(10, font_height + 2);
question_mark_index = GetFontIndex('?');
return true;
}
@ -222,14 +398,14 @@ void DrawQrCode(u16 *screen, const u8* qrcode)
}
}
void DrawCharacter(u16 *screen, int character, int x, int y, u32 color, u32 bgcolor)
void DrawCharacter(u16 *screen, u32 character, int x, int y, u32 color, u32 bgcolor)
{
for (int yy = 0; yy < (int) font_height; yy++) {
int xDisplacement = x * SCREEN_HEIGHT;
int yDisplacement = SCREEN_HEIGHT - (y + yy) - 1;
u16* screenPos = screen + xDisplacement + yDisplacement;
u8 charPos = font_bin[character * font_height + yy];
u8 charPos = font_bin[GetFontIndex(character) * font_height + yy];
for (int xx = 7; xx >= (8 - (int) font_width); xx--) {
if ((charPos >> xx) & 1) {
*screenPos = color;
@ -241,14 +417,12 @@ void DrawCharacter(u16 *screen, int character, int x, int y, u32 color, u32 bgco
}
}
void DrawString(u16 *screen, const char *str, int x, int y, u32 color, u32 bgcolor, bool fix_utf8)
void DrawString(u16 *screen, const char *str, int x, int y, u32 color, u32 bgcolor)
{
size_t max_len = (((screen == TOP_SCREEN) ? SCREEN_WIDTH_TOP : SCREEN_WIDTH_BOT) - x) / font_width;
size_t len = (strlen(str) > max_len) ? max_len : strlen(str);
for (size_t i = 0; i < len; i++) {
char c = (char) (fix_utf8 && str[i] >= 0x80) ? '?' : str[i];
DrawCharacter(screen, c, x + i * font_width, y, color, bgcolor);
for (size_t i = 0; i < max_len && *str; i++) {
DrawCharacter(screen, GetCharacter(&str), x + i * font_width, y, color, bgcolor);
}
}
@ -261,7 +435,7 @@ void DrawStringF(u16 *screen, int x, int y, u32 color, u32 bgcolor, const char *
va_end(va);
for (char* text = strtok(str, "\n"); text != NULL; text = strtok(NULL, "\n"), y += line_height)
DrawString(screen, text, x, y, color, bgcolor, true);
DrawString(screen, text, x, y, color, bgcolor);
}
void DrawStringCenter(u16 *screen, u32 color, u32 bgcolor, const char *format, ...)
@ -292,11 +466,21 @@ u32 GetDrawStringWidth(const char* str) {
char* old_lf = (char*) str;
char* str_end = (char*) str + strnlen(str, STRBUF_SIZE);
for (char* lf = strchr(str, '\n'); lf != NULL; lf = strchr(lf + 1, '\n')) {
if ((u32) (lf - old_lf) > width) width = lf - old_lf;
u32 length = 0;
for (char* c = old_lf; c != lf; c++) {
if ((*c & 0xC0) != 0x80) length++;
}
if (length > width) width = length;
old_lf = lf;
}
if ((u32) (str_end - old_lf) > width)
width = str_end - old_lf;
u32 length = 0;
for (char* c = old_lf; c != str_end; c++) {
if ((*c & 0xC0) != 0x80) length++;
}
if (length > width) width = length;
width *= font_width;
return width;
}
@ -350,26 +534,60 @@ void WordWrapString(char* str, int llen) {
}
}
void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right) {
int osize = strnlen(orig, 256);
if (nsize < osize) {
TruncateString(dest, orig, nsize, tpos);
// dest must be at least 4x the size of nlength to account for UTF-8
void ResizeString(char* dest, const char* orig, int nlength, int tpos, bool align_right) {
int olength = 0;
for (int i = 0; i < 256 && orig[i]; i++) {
if ((orig[i] & 0xC0) != 0x80) olength++;
}
if (nlength < olength) {
TruncateString(dest, orig, nlength, tpos);
} else if (!align_right) {
int nsize = 0;
for (int i = 0; i < nlength || (orig[nsize] & 0xC0) == 0x80; nsize++) {
if ((orig[nsize] & 0xC0) != 0x80) i++;
}
snprintf(dest, nsize + 1, "%-*.*s", nsize, nsize, orig);
} else {
int nsize = 0;
for (int i = 0; i < nlength || (orig[nsize] & 0xC0) == 0x80; nsize++) {
if ((orig[nsize] & 0xC0) != 0x80) i++;
}
snprintf(dest, nsize + 1, "%*.*s", nsize, nsize, orig);
}
}
void TruncateString(char* dest, const char* orig, int nsize, int tpos) {
int osize = strnlen(orig, 256);
if (nsize < 0) {
// dest must be at least 4x the size of nlength to account for UTF-8
void TruncateString(char* dest, const char* orig, int nlength, int tpos) {
int osize = strnlen(orig, 256), olength = 0;
for (int i = 0; i < 256 && orig[i]; i++) {
if ((orig[i] & 0xC0) != 0x80) olength++;
}
if (nlength < 0) {
return;
} else if ((nsize <= 3) || (nsize >= osize)) {
snprintf(dest, nsize + 1, "%s", orig);
} else if ((nlength <= 3) || (nlength >= olength)) {
strcpy(dest, orig);
} else {
if (tpos + 3 > nsize) tpos = nsize - 3;
snprintf(dest, nsize + 1, "%-.*s...%-.*s", tpos, orig, nsize - (3 + tpos), orig + osize - (nsize - (3 + tpos)));
int nsize = 0;
for (int i = 0; i < nlength || (orig[nsize] & 0xC0) == 0x80; nsize++) {
if ((orig[nsize] & 0xC0) != 0x80) i++;
}
if (tpos + 3 > nlength) tpos = nlength - 3;
int tposStart = 0;
for (int i = 0; i < tpos || (orig[tposStart] & 0xC0) == 0x80; tposStart++) {
if ((orig[tposStart] & 0xC0) != 0x80) i++;
}
int tposEnd = 0;
for (int i = 0; i < nlength - tpos - 3; tposEnd++) {
if ((orig[osize - 1 - tposEnd] & 0xC0) != 0x80) i++;
}
snprintf(dest, nsize + 1, "%-.*s...%-.*s", tposStart, orig, tposEnd, orig + osize - tposEnd);
}
}
@ -532,10 +750,10 @@ bool ShowUnlockSequence(u32 seqlvl, const char *format, ...) {
DrawStringF(MAIN_SCREEN, x, y + str_height - 28, color_font, color_bg, "To proceed, enter this:");
// generate sequence
const char dpad_symbols[] = { '\x1A', '\x1B', '\x18', '\x19' }; // R L U D
const char *dpad_symbols[] = { "", "", "", "" }; // R L U D
u32 sequence[seqlen_max];
char seqsymbols[seqlen_max];
const char *seqsymbols[seqlen_max];
u32 lastlsh = (u32) -1;
for (u32 n = 0; n < (seqlen-1); n++) {
u32 lsh = lastlsh;
@ -545,13 +763,13 @@ bool ShowUnlockSequence(u32 seqlvl, const char *format, ...) {
seqsymbols[n] = dpad_symbols[lsh];
}
sequence[seqlen-1] = BUTTON_A;
seqsymbols[seqlen-1] = 'A';
seqsymbols[seqlen-1] = "A";
while (true) {
for (u32 n = 0; n < seqlen; n++) {
DrawStringF(MAIN_SCREEN, x + (n*4*FONT_WIDTH_EXT), y + str_height - 28 + line_height,
(lvl > n) ? color_on : color_off, color_bg, "<%c>", seqsymbols[n]);
(lvl > n) ? color_on : color_off, color_bg, "<%s>", seqsymbols[n]);
}
if (lvl == seqlen)
break;
@ -669,7 +887,7 @@ u32 ShowFileScrollPrompt(u32 n, const DirEntry** options, bool hide_ext, const c
char bytestr[16];
FormatBytes(bytestr, options[i]->size);
char content_str[64 + 1];
char content_str[64 * 4 + 1];
char temp_str[256];
strncpy(temp_str, options[i]->name, 256);
@ -792,7 +1010,7 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha
ClearScreenF(true, false, COLOR_STD_BG);
DrawStringF(MAIN_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%s", str);
DrawStringF(MAIN_SCREEN, x + 8, y + str_height - 40, COLOR_STD_FONT, COLOR_STD_BG,
"R - (\x18\x19) fast scroll\nL - clear data%s", resize ? "\nX - remove char\nY - insert char" : "");
"R - (↑↓) fast scroll\nL - clear data%s", resize ? "\nX - remove char\nY - insert char" : "");
// wait for all keys released
while (HID_ReadState() & BUTTON_ANY);
@ -1068,8 +1286,8 @@ bool ShowProgress(u64 current, u64 total, const char* opstr)
const u32 text_pos_y = bar_pos_y + bar_height + 2;
u32 prog_width = ((total > 0) && (current <= total)) ? (current * (bar_width-4)) / total : 0;
u32 prog_percent = ((total > 0) && (current <= total)) ? (current * 100) / total : 0;
char tempstr[64];
char progstr[64];
char tempstr[256];
char progstr[256];
static u64 last_msec_elapsed = 0;
static u64 last_sec_remain = 0;
@ -1095,14 +1313,14 @@ bool ShowProgress(u64 current, u64 total, const char* opstr)
TruncateString(progstr, opstr, min(63, (bar_width / FONT_WIDTH_EXT) - 7), 8);
snprintf(tempstr, 64, "%s (%lu%%)", progstr, prog_percent);
ResizeString(progstr, tempstr, bar_width / FONT_WIDTH_EXT, 8, false);
DrawString(MAIN_SCREEN, progstr, bar_pos_x, text_pos_y, COLOR_STD_FONT, COLOR_STD_BG, true);
DrawString(MAIN_SCREEN, progstr, bar_pos_x, text_pos_y, COLOR_STD_FONT, COLOR_STD_BG);
if (sec_elapsed >= 1) {
snprintf(tempstr, 16, "ETA %02llum%02llus", sec_remain / 60, sec_remain % 60);
ResizeString(progstr, tempstr, 16, 8, true);
DrawString(MAIN_SCREEN, progstr, bar_pos_x + bar_width - 1 - (FONT_WIDTH_EXT * 16),
bar_pos_y - line_height - 1, COLOR_STD_FONT, COLOR_STD_BG, true);
bar_pos_y - line_height - 1, COLOR_STD_FONT, COLOR_STD_BG);
}
DrawString(MAIN_SCREEN, "(hold B to cancel)", bar_pos_x + 2, text_pos_y + 14, COLOR_STD_FONT, COLOR_STD_BG, false);
DrawString(MAIN_SCREEN, "(hold B to cancel)", bar_pos_x + 2, text_pos_y + 14, COLOR_STD_FONT, COLOR_STD_BG);
last_prog_width = prog_width;
@ -1116,8 +1334,8 @@ int ShowBrightnessConfig(int set_brightness)
int bar_x_pos, bar_y_pos, bar_width, bar_height;
const char *brightness_str =
"[\x1B] Decrease brightness\n"
"[\x1A] Increase brightness\n"
"[] Decrease brightness\n"
"[] Increase brightness\n"
" \n"
"[X] Use volume slider control\n"
"[A] Set current brightness\n"

View File

@ -45,8 +45,9 @@ bool ShowUnlockSequence(u32 seqlvl, const char *format, ...);
#define ShowUnlockSequence ShowPrompt
#endif
u8* GetFontFromPbm(const void* pbm, const u32 pbm_size, u32* w, u32* h);
bool SetFontFromPbm(const void* pbm, const u32 pbm_size);
u8* GetFontFromPbm(const void* pbm, const u32 riff_size, u32* w, u32* h);
u8* GetFontFromRiff(const void* riff, const u32 riff_size, u32* w, u32* h, u16* count);
bool SetFont(const void* font, const u32 font_size);
u16 GetColor(const u16 *screen, int x, int y);

View File

@ -127,6 +127,8 @@ u64 IdentifyFileType(const char* path) {
}
} else if (GetFontFromPbm(data, fsize, NULL, NULL)) {
return FONT_PBM;
} else if (GetFontFromRiff(data, fsize, NULL, NULL, NULL)) {
return FONT_RIFF;
} else if ((fsize > sizeof(AgbHeader)) &&
(ValidateAgbHeader((AgbHeader*) data) == 0)) {
return GAME_GBA;

View File

@ -35,8 +35,9 @@
#define TXT_GENERIC (1ULL<<30)
#define GFX_PNG (1ULL<<31)
#define FONT_PBM (1ULL<<32)
#define NOIMG_NAND (1ULL<<33)
#define HDR_NAND (1ULL<<34)
#define FONT_RIFF (1ULL<<33)
#define NOIMG_NAND (1ULL<<34)
#define HDR_NAND (1ULL<<35)
#define TYPE_BASE 0xFFFFFFFFFFULL // 40 bit reserved for base types
// #define FLAG_FIRM (1ULL<<58) // <--- for CXIs containing FIRMs
@ -73,9 +74,9 @@
#define FTYPE_KEYINIT(tp) (tp&(BIN_KEYDB))
#define FTYPE_KEYINSTALL(tp) (tp&(BIN_KEYDB))
#define FTYPE_SCRIPT(tp) (tp&(TXT_SCRIPT))
#define FTYPE_FONT(tp) (tp&(FONT_PBM))
#define FTYPE_FONT(tp) (tp&(FONT_PBM|FONT_RIFF))
#define FTYPE_GFX(tp) (tp&(GFX_PNG))
#define FTYPE_SETABLE(tp) (tp&(FONT_PBM))
#define FTYPE_SETABLE(tp) (tp&(FONT_PBM|FONT_RIFF))
#define FTYPE_BOOTABLE(tp) (tp&(SYS_FIRM))
#define FTYPE_INSTALLABLE(tp) (tp&(SYS_FIRM))
#define FTYPE_AGBSAVE(tp) (tp&(SYS_AGBSAVE))

View File

@ -1216,7 +1216,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
(filetype & BIN_LEGKEY) ? "Build " KEYDB_NAME :
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." :
(filetype & TXT_SCRIPT) ? "Execute GM9 script" :
(filetype & FONT_RIFF) ? "Font options..." :
(FTYPE_FONT(filetype)) ? "Font options..." :
(filetype & GFX_PNG) ? "View PNG file" :
(filetype & HDR_NAND) ? "Rebuild NCSD header" :
(filetype & NOIMG_NAND) ? "Rebuild NCSD header" : "???";
@ -2056,12 +2056,12 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
return 0;
}
else if (user_select == font) { // set font
u8* riff = (u8*) malloc(0x10000); // arbitrary, should be enough by far
if (!riff) return 1;
u32 riff_size = FileGetData(file_path, riff, 0x10000, 0);
if (riff_size) SetFontFromRiff(riff, riff_size);
u8* font = (u8*) malloc(0x10000); // arbitrary, should be enough by far
if (!font) return 1;
u32 font_size = FileGetData(file_path, font, 0x10000, 0);
if (font_size) SetFont(font, font_size);
ClearScreenF(true, true, COLOR_STD_BG);
free(riff);
free(font);
return 0;
}
else if (user_select == view) { // view gfx
@ -2087,7 +2087,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
}
else if (user_select == setup) { // set as default (font)
if (filetype & FONT_RIFF) {
if (SetAsSupportFile("font.riff", file_path))
if (SetAsSupportFile("font.frf", file_path))
ShowPrompt(false, "%s\nFont will be active on next boot", pathstr);
} else if (filetype & FONT_PBM) {
if (SetAsSupportFile("font.pbm", file_path))
ShowPrompt(false, "%s\nFont will be active on next boot", pathstr);
}
return 0;
@ -2300,7 +2303,7 @@ u32 GodMode(int entrypoint) {
#endif
// init font
if (!SetFontFromRiff(NULL, 0)) return exit_mode;
if (!SetFont(NULL, 0)) return exit_mode;
// show splash screen (if enabled)
ClearScreenF(true, true, COLOR_STD_BG);
@ -2320,13 +2323,20 @@ u32 GodMode(int entrypoint) {
SetScreenBrightness(brightness);
// custom font handling
if (CheckSupportFile("font.riff")) {
if (CheckSupportFile("font.frf")) {
u8* riff = (u8*) malloc(0x10000); // arbitrary, should be enough by far
if (riff) {
u32 riff_size = LoadSupportFile("font.riff", riff, 0x10000);
if (riff_size) SetFontFromRiff(riff, riff_size);
u32 riff_size = LoadSupportFile("font.frf", riff, 0x10000);
if (riff_size) SetFont(riff, riff_size);
free(riff);
}
} else if (CheckSupportFile("font.pbm")) {
u8* pbm = (u8*) malloc(0x10000); // arbitrary, should be enough by far
if (pbm) {
u32 pbm_size = LoadSupportFile("font.pbm", pbm, 0x10000);
if (pbm_size) SetFont(pbm, pbm_size);
free(pbm);
}
}
// check for embedded essential backup
@ -2877,7 +2887,7 @@ u32 GodMode(int entrypoint) {
#else
u32 ScriptRunner(int entrypoint) {
// init font and show splash
if (!SetFontFromPbm(NULL, 0)) return GODMODE_EXIT_POWEROFF;
if (!SetFont(NULL, 0)) return GODMODE_EXIT_POWEROFF;
SplashInit("scriptrunner mode");
u64 timer = timer_start();

View File

@ -6,19 +6,19 @@
// set default font
#ifndef DEFAULT_FONT
#define DEFAULT_FONT "font_default.pbm"
#define DEFAULT_FONT "font_default.frf"
#endif
// known file names inside VRAM0 TAR
#define VRAM0_AUTORUN_GM9 "autorun.gm9"
#define VRAM0_FONT_PBM DEFAULT_FONT
#define VRAM0_FONT DEFAULT_FONT
#define VRAM0_SCRIPTS "scripts"
#define VRAM0_README_MD "README_internal.md"
#define VRAM0_SPLASH_PNG FLAVOR "_splash.png"
#define VRAM0_EASTER_BIN "easter.bin"
#define VRAM0_OFFSET 0x080C0000
#define VRAM0_OFFSET 0x080C0000
#define VRAM0_LIMIT 0x00040000
#define TARDATA ((void*) VRAM0_OFFSET)

BIN
data/font_default.frf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
0x0000 0x263A 0x263B 0x2665 0x2666 0x2663 0x2660 0x2022 0x25D8 0x25CB 0x25D9 0x2642 0x2640 0x266A 0x266B 0x263C
0x25BA 0x25C4 0x2195 0x203C 0x00B6 0x00A7 0x25AC 0x21A8 0x2191 0x2193 0x2192 0x2190 0x221F 0x2194 0x25B2 0x25BC
0x0020 0x0021 0x0022 0x0023 0x0024 0x0025 0x0026 0x0027 0x0028 0x0029 0x002A 0x002B 0x002C 0x002D 0x002E 0x002F
0x0030 0x0031 0x0032 0x0033 0x0034 0x0035 0x0036 0x0037 0x0038 0x0039 0x003A 0x003B 0x003C 0x003D 0x003E 0x003F
0x0040 0x0041 0x0042 0x0043 0x0044 0x0045 0x0046 0x0047 0x0048 0x0049 0x004A 0x004B 0x004C 0x004D 0x004E 0x004F
0x0050 0x0051 0x0052 0x0053 0x0054 0x0055 0x0056 0x0057 0x0058 0x0059 0x005A 0x005B 0x005C 0x005D 0x005E 0x005F
0x0060 0x0061 0x0062 0x0063 0x0064 0x0065 0x0066 0x0067 0x0068 0x0069 0x006A 0x006B 0x006C 0x006D 0x006E 0x006F
0x0070 0x0071 0x0072 0x0073 0x0074 0x0075 0x0076 0x0077 0x0078 0x0079 0x007A 0x007B 0x007C 0x007D 0x007E 0x2302
0x00C7 0x00FC 0x00E9 0x00E2 0x00E4 0x00E0 0x00E5 0x00E7 0x00EA 0x00EB 0x00E8 0x00EF 0x00EE 0x00EC 0x00C4 0x00C5
0x00C9 0x00E6 0x00C6 0x00F4 0x00F6 0x00F2 0x00FB 0x00F9 0x00FF 0x00D6 0x00DC 0x00A2 0x00A3 0x00A5 0x20A7 0x0192
0x00E1 0x00ED 0x00F3 0x00FA 0x00F1 0x00D1 0x00AA 0x00BA 0x00BF 0x2310 0x00AC 0x00BD 0x00BC 0x00A1 0x00AB 0x00BB
0x2591 0x2592 0x2593 0x2502 0x2524 0x2561 0x2562 0x2556 0x2555 0x2563 0x2551 0x2557 0x255D 0x255C 0x255B 0x2510
0x2514 0x2534 0x252C 0x251C 0x2500 0x253C 0x255E 0x255F 0x255A 0x2554 0x2569 0x2566 0x2560 0x2550 0x256C 0x2567
0x2568 0x2564 0x2565 0x2559 0x2558 0x2552 0x2553 0x256B 0x256A 0x2518 0x250C 0x2588 0x2584 0x258C 0x2590 0x2580
0x03B1 0x00DF 0x0393 0x03C0 0x03A3 0x03C3 0x00B5 0x03C4 0x03A6 0x0398 0x03A9 0x03B4 0x221E 0x03C6 0x03B5 0x2229
0x2261 0x00B1 0x2265 0x2264 0x2320 0x2321 0x00F7 0x2248 0x00B0 0x2219 0x00B7 0x221A 0x207F 0x00B2 0x25A0 0x00A0

Binary file not shown.

View File

@ -0,0 +1,35 @@
0x0020 0x0021 0x0022 0x0023 0x0024 0x0025 0x0026 0x0027 0x0028 0x0029 0x002A 0x002B 0x002C 0x002D 0x002E 0x002F
0x0030 0x0031 0x0032 0x0033 0x0034 0x0035 0x0036 0x0037 0x0038 0x0039 0x003A 0x003B 0x003C 0x003D 0x003E 0x003F
0x0040 0x0041 0x0042 0x0043 0x0044 0x0045 0x0046 0x0047 0x0048 0x0049 0x004A 0x004B 0x004C 0x004D 0x004E 0x004F
0x0050 0x0051 0x0052 0x0053 0x0054 0x0055 0x0056 0x0057 0x0058 0x0059 0x005A 0x005B 0x005C 0x005D 0x005E 0x005F
0x0060 0x0061 0x0062 0x0063 0x0064 0x0065 0x0066 0x0067 0x0068 0x0069 0x006A 0x006B 0x006C 0x006D 0x006E 0x006F
0x0070 0x0071 0x0072 0x0073 0x0074 0x0075 0x0076 0x0077 0x0078 0x0079 0x007A 0x007B 0x007C 0x007D 0x007E 0x00A0
0x00A1 0x00A2 0x00A3 0x00A4 0x00A5 0x00A6 0x00A7 0x00A8 0x00A9 0x00AA 0x00AB 0x00AC 0x00AE 0x00B0 0x00B1 0x00B2
0x00B3 0x00B4 0x00B5 0x00B6 0x00B7 0x00BA 0x00BB 0x00BC 0x00BD 0x00BF 0x00C0 0x00C1 0x00C2 0x00C3 0x00C4 0x00C5
0x00C6 0x00C7 0x00C8 0x00C9 0x00CA 0x00CB 0x00CC 0x00CD 0x00CE 0x00CF 0x00D0 0x00D1 0x00D2 0x00D3 0x00D4 0x00D5
0x00D6 0x00D7 0x00D8 0x00D9 0x00DA 0x00DB 0x00DC 0x00DD 0x00DE 0x00DF 0x00E0 0x00E1 0x00E2 0x00E3 0x00E4 0x00E5
0x00E6 0x00E7 0x00E8 0x00E9 0x00EA 0x00EB 0x00EC 0x00ED 0x00EE 0x00EF 0x00F0 0x00F1 0x00F2 0x00F3 0x00F4 0x00F5
0x00F6 0x00F7 0x00F8 0x00F9 0x00FA 0x00FB 0x00FC 0x00FD 0x00FE 0x00FF 0x0192 0x0393 0x0398 0x03A3 0x03A6 0x03A9
0x03B1 0x03B4 0x03B5 0x03C0 0x03C3 0x03C4 0x03C6 0x0401 0x0404 0x0406 0x0407 0x0410 0x0411 0x0412 0x0413 0x0414
0x0415 0x0416 0x0417 0x0418 0x0419 0x041A 0x041B 0x041C 0x041D 0x041E 0x041F 0x0420 0x0421 0x0422 0x0423 0x0424
0x0425 0x0426 0x0427 0x0428 0x0429 0x042A 0x042B 0x042C 0x042D 0x042E 0x042F 0x0430 0x0431 0x0432 0x0433 0x0434
0x0435 0x0436 0x0437 0x0438 0x0439 0x043A 0x043B 0x043C 0x043D 0x043E 0x043F 0x0440 0x0441 0x0442 0x0443 0x0444
0x0445 0x0446 0x0447 0x0448 0x0449 0x044A 0x044B 0x044C 0x044D 0x044E 0x044F 0x0451 0x0454 0x0456 0x0457 0x2022
0x203C 0x207F 0x20A7 0x20AC 0x2122 0x2190 0x2191 0x2192 0x2193 0x2194 0x2195 0x21A8 0x2219 0x221A 0x221E 0x221F
0x2229 0x2248 0x2261 0x2264 0x2265 0x2302 0x2310 0x2320 0x2321 0x2500 0x2502 0x250C 0x2510 0x2514 0x2518 0x251C
0x2524 0x252C 0x2534 0x253C 0x2550 0x2551 0x2552 0x2553 0x2554 0x2555 0x2556 0x2557 0x2558 0x2559 0x255A 0x255B
0x255C 0x255D 0x255E 0x255F 0x2560 0x2561 0x2562 0x2563 0x2564 0x2565 0x2566 0x2567 0x2568 0x2569 0x256A 0x256B
0x256C 0x2580 0x2584 0x2588 0x258C 0x2590 0x2591 0x2592 0x2593 0x25A0 0x25A1 0x25AC 0x25B2 0x25B3 0x25BA 0x25BC
0x25BD 0x25C4 0x25C6 0x25C7 0x25CB 0x25CE 0x25CF 0x25D8 0x25D9 0x2605 0x263A 0x263B 0x263C 0x2640 0x2642 0x2660
0x2663 0x2665 0x2666 0x266A 0x266B 0x3000 0x3041 0x3042 0x3043 0x3044 0x3045 0x3046 0x3047 0x3048 0x3049 0x304A
0x304B 0x304C 0x304D 0x304E 0x304F 0x3050 0x3051 0x3052 0x3053 0x3054 0x3055 0x3056 0x3057 0x3058 0x3059 0x305A
0x305B 0x305C 0x305D 0x305E 0x305F 0x3060 0x3061 0x3062 0x3063 0x3064 0x3065 0x3066 0x3067 0x3068 0x3069 0x306A
0x306B 0x306C 0x306D 0x306E 0x306F 0x3070 0x3071 0x3072 0x3073 0x3074 0x3075 0x3076 0x3077 0x3078 0x3079 0x307A
0x307B 0x307C 0x307D 0x307E 0x307F 0x3080 0x3081 0x3082 0x3083 0x3084 0x3085 0x3086 0x3087 0x3088 0x3089 0x308A
0x308B 0x308C 0x308D 0x308E 0x308F 0x3090 0x3091 0x3092 0x3093 0x3094 0x3095 0x3096 0x309B 0x309C 0x309D 0x309E
0x309F 0x30A1 0x30A2 0x30A3 0x30A4 0x30A5 0x30A6 0x30A7 0x30A8 0x30A9 0x30AA 0x30AB 0x30AC 0x30AD 0x30AE 0x30AF
0x30B0 0x30B1 0x30B2 0x30B3 0x30B4 0x30B5 0x30B6 0x30B7 0x30B8 0x30B9 0x30BA 0x30BB 0x30BC 0x30BD 0x30BE 0x30BF
0x30C0 0x30C1 0x30C2 0x30C3 0x30C4 0x30C5 0x30C6 0x30C7 0x30C8 0x30C9 0x30CA 0x30CB 0x30CC 0x30CD 0x30CE 0x30CF
0x30D0 0x30D1 0x30D2 0x30D3 0x30D4 0x30D5 0x30D6 0x30D7 0x30D8 0x30D9 0x30DA 0x30DB 0x30DC 0x30DD 0x30DE 0x30DF
0x30E0 0x30E1 0x30E2 0x30E3 0x30E4 0x30E5 0x30E6 0x30E7 0x30E8 0x30E9 0x30EA 0x30EB 0x30EC 0x30ED 0x30EE 0x30EF
0x30F0 0x30F1 0x30F2 0x30F3 0x30F4 0x30F5 0x30F6 0x30F7 0x30F8 0x30F9 0x30FA 0x30FB 0x30FC 0x30FD 0x30FE 0x30FF

118
utils/fontriff.py Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/env python3
from argparse import ArgumentParser
import struct
from os import path
parser = ArgumentParser(description="Creates an FRF font for GodMode9 from a PBM image")
parser.add_argument("input", type=str, help="PBM image to convert from")
parser.add_argument("output", type=str, help="out.to output to")
parser.add_argument("width", type=int, help="character width")
parser.add_argument("height", type=int, help="character height")
parser.add_argument("-m", "--map", metavar="map.txt", type=str, help="character map (whitespace separated Unicode codepoints)")
args = parser.parse_args()
if(args.width > 8):
print("Fatal: Font width too large (Maximum is 8)")
exit(1)
if(args.height > 10):
print("Fatal: Font height too large (Maximum is 10)")
exit(1)
with open(args.input, "rb") as f:
# read PBM
pbm = f.read()
split = pbm.split(b"\n")
if split[0] != b"P4":
print("Fatal: Input is not a PBM file")
exit(1)
# skip comments
for i in range(1, len(split)):
if split[i][0] != ord("#"):
imgWidth = int(split[i].split()[0])
imgHeight = int(split[i].split()[1])
imgData = b"\n".join(split[i + 1:])
break
count = imgWidth * imgHeight // args.width // args.height
columns = imgWidth // args.width
fontMap = []
# prepare map
mapPath = args.map
if not mapPath and path.exists(args.input[:args.input.rfind(".")] + ".txt"):
mapPath = args.input[:args.input.rfind(".")] + ".txt"
print("Info: Using %s for font mappings" % mapPath)
if mapPath:
with open(mapPath, "r") as fontMapFile:
fontMapTemp = fontMapFile.read().split()
if (len(fontMapTemp) > count):
print("Fatal: Font map has more items than possible in image (%d items in map)" % count)
exit(1)
elif len(fontMapTemp) < count:
count = len(fontMapTemp)
print("Info: Font map has fewer items than possible in image, only using first %d" % count)
for item in fontMapTemp:
fontMap.append({"mapping": int(item, 16)})
else:
print("Warning: Font mapping not found, mapping directly to Unicode codepoints")
for i in range(count):
fontMap.append({"mapping": i})
# add unsorted tiles to map
for c in range(count):
fontMap[c]["bitmap"] = []
for row in range(args.height):
ofs = ((c // columns * args.height + row) * (imgWidth + ((8 - (imgWidth % 8)) if imgWidth % 8 != 0 else 0)) // 8)
bp0 = ((c % columns) * args.width) >> 3
bm0 = ((c % columns) * args.width) % 8
byte = (((imgData[ofs + bp0] << bm0) | ((imgData[ofs + bp0 + 1] >> (8 - bm0)) if ofs + bp0 + 1 < len(imgData) else 0)) & (0xFF << (8 - args.width))) & 0xFF
fontMap[c]["bitmap"].append(byte)
# remove duplicates
fontMap = list({x["mapping"]: x for x in fontMap}.values())
if len(fontMap) != count:
print("Info: %d duplicate mappings were removed" % (count - len(fontMap)))
count = len(fontMap)
# sort map
fontMap = sorted(fontMap, key=lambda x: x["mapping"])
# write file
with open(args.output, "wb") as out:
out.write(b"RIFF")
out.write(struct.pack("<L", 0)) # Filled at end
# metadata
out.write(b"META")
out.write(struct.pack("<LBBH", 4, args.width, args.height, count))
# character data
out.write(b"CDAT")
sectionSize = count * args.height
padding = 4 - sectionSize % 4 if sectionSize % 4 else 0
out.write(struct.pack("<L", sectionSize + padding))
for item in fontMap:
for px in item["bitmap"]:
out.write(struct.pack("B", px))
out.write(b"\0" * padding)
# character map
out.write(b"CMAP")
sectionSize = count * 2
padding = 4 - sectionSize % 4 if sectionSize % 4 else 0
out.write(struct.pack("<L", sectionSize + padding))
for item in fontMap:
out.write(struct.pack("<H", item["mapping"]))
out.write(b"\0" * padding)
# write final size
outSize = out.tell()
out.seek(4)
out.write(struct.pack("<L", outSize - 8))
print("Info: %s created with %d tiles." % (args.output, count))