Make open() posix compliant api-wise. A few calls (those with O_CREAT) need the addit...
[kugel-rb.git] / apps / plugins / sokoban.c
blob3a853c81aa05c5e980ca47c48dc0207ef0c12bb0
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"
27 PLUGIN_HEADER
29 #define SOKOBAN_TITLE "Sokoban"
31 #define SOKOBAN_LEVELS_FILE PLUGIN_GAMES_DIR "/sokoban.levels"
32 #define SOKOBAN_SAVE_FILE PLUGIN_GAMES_DIR "/sokoban.save"
33 #define SOKOBAN_SAVE_FOLDER "/games"
35 #include "pluginbitmaps/sokoban_tiles.h"
36 #define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
38 /* If tilesize is 0 (which it is during dependency generation) gcc will abort
39 (div by 0) and this plugin won't get any dependencies
41 #if SOKOBAN_TILESIZE < 1
42 #define SOKOBAN_TILESIZE 10
43 #endif
45 /* SOKOBAN_TILESIZE is the number of pixels for each block.
46 * Set dynamically so all targets can support levels
47 * that fill their entire screen, less the stat box.
48 * 16 rows & 20 cols minimum */
49 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
50 #define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
51 #if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
52 #define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
53 #else
54 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
55 #endif
56 #else /* vertical layout*/
57 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
58 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
59 #endif
61 /* Use either all but 16k of the plugin buffer for level data
62 * or 128k, which ever is less */
63 #if PLUGIN_BUFFER_SIZE - 0x4000 < 0x20000
64 #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - 0x4000)
65 #else
66 #define MAX_LEVEL_DATA 0x20000
67 #endif
69 /* Number of levels for which to allocate buffer indexes */
70 #define MAX_LEVELS MAX_LEVEL_DATA/70
72 /* Use 4k plus remaining plugin buffer (-12k for prog) for undo, up to 64k */
73 #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000 > 0x10000
74 #define MAX_UNDOS 0x10000
75 #else
76 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000)
77 #endif
79 /* Move/push definitions for undo */
80 #define SOKOBAN_PUSH_LEFT 'L'
81 #define SOKOBAN_PUSH_RIGHT 'R'
82 #define SOKOBAN_PUSH_UP 'U'
83 #define SOKOBAN_PUSH_DOWN 'D'
84 #define SOKOBAN_MOVE_LEFT 'l'
85 #define SOKOBAN_MOVE_RIGHT 'r'
86 #define SOKOBAN_MOVE_UP 'u'
87 #define SOKOBAN_MOVE_DOWN 'd'
89 #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
90 #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
92 /* variable button definitions */
93 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
94 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
95 #define SOKOBAN_LEFT BUTTON_LEFT
96 #define SOKOBAN_RIGHT BUTTON_RIGHT
97 #define SOKOBAN_UP BUTTON_UP
98 #define SOKOBAN_DOWN BUTTON_DOWN
99 #define SOKOBAN_MENU BUTTON_OFF
100 #define SOKOBAN_UNDO BUTTON_ON
101 #define SOKOBAN_REDO BUTTON_PLAY
102 #define SOKOBAN_LEVEL_DOWN BUTTON_F1
103 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2
104 #define SOKOBAN_LEVEL_UP BUTTON_F3
105 #define SOKOBAN_PAUSE BUTTON_PLAY
106 #define BUTTON_SAVE BUTTON_ON
107 #define BUTTON_SAVE_NAME "ON"
109 #elif CONFIG_KEYPAD == ONDIO_PAD
110 #define SOKOBAN_LEFT BUTTON_LEFT
111 #define SOKOBAN_RIGHT BUTTON_RIGHT
112 #define SOKOBAN_UP BUTTON_UP
113 #define SOKOBAN_DOWN BUTTON_DOWN
114 #define SOKOBAN_MENU BUTTON_OFF
115 #define SOKOBAN_UNDO_PRE BUTTON_MENU
116 #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
117 #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
118 #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
119 #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
120 #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
121 #define SOKOBAN_PAUSE BUTTON_MENU
122 #define BUTTON_SAVE BUTTON_MENU
123 #define BUTTON_SAVE_NAME "MENU"
125 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
126 (CONFIG_KEYPAD == IRIVER_H300_PAD)
127 #define SOKOBAN_LEFT BUTTON_LEFT
128 #define SOKOBAN_RIGHT BUTTON_RIGHT
129 #define SOKOBAN_UP BUTTON_UP
130 #define SOKOBAN_DOWN BUTTON_DOWN
131 #define SOKOBAN_MENU BUTTON_OFF
132 #define SOKOBAN_UNDO BUTTON_REC
133 #define SOKOBAN_REDO BUTTON_MODE
134 #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
135 #define SOKOBAN_LEVEL_REPEAT BUTTON_ON
136 #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
137 #define SOKOBAN_PAUSE BUTTON_ON
138 #define BUTTON_SAVE BUTTON_MODE
139 #define BUTTON_SAVE_NAME "MODE"
141 #define SOKOBAN_RC_MENU BUTTON_RC_STOP
143 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
144 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
145 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
146 #define SOKOBAN_LEFT BUTTON_LEFT
147 #define SOKOBAN_RIGHT BUTTON_RIGHT
148 #define SOKOBAN_UP BUTTON_MENU
149 #define SOKOBAN_DOWN BUTTON_PLAY
150 #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
151 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
152 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
153 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
154 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
155 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
156 #define SOKOBAN_PAUSE BUTTON_SELECT
157 #define BUTTON_SAVE BUTTON_SELECT
158 #define BUTTON_SAVE_NAME "SELECT"
160 /* FIXME: if/when simultaneous button presses work for X5/M5,
161 * add level up/down */
162 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
163 #define SOKOBAN_LEFT BUTTON_LEFT
164 #define SOKOBAN_RIGHT BUTTON_RIGHT
165 #define SOKOBAN_UP BUTTON_UP
166 #define SOKOBAN_DOWN BUTTON_DOWN
167 #define SOKOBAN_MENU BUTTON_POWER
168 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
169 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
170 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
171 #define SOKOBAN_REDO BUTTON_PLAY
172 #define SOKOBAN_PAUSE BUTTON_PLAY
173 #define BUTTON_SAVE BUTTON_SELECT
174 #define BUTTON_SAVE_NAME "SELECT"
176 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
177 #define SOKOBAN_LEFT BUTTON_LEFT
178 #define SOKOBAN_RIGHT BUTTON_RIGHT
179 #define SOKOBAN_UP BUTTON_SCROLL_UP
180 #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
181 #define SOKOBAN_MENU BUTTON_POWER
182 #define SOKOBAN_UNDO_PRE BUTTON_REW
183 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
184 #define SOKOBAN_REDO BUTTON_FF
185 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
186 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
187 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
188 #define SOKOBAN_PAUSE BUTTON_PLAY
189 #define BUTTON_SAVE BUTTON_PLAY
190 #define BUTTON_SAVE_NAME "PLAY"
192 #elif CONFIG_KEYPAD == GIGABEAT_PAD
193 #define SOKOBAN_LEFT BUTTON_LEFT
194 #define SOKOBAN_RIGHT BUTTON_RIGHT
195 #define SOKOBAN_UP BUTTON_UP
196 #define SOKOBAN_DOWN BUTTON_DOWN
197 #define SOKOBAN_MENU BUTTON_POWER
198 #define SOKOBAN_UNDO BUTTON_SELECT
199 #define SOKOBAN_REDO BUTTON_A
200 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
201 #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
202 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
203 #define SOKOBAN_PAUSE BUTTON_SELECT
204 #define BUTTON_SAVE BUTTON_SELECT
205 #define BUTTON_SAVE_NAME "SELECT"
207 #elif CONFIG_KEYPAD == SANSA_E200_PAD
208 #define SOKOBAN_LEFT BUTTON_LEFT
209 #define SOKOBAN_RIGHT BUTTON_RIGHT
210 #define SOKOBAN_UP BUTTON_UP
211 #define SOKOBAN_DOWN BUTTON_DOWN
212 #define SOKOBAN_MENU BUTTON_POWER
213 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
214 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
215 #define SOKOBAN_REDO BUTTON_REC
216 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
217 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
218 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
219 #define SOKOBAN_PAUSE BUTTON_SELECT
220 #define BUTTON_SAVE BUTTON_SELECT
221 #define BUTTON_SAVE_NAME "SELECT"
223 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
224 #define SOKOBAN_LEFT BUTTON_LEFT
225 #define SOKOBAN_RIGHT BUTTON_RIGHT
226 #define SOKOBAN_UP BUTTON_UP
227 #define SOKOBAN_DOWN BUTTON_DOWN
228 #define SOKOBAN_MENU (BUTTON_HOME|BUTTON_REPEAT)
229 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
230 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
231 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_LEFT)
232 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
233 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
234 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
235 #define SOKOBAN_PAUSE BUTTON_SELECT
236 #define BUTTON_SAVE BUTTON_SELECT
237 #define BUTTON_SAVE_NAME "SELECT"
239 #elif CONFIG_KEYPAD == SANSA_C200_PAD
240 #define SOKOBAN_LEFT BUTTON_LEFT
241 #define SOKOBAN_RIGHT BUTTON_RIGHT
242 #define SOKOBAN_UP BUTTON_UP
243 #define SOKOBAN_DOWN BUTTON_DOWN
244 #define SOKOBAN_MENU BUTTON_POWER
245 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
246 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
247 #define SOKOBAN_REDO BUTTON_REC
248 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
249 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
250 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
251 #define SOKOBAN_PAUSE BUTTON_SELECT
252 #define BUTTON_SAVE BUTTON_SELECT
253 #define BUTTON_SAVE_NAME "SELECT"
255 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
256 #define SOKOBAN_LEFT BUTTON_LEFT
257 #define SOKOBAN_RIGHT BUTTON_RIGHT
258 #define SOKOBAN_UP BUTTON_UP
259 #define SOKOBAN_DOWN BUTTON_DOWN
260 #define SOKOBAN_MENU BUTTON_POWER
261 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
262 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
263 #define SOKOBAN_REDO BUTTON_HOME
264 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
265 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
266 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
267 #define SOKOBAN_PAUSE BUTTON_SELECT
268 #define BUTTON_SAVE BUTTON_SELECT
269 #define BUTTON_SAVE_NAME "SELECT"
271 #elif CONFIG_KEYPAD == SANSA_M200_PAD
272 #define SOKOBAN_LEFT BUTTON_LEFT
273 #define SOKOBAN_RIGHT BUTTON_RIGHT
274 #define SOKOBAN_UP BUTTON_UP
275 #define SOKOBAN_DOWN BUTTON_DOWN
276 #define SOKOBAN_MENU BUTTON_POWER
277 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
278 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
279 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_UP)
280 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
281 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
282 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
283 #define SOKOBAN_PAUSE BUTTON_SELECT
284 #define BUTTON_SAVE BUTTON_SELECT
285 #define BUTTON_SAVE_NAME "SELECT"
287 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
288 #define SOKOBAN_LEFT BUTTON_LEFT
289 #define SOKOBAN_RIGHT BUTTON_RIGHT
290 #define SOKOBAN_UP BUTTON_UP
291 #define SOKOBAN_DOWN BUTTON_DOWN
292 #define SOKOBAN_MENU BUTTON_MENU
293 #define SOKOBAN_UNDO BUTTON_VOL_UP
294 #define SOKOBAN_REDO BUTTON_VOL_DOWN
295 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
296 #define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
297 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
298 #define SOKOBAN_PAUSE BUTTON_SELECT
299 #define BUTTON_SAVE BUTTON_SELECT
300 #define BUTTON_SAVE_NAME "SELECT"
302 #elif CONFIG_KEYPAD == MROBE100_PAD
303 #define SOKOBAN_LEFT BUTTON_LEFT
304 #define SOKOBAN_RIGHT BUTTON_RIGHT
305 #define SOKOBAN_UP BUTTON_UP
306 #define SOKOBAN_DOWN BUTTON_DOWN
307 #define SOKOBAN_MENU BUTTON_POWER
308 #define SOKOBAN_UNDO BUTTON_SELECT
309 #define SOKOBAN_REDO BUTTON_MENU
310 #define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
311 #define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
312 #define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
313 #define SOKOBAN_PAUSE BUTTON_SELECT
314 #define BUTTON_SAVE BUTTON_SELECT
315 #define BUTTON_SAVE_NAME "SELECT"
317 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
318 #define SOKOBAN_LEFT BUTTON_RC_REW
319 #define SOKOBAN_RIGHT BUTTON_RC_FF
320 #define SOKOBAN_UP BUTTON_RC_VOL_UP
321 #define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
322 #define SOKOBAN_MENU BUTTON_RC_REC
323 #define SOKOBAN_UNDO BUTTON_RC_MODE
324 #define SOKOBAN_REDO BUTTON_RC_MENU
325 #define SOKOBAN_PAUSE BUTTON_RC_PLAY
326 #define BUTTON_SAVE BUTTON_RC_PLAY
327 #define BUTTON_SAVE_NAME "PLAY"
329 #define SOKOBAN_RC_MENU BUTTON_REC
331 #elif CONFIG_KEYPAD == COWON_D2_PAD
332 #define SOKOBAN_MENU BUTTON_MENU
333 #define SOKOBAN_LEVEL_DOWN BUTTON_MINUS
334 #define SOKOBAN_LEVEL_UP BUTTON_PLUS
335 #define SOKOBAN_MENU_NAME "[MENU]"
337 #elif CONFIG_KEYPAD == IAUDIO67_PAD
338 #define SOKOBAN_LEFT BUTTON_LEFT
339 #define SOKOBAN_RIGHT BUTTON_RIGHT
340 #define SOKOBAN_UP BUTTON_STOP
341 #define SOKOBAN_DOWN BUTTON_PLAY
342 #define SOKOBAN_MENU BUTTON_MENU
343 #define SOKOBAN_UNDO BUTTON_VOLDOWN
344 #define SOKOBAN_REDO BUTTON_VOLUP
345 #define SOKOBAN_PAUSE (BUTTON_MENU|BUTTON_LEFT)
346 #define BUTTON_SAVE (BUTTON_MENU|BUTTON_PLAY)
347 #define BUTTON_SAVE_NAME "MENU+PLAY"
349 #define SOKOBAN_RC_MENU (BUTTON_MENU|BUTTON_STOP)
351 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
352 #define SOKOBAN_LEFT BUTTON_LEFT
353 #define SOKOBAN_RIGHT BUTTON_RIGHT
354 #define SOKOBAN_UP BUTTON_UP
355 #define SOKOBAN_DOWN BUTTON_DOWN
356 #define SOKOBAN_MENU BUTTON_MENU
357 #define SOKOBAN_UNDO BUTTON_BACK
358 #define SOKOBAN_REDO (BUTTON_BACK | BUTTON_PLAY)
359 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
360 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
361 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
362 #define SOKOBAN_PAUSE BUTTON_PLAY
363 #define BUTTON_SAVE BUTTON_CUSTOM
364 #define BUTTON_SAVE_NAME "CUSTOM"
366 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
367 #define SOKOBAN_LEFT BUTTON_LEFT
368 #define SOKOBAN_RIGHT BUTTON_RIGHT
369 #define SOKOBAN_UP BUTTON_UP
370 #define SOKOBAN_DOWN BUTTON_DOWN
371 #define SOKOBAN_MENU BUTTON_MENU
372 #define SOKOBAN_UNDO BUTTON_VIEW
373 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_VIEW)
374 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
375 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
376 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
377 #define SOKOBAN_PAUSE BUTTON_SELECT
378 #define BUTTON_SAVE BUTTON_PLAYLIST
379 #define BUTTON_SAVE_NAME "PLAYLIST"
381 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
382 #define SOKOBAN_LEFT BUTTON_PREV
383 #define SOKOBAN_RIGHT BUTTON_NEXT
384 #define SOKOBAN_UP BUTTON_UP
385 #define SOKOBAN_DOWN BUTTON_DOWN
386 #define SOKOBAN_MENU BUTTON_MENU
387 #define SOKOBAN_UNDO BUTTON_LEFT
388 #define SOKOBAN_REDO (BUTTON_LEFT | BUTTON_PLAY)
389 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
390 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
391 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
392 #define SOKOBAN_PAUSE BUTTON_PLAY
393 #define BUTTON_SAVE BUTTON_RIGHT
394 #define BUTTON_SAVE_NAME "RIGHT"
396 #elif CONFIG_KEYPAD == ONDAVX747_PAD
397 #define SOKOBAN_MENU BUTTON_MENU
398 #define SOKOBAN_MENU_NAME "[MENU]"
400 #elif CONFIG_KEYPAD == ONDAVX777_PAD
401 #define SOKOBAN_MENU BUTTON_POWER
402 #define SOKOBAN_MENU_NAME "[POWER]"
404 #elif CONFIG_KEYPAD == MROBE500_PAD
406 #define SOKOBAN_MENU BUTTON_POWER
407 #define SOKOBAN_MENU_NAME "[POWER]"
409 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
410 #define SOKOBAN_LEFT BUTTON_LEFT
411 #define SOKOBAN_RIGHT BUTTON_RIGHT
412 #define SOKOBAN_UP BUTTON_UP
413 #define SOKOBAN_DOWN BUTTON_DOWN
414 #define SOKOBAN_MENU BUTTON_REC
415 #define SOKOBAN_UNDO_PRE BUTTON_REW
416 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_LEFT)
417 #define SOKOBAN_REDO BUTTON_FFWD
418 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
419 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
420 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
421 #define SOKOBAN_PAUSE BUTTON_PLAY
422 #define BUTTON_SAVE BUTTON_PLAY
423 #define BUTTON_SAVE_NAME "PLAY"
425 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
426 #define SOKOBAN_LEFT BUTTON_PREV
427 #define SOKOBAN_RIGHT BUTTON_NEXT
428 #define SOKOBAN_UP BUTTON_UP
429 #define SOKOBAN_DOWN BUTTON_DOWN
430 #define SOKOBAN_MENU BUTTON_REC
431 #define SOKOBAN_UNDO BUTTON_CANCEL
432 #define SOKOBAN_REDO BUTTON_OK
433 #define SOKOBAN_LEVEL_DOWN (BUTTON_OK | BUTTON_PREV)
434 #define SOKOBAN_LEVEL_REPEAT (BUTTON_OK | BUTTON_CANCEL)
435 #define SOKOBAN_LEVEL_UP (BUTTON_OK | BUTTON_NEXT)
436 #define SOKOBAN_PAUSE BUTTON_PLAY
437 #define BUTTON_SAVE BUTTON_MENU
438 #define BUTTON_SAVE_NAME "MENU"
440 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
441 #define SOKOBAN_LEFT BUTTON_VOL_DOWN
442 #define SOKOBAN_RIGHT BUTTON_VOL_UP
443 #define SOKOBAN_UP BUTTON_PREV
444 #define SOKOBAN_DOWN BUTTON_NEXT
445 #define SOKOBAN_MENU BUTTON_SELECT
446 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_PREV)
447 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_NEXT)
448 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_VOL_DOWN)
449 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
450 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_VOL_UP)
451 #define SOKOBAN_PAUSE BUTTON_PLAY
452 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_SELECT)
453 #define BUTTON_SAVE_NAME "PLAY+SELECT"
455 #else
456 #error No keymap defined!
457 #endif
459 #ifdef HAVE_TOUCHSCREEN
460 #ifndef SOKOBAN_LEFT
461 #define SOKOBAN_LEFT BUTTON_MIDLEFT
462 #endif
463 #ifndef SOKOBAN_RIGHT
464 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
465 #endif
466 #ifndef SOKOBAN_UP
467 #define SOKOBAN_UP BUTTON_TOPMIDDLE
468 #endif
469 #ifndef SOKOBAN_DOWN
470 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
471 #endif
472 #ifndef SOKOBAN_MENU
473 #define SOKOBAN_MENU BUTTON_TOPLEFT
474 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
475 #endif
476 #ifndef SOKOBAN_UNDO
477 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
478 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
479 #endif
480 #ifndef SOKOBAN_REDO
481 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
482 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
483 #endif
484 #ifndef SOKOBAN_PAUSE
485 #define SOKOBAN_PAUSE BUTTON_CENTER
486 #define SOKOBAN_PAUSE_NAME "[CENTER]"
487 #endif
488 #ifndef SOKOBAN_LEVEL_REPEAT
489 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
490 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
491 #endif
492 #ifndef BUTTON_SAVE
493 #define BUTTON_SAVE BUTTON_CENTER
494 #define BUTTON_SAVE_NAME "CENTER"
495 #endif
496 #endif
498 #define SOKOBAN_FONT FONT_SYSFIXED
501 /* The Location, Undo and LevelInfo structs are OO-flavored.
502 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
503 * but the overall data layout becomes more manageable. */
505 /* Level data & stats */
506 struct LevelInfo {
507 int index; /* Level index (level number - 1) */
508 int moves; /* Moves & pushes for the stats */
509 int pushes;
510 short boxes_to_go; /* Number of unplaced boxes remaining in level */
511 short height; /* Height & width for centering level display */
512 short width;
515 struct Location {
516 short row;
517 short col;
520 /* Our full undo history */
521 static struct UndoInfo {
522 int count; /* How many undos have been done */
523 int current; /* Which history is the current undo */
524 int max; /* Which history is the max redoable */
525 char history[MAX_UNDOS];
526 } undo_info;
528 /* Our playing board */
529 static struct BoardInfo {
530 char board[ROWS][COLS]; /* The current board data */
531 struct LevelInfo level; /* Level data & stats */
532 struct Location player; /* Where the player is */
533 int max_level; /* The number of levels we have */
534 } current_info;
536 static struct BufferedBoards {
537 char filename[MAX_PATH]; /* Filename of the levelset we're using */
538 char data[MAX_LEVEL_DATA]; /* Buffered level data */
539 int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
540 int start; /* Index of first buffered board */
541 int end; /* Index of last buffered board */
542 short prebuffered_boards; /* Number of boards before current to store */
543 } buffered_boards;
546 static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
549 static void init_undo(void)
551 undo_info.count = 0;
552 undo_info.current = 0;
553 undo_info.max = 0;
556 static void get_delta(char direction, short *d_r, short *d_c)
558 switch (direction) {
559 case SOKOBAN_PUSH_LEFT:
560 case SOKOBAN_MOVE_LEFT:
561 *d_r = 0;
562 *d_c = -1;
563 break;
564 case SOKOBAN_PUSH_RIGHT:
565 case SOKOBAN_MOVE_RIGHT:
566 *d_r = 0;
567 *d_c = 1;
568 break;
569 case SOKOBAN_PUSH_UP:
570 case SOKOBAN_MOVE_UP:
571 *d_r = -1;
572 *d_c = 0;
573 break;
574 case SOKOBAN_PUSH_DOWN:
575 case SOKOBAN_MOVE_DOWN:
576 *d_r = 1;
577 *d_c = 0;
581 static bool undo(void)
583 char undo;
584 short r, c;
585 short d_r = 0, d_c = 0; /* delta row & delta col */
586 char *space_cur, *space_next, *space_prev;
587 bool undo_push = false;
589 /* If no more undos or we've wrapped all the way around, quit */
590 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
591 return false;
593 /* Move to previous undo in the list */
594 if (undo_info.current == 0 && undo_info.count > 1)
595 undo_info.current = MAX_UNDOS - 1;
596 else
597 undo_info.current--;
599 undo_info.count--;
601 undo = undo_info.history[undo_info.current];
603 if (undo < SOKOBAN_MOVE_MIN)
604 undo_push = true;
606 get_delta(undo, &d_r, &d_c);
608 r = current_info.player.row;
609 c = current_info.player.col;
611 /* Give the 3 spaces we're going to use better names */
612 space_cur = &current_info.board[r][c];
613 space_next = &current_info.board[r + d_r][c + d_c];
614 space_prev = &current_info.board[r - d_r][c - d_c];
616 /* Update board info */
617 if (undo_push) {
618 /* Moving box from goal to floor */
619 if (*space_next == '*' && *space_cur == '@')
620 current_info.level.boxes_to_go++;
621 /* Moving box from floor to goal */
622 else if (*space_next == '$' && *space_cur == '+')
623 current_info.level.boxes_to_go--;
625 /* Move box off of next space... */
626 *space_next = (*space_next == '*' ? '.' : ' ');
627 /* ...and on to current space */
628 *space_cur = (*space_cur == '+' ? '*' : '$');
630 current_info.level.pushes--;
631 } else
632 /* Just move player off of current space */
633 *space_cur = (*space_cur == '+' ? '.' : ' ');
634 /* Move player back to previous space */
635 *space_prev = (*space_prev == '.' ? '+' : '@');
637 /* Update position */
638 current_info.player.row -= d_r;
639 current_info.player.col -= d_c;
641 current_info.level.moves--;
643 return true;
646 static void add_undo(char undo)
648 undo_info.history[undo_info.current] = undo;
650 /* Wrap around if MAX_UNDOS exceeded */
651 if (undo_info.current < (MAX_UNDOS - 1))
652 undo_info.current++;
653 else
654 undo_info.current = 0;
656 if (undo_info.count < MAX_UNDOS)
657 undo_info.count++;
660 static bool move(char direction, bool redo)
662 short r, c;
663 short d_r = 0, d_c = 0; /* delta row & delta col */
664 char *space_cur, *space_next, *space_beyond;
665 bool push = false;
667 get_delta(direction, &d_r, &d_c);
669 r = current_info.player.row;
670 c = current_info.player.col;
672 /* Check for out-of-bounds */
673 if (r + 2*d_r < 0 || r + 2*d_r >= ROWS ||
674 c + 2*d_c < 0 || c + 2*d_c >= COLS)
675 return false;
677 /* Give the 3 spaces we're going to use better names */
678 space_cur = &current_info.board[r][c];
679 space_next = &current_info.board[r + d_r][c + d_c];
680 space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
682 if (*space_next == '$' || *space_next == '*') {
683 /* Change direction from move to push for undo */
684 if (direction >= SOKOBAN_MOVE_MIN)
685 direction -= SOKOBAN_MOVE_DIFF;
686 push = true;
688 else if (direction < SOKOBAN_MOVE_MIN)
689 /* Change back to move if redo/solution playback push is invalid */
690 direction += SOKOBAN_MOVE_DIFF;
692 /* Update board info */
693 if (push) {
694 /* Moving box from goal to floor */
695 if (*space_next == '*' && *space_beyond == ' ')
696 current_info.level.boxes_to_go++;
697 /* Moving box from floor to goal */
698 else if (*space_next == '$' && *space_beyond == '.')
699 current_info.level.boxes_to_go--;
700 /* Check for invalid move */
701 else if (*space_beyond != '.' && *space_beyond != ' ')
702 return false;
704 /* Move player onto next space */
705 *space_next = (*space_next == '*' ? '+' : '@');
706 /* Move box onto space beyond next */
707 *space_beyond = (*space_beyond == '.' ? '*' : '$');
709 current_info.level.pushes++;
710 } else {
711 /* Check for invalid move */
712 if (*space_next != '.' && *space_next != ' ')
713 return false;
715 /* Move player onto next space */
716 *space_next = (*space_next == '.' ? '+' : '@');
718 /* Move player off of current space */
719 *space_cur = (*space_cur == '+' ? '.' : ' ');
721 /* Update position */
722 current_info.player.row += d_r;
723 current_info.player.col += d_c;
725 current_info.level.moves++;
727 /* Update undo_info.max to current on every normal move,
728 * except if it's the same as a redo. */
729 /* normal move and either */
730 if (!redo &&
731 /* moves have been undone... */
732 ((undo_info.max != undo_info.current &&
733 /* ...and the current move is NOT the same as the one in history */
734 undo_info.history[undo_info.current] != direction) ||
735 /* or moves have not been undone */
736 undo_info.max == undo_info.current)) {
737 add_undo(direction);
738 undo_info.max = undo_info.current;
739 } else /* redo move or move was same as redo */
740 add_undo(direction); /* add_undo to update current */
742 return true;
745 #ifdef SOKOBAN_REDO
746 static bool redo(void)
748 /* If no moves have been undone, quit */
749 if (undo_info.current == undo_info.max)
750 return false;
752 return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
753 undo_info.current : 0)], true);
755 #endif
757 static void init_boards(void)
759 rb->strlcpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE,
760 sizeof(buffered_boards.filename));
762 current_info.level.index = 0;
763 current_info.player.row = 0;
764 current_info.player.col = 0;
765 current_info.max_level = 0;
767 buffered_boards.start = 0;
768 buffered_boards.end = 0;
769 buffered_boards.prebuffered_boards = 0;
771 init_undo();
774 static bool read_levels(bool initialize)
776 int fd = 0;
777 short len;
778 short lastlen = 0;
779 short row = 0;
780 int level_count = 0;
782 int i = 0;
783 int level_len = 0;
784 bool index_set = false;
786 /* Get the index of the first level to buffer */
787 if (current_info.level.index > buffered_boards.prebuffered_boards &&
788 !initialize)
789 buffered_boards.start = current_info.level.index -
790 buffered_boards.prebuffered_boards;
791 else
792 buffered_boards.start = 0;
794 if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
795 rb->splashf(HZ*2, "Unable to open %s", buffered_boards.filename);
796 return false;
799 do {
800 len = rb->read_line(fd, buf, sizeof(buf));
802 /* Correct len when trailing \r's or \n's are counted */
803 if (len > 2 && buf[len - 2] == '\0')
804 len -= 2;
805 else if (len > 1 && buf[len - 1] == '\0')
806 len--;
808 /* Skip short lines & lines with non-level data */
809 if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
810 buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
811 if (level_count >= buffered_boards.start) {
812 /* Set the index of this level */
813 if (!index_set &&
814 level_count - buffered_boards.start < MAX_LEVELS) {
815 buffered_boards.index[level_count - buffered_boards.start]
816 = i;
817 index_set = true;
819 /* Copy buffer to board data */
820 if (i + level_len + len < MAX_LEVEL_DATA) {
821 rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
822 buffered_boards.data[i + level_len + len] = '\n';
825 level_len += len + 1;
826 row++;
828 /* If newline & level is tall enough or is RLE */
829 } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
830 level_count++;
831 if (level_count >= buffered_boards.start) {
832 i += level_len;
833 if (i < MAX_LEVEL_DATA)
834 buffered_boards.end = level_count;
835 else if (!initialize)
836 break;
838 row = 0;
839 level_len = 0;
840 index_set = false;
842 } else if (len > 22)
843 len = 1;
845 } while ((lastlen = len));
847 /* Set the index of the end of the last level */
848 if (level_count - buffered_boards.start < MAX_LEVELS)
849 buffered_boards.index[level_count - buffered_boards.start] = i;
851 if (initialize) {
852 current_info.max_level = level_count;
853 buffered_boards.prebuffered_boards = buffered_boards.end/2;
856 rb->close(fd);
858 return true;
861 static void load_level(void)
863 int c, r;
864 int i, n;
865 int level_size;
866 int index = current_info.level.index - buffered_boards.start;
867 char *level;
869 /* Get the buffered board index of the current level */
870 if (current_info.level.index < buffered_boards.start ||
871 current_info.level.index >= buffered_boards.end) {
872 read_levels(false);
873 if (current_info.level.index > buffered_boards.prebuffered_boards)
874 index = buffered_boards.prebuffered_boards;
875 else
876 index = current_info.level.index;
878 level = &buffered_boards.data[buffered_boards.index[index]];
880 /* Reset level info */
881 current_info.level.moves = 0;
882 current_info.level.pushes = 0;
883 current_info.level.boxes_to_go = 0;
884 current_info.level.width = 0;
886 /* Clear board */
887 for (r = 0; r < ROWS; r++)
888 for (c = 0; c < COLS; c++)
889 current_info.board[r][c] = 'X';
891 level_size = buffered_boards.index[index + 1] -
892 buffered_boards.index[index];
894 for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
895 if (level[i] == '\n' || level[i] == '|') {
896 if (c > 3) {
897 /* Update max width of level & go to next row */
898 if (c > current_info.level.width)
899 current_info.level.width = c;
900 c = 0;
901 r++;
902 if (r >= ROWS)
903 break;
905 } else if (c < COLS) {
906 /* Read RLE character's length into n */
907 if (level[i] >= '0' && level[i] <= '9') {
908 n = level[i++] - '0';
909 if (level[i] >= '0' && level[i] <= '9')
910 n = n*10 + level[i++] - '0';
913 /* Cleanup & replace */
914 if (level[i] == '%')
915 level[i] = '*';
916 else if (level[i] == '-' || level[i] == '_')
917 level[i] = ' ';
919 if (n > 1) {
920 if (c + n >= COLS)
921 n = COLS - c;
923 if (level[i] == '.')
924 current_info.level.boxes_to_go += n;
926 /* Put RLE character n times */
927 while (n--)
928 current_info.board[r][c++] = level[i];
929 n = 1;
931 } else {
932 if (level[i] == '.' || level[i] == '+')
933 current_info.level.boxes_to_go++;
935 if (level[i] == '@' ||level[i] == '+') {
936 current_info.player.row = r;
937 current_info.player.col = c;
940 current_info.board[r][c++] = level[i];
945 current_info.level.height = r;
947 #if LCD_DEPTH > 2
948 /* Fill in blank space outside level on color targets */
949 for (r = 0; r < ROWS; r++)
950 for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
951 current_info.board[r][c] = 'X';
953 for (c = 0; c < COLS; c++) {
954 for (r = 0; (current_info.board[r][c] == ' ' ||
955 current_info.board[r][c] == 'X') && r < ROWS; r++)
956 current_info.board[r][c] = 'X';
957 for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
958 current_info.board[r][c] == 'X') && r >= 0; r--)
959 current_info.board[r][c] = 'X';
961 #endif
964 static void update_screen(void)
966 int c, r;
967 int rows, cols;
969 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
970 #define STAT_HEIGHT 25
971 #define STAT_X (LCD_WIDTH - 120)/2
972 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
973 #define BOARD_WIDTH LCD_WIDTH
974 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
975 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
976 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
977 rb->lcd_putsxy(STAT_X + 7, STAT_Y + 14, buf);
978 rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
979 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
980 rb->lcd_putsxy(STAT_X + 44, STAT_Y + 14, buf);
981 rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
982 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
983 rb->lcd_putsxy(STAT_X + 82, STAT_Y + 14, buf);
985 rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
986 rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
987 rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
988 #else
989 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
990 #define STAT_X (LCD_WIDTH - 40)
991 #else
992 #define STAT_X COLS*SOKOBAN_TILESIZE
993 #endif
994 #define STAT_Y (LCD_HEIGHT - 64)/2
995 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
996 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
997 #define BOARD_HEIGHT LCD_HEIGHT
998 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 2, "Level");
999 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
1000 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 12, buf);
1001 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 23, "Moves");
1002 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
1003 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 33, buf);
1004 #if STAT_WIDTH < 38
1005 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Push");
1006 #else
1007 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Pushes");
1008 #endif
1009 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
1010 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 54, buf);
1012 rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 64);
1013 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 21);
1014 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 42);
1016 #endif
1018 /* load the board to the screen */
1019 for (rows = 0; rows < ROWS; rows++) {
1020 for (cols = 0; cols < COLS; cols++) {
1021 c = cols*SOKOBAN_TILESIZE +
1022 (BOARD_WIDTH - current_info.level.width*SOKOBAN_TILESIZE)/2;
1023 r = rows*SOKOBAN_TILESIZE +
1024 (BOARD_HEIGHT - current_info.level.height*SOKOBAN_TILESIZE)/2;
1026 switch(current_info.board[rows][cols]) {
1027 case 'X': /* blank space outside of level */
1028 break;
1030 case ' ': /* floor */
1031 rb->lcd_bitmap_part(sokoban_tiles, 0, 0*SOKOBAN_TILESIZE,
1032 STRIDE( SCREEN_MAIN,
1033 BMPWIDTH_sokoban_tiles,
1034 BMPHEIGHT_sokoban_tiles),
1035 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1036 break;
1038 case '#': /* wall */
1039 rb->lcd_bitmap_part(sokoban_tiles, 0, 1*SOKOBAN_TILESIZE,
1040 STRIDE( SCREEN_MAIN,
1041 BMPWIDTH_sokoban_tiles,
1042 BMPHEIGHT_sokoban_tiles),
1043 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1044 break;
1046 case '$': /* box */
1047 rb->lcd_bitmap_part(sokoban_tiles, 0, 2*SOKOBAN_TILESIZE,
1048 STRIDE( SCREEN_MAIN,
1049 BMPWIDTH_sokoban_tiles,
1050 BMPHEIGHT_sokoban_tiles),
1051 c, r, SOKOBAN_TILESIZE,SOKOBAN_TILESIZE);
1052 break;
1054 case '*': /* box on goal */
1055 rb->lcd_bitmap_part(sokoban_tiles, 0, 3*SOKOBAN_TILESIZE,
1056 STRIDE( SCREEN_MAIN,
1057 BMPWIDTH_sokoban_tiles,
1058 BMPHEIGHT_sokoban_tiles),
1059 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1060 break;
1062 case '.': /* goal */
1063 rb->lcd_bitmap_part(sokoban_tiles, 0, 4*SOKOBAN_TILESIZE,
1064 STRIDE( SCREEN_MAIN,
1065 BMPWIDTH_sokoban_tiles,
1066 BMPHEIGHT_sokoban_tiles),
1067 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1068 break;
1070 case '@': /* player */
1071 rb->lcd_bitmap_part(sokoban_tiles, 0, 5*SOKOBAN_TILESIZE,
1072 STRIDE( SCREEN_MAIN,
1073 BMPWIDTH_sokoban_tiles,
1074 BMPHEIGHT_sokoban_tiles),
1075 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1076 break;
1078 case '+': /* player on goal */
1079 rb->lcd_bitmap_part(sokoban_tiles, 0, 6*SOKOBAN_TILESIZE,
1080 STRIDE( SCREEN_MAIN,
1081 BMPWIDTH_sokoban_tiles,
1082 BMPHEIGHT_sokoban_tiles),
1083 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1084 break;
1089 /* print out the screen */
1090 rb->lcd_update();
1093 static void draw_level(void)
1095 load_level();
1096 rb->lcd_clear_display();
1097 update_screen();
1100 static bool save(char *filename, bool solution)
1102 int fd;
1103 char *loc;
1104 DIR *dir;
1105 char dirname[MAX_PATH];
1107 rb->splash(0, "Saving...");
1109 /* Create dir if it doesn't exist */
1110 if ((loc = rb->strrchr(filename, '/')) != NULL) {
1111 rb->strlcpy(dirname, filename, loc - filename + 1);
1113 if(!(dir = rb->opendir(dirname)))
1114 rb->mkdir(dirname);
1115 else
1116 rb->closedir(dir);
1119 if (filename[0] == '\0' ||
1120 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
1121 rb->splashf(HZ*2, "Unable to open %s", filename);
1122 return false;
1125 /* Sokoban: S/P for solution/progress : level number : current undo */
1126 rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
1127 current_info.level.index + 1, undo_info.current);
1128 rb->write(fd, buf, rb->strlen(buf));
1130 /* Filename of levelset */
1131 rb->write(fd, buffered_boards.filename,
1132 rb->strlen(buffered_boards.filename));
1133 rb->write(fd, "\n", 1);
1135 /* Full undo history */
1136 rb->write(fd, undo_info.history, undo_info.max);
1138 rb->close(fd);
1140 return true;
1143 static bool load(char *filename, bool silent)
1145 int fd;
1146 int i, n;
1147 int len;
1148 int button;
1149 bool play_solution;
1150 bool paused = false;
1151 unsigned short speed = 2;
1152 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
1154 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
1155 if (!silent)
1156 rb->splashf(HZ*2, "Unable to open %s", filename);
1157 return false;
1160 /* Read header, level number, & current undo */
1161 rb->read_line(fd, buf, sizeof(buf));
1163 /* If we're opening a level file, not a solution/progress file */
1164 if (rb->strncmp(buf, "Sokoban", 7) != 0) {
1165 rb->close(fd);
1167 rb->strlcpy(buffered_boards.filename, filename,
1168 sizeof(buffered_boards.filename));
1170 if (!read_levels(true))
1171 return false;
1173 current_info.level.index = 0;
1174 load_level();
1176 /* If there aren't any boxes to go or the player position wasn't set,
1177 * the file probably wasn't a Sokoban level file */
1178 if (current_info.level.boxes_to_go == 0 ||
1179 current_info.player.row == 0 || current_info.player.col == 0) {
1180 if (!silent)
1181 rb->splash(HZ*2, "File is not a Sokoban level file");
1182 return false;
1185 } else {
1187 /* Read filename of levelset */
1188 rb->read_line(fd, buffered_boards.filename,
1189 sizeof(buffered_boards.filename));
1191 /* Read full undo history */
1192 len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
1194 /* Correct len when trailing \r's or \n's are counted */
1195 if (len > 2 && undo_info.history[len - 2] == '\0')
1196 len -= 2;
1197 else if (len > 1 && undo_info.history[len - 1] == '\0')
1198 len--;
1200 rb->close(fd);
1202 /* Check to see if we're going to play a solution or resume progress */
1203 play_solution = (buf[8] == 'S');
1205 /* Get level number */
1206 for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
1207 n = n*10 + buf[i] - '0';
1208 current_info.level.index = n - 1;
1210 /* Get undo index */
1211 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
1212 n = n*10 + buf[i] - '0';
1213 if (n > len)
1214 n = len;
1215 undo_info.max = len;
1217 if (current_info.level.index < 0) {
1218 if (!silent)
1219 rb->splash(HZ*2, "Error loading level");
1220 return false;
1222 if (!read_levels(true))
1223 return false;
1224 if (current_info.level.index >= current_info.max_level) {
1225 if (!silent)
1226 rb->splash(HZ*2, "Error loading level");
1227 return false;
1230 load_level();
1232 if (play_solution) {
1233 rb->lcd_clear_display();
1234 update_screen();
1235 rb->sleep(2*delay[speed]);
1237 /* Replay solution until menu button is pressed */
1238 i = 0;
1239 while (true) {
1240 if (i < len) {
1241 if (!move(undo_info.history[i], true)) {
1242 n = i;
1243 break;
1245 rb->lcd_clear_display();
1246 update_screen();
1247 i++;
1248 } else
1249 paused = true;
1251 rb->sleep(delay[speed]);
1253 while ((button = rb->button_get(false)) || paused) {
1254 switch (button) {
1255 case SOKOBAN_MENU:
1256 /* Pretend the level is complete so we'll quit */
1257 current_info.level.boxes_to_go = 0;
1258 return true;
1260 case SOKOBAN_PAUSE:
1261 /* Toggle pause state */
1262 paused = !paused;
1263 break;
1265 case SOKOBAN_LEFT:
1266 case SOKOBAN_LEFT | BUTTON_REPEAT:
1267 /* Go back one move */
1268 if (paused) {
1269 if (undo())
1270 i--;
1271 rb->lcd_clear_display();
1272 update_screen();
1274 break;
1276 case SOKOBAN_RIGHT:
1277 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1278 /* Go forward one move */
1279 if (paused) {
1280 if (redo())
1281 i++;
1282 rb->lcd_clear_display();
1283 update_screen();
1285 break;
1287 case SOKOBAN_UP:
1288 case SOKOBAN_UP | BUTTON_REPEAT:
1289 /* Speed up */
1290 if (speed < sizeof(delay)/sizeof(int) - 1)
1291 speed++;
1292 break;
1294 case SOKOBAN_DOWN:
1295 case SOKOBAN_DOWN | BUTTON_REPEAT:
1296 /* Slow down */
1297 if (speed > 0)
1298 speed--;
1301 if (paused)
1302 rb->sleep(HZ/33);
1306 /* If level is complete, wait for keypress before quitting */
1307 if (current_info.level.boxes_to_go == 0)
1308 rb->button_get(true);
1310 } else {
1311 /* Advance to current undo */
1312 for (i = 0; i < n; i++) {
1313 if (!move(undo_info.history[i], true)) {
1314 n = i;
1315 break;
1319 rb->button_clear_queue();
1320 rb->lcd_clear_display();
1323 undo_info.current = n;
1326 return true;
1329 static int sokoban_menu(void)
1331 int button;
1332 int selection = 0;
1333 int i;
1334 bool menu_quit;
1335 int start_selected = 0;
1336 int prev_level = current_info.level.index;
1338 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
1339 "Resume", "Select Level", "Audio Playback", "Keys",
1340 "Load Default Level Set", "Quit Without Saving",
1341 "Save Progress & Quit");
1343 do {
1344 menu_quit = true;
1345 selection = rb->do_menu(&menu, &start_selected, NULL, false);
1347 switch (selection) {
1348 case 0: /* Resume */
1349 break;
1351 case 1: /* Select level */
1352 current_info.level.index++;
1353 rb->set_int("Select Level", "", UNIT_INT,
1354 &current_info.level.index, NULL, 1, 1,
1355 current_info.max_level, NULL);
1356 current_info.level.index--;
1357 if (prev_level != current_info.level.index) {
1358 init_undo();
1359 draw_level();
1360 } else
1361 menu_quit = false;
1362 break;
1364 case 2: /* Audio playback control */
1365 playback_control(NULL);
1366 menu_quit = false;
1367 break;
1369 case 3: /* Keys */
1370 FOR_NB_SCREENS(i)
1371 rb->screens[i]->clear_display();
1372 rb->lcd_setfont(SOKOBAN_FONT);
1374 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1375 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1376 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1377 rb->lcd_putsxy(3, 16, "[ON] Undo");
1378 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1379 rb->lcd_putsxy(3, 36, "[F1] Down a Level");
1380 rb->lcd_putsxy(3, 46, "[F2] Restart Level");
1381 rb->lcd_putsxy(3, 56, "[F3] Up a Level");
1382 #elif CONFIG_KEYPAD == ONDIO_PAD
1383 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1384 rb->lcd_putsxy(3, 16, "[MODE] Undo");
1385 rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1386 rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1387 rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1388 rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1389 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1390 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1391 rb->lcd_putsxy(3, 6, "[STOP] Menu");
1392 rb->lcd_putsxy(3, 16, "[REC] Undo");
1393 rb->lcd_putsxy(3, 26, "[MODE] Redo");
1394 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1395 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1396 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1397 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1398 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1399 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1400 rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1401 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1402 rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1403 rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1404 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1405 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1406 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1407 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1408 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1409 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
1410 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1411 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1412 rb->lcd_putsxy(3, 16, "[REW] Undo");
1413 rb->lcd_putsxy(3, 26, "[FF] Redo");
1414 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1415 rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1416 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1417 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1418 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1419 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1420 rb->lcd_putsxy(3, 26, "[A] Redo");
1421 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1422 rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
1423 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1424 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1425 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1426 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1427 rb->lcd_putsxy(3, 26, "[REC] Redo");
1428 rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1429 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1430 rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1431 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1432 rb->lcd_putsxy(3, 6, "[MENU] Menu");
1433 rb->lcd_putsxy(3, 16, "[VOL+] Undo");
1434 rb->lcd_putsxy(3, 26, "[VOL-] Redo");
1435 rb->lcd_putsxy(3, 36, "[PREV] Previous Level");
1436 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1437 rb->lcd_putsxy(3, 56, "[NEXT] Next Level");
1438 #endif
1440 #ifdef HAVE_TOUCHSCREEN
1441 rb->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME " Menu");
1442 rb->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME " Undo");
1443 rb->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME " Redo");
1444 rb->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME " Pause");
1445 rb->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME " Restart Level");
1446 #endif
1448 FOR_NB_SCREENS(i)
1449 rb->screens[i]->update();
1451 /* Display until keypress */
1452 do {
1453 rb->sleep(HZ/20);
1454 button = rb->button_get(false);
1455 } while (!button || button & BUTTON_REL ||
1456 button & BUTTON_REPEAT);
1458 menu_quit = false;
1459 break;
1461 case 4: /* Load default levelset */
1462 init_boards();
1463 if (!read_levels(true))
1464 return 5; /* Quit */
1465 load_level();
1466 break;
1468 case 5: /* Quit */
1469 break;
1471 case 6: /* Save & quit */
1472 save(SOKOBAN_SAVE_FILE, false);
1473 rb->reload_directory();
1476 } while (!menu_quit);
1478 /* Restore font */
1479 rb->lcd_setfont(SOKOBAN_FONT);
1481 FOR_NB_SCREENS(i) {
1482 rb->screens[i]->clear_display();
1483 rb->screens[i]->update();
1486 return selection;
1489 static bool sokoban_loop(void)
1491 bool moved;
1492 int i = 0, button = 0, lastbutton = 0;
1493 short r = 0, c = 0;
1494 int w, h;
1495 char *loc;
1497 while (true) {
1498 moved = false;
1500 r = current_info.player.row;
1501 c = current_info.player.col;
1503 button = rb->button_get(true);
1505 switch(button)
1507 #ifdef SOKOBAN_RC_MENU
1508 case SOKOBAN_RC_MENU:
1509 #endif
1510 case SOKOBAN_MENU:
1511 switch (sokoban_menu()) {
1512 case 5: /* Quit */
1513 case 6: /* Save & quit */
1514 return PLUGIN_OK;
1516 update_screen();
1517 break;
1519 case SOKOBAN_UNDO:
1520 #ifdef SOKOBAN_UNDO_PRE
1521 if (lastbutton != SOKOBAN_UNDO_PRE)
1522 break;
1523 #else /* repeat can't work here for Ondio, iPod, et al */
1524 case SOKOBAN_UNDO | BUTTON_REPEAT:
1525 #endif
1526 undo();
1527 rb->lcd_clear_display();
1528 update_screen();
1529 break;
1531 #ifdef SOKOBAN_REDO
1532 case SOKOBAN_REDO:
1533 case SOKOBAN_REDO | BUTTON_REPEAT:
1534 moved = redo();
1535 rb->lcd_clear_display();
1536 update_screen();
1537 break;
1538 #endif
1540 #ifdef SOKOBAN_LEVEL_UP
1541 case SOKOBAN_LEVEL_UP:
1542 case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
1543 /* next level */
1544 init_undo();
1545 if (current_info.level.index + 1 < current_info.max_level)
1546 current_info.level.index++;
1548 draw_level();
1549 break;
1550 #endif
1552 #ifdef SOKOBAN_LEVEL_DOWN
1553 case SOKOBAN_LEVEL_DOWN:
1554 case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
1555 /* previous level */
1556 init_undo();
1557 if (current_info.level.index > 0)
1558 current_info.level.index--;
1560 draw_level();
1561 break;
1562 #endif
1564 #ifdef SOKOBAN_LEVEL_REPEAT
1565 case SOKOBAN_LEVEL_REPEAT:
1566 case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT:
1567 /* same level */
1568 init_undo();
1569 draw_level();
1570 break;
1571 #endif
1573 case SOKOBAN_LEFT:
1574 case SOKOBAN_LEFT | BUTTON_REPEAT:
1575 moved = move(SOKOBAN_MOVE_LEFT, false);
1576 break;
1578 case SOKOBAN_RIGHT:
1579 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1580 moved = move(SOKOBAN_MOVE_RIGHT, false);
1581 break;
1583 case SOKOBAN_UP:
1584 case SOKOBAN_UP | BUTTON_REPEAT:
1585 moved = move(SOKOBAN_MOVE_UP, false);
1586 break;
1588 case SOKOBAN_DOWN:
1589 case SOKOBAN_DOWN | BUTTON_REPEAT:
1590 moved = move(SOKOBAN_MOVE_DOWN, false);
1591 break;
1593 default:
1594 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1595 return PLUGIN_USB_CONNECTED;
1596 break;
1599 lastbutton = button;
1601 if (moved) {
1602 rb->lcd_clear_display();
1603 update_screen();
1606 /* We have completed this level */
1607 if (current_info.level.boxes_to_go == 0) {
1609 if (moved) {
1610 rb->lcd_clear_display();
1612 /* Show level complete message & stats */
1613 rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
1614 current_info.level.index + 1);
1615 rb->lcd_getstringsize(buf, &w, &h);
1616 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
1618 rb->snprintf(buf, sizeof(buf), "%4d Moves ",
1619 current_info.level.moves);
1620 rb->lcd_getstringsize(buf, &w, &h);
1621 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
1623 rb->snprintf(buf, sizeof(buf), "%4d Pushes",
1624 current_info.level.pushes);
1625 rb->lcd_getstringsize(buf, &w, &h);
1626 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
1628 if (undo_info.count < MAX_UNDOS) {
1629 rb->snprintf(buf, sizeof(buf), "%s: Save solution",
1630 BUTTON_SAVE_NAME);
1631 rb->lcd_getstringsize(buf, &w, &h);
1632 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
1635 rb->lcd_update();
1636 rb->sleep(HZ/4);
1637 rb->button_clear_queue();
1639 /* Display for 4 seconds or until new keypress */
1640 for (i = 0; i < 80; i++) {
1641 rb->sleep(HZ/20);
1642 button = rb->button_get(false);
1643 if (button && !(button & BUTTON_REL) &&
1644 !(button & BUTTON_REPEAT))
1645 break;
1648 if (button == BUTTON_SAVE) {
1649 if (undo_info.count < MAX_UNDOS) {
1650 /* Set filename to current levelset plus level number
1651 * and .sok extension. Use SAVE_FOLDER if using the
1652 * default levelset, since it's in a hidden folder. */
1653 if (rb->strcmp(buffered_boards.filename,
1654 SOKOBAN_LEVELS_FILE) == 0) {
1655 rb->snprintf(buf, sizeof(buf),
1656 "%s/sokoban.%d.sok",
1657 SOKOBAN_SAVE_FOLDER,
1658 current_info.level.index + 1);
1659 } else {
1660 if ((loc = rb->strrchr(buffered_boards.filename,
1661 '.')) != NULL)
1662 *loc = '\0';
1663 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1664 buffered_boards.filename,
1665 current_info.level.index + 1);
1666 if (loc != NULL)
1667 *loc = '.';
1670 if (!rb->kbd_input(buf, MAX_PATH))
1671 save(buf, true);
1672 } else
1673 rb->splash(HZ*2, "Solution too long to save");
1675 rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
1679 FOR_NB_SCREENS(i) {
1680 rb->screens[i]->clear_display();
1681 rb->screens[i]->update();
1684 current_info.level.index++;
1686 /* clear undo stats */
1687 init_undo();
1689 if (current_info.level.index >= current_info.max_level) {
1690 /* Show levelset complete message */
1691 rb->snprintf(buf, sizeof(buf), "You WIN!!");
1692 rb->lcd_getstringsize(buf, &w, &h);
1693 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
1695 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1696 /* Display for 4 seconds or until keypress */
1697 for (i = 0; i < 80; i++) {
1698 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1699 rb->lcd_update();
1700 rb->sleep(HZ/10);
1702 button = rb->button_get(false);
1703 if (button && !(button & BUTTON_REL))
1704 break;
1706 rb->lcd_set_drawmode(DRMODE_SOLID);
1708 /* Reset to first level & show quit menu */
1709 current_info.level.index = 0;
1711 switch (sokoban_menu()) {
1712 case 5: /* Quit */
1713 case 6: /* Save & quit */
1714 return PLUGIN_OK;
1718 load_level();
1719 update_screen();
1722 } /* end while */
1724 return PLUGIN_OK;
1728 enum plugin_status plugin_start(const void* parameter)
1730 int w, h;
1732 (void)(parameter);
1734 rb->lcd_setfont(SOKOBAN_FONT);
1736 rb->lcd_clear_display();
1737 rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
1738 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
1739 rb->lcd_update();
1740 rb->sleep(HZ); /* Show title for 1 second */
1742 init_boards();
1744 if (parameter == NULL) {
1745 /* Attempt to resume saved progress, otherwise start at beginning */
1746 if (!load(SOKOBAN_SAVE_FILE, true)) {
1747 init_boards();
1748 if (!read_levels(true))
1749 return PLUGIN_OK;
1750 load_level();
1753 } else {
1754 /* The plugin is being used to open a file */
1755 if (load((char*) parameter, false)) {
1756 /* If we loaded & played a solution, quit */
1757 if (current_info.level.boxes_to_go == 0)
1758 return PLUGIN_OK;
1759 } else
1760 return PLUGIN_OK;
1763 rb->lcd_clear_display();
1764 update_screen();
1766 return sokoban_loop();