Manual: Simplify Makefile htlatex call a bit.
[kugel-rb.git] / apps / plugins / sokoban.c
blob157f53ca92f666198249844da100081dcb103b93
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Eric Linenberg
11 * February 2003: Robert Hak performs a cleanup/rewrite/feature addition.
12 * Eric smiles. Bjorn cries. Linus say 'huh?'.
13 * March 2007: Sean Morrisey performs a major rewrite/feature addition.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
24 #include "plugin.h"
25 #include "lib/playback_control.h"
29 #define SOKOBAN_TITLE "Sokoban"
31 #define SOKOBAN_LEVELS_FILE PLUGIN_GAMES_DIR "/sokoban.levels"
32 #define SOKOBAN_SAVE_FILE PLUGIN_GAMES_DATA_DIR "/sokoban.save"
34 #ifdef APPLICATION
35 #define SOKOBAN_SAVE_FOLDER PLUGIN_GAMES_DATA_DIR
36 #else
37 #define SOKOBAN_SAVE_FOLDER "/games"
38 #endif
40 #include "pluginbitmaps/sokoban_tiles.h"
41 #define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
43 /* If tilesize is 0 (which it is during dependency generation) gcc will abort
44 (div by 0) and this plugin won't get any dependencies
46 #if SOKOBAN_TILESIZE < 1
47 #define SOKOBAN_TILESIZE 10
48 #endif
50 /* SOKOBAN_TILESIZE is the number of pixels for each block.
51 * Set dynamically so all targets can support levels
52 * that fill their entire screen, less the stat box.
53 * 16 rows & 20 cols minimum */
54 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
55 #define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
56 #if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
57 #define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
58 #else
59 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
60 #endif
61 #else /* vertical layout*/
62 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
63 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
64 #endif
66 /* size of code+bss */
67 #if CONFIG_CPU == SH7034
68 #define CODE_SIZE 0x3000 /* 12k */
69 #else
70 #define CODE_SIZE 0x5000 /* 20k */
71 #endif
73 #define CODE_AND_UNDO_SIZE (CODE_SIZE+0x1000) /* + 4k */
75 /* Use either all but code & undo of the plugin buffer for level data
76 * or 128k, which ever is less */
77 #if PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE < 0x20000
78 #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE)
79 #else
80 #define MAX_LEVEL_DATA 0x20000
81 #endif
83 /* Number of levels for which to allocate buffer indexes */
84 #define MAX_LEVELS MAX_LEVEL_DATA/70
86 /* Use remaining plugin buffer (- code prog) for undo, up to 64k */
87 #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE > 0x10000
88 #define MAX_UNDOS 0x10000
89 #else
90 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE)
91 #endif
93 /* Move/push definitions for undo */
94 #define SOKOBAN_PUSH_LEFT 'L'
95 #define SOKOBAN_PUSH_RIGHT 'R'
96 #define SOKOBAN_PUSH_UP 'U'
97 #define SOKOBAN_PUSH_DOWN 'D'
98 #define SOKOBAN_MOVE_LEFT 'l'
99 #define SOKOBAN_MOVE_RIGHT 'r'
100 #define SOKOBAN_MOVE_UP 'u'
101 #define SOKOBAN_MOVE_DOWN 'd'
103 #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
104 #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
106 /* variable button definitions */
107 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
108 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
109 #define SOKOBAN_LEFT BUTTON_LEFT
110 #define SOKOBAN_RIGHT BUTTON_RIGHT
111 #define SOKOBAN_UP BUTTON_UP
112 #define SOKOBAN_DOWN BUTTON_DOWN
113 #define SOKOBAN_MENU BUTTON_OFF
114 #define SOKOBAN_UNDO BUTTON_ON
115 #define SOKOBAN_REDO BUTTON_PLAY
116 #define SOKOBAN_LEVEL_DOWN BUTTON_F1
117 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2
118 #define SOKOBAN_LEVEL_UP BUTTON_F3
119 #define SOKOBAN_PAUSE BUTTON_PLAY
120 #define BUTTON_SAVE BUTTON_ON
121 #define BUTTON_SAVE_NAME "ON"
123 #elif CONFIG_KEYPAD == ONDIO_PAD
124 #define SOKOBAN_LEFT BUTTON_LEFT
125 #define SOKOBAN_RIGHT BUTTON_RIGHT
126 #define SOKOBAN_UP BUTTON_UP
127 #define SOKOBAN_DOWN BUTTON_DOWN
128 #define SOKOBAN_MENU BUTTON_OFF
129 #define SOKOBAN_UNDO_PRE BUTTON_MENU
130 #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
131 #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
132 #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
133 #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
134 #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
135 #define SOKOBAN_PAUSE BUTTON_MENU
136 #define BUTTON_SAVE BUTTON_MENU
137 #define BUTTON_SAVE_NAME "MENU"
139 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
140 (CONFIG_KEYPAD == IRIVER_H300_PAD)
141 #define SOKOBAN_LEFT BUTTON_LEFT
142 #define SOKOBAN_RIGHT BUTTON_RIGHT
143 #define SOKOBAN_UP BUTTON_UP
144 #define SOKOBAN_DOWN BUTTON_DOWN
145 #define SOKOBAN_MENU BUTTON_OFF
146 #define SOKOBAN_UNDO BUTTON_REC
147 #define SOKOBAN_REDO BUTTON_MODE
148 #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
149 #define SOKOBAN_LEVEL_REPEAT BUTTON_ON
150 #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
151 #define SOKOBAN_PAUSE BUTTON_ON
152 #define BUTTON_SAVE BUTTON_MODE
153 #define BUTTON_SAVE_NAME "MODE"
155 #define SOKOBAN_RC_MENU BUTTON_RC_STOP
157 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
158 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
159 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
160 #define SOKOBAN_LEFT BUTTON_LEFT
161 #define SOKOBAN_RIGHT BUTTON_RIGHT
162 #define SOKOBAN_UP BUTTON_MENU
163 #define SOKOBAN_DOWN BUTTON_PLAY
164 #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
165 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
166 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
167 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
168 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
169 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
170 #define SOKOBAN_PAUSE BUTTON_SELECT
171 #define BUTTON_SAVE BUTTON_SELECT
172 #define BUTTON_SAVE_NAME "SELECT"
174 /* FIXME: if/when simultaneous button presses work for X5/M5,
175 * add level up/down */
176 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
177 #define SOKOBAN_LEFT BUTTON_LEFT
178 #define SOKOBAN_RIGHT BUTTON_RIGHT
179 #define SOKOBAN_UP BUTTON_UP
180 #define SOKOBAN_DOWN BUTTON_DOWN
181 #define SOKOBAN_MENU BUTTON_POWER
182 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
183 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
184 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
185 #define SOKOBAN_REDO BUTTON_PLAY
186 #define SOKOBAN_PAUSE BUTTON_PLAY
187 #define BUTTON_SAVE BUTTON_SELECT
188 #define BUTTON_SAVE_NAME "SELECT"
190 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
191 #define SOKOBAN_LEFT BUTTON_LEFT
192 #define SOKOBAN_RIGHT BUTTON_RIGHT
193 #define SOKOBAN_UP BUTTON_SCROLL_UP
194 #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
195 #define SOKOBAN_MENU BUTTON_POWER
196 #define SOKOBAN_UNDO_PRE BUTTON_REW
197 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
198 #define SOKOBAN_REDO BUTTON_FF
199 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
200 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
201 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
202 #define SOKOBAN_PAUSE BUTTON_PLAY
203 #define BUTTON_SAVE BUTTON_PLAY
204 #define BUTTON_SAVE_NAME "PLAY"
206 #elif CONFIG_KEYPAD == GIGABEAT_PAD
207 #define SOKOBAN_LEFT BUTTON_LEFT
208 #define SOKOBAN_RIGHT BUTTON_RIGHT
209 #define SOKOBAN_UP BUTTON_UP
210 #define SOKOBAN_DOWN BUTTON_DOWN
211 #define SOKOBAN_MENU BUTTON_POWER
212 #define SOKOBAN_UNDO BUTTON_SELECT
213 #define SOKOBAN_REDO BUTTON_A
214 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
215 #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
216 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
217 #define SOKOBAN_PAUSE BUTTON_SELECT
218 #define BUTTON_SAVE BUTTON_SELECT
219 #define BUTTON_SAVE_NAME "SELECT"
221 #elif CONFIG_KEYPAD == SANSA_E200_PAD
222 #define SOKOBAN_LEFT BUTTON_LEFT
223 #define SOKOBAN_RIGHT BUTTON_RIGHT
224 #define SOKOBAN_UP BUTTON_UP
225 #define SOKOBAN_DOWN BUTTON_DOWN
226 #define SOKOBAN_MENU BUTTON_POWER
227 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
228 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
229 #define SOKOBAN_REDO BUTTON_REC
230 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
231 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
232 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
233 #define SOKOBAN_PAUSE BUTTON_SELECT
234 #define BUTTON_SAVE BUTTON_SELECT
235 #define BUTTON_SAVE_NAME "SELECT"
237 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
238 #define SOKOBAN_LEFT BUTTON_LEFT
239 #define SOKOBAN_RIGHT BUTTON_RIGHT
240 #define SOKOBAN_UP BUTTON_UP
241 #define SOKOBAN_DOWN BUTTON_DOWN
242 #define SOKOBAN_MENU (BUTTON_HOME|BUTTON_REPEAT)
243 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
244 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
245 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_LEFT)
246 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
247 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
248 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
249 #define SOKOBAN_PAUSE BUTTON_SELECT
250 #define BUTTON_SAVE BUTTON_SELECT
251 #define BUTTON_SAVE_NAME "SELECT"
253 #elif CONFIG_KEYPAD == SANSA_C200_PAD
254 #define SOKOBAN_LEFT BUTTON_LEFT
255 #define SOKOBAN_RIGHT BUTTON_RIGHT
256 #define SOKOBAN_UP BUTTON_UP
257 #define SOKOBAN_DOWN BUTTON_DOWN
258 #define SOKOBAN_MENU BUTTON_POWER
259 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
260 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
261 #define SOKOBAN_REDO BUTTON_REC
262 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
263 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
264 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
265 #define SOKOBAN_PAUSE BUTTON_SELECT
266 #define BUTTON_SAVE BUTTON_SELECT
267 #define BUTTON_SAVE_NAME "SELECT"
269 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
270 #define SOKOBAN_LEFT BUTTON_LEFT
271 #define SOKOBAN_RIGHT BUTTON_RIGHT
272 #define SOKOBAN_UP BUTTON_UP
273 #define SOKOBAN_DOWN BUTTON_DOWN
274 #define SOKOBAN_MENU BUTTON_POWER
275 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
276 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
277 #define SOKOBAN_REDO BUTTON_HOME
278 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
279 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
280 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
281 #define SOKOBAN_PAUSE BUTTON_SELECT
282 #define BUTTON_SAVE BUTTON_SELECT
283 #define BUTTON_SAVE_NAME "SELECT"
285 #elif CONFIG_KEYPAD == SANSA_M200_PAD
286 #define SOKOBAN_LEFT BUTTON_LEFT
287 #define SOKOBAN_RIGHT BUTTON_RIGHT
288 #define SOKOBAN_UP BUTTON_UP
289 #define SOKOBAN_DOWN BUTTON_DOWN
290 #define SOKOBAN_MENU BUTTON_POWER
291 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
292 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
293 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_UP)
294 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
295 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
296 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
297 #define SOKOBAN_PAUSE BUTTON_SELECT
298 #define BUTTON_SAVE BUTTON_SELECT
299 #define BUTTON_SAVE_NAME "SELECT"
301 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
302 #define SOKOBAN_LEFT BUTTON_LEFT
303 #define SOKOBAN_RIGHT BUTTON_RIGHT
304 #define SOKOBAN_UP BUTTON_UP
305 #define SOKOBAN_DOWN BUTTON_DOWN
306 #define SOKOBAN_MENU BUTTON_MENU
307 #define SOKOBAN_UNDO BUTTON_VOL_UP
308 #define SOKOBAN_REDO BUTTON_VOL_DOWN
309 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
310 #define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
311 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
312 #define SOKOBAN_PAUSE BUTTON_SELECT
313 #define BUTTON_SAVE BUTTON_SELECT
314 #define BUTTON_SAVE_NAME "SELECT"
316 #elif CONFIG_KEYPAD == MROBE100_PAD
317 #define SOKOBAN_LEFT BUTTON_LEFT
318 #define SOKOBAN_RIGHT BUTTON_RIGHT
319 #define SOKOBAN_UP BUTTON_UP
320 #define SOKOBAN_DOWN BUTTON_DOWN
321 #define SOKOBAN_MENU BUTTON_POWER
322 #define SOKOBAN_UNDO BUTTON_SELECT
323 #define SOKOBAN_REDO BUTTON_MENU
324 #define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
325 #define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
326 #define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
327 #define SOKOBAN_PAUSE BUTTON_SELECT
328 #define BUTTON_SAVE BUTTON_SELECT
329 #define BUTTON_SAVE_NAME "SELECT"
331 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
332 #define SOKOBAN_LEFT BUTTON_RC_REW
333 #define SOKOBAN_RIGHT BUTTON_RC_FF
334 #define SOKOBAN_UP BUTTON_RC_VOL_UP
335 #define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
336 #define SOKOBAN_MENU BUTTON_RC_REC
337 #define SOKOBAN_UNDO BUTTON_RC_MODE
338 #define SOKOBAN_REDO BUTTON_RC_MENU
339 #define SOKOBAN_PAUSE BUTTON_RC_PLAY
340 #define BUTTON_SAVE BUTTON_RC_PLAY
341 #define BUTTON_SAVE_NAME "PLAY"
343 #define SOKOBAN_RC_MENU BUTTON_REC
345 #elif CONFIG_KEYPAD == COWON_D2_PAD
346 #define SOKOBAN_MENU BUTTON_MENU
347 #define SOKOBAN_LEVEL_DOWN BUTTON_MINUS
348 #define SOKOBAN_LEVEL_UP BUTTON_PLUS
349 #define SOKOBAN_MENU_NAME "[MENU]"
351 #elif CONFIG_KEYPAD == IAUDIO67_PAD
352 #define SOKOBAN_LEFT BUTTON_LEFT
353 #define SOKOBAN_RIGHT BUTTON_RIGHT
354 #define SOKOBAN_UP BUTTON_STOP
355 #define SOKOBAN_DOWN BUTTON_PLAY
356 #define SOKOBAN_MENU BUTTON_MENU
357 #define SOKOBAN_UNDO BUTTON_VOLDOWN
358 #define SOKOBAN_REDO BUTTON_VOLUP
359 #define SOKOBAN_PAUSE (BUTTON_MENU|BUTTON_LEFT)
360 #define BUTTON_SAVE (BUTTON_MENU|BUTTON_PLAY)
361 #define BUTTON_SAVE_NAME "MENU+PLAY"
363 #define SOKOBAN_RC_MENU (BUTTON_MENU|BUTTON_STOP)
365 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
366 #define SOKOBAN_LEFT BUTTON_LEFT
367 #define SOKOBAN_RIGHT BUTTON_RIGHT
368 #define SOKOBAN_UP BUTTON_UP
369 #define SOKOBAN_DOWN BUTTON_DOWN
370 #define SOKOBAN_MENU BUTTON_MENU
371 #define SOKOBAN_UNDO BUTTON_BACK
372 #define SOKOBAN_REDO (BUTTON_BACK | BUTTON_PLAY)
373 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
374 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
375 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
376 #define SOKOBAN_PAUSE BUTTON_PLAY
377 #define BUTTON_SAVE BUTTON_CUSTOM
378 #define BUTTON_SAVE_NAME "CUSTOM"
380 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
381 #define SOKOBAN_LEFT BUTTON_LEFT
382 #define SOKOBAN_RIGHT BUTTON_RIGHT
383 #define SOKOBAN_UP BUTTON_UP
384 #define SOKOBAN_DOWN BUTTON_DOWN
385 #define SOKOBAN_MENU BUTTON_MENU
386 #define SOKOBAN_UNDO BUTTON_VIEW
387 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_VIEW)
388 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
389 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
390 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
391 #define SOKOBAN_PAUSE BUTTON_SELECT
392 #define BUTTON_SAVE BUTTON_PLAYLIST
393 #define BUTTON_SAVE_NAME "PLAYLIST"
395 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
396 #define SOKOBAN_LEFT BUTTON_LEFT
397 #define SOKOBAN_RIGHT BUTTON_RIGHT
398 #define SOKOBAN_UP BUTTON_UP
399 #define SOKOBAN_DOWN BUTTON_DOWN
400 #define SOKOBAN_MENU BUTTON_MENU
401 #define SOKOBAN_UNDO BUTTON_PREV
402 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_PREV)
403 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
404 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
405 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
406 #define SOKOBAN_PAUSE BUTTON_PLAY
407 #define BUTTON_SAVE BUTTON_NEXT
408 #define BUTTON_SAVE_NAME "NEXT"
410 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
411 #define SOKOBAN_LEFT BUTTON_PREV
412 #define SOKOBAN_RIGHT BUTTON_NEXT
413 #define SOKOBAN_UP BUTTON_UP
414 #define SOKOBAN_DOWN BUTTON_DOWN
415 #define SOKOBAN_MENU BUTTON_MENU
416 #define SOKOBAN_UNDO BUTTON_LEFT
417 #define SOKOBAN_REDO (BUTTON_LEFT | BUTTON_PLAY)
418 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
419 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
420 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
421 #define SOKOBAN_PAUSE BUTTON_PLAY
422 #define BUTTON_SAVE BUTTON_RIGHT
423 #define BUTTON_SAVE_NAME "RIGHT"
425 #elif CONFIG_KEYPAD == ONDAVX747_PAD
426 #define SOKOBAN_MENU BUTTON_MENU
427 #define SOKOBAN_MENU_NAME "[MENU]"
429 #elif CONFIG_KEYPAD == ONDAVX777_PAD
430 #define SOKOBAN_MENU BUTTON_POWER
431 #define SOKOBAN_MENU_NAME "[POWER]"
433 #elif CONFIG_KEYPAD == MROBE500_PAD
435 #define SOKOBAN_MENU BUTTON_POWER
436 #define SOKOBAN_MENU_NAME "[POWER]"
438 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
439 #define SOKOBAN_LEFT BUTTON_LEFT
440 #define SOKOBAN_RIGHT BUTTON_RIGHT
441 #define SOKOBAN_UP BUTTON_UP
442 #define SOKOBAN_DOWN BUTTON_DOWN
443 #define SOKOBAN_MENU BUTTON_REC
444 #define SOKOBAN_UNDO_PRE BUTTON_REW
445 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_LEFT)
446 #define SOKOBAN_REDO BUTTON_FFWD
447 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
448 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
449 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
450 #define SOKOBAN_PAUSE BUTTON_PLAY
451 #define BUTTON_SAVE BUTTON_PLAY
452 #define BUTTON_SAVE_NAME "PLAY"
454 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
455 #define SOKOBAN_LEFT BUTTON_PREV
456 #define SOKOBAN_RIGHT BUTTON_NEXT
457 #define SOKOBAN_UP BUTTON_UP
458 #define SOKOBAN_DOWN BUTTON_DOWN
459 #define SOKOBAN_MENU BUTTON_REC
460 #define SOKOBAN_UNDO BUTTON_CANCEL
461 #define SOKOBAN_REDO BUTTON_OK
462 #define SOKOBAN_LEVEL_DOWN (BUTTON_OK | BUTTON_PREV)
463 #define SOKOBAN_LEVEL_REPEAT (BUTTON_OK | BUTTON_CANCEL)
464 #define SOKOBAN_LEVEL_UP (BUTTON_OK | BUTTON_NEXT)
465 #define SOKOBAN_PAUSE BUTTON_PLAY
466 #define BUTTON_SAVE BUTTON_MENU
467 #define BUTTON_SAVE_NAME "MENU"
469 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
470 #define SOKOBAN_LEFT BUTTON_VOL_DOWN
471 #define SOKOBAN_RIGHT BUTTON_VOL_UP
472 #define SOKOBAN_UP BUTTON_REW
473 #define SOKOBAN_DOWN BUTTON_FF
474 #define SOKOBAN_MENU BUTTON_FUNC
475 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_REW)
476 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_FF)
477 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_VOL_DOWN)
478 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
479 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_VOL_UP)
480 #define SOKOBAN_PAUSE BUTTON_PLAY
481 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_FUNC)
482 #define BUTTON_SAVE_NAME "PLAY+FUNC"
484 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
485 #define SOKOBAN_LEFT BUTTON_REW
486 #define SOKOBAN_RIGHT BUTTON_FF
487 #define SOKOBAN_UP BUTTON_UP
488 #define SOKOBAN_DOWN BUTTON_DOWN
489 #define SOKOBAN_MENU BUTTON_MENU
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 #else
500 #error No keymap defined!
501 #endif
503 #ifdef HAVE_TOUCHSCREEN
504 #ifndef SOKOBAN_LEFT
505 #define SOKOBAN_LEFT BUTTON_MIDLEFT
506 #endif
507 #ifndef SOKOBAN_RIGHT
508 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
509 #endif
510 #ifndef SOKOBAN_UP
511 #define SOKOBAN_UP BUTTON_TOPMIDDLE
512 #endif
513 #ifndef SOKOBAN_DOWN
514 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
515 #endif
516 #ifndef SOKOBAN_MENU
517 #define SOKOBAN_MENU BUTTON_TOPLEFT
518 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
519 #endif
520 #ifndef SOKOBAN_UNDO
521 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
522 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
523 #endif
524 #ifndef SOKOBAN_REDO
525 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
526 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
527 #endif
528 #ifndef SOKOBAN_PAUSE
529 #define SOKOBAN_PAUSE BUTTON_CENTER
530 #define SOKOBAN_PAUSE_NAME "[CENTER]"
531 #endif
532 #ifndef SOKOBAN_LEVEL_REPEAT
533 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
534 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
535 #endif
536 #ifndef BUTTON_SAVE
537 #define BUTTON_SAVE BUTTON_CENTER
538 #define BUTTON_SAVE_NAME "CENTER"
539 #endif
540 #endif
542 #define SOKOBAN_FONT FONT_SYSFIXED
545 /* The Location, Undo and LevelInfo structs are OO-flavored.
546 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
547 * but the overall data layout becomes more manageable. */
549 /* Level data & stats */
550 struct LevelInfo {
551 int index; /* Level index (level number - 1) */
552 int moves; /* Moves & pushes for the stats */
553 int pushes;
554 short boxes_to_go; /* Number of unplaced boxes remaining in level */
555 short height; /* Height & width for centering level display */
556 short width;
559 struct Location {
560 short row;
561 short col;
564 /* Our full undo history */
565 static struct UndoInfo {
566 int count; /* How many undos have been done */
567 int current; /* Which history is the current undo */
568 int max; /* Which history is the max redoable */
569 char history[MAX_UNDOS];
570 } undo_info;
572 /* Our playing board */
573 static struct BoardInfo {
574 char board[ROWS][COLS]; /* The current board data */
575 struct LevelInfo level; /* Level data & stats */
576 struct Location player; /* Where the player is */
577 int max_level; /* The number of levels we have */
578 } current_info;
580 static struct BufferedBoards {
581 char filename[MAX_PATH]; /* Filename of the levelset we're using */
582 char data[MAX_LEVEL_DATA]; /* Buffered level data */
583 int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
584 int start; /* Index of first buffered board */
585 int end; /* Index of last buffered board */
586 short prebuffered_boards; /* Number of boards before current to store */
587 } buffered_boards;
590 static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
593 static void init_undo(void)
595 undo_info.count = 0;
596 undo_info.current = 0;
597 undo_info.max = 0;
600 static void get_delta(char direction, short *d_r, short *d_c)
602 switch (direction) {
603 case SOKOBAN_PUSH_LEFT:
604 case SOKOBAN_MOVE_LEFT:
605 *d_r = 0;
606 *d_c = -1;
607 break;
608 case SOKOBAN_PUSH_RIGHT:
609 case SOKOBAN_MOVE_RIGHT:
610 *d_r = 0;
611 *d_c = 1;
612 break;
613 case SOKOBAN_PUSH_UP:
614 case SOKOBAN_MOVE_UP:
615 *d_r = -1;
616 *d_c = 0;
617 break;
618 case SOKOBAN_PUSH_DOWN:
619 case SOKOBAN_MOVE_DOWN:
620 *d_r = 1;
621 *d_c = 0;
625 static bool undo(void)
627 char undo;
628 short r, c;
629 short d_r = 0, d_c = 0; /* delta row & delta col */
630 char *space_cur, *space_next, *space_prev;
631 bool undo_push = false;
633 /* If no more undos or we've wrapped all the way around, quit */
634 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
635 return false;
637 /* Move to previous undo in the list */
638 if (undo_info.current == 0 && undo_info.count > 1)
639 undo_info.current = MAX_UNDOS - 1;
640 else
641 undo_info.current--;
643 undo_info.count--;
645 undo = undo_info.history[undo_info.current];
647 if (undo < SOKOBAN_MOVE_MIN)
648 undo_push = true;
650 get_delta(undo, &d_r, &d_c);
652 r = current_info.player.row;
653 c = current_info.player.col;
655 /* Give the 3 spaces we're going to use better names */
656 space_cur = &current_info.board[r][c];
657 space_next = &current_info.board[r + d_r][c + d_c];
658 space_prev = &current_info.board[r - d_r][c - d_c];
660 /* Update board info */
661 if (undo_push) {
662 /* Moving box from goal to floor */
663 if (*space_next == '*' && *space_cur == '@')
664 current_info.level.boxes_to_go++;
665 /* Moving box from floor to goal */
666 else if (*space_next == '$' && *space_cur == '+')
667 current_info.level.boxes_to_go--;
669 /* Move box off of next space... */
670 *space_next = (*space_next == '*' ? '.' : ' ');
671 /* ...and on to current space */
672 *space_cur = (*space_cur == '+' ? '*' : '$');
674 current_info.level.pushes--;
675 } else
676 /* Just move player off of current space */
677 *space_cur = (*space_cur == '+' ? '.' : ' ');
678 /* Move player back to previous space */
679 *space_prev = (*space_prev == '.' ? '+' : '@');
681 /* Update position */
682 current_info.player.row -= d_r;
683 current_info.player.col -= d_c;
685 current_info.level.moves--;
687 return true;
690 static void add_undo(char undo)
692 undo_info.history[undo_info.current] = undo;
694 /* Wrap around if MAX_UNDOS exceeded */
695 if (undo_info.current < (MAX_UNDOS - 1))
696 undo_info.current++;
697 else
698 undo_info.current = 0;
700 if (undo_info.count < MAX_UNDOS)
701 undo_info.count++;
704 static bool move(char direction, bool redo)
706 short r, c;
707 short d_r = 0, d_c = 0; /* delta row & delta col */
708 char *space_cur, *space_next, *space_beyond;
709 bool push = false;
711 get_delta(direction, &d_r, &d_c);
713 r = current_info.player.row;
714 c = current_info.player.col;
716 /* Check for out-of-bounds */
717 if (r + 2*d_r < 0 || r + 2*d_r >= ROWS ||
718 c + 2*d_c < 0 || c + 2*d_c >= COLS)
719 return false;
721 /* Give the 3 spaces we're going to use better names */
722 space_cur = &current_info.board[r][c];
723 space_next = &current_info.board[r + d_r][c + d_c];
724 space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
726 if (*space_next == '$' || *space_next == '*') {
727 /* Change direction from move to push for undo */
728 if (direction >= SOKOBAN_MOVE_MIN)
729 direction -= SOKOBAN_MOVE_DIFF;
730 push = true;
732 else if (direction < SOKOBAN_MOVE_MIN)
733 /* Change back to move if redo/solution playback push is invalid */
734 direction += SOKOBAN_MOVE_DIFF;
736 /* Update board info */
737 if (push) {
738 /* Moving box from goal to floor */
739 if (*space_next == '*' && *space_beyond == ' ')
740 current_info.level.boxes_to_go++;
741 /* Moving box from floor to goal */
742 else if (*space_next == '$' && *space_beyond == '.')
743 current_info.level.boxes_to_go--;
744 /* Check for invalid move */
745 else if (*space_beyond != '.' && *space_beyond != ' ')
746 return false;
748 /* Move player onto next space */
749 *space_next = (*space_next == '*' ? '+' : '@');
750 /* Move box onto space beyond next */
751 *space_beyond = (*space_beyond == '.' ? '*' : '$');
753 current_info.level.pushes++;
754 } else {
755 /* Check for invalid move */
756 if (*space_next != '.' && *space_next != ' ')
757 return false;
759 /* Move player onto next space */
760 *space_next = (*space_next == '.' ? '+' : '@');
762 /* Move player off of current space */
763 *space_cur = (*space_cur == '+' ? '.' : ' ');
765 /* Update position */
766 current_info.player.row += d_r;
767 current_info.player.col += d_c;
769 current_info.level.moves++;
771 /* Update undo_info.max to current on every normal move,
772 * except if it's the same as a redo. */
773 /* normal move and either */
774 if (!redo &&
775 /* moves have been undone... */
776 ((undo_info.max != undo_info.current &&
777 /* ...and the current move is NOT the same as the one in history */
778 undo_info.history[undo_info.current] != direction) ||
779 /* or moves have not been undone */
780 undo_info.max == undo_info.current)) {
781 add_undo(direction);
782 undo_info.max = undo_info.current;
783 } else /* redo move or move was same as redo */
784 add_undo(direction); /* add_undo to update current */
786 return true;
789 #ifdef SOKOBAN_REDO
790 static bool redo(void)
792 /* If no moves have been undone, quit */
793 if (undo_info.current == undo_info.max)
794 return false;
796 return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
797 undo_info.current : 0)], true);
799 #endif
801 static void init_boards(void)
803 rb->strlcpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE,
804 sizeof(buffered_boards.filename));
806 current_info.level.index = 0;
807 current_info.player.row = 0;
808 current_info.player.col = 0;
809 current_info.max_level = 0;
811 buffered_boards.start = 0;
812 buffered_boards.end = 0;
813 buffered_boards.prebuffered_boards = 0;
815 init_undo();
818 static bool read_levels(bool initialize)
820 int fd = 0;
821 short len;
822 short lastlen = 0;
823 short row = 0;
824 int level_count = 0;
826 int i = 0;
827 int level_len = 0;
828 bool index_set = false;
830 /* Get the index of the first level to buffer */
831 if (current_info.level.index > buffered_boards.prebuffered_boards &&
832 !initialize)
833 buffered_boards.start = current_info.level.index -
834 buffered_boards.prebuffered_boards;
835 else
836 buffered_boards.start = 0;
838 if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
839 rb->splashf(HZ*2, "Unable to open %s", buffered_boards.filename);
840 return false;
843 do {
844 len = rb->read_line(fd, buf, sizeof(buf));
846 /* Correct len when trailing \r's or \n's are counted */
847 if (len > 2 && buf[len - 2] == '\0')
848 len -= 2;
849 else if (len > 1 && buf[len - 1] == '\0')
850 len--;
852 /* Skip short lines & lines with non-level data */
853 if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
854 buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
855 if (level_count >= buffered_boards.start) {
856 /* Set the index of this level */
857 if (!index_set &&
858 level_count - buffered_boards.start < MAX_LEVELS) {
859 buffered_boards.index[level_count - buffered_boards.start]
860 = i;
861 index_set = true;
863 /* Copy buffer to board data */
864 if (i + level_len + len < MAX_LEVEL_DATA) {
865 rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
866 buffered_boards.data[i + level_len + len] = '\n';
869 level_len += len + 1;
870 row++;
872 /* If newline & level is tall enough or is RLE */
873 } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
874 level_count++;
875 if (level_count >= buffered_boards.start) {
876 i += level_len;
877 if (i < MAX_LEVEL_DATA)
878 buffered_boards.end = level_count;
879 else if (!initialize)
880 break;
882 row = 0;
883 level_len = 0;
884 index_set = false;
886 } else if (len > 22)
887 len = 1;
889 } while ((lastlen = len));
891 /* Set the index of the end of the last level */
892 if (level_count - buffered_boards.start < MAX_LEVELS)
893 buffered_boards.index[level_count - buffered_boards.start] = i;
895 if (initialize) {
896 current_info.max_level = level_count;
897 buffered_boards.prebuffered_boards = buffered_boards.end/2;
900 rb->close(fd);
902 return true;
905 static void load_level(void)
907 int c, r;
908 int i, n;
909 int level_size;
910 int index = current_info.level.index - buffered_boards.start;
911 char *level;
913 /* Get the buffered board index of the current level */
914 if (current_info.level.index < buffered_boards.start ||
915 current_info.level.index >= buffered_boards.end) {
916 read_levels(false);
917 if (current_info.level.index > buffered_boards.prebuffered_boards)
918 index = buffered_boards.prebuffered_boards;
919 else
920 index = current_info.level.index;
922 level = &buffered_boards.data[buffered_boards.index[index]];
924 /* Reset level info */
925 current_info.level.moves = 0;
926 current_info.level.pushes = 0;
927 current_info.level.boxes_to_go = 0;
928 current_info.level.width = 0;
930 /* Clear board */
931 for (r = 0; r < ROWS; r++)
932 for (c = 0; c < COLS; c++)
933 current_info.board[r][c] = 'X';
935 level_size = buffered_boards.index[index + 1] -
936 buffered_boards.index[index];
938 for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
939 if (level[i] == '\n' || level[i] == '|') {
940 if (c > 3) {
941 /* Update max width of level & go to next row */
942 if (c > current_info.level.width)
943 current_info.level.width = c;
944 c = 0;
945 r++;
946 if (r >= ROWS)
947 break;
949 } else if (c < COLS) {
950 /* Read RLE character's length into n */
951 if (level[i] >= '0' && level[i] <= '9') {
952 n = level[i++] - '0';
953 if (level[i] >= '0' && level[i] <= '9')
954 n = n*10 + level[i++] - '0';
957 /* Cleanup & replace */
958 if (level[i] == '%')
959 level[i] = '*';
960 else if (level[i] == '-' || level[i] == '_')
961 level[i] = ' ';
963 if (n > 1) {
964 if (c + n >= COLS)
965 n = COLS - c;
967 if (level[i] == '.')
968 current_info.level.boxes_to_go += n;
970 /* Put RLE character n times */
971 while (n--)
972 current_info.board[r][c++] = level[i];
973 n = 1;
975 } else {
976 if (level[i] == '.' || level[i] == '+')
977 current_info.level.boxes_to_go++;
979 if (level[i] == '@' ||level[i] == '+') {
980 current_info.player.row = r;
981 current_info.player.col = c;
984 current_info.board[r][c++] = level[i];
989 current_info.level.height = r;
991 #if LCD_DEPTH > 2
992 /* Fill in blank space outside level on color targets */
993 for (r = 0; r < ROWS; r++)
994 for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
995 current_info.board[r][c] = 'X';
997 for (c = 0; c < COLS; c++) {
998 for (r = 0; (current_info.board[r][c] == ' ' ||
999 current_info.board[r][c] == 'X') && r < ROWS; r++)
1000 current_info.board[r][c] = 'X';
1001 for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
1002 current_info.board[r][c] == 'X') && r >= 0; r--)
1003 current_info.board[r][c] = 'X';
1005 #endif
1008 static void update_screen(void)
1010 int c, r;
1011 int rows, cols;
1013 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
1014 #define STAT_HEIGHT 25
1015 #define STAT_X (LCD_WIDTH - 120)/2
1016 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
1017 #define BOARD_WIDTH LCD_WIDTH
1018 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
1019 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
1020 rb->lcd_putsxyf(STAT_X + 7, STAT_Y + 14, "%d", current_info.level.index + 1);
1021 rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
1022 rb->lcd_putsxyf(STAT_X + 44, STAT_Y + 14, "%d", current_info.level.moves);
1023 rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
1024 rb->lcd_putsxyf(STAT_X + 82, STAT_Y + 14, "%d", current_info.level.pushes);
1026 rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
1027 rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
1028 rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
1029 #else
1030 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
1031 #define STAT_X (LCD_WIDTH - 40)
1032 #else
1033 #define STAT_X COLS*SOKOBAN_TILESIZE
1034 #endif
1035 #define STAT_Y (LCD_HEIGHT - 64)/2
1036 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
1037 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
1038 #define BOARD_HEIGHT LCD_HEIGHT
1039 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 2, "Level");
1040 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 12, "%d", current_info.level.index + 1);
1041 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 23, "Moves");
1042 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 33, "%d", current_info.level.moves);
1043 #if STAT_WIDTH < 38
1044 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Push");
1045 #else
1046 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Pushes");
1047 #endif
1048 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 54, "%d", current_info.level.pushes);
1050 rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 64);
1051 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 21);
1052 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 42);
1054 #endif
1056 /* load the board to the screen */
1057 for (rows = 0; rows < ROWS; rows++) {
1058 for (cols = 0; cols < COLS; cols++) {
1059 c = cols*SOKOBAN_TILESIZE +
1060 (BOARD_WIDTH - current_info.level.width*SOKOBAN_TILESIZE)/2;
1061 r = rows*SOKOBAN_TILESIZE +
1062 (BOARD_HEIGHT - current_info.level.height*SOKOBAN_TILESIZE)/2;
1064 switch(current_info.board[rows][cols]) {
1065 case 'X': /* blank space outside of level */
1066 break;
1068 case ' ': /* floor */
1069 rb->lcd_bitmap_part(sokoban_tiles, 0, 0*SOKOBAN_TILESIZE,
1070 STRIDE( SCREEN_MAIN,
1071 BMPWIDTH_sokoban_tiles,
1072 BMPHEIGHT_sokoban_tiles),
1073 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1074 break;
1076 case '#': /* wall */
1077 rb->lcd_bitmap_part(sokoban_tiles, 0, 1*SOKOBAN_TILESIZE,
1078 STRIDE( SCREEN_MAIN,
1079 BMPWIDTH_sokoban_tiles,
1080 BMPHEIGHT_sokoban_tiles),
1081 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1082 break;
1084 case '$': /* box */
1085 rb->lcd_bitmap_part(sokoban_tiles, 0, 2*SOKOBAN_TILESIZE,
1086 STRIDE( SCREEN_MAIN,
1087 BMPWIDTH_sokoban_tiles,
1088 BMPHEIGHT_sokoban_tiles),
1089 c, r, SOKOBAN_TILESIZE,SOKOBAN_TILESIZE);
1090 break;
1092 case '*': /* box on goal */
1093 rb->lcd_bitmap_part(sokoban_tiles, 0, 3*SOKOBAN_TILESIZE,
1094 STRIDE( SCREEN_MAIN,
1095 BMPWIDTH_sokoban_tiles,
1096 BMPHEIGHT_sokoban_tiles),
1097 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1098 break;
1100 case '.': /* goal */
1101 rb->lcd_bitmap_part(sokoban_tiles, 0, 4*SOKOBAN_TILESIZE,
1102 STRIDE( SCREEN_MAIN,
1103 BMPWIDTH_sokoban_tiles,
1104 BMPHEIGHT_sokoban_tiles),
1105 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1106 break;
1108 case '@': /* player */
1109 rb->lcd_bitmap_part(sokoban_tiles, 0, 5*SOKOBAN_TILESIZE,
1110 STRIDE( SCREEN_MAIN,
1111 BMPWIDTH_sokoban_tiles,
1112 BMPHEIGHT_sokoban_tiles),
1113 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1114 break;
1116 case '+': /* player on goal */
1117 rb->lcd_bitmap_part(sokoban_tiles, 0, 6*SOKOBAN_TILESIZE,
1118 STRIDE( SCREEN_MAIN,
1119 BMPWIDTH_sokoban_tiles,
1120 BMPHEIGHT_sokoban_tiles),
1121 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1122 break;
1127 /* print out the screen */
1128 rb->lcd_update();
1131 static void draw_level(void)
1133 load_level();
1134 rb->lcd_clear_display();
1135 update_screen();
1138 static bool save(char *filename, bool solution)
1140 int fd;
1141 char *loc;
1142 DIR *dir;
1143 char dirname[MAX_PATH];
1145 rb->splash(0, "Saving...");
1147 /* Create dir if it doesn't exist */
1148 if ((loc = rb->strrchr(filename, '/')) != NULL) {
1149 rb->strlcpy(dirname, filename, loc - filename + 1);
1151 if(!(dir = rb->opendir(dirname)))
1152 rb->mkdir(dirname);
1153 else
1154 rb->closedir(dir);
1157 if (filename[0] == '\0' ||
1158 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
1159 rb->splashf(HZ*2, "Unable to open %s", filename);
1160 return false;
1163 /* Sokoban: S/P for solution/progress : level number : current undo */
1164 rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
1165 current_info.level.index + 1, undo_info.current);
1166 rb->write(fd, buf, rb->strlen(buf));
1168 /* Filename of levelset */
1169 rb->write(fd, buffered_boards.filename,
1170 rb->strlen(buffered_boards.filename));
1171 rb->write(fd, "\n", 1);
1173 /* Full undo history */
1174 rb->write(fd, undo_info.history, undo_info.max);
1176 rb->close(fd);
1178 return true;
1181 static bool load(char *filename, bool silent)
1183 int fd;
1184 int i, n;
1185 int len;
1186 int button;
1187 bool play_solution;
1188 bool paused = false;
1189 unsigned short speed = 2;
1190 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
1192 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
1193 if (!silent)
1194 rb->splashf(HZ*2, "Unable to open %s", filename);
1195 return false;
1198 /* Read header, level number, & current undo */
1199 rb->read_line(fd, buf, sizeof(buf));
1201 /* If we're opening a level file, not a solution/progress file */
1202 if (rb->strncmp(buf, "Sokoban", 7) != 0) {
1203 rb->close(fd);
1205 rb->strlcpy(buffered_boards.filename, filename,
1206 sizeof(buffered_boards.filename));
1208 if (!read_levels(true))
1209 return false;
1211 current_info.level.index = 0;
1212 load_level();
1214 /* If there aren't any boxes to go or the player position wasn't set,
1215 * the file probably wasn't a Sokoban level file */
1216 if (current_info.level.boxes_to_go == 0 ||
1217 current_info.player.row == 0 || current_info.player.col == 0) {
1218 if (!silent)
1219 rb->splash(HZ*2, "File is not a Sokoban level file");
1220 return false;
1223 } else {
1225 /* Read filename of levelset */
1226 rb->read_line(fd, buffered_boards.filename,
1227 sizeof(buffered_boards.filename));
1229 /* Read full undo history */
1230 len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
1232 /* Correct len when trailing \r's or \n's are counted */
1233 if (len > 2 && undo_info.history[len - 2] == '\0')
1234 len -= 2;
1235 else if (len > 1 && undo_info.history[len - 1] == '\0')
1236 len--;
1238 rb->close(fd);
1240 /* Check to see if we're going to play a solution or resume progress */
1241 play_solution = (buf[8] == 'S');
1243 /* Get level number */
1244 for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
1245 n = n*10 + buf[i] - '0';
1246 current_info.level.index = n - 1;
1248 /* Get undo index */
1249 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
1250 n = n*10 + buf[i] - '0';
1251 if (n > len)
1252 n = len;
1253 undo_info.max = len;
1255 if (current_info.level.index < 0) {
1256 if (!silent)
1257 rb->splash(HZ*2, "Error loading level");
1258 return false;
1260 if (!read_levels(true))
1261 return false;
1262 if (current_info.level.index >= current_info.max_level) {
1263 if (!silent)
1264 rb->splash(HZ*2, "Error loading level");
1265 return false;
1268 load_level();
1270 if (play_solution) {
1271 rb->lcd_clear_display();
1272 update_screen();
1273 rb->sleep(2*delay[speed]);
1275 /* Replay solution until menu button is pressed */
1276 i = 0;
1277 while (true) {
1278 if (i < len) {
1279 if (!move(undo_info.history[i], true)) {
1280 n = i;
1281 break;
1283 rb->lcd_clear_display();
1284 update_screen();
1285 i++;
1286 } else
1287 paused = true;
1289 rb->sleep(delay[speed]);
1291 while ((button = rb->button_get(false)) || paused) {
1292 switch (button) {
1293 case SOKOBAN_MENU:
1294 /* Pretend the level is complete so we'll quit */
1295 current_info.level.boxes_to_go = 0;
1296 return true;
1298 case SOKOBAN_PAUSE:
1299 /* Toggle pause state */
1300 paused = !paused;
1301 break;
1303 case SOKOBAN_LEFT:
1304 case SOKOBAN_LEFT | BUTTON_REPEAT:
1305 /* Go back one move */
1306 if (paused) {
1307 if (undo())
1308 i--;
1309 rb->lcd_clear_display();
1310 update_screen();
1312 break;
1314 case SOKOBAN_RIGHT:
1315 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1316 /* Go forward one move */
1317 if (paused) {
1318 if (redo())
1319 i++;
1320 rb->lcd_clear_display();
1321 update_screen();
1323 break;
1325 case SOKOBAN_UP:
1326 case SOKOBAN_UP | BUTTON_REPEAT:
1327 /* Speed up */
1328 if (speed < sizeof(delay)/sizeof(int) - 1)
1329 speed++;
1330 break;
1332 case SOKOBAN_DOWN:
1333 case SOKOBAN_DOWN | BUTTON_REPEAT:
1334 /* Slow down */
1335 if (speed > 0)
1336 speed--;
1339 if (paused)
1340 rb->sleep(HZ/33);
1344 /* If level is complete, wait for keypress before quitting */
1345 if (current_info.level.boxes_to_go == 0)
1346 rb->button_get(true);
1348 } else {
1349 /* Advance to current undo */
1350 for (i = 0; i < n; i++) {
1351 if (!move(undo_info.history[i], true)) {
1352 n = i;
1353 break;
1357 rb->button_clear_queue();
1358 rb->lcd_clear_display();
1361 undo_info.current = n;
1364 return true;
1367 static int sokoban_menu(void)
1369 int button;
1370 int selection = 0;
1371 int i;
1372 bool menu_quit;
1373 int start_selected = 0;
1374 int prev_level = current_info.level.index;
1376 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
1377 "Resume", "Select Level", "Audio Playback", "Keys",
1378 "Load Default Level Set", "Quit Without Saving",
1379 "Save Progress & Quit");
1381 do {
1382 menu_quit = true;
1383 selection = rb->do_menu(&menu, &start_selected, NULL, false);
1385 switch (selection) {
1386 case 0: /* Resume */
1387 break;
1389 case 1: /* Select level */
1390 current_info.level.index++;
1391 rb->set_int("Select Level", "", UNIT_INT,
1392 &current_info.level.index, NULL, 1, 1,
1393 current_info.max_level, NULL);
1394 current_info.level.index--;
1395 if (prev_level != current_info.level.index) {
1396 init_undo();
1397 draw_level();
1398 } else
1399 menu_quit = false;
1400 break;
1402 case 2: /* Audio playback control */
1403 playback_control(NULL);
1404 menu_quit = false;
1405 break;
1407 case 3: /* Keys */
1408 FOR_NB_SCREENS(i)
1409 rb->screens[i]->clear_display();
1410 rb->lcd_setfont(SOKOBAN_FONT);
1412 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1413 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1414 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1415 rb->lcd_putsxy(3, 16, "[ON] Undo");
1416 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1417 rb->lcd_putsxy(3, 36, "[F1] Down a Level");
1418 rb->lcd_putsxy(3, 46, "[F2] Restart Level");
1419 rb->lcd_putsxy(3, 56, "[F3] Up a Level");
1420 #elif CONFIG_KEYPAD == ONDIO_PAD
1421 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1422 rb->lcd_putsxy(3, 16, "[MODE] Undo");
1423 rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1424 rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1425 rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1426 rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1427 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1428 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1429 rb->lcd_putsxy(3, 6, "[STOP] Menu");
1430 rb->lcd_putsxy(3, 16, "[REC] Undo");
1431 rb->lcd_putsxy(3, 26, "[MODE] Redo");
1432 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1433 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1434 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1435 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1436 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1437 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1438 rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1439 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1440 rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1441 rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1442 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1443 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1444 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1445 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1446 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1447 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
1448 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1449 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1450 rb->lcd_putsxy(3, 16, "[REW] Undo");
1451 rb->lcd_putsxy(3, 26, "[FF] Redo");
1452 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1453 rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1454 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1455 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1456 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1457 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1458 rb->lcd_putsxy(3, 26, "[A] Redo");
1459 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1460 rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
1461 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1462 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1463 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1464 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1465 rb->lcd_putsxy(3, 26, "[REC] Redo");
1466 rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1467 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1468 rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1469 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1470 rb->lcd_putsxy(3, 6, "[MENU] Menu");
1471 rb->lcd_putsxy(3, 16, "[VOL+] Undo");
1472 rb->lcd_putsxy(3, 26, "[VOL-] Redo");
1473 rb->lcd_putsxy(3, 36, "[PREV] Previous Level");
1474 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1475 rb->lcd_putsxy(3, 56, "[NEXT] Next Level");
1476 #endif
1478 #ifdef HAVE_TOUCHSCREEN
1479 rb->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME " Menu");
1480 rb->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME " Undo");
1481 rb->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME " Redo");
1482 rb->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME " Pause");
1483 rb->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME " Restart Level");
1484 #endif
1486 FOR_NB_SCREENS(i)
1487 rb->screens[i]->update();
1489 /* Display until keypress */
1490 do {
1491 rb->sleep(HZ/20);
1492 button = rb->button_get(false);
1493 } while (!button || button & BUTTON_REL ||
1494 button & BUTTON_REPEAT);
1496 menu_quit = false;
1497 break;
1499 case 4: /* Load default levelset */
1500 init_boards();
1501 if (!read_levels(true))
1502 return 5; /* Quit */
1503 load_level();
1504 break;
1506 case 5: /* Quit */
1507 break;
1509 case 6: /* Save & quit */
1510 save(SOKOBAN_SAVE_FILE, false);
1511 rb->reload_directory();
1514 } while (!menu_quit);
1516 /* Restore font */
1517 rb->lcd_setfont(SOKOBAN_FONT);
1519 FOR_NB_SCREENS(i) {
1520 rb->screens[i]->clear_display();
1521 rb->screens[i]->update();
1524 return selection;
1527 static bool sokoban_loop(void)
1529 bool moved;
1530 int i = 0, button = 0;
1531 #if defined(SOKOBAN_UNDO_PRE)
1532 int lastbutton = 0;
1533 #endif
1534 int w, h;
1535 char *loc;
1537 while (true) {
1538 moved = false;
1540 button = rb->button_get(true);
1542 switch(button)
1544 #ifdef SOKOBAN_RC_MENU
1545 case SOKOBAN_RC_MENU:
1546 #endif
1547 case SOKOBAN_MENU:
1548 switch (sokoban_menu()) {
1549 case 5: /* Quit */
1550 case 6: /* Save & quit */
1551 return PLUGIN_OK;
1553 update_screen();
1554 break;
1556 case SOKOBAN_UNDO:
1557 #ifdef SOKOBAN_UNDO_PRE
1558 if (lastbutton != SOKOBAN_UNDO_PRE)
1559 break;
1560 #else /* repeat can't work here for Ondio, iPod, et al */
1561 case SOKOBAN_UNDO | BUTTON_REPEAT:
1562 #endif
1563 undo();
1564 rb->lcd_clear_display();
1565 update_screen();
1566 break;
1568 #ifdef SOKOBAN_REDO
1569 case SOKOBAN_REDO:
1570 case SOKOBAN_REDO | BUTTON_REPEAT:
1571 moved = redo();
1572 rb->lcd_clear_display();
1573 update_screen();
1574 break;
1575 #endif
1577 #ifdef SOKOBAN_LEVEL_UP
1578 case SOKOBAN_LEVEL_UP:
1579 case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
1580 /* next level */
1581 init_undo();
1582 if (current_info.level.index + 1 < current_info.max_level)
1583 current_info.level.index++;
1585 draw_level();
1586 break;
1587 #endif
1589 #ifdef SOKOBAN_LEVEL_DOWN
1590 case SOKOBAN_LEVEL_DOWN:
1591 case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
1592 /* previous level */
1593 init_undo();
1594 if (current_info.level.index > 0)
1595 current_info.level.index--;
1597 draw_level();
1598 break;
1599 #endif
1601 #ifdef SOKOBAN_LEVEL_REPEAT
1602 case SOKOBAN_LEVEL_REPEAT:
1603 case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT:
1604 /* same level */
1605 init_undo();
1606 draw_level();
1607 break;
1608 #endif
1610 case SOKOBAN_LEFT:
1611 case SOKOBAN_LEFT | BUTTON_REPEAT:
1612 moved = move(SOKOBAN_MOVE_LEFT, false);
1613 break;
1615 case SOKOBAN_RIGHT:
1616 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1617 moved = move(SOKOBAN_MOVE_RIGHT, false);
1618 break;
1620 case SOKOBAN_UP:
1621 case SOKOBAN_UP | BUTTON_REPEAT:
1622 moved = move(SOKOBAN_MOVE_UP, false);
1623 break;
1625 case SOKOBAN_DOWN:
1626 case SOKOBAN_DOWN | BUTTON_REPEAT:
1627 moved = move(SOKOBAN_MOVE_DOWN, false);
1628 break;
1630 default:
1631 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1632 return PLUGIN_USB_CONNECTED;
1633 break;
1635 #if defined(SOKOBAN_UNDO_PRE)
1636 lastbutton = button;
1637 #endif
1639 if (moved) {
1640 rb->lcd_clear_display();
1641 update_screen();
1644 /* We have completed this level */
1645 if (current_info.level.boxes_to_go == 0) {
1647 if (moved) {
1648 rb->lcd_clear_display();
1650 /* Show level complete message & stats */
1651 rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
1652 current_info.level.index + 1);
1653 rb->lcd_getstringsize(buf, &w, &h);
1654 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
1656 rb->snprintf(buf, sizeof(buf), "%4d Moves ",
1657 current_info.level.moves);
1658 rb->lcd_getstringsize(buf, &w, &h);
1659 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
1661 rb->snprintf(buf, sizeof(buf), "%4d Pushes",
1662 current_info.level.pushes);
1663 rb->lcd_getstringsize(buf, &w, &h);
1664 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
1666 if (undo_info.count < MAX_UNDOS) {
1667 rb->snprintf(buf, sizeof(buf), "%s: Save solution",
1668 BUTTON_SAVE_NAME);
1669 rb->lcd_getstringsize(buf, &w, &h);
1670 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
1673 rb->lcd_update();
1674 rb->sleep(HZ/4);
1675 rb->button_clear_queue();
1677 /* Display for 4 seconds or until new keypress */
1678 for (i = 0; i < 80; i++) {
1679 rb->sleep(HZ/20);
1680 button = rb->button_get(false);
1681 if (button && !(button & BUTTON_REL) &&
1682 !(button & BUTTON_REPEAT))
1683 break;
1686 if (button == BUTTON_SAVE) {
1687 if (undo_info.count < MAX_UNDOS) {
1688 /* Set filename to current levelset plus level number
1689 * and .sok extension. Use SAVE_FOLDER if using the
1690 * default levelset, since it's in a hidden folder. */
1691 if (rb->strcmp(buffered_boards.filename,
1692 SOKOBAN_LEVELS_FILE) == 0) {
1693 rb->snprintf(buf, sizeof(buf),
1694 "%s/sokoban.%d.sok",
1695 SOKOBAN_SAVE_FOLDER,
1696 current_info.level.index + 1);
1697 } else {
1698 if ((loc = rb->strrchr(buffered_boards.filename,
1699 '.')) != NULL)
1700 *loc = '\0';
1701 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1702 buffered_boards.filename,
1703 current_info.level.index + 1);
1704 if (loc != NULL)
1705 *loc = '.';
1708 if (!rb->kbd_input(buf, MAX_PATH))
1709 save(buf, true);
1710 } else
1711 rb->splash(HZ*2, "Solution too long to save");
1713 rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
1717 FOR_NB_SCREENS(i) {
1718 rb->screens[i]->clear_display();
1719 rb->screens[i]->update();
1722 current_info.level.index++;
1724 /* clear undo stats */
1725 init_undo();
1727 if (current_info.level.index >= current_info.max_level) {
1728 /* Show levelset complete message */
1729 rb->snprintf(buf, sizeof(buf), "You WIN!!");
1730 rb->lcd_getstringsize(buf, &w, &h);
1731 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
1733 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1734 /* Display for 4 seconds or until keypress */
1735 for (i = 0; i < 80; i++) {
1736 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1737 rb->lcd_update();
1738 rb->sleep(HZ/10);
1740 button = rb->button_get(false);
1741 if (button && !(button & BUTTON_REL))
1742 break;
1744 rb->lcd_set_drawmode(DRMODE_SOLID);
1746 /* Reset to first level & show quit menu */
1747 current_info.level.index = 0;
1749 switch (sokoban_menu()) {
1750 case 5: /* Quit */
1751 case 6: /* Save & quit */
1752 return PLUGIN_OK;
1756 load_level();
1757 update_screen();
1760 } /* end while */
1762 return PLUGIN_OK;
1766 enum plugin_status plugin_start(const void* parameter)
1768 int w, h;
1770 (void)(parameter);
1772 rb->lcd_setfont(SOKOBAN_FONT);
1774 rb->lcd_clear_display();
1775 rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
1776 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
1777 rb->lcd_update();
1778 rb->sleep(HZ); /* Show title for 1 second */
1780 init_boards();
1782 if (parameter == NULL) {
1783 /* Attempt to resume saved progress, otherwise start at beginning */
1784 if (!load(SOKOBAN_SAVE_FILE, true)) {
1785 init_boards();
1786 if (!read_levels(true))
1787 return PLUGIN_OK;
1788 load_level();
1791 } else {
1792 /* The plugin is being used to open a file */
1793 if (load((char*) parameter, false)) {
1794 /* If we loaded & played a solution, quit */
1795 if (current_info.level.boxes_to_go == 0)
1796 return PLUGIN_OK;
1797 } else
1798 return PLUGIN_OK;
1801 rb->lcd_clear_display();
1802 update_screen();
1804 return sokoban_loop();