summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/sokoban.c205
-rw-r--r--apps/plugins/sokoban.levels (renamed from apps/plugins/sokobanlevels.sok)38
-rw-r--r--manual/plugins/sokoban.tex44
-rwxr-xr-xtools/buildzip.pl2
4 files changed, 196 insertions, 93 deletions
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 976f552dbd..a9ac711b67 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -33,8 +33,9 @@ extern const fb_data sokoban_tiles[];
33 33
34#define SOKOBAN_TITLE "Sokoban" 34#define SOKOBAN_TITLE "Sokoban"
35 35
36#define SOKOBAN_LEVELS_FILE PLUGIN_DIR "/sokobanlevels.sok" 36#define SOKOBAN_LEVELS_FILE PLUGIN_DIR "/sokoban.levels"
37#define SOKOBAN_SAVE_FILE PLUGIN_DIR "/sokobansave.sok" 37#define SOKOBAN_SAVE_FILE PLUGIN_DIR "/sokoban.save"
38#define SOKOBAN_SAVE_FOLDER "/games"
38 39
39/* Magnify is the number of pixels for each block. 40/* Magnify is the number of pixels for each block.
40 * Set dynamically so all targets can support levels 41 * Set dynamically so all targets can support levels
@@ -112,6 +113,7 @@ extern const fb_data sokoban_tiles[];
112#define SOKOBAN_LEVEL_DOWN BUTTON_F1 113#define SOKOBAN_LEVEL_DOWN BUTTON_F1
113#define SOKOBAN_LEVEL_REPEAT BUTTON_F2 114#define SOKOBAN_LEVEL_REPEAT BUTTON_F2
114#define SOKOBAN_LEVEL_UP BUTTON_F3 115#define SOKOBAN_LEVEL_UP BUTTON_F3
116#define SOKOBAN_PAUSE BUTTON_PLAY
115#define BUTTON_SAVE BUTTON_ON 117#define BUTTON_SAVE BUTTON_ON
116#define BUTTON_SAVE_NAME "ON" 118#define BUTTON_SAVE_NAME "ON"
117 119
@@ -125,6 +127,7 @@ extern const fb_data sokoban_tiles[];
125#define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT) 127#define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
126#define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP) 128#define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
127#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT) 129#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
130#define SOKOBAN_PAUSE BUTTON_MENU
128#define BUTTON_SAVE BUTTON_MENU 131#define BUTTON_SAVE BUTTON_MENU
129#define BUTTON_SAVE_NAME "MENU" 132#define BUTTON_SAVE_NAME "MENU"
130 133
@@ -138,10 +141,11 @@ extern const fb_data sokoban_tiles[];
138#define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN) 141#define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
139#define SOKOBAN_LEVEL_REPEAT BUTTON_ON 142#define SOKOBAN_LEVEL_REPEAT BUTTON_ON
140#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP) 143#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
144#define SOKOBAN_PAUSE BUTTON_ON
141#define BUTTON_SAVE BUTTON_MODE 145#define BUTTON_SAVE BUTTON_MODE
142#define BUTTON_SAVE_NAME "MODE" 146#define BUTTON_SAVE_NAME "MODE"
143 147
144#define SOKOBAN_RC_QUIT BUTTON_RC_STOP 148#define SOKOBAN_RC_MENU BUTTON_RC_STOP
145 149
146#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ 150#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
147 (CONFIG_KEYPAD == IPOD_3G_PAD) 151 (CONFIG_KEYPAD == IPOD_3G_PAD)
@@ -153,19 +157,21 @@ extern const fb_data sokoban_tiles[];
153#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY) 157#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
154#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT) 158#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
155#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT) 159#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
160#define SOKOBAN_PAUSE BUTTON_SELECT
156#define BUTTON_SAVE BUTTON_SELECT 161#define BUTTON_SAVE BUTTON_SELECT
157#define BUTTON_SAVE_NAME "SELECT" 162#define BUTTON_SAVE_NAME "SELECT"
158 163
159/* FIXME: if/when simultaneous button presses work for X5, 164/* FIXME: if/when simultaneous button presses work for X5/M5,
160 * add redo & level repeat */ 165 * add level up/down */
161#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD 166#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
162#define SOKOBAN_UP BUTTON_UP 167#define SOKOBAN_UP BUTTON_UP
163#define SOKOBAN_DOWN BUTTON_DOWN 168#define SOKOBAN_DOWN BUTTON_DOWN
164#define SOKOBAN_MENU BUTTON_POWER 169#define SOKOBAN_MENU BUTTON_POWER
165#define SOKOBAN_UNDO_PRE BUTTON_SELECT 170#define SOKOBAN_UNDO_PRE BUTTON_SELECT
166#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) 171#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
167#define SOKOBAN_LEVEL_DOWN BUTTON_REC 172#define SOKOBAN_LEVEL_REPEAT BUTTON_REC
168#define SOKOBAN_LEVEL_UP BUTTON_PLAY 173#define SOKOBAN_REDO BUTTON_PLAY
174#define SOKOBAN_PAUSE BUTTON_PLAY
169#define BUTTON_SAVE BUTTON_SELECT 175#define BUTTON_SAVE BUTTON_SELECT
170#define BUTTON_SAVE_NAME "SELECT" 176#define BUTTON_SAVE_NAME "SELECT"
171 177
@@ -179,6 +185,7 @@ extern const fb_data sokoban_tiles[];
179#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN) 185#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
180#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT) 186#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
181#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP) 187#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
188#define SOKOBAN_PAUSE BUTTON_PLAY
182#define BUTTON_SAVE BUTTON_PLAY 189#define BUTTON_SAVE BUTTON_PLAY
183#define BUTTON_SAVE_NAME "PLAY" 190#define BUTTON_SAVE_NAME "PLAY"
184 191
@@ -191,6 +198,7 @@ extern const fb_data sokoban_tiles[];
191#define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN 198#define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
192#define SOKOBAN_LEVEL_REPEAT BUTTON_MENU 199#define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
193#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP 200#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
201#define SOKOBAN_PAUSE BUTTON_SELECT
194#define BUTTON_SAVE BUTTON_SELECT 202#define BUTTON_SAVE BUTTON_SELECT
195#define BUTTON_SAVE_NAME "SELECT" 203#define BUTTON_SAVE_NAME "SELECT"
196 204
@@ -204,6 +212,7 @@ extern const fb_data sokoban_tiles[];
204#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN) 212#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
205#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT) 213#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
206#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP) 214#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
215#define SOKOBAN_PAUSE BUTTON_SELECT
207#define BUTTON_SAVE BUTTON_SELECT 216#define BUTTON_SAVE BUTTON_SELECT
208#define BUTTON_SAVE_NAME "SELECT" 217#define BUTTON_SAVE_NAME "SELECT"
209 218
@@ -218,7 +227,7 @@ extern const fb_data sokoban_tiles[];
218 227
219/* Level data & stats */ 228/* Level data & stats */
220struct LevelInfo { 229struct LevelInfo {
221 short index; /* Level index (level number - 1) */ 230 int index; /* Level index (level number - 1) */
222 int moves; /* Moves & pushes for the stats */ 231 int moves; /* Moves & pushes for the stats */
223 int pushes; 232 int pushes;
224 short boxes_to_go; /* Number of unplaced boxes remaining in level */ 233 short boxes_to_go; /* Number of unplaced boxes remaining in level */
@@ -294,7 +303,7 @@ static void get_delta(char direction, short *d_r, short *d_c)
294 } 303 }
295} 304}
296 305
297static void undo(void) 306static bool undo(void)
298{ 307{
299 char undo; 308 char undo;
300 short r, c; 309 short r, c;
@@ -304,7 +313,7 @@ static void undo(void)
304 313
305 /* If no more undos or we've wrapped all the way around, quit */ 314 /* If no more undos or we've wrapped all the way around, quit */
306 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max) 315 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
307 return; 316 return false;
308 317
309 /* Move to previous undo in the list */ 318 /* Move to previous undo in the list */
310 if (undo_info.current == 0 && undo_info.count > 1) 319 if (undo_info.current == 0 && undo_info.count > 1)
@@ -356,7 +365,7 @@ static void undo(void)
356 365
357 current_info.level.moves--; 366 current_info.level.moves--;
358 367
359 return; 368 return true;
360} 369}
361 370
362static void add_undo(char undo) 371static void add_undo(char undo)
@@ -844,9 +853,22 @@ static void draw_level(void)
844static bool save(char *filename, bool solution) 853static bool save(char *filename, bool solution)
845{ 854{
846 int fd; 855 int fd;
856 char *loc;
857 DIR *dir;
858 char dirname[MAX_PATH];
847 859
848 rb->splash(0, "Saving..."); 860 rb->splash(0, "Saving...");
849 861
862 /* Create dir if it doesn't exist */
863 if ((loc = rb->strrchr(filename, '/')) != NULL) {
864 rb->strncpy(dirname, filename, loc - filename);
865 dirname[loc - filename] = '\0';
866 if(!(dir = rb->opendir(dirname)))
867 rb->mkdir(dirname);
868 else
869 rb->closedir(dir);
870 }
871
850 if (filename[0] == '\0' || 872 if (filename[0] == '\0' ||
851 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC)) < 0) { 873 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
852 rb->splash(HZ*2, "Unable to open %s", filename); 874 rb->splash(HZ*2, "Unable to open %s", filename);
@@ -874,11 +896,13 @@ static bool save(char *filename, bool solution)
874static bool load(char *filename, bool silent) 896static bool load(char *filename, bool silent)
875{ 897{
876 int fd; 898 int fd;
877 int i = 0, n; 899 int i, n;
878 int len; 900 int len;
879 bool play_solution;
880 int button; 901 int button;
881 int step_delay = HZ/4; 902 bool play_solution;
903 bool paused = false;
904 unsigned short speed = 2;
905 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
882 906
883 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) { 907 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
884 if (!silent) 908 if (!silent)
@@ -934,11 +958,12 @@ static bool load(char *filename, bool silent)
934 n = n*10 + buf[i] - '0'; 958 n = n*10 + buf[i] - '0';
935 current_info.level.index = n - 1; 959 current_info.level.index = n - 1;
936 960
937 /* Get current undo index */ 961 /* Get undo index */
938 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++) 962 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
939 n = n*10 + buf[i] - '0'; 963 n = n*10 + buf[i] - '0';
940 if (n > len) 964 if (n > len)
941 n = len; 965 n = len;
966 undo_info.max = len;
942 967
943 if (current_info.level.index < 0) { 968 if (current_info.level.index < 0) {
944 if (!silent) 969 if (!silent)
@@ -958,40 +983,78 @@ static bool load(char *filename, bool silent)
958 if (play_solution) { 983 if (play_solution) {
959 rb->lcd_clear_display(); 984 rb->lcd_clear_display();
960 update_screen(); 985 update_screen();
961 rb->sleep(2*step_delay); 986 rb->sleep(2*delay[speed]);
962 987
963 /* Replay solution until the end or quit button is pressed */ 988 /* Replay solution until menu button is pressed */
964 for (i = 0; i < len; i++) { 989 i = 0;
965 if (!move(undo_info.history[i], true)) { 990 while (true) {
966 n = i; 991 if (i < len) {
967 break; 992 if (!move(undo_info.history[i], true)) {
968 } 993 n = i;
994 break;
995 }
996 rb->lcd_clear_display();
997 update_screen();
998 i++;
999 } else
1000 paused = true;
969 1001
970 rb->lcd_clear_display(); 1002 rb->sleep(delay[speed]);
971 update_screen();
972 rb->sleep(step_delay);
973 1003
974 /* Ignore keypresses except for quit & changing speed */ 1004 while ((button = rb->button_get(false)) || paused) {
975 while ((button = rb->button_get(false)) != BUTTON_NONE) {
976 switch (button) { 1005 switch (button) {
977 case SOKOBAN_MENU: 1006 case SOKOBAN_MENU:
978 /* Pretend the level is complete so we'll quit */ 1007 /* Pretend the level is complete so we'll quit */
979 current_info.level.boxes_to_go = 0; 1008 current_info.level.boxes_to_go = 0;
980 return true; 1009 return true;
981 1010
1011 case SOKOBAN_PAUSE:
1012 /* Toggle pause state */
1013 paused = !paused;
1014 break;
1015
1016 case BUTTON_LEFT:
1017 case BUTTON_LEFT | BUTTON_REPEAT:
1018 /* Go back one move */
1019 if (paused) {
1020 if (undo())
1021 i--;
1022 rb->lcd_clear_display();
1023 update_screen();
1024 }
1025 break;
1026
1027 case BUTTON_RIGHT:
1028 case BUTTON_RIGHT | BUTTON_REPEAT:
1029 /* Go forward one move */
1030 if (paused) {
1031 if (redo())
1032 i++;
1033 rb->lcd_clear_display();
1034 update_screen();
1035 }
1036 break;
1037
982 case SOKOBAN_UP: 1038 case SOKOBAN_UP:
983 if (step_delay > HZ/12) 1039 case SOKOBAN_UP | BUTTON_REPEAT:
984 step_delay = 5*step_delay/6; 1040 /* Speed up */
1041 if (speed < sizeof(delay)/sizeof(int) - 1)
1042 speed++;
985 break; 1043 break;
986 1044
987 case SOKOBAN_DOWN: 1045 case SOKOBAN_DOWN:
988 if (step_delay < 3*HZ/4) 1046 case SOKOBAN_DOWN | BUTTON_REPEAT:
989 step_delay = 6*step_delay/5; 1047 /* Slow down */
1048 if (speed > 0)
1049 speed--;
990 } 1050 }
1051
1052 if (paused)
1053 rb->sleep(HZ/33);
991 } 1054 }
992 } 1055 }
993 1056
994 /* If level complete, wait for keypress before quitting */ 1057 /* If level is complete, wait for keypress before quitting */
995 if (current_info.level.boxes_to_go == 0) 1058 if (current_info.level.boxes_to_go == 0)
996 rb->button_get(true); 1059 rb->button_get(true);
997 1060
@@ -1008,7 +1071,6 @@ static bool load(char *filename, bool silent)
1008 rb->lcd_clear_display(); 1071 rb->lcd_clear_display();
1009 } 1072 }
1010 1073
1011 undo_info.max = len;
1012 undo_info.current = n; 1074 undo_info.current = n;
1013 } 1075 }
1014 1076
@@ -1022,9 +1084,10 @@ static int sokoban_menu(void)
1022 int i; 1084 int i;
1023 bool menu_quit; 1085 bool menu_quit;
1024 int start_selected = 0; 1086 int start_selected = 0;
1087 int prev_level = current_info.level.index;
1025 1088
1026 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL, 1089 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
1027 "Resume", "Audio Playback", "Keys", 1090 "Resume", "Select Level", "Audio Playback", "Keys",
1028 "Load Default Level Set", "Quit Without Saving", 1091 "Load Default Level Set", "Quit Without Saving",
1029 "Save Progress & Quit"); 1092 "Save Progress & Quit");
1030 1093
@@ -1036,12 +1099,25 @@ static int sokoban_menu(void)
1036 case 0: /* Resume */ 1099 case 0: /* Resume */
1037 break; 1100 break;
1038 1101
1039 case 1: /* Audio playback control */ 1102 case 1: /* Select level */
1103 current_info.level.index++;
1104 rb->set_int("Select Level", "", UNIT_INT,
1105 &current_info.level.index, NULL, 1, 1,
1106 current_info.max_level, NULL);
1107 current_info.level.index--;
1108 if (prev_level != current_info.level.index) {
1109 init_undo();
1110 draw_level();
1111 } else
1112 menu_quit = false;
1113 break;
1114
1115 case 2: /* Audio playback control */
1040 playback_control(rb); 1116 playback_control(rb);
1041 menu_quit = false; 1117 menu_quit = false;
1042 break; 1118 break;
1043 1119
1044 case 2: /* Keys */ 1120 case 3: /* Keys */
1045 FOR_NB_SCREENS(i) 1121 FOR_NB_SCREENS(i)
1046 rb->screens[i]->clear_display(); 1122 rb->screens[i]->clear_display();
1047 rb->lcd_setfont(SOKOBAN_FONT); 1123 rb->lcd_setfont(SOKOBAN_FONT);
@@ -1079,8 +1155,8 @@ static int sokoban_menu(void)
1079#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD 1155#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1080 rb->lcd_putsxy(3, 6, "[POWER] Menu"); 1156 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1081 rb->lcd_putsxy(3, 16, "[SELECT] Undo"); 1157 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1082 rb->lcd_putsxy(3, 26, "[REC] Previous Level"); 1158 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1083 rb->lcd_putsxy(3, 36, "[PLAY] Next Level"); 1159 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
1084#elif CONFIG_KEYPAD == IRIVER_H10_PAD 1160#elif CONFIG_KEYPAD == IRIVER_H10_PAD
1085 rb->lcd_putsxy(3, 6, "[POWER] Menu"); 1161 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1086 rb->lcd_putsxy(3, 16, "[REW] Undo"); 1162 rb->lcd_putsxy(3, 16, "[REW] Undo");
@@ -1117,17 +1193,17 @@ static int sokoban_menu(void)
1117 menu_quit = false; 1193 menu_quit = false;
1118 break; 1194 break;
1119 1195
1120 case 3: /* Load default levelset */ 1196 case 4: /* Load default levelset */
1121 init_boards(); 1197 init_boards();
1122 if (!read_levels(true)) 1198 if (!read_levels(true))
1123 return 4; 1199 return 5; /* Quit */
1124 load_level(); 1200 load_level();
1125 break; 1201 break;
1126 1202
1127 case 4: /* Quit */ 1203 case 5: /* Quit */
1128 break; 1204 break;
1129 1205
1130 case 5: /* Save & quit */ 1206 case 6: /* Save & quit */
1131 save(SOKOBAN_SAVE_FILE, false); 1207 save(SOKOBAN_SAVE_FILE, false);
1132 rb->reload_directory(); 1208 rb->reload_directory();
1133 } 1209 }
@@ -1163,13 +1239,13 @@ static bool sokoban_loop(void)
1163 1239
1164 switch(button) 1240 switch(button)
1165 { 1241 {
1166#ifdef SOKOBAN_RC_QUIT 1242#ifdef SOKOBAN_RC_MENU
1167 case SOKOBAN_RC_QUIT: 1243 case SOKOBAN_RC_MENU:
1168#endif 1244#endif
1169 case SOKOBAN_MENU: 1245 case SOKOBAN_MENU:
1170 switch (sokoban_menu()) { 1246 switch (sokoban_menu()) {
1171 case 4: /* Quit */ 1247 case 5: /* Quit */
1172 case 5: /* Save & quit */ 1248 case 6: /* Save & quit */
1173 return PLUGIN_OK; 1249 return PLUGIN_OK;
1174 } 1250 }
1175 update_screen(); 1251 update_screen();
@@ -1292,7 +1368,7 @@ static bool sokoban_loop(void)
1292 rb->button_clear_queue(); 1368 rb->button_clear_queue();
1293 1369
1294 /* Display for 4 seconds or until new keypress */ 1370 /* Display for 4 seconds or until new keypress */
1295 for (i = 0; i < 75; i++) { 1371 for (i = 0; i < 80; i++) {
1296 rb->sleep(HZ/20); 1372 rb->sleep(HZ/20);
1297 button = rb->button_get(false); 1373 button = rb->button_get(false);
1298 if (button && !(button & BUTTON_REL) && 1374 if (button && !(button & BUTTON_REL) &&
@@ -1302,16 +1378,25 @@ static bool sokoban_loop(void)
1302 1378
1303 if (button == BUTTON_SAVE) { 1379 if (button == BUTTON_SAVE) {
1304 if (undo_info.count < MAX_UNDOS) { 1380 if (undo_info.count < MAX_UNDOS) {
1305 /* Default filename to current levelset plus 1381 /* Set filename to current levelset plus level number
1306 * level number and .sok extension */ 1382 * and .sok extension. Use SAVE_FOLDER if using the
1307 loc = rb->strrchr(buffered_boards.filename, '.'); 1383 * default levelset, since it's in a hidden folder. */
1308 if (loc != NULL) 1384 if (rb->strcmp(buffered_boards.filename,
1309 *loc = '\0'; 1385 SOKOBAN_LEVELS_FILE) == 0) {
1310 rb->snprintf(buf, sizeof(buf), "%s.%d.sok", 1386 rb->snprintf(buf, sizeof(buf),
1311 buffered_boards.filename, 1387 "%s/sokoban.%d.sok",
1312 current_info.level.index + 1); 1388 SOKOBAN_SAVE_FOLDER,
1313 if (loc != NULL) 1389 current_info.level.index + 1);
1314 *loc = '.'; 1390 } else {
1391 if ((loc = rb->strrchr(buffered_boards.filename,
1392 '.')) != NULL)
1393 *loc = '\0';
1394 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1395 buffered_boards.filename,
1396 current_info.level.index + 1);
1397 if (loc != NULL)
1398 *loc = '.';
1399 }
1315 1400
1316 if (!rb->kbd_input(buf, MAX_PATH)) 1401 if (!rb->kbd_input(buf, MAX_PATH))
1317 save(buf, true); 1402 save(buf, true);
@@ -1355,8 +1440,8 @@ static bool sokoban_loop(void)
1355 current_info.level.index = 0; 1440 current_info.level.index = 0;
1356 1441
1357 switch (sokoban_menu()) { 1442 switch (sokoban_menu()) {
1358 case 4: /* Quit */ 1443 case 5: /* Quit */
1359 case 5: /* Save & quit */ 1444 case 6: /* Save & quit */
1360 return PLUGIN_OK; 1445 return PLUGIN_OK;
1361 } 1446 }
1362 } 1447 }
diff --git a/apps/plugins/sokobanlevels.sok b/apps/plugins/sokoban.levels
index 869dcfe76e..1d8ab7a8ed 100644
--- a/apps/plugins/sokobanlevels.sok
+++ b/apps/plugins/sokoban.levels
@@ -637,8 +637,8 @@
637 637
638 #### 638 ####
639 #### # # 639 #### # #
640 ### @###$ # 640 ### ###$ #
641 ## $ # 641 ## @ $ #
642 ## $ $$## ## 642 ## $ $$## ##
643 # #$## # 643 # #$## #
644 # # $ $$ # ### 644 # # $ $$ # ###
@@ -656,7 +656,7 @@
656# # ###### # 656# # ###### #
657# # $ $ $ $# # 657# # $ $ $ $# #
658# # $@$ ## ## 658# # $@$ ## ##
659# # $ $ $###...# 659# # #$ $ $###...#
660# # $ $ ##...# 660# # $ $ ##...#
661# ###$$$ $ ##...# 661# ###$$$ $ ##...#
662# # ## ##...# 662# # ## ##...#
@@ -780,14 +780,14 @@
780# # $ #@ # 780# # $ #@ #
781# #######$#### ### 781# #######$#### ###
782# # ## # #$ ..# 782# # ## # #$ ..#
783# # $ # # #.# 783# # $ $ # # #.#
784# # $ # #$ ..# 784# # $ # #$ ..#
785# # ### ## #.# 785# # ### ## #.#
786# ### # # #$ ..# 786# ### # # #$ ..#
787# # # #### #.# 787# # # $#### #.#
788# #$ $ $ #$ ..# 788# #$ $ $ #* ..#
789# $ # $ $ # #.# 789# $ # $ $ # #.#
790#### $### #$ ..# 790#### $### #* ..#
791 # $$ ###....# 791 # $$ ###....#
792 # ## ###### 792 # ## ######
793 ######## 793 ########
@@ -847,10 +847,10 @@
847# $ $ $ # 847# $ $ $ #
848## #### # $ # # 848## #### # $ # #
849# # ## # ## 849# # ## # ##
850# $# # ## ### ## 850# $# # ## ### #
851# $ #$### ### ## 851# $ #$### # # #
852### $ # # ### ## 852### $ # # ### #
853### $ ## # # ## 853 ## $ ## # # ##
854 # $ # $ $ $ # 854 # $ # $ $ $ #
855 # $ $#$$$ # # 855 # $ $#$$$ # #
856 # # $ ##### 856 # # $ #####
@@ -869,7 +869,7 @@
869# # #$#$### 869# # #$#$###
870# #### #$$$$$ # 870# #### #$$$$$ #
871# # $ # # 871# # $ # #
872# # ## ### 872# # ## ## ###
873# ######$###### $ # 873# ######$###### $ #
874# # # # 874# # # #
875########## ##### 875########## #####
@@ -1049,7 +1049,7 @@
1049#...... ######### 1049#...... #########
1050#...... # ## # 1050#...... # ## #
1051#..### $ $ # 1051#..### $ $ #
1052#... $ $ # ## # 1052#... $ $ # ### #
1053#...#$##### # # 1053#...#$##### # #
1054### # #$ #$ # 1054### # #$ #$ #
1055 # $$ $ $ $## # 1055 # $$ $ $ $## #
@@ -1130,8 +1130,8 @@
1130### ......# $$ ## 1130### ......# $$ ##
1131# ......# # # 1131# ......# # #
1132# # ......#$ $ # 1132# # ......#$ $ #
1133# #$...... $$# $ # 1133# # ...... $$# $ #
1134# ### ###$ $ ## 1134# $ ### ###$ $ ##
1135### $ $ $ $ # 1135### $ $ $ $ #
1136 # $ $ $ $ # 1136 # $ $ $ $ #
1137 ###### ###### 1137 ###### ######
@@ -1202,7 +1202,7 @@
1202## $ $ ##### 1202## $ $ #####
1203# ## ## ##...# 1203# ## ## ##...#
1204# #$$ $ $$#$##...# 1204# #$$ $ $$#$##...#
1205# # @ # ...# 1205# # @ # ...#
1206# $# ###$$ ...# 1206# $# ###$$ ...#
1207# $ $$ $ ##....# 1207# $ $$ $ ##....#
1208###$ ####### 1208###$ #######
@@ -1245,16 +1245,16 @@
1245 ############ 1245 ############
1246 ##.. # # 1246 ##.. # #
1247 ##..* $ $ # 1247 ##..* $ $ #
1248 ##..*.# # # $## 1248 ##..*.# # #$ ##
1249 #..*.# # # $ # 1249 #..*.# # # $ #
1250####...# # # # 1250####...# # # #
1251# ## # # 1251# ## # #
1252# @$ $ ### # ## 1252# @$ $ ### # # ##
1253# $ $ # # # 1253# $ $ # # #
1254###$$ # # # # # 1254###$$ # # # # #
1255 # $ # # ##### 1255 # $ # # #####
1256 # $# ##### # 1256 # $# ##### #
1257 #$ # # # # 1257 #$ # # # #
1258 # ### ## # 1258 # ### ## #
1259 # # # ## 1259 # # # ##
1260 #### ###### 1260 #### ######
diff --git a/manual/plugins/sokoban.tex b/manual/plugins/sokoban.tex
index 246eceda51..be0cfcd3de 100644
--- a/manual/plugins/sokoban.tex
+++ b/manual/plugins/sokoban.tex
@@ -13,12 +13,14 @@ information about the level format, see
13 13
14\begin{table} 14\begin{table}
15\begin{btnmap}{}{} 15\begin{btnmap}{}{}
16\multicolumn{2}{c}{\textbf{In game}} \\
17\hline
16\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD} 18\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD}
17 {\ButtonUp, \ButtonDown,} 19 {\ButtonUp, \ButtonDown, }%
18\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu, \ButtonPlay,} 20\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu, \ButtonPlay, }%
19\opt{IRIVER_H10_PAD}{\ButtonScrollUp, \ButtonScrollDown,} 21\opt{IRIVER_H10_PAD}{\ButtonScrollUp, \ButtonScrollDown, }%
20 \ButtonLeft, \ButtonRight 22 \ButtonLeft, \ButtonRight
21 & Move the ``sokoban'' up, down, left or right\\ 23 & Move the ``sokoban'' up, down, left, or right\\
22\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff} 24\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff}
23\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonMenu} 25\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonMenu}
24\opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonPower} 26\opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonPower}
@@ -29,11 +31,10 @@ information about the level format, see
29\opt{IPOD_4G_PAD,IPOD_3G_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonSelect} 31\opt{IPOD_4G_PAD,IPOD_3G_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonSelect}
30\opt{IRIVER_H10_PAD}{\ButtonRew} 32\opt{IRIVER_H10_PAD}{\ButtonRew}
31 & Undo last movement \\ 33 & Undo last movement \\
32\opt{RECORDER_PAD,ARCHOS_AV300_PAD}{\ButtonPlay} 34\opt{RECORDER_PAD,ARCHOS_AV300_PAD,IAUDIO_X5_PAD}{\ButtonPlay}
33\opt{ONDIO_PAD}{\ButtonMenu+\ButtonDown} 35\opt{ONDIO_PAD}{\ButtonMenu+\ButtonDown}
34\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonMode} 36\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonMode}
35\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonPlay} 37\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonPlay}
36\opt{IAUDIO_X5_PAD}{n/a}
37\opt{IRIVER_H10_PAD}{\ButtonFF} 38\opt{IRIVER_H10_PAD}{\ButtonFF}
38\opt{GIGABEAT_PAD}{\ButtonA} 39\opt{GIGABEAT_PAD}{\ButtonA}
39\opt{SANSA_E200_PAD}{\ButtonRec} 40\opt{SANSA_E200_PAD}{\ButtonRec}
@@ -42,7 +43,7 @@ information about the level format, see
42\opt{ONDIO_PAD}{\ButtonMenu+\ButtonLeft} 43\opt{ONDIO_PAD}{\ButtonMenu+\ButtonLeft}
43\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn+\ButtonDown} 44\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn+\ButtonDown}
44\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonLeft} 45\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonLeft}
45\opt{IAUDIO_X5_PAD}{\ButtonRec} 46\opt{IAUDIO_X5_PAD}{n/a}
46\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonScrollDown} 47\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonScrollDown}
47\opt{GIGABEAT_PAD}{\ButtonVolDown} 48\opt{GIGABEAT_PAD}{\ButtonVolDown}
48\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonDown} 49\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonDown}
@@ -50,7 +51,8 @@ information about the level format, see
50\opt{RECORDER_PAD,ARCHOS_AV300_PAD}{\ButtonFTwo} 51\opt{RECORDER_PAD,ARCHOS_AV300_PAD}{\ButtonFTwo}
51\opt{ONDIO_PAD}{\ButtonMenu+\ButtonUp} 52\opt{ONDIO_PAD}{\ButtonMenu+\ButtonUp}
52\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn} 53\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn}
53\opt{IPOD_4G_PAD,IPOD_3G_PAD,IAUDIO_X5_PAD}{n/a} 54\opt{IPOD_4G_PAD,IPOD_3G_PAD}{n/a}
55\opt{IAUDIO_X5_PAD}{\ButtonRec}
54\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonRight} 56\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonRight}
55\opt{GIGABEAT_PAD}{\ButtonMenu} 57\opt{GIGABEAT_PAD}{\ButtonMenu}
56\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonRight} 58\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonRight}
@@ -59,16 +61,30 @@ information about the level format, see
59\opt{ONDIO_PAD}{\ButtonMenu+\ButtonRight} 61\opt{ONDIO_PAD}{\ButtonMenu+\ButtonRight}
60\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn+\ButtonUp} 62\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn+\ButtonUp}
61\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonRight} 63\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonRight}
62\opt{IAUDIO_X5_PAD}{\ButtonPlay} 64\opt{IAUDIO_X5_PAD}{n/a}
63\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonScrollUp} 65\opt{IRIVER_H10_PAD}{\ButtonPlay+\ButtonScrollUp}
64\opt{GIGABEAT_PAD}{\ButtonVolUp} 66\opt{GIGABEAT_PAD}{\ButtonVolUp}
65\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonUp} 67\opt{SANSA_E200_PAD}{\ButtonSelect+\ButtonUp}
66 & Go to next level \\ 68 & Go to next level \\
69\hline
70\multicolumn{2}{c}{\textbf{Solution playback}} \\
71\hline
72\opt{RECORDER_PAD,ARCHOS_AV300_PAD,IAUDIO_X5_PAD,IRIVER_H10_PAD}{\ButtonPlay}
73\opt{ONDIO_PAD}{\ButtonMenu}
74\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn}
75\opt{IPOD_4G_PAD,IPOD_3G_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonSelect}
76 & Pause/resume \\
67\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD} 77\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,SANSA_E200_PAD}
68 {\ButtonUp/\ButtonDown,} 78 {\ButtonUp/\ButtonDown}
69\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu/\ButtonPlay,} 79\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu/\ButtonPlay}
70\opt{IRIVER_H10_PAD}{\ButtonScrollUp/\ButtonScrollDown,} 80\opt{IRIVER_H10_PAD}{\ButtonScrollUp/\ButtonScrollDown}
71 & Increase/decrease solution playback speed 81 & Increase/decrease playback speed \\
82 \ButtonLeft/\ButtonRight
83 & Go backward/forward (while paused) \\
84\opt{RECORDER_PAD,ARCHOS_AV300_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff}
85\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonMenu}
86\opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,GIGABEAT_PAD,SANSA_E200_PAD}{\ButtonPower}
87 & Quit \\
72\end{btnmap} 88\end{btnmap}
73\end{table} 89\end{table}
74 90
@@ -77,3 +93,5 @@ Some places where can you can find level sets:
77\item \url{http://www.sourcecode.se/sokoban/levels.php} 93\item \url{http://www.sourcecode.se/sokoban/levels.php}
78\item \url{http://sokobano.de/en/levels.php} 94\item \url{http://sokobano.de/en/levels.php}
79\end{itemize} 95\end{itemize}
96Note that some level sets may contain levels that are too large for this
97version of Sokoban and are unplayable as a result.
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index 404342b68e..8c78b82170 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -329,7 +329,7 @@ STOP
329 `cp $ROOT/apps/tagnavi.config .rockbox/`; 329 `cp $ROOT/apps/tagnavi.config .rockbox/`;
330 330
331 if($bitmap) { 331 if($bitmap) {
332 `cp $ROOT/apps/plugins/sokobanlevels.sok .rockbox/rocks/`; # sokoban levels 332 `cp $ROOT/apps/plugins/sokoban.levels .rockbox/rocks/`; # sokoban levels
333 `cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels 333 `cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels
334 } 334 }
335 335