summaryrefslogtreecommitdiffstats
path: root/apps/plugins
diff options
context:
space:
mode:
authorAntoine Cellerier <dionoea@videolan.org>2007-06-28 20:45:00 +0000
committerAntoine Cellerier <dionoea@videolan.org>2007-06-28 20:45:00 +0000
commitf91d06de7bf724e8e0aa580c18efa3eb345f88f9 (patch)
tree29c6446a4556cd074dbee3c3d97cdaae207ff228 /apps/plugins
parent7a1108227b67fb62f3d3447d795447b5b631ed32 (diff)
downloadrockbox-f91d06de7bf724e8e0aa580c18efa3eb345f88f9.tar.gz
rockbox-f91d06de7bf724e8e0aa580c18efa3eb345f88f9.zip
Apply FS #6702: More Sokoban Improvements.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13731 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/bitmaps/native/SOURCES8
-rw-r--r--apps/plugins/sokoban.c1355
-rw-r--r--apps/plugins/sokoban.levels1816
-rw-r--r--apps/plugins/sokobanlevels.sok1277
-rw-r--r--apps/plugins/viewers.config1
5 files changed, 2163 insertions, 2294 deletions
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 90a635ef01..00a6e3ab30 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -331,11 +331,13 @@ sokoban_tiles.14x14.bmp
#elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 212) || \
(LCD_HEIGHT >= 169) && (LCD_WIDTH >= 180-4)
sokoban_tiles.9x9.bmp
-#else
-/* LCD_HEIGHT >= 96~121 && LCD_WIDTH >= 152~120 */
+#elif (LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \
+ (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120)
sokoban_tiles.6x6.bmp
#endif
-#elif LCD_DEPTH >= 2
+#elif LCD_DEPTH >= 2 && \
+ ((LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \
+ (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120))
sokoban_tiles.6x6x2.bmp
#endif
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 40a2f549ad..976f552dbd 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -10,6 +10,7 @@
* Copyright (C) 2002 Eric Linenberg
* February 2003: Robert Hak performs a cleanup/rewrite/feature addition.
* Eric smiles. Bjorn cries. Linus say 'huh?'.
+ * March 2007: Sean Morrisey performs a major rewrite/feature addition.
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
@@ -19,95 +20,126 @@
*
****************************************************************************/
#include "plugin.h"
+#include "lib/playback_control.h"
#ifdef HAVE_LCD_BITMAP
PLUGIN_HEADER
-#if LCD_DEPTH >= 2
+#if LCD_DEPTH >= 2 && ((LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \
+ (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120))
extern const fb_data sokoban_tiles[];
#endif
-#define SOKOBAN_TITLE "Sokoban"
+#define SOKOBAN_TITLE "Sokoban"
-#define LEVELS_FILE PLUGIN_DIR "/sokoban.levels"
+#define SOKOBAN_LEVELS_FILE PLUGIN_DIR "/sokobanlevels.sok"
+#define SOKOBAN_SAVE_FILE PLUGIN_DIR "/sokobansave.sok"
-#define ROWS 16
-#define COLS 20
-#define SOKOBAN_LEVEL_SIZE (ROWS*COLS)
-#define MAX_BUFFERED_BOARDS 500
-/* Use either all but 12k of the plugin buffer for board data
- or just enough for MAX_BUFFERED_BOARDS, which ever is less */
-#if (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE < MAX_BUFFERED_BOARDS
-#define NUM_BUFFERED_BOARDS (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE
+/* Magnify is the number of pixels for each block.
+ * Set dynamically so all targets can support levels
+ * that fill their entire screen, less the stat box.
+ * 16 rows & 20 cols minimum */
+#if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 320)
+#define MAGNIFY 14
+#define ROWS (LCD_HEIGHT/MAGNIFY)
+#define COLS ((LCD_WIDTH-40)/MAGNIFY)
+#elif (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280)
+#define MAGNIFY 14
+#define ROWS ((LCD_HEIGHT-25)/MAGNIFY)
+#define COLS (LCD_WIDTH/MAGNIFY)
+#elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 220)
+#define MAGNIFY 9
+#define ROWS (LCD_HEIGHT/MAGNIFY)
+#define COLS ((LCD_WIDTH-40)/MAGNIFY)
+#elif (LCD_HEIGHT >= 169) && (LCD_WIDTH+4 >= 180) /* plus 4 for sansa */
+#define MAGNIFY 9
+#define ROWS ((LCD_HEIGHT-25)/MAGNIFY)
+#define COLS ((LCD_WIDTH+4)/MAGNIFY)
+#elif (LCD_HEIGHT >= 96) && (LCD_WIDTH >= 160)
+#define MAGNIFY 6
+#define ROWS (LCD_HEIGHT/MAGNIFY)
+#define COLS ((LCD_WIDTH-40)/MAGNIFY)
+#elif (LCD_HEIGHT >= 121) && (LCD_WIDTH >= 120)
+#define MAGNIFY 6
+#define ROWS ((LCD_HEIGHT-25)/MAGNIFY)
+#define COLS (LCD_WIDTH/MAGNIFY)
#else
-#define NUM_BUFFERED_BOARDS MAX_BUFFERED_BOARDS
+#define MAGNIFY 4
+#define ROWS 16
+#define COLS 20
#endif
-/* Use 4k plus remaining plugin buffer (-8k for prog) for undo, up to 32k */
-#if PLUGIN_BUFFER_SIZE - NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000 > \
- 0x7FFF
-#define MAX_UNDOS 0x7FFF
+
+/* Use either all but 16k of the plugin buffer for level data
+ * or 128k, which ever is less */
+#if PLUGIN_BUFFER_SIZE - 0x4000 < 0x20000
+#define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - 0x4000)
+#else
+#define MAX_LEVEL_DATA 0x20000
+#endif
+
+/* Number of levels for which to allocate buffer indexes */
+#define MAX_LEVELS MAX_LEVEL_DATA/70
+
+/* Use 4k plus remaining plugin buffer (-12k for prog) for undo, up to 64k */
+#if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000 > 0x10000
+#define MAX_UNDOS 0x10000
#else
-#define MAX_UNDOS PLUGIN_BUFFER_SIZE - \
- NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000
+#define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000)
#endif
/* Move/push definitions for undo */
-enum {
- SOKOBAN_PUSH_LEFT,
- SOKOBAN_PUSH_RIGHT,
- SOKOBAN_PUSH_UP,
- SOKOBAN_PUSH_DOWN,
- SOKOBAN_MOVE_LEFT,
- SOKOBAN_MOVE_RIGHT,
- SOKOBAN_MOVE_UP,
- SOKOBAN_MOVE_DOWN
-};
-#define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
-#define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_LEFT
+#define SOKOBAN_PUSH_LEFT 'L'
+#define SOKOBAN_PUSH_RIGHT 'R'
+#define SOKOBAN_PUSH_UP 'U'
+#define SOKOBAN_PUSH_DOWN 'D'
+#define SOKOBAN_MOVE_LEFT 'l'
+#define SOKOBAN_MOVE_RIGHT 'r'
+#define SOKOBAN_MOVE_UP 'u'
+#define SOKOBAN_MOVE_DOWN 'd'
+
+#define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
+#define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
/* variable button definitions */
-#if CONFIG_KEYPAD == RECORDER_PAD
+#if (CONFIG_KEYPAD == RECORDER_PAD) || \
+ (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_OFF
+#define SOKOBAN_MENU BUTTON_OFF
#define SOKOBAN_UNDO BUTTON_ON
#define SOKOBAN_REDO BUTTON_PLAY
-#define SOKOBAN_LEVEL_UP BUTTON_F3
#define SOKOBAN_LEVEL_DOWN BUTTON_F1
#define SOKOBAN_LEVEL_REPEAT BUTTON_F2
-
-#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
-#define SOKOBAN_UP BUTTON_UP
-#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_OFF
-#define SOKOBAN_UNDO BUTTON_ON
-#define SOKOBAN_REDO BUTTON_PLAY
#define SOKOBAN_LEVEL_UP BUTTON_F3
-#define SOKOBAN_LEVEL_DOWN BUTTON_F1
-#define SOKOBAN_LEVEL_REPEAT BUTTON_F2
+#define BUTTON_SAVE BUTTON_ON
+#define BUTTON_SAVE_NAME "ON"
#elif CONFIG_KEYPAD == ONDIO_PAD
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_OFF
+#define SOKOBAN_MENU BUTTON_OFF
#define SOKOBAN_UNDO_PRE BUTTON_MENU
#define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
#define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
-#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
#define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
#define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
+#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
+#define BUTTON_SAVE BUTTON_MENU
+#define BUTTON_SAVE_NAME "MENU"
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_OFF
+#define SOKOBAN_MENU BUTTON_OFF
#define SOKOBAN_UNDO BUTTON_REC
#define SOKOBAN_REDO BUTTON_MODE
-#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
#define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
#define SOKOBAN_LEVEL_REPEAT BUTTON_ON
+#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
+#define BUTTON_SAVE BUTTON_MODE
+#define BUTTON_SAVE_NAME "MODE"
#define SOKOBAN_RC_QUIT BUTTON_RC_STOP
@@ -115,65 +147,70 @@ enum {
(CONFIG_KEYPAD == IPOD_3G_PAD)
#define SOKOBAN_UP BUTTON_MENU
#define SOKOBAN_DOWN BUTTON_PLAY
-#define SOKOBAN_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
#define SOKOBAN_UNDO_PRE BUTTON_SELECT
#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
-#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
+#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
+#define BUTTON_SAVE BUTTON_SELECT
+#define BUTTON_SAVE_NAME "SELECT"
-/* fixme: if/when simultaneous button presses work for X5,
- add redo & level repeat */
-#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
+/* FIXME: if/when simultaneous button presses work for X5,
+ * add redo & level repeat */
+#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_POWER
+#define SOKOBAN_MENU BUTTON_POWER
#define SOKOBAN_UNDO_PRE BUTTON_SELECT
#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
-#define SOKOBAN_LEVEL_UP BUTTON_PLAY
#define SOKOBAN_LEVEL_DOWN BUTTON_REC
+#define SOKOBAN_LEVEL_UP BUTTON_PLAY
+#define BUTTON_SAVE BUTTON_SELECT
+#define BUTTON_SAVE_NAME "SELECT"
-#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define SOKOBAN_UP BUTTON_SCROLL_UP
+#define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
+#define SOKOBAN_MENU BUTTON_POWER
+#define SOKOBAN_UNDO_PRE BUTTON_REW
+#define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
+#define SOKOBAN_REDO BUTTON_FF
+#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
+#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
+#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
+#define BUTTON_SAVE BUTTON_PLAY
+#define BUTTON_SAVE_NAME "PLAY"
+
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_POWER
+#define SOKOBAN_MENU BUTTON_POWER
#define SOKOBAN_UNDO BUTTON_SELECT
#define SOKOBAN_REDO BUTTON_A
-#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
#define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
#define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
+#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
+#define BUTTON_SAVE BUTTON_SELECT
+#define BUTTON_SAVE_NAME "SELECT"
-#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
+#elif CONFIG_KEYPAD == SANSA_E200_PAD
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
-#define SOKOBAN_QUIT BUTTON_POWER
+#define SOKOBAN_MENU BUTTON_POWER
#define SOKOBAN_UNDO_PRE BUTTON_SELECT
#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
#define SOKOBAN_REDO BUTTON_REC
-#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
-
-#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
-#define SOKOBAN_UP BUTTON_SCROLL_UP
-#define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
-#define SOKOBAN_QUIT BUTTON_POWER
-#define SOKOBAN_UNDO_PRE BUTTON_REW
-#define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
-#define SOKOBAN_REDO BUTTON_FF
-#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
-#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
-#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
+#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
+#define BUTTON_SAVE BUTTON_SELECT
+#define BUTTON_SAVE_NAME "SELECT"
#endif
-#ifdef HAVE_LCD_COLOR
-/* Background color. Default Rockbox light blue. */
-#define BG_COLOR LCD_RGBPACK(181, 199, 231)
+#define SOKOBAN_FONT FONT_SYSFIXED
-#elif LCD_DEPTH >= 2
-#define MEDIUM_GRAY LCD_BRIGHTNESS(127)
-#endif
/* The Location, Undo and LevelInfo structs are OO-flavored.
* (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
@@ -181,10 +218,12 @@ enum {
/* Level data & stats */
struct LevelInfo {
- short level;
- short moves;
- short pushes;
- short boxes_to_go;
+ short index; /* Level index (level number - 1) */
+ int moves; /* Moves & pushes for the stats */
+ int pushes;
+ short boxes_to_go; /* Number of unplaced boxes remaining in level */
+ short height; /* Height & width for centering level display */
+ short width;
};
struct Location {
@@ -192,39 +231,42 @@ struct Location {
short col;
};
-struct Board {
- char spaces[ROWS][COLS];
-};
-
/* Our full undo history */
static struct UndoInfo {
- short count; /* How many undos are left */
- short current; /* Which history is the current undo */
- short max; /* Which history is the max redoable */
+ int count; /* How many undos have been done */
+ int current; /* Which history is the current undo */
+ int max; /* Which history is the max redoable */
char history[MAX_UNDOS];
} undo_info;
/* Our playing board */
static struct BoardInfo {
- char board[ROWS][COLS];
- struct LevelInfo level;
- struct Location player;
- int max_level; /* How many levels do we have? */
- int loaded_level; /* Which level is in memory */
+ char board[ROWS][COLS]; /* The current board data */
+ struct LevelInfo level; /* Level data & stats */
+ struct Location player; /* Where the player is */
+ int max_level; /* The number of levels we have */
} current_info;
static struct BufferedBoards {
- struct Board levels[NUM_BUFFERED_BOARDS];
- int low;
+ char filename[MAX_PATH]; /* Filename of the levelset we're using */
+ char data[MAX_LEVEL_DATA]; /* Buffered level data */
+ int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
+ int start; /* Index of first buffered board */
+ int end; /* Index of last buffered board */
+ short prebuffered_boards; /* Number of boards before current to store */
} buffered_boards;
+
static struct plugin_api* rb;
+static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
+
+
static void init_undo(void)
{
undo_info.count = 0;
- undo_info.current = -1;
- undo_info.max = -1;
+ undo_info.current = 0;
+ undo_info.max = 0;
}
static void get_delta(char direction, short *d_r, short *d_c)
@@ -261,9 +303,17 @@ static void undo(void)
bool undo_push = false;
/* If no more undos or we've wrapped all the way around, quit */
- if (undo_info.count == 0 || undo_info.current-1 == undo_info.max)
+ if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
return;
+ /* Move to previous undo in the list */
+ if (undo_info.current == 0 && undo_info.count > 1)
+ undo_info.current = MAX_UNDOS - 1;
+ else
+ undo_info.current--;
+
+ undo_info.count--;
+
undo = undo_info.history[undo_info.current];
if (undo < SOKOBAN_MOVE_MIN)
@@ -281,17 +331,17 @@ static void undo(void)
/* Update board info */
if (undo_push) {
- /* Moving box from goal to blank */
- if (*space_next == '%' && *space_cur == '@')
+ /* Moving box from goal to floor */
+ if (*space_next == '*' && *space_cur == '@')
current_info.level.boxes_to_go++;
- /* Moving box from blank to goal */
+ /* Moving box from floor to goal */
else if (*space_next == '$' && *space_cur == '+')
current_info.level.boxes_to_go--;
/* Move box off of next space... */
- *space_next = (*space_next == '%' ? '.' : ' ');
+ *space_next = (*space_next == '*' ? '.' : ' ');
/* ...and on to current space */
- *space_cur = (*space_cur == '+' ? '%' : '$');
+ *space_cur = (*space_cur == '+' ? '*' : '$');
current_info.level.pushes--;
} else
@@ -306,27 +356,19 @@ static void undo(void)
current_info.level.moves--;
- /* Move to previous undo in the list */
- if (undo_info.current == 0 && undo_info.count > 1)
- undo_info.current = MAX_UNDOS - 1;
- else
- undo_info.current--;
-
- undo_info.count--;
-
return;
}
static void add_undo(char undo)
{
+ undo_info.history[undo_info.current] = undo;
+
/* Wrap around if MAX_UNDOS exceeded */
if (undo_info.current < (MAX_UNDOS - 1))
undo_info.current++;
else
undo_info.current = 0;
- undo_info.history[undo_info.current] = undo;
-
if (undo_info.count < MAX_UNDOS)
undo_info.count++;
}
@@ -353,34 +395,37 @@ static bool move(char direction, bool redo)
space_next = &current_info.board[r + d_r][c + d_c];
space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
- if (*space_next == '$' || *space_next == '%') {
+ if (*space_next == '$' || *space_next == '*') {
/* Change direction from move to push for undo */
if (direction >= SOKOBAN_MOVE_MIN)
direction -= SOKOBAN_MOVE_DIFF;
push = true;
}
+ else if (direction < SOKOBAN_MOVE_MIN)
+ /* Change back to move if redo/solution playback push is invalid */
+ direction += SOKOBAN_MOVE_DIFF;
/* Update board info */
if (push) {
- /* Moving box from goal to blank */
- if (*space_next == '%' && *space_beyond == ' ')
+ /* Moving box from goal to floor */
+ if (*space_next == '*' && *space_beyond == ' ')
current_info.level.boxes_to_go++;
- /* Moving box from blank to goal */
+ /* Moving box from floor to goal */
else if (*space_next == '$' && *space_beyond == '.')
current_info.level.boxes_to_go--;
- /* Check for illegal move */
+ /* Check for invalid move */
else if (*space_beyond != '.' && *space_beyond != ' ')
return false;
/* Move player onto next space */
- *space_next = (*space_next == '%' ? '+' : '@');
+ *space_next = (*space_next == '*' ? '+' : '@');
/* Move box onto space beyond next */
- *space_beyond = (*space_beyond == '.' ? '%' : '$');
+ *space_beyond = (*space_beyond == '.' ? '*' : '$');
current_info.level.pushes++;
} else {
- /* Check for illegal move */
- if (*space_next == '#' || *space_next == 'X')
+ /* Check for invalid move */
+ if (*space_next != '.' && *space_next != ' ')
return false;
/* Move player onto next space */
@@ -396,19 +441,19 @@ static bool move(char direction, bool redo)
current_info.level.moves++;
/* Update undo_info.max to current on every normal move,
- except if it's the same as a redo. */
- /* normal move */
+ * except if it's the same as a redo. */
+ /* normal move and either */
if (!redo &&
- /* moves have been undone */
+ /* moves have been undone... */
((undo_info.max != undo_info.current &&
- /* and the current move is NOT the same as the one in history */
- undo_info.history[undo_info.current+1] != direction) ||
+ /* ...and the current move is NOT the same as the one in history */
+ undo_info.history[undo_info.current] != direction) ||
/* or moves have not been undone */
undo_info.max == undo_info.current)) {
add_undo(direction);
undo_info.max = undo_info.current;
} else /* redo move or move was same as redo */
- add_undo(direction); /* (just to update current) */
+ add_undo(direction); /* add_undo to update current */
return true;
}
@@ -420,290 +465,370 @@ static bool redo(void)
if (undo_info.current == undo_info.max)
return false;
- return move(undo_info.history[(undo_info.current+1 < MAX_UNDOS ?
- undo_info.current+1 : 0)], true);
+ return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
+ undo_info.current : 0)], true);
}
#endif
static void init_boards(void)
{
- current_info.level.level = 0;
- current_info.level.moves = 0;
- current_info.level.pushes = 0;
- current_info.level.boxes_to_go = 0;
+ rb->strncpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE, MAX_PATH);
+
+ current_info.level.index = 0;
current_info.player.row = 0;
current_info.player.col = 0;
current_info.max_level = 0;
- current_info.loaded_level = 0;
- buffered_boards.low = 0;
+ buffered_boards.start = 0;
+ buffered_boards.end = 0;
+ buffered_boards.prebuffered_boards = 0;
init_undo();
}
-static int read_levels(int initialize_count)
+static bool read_levels(bool initialize)
{
int fd = 0;
- int len;
- int lastlen = 0;
- int row = 0;
+ short len;
+ short lastlen = 0;
+ short row = 0;
int level_count = 0;
- char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */
- int endpoint = current_info.level.level-1;
-
- if (endpoint < buffered_boards.low)
- endpoint = current_info.level.level - NUM_BUFFERED_BOARDS;
- if (endpoint < 0) endpoint = 0;
+ int i = 0;
+ int level_len = 0;
+ bool index_set = false;
- buffered_boards.low = endpoint;
- endpoint += NUM_BUFFERED_BOARDS;
+ /* Get the index of the first level to buffer */
+ if (current_info.level.index > buffered_boards.prebuffered_boards &&
+ !initialize)
+ buffered_boards.start = current_info.level.index -
+ buffered_boards.prebuffered_boards;
+ else
+ buffered_boards.start = 0;
- if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) {
- rb->splash(HZ*2, "Unable to open %s", LEVELS_FILE);
- return -1;
+ if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
+ rb->splash(HZ*2, "Unable to open %s", buffered_boards.filename);
+ return false;
}
do {
- len = rb->read_line(fd, buffer, sizeof(buffer));
- if (len >= 3) {
- /* This finds lines that are more than 1 or 2 characters
- * shorter than they should be. Due to the possibility of
- * a mixed unix and dos CR/LF file format, I'm not going to
- * do a precise check */
- if (len < COLS) {
- rb->splash(HZ*2, "Error in levels file: short line");
- return -1;
- }
- if (level_count >= buffered_boards.low && level_count < endpoint) {
- int index = level_count - buffered_boards.low;
- rb->memcpy(
- buffered_boards.levels[index].spaces[row], buffer, COLS);
+ len = rb->read_line(fd, buf, sizeof(buf));
+
+ /* Correct len when trailing \r's or \n's are counted */
+ if (len > 2 && buf[len - 2] == '\0')
+ len -= 2;
+ else if (len > 1 && buf[len - 1] == '\0')
+ len--;
+
+ /* Skip short lines & lines with non-level data */
+ if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
+ buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
+ if (level_count >= buffered_boards.start) {
+ /* Set the index of this level */
+ if (!index_set &&
+ level_count - buffered_boards.start < MAX_LEVELS) {
+ buffered_boards.index[level_count - buffered_boards.start]
+ = i;
+ index_set = true;
+ }
+ /* Copy buffer to board data */
+ if (i + level_len + len < MAX_LEVEL_DATA) {
+ rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
+ buffered_boards.data[i + level_len + len] = '\n';
+ }
}
+ level_len += len + 1;
row++;
- } else if (len) {
- if (lastlen < 3) {
- /* Two short lines in a row means new level */
- level_count++;
- if (level_count >= endpoint && !initialize_count) break;
- if (level_count && row != ROWS) {
- rb->splash(HZ*2, "Error in levels file: short board");
- return -1;
- }
- row = 0;
+
+ /* If newline & level is tall enough or is RLE */
+ } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
+ level_count++;
+ if (level_count >= buffered_boards.start) {
+ i += level_len;
+ if (i < MAX_LEVEL_DATA)
+ buffered_boards.end = level_count;
+ else if (!initialize)
+ break;
}
- }
- } while ((lastlen=len));
+ row = 0;
+ level_len = 0;
+ index_set = false;
- rb->close(fd);
- if (initialize_count) {
- /* Plus one because there aren't trailing short lines in the file */
- current_info.max_level = level_count + 1;
+ } else if (len > 22)
+ len = 1;
+
+ } while ((lastlen = len));
+
+ /* Set the index of the end of the last level */
+ if (level_count - buffered_boards.start < MAX_LEVELS)
+ buffered_boards.index[level_count - buffered_boards.start] = i;
+
+ if (initialize) {
+ current_info.max_level = level_count;
+ buffered_boards.prebuffered_boards = buffered_boards.end/2;
}
- return 0;
+
+ rb->close(fd);
+
+ return true;
}
-/* return non-zero on error */
static void load_level(void)
{
- int c = 0;
- int r = 0;
- int index = current_info.level.level - buffered_boards.low - 1;
- struct Board *level;
-
- if (index < 0 || index >= NUM_BUFFERED_BOARDS) {
+ int c, r;
+ int i, n;
+ int level_size;
+ int index = current_info.level.index - buffered_boards.start;
+ char *level;
+
+ /* Get the buffered board index of the current level */
+ if (current_info.level.index < buffered_boards.start ||
+ current_info.level.index >= buffered_boards.end) {
read_levels(false);
- index = index < 0 ? NUM_BUFFERED_BOARDS-1 : 0;
+ if (current_info.level.index > buffered_boards.prebuffered_boards)
+ index = buffered_boards.prebuffered_boards;
+ else
+ index = current_info.level.index;
}
- level = &buffered_boards.levels[index];
+ level = &buffered_boards.data[buffered_boards.index[index]];
- current_info.level.boxes_to_go = 0;
+ /* Reset level info */
current_info.level.moves = 0;
current_info.level.pushes = 0;
- current_info.loaded_level = current_info.level.level;
+ current_info.level.boxes_to_go = 0;
+ current_info.level.width = 0;
+
+ /* Clear board */
+ for (r = 0; r < ROWS; r++)
+ for (c = 0; c < COLS; c++)
+ current_info.board[r][c] = 'X';
+
+ level_size = buffered_boards.index[index + 1] -
+ buffered_boards.index[index];
+
+ for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
+ if (level[i] == '\n' || level[i] == '|') {
+ if (c > 3) {
+ /* Update max width of level & go to next row */
+ if (c > current_info.level.width)
+ current_info.level.width = c;
+ c = 0;
+ r++;
+ if (r >= ROWS)
+ break;
+ }
+ } else if (c < COLS) {
+ /* Read RLE character's length into n */
+ if (level[i] >= '0' && level[i] <= '9') {
+ n = level[i++] - '0';
+ if (level[i] >= '0' && level[i] <= '9')
+ n = n*10 + level[i++] - '0';
+ }
+
+ /* Cleanup & replace */
+ if (level[i] == '%')
+ level[i] = '*';
+ else if (level[i] == '-' || level[i] == '_')
+ level[i] = ' ';
+
+ if (n > 1) {
+ if (c + n >= COLS)
+ n = COLS - c;
- for (r = 0; r < ROWS; r++) {
- for (c = 0; c < COLS; c++) {
- current_info.board[r][c] = level->spaces[r][c];
+ if (level[i] == '.')
+ current_info.level.boxes_to_go += n;
- if (current_info.board[r][c] == '.' ||
- current_info.board[r][c] == '+')
- current_info.level.boxes_to_go++;
+ /* Put RLE character n times */
+ while (n--)
+ current_info.board[r][c++] = level[i];
+ n = 1;
- if (current_info.board[r][c] == '@' ||
- current_info.board[r][c] == '+') {
- current_info.player.row = r;
- current_info.player.col = c;
+ } else {
+ if (level[i] == '.' || level[i] == '+')
+ current_info.level.boxes_to_go++;
+
+ if (level[i] == '@' ||level[i] == '+') {
+ current_info.player.row = r;
+ current_info.player.col = c;
+ }
+
+ current_info.board[r][c++] = level[i];
}
}
}
+
+ current_info.level.height = r;
+
+#if LCD_DEPTH > 2
+ /* Fill in blank space outside level on color targets */
+ for (r = 0; r < ROWS; r++)
+ for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
+ current_info.board[r][c] = 'X';
+
+ for (c = 0; c < COLS; c++) {
+ for (r = 0; (current_info.board[r][c] == ' ' ||
+ current_info.board[r][c] == 'X') && r < ROWS; r++)
+ current_info.board[r][c] = 'X';
+ for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
+ current_info.board[r][c] == 'X') && r >= 0; r--)
+ current_info.board[r][c] = 'X';
+ }
+#endif
}
static void update_screen(void)
{
- int b = 0, c = 0;
- int rows = 0, cols = 0;
- char s[25];
-
-/* magnify is the number of pixels for each block */
-#if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 312) || \
- (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280) /* ipod 5g */
-#define MAGNIFY 14
-#elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 212) || \
- (LCD_HEIGHT >= 169) && (LCD_WIDTH >= 180-4) /* h3x0, ipod color/photo */
-#define MAGNIFY 9
-#elif (LCD_HEIGHT >= 96) && (LCD_WIDTH >= 152) || \
- (LCD_HEIGHT >= 121) && (LCD_WIDTH >= 120) /* h1x0, ipod nano/mini */
-#define MAGNIFY 6
-#else /* other */
-#define MAGNIFY 4
-#endif
+ int c, r;
+ int rows, cols;
-#if LCD_DEPTH < 2
+#if LCD_DEPTH < 2 || ((LCD_HEIGHT < 96 || LCD_WIDTH < 152) && \
+ (LCD_HEIGHT < 121 || LCD_WIDTH < 120))
int i, j;
int max = MAGNIFY - 1;
- int middle = max / 2;
- int ldelta = (middle + 1) / 2;
+ int middle = max/2;
+ int ldelta = (middle + 1)/2;
#endif
- /* load the board to the screen */
- for (rows=0; rows < ROWS; rows++) {
- for (cols = 0; cols < COLS; cols++) {
- c = cols * MAGNIFY;
- b = rows * MAGNIFY;
-
- switch(current_info.board[rows][cols]) {
- case 'X': /* black space */
- break;
-
- case '#': /* this is a wall */
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 1*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY);
+#if LCD_WIDTH - (COLS*MAGNIFY) < 32
+#define STAT_HEIGHT 25
+#define STAT_X (LCD_WIDTH - 120)/2
+#define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
+#define BOARD_WIDTH LCD_WIDTH
+#define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
+ rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
+ rb->lcd_putsxy(STAT_X + 7, STAT_Y + 14, buf);
+ rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
+ rb->lcd_putsxy(STAT_X + 44, STAT_Y + 14, buf);
+ rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
+ rb->lcd_putsxy(STAT_X + 82, STAT_Y + 14, buf);
+
+ rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
+ rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
+ rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
#else
- for (i = c; i < c + MAGNIFY; i++)
- for (j = b; j < b + MAGNIFY; j++)
- if ((i ^ j) & 1)
- rb->lcd_drawpixel(i, j);
+#if LCD_WIDTH - (COLS*MAGNIFY) > 40
+#define STAT_X (LCD_WIDTH - 40)
+#else
+#define STAT_X COLS*MAGNIFY
#endif
- break;
-
- case '$': /* this is a box */
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 2*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY);
+#if LCD_HEIGHT >= 70
+#define STAT_Y (LCD_HEIGHT - 70)/2
#else
- /* Free boxes are not filled in */
- rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY);
+#define STAT_Y (LCD_HEIGHT - 47)/2
#endif
- break;
+#define STAT_WIDTH (LCD_WIDTH - STAT_X)
+#define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
+#define BOARD_HEIGHT LCD_HEIGHT
+ rb->lcd_putsxy(STAT_X + 1, STAT_Y + 3, "Level");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
+ rb->lcd_putsxy(STAT_X + 4, STAT_Y + 13, buf);
+ rb->lcd_putsxy(STAT_X + 1, STAT_Y + 26, "Moves");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
+ rb->lcd_putsxy(STAT_X + 4, STAT_Y + 36, buf);
+
+ rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 24);
+ rb->lcd_drawrect(STAT_X, STAT_Y + 23, STAT_WIDTH, 24);
- case '*':
- case '%': /* this is a box on a goal */
+#if LCD_HEIGHT >= 70
+ rb->lcd_putsxy(STAT_X + 1, STAT_Y + 49, "Pushes");
+ rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
+ rb->lcd_putsxy(STAT_X + 4, STAT_Y + 59, buf);
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY );
-#else
- rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY);
- rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2,
- MAGNIFY/2);
+ rb->lcd_drawrect(STAT_X, STAT_Y + 46, STAT_WIDTH, 24);
#endif
- break;
- case '.': /* this is a goal */
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 4*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY);
-#else
- rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2,
- MAGNIFY/2);
#endif
- break;
- case '@': /* this is you */
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 5*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY);
-#else
- rb->lcd_drawline(c, b+middle, c+max, b+middle);
- rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta);
- rb->lcd_drawline(c+max-middle, b,
- c+max-middle, b+max-ldelta);
- rb->lcd_drawline(c+middle, b+max-ldelta,
- c+middle-ldelta, b+max);
- rb->lcd_drawline(c+max-middle, b+max-ldelta,
- c+max-middle+ldelta, b+max);
-#endif
- break;
+ /* load the board to the screen */
+ for (rows = 0; rows < ROWS; rows++) {
+ for (cols = 0; cols < COLS; cols++) {
+ c = cols*MAGNIFY +
+ (BOARD_WIDTH - current_info.level.width*MAGNIFY)/2;
+ r = rows*MAGNIFY +
+ (BOARD_HEIGHT - current_info.level.height*MAGNIFY)/2;
- case '+': /* this is you on drugs, erm, on a goal */
-#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part(sokoban_tiles, 0, 6*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY );
-#else
- rb->lcd_drawline(c, b+middle, c+max, b+middle);
- rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta);
- rb->lcd_drawline(c+max-middle, b, c+max-middle, b+max-ldelta);
- rb->lcd_drawline(c+middle, b+max-ldelta, c+middle-ldelta,
- b+max);
- rb->lcd_drawline(c+max-middle, b+max-ldelta,
- c+max-middle+ldelta, b+max);
- rb->lcd_drawline(c+middle-1, b+middle+1, c+max-middle+1,
- b+middle+1);
-#endif
- break;
+ switch(current_info.board[rows][cols]) {
+ case 'X': /* blank space outside of level */
+ break;
-#if LCD_DEPTH >= 2
- default:
- rb->lcd_bitmap_part(sokoban_tiles, 0, 0*MAGNIFY, MAGNIFY,
- c, b, MAGNIFY, MAGNIFY );
-#endif
- }
- }
- }
+#if LCD_DEPTH >= 2 && ((LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \
+ (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120))
+ case ' ': /* floor */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 0*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
-#if LCD_WIDTH-(COLS*MAGNIFY) < 32
-#define STAT_SIZE 25
-#define STAT_POS LCD_HEIGHT-STAT_SIZE
-#define STAT_CENTER (LCD_WIDTH-120)/2
-
- rb->lcd_putsxy(4+STAT_CENTER, STAT_POS+4, "Level");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.level);
- rb->lcd_putsxy(7+STAT_CENTER, STAT_POS+14, s);
- rb->lcd_putsxy(41+STAT_CENTER, STAT_POS+4, "Moves");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.moves);
- rb->lcd_putsxy(44+STAT_CENTER, STAT_POS+14, s);
- rb->lcd_putsxy(79+STAT_CENTER, STAT_POS+4, "Pushes");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes);
- rb->lcd_putsxy(82+STAT_CENTER, STAT_POS+14, s);
-
- rb->lcd_drawrect(STAT_CENTER, STAT_POS, 38, STAT_SIZE);
- rb->lcd_drawrect(37+STAT_CENTER, STAT_POS, 39, STAT_SIZE);
- rb->lcd_drawrect(75+STAT_CENTER, STAT_POS, 45, STAT_SIZE);
+ case '#': /* wall */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 1*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
+
+ case '$': /* box */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 2*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
+ case '*': /* box on goal */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
+
+ case '.': /* goal */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 4*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
+
+ case '@': /* player */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 5*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
+
+ case '+': /* player on goal */
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 6*MAGNIFY, MAGNIFY,
+ c, r, MAGNIFY, MAGNIFY);
+ break;
#else
-#define STAT_POS COLS*MAGNIFY
-#define STAT_SIZE LCD_WIDTH-STAT_POS
+ case '#': /* wall */
+ for (i = c; i < c + MAGNIFY; i++)
+ for (j = r; j < r + MAGNIFY; j++)
+ if ((i ^ j) & 1)
+ rb->lcd_drawpixel(i, j);
+ break;
- rb->lcd_putsxy(STAT_POS+1, 3, "Level");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.level);
- rb->lcd_putsxy(STAT_POS+4, 13, s);
- rb->lcd_putsxy(STAT_POS+1, 26, "Moves");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.moves);
- rb->lcd_putsxy(STAT_POS+4, 36, s);
+ case '$': /* box */
+ rb->lcd_drawrect(c, r, MAGNIFY, MAGNIFY);
+ break;
- rb->lcd_drawrect(STAT_POS, 0, STAT_SIZE, 24);
- rb->lcd_drawrect(STAT_POS, 23, STAT_SIZE, 24);
+ case '*': /* box on goal */
+ rb->lcd_drawrect(c, r, MAGNIFY, MAGNIFY);
+ rb->lcd_drawrect(c + MAGNIFY/2 - 1, r + MAGNIFY/2 - 1,
+ MAGNIFY/2, MAGNIFY/2);
+ break;
-#if LCD_HEIGHT >= 70
- rb->lcd_putsxy(STAT_POS+1, 49, "Pushes");
- rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes);
- rb->lcd_putsxy(STAT_POS+4, 59, s);
+ case '.': /* goal */
+ rb->lcd_drawrect(c + MAGNIFY/2 - 1, r + MAGNIFY/2 - 1,
+ MAGNIFY/2, MAGNIFY/2);
+ break;
- rb->lcd_drawrect(STAT_POS, 46, STAT_SIZE, 24);
-#endif
+ case '@': /* player */
+ case '+': /* player on goal */
+ rb->lcd_drawline(c, r + middle, c + max, r + middle);
+ rb->lcd_drawline(c + middle, r, c + middle,
+ r + max - ldelta);
+ rb->lcd_drawline(c + max - middle, r, c + max - middle,
+ r + max - ldelta);
+ rb->lcd_drawline(c + middle, r + max - ldelta,
+ c + middle - ldelta, r + max);
+ rb->lcd_drawline(c + max - middle, r + max - ldelta,
+ c + max - middle + ldelta, r + max);
+ break;
#endif
+ }
+ }
+ }
/* print out the screen */
rb->lcd_update();
@@ -716,21 +841,320 @@ static void draw_level(void)
update_screen();
}
+static bool save(char *filename, bool solution)
+{
+ int fd;
+
+ rb->splash(0, "Saving...");
+
+ if (filename[0] == '\0' ||
+ (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
+ rb->splash(HZ*2, "Unable to open %s", filename);
+ return false;
+ }
+
+ /* Sokoban: S/P for solution/progress : level number : current undo */
+ rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
+ current_info.level.index + 1, undo_info.current);
+ rb->write(fd, buf, rb->strlen(buf));
+
+ /* Filename of levelset */
+ rb->write(fd, buffered_boards.filename,
+ rb->strlen(buffered_boards.filename));
+ rb->write(fd, "\n", 1);
+
+ /* Full undo history */
+ rb->write(fd, undo_info.history, undo_info.max);
+
+ rb->close(fd);
+
+ return true;
+}
+
+static bool load(char *filename, bool silent)
+{
+ int fd;
+ int i = 0, n;
+ int len;
+ bool play_solution;
+ int button;
+ int step_delay = HZ/4;
+
+ if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
+ if (!silent)
+ rb->splash(HZ*2, "Unable to open %s", filename);
+ return false;
+ }
+
+ /* Read header, level number, & current undo */
+ rb->read_line(fd, buf, sizeof(buf));
+
+ /* If we're opening a level file, not a solution/progress file */
+ if (rb->strncmp(buf, "Sokoban", 7) != 0) {
+ rb->close(fd);
+
+ rb->strncpy(buffered_boards.filename, filename, MAX_PATH);
+ if (!read_levels(true))
+ return false;
+
+ current_info.level.index = 0;
+ load_level();
+
+ /* If there aren't any boxes to go or the player position wasn't set,
+ * the file probably wasn't a Sokoban level file */
+ if (current_info.level.boxes_to_go == 0 ||
+ current_info.player.row == 0 || current_info.player.col == 0) {
+ if (!silent)
+ rb->splash(HZ*2, "File is not a Sokoban level file");
+ return false;
+ }
+
+ } else {
+
+ /* Read filename of levelset */
+ rb->read_line(fd, buffered_boards.filename,
+ sizeof(buffered_boards.filename));
+
+ /* Read full undo history */
+ len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
+
+ /* Correct len when trailing \r's or \n's are counted */
+ if (len > 2 && undo_info.history[len - 2] == '\0')
+ len -= 2;
+ else if (len > 1 && undo_info.history[len - 1] == '\0')
+ len--;
+
+ rb->close(fd);
+
+ /* Check to see if we're going to play a solution or resume progress */
+ play_solution = (buf[8] == 'S');
+
+ /* Get level number */
+ for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
+ n = n*10 + buf[i] - '0';
+ current_info.level.index = n - 1;
+
+ /* Get current undo index */
+ for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
+ n = n*10 + buf[i] - '0';
+ if (n > len)
+ n = len;
+
+ if (current_info.level.index < 0) {
+ if (!silent)
+ rb->splash(HZ*2, "Error loading level");
+ return false;
+ }
+ if (!read_levels(true))
+ return false;
+ if (current_info.level.index >= current_info.max_level) {
+ if (!silent)
+ rb->splash(HZ*2, "Error loading level");
+ return false;
+ }
+
+ load_level();
+
+ if (play_solution) {
+ rb->lcd_clear_display();
+ update_screen();
+ rb->sleep(2*step_delay);
+
+ /* Replay solution until the end or quit button is pressed */
+ for (i = 0; i < len; i++) {
+ if (!move(undo_info.history[i], true)) {
+ n = i;
+ break;
+ }
+
+ rb->lcd_clear_display();
+ update_screen();
+ rb->sleep(step_delay);
+
+ /* Ignore keypresses except for quit & changing speed */
+ while ((button = rb->button_get(false)) != BUTTON_NONE) {
+ switch (button) {
+ case SOKOBAN_MENU:
+ /* Pretend the level is complete so we'll quit */
+ current_info.level.boxes_to_go = 0;
+ return true;
+
+ case SOKOBAN_UP:
+ if (step_delay > HZ/12)
+ step_delay = 5*step_delay/6;
+ break;
+
+ case SOKOBAN_DOWN:
+ if (step_delay < 3*HZ/4)
+ step_delay = 6*step_delay/5;
+ }
+ }
+ }
+
+ /* If level complete, wait for keypress before quitting */
+ if (current_info.level.boxes_to_go == 0)
+ rb->button_get(true);
+
+ } else {
+ /* Advance to current undo */
+ for (i = 0; i < n; i++) {
+ if (!move(undo_info.history[i], true)) {
+ n = i;
+ break;
+ }
+ }
+
+ rb->button_clear_queue();
+ rb->lcd_clear_display();
+ }
+
+ undo_info.max = len;
+ undo_info.current = n;
+ }
+
+ return true;
+}
+
+static int sokoban_menu(void)
+{
+ int button;
+ int selection = 0;
+ int i;
+ bool menu_quit;
+ int start_selected = 0;
+
+ MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
+ "Resume", "Audio Playback", "Keys",
+ "Load Default Level Set", "Quit Without Saving",
+ "Save Progress & Quit");
+
+ do {
+ menu_quit = true;
+ selection = rb->do_menu(&menu, &start_selected);
+
+ switch (selection) {
+ case 0: /* Resume */
+ break;
+
+ case 1: /* Audio playback control */
+ playback_control(rb);
+ menu_quit = false;
+ break;
+
+ case 2: /* Keys */
+ FOR_NB_SCREENS(i)
+ rb->screens[i]->clear_display();
+ rb->lcd_setfont(SOKOBAN_FONT);
+
+#if (CONFIG_KEYPAD == RECORDER_PAD) || \
+ (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
+ rb->lcd_putsxy(3, 6, "[OFF] Menu");
+ rb->lcd_putsxy(3, 16, "[ON] Undo");
+ rb->lcd_putsxy(3, 26, "[PLAY] Redo");
+ rb->lcd_putsxy(3, 36, "[F1] Down a Level");
+ rb->lcd_putsxy(3, 46, "[F2] Restart Level");
+ rb->lcd_putsxy(3, 56, "[F3] Up a Level");
+#elif CONFIG_KEYPAD == ONDIO_PAD
+ rb->lcd_putsxy(3, 6, "[OFF] Menu");
+ rb->lcd_putsxy(3, 16, "[MODE] Undo");
+ rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
+ rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
+ rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
+ rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
+#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+ rb->lcd_putsxy(3, 6, "[STOP] Menu");
+ rb->lcd_putsxy(3, 16, "[REC] Undo");
+ rb->lcd_putsxy(3, 26, "[MODE] Redo");
+ rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
+ rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
+ rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD)
+ rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
+ rb->lcd_putsxy(3, 16, "[SELECT] Undo");
+ rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
+ rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
+ rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
+#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
+ rb->lcd_putsxy(3, 6, "[POWER] Menu");
+ rb->lcd_putsxy(3, 16, "[SELECT] Undo");
+ rb->lcd_putsxy(3, 26, "[REC] Previous Level");
+ rb->lcd_putsxy(3, 36, "[PLAY] Next Level");
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+ rb->lcd_putsxy(3, 6, "[POWER] Menu");
+ rb->lcd_putsxy(3, 16, "[REW] Undo");
+ rb->lcd_putsxy(3, 26, "[FF] Redo");
+ rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
+ rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
+ rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
+ rb->lcd_putsxy(3, 6, "[POWER] Menu");
+ rb->lcd_putsxy(3, 16, "[SELECT] Undo");
+ rb->lcd_putsxy(3, 26, "[A] Redo");
+ rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
+ rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
+ rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
+#elif CONFIG_KEYPAD == SANSA_E200_PAD
+ rb->lcd_putsxy(3, 6, "[POWER] Menu");
+ rb->lcd_putsxy(3, 16, "[SELECT] Undo");
+ rb->lcd_putsxy(3, 26, "[REC] Redo");
+ rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
+ rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
+ rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
+#endif
+
+ FOR_NB_SCREENS(i)
+ rb->screens[i]->update();
+
+ /* Display until keypress */
+ do {
+ rb->sleep(HZ/20);
+ button = rb->button_get(false);
+ } while (!button || button & BUTTON_REL ||
+ button & BUTTON_REPEAT);
+
+ menu_quit = false;
+ break;
+
+ case 3: /* Load default levelset */
+ init_boards();
+ if (!read_levels(true))
+ return 4;
+ load_level();
+ break;
+
+ case 4: /* Quit */
+ break;
+
+ case 5: /* Save & quit */
+ save(SOKOBAN_SAVE_FILE, false);
+ rb->reload_directory();
+ }
+
+ } while (!menu_quit);
+
+ /* Restore font */
+ rb->lcd_setfont(SOKOBAN_FONT);
+
+ FOR_NB_SCREENS(i) {
+ rb->screens[i]->clear_display();
+ rb->screens[i]->update();
+ }
+
+ return selection;
+}
+
static bool sokoban_loop(void)
{
- bool moved = true;
+ bool moved;
int i = 0, button = 0, lastbutton = 0;
short r = 0, c = 0;
int w, h;
- char s[25];
+ char *loc;
- current_info.level.level = 1;
-
- load_level();
- update_screen();
-
- while (1) {
- moved = true;
+ while (true) {
+ moved = false;
r = current_info.player.row;
c = current_info.player.col;
@@ -742,24 +1166,25 @@ static bool sokoban_loop(void)
#ifdef SOKOBAN_RC_QUIT
case SOKOBAN_RC_QUIT:
#endif
- case SOKOBAN_QUIT:
- /* get out of here */
-#ifdef HAVE_LCD_COLOR /* reset background color */
- rb->lcd_set_background(rb->global_settings->bg_color);
-#endif
- return PLUGIN_OK;
+ case SOKOBAN_MENU:
+ switch (sokoban_menu()) {
+ case 4: /* Quit */
+ case 5: /* Save & quit */
+ return PLUGIN_OK;
+ }
+ update_screen();
+ break;
case SOKOBAN_UNDO:
#ifdef SOKOBAN_UNDO_PRE
if (lastbutton != SOKOBAN_UNDO_PRE)
break;
-#else /* repeat can't work here for Ondio et al */
+#else /* repeat can't work here for Ondio, iPod, et al */
case SOKOBAN_UNDO | BUTTON_REPEAT:
#endif
undo();
rb->lcd_clear_display();
update_screen();
- moved = false;
break;
#ifdef SOKOBAN_REDO
@@ -775,22 +1200,20 @@ static bool sokoban_loop(void)
case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
/* next level */
init_undo();
- if (current_info.level.level < current_info.max_level)
- current_info.level.level++;
+ if (current_info.level.index + 1 < current_info.max_level)
+ current_info.level.index++;
draw_level();
- moved = false;
break;
case SOKOBAN_LEVEL_DOWN:
case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
/* previous level */
init_undo();
- if (current_info.level.level > 1)
- current_info.level.level--;
+ if (current_info.level.index > 0)
+ current_info.level.index--;
draw_level();
- moved = false;
break;
#ifdef SOKOBAN_LEVEL_REPEAT
@@ -799,7 +1222,6 @@ static bool sokoban_loop(void)
/* same level */
init_undo();
draw_level();
- moved = false;
break;
#endif
@@ -826,13 +1248,10 @@ static bool sokoban_loop(void)
default:
if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
-
- moved = false;
break;
}
- if (button != BUTTON_NONE)
- lastbutton = button;
+ lastbutton = button;
if (moved) {
rb->lcd_clear_display();
@@ -844,58 +1263,102 @@ static bool sokoban_loop(void)
if (moved) {
rb->lcd_clear_display();
- /* Center level completed message */
- rb->snprintf(s, sizeof(s), "Level %d Complete!",
- current_info.level.level);
- rb->lcd_getstringsize(s, &w, &h);
- rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - 16 , s);
- rb->snprintf(s, sizeof(s), "%4d Moves ",
+
+ /* Show level complete message & stats */
+ rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
+ current_info.level.index + 1);
+ rb->lcd_getstringsize(buf, &w, &h);
+ rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
+
+ rb->snprintf(buf, sizeof(buf), "%4d Moves ",
current_info.level.moves);
- rb->lcd_getstringsize(s, &w, &h);
- rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 0 , s);
- rb->snprintf(s, sizeof(s), "%4d Pushes",
+ rb->lcd_getstringsize(buf, &w, &h);
+ rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
+
+ rb->snprintf(buf, sizeof(buf), "%4d Pushes",
current_info.level.pushes);
- rb->lcd_getstringsize(s, &w, &h);
- rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 8 , s);
+ rb->lcd_getstringsize(buf, &w, &h);
+ rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
+
+ if (undo_info.count < MAX_UNDOS) {
+ rb->snprintf(buf, sizeof(buf), "%s: Save solution",
+ BUTTON_SAVE_NAME);
+ rb->lcd_getstringsize(buf, &w, &h);
+ rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
+ }
+
rb->lcd_update();
- rb->button_get(false);
+ rb->sleep(HZ/4);
+ rb->button_clear_queue();
- rb->sleep(HZ/2);
- for (i = 0; i < 30; i++) {
+ /* Display for 4 seconds or until new keypress */
+ for (i = 0; i < 75; i++) {
rb->sleep(HZ/20);
button = rb->button_get(false);
- if (button && ((button & BUTTON_REL) != BUTTON_REL))
+ if (button && !(button & BUTTON_REL) &&
+ !(button & BUTTON_REPEAT))
break;
}
+
+ if (button == BUTTON_SAVE) {
+ if (undo_info.count < MAX_UNDOS) {
+ /* Default filename to current levelset plus
+ * level number and .sok extension */
+ loc = rb->strrchr(buffered_boards.filename, '.');
+ if (loc != NULL)
+ *loc = '\0';
+ rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
+ buffered_boards.filename,
+ current_info.level.index + 1);
+ if (loc != NULL)
+ *loc = '.';
+
+ if (!rb->kbd_input(buf, MAX_PATH))
+ save(buf, true);
+ } else
+ rb->splash(HZ*2, "Solution too long to save");
+
+ rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
+ }
}
- current_info.level.level++;
+ FOR_NB_SCREENS(i) {
+ rb->screens[i]->clear_display();
+ rb->screens[i]->update();
+ }
+
+ current_info.level.index++;
/* clear undo stats */
init_undo();
- rb->lcd_clear_display();
-
- if (current_info.level.level > current_info.max_level) {
- /* Center "You WIN!!" on all screen sizes */
- rb->snprintf(s, sizeof(s), "You WIN!!");
- rb->lcd_getstringsize(s, &w, &h);
- rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, s);
+ if (current_info.level.index >= current_info.max_level) {
+ /* Show levelset complete message */
+ rb->snprintf(buf, sizeof(buf), "You WIN!!");
+ rb->lcd_getstringsize(buf, &w, &h);
+ rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
- /* Display for 10 seconds or until keypress */
- for (i = 0; i < 200; i++) {
+ /* Display for 4 seconds or until keypress */
+ for (i = 0; i < 80; i++) {
rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
rb->lcd_update();
- rb->sleep(HZ/20);
+ rb->sleep(HZ/10);
button = rb->button_get(false);
- if (button && ((button & BUTTON_REL) != BUTTON_REL))
+ if (button && !(button & BUTTON_REL))
break;
}
rb->lcd_set_drawmode(DRMODE_SOLID);
- return PLUGIN_OK;
+ /* Reset to first level & show quit menu */
+ current_info.level.index = 0;
+
+ switch (sokoban_menu()) {
+ case 4: /* Quit */
+ case 5: /* Save & quit */
+ return PLUGIN_OK;
+ }
}
load_level();
@@ -911,99 +1374,41 @@ static bool sokoban_loop(void)
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
{
int w, h;
- int i;
- int button = 0;
(void)(parameter);
rb = api;
- rb->lcd_setfont(FONT_SYSFIXED);
-
-#ifdef HAVE_LCD_COLOR
- rb->lcd_set_background(BG_COLOR);
-#endif
+ rb->lcd_setfont(SOKOBAN_FONT);
rb->lcd_clear_display();
rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
rb->lcd_update();
- rb->sleep(HZ);
+ rb->sleep(HZ); /* Show title for 1 second */
- rb->lcd_clear_display();
+ init_boards();
-#if (CONFIG_KEYPAD == RECORDER_PAD) || \
- (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
- rb->lcd_putsxy(3, 6, "[OFF] Quit");
- rb->lcd_putsxy(3, 16, "[ON] Undo");
- rb->lcd_putsxy(3, 26, "[PLAY] Redo");
- rb->lcd_putsxy(3, 36, "[F1] Down a Level");
- rb->lcd_putsxy(3, 46, "[F2] Restart Level");
- rb->lcd_putsxy(3, 56, "[F3] Up a Level");
-#elif CONFIG_KEYPAD == ONDIO_PAD
- rb->lcd_putsxy(3, 6, "[OFF] Quit");
- rb->lcd_putsxy(3, 16, "[MODE] Undo");
- rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
- rb->lcd_putsxy(3, 36, "[MODE+LEFT] Down a Level");
- rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
- rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
-#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
- (CONFIG_KEYPAD == IRIVER_H300_PAD)
- rb->lcd_putsxy(3, 6, "[STOP] Quit");
- rb->lcd_putsxy(3, 16, "[REC] Undo");
- rb->lcd_putsxy(3, 26, "[MODE] Redo");
- rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level");
- rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
- rb->lcd_putsxy(3, 56, "[PLAY+UP] Up a Level");
-#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
- (CONFIG_KEYPAD == IPOD_3G_PAD)
- rb->lcd_putsxy(3, 6, "[SELECT+MENU] Quit");
- rb->lcd_putsxy(3, 16, "[SELECT] Undo");
- rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
- rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Down a Level");
- rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Up a Level");
-#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
- rb->lcd_putsxy(3, 6, "[POWER] Quit");
- rb->lcd_putsxy(3, 16, "[SELECT] Undo");
- rb->lcd_putsxy(3, 26, "[REC] Down a Level");
- rb->lcd_putsxy(3, 36, "[PLAY] Up Level");
-#elif CONFIG_KEYPAD == GIGABEAT_PAD
- rb->lcd_putsxy(3, 6, "[POWER] Quit");
- rb->lcd_putsxy(3, 16, "[SELECT] Undo");
- rb->lcd_putsxy(3, 26, "[A] Redo");
- rb->lcd_putsxy(3, 36, "[VOL-] Down a Level");
- rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
- rb->lcd_putsxy(3, 56, "[VOL+] Up Level");
-#elif CONFIG_KEYPAD == SANSA_E200_PAD
- rb->lcd_putsxy(3, 6, "[POWER] Quit");
- rb->lcd_putsxy(3, 16, "[SELECT] Undo");
- rb->lcd_putsxy(3, 26, "[REC] Redo");
- rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Down a Level");
- rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
- rb->lcd_putsxy(3, 56, "[SELECT+UP] Up Level");
-#elif CONFIG_KEYPAD == IRIVER_H10_PAD
- rb->lcd_putsxy(3, 6, "[POWER] Quit");
- rb->lcd_putsxy(3, 16, "[REW] Undo");
- rb->lcd_putsxy(3, 26, "[FF] Redo");
- rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level");
- rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
- rb->lcd_putsxy(3, 56, "[PLAY+UP] Up Level");
-#endif
+ if (parameter == NULL) {
+ /* Attempt to resume saved progress, otherwise start at beginning */
+ if (!load(SOKOBAN_SAVE_FILE, true)) {
+ init_boards();
+ if (!read_levels(true))
+ return PLUGIN_OK;
+ load_level();
+ }
- rb->lcd_update();
- rb->button_get(false);
- /* Display for 3 seconds or until keypress */
- for (i = 0; i < 60; i++) {
- rb->sleep(HZ/20);
- button = rb->button_get(false);
- if (button && ((button & BUTTON_REL) != BUTTON_REL))
- break;
+ } else {
+ /* The plugin is being used to open a file */
+ if (load((char*) parameter, false)) {
+ /* If we loaded & played a solution, quit */
+ if (current_info.level.boxes_to_go == 0)
+ return PLUGIN_OK;
+ } else
+ return PLUGIN_OK;
}
- rb->lcd_clear_display();
-
- init_boards();
- if (read_levels(1) != 0)
- return PLUGIN_OK;
+ rb->lcd_clear_display();
+ update_screen();
return sokoban_loop();
}
diff --git a/apps/plugins/sokoban.levels b/apps/plugins/sokoban.levels
deleted file mode 100644
index 4d73a5ed9d..0000000000
--- a/apps/plugins/sokoban.levels
+++ /dev/null
@@ -1,1816 +0,0 @@
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX###XXXXXXXXX
-XXXXXXXX#.#XXXXXXXXX
-XXXXXXXX# ####XXXXXX
-XXXXXX###$ $.#XXXXXX
-XXXXXX#. $@###XXXXXX
-XXXXXX####$#XXXXXXXX
-XXXXXXXXX#.#XXXXXXXX
-XXXXXXXXX###XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX#####XXXXXXXXXX
-XXXXX# #XXXXXXXXXX
-XXXXX#@$$#X###XXXXXX
-XXXXX# $ #X#.#XXXXXX
-XXXXX### ###.#XXXXXX
-XXXXXX## .#XXXXXX
-XXXXXX# # #XXXXXX
-XXXXXX# ####XXXXXX
-XXXXXX#####XXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#######XXXXXXX
-XXXXXX# ###XXXXX
-XXXXX##$### #XXXXX
-XXXXX# @ $ $ #XXXXX
-XXXXX# ..# $ ##XXXXX
-XXXXX##..# #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX#####XXXXXX
-XXXXXX#### .#XXXXXX
-XXXXXX# $ %#XXXXXX
-XXXXXX# $$ $.#XXXXXX
-XXXXXX##@##..#XXXXXX
-XXXXXXX#######XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX####XXXXXXXXX
-XXXXXXX#@ ###XXXXXXX
-XXXXXXX# $ #XXXXXXX
-XXXXXX### # ##XXXXXX
-XXXXXX#.# # #XXXXXX
-XXXXXX#.$ # #XXXXXX
-XXXXXX#. $ #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX#######XXXXX
-XXXXXXX## # @#XXXXX
-XXXXXXX# # #XXXXX
-XXXXXXX#$ $ $ #XXXXX
-XXXXXXX# $## #XXXXX
-XXXXX### $ # ##XXXXX
-XXXXX#..... #XXXXXX
-XXXXX#########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX######XXXXXX
-XXXXXX### #XXXXXX
-XXXXX##. $## ##XXXXX
-XXXXX#..$ $ @#XXXXX
-XXXXX#.. $ $ ##XXXXX
-XXXXX###### #XXXXXX
-XXXXXXXXXX####XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX######XXXXXX
-XXXXXXXX# #XXXXXX
-XXXXXX###$$$ #XXXXXX
-XXXXXX#@ $.. #XXXXXX
-XXXXXX# $...##XXXXXX
-XXXXXX#### #XXXXXXX
-XXXXXXXXX####XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX####XXXXXXXX
-XXXXXXXX#..#XXXXXXXX
-XXXXXXX## .##XXXXXXX
-XXXXXXX# $.#XXXXXXX
-XXXXXX## $ ##XXXXXX
-XXXXXX# #$$ #XXXXXX
-XXXXXX# @ #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX#####XXXXXXX
-XXXXXX### @#XXXXXXX
-XXXXXX# $. ##XXXXXX
-XXXXXX# .$. #XXXXXX
-XXXXXX### %$ #XXXXXX
-XXXXXXXX# ##XXXXXX
-XXXXXXXX#####XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX########XXXXXX
-XXXXXX# # #XXXXXX
-XXXXXX# $..$ #XXXXXX
-XXXXXX#@$.% ##XXXXXX
-XXXXXX# $..$ #XXXXXX
-XXXXXX# # #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXX# ###XXXXX
-XXXXXXX# $ #XXXXX
-XXXXX### $ ## #XXXXX
-XXXXX#... $ #XXXXX
-XXXXX#...$#$ ##XXXXX
-XXXXX#### # $ #XXXXX
-XXXXXXXX# @ #XXXXX
-XXXXXXXX#######XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX######XXXXXXXX
-XXXXXX# #XXXXXXXX
-XXXXXX# $$$##XXXXXXX
-XXXXXX# #..###XXXXX
-XXXXXX## ..$ #XXXXX
-XXXXXXX# @ #XXXXX
-XXXXXXX########XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX########XXXXX
-XXXXXXX# #. #XXXXX
-XXXXXX## $...#XXXXX
-XXXXXX# $ #%.#XXXXX
-XXXXX## ##$# ##XXXXX
-XXXXX# $ $ #XXXXX
-XXXXX# # #XXXXX
-XXXXX#######@ #XXXXX
-XXXXXXXXXXX####XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#######XXXXXXX
-XXXXXX#.... #XXXXXXX
-XXXXX###...$###XXXXX
-XXXXX# $#$ $ #XXXXX
-XXXXX# $$ #$ #XXXXX
-XXXXX# # #XXXXX
-XXXXX#### @ ###XXXXX
-XXXXXXXX#####XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX########XXXXXX
-XXXXXX#.. #XXXXXX
-XXXXXX#..$ $@#XXXXXX
-XXXXXX#$#$$$##XXXXXX
-XXXXXX#..$ $ #XXXXXX
-XXXXXX#.. #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX########XXXXXXX
-XXXXX# #XXXXXXX
-XXXXX# #$$ #XXXXXXX
-XXXXX# ...# #XXXXXXX
-XXXXX##...$ ##XXXXXX
-XXXXXX# ## $ #XXXXXX
-XXXXXX#$ $ #XXXXXX
-XXXXXX# # @#XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX#####XXXXXXXX
-XXXXX### ####XXXXX
-XXXXX# $ $ #XXXXX
-XXXXX# $ $ @#XXXXX
-XXXXX###$$#####XXXXX
-XXXXXXX# ..#XXXXXXX
-XXXXXXX#....#XXXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX#########XXXXXX
-XXXXX# % #XXXXXX
-XXXXX# $.$. @#XXXXXX
-XXXXX# .$.$. #XXXXXX
-XXXXX# $.$.$ #XXXXXX
-XXXXX# % #XXXXXX
-XXXXX#########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#########XXXXX
-XXXXX## #XXXXX
-XXXXX# #$#$ #XXXXX
-XXXXX# $$ .$.#XXXXX
-XXXXX# @###...#XXXXX
-XXXXX####X#####XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXX#. ..#XXXXXXX
-XXXXXXX#. $.#XXXXXXX
-XXXXXX### $##XXXXXX
-XXXXXX# $ $ #XXXXXX
-XXXXXX# #$## #XXXXXX
-XXXXXX# @ #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#####XXXXXXXXX
-XXXXXX# ####XXXXXX
-XXXXX## #$ #XXXXXX
-XXXXX# $ $$ #XXXXXX
-XXXXX# #$#.%.#XXXXXX
-XXXXX# @...#XXXXXX
-XXXXX#########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX#####XXXXXX
-XXXXXX#### ##XXXXX
-XXXXXX# $ $ #XXXXX
-XXXXXX#@#.%.# #XXXXX
-XXXXXX# #.%.# #XXXXX
-XXXXXX# $ $ #XXXXX
-XXXXXX## ####XXXXX
-XXXXXXX#####XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX#####XXXXXXX
-XXXXX####. ##XXXXXX
-XXXXX# $.$. #XXXXXX
-XXXXX#@$# #$ #XXXXXX
-XXXXX# $. . #XXXXXX
-XXXXX####$#$ #XXXXXX
-XXXXXXX#. . #XXXXXX
-XXXXXXX#######XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX########XXXX
-XXXXX#### . #XXXX
-XXXXX# $ $ $. #XXXX
-XXXXX# .####.##XXXX
-XXXXX# $.$ $ @#XXXXX
-XXXXX# . ##XXXXX
-XXXXX#########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX#######XXXXXXXX
-XXXXX# #####XXXX
-XXXXX# $ .%. $ #XXXX
-XXXXX#@$.% %.$ #XXXX
-XXXXX# $ .%. $ #XXXX
-XXXXX###### #XXXX
-XXXXXXXXXX######XXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX######XXXXXXXXX
-XXXXX# #XXXXXXXXX
-XXXXX# $ ####XXXXXX
-XXXXX# $%..% #XXXXXX
-XXXXX# %..%$ #XXXXXX
-XXXXX#### $ #XXXXXX
-XXXXXXXX# @ #XXXXXX
-XXXXXXXX######XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX######XXXXX
-XXXXXX####. @#XXXXX
-XXXXXX# $$$ #XXXXX
-XXXXXX#.##.##.#XXXXX
-XXXXXX# $ #XXXXX
-XXXXXX# $.# ##XXXXX
-XXXXXX#### #XXXXXX
-XXXXXXXXX#####XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#########XXXXX
-XXXXXX#. $. @#XXXXX
-XXXXXX# .$.$ #XXXXX
-XXXXXX##$.$ $##XXXXX
-XXXXXX# .$.$ #XXXXX
-XXXXXX#. $. #XXXXX
-XXXXXX#########XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX#######XXXXXX
-XXXXXXX# @ #XXXXXX
-XXXXXXX##$ $ #XXXXXX
-XXXXXXX# $$##XXXXXX
-XXXXXXX#....#XXXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX####XXXXXXXXXX
-XXXXXX# #####XXXXXX
-XXXXX##$ ## #XXXXXX
-XXXXX# $@$ #XXXXXX
-XXXXX# ##$ #XXXXXX
-XXXXX###.## ###XXXXX
-XXXXXX#...$ $ #XXXXX
-XXXXXX##.. #XXXXX
-XXXXXXX########XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX########XXXXXX
-XXXXXX# #.. ##XXXXX
-XXXXXX# $. $% #XXXXX
-XXXXXX# $@$ #XXXXX
-XXXXXX# %$ .$ #XXXXX
-XXXXXX## ..# #XXXXX
-XXXXXXX########XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#######XXXXXXX
-XXXXX## % ##XXXXXX
-XXXXX# . . . #XXXXXX
-XXXXX# $ % #XXXXXX
-XXXXX#%$$%$$%#XXXXXX
-XXXXX# % $ #XXXXXX
-XXXXX# . .@. #XXXXXX
-XXXXX## % ##XXXXXX
-XXXXXX#######XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX#########XXXXXX
-XXXXX# .$ @#XXXXXX
-XXXXX# $ % $ #XXXXXX
-XXXXX#%...%.%#XXXXXX
-XXXXX# $$% #XXXXXX
-XXXXX# . $ #XXXXXX
-XXXXX#########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX######XXXXX
-XXXXX#####. #XXXXX
-XXXXX# #..## #XXXXX
-XXXXX# $.. #XXXXX
-XXXXX# # .# ##XXXXX
-XXXX### ##$# #XXXXX
-XXXX# $ $$ #XXXXX
-XXXX# #$# # #XXXXX
-XXXX#@ #######XXXXX
-XXXX#####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXX# ...#XXXXXXX
-XXXX####....#XXXXXXX
-XXXX# ###$ ###XXXXX
-XXXX# $ $ $$ #XXXXX
-XXXX#@ $ $ #XXXXX
-XXXX# #### #XXXXX
-XXXX####XX#####XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#########XXXXX
-XXXXXX# ## #XXXXX
-XXXXXX# $ #XXXXX
-XXXXXX#$ ### $#XXXXX
-XXXXXX# #...# #XXXXX
-XXXXX## #...# ##XXXX
-XXXXX# $ $ $ #XXXX
-XXXXX# # @ #XXXX
-XXXXX###########XXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX#######XXXXX
-XXXXXX### #XXXXX
-XXXXX## # # #XXXXX
-XXXXX# #.$$$ #XXXXX
-XXXXX# #.%# ###XXXXX
-XXXXX# ..# #XXXXXXX
-XXXXX###..$ ##XXXXXX
-XXXXXXX#.# $ #XXXXXX
-XXXXXX## # #@#XXXXXX
-XXXXXX# $ $ #XXXXXX
-XXXXXX# ###XXXXXX
-XXXXXX######XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX##########XXXX
-XXXXX## #XXXX
-XXXXX#@ $$$ $ #XXXX
-XXXXX## $ $ $ #XXXX
-XXXXXX## #### ##XXXX
-XXXXXXX# $ ##XXXX
-XXXXX### ##$ $ #XXXX
-XXXXX# # #XXXX
-XXXXX# ######XXXX
-XXXXX# ##XXXXXXXX
-XXXXX#.....#XXXXXXXX
-XXXXX#.....#XXXXXXXX
-XXXXX#######XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX########XXXXXXX
-XXXXX# #XXXXXXX
-XXXXX# @ $# #XXXXXXX
-XXXXX## # #XXXXXXX
-XXXXX# $#.# #XXXXXXX
-XXXXX# .%.$#XXXXXXX
-XXXXX## #.# #XXXXXXX
-XXXXXX# #. ##XXXXXX
-XXXXX## $.# #XXXXXX
-XXXXX# # $ #XXXXXX
-XXXXX# $ ###XXXXXX
-XXXXX# ####XXXXXXXX
-XXXXX####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXX####XXXXX
-XXXXXXXXXXX#@ #XXXXX
-XXXXXXX##### #XXXXX
-XXXX#### #XXXXX
-XXXX# .### ##XXXXX
-XXXX# # # ##XXXXX
-XXXX# # $ $#. #XXXXX
-XXXX# # % # #XXXXX
-XXXX# .#$ $ # #XXXXX
-XXXX## # # #XXXXX
-XXXXX# ###. #XXXXX
-XXXXX# ####XXXXX
-XXXXX#######XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXX#####XXXXXXXX
-XXXXXXX# ##XXXXXXX
-XXXXXXX# $ #XXXXXXX
-XXXXXXX# $ @#XXXXXXX
-XXXXXXX###. #XXXXXXX
-XXXXXXXXX#.##XXXXXXX
-XXXXXXXXX#. #XXXXXXX
-XXXXXXX###. #XXXXXXX
-XXXXXXX# $ #XXXXXXX
-XXXXXXX# $ #XXXXXXX
-XXXXXXX## ##XXXXXXX
-XXXXXXXX####XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX####XXXXXXXXXX
-XXXXXX# #####XXXXXX
-XXXXXX# # #XXXXXX
-XXXXXX# . # #XXXXXX
-XXXXXX##$# .#XXXXXX
-XXXXXXX# #$# #XXXXXX
-XXXXXX##. . #XXXXXX
-XXXXXX# ## #XXXXXX
-XXXXXX# # %#XXXXXX
-XXXXXX# $ $ #XXXXXX
-XXXXXX# $#% @#XXXXXX
-XXXXXX# #.###XXXXXX
-XXXXXX######XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX###XXXXXXXXX
-XXXXXXXX#.######XXXX
-XXXXXX###.$ # #XXXX
-XXXXXX#... #$ #XXXX
-XXXXXX#.## $ #XXXX
-XXXXX##.$ $## ##XXXX
-XXXXX#.$ # ## #XXXXX
-XXXXX#.#$ #XXXXX
-XXXXX#.$ #$ $#XXXXX
-XXXXX#. $## @ #XXXXX
-XXXXX#.$ $ #XXXXX
-XXXXX#. #######XXXXX
-XXXXX####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX####XXXXXXX
-XXXXXXX### ##XXXXXX
-XXXXXX## ##XXXXX
-XXXXXX# # ..#XXXXX
-XXXXX## $#%#$.#XXXXX
-XXXXX# $ # $.#XXXXX
-XXXXX# $ @ $.#XXXXX
-XXXXX# $ # $.#XXXXX
-XXXXX##$$#%#$.#XXXXX
-XXXXXX#. # ..#XXXXX
-XXXXXX##. ##XXXXX
-XXXXXXX### ##XXXXXX
-XXXXXXXXX####XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX#########XXXXX
-XXXXXX# #XXXXX
-XXXXXX# # ### #XXXXX
-XXXXXX# $$ # #XXXXX
-XXXXXX#@$ # #XXXXX
-XXXXXX## ## # #XXXXX
-XXXXXX# $ $ #XXXXX
-XXXXXX# $## # #XXXXX
-XXXXXX# #$# #XXXXX
-XXXXXX#...# #XXXXX
-XXXXXX#.#.#$###XXXXX
-XXXXXX#... #XXXXXXX
-XXXXXX#######XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX########XXXXXXX
-XXXXX# .#XXXXXXX
-XXXXX# #$#..##XXXXXX
-XXXXX# $...#XXXXXX
-XXXXX## ##.$.#XXXXXX
-XXXXXX# ## ##XXXXXX
-XXXXXX#$## #XXXXXXX
-XXXXX## #$#XXXXXXX
-XXXXX# # #XXXXXXX
-XXXXX# $ ####XXXXX
-XXXXX# $ $ @#XXXXX
-XXXXX# ### #XXXXX
-XXXXX####X#####XXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX####XXXXXXXXXX
-XXXXXX# ####XXXXXXX
-XXXXXX# $ #XXXXXXX
-XXXXXX# #.# #XXXXXXX
-XXXXXX# # # #XXXXXXX
-XXXXXX#.$.$.#XXXXXXX
-XXXXXX# # # #XXXXXXX
-XXXXXX# #.# #XXXXXXX
-XXXXXX# $$ #XXXXXXX
-XXXXXX## @ #XXXXXXX
-XXXXXXX# ###XXXXXXX
-XXXXXXX####XXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXX######X####XXXXX
-XXXX# ### #XXXXX
-XXXX# $$ ## #XXXXX
-XXXX# # $ #XXXXX
-XXXX## ## $# #XXXXX
-XXXXX# #... ##XXXXX
-XXXXX# ...#$##XXXXX
-XXXXX# #...# #XXXXX
-XXXX## # ###@ #XXXXX
-XXXX# $ $ $ #XXXXX
-XXXX# #$#### #XXXXX
-XXXX# #XX####XXXXX
-XXXX#####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXX##########XXXXX
-XXXXX#..$ $ %.#XXXXX
-XXXXX#.% $ $..#XXXXX
-XXXXX##.$ $ %##XXXXX
-XXXXXX#% $ $.#XXXXXX
-XXXXXX#.$ $ .#XXXXXX
-XXXXXX#. $ $.#XXXXXX
-XXXXXX#.$@$ %#XXXXXX
-XXXXX##% $ $.##XXXXX
-XXXXX#..$ $ %.#XXXXX
-XXXXX#.% $ $..#XXXXX
-XXXXX##########XXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXX#####XXXXXXXXXXX
-XXXX# #XXXXXXXXXXX
-XXXX#$ #XXXXXXXXXXX
-XX### $##XXXXXXXXXX
-XX# $ $ #XXXXXXXXXX
-### # ## #XXX######X
-# # ## ##### ..#X
-# $ $ ..#X
-##### ### #@## ..#X
-XXXX# #########X
-XXXX#######XXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXX############XXXXX
-XXX#.. # ###XXX
-XXX#.. # $ $ #XXX
-XXX#.. #$#### #XXX
-XXX#.. @ ## #XXX
-XXX#.. # $ ##XXX
-XXX###### ##$ $ #XXX
-XXXXX# $ $ $ $ #XXX
-XXXXX# # #XXX
-XXXXX############XXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX########XXX
-XXXXXXXXX# @#XXX
-XXXXXXXXX# $#$ ##XXX
-XXXXXXXXX# $ $#XXXX
-XXXXXXXXX##$ $ #XXXX
-X######### $ # ###XX
-X#.... ## $ $ #XX
-X##... $ $ #XX
-X#.... ##########XX
-X########XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXX########
-XXXXXXXXXXXX# ....#
-X############ ....#
-X# # $ $ ....#
-X# $$$#$ $ # ....#
-X# $ $ # ....#
-X# $$ #$ $ $########
-X# $ # #XXXXXXX
-X## #########XXXXXXX
-X# # ##XXXXXXX
-X# $ ##XXXXXXX
-X# $$#$$ @#XXXXXXX
-X# # ##XXXXXXX
-X###########XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXX#####XXXXXX
-XXXXXXXXX# #####XX
-XXXXXXXXX# #$## #XX
-XXXXXXXXX# $ #XX
-X######### ### #XX
-X#.... ## $ $###XX
-X#.... $ $$ ##XXX
-X#.... ##$ $ @#XXX
-X######### $ ##XXX
-XXXXXXXXX# $ $ #XXX
-XXXXXXXXX### ## #XXX
-XXXXXXXXXXX# #XXX
-XXXXXXXXXXX######XXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXX######XX###XXXXX
-XXXX#.. #X##@##XXXX
-XXXX#.. ### #XXXX
-XXXX#.. $$ #XXXX
-XXXX#.. # # $ #XXXX
-XXXX#..### # $ #XXXX
-XXXX#### $ #$ #XXXX
-XXXXXXX# $# $ #XXXX
-XXXXXXX# $ $ #XXXX
-XXXXXXX# ## #XXXX
-XXXXXXX#########XXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXX#####XXXX
-XXXXX####### ##XXX
-XXXX## # @## $$ #XXX
-XXXX# $ #XXX
-XXXX# $ ### #XXX
-XXXX### #####$###XXX
-XXXX# $ ### ..#XXXX
-XXXX# $ $ $ ...#XXXX
-XXXX# ###...#XXXX
-XXXX# $$ #X#...#XXXX
-XXXX# ###X#####XXXX
-XXXX####XXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXX####XXXXXXXXXXXX
-XXXX# ###########XX
-XXXX# $ $ $ #XX
-XXXX# $# $ # $ #XX
-XXXX# $ $ # #XX
-XX### $# # #### #XX
-XX#@#$ $ $ ## #XX
-XX# $ #$# # #XX
-XX# $ $ $ $ #XX
-XX##### #########XX
-XXXX# #XXXXXXXX
-XXXX#......#XXXXXXXX
-XXXX#......#XXXXXXXX
-XXXX#......#XXXXXXXX
-XXXX########XXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXX#######XX
-XXXXXXXXXXX# ...#XX
-XXXXXXX##### ...#XX
-XXXXXXX# . .#XX
-XXXXXXX# ## ...#XX
-XXXXXXX## ## ...#XX
-XXXXXX### ########XX
-XXXXXX# $$$ ##XXXXXX
-XX##### $ $ #####XX
-X## #$ $ # #XX
-X#@ $ $ $ $ #XX
-X###### $$ $ #####XX
-XXXXXX# #XXXXXX
-XXXXXX########XXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-X###XX#############X
-##@#### # #X
-# $$ $$ $ $ ...#X
-# $$$# $ #...#X
-# $ # $$ $$ #...#X
-### # $ #...#X
-# # $ $ $ #...#X
-# ###### ###...#X
-## # # $ $ #...#X
-# ## # $$ $ $##..#X
-# ..# # $ #.#X
-# ..# # $$$ $$$ #.#X
-##### # # #.#X
-XXXX# ######### #.#X
-XXXX# #.#X
-XXXX###############X
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXX####XXXXX
-XXXXXX####X# #XXXXX
-XXXX### ###$ #XXXXX
-XXX## @ $ #XXXXX
-XX## $ $$## ##XXXXX
-XX# #$## #XXXXX
-XX# # $ $$ # ###XXXX
-XX# $ # # $ #####
-X#### # $$ # #
-X#### ## $ #
-X#. ### ########
-X#.. ..#X####XXXXXXX
-X#...#.#XXXXXXXXXXXX
-X#.....#XXXXXXXXXXXX
-X#######XXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XX################XX
-XX# #XX
-XX# # ###### #XX
-XX# # $ $ $ $# #XX
-XX# # $@$ ## ##X
-XX# # #$ $ $###...#X
-XX# # $ $ ##...#X
-XX# ###$$$ $ ##...#X
-XX# # ## ##...#X
-XX##### ## ##...#X
-XXXXXX##### ###X
-XXXXXXXXXX# #XXX
-XXXXXXXXXX#######XXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXX#########XXXXXXXX
-XX## ## ######XXX
-### # # ###X
-# $ #$ # # ... #X
-# # $#@$## # #.#. #X
-# # #$ # . . #X
-# $ $ # # #.#. #X
-# ## ##$ $ . . #X
-# $ # # #$#.#. #X
-## $ $ $ $... #X
-X#$ ###### ## #X
-X# #XXXX##########X
-X####XXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXX#######XXXXX
-XX####### #XXXXX
-XX# # $@$ #XXXXX
-XX#$$ # #########X
-XX# ###......## #X
-XX# $......## # #X
-XX# ###...... #X
-X## #### ### #$##X
-X# #$ # $ # #XX
-X# $ $$$ # $## #XX
-X# $ $ ###$$ # #XX
-X##### $ # #XX
-XXXXX### ### # #XX
-XXXXXXX# # #XX
-XXXXXXX######## #XX
-XXXXXXXXXXXXXX####XX
-
-
-XXXXX########XXXXXXX
-XXXXX# # #XXXXXXX
-XXXXX# $ #XXXXXXX
-XXX### #$ ####XXXX
-XXX# $ ##$ #XXXX
-XXX# # @ $ # $#XXXX
-XXX# # $ ####X
-XXX## ####$## #X
-XXX# $#.....# # #X
-XXX# $..%%. $# ###X
-XX## #.....# #XXX
-XX# ### #######XXX
-XX# $$ # #XXXXXXXX
-XX# # #XXXXXXXX
-XX###### #XXXXXXXX
-XXXXXXX#####XXXXXXXX
-
-
-XXX#####XXXXXXXXXXXX
-XXX# ##XXXXXXXXXXX
-XXX# #XX####XXXXX
-XXX# $ #### #XXXXX
-XXX# $$ $ $#XXXXX
-XXX###@ #$ ##XXXX
-XXXX# ## $ $ ##XXX
-XXXX# $ ## ## .#XXX
-XXXX# #$##$ #.#XXX
-XXXX### $..##.#XXX
-XXXXX# #.%...#XXX
-XXXXX# $$ #.....#XXX
-XXXXX# #########XXX
-XXXXX# #XXXXXXXXXXX
-XXXXX####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXX##########XXXXX
-XXXXX#.. # #XXXXX
-XXXXX#.. #XXXXX
-XXXXX#.. # ####XXX
-XXXX####### # ##XX
-XXXX# #XX
-XXXX# # ## # #XX
-XX#### ## #### ##XX
-XX# $ ##### # #XX
-XX# # $ $ # $ #XX
-XX# @$ $ # ##XX
-XX#### ## #######XXX
-XXXXX# #XXXXXXXXX
-XXXXX######XXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXX###########XXXX
-XXXXX# . # #XXXX
-XXXXX# #. @ #XXXX
-X##### ##..# ####XXX
-## # ..### ###X
-# $ #... $ # $ #X
-# .. ## ## ## #X
-####$##$# $ # # #X
-XX## # #$ $$ # #X
-XX# $ # # # $## #X
-XX# #X
-XX# ########### #X
-XX####XXXXXXXXX####X
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXX######XXXXXXXXXXX
-XXX# @####XXXXXXXX
-X##### $ #XXXXXXXX
-X# ## ####XXXXX
-X# $ # ## #XXXXX
-X# $ # ##### #XXXXX
-X## $ $ # #XXXXX
-X## $ $ ### # #XXXXX
-X## # $ # # #XXXXX
-X## # #$# # #XXXXX
-X## ### # # ######
-X# $ #### # #....#
-X# $ $ ..#.#
-X####$ $# $ ....#
-X# # ## ....#
-X###################
-
-
-XXXXX##########XXXXX
-X##### ####XX
-X# # $ #@ #XX
-X# #######$#### ###
-X# # ## # #$ ..#
-X# # $ $ # # #.#
-X# # $ # #$ ..#
-X# # ### ## #.#
-X# ### # # #$ ..#
-X# # # $#### #.#
-X# #$ $ $ #% ..#
-X# $ # $ $ # #.#
-X#### $### #% ..#
-XXXX# $$ ###....#
-XXXX# ##X######
-XXXX########XXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXX#########XXXXXXXX
-XXX# #XXXXXXXX
-XXX# ####XXXXX
-XXX## #### # #XXXXX
-XXX## #@## #XXXXX
-XXX# $$$ $ $$#XXXXX
-XXX# # ## $ #XXXXX
-XXX# # ## $ ####XX
-XXX#### $$$ $# #XX
-XXXX# ## ....#XX
-XXXX# # # #.. .#XX
-XXXX# # # ##...#XX
-XXXX##### $ #...#XX
-XXXXXXXX## #####XX
-XXXXXXXXX#####XXXXXX
-
-
-X######XXXXX####XXXX
-X# ####### #####
-X# $# # $ # #
-X# $ $ $ # $ $ #
-X##$ $ # @# $ #
-X# $ ########### ##
-X# # #.......# $#X
-X# ## # ......# #X
-X# # $........$ #X
-X# # $ #.... ..# #X
-X# $ $####$#### $#X
-X# $ ### $ $ ##
-X# $ $ $ $ #
-X## ###### $ ##### #
-X# # #
-X###################
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXX#######XXXXXXXX
-XXXXX# # ####XXXXX
-X##### $#$ # ##XXXX
-X#.. # # # ####X
-X#.. # $#$ # $# #X
-X#. # #$ # #X
-X#.. $# # $ #X
-X#..@# #$ #$ # #X
-X#.. # $# $# #X
-X#.. # #$$#$ # ##
-X#.. # $# # $#$ #
-X#.. # # # # #
-X##. #### ##### #
-XX####XX####XXX#####
-XXXXXXXXXXXXXXXXXXXX
-
-
-X###############XXXX
-X#.......... .####X
-X#..........$$.# #X
-X###########$ # ##
-X# $ $ $ #
-X## #### # $ # #
-X# # ## # ##
-X# $# # ## ### ##
-X# $ #$### ### ##
-X### $ # # ### ##
-XX## $ ## # # ##
-XX# $ # $ $ $ #
-XX# $ $#$$$ # #
-XX# # $ #####
-XX# @## # #XXXX
-XX##############XXXX
-
-
-X####XXXXXXXXXXXXXXX
-X# ##############XX
-X# # ..#......#XX
-X# # # ##### ...#XX
-X##$# ........#XX
-X# ##$###### ####
-X# $ # ######@ #
-X##$ # $ ###### #
-X# $ #$$$## #
-X# # #$#$###
-X# #### #$$$$$ #X
-X# # $ # #X
-X# # ## ## ###
-X# ######$###### $ #
-X# #XXXX# #
-X##########XXXX#####
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXX#######XXXXXXXXXX
-XXX# # #####XXXXXX
-XX## # #...###XXXX
-XX# $# #... #XXXX
-XX# $ #$$ ... #XXXX
-XX# $# #... .#XXXX
-XX# # $########XXX
-XX##$ $ $ #XXX
-XX## # $$ # #XXX
-XXX###### ##$$@#XXX
-XXXXXXXX# ##XXX
-XXXXXXXX########XXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-X#################XX
-X#... # # ##X
-##..... $## # #$ #X
-#......# $ # #X
-#......# # # # #X
-######### $ $ $ #X
-XX# #$##$ ##$##X
-X## $ # $ #X
-X# ## ### # ##$ #X
-X# $ $$ $ $ #X
-X# $ $##$ ######X
-X####### @ ##XXXXXX
-XXXXXXX######XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXX#####XXXX
-XXXXXXX##### #XXXX
-XXXXXX## $ $ ####X
-XX##### $ $ $ ##.#X
-XX# $$ ##..#X
-XX# ###### ###.. #X
-XX## # # #... #X
-XX# $ # #... #X
-XX#@ #$ ## ####...#X
-XX#### $ $$ ##..#X
-XXXXX## $ $ $...#X
-XXXXXX# $$ $ # .#X
-XXXXXX# $ $ ####X
-XXXXXX###### #XXXX
-XXXXXXXXXXX#####XXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-X#####XXXXXXXXXXXXXX
-X# ##XXXXXXXXXXXXX
-X# $ #########XXXXX
-X## # # ######
-X## # $#$#@ # #
-X# # $ # $ #
-X# ### ######### ##
-X# ## ..%..... # ##
-X## ## %.%..%.% # ##
-X# $########## ##$ #
-X# $ $ $ $ #
-X# # # # # #
-X###################
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX###########X
-XXXXXXXX# # #X
-X#####XX# $ $ #X
-X# ##### $## # ##X
-X# $ ## # ## $ #X
-X# $ @$$ # ##$$$ #X
-X## ### # ## #X
-X## # ### #####$#X
-X## # $ #....#X
-X# ### ## $ #....##
-X# $ $ # #..$. #
-X# ## $ # ##.... #
-X##### ######...##
-XXXXX#####XXXX#####X
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXX####XXXXXXXXXXXXX
-XXX# #########XXXXX
-XX## ## # #XXXXX
-XX# $# $@$ ####XX
-XX#$ $ # $ $# ##X
-X## $## #$ $ #X
-X# # # # $$$ #X
-X# $ $ $## ####X
-X# $ $ #$# # #XXXX
-X## ### ###$ #XXXX
-XX# #.... #XXXX
-XX####......####XXXX
-XXXX#....####XXXXXXX
-XXXX#...##XXXXXXXXXX
-XXXX#...#XXXXXXXXXXX
-XXXX#####XXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXX####XXXXXX
-XXXXXX##### #XXXXXX
-XXXXX## $#XXXXXX
-XXXX## $ ## ###XXXX
-XXXX#@$ $ # $ #XXXX
-XXXX#### ## $#XXXX
-XXXXX#....#$ $ #XXXX
-XXXXX#....# $#XXXX
-XXXXX#.... $$ ##XXX
-XXXXX#... # $ #XXX
-XXXXX######$ $ #XXX
-XXXXXXXXXX# ###XXX
-XXXXXXXXXX#$ ###XXXX
-XXXXXXXXXX# #XXXXXX
-XXXXXXXXXX####XXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXX############XXXX
-XXXX## ## #XXXX
-XXXX## $ $ #XXXX
-XXXX#### ## $$ #XXXX
-XXXX# $ # #XXXX
-XXXX# $$$ # ####XXXX
-XXXX# # # $ ##XXXX
-XXXX# # # $ #XXXX
-XXXX# $# $# #XXXX
-XXXX# ..# ####XXXX
-XXXX####.. $ #@#XXXX
-XXXX#.....# $# #XXXX
-XXXX##....# $ #XXXX
-XXXX###..## #XXXX
-XXXX############XXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXX#########XXXXXXX
-XXXX#.... ##XXXXXX
-XXXX#.#.# $ ##XXXXX
-XXX##....# # @##XXXX
-XXX# ....# # ##XXX
-XXX# #$ ##$ #XXX
-XXX## ### $ #XXX
-XXXX#$ $ $ $# #XXX
-XXXX# # $ $ ## #XXX
-XXXX# ### ## #XXX
-XXXX# ## ## ##XXX
-XXXX# $ # $ #XXXX
-XXXX###$ $ ###XXXX
-XXXXXX# #####XXXXXX
-XXXXXX####XXXXXXXXXX
-
-
-############X######X
-# # # ###....#X
-# $$# @ .....#X
-# # ### # ....#X
-## ## ### # ....#X
-X# $ $ # # ####X
-X# $ $## # #X
-#### # #### # ## #X
-# # #$ ## # #X
-# $ $ # ## # ##X
-# # $ $ # # #XX
-# $ ## ## # #####XX
-# $$ $$ #XXXXXX
-## ## ### $ #XXXXXX
-X# #X# #XXXXXX
-X######X######XXXXXX
-
-
-XXXXXXXXXXXXX#####XX
-X#####XX###### #XX
-X# #### $ $ $ #XX
-X# $ ## ## ## ##X
-X# $ $ $ $ #X
-X### $ ## ## ##
-XXX# ##### #####$$ #
-XX##$##### @## #
-XX# $ ###$### $ ##
-XX# $ # ### ###X
-XX# $$ $ # $$ #XXX
-XX# # ## #XXX
-XX#######.. .####XXX
-XXXXX#.........#XXXX
-XXXXX#.........#XXXX
-XXXXX###########XXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-###########XXXXXXXXX
-#...... #########X
-#...... # ## #X
-#..### $ $ #X
-#... $ $ # ### #X
-#...#$##### # #X
-### # #$ #$ #X
-XX# $$ $ $ $## #X
-XX# $ #$#$ ##$ #X
-XX### ## # ## #X
-XXX# $ $ ## ######X
-XXX# $ $ #XXXXX
-XXX## # # #XXXXX
-XXXX#####@#####XXXXX
-XXXXXXXX###XXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXX####XXXXXX
-XXXX####### @#XXXXXX
-XXXX# $ #XXXXXX
-XXXX# $## $#XXXXXX
-XXXX##$#...# #XXXXXX
-XXXXX# $... #XXXXXX
-XXXXX# #. .# ##XXXXX
-XXXXX# # #$ #XXXXX
-XXXXX#$ $ #XXXXX
-XXXXX# #######XXXXX
-XXXXX####XXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXX######
-XX#############....#
-X## ## ##....#
-X# $$## $ @##....#
-X# $$ $# ....#
-X# $ ## $$ # # ...#
-X# $ ## $ # ....#
-X## ##### ### ##.###
-X## $ $ ## . #
-X# $### # ##### ###
-X# $ # #XX
-X# $ #$ $ $### #XX
-X# $$$# $ #X####XX
-X# # $$ #XXXXXXX
-X###### ###XXXXXXX
-XXXXXX#####XXXXXXXXX
-
-
-XXXXXX############XX
-XXXXXX# ##X
-XXXXXX# # #$$ $ #X
-XXXXXX#$ #$# ## @#X
-XXXXX## ## # $ # ##X
-XXXXX# $ #$ # #XX
-XXXXX# # $ # #XX
-XXXXX## $ $ ## #XX
-XXXXX# # ## $ #XX
-XXXXX# ## $$# #XX
-XX######$$ # #XX
-XX#....# ########XX
-XX#.#... ##XXXXXXXXX
-XX#.... #XXXXXXXXX
-XX#.... #XXXXXXXXX
-XX#########XXXXXXXXX
-
-
-XXXXXXXXXXX#####XXXX
-XXXXXXXXXX## ##XXX
-XXXXXXXXX## #XXX
-XXXXXXXX## $$ #XXX
-XXXXXXX## $$ $ #XXX
-XXXXXXX# $ $ #XXX
-####XXX# $$ #####X
-# ######## ## #X
-#. $$$@#X
-#.# ####### ## ##X
-#.# #######. #$ $##X
-#........... # #X
-############## $ #X
-XXXXXXXXXXXXX## ##X
-XXXXXXXXXXXXXX####XX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXX########XXXXXX
-XXX#### ######X
-XXX# ## $ $ @#X
-XXX# ## ##$#$ $ $##X
-X### ......# $$ ##X
-X# ......# # #X
-X# # ......#$ $ #X
-X# # ...... $$# $ #X
-X# $### ###$ $ ##X
-X### $ $ $ $ #XX
-XXX# $ $ $ $ #XX
-XXX###### ######XX
-XXXXXXXX#####XXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXX#######XXXXX
-XXXX##### # ####XX
-XXXX# # $ #XX
-X#### #$$ ## ## #XX
-## # # ## ###X
-# ### $#$ $ $ #X
-#... # ## # #X
-#...# @ # ### ##X
-#...# ### $ $ #X
-########X## # #X
-XXXXXXXXXX#########X
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-X#####XXXXXXXXXXXXXX
-X# #XXXXXXXXXXXXXX
-X# # #######XXXXXXXX
-X# $@######XXXX
-X# $ ##$ ### #XXXX
-X# #### $ $ #XXXX
-X# ##### # #$ ####X
-## #### ##$ #X
-# $# $ # ## ## #X
-# # #...# #X
-###### ### ... #X
-XXXXX####X# #...# #X
-XXXXXXXXXX# ### # #X
-XXXXXXXXXX# #X
-XXXXXXXXXX#########X
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXX#####X####XXXXXXX
-XXX#...#X# ####XXXX
-XXX#...### $ #XXXX
-XXX#....## $ $###XX
-XXX##....## $ #XX
-XXX###... ## $ $ #XX
-XXX# ## # $ #XX
-XXX# ## # ### ####X
-XXX# $ # #$ $ #X
-XXX# $ @ $ $ #X
-XXX# # $ $$ $ ###X
-XXX# ###### ###XXX
-XXX# ##XXXX####XXXXX
-XXX###XXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXX##########XXXXXXX
-XXX# ####XXXX
-XXX# ###### # ##XXX
-XXX# # $ $ $ $ #XXX
-XXX# #$ #XXX
-XXX###$ $$# ###XXX
-XXXXX# ## # $##XXXX
-XXXXX##$# $ @#XXXX
-XXXXXX# $ $ ###XXXX
-XXXXXX# # $ #XXXX
-XXXXXX# ## # #XXXX
-XXXXX## ##### #XXXX
-XXXXX# #XXXX
-XXXXX#.......###XXXX
-XXXXX#.......#XXXXXX
-XXXXX#########XXXXXX
-
-
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXX####XXXXXX
-XX######### ##XXXXX
-X## $ $ #####X
-X# ## ## ##...#X
-X# #$$ $ $$#$##...#X
-X# # @ # ...#X
-X# $# ###$$ ...#X
-X# $ $$ $ ##....#X
-X###$ #######X
-XXX# #######XXXXXXX
-XXX####XXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXX#########XXXXXX
-XXXXX#%.%#%.%#XXXXXX
-XXXXX#.%.%.%.#XXXXXX
-XXXXX#%.%.%.%#XXXXXX
-XXXXX#.%.%.%.#XXXXXX
-XXXXX#%.%.%.%#XXXXXX
-XXXXX### ###XXXXXX
-XXXXXXX# #XXXXXXXX
-XXX###### ######XXXX
-XXX# #XXXX
-XXX# $ $ $ $ $ #XXXX
-XXX## $ $ $ $ ##XXXX
-XXXX#$ $ $ $ $#XXXXX
-XXXX# $@$ #XXXXX
-XXXX# ##### #XXXXX
-XXXX####XXX####XXXXX
-
-
-XXXXXXXX####XXXXXXXX
-XXXXXXXX# ##XXXXXXX
-XXXXXXXX# ##XXXXXX
-XXXXXXXX# $$ ##XXXXX
-XXXXXX###$ $ ##XXXX
-XXX#### $ #XXXX
-X### # ##### #XXXX
-X# # #....$ #XXXX
-X# # $ ....# #XXXX
-X# $ # #.%..# #XXXX
-X### #### ### #XXXX
-XXX#### @$ ##$##XXX
-XXXXXX### $ #XXX
-XXXXXXXX# ## #XXX
-XXXXXXXX#########XXX
-XXXXXXXXXXXXXXXXXXXX
-
-
-XXXXXX############XX
-XXXXX##.. #XX
-XXXX##..% $ $ #XX
-XXX##..%.# # #$ ##XX
-XXX#..%.# # # $ #XX
-####...# # # #XX
-# ## # #XX
-# @$ $ ### # # ##XX
-# $ $ # # #XXX
-###$$ # # # # #XXX
-XX# $ # # #####X
-XX# $# ##### #X
-XX#$ #XXX# # #X
-XX# ###XXX## #X
-XX# #XXXXXX# ##X
-XX####XXXXXX######XX
-
-
-###################X
-# ### # #X
-# ##$ $ # $$ # $ #X
-#.# . .$ # #..# #.#X
-#.####.# # # ## # #X
-# # # # # # #X
-# $ # # ### # ## #X
-# # # # #.$ # # #X
-# $$.# # ## # ## #X
-#$ # # # # ## # #X
-# .# # ## # # ## #X
-# # # # # ## #X
-## ## # # # # $.#X
-### ### $.# $.# $.#X
-####### # # @#X
-XXXXXX#############X
diff --git a/apps/plugins/sokobanlevels.sok b/apps/plugins/sokobanlevels.sok
new file mode 100644
index 0000000000..869dcfe76e
--- /dev/null
+++ b/apps/plugins/sokobanlevels.sok
@@ -0,0 +1,1277 @@
+ ###
+ #.#
+ # ####
+###$ $.#
+#. $@###
+####$#
+ #.#
+ ###
+
+#####
+# #
+#@$$# ###
+# $ # #.#
+### ###.#
+ ## .#
+ # # #
+ # ####
+ #####
+
+ #######
+ # ###
+##$### #
+# @ $ $ #
+# ..# $ ##
+##..# #
+ ########
+
+ #####
+#### .#
+# $ *#
+# $$ $.#
+##@##..#
+ #######
+
+ ####
+ #@ ###
+ # $ #
+### # ##
+#.# # #
+#.$ # #
+#. $ #
+########
+
+ #######
+ ## # @#
+ # # #
+ #$ $ $ #
+ # $## #
+### $ # ##
+#..... #
+#########
+
+ ######
+ ### #
+##. $## ##
+#..$ $ @#
+#.. $ $ ##
+###### #
+ ####
+
+ ######
+ # #
+###$$$ #
+#@ $.. #
+# $...##
+#### #
+ ####
+
+ ####
+ #..#
+ ## .##
+ # $.#
+## $ ##
+# #$$ #
+# @ #
+########
+
+ #####
+### @#
+# $. ##
+# .$. #
+### *$ #
+ # ##
+ #####
+
+########
+# # #
+# $..$ #
+#@$.* ##
+# $..$ #
+# # #
+########
+
+ ######
+ # ###
+ # $ #
+### $ ## #
+#... $ #
+#...$#$ ##
+#### # $ #
+ # @ #
+ #######
+
+######
+# #
+# $$$##
+# #..###
+## ..$ #
+ # @ #
+ ########
+
+ ########
+ # #. #
+ ## $...#
+ # $ #*.#
+## ##$# ##
+# $ $ #
+# # #
+#######@ #
+ ####
+
+ #######
+ #.... #
+###...$###
+# $#$ $ #
+# $$ #$ #
+# # #
+#### @ ###
+ #####
+
+########
+#.. #
+#..$ $@#
+#$#$$$##
+#..$ $ #
+#.. #
+########
+
+########
+# #
+# #$$ #
+# ...# #
+##...$ ##
+ # ## $ #
+ #$ $ #
+ # # @#
+ ########
+
+ #####
+### ####
+# $ $ #
+# $ $ @#
+###$$#####
+ # ..#
+ #....#
+ ######
+
+#########
+# * #
+# $.$. @#
+# .$.$. #
+# $.$.$ #
+# * #
+#########
+
+ #########
+## #
+# #$#$ #
+# $$ .$.#
+# @###...#
+#### #####
+
+ ######
+ #. ..#
+ #. $.#
+### $##
+# $ $ #
+# #$## #
+# @ #
+########
+
+ #####
+ # ####
+## #$ #
+# $ $$ #
+# #$#.*.#
+# @...#
+#########
+
+ #####
+#### ##
+# $ $ #
+#@#.*.# #
+# #.*.# #
+# $ $ #
+## ####
+ #####
+
+ #####
+####. ##
+# $.$. #
+#@$# #$ #
+# $. . #
+####$#$ #
+ #. . #
+ #######
+
+ ########
+#### . #
+# $ $ $. #
+# .####.##
+# $.$ $ @#
+# . ##
+#########
+
+#######
+# #####
+# $ .*. $ #
+#@$.* *.$ #
+# $ .*. $ #
+###### #
+ ######
+
+######
+# #
+# $ ####
+# $*..* #
+# *..*$ #
+#### $ #
+ # @ #
+ ######
+
+ ######
+####. @#
+# $$$ #
+#.##.##.#
+# $ #
+# $.# ##
+#### #
+ #####
+
+#########
+#. $. @#
+# .$.$ #
+##$.$ $##
+# .$.$ #
+#. $. #
+#########
+
+#######
+# @ #
+##$ $ #
+# $$##
+#....#
+######
+
+ ####
+ # #####
+##$ ## #
+# $@$ #
+# ##$ #
+###.## ###
+ #...$ $ #
+ ##.. #
+ ########
+
+########
+# #.. ##
+# $. $* #
+# $@$ #
+# *$ .$ #
+## ..# #
+ ########
+
+ #######
+## * ##
+# . . . #
+# $ * #
+#*$$*$$*#
+# * $ #
+# . .@. #
+## * ##
+ #######
+
+#########
+# .$ @#
+# $ * $ #
+#*...*.*#
+# $$* #
+# . $ #
+#########
+
+ ######
+ #####. #
+ # #..## #
+ # $.. #
+ # # .# ##
+### ##$# #
+# $ $$ #
+# #$# # #
+#@ #######
+#####
+
+ ######
+ # ...#
+####....#
+# ###$ ###
+# $ $ $$ #
+#@ $ $ #
+# #### #
+#### #####
+
+ #########
+ # ## #
+ # $ #
+ #$ ### $#
+ # #...# #
+## #...# ##
+# $ $ $ #
+# # @ #
+###########
+
+ #######
+ ### #
+## # # #
+# #.$$$ #
+# #.*# ###
+# ..# #
+###..$ ##
+ #.# $ #
+ ## # #@#
+ # $ $ #
+ # ###
+ ######
+
+ ##########
+## #
+#@ $$$ $ #
+## $ $ $ #
+ ## #### ##
+ # $ ##
+### ##$ $ #
+# # #
+# ######
+# ##
+#.....#
+#.....#
+#######
+
+########
+# #
+# @ $# #
+## # #
+# $#.# #
+# .*.$#
+## #.# #
+ # #. ##
+## $.# #
+# # $ #
+# $ ###
+# ####
+####
+
+ ####
+ #@ #
+ ##### #
+#### #
+# .### ##
+# # # ##
+# # $ $#. #
+# # * # #
+# .#$ $ # #
+## # # #
+ # ###. #
+ # ####
+ #######
+
+#####
+# ##
+# $ #
+# $ @#
+###. #
+ #.##
+ #. #
+###. #
+# $ #
+# $ #
+## ##
+ ####
+
+####
+# #####
+# # #
+# . # #
+##$# .#
+ # #$# #
+##. . #
+# ## #
+# # *#
+# $ $ #
+# $#* @#
+# #.###
+######
+
+ ###
+ #.######
+ ###.$ # #
+ #... #$ #
+ #.## $ #
+##.$ $## ##
+#.$ # ## #
+#.#$ #
+#.$ #$ $#
+#. $## @ #
+#.$ $ #
+#. #######
+####
+
+ ####
+ ### ##
+ ## ##
+ # # ..#
+## $#*#$.#
+# $ # $.#
+# $ @ $.#
+# $ # $.#
+##$$#*#$.#
+ #. # ..#
+ ##. ##
+ ### ##
+ ####
+
+#########
+# #
+# # ### #
+# $$ # #
+#@$ # #
+## ## # #
+# $ $ #
+# $## # #
+# #$# #
+#...# #
+#.#.#$###
+#... #
+#######
+
+########
+# .#
+# #$#..##
+# $...#
+## ##.$.#
+ # ## ##
+ #$## #
+## #$#
+# # #
+# $ ####
+# $ $ @#
+# ### #
+#### #####
+
+####
+# ####
+# $ #
+# #.# #
+# # # #
+#.$.$.#
+# # # #
+# #.# #
+# $$ #
+## @ #
+ # ###
+ ####
+
+###### ####
+# ### #
+# $$ ## #
+# # $ #
+## ## $# #
+ # #... ##
+ # ...#$##
+ # #...# #
+## # ###@ #
+# $ $ $ #
+# #$#### #
+# # ####
+#####
+
+##########
+#..$ $ *.#
+#.* $ $..#
+##.$ $ *##
+ #* $ $.#
+ #.$ $ .#
+ #. $ $.#
+ #.$@$ *#
+##* $ $.##
+#..$ $ *.#
+#.* $ $..#
+##########
+
+ #####
+ # #
+ #$ #
+ ### $##
+ # $ $ #
+### # ## # ######
+# # ## ##### ..#
+# $ $ ..#
+##### ### #@## ..#
+ # #########
+ #######
+
+############
+#.. # ###
+#.. # $ $ #
+#.. #$#### #
+#.. @ ## #
+#.. # # $ ##
+###### ##$ $ #
+ # $ $ $ $ #
+ # # #
+ ############
+
+ ########
+ # @#
+ # $#$ ##
+ # $ $#
+ ##$ $ #
+######### $ # ###
+#.... ## $ $ #
+##... $ $ #
+#.... ##########
+########
+
+ ########
+ # ....#
+############ ....#
+# # $ $ ....#
+# $$$#$ $ # ....#
+# $ $ # ....#
+# $$ #$ $ $########
+# $ # #
+## #########
+# # ##
+# $ ##
+# $$#$$ @#
+# # ##
+###########
+
+ #####
+ # #####
+ # #$## #
+ # $ #
+######### ### #
+#.... ## $ $###
+#.... $ $$ ##
+#.... ##$ $ @#
+######### $ ##
+ # $ $ #
+ ### ## #
+ # #
+ ######
+
+###### ###
+#.. # ##@##
+#.. ### #
+#.. $$ #
+#.. # # $ #
+#..### # $ #
+#### $ #$ #
+ # $# $ #
+ # $ $ #
+ # ## #
+ #########
+
+ #####
+ ####### ##
+## # @## $$ #
+# $ #
+# $ ### #
+### #####$###
+# $ ### ..#
+# $ $ $ ...#
+# ###...#
+# $$ # #...#
+# ### #####
+####
+
+ ####
+ # ###########
+ # $ $ $ #
+ # $# $ # $ #
+ # $ $ # #
+### $# # #### #
+#@#$ $ $ ## #
+# $ #$# # #
+# $ $ $ $ #
+##### #########
+ # #
+ # #
+ #......#
+ #......#
+ #......#
+ ########
+
+ #######
+ # ...#
+ ##### ...#
+ # . .#
+ # ## ...#
+ ## ## ...#
+ ### ########
+ # $$$ ##
+ ##### $ $ #####
+## #$ $ # #
+#@ $ $ $ $ #
+###### $$ $ #####
+ # #
+ ########
+
+ ### #############
+##@#### # #
+# $$ $$ $ $ ...#
+# $$$# $ #...#
+# $ # $$ $$ #...#
+### # $ #...#
+# # $ $ $ #...#
+# ###### ###...#
+## # # $ $ #...#
+# ## # $$ $ $##..#
+# ..# # $ #.#
+# ..# # $$$ $$$ #.#
+##### # # #.#
+ # ######### #.#
+ # #.#
+ ###############
+
+ ####
+ #### # #
+ ### @###$ #
+ ## $ #
+ ## $ $$## ##
+ # #$## #
+ # # $ $$ # ###
+ # $ # # $ #####
+#### # $$ # #
+#### ## $ #
+#. ### ########
+#.. ..# ####
+#...#.#
+#.....#
+#######
+
+################
+# #
+# # ###### #
+# # $ $ $ $# #
+# # $@$ ## ##
+# # $ $ $###...#
+# # $ $ ##...#
+# ###$$$ $ ##...#
+# # ## ##...#
+##### ## ##...#
+ ##### ###
+ # #
+ #######
+
+ #########
+ ## ## ######
+### # # ###
+# $ #$ # # ... #
+# # $#@$## # #.#. #
+# # #$ # . . #
+# $ $ # # #.#. #
+# ## ##$ $ . . #
+# $ # # #$#.#. #
+## $ $ $ $... #
+ #$ ###### ## #
+ # # ##########
+ ####
+
+ #######
+ ####### #
+ # # $@$ #
+ #$$ # #########
+ # ###......## #
+ # $......## # #
+ # ###...... #
+## #### ### #$##
+# #$ # $ # #
+# $ $$$ # $## #
+# $ $ ###$$ # #
+##### $ # #
+ ### ### # #
+ # # #
+ ######## #
+ ####
+
+ ########
+ # # #
+ # $ #
+ ### #$ ####
+ # $ ##$ #
+ # # @ $ # $#
+ # # $ ####
+ ## ####$## #
+ # $#.....# # #
+ # $..**. $# ###
+## #.....# #
+# ### #######
+# $$ # #
+# # #
+###### #
+ #####
+
+#####
+# ##
+# # ####
+# $ #### #
+# $$ $ $#
+###@ #$ ##
+ # ## $ $ ##
+ # $ ## ## .#
+ # #$##$ #.#
+ ### $..##.#
+ # #.*...#
+ # $$ #.....#
+ # #########
+ # #
+ ####
+
+ ##########
+ #.. # #
+ #.. #
+ #.. # ####
+ ####### # ##
+ # #
+ # # ## # #
+#### ## #### ##
+# $ ##### # #
+# # $ $ # $ #
+# @$ $ # ##
+#### ## #######
+ # #
+ ######
+
+ ###########
+ # . # #
+ # #. @ #
+ ##### ##..# ####
+## # ..### ###
+# $ #... $ # $ #
+# .. ## ## ## #
+####$##$# $ # # #
+ ## # #$ $$ # #
+ # $ # # # $## #
+ # #
+ # ########### #
+ #### ####
+
+ ######
+ # @####
+##### $ #
+# ## ####
+# $ # ## #
+# $ # ##### #
+## $ $ # #
+## $ $ ### # #
+## # $ # # #
+## # #$# # #
+## ### # # ######
+# $ #### # #....#
+# $ $ ..#.#
+####$ $# $ ....#
+# # ## ....#
+###################
+
+ ##########
+##### ####
+# # $ #@ #
+# #######$#### ###
+# # ## # #$ ..#
+# # $ # # #.#
+# # $ # #$ ..#
+# # ### ## #.#
+# ### # # #$ ..#
+# # # #### #.#
+# #$ $ $ #$ ..#
+# $ # $ $ # #.#
+#### $### #$ ..#
+ # $$ ###....#
+ # ## ######
+ ########
+
+#########
+# #
+# ####
+## #### # #
+## #@## #
+# $$$ $ $$#
+# # ## $ #
+# # ## $ ####
+#### $$$ $# #
+ # ## ....#
+ # # # #.. .#
+ # # # ##...#
+ ##### $ #...#
+ ## #####
+ #####
+
+###### ####
+# ####### #####
+# $# # $ # #
+# $ $ $ # $ $ #
+##$ $ # @# $ #
+# $ ########### ##
+# # #.......# $#
+# ## # ......# #
+# # $........$ #
+# # $ #.... ..# #
+# $ $####$#### $#
+# $ ### $ $ ##
+# $ $ $ $ #
+## ###### $ ##### #
+# # #
+###################
+
+ #######
+ # # ####
+##### $#$ # ##
+#.. # # # #
+#.. # $#$ # $####
+#. # #$ # #
+#.. $# # $ #
+#..@# #$ #$ # #
+#.. # $# $# #
+#.. # #$$#$ # ##
+#.. # $# # $#$ #
+#.. # # # # #
+##. #### ##### #
+ #### #### #####
+
+###############
+#.......... .####
+#..........$$.# #
+###########$ # ##
+# $ $ $ #
+## #### # $ # #
+# # ## # ##
+# $# # ## ### ##
+# $ #$### ### ##
+### $ # # ### ##
+### $ ## # # ##
+ # $ # $ $ $ #
+ # $ $#$$$ # #
+ # # $ #####
+ # @## # # #
+ ##############
+
+####
+# ##############
+# # ..#......#
+# # # ##### ...#
+##$# ........#
+# ##$###### ####
+# $ # ######@ #
+##$ # $ ###### #
+# $ #$$$## #
+# # #$#$###
+# #### #$$$$$ #
+# # $ # #
+# # ## ###
+# ######$###### $ #
+# # # #
+########## #####
+
+ #######
+ # # #####
+## # #...###
+# $# #... #
+# $ #$$ ... #
+# $# #... .#
+# # $########
+##$ $ $ #
+## # $$ # #
+ ###### ##$$@#
+ # ##
+ ########
+
+ #################
+ #... # # ##
+##..... $## # #$ #
+#......# $ # #
+#......# # # # #
+######### $ $ $ #
+ # #$##$ ##$##
+ ## $ # $ #
+ # ## ### # ##$ #
+ # $ $$ $ $ #
+ # $ $##$ ######
+ ####### @ ##
+ ######
+
+ #####
+ ##### #
+ ## $ $ ####
+##### $ $ $ ##.#
+# $$ ##..#
+# ###### ###.. #
+## # # #... #
+# $ # #... #
+#@ #$ ## ####...#
+#### $ $$ ##..#
+ ## $ $ $...#
+ # $$ $ # .#
+ # $ $ ####
+ ###### #
+ #####
+
+#####
+# ##
+# $ #########
+## # # ######
+## # $#$#@ # #
+# # $ # $ #
+# ### ######### ##
+# ## ..*..... # ##
+## ## *.*..*.* # ##
+# $########## ##$ #
+# $ $ $ $ #
+# # # # # #
+###################
+
+ ###########
+ # # #
+##### # $ $ #
+# ##### $## # ##
+# $ ## # ## $ #
+# $ @$$ # ##$$$ #
+## ### # ## #
+## # ### #####$#
+## # $ #....#
+# ### ## $ #....##
+# $ $ # #..$. #
+# ## $ # ##.... #
+##### ######...##
+ ##### #####
+
+ ####
+ # #########
+ ## ## # #
+ # $# $@$ ####
+ #$ $ # $ $# ##
+## $## #$ $ #
+# # # # $$$ #
+# $ $ $## ####
+# $ $ #$# # #
+## ### ###$ #
+ # #.... #
+ ####......####
+ #....####
+ #...##
+ #...#
+ #####
+
+ ####
+ ##### #
+ ## $#
+## $ ## ###
+#@$ $ # $ #
+#### ## $#
+ #....#$ $ #
+ #....# $#
+ #.... $$ ##
+ #... # $ #
+ ######$ $ #
+ # ###
+ #$ ###
+ # #
+ ####
+
+############
+## ## #
+## $ $ #
+#### ## $$ #
+# $ # #
+# $$$ # ####
+# # # $ ##
+# # # $ #
+# $# $# #
+# ..# ####
+####.. $ #@#
+#.....# $# #
+##....# $ #
+###..## #
+############
+
+ #########
+ #.... ##
+ #.#.# $ ##
+##....# # @##
+# ....# # ##
+# #$ ##$ #
+## ### $ #
+ #$ $ $ $# #
+ # # $ $ ## #
+ # ### ## #
+ # ## ## ##
+ # $ # $ #
+ ###$ $ ###
+ # #####
+ ####
+
+############ ######
+# # # ###....#
+# $$# @ .....#
+# # ### # ....#
+## ## ### # ....#
+ # $ $ # # ####
+ # $ $## # #
+#### # #### # ## #
+# # #$ ## # #
+# $ $ # ## # ##
+# # $ $ # # #
+# $ ## ## # #####
+# $$ $$ #
+## ## ### $ #
+ # # # #
+ ###### ######
+
+ #####
+##### ###### #
+# #### $ $ $ #
+# $ ## ## ## ##
+# $ $ $ $ #
+### $ ## ## ##
+ # ##### #####$$ #
+ ##$##### @## #
+ # $ ###$### $ ##
+ # $ # ### ###
+ # $$ $ # $$ #
+ # # ## #
+ #######.. .####
+ #.........#
+ #.........#
+ ###########
+
+###########
+#...... #########
+#...... # ## #
+#..### $ $ #
+#... $ $ # ## #
+#...#$##### # #
+### # #$ #$ #
+ # $$ $ $ $## #
+ # $ #$#$ ##$ #
+ ### ## # ## #
+ # $ $ ## ######
+ # $ $ #
+ ## # # #
+ #####@#####
+ ###
+
+ ####
+####### @#
+# $ #
+# $## $#
+##$#...# #
+ # $... #
+ # #. .# ##
+ # # #$ #
+ #$ $ #
+ # #######
+ ####
+
+ ######
+ #############....#
+## ## ##....#
+# $$## $ @##....#
+# $$ $# ....#
+# $ ## $$ # # ...#
+# $ ## $ # ....#
+## ##### ### ##.###
+## $ $ ## . #
+# $### # ##### ###
+# $ # #
+# $ #$ $ $### #
+# $$$# $ # ####
+# # $$ #
+###### ###
+ #####
+
+ ############
+ # ##
+ # # #$$ $ #
+ #$ #$# ## @#
+ ## ## # $ # ##
+ # $ #$ # #
+ # # $ # #
+ ## $ $ ## #
+ # # ## $ #
+ # ## $$# #
+######$$ # #
+#....# ########
+#.#... ##
+#.... #
+#.... #
+#########
+
+ #####
+ ## ##
+ ## #
+ ## $$ #
+ ## $$ $ #
+ # $ $ #
+#### # $$ #####
+# ######## ## #
+#. $$$@#
+#.# ####### ## ##
+#.# #######. #$ $##
+#........... # #
+############## $ #
+ ## ##
+ ####
+
+ ########
+ #### ######
+ # ## $ $ @#
+ # ## ##$#$ $ $##
+### ......# $$ ##
+# ......# # #
+# # ......#$ $ #
+# #$...... $$# $ #
+# ### ###$ $ ##
+### $ $ $ $ #
+ # $ $ $ $ #
+ ###### ######
+ #####
+
+ #######
+ ##### # ####
+ # # $ #
+ #### #$$ ## ## #
+## # # ## ###
+# ### $#$ $ $ #
+#... # ## # #
+#...# @ # ### ##
+#...# ### $ $ #
+######## ## # #
+ #########
+
+ #####
+ # #
+ # # #######
+ # $@######
+ # $ ##$ ### #
+ # #### $ $ #
+ # ##### # #$ ####
+## #### ##$ #
+# $# $ # ## ## #
+# # #...# #
+###### ### ... #
+ #### # #...# #
+ # ### # #
+ # #
+ #########
+
+##### ####
+#...# # ####
+#...### $ #
+#....## $ $###
+##....## $ #
+###... ## $ $ #
+# ## # $ #
+# ## # ### ####
+# $ # #$ $ #
+# $ @ $ $ #
+# # $ $$ $ ###
+# ###### ###
+# ## ####
+###
+
+##########
+# ####
+# ###### # ##
+# # $ $ $ $ #
+# #$ #
+###$ $$# ###
+ # ## # $##
+ ##$# $ @#
+ # $ $ ###
+ # # $ #
+ # ## # #
+ ## ##### #
+ # #
+ #.......###
+ #.......#
+ #########
+
+ ####
+ ######### ##
+## $ $ #####
+# ## ## ##...#
+# #$$ $ $$#$##...#
+# # @ # ...#
+# $# ###$$ ...#
+# $ $$ $ ##....#
+###$ #######
+ # #######
+ ####
+
+ #########
+ #*.*#*.*#
+ #.*.*.*.#
+ #*.*.*.*#
+ #.*.*.*.#
+ #*.*.*.*#
+ ### ###
+ # #
+###### ######
+# #
+# $ $ $ $ $ #
+## $ $ $ $ ##
+ #$ $ $ $ $#
+ # $@$ #
+ # ##### #
+ #### ####
+
+ ####
+ # ##
+ # ##
+ # $$ ##
+ ###$ $ ##
+ #### $ #
+### # ##### #
+# # #....$ #
+# # $ ....# #
+# $ # #.*..# #
+### #### ### #
+ #### @$ ##$##
+ ### $ #
+ # ## #
+ #########
+
+ ############
+ ##.. # #
+ ##..* $ $ #
+ ##..*.# # # $##
+ #..*.# # # $ #
+####...# # # #
+# ## # #
+# @$ $ ### # ##
+# $ $ # # #
+###$$ # # # # #
+ # $ # # #####
+ # $# ##### #
+ #$ # # # #
+ # ### ## #
+ # # # ##
+ #### ######
+
+###################
+# ### # #
+# ##$ $ # $$ # $ #
+#.# . .$ # #..# #.#
+#.####.# # # ## # #
+# # # # # # #
+# $ # # ### # ## #
+# # # # #.$ # # #
+# $$.# # ## # ## #
+#$ # # # # ## # #
+# .# # ## # # ## #
+# # # # # ## #
+## ## # # # # $.#
+### ### $.# $.# $.#
+####### # # @#
+ #############
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 95a7757e53..cf5ceccac5 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -16,6 +16,7 @@ m3u,viewers/iriverify,-
mid,viewers/midiplay,7
rmi,viewers/midiplay,7
rsp,viewers/searchengine,8
+sok,rocks/sokoban,1
pgn,rocks/chessbox,1
ss,rocks/sudoku,1
wav,viewers/wav2wv,-