Prepare new maemo release
[maemo-rb.git] / apps / plugins / sokoban.c
blob79db233113d0fd70825d8fed4d5052c353998acd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
24 #include "plugin.h"
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_DATA_DIR "/sokoban.save"
34 #ifdef APPLICATION
35 #define SOKOBAN_SAVE_FOLDER PLUGIN_GAMES_DATA_DIR
36 #else
37 #define SOKOBAN_SAVE_FOLDER "/games"
38 #endif
40 #include "pluginbitmaps/sokoban_tiles.h"
41 #define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
43 /* If tilesize is 0 (which it is during dependency generation) gcc will abort
44 (div by 0) and this plugin won't get any dependencies
46 #if SOKOBAN_TILESIZE < 1
47 #define SOKOBAN_TILESIZE 10
48 #endif
50 /* SOKOBAN_TILESIZE is the number of pixels for each block.
51 * Set dynamically so all targets can support levels
52 * that fill their entire screen, less the stat box.
53 * 16 rows & 20 cols minimum */
54 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
55 #define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
56 #if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
57 #define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
58 #else
59 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
60 #endif
61 #else /* vertical layout*/
62 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
63 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
64 #endif
66 /* size of code+bss */
67 #if CONFIG_CPU == SH7034
68 #define CODE_SIZE 0x3000 /* 12k */
69 #else
70 #define CODE_SIZE 0x5000 /* 20k */
71 #endif
73 #define CODE_AND_UNDO_SIZE (CODE_SIZE+0x1000) /* + 4k */
75 /* Use either all but code & undo of the plugin buffer for level data
76 * or 128k, which ever is less */
77 #if PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE < 0x20000
78 #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE)
79 #else
80 #define MAX_LEVEL_DATA 0x20000
81 #endif
83 /* Number of levels for which to allocate buffer indexes */
84 #define MAX_LEVELS MAX_LEVEL_DATA/70
86 /* Use remaining plugin buffer (- code prog) for undo, up to 64k */
87 #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE > 0x10000
88 #define MAX_UNDOS 0x10000
89 #else
90 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE)
91 #endif
93 /* Move/push definitions for undo */
94 #define SOKOBAN_PUSH_LEFT 'L'
95 #define SOKOBAN_PUSH_RIGHT 'R'
96 #define SOKOBAN_PUSH_UP 'U'
97 #define SOKOBAN_PUSH_DOWN 'D'
98 #define SOKOBAN_MOVE_LEFT 'l'
99 #define SOKOBAN_MOVE_RIGHT 'r'
100 #define SOKOBAN_MOVE_UP 'u'
101 #define SOKOBAN_MOVE_DOWN 'd'
103 #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
104 #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
106 /* variable button definitions */
107 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
108 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
109 #define SOKOBAN_LEFT BUTTON_LEFT
110 #define SOKOBAN_RIGHT BUTTON_RIGHT
111 #define SOKOBAN_UP BUTTON_UP
112 #define SOKOBAN_DOWN BUTTON_DOWN
113 #define SOKOBAN_MENU BUTTON_OFF
114 #define SOKOBAN_UNDO BUTTON_ON
115 #define SOKOBAN_REDO BUTTON_PLAY
116 #define SOKOBAN_LEVEL_DOWN BUTTON_F1
117 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2
118 #define SOKOBAN_LEVEL_UP BUTTON_F3
119 #define SOKOBAN_PAUSE BUTTON_PLAY
120 #define BUTTON_SAVE BUTTON_ON
121 #define BUTTON_SAVE_NAME "ON"
123 #elif CONFIG_KEYPAD == ONDIO_PAD
124 #define SOKOBAN_LEFT BUTTON_LEFT
125 #define SOKOBAN_RIGHT BUTTON_RIGHT
126 #define SOKOBAN_UP BUTTON_UP
127 #define SOKOBAN_DOWN BUTTON_DOWN
128 #define SOKOBAN_MENU BUTTON_OFF
129 #define SOKOBAN_UNDO_PRE BUTTON_MENU
130 #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
131 #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
132 #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
133 #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
134 #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
135 #define SOKOBAN_PAUSE BUTTON_MENU
136 #define BUTTON_SAVE BUTTON_MENU
137 #define BUTTON_SAVE_NAME "MENU"
139 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
140 (CONFIG_KEYPAD == IRIVER_H300_PAD)
141 #define SOKOBAN_LEFT BUTTON_LEFT
142 #define SOKOBAN_RIGHT BUTTON_RIGHT
143 #define SOKOBAN_UP BUTTON_UP
144 #define SOKOBAN_DOWN BUTTON_DOWN
145 #define SOKOBAN_MENU BUTTON_OFF
146 #define SOKOBAN_UNDO BUTTON_REC
147 #define SOKOBAN_REDO BUTTON_MODE
148 #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
149 #define SOKOBAN_LEVEL_REPEAT BUTTON_ON
150 #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
151 #define SOKOBAN_PAUSE BUTTON_ON
152 #define BUTTON_SAVE BUTTON_MODE
153 #define BUTTON_SAVE_NAME "MODE"
155 #define SOKOBAN_RC_MENU BUTTON_RC_STOP
157 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
158 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
159 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
160 #define SOKOBAN_LEFT BUTTON_LEFT
161 #define SOKOBAN_RIGHT BUTTON_RIGHT
162 #define SOKOBAN_UP BUTTON_MENU
163 #define SOKOBAN_DOWN BUTTON_PLAY
164 #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
165 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
166 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
167 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
168 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
169 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
170 #define SOKOBAN_PAUSE BUTTON_SELECT
171 #define BUTTON_SAVE BUTTON_SELECT
172 #define BUTTON_SAVE_NAME "SELECT"
174 /* FIXME: if/when simultaneous button presses work for X5/M5,
175 * add level up/down */
176 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
177 #define SOKOBAN_LEFT BUTTON_LEFT
178 #define SOKOBAN_RIGHT BUTTON_RIGHT
179 #define SOKOBAN_UP BUTTON_UP
180 #define SOKOBAN_DOWN BUTTON_DOWN
181 #define SOKOBAN_MENU BUTTON_POWER
182 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
183 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
184 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
185 #define SOKOBAN_REDO BUTTON_PLAY
186 #define SOKOBAN_PAUSE BUTTON_PLAY
187 #define BUTTON_SAVE BUTTON_SELECT
188 #define BUTTON_SAVE_NAME "SELECT"
190 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
191 #define SOKOBAN_LEFT BUTTON_LEFT
192 #define SOKOBAN_RIGHT BUTTON_RIGHT
193 #define SOKOBAN_UP BUTTON_SCROLL_UP
194 #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
195 #define SOKOBAN_MENU BUTTON_POWER
196 #define SOKOBAN_UNDO_PRE BUTTON_REW
197 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
198 #define SOKOBAN_REDO BUTTON_FF
199 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
200 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
201 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
202 #define SOKOBAN_PAUSE BUTTON_PLAY
203 #define BUTTON_SAVE BUTTON_PLAY
204 #define BUTTON_SAVE_NAME "PLAY"
206 #elif CONFIG_KEYPAD == GIGABEAT_PAD
207 #define SOKOBAN_LEFT BUTTON_LEFT
208 #define SOKOBAN_RIGHT BUTTON_RIGHT
209 #define SOKOBAN_UP BUTTON_UP
210 #define SOKOBAN_DOWN BUTTON_DOWN
211 #define SOKOBAN_MENU BUTTON_POWER
212 #define SOKOBAN_UNDO BUTTON_SELECT
213 #define SOKOBAN_REDO BUTTON_A
214 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
215 #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
216 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
217 #define SOKOBAN_PAUSE BUTTON_SELECT
218 #define BUTTON_SAVE BUTTON_SELECT
219 #define BUTTON_SAVE_NAME "SELECT"
221 #elif CONFIG_KEYPAD == SANSA_E200_PAD
222 #define SOKOBAN_LEFT BUTTON_LEFT
223 #define SOKOBAN_RIGHT BUTTON_RIGHT
224 #define SOKOBAN_UP BUTTON_UP
225 #define SOKOBAN_DOWN BUTTON_DOWN
226 #define SOKOBAN_MENU BUTTON_POWER
227 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
228 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
229 #define SOKOBAN_REDO BUTTON_REC
230 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
231 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
232 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
233 #define SOKOBAN_PAUSE BUTTON_SELECT
234 #define BUTTON_SAVE BUTTON_SELECT
235 #define BUTTON_SAVE_NAME "SELECT"
237 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
238 #define SOKOBAN_LEFT BUTTON_LEFT
239 #define SOKOBAN_RIGHT BUTTON_RIGHT
240 #define SOKOBAN_UP BUTTON_UP
241 #define SOKOBAN_DOWN BUTTON_DOWN
242 #define SOKOBAN_MENU (BUTTON_HOME|BUTTON_REPEAT)
243 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
244 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
245 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_LEFT)
246 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
247 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
248 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
249 #define SOKOBAN_PAUSE BUTTON_SELECT
250 #define BUTTON_SAVE BUTTON_SELECT
251 #define BUTTON_SAVE_NAME "SELECT"
253 #elif CONFIG_KEYPAD == SANSA_C200_PAD
254 #define SOKOBAN_LEFT BUTTON_LEFT
255 #define SOKOBAN_RIGHT BUTTON_RIGHT
256 #define SOKOBAN_UP BUTTON_UP
257 #define SOKOBAN_DOWN BUTTON_DOWN
258 #define SOKOBAN_MENU BUTTON_POWER
259 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
260 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
261 #define SOKOBAN_REDO BUTTON_REC
262 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
263 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
264 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
265 #define SOKOBAN_PAUSE BUTTON_SELECT
266 #define BUTTON_SAVE BUTTON_SELECT
267 #define BUTTON_SAVE_NAME "SELECT"
269 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
270 #define SOKOBAN_LEFT BUTTON_LEFT
271 #define SOKOBAN_RIGHT BUTTON_RIGHT
272 #define SOKOBAN_UP BUTTON_UP
273 #define SOKOBAN_DOWN BUTTON_DOWN
274 #define SOKOBAN_MENU BUTTON_POWER
275 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
276 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
277 #define SOKOBAN_REDO BUTTON_HOME
278 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
279 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
280 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
281 #define SOKOBAN_PAUSE BUTTON_SELECT
282 #define BUTTON_SAVE BUTTON_SELECT
283 #define BUTTON_SAVE_NAME "SELECT"
285 #elif CONFIG_KEYPAD == SANSA_M200_PAD
286 #define SOKOBAN_LEFT BUTTON_LEFT
287 #define SOKOBAN_RIGHT BUTTON_RIGHT
288 #define SOKOBAN_UP BUTTON_UP
289 #define SOKOBAN_DOWN BUTTON_DOWN
290 #define SOKOBAN_MENU BUTTON_POWER
291 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
292 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
293 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_UP)
294 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
295 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
296 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
297 #define SOKOBAN_PAUSE BUTTON_SELECT
298 #define BUTTON_SAVE BUTTON_SELECT
299 #define BUTTON_SAVE_NAME "SELECT"
301 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
302 #define SOKOBAN_LEFT BUTTON_LEFT
303 #define SOKOBAN_RIGHT BUTTON_RIGHT
304 #define SOKOBAN_UP BUTTON_UP
305 #define SOKOBAN_DOWN BUTTON_DOWN
306 #define SOKOBAN_MENU BUTTON_MENU
307 #define SOKOBAN_UNDO BUTTON_VOL_UP
308 #define SOKOBAN_REDO BUTTON_VOL_DOWN
309 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
310 #define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
311 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
312 #define SOKOBAN_PAUSE BUTTON_SELECT
313 #define BUTTON_SAVE BUTTON_SELECT
314 #define BUTTON_SAVE_NAME "SELECT"
316 #elif CONFIG_KEYPAD == MROBE100_PAD
317 #define SOKOBAN_LEFT BUTTON_LEFT
318 #define SOKOBAN_RIGHT BUTTON_RIGHT
319 #define SOKOBAN_UP BUTTON_UP
320 #define SOKOBAN_DOWN BUTTON_DOWN
321 #define SOKOBAN_MENU BUTTON_POWER
322 #define SOKOBAN_UNDO BUTTON_SELECT
323 #define SOKOBAN_REDO BUTTON_MENU
324 #define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
325 #define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
326 #define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
327 #define SOKOBAN_PAUSE BUTTON_SELECT
328 #define BUTTON_SAVE BUTTON_SELECT
329 #define BUTTON_SAVE_NAME "SELECT"
331 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
332 #define SOKOBAN_LEFT BUTTON_RC_REW
333 #define SOKOBAN_RIGHT BUTTON_RC_FF
334 #define SOKOBAN_UP BUTTON_RC_VOL_UP
335 #define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
336 #define SOKOBAN_MENU BUTTON_RC_REC
337 #define SOKOBAN_UNDO BUTTON_RC_MODE
338 #define SOKOBAN_REDO BUTTON_RC_MENU
339 #define SOKOBAN_PAUSE BUTTON_RC_PLAY
340 #define BUTTON_SAVE BUTTON_RC_PLAY
341 #define BUTTON_SAVE_NAME "PLAY"
343 #define SOKOBAN_RC_MENU BUTTON_REC
345 #elif CONFIG_KEYPAD == COWON_D2_PAD
346 #define SOKOBAN_MENU BUTTON_MENU
347 #define SOKOBAN_LEVEL_DOWN BUTTON_MINUS
348 #define SOKOBAN_LEVEL_UP BUTTON_PLUS
349 #define SOKOBAN_MENU_NAME "[MENU]"
351 #elif CONFIG_KEYPAD == IAUDIO67_PAD
352 #define SOKOBAN_LEFT BUTTON_LEFT
353 #define SOKOBAN_RIGHT BUTTON_RIGHT
354 #define SOKOBAN_UP BUTTON_STOP
355 #define SOKOBAN_DOWN BUTTON_PLAY
356 #define SOKOBAN_MENU BUTTON_MENU
357 #define SOKOBAN_UNDO BUTTON_VOLDOWN
358 #define SOKOBAN_REDO BUTTON_VOLUP
359 #define SOKOBAN_PAUSE (BUTTON_MENU|BUTTON_LEFT)
360 #define BUTTON_SAVE (BUTTON_MENU|BUTTON_PLAY)
361 #define BUTTON_SAVE_NAME "MENU+PLAY"
363 #define SOKOBAN_RC_MENU (BUTTON_MENU|BUTTON_STOP)
365 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
366 #define SOKOBAN_LEFT BUTTON_LEFT
367 #define SOKOBAN_RIGHT BUTTON_RIGHT
368 #define SOKOBAN_UP BUTTON_UP
369 #define SOKOBAN_DOWN BUTTON_DOWN
370 #define SOKOBAN_MENU BUTTON_MENU
371 #define SOKOBAN_UNDO BUTTON_BACK
372 #define SOKOBAN_REDO (BUTTON_BACK | BUTTON_PLAY)
373 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
374 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
375 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
376 #define SOKOBAN_PAUSE BUTTON_PLAY
377 #define BUTTON_SAVE BUTTON_CUSTOM
378 #define BUTTON_SAVE_NAME "CUSTOM"
380 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
381 #define SOKOBAN_LEFT BUTTON_LEFT
382 #define SOKOBAN_RIGHT BUTTON_RIGHT
383 #define SOKOBAN_UP BUTTON_UP
384 #define SOKOBAN_DOWN BUTTON_DOWN
385 #define SOKOBAN_MENU BUTTON_MENU
386 #define SOKOBAN_UNDO BUTTON_VIEW
387 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_VIEW)
388 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
389 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
390 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
391 #define SOKOBAN_PAUSE BUTTON_SELECT
392 #define BUTTON_SAVE BUTTON_PLAYLIST
393 #define BUTTON_SAVE_NAME "PLAYLIST"
395 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
396 #define SOKOBAN_LEFT BUTTON_LEFT
397 #define SOKOBAN_RIGHT BUTTON_RIGHT
398 #define SOKOBAN_UP BUTTON_UP
399 #define SOKOBAN_DOWN BUTTON_DOWN
400 #define SOKOBAN_MENU BUTTON_MENU
401 #define SOKOBAN_UNDO BUTTON_PREV
402 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_PREV)
403 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
404 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
405 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
406 #define SOKOBAN_PAUSE BUTTON_PLAY
407 #define BUTTON_SAVE BUTTON_NEXT
408 #define BUTTON_SAVE_NAME "NEXT"
410 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
411 #define SOKOBAN_LEFT BUTTON_PREV
412 #define SOKOBAN_RIGHT BUTTON_NEXT
413 #define SOKOBAN_UP BUTTON_UP
414 #define SOKOBAN_DOWN BUTTON_DOWN
415 #define SOKOBAN_MENU BUTTON_MENU
416 #define SOKOBAN_UNDO BUTTON_LEFT
417 #define SOKOBAN_REDO (BUTTON_LEFT | BUTTON_PLAY)
418 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
419 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
420 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
421 #define SOKOBAN_PAUSE BUTTON_PLAY
422 #define BUTTON_SAVE BUTTON_RIGHT
423 #define BUTTON_SAVE_NAME "RIGHT"
425 #elif CONFIG_KEYPAD == ONDAVX747_PAD
426 #define SOKOBAN_MENU BUTTON_MENU
427 #define SOKOBAN_MENU_NAME "[MENU]"
429 #elif CONFIG_KEYPAD == ONDAVX777_PAD
430 #define SOKOBAN_MENU BUTTON_POWER
431 #define SOKOBAN_MENU_NAME "[POWER]"
433 #elif CONFIG_KEYPAD == MROBE500_PAD
435 #define SOKOBAN_MENU BUTTON_POWER
436 #define SOKOBAN_MENU_NAME "[POWER]"
438 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
439 #define SOKOBAN_LEFT BUTTON_LEFT
440 #define SOKOBAN_RIGHT BUTTON_RIGHT
441 #define SOKOBAN_UP BUTTON_UP
442 #define SOKOBAN_DOWN BUTTON_DOWN
443 #define SOKOBAN_MENU BUTTON_REC
444 #define SOKOBAN_UNDO_PRE BUTTON_REW
445 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_LEFT)
446 #define SOKOBAN_REDO BUTTON_FFWD
447 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
448 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
449 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
450 #define SOKOBAN_PAUSE BUTTON_PLAY
451 #define BUTTON_SAVE BUTTON_PLAY
452 #define BUTTON_SAVE_NAME "PLAY"
454 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
455 #define SOKOBAN_LEFT BUTTON_PREV
456 #define SOKOBAN_RIGHT BUTTON_NEXT
457 #define SOKOBAN_UP BUTTON_UP
458 #define SOKOBAN_DOWN BUTTON_DOWN
459 #define SOKOBAN_MENU BUTTON_REC
460 #define SOKOBAN_UNDO BUTTON_CANCEL
461 #define SOKOBAN_REDO BUTTON_OK
462 #define SOKOBAN_LEVEL_DOWN (BUTTON_OK | BUTTON_PREV)
463 #define SOKOBAN_LEVEL_REPEAT (BUTTON_OK | BUTTON_CANCEL)
464 #define SOKOBAN_LEVEL_UP (BUTTON_OK | BUTTON_NEXT)
465 #define SOKOBAN_PAUSE BUTTON_PLAY
466 #define BUTTON_SAVE BUTTON_MENU
467 #define BUTTON_SAVE_NAME "MENU"
469 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
470 #define SOKOBAN_LEFT BUTTON_VOL_DOWN
471 #define SOKOBAN_RIGHT BUTTON_VOL_UP
472 #define SOKOBAN_UP BUTTON_REW
473 #define SOKOBAN_DOWN BUTTON_FF
474 #define SOKOBAN_MENU BUTTON_FUNC
475 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_REW)
476 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_FF)
477 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_VOL_DOWN)
478 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
479 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_VOL_UP)
480 #define SOKOBAN_PAUSE BUTTON_PLAY
481 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_FUNC)
482 #define BUTTON_SAVE_NAME "PLAY+FUNC"
484 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
485 #define SOKOBAN_LEFT BUTTON_REW
486 #define SOKOBAN_RIGHT BUTTON_FF
487 #define SOKOBAN_UP BUTTON_UP
488 #define SOKOBAN_DOWN BUTTON_DOWN
489 #define SOKOBAN_MENU (BUTTON_MENU | BUTTON_REPEAT)
490 #define SOKOBAN_UNDO BUTTON_REC
491 #define SOKOBAN_REDO BUTTON_PLAY
492 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_REW)
493 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_ENTER)
494 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_FF)
495 #define SOKOBAN_PAUSE BUTTON_PLAY
496 #define BUTTON_SAVE (BUTTON_ENTER | BUTTON_REL)
497 #define BUTTON_SAVE_NAME "ENTER"
499 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
500 #define SOKOBAN_LEFT BUTTON_LEFT
501 #define SOKOBAN_RIGHT BUTTON_RIGHT
502 #define SOKOBAN_UP BUTTON_UP
503 #define SOKOBAN_DOWN BUTTON_DOWN
504 #define SOKOBAN_MENU BUTTON_POWER
505 #define SOKOBAN_UNDO_PRE BUTTON_BOTTOMLEFT
506 #define SOKOBAN_UNDO (BUTTON_BOTTOMLEFT|BUTTON_REL)
507 #define SOKOBAN_REDO_PRE BUTTON_BOTTOMRIGHT
508 #define SOKOBAN_REDO (BUTTON_BOTTOMRIGHT|BUTTON_REL)
509 #define SOKOBAN_LEVEL_REPEAT BUTTON_BACK
510 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
511 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
512 #define SOKOBAN_PAUSE BUTTON_PLAYPAUSE
513 #define BUTTON_SAVE (BUTTON_SELECT|BUTTON_REPEAT)
514 #define BUTTON_SAVE_NAME "SELECT LONG"
516 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
517 #define SOKOBAN_LEFT BUTTON_LEFT
518 #define SOKOBAN_RIGHT BUTTON_RIGHT
519 #define SOKOBAN_UP BUTTON_UP
520 #define SOKOBAN_DOWN BUTTON_DOWN
521 #define SOKOBAN_MENU BUTTON_POWER
522 #define SOKOBAN_UNDO BUTTON_PREV
523 #define SOKOBAN_REDO BUTTON_NEXT
524 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
525 #define SOKOBAN_LEVEL_REPEAT (BUTTON_NEXT|BUTTON_PREV)
526 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
527 #define SOKOBAN_PAUSE BUTTON_SELECT
528 #define BUTTON_SAVE (BUTTON_SELECT|BUTTON_REPEAT)
529 #define BUTTON_SAVE_NAME "SELECT LONG"
531 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
532 #define SOKOBAN_LEFT BUTTON_LEFT
533 #define SOKOBAN_RIGHT BUTTON_RIGHT
534 #define SOKOBAN_UP BUTTON_UP
535 #define SOKOBAN_DOWN BUTTON_DOWN
536 #define SOKOBAN_MENU BUTTON_MENU
537 #define SOKOBAN_UNDO BUTTON_BACK
538 #define SOKOBAN_REDO BUTTON_USER
539 //#define SOKOBAN_LEVEL_DOWN (BUTTON_POWER|BUTTON_REL)
540 //#define SOKOBAN_LEVEL_REPEAT (BUTTON_CENTER|BUTTON_REPEAT)
541 //#define SOKOBAN_LEVEL_UP (BUTTON_MENU|BUTTON_REPEAT)
542 #define SOKOBAN_PAUSE BUTTON_SELECT
543 #define BUTTON_SAVE BUTTON_SELECT
544 #define BUTTON_SAVE_NAME "SELECT"
546 #elif CONFIG_KEYPAD == HM60X_PAD
547 #define SOKOBAN_LEFT BUTTON_LEFT
548 #define SOKOBAN_RIGHT BUTTON_RIGHT
549 #define SOKOBAN_UP BUTTON_UP
550 #define SOKOBAN_DOWN BUTTON_DOWN
551 #define SOKOBAN_MENU BUTTON_POWER
552 #define SOKOBAN_UNDO BUTTON_SELECT
553 #define SOKOBAN_REDO (BUTTON_SELECT|BUTTON_POWER)
554 #define SOKOBAN_LEVEL_DOWN (BUTTON_DOWN|BUTTON_POWER)
555 #define SOKOBAN_LEVEL_UP (BUTTON_UP | BUTTON_POWER)
556 #define SOKOBAN_PAUSE (BUTTON_RIGHT|BUTTON_POWER)
557 #define BUTTON_SAVE (BUTTON_LEFT|BUTTON_POWER)
558 #define BUTTON_SAVE_NAME "LEFT + POWER"
560 #elif CONFIG_KEYPAD == HM801_PAD
561 #define SOKOBAN_LEFT BUTTON_LEFT
562 #define SOKOBAN_RIGHT BUTTON_RIGHT
563 #define SOKOBAN_UP BUTTON_UP
564 #define SOKOBAN_DOWN BUTTON_DOWN
565 #define SOKOBAN_MENU BUTTON_POWER
566 #define SOKOBAN_UNDO BUTTON_SELECT
567 #define SOKOBAN_REDO (BUTTON_POWER | BUTTON_SELECT)
568 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
569 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
570 #define SOKOBAN_PAUSE BUTTON_PLAY
571 #define BUTTON_SAVE (BUTTON_POWER | BUTTON_PLAY)
572 #define BUTTON_SAVE_NAME "POWER + PLAY"
574 #else
575 #error No keymap defined!
576 #endif
578 #ifdef HAVE_TOUCHSCREEN
579 #ifndef SOKOBAN_LEFT
580 #define SOKOBAN_LEFT BUTTON_MIDLEFT
581 #endif
582 #ifndef SOKOBAN_RIGHT
583 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
584 #endif
585 #ifndef SOKOBAN_UP
586 #define SOKOBAN_UP BUTTON_TOPMIDDLE
587 #endif
588 #ifndef SOKOBAN_DOWN
589 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
590 #endif
591 #ifndef SOKOBAN_MENU
592 #define SOKOBAN_MENU BUTTON_TOPLEFT
593 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
594 #endif
595 #ifndef SOKOBAN_UNDO
596 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
597 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
598 #endif
599 #ifndef SOKOBAN_REDO
600 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
601 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
602 #endif
603 #ifndef SOKOBAN_PAUSE
604 #define SOKOBAN_PAUSE BUTTON_CENTER
605 #define SOKOBAN_PAUSE_NAME "[CENTER]"
606 #endif
607 #ifndef SOKOBAN_LEVEL_REPEAT
608 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
609 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
610 #endif
611 #ifndef BUTTON_SAVE
612 #define BUTTON_SAVE BUTTON_CENTER
613 #define BUTTON_SAVE_NAME "CENTER"
614 #endif
615 #endif
617 #define SOKOBAN_FONT FONT_SYSFIXED
620 /* The Location, Undo and LevelInfo structs are OO-flavored.
621 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
622 * but the overall data layout becomes more manageable. */
624 /* Level data & stats */
625 struct LevelInfo {
626 int index; /* Level index (level number - 1) */
627 int moves; /* Moves & pushes for the stats */
628 int pushes;
629 short boxes_to_go; /* Number of unplaced boxes remaining in level */
630 short height; /* Height & width for centering level display */
631 short width;
634 struct Location {
635 short row;
636 short col;
639 /* Our full undo history */
640 static struct UndoInfo {
641 int count; /* How many undos have been done */
642 int current; /* Which history is the current undo */
643 int max; /* Which history is the max redoable */
644 char history[MAX_UNDOS];
645 } undo_info;
647 /* Our playing board */
648 static struct BoardInfo {
649 char board[ROWS][COLS]; /* The current board data */
650 struct LevelInfo level; /* Level data & stats */
651 struct Location player; /* Where the player is */
652 int max_level; /* The number of levels we have */
653 } current_info;
655 static struct BufferedBoards {
656 char filename[MAX_PATH]; /* Filename of the levelset we're using */
657 char data[MAX_LEVEL_DATA]; /* Buffered level data */
658 int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
659 int start; /* Index of first buffered board */
660 int end; /* Index of last buffered board */
661 short prebuffered_boards; /* Number of boards before current to store */
662 } buffered_boards;
665 static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
668 static void init_undo(void)
670 undo_info.count = 0;
671 undo_info.current = 0;
672 undo_info.max = 0;
675 static void get_delta(char direction, short *d_r, short *d_c)
677 switch (direction) {
678 case SOKOBAN_PUSH_LEFT:
679 case SOKOBAN_MOVE_LEFT:
680 *d_r = 0;
681 *d_c = -1;
682 break;
683 case SOKOBAN_PUSH_RIGHT:
684 case SOKOBAN_MOVE_RIGHT:
685 *d_r = 0;
686 *d_c = 1;
687 break;
688 case SOKOBAN_PUSH_UP:
689 case SOKOBAN_MOVE_UP:
690 *d_r = -1;
691 *d_c = 0;
692 break;
693 case SOKOBAN_PUSH_DOWN:
694 case SOKOBAN_MOVE_DOWN:
695 *d_r = 1;
696 *d_c = 0;
700 static bool undo(void)
702 char undo;
703 short r, c;
704 short d_r = 0, d_c = 0; /* delta row & delta col */
705 char *space_cur, *space_next, *space_prev;
706 bool undo_push = false;
708 /* If no more undos or we've wrapped all the way around, quit */
709 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
710 return false;
712 /* Move to previous undo in the list */
713 if (undo_info.current == 0 && undo_info.count > 1)
714 undo_info.current = MAX_UNDOS - 1;
715 else
716 undo_info.current--;
718 undo_info.count--;
720 undo = undo_info.history[undo_info.current];
722 if (undo < SOKOBAN_MOVE_MIN)
723 undo_push = true;
725 get_delta(undo, &d_r, &d_c);
727 r = current_info.player.row;
728 c = current_info.player.col;
730 /* Give the 3 spaces we're going to use better names */
731 space_cur = &current_info.board[r][c];
732 space_next = &current_info.board[r + d_r][c + d_c];
733 space_prev = &current_info.board[r - d_r][c - d_c];
735 /* Update board info */
736 if (undo_push) {
737 /* Moving box from goal to floor */
738 if (*space_next == '*' && *space_cur == '@')
739 current_info.level.boxes_to_go++;
740 /* Moving box from floor to goal */
741 else if (*space_next == '$' && *space_cur == '+')
742 current_info.level.boxes_to_go--;
744 /* Move box off of next space... */
745 *space_next = (*space_next == '*' ? '.' : ' ');
746 /* ...and on to current space */
747 *space_cur = (*space_cur == '+' ? '*' : '$');
749 current_info.level.pushes--;
750 } else
751 /* Just move player off of current space */
752 *space_cur = (*space_cur == '+' ? '.' : ' ');
753 /* Move player back to previous space */
754 *space_prev = (*space_prev == '.' ? '+' : '@');
756 /* Update position */
757 current_info.player.row -= d_r;
758 current_info.player.col -= d_c;
760 current_info.level.moves--;
762 return true;
765 static void add_undo(char undo)
767 undo_info.history[undo_info.current] = undo;
769 /* Wrap around if MAX_UNDOS exceeded */
770 if (undo_info.current < (MAX_UNDOS - 1))
771 undo_info.current++;
772 else
773 undo_info.current = 0;
775 if (undo_info.count < MAX_UNDOS)
776 undo_info.count++;
779 static bool move(char direction, bool redo)
781 short r, c;
782 short d_r = 0, d_c = 0; /* delta row & delta col */
783 char *space_cur, *space_next, *space_beyond;
784 bool push = false;
786 get_delta(direction, &d_r, &d_c);
788 r = current_info.player.row;
789 c = current_info.player.col;
791 /* Check for out-of-bounds */
792 if (r + 2*d_r < 0 || r + 2*d_r >= ROWS ||
793 c + 2*d_c < 0 || c + 2*d_c >= COLS)
794 return false;
796 /* Give the 3 spaces we're going to use better names */
797 space_cur = &current_info.board[r][c];
798 space_next = &current_info.board[r + d_r][c + d_c];
799 space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
801 if (*space_next == '$' || *space_next == '*') {
802 /* Change direction from move to push for undo */
803 if (direction >= SOKOBAN_MOVE_MIN)
804 direction -= SOKOBAN_MOVE_DIFF;
805 push = true;
807 else if (direction < SOKOBAN_MOVE_MIN)
808 /* Change back to move if redo/solution playback push is invalid */
809 direction += SOKOBAN_MOVE_DIFF;
811 /* Update board info */
812 if (push) {
813 /* Moving box from goal to floor */
814 if (*space_next == '*' && *space_beyond == ' ')
815 current_info.level.boxes_to_go++;
816 /* Moving box from floor to goal */
817 else if (*space_next == '$' && *space_beyond == '.')
818 current_info.level.boxes_to_go--;
819 /* Check for invalid move */
820 else if (*space_beyond != '.' && *space_beyond != ' ')
821 return false;
823 /* Move player onto next space */
824 *space_next = (*space_next == '*' ? '+' : '@');
825 /* Move box onto space beyond next */
826 *space_beyond = (*space_beyond == '.' ? '*' : '$');
828 current_info.level.pushes++;
829 } else {
830 /* Check for invalid move */
831 if (*space_next != '.' && *space_next != ' ')
832 return false;
834 /* Move player onto next space */
835 *space_next = (*space_next == '.' ? '+' : '@');
837 /* Move player off of current space */
838 *space_cur = (*space_cur == '+' ? '.' : ' ');
840 /* Update position */
841 current_info.player.row += d_r;
842 current_info.player.col += d_c;
844 current_info.level.moves++;
846 /* Update undo_info.max to current on every normal move,
847 * except if it's the same as a redo. */
848 /* normal move and either */
849 if (!redo &&
850 /* moves have been undone... */
851 ((undo_info.max != undo_info.current &&
852 /* ...and the current move is NOT the same as the one in history */
853 undo_info.history[undo_info.current] != direction) ||
854 /* or moves have not been undone */
855 undo_info.max == undo_info.current)) {
856 add_undo(direction);
857 undo_info.max = undo_info.current;
858 } else /* redo move or move was same as redo */
859 add_undo(direction); /* add_undo to update current */
861 return true;
864 #ifdef SOKOBAN_REDO
865 static bool redo(void)
867 /* If no moves have been undone, quit */
868 if (undo_info.current == undo_info.max)
869 return false;
871 return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
872 undo_info.current : 0)], true);
874 #endif
876 static void init_boards(void)
878 rb->strlcpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE,
879 sizeof(buffered_boards.filename));
881 current_info.level.index = 0;
882 current_info.player.row = 0;
883 current_info.player.col = 0;
884 current_info.max_level = 0;
886 buffered_boards.start = 0;
887 buffered_boards.end = 0;
888 buffered_boards.prebuffered_boards = 0;
890 init_undo();
893 static bool read_levels(bool initialize)
895 int fd = 0;
896 short len;
897 short lastlen = 0;
898 short row = 0;
899 int level_count = 0;
901 int i = 0;
902 int level_len = 0;
903 bool index_set = false;
905 /* Get the index of the first level to buffer */
906 if (current_info.level.index > buffered_boards.prebuffered_boards &&
907 !initialize)
908 buffered_boards.start = current_info.level.index -
909 buffered_boards.prebuffered_boards;
910 else
911 buffered_boards.start = 0;
913 if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
914 rb->splashf(HZ*2, "Unable to open %s", buffered_boards.filename);
915 return false;
918 do {
919 len = rb->read_line(fd, buf, sizeof(buf));
921 /* Correct len when trailing \r's or \n's are counted */
922 if (len > 2 && buf[len - 2] == '\0')
923 len -= 2;
924 else if (len > 1 && buf[len - 1] == '\0')
925 len--;
927 /* Skip short lines & lines with non-level data */
928 if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
929 buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
930 if (level_count >= buffered_boards.start) {
931 /* Set the index of this level */
932 if (!index_set &&
933 level_count - buffered_boards.start < MAX_LEVELS) {
934 buffered_boards.index[level_count - buffered_boards.start]
935 = i;
936 index_set = true;
938 /* Copy buffer to board data */
939 if (i + level_len + len < MAX_LEVEL_DATA) {
940 rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
941 buffered_boards.data[i + level_len + len] = '\n';
944 level_len += len + 1;
945 row++;
947 /* If newline & level is tall enough or is RLE */
948 } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
949 level_count++;
950 if (level_count >= buffered_boards.start) {
951 i += level_len;
952 if (i < MAX_LEVEL_DATA)
953 buffered_boards.end = level_count;
954 else if (!initialize)
955 break;
957 row = 0;
958 level_len = 0;
959 index_set = false;
961 } else if (len > 22)
962 len = 1;
964 } while ((lastlen = len));
966 /* Set the index of the end of the last level */
967 if (level_count - buffered_boards.start < MAX_LEVELS)
968 buffered_boards.index[level_count - buffered_boards.start] = i;
970 if (initialize) {
971 current_info.max_level = level_count;
972 buffered_boards.prebuffered_boards = buffered_boards.end/2;
975 rb->close(fd);
977 return true;
980 static void load_level(void)
982 int c, r;
983 int i, n;
984 int level_size;
985 int index = current_info.level.index - buffered_boards.start;
986 char *level;
988 /* Get the buffered board index of the current level */
989 if (current_info.level.index < buffered_boards.start ||
990 current_info.level.index >= buffered_boards.end) {
991 read_levels(false);
992 if (current_info.level.index > buffered_boards.prebuffered_boards)
993 index = buffered_boards.prebuffered_boards;
994 else
995 index = current_info.level.index;
997 level = &buffered_boards.data[buffered_boards.index[index]];
999 /* Reset level info */
1000 current_info.level.moves = 0;
1001 current_info.level.pushes = 0;
1002 current_info.level.boxes_to_go = 0;
1003 current_info.level.width = 0;
1005 /* Clear board */
1006 for (r = 0; r < ROWS; r++)
1007 for (c = 0; c < COLS; c++)
1008 current_info.board[r][c] = 'X';
1010 level_size = buffered_boards.index[index + 1] -
1011 buffered_boards.index[index];
1013 for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
1014 if (level[i] == '\n' || level[i] == '|') {
1015 if (c > 3) {
1016 /* Update max width of level & go to next row */
1017 if (c > current_info.level.width)
1018 current_info.level.width = c;
1019 c = 0;
1020 r++;
1021 if (r >= ROWS)
1022 break;
1024 } else if (c < COLS) {
1025 /* Read RLE character's length into n */
1026 if (level[i] >= '0' && level[i] <= '9') {
1027 n = level[i++] - '0';
1028 if (level[i] >= '0' && level[i] <= '9')
1029 n = n*10 + level[i++] - '0';
1032 /* Cleanup & replace */
1033 if (level[i] == '%')
1034 level[i] = '*';
1035 else if (level[i] == '-' || level[i] == '_')
1036 level[i] = ' ';
1038 if (n > 1) {
1039 if (c + n >= COLS)
1040 n = COLS - c;
1042 if (level[i] == '.')
1043 current_info.level.boxes_to_go += n;
1045 /* Put RLE character n times */
1046 while (n--)
1047 current_info.board[r][c++] = level[i];
1048 n = 1;
1050 } else {
1051 if (level[i] == '.' || level[i] == '+')
1052 current_info.level.boxes_to_go++;
1054 if (level[i] == '@' ||level[i] == '+') {
1055 current_info.player.row = r;
1056 current_info.player.col = c;
1059 current_info.board[r][c++] = level[i];
1064 current_info.level.height = r;
1066 #if LCD_DEPTH > 2
1067 /* Fill in blank space outside level on color targets */
1068 for (r = 0; r < ROWS; r++)
1069 for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
1070 current_info.board[r][c] = 'X';
1072 for (c = 0; c < COLS; c++) {
1073 for (r = 0; (current_info.board[r][c] == ' ' ||
1074 current_info.board[r][c] == 'X') && r < ROWS; r++)
1075 current_info.board[r][c] = 'X';
1076 for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
1077 current_info.board[r][c] == 'X') && r >= 0; r--)
1078 current_info.board[r][c] = 'X';
1080 #endif
1083 static void update_screen(void)
1085 int c, r;
1086 int rows, cols;
1088 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
1089 #define STAT_HEIGHT 25
1090 #define STAT_X (LCD_WIDTH - 120)/2
1091 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
1092 #define BOARD_WIDTH LCD_WIDTH
1093 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
1094 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
1095 rb->lcd_putsxyf(STAT_X + 7, STAT_Y + 14, "%d", current_info.level.index + 1);
1096 rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
1097 rb->lcd_putsxyf(STAT_X + 44, STAT_Y + 14, "%d", current_info.level.moves);
1098 rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
1099 rb->lcd_putsxyf(STAT_X + 82, STAT_Y + 14, "%d", current_info.level.pushes);
1101 rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
1102 rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
1103 rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
1104 #else
1105 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
1106 #define STAT_X (LCD_WIDTH - 40)
1107 #else
1108 #define STAT_X COLS*SOKOBAN_TILESIZE
1109 #endif
1110 #define STAT_Y (LCD_HEIGHT - 64)/2
1111 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
1112 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
1113 #define BOARD_HEIGHT LCD_HEIGHT
1114 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 2, "Level");
1115 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 12, "%d", current_info.level.index + 1);
1116 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 23, "Moves");
1117 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 33, "%d", current_info.level.moves);
1118 #if STAT_WIDTH < 38
1119 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Push");
1120 #else
1121 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Pushes");
1122 #endif
1123 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 54, "%d", current_info.level.pushes);
1125 rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 64);
1126 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 21);
1127 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 42);
1129 #endif
1131 /* load the board to the screen */
1132 for (rows = 0; rows < ROWS; rows++) {
1133 for (cols = 0; cols < COLS; cols++) {
1134 c = cols*SOKOBAN_TILESIZE +
1135 (BOARD_WIDTH - current_info.level.width*SOKOBAN_TILESIZE)/2;
1136 r = rows*SOKOBAN_TILESIZE +
1137 (BOARD_HEIGHT - current_info.level.height*SOKOBAN_TILESIZE)/2;
1139 switch(current_info.board[rows][cols]) {
1140 case 'X': /* blank space outside of level */
1141 break;
1143 case ' ': /* floor */
1144 rb->lcd_bitmap_part(sokoban_tiles, 0, 0*SOKOBAN_TILESIZE,
1145 STRIDE( SCREEN_MAIN,
1146 BMPWIDTH_sokoban_tiles,
1147 BMPHEIGHT_sokoban_tiles),
1148 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1149 break;
1151 case '#': /* wall */
1152 rb->lcd_bitmap_part(sokoban_tiles, 0, 1*SOKOBAN_TILESIZE,
1153 STRIDE( SCREEN_MAIN,
1154 BMPWIDTH_sokoban_tiles,
1155 BMPHEIGHT_sokoban_tiles),
1156 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1157 break;
1159 case '$': /* box */
1160 rb->lcd_bitmap_part(sokoban_tiles, 0, 2*SOKOBAN_TILESIZE,
1161 STRIDE( SCREEN_MAIN,
1162 BMPWIDTH_sokoban_tiles,
1163 BMPHEIGHT_sokoban_tiles),
1164 c, r, SOKOBAN_TILESIZE,SOKOBAN_TILESIZE);
1165 break;
1167 case '*': /* box on goal */
1168 rb->lcd_bitmap_part(sokoban_tiles, 0, 3*SOKOBAN_TILESIZE,
1169 STRIDE( SCREEN_MAIN,
1170 BMPWIDTH_sokoban_tiles,
1171 BMPHEIGHT_sokoban_tiles),
1172 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1173 break;
1175 case '.': /* goal */
1176 rb->lcd_bitmap_part(sokoban_tiles, 0, 4*SOKOBAN_TILESIZE,
1177 STRIDE( SCREEN_MAIN,
1178 BMPWIDTH_sokoban_tiles,
1179 BMPHEIGHT_sokoban_tiles),
1180 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1181 break;
1183 case '@': /* player */
1184 rb->lcd_bitmap_part(sokoban_tiles, 0, 5*SOKOBAN_TILESIZE,
1185 STRIDE( SCREEN_MAIN,
1186 BMPWIDTH_sokoban_tiles,
1187 BMPHEIGHT_sokoban_tiles),
1188 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1189 break;
1191 case '+': /* player on goal */
1192 rb->lcd_bitmap_part(sokoban_tiles, 0, 6*SOKOBAN_TILESIZE,
1193 STRIDE( SCREEN_MAIN,
1194 BMPWIDTH_sokoban_tiles,
1195 BMPHEIGHT_sokoban_tiles),
1196 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1197 break;
1202 /* print out the screen */
1203 rb->lcd_update();
1206 static void draw_level(void)
1208 load_level();
1209 rb->lcd_clear_display();
1210 update_screen();
1213 static bool save(char *filename, bool solution)
1215 int fd;
1216 char *loc;
1217 DIR *dir;
1218 char dirname[MAX_PATH];
1220 rb->splash(0, "Saving...");
1222 /* Create dir if it doesn't exist */
1223 if ((loc = rb->strrchr(filename, '/')) != NULL) {
1224 rb->strlcpy(dirname, filename, loc - filename + 1);
1226 if(!(dir = rb->opendir(dirname)))
1227 rb->mkdir(dirname);
1228 else
1229 rb->closedir(dir);
1232 if (filename[0] == '\0' ||
1233 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
1234 rb->splashf(HZ*2, "Unable to open %s", filename);
1235 return false;
1238 /* Sokoban: S/P for solution/progress : level number : current undo */
1239 rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
1240 current_info.level.index + 1, undo_info.current);
1241 rb->write(fd, buf, rb->strlen(buf));
1243 /* Filename of levelset */
1244 rb->write(fd, buffered_boards.filename,
1245 rb->strlen(buffered_boards.filename));
1246 rb->write(fd, "\n", 1);
1248 /* Full undo history */
1249 rb->write(fd, undo_info.history, undo_info.max);
1251 rb->close(fd);
1253 return true;
1256 static bool load(char *filename, bool silent)
1258 int fd;
1259 int i, n;
1260 int len;
1261 int button;
1262 bool play_solution;
1263 bool paused = false;
1264 unsigned short speed = 2;
1265 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
1267 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
1268 if (!silent)
1269 rb->splashf(HZ*2, "Unable to open %s", filename);
1270 return false;
1273 /* Read header, level number, & current undo */
1274 rb->read_line(fd, buf, sizeof(buf));
1276 /* If we're opening a level file, not a solution/progress file */
1277 if (rb->strncmp(buf, "Sokoban", 7) != 0) {
1278 rb->close(fd);
1280 rb->strlcpy(buffered_boards.filename, filename,
1281 sizeof(buffered_boards.filename));
1283 if (!read_levels(true))
1284 return false;
1286 current_info.level.index = 0;
1287 load_level();
1289 /* If there aren't any boxes to go or the player position wasn't set,
1290 * the file probably wasn't a Sokoban level file */
1291 if (current_info.level.boxes_to_go == 0 ||
1292 current_info.player.row == 0 || current_info.player.col == 0) {
1293 if (!silent)
1294 rb->splash(HZ*2, "File is not a Sokoban level file");
1295 return false;
1298 } else {
1300 /* Read filename of levelset */
1301 rb->read_line(fd, buffered_boards.filename,
1302 sizeof(buffered_boards.filename));
1304 /* Read full undo history */
1305 len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
1307 /* Correct len when trailing \r's or \n's are counted */
1308 if (len > 2 && undo_info.history[len - 2] == '\0')
1309 len -= 2;
1310 else if (len > 1 && undo_info.history[len - 1] == '\0')
1311 len--;
1313 rb->close(fd);
1315 /* Check to see if we're going to play a solution or resume progress */
1316 play_solution = (buf[8] == 'S');
1318 /* Get level number */
1319 for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
1320 n = n*10 + buf[i] - '0';
1321 current_info.level.index = n - 1;
1323 /* Get undo index */
1324 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
1325 n = n*10 + buf[i] - '0';
1326 if (n > len)
1327 n = len;
1328 undo_info.max = len;
1330 if (current_info.level.index < 0) {
1331 if (!silent)
1332 rb->splash(HZ*2, "Error loading level");
1333 return false;
1335 if (!read_levels(true))
1336 return false;
1337 if (current_info.level.index >= current_info.max_level) {
1338 if (!silent)
1339 rb->splash(HZ*2, "Error loading level");
1340 return false;
1343 load_level();
1345 if (play_solution) {
1346 rb->lcd_clear_display();
1347 update_screen();
1348 rb->sleep(2*delay[speed]);
1350 /* Replay solution until menu button is pressed */
1351 i = 0;
1352 while (true) {
1353 if (i < len) {
1354 if (!move(undo_info.history[i], true)) {
1355 n = i;
1356 break;
1358 rb->lcd_clear_display();
1359 update_screen();
1360 i++;
1361 } else
1362 paused = true;
1364 rb->sleep(delay[speed]);
1366 while ((button = rb->button_get(false)) || paused) {
1367 switch (button) {
1368 case SOKOBAN_MENU:
1369 /* Pretend the level is complete so we'll quit */
1370 current_info.level.boxes_to_go = 0;
1371 return true;
1373 case SOKOBAN_PAUSE:
1374 /* Toggle pause state */
1375 paused = !paused;
1376 break;
1378 case SOKOBAN_LEFT:
1379 case SOKOBAN_LEFT | BUTTON_REPEAT:
1380 /* Go back one move */
1381 if (paused) {
1382 if (undo())
1383 i--;
1384 rb->lcd_clear_display();
1385 update_screen();
1387 break;
1389 case SOKOBAN_RIGHT:
1390 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1391 /* Go forward one move */
1392 if (paused) {
1393 if (redo())
1394 i++;
1395 rb->lcd_clear_display();
1396 update_screen();
1398 break;
1400 case SOKOBAN_UP:
1401 case SOKOBAN_UP | BUTTON_REPEAT:
1402 /* Speed up */
1403 if (speed < sizeof(delay)/sizeof(int) - 1)
1404 speed++;
1405 break;
1407 case SOKOBAN_DOWN:
1408 case SOKOBAN_DOWN | BUTTON_REPEAT:
1409 /* Slow down */
1410 if (speed > 0)
1411 speed--;
1414 if (paused)
1415 rb->sleep(HZ/33);
1419 /* If level is complete, wait for keypress before quitting */
1420 if (current_info.level.boxes_to_go == 0)
1421 rb->button_get(true);
1423 } else {
1424 /* Advance to current undo */
1425 for (i = 0; i < n; i++) {
1426 if (!move(undo_info.history[i], true)) {
1427 n = i;
1428 break;
1432 rb->button_clear_queue();
1433 rb->lcd_clear_display();
1436 undo_info.current = n;
1439 return true;
1442 static int sokoban_menu(void)
1444 int button;
1445 int selection = 0;
1446 bool menu_quit;
1447 int start_selected = 0;
1448 int prev_level = current_info.level.index;
1450 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
1451 "Resume", "Select Level", "Audio Playback", "Keys",
1452 "Load Default Level Set", "Quit Without Saving",
1453 "Save Progress & Quit");
1455 do {
1456 menu_quit = true;
1457 selection = rb->do_menu(&menu, &start_selected, NULL, false);
1459 switch (selection) {
1460 case 0: /* Resume */
1461 break;
1463 case 1: /* Select level */
1464 current_info.level.index++;
1465 rb->set_int("Select Level", "", UNIT_INT,
1466 &current_info.level.index, NULL, 1, 1,
1467 current_info.max_level, NULL);
1468 current_info.level.index--;
1469 if (prev_level != current_info.level.index) {
1470 init_undo();
1471 draw_level();
1472 } else
1473 menu_quit = false;
1474 break;
1476 case 2: /* Audio playback control */
1477 playback_control(NULL);
1478 menu_quit = false;
1479 break;
1481 case 3: /* Keys */
1482 FOR_NB_SCREENS(i)
1483 rb->screens[i]->clear_display();
1484 rb->lcd_setfont(SOKOBAN_FONT);
1486 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1487 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1488 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1489 rb->lcd_putsxy(3, 16, "[ON] Undo");
1490 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1491 rb->lcd_putsxy(3, 36, "[F1] Down a Level");
1492 rb->lcd_putsxy(3, 46, "[F2] Restart Level");
1493 rb->lcd_putsxy(3, 56, "[F3] Up a Level");
1494 #elif CONFIG_KEYPAD == ONDIO_PAD
1495 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1496 rb->lcd_putsxy(3, 16, "[MODE] Undo");
1497 rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1498 rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1499 rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1500 rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1501 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1502 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1503 rb->lcd_putsxy(3, 6, "[STOP] Menu");
1504 rb->lcd_putsxy(3, 16, "[REC] Undo");
1505 rb->lcd_putsxy(3, 26, "[MODE] Redo");
1506 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1507 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1508 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1509 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1510 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1511 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1512 rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1513 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1514 rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1515 rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1516 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1517 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1518 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1519 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1520 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1521 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
1522 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1523 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1524 rb->lcd_putsxy(3, 16, "[REW] Undo");
1525 rb->lcd_putsxy(3, 26, "[FF] Redo");
1526 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1527 rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1528 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1529 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1530 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1531 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1532 rb->lcd_putsxy(3, 26, "[A] Redo");
1533 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1534 rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
1535 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1536 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1537 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1538 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1539 rb->lcd_putsxy(3, 26, "[REC] Redo");
1540 rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1541 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1542 rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1543 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1544 rb->lcd_putsxy(3, 6, "[MENU] Menu");
1545 rb->lcd_putsxy(3, 16, "[VOL+] Undo");
1546 rb->lcd_putsxy(3, 26, "[VOL-] Redo");
1547 rb->lcd_putsxy(3, 36, "[PREV] Previous Level");
1548 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1549 rb->lcd_putsxy(3, 56, "[NEXT] Next Level");
1550 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
1551 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1552 rb->lcd_putsxy(3, 16, "[PREV] Undo");
1553 rb->lcd_putsxy(3, 26, "[NEXT] Redo");
1554 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1555 rb->lcd_putsxy(3, 46, "[NEXT+PREV] Restart Level");
1556 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1557 #endif
1559 #ifdef HAVE_TOUCHSCREEN
1560 rb->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME " Menu");
1561 rb->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME " Undo");
1562 rb->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME " Redo");
1563 rb->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME " Pause");
1564 rb->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME " Restart Level");
1565 #endif
1567 FOR_NB_SCREENS(i)
1568 rb->screens[i]->update();
1570 /* Display until keypress */
1571 do {
1572 rb->sleep(HZ/20);
1573 button = rb->button_get(false);
1574 } while (!button || button & BUTTON_REL ||
1575 button & BUTTON_REPEAT);
1577 menu_quit = false;
1578 break;
1580 case 4: /* Load default levelset */
1581 init_boards();
1582 if (!read_levels(true))
1583 return 5; /* Quit */
1584 load_level();
1585 break;
1587 case 5: /* Quit */
1588 break;
1590 case 6: /* Save & quit */
1591 save(SOKOBAN_SAVE_FILE, false);
1592 rb->reload_directory();
1595 } while (!menu_quit);
1597 /* Restore font */
1598 rb->lcd_setfont(SOKOBAN_FONT);
1600 FOR_NB_SCREENS(i) {
1601 rb->screens[i]->clear_display();
1602 rb->screens[i]->update();
1605 return selection;
1608 static bool sokoban_loop(void)
1610 bool moved;
1611 int i = 0, button = 0;
1612 #if defined(SOKOBAN_UNDO_PRE)
1613 int lastbutton = 0;
1614 #endif
1615 int w, h;
1616 char *loc;
1618 while (true) {
1619 moved = false;
1621 button = rb->button_get(true);
1623 switch(button)
1625 #ifdef SOKOBAN_RC_MENU
1626 case SOKOBAN_RC_MENU:
1627 #endif
1628 case SOKOBAN_MENU:
1629 switch (sokoban_menu()) {
1630 case 5: /* Quit */
1631 case 6: /* Save & quit */
1632 return PLUGIN_OK;
1634 update_screen();
1635 break;
1637 case SOKOBAN_UNDO:
1638 #ifdef SOKOBAN_UNDO_PRE
1639 if (lastbutton != SOKOBAN_UNDO_PRE)
1640 break;
1641 #else /* repeat can't work here for Ondio, iPod, et al */
1642 case SOKOBAN_UNDO | BUTTON_REPEAT:
1643 #endif
1644 undo();
1645 rb->lcd_clear_display();
1646 update_screen();
1647 break;
1649 #ifdef SOKOBAN_REDO
1650 case SOKOBAN_REDO:
1651 case SOKOBAN_REDO | BUTTON_REPEAT:
1652 moved = redo();
1653 rb->lcd_clear_display();
1654 update_screen();
1655 break;
1656 #endif
1658 #ifdef SOKOBAN_LEVEL_UP
1659 case SOKOBAN_LEVEL_UP:
1660 case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
1661 /* next level */
1662 init_undo();
1663 if (current_info.level.index + 1 < current_info.max_level)
1664 current_info.level.index++;
1666 draw_level();
1667 break;
1668 #endif
1670 #ifdef SOKOBAN_LEVEL_DOWN
1671 case SOKOBAN_LEVEL_DOWN:
1672 case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
1673 /* previous level */
1674 init_undo();
1675 if (current_info.level.index > 0)
1676 current_info.level.index--;
1678 draw_level();
1679 break;
1680 #endif
1682 #ifdef SOKOBAN_LEVEL_REPEAT
1683 case SOKOBAN_LEVEL_REPEAT:
1684 case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT:
1685 /* same level */
1686 init_undo();
1687 draw_level();
1688 break;
1689 #endif
1691 case SOKOBAN_LEFT:
1692 case SOKOBAN_LEFT | BUTTON_REPEAT:
1693 moved = move(SOKOBAN_MOVE_LEFT, false);
1694 break;
1696 case SOKOBAN_RIGHT:
1697 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1698 moved = move(SOKOBAN_MOVE_RIGHT, false);
1699 break;
1701 case SOKOBAN_UP:
1702 case SOKOBAN_UP | BUTTON_REPEAT:
1703 moved = move(SOKOBAN_MOVE_UP, false);
1704 break;
1706 case SOKOBAN_DOWN:
1707 case SOKOBAN_DOWN | BUTTON_REPEAT:
1708 moved = move(SOKOBAN_MOVE_DOWN, false);
1709 break;
1711 default:
1712 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1713 return PLUGIN_USB_CONNECTED;
1714 break;
1716 #if defined(SOKOBAN_UNDO_PRE)
1717 lastbutton = button;
1718 #endif
1720 if (moved) {
1721 rb->lcd_clear_display();
1722 update_screen();
1725 /* We have completed this level */
1726 if (current_info.level.boxes_to_go == 0) {
1728 if (moved) {
1729 rb->lcd_clear_display();
1731 /* Show level complete message & stats */
1732 rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
1733 current_info.level.index + 1);
1734 rb->lcd_getstringsize(buf, &w, &h);
1735 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
1737 rb->snprintf(buf, sizeof(buf), "%4d Moves ",
1738 current_info.level.moves);
1739 rb->lcd_getstringsize(buf, &w, &h);
1740 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
1742 rb->snprintf(buf, sizeof(buf), "%4d Pushes",
1743 current_info.level.pushes);
1744 rb->lcd_getstringsize(buf, &w, &h);
1745 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
1747 if (undo_info.count < MAX_UNDOS) {
1748 rb->snprintf(buf, sizeof(buf), "%s: Save solution",
1749 BUTTON_SAVE_NAME);
1750 rb->lcd_getstringsize(buf, &w, &h);
1751 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
1754 rb->lcd_update();
1755 rb->sleep(HZ/4);
1756 rb->button_clear_queue();
1758 /* Display for 4 seconds or until new keypress */
1759 for (i = 0; i < 80; i++) {
1760 rb->sleep(HZ/20);
1761 button = rb->button_get(false);
1762 if (button && !(button & BUTTON_REL) &&
1763 !(button & BUTTON_REPEAT))
1764 break;
1767 if (button == BUTTON_SAVE) {
1768 if (undo_info.count < MAX_UNDOS) {
1769 /* Set filename to current levelset plus level number
1770 * and .sok extension. Use SAVE_FOLDER if using the
1771 * default levelset, since it's in a hidden folder. */
1772 if (rb->strcmp(buffered_boards.filename,
1773 SOKOBAN_LEVELS_FILE) == 0) {
1774 rb->snprintf(buf, sizeof(buf),
1775 "%s/sokoban.%d.sok",
1776 SOKOBAN_SAVE_FOLDER,
1777 current_info.level.index + 1);
1778 } else {
1779 if ((loc = rb->strrchr(buffered_boards.filename,
1780 '.')) != NULL)
1781 *loc = '\0';
1782 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1783 buffered_boards.filename,
1784 current_info.level.index + 1);
1785 if (loc != NULL)
1786 *loc = '.';
1789 if (!rb->kbd_input(buf, MAX_PATH))
1790 save(buf, true);
1791 } else
1792 rb->splash(HZ*2, "Solution too long to save");
1794 rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
1798 FOR_NB_SCREENS(i) {
1799 rb->screens[i]->clear_display();
1800 rb->screens[i]->update();
1803 current_info.level.index++;
1805 /* clear undo stats */
1806 init_undo();
1808 if (current_info.level.index >= current_info.max_level) {
1809 /* Show levelset complete message */
1810 rb->snprintf(buf, sizeof(buf), "You WIN!!");
1811 rb->lcd_getstringsize(buf, &w, &h);
1812 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
1814 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1815 /* Display for 4 seconds or until keypress */
1816 for (i = 0; i < 80; i++) {
1817 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1818 rb->lcd_update();
1819 rb->sleep(HZ/10);
1821 button = rb->button_get(false);
1822 if (button && !(button & BUTTON_REL))
1823 break;
1825 rb->lcd_set_drawmode(DRMODE_SOLID);
1827 /* Reset to first level & show quit menu */
1828 current_info.level.index = 0;
1830 switch (sokoban_menu()) {
1831 case 5: /* Quit */
1832 case 6: /* Save & quit */
1833 return PLUGIN_OK;
1837 load_level();
1838 update_screen();
1841 } /* end while */
1843 return PLUGIN_OK;
1847 enum plugin_status plugin_start(const void* parameter)
1849 int w, h;
1851 (void)(parameter);
1853 rb->lcd_setfont(SOKOBAN_FONT);
1855 rb->lcd_clear_display();
1856 rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
1857 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
1858 rb->lcd_update();
1859 rb->sleep(HZ); /* Show title for 1 second */
1861 init_boards();
1863 if (parameter == NULL) {
1864 /* Attempt to resume saved progress, otherwise start at beginning */
1865 if (!load(SOKOBAN_SAVE_FILE, true)) {
1866 init_boards();
1867 if (!read_levels(true))
1868 return PLUGIN_OK;
1869 load_level();
1872 } else {
1873 /* The plugin is being used to open a file */
1874 if (load((char*) parameter, false)) {
1875 /* If we loaded & played a solution, quit */
1876 if (current_info.level.boxes_to_go == 0)
1877 return PLUGIN_OK;
1878 } else
1879 return PLUGIN_OK;
1882 rb->lcd_clear_display();
1883 update_screen();
1885 return sokoban_loop();