Sansa clip+: do not set GPIO B7 in the display driver, it's already used for FM radio I2C
[kugel-rb.git] / apps / plugins / sliding_puzzle.c
blobaeac96dfba9976139ea63aa6f7c9171b4a92106a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Vicentini Martin
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
23 /* variable button definitions */
24 #if CONFIG_KEYPAD == RECORDER_PAD
25 #define PUZZLE_QUIT BUTTON_OFF
26 #define PUZZLE_LEFT BUTTON_LEFT
27 #define PUZZLE_RIGHT BUTTON_RIGHT
28 #define PUZZLE_UP BUTTON_UP
29 #define PUZZLE_DOWN BUTTON_DOWN
30 #define PUZZLE_SHUFFLE BUTTON_F1
31 #define PUZZLE_PICTURE BUTTON_F2
33 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
34 #define PUZZLE_QUIT BUTTON_OFF
35 #define PUZZLE_LEFT BUTTON_LEFT
36 #define PUZZLE_RIGHT BUTTON_RIGHT
37 #define PUZZLE_UP BUTTON_UP
38 #define PUZZLE_DOWN BUTTON_DOWN
39 #define PUZZLE_SHUFFLE BUTTON_F1
40 #define PUZZLE_PICTURE BUTTON_F2
42 #elif CONFIG_KEYPAD == ONDIO_PAD
43 #define PUZZLE_QUIT BUTTON_OFF
44 #define PUZZLE_LEFT BUTTON_LEFT
45 #define PUZZLE_RIGHT BUTTON_RIGHT
46 #define PUZZLE_UP BUTTON_UP
47 #define PUZZLE_DOWN BUTTON_DOWN
48 #define PUZZLE_SHUFFLE_PICTURE_PRE BUTTON_MENU
49 #define PUZZLE_SHUFFLE (BUTTON_MENU | BUTTON_REPEAT)
50 #define PUZZLE_PICTURE (BUTTON_MENU | BUTTON_REL)
52 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
53 (CONFIG_KEYPAD == IRIVER_H300_PAD)
54 #define PUZZLE_QUIT BUTTON_OFF
55 #define PUZZLE_LEFT BUTTON_LEFT
56 #define PUZZLE_RIGHT BUTTON_RIGHT
57 #define PUZZLE_UP BUTTON_UP
58 #define PUZZLE_DOWN BUTTON_DOWN
59 #define PUZZLE_SHUFFLE BUTTON_SELECT
60 #define PUZZLE_PICTURE BUTTON_ON
62 #define PUZZLE_RC_QUIT BUTTON_RC_STOP
64 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
65 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
66 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
67 #define PUZZLE_QUIT (BUTTON_SELECT | BUTTON_MENU)
68 #define PUZZLE_LEFT BUTTON_LEFT
69 #define PUZZLE_RIGHT BUTTON_RIGHT
70 #define PUZZLE_UP BUTTON_MENU
71 #define PUZZLE_DOWN BUTTON_PLAY
72 #define PUZZLE_SHUFFLE (BUTTON_SELECT | BUTTON_LEFT)
73 #define PUZZLE_PICTURE (BUTTON_SELECT | BUTTON_RIGHT)
75 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
76 #define PUZZLE_QUIT BUTTON_POWER
77 #define PUZZLE_LEFT BUTTON_LEFT
78 #define PUZZLE_RIGHT BUTTON_RIGHT
79 #define PUZZLE_UP BUTTON_UP
80 #define PUZZLE_DOWN BUTTON_DOWN
81 #define PUZZLE_SHUFFLE BUTTON_REC
82 #define PUZZLE_PICTURE BUTTON_PLAY
84 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
85 #define PUZZLE_QUIT BUTTON_POWER
86 #define PUZZLE_LEFT BUTTON_LEFT
87 #define PUZZLE_RIGHT BUTTON_RIGHT
88 #define PUZZLE_UP BUTTON_UP
89 #define PUZZLE_DOWN BUTTON_DOWN
90 #define PUZZLE_SHUFFLE BUTTON_SELECT
91 #define PUZZLE_PICTURE BUTTON_A
93 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
94 (CONFIG_KEYPAD == SANSA_C200_PAD)
95 #define PUZZLE_QUIT BUTTON_POWER
96 #define PUZZLE_LEFT BUTTON_LEFT
97 #define PUZZLE_RIGHT BUTTON_RIGHT
98 #define PUZZLE_UP BUTTON_UP
99 #define PUZZLE_DOWN BUTTON_DOWN
100 #define PUZZLE_SHUFFLE BUTTON_REC
101 #define PUZZLE_PICTURE BUTTON_SELECT
103 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
104 #define PUZZLE_QUIT (BUTTON_HOME|BUTTON_REPEAT)
105 #define PUZZLE_LEFT BUTTON_LEFT
106 #define PUZZLE_RIGHT BUTTON_RIGHT
107 #define PUZZLE_UP BUTTON_UP
108 #define PUZZLE_DOWN BUTTON_DOWN
109 #define PUZZLE_SHUFFLE BUTTON_SELECT|BUTTON_DOWN
110 #define PUZZLE_PICTURE BUTTON_SELECT
112 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
113 #define PUZZLE_QUIT BUTTON_POWER
114 #define PUZZLE_LEFT BUTTON_LEFT
115 #define PUZZLE_RIGHT BUTTON_RIGHT
116 #define PUZZLE_UP BUTTON_UP
117 #define PUZZLE_DOWN BUTTON_DOWN
118 #define PUZZLE_SHUFFLE BUTTON_HOME
119 #define PUZZLE_PICTURE BUTTON_SELECT
121 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
122 #define PUZZLE_QUIT BUTTON_POWER
123 #define PUZZLE_LEFT BUTTON_LEFT
124 #define PUZZLE_RIGHT BUTTON_RIGHT
125 #define PUZZLE_UP BUTTON_UP
126 #define PUZZLE_DOWN BUTTON_DOWN
127 #define PUZZLE_SHUFFLE (BUTTON_SELECT | BUTTON_UP)
128 #define PUZZLE_PICTURE (BUTTON_SELECT | BUTTON_REL)
130 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
131 #define PUZZLE_QUIT BUTTON_POWER
132 #define PUZZLE_LEFT BUTTON_LEFT
133 #define PUZZLE_RIGHT BUTTON_RIGHT
134 #define PUZZLE_UP BUTTON_SCROLL_UP
135 #define PUZZLE_DOWN BUTTON_SCROLL_DOWN
136 #define PUZZLE_SHUFFLE BUTTON_REW
137 #define PUZZLE_PICTURE BUTTON_PLAY
139 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
140 #define PUZZLE_QUIT BUTTON_BACK
141 #define PUZZLE_LEFT BUTTON_LEFT
142 #define PUZZLE_RIGHT BUTTON_RIGHT
143 #define PUZZLE_UP BUTTON_UP
144 #define PUZZLE_DOWN BUTTON_DOWN
145 #define PUZZLE_SHUFFLE BUTTON_SELECT
146 #define PUZZLE_PICTURE BUTTON_MENU
148 #elif (CONFIG_KEYPAD == MROBE100_PAD)
149 #define PUZZLE_QUIT BUTTON_POWER
150 #define PUZZLE_LEFT BUTTON_LEFT
151 #define PUZZLE_RIGHT BUTTON_RIGHT
152 #define PUZZLE_UP BUTTON_UP
153 #define PUZZLE_DOWN BUTTON_DOWN
154 #define PUZZLE_SHUFFLE BUTTON_SELECT
155 #define PUZZLE_PICTURE BUTTON_DISPLAY
157 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
158 #define PUZZLE_QUIT BUTTON_RC_REC
159 #define PUZZLE_LEFT BUTTON_RC_REW
160 #define PUZZLE_RIGHT BUTTON_RC_FF
161 #define PUZZLE_UP BUTTON_RC_VOL_UP
162 #define PUZZLE_DOWN BUTTON_RC_VOL_DOWN
163 #define PUZZLE_SHUFFLE BUTTON_RC_MODE
164 #define PUZZLE_PICTURE BUTTON_RC_MENU
166 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
167 #define PUZZLE_QUIT BUTTON_POWER
168 #define PUZZLE_QUIT_TEXT "[POWER]"
170 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
171 #define PUZZLE_QUIT BUTTON_BACK
172 #define PUZZLE_LEFT BUTTON_LEFT
173 #define PUZZLE_RIGHT BUTTON_RIGHT
174 #define PUZZLE_UP BUTTON_UP
175 #define PUZZLE_DOWN BUTTON_DOWN
176 #define PUZZLE_SHUFFLE BUTTON_PLAY
177 #define PUZZLE_PICTURE BUTTON_MENU
179 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
180 #define PUZZLE_QUIT BUTTON_POWER
181 #define PUZZLE_LEFT BUTTON_LEFT
182 #define PUZZLE_RIGHT BUTTON_RIGHT
183 #define PUZZLE_UP BUTTON_UP
184 #define PUZZLE_DOWN BUTTON_DOWN
185 #define PUZZLE_SHUFFLE BUTTON_VIEW
186 #define PUZZLE_PICTURE BUTTON_MENU
188 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
189 #define PUZZLE_QUIT BUTTON_POWER
190 #define PUZZLE_LEFT BUTTON_LEFT
191 #define PUZZLE_RIGHT BUTTON_RIGHT
192 #define PUZZLE_UP BUTTON_UP
193 #define PUZZLE_DOWN BUTTON_DOWN
194 #define PUZZLE_SHUFFLE BUTTON_NEXT
195 #define PUZZLE_PICTURE BUTTON_MENU
197 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
198 #define PUZZLE_QUIT BUTTON_POWER
199 #define PUZZLE_LEFT BUTTON_PREV
200 #define PUZZLE_RIGHT BUTTON_NEXT
201 #define PUZZLE_UP BUTTON_UP
202 #define PUZZLE_DOWN BUTTON_DOWN
203 #define PUZZLE_SHUFFLE BUTTON_RIGHT
204 #define PUZZLE_PICTURE BUTTON_MENU
206 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
207 CONFIG_KEYPAD == ONDAVX777_PAD || \
208 CONFIG_KEYPAD == MROBE500_PAD
209 #define PUZZLE_QUIT BUTTON_POWER
210 #define PUZZLE_QUIT_TEXT "[POWER]"
212 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
213 #define PUZZLE_QUIT BUTTON_REC
214 #define PUZZLE_LEFT BUTTON_LEFT
215 #define PUZZLE_RIGHT BUTTON_RIGHT
216 #define PUZZLE_UP BUTTON_UP
217 #define PUZZLE_DOWN BUTTON_DOWN
218 #define PUZZLE_SHUFFLE BUTTON_REW
219 #define PUZZLE_PICTURE BUTTON_PLAY
221 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
222 #define PUZZLE_QUIT BUTTON_REC
223 #define PUZZLE_LEFT BUTTON_PREV
224 #define PUZZLE_RIGHT BUTTON_NEXT
225 #define PUZZLE_UP BUTTON_UP
226 #define PUZZLE_DOWN BUTTON_DOWN
227 #define PUZZLE_SHUFFLE BUTTON_CANCEL
228 #define PUZZLE_PICTURE BUTTON_MENU
230 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
231 #define PUZZLE_QUIT (BUTTON_REC | BUTTON_PLAY)
232 #define PUZZLE_LEFT BUTTON_VOL_DOWN
233 #define PUZZLE_RIGHT BUTTON_VOL_UP
234 #define PUZZLE_UP BUTTON_REW
235 #define PUZZLE_DOWN BUTTON_FF
236 #define PUZZLE_SHUFFLE BUTTON_REC
237 #define PUZZLE_PICTURE BUTTON_PLAY
239 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
240 #define PUZZLE_QUIT (BUTTON_REC | BUTTON_REPEAT)
241 #define PUZZLE_LEFT BUTTON_REW
242 #define PUZZLE_RIGHT BUTTON_FF
243 #define PUZZLE_UP BUTTON_UP
244 #define PUZZLE_DOWN BUTTON_DOWN
245 #define PUZZLE_SHUFFLE BUTTON_ENTER
246 #define PUZZLE_PICTURE BUTTON_PLAY
248 #else
249 #error No keymap defined!
250 #endif
252 #ifdef HAVE_TOUCHSCREEN
253 #ifndef PUZZLE_QUIT
254 #define PUZZLE_QUIT BUTTON_TOPLEFT
255 #endif
256 #ifndef PUZZLE_LEFT
257 #define PUZZLE_LEFT BUTTON_MIDLEFT
258 #endif
259 #ifndef PUZZLE_RIGHT
260 #define PUZZLE_RIGHT BUTTON_MIDRIGHT
261 #endif
262 #ifndef PUZZLE_UP
263 #define PUZZLE_UP BUTTON_TOPMIDDLE
264 #endif
265 #ifndef PUZZLE_DOWN
266 #define PUZZLE_DOWN BUTTON_BOTTOMMIDDLE
267 #endif
268 #ifndef PUZZLE_SHUFFLE
269 #define PUZZLE_SHUFFLE BUTTON_BOTTOMLEFT
270 #endif
271 #ifndef PUZZLE_PICTURE
272 #define PUZZLE_PICTURE BUTTON_CENTER
273 #endif
274 #ifndef PUZZLE_QUIT_TEXT
275 #define PUZZLE_QUIT_TEXT "[TOPLEFT]"
276 #endif
277 #ifndef PUZZLE_SHUFFLE_TEXT
278 #define PUZZLE_SHUFFLE_TEXT "[BOTTOMLEFT]"
279 #endif
280 #ifndef PUZZLE_PICTURE_TEXT
281 #define PUZZLE_PICTURE_TEXT "[CENTER]"
282 #endif
283 #endif
285 #ifdef HAVE_ALBUMART
286 #include "lib/read_image.h"
287 #define READ_IMAGE read_image_file
288 #else
289 #define READ_IMAGE rb->read_bmp_file
290 #endif
292 #include "pluginbitmaps/sliding_puzzle.h"
293 #define IMAGE_WIDTH BMPWIDTH_sliding_puzzle
294 #define IMAGE_HEIGHT BMPHEIGHT_sliding_puzzle
295 #define IMAGE_SIZE IMAGE_WIDTH
297 /* use a square image, (the default Archos bitmap looks square on its display)
298 Puzzle image dimension is min(lcd_height,lcd_width)
299 4x4 is more convenient for square puzzles
300 Note: sliding_puzzle.bmp should be evenly divisible by SPOTS_X
301 and SPOTS_Y, otherwise lcd_bitmap_part stride won't be correct */
302 #define SPOTS_X 4
303 #define SPOTS_Y 4
304 #define SPOTS_WIDTH (IMAGE_WIDTH / SPOTS_X)
305 #define SPOTS_HEIGHT (IMAGE_HEIGHT / SPOTS_Y)
306 #define NUM_SPOTS (SPOTS_X*SPOTS_Y)
307 #define HOLE_ID (NUM_SPOTS)
308 #define INITIAL_HOLE (HOLE_ID-1)
310 enum picmodes
312 PICMODE_NUMERALS = 0,
313 PICMODE_INITIAL_PICTURE,
314 PICMODE_DEFAULT_PICTURE,
315 #ifdef HAVE_ALBUMART
316 PICMODE_ALBUM_ART,
317 #endif
318 // PICMODE_RANDOM,
319 PICMODE_LAST_XXX /* placeholder */
322 static const char* const picmode_descriptions[] = {
323 "Numerals",
324 "Viewer Picture",
325 "Default Picture",
326 #ifdef HAVE_ALBUMART
327 "Album Art",
328 #endif
329 "Shouldn't Get Here",
332 static int spots[NUM_SPOTS];
333 static int hole = INITIAL_HOLE, moves;
334 static unsigned char s[32];
335 static enum picmodes picmode = PICMODE_INITIAL_PICTURE;
336 static int num_font = FONT_UI;
337 static int moves_font = FONT_UI;
338 static int moves_y = 0;
340 static unsigned char *img_buf;
341 static size_t img_buf_len;
342 #ifdef HAVE_ALBUMART
343 static char albumart_path[MAX_PATH+1];
344 #endif
345 static char img_buf_path[MAX_PATH+1];
347 static const fb_data * puzzle_bmp_ptr;
348 /* initial_bmp_path points to selected bitmap if this game is launched
349 as a viewer for a .bmp file, or NULL if game is launched regular way */
350 static const char * initial_bmp_path=NULL;
352 #ifdef HAVE_ALBUMART
353 const char * get_albumart_bmp_path(void)
355 struct mp3entry* track = rb->audio_current_track();
357 if (!track || !track->path || track->path[0] == '\0')
358 return NULL;
360 if (!rb->search_albumart_files(track, "", albumart_path, MAX_PATH ) )
361 return NULL;
363 albumart_path[ MAX_PATH ] = '\0';
364 return albumart_path;
366 #endif
368 const char * get_random_bmp_path(void)
370 return(initial_bmp_path);
373 static bool load_resize_bitmap(void)
375 int rc;
376 const char * filename = NULL;
378 /* initially assume using the built-in default */
379 puzzle_bmp_ptr = sliding_puzzle;
381 switch( picmode ){
382 /* some modes don't even need to touch disk and trivially succeed */
383 case PICMODE_NUMERALS:
384 case PICMODE_DEFAULT_PICTURE:
385 default:
386 return(true);
388 #ifdef HAVE_ALBUMART
389 case PICMODE_ALBUM_ART:
390 filename = get_albumart_bmp_path();
391 break;
392 #endif
394 case PICMODE_RANDOM:
395 if(NULL == (filename=get_random_bmp_path()) )
396 filename = initial_bmp_path;
397 break;
399 case PICMODE_INITIAL_PICTURE:
400 filename = initial_bmp_path;
401 break;
404 if( filename != NULL )
406 /* if we already loaded image before, don't touch disk */
407 if( 0 == rb->strcmp( filename, img_buf_path ) )
409 puzzle_bmp_ptr = (const fb_data *)img_buf;
410 return true;
413 struct bitmap main_bitmap;
414 rb->memset(&main_bitmap,0,sizeof(struct bitmap));
415 main_bitmap.data = img_buf;
417 main_bitmap.width = IMAGE_WIDTH;
418 main_bitmap.height = IMAGE_HEIGHT;
420 rc = READ_IMAGE( filename, &main_bitmap,
421 img_buf_len,
422 FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_DITHER,
423 NULL);
424 if( rc > 0 )
426 puzzle_bmp_ptr = (const fb_data *)img_buf;
427 rb->strcpy( img_buf_path, filename );
428 return true;
432 /* something must have failed. get_albumart_bmp_path could return
433 NULL if albumart doesn't exist or couldn't be loaded, or
434 read_bmp_file could have failed. return false and caller should
435 try the next mode (PICMODE_DEFAULT_PICTURE and PICMODE_NUMERALS will
436 always succeed) */
437 return false;
440 /* draws a spot at the coordinates (x,y), range of p is 1-20 */
441 static void draw_spot(int p, int x, int y)
443 int w, h;
445 if (p == HOLE_ID)
447 #if LCD_DEPTH==1
448 /* the bottom-right cell of the default sliding_puzzle image is
449 an appropriate hole graphic */
450 rb->lcd_bitmap_part(sliding_puzzle, ((p-1)%SPOTS_X)*SPOTS_WIDTH,
451 ((p-1)/SPOTS_X)*SPOTS_HEIGHT,
452 STRIDE( SCREEN_MAIN,
453 BMPWIDTH_sliding_puzzle, BMPHEIGHT_sliding_puzzle),
454 x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
455 #else
456 /* just draw a black rectangle */
457 int old_fg = rb->lcd_get_foreground();
458 rb->lcd_set_foreground(LCD_BLACK);
459 rb->lcd_fillrect(x,y,SPOTS_WIDTH,SPOTS_HEIGHT);
460 rb->lcd_set_foreground(LCD_WHITE);
461 rb->lcd_drawrect(x,y,SPOTS_WIDTH,SPOTS_HEIGHT);
462 rb->lcd_set_foreground(old_fg);
463 #endif
465 else if (picmode != PICMODE_NUMERALS)
467 rb->lcd_bitmap_part( puzzle_bmp_ptr, ((p-1)%SPOTS_X)*SPOTS_WIDTH,
468 ((p-1)/SPOTS_X)*SPOTS_HEIGHT,
469 STRIDE( SCREEN_MAIN,
470 BMPWIDTH_sliding_puzzle, BMPHEIGHT_sliding_puzzle),
471 x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
472 } else {
473 rb->lcd_drawrect(x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
474 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
475 rb->lcd_fillrect(x+1, y+1, SPOTS_WIDTH-2, SPOTS_HEIGHT-2);
476 rb->lcd_set_drawmode(DRMODE_SOLID);
477 rb->snprintf(s, sizeof(s), "%d", p);
478 rb->lcd_setfont(num_font);
479 rb->lcd_getstringsize(s, &w, &h);
480 rb->lcd_putsxy(x + (SPOTS_WIDTH/2) - w / 2,
481 y + (SPOTS_HEIGHT/2) - h / 2, s);
485 /* check if the puzzle is solved */
486 static bool puzzle_finished(void)
488 int i;
489 for (i=0; i<NUM_SPOTS; i++)
490 if (spots[i] != (i+1))
491 return false;
492 return true;
495 /* move a piece in any direction */
496 static void move_spot(int x, int y)
498 int i, w;
499 spots[hole] = spots[hole-x-SPOTS_X*y];
500 hole -= (x+SPOTS_X*y);
501 moves++;
502 rb->lcd_setfont(moves_font);
503 #if LCD_WIDTH > LCD_HEIGHT
504 rb->snprintf(s, sizeof(s), "%d", moves);
505 w = rb->lcd_getstringsize(s, NULL, NULL);
506 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
507 moves_y, s);
508 #else
509 (void)w;
510 rb->lcd_putsxyf(3, moves_y, "Moves: %d", moves);
511 #endif
512 for(i=1;i<=4;i++)
514 draw_spot(HOLE_ID,
515 (hole%SPOTS_X)*SPOTS_WIDTH,
516 (hole/SPOTS_X)*SPOTS_HEIGHT);
517 draw_spot(spots[hole],
518 (hole%SPOTS_X)*SPOTS_WIDTH + (i*x*SPOTS_WIDTH)/5,
519 (hole/SPOTS_X)*SPOTS_HEIGHT + (i*y*SPOTS_HEIGHT)/5);
520 rb->lcd_update();
521 rb->sleep(HZ/50);
523 draw_spot(HOLE_ID,
524 (hole%SPOTS_X)*SPOTS_WIDTH,
525 (hole/SPOTS_X)*SPOTS_HEIGHT);
526 draw_spot(spots[hole],
527 ((hole%SPOTS_X)+x)*SPOTS_WIDTH,
528 ((hole/SPOTS_X)+y)*SPOTS_HEIGHT);
529 rb->lcd_update();
531 spots[hole] = HOLE_ID;
534 static void draw_playfield(void)
536 int i, w;
538 rb->lcd_clear_display();
539 rb->lcd_setfont(moves_font);
540 #if LCD_WIDTH > LCD_HEIGHT
541 rb->lcd_vline(IMAGE_WIDTH, 0, LCD_HEIGHT-1);
542 w = rb->lcd_getstringsize("Moves", NULL, NULL);
543 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
544 10, "Moves");
545 rb->snprintf(s, sizeof(s), "%d", moves);
546 w = rb->lcd_getstringsize(s, NULL, NULL);
547 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
548 moves_y, s);
549 #else
550 (void)w;
551 rb->lcd_hline(0, LCD_WIDTH-1, IMAGE_HEIGHT);
552 rb->lcd_putsxyf(3, moves_y, "Moves: %d", moves);
553 #endif
555 /* draw spots to the lcd */
556 for (i=0; i<NUM_SPOTS; i++)
557 draw_spot(spots[i], (i%SPOTS_X)*SPOTS_WIDTH, (i/SPOTS_X)*SPOTS_HEIGHT);
559 rb->lcd_update();
562 /* initializes the puzzle */
563 static void puzzle_init(void)
565 int i, r, temp, tsp[NUM_SPOTS];
567 moves = 0;
569 /* shuffle spots */
570 for (i=NUM_SPOTS-1; i>=0; i--) {
571 r = (rb->rand() % (i+1));
573 temp = spots[r];
574 spots[r] = spots[i];
575 spots[i] = temp;
577 if (spots[i]==HOLE_ID)
578 hole = i;
581 /* test if the puzzle is solvable */
582 for (i=0; i<NUM_SPOTS; i++)
583 tsp[i] = spots[i];
584 r=0;
586 /* First, check if the problem has even or odd parity,
587 depending on where the empty square is */
588 if ((((SPOTS_X-1)-hole%SPOTS_X) + ((SPOTS_Y-1)-hole/SPOTS_X))%2 == 1)
589 ++r;
591 /* Now check how many swaps we need to solve it */
592 for (i=0; i<NUM_SPOTS-1; i++) {
593 while (tsp[i] != (i+1)) {
594 temp = tsp[i];
595 tsp[i] = tsp[temp-1];
596 tsp[temp-1] = temp;
597 ++r;
601 /* if the random puzzle isn't solvable just change two spots */
602 if (r%2 == 1) {
603 if (spots[0]!=HOLE_ID && spots[1]!=HOLE_ID) {
604 temp = spots[0];
605 spots[0] = spots[1];
606 spots[1] = temp;
607 } else {
608 temp = spots[2];
609 spots[2] = spots[3];
610 spots[3] = temp;
614 draw_playfield();
617 /* the main game loop */
618 static int puzzle_loop(void)
620 int button;
621 int lastbutton = BUTTON_NONE;
622 bool load_success;
624 puzzle_init();
625 while(true) {
626 button = rb->button_get(true);
627 switch (button) {
628 #ifdef PUZZLE_RC_QUIT
629 case PUZZLE_RC_QUIT:
630 #endif
631 case PUZZLE_QUIT:
632 /* get out of here */
633 return PLUGIN_OK;
635 case PUZZLE_SHUFFLE:
636 #ifdef PUZZLE_SHUFFLE_PICTURE_PRE
637 if (lastbutton != PUZZLE_SHUFFLE_PICTURE_PRE)
638 break;
639 #endif
640 /* mix up the pieces */
641 puzzle_init();
642 break;
644 case PUZZLE_PICTURE:
645 #ifdef PUZZLE_SHUFFLE_PICTURE_PRE
646 if (lastbutton != PUZZLE_SHUFFLE_PICTURE_PRE)
647 break;
648 #endif
649 /* change picture */
650 picmode = (picmode+1)%PICMODE_LAST_XXX;
652 /* if load_resize_bitmap fails to load bitmap, try next picmode
656 load_success = load_resize_bitmap();
657 if( !load_success )
658 picmode = (picmode+1)%PICMODE_LAST_XXX;
660 while( !load_success );
662 /* tell the user what mode we picked in the end! */
663 rb->splash(HZ,picmode_descriptions[ picmode ] );
664 draw_playfield();
665 break;
667 case PUZZLE_LEFT:
668 if ((hole%SPOTS_X)<(SPOTS_X-1) && !puzzle_finished())
669 move_spot(-1, 0);
670 break;
672 case PUZZLE_RIGHT:
673 if ((hole%SPOTS_X)>0 && !puzzle_finished())
674 move_spot(1, 0);
675 break;
677 case PUZZLE_UP:
678 if ((hole/SPOTS_X)<(SPOTS_Y-1) && !puzzle_finished())
679 move_spot(0, -1);
680 break;
682 case PUZZLE_DOWN:
683 if ((hole/SPOTS_X)>0 && !puzzle_finished())
684 move_spot(0, 1);
685 break;
687 default:
688 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
689 return PLUGIN_USB_CONNECTED;
690 break;
692 if (button != BUTTON_NONE)
693 lastbutton = button;
697 enum plugin_status plugin_start(
698 const void* parameter)
700 int i, w, h;
702 initial_bmp_path=(const char *)parameter;
703 img_buf = rb->plugin_get_buffer(&img_buf_len);
704 picmode = PICMODE_INITIAL_PICTURE;
705 img_buf_path[0] = '\0';
707 /* If launched as a viewer, just go straight to the game without
708 bothering with the splash or instructions page */
709 if(parameter==NULL)
711 /* if not launched as a viewer, use default puzzle, and show help */
712 picmode = PICMODE_DEFAULT_PICTURE;
714 /* print title */
715 rb->lcd_getstringsize((unsigned char *)"Sliding Puzzle", &w, &h);
716 w = (w+1)/2;
717 h = (h+1)/2;
718 rb->lcd_clear_display();
719 rb->lcd_putsxy(LCD_WIDTH/2-w, (LCD_HEIGHT/2)-h,
720 (unsigned char *)"Sliding Puzzle");
721 rb->lcd_update();
722 rb->sleep(HZ);
724 /* print instructions */
725 rb->lcd_clear_display();
726 rb->lcd_setfont(FONT_SYSFIXED);
727 #if CONFIG_KEYPAD == RECORDER_PAD || CONFIG_KEYPAD == ARCHOS_AV300_PAD
728 rb->lcd_putsxy(3, 18, "[OFF] to stop");
729 rb->lcd_putsxy(3, 28, "[F1] shuffle");
730 rb->lcd_putsxy(3, 38, "[F2] change pic");
731 #elif CONFIG_KEYPAD == ONDIO_PAD
732 rb->lcd_putsxy(0, 18, "[OFF] to stop");
733 rb->lcd_putsxy(0, 28, "[MODE..] shuffle");
734 rb->lcd_putsxy(0, 38, "[MODE] change pic");
735 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
736 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
737 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
738 rb->lcd_putsxy(0, 18, "[S-MENU] to stop");
739 rb->lcd_putsxy(0, 28, "[S-LEFT] shuffle");
740 rb->lcd_putsxy(0, 38, "[S-RIGHT] change pic");
741 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
742 (CONFIG_KEYPAD == IRIVER_H300_PAD)
743 rb->lcd_putsxy(0, 18, "[STOP] to stop");
744 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
745 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
746 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
747 rb->lcd_putsxy(0, 18, "[OFF] to stop");
748 rb->lcd_putsxy(0, 28, "[REC] shuffle");
749 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
750 #elif CONFIG_KEYPAD == GIGABEAT_PAD
751 rb->lcd_putsxy(0, 18, "[POWER] to stop");
752 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
753 rb->lcd_putsxy(0, 38, "[A] change pic");
754 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
755 (CONFIG_KEYPAD == SANSA_C200_PAD)
756 rb->lcd_putsxy(0, 18, "[OFF] to stop");
757 rb->lcd_putsxy(0, 28, "[REC] shuffle");
758 rb->lcd_putsxy(0, 38, "[SELECT] change pic");
759 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
760 rb->lcd_putsxy(0, 18, "[OFF] to stop");
761 rb->lcd_putsxy(0, 28, "[REW] shuffle");
762 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
763 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
764 rb->lcd_putsxy(0, 18, "[BACK] to stop");
765 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
766 rb->lcd_putsxy(0, 38, "[MENU] change pic");
767 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
768 rb->lcd_putsxy(0, 18, "[REC] to stop");
769 rb->lcd_putsxy(0, 28, "[MODE] shuffle");
770 rb->lcd_putsxy(0, 38, "[MENU] change pic");
771 #endif
772 #ifdef HAVE_TOUCHSCREEN
773 rb->lcd_putsxy(0, 18, PUZZLE_QUIT_TEXT " to stop");
774 rb->lcd_putsxy(0, 28, PUZZLE_SHUFFLE_TEXT " shuffle");
775 rb->lcd_putsxy(0, 38, PUZZLE_PICTURE_TEXT " change pic");
776 #endif
777 #ifdef HAVE_ALBUMART
778 rb->lcd_putsxy(0,48," pic->albumart->num");
779 #else
780 rb->lcd_putsxy(0,48," pic<->num");
781 #endif
782 rb->lcd_update();
783 rb->button_get_w_tmo(HZ*2);
786 hole = INITIAL_HOLE;
788 if( !load_resize_bitmap() )
790 rb->lcd_clear_display();
791 rb->splash(HZ*2,"Failed to load bitmap!");
792 return PLUGIN_OK;
795 /* Calculate possible font sizes and text positions */
796 rb->lcd_setfont(FONT_UI);
797 rb->lcd_getstringsize("15", &w, &h);
798 if ((w > (SPOTS_WIDTH-2)) || (h > (SPOTS_HEIGHT-2)))
799 num_font = FONT_SYSFIXED;
801 #if LCD_WIDTH > LCD_HEIGHT
802 rb->lcd_getstringsize("Moves", &w, &h);
803 if (w > (LCD_WIDTH-IMAGE_WIDTH-1))
804 moves_font = FONT_SYSFIXED;
805 rb->lcd_setfont(moves_font);
806 rb->lcd_getstringsize("Moves", &w, &h);
807 moves_y = 10 + h;
808 #else
809 rb->lcd_getstringsize("Moves: 999", &w, &h);
810 if ((w > LCD_WIDTH) || (h > (LCD_HEIGHT-IMAGE_HEIGHT-1)))
811 moves_font = FONT_SYSFIXED;
812 rb->lcd_setfont(moves_font);
813 rb->lcd_getstringsize("Moves: 999", &w, &h);
814 moves_y = (IMAGE_HEIGHT+1+(LCD_HEIGHT-IMAGE_HEIGHT-1)/2) - h / 2;
815 #endif
816 for (i=0; i<NUM_SPOTS; i++)
817 spots[i]=(i+1);
819 #ifdef HAVE_LCD_COLOR
820 rb->lcd_set_background(LCD_BLACK);
821 rb->lcd_set_foreground(LCD_WHITE);
822 rb->lcd_set_backdrop(NULL);
823 #elif LCD_DEPTH > 1
824 rb->lcd_set_background(LCD_WHITE);
825 rb->lcd_set_foreground(LCD_BLACK);
826 rb->lcd_set_backdrop(NULL);
827 #endif
829 draw_playfield();
830 rb->sleep(HZ*2);
832 return puzzle_loop();