diff --git a/apps/plugins/puzzles/README.rockbox b/apps/plugins/puzzles/README.rockbox index dc45e73ade..94dab32c93 100644 --- a/apps/plugins/puzzles/README.rockbox +++ b/apps/plugins/puzzles/README.rockbox @@ -1,25 +1,40 @@ +Introduction +============ + This is the readme for the Rockbox port of Simon Tatham's Portable Puzzle Collection. -Upstream version used is 7cae89fb4b22c305b3fd98b4e1be065ad527a9f7 from -December 2016. It should be relatively trivial to update it to a newer -version, and should probably be done periodically as changes are made. +The upstream version used is subject to change, as it should be +relatively trivial to update it to a newer version. Simply copying the +upstream repo's contents into src/ and running genhelp.sh ought to do +it (watch out for API changes, though!). -Most of the upstream files are essentially untouched, apart from some -minor adjustments to make it compile happily on Rockbox. Some games -still don't work due to issues with their cursor-only control scheme -(untangle being the big culprit here) but the ones that don't are -commented out in SOURCES.games. I'll get around to fixing them -eventually. +Source structure +================ -Building is done rather hackily, with a rule for every puzzle to be -built... almost 40 at the time of writing. Mr. Someone ought to figure -out how to do that with a wildcard or something. +Most of the upstream files in src/ are essentially untouched, apart +from some minor adjustments to make them compile and run happily on +Rockbox. The majority of the rockbox-specific code is found in +rockbox.c, with some minor stuff in rbwrappers.c and rbmalloc.c. + +Help feature +============ + +The Help feature is implemented by compiling each puzzle against a +compressed version of each puzzle's section from the upstream +documentation. These files are stored under help/, and are generated +by genhelp.sh from the puzzles.but file in the source +distribution. The compression is LZ4, implemented in lz4tiny.c (for +decompression on target), and compress.c (for generation). genhelp.sh +should be run whenever the documentation is chagned. Kudos to Simon (duh), and Frank, for telling me about it. Franklin Wei (__builtin) +Changelog +========= + April 2017: Changes made to move upstream sources to a separate subdirectory, where they are completely unmodified from the original. Updating the upstream version is now as simple as copying a @@ -29,3 +44,5 @@ of dummy header files. August 2017: Every game that can be played with only the cursor keys is now functional. + +October 2017: Added zoom feature. diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c index c97959d0d1..454ccfcaea 100644 --- a/apps/plugins/puzzles/rockbox.c +++ b/apps/plugins/puzzles/rockbox.c @@ -21,6 +21,15 @@ /* rockbox frontend for puzzles */ +/* This file contains the majority of the rockbox-specific code for + * the sgt-puzzles port. It implements a set of functions for the + * backend to call to actually run the games, as well as rockbox UI + * code (menus, input, etc). For a good overview of the rest of the + * puzzles code, see: + * + * . + */ + #include "plugin.h" #include "help.h" @@ -44,6 +53,7 @@ #define FONT_CACHING #endif +/* background color (mimicking from the JS frontend) */ #define BG_R .9f /* very light gray */ #define BG_G .9f #define BG_B .9f @@ -52,9 +62,23 @@ #define ERROR_COLOR LCD_RGBPACK(255, 0, 0) #define MAX_FONTS (MAXUSERFONTS - 2) - #define FONT_TABLE PLUGIN_GAMES_DATA_DIR "/.sgt-puzzles.fnttab" +/* font bundle size range */ +#define BUNDLE_MIN 7 +#define BUNDLE_MAX 36 +#define BUNDLE_COUNT (BUNDLE_MAX - BUNDLE_MIN + 1) + +/* max length of C_STRING config vals */ +#define MAX_STRLEN 128 + +/* try to increment a numeric config value up to this much */ +#define CHOOSER_MAX_INCR 2 + +/* max font table line */ +#define MAX_LINE 128 + +/* Sorry. */ #define MURICA #ifdef MURICA @@ -64,10 +88,25 @@ #define midend_colors midend_colours #endif +/* zoom stuff */ +#define ZOOM_FACTOR 3 #define PAN_X (MIN(LCD_HEIGHT, LCD_WIDTH) / 4) #define PAN_Y (MIN(LCD_HEIGHT, LCD_WIDTH) / 4) -#define ZOOM_FACTOR 3 +/* utility macros */ +#undef ABS +#define ABS(a) ((a)<0?-(a):(a)) +#define SWAP(a, b, t) do { t = a; a = b; b = t; } while(0); +#define fp_fpart(f, bits) ((f) & ((1 << (bits)) - 1)) +#define fp_rfpart(f, bits) ((1 << (bits)) - fp_fpart(f, bits)) +#define FRACBITS 16 + +static void fix_size(void); +static int pause_menu(void); +static inline void plot(fb_data *fb, int w, int h, + unsigned x, unsigned y, unsigned long a, + unsigned long r1, unsigned long g1, unsigned long b1, + unsigned cl, unsigned cr, unsigned cu, unsigned cd); static midend *me = NULL; static unsigned *colors = NULL; @@ -81,29 +120,36 @@ static bool debug_mode = false; static int help_times = 0; #endif -static void fix_size(void); -static int pause_menu(void); - +/* clipping stuff */ static struct viewport clip_rect; static bool clipped = false, zoom_enabled = false, view_mode = true; -extern bool audiobuf_available; +extern bool audiobuf_available; /* defined in rbmalloc.c */ -static fb_data *zoom_fb; +static fb_data *zoom_fb; /* dynamically allocated */ static int zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr; static int cur_font = FONT_UI; +static bool need_draw_update = false; +static int ud_l = 0, ud_u = 0, ud_r = LCD_WIDTH, ud_d = LCD_HEIGHT; + +static char *titlebar = NULL; + +static bool want_redraw = true, accept_input = true; + +/* last timer call */ +static long last_tstamp; +static volatile bool timer_on = false; + +static bool load_success; + +/* debug settings */ +/* did I mention there's a secret debug menu? */ static struct settings_t { int slowmo_factor; bool timerflash, clipoff, shortcuts, no_aa, polyanim; } settings; -static inline void plot(fb_data *fb, int w, int h, - unsigned x, unsigned y, unsigned long a, - unsigned long r1, unsigned long g1, unsigned long b1, - unsigned cl, unsigned cr, unsigned cu, unsigned cd); - - /* re-implementations of many rockbox primitives, adapted to draw into * a custom framebuffer. */ static void zoom_drawpixel(int x, int y) @@ -268,78 +314,7 @@ static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, in } } -/* clipping is implemented through viewports and offsetting - * coordinates */ -static void rb_clip(void *handle, int x, int y, int w, int h) -{ - if(!zoom_enabled) - { - if(!settings.clipoff) - { - LOGF("rb_clip(%d %d %d %d)", x, y, w, h); - clip_rect.x = MAX(0, x); - clip_rect.y = MAX(0, y); - clip_rect.width = MIN(LCD_WIDTH, w); - clip_rect.height = MIN(LCD_HEIGHT, h); - clip_rect.font = FONT_UI; - clip_rect.drawmode = DRMODE_SOLID; -#if LCD_DEPTH > 1 - clip_rect.fg_pattern = LCD_DEFAULT_FG; - clip_rect.bg_pattern = LCD_DEFAULT_BG; -#endif - rb->lcd_set_viewport(&clip_rect); - clipped = true; - } - } - else - { - zoom_clipu = y; - zoom_clipd = y + h; - zoom_clipl = x; - zoom_clipr = x + w; - } -} - -static void rb_unclip(void *handle) -{ - if(!zoom_enabled) - { - LOGF("rb_unclip"); - rb->lcd_set_viewport(NULL); - clipped = false; - } - else - { - zoom_clipu = 0; - zoom_clipd = zoom_h; - zoom_clipl = 0; - zoom_clipr = zoom_w; - } -} - -static void offset_coords(int *x, int *y) -{ - if(clipped) - { - *x -= clip_rect.x; - *y -= clip_rect.y; - } -} - -static void rb_color(int n) -{ - if(n < 0) - { - fatal("bad color %d", n); - return; - } - rb->lcd_set_foreground(colors[n]); -} - -/* font bundle size range */ -#define BUNDLE_MIN 7 -#define BUNDLE_MAX 36 -#define BUNDLE_COUNT (BUNDLE_MAX - BUNDLE_MIN + 1) +/* font management */ static struct bundled_font { int status; /* -3 = never tried loading, or unloaded, -2 = failed to load, >= -1: loaded successfully */ @@ -467,6 +442,76 @@ fallback: return; } +/*** Drawing API ***/ + +static void offset_coords(int *x, int *y) +{ + if(clipped) + { + *x -= clip_rect.x; + *y -= clip_rect.y; + } +} + +static void rb_color(int n) +{ + if(n < 0) + { + fatal("bad color %d", n); + return; + } + rb->lcd_set_foreground(colors[n]); +} + +/* clipping is implemented through viewports and offsetting + * coordinates */ +static void rb_clip(void *handle, int x, int y, int w, int h) +{ + if(!zoom_enabled) + { + if(!settings.clipoff) + { + LOGF("rb_clip(%d %d %d %d)", x, y, w, h); + clip_rect.x = MAX(0, x); + clip_rect.y = MAX(0, y); + clip_rect.width = MIN(LCD_WIDTH, w); + clip_rect.height = MIN(LCD_HEIGHT, h); + clip_rect.font = FONT_UI; + clip_rect.drawmode = DRMODE_SOLID; +#if LCD_DEPTH > 1 + clip_rect.fg_pattern = LCD_DEFAULT_FG; + clip_rect.bg_pattern = LCD_DEFAULT_BG; +#endif + rb->lcd_set_viewport(&clip_rect); + clipped = true; + } + } + else + { + zoom_clipu = y; + zoom_clipd = y + h; + zoom_clipl = x; + zoom_clipr = x + w; + } +} + +static void rb_unclip(void *handle) +{ + if(!zoom_enabled) + { + LOGF("rb_unclip"); + rb->lcd_set_viewport(NULL); + clipped = false; + } + else + { + zoom_clipu = 0; + zoom_clipd = zoom_h; + zoom_clipl = 0; + zoom_clipr = zoom_w; + } +} + static void rb_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int color, const char *text) { @@ -563,13 +608,6 @@ static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color) } } -#define SWAP(a, b, t) do { t = a; a = b; b = t; } while(0); - -#define fp_fpart(f, bits) ((f) & ((1 << (bits)) - 1)) -#define fp_rfpart(f, bits) ((1 << (bits)) - fp_fpart(f, bits)) - -#define FRACBITS 16 - /* a goes from 0-255, with a = 255 being fully opaque and a = 0 transparent */ static inline void plot(fb_data *fb, int w, int h, unsigned x, unsigned y, unsigned long a, @@ -609,9 +647,6 @@ static inline void plot(fb_data *fb, int w, int h, #endif } -#undef ABS -#define ABS(a) ((a)<0?-(a):(a)) - /* speed benchmark: 34392 lines/sec vs 112687 non-antialiased * lines/sec at full optimization on ipod6g */ @@ -902,6 +937,7 @@ static void zoom_filltriangle(int x1, int y1, } } +/* Should probably refactor this */ static void rb_draw_poly(void *handle, int *coords, int npoints, int fillcolor, int outlinecolor) { @@ -1207,10 +1243,6 @@ static void rb_blitter_load(void *handle, blitter *bl, int x, int y) } } -static bool need_draw_update = false; - -static int ud_l = 0, ud_u = 0, ud_r = LCD_WIDTH, ud_d = LCD_HEIGHT; - static void rb_draw_update(void *handle, int x, int y, int w, int h) { LOGF("rb_draw_update(%d, %d, %d, %d)", x, y, w, h); @@ -1260,8 +1292,6 @@ static void rb_end_draw(void *handle) } } -static char *titlebar = NULL; - static void rb_status_bar(void *handle, const char *text) { if(titlebar) @@ -1342,8 +1372,70 @@ const drawing_api rb_drawing = { NULL, }; -static bool want_redraw = true; -static bool accept_input = true; +/** functions exported to puzzles code **/ + +void fatal(const char *fmt, ...) +{ + va_list ap; + + rb->splash(HZ, "FATAL"); + + va_start(ap, fmt); + char buf[80]; + rb->vsnprintf(buf, 80, fmt, ap); + rb->splash(HZ * 2, buf); + va_end(ap); + + exit(1); +} + +void get_random_seed(void **randseed, int *randseedsize) +{ + *randseed = snew(long); + long seed = *rb->current_tick; + rb->memcpy(*randseed, &seed, sizeof(seed)); + *randseedsize = sizeof(long); +} + +static void timer_cb(void) +{ +#if LCD_DEPTH != 24 + if(settings.timerflash) + { + static bool what = false; + what = !what; + if(what) + rb->lcd_framebuffer[0] = LCD_BLACK; + else + rb->lcd_framebuffer[0] = LCD_WHITE; + rb->lcd_update(); + } +#endif + + LOGF("timer callback"); + midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor); + last_tstamp = *rb->current_tick; +} + +void activate_timer(frontend *fe) +{ + last_tstamp = *rb->current_tick; + timer_on = true; +} + +void deactivate_timer(frontend *fe) +{ + timer_on = false; +} + +void frontend_default_color(frontend *fe, float *out) +{ + *out++ = BG_R; + *out++ = BG_G; + *out++ = BG_B; +} + +/** frontend code -- mostly UI stuff **/ /* set do_pausemenu to false to just return -1 on BTN_PAUSE and do * nothing else. */ @@ -1537,42 +1629,7 @@ static int process_input(int tmo, bool do_pausemenu) return state; } -static long last_tstamp; - -static void timer_cb(void) -{ -#if LCD_DEPTH != 24 - if(settings.timerflash) - { - static bool what = false; - what = !what; - if(what) - rb->lcd_framebuffer[0] = LCD_BLACK; - else - rb->lcd_framebuffer[0] = LCD_WHITE; - rb->lcd_update(); - } -#endif - - LOGF("timer callback"); - midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor); - last_tstamp = *rb->current_tick; -} - -static volatile bool timer_on = false; - -void activate_timer(frontend *fe) -{ - last_tstamp = *rb->current_tick; - timer_on = true; -} - -void deactivate_timer(frontend *fe) -{ - timer_on = false; -} - -/* render to a virtual framebuffer and let the user pan (but not make any moves) */ +/* either pan around a zoomed-in image or play zoomed-in */ static void zoom(void) { rb->splash(0, "Please wait..."); @@ -1773,9 +1830,6 @@ static bool is_integer(const char *str) return true; } -/* max length of C_STRING config vals */ -#define MAX_STRLEN 128 - static void int_chooser(config_item *cfgs, int idx, int val) { config_item *cfg = cfgs + idx; @@ -1819,9 +1873,6 @@ static void int_chooser(config_item *cfgs, int idx, int val) } if(d) { - /* we try to increment the value up to this much (mainly - * a workaround for Unruly): */ -#define CHOOSER_MAX_INCR 2 const char *ret; for(int i = 0; i < CHOOSER_MAX_INCR; ++i) @@ -2026,7 +2077,7 @@ done: return success; } -const char *preset_formatter(int sel, void *data, char *buf, size_t len) +static const char *preset_formatter(int sel, void *data, char *buf, size_t len) { struct preset_menu *menu = data; rb->snprintf(buf, len, "%s", menu->entries[sel].title); @@ -2433,7 +2484,9 @@ static int pause_menu(void) return 0; } -/* points to pluginbuf */ +/* points to pluginbuf, used by rbmalloc.c */ +/* useless side note: originally giant_buffer was a statically + * allocated giant array (4096KB IIRC), hence its name. */ char *giant_buffer = NULL; static size_t giant_buffer_len = 0; /* set on start */ @@ -2528,8 +2581,6 @@ static void exit_handler(void) #endif } -#define MAX_LINE 128 - #ifdef FONT_CACHING /* try loading the fonts indicated in the on-disk font table */ static void load_fonts(void) @@ -2742,8 +2793,6 @@ static void save_game(void) rb->lcd_update(); } -static bool load_success; - static int mainmenu_cb(int action, const struct menu_item_ex *this_item) { int i = (intptr_t) this_item;