Prepare new maemo release
[maemo-rb.git] / apps / plugins / sliding_puzzle.c
blob710db94363db2d0a6a2210b1fd6f68571f7736e7
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 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
141 #define PUZZLE_QUIT BUTTON_BACK
142 #define PUZZLE_LEFT BUTTON_LEFT
143 #define PUZZLE_RIGHT BUTTON_RIGHT
144 #define PUZZLE_UP BUTTON_UP
145 #define PUZZLE_DOWN BUTTON_DOWN
146 #define PUZZLE_SHUFFLE BUTTON_SELECT
147 #define PUZZLE_PICTURE BUTTON_MENU
149 #elif (CONFIG_KEYPAD == MROBE100_PAD)
150 #define PUZZLE_QUIT BUTTON_POWER
151 #define PUZZLE_LEFT BUTTON_LEFT
152 #define PUZZLE_RIGHT BUTTON_RIGHT
153 #define PUZZLE_UP BUTTON_UP
154 #define PUZZLE_DOWN BUTTON_DOWN
155 #define PUZZLE_SHUFFLE BUTTON_SELECT
156 #define PUZZLE_PICTURE BUTTON_DISPLAY
158 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
159 #define PUZZLE_QUIT BUTTON_RC_REC
160 #define PUZZLE_LEFT BUTTON_RC_REW
161 #define PUZZLE_RIGHT BUTTON_RC_FF
162 #define PUZZLE_UP BUTTON_RC_VOL_UP
163 #define PUZZLE_DOWN BUTTON_RC_VOL_DOWN
164 #define PUZZLE_SHUFFLE BUTTON_RC_MODE
165 #define PUZZLE_PICTURE BUTTON_RC_MENU
167 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
168 #define PUZZLE_QUIT BUTTON_POWER
169 #define PUZZLE_QUIT_TEXT "[POWER]"
171 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
172 #define PUZZLE_QUIT BUTTON_BACK
173 #define PUZZLE_LEFT BUTTON_LEFT
174 #define PUZZLE_RIGHT BUTTON_RIGHT
175 #define PUZZLE_UP BUTTON_UP
176 #define PUZZLE_DOWN BUTTON_DOWN
177 #define PUZZLE_SHUFFLE BUTTON_PLAY
178 #define PUZZLE_PICTURE BUTTON_MENU
180 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
181 #define PUZZLE_QUIT BUTTON_POWER
182 #define PUZZLE_LEFT BUTTON_LEFT
183 #define PUZZLE_RIGHT BUTTON_RIGHT
184 #define PUZZLE_UP BUTTON_UP
185 #define PUZZLE_DOWN BUTTON_DOWN
186 #define PUZZLE_SHUFFLE BUTTON_VIEW
187 #define PUZZLE_PICTURE BUTTON_MENU
189 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
190 #define PUZZLE_QUIT BUTTON_POWER
191 #define PUZZLE_LEFT BUTTON_LEFT
192 #define PUZZLE_RIGHT BUTTON_RIGHT
193 #define PUZZLE_UP BUTTON_UP
194 #define PUZZLE_DOWN BUTTON_DOWN
195 #define PUZZLE_SHUFFLE BUTTON_NEXT
196 #define PUZZLE_PICTURE BUTTON_MENU
198 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
199 #define PUZZLE_QUIT BUTTON_POWER
200 #define PUZZLE_LEFT BUTTON_PREV
201 #define PUZZLE_RIGHT BUTTON_NEXT
202 #define PUZZLE_UP BUTTON_UP
203 #define PUZZLE_DOWN BUTTON_DOWN
204 #define PUZZLE_SHUFFLE BUTTON_RIGHT
205 #define PUZZLE_PICTURE BUTTON_MENU
207 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
208 CONFIG_KEYPAD == ONDAVX777_PAD || \
209 CONFIG_KEYPAD == MROBE500_PAD
210 #define PUZZLE_QUIT BUTTON_POWER
211 #define PUZZLE_QUIT_TEXT "[POWER]"
213 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
214 #define PUZZLE_QUIT BUTTON_REC
215 #define PUZZLE_LEFT BUTTON_LEFT
216 #define PUZZLE_RIGHT BUTTON_RIGHT
217 #define PUZZLE_UP BUTTON_UP
218 #define PUZZLE_DOWN BUTTON_DOWN
219 #define PUZZLE_SHUFFLE BUTTON_REW
220 #define PUZZLE_PICTURE BUTTON_PLAY
222 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
223 #define PUZZLE_QUIT BUTTON_REC
224 #define PUZZLE_LEFT BUTTON_PREV
225 #define PUZZLE_RIGHT BUTTON_NEXT
226 #define PUZZLE_UP BUTTON_UP
227 #define PUZZLE_DOWN BUTTON_DOWN
228 #define PUZZLE_SHUFFLE BUTTON_CANCEL
229 #define PUZZLE_PICTURE BUTTON_MENU
231 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
232 #define PUZZLE_QUIT (BUTTON_REC | BUTTON_PLAY)
233 #define PUZZLE_LEFT BUTTON_VOL_DOWN
234 #define PUZZLE_RIGHT BUTTON_VOL_UP
235 #define PUZZLE_UP BUTTON_REW
236 #define PUZZLE_DOWN BUTTON_FF
237 #define PUZZLE_SHUFFLE BUTTON_REC
238 #define PUZZLE_PICTURE BUTTON_PLAY
240 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
241 #define PUZZLE_QUIT (BUTTON_MENU | BUTTON_REPEAT)
242 #define PUZZLE_LEFT BUTTON_FF
243 #define PUZZLE_RIGHT BUTTON_REW
244 #define PUZZLE_UP BUTTON_DOWN
245 #define PUZZLE_DOWN BUTTON_UP
246 #define PUZZLE_SHUFFLE BUTTON_ENTER
247 #define PUZZLE_PICTURE BUTTON_PLAY
249 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
250 #define PUZZLE_QUIT BUTTON_POWER
251 #define PUZZLE_LEFT BUTTON_LEFT
252 #define PUZZLE_RIGHT BUTTON_RIGHT
253 #define PUZZLE_UP BUTTON_UP
254 #define PUZZLE_DOWN BUTTON_DOWN
255 #define PUZZLE_SHUFFLE BUTTON_PLAYPAUSE|BUTTON_REPEAT
256 #define PUZZLE_PICTURE BUTTON_SELECT|BUTTON_REPEAT
258 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
259 #define PUZZLE_QUIT BUTTON_POWER
260 #define PUZZLE_LEFT BUTTON_LEFT
261 #define PUZZLE_RIGHT BUTTON_RIGHT
262 #define PUZZLE_UP BUTTON_UP
263 #define PUZZLE_DOWN BUTTON_DOWN
264 #define PUZZLE_SHUFFLE BUTTON_VOL_DOWN
265 #define PUZZLE_PICTURE BUTTON_SELECT
267 #elif (CONFIG_KEYPAD == HM60X_PAD)
268 #define PUZZLE_QUIT BUTTON_POWER
269 #define PUZZLE_LEFT BUTTON_LEFT
270 #define PUZZLE_RIGHT BUTTON_RIGHT
271 #define PUZZLE_UP BUTTON_UP
272 #define PUZZLE_DOWN BUTTON_DOWN
273 #define PUZZLE_SHUFFLE BUTTON_SELECT
274 #define PUZZLE_PICTURE (BUTTON_POWER|BUTTON_SELECT)
276 #elif (CONFIG_KEYPAD == HM801_PAD)
277 #define PUZZLE_QUIT BUTTON_POWER
278 #define PUZZLE_LEFT BUTTON_LEFT
279 #define PUZZLE_RIGHT BUTTON_RIGHT
280 #define PUZZLE_UP BUTTON_UP
281 #define PUZZLE_DOWN BUTTON_DOWN
282 #define PUZZLE_SHUFFLE BUTTON_SELECT
283 #define PUZZLE_PICTURE BUTTON_PLAY
285 #else
286 #error No keymap defined!
287 #endif
289 #ifdef HAVE_TOUCHSCREEN
290 #ifndef PUZZLE_QUIT
291 #define PUZZLE_QUIT BUTTON_TOPLEFT
292 #endif
293 #ifndef PUZZLE_LEFT
294 #define PUZZLE_LEFT BUTTON_MIDLEFT
295 #endif
296 #ifndef PUZZLE_RIGHT
297 #define PUZZLE_RIGHT BUTTON_MIDRIGHT
298 #endif
299 #ifndef PUZZLE_UP
300 #define PUZZLE_UP BUTTON_TOPMIDDLE
301 #endif
302 #ifndef PUZZLE_DOWN
303 #define PUZZLE_DOWN BUTTON_BOTTOMMIDDLE
304 #endif
305 #ifndef PUZZLE_SHUFFLE
306 #define PUZZLE_SHUFFLE BUTTON_BOTTOMLEFT
307 #endif
308 #ifndef PUZZLE_PICTURE
309 #define PUZZLE_PICTURE BUTTON_CENTER
310 #endif
311 #ifndef PUZZLE_QUIT_TEXT
312 #define PUZZLE_QUIT_TEXT "[TOPLEFT]"
313 #endif
314 #ifndef PUZZLE_SHUFFLE_TEXT
315 #define PUZZLE_SHUFFLE_TEXT "[BOTTOMLEFT]"
316 #endif
317 #ifndef PUZZLE_PICTURE_TEXT
318 #define PUZZLE_PICTURE_TEXT "[CENTER]"
319 #endif
320 #endif
322 #ifdef HAVE_ALBUMART
323 #include "lib/read_image.h"
324 #define READ_IMAGE read_image_file
325 #else
326 #define READ_IMAGE rb->read_bmp_file
327 #endif
329 #include "pluginbitmaps/sliding_puzzle.h"
330 #define IMAGE_WIDTH BMPWIDTH_sliding_puzzle
331 #define IMAGE_HEIGHT BMPHEIGHT_sliding_puzzle
332 #define IMAGE_SIZE IMAGE_WIDTH
334 /* use a square image, (the default Archos bitmap looks square on its display)
335 Puzzle image dimension is min(lcd_height,lcd_width)
336 4x4 is more convenient for square puzzles
337 Note: sliding_puzzle.bmp should be evenly divisible by SPOTS_X
338 and SPOTS_Y, otherwise lcd_bitmap_part stride won't be correct */
339 #define SPOTS_X 4
340 #define SPOTS_Y 4
341 #define SPOTS_WIDTH (IMAGE_WIDTH / SPOTS_X)
342 #define SPOTS_HEIGHT (IMAGE_HEIGHT / SPOTS_Y)
343 #define NUM_SPOTS (SPOTS_X*SPOTS_Y)
344 #define HOLE_ID (NUM_SPOTS)
345 #define INITIAL_HOLE (HOLE_ID-1)
347 enum picmodes
349 PICMODE_NUMERALS = 0,
350 PICMODE_INITIAL_PICTURE,
351 PICMODE_DEFAULT_PICTURE,
352 #ifdef HAVE_ALBUMART
353 PICMODE_ALBUM_ART,
354 #endif
355 // PICMODE_RANDOM,
356 PICMODE_LAST_XXX /* placeholder */
359 static const char* const picmode_descriptions[] = {
360 "Numerals",
361 "Viewer Picture",
362 "Default Picture",
363 #ifdef HAVE_ALBUMART
364 "Album Art",
365 #endif
366 "Shouldn't Get Here",
369 static int spots[NUM_SPOTS];
370 static int hole = INITIAL_HOLE, moves;
371 static unsigned char s[32];
372 static enum picmodes picmode = PICMODE_INITIAL_PICTURE;
373 static int num_font = FONT_UI;
374 static int moves_font = FONT_UI;
375 static int moves_y = 0;
377 static unsigned char *img_buf;
378 static size_t img_buf_len;
379 #ifdef HAVE_ALBUMART
380 static char albumart_path[MAX_PATH+1];
381 #endif
382 static char img_buf_path[MAX_PATH+1];
384 static const fb_data * puzzle_bmp_ptr;
385 /* initial_bmp_path points to selected bitmap if this game is launched
386 as a viewer for a .bmp file, or NULL if game is launched regular way */
387 static const char * initial_bmp_path=NULL;
389 #ifdef HAVE_ALBUMART
390 static const char * get_albumart_bmp_path(void)
392 struct mp3entry* track = rb->audio_current_track();
394 if (!track || !track->path || track->path[0] == '\0')
395 return NULL;
397 if (!rb->search_albumart_files(track, "", albumart_path, MAX_PATH ) )
398 return NULL;
400 albumart_path[ MAX_PATH ] = '\0';
401 return albumart_path;
403 #endif
405 #if 0 /* unused */
406 static const char * get_random_bmp_path(void)
408 return(initial_bmp_path);
410 #endif
412 static bool load_resize_bitmap(void)
414 int rc;
415 const char * filename = NULL;
417 /* initially assume using the built-in default */
418 puzzle_bmp_ptr = sliding_puzzle;
420 switch( picmode ){
421 /* some modes don't even need to touch disk and trivially succeed */
422 case PICMODE_NUMERALS:
423 case PICMODE_DEFAULT_PICTURE:
424 default:
425 return(true);
427 #ifdef HAVE_ALBUMART
428 case PICMODE_ALBUM_ART:
429 filename = get_albumart_bmp_path();
430 break;
431 #endif
433 case PICMODE_RANDOM:
434 if(NULL == (filename=get_random_bmp_path()) )
435 filename = initial_bmp_path;
436 break;
438 case PICMODE_INITIAL_PICTURE:
439 filename = initial_bmp_path;
440 break;
443 if( filename != NULL )
445 /* if we already loaded image before, don't touch disk */
446 if( 0 == rb->strcmp( filename, img_buf_path ) )
448 puzzle_bmp_ptr = (const fb_data *)img_buf;
449 return true;
452 struct bitmap main_bitmap;
453 rb->memset(&main_bitmap,0,sizeof(struct bitmap));
454 main_bitmap.data = img_buf;
456 main_bitmap.width = IMAGE_WIDTH;
457 main_bitmap.height = IMAGE_HEIGHT;
459 rc = READ_IMAGE( filename, &main_bitmap,
460 img_buf_len,
461 FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_DITHER,
462 NULL);
463 if( rc > 0 )
465 puzzle_bmp_ptr = (const fb_data *)img_buf;
466 rb->strcpy( img_buf_path, filename );
467 return true;
471 /* something must have failed. get_albumart_bmp_path could return
472 NULL if albumart doesn't exist or couldn't be loaded, or
473 read_bmp_file could have failed. return false and caller should
474 try the next mode (PICMODE_DEFAULT_PICTURE and PICMODE_NUMERALS will
475 always succeed) */
476 return false;
479 /* draws a spot at the coordinates (x,y), range of p is 1-20 */
480 static void draw_spot(int p, int x, int y)
482 int w, h;
484 if (p == HOLE_ID)
486 #if LCD_DEPTH==1
487 /* the bottom-right cell of the default sliding_puzzle image is
488 an appropriate hole graphic */
489 rb->lcd_bitmap_part(sliding_puzzle, ((p-1)%SPOTS_X)*SPOTS_WIDTH,
490 ((p-1)/SPOTS_X)*SPOTS_HEIGHT,
491 STRIDE( SCREEN_MAIN,
492 BMPWIDTH_sliding_puzzle, BMPHEIGHT_sliding_puzzle),
493 x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
494 #else
495 /* just draw a black rectangle */
496 int old_fg = rb->lcd_get_foreground();
497 rb->lcd_set_foreground(LCD_BLACK);
498 rb->lcd_fillrect(x,y,SPOTS_WIDTH,SPOTS_HEIGHT);
499 rb->lcd_set_foreground(LCD_WHITE);
500 rb->lcd_drawrect(x,y,SPOTS_WIDTH,SPOTS_HEIGHT);
501 rb->lcd_set_foreground(old_fg);
502 #endif
504 else if (picmode != PICMODE_NUMERALS)
506 rb->lcd_bitmap_part( puzzle_bmp_ptr, ((p-1)%SPOTS_X)*SPOTS_WIDTH,
507 ((p-1)/SPOTS_X)*SPOTS_HEIGHT,
508 STRIDE( SCREEN_MAIN,
509 BMPWIDTH_sliding_puzzle, BMPHEIGHT_sliding_puzzle),
510 x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
511 } else {
512 rb->lcd_drawrect(x, y, SPOTS_WIDTH, SPOTS_HEIGHT);
513 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
514 rb->lcd_fillrect(x+1, y+1, SPOTS_WIDTH-2, SPOTS_HEIGHT-2);
515 rb->lcd_set_drawmode(DRMODE_SOLID);
516 rb->snprintf(s, sizeof(s), "%d", p);
517 rb->lcd_setfont(num_font);
518 rb->lcd_getstringsize(s, &w, &h);
519 rb->lcd_putsxy(x + (SPOTS_WIDTH/2) - w / 2,
520 y + (SPOTS_HEIGHT/2) - h / 2, s);
524 /* check if the puzzle is solved */
525 static bool puzzle_finished(void)
527 int i;
528 for (i=0; i<NUM_SPOTS; i++)
529 if (spots[i] != (i+1))
530 return false;
531 return true;
534 /* move a piece in any direction */
535 static void move_spot(int x, int y)
537 int i, w;
538 spots[hole] = spots[hole-x-SPOTS_X*y];
539 hole -= (x+SPOTS_X*y);
540 moves++;
541 rb->lcd_setfont(moves_font);
542 #if LCD_WIDTH > LCD_HEIGHT
543 rb->snprintf(s, sizeof(s), "%d", moves);
544 w = rb->lcd_getstringsize(s, NULL, NULL);
545 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
546 moves_y, s);
547 #else
548 (void)w;
549 rb->lcd_putsxyf(3, moves_y, "Moves: %d", moves);
550 #endif
551 for(i=1;i<=4;i++)
553 draw_spot(HOLE_ID,
554 (hole%SPOTS_X)*SPOTS_WIDTH,
555 (hole/SPOTS_X)*SPOTS_HEIGHT);
556 draw_spot(spots[hole],
557 (hole%SPOTS_X)*SPOTS_WIDTH + (i*x*SPOTS_WIDTH)/5,
558 (hole/SPOTS_X)*SPOTS_HEIGHT + (i*y*SPOTS_HEIGHT)/5);
559 rb->lcd_update();
560 rb->sleep(HZ/50);
562 draw_spot(HOLE_ID,
563 (hole%SPOTS_X)*SPOTS_WIDTH,
564 (hole/SPOTS_X)*SPOTS_HEIGHT);
565 draw_spot(spots[hole],
566 ((hole%SPOTS_X)+x)*SPOTS_WIDTH,
567 ((hole/SPOTS_X)+y)*SPOTS_HEIGHT);
568 rb->lcd_update();
570 spots[hole] = HOLE_ID;
573 static void draw_playfield(void)
575 int i, w;
577 rb->lcd_clear_display();
578 rb->lcd_setfont(moves_font);
579 #if LCD_WIDTH > LCD_HEIGHT
580 rb->lcd_vline(IMAGE_WIDTH, 0, LCD_HEIGHT-1);
581 w = rb->lcd_getstringsize("Moves", NULL, NULL);
582 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
583 10, "Moves");
584 rb->snprintf(s, sizeof(s), "%d", moves);
585 w = rb->lcd_getstringsize(s, NULL, NULL);
586 rb->lcd_putsxy((IMAGE_WIDTH+1+(LCD_WIDTH-IMAGE_WIDTH-1)/2) - w / 2,
587 moves_y, s);
588 #else
589 (void)w;
590 rb->lcd_hline(0, LCD_WIDTH-1, IMAGE_HEIGHT);
591 rb->lcd_putsxyf(3, moves_y, "Moves: %d", moves);
592 #endif
594 /* draw spots to the lcd */
595 for (i=0; i<NUM_SPOTS; i++)
596 draw_spot(spots[i], (i%SPOTS_X)*SPOTS_WIDTH, (i/SPOTS_X)*SPOTS_HEIGHT);
598 rb->lcd_update();
601 /* initializes the puzzle */
602 static void puzzle_init(void)
604 int i, r, temp, tsp[NUM_SPOTS];
606 moves = 0;
608 /* shuffle spots */
609 for (i=NUM_SPOTS-1; i>=0; i--) {
610 r = (rb->rand() % (i+1));
612 temp = spots[r];
613 spots[r] = spots[i];
614 spots[i] = temp;
616 if (spots[i]==HOLE_ID)
617 hole = i;
620 /* test if the puzzle is solvable */
621 for (i=0; i<NUM_SPOTS; i++)
622 tsp[i] = spots[i];
623 r=0;
625 /* First, check if the problem has even or odd parity,
626 depending on where the empty square is */
627 if ((((SPOTS_X-1)-hole%SPOTS_X) + ((SPOTS_Y-1)-hole/SPOTS_X))%2 == 1)
628 ++r;
630 /* Now check how many swaps we need to solve it */
631 for (i=0; i<NUM_SPOTS-1; i++) {
632 while (tsp[i] != (i+1)) {
633 temp = tsp[i];
634 tsp[i] = tsp[temp-1];
635 tsp[temp-1] = temp;
636 ++r;
640 /* if the random puzzle isn't solvable just change two spots */
641 if (r%2 == 1) {
642 if (spots[0]!=HOLE_ID && spots[1]!=HOLE_ID) {
643 temp = spots[0];
644 spots[0] = spots[1];
645 spots[1] = temp;
646 } else {
647 temp = spots[2];
648 spots[2] = spots[3];
649 spots[3] = temp;
653 draw_playfield();
656 /* the main game loop */
657 static int puzzle_loop(void)
659 int button;
660 #if defined(PUZZLE_SHUFFLE_PICTURE_PRE)
661 int lastbutton = BUTTON_NONE;
662 #endif
663 bool load_success;
665 puzzle_init();
666 while(true) {
667 button = rb->button_get(true);
668 switch (button) {
669 #ifdef PUZZLE_RC_QUIT
670 case PUZZLE_RC_QUIT:
671 #endif
672 case PUZZLE_QUIT:
673 /* get out of here */
674 return PLUGIN_OK;
676 case PUZZLE_SHUFFLE:
677 #ifdef PUZZLE_SHUFFLE_PICTURE_PRE
678 if (lastbutton != PUZZLE_SHUFFLE_PICTURE_PRE)
679 break;
680 #endif
681 /* mix up the pieces */
682 puzzle_init();
683 break;
685 case PUZZLE_PICTURE:
686 #ifdef PUZZLE_SHUFFLE_PICTURE_PRE
687 if (lastbutton != PUZZLE_SHUFFLE_PICTURE_PRE)
688 break;
689 #endif
690 /* change picture */
691 picmode = (picmode+1)%PICMODE_LAST_XXX;
693 /* if load_resize_bitmap fails to load bitmap, try next picmode
697 load_success = load_resize_bitmap();
698 if( !load_success )
699 picmode = (picmode+1)%PICMODE_LAST_XXX;
701 while( !load_success );
703 /* tell the user what mode we picked in the end! */
704 rb->splash(HZ,picmode_descriptions[ picmode ] );
705 draw_playfield();
706 break;
708 case PUZZLE_LEFT:
709 if ((hole%SPOTS_X)<(SPOTS_X-1) && !puzzle_finished())
710 move_spot(-1, 0);
711 break;
713 case PUZZLE_RIGHT:
714 if ((hole%SPOTS_X)>0 && !puzzle_finished())
715 move_spot(1, 0);
716 break;
718 case PUZZLE_UP:
719 if ((hole/SPOTS_X)<(SPOTS_Y-1) && !puzzle_finished())
720 move_spot(0, -1);
721 break;
723 case PUZZLE_DOWN:
724 if ((hole/SPOTS_X)>0 && !puzzle_finished())
725 move_spot(0, 1);
726 break;
728 default:
729 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
730 return PLUGIN_USB_CONNECTED;
731 break;
733 #if defined(PUZZLE_SHUFFLE_PICTURE_PRE)
734 if (button != BUTTON_NONE)
735 lastbutton = button;
736 #endif
740 enum plugin_status plugin_start(
741 const void* parameter)
743 int i, w, h;
745 initial_bmp_path=(const char *)parameter;
746 img_buf = rb->plugin_get_buffer(&img_buf_len);
747 picmode = PICMODE_INITIAL_PICTURE;
748 img_buf_path[0] = '\0';
750 /* If launched as a viewer, just go straight to the game without
751 bothering with the splash or instructions page */
752 if(parameter==NULL)
754 /* if not launched as a viewer, use default puzzle, and show help */
755 picmode = PICMODE_DEFAULT_PICTURE;
757 /* print title */
758 rb->lcd_getstringsize((unsigned char *)"Sliding Puzzle", &w, &h);
759 w = (w+1)/2;
760 h = (h+1)/2;
761 rb->lcd_clear_display();
762 rb->lcd_putsxy(LCD_WIDTH/2-w, (LCD_HEIGHT/2)-h,
763 (unsigned char *)"Sliding Puzzle");
764 rb->lcd_update();
765 rb->sleep(HZ);
767 /* print instructions */
768 rb->lcd_clear_display();
769 rb->lcd_setfont(FONT_SYSFIXED);
770 #if CONFIG_KEYPAD == RECORDER_PAD || CONFIG_KEYPAD == ARCHOS_AV300_PAD
771 rb->lcd_putsxy(3, 18, "[OFF] to stop");
772 rb->lcd_putsxy(3, 28, "[F1] shuffle");
773 rb->lcd_putsxy(3, 38, "[F2] change pic");
774 #elif CONFIG_KEYPAD == ONDIO_PAD
775 rb->lcd_putsxy(0, 18, "[OFF] to stop");
776 rb->lcd_putsxy(0, 28, "[MODE..] shuffle");
777 rb->lcd_putsxy(0, 38, "[MODE] change pic");
778 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
779 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
780 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
781 rb->lcd_putsxy(0, 18, "[S-MENU] to stop");
782 rb->lcd_putsxy(0, 28, "[S-LEFT] shuffle");
783 rb->lcd_putsxy(0, 38, "[S-RIGHT] change pic");
784 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
785 (CONFIG_KEYPAD == IRIVER_H300_PAD)
786 rb->lcd_putsxy(0, 18, "[STOP] to stop");
787 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
788 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
789 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
790 rb->lcd_putsxy(0, 18, "[OFF] to stop");
791 rb->lcd_putsxy(0, 28, "[REC] shuffle");
792 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
793 #elif CONFIG_KEYPAD == GIGABEAT_PAD
794 rb->lcd_putsxy(0, 18, "[POWER] to stop");
795 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
796 rb->lcd_putsxy(0, 38, "[A] change pic");
797 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
798 (CONFIG_KEYPAD == SANSA_C200_PAD)
799 rb->lcd_putsxy(0, 18, "[OFF] to stop");
800 rb->lcd_putsxy(0, 28, "[REC] shuffle");
801 rb->lcd_putsxy(0, 38, "[SELECT] change pic");
802 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
803 rb->lcd_putsxy(0, 18, "[OFF] to stop");
804 rb->lcd_putsxy(0, 28, "[REW] shuffle");
805 rb->lcd_putsxy(0, 38, "[PLAY] change pic");
806 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD || \
807 CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
808 rb->lcd_putsxy(0, 18, "[BACK] to stop");
809 rb->lcd_putsxy(0, 28, "[SELECT] shuffle");
810 rb->lcd_putsxy(0, 38, "[MENU] change pic");
811 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
812 rb->lcd_putsxy(0, 18, "[REC] to stop");
813 rb->lcd_putsxy(0, 28, "[MODE] shuffle");
814 rb->lcd_putsxy(0, 38, "[MENU] change pic");
815 #endif
816 #ifdef HAVE_TOUCHSCREEN
817 rb->lcd_putsxy(0, 18, PUZZLE_QUIT_TEXT " to stop");
818 rb->lcd_putsxy(0, 28, PUZZLE_SHUFFLE_TEXT " shuffle");
819 rb->lcd_putsxy(0, 38, PUZZLE_PICTURE_TEXT " change pic");
820 #endif
821 #ifdef HAVE_ALBUMART
822 rb->lcd_putsxy(0,48," pic->albumart->num");
823 #else
824 rb->lcd_putsxy(0,48," pic<->num");
825 #endif
826 rb->lcd_update();
827 rb->button_get_w_tmo(HZ*2);
830 hole = INITIAL_HOLE;
832 if( !load_resize_bitmap() )
834 rb->lcd_clear_display();
835 rb->splash(HZ*2,"Failed to load bitmap!");
836 return PLUGIN_OK;
839 /* Calculate possible font sizes and text positions */
840 rb->lcd_setfont(FONT_UI);
841 rb->lcd_getstringsize("15", &w, &h);
842 if ((w > (SPOTS_WIDTH-2)) || (h > (SPOTS_HEIGHT-2)))
843 num_font = FONT_SYSFIXED;
845 #if LCD_WIDTH > LCD_HEIGHT
846 rb->lcd_getstringsize("Moves", &w, &h);
847 if (w > (LCD_WIDTH-IMAGE_WIDTH-1))
848 moves_font = FONT_SYSFIXED;
849 rb->lcd_setfont(moves_font);
850 rb->lcd_getstringsize("Moves", &w, &h);
851 moves_y = 10 + h;
852 #else
853 rb->lcd_getstringsize("Moves: 999", &w, &h);
854 if ((w > LCD_WIDTH) || (h > (LCD_HEIGHT-IMAGE_HEIGHT-1)))
855 moves_font = FONT_SYSFIXED;
856 rb->lcd_setfont(moves_font);
857 rb->lcd_getstringsize("Moves: 999", &w, &h);
858 moves_y = (IMAGE_HEIGHT+1+(LCD_HEIGHT-IMAGE_HEIGHT-1)/2) - h / 2;
859 #endif
860 for (i=0; i<NUM_SPOTS; i++)
861 spots[i]=(i+1);
863 #ifdef HAVE_LCD_COLOR
864 rb->lcd_set_background(LCD_BLACK);
865 rb->lcd_set_foreground(LCD_WHITE);
866 rb->lcd_set_backdrop(NULL);
867 #elif LCD_DEPTH > 1
868 rb->lcd_set_background(LCD_WHITE);
869 rb->lcd_set_foreground(LCD_BLACK);
870 rb->lcd_set_backdrop(NULL);
871 #endif
873 draw_playfield();
874 rb->sleep(HZ*2);
876 return puzzle_loop();