diff options
author | Mustapha Senhaji <moos@rockbox.org> | 2009-02-11 16:03:17 +0000 |
---|---|---|
committer | Mustapha Senhaji <moos@rockbox.org> | 2009-02-11 16:03:17 +0000 |
commit | f2d5c3532fd21c04e77aa86732742b578283b697 (patch) | |
tree | 1c55cb7e69ed11391f77187c86ff8c435ed85877 /apps/plugins/goban/goban.c | |
parent | 21f0c9a2829415f52b64cbdf965b01525e78f17a (diff) | |
download | rockbox-f2d5c3532fd21c04e77aa86732742b578283b697.tar.gz rockbox-f2d5c3532fd21c04e77aa86732742b578283b697.zip |
New game plugin by Joshua Simmons FS#7369: Goban plugin, Go/Igo/Weiqi/Baduk game recorder and viewer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19972 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/goban/goban.c')
-rw-r--r-- | apps/plugins/goban/goban.c | 1232 |
1 files changed, 1232 insertions, 0 deletions
diff --git a/apps/plugins/goban/goban.c b/apps/plugins/goban/goban.c new file mode 100644 index 0000000000..e5943c0ba6 --- /dev/null +++ b/apps/plugins/goban/goban.c | |||
@@ -0,0 +1,1232 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com> | ||
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 | #include "plugin.h" | ||
23 | #include "lib/playback_control.h" | ||
24 | #include "lib/configfile.h" | ||
25 | |||
26 | PLUGIN_HEADER | ||
27 | |||
28 | #include "goban.h" | ||
29 | #include "game.h" | ||
30 | #include "board.h" | ||
31 | #include "display.h" | ||
32 | #include "sgf.h" | ||
33 | #include "sgf_storage.h" | ||
34 | #include "types.h" | ||
35 | #include "util.h" | ||
36 | |||
37 | enum play_mode_t play_mode = MODE_PLAY; | ||
38 | |||
39 | #if defined(GBN_BUTTON_NAV_MODE) | ||
40 | |||
41 | #define NAV_MODE_BOARD 0 | ||
42 | #define NAV_MODE_TREE 1 | ||
43 | |||
44 | int nav_mode = NAV_MODE_BOARD; | ||
45 | |||
46 | #endif | ||
47 | |||
48 | #define PARSE_STACK_BUFFER_SIZE (max(MAX_BOARD_SIZE * MAX_BOARD_SIZE * sizeof(unsigned short), 50 * sizeof(int))) | ||
49 | |||
50 | /* used in SGF file parsing and outputting as well as in liberty counting | ||
51 | and capturing/uncapturing */ | ||
52 | struct stack_t parse_stack; | ||
53 | char parse_stack_buffer[PARSE_STACK_BUFFER_SIZE]; | ||
54 | |||
55 | static void global_setup (void); | ||
56 | static void global_cleanup (void); | ||
57 | |||
58 | static bool do_main_menu (void); | ||
59 | static void do_gameinfo_menu (void); | ||
60 | static enum prop_type_t menu_selection_to_prop (int selection); | ||
61 | static void do_context_menu (void); | ||
62 | static void do_options_menu (void); | ||
63 | |||
64 | static bool do_comment_edit (void); | ||
65 | static bool do_zoom (void); | ||
66 | static void set_defaults (void); | ||
67 | |||
68 | bool auto_show_comments = true; | ||
69 | bool disable_shutdown = false; | ||
70 | unsigned int autosave_time = 0; | ||
71 | unsigned int autosave_counter = 0; | ||
72 | |||
73 | #define SETTINGS_VERSION 2 | ||
74 | #define SETTINGS_MIN_VERSION 1 | ||
75 | #define SETTINGS_FILENAME "goban.cfg" | ||
76 | |||
77 | static struct configdata config[] = | ||
78 | { /* INT in MAX_INT_SIZE is intersection, not integer */ | ||
79 | {TYPE_INT, 0, MAX_INT_SIZE, | ||
80 | { .int_p = &saved_circle_size }, | ||
81 | "stone size", NULL}, | ||
82 | {TYPE_BOOL, 0, 1, | ||
83 | { .bool_p = &draw_variations }, | ||
84 | "draw variations", | ||
85 | NULL}, | ||
86 | {TYPE_BOOL, 0, 1, | ||
87 | { .bool_p = &auto_show_comments }, | ||
88 | "auto show comments", | ||
89 | NULL}, | ||
90 | {TYPE_BOOL, 0, 1, | ||
91 | { .bool_p = &disable_shutdown }, | ||
92 | "disable shutdown", | ||
93 | NULL}, | ||
94 | {TYPE_INT, 0, MAX_AUTOSAVE, | ||
95 | { .int_p = &autosave_time }, | ||
96 | "autosave time", | ||
97 | NULL} | ||
98 | }; | ||
99 | |||
100 | static void | ||
101 | set_defaults (void) | ||
102 | { | ||
103 | saved_circle_size = 0; | ||
104 | draw_variations = true; | ||
105 | auto_show_comments = true; | ||
106 | |||
107 | disable_shutdown = false; | ||
108 | autosave_time = 7; | ||
109 | } | ||
110 | |||
111 | static void | ||
112 | komi_formatter (char *dest, size_t size, int menu_item, const char *unknown) | ||
113 | { | ||
114 | (void) unknown; | ||
115 | snprint_fixed (dest, size, menu_item); | ||
116 | } | ||
117 | |||
118 | static void | ||
119 | ruleset_formatter (char *dest, size_t size, int menu_item, const char *unknown) | ||
120 | { | ||
121 | (void) unknown; | ||
122 | rb->snprintf (dest, size, "%s", ruleset_names[menu_item]); | ||
123 | } | ||
124 | |||
125 | static void | ||
126 | autosave_formatter (char *dest, size_t size, int menu_item, const char * | ||
127 | unknown) | ||
128 | { | ||
129 | (void) unknown; | ||
130 | if (menu_item == 0) | ||
131 | { | ||
132 | rb->snprintf (dest, size, "Off"); | ||
133 | } | ||
134 | else | ||
135 | { | ||
136 | rb->snprintf (dest, size, "%d minute%s", menu_item, | ||
137 | menu_item == 1 ? "" : "s"); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void | ||
142 | time_formatter (char *dest, size_t size, int menu_item, const char *unknown) | ||
143 | { | ||
144 | int time_values[4]; /* days hours minutes seconds */ | ||
145 | int min_set, max_set; | ||
146 | int temp; | ||
147 | |||
148 | (void) unknown; | ||
149 | |||
150 | time_values[0] = menu_item / (24 * 60 * 60); | ||
151 | menu_item %= (24 * 60 * 60); | ||
152 | time_values[1] = menu_item / (60 * 60); | ||
153 | menu_item %= (60 * 60); | ||
154 | time_values[2] = menu_item / 60; | ||
155 | time_values[3] = menu_item % 60; | ||
156 | |||
157 | min_set = 500; | ||
158 | max_set = -1; | ||
159 | int i; | ||
160 | for (i = 0; | ||
161 | (unsigned int) i < (sizeof (time_values) / sizeof (time_values[0])); | ||
162 | ++i) | ||
163 | { | ||
164 | if (time_values[i]) | ||
165 | { | ||
166 | if (i < min_set) | ||
167 | { | ||
168 | min_set = i; | ||
169 | } | ||
170 | |||
171 | if (i > max_set) | ||
172 | { | ||
173 | max_set = i; | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | if (max_set == -1) | ||
179 | { | ||
180 | rb->snprintf (dest, size, "0"); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | for (i = min_set; i <= 3; ++i) | ||
185 | { | ||
186 | if (i <= max_set) | ||
187 | { | ||
188 | if (i == 0 || i == 1 || i == min_set) | ||
189 | { | ||
190 | rb->snprintf (dest, size, "%d", time_values[i]); | ||
191 | } | ||
192 | else | ||
193 | { | ||
194 | rb->snprintf (dest, size, "%02d", time_values[i]); | ||
195 | } | ||
196 | temp = rb->strlen (dest); | ||
197 | dest += temp; | ||
198 | size -= temp; | ||
199 | } | ||
200 | else if (i != 3) | ||
201 | { | ||
202 | continue; | ||
203 | } | ||
204 | |||
205 | if (i == 0) /* days */ | ||
206 | { | ||
207 | rb->snprintf (dest, size, " d "); | ||
208 | } | ||
209 | else if (i == 3) /* seconds, print the final units */ | ||
210 | { | ||
211 | if (min_set == 0 || min_set == 1) | ||
212 | { | ||
213 | rb->snprintf (dest, size, " h"); | ||
214 | } | ||
215 | else if (min_set == 2) | ||
216 | { | ||
217 | rb->snprintf (dest, size, " m"); | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | rb->snprintf (dest, size, " s"); | ||
222 | } | ||
223 | } | ||
224 | else if (i != max_set) | ||
225 | { | ||
226 | rb->snprintf (dest, size, ":"); | ||
227 | } | ||
228 | |||
229 | temp = rb->strlen (dest); | ||
230 | dest += temp; | ||
231 | size -= temp; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | enum plugin_status | ||
236 | plugin_start (const void *parameter) | ||
237 | { | ||
238 | int btn; | ||
239 | int temp; | ||
240 | |||
241 | rb->mkdir ("/sgf"); | ||
242 | |||
243 | global_setup (); | ||
244 | |||
245 | #ifdef GBN_TEST | ||
246 | run_tests (); | ||
247 | return 0; | ||
248 | #endif | ||
249 | |||
250 | if (!(parameter && load_game (parameter))) | ||
251 | { | ||
252 | if (parameter) | ||
253 | { | ||
254 | rb->splashf (2 * HZ, "Loading %s failed.", (char *) parameter); | ||
255 | } | ||
256 | |||
257 | if (!load_game (DEFAULT_SAVE)) | ||
258 | { | ||
259 | rb->strcpy (save_file, DEFAULT_SAVE); | ||
260 | |||
261 | if (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0)) | ||
262 | { | ||
263 | return PLUGIN_ERROR; | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | /* game loaded */ | ||
270 | if (rb->strcmp (save_file, DEFAULT_SAVE)) | ||
271 | { | ||
272 | /* delete the scratch file if we loaded a game and it wasn't | ||
273 | * from the scratch file | ||
274 | */ | ||
275 | rb->remove (DEFAULT_SAVE); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | draw_screen_display (); | ||
280 | |||
281 | autosave_counter = 0; | ||
282 | for (;;) | ||
283 | { | ||
284 | btn = rb->button_get_w_tmo (HZ * 30); | ||
285 | |||
286 | if (disable_shutdown) | ||
287 | { | ||
288 | /* tell rockbox we're not idle */ | ||
289 | rb->reset_poweroff_timer (); | ||
290 | } | ||
291 | |||
292 | bool is_idle = false; | ||
293 | |||
294 | switch (btn) | ||
295 | { | ||
296 | |||
297 | #if defined(GBN_BUTTON_NAV_MODE) | ||
298 | case GBN_BUTTON_NAV_MODE: | ||
299 | case GBN_BUTTON_NAV_MODE | BUTTON_REPEAT: | ||
300 | if (nav_mode == NAV_MODE_TREE) | ||
301 | { | ||
302 | nav_mode = NAV_MODE_BOARD; | ||
303 | rb->splash (2 * HZ / 3, "board navigation mode"); | ||
304 | draw_screen_display (); | ||
305 | } | ||
306 | else | ||
307 | { | ||
308 | nav_mode = NAV_MODE_TREE; | ||
309 | rb->splash (2 * HZ / 3, "tree navigation mode"); | ||
310 | draw_screen_display (); | ||
311 | } | ||
312 | break; | ||
313 | #endif | ||
314 | |||
315 | #if defined(GBN_BUTTON_ADVANCE) | ||
316 | case GBN_BUTTON_ADVANCE: | ||
317 | case GBN_BUTTON_ADVANCE | BUTTON_REPEAT: | ||
318 | if (has_more_nodes_sgf ()) | ||
319 | { | ||
320 | if (!redo_node_sgf ()) | ||
321 | { | ||
322 | rb->splash (2 * HZ, "redo failed"); | ||
323 | } | ||
324 | draw_screen_display (); | ||
325 | } | ||
326 | break; | ||
327 | #endif | ||
328 | |||
329 | #if defined(GBN_BUTTON_RETREAT) | ||
330 | case GBN_BUTTON_RETREAT: | ||
331 | case GBN_BUTTON_RETREAT | BUTTON_REPEAT: | ||
332 | if (has_prev_nodes_sgf ()) | ||
333 | { | ||
334 | if (!undo_node_sgf ()) | ||
335 | { | ||
336 | rb->splash (3 * HZ / 2, "Undo Failed"); | ||
337 | } | ||
338 | draw_screen_display (); | ||
339 | } | ||
340 | break; | ||
341 | #endif | ||
342 | |||
343 | case GBN_BUTTON_PLAY: | ||
344 | if (play_mode == MODE_PLAY || play_mode == MODE_FORCE_PLAY) | ||
345 | { | ||
346 | if (!play_move_sgf (cursor_pos, current_player)) | ||
347 | { | ||
348 | rb->splash (HZ / 3, "Illegal Move"); | ||
349 | } | ||
350 | } | ||
351 | else if (play_mode == MODE_ADD_BLACK) | ||
352 | { | ||
353 | if (!add_stone_sgf (cursor_pos, BLACK)) | ||
354 | { | ||
355 | rb->splash (HZ / 3, "Illegal"); | ||
356 | } | ||
357 | } | ||
358 | else if (play_mode == MODE_ADD_WHITE) | ||
359 | { | ||
360 | if (!add_stone_sgf (cursor_pos, WHITE)) | ||
361 | { | ||
362 | rb->splash (HZ / 3, "Illegal"); | ||
363 | } | ||
364 | } | ||
365 | else if (play_mode == MODE_REMOVE) | ||
366 | { | ||
367 | if (!add_stone_sgf (cursor_pos, EMPTY)) | ||
368 | { | ||
369 | rb->splash (HZ / 3, "Illegal"); | ||
370 | } | ||
371 | } | ||
372 | else if (play_mode == MODE_MARK) | ||
373 | { | ||
374 | if (!add_mark_sgf (cursor_pos, PROP_MARK)) | ||
375 | { | ||
376 | rb->splash (HZ / 3, "Couldn't Mark"); | ||
377 | } | ||
378 | } | ||
379 | else if (play_mode == MODE_CIRCLE) | ||
380 | { | ||
381 | if (!add_mark_sgf (cursor_pos, PROP_CIRCLE)) | ||
382 | { | ||
383 | rb->splash (HZ / 3, "Couldn't Mark"); | ||
384 | } | ||
385 | } | ||
386 | else if (play_mode == MODE_SQUARE) | ||
387 | { | ||
388 | if (!add_mark_sgf (cursor_pos, PROP_SQUARE)) | ||
389 | { | ||
390 | rb->splash (HZ / 3, "Couldn't Mark"); | ||
391 | } | ||
392 | } | ||
393 | else if (play_mode == MODE_TRIANGLE) | ||
394 | { | ||
395 | if (!add_mark_sgf (cursor_pos, PROP_TRIANGLE)) | ||
396 | { | ||
397 | rb->splash (HZ / 3, "Couldn't Mark"); | ||
398 | } | ||
399 | } | ||
400 | else if (play_mode == MODE_LABEL) | ||
401 | { | ||
402 | if (!add_mark_sgf (cursor_pos, PROP_LABEL)) | ||
403 | { | ||
404 | rb->splash (HZ / 3, "Couldn't Label"); | ||
405 | } | ||
406 | } | ||
407 | else | ||
408 | { | ||
409 | rb->splash (HZ, "mode not implemented"); | ||
410 | } | ||
411 | |||
412 | draw_screen_display (); | ||
413 | break; | ||
414 | |||
415 | case GBN_BUTTON_RIGHT: | ||
416 | case GBN_BUTTON_RIGHT | BUTTON_REPEAT: | ||
417 | #if defined(GBN_BUTTON_NAV_MODE) | ||
418 | if (nav_mode == NAV_MODE_TREE) | ||
419 | { | ||
420 | if (has_more_nodes_sgf ()) | ||
421 | { | ||
422 | if (!redo_node_sgf ()) | ||
423 | { | ||
424 | rb->splash (2 * HZ, "Redo Failed"); | ||
425 | } | ||
426 | draw_screen_display (); | ||
427 | } | ||
428 | } | ||
429 | else | ||
430 | { | ||
431 | #endif | ||
432 | cursor_pos = WRAP (EAST (cursor_pos)); | ||
433 | draw_screen_display (); | ||
434 | #if defined(GBN_BUTTON_NAV_MODE) | ||
435 | } | ||
436 | #endif | ||
437 | break; | ||
438 | |||
439 | case GBN_BUTTON_LEFT: | ||
440 | case GBN_BUTTON_LEFT | BUTTON_REPEAT: | ||
441 | #if defined(GBN_BUTTON_NAV_MODE) | ||
442 | if (nav_mode == NAV_MODE_TREE) | ||
443 | { | ||
444 | if (has_prev_nodes_sgf ()) | ||
445 | { | ||
446 | if (!undo_node_sgf ()) | ||
447 | { | ||
448 | rb->splash (2 * HZ, "Undo Failed"); | ||
449 | } | ||
450 | draw_screen_display (); | ||
451 | } | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | #endif | ||
456 | cursor_pos = WRAP (WEST (cursor_pos)); | ||
457 | draw_screen_display (); | ||
458 | #if defined(GBN_BUTTON_NAV_MODE) | ||
459 | } | ||
460 | #endif | ||
461 | break; | ||
462 | |||
463 | case GBN_BUTTON_DOWN: | ||
464 | case GBN_BUTTON_DOWN | BUTTON_REPEAT: | ||
465 | cursor_pos = WRAP (SOUTH (cursor_pos)); | ||
466 | draw_screen_display (); | ||
467 | break; | ||
468 | |||
469 | case GBN_BUTTON_UP: | ||
470 | case GBN_BUTTON_UP | BUTTON_REPEAT: | ||
471 | cursor_pos = WRAP (NORTH (cursor_pos)); | ||
472 | draw_screen_display (); | ||
473 | break; | ||
474 | |||
475 | case GBN_BUTTON_MENU: | ||
476 | if (do_main_menu ()) | ||
477 | { | ||
478 | save_game (DEFAULT_SAVE); | ||
479 | |||
480 | global_cleanup (); | ||
481 | return PLUGIN_OK; | ||
482 | } | ||
483 | |||
484 | draw_screen_display (); | ||
485 | break; | ||
486 | |||
487 | #if defined(GBN_BUTTON_CONTEXT) | ||
488 | case GBN_BUTTON_CONTEXT: | ||
489 | do_context_menu (); | ||
490 | draw_screen_display (); | ||
491 | break; | ||
492 | #endif | ||
493 | |||
494 | #if defined(GBN_BUTTON_NEXT_VAR) | ||
495 | case GBN_BUTTON_NEXT_VAR: | ||
496 | case GBN_BUTTON_NEXT_VAR | BUTTON_REPEAT: | ||
497 | if ((temp = next_variation_sgf ()) >= 0) | ||
498 | { | ||
499 | draw_screen_display (); | ||
500 | rb->splashf (2 * HZ / 3, "%d of %d", temp, | ||
501 | num_variations_sgf ()); | ||
502 | draw_screen_display (); | ||
503 | } | ||
504 | else | ||
505 | { | ||
506 | if (num_variations_sgf () > 1) | ||
507 | { | ||
508 | rb->splashf (HZ, "Error %d in next_variation_sgf", temp); | ||
509 | } | ||
510 | draw_screen_display (); | ||
511 | } | ||
512 | break; | ||
513 | #endif | ||
514 | |||
515 | case BUTTON_NONE: | ||
516 | is_idle = true; | ||
517 | default: | ||
518 | if (rb->default_event_handler (btn) == SYS_USB_CONNECTED) | ||
519 | { | ||
520 | return PLUGIN_USB_CONNECTED; | ||
521 | } | ||
522 | break; | ||
523 | }; | ||
524 | |||
525 | if (is_idle && autosave_dirty) | ||
526 | { | ||
527 | ++autosave_counter; | ||
528 | |||
529 | if (autosave_time != 0 && | ||
530 | autosave_counter / 2 >= autosave_time) | ||
531 | /* counter is in 30 second increments, autosave_time is in | ||
532 | * minutes | ||
533 | */ | ||
534 | { | ||
535 | DEBUGF("autosaving\n"); | ||
536 | rb->splash(HZ / 4, "Autosaving..."); | ||
537 | save_game(DEFAULT_SAVE); | ||
538 | draw_screen_display(); | ||
539 | autosave_counter = 0; | ||
540 | } | ||
541 | } | ||
542 | else | ||
543 | { | ||
544 | autosave_counter = 0; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | return PLUGIN_OK; | ||
549 | } | ||
550 | |||
551 | static void | ||
552 | global_cleanup (void) | ||
553 | { | ||
554 | cleanup_sgf (); | ||
555 | configfile_save(SETTINGS_FILENAME, config, | ||
556 | sizeof(config)/sizeof(*config), | ||
557 | SETTINGS_VERSION); | ||
558 | } | ||
559 | |||
560 | static void | ||
561 | global_setup (void) | ||
562 | { | ||
563 | setup_stack (&parse_stack, parse_stack_buffer, | ||
564 | sizeof (parse_stack_buffer)); | ||
565 | setup_display (); | ||
566 | setup_sgf (); | ||
567 | |||
568 | set_defaults(); | ||
569 | if (configfile_load(SETTINGS_FILENAME, config, | ||
570 | sizeof(config)/sizeof(*config), | ||
571 | SETTINGS_MIN_VERSION ) < 0) | ||
572 | { | ||
573 | /* If the loading failed, save a new config file (as the disk is | ||
574 | already spinning) */ | ||
575 | |||
576 | /* set defaults again just in case (don't know if they can ever | ||
577 | * be messed up by configfile_load, and it's basically free anyway) | ||
578 | */ | ||
579 | set_defaults(); | ||
580 | |||
581 | configfile_save(SETTINGS_FILENAME, config, | ||
582 | sizeof(config)/sizeof(*config), | ||
583 | SETTINGS_VERSION); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | enum main_menu_selections | ||
588 | { | ||
589 | MAIN_NEW = 0, | ||
590 | MAIN_SAVE, | ||
591 | MAIN_SAVE_AS, | ||
592 | MAIN_GAME_INFO, | ||
593 | MAIN_PLAYBACK, | ||
594 | MAIN_ZOOM, | ||
595 | MAIN_OPTIONS, | ||
596 | MAIN_CONTEXT, | ||
597 | MAIN_QUIT | ||
598 | }; | ||
599 | |||
600 | static bool | ||
601 | do_main_menu (void) | ||
602 | { | ||
603 | int selection = 0; | ||
604 | MENUITEM_STRINGLIST (menu, "Rockbox Goban", NULL, | ||
605 | "New", | ||
606 | "Save", | ||
607 | "Save As", | ||
608 | "Game Info", | ||
609 | "Playback Control", | ||
610 | "Zoom Level", | ||
611 | "Options", | ||
612 | "Context Menu", | ||
613 | "Quit"); | ||
614 | |||
615 | /* for "New" in menu */ | ||
616 | int new_handi = 0, new_bs = MAX_BOARD_SIZE, new_komi = 15; | ||
617 | |||
618 | char new_save_file[SAVE_FILE_LENGTH]; | ||
619 | |||
620 | |||
621 | bool done = false; | ||
622 | |||
623 | while (!done) | ||
624 | { | ||
625 | selection = rb->do_menu (&menu, &selection, NULL, false); | ||
626 | |||
627 | switch (selection) | ||
628 | { | ||
629 | case MAIN_NEW: | ||
630 | rb->set_int ("board size", "lines", UNIT_INT, | ||
631 | &new_bs, NULL, 1, MIN_BOARD_SIZE, MAX_BOARD_SIZE, | ||
632 | NULL); | ||
633 | |||
634 | rb->set_int ("handicap", "stones", UNIT_INT, | ||
635 | &new_handi, NULL, 1, 0, 9, NULL); | ||
636 | |||
637 | if (new_handi > 0) | ||
638 | { | ||
639 | new_komi = 1; | ||
640 | } | ||
641 | else | ||
642 | { | ||
643 | new_komi = 13; | ||
644 | } | ||
645 | |||
646 | rb->set_int ("komi", "moku", UNIT_INT, &new_komi, NULL, | ||
647 | 1, -300, 300, &komi_formatter); | ||
648 | |||
649 | setup_game (new_bs, new_bs, new_handi, new_komi); | ||
650 | draw_screen_display (); | ||
651 | done = true; | ||
652 | break; | ||
653 | |||
654 | case MAIN_SAVE: | ||
655 | if (!save_game (save_file)) | ||
656 | { | ||
657 | rb->splash (2 * HZ, "Save Failed!"); | ||
658 | } | ||
659 | else | ||
660 | { | ||
661 | rb->splash (2 * HZ / 3, "Saved"); | ||
662 | } | ||
663 | done = true; | ||
664 | draw_screen_display (); | ||
665 | break; | ||
666 | |||
667 | case MAIN_SAVE_AS: | ||
668 | rb->strcpy (new_save_file, save_file); | ||
669 | |||
670 | if (!rb->kbd_input (new_save_file, SAVE_FILE_LENGTH)) | ||
671 | { | ||
672 | break; | ||
673 | } | ||
674 | |||
675 | if (!save_game (new_save_file)) | ||
676 | { | ||
677 | rb->splash (2 * HZ, "Save Failed!"); | ||
678 | } | ||
679 | else | ||
680 | { | ||
681 | rb->strcpy (save_file, new_save_file); | ||
682 | rb->splash (2 * HZ / 3, "Saved"); | ||
683 | } | ||
684 | |||
685 | done = true; | ||
686 | draw_screen_display (); | ||
687 | break; | ||
688 | |||
689 | case MAIN_GAME_INFO: | ||
690 | do_gameinfo_menu (); | ||
691 | break; | ||
692 | |||
693 | case MAIN_PLAYBACK: | ||
694 | if (!audio_stolen_sgf ()) | ||
695 | { | ||
696 | playback_control (NULL); | ||
697 | } | ||
698 | else | ||
699 | { | ||
700 | rb->splash (1 * HZ, "Audio has been disabled!"); | ||
701 | } | ||
702 | break; | ||
703 | |||
704 | case MAIN_ZOOM: | ||
705 | if (do_zoom ()) | ||
706 | { | ||
707 | return true; | ||
708 | } | ||
709 | done = true; | ||
710 | draw_screen_display (); | ||
711 | break; | ||
712 | |||
713 | case MAIN_OPTIONS: | ||
714 | do_options_menu(); | ||
715 | break; | ||
716 | |||
717 | case MAIN_CONTEXT: | ||
718 | do_context_menu (); | ||
719 | done = true; | ||
720 | break; | ||
721 | |||
722 | case MAIN_QUIT: | ||
723 | case MENU_ATTACHED_USB: | ||
724 | return true; | ||
725 | |||
726 | case GO_TO_ROOT: | ||
727 | case GO_TO_PREVIOUS: | ||
728 | default: | ||
729 | |||
730 | done = true; | ||
731 | break; | ||
732 | }; | ||
733 | } | ||
734 | |||
735 | return false; | ||
736 | } | ||
737 | |||
738 | void | ||
739 | zoom_preview (int current) | ||
740 | { | ||
741 | set_zoom_display (current); | ||
742 | draw_screen_display (); | ||
743 | rb->splash (0, "Preview"); | ||
744 | } | ||
745 | |||
746 | static bool | ||
747 | do_zoom (void) | ||
748 | { | ||
749 | unsigned int zoom_level; | ||
750 | unsigned int old_val; | ||
751 | bool done = false; | ||
752 | |||
753 | unsigned int min_val = min_zoom_display (); | ||
754 | unsigned int max_val = max_zoom_display (); | ||
755 | |||
756 | zoom_level = old_val = current_zoom_display (); | ||
757 | |||
758 | int action; | ||
759 | |||
760 | zoom_preview (zoom_level); | ||
761 | while (!done) | ||
762 | { | ||
763 | switch (action = rb->get_action (CONTEXT_LIST, TIMEOUT_BLOCK)) | ||
764 | { | ||
765 | case ACTION_STD_OK: | ||
766 | set_zoom_display (zoom_level); | ||
767 | done = true; | ||
768 | rb->splash (HZ / 2, "Zoom Set"); | ||
769 | saved_circle_size = intersection_size; | ||
770 | break; | ||
771 | |||
772 | case ACTION_STD_CANCEL: | ||
773 | set_zoom_display (old_val); | ||
774 | done = true; | ||
775 | rb->splash (HZ / 2, "Cancelled"); | ||
776 | break; | ||
777 | |||
778 | case ACTION_STD_CONTEXT: | ||
779 | zoom_level = old_val; | ||
780 | zoom_preview (zoom_level); | ||
781 | break; | ||
782 | |||
783 | case ACTION_STD_NEXT: | ||
784 | case ACTION_STD_NEXTREPEAT: | ||
785 | zoom_level = zoom_level * 3 / 2; | ||
786 | |||
787 | /* 1 * 3 / 2 is 1 again... */ | ||
788 | if (zoom_level == 1) | ||
789 | { | ||
790 | ++zoom_level; | ||
791 | } | ||
792 | |||
793 | if (zoom_level > max_val) | ||
794 | { | ||
795 | zoom_level = min_val; | ||
796 | } | ||
797 | |||
798 | zoom_preview (zoom_level); | ||
799 | break; | ||
800 | |||
801 | case ACTION_STD_PREV: | ||
802 | case ACTION_STD_PREVREPEAT: | ||
803 | zoom_level = zoom_level * 2 / 3; | ||
804 | |||
805 | if (zoom_level < min_val) | ||
806 | { | ||
807 | zoom_level = max_val; | ||
808 | } | ||
809 | zoom_preview (zoom_level); | ||
810 | break; | ||
811 | |||
812 | case ACTION_NONE: | ||
813 | break; | ||
814 | |||
815 | default: | ||
816 | if (rb->default_event_handler (action) == SYS_USB_CONNECTED) | ||
817 | { | ||
818 | return true; | ||
819 | } | ||
820 | break; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | return false; | ||
825 | } | ||
826 | |||
827 | enum gameinfo_menu_selections | ||
828 | { | ||
829 | GINFO_BASIC_INFO = 0, | ||
830 | GINFO_TIME_LIMIT, | ||
831 | GINFO_OVERTIME, | ||
832 | GINFO_RESULT, | ||
833 | GINFO_HANDICAP, | ||
834 | GINFO_KOMI, | ||
835 | GINFO_RULESET, | ||
836 | GINFO_BLACK_PLAYER, | ||
837 | GINFO_BLACK_RANK, | ||
838 | GINFO_BLACK_TEAM, | ||
839 | GINFO_WHITE_PLAYER, | ||
840 | GINFO_WHITE_RANK, | ||
841 | GINFO_WHITE_TEAM, | ||
842 | GINFO_DATE, | ||
843 | GINFO_EVENT, | ||
844 | GINFO_PLACE, | ||
845 | GINFO_ROUND, | ||
846 | GINFO_DONE | ||
847 | }; | ||
848 | |||
849 | |||
850 | static void | ||
851 | do_gameinfo_menu (void) | ||
852 | { | ||
853 | MENUITEM_STRINGLIST (gameinfo_menu, "Game Info", NULL, | ||
854 | "Basic Info", | ||
855 | "Time Limit", | ||
856 | "Overtime", | ||
857 | "Result", | ||
858 | "Handicap", | ||
859 | "Komi", | ||
860 | "Ruleset", | ||
861 | "Black Player", | ||
862 | "Black Rank", | ||
863 | "Black Team", | ||
864 | "White Player", | ||
865 | "White Rank", | ||
866 | "White Team", | ||
867 | "Date", | ||
868 | "Event", | ||
869 | "Place", | ||
870 | "Round", | ||
871 | "Done"); | ||
872 | /* IMPORTANT: | ||
873 | * | ||
874 | * if you edit this string list, make sure you keep | ||
875 | * menu_selection_to_prop function in line with it!! (see the bottom | ||
876 | * of this file). | ||
877 | */ | ||
878 | |||
879 | int new_ruleset = 0; | ||
880 | |||
881 | char *gameinfo_string; | ||
882 | int gameinfo_string_size; | ||
883 | |||
884 | bool done = false; | ||
885 | int selection = 0; | ||
886 | |||
887 | while (!done) | ||
888 | { | ||
889 | selection = rb->do_menu (&gameinfo_menu, &selection, NULL, false); | ||
890 | |||
891 | switch (selection) | ||
892 | { | ||
893 | case GINFO_OVERTIME: | ||
894 | case GINFO_RESULT: | ||
895 | case GINFO_BLACK_PLAYER: | ||
896 | case GINFO_BLACK_RANK: | ||
897 | case GINFO_BLACK_TEAM: | ||
898 | case GINFO_WHITE_PLAYER: | ||
899 | case GINFO_WHITE_RANK: | ||
900 | case GINFO_WHITE_TEAM: | ||
901 | case GINFO_DATE: | ||
902 | case GINFO_EVENT: | ||
903 | case GINFO_PLACE: | ||
904 | case GINFO_ROUND: | ||
905 | if (!get_header_string_and_size (&header, | ||
906 | menu_selection_to_prop | ||
907 | (selection), &gameinfo_string, | ||
908 | &gameinfo_string_size)) | ||
909 | { | ||
910 | rb->splash (3 * HZ, "Couldn't get header string"); | ||
911 | break; | ||
912 | } | ||
913 | |||
914 | rb->kbd_input (gameinfo_string, gameinfo_string_size); | ||
915 | sanitize_string (gameinfo_string); | ||
916 | set_game_modified(); | ||
917 | break; | ||
918 | |||
919 | /* these need special handling in some way, so they are | ||
920 | separate */ | ||
921 | |||
922 | case GINFO_BASIC_INFO: | ||
923 | metadata_summary(); | ||
924 | break; | ||
925 | |||
926 | case GINFO_TIME_LIMIT: | ||
927 | rb->set_int ("Time Limit", "", UNIT_INT, &header.time_limit, | ||
928 | NULL, 60, 0, 24 * 60 * 60, &time_formatter); | ||
929 | set_game_modified(); | ||
930 | break; | ||
931 | |||
932 | case GINFO_HANDICAP: | ||
933 | rb->splashf (0, "%d stones. Start a new game to set handicap", | ||
934 | header.handicap); | ||
935 | rb->action_userabort(TIMEOUT_BLOCK); | ||
936 | break; | ||
937 | |||
938 | case GINFO_KOMI: | ||
939 | rb->set_int ("Komi", "moku", UNIT_INT, &header.komi, NULL, | ||
940 | 1, -300, 300, &komi_formatter); | ||
941 | set_game_modified(); | ||
942 | break; | ||
943 | |||
944 | case GINFO_RULESET: | ||
945 | new_ruleset = 0; | ||
946 | rb->set_int ("Ruleset", "", UNIT_INT, &new_ruleset, NULL, | ||
947 | 1, 0, NUM_RULESETS - 1, &ruleset_formatter); | ||
948 | |||
949 | rb->strcpy (header.ruleset, ruleset_names[new_ruleset]); | ||
950 | set_game_modified(); | ||
951 | break; | ||
952 | |||
953 | case GINFO_DONE: | ||
954 | case GO_TO_ROOT: | ||
955 | case GO_TO_PREVIOUS: | ||
956 | case MENU_ATTACHED_USB: | ||
957 | default: | ||
958 | done = true; | ||
959 | break; | ||
960 | }; | ||
961 | } | ||
962 | } | ||
963 | |||
964 | enum context_menu_selections | ||
965 | { | ||
966 | CTX_PLAY = 0, | ||
967 | CTX_ADD_BLACK, | ||
968 | CTX_ADD_WHITE, | ||
969 | CTX_ERASE, | ||
970 | CTX_PASS, | ||
971 | CTX_NEXT_VAR, | ||
972 | CTX_FORCE, | ||
973 | CTX_MARK, | ||
974 | CTX_CIRCLE, | ||
975 | CTX_SQUARE, | ||
976 | CTX_TRIANGLE, | ||
977 | CTX_LABEL, | ||
978 | CTX_COMMENT, | ||
979 | CTX_DONE | ||
980 | }; | ||
981 | |||
982 | static void | ||
983 | do_context_menu (void) | ||
984 | { | ||
985 | int selection; | ||
986 | bool done = false; | ||
987 | int temp; | ||
988 | |||
989 | MENUITEM_STRINGLIST (context_menu, "Context Menu", NULL, | ||
990 | "Play Mode (default)", | ||
991 | "Add Black Mode", | ||
992 | "Add White Mode", | ||
993 | "Erase Stone Mode", | ||
994 | "Pass", | ||
995 | "Next Variation", | ||
996 | "Force Play Mode", | ||
997 | "Mark Mode", | ||
998 | "Circle Mode", | ||
999 | "Square Mode", | ||
1000 | "Triangle Mode", | ||
1001 | "Label Mode", | ||
1002 | "Add/Edit Comment", | ||
1003 | "Done"); | ||
1004 | |||
1005 | while (!done) | ||
1006 | { | ||
1007 | selection = rb->do_menu (&context_menu, &selection, NULL, false); | ||
1008 | |||
1009 | switch (selection) | ||
1010 | { | ||
1011 | case CTX_PLAY: | ||
1012 | play_mode = MODE_PLAY; | ||
1013 | done = true; | ||
1014 | break; | ||
1015 | |||
1016 | case CTX_ADD_BLACK: | ||
1017 | play_mode = MODE_ADD_BLACK; | ||
1018 | done = true; | ||
1019 | break; | ||
1020 | |||
1021 | case CTX_ADD_WHITE: | ||
1022 | play_mode = MODE_ADD_WHITE; | ||
1023 | done = true; | ||
1024 | break; | ||
1025 | |||
1026 | case CTX_ERASE: | ||
1027 | play_mode = MODE_REMOVE; | ||
1028 | done = true; | ||
1029 | break; | ||
1030 | |||
1031 | case CTX_PASS: | ||
1032 | if (!play_move_sgf (PASS_POS, current_player)) | ||
1033 | { | ||
1034 | rb->splash (HZ, "Error while passing!"); | ||
1035 | } | ||
1036 | done = true; | ||
1037 | break; | ||
1038 | |||
1039 | case CTX_NEXT_VAR: | ||
1040 | if ((temp = next_variation_sgf ()) >= 0) | ||
1041 | { | ||
1042 | draw_screen_display (); | ||
1043 | rb->splashf (2 * HZ / 3, "%d of %d", temp, | ||
1044 | num_variations_sgf ()); | ||
1045 | draw_screen_display (); | ||
1046 | } | ||
1047 | else | ||
1048 | { | ||
1049 | if (num_variations_sgf () > 1) | ||
1050 | { | ||
1051 | rb->splashf (HZ, "Error %d in next_variation_sgf", temp); | ||
1052 | } | ||
1053 | else | ||
1054 | { | ||
1055 | rb->splash (HZ, "No next variation"); | ||
1056 | } | ||
1057 | } | ||
1058 | break; | ||
1059 | |||
1060 | case CTX_FORCE: | ||
1061 | play_mode = MODE_FORCE_PLAY; | ||
1062 | done = true; | ||
1063 | break; | ||
1064 | |||
1065 | case CTX_MARK: | ||
1066 | play_mode = MODE_MARK; | ||
1067 | done = true; | ||
1068 | break; | ||
1069 | |||
1070 | case CTX_CIRCLE: | ||
1071 | play_mode = MODE_CIRCLE; | ||
1072 | done = true; | ||
1073 | break; | ||
1074 | |||
1075 | case CTX_SQUARE: | ||
1076 | play_mode = MODE_SQUARE; | ||
1077 | done = true; | ||
1078 | break; | ||
1079 | |||
1080 | case CTX_TRIANGLE: | ||
1081 | play_mode = MODE_TRIANGLE; | ||
1082 | done = true; | ||
1083 | break; | ||
1084 | |||
1085 | case CTX_LABEL: | ||
1086 | play_mode = MODE_LABEL; | ||
1087 | done = true; | ||
1088 | break; | ||
1089 | |||
1090 | case CTX_COMMENT: | ||
1091 | if (!do_comment_edit ()) | ||
1092 | { | ||
1093 | DEBUGF ("Editing comment failed\n"); | ||
1094 | rb->splash (HZ, "Read or write failed!\n"); | ||
1095 | } | ||
1096 | done = true; | ||
1097 | break; | ||
1098 | |||
1099 | case CTX_DONE: | ||
1100 | case GO_TO_ROOT: | ||
1101 | case GO_TO_PREVIOUS: | ||
1102 | case MENU_ATTACHED_USB: | ||
1103 | default: | ||
1104 | done = true; | ||
1105 | break; | ||
1106 | }; | ||
1107 | } | ||
1108 | } | ||
1109 | |||
1110 | enum options_menu_selections | ||
1111 | { | ||
1112 | OMENU_SHOW_VARIATIONS = 0, | ||
1113 | OMENU_DISABLE_POWEROFF, | ||
1114 | OMENU_AUTOSAVE_TIME, | ||
1115 | OMENU_AUTO_COMMENT | ||
1116 | }; | ||
1117 | |||
1118 | static void | ||
1119 | do_options_menu (void) | ||
1120 | { | ||
1121 | int selection; | ||
1122 | bool done = false; | ||
1123 | |||
1124 | MENUITEM_STRINGLIST (options_menu, "Options Menu", NULL, | ||
1125 | "Show Child Variations?", | ||
1126 | "Disable Idle Poweroff?", | ||
1127 | "Idle Autosave Time", | ||
1128 | "Automatically Show Comments?"); | ||
1129 | |||
1130 | while (!done) | ||
1131 | { | ||
1132 | selection = rb->do_menu (&options_menu, &selection, NULL, false); | ||
1133 | |||
1134 | switch (selection) | ||
1135 | { | ||
1136 | case OMENU_SHOW_VARIATIONS: | ||
1137 | rb->set_bool("Draw Variations?", &draw_variations); | ||
1138 | clear_marks_display (); | ||
1139 | set_all_marks_sgf (); | ||
1140 | if (draw_variations) | ||
1141 | { | ||
1142 | mark_child_variations_sgf (); | ||
1143 | } | ||
1144 | break; | ||
1145 | |||
1146 | case OMENU_DISABLE_POWEROFF: | ||
1147 | rb->set_bool("Disable Idle Poweroff?", &disable_shutdown); | ||
1148 | break; | ||
1149 | |||
1150 | case OMENU_AUTOSAVE_TIME: | ||
1151 | rb->set_int("Idle Autosave Time", "minutes", UNIT_INT, | ||
1152 | &autosave_time, NULL, 1, 0, MAX_AUTOSAVE, | ||
1153 | &autosave_formatter); | ||
1154 | autosave_counter = 0; | ||
1155 | break; | ||
1156 | |||
1157 | case OMENU_AUTO_COMMENT: | ||
1158 | rb->set_bool("Auto Show Comments?", &auto_show_comments); | ||
1159 | break; | ||
1160 | |||
1161 | case GO_TO_ROOT: | ||
1162 | case GO_TO_PREVIOUS: | ||
1163 | case MENU_ATTACHED_USB: | ||
1164 | default: | ||
1165 | done = true; | ||
1166 | break; | ||
1167 | }; | ||
1168 | } | ||
1169 | |||
1170 | } | ||
1171 | |||
1172 | static bool | ||
1173 | do_comment_edit (void) | ||
1174 | { | ||
1175 | char cbuffer[512]; | ||
1176 | |||
1177 | rb->memset (cbuffer, 0, sizeof (cbuffer)); | ||
1178 | |||
1179 | if (read_comment_sgf (cbuffer, sizeof (cbuffer)) < 0) | ||
1180 | { | ||
1181 | return false; | ||
1182 | } | ||
1183 | |||
1184 | if (!rb->kbd_input (cbuffer, sizeof (cbuffer))) | ||
1185 | { | ||
1186 | /* user didn't edit, no reason to write it back */ | ||
1187 | return true; | ||
1188 | } | ||
1189 | |||
1190 | if (write_comment_sgf (cbuffer) < 0) | ||
1191 | { | ||
1192 | return false; | ||
1193 | } | ||
1194 | |||
1195 | return true; | ||
1196 | } | ||
1197 | |||
1198 | static enum prop_type_t | ||
1199 | menu_selection_to_prop (int selection) | ||
1200 | { | ||
1201 | switch (selection) | ||
1202 | { | ||
1203 | case GINFO_OVERTIME: | ||
1204 | return PROP_OVERTIME; | ||
1205 | case GINFO_RESULT: | ||
1206 | return PROP_RESULT; | ||
1207 | case GINFO_BLACK_PLAYER: | ||
1208 | return PROP_BLACK_NAME; | ||
1209 | case GINFO_BLACK_RANK: | ||
1210 | return PROP_BLACK_RANK; | ||
1211 | case GINFO_BLACK_TEAM: | ||
1212 | return PROP_BLACK_TEAM; | ||
1213 | case GINFO_WHITE_PLAYER: | ||
1214 | return PROP_WHITE_NAME; | ||
1215 | case GINFO_WHITE_RANK: | ||
1216 | return PROP_WHITE_RANK; | ||
1217 | case GINFO_WHITE_TEAM: | ||
1218 | return PROP_WHITE_TEAM; | ||
1219 | case GINFO_DATE: | ||
1220 | return PROP_DATE; | ||
1221 | case GINFO_EVENT: | ||
1222 | return PROP_EVENT; | ||
1223 | case GINFO_PLACE: | ||
1224 | return PROP_PLACE; | ||
1225 | case GINFO_ROUND: | ||
1226 | return PROP_ROUND; | ||
1227 | default: | ||
1228 | DEBUGF ("Tried to get prop from invalid menu selection!!!\n"); | ||
1229 | return PROP_PLACE; /* just pick one, there's a major bug if | ||
1230 | we got here */ | ||
1231 | }; | ||
1232 | } | ||