diff options
author | Franklin Wei <me@fwei.tk> | 2017-07-26 23:26:32 -0400 |
---|---|---|
committer | Franklin Wei <git@fwei.tk> | 2017-07-27 19:09:50 -0400 |
commit | aa7b168fd7268d557af84a70ba155e69048d9e59 (patch) | |
tree | 793bc53e4442b24cec045e64d93072d0f06337bb | |
parent | eb43bce106975736223adfa57f477d3e8d7a3ceb (diff) |
puzzles: font caching
This lets puzzles remember which fonts were loaded previously so
they can be preloaded when the puzzle is started (and the disk is
spinning), instead of while the game is being played.
-rw-r--r-- | apps/plugins/puzzles/rockbox.c | 233 |
1 files changed, 198 insertions, 35 deletions
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c index 1df02bb72e..b193a85430 100644 --- a/apps/plugins/puzzles/rockbox.c +++ b/apps/plugins/puzzles/rockbox.c @@ -47,6 +47,8 @@ #define ERROR_COLOR LCD_RGBPACK(255, 0, 0) +#define MAX_FONTS (MAXUSERFONTS - 2) + #ifdef COMBINED #define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/puzzles.sav" #else @@ -54,6 +56,7 @@ static char save_file_path[MAX_PATH]; #define SAVE_FILE ((const char*)save_file_path) #endif +#define FONT_TABLE PLUGIN_GAMES_DATA_DIR "/.sgt-puzzles.fnttab" #define MURICA @@ -147,6 +150,8 @@ static struct bundled_font { static int n_fonts, access_counter = -1; +/* called on exit and before entering help viewer (workaround for a + possible bug in simple_viewer) */ static void unload_fonts(void) { for(int i = 0; i < 2 * BUNDLE_COUNT; ++i) @@ -159,19 +164,53 @@ static void unload_fonts(void) rb->lcd_setfont(FONT_UI); } -static void rb_setfont(int type, int size) +static void init_fonttab(void) { - if(access_counter < 0) + for(int i = 0; i < 2 * BUNDLE_COUNT; ++i) + loaded_fonts[i].status = -3; + access_counter = 0; + n_fonts = 0; +} + +static void font_path(char *buf, int type, int size) +{ + if(size < 10) /* Deja Vu only goes down to 10px, below that it's a giant blob */ { - for(int i = 0; i < 2 * BUNDLE_COUNT; ++i) - loaded_fonts[i].status = -3; - access_counter = 0; - n_fonts = 0; + if(size < 7) + size = 7; /* we're not going to force anyone to read 05-Tiny :P */ + /* we also don't care about monospace/proportional at this point */ + switch(size) + { + case 7: + rb->snprintf(buf, MAX_PATH, FONT_DIR "/07-Fixed.fnt"); + break; + case 8: + rb->snprintf(buf, MAX_PATH, FONT_DIR "/08-Rockfont.fnt"); + break; + case 9: + rb->snprintf(buf, MAX_PATH, FONT_DIR "/09-Fixed.fnt"); + break; + default: + assert(false); + } } + else + rb->snprintf(buf, MAX_PATH, FONT_DIR "/%02d-%s.fnt", size, type == FONT_FIXED ? "DejaVuSansMono" : "DejaVuSans"); +} - /* out of range */ +static void rb_setfont(int type, int size) +{ + /* out of range (besides, no puzzle should ever need this large + of a font, anyways) */ if(BUNDLE_MAX < size) size = BUNDLE_MAX; + if(size < 10) + { + if(size < 7) /* no teeny-tiny fonts */ + size = 7; + /* assume monospace for these */ + type = FONT_FIXED; + } int font_idx = (type == FONT_FIXED ? 0 : BUNDLE_COUNT) + size - BUNDLE_MIN; switch(loaded_fonts[font_idx].status) @@ -180,30 +219,8 @@ static void rb_setfont(int type, int size) { /* never loaded */ char buf[MAX_PATH]; - if(size < 10) /* Deja Vu only goes down to 10px, below that it's a giant blob */ - { - if(size < 7) - size = 7; /* we're not going to force anyone to read 05-Tiny :P */ - /* we also don't care about monospace/proportional at this point */ - switch(size) - { - case 7: - rb->snprintf(buf, sizeof(buf), FONT_DIR "/07-Fixed.fnt"); - break; - case 8: - rb->snprintf(buf, sizeof(buf), FONT_DIR "/08-Rockfont.fnt"); - break; - case 9: - rb->snprintf(buf, sizeof(buf), FONT_DIR "/09-Fixed.fnt"); - break; - default: - assert(false); - } - } - else - rb->snprintf(buf, sizeof(buf), FONT_DIR "/%02d-%s.fnt", size, type == FONT_FIXED ? "DejaVuSansMono" : "DejaVuSans"); - - if(n_fonts >= MAXUSERFONTS - 3) /* safety margin, FIXME */ + font_path(buf, type, size); + if(n_fonts >= MAX_FONTS) /* safety margin, FIXME */ { /* unload an old font */ int oldest_use = -1, oldest_idx = -1; @@ -1911,6 +1928,139 @@ static void exit_handler(void) #endif } +#define MAX_LINE 128 + +/* try loading the fonts indicated in the on-disk font table */ +static void load_fonts(void) +{ + int fd = rb->open(FONT_TABLE, O_RDONLY); + if(fd < 0) + return; + + uint64_t fontmask = 0; + while(1) + { + char linebuf[MAX_LINE], *ptr = linebuf; + int len = rb->read_line(fd, linebuf, sizeof(linebuf)); + if(len <= 0) + break; + + char *tok, *save; + tok = rb->strtok_r(ptr, ":", &save); + ptr = NULL; + + if(!strcmp(tok, midend_which_game(me)->name)) + { + uint32_t left, right; + tok = rb->strtok_r(ptr, ":", &save); + left = atoi(tok); + tok = rb->strtok_r(ptr, ":", &save); + right = atoi(tok); + fontmask = ((uint64_t)left << 31) | right; + break; + } + } + + /* nothing to do */ + if(!fontmask) + { + rb->close(fd); + return; + } + + /* loop through each bit of the mask and try loading the + corresponding font */ + for(int i = 0; i < 2 * BUNDLE_COUNT; ++i) + { + if(fontmask & ((uint64_t)1 << i)) + { + int size = (i > BUNDLE_COUNT ? i - BUNDLE_COUNT : i) + BUNDLE_MIN; + int type = i > BUNDLE_COUNT ? FONT_VARIABLE : FONT_FIXED; + rb_setfont(type, size); + } + } + + rb->close(fd); +} + +/* remember which fonts were loaded */ +static void save_fonts(void) +{ +#if 2*BUNDLE_COUNT > 62 +#error too many fonts for 62-bit mask +#endif + + /* first assemble the bitmask */ + uint64_t fontmask = 0; + for(int i = 0; i < 2 * BUNDLE_COUNT; ++i) + { + /* try loading if we attempted to load */ + if(loaded_fonts[i].status >= -2) + { + fontmask |= (uint64_t)1 << i; + } + } + + if(fontmask) + { + /* font table format is as follows: + * [GAME NAME]:[32-halves of bit mask in decimal][newline] + */ + int fd = rb->open(FONT_TABLE, O_RDONLY); + int outfd = rb->open(FONT_TABLE ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666); + if(outfd < 0) + return; + + uint64_t oldmask = 0; + + if(fd >= 0) + { + while(1) + { + char linebuf[MAX_LINE], *ptr = linebuf; + char origbuf[MAX_LINE]; + int len = rb->read_line(fd, linebuf, sizeof(linebuf)); + if(len <= 0) + break; + rb->memcpy(origbuf, linebuf, len); + + char *tok, *save; + tok = rb->strtok_r(ptr, ":", &save); + ptr = NULL; + + /* copy line if not matching */ + if(strcmp(tok, midend_which_game(me)->name) != 0) + { + rb->write(outfd, origbuf, len); + rb->fdprintf(outfd, "\n"); + } + else + { + /* matching, remember the old mask */ + assert(oldmask == 0); + uint32_t left, right; + tok = rb->strtok_r(ptr, ":", &save); + left = atoi(tok); + tok = rb->strtok_r(ptr, ":", &save); + right = atoi(tok); + oldmask = ((uint64_t)left << 31) | right; + } + } + rb->close(fd); + } + uint64_t final = fontmask; + if(n_fonts < MAX_FONTS) + final |= oldmask; + uint32_t left = final >> 31; + uint32_t right = final & 0x7fffffff; + if(fd < 0) + rb->fdprintf(outfd, "# Please do not edit this file!\n"); + rb->fdprintf(outfd, "%s:%u:%u\n", midend_which_game(me)->name, left, right); + rb->close(outfd); + rb->rename(FONT_TABLE ".tmp", FONT_TABLE); + } +} + /* expects a totally free me* pointer */ static bool load_game(void) { @@ -1922,8 +2072,6 @@ static bool load_game(void) rb->splash(0, "Loading..."); - LOGF("opening %s", SAVE_FILE); - char *game; char *ret = identify_game(&game, read_wrapper, (void*)fd); @@ -1955,7 +2103,6 @@ static bool load_game(void) return false; } rb->close(fd); - /* success, we delete the save */ rb->remove(SAVE_FILE); return true; } @@ -1973,14 +2120,23 @@ static bool load_game(void) rb->splash(HZ, ret); sfree(ret); rb->close(fd); + rb->remove(SAVE_FILE); return false; } rb->close(fd); - /* success, we delete the save */ rb->remove(SAVE_FILE); + + load_fonts(); + + /* success */ return true; } rb->splashf(HZ, "Cannot load save game for %s!", game); + + /* clean up, even on failure */ + rb->close(fd); + rb->remove(SAVE_FILE); + return false; #endif } @@ -1989,9 +2145,14 @@ static bool load_game(void) static void save_game(void) { rb->splash(0, "Saving..."); + + /* save game */ int fd = rb->open(SAVE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666); midend_serialize(me, write_wrapper, (void*) fd); rb->close(fd); + + save_fonts(); + rb->lcd_update(); } @@ -2064,6 +2225,8 @@ enum plugin_status plugin_start(const void *param) init_default_settings(); + init_fonttab(); + load_success = load_game(); #ifndef COMBINED |