From b366200d4b80dc52f795244351f2bd362bdabc32 Mon Sep 17 00:00:00 2001 From: Pk11 Date: Sun, 1 Aug 2021 23:24:16 -0500 Subject: [PATCH] Switch to a RIFF font format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- README.md | 4 +- arm9/source/common/ui.c | 348 +++++++++++++++++++++++++++------ arm9/source/common/ui.h | 5 +- arm9/source/filesys/filetype.c | 2 + arm9/source/filesys/filetype.h | 9 +- arm9/source/godmode.c | 34 ++-- arm9/source/system/vram0.h | 6 +- data/font_default.frf | Bin 0 -> 6756 bytes data/font_default.pbm | Bin 2006 -> 0 bytes resources/fonts/cp_437.txt | 16 ++ resources/fonts/font_6x10.pbm | Bin 2006 -> 4256 bytes resources/fonts/font_6x10.txt | 35 ++++ utils/fontriff.py | 118 +++++++++++ 13 files changed, 489 insertions(+), 88 deletions(-) create mode 100644 data/font_default.frf delete mode 100644 data/font_default.pbm create mode 100644 resources/fonts/cp_437.txt create mode 100644 resources/fonts/font_6x10.txt create mode 100755 utils/fontriff.py diff --git a/README.md b/README.md index bdcfba2..a54835d 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 57c6fd4..33b5a1d 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; - } - 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(§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; + } + } + } 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" diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index be61beb..00c625c 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 aa4f483..19a81e2 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 31b53a6..d60e330 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 de652c2..994b846 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 490e11a..62811a7 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 0000000000000000000000000000000000000000..65f86e6d99f6086e9797b36898e935038d3c048a GIT binary patch literal 6756 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 literal 0 HcmV?d00001 diff --git a/data/font_default.pbm b/data/font_default.pbm deleted file mode 100644 index 36c1c93dbf8d1335e4b6d18f3638f5116455a6c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2006 zcma)-T}%{L6vxlZPIVa-?%f@`HZk2nAzfnA#foIM(s=K3r$D1*M|LZwF`z=LiC+b5 zFo9&STS5t)3TkZ9q*l^CHhrr}A1vxt6QXO0`qmW4V(lXcgeKic&n$~t8yin1dw=&o zXa0BY$C-^)s2lYihmM};)GIqTm+K!KJKCX#^@AN9$J^fuhd(^jaqz_cHx3*-67D$G z_VK>ME$!jMZg#k%t+h3LWM6wnYa1&2!|sFaYNS%%Tv@I{F0w@<*h&v<{LL2KHV&lZ z;3%1d?9E@qu7qb>laU#?wW(G#q~+jv_od%^1791__dVO{BbRR8+GLSvz5H8>aLgZz zT)*e~WbABFn)as9Fy}dvYyZVn6M2nMUxFxvx!vY1Lvg#%;EGfnJbq$Vf~}3SKA8qP zXMAt#3aJ^do^?>yE}WyWzNp`6-ePM_4w+Q=Ybo^6!b$Y$T<%cI1qLubt!m#Mz`?Dgb0)!B{Ei8$*}d z^`Rcw!f;%d0SEy46SvKW{uHBRR67MT7mNu| zA%~(lE*Yf0uY^#%4A|XzGD$^E3VAtJC?ijH87HqV-@ukAxKAUDlX6@OWC<7$cUSH^ z6_aZ^uleYA4!msb^q&!2qwiTT7c*z5;OlAJl2DiYg8PtR-l>>bBPMAizBFG6&(3lb z!ir^dTbbByliU0Jk$%jL_r8c|30w!ll-5d|d!mf=!3NdT3Xv&Qh-tM}Nc7EfWj=g% zu<`DztH)HafcZg|a{=P|=Xt^%2p|dECNhxZ4&$O4+Q35i%Y6dxNVa=lcDUa$GI$M? zr!1~)QAsVpgOt2bJ&wOqM#to3GqAjkG8!MkF z9^dyV618F24qXIuw|LCY=>Dtr?C|A3e*n~{>o1cHr&di?VZQ%r|LpKje-$aY{C+ar zkCV&!<66B4 zhd{*zBy78T+QZk@Lz*eFZ!=6V!r30y}qqKD{Wuw8NqKXMEl8$bF;hp zOM4CHN4jvAGrizROB%zSjcB!?(XAy-WbT5gy<<$#x)QaZBx%moB^n0nG?kVlBU)V| zPLWGXlGOKEe2Q*uQM+zb8-HAnc*fkqRoo%+@igNH9AKXf5S3Rms0As;AdBU~y|p06 zKCz|gKEl?PGugUkyL!!4nAE_76O;+_0&_X*9?P}vx7TA!vkqbM660_botI;&nz8o} Db<5I9 diff --git a/resources/fonts/cp_437.txt b/resources/fonts/cp_437.txt new file mode 100644 index 0000000..032280a --- /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 36c1c93dbf8d1335e4b6d18f3638f5116455a6c1..aaee9061796f26b7912b073fbb00af520b8b493a 100644 GIT binary patch literal 4256 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 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("