summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/rockbox.c
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2016-11-20 15:16:41 -0500
committerFranklin Wei <me@fwei.tk>2016-12-18 18:13:22 +0100
commit1a6a8b52f7aa4e2da6f4c34a0c743c760b8cfd99 (patch)
tree8e7f2d6b0cbdb5d15c13457b2c3e1de69f598440 /apps/plugins/puzzles/rockbox.c
parent3ee79724f6fb033d50e26ef37b33d3f8cedf0c5b (diff)
downloadrockbox-1a6a8b52f7aa4e2da6f4c34a0c743c760b8cfd99.tar.gz
rockbox-1a6a8b52f7aa4e2da6f4c34a0c743c760b8cfd99.zip
Port of Simon Tatham's Puzzle Collection
Original revision: 5123b1bf68777ffa86e651f178046b26a87cf2d9 MIT Licensed. Some games still crash and others are unplayable due to issues with controls. Still need a "real" polygon filling algorithm. Currently builds one plugin per puzzle (about 40 in total, around 100K each on ARM), but can easily be made to build a single monolithic overlay (800K or so on ARM). The following games are at least partially broken for various reasons, and have been disabled on this commit: Cube: failed assertion with "Icosahedron" setting Keen: input issues Mines: weird stuff happens on target Palisade: input issues Solo: input issues, occasional crash on target Towers: input issues Undead: input issues Unequal: input and drawing issues (concave polys) Untangle: input issues Features left to do: - In-game help system - Figure out the weird bugs Change-Id: I7c69b6860ab115f973c8d76799502e9bb3d52368
Diffstat (limited to 'apps/plugins/puzzles/rockbox.c')
-rw-r--r--apps/plugins/puzzles/rockbox.c1682
1 files changed, 1682 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
new file mode 100644
index 0000000000..3a1c00e615
--- /dev/null
+++ b/apps/plugins/puzzles/rockbox.c
@@ -0,0 +1,1682 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2016 Franklin Wei
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22/* rockbox frontend for puzzles */
23
24#include "plugin.h"
25
26#include "puzzles.h"
27#include "keymaps.h"
28
29#ifndef COMBINED
30#include "lib/playback_control.h"
31#endif
32#include "lib/xlcd.h"
33
34/* how many ticks between timer callbacks */
35#define TIMER_INTERVAL (HZ / 50)
36#define BG_R .9f /* very light gray */
37#define BG_G .9f
38#define BG_B .9f
39
40#ifdef COMBINED
41#define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/puzzles.sav"
42#else
43static char save_file_path[MAX_PATH];
44#define SAVE_FILE ((const char*)save_file_path)
45#endif
46
47#define BG_COLOR LCD_RGBPACK((int)(255*BG_R), (int)(255*BG_G), (int)(255*BG_B))
48
49#define MURICA
50
51#ifdef MURICA
52#define midend_serialize midend_serialise
53#define midend_deserialize midend_deserialise
54#define frontend_default_color frontend_default_colour
55#define midend_colors midend_colours
56#endif
57
58static midend *me = NULL;
59static unsigned *colors = NULL;
60static int ncolors = 0;
61static long last_keystate = 0;
62
63static void fix_size(void);
64
65static void rb_start_draw(void *handle)
66{
67 (void) handle;
68}
69
70static struct viewport clip_rect;
71static bool clipped = false;
72
73static struct settings_t {
74 int slowmo_factor;
75 bool bulk, timerflash;
76} settings;
77
78/* clipping is implemented through viewports and offsetting
79 * coordinates */
80static void rb_clip(void *handle, int x, int y, int w, int h)
81{
82 LOGF("rb_clip(%d %d %d %d)", x, y, w, h);
83 clip_rect.x = x;
84 clip_rect.y = y;
85 clip_rect.width = w;
86 clip_rect.height = h;
87 clip_rect.font = FONT_UI;
88 clip_rect.drawmode = DRMODE_SOLID;
89#if LCD_DEPTH > 1
90 clip_rect.fg_pattern = LCD_DEFAULT_FG;
91 clip_rect.bg_pattern = LCD_DEFAULT_BG;
92#endif
93 rb->lcd_set_viewport(&clip_rect);
94 clipped = true;
95}
96
97static void rb_unclip(void *handle)
98{
99 LOGF("rb_unclip");
100 rb->lcd_set_viewport(NULL);
101 clipped = false;
102}
103
104static void offset_coords(int *x, int *y)
105{
106 if(clipped)
107 {
108 *x -= clip_rect.x;
109 *y -= clip_rect.y;
110 }
111}
112
113static void rb_color(int n)
114{
115 if(n < 0)
116 {
117 fatal("bad color %d", n);
118 return;
119 }
120 rb->lcd_set_foreground(colors[n]);
121}
122
123static void rb_draw_text(void *handle, int x, int y, int fonttype,
124 int fontsize, int align, int color, char *text)
125{
126 (void) fontsize;
127 LOGF("rb_draw_text(%d %d %s)", x, y, text);
128
129 offset_coords(&x, &y);
130
131 /* TODO: variable font size */
132 switch(fonttype)
133 {
134 case FONT_FIXED:
135 rb->lcd_setfont(FONT_SYSFIXED);
136 break;
137 case FONT_VARIABLE:
138 rb->lcd_setfont(FONT_UI);
139 break;
140 default:
141 fatal("bad font");
142 break;
143 }
144
145 int w, h;
146 rb->lcd_getstringsize(text, &w, &h);
147
148 static int cap_h = -1;
149 if(cap_h < 0)
150 rb->lcd_getstringsize("X", NULL, &cap_h);
151
152 if(align & ALIGN_VNORMAL)
153 y -= h;
154 else if(align & ALIGN_VCENTRE)
155 y -= cap_h / 2;
156
157 if(align & ALIGN_HCENTRE)
158 x -= w / 2;
159 else if(align & ALIGN_HRIGHT)
160 x -= w;
161
162 rb_color(color);
163 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
164 rb->lcd_putsxy(x, y, text);
165 rb->lcd_set_drawmode(DRMODE_SOLID);
166}
167
168static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color)
169{
170 LOGF("rb_draw_rect(%d, %d, %d, %d, %d)", x, y, w, h, color);
171 rb_color(color);
172 offset_coords(&x, &y);
173 rb->lcd_fillrect(x, y, w, h);
174}
175
176static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2,
177 int color)
178{
179 LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color);
180 offset_coords(&x1, &y1);
181 offset_coords(&x2, &y2);
182 rb_color(color);
183 rb->lcd_drawline(x1, y1, x2, y2);
184}
185
186/*
187 * draw filled polygon
188 * originally by Sebastian Leonhardt (ulmutul)
189 * 'count' : number of coordinate pairs
190 * 'pxy': array of coordinates. pxy[0]=x0,pxy[1]=y0,...
191 * note: provide space for one extra coordinate, because the starting point
192 * will automatically be inserted as end point.
193 */
194
195/*
196 * helper function:
197 * find points of intersection between polygon and scanline
198 */
199
200#define MAX_INTERSECTION 32
201
202static void fill_poly_line(int scanline, int count, int *pxy)
203{
204 int i;
205 int j;
206 int num_of_intersects;
207 int direct, old_direct;
208 //intersections of every line with scanline (y-coord)
209 int intersection[MAX_INTERSECTION];
210 /* add starting point as ending point */
211 pxy[count*2] = pxy[0];
212 pxy[count*2+1] = pxy[1];
213
214 old_direct=0;
215 num_of_intersects=0;
216 for (i=0; i<count*2; i+=2) {
217 int x1=pxy[i];
218 int y1=pxy[i+1];
219 int x2=pxy[i+2];
220 int y2=pxy[i+3];
221 // skip if line is outside of scanline
222 if (y1 < y2) {
223 if (scanline < y1 || scanline > y2)
224 continue;
225 }
226 else {
227 if (scanline < y2 || scanline > y1)
228 continue;
229 }
230 // calculate x-coord of intersection
231 if (y1==y2) {
232 direct=0;
233 }
234 else {
235 direct = y1>y2 ? 1 : -1;
236 // omit double intersections, if both lines lead in the same direction
237 intersection[num_of_intersects] =
238 x1+((scanline-y1)*(x2-x1))/(y2-y1);
239 if ( (direct!=old_direct)
240 || (intersection[num_of_intersects] != intersection[num_of_intersects-1])
241 )
242 ++num_of_intersects;
243 }
244 old_direct = direct;
245 }
246
247 // sort points of intersection
248 for (i=0; i<num_of_intersects-1; ++i) {
249 for (j=i+1; j<num_of_intersects; ++j) {
250 if (intersection[j]<intersection[i]) {
251 int temp=intersection[i];
252 intersection[i]=intersection[j];
253 intersection[j]=temp;
254 }
255 }
256 }
257 // draw
258 for (i=0; i<num_of_intersects; i+=2) {
259 rb->lcd_hline(intersection[i], intersection[i+1], scanline);
260 }
261}
262
263/* two extra elements at end of pxy needed */
264static void v_fillarea(int count, int *pxy)
265{
266 int i;
267 int y1, y2;
268
269 // find min and max y coords
270 y1=y2=pxy[1];
271 for (i=3; i<count*2; i+=2) {
272 if (pxy[i] < y1) y1 = pxy[i];
273 else if (pxy[i] > y2) y2 = pxy[i];
274 }
275
276 for (i=y1; i<=y2; ++i) {
277 fill_poly_line(i, count, pxy);
278 }
279}
280
281static void rb_draw_poly(void *handle, int *coords, int npoints,
282 int fillcolor, int outlinecolor)
283{
284 LOGF("rb_draw_poly");
285
286 if(fillcolor >= 0)
287 {
288 rb_color(fillcolor);
289#if 1
290 /* serious hack: draw a bunch of triangles between adjacent points */
291 /* this generally works, even with some concave polygons */
292 for(int i = 2; i < npoints; ++i)
293 {
294 int x1, y1, x2, y2, x3, y3;
295 x1 = coords[0];
296 y1 = coords[1];
297 x2 = coords[(i - 1) * 2];
298 y2 = coords[(i - 1) * 2 + 1];
299 x3 = coords[i * 2];
300 y3 = coords[i * 2 + 1];
301 offset_coords(&x1, &y1);
302 offset_coords(&x2, &y2);
303 offset_coords(&x3, &y3);
304 xlcd_filltriangle(x1, y1,
305 x2, y2,
306 x3, y3);
307
308#if 0
309 /* debug code */
310 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0));
311 rb->lcd_drawpixel(x1, y1);
312 rb->lcd_drawpixel(x2, y2);
313 rb->lcd_drawpixel(x3, y3);
314 rb->lcd_update();
315 rb->sleep(HZ);
316 rb_color(fillcolor);
317 rb->lcd_drawpixel(x1, y1);
318 rb->lcd_drawpixel(x2, y2);
319 rb->lcd_drawpixel(x3, y3);
320 rb->lcd_update();
321#endif
322 }
323#else
324 int *pxy = smalloc(sizeof(int) * 2 * npoints + 2);
325 /* copy points, offsetted */
326 for(int i = 0; i < npoints; ++i)
327 {
328 pxy[2 * i + 0] = coords[2 * i + 0];
329 pxy[2 * i + 1] = coords[2 * i + 1];
330 offset_coords(&pxy[2*i+0], &pxy[2*i+1]);
331 }
332 v_fillarea(npoints, pxy);
333 sfree(pxy);
334#endif
335 }
336
337 /* draw outlines last so they're not covered by the fill */
338 assert(outlinecolor >= 0);
339 rb_color(outlinecolor);
340
341 for(int i = 1; i < npoints; ++i)
342 {
343 int x1, y1, x2, y2;
344 x1 = coords[2 * (i - 1)];
345 y1 = coords[2 * (i - 1) + 1];
346 x2 = coords[2 * i];
347 y2 = coords[2 * i + 1];
348 offset_coords(&x1, &y1);
349 offset_coords(&x2, &y2);
350 rb->lcd_drawline(x1, y1,
351 x2, y2);
352 //rb->lcd_update();
353 //rb->sleep(HZ/2);
354 }
355
356 int x1, y1, x2, y2;
357 x1 = coords[0];
358 y1 = coords[1];
359 x2 = coords[2 * (npoints - 1)];
360 y2 = coords[2 * (npoints - 1) + 1];
361 offset_coords(&x1, &y1);
362 offset_coords(&x2, &y2);
363
364 rb->lcd_drawline(x1, y1,
365 x2, y2);
366}
367
368static void rb_draw_circle(void *handle, int cx, int cy, int radius,
369 int fillcolor, int outlinecolor)
370{
371 LOGF("rb_draw_circle(%d, %d, %d)", cx, cy, radius);
372 offset_coords(&cx, &cy);
373
374 if(fillcolor >= 0)
375 {
376 rb_color(fillcolor);
377 xlcd_fillcircle(cx, cy, radius - 1);
378 }
379
380 assert(outlinecolor >= 0);
381 rb_color(outlinecolor);
382 xlcd_drawcircle(cx, cy, radius - 1);
383}
384
385struct blitter {
386 bool have_data;
387 int x, y;
388 struct bitmap bmp;
389};
390
391static blitter *rb_blitter_new(void *handle, int w, int h)
392{
393 LOGF("rb_blitter_new");
394 blitter *b = snew(blitter);
395 b->bmp.width = w;
396 b->bmp.height = h;
397 b->bmp.data = smalloc(w * h * sizeof(fb_data));
398 b->have_data = false;
399 return b;
400}
401
402static void rb_blitter_free(void *handle, blitter *bl)
403{
404 LOGF("rb_blitter_free");
405 sfree(bl->bmp.data);
406 sfree(bl);
407 return;
408}
409
410static void trim_rect(int *x, int *y, int *w, int *h)
411{
412 int x0, x1, y0, y1;
413
414 /*
415 * Reduce the size of the copied rectangle to stop it going
416 * outside the bounds of the canvas.
417 */
418
419 /* Transform from x,y,w,h form into coordinates of all edges */
420 x0 = *x;
421 y0 = *y;
422 x1 = *x + *w;
423 y1 = *y + *h;
424
425 /* Clip each coordinate at both extremes of the canvas */
426 x0 = (x0 < 0 ? 0 : x0 > LCD_WIDTH ? LCD_WIDTH : x0);
427 x1 = (x1 < 0 ? 0 : x1 > LCD_WIDTH ? LCD_WIDTH : x1);
428 y0 = (y0 < 0 ? 0 : y0 > LCD_HEIGHT ? LCD_HEIGHT : y0);
429 y1 = (y1 < 0 ? 0 : y1 > LCD_HEIGHT ? LCD_HEIGHT : y1);
430
431 /* Transform back into x,y,w,h to return */
432 *x = x0;
433 *y = y0;
434 *w = x1 - x0;
435 *h = y1 - y0;
436}
437
438/* copy a section of the framebuffer */
439static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
440{
441 /* no viewport offset */
442#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
443#error no vertical stride
444#else
445 if(bl->bmp.data)
446 {
447 int w = bl->bmp.width, h = bl->bmp.height;
448 trim_rect(&x, &y, &w, &h);
449 LOGF("rb_blitter_save(%d, %d, %d, %d)", x, y, w, h);
450 for(int i = 0; i < h; ++i)
451 {
452 /* copy line-by-line */
453 rb->memcpy(bl->bmp.data + sizeof(fb_data) * i * w,
454 rb->lcd_framebuffer + (y + i) * LCD_WIDTH + x,
455 w * sizeof(fb_data));
456 }
457 bl->x = x;
458 bl->y = y;
459 bl->have_data = true;
460 }
461#endif
462}
463
464static void rb_blitter_load(void *handle, blitter *bl, int x, int y)
465{
466 LOGF("rb_blitter_load");
467 if(!bl->have_data)
468 return;
469 int w = bl->bmp.width, h = bl->bmp.height;
470
471 if(x == BLITTER_FROMSAVED) x = bl->x;
472 if(y == BLITTER_FROMSAVED) y = bl->y;
473
474 offset_coords(&x, &y);
475 trim_rect(&x, &y, &w, &h);
476 rb->lcd_bitmap((fb_data*)bl->bmp.data, x, y, w, h);
477}
478
479static void rb_draw_update(void *handle, int x, int y, int w, int h)
480{
481 LOGF("rb_draw_update(%d, %d, %d, %d)", x, y, w, h);
482 if(!settings.bulk)
483 rb->lcd_update_rect(x, y, w, h);
484 else
485 rb->lcd_update();
486}
487
488static void rb_end_draw(void *handle)
489{
490 LOGF("rb_end_draw");
491}
492
493static char *titlebar = NULL;
494
495static void rb_status_bar(void *handle, char *text)
496{
497 if(titlebar)
498 sfree(titlebar);
499 titlebar = dupstr(text);
500 LOGF("game title is %s\n", text);
501}
502
503static void draw_title(void)
504{
505 const char *str = NULL;
506 if(titlebar)
507 str = titlebar;
508 else
509 str = midend_which_game(me)->name;
510
511 /* quick hack */
512 bool orig_clipped = clipped;
513 if(orig_clipped)
514 rb_unclip(NULL);
515
516 int h;
517 rb->lcd_setfont(FONT_UI);
518 rb->lcd_getstringsize(str, NULL, &h);
519
520 rb->lcd_set_foreground(BG_COLOR);
521 rb->lcd_fillrect(0, LCD_HEIGHT - h, LCD_WIDTH, h);
522
523 rb->lcd_set_foreground(LCD_BLACK);
524 rb->lcd_putsxy(0, LCD_HEIGHT - h, str);
525 rb->lcd_update_rect(0, LCD_HEIGHT - h, LCD_WIDTH, h);
526
527 if(orig_clipped)
528 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
529}
530
531static char *rb_text_fallback(void *handle, const char *const *strings,
532 int nstrings)
533{
534 return dupstr(strings[0]);
535}
536
537const drawing_api rb_drawing = {
538 rb_draw_text,
539 rb_draw_rect,
540 rb_draw_line,
541 rb_draw_poly,
542 rb_draw_circle,
543 rb_draw_update,
544 rb_clip,
545 rb_unclip,
546 rb_start_draw,
547 rb_end_draw,
548 rb_status_bar,
549 rb_blitter_new,
550 rb_blitter_free,
551 rb_blitter_save,
552 rb_blitter_load,
553 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
554 NULL, NULL, /* line_width, line_dotted */
555 rb_text_fallback,
556 NULL,
557};
558
559void frontend_default_color(frontend *fe, float *out)
560{
561 *out++ = BG_R;
562 *out++ = BG_G;
563 *out++ = BG_B;
564}
565
566void fatal(char *fmt, ...)
567{
568 va_list ap;
569
570 rb->splash(HZ, "FATAL ERROR");
571
572 va_start(ap, fmt);
573 char buf[80];
574 rb->vsnprintf(buf, 80, fmt, ap);
575 rb->splash(HZ * 2, buf);
576 va_end(ap);
577
578 exit(1);
579}
580
581void get_random_seed(void **randseed, int *randseedsize)
582{
583 *randseed = snew(long);
584 long seed = *rb->current_tick;
585 rb->memcpy(*randseed, &seed, sizeof(seed));
586 //*(long*)*randseed = 42; // debug
587 //rb->splash(HZ, "DEBUG SEED ON");
588 *randseedsize = sizeof(long);
589}
590
591const char *config_choices_formatter(int sel, void *data, char *buf, size_t len)
592{
593 /* we can't rely on being called in any particular order */
594 char *list = dupstr(data);
595 char delimbuf[2] = { *list, 0 };
596 char *save = NULL;
597 char *str = rb->strtok_r(list, delimbuf, &save);
598 for(int i = 0; i < sel; ++i)
599 str = rb->strtok_r(NULL, delimbuf, &save);
600 rb->snprintf(buf, len, "%s", str);
601 sfree(list);
602 return buf;
603}
604
605static int list_choose(const char *list_str, const char *title)
606{
607 char delim = *list_str;
608
609 const char *ptr = list_str + 1;
610 int n = 0;
611 while(ptr)
612 {
613 n++;
614 ptr = strchr(ptr + 1, delim);
615 }
616
617 struct gui_synclist list;
618
619 rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
620 rb->gui_synclist_set_icon_callback(&list, NULL);
621 rb->gui_synclist_set_nb_items(&list, n);
622 rb->gui_synclist_limit_scroll(&list, false);
623
624 rb->gui_synclist_select_item(&list, 0);
625
626 rb->gui_synclist_set_title(&list, (char*)title, NOICON);
627 while (1)
628 {
629 rb->gui_synclist_draw(&list);
630 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
631 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
632 continue;
633 switch(button)
634 {
635 case ACTION_STD_OK:
636 return rb->gui_synclist_get_sel_pos(&list);
637 case ACTION_STD_PREV:
638 case ACTION_STD_CANCEL:
639 return -1;
640 default:
641 break;
642 }
643 }
644}
645
646static void do_configure_item(config_item *cfg)
647{
648 switch(cfg->type)
649 {
650 case C_STRING:
651 {
652#define MAX_STRLEN 128
653 char *newstr = smalloc(MAX_STRLEN);
654 rb->strlcpy(newstr, cfg->sval, MAX_STRLEN);
655 rb->lcd_set_foreground(LCD_WHITE);
656 rb->lcd_set_background(LCD_BLACK);
657 if(rb->kbd_input(newstr, MAX_STRLEN) < 0)
658 {
659 sfree(newstr);
660 break;
661 }
662 sfree(cfg->sval);
663 cfg->sval = newstr;
664 break;
665 }
666 case C_BOOLEAN:
667 {
668 bool res = cfg->ival != 0;
669 rb->set_bool(cfg->name, &res);
670 cfg->ival = res;
671 break;
672 }
673 case C_CHOICES:
674 {
675 int sel = list_choose(cfg->sval, cfg->name);
676 if(sel >= 0)
677 cfg->ival = sel;
678 break;
679 }
680 default:
681 fatal("bad type");
682 break;
683 }
684}
685
686const char *config_formatter(int sel, void *data, char *buf, size_t len)
687{
688 config_item *cfg = data;
689 cfg += sel;
690 rb->snprintf(buf, len, "%s", cfg->name);
691 return buf;
692}
693
694static void config_menu(void)
695{
696 char *title;
697 config_item *config = midend_get_config(me, CFG_SETTINGS, &title);
698
699 if(!config)
700 {
701 rb->splash(HZ, "Nothing to configure.");
702 goto done;
703 }
704
705 /* count */
706 int n = 0;
707 config_item *ptr = config;
708 while(ptr->type != C_END)
709 {
710 n++;
711 ptr++;
712 }
713
714 /* display a list */
715 struct gui_synclist list;
716
717 rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
718 rb->gui_synclist_set_icon_callback(&list, NULL);
719 rb->gui_synclist_set_nb_items(&list, n);
720 rb->gui_synclist_limit_scroll(&list, false);
721
722 rb->gui_synclist_select_item(&list, 0);
723
724 bool done = false;
725 rb->gui_synclist_set_title(&list, title, NOICON);
726 while (!done)
727 {
728 rb->gui_synclist_draw(&list);
729 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
730 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
731 continue;
732 switch(button)
733 {
734 case ACTION_STD_OK:
735 {
736 config_item old;
737 int pos = rb->gui_synclist_get_sel_pos(&list);
738 memcpy(&old, config + pos, sizeof(old));
739 do_configure_item(config + pos);
740 char *err = midend_set_config(me, CFG_SETTINGS, config);
741 if(err)
742 {
743 rb->splash(HZ, err);
744 memcpy(config + pos, &old, sizeof(old));
745 }
746 break;
747 }
748 case ACTION_STD_PREV:
749 case ACTION_STD_CANCEL:
750 done = true;
751 break;
752 default:
753 break;
754 }
755 }
756
757done:
758 sfree(title);
759 free_cfg(config);
760}
761
762const char *preset_formatter(int sel, void *data, char *buf, size_t len)
763{
764 char *name;
765 game_params *junk;
766 midend_fetch_preset(me, sel, &name, &junk);
767 rb->strlcpy(buf, name, len);
768 return buf;
769}
770
771static void presets_menu(void)
772{
773 if(!midend_num_presets(me))
774 {
775 rb->splash(HZ, "No presets!");
776 return;
777 }
778
779 /* display a list */
780 struct gui_synclist list;
781
782 rb->gui_synclist_init(&list, &preset_formatter, NULL, false, 1, NULL);
783 rb->gui_synclist_set_icon_callback(&list, NULL);
784 rb->gui_synclist_set_nb_items(&list, midend_num_presets(me));
785 rb->gui_synclist_limit_scroll(&list, false);
786
787 int current = midend_which_preset(me);
788 rb->gui_synclist_select_item(&list, current >= 0 ? current : 0);
789
790 bool done = false;
791 rb->gui_synclist_set_title(&list, "Game Type", NOICON);
792 while (!done)
793 {
794 rb->gui_synclist_draw(&list);
795 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
796 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
797 continue;
798 switch(button)
799 {
800 case ACTION_STD_OK:
801 {
802 int sel = rb->gui_synclist_get_sel_pos(&list);
803 char *junk;
804 game_params *params;
805 midend_fetch_preset(me, sel, &junk, &params);
806 midend_set_params(me, params);
807 done = true;
808 break;
809 }
810 case ACTION_STD_PREV:
811 case ACTION_STD_CANCEL:
812 done = true;
813 break;
814 default:
815 break;
816 }
817 }
818}
819
820static const struct {
821 const char *game, *help;
822} quick_help_text[] = {
823 { "Black Box", "Find the hidden balls in the box by bouncing laser beams off them." },
824 { "Bridges", "Connect all the islands with a network of bridges." },
825 { "Cube", "Pick up all the blue squares by rolling the cube over them." },
826 { "Dominosa", "Tile the rectangle with a full set of dominoes." },
827 { "Fifteen", "Slide the tiles around to arrange them into order." },
828 { "Filling", "Mark every square with the area of its containing region." },
829 { "Flip", "Flip groups of squares to light them all up at once." },
830 { "Flood", "Turn the grid the same colour in as few flood fills as possible." },
831 { "Galaxies", "Divide the grid into rotationally symmetric regions each centred on a dot." },
832 { "Guess", "Guess the hidden combination of colours." },
833 { "Inertia", "Collect all the gems without running into any of the mines." },
834 { "Keen", "Complete the latin square in accordance with the arithmetic clues." },
835 { "Light Up", "Place bulbs to light up all the squares." },
836 { "Loopy", "Draw a single closed loop, given clues about number of adjacent edges." },
837 { "Magnets", "Place magnets to satisfy the clues and avoid like poles touching." },
838 { "Map", "Colour the map so that adjacent regions are never the same colour." },
839 { "Mines", "Find all the mines without treading on any of them." },
840 { "Net", "Rotate each tile to reassemble the network." },
841 { "Netslide", "Slide a row at a time to reassemble the network." },
842 { "Palisade", "Divide the grid into equal-sized areas in accordance with the clues." },
843 { "Pattern", "Fill in the pattern in the grid, given only the lengths of runs of black squares." },
844 { "Pearl", "Draw a single closed loop, given clues about corner and straight squares." },
845 { "Pegs", "Jump pegs over each other to remove all but one." },
846 { "Range", "Place black squares to limit the visible distance from each numbered cell." },
847 { "Rectangles", "Divide the grid into rectangles with areas equal to the numbers." },
848 { "Same Game", "Clear the grid by removing touching groups of the same colour squares." },
849 { "Signpost", "Connect the squares into a path following the arrows." },
850 { "Singles", "Black out the right set of duplicate numbers." },
851 { "Sixteen", "Slide a row at a time to arrange the tiles into order." },
852 { "Slant", "Draw a maze of slanting lines that matches the clues." },
853 { "Solo", "Fill in the grid so that each row, column and square block contains one of every digit." },
854 { "Tents", "Place a tent next to each tree." },
855 { "Towers", "Complete the latin square of towers in accordance with the clues." },
856 { "Tracks", "Fill in the railway track according to the clues." },
857 { "Twiddle", "Rotate the tiles around themselves to arrange them into order." },
858 { "Undead", "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors." },
859 { "Unequal", "Complete the latin square in accordance with the > signs." },
860 { "Unruly", "Fill in the black and white grid to avoid runs of three." },
861 { "Untangle", "Reposition the points so that the lines do not cross." },
862};
863
864static void quick_help(void)
865{
866 for(int i = 0; i < ARRAYLEN(quick_help_text); ++i)
867 {
868 if(!strcmp(midend_which_game(me)->name, quick_help_text[i].game))
869 {
870 rb->splash(0, quick_help_text[i].help);
871 rb->button_get(true);
872 return;
873 }
874 }
875}
876
877static void full_help(void)
878{
879 /* TODO */
880}
881
882static void init_default_settings(void)
883{
884 settings.slowmo_factor = 1;
885 settings.bulk = false;
886 settings.timerflash = false;
887}
888
889static void debug_menu(void)
890{
891 MENUITEM_STRINGLIST(menu, "Debug Menu", NULL,
892 "Slowmo factor",
893 "Randomize colors",
894 "Toggle bulk update",
895 "Toggle flash pixel on timer",
896 "Back");
897 bool quit = false;
898 int sel = 0;
899 while(!quit)
900 {
901 switch(rb->do_menu(&menu, &sel, NULL, false))
902 {
903 case 0:
904 rb->set_int("Slowmo factor", "", UNIT_INT, &settings.slowmo_factor, NULL, 1, 1, 10, NULL);
905 break;
906 case 1:
907 {
908 unsigned *ptr = colors;
909 for(int i = 0; i < ncolors; ++i)
910 {
911 /* not seeded, who cares? */
912 *ptr++ = LCD_RGBPACK(rb->rand()%255, rb->rand()%255, rb->rand()%255);
913 }
914 break;
915 }
916 case 2:
917 settings.bulk = !settings.bulk;
918 break;
919 case 3:
920 settings.timerflash = !settings.timerflash;
921 break;
922 case 4:
923 default:
924 quit = true;
925 break;
926 }
927 }
928}
929
930static int pausemenu_cb(int action, const struct menu_item_ex *this_item)
931{
932 int i = (intptr_t) this_item;
933 if(action == ACTION_REQUEST_MENUITEM)
934 {
935 switch(i)
936 {
937 case 3:
938 if(!midend_can_undo(me))
939 return ACTION_EXIT_MENUITEM;
940 break;
941 case 4:
942 if(!midend_can_redo(me))
943 return ACTION_EXIT_MENUITEM;
944 break;
945 case 5:
946 if(!midend_which_game(me)->can_solve)
947 return ACTION_EXIT_MENUITEM;
948 break;
949 case 7:
950#ifdef FOR_REAL
951 return ACTION_EXIT_MENUITEM;
952#else
953 break;
954#endif
955 case 8:
956 if(!midend_num_presets(me))
957 return ACTION_EXIT_MENUITEM;
958 break;
959 case 9:
960#ifdef FOR_REAL
961 return ACTION_EXIT_MENUITEM;
962#else
963 break;
964#endif
965 case 10:
966 if(!midend_which_game(me)->can_configure)
967 return ACTION_EXIT_MENUITEM;
968 break;
969 default:
970 break;
971 }
972 }
973 return action;
974}
975
976static int pause_menu(void)
977{
978#define static auto
979#define const
980 MENUITEM_STRINGLIST(menu, NULL, pausemenu_cb,
981 "Resume Game",
982 "New Game",
983 "Restart Game",
984 "Undo",
985 "Redo",
986 "Solve",
987 "Quick Help",
988 "Extensive Help",
989 "Game Type",
990 "Debug Menu",
991 "Configure Game",
992#ifdef COMBINED
993 "Select Another Game",
994#endif
995 "Quit without Saving",
996 "Quit");
997#undef static
998#undef const
999 /* HACK ALERT */
1000 char title[32] = { 0 };
1001 rb->snprintf(title, sizeof(title), "%s Menu", midend_which_game(me)->name);
1002 menu__.desc = title;
1003
1004 bool quit = false;
1005 int sel = 0;
1006 while(!quit)
1007 {
1008 switch(rb->do_menu(&menu, &sel, NULL, false))
1009 {
1010 case 0:
1011 quit = true;
1012 break;
1013 case 1:
1014 midend_new_game(me);
1015 fix_size();
1016 quit = true;
1017 break;
1018 case 2:
1019 midend_restart_game(me);
1020 fix_size();
1021 quit = true;
1022 break;
1023 case 3:
1024 if(!midend_can_undo(me))
1025 rb->splash(HZ, "Cannot undo.");
1026 else
1027 midend_process_key(me, 0, 0, 'u');
1028 quit = true;
1029 break;
1030 case 4:
1031 if(!midend_can_redo(me))
1032 rb->splash(HZ, "Cannot redo.");
1033 else
1034 midend_process_key(me, 0, 0, 'r');
1035 quit = true;
1036 break;
1037 case 5:
1038 {
1039 char *msg = midend_solve(me);
1040 if(msg)
1041 rb->splash(HZ, msg);
1042 quit = true;
1043 break;
1044 }
1045 case 6:
1046 quick_help();
1047 break;
1048 case 7:
1049 full_help();
1050 break;
1051 case 8:
1052 presets_menu();
1053 midend_new_game(me);
1054 fix_size();
1055 quit = true;
1056 break;
1057 case 9:
1058 debug_menu();
1059 break;
1060 case 10:
1061 config_menu();
1062 midend_new_game(me);
1063 fix_size();
1064 quit = true;
1065 break;
1066#ifdef COMBINED
1067 case 11:
1068 return -1;
1069 case 12:
1070 return -2;
1071 case 13:
1072 return -3;
1073#else
1074 case 11:
1075 return -2;
1076 case 12:
1077 return -3;
1078#endif
1079 default:
1080 break;
1081 }
1082 }
1083 rb->lcd_set_background(BG_COLOR);
1084 rb->lcd_clear_display();
1085 rb->lcd_update();
1086 midend_force_redraw(me);
1087 return 0;
1088}
1089
1090static bool want_redraw = true;
1091static bool accept_input = true;
1092
1093/* ignore the excess of LOGFs below... */
1094#ifdef LOGF_ENABLE
1095#undef LOGF_ENABLE
1096#endif
1097static int process_input(int tmo)
1098{
1099 LOGF("process_input start");
1100 LOGF("------------------");
1101 int state = 0;
1102
1103#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1104 rb->cpu_boost(false); /* about to block for button input */
1105#endif
1106
1107 int button = rb->button_get_w_tmo(tmo);
1108
1109 /* weird stuff */
1110 exit_on_usb(button);
1111
1112 button = rb->button_status();
1113
1114#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1115 rb->cpu_boost(true);
1116#endif
1117
1118 if(button == BTN_PAUSE)
1119 {
1120 want_redraw = false;
1121 /* quick hack to preserve the clipping state */
1122 bool orig_clipped = clipped;
1123 if(orig_clipped)
1124 rb_unclip(NULL);
1125
1126 int rc = pause_menu();
1127
1128 if(orig_clipped)
1129 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1130
1131 last_keystate = 0;
1132 accept_input = true;
1133
1134 return rc;
1135 }
1136
1137 /* special case for inertia: moves occur after RELEASES */
1138 if(!strcmp("Inertia", midend_which_game(me)->name))
1139 {
1140 LOGF("received button 0x%08x", button);
1141
1142 unsigned released = ~button & last_keystate;
1143 last_keystate = button;
1144
1145 if(!button)
1146 {
1147 if(!accept_input)
1148 {
1149 LOGF("ignoring, all keys released but not accepting input before, can accept input later");
1150 accept_input = true;
1151 return 0;
1152 }
1153 }
1154
1155 if(!released || !accept_input)
1156 {
1157 LOGF("released keys detected: 0x%08x", released);
1158 LOGF("ignoring, either no keys released or not accepting input");
1159 return 0;
1160 }
1161
1162 button |= released;
1163 if(last_keystate)
1164 {
1165 LOGF("ignoring input from now until all released");
1166 accept_input = false;
1167 }
1168 LOGF("accepting event 0x%08x", button);
1169 }
1170 /* not inertia: events fire on presses */
1171 else
1172 {
1173 /* start accepting input again after a release */
1174 if(!button)
1175 {
1176 accept_input = true;
1177 return 0;
1178 }
1179 /* ignore repeats */
1180 if(!accept_input)
1181 return 0;
1182 accept_input = false;
1183 }
1184
1185 switch(button)
1186 {
1187 case BTN_UP:
1188 state = CURSOR_UP;
1189 break;
1190 case BTN_DOWN:
1191 state = CURSOR_DOWN;
1192 break;
1193 case BTN_LEFT:
1194 state = CURSOR_LEFT;
1195 break;
1196 case BTN_RIGHT:
1197 state = CURSOR_RIGHT;
1198 break;
1199
1200 /* handle diagonals (mainly for Inertia) */
1201 case BTN_DOWN | BTN_LEFT:
1202#ifdef BTN_DOWN_LEFT
1203 case BTN_DOWN_LEFT:
1204#endif
1205 state = '1' | MOD_NUM_KEYPAD;
1206 break;
1207 case BTN_DOWN | BTN_RIGHT:
1208#ifdef BTN_DOWN_RIGHT
1209 case BTN_DOWN_RIGHT:
1210#endif
1211 state = '3' | MOD_NUM_KEYPAD;
1212 break;
1213 case BTN_UP | BTN_LEFT:
1214#ifdef BTN_UP_LEFT
1215 case BTN_UP_LEFT:
1216#endif
1217 state = '7' | MOD_NUM_KEYPAD;
1218 break;
1219 case BTN_UP | BTN_RIGHT:
1220#ifdef BTN_UP_RIGHT
1221 case BTN_UP_RIGHT:
1222#endif
1223 state = '9' | MOD_NUM_KEYPAD;
1224 break;
1225
1226 case BTN_FIRE:
1227 state = CURSOR_SELECT;
1228 break;
1229 }
1230 LOGF("process_input done");
1231 LOGF("------------------");
1232 return state;
1233}
1234
1235static long last_tstamp;
1236
1237static void timer_cb(void)
1238{
1239#if LCD_DEPTH != 24
1240 if(settings.timerflash)
1241 {
1242 static bool what = false;
1243 what = !what;
1244 if(what)
1245 rb->lcd_framebuffer[0] = LCD_BLACK;
1246 else
1247 rb->lcd_framebuffer[0] = LCD_WHITE;
1248 rb->lcd_update();
1249 }
1250#endif
1251
1252 LOGF("timer callback");
1253 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor);
1254 last_tstamp = *rb->current_tick;
1255}
1256
1257static volatile bool timer_on = false;
1258
1259void activate_timer(frontend *fe)
1260{
1261 last_tstamp = *rb->current_tick;
1262 timer_on = true;
1263}
1264
1265void deactivate_timer(frontend *fe)
1266{
1267 timer_on = false;
1268}
1269
1270#ifdef COMBINED
1271/* can't use audio buffer */
1272static char giant_buffer[1024*1024*4];
1273#else
1274/* points to audiobuf */
1275static char *giant_buffer = NULL;
1276#endif
1277static size_t giant_buffer_len = 0; /* set on start */
1278
1279#ifdef COMBINED
1280const char *formatter(char *buf, size_t n, int i, const char *unit)
1281{
1282 rb->snprintf(buf, n, "%s", gamelist[i]->name);
1283 return buf;
1284}
1285#endif
1286
1287static void fix_size(void)
1288{
1289 int w = LCD_WIDTH, h = LCD_HEIGHT, h_x;
1290 rb->lcd_setfont(FONT_UI);
1291 rb->lcd_getstringsize("X", NULL, &h_x);
1292 h -= h_x;
1293 midend_size(me, &w, &h, TRUE);
1294}
1295
1296static void reset_tlsf(void)
1297{
1298 /* reset tlsf by nuking the signature */
1299 /* will make any already-allocated memory point to garbage */
1300 memset(giant_buffer, 0, 4);
1301 init_memory_pool(giant_buffer_len, giant_buffer);
1302}
1303
1304static int read_wrapper(void *ptr, void *buf, int len)
1305{
1306 int fd = (int) ptr;
1307 return rb->read(fd, buf, len);
1308}
1309
1310static void write_wrapper(void *ptr, void *buf, int len)
1311{
1312 int fd = (int) ptr;
1313 rb->write(fd, buf, len);
1314}
1315
1316static void clear_and_draw(void)
1317{
1318 rb->lcd_clear_display();
1319 rb->lcd_update();
1320
1321 midend_force_redraw(me);
1322 draw_title();
1323}
1324
1325static char *init_for_game(const game *gm, int load_fd, bool draw)
1326{
1327 /* if we are loading a game tlsf has already been initialized */
1328 if(load_fd < 0)
1329 reset_tlsf();
1330
1331 me = midend_new(NULL, gm, &rb_drawing, NULL);
1332
1333 if(load_fd < 0)
1334 midend_new_game(me);
1335 else
1336 {
1337 char *ret = midend_deserialize(me, read_wrapper, (void*) load_fd);
1338 if(ret)
1339 return ret;
1340 }
1341
1342 fix_size();
1343
1344 float *floatcolors = midend_colors(me, &ncolors);
1345
1346 /* convert them to packed RGB */
1347 colors = smalloc(ncolors * sizeof(unsigned));
1348 unsigned *ptr = colors;
1349 float *floatptr = floatcolors;
1350 for(int i = 0; i < ncolors; ++i)
1351 {
1352 int r = 255 * *(floatptr++);
1353 int g = 255 * *(floatptr++);
1354 int b = 255 * *(floatptr++);
1355 LOGF("color %d is %d %d %d", i, r, g, b);
1356 *ptr++ = LCD_RGBPACK(r, g, b);
1357 }
1358 sfree(floatcolors);
1359
1360 rb->lcd_set_viewport(NULL);
1361 rb->lcd_set_backdrop(NULL);
1362 rb->lcd_set_foreground(LCD_BLACK);
1363 rb->lcd_set_background(BG_COLOR);
1364
1365 if(draw)
1366 {
1367 clear_and_draw();
1368 }
1369 return NULL;
1370}
1371
1372static void exit_handler(void)
1373{
1374#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1375 rb->cpu_boost(false);
1376#endif
1377}
1378
1379/* expects a totally free me* pointer */
1380static bool load_game(void)
1381{
1382 reset_tlsf();
1383
1384 int fd = rb->open(SAVE_FILE, O_RDONLY);
1385 if(fd < 0)
1386 return false;
1387
1388 rb->splash(0, "Loading...");
1389
1390 LOGF("opening %s", SAVE_FILE);
1391
1392 char *game;
1393 char *ret = identify_game(&game, read_wrapper, (void*)fd);
1394
1395 if(!*game && ret)
1396 {
1397 sfree(game);
1398 rb->splash(HZ, ret);
1399 rb->close(fd);
1400 return false;
1401 }
1402 else
1403 {
1404 /* seek to beginning */
1405 rb->lseek(fd, 0, SEEK_SET);
1406
1407#ifdef COMBINED
1408 /* search for the game and initialize the midend */
1409 for(int i = 0; i < gamecount; ++i)
1410 {
1411 if(!strcmp(game, gamelist[i]->name))
1412 {
1413 sfree(ret);
1414 ret = init_for_game(gamelist[i], fd, true);
1415 if(ret)
1416 {
1417 rb->splash(HZ, ret);
1418 sfree(ret);
1419 rb->close(fd);
1420 return false;
1421 }
1422 rb->close(fd);
1423 /* success, we delete the save */
1424 rb->remove(SAVE_FILE);
1425 return true;
1426 }
1427 }
1428 rb->splashf(HZ, "Incompatible game %s reported as compatible!?!? REEEPORT MEEEE!!!!", game);
1429 rb->close(fd);
1430 return false;
1431#else
1432 if(!strcmp(game, thegame.name))
1433 {
1434 sfree(ret);
1435 ret = init_for_game(&thegame, fd, false);
1436 if(ret)
1437 {
1438 rb->splash(HZ, ret);
1439 sfree(ret);
1440 rb->close(fd);
1441 return false;
1442 }
1443 rb->close(fd);
1444 /* success, we delete the save */
1445 rb->remove(SAVE_FILE);
1446 return true;
1447 }
1448 rb->splashf(HZ, "Cannot load save game for %s!", game);
1449 return false;
1450#endif
1451 }
1452}
1453
1454static void save_game(void)
1455{
1456 rb->splash(0, "Saving...");
1457 int fd = rb->open(SAVE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666);
1458 midend_serialize(me, write_wrapper, (void*) fd);
1459 rb->close(fd);
1460 rb->lcd_update();
1461}
1462
1463static bool load_success;
1464
1465static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
1466{
1467 int i = (intptr_t) this_item;
1468 if(action == ACTION_REQUEST_MENUITEM)
1469 {
1470 switch(i)
1471 {
1472 case 0:
1473 case 7:
1474 if(!load_success)
1475 return ACTION_EXIT_MENUITEM;
1476 break;
1477 case 3:
1478#ifdef FOR_REAL
1479 return ACTION_EXIT_MENUITEM;
1480#else
1481 break;
1482#endif
1483 case 5:
1484 if(!midend_num_presets(me))
1485 return ACTION_EXIT_MENUITEM;
1486 break;
1487 case 6:
1488 if(!midend_which_game(me)->can_configure)
1489 return ACTION_EXIT_MENUITEM;
1490 break;
1491 default:
1492 break;
1493 }
1494 }
1495 return action;
1496}
1497
1498enum plugin_status plugin_start(const void *param)
1499{
1500 (void) param;
1501
1502#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1503 /* boost for init */
1504 rb->cpu_boost(true);
1505#endif
1506
1507#ifdef COMBINED
1508 giant_buffer_len = sizeof(giant_buffer);
1509#else
1510 giant_buffer = rb->plugin_get_buffer(&giant_buffer_len);
1511#endif
1512
1513#ifndef COMBINED
1514 rb->snprintf(save_file_path, sizeof(save_file_path), "%s/sgt-%s.sav", PLUGIN_GAMES_DATA_DIR, thegame.htmlhelp_topic);
1515#endif
1516
1517 rb_atexit(exit_handler);
1518
1519 if(fabs(sqrt(3)/2 - sin(PI/3)) > .01)
1520 rb->splash(HZ, "WARNING: floating-point functions are being weird... report me!");
1521
1522 init_default_settings();
1523
1524 load_success = load_game();
1525
1526#ifndef COMBINED
1527 if(!load_success)
1528 {
1529 /* our main menu expects a ready-to-use midend */
1530 init_for_game(&thegame, -1, false);
1531 }
1532#endif
1533
1534#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1535 /* about to go to menu or button block */
1536 rb->cpu_boost(false);
1537#endif
1538
1539#ifndef COMBINED
1540#define static auto
1541#define const
1542 MENUITEM_STRINGLIST(menu, NULL, mainmenu_cb,
1543 "Resume Game",
1544 "New Game",
1545 "Quick Help",
1546 "Extensive Help",
1547 "Playback Control",
1548 "Game Type",
1549 "Configure Game",
1550 "Quit without Saving",
1551 "Quit");
1552#undef static
1553#undef const
1554
1555 /* HACK ALERT */
1556 char title[32] = { 0 };
1557 rb->snprintf(title, sizeof(title), "%s Menu", midend_which_game(me)->name);
1558 menu__.desc = title;
1559
1560 bool quit = false;
1561 int sel = 0;
1562 while(!quit)
1563 {
1564 switch(rb->do_menu(&menu, &sel, NULL, false))
1565 {
1566 case 0:
1567 clear_and_draw();
1568 goto game_loop;
1569 case 1:
1570 if(!load_success)
1571 {
1572 clear_and_draw();
1573 goto game_loop;
1574 }
1575 quit = true;
1576 break;
1577 case 2:
1578 quick_help();
1579 break;
1580 case 3:
1581 full_help();
1582 break;
1583 case 4:
1584 playback_control(NULL);
1585 break;
1586 case 5:
1587 presets_menu();
1588 break;
1589 case 6:
1590 config_menu();
1591 break;
1592 case 8:
1593 if(load_success)
1594 save_game();
1595 /* fall through */
1596 case 7:
1597 /* we don't care about freeing anything because tlsf will
1598 * be wiped out the next time around */
1599 return PLUGIN_OK;
1600 default:
1601 break;
1602 }
1603 }
1604#else
1605 if(load_success)
1606 goto game_loop;
1607#endif
1608
1609#ifdef COMBINED
1610 int gm = 0;
1611#endif
1612 while(1)
1613 {
1614#ifdef COMBINED
1615 if(rb->set_int("Choose Game", "", UNIT_INT, &gm, NULL, 1, 0, gamecount - 1, formatter))
1616 return PLUGIN_OK;
1617
1618 init_for_game(gamelist[gm], -1, true);
1619#else
1620 init_for_game(&thegame, -1, true);
1621#endif
1622
1623 last_keystate = 0;
1624 accept_input = true;
1625
1626 game_loop:
1627 while(1)
1628 {
1629 want_redraw = true;
1630
1631 draw_title();
1632
1633 int button = process_input(timer_on ? TIMER_INTERVAL : -1);
1634
1635 if(button < 0)
1636 {
1637 rb_unclip(NULL);
1638 deactivate_timer(NULL);
1639
1640 if(titlebar)
1641 {
1642 sfree(titlebar);
1643 titlebar = NULL;
1644 }
1645
1646 if(button == -1)
1647 {
1648 /* new game */
1649 midend_free(me);
1650 break;
1651 }
1652 else if(button == -2)
1653 {
1654 /* quit without saving */
1655 midend_free(me);
1656 sfree(colors);
1657 exit(PLUGIN_OK);
1658 }
1659 else if(button == -3)
1660 {
1661 /* save and quit */
1662 save_game();
1663 midend_free(me);
1664 sfree(colors);
1665 exit(PLUGIN_OK);
1666 }
1667 }
1668
1669 if(button)
1670 midend_process_key(me, 0, 0, button);
1671
1672 if(want_redraw)
1673 midend_redraw(me);
1674
1675 if(timer_on)
1676 timer_cb();
1677
1678 rb->yield();
1679 }
1680 sfree(colors);
1681 }
1682}