diff --git a/README.md b/README.md index bdcfba280..a54835dcb 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/arm9/source/common/ui.c b/arm9/source/common/ui.c index 57c6fd434..33b5a1d8a 100644 --- a/arm9/source/common/ui.c +++ b/arm9/source/common/ui.c @@ -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(§ion_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; + + if ((ptr = GetFontFromRiff(font, font_size, &w, &h, &count))) { // RIFF font + font_width = w; + font_height = h; + font_count = count; + + // character data + if (memcmp(ptr, "CDAT", 4) == 0) { + u32 section_size; + memcpy(§ion_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(§ion_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; } - 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 { - font_width = w; - font_height = h / 256; - memcpy(font_bin, ptr, h); + 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" diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index be61beb25..00c625c0d 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -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); diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index aa4f4834b..19a81e221 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -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; diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index 31b53a604..d60e3303d 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -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)) diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index de652c2f9..994b846ee 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -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(); diff --git a/arm9/source/system/vram0.h b/arm9/source/system/vram0.h index 490e11a58..62811a710 100644 --- a/arm9/source/system/vram0.h +++ b/arm9/source/system/vram0.h @@ -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) diff --git a/data/font_default.frf b/data/font_default.frf new file mode 100644 index 000000000..65f86e6d9 Binary files /dev/null and b/data/font_default.frf differ diff --git a/data/font_default.pbm b/data/font_default.pbm deleted file mode 100644 index 36c1c93db..000000000 Binary files a/data/font_default.pbm and /dev/null differ diff --git a/resources/fonts/cp_437.txt b/resources/fonts/cp_437.txt new file mode 100644 index 000000000..032280a85 --- /dev/null +++ b/resources/fonts/cp_437.txt @@ -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 diff --git a/resources/fonts/font_6x10.pbm b/resources/fonts/font_6x10.pbm index 36c1c93db..aaee90617 100644 Binary files a/resources/fonts/font_6x10.pbm and b/resources/fonts/font_6x10.pbm differ diff --git a/resources/fonts/font_6x10.txt b/resources/fonts/font_6x10.txt new file mode 100644 index 000000000..4ad1b8996 --- /dev/null +++ b/resources/fonts/font_6x10.txt @@ -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 diff --git a/utils/fontriff.py b/utils/fontriff.py new file mode 100755 index 000000000..12ad52a2f --- /dev/null +++ b/utils/fontriff.py @@ -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("