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_DATA_DIR "/sokoban.save"
35 #define SOKOBAN_SAVE_FOLDER PLUGIN_GAMES_DATA_DIR
37 #define SOKOBAN_SAVE_FOLDER "/games"
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
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)
59 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
61 #else /* vertical layout*/
62 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
63 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
66 /* size of code+bss */
67 #if CONFIG_CPU == SH7034
68 #define CODE_SIZE 0x3000 /* 12k */
70 #define CODE_SIZE 0x5000 /* 20k */
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)
80 #define MAX_LEVEL_DATA 0x20000
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
90 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE)
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
490 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_REW)
491 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_FF)
492 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
493 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
494 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
495 #define SOKOBAN_PAUSE BUTTON_PLAY
496 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_ENTER)
497 #define BUTTON_SAVE_NAME "PLAY+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_SELECT
505 #define SOKOBAN_UNDO (BUTTON_BACK|BUTTON_SELECT)
506 #define SOKOBAN_REDO (BUTTON_PLAYPAUSE|BUTTON_SELECT)
507 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
508 #define SOKOBAN_LEVEL_REPEAT BUTTON_BACK
509 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
510 #define SOKOBAN_PAUSE BUTTON_PLAYPAUSE
511 #define BUTTON_SAVE (BUTTON_SELECT|BUTTON_REPEAT)
512 #define BUTTON_SAVE_NAME "SELECT LONG"
514 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
515 #define SOKOBAN_LEFT BUTTON_LEFT
516 #define SOKOBAN_RIGHT BUTTON_RIGHT
517 #define SOKOBAN_UP BUTTON_UP
518 #define SOKOBAN_DOWN BUTTON_DOWN
519 #define SOKOBAN_MENU BUTTON_POWER
520 #define SOKOBAN_UNDO BUTTON_PREV
521 #define SOKOBAN_REDO BUTTON_NEXT
522 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
523 #define SOKOBAN_LEVEL_REPEAT (BUTTON_NEXT|BUTTON_PREV)
524 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
525 #define SOKOBAN_PAUSE BUTTON_SELECT
526 #define BUTTON_SAVE (BUTTON_SELECT|BUTTON_REPEAT)
527 #define BUTTON_SAVE_NAME "SELECT LONG"
529 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
530 #define SOKOBAN_LEFT BUTTON_LEFT
531 #define SOKOBAN_RIGHT BUTTON_RIGHT
532 #define SOKOBAN_UP BUTTON_UP
533 #define SOKOBAN_DOWN BUTTON_DOWN
534 #define SOKOBAN_MENU BUTTON_MENU
535 #define SOKOBAN_UNDO BUTTON_BACK
536 #define SOKOBAN_REDO BUTTON_USER
537 //#define SOKOBAN_LEVEL_DOWN (BUTTON_POWER|BUTTON_REL)
538 //#define SOKOBAN_LEVEL_REPEAT (BUTTON_CENTER|BUTTON_REPEAT)
539 //#define SOKOBAN_LEVEL_UP (BUTTON_MENU|BUTTON_REPEAT)
540 #define SOKOBAN_PAUSE BUTTON_SELECT
541 #define BUTTON_SAVE BUTTON_SELECT
542 #define BUTTON_SAVE_NAME "SELECT"
545 #error No keymap defined!
548 #ifdef HAVE_TOUCHSCREEN
550 #define SOKOBAN_LEFT BUTTON_MIDLEFT
552 #ifndef SOKOBAN_RIGHT
553 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
556 #define SOKOBAN_UP BUTTON_TOPMIDDLE
559 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
562 #define SOKOBAN_MENU BUTTON_TOPLEFT
563 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
566 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
567 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
570 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
571 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
573 #ifndef SOKOBAN_PAUSE
574 #define SOKOBAN_PAUSE BUTTON_CENTER
575 #define SOKOBAN_PAUSE_NAME "[CENTER]"
577 #ifndef SOKOBAN_LEVEL_REPEAT
578 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
579 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
582 #define BUTTON_SAVE BUTTON_CENTER
583 #define BUTTON_SAVE_NAME "CENTER"
587 #define SOKOBAN_FONT FONT_SYSFIXED
590 /* The Location, Undo and LevelInfo structs are OO-flavored.
591 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
592 * but the overall data layout becomes more manageable. */
594 /* Level data & stats */
596 int index
; /* Level index (level number - 1) */
597 int moves
; /* Moves & pushes for the stats */
599 short boxes_to_go
; /* Number of unplaced boxes remaining in level */
600 short height
; /* Height & width for centering level display */
609 /* Our full undo history */
610 static struct UndoInfo
{
611 int count
; /* How many undos have been done */
612 int current
; /* Which history is the current undo */
613 int max
; /* Which history is the max redoable */
614 char history
[MAX_UNDOS
];
617 /* Our playing board */
618 static struct BoardInfo
{
619 char board
[ROWS
][COLS
]; /* The current board data */
620 struct LevelInfo level
; /* Level data & stats */
621 struct Location player
; /* Where the player is */
622 int max_level
; /* The number of levels we have */
625 static struct BufferedBoards
{
626 char filename
[MAX_PATH
]; /* Filename of the levelset we're using */
627 char data
[MAX_LEVEL_DATA
]; /* Buffered level data */
628 int index
[MAX_LEVELS
+ 1]; /* Where each buffered board begins & ends */
629 int start
; /* Index of first buffered board */
630 int end
; /* Index of last buffered board */
631 short prebuffered_boards
; /* Number of boards before current to store */
635 static char buf
[ROWS
*(COLS
+ 1)]; /* Enough for a whole board or a filename */
638 static void init_undo(void)
641 undo_info
.current
= 0;
645 static void get_delta(char direction
, short *d_r
, short *d_c
)
648 case SOKOBAN_PUSH_LEFT
:
649 case SOKOBAN_MOVE_LEFT
:
653 case SOKOBAN_PUSH_RIGHT
:
654 case SOKOBAN_MOVE_RIGHT
:
658 case SOKOBAN_PUSH_UP
:
659 case SOKOBAN_MOVE_UP
:
663 case SOKOBAN_PUSH_DOWN
:
664 case SOKOBAN_MOVE_DOWN
:
670 static bool undo(void)
674 short d_r
= 0, d_c
= 0; /* delta row & delta col */
675 char *space_cur
, *space_next
, *space_prev
;
676 bool undo_push
= false;
678 /* If no more undos or we've wrapped all the way around, quit */
679 if (undo_info
.count
== 0 || undo_info
.current
- 1 == undo_info
.max
)
682 /* Move to previous undo in the list */
683 if (undo_info
.current
== 0 && undo_info
.count
> 1)
684 undo_info
.current
= MAX_UNDOS
- 1;
690 undo
= undo_info
.history
[undo_info
.current
];
692 if (undo
< SOKOBAN_MOVE_MIN
)
695 get_delta(undo
, &d_r
, &d_c
);
697 r
= current_info
.player
.row
;
698 c
= current_info
.player
.col
;
700 /* Give the 3 spaces we're going to use better names */
701 space_cur
= ¤t_info
.board
[r
][c
];
702 space_next
= ¤t_info
.board
[r
+ d_r
][c
+ d_c
];
703 space_prev
= ¤t_info
.board
[r
- d_r
][c
- d_c
];
705 /* Update board info */
707 /* Moving box from goal to floor */
708 if (*space_next
== '*' && *space_cur
== '@')
709 current_info
.level
.boxes_to_go
++;
710 /* Moving box from floor to goal */
711 else if (*space_next
== '$' && *space_cur
== '+')
712 current_info
.level
.boxes_to_go
--;
714 /* Move box off of next space... */
715 *space_next
= (*space_next
== '*' ? '.' : ' ');
716 /* ...and on to current space */
717 *space_cur
= (*space_cur
== '+' ? '*' : '$');
719 current_info
.level
.pushes
--;
721 /* Just move player off of current space */
722 *space_cur
= (*space_cur
== '+' ? '.' : ' ');
723 /* Move player back to previous space */
724 *space_prev
= (*space_prev
== '.' ? '+' : '@');
726 /* Update position */
727 current_info
.player
.row
-= d_r
;
728 current_info
.player
.col
-= d_c
;
730 current_info
.level
.moves
--;
735 static void add_undo(char undo
)
737 undo_info
.history
[undo_info
.current
] = undo
;
739 /* Wrap around if MAX_UNDOS exceeded */
740 if (undo_info
.current
< (MAX_UNDOS
- 1))
743 undo_info
.current
= 0;
745 if (undo_info
.count
< MAX_UNDOS
)
749 static bool move(char direction
, bool redo
)
752 short d_r
= 0, d_c
= 0; /* delta row & delta col */
753 char *space_cur
, *space_next
, *space_beyond
;
756 get_delta(direction
, &d_r
, &d_c
);
758 r
= current_info
.player
.row
;
759 c
= current_info
.player
.col
;
761 /* Check for out-of-bounds */
762 if (r
+ 2*d_r
< 0 || r
+ 2*d_r
>= ROWS
||
763 c
+ 2*d_c
< 0 || c
+ 2*d_c
>= COLS
)
766 /* Give the 3 spaces we're going to use better names */
767 space_cur
= ¤t_info
.board
[r
][c
];
768 space_next
= ¤t_info
.board
[r
+ d_r
][c
+ d_c
];
769 space_beyond
= ¤t_info
.board
[r
+ 2*d_r
][c
+ 2*d_c
];
771 if (*space_next
== '$' || *space_next
== '*') {
772 /* Change direction from move to push for undo */
773 if (direction
>= SOKOBAN_MOVE_MIN
)
774 direction
-= SOKOBAN_MOVE_DIFF
;
777 else if (direction
< SOKOBAN_MOVE_MIN
)
778 /* Change back to move if redo/solution playback push is invalid */
779 direction
+= SOKOBAN_MOVE_DIFF
;
781 /* Update board info */
783 /* Moving box from goal to floor */
784 if (*space_next
== '*' && *space_beyond
== ' ')
785 current_info
.level
.boxes_to_go
++;
786 /* Moving box from floor to goal */
787 else if (*space_next
== '$' && *space_beyond
== '.')
788 current_info
.level
.boxes_to_go
--;
789 /* Check for invalid move */
790 else if (*space_beyond
!= '.' && *space_beyond
!= ' ')
793 /* Move player onto next space */
794 *space_next
= (*space_next
== '*' ? '+' : '@');
795 /* Move box onto space beyond next */
796 *space_beyond
= (*space_beyond
== '.' ? '*' : '$');
798 current_info
.level
.pushes
++;
800 /* Check for invalid move */
801 if (*space_next
!= '.' && *space_next
!= ' ')
804 /* Move player onto next space */
805 *space_next
= (*space_next
== '.' ? '+' : '@');
807 /* Move player off of current space */
808 *space_cur
= (*space_cur
== '+' ? '.' : ' ');
810 /* Update position */
811 current_info
.player
.row
+= d_r
;
812 current_info
.player
.col
+= d_c
;
814 current_info
.level
.moves
++;
816 /* Update undo_info.max to current on every normal move,
817 * except if it's the same as a redo. */
818 /* normal move and either */
820 /* moves have been undone... */
821 ((undo_info
.max
!= undo_info
.current
&&
822 /* ...and the current move is NOT the same as the one in history */
823 undo_info
.history
[undo_info
.current
] != direction
) ||
824 /* or moves have not been undone */
825 undo_info
.max
== undo_info
.current
)) {
827 undo_info
.max
= undo_info
.current
;
828 } else /* redo move or move was same as redo */
829 add_undo(direction
); /* add_undo to update current */
835 static bool redo(void)
837 /* If no moves have been undone, quit */
838 if (undo_info
.current
== undo_info
.max
)
841 return move(undo_info
.history
[(undo_info
.current
< MAX_UNDOS
?
842 undo_info
.current
: 0)], true);
846 static void init_boards(void)
848 rb
->strlcpy(buffered_boards
.filename
, SOKOBAN_LEVELS_FILE
,
849 sizeof(buffered_boards
.filename
));
851 current_info
.level
.index
= 0;
852 current_info
.player
.row
= 0;
853 current_info
.player
.col
= 0;
854 current_info
.max_level
= 0;
856 buffered_boards
.start
= 0;
857 buffered_boards
.end
= 0;
858 buffered_boards
.prebuffered_boards
= 0;
863 static bool read_levels(bool initialize
)
873 bool index_set
= false;
875 /* Get the index of the first level to buffer */
876 if (current_info
.level
.index
> buffered_boards
.prebuffered_boards
&&
878 buffered_boards
.start
= current_info
.level
.index
-
879 buffered_boards
.prebuffered_boards
;
881 buffered_boards
.start
= 0;
883 if ((fd
= rb
->open(buffered_boards
.filename
, O_RDONLY
)) < 0) {
884 rb
->splashf(HZ
*2, "Unable to open %s", buffered_boards
.filename
);
889 len
= rb
->read_line(fd
, buf
, sizeof(buf
));
891 /* Correct len when trailing \r's or \n's are counted */
892 if (len
> 2 && buf
[len
- 2] == '\0')
894 else if (len
> 1 && buf
[len
- 1] == '\0')
897 /* Skip short lines & lines with non-level data */
898 if (len
>= 3 && ((buf
[0] >= '1' && buf
[0] <= '9') || buf
[0] == '#' ||
899 buf
[0] == ' ' || buf
[0] == '-' || buf
[0] == '_')) {
900 if (level_count
>= buffered_boards
.start
) {
901 /* Set the index of this level */
903 level_count
- buffered_boards
.start
< MAX_LEVELS
) {
904 buffered_boards
.index
[level_count
- buffered_boards
.start
]
908 /* Copy buffer to board data */
909 if (i
+ level_len
+ len
< MAX_LEVEL_DATA
) {
910 rb
->memcpy(&buffered_boards
.data
[i
+ level_len
], buf
, len
);
911 buffered_boards
.data
[i
+ level_len
+ len
] = '\n';
914 level_len
+= len
+ 1;
917 /* If newline & level is tall enough or is RLE */
918 } else if (buf
[0] == '\0' && (row
> 2 || lastlen
> 22)) {
920 if (level_count
>= buffered_boards
.start
) {
922 if (i
< MAX_LEVEL_DATA
)
923 buffered_boards
.end
= level_count
;
924 else if (!initialize
)
934 } while ((lastlen
= len
));
936 /* Set the index of the end of the last level */
937 if (level_count
- buffered_boards
.start
< MAX_LEVELS
)
938 buffered_boards
.index
[level_count
- buffered_boards
.start
] = i
;
941 current_info
.max_level
= level_count
;
942 buffered_boards
.prebuffered_boards
= buffered_boards
.end
/2;
950 static void load_level(void)
955 int index
= current_info
.level
.index
- buffered_boards
.start
;
958 /* Get the buffered board index of the current level */
959 if (current_info
.level
.index
< buffered_boards
.start
||
960 current_info
.level
.index
>= buffered_boards
.end
) {
962 if (current_info
.level
.index
> buffered_boards
.prebuffered_boards
)
963 index
= buffered_boards
.prebuffered_boards
;
965 index
= current_info
.level
.index
;
967 level
= &buffered_boards
.data
[buffered_boards
.index
[index
]];
969 /* Reset level info */
970 current_info
.level
.moves
= 0;
971 current_info
.level
.pushes
= 0;
972 current_info
.level
.boxes_to_go
= 0;
973 current_info
.level
.width
= 0;
976 for (r
= 0; r
< ROWS
; r
++)
977 for (c
= 0; c
< COLS
; c
++)
978 current_info
.board
[r
][c
] = 'X';
980 level_size
= buffered_boards
.index
[index
+ 1] -
981 buffered_boards
.index
[index
];
983 for (r
= 0, c
= 0, n
= 1, i
= 0; i
< level_size
; i
++) {
984 if (level
[i
] == '\n' || level
[i
] == '|') {
986 /* Update max width of level & go to next row */
987 if (c
> current_info
.level
.width
)
988 current_info
.level
.width
= c
;
994 } else if (c
< COLS
) {
995 /* Read RLE character's length into n */
996 if (level
[i
] >= '0' && level
[i
] <= '9') {
997 n
= level
[i
++] - '0';
998 if (level
[i
] >= '0' && level
[i
] <= '9')
999 n
= n
*10 + level
[i
++] - '0';
1002 /* Cleanup & replace */
1003 if (level
[i
] == '%')
1005 else if (level
[i
] == '-' || level
[i
] == '_')
1012 if (level
[i
] == '.')
1013 current_info
.level
.boxes_to_go
+= n
;
1015 /* Put RLE character n times */
1017 current_info
.board
[r
][c
++] = level
[i
];
1021 if (level
[i
] == '.' || level
[i
] == '+')
1022 current_info
.level
.boxes_to_go
++;
1024 if (level
[i
] == '@' ||level
[i
] == '+') {
1025 current_info
.player
.row
= r
;
1026 current_info
.player
.col
= c
;
1029 current_info
.board
[r
][c
++] = level
[i
];
1034 current_info
.level
.height
= r
;
1037 /* Fill in blank space outside level on color targets */
1038 for (r
= 0; r
< ROWS
; r
++)
1039 for (c
= 0; current_info
.board
[r
][c
] == ' ' && c
< COLS
; c
++)
1040 current_info
.board
[r
][c
] = 'X';
1042 for (c
= 0; c
< COLS
; c
++) {
1043 for (r
= 0; (current_info
.board
[r
][c
] == ' ' ||
1044 current_info
.board
[r
][c
] == 'X') && r
< ROWS
; r
++)
1045 current_info
.board
[r
][c
] = 'X';
1046 for (r
= ROWS
- 1; (current_info
.board
[r
][c
] == ' ' ||
1047 current_info
.board
[r
][c
] == 'X') && r
>= 0; r
--)
1048 current_info
.board
[r
][c
] = 'X';
1053 static void update_screen(void)
1058 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
1059 #define STAT_HEIGHT 25
1060 #define STAT_X (LCD_WIDTH - 120)/2
1061 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
1062 #define BOARD_WIDTH LCD_WIDTH
1063 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
1064 rb
->lcd_putsxy(STAT_X
+ 4, STAT_Y
+ 4, "Level");
1065 rb
->lcd_putsxyf(STAT_X
+ 7, STAT_Y
+ 14, "%d", current_info
.level
.index
+ 1);
1066 rb
->lcd_putsxy(STAT_X
+ 41, STAT_Y
+ 4, "Moves");
1067 rb
->lcd_putsxyf(STAT_X
+ 44, STAT_Y
+ 14, "%d", current_info
.level
.moves
);
1068 rb
->lcd_putsxy(STAT_X
+ 79, STAT_Y
+ 4, "Pushes");
1069 rb
->lcd_putsxyf(STAT_X
+ 82, STAT_Y
+ 14, "%d", current_info
.level
.pushes
);
1071 rb
->lcd_drawrect(STAT_X
, STAT_Y
, 38, STAT_HEIGHT
);
1072 rb
->lcd_drawrect(STAT_X
+ 37, STAT_Y
, 39, STAT_HEIGHT
);
1073 rb
->lcd_drawrect(STAT_X
+ 75, STAT_Y
, 45, STAT_HEIGHT
);
1075 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
1076 #define STAT_X (LCD_WIDTH - 40)
1078 #define STAT_X COLS*SOKOBAN_TILESIZE
1080 #define STAT_Y (LCD_HEIGHT - 64)/2
1081 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
1082 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
1083 #define BOARD_HEIGHT LCD_HEIGHT
1084 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 2, "Level");
1085 rb
->lcd_putsxyf(STAT_X
+ 4, STAT_Y
+ 12, "%d", current_info
.level
.index
+ 1);
1086 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 23, "Moves");
1087 rb
->lcd_putsxyf(STAT_X
+ 4, STAT_Y
+ 33, "%d", current_info
.level
.moves
);
1089 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 44, "Push");
1091 rb
->lcd_putsxy(STAT_X
+ 1, STAT_Y
+ 44, "Pushes");
1093 rb
->lcd_putsxyf(STAT_X
+ 4, STAT_Y
+ 54, "%d", current_info
.level
.pushes
);
1095 rb
->lcd_drawrect(STAT_X
, STAT_Y
+ 0, STAT_WIDTH
, 64);
1096 rb
->lcd_hline(STAT_X
, LCD_WIDTH
- 1, STAT_Y
+ 21);
1097 rb
->lcd_hline(STAT_X
, LCD_WIDTH
- 1, STAT_Y
+ 42);
1101 /* load the board to the screen */
1102 for (rows
= 0; rows
< ROWS
; rows
++) {
1103 for (cols
= 0; cols
< COLS
; cols
++) {
1104 c
= cols
*SOKOBAN_TILESIZE
+
1105 (BOARD_WIDTH
- current_info
.level
.width
*SOKOBAN_TILESIZE
)/2;
1106 r
= rows
*SOKOBAN_TILESIZE
+
1107 (BOARD_HEIGHT
- current_info
.level
.height
*SOKOBAN_TILESIZE
)/2;
1109 switch(current_info
.board
[rows
][cols
]) {
1110 case 'X': /* blank space outside of level */
1113 case ' ': /* floor */
1114 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 0*SOKOBAN_TILESIZE
,
1115 STRIDE( SCREEN_MAIN
,
1116 BMPWIDTH_sokoban_tiles
,
1117 BMPHEIGHT_sokoban_tiles
),
1118 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1121 case '#': /* wall */
1122 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 1*SOKOBAN_TILESIZE
,
1123 STRIDE( SCREEN_MAIN
,
1124 BMPWIDTH_sokoban_tiles
,
1125 BMPHEIGHT_sokoban_tiles
),
1126 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1130 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 2*SOKOBAN_TILESIZE
,
1131 STRIDE( SCREEN_MAIN
,
1132 BMPWIDTH_sokoban_tiles
,
1133 BMPHEIGHT_sokoban_tiles
),
1134 c
, r
, SOKOBAN_TILESIZE
,SOKOBAN_TILESIZE
);
1137 case '*': /* box on goal */
1138 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 3*SOKOBAN_TILESIZE
,
1139 STRIDE( SCREEN_MAIN
,
1140 BMPWIDTH_sokoban_tiles
,
1141 BMPHEIGHT_sokoban_tiles
),
1142 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1145 case '.': /* goal */
1146 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 4*SOKOBAN_TILESIZE
,
1147 STRIDE( SCREEN_MAIN
,
1148 BMPWIDTH_sokoban_tiles
,
1149 BMPHEIGHT_sokoban_tiles
),
1150 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1153 case '@': /* player */
1154 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 5*SOKOBAN_TILESIZE
,
1155 STRIDE( SCREEN_MAIN
,
1156 BMPWIDTH_sokoban_tiles
,
1157 BMPHEIGHT_sokoban_tiles
),
1158 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1161 case '+': /* player on goal */
1162 rb
->lcd_bitmap_part(sokoban_tiles
, 0, 6*SOKOBAN_TILESIZE
,
1163 STRIDE( SCREEN_MAIN
,
1164 BMPWIDTH_sokoban_tiles
,
1165 BMPHEIGHT_sokoban_tiles
),
1166 c
, r
, SOKOBAN_TILESIZE
, SOKOBAN_TILESIZE
);
1172 /* print out the screen */
1176 static void draw_level(void)
1179 rb
->lcd_clear_display();
1183 static bool save(char *filename
, bool solution
)
1188 char dirname
[MAX_PATH
];
1190 rb
->splash(0, "Saving...");
1192 /* Create dir if it doesn't exist */
1193 if ((loc
= rb
->strrchr(filename
, '/')) != NULL
) {
1194 rb
->strlcpy(dirname
, filename
, loc
- filename
+ 1);
1196 if(!(dir
= rb
->opendir(dirname
)))
1202 if (filename
[0] == '\0' ||
1203 (fd
= rb
->open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666)) < 0) {
1204 rb
->splashf(HZ
*2, "Unable to open %s", filename
);
1208 /* Sokoban: S/P for solution/progress : level number : current undo */
1209 rb
->snprintf(buf
, sizeof(buf
), "Sokoban:%c:%d:%d\n", (solution
? 'S' : 'P'),
1210 current_info
.level
.index
+ 1, undo_info
.current
);
1211 rb
->write(fd
, buf
, rb
->strlen(buf
));
1213 /* Filename of levelset */
1214 rb
->write(fd
, buffered_boards
.filename
,
1215 rb
->strlen(buffered_boards
.filename
));
1216 rb
->write(fd
, "\n", 1);
1218 /* Full undo history */
1219 rb
->write(fd
, undo_info
.history
, undo_info
.max
);
1226 static bool load(char *filename
, bool silent
)
1233 bool paused
= false;
1234 unsigned short speed
= 2;
1235 int delay
[] = {HZ
/2, HZ
/3, HZ
/4, HZ
/6, HZ
/8, HZ
/12, HZ
/16, HZ
/25};
1237 if (filename
[0] == '\0' || (fd
= rb
->open(filename
, O_RDONLY
)) < 0) {
1239 rb
->splashf(HZ
*2, "Unable to open %s", filename
);
1243 /* Read header, level number, & current undo */
1244 rb
->read_line(fd
, buf
, sizeof(buf
));
1246 /* If we're opening a level file, not a solution/progress file */
1247 if (rb
->strncmp(buf
, "Sokoban", 7) != 0) {
1250 rb
->strlcpy(buffered_boards
.filename
, filename
,
1251 sizeof(buffered_boards
.filename
));
1253 if (!read_levels(true))
1256 current_info
.level
.index
= 0;
1259 /* If there aren't any boxes to go or the player position wasn't set,
1260 * the file probably wasn't a Sokoban level file */
1261 if (current_info
.level
.boxes_to_go
== 0 ||
1262 current_info
.player
.row
== 0 || current_info
.player
.col
== 0) {
1264 rb
->splash(HZ
*2, "File is not a Sokoban level file");
1270 /* Read filename of levelset */
1271 rb
->read_line(fd
, buffered_boards
.filename
,
1272 sizeof(buffered_boards
.filename
));
1274 /* Read full undo history */
1275 len
= rb
->read_line(fd
, undo_info
.history
, MAX_UNDOS
);
1277 /* Correct len when trailing \r's or \n's are counted */
1278 if (len
> 2 && undo_info
.history
[len
- 2] == '\0')
1280 else if (len
> 1 && undo_info
.history
[len
- 1] == '\0')
1285 /* Check to see if we're going to play a solution or resume progress */
1286 play_solution
= (buf
[8] == 'S');
1288 /* Get level number */
1289 for (n
= 0, i
= 10; buf
[i
] >= '0' && buf
[i
] <= '9' && i
< 15; i
++)
1290 n
= n
*10 + buf
[i
] - '0';
1291 current_info
.level
.index
= n
- 1;
1293 /* Get undo index */
1294 for (n
= 0, i
++; buf
[i
] >= '0' && buf
[i
] <= '9' && i
< 21; i
++)
1295 n
= n
*10 + buf
[i
] - '0';
1298 undo_info
.max
= len
;
1300 if (current_info
.level
.index
< 0) {
1302 rb
->splash(HZ
*2, "Error loading level");
1305 if (!read_levels(true))
1307 if (current_info
.level
.index
>= current_info
.max_level
) {
1309 rb
->splash(HZ
*2, "Error loading level");
1315 if (play_solution
) {
1316 rb
->lcd_clear_display();
1318 rb
->sleep(2*delay
[speed
]);
1320 /* Replay solution until menu button is pressed */
1324 if (!move(undo_info
.history
[i
], true)) {
1328 rb
->lcd_clear_display();
1334 rb
->sleep(delay
[speed
]);
1336 while ((button
= rb
->button_get(false)) || paused
) {
1339 /* Pretend the level is complete so we'll quit */
1340 current_info
.level
.boxes_to_go
= 0;
1344 /* Toggle pause state */
1349 case SOKOBAN_LEFT
| BUTTON_REPEAT
:
1350 /* Go back one move */
1354 rb
->lcd_clear_display();
1360 case SOKOBAN_RIGHT
| BUTTON_REPEAT
:
1361 /* Go forward one move */
1365 rb
->lcd_clear_display();
1371 case SOKOBAN_UP
| BUTTON_REPEAT
:
1373 if (speed
< sizeof(delay
)/sizeof(int) - 1)
1378 case SOKOBAN_DOWN
| BUTTON_REPEAT
:
1389 /* If level is complete, wait for keypress before quitting */
1390 if (current_info
.level
.boxes_to_go
== 0)
1391 rb
->button_get(true);
1394 /* Advance to current undo */
1395 for (i
= 0; i
< n
; i
++) {
1396 if (!move(undo_info
.history
[i
], true)) {
1402 rb
->button_clear_queue();
1403 rb
->lcd_clear_display();
1406 undo_info
.current
= n
;
1412 static int sokoban_menu(void)
1417 int start_selected
= 0;
1418 int prev_level
= current_info
.level
.index
;
1420 MENUITEM_STRINGLIST(menu
, "Sokoban Menu", NULL
,
1421 "Resume", "Select Level", "Audio Playback", "Keys",
1422 "Load Default Level Set", "Quit Without Saving",
1423 "Save Progress & Quit");
1427 selection
= rb
->do_menu(&menu
, &start_selected
, NULL
, false);
1429 switch (selection
) {
1430 case 0: /* Resume */
1433 case 1: /* Select level */
1434 current_info
.level
.index
++;
1435 rb
->set_int("Select Level", "", UNIT_INT
,
1436 ¤t_info
.level
.index
, NULL
, 1, 1,
1437 current_info
.max_level
, NULL
);
1438 current_info
.level
.index
--;
1439 if (prev_level
!= current_info
.level
.index
) {
1446 case 2: /* Audio playback control */
1447 playback_control(NULL
);
1453 rb
->screens
[i
]->clear_display();
1454 rb
->lcd_setfont(SOKOBAN_FONT
);
1456 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1457 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1458 rb
->lcd_putsxy(3, 6, "[OFF] Menu");
1459 rb
->lcd_putsxy(3, 16, "[ON] Undo");
1460 rb
->lcd_putsxy(3, 26, "[PLAY] Redo");
1461 rb
->lcd_putsxy(3, 36, "[F1] Down a Level");
1462 rb
->lcd_putsxy(3, 46, "[F2] Restart Level");
1463 rb
->lcd_putsxy(3, 56, "[F3] Up a Level");
1464 #elif CONFIG_KEYPAD == ONDIO_PAD
1465 rb
->lcd_putsxy(3, 6, "[OFF] Menu");
1466 rb
->lcd_putsxy(3, 16, "[MODE] Undo");
1467 rb
->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1468 rb
->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1469 rb
->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1470 rb
->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1471 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1472 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1473 rb
->lcd_putsxy(3, 6, "[STOP] Menu");
1474 rb
->lcd_putsxy(3, 16, "[REC] Undo");
1475 rb
->lcd_putsxy(3, 26, "[MODE] Redo");
1476 rb
->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1477 rb
->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1478 rb
->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1479 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1480 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1481 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1482 rb
->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1483 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1484 rb
->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1485 rb
->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1486 rb
->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1487 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1488 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1489 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1490 rb
->lcd_putsxy(3, 26, "[PLAY] Redo");
1491 rb
->lcd_putsxy(3, 36, "[REC] Restart Level");
1492 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1493 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1494 rb
->lcd_putsxy(3, 16, "[REW] Undo");
1495 rb
->lcd_putsxy(3, 26, "[FF] Redo");
1496 rb
->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1497 rb
->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1498 rb
->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1499 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1500 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1501 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1502 rb
->lcd_putsxy(3, 26, "[A] Redo");
1503 rb
->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1504 rb
->lcd_putsxy(3, 46, "[MENU] Restart Level");
1505 rb
->lcd_putsxy(3, 56, "[VOL+] Next Level");
1506 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1507 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1508 rb
->lcd_putsxy(3, 16, "[SELECT] Undo");
1509 rb
->lcd_putsxy(3, 26, "[REC] Redo");
1510 rb
->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1511 rb
->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1512 rb
->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1513 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1514 rb
->lcd_putsxy(3, 6, "[MENU] Menu");
1515 rb
->lcd_putsxy(3, 16, "[VOL+] Undo");
1516 rb
->lcd_putsxy(3, 26, "[VOL-] Redo");
1517 rb
->lcd_putsxy(3, 36, "[PREV] Previous Level");
1518 rb
->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1519 rb
->lcd_putsxy(3, 56, "[NEXT] Next Level");
1520 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
1521 rb
->lcd_putsxy(3, 6, "[POWER] Menu");
1522 rb
->lcd_putsxy(3, 16, "[PREV] Undo");
1523 rb
->lcd_putsxy(3, 26, "[NEXT] Redo");
1524 rb
->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1525 rb
->lcd_putsxy(3, 46, "[NEXT+PREV] Restart Level");
1526 rb
->lcd_putsxy(3, 56, "[VOL+] Next Level");
1529 #ifdef HAVE_TOUCHSCREEN
1530 rb
->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME
" Menu");
1531 rb
->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME
" Undo");
1532 rb
->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME
" Redo");
1533 rb
->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME
" Pause");
1534 rb
->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME
" Restart Level");
1538 rb
->screens
[i
]->update();
1540 /* Display until keypress */
1543 button
= rb
->button_get(false);
1544 } while (!button
|| button
& BUTTON_REL
||
1545 button
& BUTTON_REPEAT
);
1550 case 4: /* Load default levelset */
1552 if (!read_levels(true))
1553 return 5; /* Quit */
1560 case 6: /* Save & quit */
1561 save(SOKOBAN_SAVE_FILE
, false);
1562 rb
->reload_directory();
1565 } while (!menu_quit
);
1568 rb
->lcd_setfont(SOKOBAN_FONT
);
1571 rb
->screens
[i
]->clear_display();
1572 rb
->screens
[i
]->update();
1578 static bool sokoban_loop(void)
1581 int i
= 0, button
= 0;
1582 #if defined(SOKOBAN_UNDO_PRE)
1591 button
= rb
->button_get(true);
1595 #ifdef SOKOBAN_RC_MENU
1596 case SOKOBAN_RC_MENU
:
1599 switch (sokoban_menu()) {
1601 case 6: /* Save & quit */
1608 #ifdef SOKOBAN_UNDO_PRE
1609 if (lastbutton
!= SOKOBAN_UNDO_PRE
)
1611 #else /* repeat can't work here for Ondio, iPod, et al */
1612 case SOKOBAN_UNDO
| BUTTON_REPEAT
:
1615 rb
->lcd_clear_display();
1621 case SOKOBAN_REDO
| BUTTON_REPEAT
:
1623 rb
->lcd_clear_display();
1628 #ifdef SOKOBAN_LEVEL_UP
1629 case SOKOBAN_LEVEL_UP
:
1630 case SOKOBAN_LEVEL_UP
| BUTTON_REPEAT
:
1633 if (current_info
.level
.index
+ 1 < current_info
.max_level
)
1634 current_info
.level
.index
++;
1640 #ifdef SOKOBAN_LEVEL_DOWN
1641 case SOKOBAN_LEVEL_DOWN
:
1642 case SOKOBAN_LEVEL_DOWN
| BUTTON_REPEAT
:
1643 /* previous level */
1645 if (current_info
.level
.index
> 0)
1646 current_info
.level
.index
--;
1652 #ifdef SOKOBAN_LEVEL_REPEAT
1653 case SOKOBAN_LEVEL_REPEAT
:
1654 case SOKOBAN_LEVEL_REPEAT
| BUTTON_REPEAT
:
1662 case SOKOBAN_LEFT
| BUTTON_REPEAT
:
1663 moved
= move(SOKOBAN_MOVE_LEFT
, false);
1667 case SOKOBAN_RIGHT
| BUTTON_REPEAT
:
1668 moved
= move(SOKOBAN_MOVE_RIGHT
, false);
1672 case SOKOBAN_UP
| BUTTON_REPEAT
:
1673 moved
= move(SOKOBAN_MOVE_UP
, false);
1677 case SOKOBAN_DOWN
| BUTTON_REPEAT
:
1678 moved
= move(SOKOBAN_MOVE_DOWN
, false);
1682 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1683 return PLUGIN_USB_CONNECTED
;
1686 #if defined(SOKOBAN_UNDO_PRE)
1687 lastbutton
= button
;
1691 rb
->lcd_clear_display();
1695 /* We have completed this level */
1696 if (current_info
.level
.boxes_to_go
== 0) {
1699 rb
->lcd_clear_display();
1701 /* Show level complete message & stats */
1702 rb
->snprintf(buf
, sizeof(buf
), "Level %d Complete!",
1703 current_info
.level
.index
+ 1);
1704 rb
->lcd_getstringsize(buf
, &w
, &h
);
1705 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
*3, buf
);
1707 rb
->snprintf(buf
, sizeof(buf
), "%4d Moves ",
1708 current_info
.level
.moves
);
1709 rb
->lcd_getstringsize(buf
, &w
, &h
);
1710 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
, buf
);
1712 rb
->snprintf(buf
, sizeof(buf
), "%4d Pushes",
1713 current_info
.level
.pushes
);
1714 rb
->lcd_getstringsize(buf
, &w
, &h
);
1715 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2, buf
);
1717 if (undo_info
.count
< MAX_UNDOS
) {
1718 rb
->snprintf(buf
, sizeof(buf
), "%s: Save solution",
1720 rb
->lcd_getstringsize(buf
, &w
, &h
);
1721 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 + h
*2, buf
);
1726 rb
->button_clear_queue();
1728 /* Display for 4 seconds or until new keypress */
1729 for (i
= 0; i
< 80; i
++) {
1731 button
= rb
->button_get(false);
1732 if (button
&& !(button
& BUTTON_REL
) &&
1733 !(button
& BUTTON_REPEAT
))
1737 if (button
== BUTTON_SAVE
) {
1738 if (undo_info
.count
< MAX_UNDOS
) {
1739 /* Set filename to current levelset plus level number
1740 * and .sok extension. Use SAVE_FOLDER if using the
1741 * default levelset, since it's in a hidden folder. */
1742 if (rb
->strcmp(buffered_boards
.filename
,
1743 SOKOBAN_LEVELS_FILE
) == 0) {
1744 rb
->snprintf(buf
, sizeof(buf
),
1745 "%s/sokoban.%d.sok",
1746 SOKOBAN_SAVE_FOLDER
,
1747 current_info
.level
.index
+ 1);
1749 if ((loc
= rb
->strrchr(buffered_boards
.filename
,
1752 rb
->snprintf(buf
, sizeof(buf
), "%s.%d.sok",
1753 buffered_boards
.filename
,
1754 current_info
.level
.index
+ 1);
1759 if (!rb
->kbd_input(buf
, MAX_PATH
))
1762 rb
->splash(HZ
*2, "Solution too long to save");
1764 rb
->lcd_setfont(SOKOBAN_FONT
); /* Restore font */
1769 rb
->screens
[i
]->clear_display();
1770 rb
->screens
[i
]->update();
1773 current_info
.level
.index
++;
1775 /* clear undo stats */
1778 if (current_info
.level
.index
>= current_info
.max_level
) {
1779 /* Show levelset complete message */
1780 rb
->snprintf(buf
, sizeof(buf
), "You WIN!!");
1781 rb
->lcd_getstringsize(buf
, &w
, &h
);
1782 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
/2, buf
);
1784 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1785 /* Display for 4 seconds or until keypress */
1786 for (i
= 0; i
< 80; i
++) {
1787 rb
->lcd_fillrect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
1791 button
= rb
->button_get(false);
1792 if (button
&& !(button
& BUTTON_REL
))
1795 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1797 /* Reset to first level & show quit menu */
1798 current_info
.level
.index
= 0;
1800 switch (sokoban_menu()) {
1802 case 6: /* Save & quit */
1817 enum plugin_status
plugin_start(const void* parameter
)
1823 rb
->lcd_setfont(SOKOBAN_FONT
);
1825 rb
->lcd_clear_display();
1826 rb
->lcd_getstringsize(SOKOBAN_TITLE
, &w
, &h
);
1827 rb
->lcd_putsxy(LCD_WIDTH
/2 - w
/2, LCD_HEIGHT
/2 - h
/2, SOKOBAN_TITLE
);
1829 rb
->sleep(HZ
); /* Show title for 1 second */
1833 if (parameter
== NULL
) {
1834 /* Attempt to resume saved progress, otherwise start at beginning */
1835 if (!load(SOKOBAN_SAVE_FILE
, true)) {
1837 if (!read_levels(true))
1843 /* The plugin is being used to open a file */
1844 if (load((char*) parameter
, false)) {
1845 /* If we loaded & played a solution, quit */
1846 if (current_info
.level
.boxes_to_go
== 0)
1852 rb
->lcd_clear_display();
1855 return sokoban_loop();