1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Eric Linenberg
11 * February 2003: Robert Hak performs a cleanup/rewrite/feature addition.
12 * Eric smiles. Bjorn cries. Linus say 'huh?'.
13 * March 2007: Sean Morrisey performs a major rewrite/feature addition.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
25 #include "lib/playback_control.h"
29 #define SOKOBAN_TITLE "Sokoban"
31 #define SOKOBAN_LEVELS_FILE PLUGIN_GAMES_DIR "/sokoban.levels"
32 #define SOKOBAN_SAVE_FILE PLUGIN_GAMES_DIR "/sokoban.save"
33 #define SOKOBAN_SAVE_FOLDER "/games"
35 #include "pluginbitmaps/sokoban_tiles.h"
36 #define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
38 /* If tilesize is 0 (which it is during dependency generation) gcc will abort
39 (div by 0) and this plugin won't get any dependencies
41 #if SOKOBAN_TILESIZE < 1
42 #define SOKOBAN_TILESIZE 10
45 /* SOKOBAN_TILESIZE is the number of pixels for each block.
46 * Set dynamically so all targets can support levels
47 * that fill their entire screen, less the stat box.
48 * 16 rows & 20 cols minimum */
49 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
50 #define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
51 #if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
52 #define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
54 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
56 #else /* vertical layout*/
57 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
58 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
61 /* size of code+bss */
62 #if CONFIG_CPU == SH7034
63 #define CODE_SIZE 0x3000 /* 12k */
65 #define CODE_SIZE 0x5000 /* 20k */
68 #define CODE_AND_UNDO_SIZE (CODE_SIZE+0x1000) /* + 4k */
70 /* Use either all but code & undo of the plugin buffer for level data
71 * or 128k, which ever is less */
72 #if PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE < 0x20000
73 #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE)
75 #define MAX_LEVEL_DATA 0x20000
78 /* Number of levels for which to allocate buffer indexes */
79 #define MAX_LEVELS MAX_LEVEL_DATA/70
81 /* Use remaining plugin buffer (- code prog) for undo, up to 64k */
82 #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE > 0x10000
83 #define MAX_UNDOS 0x10000
85 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE)
88 /* Move/push definitions for undo */
89 #define SOKOBAN_PUSH_LEFT 'L'
90 #define SOKOBAN_PUSH_RIGHT 'R'
91 #define SOKOBAN_PUSH_UP 'U'
92 #define SOKOBAN_PUSH_DOWN 'D'
93 #define SOKOBAN_MOVE_LEFT 'l'
94 #define SOKOBAN_MOVE_RIGHT 'r'
95 #define SOKOBAN_MOVE_UP 'u'
96 #define SOKOBAN_MOVE_DOWN 'd'
98 #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
99 #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
101 /* variable button definitions */
102 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
103 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
104 #define SOKOBAN_LEFT BUTTON_LEFT
105 #define SOKOBAN_RIGHT BUTTON_RIGHT
106 #define SOKOBAN_UP BUTTON_UP
107 #define SOKOBAN_DOWN BUTTON_DOWN
108 #define SOKOBAN_MENU BUTTON_OFF
109 #define SOKOBAN_UNDO BUTTON_ON
110 #define SOKOBAN_REDO BUTTON_PLAY
111 #define SOKOBAN_LEVEL_DOWN BUTTON_F1
112 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2
113 #define SOKOBAN_LEVEL_UP BUTTON_F3
114 #define SOKOBAN_PAUSE BUTTON_PLAY
115 #define BUTTON_SAVE BUTTON_ON
116 #define BUTTON_SAVE_NAME "ON"
118 #elif CONFIG_KEYPAD == ONDIO_PAD
119 #define SOKOBAN_LEFT BUTTON_LEFT
120 #define SOKOBAN_RIGHT BUTTON_RIGHT
121 #define SOKOBAN_UP BUTTON_UP
122 #define SOKOBAN_DOWN BUTTON_DOWN
123 #define SOKOBAN_MENU BUTTON_OFF
124 #define SOKOBAN_UNDO_PRE BUTTON_MENU
125 #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
126 #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
127 #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
128 #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
129 #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
130 #define SOKOBAN_PAUSE BUTTON_MENU
131 #define BUTTON_SAVE BUTTON_MENU
132 #define BUTTON_SAVE_NAME "MENU"
134 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
135 (CONFIG_KEYPAD == IRIVER_H300_PAD)
136 #define SOKOBAN_LEFT BUTTON_LEFT
137 #define SOKOBAN_RIGHT BUTTON_RIGHT
138 #define SOKOBAN_UP BUTTON_UP
139 #define SOKOBAN_DOWN BUTTON_DOWN
140 #define SOKOBAN_MENU BUTTON_OFF
141 #define SOKOBAN_UNDO BUTTON_REC
142 #define SOKOBAN_REDO BUTTON_MODE
143 #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
144 #define SOKOBAN_LEVEL_REPEAT BUTTON_ON
145 #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
146 #define SOKOBAN_PAUSE BUTTON_ON
147 #define BUTTON_SAVE BUTTON_MODE
148 #define BUTTON_SAVE_NAME "MODE"
150 #define SOKOBAN_RC_MENU BUTTON_RC_STOP
152 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
153 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
154 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
155 #define SOKOBAN_LEFT BUTTON_LEFT
156 #define SOKOBAN_RIGHT BUTTON_RIGHT
157 #define SOKOBAN_UP BUTTON_MENU
158 #define SOKOBAN_DOWN BUTTON_PLAY
159 #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
160 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
161 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
162 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
163 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
164 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
165 #define SOKOBAN_PAUSE BUTTON_SELECT
166 #define BUTTON_SAVE BUTTON_SELECT
167 #define BUTTON_SAVE_NAME "SELECT"
169 /* FIXME: if/when simultaneous button presses work for X5/M5,
170 * add level up/down */
171 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
172 #define SOKOBAN_LEFT BUTTON_LEFT
173 #define SOKOBAN_RIGHT BUTTON_RIGHT
174 #define SOKOBAN_UP BUTTON_UP
175 #define SOKOBAN_DOWN BUTTON_DOWN
176 #define SOKOBAN_MENU BUTTON_POWER
177 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
178 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
179 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
180 #define SOKOBAN_REDO BUTTON_PLAY
181 #define SOKOBAN_PAUSE BUTTON_PLAY
182 #define BUTTON_SAVE BUTTON_SELECT
183 #define BUTTON_SAVE_NAME "SELECT"
185 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
186 #define SOKOBAN_LEFT BUTTON_LEFT
187 #define SOKOBAN_RIGHT BUTTON_RIGHT
188 #define SOKOBAN_UP BUTTON_SCROLL_UP
189 #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
190 #define SOKOBAN_MENU BUTTON_POWER
191 #define SOKOBAN_UNDO_PRE BUTTON_REW
192 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
193 #define SOKOBAN_REDO BUTTON_FF
194 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
195 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
196 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
197 #define SOKOBAN_PAUSE BUTTON_PLAY
198 #define BUTTON_SAVE BUTTON_PLAY
199 #define BUTTON_SAVE_NAME "PLAY"
201 #elif CONFIG_KEYPAD == GIGABEAT_PAD
202 #define SOKOBAN_LEFT BUTTON_LEFT
203 #define SOKOBAN_RIGHT BUTTON_RIGHT
204 #define SOKOBAN_UP BUTTON_UP
205 #define SOKOBAN_DOWN BUTTON_DOWN
206 #define SOKOBAN_MENU BUTTON_POWER
207 #define SOKOBAN_UNDO BUTTON_SELECT
208 #define SOKOBAN_REDO BUTTON_A
209 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
210 #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
211 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
212 #define SOKOBAN_PAUSE BUTTON_SELECT
213 #define BUTTON_SAVE BUTTON_SELECT
214 #define BUTTON_SAVE_NAME "SELECT"
216 #elif CONFIG_KEYPAD == SANSA_E200_PAD
217 #define SOKOBAN_LEFT BUTTON_LEFT
218 #define SOKOBAN_RIGHT BUTTON_RIGHT
219 #define SOKOBAN_UP BUTTON_UP
220 #define SOKOBAN_DOWN BUTTON_DOWN
221 #define SOKOBAN_MENU BUTTON_POWER
222 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
223 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
224 #define SOKOBAN_REDO BUTTON_REC
225 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
226 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
227 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
228 #define SOKOBAN_PAUSE BUTTON_SELECT
229 #define BUTTON_SAVE BUTTON_SELECT
230 #define BUTTON_SAVE_NAME "SELECT"
232 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
233 #define SOKOBAN_LEFT BUTTON_LEFT
234 #define SOKOBAN_RIGHT BUTTON_RIGHT
235 #define SOKOBAN_UP BUTTON_UP
236 #define SOKOBAN_DOWN BUTTON_DOWN
237 #define SOKOBAN_MENU (BUTTON_HOME|BUTTON_REPEAT)
238 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
239 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
240 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_LEFT)
241 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
242 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
243 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
244 #define SOKOBAN_PAUSE BUTTON_SELECT
245 #define BUTTON_SAVE BUTTON_SELECT
246 #define BUTTON_SAVE_NAME "SELECT"
248 #elif CONFIG_KEYPAD == SANSA_C200_PAD
249 #define SOKOBAN_LEFT BUTTON_LEFT
250 #define SOKOBAN_RIGHT BUTTON_RIGHT
251 #define SOKOBAN_UP BUTTON_UP
252 #define SOKOBAN_DOWN BUTTON_DOWN
253 #define SOKOBAN_MENU BUTTON_POWER
254 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
255 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
256 #define SOKOBAN_REDO BUTTON_REC
257 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
258 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
259 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
260 #define SOKOBAN_PAUSE BUTTON_SELECT
261 #define BUTTON_SAVE BUTTON_SELECT
262 #define BUTTON_SAVE_NAME "SELECT"
264 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
265 #define SOKOBAN_LEFT BUTTON_LEFT
266 #define SOKOBAN_RIGHT BUTTON_RIGHT
267 #define SOKOBAN_UP BUTTON_UP
268 #define SOKOBAN_DOWN BUTTON_DOWN
269 #define SOKOBAN_MENU BUTTON_POWER
270 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
271 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
272 #define SOKOBAN_REDO BUTTON_HOME
273 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
274 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
275 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
276 #define SOKOBAN_PAUSE BUTTON_SELECT
277 #define BUTTON_SAVE BUTTON_SELECT
278 #define BUTTON_SAVE_NAME "SELECT"
280 #elif CONFIG_KEYPAD == SANSA_M200_PAD
281 #define SOKOBAN_LEFT BUTTON_LEFT
282 #define SOKOBAN_RIGHT BUTTON_RIGHT
283 #define SOKOBAN_UP BUTTON_UP
284 #define SOKOBAN_DOWN BUTTON_DOWN
285 #define SOKOBAN_MENU BUTTON_POWER
286 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
287 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
288 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_UP)
289 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
290 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
291 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
292 #define SOKOBAN_PAUSE BUTTON_SELECT
293 #define BUTTON_SAVE BUTTON_SELECT
294 #define BUTTON_SAVE_NAME "SELECT"
296 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
297 #define SOKOBAN_LEFT BUTTON_LEFT
298 #define SOKOBAN_RIGHT BUTTON_RIGHT
299 #define SOKOBAN_UP BUTTON_UP
300 #define SOKOBAN_DOWN BUTTON_DOWN
301 #define SOKOBAN_MENU BUTTON_MENU
302 #define SOKOBAN_UNDO BUTTON_VOL_UP
303 #define SOKOBAN_REDO BUTTON_VOL_DOWN
304 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
305 #define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
306 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
307 #define SOKOBAN_PAUSE BUTTON_SELECT
308 #define BUTTON_SAVE BUTTON_SELECT
309 #define BUTTON_SAVE_NAME "SELECT"
311 #elif CONFIG_KEYPAD == MROBE100_PAD
312 #define SOKOBAN_LEFT BUTTON_LEFT
313 #define SOKOBAN_RIGHT BUTTON_RIGHT
314 #define SOKOBAN_UP BUTTON_UP
315 #define SOKOBAN_DOWN BUTTON_DOWN
316 #define SOKOBAN_MENU BUTTON_POWER
317 #define SOKOBAN_UNDO BUTTON_SELECT
318 #define SOKOBAN_REDO BUTTON_MENU
319 #define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
320 #define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
321 #define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
322 #define SOKOBAN_PAUSE BUTTON_SELECT
323 #define BUTTON_SAVE BUTTON_SELECT
324 #define BUTTON_SAVE_NAME "SELECT"
326 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
327 #define SOKOBAN_LEFT BUTTON_RC_REW
328 #define SOKOBAN_RIGHT BUTTON_RC_FF
329 #define SOKOBAN_UP BUTTON_RC_VOL_UP
330 #define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
331 #define SOKOBAN_MENU BUTTON_RC_REC
332 #define SOKOBAN_UNDO BUTTON_RC_MODE
333 #define SOKOBAN_REDO BUTTON_RC_MENU
334 #define SOKOBAN_PAUSE BUTTON_RC_PLAY
335 #define BUTTON_SAVE BUTTON_RC_PLAY
336 #define BUTTON_SAVE_NAME "PLAY"
338 #define SOKOBAN_RC_MENU BUTTON_REC
340 #elif CONFIG_KEYPAD == COWON_D2_PAD
341 #define SOKOBAN_MENU BUTTON_MENU
342 #define SOKOBAN_LEVEL_DOWN BUTTON_MINUS
343 #define SOKOBAN_LEVEL_UP BUTTON_PLUS
344 #define SOKOBAN_MENU_NAME "[MENU]"
346 #elif CONFIG_KEYPAD == IAUDIO67_PAD
347 #define SOKOBAN_LEFT BUTTON_LEFT
348 #define SOKOBAN_RIGHT BUTTON_RIGHT
349 #define SOKOBAN_UP BUTTON_STOP
350 #define SOKOBAN_DOWN BUTTON_PLAY
351 #define SOKOBAN_MENU BUTTON_MENU
352 #define SOKOBAN_UNDO BUTTON_VOLDOWN
353 #define SOKOBAN_REDO BUTTON_VOLUP
354 #define SOKOBAN_PAUSE (BUTTON_MENU|BUTTON_LEFT)
355 #define BUTTON_SAVE (BUTTON_MENU|BUTTON_PLAY)
356 #define BUTTON_SAVE_NAME "MENU+PLAY"
358 #define SOKOBAN_RC_MENU (BUTTON_MENU|BUTTON_STOP)
360 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
361 #define SOKOBAN_LEFT BUTTON_LEFT
362 #define SOKOBAN_RIGHT BUTTON_RIGHT
363 #define SOKOBAN_UP BUTTON_UP
364 #define SOKOBAN_DOWN BUTTON_DOWN
365 #define SOKOBAN_MENU BUTTON_MENU
366 #define SOKOBAN_UNDO BUTTON_BACK
367 #define SOKOBAN_REDO (BUTTON_BACK | BUTTON_PLAY)
368 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
369 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
370 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
371 #define SOKOBAN_PAUSE BUTTON_PLAY
372 #define BUTTON_SAVE BUTTON_CUSTOM
373 #define BUTTON_SAVE_NAME "CUSTOM"
375 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
376 #define SOKOBAN_LEFT BUTTON_LEFT
377 #define SOKOBAN_RIGHT BUTTON_RIGHT
378 #define SOKOBAN_UP BUTTON_UP
379 #define SOKOBAN_DOWN BUTTON_DOWN
380 #define SOKOBAN_MENU BUTTON_MENU
381 #define SOKOBAN_UNDO BUTTON_VIEW
382 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_VIEW)
383 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
384 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
385 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
386 #define SOKOBAN_PAUSE BUTTON_SELECT
387 #define BUTTON_SAVE BUTTON_PLAYLIST
388 #define BUTTON_SAVE_NAME "PLAYLIST"
390 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
391 #define SOKOBAN_LEFT BUTTON_PREV
392 #define SOKOBAN_RIGHT BUTTON_NEXT
393 #define SOKOBAN_UP BUTTON_UP
394 #define SOKOBAN_DOWN BUTTON_DOWN
395 #define SOKOBAN_MENU BUTTON_MENU
396 #define SOKOBAN_UNDO BUTTON_LEFT
397 #define SOKOBAN_REDO (BUTTON_LEFT | BUTTON_PLAY)
398 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
399 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
400 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
401 #define SOKOBAN_PAUSE BUTTON_PLAY
402 #define BUTTON_SAVE BUTTON_RIGHT
403 #define BUTTON_SAVE_NAME "RIGHT"
405 #elif CONFIG_KEYPAD == ONDAVX747_PAD
406 #define SOKOBAN_MENU BUTTON_MENU
407 #define SOKOBAN_MENU_NAME "[MENU]"
409 #elif CONFIG_KEYPAD == ONDAVX777_PAD
410 #define SOKOBAN_MENU BUTTON_POWER
411 #define SOKOBAN_MENU_NAME "[POWER]"
413 #elif CONFIG_KEYPAD == MROBE500_PAD
415 #define SOKOBAN_MENU BUTTON_POWER
416 #define SOKOBAN_MENU_NAME "[POWER]"
418 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
419 #define SOKOBAN_LEFT BUTTON_LEFT
420 #define SOKOBAN_RIGHT BUTTON_RIGHT
421 #define SOKOBAN_UP BUTTON_UP
422 #define SOKOBAN_DOWN BUTTON_DOWN
423 #define SOKOBAN_MENU BUTTON_REC
424 #define SOKOBAN_UNDO_PRE BUTTON_REW
425 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_LEFT)
426 #define SOKOBAN_REDO BUTTON_FFWD
427 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
428 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
429 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
430 #define SOKOBAN_PAUSE BUTTON_PLAY
431 #define BUTTON_SAVE BUTTON_PLAY
432 #define BUTTON_SAVE_NAME "PLAY"
434 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
435 #define SOKOBAN_LEFT BUTTON_PREV
436 #define SOKOBAN_RIGHT BUTTON_NEXT
437 #define SOKOBAN_UP BUTTON_UP
438 #define SOKOBAN_DOWN BUTTON_DOWN
439 #define SOKOBAN_MENU BUTTON_REC
440 #define SOKOBAN_UNDO BUTTON_CANCEL
441 #define SOKOBAN_REDO BUTTON_OK
442 #define SOKOBAN_LEVEL_DOWN (BUTTON_OK | BUTTON_PREV)
443 #define SOKOBAN_LEVEL_REPEAT (BUTTON_OK | BUTTON_CANCEL)
444 #define SOKOBAN_LEVEL_UP (BUTTON_OK | BUTTON_NEXT)
445 #define SOKOBAN_PAUSE BUTTON_PLAY
446 #define BUTTON_SAVE BUTTON_MENU
447 #define BUTTON_SAVE_NAME "MENU"
449 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
450 #define SOKOBAN_LEFT BUTTON_VOL_DOWN
451 #define SOKOBAN_RIGHT BUTTON_VOL_UP
452 #define SOKOBAN_UP BUTTON_PREV
453 #define SOKOBAN_DOWN BUTTON_NEXT
454 #define SOKOBAN_MENU BUTTON_SELECT
455 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_PREV)
456 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_NEXT)
457 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_VOL_DOWN)
458 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
459 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_VOL_UP)
460 #define SOKOBAN_PAUSE BUTTON_PLAY
461 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_SELECT)
462 #define BUTTON_SAVE_NAME "PLAY+SELECT"
465 #error No keymap defined!
468 #ifdef HAVE_TOUCHSCREEN
470 #define SOKOBAN_LEFT BUTTON_MIDLEFT
472 #ifndef SOKOBAN_RIGHT
473 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
476 #define SOKOBAN_UP BUTTON_TOPMIDDLE
479 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
482 #define SOKOBAN_MENU BUTTON_TOPLEFT
483 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
486 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
487 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
490 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
491 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
493 #ifndef SOKOBAN_PAUSE
494 #define SOKOBAN_PAUSE BUTTON_CENTER
495 #define SOKOBAN_PAUSE_NAME "[CENTER]"
497 #ifndef SOKOBAN_LEVEL_REPEAT
498 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
499 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
502 #define BUTTON_SAVE BUTTON_CENTER
503 #define BUTTON_SAVE_NAME "CENTER"
507 #define SOKOBAN_FONT FONT_SYSFIXED
510 /* The Location, Undo and LevelInfo structs are OO-flavored.
511 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
512 * but the overall data layout becomes more manageable. */
514 /* Level data & stats */
516 int index
; /* Level index (level number - 1) */
517 int moves
; /* Moves & pushes for the stats */
519 short boxes_to_go
; /* Number of unplaced boxes remaining in level */
520 short height
; /* Height & width for centering level display */
529 /* Our full undo history */
530 static struct UndoInfo
{
531 int count
; /* How many undos have been done */
532 int current
; /* Which history is the current undo */
533 int max
; /* Which history is the max redoable */
534 char history
[MAX_UNDOS
];
537 /* Our playing board */
538 static struct BoardInfo
{
539 char board
[ROWS
][COLS
]; /* The current board data */
540 struct LevelInfo level
; /* Level data & stats */
541 struct Location player
; /* Where the player is */
542 int max_level
; /* The number of levels we have */
545 static struct BufferedBoards
{
546 char filename
[MAX_PATH
]; /* Filename of the levelset we're using */
547 char data
[MAX_LEVEL_DATA
]; /* Buffered level data */
548 int index
[MAX_LEVELS
+ 1]; /* Where each buffered board begins & ends */
549 int start
; /* Index of first buffered board */
550 int end
; /* Index of last buffered board */
551 short prebuffered_boards
; /* Number of boards before current to store */
555 static char buf
[ROWS
*(COLS
+ 1)]; /* Enough for a whole board or a filename */
558 static void init_undo(void)
561 undo_info
.current
= 0;
565 static void get_delta(char direction
, short *d_r
, short *d_c
)
568 case SOKOBAN_PUSH_LEFT
:
569 case SOKOBAN_MOVE_LEFT
:
573 case SOKOBAN_PUSH_RIGHT
:
574 case SOKOBAN_MOVE_RIGHT
:
578 case SOKOBAN_PUSH_UP
:
579 case SOKOBAN_MOVE_UP
:
583 case SOKOBAN_PUSH_DOWN
:
584 case SOKOBAN_MOVE_DOWN
:
590 static bool undo(void)
594 short d_r
= 0, d_c
= 0; /* delta row & delta col */
595 char *space_cur
, *space_next
, *space_prev
;
596 bool undo_push
= false;
598 /* If no more undos or we've wrapped all the way around, quit */
599 if (undo_info
.count
== 0 || undo_info
.current
- 1 == undo_info
.max
)
602 /* Move to previous undo in the list */
603 if (undo_info
.current
== 0 && undo_info
.count
> 1)
604 undo_info
.current
= MAX_UNDOS
- 1;
610 undo
= undo_info
.history
[undo_info
.current
];
612 if (undo
< SOKOBAN_MOVE_MIN
)
615 get_delta(undo
, &d_r
, &d_c
);
617 r
= current_info
.player
.row
;
618 c
= current_info
.player
.col
;
620 /* Give the 3 spaces we're going to use better names */
621 space_cur
= ¤t_info
.board
[r
][c
];
622 space_next
= ¤t_info
.board
[r
+ d_r
][c
+ d_c
];
623 space_prev
= ¤t_info
.board
[r
- d_r
][c
- d_c
];
625 /* Update board info */
627 /* Moving box from goal to floor */
628 if (*space_next
== '*' && *space_cur
== '@')
629 current_info
.level
.boxes_to_go
++;
630 /* Moving box from floor to goal */
631 else if (*space_next
== '$' && *space_cur
== '+')
632 current_info
.level
.boxes_to_go
--;
634 /* Move box off of next space... */
635 *space_next
= (*space_next
== '*' ? '.' : ' ');
636 /* ...and on to current space */
637 *space_cur
= (*space_cur
== '+' ? '*' : '$');
639 current_info
.level
.pushes
--;
641 /* Just move player off of current space */
642 *space_cur
= (*space_cur
== '+' ? '.' : ' ');
643 /* Move player back to previous space */
644 *space_prev
= (*space_prev
== '.' ? '+' : '@');
646 /* Update position */
647 current_info
.player
.row
-= d_r
;
648 current_info
.player
.col
-= d_c
;
650 current_info
.level
.moves
--;
655 static void add_undo(char undo
)
657 undo_info
.history
[undo_info
.current
] = undo
;
659 /* Wrap around if MAX_UNDOS exceeded */
660 if (undo_info
.current
< (MAX_UNDOS
- 1))
663 undo_info
.current
= 0;
665 if (undo_info
.count
< MAX_UNDOS
)
669 static bool move(char direction
, bool redo
)
672 short d_r
= 0, d_c
= 0; /* delta row & delta col */
673 char *space_cur
, *space_next
, *space_beyond
;
676 get_delta(direction
, &d_r
, &d_c
);
678 r
= current_info
.player
.row
;
679 c
= current_info
.player
.col
;
681 /* Check for out-of-bounds */
682 if (r
+ 2*d_r
< 0 || r
+ 2*d_r
>= ROWS
||
683 c
+ 2*d_c
< 0 || c
+ 2*d_c
>= COLS
)
686 /* Give the 3 spaces we're going to use better names */
687 space_cur
= ¤t_info
.board
[r
][c
];
688 space_next
= ¤t_info
.board
[r
+ d_r
][c
+ d_c
];
689 space_beyond
= ¤t_info
.board
[r
+ 2*d_r
][c
+ 2*d_c
];
691 if (*space_next
== '$' || *space_next
== '*') {
692 /* Change direction from move to push for undo */
693 if (direction
>= SOKOBAN_MOVE_MIN
)
694 direction
-= SOKOBAN_MOVE_DIFF
;
697 else if (direction
< SOKOBAN_MOVE_MIN
)
698 /* Change back to move if redo/solution playback push is invalid */
699 direction
+= SOKOBAN_MOVE_DIFF
;
701 /* Update board info */
703 /* Moving box from goal to floor */
704 if (*space_next
== '*' && *space_beyond
== ' ')
705 current_info
.level
.boxes_to_go
++;
706 /* Moving box from floor to goal */
707 else if (*space_next
== '$' && *space_beyond
== '.')
708 current_info
.level
.boxes_to_go
--;
709 /* Check for invalid move */
710 else if (*space_beyond
!= '.' && *space_beyond
!= ' ')
713 /* Move player onto next space */
714 *space_next
= (*space_next
== '*' ? '+' : '@');
715 /* Move box onto space beyond next */
716 *space_beyond
= (*space_beyond
== '.' ? '*' : '$');
718 current_info
.level
.pushes
++;
720 /* Check for invalid move */
721 if (*space_next
!= '.' && *space_next
!= ' ')
724 /* Move player onto next space */
725 *space_next
= (*space_next
== '.' ? '+' : '@');
727 /* Move player off of current space */
728 *space_cur
= (*space_cur
== '+' ? '.' : ' ');
730 /* Update position */
731 current_info
.player
.row
+= d_r
;
732 current_info
.player
.col
+= d_c
;
734 current_info
.level
.moves
++;
736 /* Update undo_info.max to current on every normal move,
737 * except if it's the same as a redo. */
738 /* normal move and either */
740 /* moves have been undone... */
741 ((undo_info
.max
!= undo_info
.current
&&
742 /* ...and the current move is NOT the same as the one in history */
743 undo_info
.history
[undo_info
.current
] != direction
) ||
744 /* or moves have not been undone */
745 undo_info
.max
== undo_info
.current
)) {
747 undo_info
.max
= undo_info
.current
;
748 } else /* redo move or move was same as redo */
749 add_undo(direction
); /* add_undo to update current */
755 static bool redo(void)
757 /* If no moves have been undone, quit */
758 if (undo_info
.current
== undo_info
.max
)
761 return move(undo_info
.history
[(undo_info
.current
< MAX_UNDOS
?
762 undo_info
.current
: 0)], true);
766 static void init_boards(void)
768 rb
->strlcpy(buffered_boards
.filename
, SOKOBAN_LEVELS_FILE
,
769 sizeof(buffered_boards
.filename
));
771 current_info
.level
.index
= 0;
772 current_info
.player
.row
= 0;
773 current_info
.player
.col
= 0;
774 current_info
.max_level
= 0;
776 buffered_boards
.start
= 0;
777 buffered_boards
.end
= 0;
778 buffered_boards
.prebuffered_boards
= 0;
783 static bool read_levels(bool initialize
)
793 bool index_set
= false;
795 /* Get the index of the first level to buffer */
796 if (current_info
.level
.index
> buffered_boards
.prebuffered_boards
&&
798 buffered_boards
.start
= current_info
.level
.index
-
799 buffered_boards
.prebuffered_boards
;
801 buffered_boards
.start
= 0;
803 if ((fd
= rb
->open(buffered_boards
.filename
, O_RDONLY
)) < 0) {
804 rb
->splashf(HZ
*2, "Unable to open %s", buffered_boards
.filename
);
809 len
= rb
->read_line(fd
, buf
, sizeof(buf
));
811 /* Correct len when trailing \r's or \n's are counted */
812 if (len
> 2 && buf
[len
- 2] == '\0')
814 else if (len
> 1 && buf
[len
- 1] == '\0')
817 /* Skip short lines & lines with non-level data */
818 if (len
>= 3 && ((buf
[0] >= '1' && buf
[0] <= '9') || buf
[0] == '#' ||
819 buf
[0] == ' ' || buf
[0] == '-' || buf
[0] == '_')) {
820 if (level_count
>= buffered_boards
.start
) {
821 /* Set the index of this level */
823 level_count
- buffered_boards
.start
< MAX_LEVELS
) {
824 buffered_boards
.index
[level_count
- buffered_boards
.start
]
828 /* Copy buffer to board data */
829 if (i
+ level_len
+ len
< MAX_LEVEL_DATA
) {
830 rb
->memcpy(&buffered_boards
.data
[i
+ level_len
], buf
, len
);
831 buffered_boards
.data
[i
+ level_len
+ len
] = '\n';
834 level_len
+= len
+ 1;
837 /* If newline & level is tall enough or is RLE */
838 } else if (buf
[0] == '\0' && (row
> 2 || lastlen
> 22)) {
840 if (level_count
>= buffered_boards
.start
) {
842 if (i
< MAX_LEVEL_DATA
)
843 buffered_boards
.end
= level_count
;
844 else if (!initialize
)
854 } while ((lastlen
= len
));
856 /* Set the index of the end of the last level */
857 if (level_count
- buffered_boards
.start
< MAX_LEVELS
)
858 buffered_boards
.index
[level_count
- buffered_boards
.start
] = i
;
861 current_info
.max_level
= level_count
;
862 buffered_boards
.prebuffered_boards
= buffered_boards
.end
/2;
870 static void load_level(void)
875 int index
= current_info
.level
.index
- buffered_boards
.start
;
878 /* Get the buffered board index of the current level */
879 if (current_info
.level
.index
< buffered_boards
.start
||
880 current_info
.level
.index
>= buffered_boards
.end
) {
882 if (current_info
.level
.index
> buffered_boards
.prebuffered_boards
)
883 index
= buffered_boards
.prebuffered_boards
;
885 index
= current_info
.level
.index
;
887 level
= &buffered_boards
.data
[buffered_boards
.index
[index
]];
889 /* Reset level info */
890 current_info
.level
.moves
= 0;
891 current_info
.level
.pushes
= 0;
892 current_info
.level
.boxes_to_go
= 0;
893 current_info
.level
.width
= 0;
896 for (r
= 0; r
< ROWS
; r
++)
897 for (c
= 0; c
< COLS
; c
++)
898 current_info
.board
[r
][c
] = 'X';
900 level_size
= buffered_boards
.index
[index
+ 1] -
901 buffered_boards
.index
[index
];
903 for (r
= 0, c
= 0, n
= 1, i
= 0; i
< level_size
; i
++) {
904 if (level
[i
] == '\n' || level
[i
] == '|') {
906 /* Update max width of level & go to next row */
907 if (c
> current_info
.level
.width
)
908 current_info
.level
.width
= c
;
914 } else if (c
< COLS
) {
915 /* Read RLE character's length into n */
916 if (level
[i
] >= '0' && level
[i
] <= '9') {
917 n
= level
[i
++] - '0';
918 if (level
[i
] >= '0' && level
[i
] <= '9')
919 n
= n
*10 + level
[i
++] - '0';
922 /* Cleanup & replace */
925 else if (level
[i
] == '-' || level
[i
] == '_')
933 current_info
.level
.boxes_to_go
+= n
;
935 /* Put RLE character n times */
937 current_info
.board
[r
][c
++] = level
[i
];
941 if (level
[i
] == '.' || level
[i
] == '+')
942 current_info
.level
.boxes_to_go
++;
944 if (level
[i
] == '@' ||level
[i
] == '+') {
945 current_info
.player
.row
= r
;
946 current_info
.player
.col
= c
;
949 current_info
.board
[r
][c
++] = level
[i
];
954 current_info
.level
.height
= r
;
957 /* Fill in blank space outside level on color targets */
958 for (r
= 0; r
< ROWS
; r
++)
959 for (c
= 0; current_info
.board
[r
][c
] == ' ' && c
< COLS
; c
++)
960 current_info
.board
[r
][c
] = 'X';
962 for (c
= 0; c
< COLS
; c
++) {
963 for (r
= 0; (current_info
.board
[r
][c
] == ' ' ||
964 current_info
.board
[r
][c
] == 'X') && r
< ROWS
; r
++)
965 current_info
.board
[r
][c
] = 'X';
966 for (r
= ROWS
- 1; (current_info
.board
[r
][c
] == ' ' ||
967 current_info
.board
[r
][c
] == 'X') && r
>= 0; r
--)
968 current_info
.board
[r
][c
] = 'X';
973 static void update_screen(void)
978 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
979 #define STAT_HEIGHT 25
980 #define STAT_X (LCD_WIDTH - 120)/2
981 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
982 #define BOARD_WIDTH LCD_WIDTH
983 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
984 rb
->lcd_putsxy(STAT_X
+ 4, STAT_Y
+ 4, "Level");
985 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.index
+ 1);
986 rb
->lcd_putsxy(STAT_X
+ 7, STAT_Y
+ 14, buf
);
987 rb
->lcd_putsxy(STAT_X
+ 41, STAT_Y
+ 4, "Moves");
988 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.moves
);
989 rb
->lcd_putsxy(STAT_X
+ 44, STAT_Y
+ 14, buf
);
990 rb
->lcd_putsxy(STAT_X
+ 79, STAT_Y
+ 4, "Pushes");
991 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.pushes
);
992 rb
->lcd_putsxy(STAT_X
+ 82, STAT_Y
+ 14, buf
);
994 rb
->lcd_drawrect(STAT_X
, STAT_Y
, 38, STAT_HEIGHT
);
995 rb
->lcd_drawrect(STAT_X
+ 37, STAT_Y
, 39, STAT_HEIGHT
);
996 rb
->lcd_drawrect(STAT_X
+ 75, STAT_Y
, 45, STAT_HEIGHT
);
998 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
999 #define STAT_X (LCD_WIDTH - 40)
1001 #define STAT_X COLS*SOKOBAN_TILESIZE
1003 #define STAT_Y (LCD_HEIGHT - 64)/2
1004 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
1005 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
1006 #define BOARD_HEIGHT LCD_HEIGHT
1007 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 2, "Level");
1008 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.index
+ 1);
1009 rb
->lcd_putsxy(STAT_X
+ 4, STAT_Y
+ 12, buf
);
1010 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 23, "Moves");
1011 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.moves
);
1012 rb
->lcd_putsxy(STAT_X
+ 4, STAT_Y
+ 33, buf
);
1014 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 44, "Push");
1016 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 44, "Pushes");
1018 rb
->snprintf(buf
, sizeof(buf
), "%d", current_info
.level
.pushes
);
1019 rb
->lcd_putsxy(STAT_X
+ 4, STAT_Y
+ 54, buf
);
1021 rb
->lcd_drawrect(STAT_X
, STAT_Y
+ 0, STAT_WIDTH
, 64);
1022 rb
->lcd_hline(STAT_X
, LCD_WIDTH
- 1, STAT_Y
+ 21);
1023 rb
->lcd_hline(STAT_X
, LCD_WIDTH
- 1, STAT_Y
+ 42);
1027 /* load the board to the screen */
1028 for (rows
= 0; rows
< ROWS
; rows
++) {
1029 for (cols
= 0; cols
< COLS
; cols
++) {
1030 c
= cols
*SOKOBAN_TILESIZE
+
1031 (BOARD_WIDTH
- current_info
.level
.width
*SOKOBAN_TILESIZE
)/2;
1032 r
= rows
*SOKOBAN_TILESIZE
+
1033 (BOARD_HEIGHT
- current_info
.level
.height
*SOKOBAN_TILESIZE
)/2;
1035 switch(current_info
.board
[rows
][cols
]) {
1036 case 'X': /* blank space outside of level */
1039 case ' ': /* floor */
1040 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 0*SOKOBAN_TILESIZE
,
1041 STRIDE( SCREEN_MAIN
,
1042 BMPWIDTH_sokoban_tiles
,
1043 BMPHEIGHT_sokoban_tiles
),
1044 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1047 case '#': /* wall */
1048 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 1*SOKOBAN_TILESIZE
,
1049 STRIDE( SCREEN_MAIN
,
1050 BMPWIDTH_sokoban_tiles
,
1051 BMPHEIGHT_sokoban_tiles
),
1052 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1056 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 2*SOKOBAN_TILESIZE
,
1057 STRIDE( SCREEN_MAIN
,
1058 BMPWIDTH_sokoban_tiles
,
1059 BMPHEIGHT_sokoban_tiles
),
1060 c
, r
, SOKOBAN_TILESIZE
,SOKOBAN_TILESIZE
);
1063 case '*': /* box on goal */
1064 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 3*SOKOBAN_TILESIZE
,
1065 STRIDE( SCREEN_MAIN
,
1066 BMPWIDTH_sokoban_tiles
,
1067 BMPHEIGHT_sokoban_tiles
),
1068 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1071 case '.': /* goal */
1072 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 4*SOKOBAN_TILESIZE
,
1073 STRIDE( SCREEN_MAIN
,
1074 BMPWIDTH_sokoban_tiles
,
1075 BMPHEIGHT_sokoban_tiles
),
1076 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1079 case '@': /* player */
1080 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 5*SOKOBAN_TILESIZE
,
1081 STRIDE( SCREEN_MAIN
,
1082 BMPWIDTH_sokoban_tiles
,
1083 BMPHEIGHT_sokoban_tiles
),
1084 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1087 case '+': /* player on goal */
1088 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 6*SOKOBAN_TILESIZE
,
1089 STRIDE( SCREEN_MAIN
,
1090 BMPWIDTH_sokoban_tiles
,
1091 BMPHEIGHT_sokoban_tiles
),
1092 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1098 /* print out the screen */
1102 static void draw_level(void)
1105 rb
->lcd_clear_display();
1109 static bool save(char *filename
, bool solution
)
1114 char dirname
[MAX_PATH
];
1116 rb
->splash(0, "Saving...");
1118 /* Create dir if it doesn't exist */
1119 if ((loc
= rb
->strrchr(filename
, '/')) != NULL
) {
1120 rb
->strlcpy(dirname
, filename
, loc
- filename
+ 1);
1122 if(!(dir
= rb
->opendir(dirname
)))
1128 if (filename
[0] == '\0' ||
1129 (fd
= rb
->open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666)) < 0) {
1130 rb
->splashf(HZ
*2, "Unable to open %s", filename
);
1134 /* Sokoban: S/P for solution/progress : level number : current undo */
1135 rb
->snprintf(buf
, sizeof(buf
), "Sokoban:%c:%d:%d\n", (solution
? 'S' : 'P'),
1136 current_info
.level
.index
+ 1, undo_info
.current
);
1137 rb
->write(fd
, buf
, rb
->strlen(buf
));
1139 /* Filename of levelset */
1140 rb
->write(fd
, buffered_boards
.filename
,
1141 rb
->strlen(buffered_boards
.filename
));
1142 rb
->write(fd
, "\n", 1);
1144 /* Full undo history */
1145 rb
->write(fd
, undo_info
.history
, undo_info
.max
);
1152 static bool load(char *filename
, bool silent
)
1159 bool paused
= false;
1160 unsigned short speed
= 2;
1161 int delay
[] = {HZ
/2, HZ
/3, HZ
/4, HZ
/6, HZ
/8, HZ
/12, HZ
/16, HZ
/25};
1163 if (filename
[0] == '\0' || (fd
= rb
->open(filename
, O_RDONLY
)) < 0) {
1165 rb
->splashf(HZ
*2, "Unable to open %s", filename
);
1169 /* Read header, level number, & current undo */
1170 rb
->read_line(fd
, buf
, sizeof(buf
));
1172 /* If we're opening a level file, not a solution/progress file */
1173 if (rb
->strncmp(buf
, "Sokoban", 7) != 0) {
1176 rb
->strlcpy(buffered_boards
.filename
, filename
,
1177 sizeof(buffered_boards
.filename
));
1179 if (!read_levels(true))
1182 current_info
.level
.index
= 0;
1185 /* If there aren't any boxes to go or the player position wasn't set,
1186 * the file probably wasn't a Sokoban level file */
1187 if (current_info
.level
.boxes_to_go
== 0 ||
1188 current_info
.player
.row
== 0 || current_info
.player
.col
== 0) {
1190 rb
->splash(HZ
*2, "File is not a Sokoban level file");
1196 /* Read filename of levelset */
1197 rb
->read_line(fd
, buffered_boards
.filename
,
1198 sizeof(buffered_boards
.filename
));
1200 /* Read full undo history */
1201 len
= rb
->read_line(fd
, undo_info
.history
, MAX_UNDOS
);
1203 /* Correct len when trailing \r's or \n's are counted */
1204 if (len
> 2 && undo_info
.history
[len
- 2] == '\0')
1206 else if (len
> 1 && undo_info
.history
[len
- 1] == '\0')
1211 /* Check to see if we're going to play a solution or resume progress */
1212 play_solution
= (buf
[8] == 'S');
1214 /* Get level number */
1215 for (n
= 0, i
= 10; buf
[i
] >= '0' && buf
[i
] <= '9' && i
< 15; i
++)
1216 n
= n
*10 + buf
[i
] - '0';
1217 current_info
.level
.index
= n
- 1;
1219 /* Get undo index */
1220 for (n
= 0, i
++; buf
[i
] >= '0' && buf
[i
] <= '9' && i
< 21; i
++)
1221 n
= n
*10 + buf
[i
] - '0';
1224 undo_info
.max
= len
;
1226 if (current_info
.level
.index
< 0) {
1228 rb
->splash(HZ
*2, "Error loading level");
1231 if (!read_levels(true))
1233 if (current_info
.level
.index
>= current_info
.max_level
) {
1235 rb
->splash(HZ
*2, "Error loading level");
1241 if (play_solution
) {
1242 rb
->lcd_clear_display();
1244 rb
->sleep(2*delay
[speed
]);
1246 /* Replay solution until menu button is pressed */
1250 if (!move(undo_info
.history
[i
], true)) {
1254 rb
->lcd_clear_display();
1260 rb
->sleep(delay
[speed
]);
1262 while ((button
= rb
->button_get(false)) || paused
) {
1265 /* Pretend the level is complete so we'll quit */
1266 current_info
.level
.boxes_to_go
= 0;
1270 /* Toggle pause state */
1275 case SOKOBAN_LEFT
| BUTTON_REPEAT
:
1276 /* Go back one move */
1280 rb
->lcd_clear_display();
1286 case SOKOBAN_RIGHT
| BUTTON_REPEAT
:
1287 /* Go forward one move */
1291 rb
->lcd_clear_display();
1297 case SOKOBAN_UP
| BUTTON_REPEAT
:
1299 if (speed
< sizeof(delay
)/sizeof(int) - 1)
1304 case SOKOBAN_DOWN
| BUTTON_REPEAT
:
1315 /* If level is complete, wait for keypress before quitting */
1316 if (current_info
.level
.boxes_to_go
== 0)
1317 rb
->button_get(true);
1320 /* Advance to current undo */
1321 for (i
= 0; i
< n
; i
++) {
1322 if (!move(undo_info
.history
[i
], true)) {
1328 rb
->button_clear_queue();
1329 rb
->lcd_clear_display();
1332 undo_info
.current
= n
;
1338 static int sokoban_menu(void)
1344 int start_selected
= 0;
1345 int prev_level
= current_info
.level
.index
;
1347 MENUITEM_STRINGLIST(menu
, "Sokoban Menu", NULL
,
1348 "Resume", "Select Level", "Audio Playback", "Keys",
1349 "Load Default Level Set", "Quit Without Saving",
1350 "Save Progress & Quit");
1354 selection
= rb
->do_menu(&menu
, &start_selected
, NULL
, false);
1356 switch (selection
) {
1357 case 0: /* Resume */
1360 case 1: /* Select level */
1361 current_info
.level
.index
++;
1362 rb
->set_int("Select Level", "", UNIT_INT
,
1363 ¤t_info
.level
.index
, NULL
, 1, 1,
1364 current_info
.max_level
, NULL
);
1365 current_info
.level
.index
--;
1366 if (prev_level
!= current_info
.level
.index
) {
1373 case 2: /* Audio playback control */
1374 playback_control(NULL
);
1380 rb
->screens
[i
]->clear_display();
1381 rb
->lcd_setfont(SOKOBAN_FONT
);
1383 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1384 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1385 rb
->lcd_putsxy(3, 6, "[OFF] Menu");
1386 rb
->lcd_putsxy(3, 16, "[ON] Undo");
1387 rb
->lcd_putsxy(3, 26, "[PLAY] Redo");
1388 rb
->lcd_putsxy(3, 36, "[F1] Down a Level");
1389 rb
->lcd_putsxy(3, 46, "[F2] Restart Level");
1390 rb
->lcd_putsxy(3, 56, "[F3] Up a Level");
1391 #elif CONFIG_KEYPAD == ONDIO_PAD
1392 rb
->lcd_putsxy(3, 6, "[OFF] Menu");
1393 rb
->lcd_putsxy(3, 16, "[MODE] Undo");
1394 rb
->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1395 rb
->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1396 rb
->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1397 rb
->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1398 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1399 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1400 rb
->lcd_putsxy(3, 6, "[STOP] Menu");
1401 rb
->lcd_putsxy(3, 16, "[REC] Undo");
1402 rb
->lcd_putsxy(3, 26, "[MODE] Redo");
1403 rb
->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1404 rb
->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1405 rb
->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1406 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1407 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1408 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1409 rb
->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1410 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1411 rb
->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1412 rb
->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1413 rb
->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1414 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1415 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1416 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1417 rb
->lcd_putsxy(3, 26, "[PLAY] Redo");
1418 rb
->lcd_putsxy(3, 36, "[REC] Restart Level");
1419 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1420 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1421 rb
->lcd_putsxy(3, 16, "[REW] Undo");
1422 rb
->lcd_putsxy(3, 26, "[FF] Redo");
1423 rb
->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1424 rb
->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1425 rb
->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1426 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1427 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1428 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1429 rb
->lcd_putsxy(3, 26, "[A] Redo");
1430 rb
->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1431 rb
->lcd_putsxy(3, 46, "[MENU] Restart Level");
1432 rb
->lcd_putsxy(3, 56, "[VOL+] Next Level");
1433 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1434 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1435 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1436 rb
->lcd_putsxy(3, 26, "[REC] Redo");
1437 rb
->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1438 rb
->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1439 rb
->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1440 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1441 rb
->lcd_putsxy(3, 6, "[MENU] Menu");
1442 rb
->lcd_putsxy(3, 16, "[VOL+] Undo");
1443 rb
->lcd_putsxy(3, 26, "[VOL-] Redo");
1444 rb
->lcd_putsxy(3, 36, "[PREV] Previous Level");
1445 rb
->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1446 rb
->lcd_putsxy(3, 56, "[NEXT] Next Level");
1449 #ifdef HAVE_TOUCHSCREEN
1450 rb
->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME
" Menu");
1451 rb
->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME
" Undo");
1452 rb
->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME
" Redo");
1453 rb
->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME
" Pause");
1454 rb
->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME
" Restart Level");
1458 rb
->screens
[i
]->update();
1460 /* Display until keypress */
1463 button
= rb
->button_get(false);
1464 } while (!button
|| button
& BUTTON_REL
||
1465 button
& BUTTON_REPEAT
);
1470 case 4: /* Load default levelset */
1472 if (!read_levels(true))
1473 return 5; /* Quit */
1480 case 6: /* Save & quit */
1481 save(SOKOBAN_SAVE_FILE
, false);
1482 rb
->reload_directory();
1485 } while (!menu_quit
);
1488 rb
->lcd_setfont(SOKOBAN_FONT
);
1491 rb
->screens
[i
]->clear_display();
1492 rb
->screens
[i
]->update();
1498 static bool sokoban_loop(void)
1501 int i
= 0, button
= 0, lastbutton
= 0;
1509 r
= current_info
.player
.row
;
1510 c
= current_info
.player
.col
;
1512 button
= rb
->button_get(true);
1516 #ifdef SOKOBAN_RC_MENU
1517 case SOKOBAN_RC_MENU
:
1520 switch (sokoban_menu()) {
1522 case 6: /* Save & quit */
1529 #ifdef SOKOBAN_UNDO_PRE
1530 if (lastbutton
!= SOKOBAN_UNDO_PRE
)
1532 #else /* repeat can't work here for Ondio, iPod, et al */
1533 case SOKOBAN_UNDO
| BUTTON_REPEAT
:
1536 rb
->lcd_clear_display();
1542 case SOKOBAN_REDO
| BUTTON_REPEAT
:
1544 rb
->lcd_clear_display();
1549 #ifdef SOKOBAN_LEVEL_UP
1550 case SOKOBAN_LEVEL_UP
:
1551 case SOKOBAN_LEVEL_UP
| BUTTON_REPEAT
:
1554 if (current_info
.level
.index
+ 1 < current_info
.max_level
)
1555 current_info
.level
.index
++;
1561 #ifdef SOKOBAN_LEVEL_DOWN
1562 case SOKOBAN_LEVEL_DOWN
:
1563 case SOKOBAN_LEVEL_DOWN
| BUTTON_REPEAT
:
1564 /* previous level */
1566 if (current_info
.level
.index
> 0)
1567 current_info
.level
.index
--;
1573 #ifdef SOKOBAN_LEVEL_REPEAT
1574 case SOKOBAN_LEVEL_REPEAT
:
1575 case SOKOBAN_LEVEL_REPEAT
| BUTTON_REPEAT
:
1583 case SOKOBAN_LEFT
| BUTTON_REPEAT
:
1584 moved
= move(SOKOBAN_MOVE_LEFT
, false);
1588 case SOKOBAN_RIGHT
| BUTTON_REPEAT
:
1589 moved
= move(SOKOBAN_MOVE_RIGHT
, false);
1593 case SOKOBAN_UP
| BUTTON_REPEAT
:
1594 moved
= move(SOKOBAN_MOVE_UP
, false);
1598 case SOKOBAN_DOWN
| BUTTON_REPEAT
:
1599 moved
= move(SOKOBAN_MOVE_DOWN
, false);
1603 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1604 return PLUGIN_USB_CONNECTED
;
1608 lastbutton
= button
;
1611 rb
->lcd_clear_display();
1615 /* We have completed this level */
1616 if (current_info
.level
.boxes_to_go
== 0) {
1619 rb
->lcd_clear_display();
1621 /* Show level complete message & stats */
1622 rb
->snprintf(buf
, sizeof(buf
), "Level %d Complete!",
1623 current_info
.level
.index
+ 1);
1624 rb
->lcd_getstringsize(buf
, &w
, &h
);
1625 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
*3, buf
);
1627 rb
->snprintf(buf
, sizeof(buf
), "%4d Moves ",
1628 current_info
.level
.moves
);
1629 rb
->lcd_getstringsize(buf
, &w
, &h
);
1630 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
, buf
);
1632 rb
->snprintf(buf
, sizeof(buf
), "%4d Pushes",
1633 current_info
.level
.pushes
);
1634 rb
->lcd_getstringsize(buf
, &w
, &h
);
1635 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2, buf
);
1637 if (undo_info
.count
< MAX_UNDOS
) {
1638 rb
->snprintf(buf
, sizeof(buf
), "%s: Save solution",
1640 rb
->lcd_getstringsize(buf
, &w
, &h
);
1641 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 + h
*2, buf
);
1646 rb
->button_clear_queue();
1648 /* Display for 4 seconds or until new keypress */
1649 for (i
= 0; i
< 80; i
++) {
1651 button
= rb
->button_get(false);
1652 if (button
&& !(button
& BUTTON_REL
) &&
1653 !(button
& BUTTON_REPEAT
))
1657 if (button
== BUTTON_SAVE
) {
1658 if (undo_info
.count
< MAX_UNDOS
) {
1659 /* Set filename to current levelset plus level number
1660 * and .sok extension. Use SAVE_FOLDER if using the
1661 * default levelset, since it's in a hidden folder. */
1662 if (rb
->strcmp(buffered_boards
.filename
,
1663 SOKOBAN_LEVELS_FILE
) == 0) {
1664 rb
->snprintf(buf
, sizeof(buf
),
1665 "%s/sokoban.%d.sok",
1666 SOKOBAN_SAVE_FOLDER
,
1667 current_info
.level
.index
+ 1);
1669 if ((loc
= rb
->strrchr(buffered_boards
.filename
,
1672 rb
->snprintf(buf
, sizeof(buf
), "%s.%d.sok",
1673 buffered_boards
.filename
,
1674 current_info
.level
.index
+ 1);
1679 if (!rb
->kbd_input(buf
, MAX_PATH
))
1682 rb
->splash(HZ
*2, "Solution too long to save");
1684 rb
->lcd_setfont(SOKOBAN_FONT
); /* Restore font */
1689 rb
->screens
[i
]->clear_display();
1690 rb
->screens
[i
]->update();
1693 current_info
.level
.index
++;
1695 /* clear undo stats */
1698 if (current_info
.level
.index
>= current_info
.max_level
) {
1699 /* Show levelset complete message */
1700 rb
->snprintf(buf
, sizeof(buf
), "You WIN!!");
1701 rb
->lcd_getstringsize(buf
, &w
, &h
);
1702 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
/2, buf
);
1704 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1705 /* Display for 4 seconds or until keypress */
1706 for (i
= 0; i
< 80; i
++) {
1707 rb
->lcd_fillrect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
1711 button
= rb
->button_get(false);
1712 if (button
&& !(button
& BUTTON_REL
))
1715 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1717 /* Reset to first level & show quit menu */
1718 current_info
.level
.index
= 0;
1720 switch (sokoban_menu()) {
1722 case 6: /* Save & quit */
1737 enum plugin_status
plugin_start(const void* parameter
)
1743 rb
->lcd_setfont(SOKOBAN_FONT
);
1745 rb
->lcd_clear_display();
1746 rb
->lcd_getstringsize(SOKOBAN_TITLE
, &w
, &h
);
1747 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
/2, SOKOBAN_TITLE
);
1749 rb
->sleep(HZ
); /* Show title for 1 second */
1753 if (parameter
== NULL
) {
1754 /* Attempt to resume saved progress, otherwise start at beginning */
1755 if (!load(SOKOBAN_SAVE_FILE
, true)) {
1757 if (!read_levels(true))
1763 /* The plugin is being used to open a file */
1764 if (load((char*) parameter
, false)) {
1765 /* If we loaded & played a solution, quit */
1766 if (current_info
.level
.boxes_to_go
== 0)
1772 rb
->lcd_clear_display();
1775 return sokoban_loop();