summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/debug_menu.c121
-rw-r--r--apps/fileop.c159
-rw-r--r--apps/filetree.c6
-rw-r--r--apps/gui/list.c28
-rw-r--r--apps/gui/option_select.c24
-rw-r--r--apps/gui/skin_engine/skin_tokens.c2
-rw-r--r--apps/gui/usb_screen.c11
-rw-r--r--apps/gui/wps.c4
-rw-r--r--apps/lang/InvalidVoice_english.talkbin2707 -> 4116 bytes
-rw-r--r--apps/lang/bulgarian.lang5737
-rw-r--r--apps/lang/chinese-simp.lang5150
-rw-r--r--apps/lang/czech.lang9
-rw-r--r--apps/lang/deutsch.lang43
-rw-r--r--apps/lang/english-us.lang137
-rw-r--r--apps/lang/english.lang137
-rw-r--r--apps/lang/francais.lang2624
-rw-r--r--apps/lang/italiano.lang111
-rw-r--r--apps/lang/korean.lang12230
-rw-r--r--apps/lang/nederlands.lang9
-rw-r--r--apps/lang/polski.lang205
-rw-r--r--apps/lang/russian.lang1436
-rw-r--r--apps/lang/slovak.lang9
-rw-r--r--apps/lang/srpski.lang9
-rw-r--r--apps/lang/turkce.lang9
-rw-r--r--apps/main.c22
-rw-r--r--apps/menus/display_menu.c5
-rw-r--r--apps/menus/main_menu.c18
-rw-r--r--apps/misc.c25
-rw-r--r--apps/misc.h23
-rw-r--r--apps/onplay.c24
-rw-r--r--apps/onplay.h7
-rw-r--r--apps/pcmbuf.c3
-rw-r--r--apps/playback.c160
-rw-r--r--apps/playlist.c15
-rw-r--r--apps/playlist.h2
-rw-r--r--apps/playlist_viewer.c208
-rw-r--r--apps/plugin.c42
-rw-r--r--apps/plugin.h30
-rw-r--r--apps/plugins/CATEGORIES3
-rw-r--r--apps/plugins/SOURCES2
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.c1
-rw-r--r--apps/plugins/cue_playlist.c375
-rw-r--r--apps/plugins/file_picker.c295
-rw-r--r--apps/plugins/keybox.c1
-rw-r--r--apps/plugins/lib/arg_helper.c25
-rw-r--r--apps/plugins/lib/arg_helper.h4
-rw-r--r--apps/plugins/lib/id3.c2
-rw-r--r--apps/plugins/lib/mul_id3.c175
-rw-r--r--apps/plugins/lib/mul_id3.h32
-rw-r--r--apps/plugins/lua/lua.make2
-rw-r--r--apps/plugins/lua/rocklua.c17
-rw-r--r--apps/plugins/open_plugins.c1
-rw-r--r--apps/plugins/pictureflow/pictureflow.c2
-rw-r--r--apps/plugins/pitch_screen.c5
-rw-r--r--apps/plugins/playing_time.c592
-rw-r--r--apps/plugins/properties.c433
-rw-r--r--apps/plugins/puzzles/README.rockbox54
-rw-r--r--apps/plugins/puzzles/SOURCES14
-rw-r--r--apps/plugins/puzzles/SOURCES.games11
-rw-r--r--apps/plugins/puzzles/SOURCES.rockbox5
-rw-r--r--apps/plugins/puzzles/compress.c3
-rw-r--r--apps/plugins/puzzles/dummy/nullhelp.c8
-rwxr-xr-xapps/plugins/puzzles/genhelp.sh27
-rw-r--r--apps/plugins/puzzles/help.h4
-rw-r--r--apps/plugins/puzzles/help/blackbox.c654
-rw-r--r--apps/plugins/puzzles/help/bridges.c673
-rw-r--r--apps/plugins/puzzles/help/cube.c308
-rw-r--r--apps/plugins/puzzles/help/dominosa.c329
-rw-r--r--apps/plugins/puzzles/help/fifteen.c241
-rw-r--r--apps/plugins/puzzles/help/filling.c261
-rw-r--r--apps/plugins/puzzles/help/flip.c237
-rw-r--r--apps/plugins/puzzles/help/flood.c339
-rw-r--r--apps/plugins/puzzles/help/galaxies.c391
-rw-r--r--apps/plugins/puzzles/help/guess.c420
-rw-r--r--apps/plugins/puzzles/help/inertia.c332
-rw-r--r--apps/plugins/puzzles/help/keen.c496
-rw-r--r--apps/plugins/puzzles/help/lightup.c346
-rw-r--r--apps/plugins/puzzles/help/loopy.c425
-rw-r--r--apps/plugins/puzzles/help/magnets.c354
-rw-r--r--apps/plugins/puzzles/help/map.c503
-rw-r--r--apps/plugins/puzzles/help/mines.c495
-rw-r--r--apps/plugins/puzzles/help/mosaic.c151
-rw-r--r--apps/plugins/puzzles/help/net.c544
-rw-r--r--apps/plugins/puzzles/help/netslide.c92
-rw-r--r--apps/plugins/puzzles/help/nullgame.c1
-rw-r--r--apps/plugins/puzzles/help/palisade.c217
-rw-r--r--apps/plugins/puzzles/help/pattern.c183
-rw-r--r--apps/plugins/puzzles/help/pearl.c408
-rw-r--r--apps/plugins/puzzles/help/pegs.c268
-rw-r--r--apps/plugins/puzzles/help/range.c304
-rw-r--r--apps/plugins/puzzles/help/rect.c494
-rw-r--r--apps/plugins/puzzles/help/samegame.c348
-rw-r--r--apps/plugins/puzzles/help/signpost.c407
-rw-r--r--apps/plugins/puzzles/help/singles.c252
-rw-r--r--apps/plugins/puzzles/help/sixteen.c370
-rw-r--r--apps/plugins/puzzles/help/slant.c320
-rw-r--r--apps/plugins/puzzles/help/solo.c744
-rw-r--r--apps/plugins/puzzles/help/tents.c300
-rw-r--r--apps/plugins/puzzles/help/towers.c481
-rw-r--r--apps/plugins/puzzles/help/tracks.c274
-rw-r--r--apps/plugins/puzzles/help/twiddle.c386
-rw-r--r--apps/plugins/puzzles/help/undead.c457
-rw-r--r--apps/plugins/puzzles/help/unequal.c491
-rw-r--r--apps/plugins/puzzles/help/unruly.c264
-rw-r--r--apps/plugins/puzzles/help/untangle.c151
-rw-r--r--apps/plugins/puzzles/puzzles.make9
-rw-r--r--apps/plugins/puzzles/rbcompat.h2
-rwxr-xr-xapps/plugins/puzzles/resync.sh45
-rw-r--r--apps/plugins/puzzles/rockbox.c535
-rw-r--r--apps/plugins/puzzles/src/CMakeLists.txt291
-rw-r--r--apps/plugins/puzzles/src/LICENCE4
-rw-r--r--apps/plugins/puzzles/src/README46
-rw-r--r--apps/plugins/puzzles/src/blackbox.R19
-rw-r--r--apps/plugins/puzzles/src/blackbox.c115
-rw-r--r--apps/plugins/puzzles/src/bridges.R21
-rw-r--r--apps/plugins/puzzles/src/bridges.c210
-rw-r--r--apps/plugins/puzzles/src/combi.c37
-rw-r--r--apps/plugins/puzzles/src/cube.R19
-rw-r--r--apps/plugins/puzzles/src/cube.c110
-rw-r--r--apps/plugins/puzzles/src/devel.but5061
-rw-r--r--apps/plugins/puzzles/src/divvy.c141
-rw-r--r--apps/plugins/puzzles/src/dominosa.R24
-rw-r--r--apps/plugins/puzzles/src/dominosa.c136
-rw-r--r--apps/plugins/puzzles/src/draw-poly.c302
-rw-r--r--apps/plugins/puzzles/src/drawing.c212
-rw-r--r--apps/plugins/puzzles/src/dsf.c411
-rw-r--r--apps/plugins/puzzles/src/emcc.c978
-rw-r--r--apps/plugins/puzzles/src/fifteen.R22
-rw-r--r--apps/plugins/puzzles/src/fifteen.c259
-rw-r--r--apps/plugins/puzzles/src/filling.R24
-rw-r--r--apps/plugins/puzzles/src/filling.c198
-rw-r--r--apps/plugins/puzzles/src/flip.R21
-rw-r--r--apps/plugins/puzzles/src/flip.c89
-rw-r--r--apps/plugins/puzzles/src/flood.R19
-rw-r--r--apps/plugins/puzzles/src/flood.c184
-rw-r--r--apps/plugins/puzzles/src/galaxies.R28
-rw-r--r--apps/plugins/puzzles/src/galaxies.c734
-rw-r--r--apps/plugins/puzzles/src/grid.c1357
-rw-r--r--apps/plugins/puzzles/src/grid.h23
-rw-r--r--apps/plugins/puzzles/src/gtk.c4031
-rw-r--r--apps/plugins/puzzles/src/gtk.h7
-rw-r--r--apps/plugins/puzzles/src/guess.R19
-rw-r--r--apps/plugins/puzzles/src/guess.c221
-rw-r--r--apps/plugins/puzzles/src/hat-internal.h271
-rw-r--r--apps/plugins/puzzles/src/hat-tables.h2120
-rw-r--r--apps/plugins/puzzles/src/hat.c891
-rw-r--r--apps/plugins/puzzles/src/hat.h67
-rw-r--r--apps/plugins/puzzles/src/inertia.R19
-rw-r--r--apps/plugins/puzzles/src/inertia.c88
-rw-r--r--apps/plugins/puzzles/src/keen.R25
-rw-r--r--apps/plugins/puzzles/src/keen.c250
-rw-r--r--apps/plugins/puzzles/src/latin.c164
-rw-r--r--apps/plugins/puzzles/src/latin.h9
-rw-r--r--apps/plugins/puzzles/src/lightup.R24
-rw-r--r--apps/plugins/puzzles/src/lightup.c137
-rw-r--r--apps/plugins/puzzles/src/loopgen.c16
-rw-r--r--apps/plugins/puzzles/src/loopgen.h6
-rw-r--r--apps/plugins/puzzles/src/loopy.R31
-rw-r--r--apps/plugins/puzzles/src/loopy.c576
-rw-r--r--apps/plugins/puzzles/src/magnets.R24
-rw-r--r--apps/plugins/puzzles/src/magnets.c125
-rw-r--r--apps/plugins/puzzles/src/malloc.c53
-rw-r--r--apps/plugins/puzzles/src/map.R24
-rw-r--r--apps/plugins/puzzles/src/map.c260
-rw-r--r--apps/plugins/puzzles/src/matching.c424
-rw-r--r--apps/plugins/puzzles/src/matching.h28
-rw-r--r--apps/plugins/puzzles/src/midend.c797
-rw-r--r--apps/plugins/puzzles/src/mines.R24
-rw-r--r--apps/plugins/puzzles/src/mines.c218
-rw-r--r--apps/plugins/puzzles/src/misc.c289
-rw-r--r--apps/plugins/puzzles/src/mosaic.c1626
-rw-r--r--apps/plugins/puzzles/src/nestedvm.c486
-rw-r--r--apps/plugins/puzzles/src/net.R23
-rw-r--r--apps/plugins/puzzles/src/net.c231
-rw-r--r--apps/plugins/puzzles/src/netslide.R21
-rw-r--r--apps/plugins/puzzles/src/netslide.c105
-rw-r--r--apps/plugins/puzzles/src/no-icon.c8
-rw-r--r--apps/plugins/puzzles/src/nullfe.c75
-rw-r--r--apps/plugins/puzzles/src/nullgame.R12
-rw-r--r--apps/plugins/puzzles/src/nullgame.c313
-rw-r--r--apps/plugins/puzzles/src/obfusc.c130
-rw-r--r--apps/plugins/puzzles/src/osx-help.but14
-rw-r--r--apps/plugins/puzzles/src/palisade.R21
-rw-r--r--apps/plugins/puzzles/src/palisade.c328
-rw-r--r--apps/plugins/puzzles/src/pattern.R25
-rw-r--r--apps/plugins/puzzles/src/pattern.c224
-rw-r--r--apps/plugins/puzzles/src/pearl.R23
-rw-r--r--apps/plugins/puzzles/src/pearl.c499
-rw-r--r--apps/plugins/puzzles/src/pegs.R21
-rw-r--r--apps/plugins/puzzles/src/pegs.c172
-rw-r--r--apps/plugins/puzzles/src/penrose-internal.h289
-rw-r--r--apps/plugins/puzzles/src/penrose-legacy.c506
-rw-r--r--apps/plugins/puzzles/src/penrose-legacy.h63
-rw-r--r--apps/plugins/puzzles/src/penrose.c1271
-rw-r--r--apps/plugins/puzzles/src/penrose.h119
-rw-r--r--apps/plugins/puzzles/src/printing.c15
-rw-r--r--apps/plugins/puzzles/src/ps.c432
-rw-r--r--apps/plugins/puzzles/src/puzzles.but341
-rw-r--r--apps/plugins/puzzles/src/puzzles.h251
-rw-r--r--apps/plugins/puzzles/src/random.c6
-rw-r--r--apps/plugins/puzzles/src/range.R21
-rw-r--r--apps/plugins/puzzles/src/range.c182
-rw-r--r--apps/plugins/puzzles/src/rect.R19
-rw-r--r--apps/plugins/puzzles/src/rect.c82
-rw-r--r--apps/plugins/puzzles/src/resource.h20
-rw-r--r--apps/plugins/puzzles/src/samegame.R19
-rw-r--r--apps/plugins/puzzles/src/samegame.c182
-rw-r--r--apps/plugins/puzzles/src/signpost.R23
-rw-r--r--apps/plugins/puzzles/src/signpost.c185
-rw-r--r--apps/plugins/puzzles/src/singles.R23
-rw-r--r--apps/plugins/puzzles/src/singles.c111
-rw-r--r--apps/plugins/puzzles/src/sixteen.R19
-rw-r--r--apps/plugins/puzzles/src/sixteen.c87
-rw-r--r--apps/plugins/puzzles/src/slant.R24
-rw-r--r--apps/plugins/puzzles/src/slant.c180
-rw-r--r--apps/plugins/puzzles/src/solo.R24
-rw-r--r--apps/plugins/puzzles/src/solo.c197
-rw-r--r--apps/plugins/puzzles/src/sort.c91
-rw-r--r--apps/plugins/puzzles/src/spectre-internal.h327
-rw-r--r--apps/plugins/puzzles/src/spectre-tables-auto.h1220
-rw-r--r--apps/plugins/puzzles/src/spectre-tables-manual.h160
-rw-r--r--apps/plugins/puzzles/src/spectre.c599
-rw-r--r--apps/plugins/puzzles/src/spectre.h72
-rw-r--r--apps/plugins/puzzles/src/tents.R24
-rw-r--r--apps/plugins/puzzles/src/tents.c121
-rw-r--r--apps/plugins/puzzles/src/towers.R25
-rw-r--r--apps/plugins/puzzles/src/towers.c199
-rw-r--r--apps/plugins/puzzles/src/tracks.R24
-rw-r--r--apps/plugins/puzzles/src/tracks.c351
-rw-r--r--apps/plugins/puzzles/src/tree234.c782
-rw-r--r--apps/plugins/puzzles/src/tree234.h24
-rw-r--r--apps/plugins/puzzles/src/twiddle.R19
-rw-r--r--apps/plugins/puzzles/src/twiddle.c108
-rw-r--r--apps/plugins/puzzles/src/undead.R18
-rw-r--r--apps/plugins/puzzles/src/undead.c210
-rw-r--r--apps/plugins/puzzles/src/unequal.R25
-rw-r--r--apps/plugins/puzzles/src/unequal.c193
-rw-r--r--apps/plugins/puzzles/src/unfinished/CMakeLists.txt31
-rw-r--r--apps/plugins/puzzles/src/unfinished/README14
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.c2497
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.gap97
-rw-r--r--apps/plugins/puzzles/src/unfinished/numgame.c1294
-rw-r--r--apps/plugins/puzzles/src/unfinished/path.c866
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.c861
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.c2444
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.c1476
-rw-r--r--apps/plugins/puzzles/src/unruly.R21
-rw-r--r--apps/plugins/puzzles/src/unruly.c189
-rw-r--r--apps/plugins/puzzles/src/untangle.R21
-rw-r--r--apps/plugins/puzzles/src/untangle.c512
-rw-r--r--apps/plugins/puzzles/src/version.c1
-rw-r--r--apps/plugins/puzzles/src/version.h2
-rw-r--r--apps/plugins/puzzles/src/windows.c3821
-rw-r--r--apps/plugins/random_folder_advance_config.c1
-rw-r--r--apps/plugins/rb_info.c1
-rw-r--r--apps/plugins/rockbox-fonts.config4
-rw-r--r--apps/plugins/stats.c207
-rw-r--r--apps/plugins/superdom.c22
-rw-r--r--apps/plugins/tagcache/tagcache.c1
-rw-r--r--apps/plugins/text_editor.c1
-rw-r--r--apps/plugins/vbrfix.c4
-rw-r--r--apps/plugins/viewers.config1
-rw-r--r--apps/radio/presets.c1
-rw-r--r--apps/recorder/keyboard.c50
-rw-r--r--apps/screens.c5
-rw-r--r--apps/settings.h9
-rw-r--r--apps/settings_list.c24
-rw-r--r--apps/tagnavi.config15
-rw-r--r--apps/tagtree.c278
-rw-r--r--apps/tagtree.h1
-rw-r--r--apps/talk.c63
-rw-r--r--apps/talk.h4
-rw-r--r--apps/tree.c43
-rw-r--r--apps/tree.h3
274 files changed, 59431 insertions, 37616 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 627d7f7bea..8e16ff1c21 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -124,6 +124,10 @@
124 124
125#include "talk.h" 125#include "talk.h"
126 126
127#if defined(HAVE_DEVICEDATA)// && !defined(SIMULATOR)
128#include "devicedata.h"
129#endif
130
127#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 131#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
128#include "bootdata.h" 132#include "bootdata.h"
129#include "multiboot.h" 133#include "multiboot.h"
@@ -557,14 +561,16 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
557 if (!disk_partinfo(partition, &p)) 561 if (!disk_partinfo(partition, &p))
558 return buffer; 562 return buffer;
559 563
564 // XXX fix this up to use logical sector size
565 // XXX and if mounted, show free info...
560 if (selected_item%2) 566 if (selected_item%2)
561 { 567 {
562 snprintf(buffer, buffer_len, " T:%x %ld MB", p.type, 568 snprintf(buffer, buffer_len, " T:%x %llu MB", p.type,
563 p.size / ( 2048 / ( SECTOR_SIZE / 512 ))); 569 (uint64_t)(p.size / ( 2048 / ( SECTOR_SIZE / 512 ))));
564 } 570 }
565 else 571 else
566 { 572 {
567 snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p.start); 573 snprintf(buffer, buffer_len, "P%d: S:%llx", partition, (uint64_t)p.start);
568 } 574 }
569 return buffer; 575 return buffer;
570} 576}
@@ -572,7 +578,7 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
572static bool dbg_partitions(void) 578static bool dbg_partitions(void)
573{ 579{
574 struct simplelist_info info; 580 struct simplelist_info info;
575 simplelist_info_init(&info, "Partition Info", NUM_DRIVES * 4, NULL); 581 simplelist_info_init(&info, "Partition Info", NUM_DRIVES * MAX_PARTITIONS_PER_DRIVE, NULL);
576 info.selection_size = 2; 582 info.selection_size = 2;
577 info.scroll_all = true; 583 info.scroll_all = true;
578 info.get_name = dbg_partitions_getname; 584 info.get_name = dbg_partitions_getname;
@@ -1343,6 +1349,22 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1343 "R2W: *%d", card->r2w_factor); 1349 "R2W: *%d", card->r2w_factor);
1344#if (CONFIG_STORAGE & STORAGE_SD) 1350#if (CONFIG_STORAGE & STORAGE_SD)
1345 int csd_structure = card_extract_bits(card->csd, 127, 2); 1351 int csd_structure = card_extract_bits(card->csd, 127, 2);
1352 const char *ver;
1353 switch(csd_structure) {
1354 case 0:
1355 ver = "1 (SD)";
1356 break;
1357 case 1:
1358 ver = "2 (SDHC/SDXC)";
1359 break;
1360 case 2:
1361 ver = "3 (SDUC)";
1362 break;
1363 default:
1364 ver = "Unknown";
1365 break;
1366 }
1367 simplelist_addline("SDVer: %s\n", ver);
1346 if (csd_structure == 0) /* CSD version 1.0 */ 1368 if (csd_structure == 0) /* CSD version 1.0 */
1347#endif 1369#endif
1348 { 1370 {
@@ -1407,15 +1429,41 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1407 buf[8]=0; 1429 buf[8]=0;
1408 simplelist_addline( 1430 simplelist_addline(
1409 "Firmware: %s", buf); 1431 "Firmware: %s", buf);
1410 snprintf(buf, sizeof buf, "%ld MB", 1432
1411 ((unsigned long)identify_info[61] << 16 | 1433 uint64_t total_sectors = identify_info[60] | (identify_info[61] << 16);
1412 (unsigned long)identify_info[60]) / 2048 ); 1434#ifdef HAVE_LBA48
1435 if (identify_info[83] & 0x0400
1436 && total_sectors == 0x0FFFFFFF)
1437 total_sectors = identify_info[100] | (identify_info[101] << 16) | ((uint64_t)identify_info[102] << 32) | ((uint64_t)identify_info[103] << 48);
1438#endif
1439
1440 uint32_t sector_size;
1441
1442 /* Logical sector size > 512B ? */
1443 if ((identify_info[106] & 0xd000) == 0x5000)
1444 sector_size = identify_info[117] | (identify_info[118] << 16);
1445 else
1446 sector_size = SECTOR_SIZE;
1447
1448 total_sectors *= sector_size; /* Convert to bytes */
1449 total_sectors /= (1024 * 1024); /* Convert to MB */
1450
1451 simplelist_addline("Size: %llu MB", total_sectors);
1452 simplelist_addline("Logical sector size: %u B", sector_size);
1453
1454 if((identify_info[106] & 0xe000) == 0x6000)
1455 sector_size *= BIT_N(identify_info[106] & 0x000f);
1413 simplelist_addline( 1456 simplelist_addline(
1414 "Size: %s", buf); 1457 "Physical sector size: %d B", sector_size);
1415 unsigned long free; 1458
1459#ifndef HAVE_MULTIVOLUME
1460 // XXX this needs to be fixed for multi-volume setups
1461 sector_t free;
1416 volume_size( IF_MV(0,) NULL, &free ); 1462 volume_size( IF_MV(0,) NULL, &free );
1417 simplelist_addline( 1463 simplelist_addline(
1418 "Free: %ld MB", free / 1024); 1464 "Free: %llu MB", free / 1024);
1465#endif
1466
1419 simplelist_addline("SSD detected: %s", ata_disk_isssd() ? "yes" : "no"); 1467 simplelist_addline("SSD detected: %s", ata_disk_isssd() ? "yes" : "no");
1420 simplelist_addline( 1468 simplelist_addline(
1421 "Spinup time: %d ms", storage_spinup_time() * (1000/HZ)); 1469 "Spinup time: %d ms", storage_spinup_time() * (1000/HZ));
@@ -1452,11 +1500,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1452 simplelist_addline( 1500 simplelist_addline(
1453 "No timing info"); 1501 "No timing info");
1454 } 1502 }
1455 int sector_size = 512; 1503
1456 if((identify_info[106] & 0xe000) == 0x6000)
1457 sector_size *= BIT_N(identify_info[106] & 0x000f);
1458 simplelist_addline(
1459 "Physical sector size: %d", sector_size);
1460#ifdef HAVE_ATA_DMA 1504#ifdef HAVE_ATA_DMA
1461 if (identify_info[63] & (1<<0)) { 1505 if (identify_info[63] & (1<<0)) {
1462 simplelist_addline( 1506 simplelist_addline(
@@ -1751,8 +1795,8 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1751 simplelist_addline("Model: %s", info.product); 1795 simplelist_addline("Model: %s", info.product);
1752 simplelist_addline("Firmware: %s", info.revision); 1796 simplelist_addline("Firmware: %s", info.revision);
1753 simplelist_addline( 1797 simplelist_addline(
1754 "Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024); 1798 "Size: %lld MB", (uint64_t)(info.num_sectors*(info.sector_size/512)/2048));
1755 unsigned long free; 1799 sector_t free;
1756 volume_size( IF_MV(0,) NULL, &free ); 1800 volume_size( IF_MV(0,) NULL, &free );
1757 simplelist_addline( 1801 simplelist_addline(
1758 "Free: %ld MB", free / 1024); 1802 "Free: %ld MB", free / 1024);
@@ -1771,13 +1815,13 @@ static bool dbg_identify_info(void)
1771 const unsigned short *identify_info = ata_get_identify(); 1815 const unsigned short *identify_info = ata_get_identify();
1772#ifdef ROCKBOX_LITTLE_ENDIAN 1816#ifdef ROCKBOX_LITTLE_ENDIAN
1773 /* this is a pointer to a driver buffer so we can't modify it */ 1817 /* this is a pointer to a driver buffer so we can't modify it */
1774 for (int i = 0; i < SECTOR_SIZE/2; ++i) 1818 for (int i = 0; i < ATA_IDENTIFY_WORDS; ++i)
1775 { 1819 {
1776 unsigned short word = swap16(identify_info[i]); 1820 unsigned short word = swap16(identify_info[i]);
1777 write(fd, &word, 2); 1821 write(fd, &word, 2);
1778 } 1822 }
1779#else 1823#else
1780 write(fd, identify_info, SECTOR_SIZE); 1824 write(fd, identify_info, ATA_IDENTIFY_WORDS*2);
1781#endif 1825#endif
1782 close(fd); 1826 close(fd);
1783 } 1827 }
@@ -2585,8 +2629,35 @@ static bool dbg_boot_data(void)
2585} 2629}
2586#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */ 2630#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */
2587 2631
2632#if defined(HAVE_DEVICEDATA)// && !defined(SIMULATOR)
2633static bool dbg_device_data(void)
2634{
2635 struct simplelist_info info;
2636 info.scroll_all = true;
2637 simplelist_info_init(&info, "Device data", 1, NULL);
2638 simplelist_set_line_count(0);
2639
2640 simplelist_addline("Device data");
2641
2642#if defined(EROS_QN)
2643 simplelist_addline("Lcd Version: %d", (int)device_data.lcd_version);
2644#endif
2645
2646 simplelist_addline("Device data RAW:");
2647 for (size_t i = 0; i < device_data.length; i += 4)
2648 {
2649 simplelist_addline("%02x: %02x %02x %02x %02x", i,
2650 device_data.payload[i + 0], device_data.payload[i + 1],
2651 device_data.payload[i + 2], device_data.payload[i + 3]);
2652 }
2653
2654 return simplelist_show_list(&info);
2655}
2656#endif /* defined(HAVE_DEVICEDATA)*/
2657
2658
2588#if defined(IPOD_6G) && !defined(SIMULATOR) 2659#if defined(IPOD_6G) && !defined(SIMULATOR)
2589#define SYSCFG_MAX_ENTRIES 10 // 9 on iPod Classic/6G 2660#define SYSCFG_MAX_ENTRIES 9 // 9 on iPod Classic/6G
2590 2661
2591static bool dbg_syscfg(void) { 2662static bool dbg_syscfg(void) {
2592 struct simplelist_info info; 2663 struct simplelist_info info;
@@ -2607,8 +2678,7 @@ static bool dbg_syscfg(void) {
2607 return simplelist_show_list(&info); 2678 return simplelist_show_list(&info);
2608 } 2679 }
2609 2680
2610 simplelist_addline("Total size: %u bytes", syscfg_hdr.size); 2681 simplelist_addline("Total size: %u bytes, %u entries", syscfg_hdr.size, syscfg_hdr.num_entries);
2611 simplelist_addline("Entries: %u", syscfg_hdr.num_entries);
2612 2682
2613 size_t calculated_syscfg_size = syscfg_hdr_size + syscfg_entry_size * syscfg_hdr.num_entries; 2683 size_t calculated_syscfg_size = syscfg_hdr_size + syscfg_entry_size * syscfg_hdr.num_entries;
2614 2684
@@ -2619,7 +2689,7 @@ static bool dbg_syscfg(void) {
2619 } 2689 }
2620 2690
2621 if (syscfg_hdr.num_entries > SYSCFG_MAX_ENTRIES) { 2691 if (syscfg_hdr.num_entries > SYSCFG_MAX_ENTRIES) {
2622 simplelist_addline("Too many entries, showing first %u", syscfg_hdr.num_entries); 2692 simplelist_addline("Too many entries, showing only first %u", SYSCFG_MAX_ENTRIES);
2623 } 2693 }
2624 2694
2625 size_t syscfg_num_entries = MIN(syscfg_hdr.num_entries, SYSCFG_MAX_ENTRIES); 2695 size_t syscfg_num_entries = MIN(syscfg_hdr.num_entries, SYSCFG_MAX_ENTRIES);
@@ -2784,6 +2854,11 @@ static const struct {
2784#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 2854#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
2785 {"Boot data", dbg_boot_data }, 2855 {"Boot data", dbg_boot_data },
2786#endif 2856#endif
2857
2858#if defined(HAVE_DEVICEDATA)// && !defined(SIMULATOR)
2859 {"Device data", dbg_device_data },
2860#endif
2861
2787#if defined(IPOD_6G) && !defined(SIMULATOR) 2862#if defined(IPOD_6G) && !defined(SIMULATOR)
2788 {"View SysCfg", dbg_syscfg }, 2863 {"View SysCfg", dbg_syscfg },
2789#endif 2864#endif
diff --git a/apps/fileop.c b/apps/fileop.c
index 35bd9f2241..bcd5202cf4 100644
--- a/apps/fileop.c
+++ b/apps/fileop.c
@@ -26,6 +26,7 @@
26#include <string.h> 26#include <string.h>
27#include "string-extra.h" 27#include "string-extra.h"
28#include "debug.h" 28#include "debug.h"
29#include "powermgmt.h"
29 30
30#include "misc.h" 31#include "misc.h"
31#include "plugin.h" 32#include "plugin.h"
@@ -44,10 +45,14 @@
44struct file_op_params 45struct file_op_params
45{ 46{
46 char path[MAX_PATH]; /* Buffer for full path */ 47 char path[MAX_PATH]; /* Buffer for full path */
48 const char* toplevel_name;
47 bool is_dir; 49 bool is_dir;
48 int objects; /* how many files and subdirectories*/ 50 int objects; /* how many files and subdirectories*/
49 int processed; 51 int processed;
52 unsigned long long total_size;
53 unsigned long long processed_size;
50 size_t append; /* Append position in 'path' for stack push */ 54 size_t append; /* Append position in 'path' for stack push */
55 size_t extra_len; /* Length added by dst path compared to src */
51}; 56};
52 57
53static int prompt_name(char* buf, size_t bufsz) 58static int prompt_name(char* buf, size_t bufsz)
@@ -61,28 +66,52 @@ static int prompt_name(char* buf, size_t bufsz)
61 return FORC_UNKNOWN_FAILURE; 66 return FORC_UNKNOWN_FAILURE;
62} 67}
63 68
64static bool poll_cancel_action(const char *path, int operation, int current, int total) 69static bool poll_cancel_action(int operation, struct file_op_params *param)
65{ 70{
66 const char *op_str = ""; 71 static unsigned long last_tick;
67 72
68 clear_screen_buffer(false); 73 if (operation == FOC_COUNT)
69 74 {
70 if (operation == FOC_COPY) 75 if (param->objects <= 1)
71 op_str = str(LANG_COPYING); 76 last_tick = current_tick;
72 else if (operation == FOC_MOVE) 77 else if (TIME_AFTER(current_tick, last_tick + HZ/2))
73 op_str = str(LANG_MOVING); 78 {
74 else if (operation == FOC_COUNT) 79 clear_screen_buffer(false);
75 op_str = str(LANG_SCANNING_DISK); 80 splashf(0, "%s (%d)", str(LANG_SCANNING_DISK), param->objects);
76 else if (operation == FOC_DELETE) 81 last_tick = current_tick;
77 op_str = str(LANG_DELETING); 82 }
78 83 }
79 path_basename(path, &path);
80
81 if (total <= 0)
82 splashf(0, "%s (%d) %s", op_str, current, path);
83 else 84 else
84 splash_progress(current, total, "%s %s", op_str, path); 85 {
86 const char *op_str = (operation == FOC_DELETE) ? str(LANG_DELETING) :
87 (operation == FOC_COPY) ? str(LANG_COPYING) :
88 str(LANG_MOVING);
85 89
90 if ((operation == FOC_DELETE || !param->total_size) &&
91 param->objects > 0)
92 {
93 splash_progress(param->processed, param->objects,
94 "%s %s", op_str, param->toplevel_name);
95 }
96 else if (param->total_size >= 10 * 1024 * 1024)
97 {
98 int total_shft = (int) (param->total_size >> 15);
99 int current_shft = (int) (param->processed_size >> 15);
100 splash_progress(current_shft, total_shft,
101 "%s %s (%d MiB)\n%d MiB",
102 op_str, param->toplevel_name,
103 total_shft >> 5, current_shft >> 5);
104 }
105 else if (param->total_size >= 1024)
106 {
107 int total_kib = (int) (param->total_size >> 10);
108 int current_kib = (int) (param->processed_size >> 10);
109 splash_progress(current_kib, total_kib,
110 "%s %s (%d KiB)\n%d KiB",
111 op_str, param->toplevel_name,
112 total_kib, current_kib);
113 }
114 }
86 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); 115 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
87} 116}
88 117
@@ -94,13 +123,21 @@ static void init_file_op(struct file_op_params *param,
94 if (selected_file == NULL) 123 if (selected_file == NULL)
95 { 124 {
96 param->append = strlcpy(param->path, basename, sizeof (param->path)); 125 param->append = strlcpy(param->path, basename, sizeof (param->path));
126 path_basename(basename, &basename);
127 param->toplevel_name = basename;
97 } 128 }
98 else 129 else
130 {
99 param->append = path_append(param->path, basename, 131 param->append = path_append(param->path, basename,
100 selected_file, sizeof (param->path)); 132 selected_file, sizeof (param->path));
133 param->toplevel_name = selected_file;
134 }
101 param->is_dir = dir_exists(param->path); 135 param->is_dir = dir_exists(param->path);
136 param->extra_len = 0;
102 param->objects = 0; /* how many files and subdirectories*/ 137 param->objects = 0; /* how many files and subdirectories*/
103 param->processed = 0; 138 param->processed = 0;
139 param->total_size = 0;
140 param->processed_size = 0;
104} 141}
105 142
106/* counts file objects, deletes file objects */ 143/* counts file objects, deletes file objects */
@@ -156,7 +193,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
156 rc = directory_fileop(param, fileop); /* recursion */ 193 rc = directory_fileop(param, fileop); /* recursion */
157 } else { 194 } else {
158 /* remove a file */ 195 /* remove a file */
159 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) 196 if (poll_cancel_action(FOC_DELETE, param))
160 { 197 {
161 rc = FORC_CANCELLED; 198 rc = FORC_CANCELLED;
162 break; 199 break;
@@ -167,8 +204,15 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
167 else /* count objects */ 204 else /* count objects */
168 { 205 {
169 param->objects++; 206 param->objects++;
207 param->total_size += info.size;
208
209 if (poll_cancel_action(FOC_COUNT, param))
210 {
211 rc = FORC_CANCELLED;
212 break;
213 }
170 214
171 if (param->append >= sizeof (param->path)) { 215 if (param->append + param->extra_len >= sizeof (param->path)) {
172 rc = FORC_PATH_TOO_LONG; 216 rc = FORC_PATH_TOO_LONG;
173 break; /* no space left in buffer */ 217 break; /* no space left in buffer */
174 } 218 }
@@ -176,12 +220,6 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
176 if (info.attribute & ATTR_DIRECTORY) { 220 if (info.attribute & ATTR_DIRECTORY) {
177 /* enter subdirectory */ 221 /* enter subdirectory */
178 rc = directory_fileop(param, FOC_COUNT); /* recursion */ 222 rc = directory_fileop(param, FOC_COUNT); /* recursion */
179 } else {
180 if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0))
181 {
182 rc = FORC_CANCELLED;
183 break;
184 }
185 } 223 }
186 } 224 }
187 param->append = append; /* other functions may use param, reset append */ 225 param->append = append; /* other functions may use param, reset append */
@@ -193,7 +231,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
193 231
194 if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { 232 if (fileop == FOC_DELETE && rc == FORC_SUCCESS) {
195 /* remove the now empty directory */ 233 /* remove the now empty directory */
196 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) 234 if (poll_cancel_action(FOC_DELETE, param))
197 { 235 {
198 rc = FORC_CANCELLED; 236 rc = FORC_CANCELLED;
199 } else { 237 } else {
@@ -216,28 +254,25 @@ static int check_count_fileobjects(struct file_op_params *param)
216} 254}
217 255
218/* Attempt to just rename a file or directory */ 256/* Attempt to just rename a file or directory */
219static int move_by_rename(const char *src_path, 257static int move_by_rename(struct file_op_params *src,
220 const char *dst_path, 258 const char *dst_path,
221 unsigned int *pflags) 259 unsigned int *pflags)
222{ 260{
223 unsigned int flags = *pflags; 261 unsigned int flags = *pflags;
224 int rc = FORC_UNKNOWN_FAILURE; 262 int rc = FORC_UNKNOWN_FAILURE;
263 reset_poweroff_timer();
225 if (!(flags & (PASTE_COPY | PASTE_EXDEV))) { 264 if (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
226 if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { 265 if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) {
227 /* Just try to move the directory / file */ 266 /* Just try to move the directory / file */
228 if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { 267 rc = rename(src->path, dst_path);
229 rc = FORC_CANCELLED;
230 } else {
231 rc = rename(src_path, dst_path);
232#ifdef HAVE_MULTIVOLUME 268#ifdef HAVE_MULTIVOLUME
233 if (rc < FORC_SUCCESS && errno == EXDEV) { 269 if (rc < FORC_SUCCESS && errno == EXDEV) {
234 /* Failed because cross volume rename doesn't work */ 270 /* Failed because cross volume rename doesn't work */
235 *pflags |= PASTE_EXDEV; /* force a move instead */ 271 *pflags |= PASTE_EXDEV; /* force a move instead */
236 } 272 }
237#endif /* HAVE_MULTIVOLUME */ 273#endif /* HAVE_MULTIVOLUME */
238 /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { 274 /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
239 * Directory is not empty thus rename() will not do a quick overwrite */ 275 * Directory is not empty thus rename() will not do a quick overwrite */
240 }
241 } 276 }
242 277
243 } 278 }
@@ -245,12 +280,16 @@ static int move_by_rename(const char *src_path,
245} 280}
246 281
247/* Paste a file */ 282/* Paste a file */
248static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) 283static int copy_move_file(struct file_op_params *src, const char *dst_path,
284 unsigned int flags)
249{ 285{
250 /* Try renaming first */ 286 /* Try renaming first */
251 int rc = move_by_rename(src_path, dst_path, &flags); 287 int rc = move_by_rename(src, dst_path, &flags);
252 if (rc == FORC_SUCCESS) 288 if (rc == FORC_SUCCESS)
289 {
290 src->total_size = 0; /* switch from counting size to number of items */
253 return rc; 291 return rc;
292 }
254 293
255 /* See if we can get the plugin buffer for the file copy buffer */ 294 /* See if we can get the plugin buffer for the file copy buffer */
256 size_t buffersize; 295 size_t buffersize;
@@ -268,9 +307,11 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
268 307
269 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */ 308 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */
270 309
271 int src_fd = open(src_path, O_RDONLY); 310 int src_fd = open(src->path, O_RDONLY);
272 if (src_fd >= 0) { 311 if (src_fd >= 0) {
273 off_t src_sz = lseek(src_fd, 0, SEEK_END); 312 off_t src_sz = lseek(src_fd, 0, SEEK_END);
313 if (!src->total_size && !src->processed) /* single file copy */
314 src->total_size = src_sz;
274 lseek(src_fd, 0, SEEK_SET); 315 lseek(src_fd, 0, SEEK_SET);
275 316
276 int oflag = O_WRONLY|O_CREAT; 317 int oflag = O_WRONLY|O_CREAT;
@@ -289,9 +330,8 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
289 while (rc == FORC_SUCCESS) { 330 while (rc == FORC_SUCCESS) {
290 if (total_size >= next_cancel_test) { 331 if (total_size >= next_cancel_test) {
291 next_cancel_test = total_size + 0x10000; 332 next_cancel_test = total_size + 0x10000;
292 if (poll_cancel_action(src_path, 333 if (poll_cancel_action(!(flags & PASTE_COPY) ?
293 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, 334 FOC_MOVE : FOC_COPY, src))
294 total_size, src_sz))
295 { 335 {
296 rc = FORC_CANCELLED; 336 rc = FORC_CANCELLED;
297 break; 337 break;
@@ -315,6 +355,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
315 } 355 }
316 356
317 total_size += byteswritten; 357 total_size += byteswritten;
358 src->processed_size += byteswritten;
318 359
319 if (bytesread < (ssize_t)buffersize) { 360 if (bytesread < (ssize_t)buffersize) {
320 /* EOF with trailing bytes */ 361 /* EOF with trailing bytes */
@@ -344,7 +385,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
344 385
345 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { 386 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) {
346 /* Remove the source file */ 387 /* Remove the source file */
347 rc = remove(src_path) * 10; 388 rc = remove(src->path) * 10;
348 } 389 }
349 390
350 return rc; 391 return rc;
@@ -408,9 +449,8 @@ static int copy_move_directory(struct file_op_params *src,
408 break; 449 break;
409 } 450 }
410 451
411 if (poll_cancel_action(src->path, 452 if (poll_cancel_action(!(flags & PASTE_COPY) ?
412 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, 453 FOC_MOVE : FOC_COPY, src))
413 src->processed, src->objects))
414 { 454 {
415 rc = FORC_CANCELLED; 455 rc = FORC_CANCELLED;
416 break; 456 break;
@@ -419,11 +459,12 @@ static int copy_move_directory(struct file_op_params *src,
419 DEBUGF("Copy %s to %s\n", src->path, dst->path); 459 DEBUGF("Copy %s to %s\n", src->path, dst->path);
420 460
421 if (info.attribute & ATTR_DIRECTORY) { 461 if (info.attribute & ATTR_DIRECTORY) {
462 src->processed_size += info.size;
422 /* Copy/move a subdirectory */ 463 /* Copy/move a subdirectory */
423 rc = copy_move_directory(src, dst, flags); /* recursion */; 464 rc = copy_move_directory(src, dst, flags); /* recursion */;
424 } else { 465 } else {
425 /* Copy/move a file */ 466 /* Copy/move a file */
426 rc = copy_move_file(src->path, dst->path, flags); 467 rc = copy_move_file(src, dst->path, flags);
427 } 468 }
428 469
429 /* Remove basenames we added above */ 470 /* Remove basenames we added above */
@@ -484,8 +525,12 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in
484 if (src.is_dir) { 525 if (src.is_dir) {
485 /* Copy or move a subdirectory */ 526 /* Copy or move a subdirectory */
486 /* Try renaming first */ 527 /* Try renaming first */
487 rc = move_by_rename(src.path, dst.path, &flags); 528 rc = move_by_rename(&src, dst.path, &flags);
488 if (rc < FORC_SUCCESS) { 529 if (rc < FORC_SUCCESS) {
530 int extra_len = dst.append - src.append;
531 if (extra_len > 0)
532 src.extra_len = extra_len;
533
489 rc = check_count_fileobjects(&src); 534 rc = check_count_fileobjects(&src);
490 if (rc == FORC_SUCCESS) { 535 if (rc == FORC_SUCCESS) {
491 rc = copy_move_directory(&src, &dst, flags); 536 rc = copy_move_directory(&src, &dst, flags);
@@ -493,7 +538,7 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in
493 } 538 }
494 } else { 539 } else {
495 /* Copy or move a file */ 540 /* Copy or move a file */
496 rc = copy_move_file(src_path, dst.path, flags); 541 rc = copy_move_file(&src, dst.path, flags);
497 } 542 }
498 543
499 cpu_boost(false); 544 cpu_boost(false);
@@ -533,6 +578,12 @@ int delete_fileobject(const char *selected_file)
533 if (param.append >= sizeof (param.path)) 578 if (param.append >= sizeof (param.path))
534 return FORC_PATH_TOO_LONG; 579 return FORC_PATH_TOO_LONG;
535 580
581 /* Note: delete_fileobject() will happily delete whatever
582 * path is passed (after confirmation) */
583 if (confirm_delete_yesno(param.path) != YESNO_YES) {
584 return FORC_CANCELLED;
585 }
586
536 if (param.is_dir) { 587 if (param.is_dir) {
537 int rc = check_count_fileobjects(&param); 588 int rc = check_count_fileobjects(&param);
538 DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); 589 DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path);
@@ -540,15 +591,7 @@ int delete_fileobject(const char *selected_file)
540 return rc; 591 return rc;
541 } 592 }
542 593
543 /* Note: delete_fileobject() will happily delete whatever
544 * path is passed (after confirmation) */
545 if (confirm_delete_yesno(param.path) != YESNO_YES) {
546 return FORC_CANCELLED;
547 }
548
549 clear_screen_buffer(true); 594 clear_screen_buffer(true);
550 if (poll_cancel_action(param.path, FOC_DELETE, param.processed, param.objects))
551 return FORC_CANCELLED;
552 595
553 if (param.is_dir) { /* if directory */ 596 if (param.is_dir) { /* if directory */
554 cpu_boost(true); 597 cpu_boost(true);
diff --git a/apps/filetree.c b/apps/filetree.c
index eadb19ea59..6a06fcd5ad 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -348,6 +348,12 @@ int ft_load(struct tree_context* c, const char* tempdir)
348 continue; 348 continue;
349 } 349 }
350 350
351 if (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & ATTR_DIRECTORY) &&
352 (dptr->attr &
353 (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_VOLUME)) != 0) {
354 continue; /* skip non plugin folders */
355 }
356
351 /* check for known file types */ 357 /* check for known file types */
352 if ( !(dir_attr) ) 358 if ( !(dir_attr) )
353 dptr->attr |= filetype_get_attr((char *)entry->d_name); 359 dptr->attr |= filetype_get_attr((char *)entry->d_name);
diff --git a/apps/gui/list.c b/apps/gui/list.c
index 85046ead54..c119c4f193 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -51,6 +51,7 @@ void list_draw(struct screen *display, struct gui_synclist *list);
51 51
52static long last_dirty_tick; 52static long last_dirty_tick;
53static struct viewport parent[NB_SCREENS]; 53static struct viewport parent[NB_SCREENS];
54static struct gui_synclist *current_lists;
54 55
55static bool list_is_dirty(struct gui_synclist *list) 56static bool list_is_dirty(struct gui_synclist *list)
56{ 57{
@@ -160,7 +161,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
160 gui_list->nb_items = 0; 161 gui_list->nb_items = 0;
161 gui_list->selected_item = 0; 162 gui_list->selected_item = 0;
162 gui_synclist_init_display_settings(gui_list); 163 gui_synclist_init_display_settings(gui_list);
163
164#ifdef HAVE_TOUCHSCREEN 164#ifdef HAVE_TOUCHSCREEN
165 gui_list->y_pos = 0; 165 gui_list->y_pos = 0;
166#endif 166#endif
@@ -586,16 +586,19 @@ bool gui_synclist_keyclick_callback(int action, void* data)
586 * loop. 586 * loop.
587 * 587 *
588 * The GUI_EVENT_NEED_UI_UPDATE event is registered for in list_do_action_timeout() 588 * The GUI_EVENT_NEED_UI_UPDATE event is registered for in list_do_action_timeout()
589 * and unregistered in gui_synclict_do_button(). This is done because 589 * as a oneshot and current_lists updated. later current_lists is set to NULL
590 * if something is using the list UI they *must* be calling those 590 * in gui_synclist_do_button() effectively disabling the callback.
591* This is done because if something is using the list UI they *must* be calling those
591 * two functions in the correct order or the list wont work. 592 * two functions in the correct order or the list wont work.
592 */ 593 */
593static struct gui_synclist *current_lists; 594
594static bool ui_update_event_registered = false; 595static void _lists_uiviewport_update_callback(unsigned short id,
595static void _lists_uiviewport_update_callback(unsigned short id, void *data) 596 void *data, void *userdata)
596{ 597{
597 (void)id; 598 (void)id;
598 (void)data; 599 (void)data;
600 (void)userdata;
601
599 if (current_lists) 602 if (current_lists)
600 gui_synclist_draw(current_lists); 603 gui_synclist_draw(current_lists);
601} 604}
@@ -697,6 +700,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr)
697 case ACTION_TREE_PGRIGHT: 700 case ACTION_TREE_PGRIGHT:
698 gui_synclist_scroll_right(lists); 701 gui_synclist_scroll_right(lists);
699 gui_synclist_draw(lists); 702 gui_synclist_draw(lists);
703 yield();
700 return true; 704 return true;
701 case ACTION_TREE_ROOT_INIT: 705 case ACTION_TREE_ROOT_INIT:
702 /* After this button press ACTION_TREE_PGLEFT is allowed 706 /* After this button press ACTION_TREE_PGLEFT is allowed
@@ -724,6 +728,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr)
724 gui_synclist_draw(lists); 728 gui_synclist_draw(lists);
725 pgleft_allow_cancel = false; /* stop ACTION_TREE_PAGE_LEFT 729 pgleft_allow_cancel = false; /* stop ACTION_TREE_PAGE_LEFT
726 skipping to root */ 730 skipping to root */
731 yield();
727 return true; 732 return true;
728/* for pgup / pgdown, we are obliged to have a different behaviour depending 733/* for pgup / pgdown, we are obliged to have a different behaviour depending
729 * on the screen for which the user pressed the key since for example, remote 734 * on the screen for which the user pressed the key since for example, remote
@@ -769,13 +774,8 @@ int list_do_action_timeout(struct gui_synclist *lists, int timeout)
769/* Returns the lowest of timeout or the delay until a postponed 774/* Returns the lowest of timeout or the delay until a postponed
770 scheduled announcement is due (if any). */ 775 scheduled announcement is due (if any). */
771{ 776{
772 if (lists != current_lists) 777 add_event_ex(GUI_EVENT_NEED_UI_UPDATE, true, _lists_uiviewport_update_callback, NULL);
773 { 778 current_lists = lists;
774 if (!ui_update_event_registered)
775 ui_update_event_registered =
776 add_event(GUI_EVENT_NEED_UI_UPDATE, _lists_uiviewport_update_callback);
777 current_lists = lists;
778 }
779 if(lists->scheduled_talk_tick) 779 if(lists->scheduled_talk_tick)
780 { 780 {
781 long delay = lists->scheduled_talk_tick -current_tick +1; 781 long delay = lists->scheduled_talk_tick -current_tick +1;
@@ -944,7 +944,9 @@ bool simplelist_show_list(struct simplelist_info *info)
944 old_line_count = simplelist_line_count; 944 old_line_count = simplelist_line_count;
945 } 945 }
946 else if(default_event_handler(action) == SYS_USB_CONNECTED) 946 else if(default_event_handler(action) == SYS_USB_CONNECTED)
947 {
947 return true; 948 return true;
949 }
948 } 950 }
949 talk_shutup(); 951 talk_shutup();
950 952
diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c
index 1ce7fd5026..65f2a0491d 100644
--- a/apps/gui/option_select.c
+++ b/apps/gui/option_select.c
@@ -219,10 +219,15 @@ static int option_talk(int selected_item, void * data)
219void option_select_next_val(const struct settings_list *setting, 219void option_select_next_val(const struct settings_list *setting,
220 bool previous, bool apply) 220 bool previous, bool apply)
221{ 221{
222 bool repeated = get_action_statuscode(NULL) & ACTION_REPEAT;
223
222 int val = 0; 224 int val = 0;
223 int *value = setting->setting; 225 int *value = setting->setting;
224 if (HASFLAG(setting, F_BOOL_SETTING)) 226 if (HASFLAG(setting, F_BOOL_SETTING))
225 { 227 {
228 if (repeated)
229 return;
230
226 *(bool*)value = !*(bool*)value; 231 *(bool*)value = !*(bool*)value;
227 if (apply && setting->bool_setting->option_callback) 232 if (apply && setting->bool_setting->option_callback)
228 setting->bool_setting->option_callback(*(bool*)value); 233 setting->bool_setting->option_callback(*(bool*)value);
@@ -236,13 +241,13 @@ void option_select_next_val(const struct settings_list *setting,
236 { 241 {
237 val = *value + info->step; 242 val = *value + info->step;
238 if (neg_step ? (val < info->max) : (val > info->max)) 243 if (neg_step ? (val < info->max) : (val > info->max))
239 val = info->min; 244 val = repeated ? *value : info->min;
240 } 245 }
241 else 246 else
242 { 247 {
243 val = *value - info->step; 248 val = *value - info->step;
244 if (neg_step ? (val > info->min) : (val < info->min)) 249 if (neg_step ? (val > info->min) : (val < info->min))
245 val = info->max; 250 val = repeated ? *value : info->max;
246 } 251 }
247 *value = val; 252 *value = val;
248 if (apply && info->option_callback) 253 if (apply && info->option_callback)
@@ -258,13 +263,13 @@ void option_select_next_val(const struct settings_list *setting,
258 { 263 {
259 val = *value + steps; 264 val = *value + steps;
260 if (val >= max) 265 if (val >= max)
261 val = min; 266 val = repeated ? *value : min;
262 } 267 }
263 else 268 else
264 { 269 {
265 val = *value - steps; 270 val = *value - steps;
266 if (val < min) 271 if (val < min)
267 val = max; 272 val = repeated ? *value : max;
268 } 273 }
269 *value = val; 274 *value = val;
270 if (apply) 275 if (apply)
@@ -277,13 +282,13 @@ void option_select_next_val(const struct settings_list *setting,
277 { 282 {
278 val = *value + 1; 283 val = *value + 1;
279 if (val >= info->count) 284 if (val >= info->count)
280 val = 0; 285 val = repeated ? *value : 0;
281 } 286 }
282 else 287 else
283 { 288 {
284 val = *value - 1; 289 val = *value - 1;
285 if (val < 0) 290 if (val < 0)
286 val = info->count-1; 291 val = repeated ? *value : info->count-1;
287 } 292 }
288 *value = val; 293 *value = val;
289 if (apply && info->option_callback) 294 if (apply && info->option_callback)
@@ -300,7 +305,11 @@ void option_select_next_val(const struct settings_list *setting,
300 (settings->flags&F_ALLOW_ARBITRARY_VALS && 305 (settings->flags&F_ALLOW_ARBITRARY_VALS &&
301 *value < tbl_info->values[i])) 306 *value < tbl_info->values[i]))
302 { 307 {
303 val = tbl_info->values[(i+add)%tbl_info->count]; 308 int index = (i+add)%tbl_info->count;
309 if (repeated && ((i == 0 && previous) || (!previous && i == tbl_info->count -1)))
310 val = *value;
311 else
312 val = tbl_info->values[index];
304 break; 313 break;
305 } 314 }
306 } 315 }
@@ -504,7 +513,6 @@ bool option_screen(const struct settings_list *setting,
504 title = P2STR(option_title); 513 title = P2STR(option_title);
505 514
506 gui_synclist_set_title(&lists, title, Icon_Questionmark); 515 gui_synclist_set_title(&lists, title, Icon_Questionmark);
507 gui_synclist_set_icon_callback(&lists, NULL);
508 if(global_settings.talk_menu) 516 if(global_settings.talk_menu)
509 gui_synclist_set_voice_callback(&lists, option_talk); 517 gui_synclist_set_voice_callback(&lists, option_talk);
510 518
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index ba9396ae74..082619432f 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -74,7 +74,7 @@
74static const char* get_codectype(const struct mp3entry* id3) 74static const char* get_codectype(const struct mp3entry* id3)
75{ 75{
76 if (id3 && id3->codectype < AFMT_NUM_CODECS) { 76 if (id3 && id3->codectype < AFMT_NUM_CODECS) {
77 return audio_formats[id3->codectype].label; 77 return get_codec_string(id3->codectype);
78 } else { 78 } else {
79 return NULL; 79 return NULL;
80 } 80 }
diff --git a/apps/gui/usb_screen.c b/apps/gui/usb_screen.c
index fb59f820b6..8a3510ea15 100644
--- a/apps/gui/usb_screen.c
+++ b/apps/gui/usb_screen.c
@@ -243,7 +243,13 @@ static void usb_screens_draw(struct usb_screen_vps_t *usb_screen_vps_ar)
243 243
244void gui_usb_screen_run(bool early_usb) 244void gui_usb_screen_run(bool early_usb)
245{ 245{
246 (void) early_usb; 246#ifdef SIMULATOR /* the sim allows toggling USB fast enough to overflow viewportmanagers stack */
247 static bool in_usb_screen = false;
248 if (in_usb_screen)
249 return;
250 in_usb_screen = true;
251#endif
252
247 struct usb_screen_vps_t usb_screen_vps_ar[NB_SCREENS]; 253 struct usb_screen_vps_t usb_screen_vps_ar[NB_SCREENS];
248#if defined HAVE_TOUCHSCREEN 254#if defined HAVE_TOUCHSCREEN
249 enum touchscreen_mode old_mode = touchscreen_get_mode(); 255 enum touchscreen_mode old_mode = touchscreen_get_mode();
@@ -334,4 +340,7 @@ void gui_usb_screen_run(bool early_usb)
334 } 340 }
335 341
336 pop_current_activity(); 342 pop_current_activity();
343#ifdef SIMULATOR
344 in_usb_screen = false;
345#endif
337} 346}
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index 0de805bd02..82b0394a53 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -806,7 +806,7 @@ long gui_wps_show(void)
806 theme_enabled = false; 806 theme_enabled = false;
807 gwps_leave_wps(theme_enabled); 807 gwps_leave_wps(theme_enabled);
808 onplay(state->id3->path, 808 onplay(state->id3->path,
809 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 809 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION);
810 if (!audio_status()) 810 if (!audio_status())
811 { 811 {
812 /* re-enable theme since we're returning to SBS */ 812 /* re-enable theme since we're returning to SBS */
@@ -823,7 +823,7 @@ long gui_wps_show(void)
823 { 823 {
824 gwps_leave_wps(true); 824 gwps_leave_wps(true);
825 int retval = onplay(state->id3->path, 825 int retval = onplay(state->id3->path,
826 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 826 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION);
827 /* if music is stopped in the context menu we want to exit the wps */ 827 /* if music is stopped in the context menu we want to exit the wps */
828 if (retval == ONPLAY_MAINMENU 828 if (retval == ONPLAY_MAINMENU
829 || !audio_status()) 829 || !audio_status())
diff --git a/apps/lang/InvalidVoice_english.talk b/apps/lang/InvalidVoice_english.talk
index e40f227c33..6e54e63129 100644
--- a/apps/lang/InvalidVoice_english.talk
+++ b/apps/lang/InvalidVoice_english.talk
Binary files differ
diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang
index 3eb90f5277..8c050e5f08 100644
--- a/apps/lang/bulgarian.lang
+++ b/apps/lang/bulgarian.lang
@@ -19,7 +19,7 @@
19# - Nicolay Jordanov 19# - Nicolay Jordanov
20# - Hristo Kovachev 20# - Hristo Kovachev
21# - Vencislav Atanasov 21# - Vencislav Atanasov
22#- Zahari yurukov <zahari.yurukov@gmail.com> 22# - Zahari Yurukov <zahari.yurukov@gmail.com>
23<phrase> 23<phrase>
24 id: LANG_SET_BOOL_YES 24 id: LANG_SET_BOOL_YES
25 desc: bool true representation 25 desc: bool true representation
@@ -143,7 +143,7 @@
143 *: "Зареждане..." 143 *: "Зареждане..."
144 </dest> 144 </dest>
145 <voice> 145 <voice>
146 *: "Зареждане..." 146 *: "Зареждане"
147 </voice> 147 </voice>
148</phrase> 148</phrase>
149<phrase> 149<phrase>
@@ -157,7 +157,7 @@
157 *: "Зареждане... %d%% готови (%s)" 157 *: "Зареждане... %d%% готови (%s)"
158 </dest> 158 </dest>
159 <voice> 159 <voice>
160 *: "" 160 *: "Зареждане"
161 </voice> 161 </voice>
162</phrase> 162</phrase>
163<phrase> 163<phrase>
@@ -171,7 +171,7 @@
171 *: "Сканиране на диÑка..." 171 *: "Сканиране на диÑка..."
172 </dest> 172 </dest>
173 <voice> 173 <voice>
174 *: "Сканиране на диÑка..." 174 *: "Сканиране на диÑка"
175 </voice> 175 </voice>
176</phrase> 176</phrase>
177<phrase> 177<phrase>
@@ -185,7 +185,7 @@
185 *: "Изключване..." 185 *: "Изключване..."
186 </dest> 186 </dest>
187 <voice> 187 <voice>
188 *: "Изключване..." 188 *: "Изключване"
189 </voice> 189 </voice>
190</phrase> 190</phrase>
191<phrase> 191<phrase>
@@ -250,20 +250,16 @@
250 user: core 250 user: core
251 <source> 251 <source>
252 *: "PLAY = Yes" 252 *: "PLAY = Yes"
253 archosplayer: "(PLAY/STOP)"
254 cowond2*: "MENU, or top-right = Yes" 253 cowond2*: "MENU, or top-right = Yes"
255 creativezen*: "SELECT = Yes" 254 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
256 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
257 iriverh100,iriverh120,iriverh300: "NAVI = Yes" 255 iriverh100,iriverh120,iriverh300: "NAVI = Yes"
258 mrobe500: "PLAY, POWER, or top-right = Yes" 256 mrobe500: "PLAY, POWER, or top-right = Yes"
259 vibe500: "OK = Yes" 257 vibe500: "OK = Yes"
260 </source> 258 </source>
261 <dest> 259 <dest>
262 *: "PLAY = Да" 260 *: "PLAY = Да"
263 archosplayer: "(PLAY/STOP)"
264 cowond2*: "MENU или горе лÑво = Да" 261 cowond2*: "MENU или горе лÑво = Да"
265 creativezen*: "SELECT = Да" 262 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да"
266 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да"
267 iriverh100,iriverh120,iriverh300: "NAVI = Да" 263 iriverh100,iriverh120,iriverh300: "NAVI = Да"
268 mrobe500: "PLAY, POWER или горе дÑÑно = Да" 264 mrobe500: "PLAY, POWER или горе дÑÑно = Да"
269 vibe500: "OK = Да" 265 vibe500: "OK = Да"
@@ -278,15 +274,12 @@
278 user: core 274 user: core
279 <source> 275 <source>
280 *: "Any Other = No" 276 *: "Any Other = No"
281 archosplayer: none
282 </source> 277 </source>
283 <dest> 278 <dest>
284 *: "Ð’Ñеки друг = Ðе" 279 *: "Ð’Ñеки друг = Ðе"
285 archosplayer: none
286 </dest> 280 </dest>
287 <voice> 281 <voice>
288 *: "" 282 *: ""
289 archosplayer: none
290 </voice> 283 </voice>
291</phrase> 284</phrase>
292<phrase> 285<phrase>
@@ -548,20 +541,6 @@
548 </voice> 541 </voice>
549</phrase> 542</phrase>
550<phrase> 543<phrase>
551 id: LANG_BOOKMARK_CONTEXT_DELETE
552 desc: bookmark context menu, delete this bookmark
553 user: core
554 <source>
555 *: "Delete"
556 </source>
557 <dest>
558 *: "Изтриване"
559 </dest>
560 <voice>
561 *: "Изтриване"
562 </voice>
563</phrase>
564<phrase>
565 id: LANG_AUTO_BOOKMARK_QUERY 544 id: LANG_AUTO_BOOKMARK_QUERY
566 desc: prompt for user to decide to create an bookmark 545 desc: prompt for user to decide to create an bookmark
567 user: core 546 user: core
@@ -703,7 +682,7 @@
703</phrase> 682</phrase>
704<phrase> 683<phrase>
705 id: LANG_CHANNEL_STEREO 684 id: LANG_CHANNEL_STEREO
706 desc: in sound_settings 685 desc: in sound_settings and radio screen
707 user: core 686 user: core
708 <source> 687 <source>
709 *: "Stereo" 688 *: "Stereo"
@@ -717,7 +696,7 @@
717</phrase> 696</phrase>
718<phrase> 697<phrase>
719 id: LANG_CHANNEL_MONO 698 id: LANG_CHANNEL_MONO
720 desc: in sound_settings 699 desc: in sound_settings and radio screen
721 user: core 700 user: core
722 <source> 701 <source>
723 *: "Mono" 702 *: "Mono"
@@ -999,20 +978,6 @@
999 </voice> 978 </voice>
1000</phrase> 979</phrase>
1001<phrase> 980<phrase>
1002 id: LANG_EQUALIZER_EDIT_MODE
1003 desc: in the equalizer settings menu
1004 user: core
1005 <source>
1006 *: "Edit mode: %s"
1007 </source>
1008 <dest>
1009 *: "Режим на редактиране: %s"
1010 </dest>
1011 <voice>
1012 *: ""
1013 </voice>
1014</phrase>
1015<phrase>
1016 id: LANG_EQUALIZER_GAIN_ITEM 981 id: LANG_EQUALIZER_GAIN_ITEM
1017 desc: in the equalizer settings menu 982 desc: in the equalizer settings menu
1018 user: core 983 user: core
@@ -1112,7 +1077,7 @@
1112</phrase> 1077</phrase>
1113<phrase> 1078<phrase>
1114 id: LANG_DITHERING 1079 id: LANG_DITHERING
1115 desc: in the sound settings menu 1080 desc: in the sound settings and some other menus
1116 user: core 1081 user: core
1117 <source> 1082 <source>
1118 *: "Dithering" 1083 *: "Dithering"
@@ -1125,159 +1090,6 @@
1125 </voice> 1090 </voice>
1126</phrase> 1091</phrase>
1127<phrase> 1092<phrase>
1128 id: LANG_LOUDNESS
1129 desc: in sound_settings
1130 user: core
1131 <source>
1132 *: none
1133 masf: "Loudness"
1134 </source>
1135 <dest>
1136 *: none
1137 masf: "Сила"
1138 </dest>
1139 <voice>
1140 *: none
1141 masf: "Сила"
1142 </voice>
1143</phrase>
1144<phrase>
1145 id: LANG_AUTOVOL
1146 desc: in sound_settings
1147 user: core
1148 <source>
1149 *: none
1150 masf: "Auto Volume"
1151 </source>
1152 <dest>
1153 *: none
1154 masf: "Ðвтоматична Ñила на звука"
1155 </dest>
1156 <voice>
1157 *: none
1158 masf: "Ðвтоматична Ñила на звука"
1159 </voice>
1160</phrase>
1161<phrase>
1162 id: LANG_DECAY
1163 desc: in sound_settings
1164 user: core
1165 <source>
1166 *: none
1167 masf: "AV Decay Time"
1168 </source>
1169 <dest>
1170 *: none
1171 masf: "Време на отÑлабване на AV"
1172 </dest>
1173 <voice>
1174 *: none
1175 masf: "Време на отÑлабване на AV"
1176 </voice>
1177</phrase>
1178<phrase>
1179 id: LANG_SUPERBASS
1180 desc: in sound settings
1181 user: core
1182 <source>
1183 *: none
1184 masf: "Super Bass"
1185 </source>
1186 <dest>
1187 *: none
1188 masf: "Супер баÑ"
1189 </dest>
1190 <voice>
1191 *: none
1192 masf: "Супер баÑ"
1193 </voice>
1194</phrase>
1195<phrase>
1196 id: LANG_MDB_ENABLE
1197 desc: in sound settings
1198 user: core
1199 <source>
1200 *: none
1201 masf: "MDB Enable"
1202 </source>
1203 <dest>
1204 *: none
1205 masf: "Включване на MDB"
1206 </dest>
1207 <voice>
1208 *: none
1209 masf: "Включване на MDB"
1210 </voice>
1211</phrase>
1212<phrase>
1213 id: LANG_MDB_STRENGTH
1214 desc: in sound settings
1215 user: core
1216 <source>
1217 *: none
1218 masf: "MDB Strength"
1219 </source>
1220 <dest>
1221 *: none
1222 masf: "Сила на MDB"
1223 </dest>
1224 <voice>
1225 *: none
1226 masf: "Сила на MDB"
1227 </voice>
1228</phrase>
1229<phrase>
1230 id: LANG_MDB_HARMONICS
1231 desc: in sound settings
1232 user: core
1233 <source>
1234 *: none
1235 masf: "MDB Harmonics"
1236 </source>
1237 <dest>
1238 *: none
1239 masf: "Хармоници на MDB"
1240 </dest>
1241 <voice>
1242 *: none
1243 masf: "Хармоници на MDB"
1244 </voice>
1245</phrase>
1246<phrase>
1247 id: LANG_MDB_CENTER
1248 desc: in sound settings
1249 user: core
1250 <source>
1251 *: none
1252 masf: "MDB Centre Frequency"
1253 </source>
1254 <dest>
1255 *: none
1256 masf: "Централна чеÑтота на MDB"
1257 </dest>
1258 <voice>
1259 *: none
1260 masf: "Централна чеÑтота на MDB"
1261 </voice>
1262</phrase>
1263<phrase>
1264 id: LANG_MDB_SHAPE
1265 desc: in sound settings
1266 user: core
1267 <source>
1268 *: none
1269 masf: "MDB Shape"
1270 </source>
1271 <dest>
1272 *: none
1273 masf: "Форма на MDB"
1274 </dest>
1275 <voice>
1276 *: none
1277 masf: "Форма на MDB"
1278 </voice>
1279</phrase>
1280<phrase>
1281 id: LANG_GENERAL_SETTINGS 1093 id: LANG_GENERAL_SETTINGS
1282 desc: in the main menu 1094 desc: in the main menu
1283 user: core 1095 user: core
@@ -1847,10 +1659,10 @@
1847</phrase> 1659</phrase>
1848<phrase> 1660<phrase>
1849 id: LANG_AUDIOSCROBBLER 1661 id: LANG_AUDIOSCROBBLER
1850 desc: "Last.fm Log" in the playback menu 1662 desc: "Last.fm Logger" in Plugin/apps/scrobbler
1851 user: core 1663 user: core
1852 <source> 1664 <source>
1853 *: "Last.fm Log" 1665 *: "Last.fm Logger"
1854 </source> 1666 </source>
1855 <dest> 1667 <dest>
1856 *: "Last.fm лог" 1668 *: "Last.fm лог"
@@ -2140,7 +1952,6 @@
2140 user: core 1952 user: core
2141 <source> 1953 <source>
2142 *: "Building database... %d found (OFF to return)" 1954 *: "Building database... %d found (OFF to return)"
2143 archosplayer: "Building DB %d found"
2144 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)" 1955 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
2145 gogearsa9200: "Building database... %d found (REW to return)" 1956 gogearsa9200: "Building database... %d found (REW to return)"
2146 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)" 1957 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
@@ -2148,14 +1959,13 @@
2148 </source> 1959 </source>
2149 <dest> 1960 <dest>
2150 *: "Изграждане на базата данни... намерени %d (OFF за връщане)" 1961 *: "Изграждане на базата данни... намерени %d (OFF за връщане)"
2151 archosplayer: "Изгр. на БД %d файла"
2152 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Изграждане на базата данни... намерени %d (LEFT за връщане)" 1962 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Изграждане на базата данни... намерени %d (LEFT за връщане)"
2153 gogearsa9200: "Изграждане на базата данни... намерени %d (REW за връщане)" 1963 gogearsa9200: "Изграждане на базата данни... намерени %d (REW за връщане)"
2154 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Изграждане на базата данни... намерени %d (PREV за връщане)" 1964 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Изграждане на базата данни... намерени %d (PREV за връщане)"
2155 iriverh100,iriverh120,iriverh300: "Изграждане на базата данни... намерени %d (STOP за връщане)" 1965 iriverh100,iriverh120,iriverh300: "Изграждане на базата данни... намерени %d (STOP за връщане)"
2156 </dest> 1966 </dest>
2157 <voice> 1967 <voice>
2158 *: "ИзграÐдане на базата данни... намерени запиÑи..." 1968 *: "намерени запиÑи за базата данни"
2159 </voice> 1969 </voice>
2160</phrase> 1970</phrase>
2161<phrase> 1971<phrase>
@@ -2424,15 +2234,15 @@
2424 desc: in lcd settings 2234 desc: in lcd settings
2425 user: core 2235 user: core
2426 <source> 2236 <source>
2427 *: none 2237 *: "Backlight on Lock"
2428 hold_button: "Backlight on Hold" 2238 hold_button: "Backlight on Hold"
2429 </source> 2239 </source>
2430 <dest> 2240 <dest>
2431 *: none 2241 *: "ПодÑветка при заключване"
2432 hold_button: "ПодÑветка при закл. клав." 2242 hold_button: "ПодÑветка при закл. клав."
2433 </dest> 2243 </dest>
2434 <voice> 2244 <voice>
2435 *: none 2245 *: "ПодÑветка при заключване"
2436 hold_button: "ПодÑветка при заключена клавиатура" 2246 hold_button: "ПодÑветка при заключена клавиатура"
2437 </voice> 2247 </voice>
2438</phrase> 2248</phrase>
@@ -2520,16 +2330,13 @@
2520 desc: in lcd settings 2330 desc: in lcd settings
2521 user: core 2331 user: core
2522 <source> 2332 <source>
2523 *: none 2333 *: "Never"
2524 lcd_sleep: "Never"
2525 </source> 2334 </source>
2526 <dest> 2335 <dest>
2527 *: none 2336 *: "Ðикога"
2528 lcd_sleep: "Ðикога"
2529 </dest> 2337 </dest>
2530 <voice> 2338 <voice>
2531 *: none 2339 *: "Ðикога"
2532 lcd_sleep: "Ðикога"
2533 </voice> 2340 </voice>
2534</phrase> 2341</phrase>
2535<phrase> 2342<phrase>
@@ -2731,11 +2538,11 @@
2731 </source> 2538 </source>
2732 <dest> 2539 <dest>
2733 *: none 2540 *: none
2734 lcd_color: "RGB" 2541 lcd_color: "ЧЗС"
2735 </dest> 2542 </dest>
2736 <voice> 2543 <voice>
2737 *: none 2544 *: none
2738 lcd_color: "RGB" 2545 lcd_color: ""
2739 </voice> 2546 </voice>
2740</phrase> 2547</phrase>
2741<phrase> 2548<phrase>
@@ -2769,7 +2576,7 @@
2769 </dest> 2576 </dest>
2770 <voice> 2577 <voice>
2771 *: none 2578 *: none
2772 lcd_color: "Ðевалиден цвÑÑ‚" 2579 lcd_color: ""
2773 </voice> 2580 </voice>
2774</phrase> 2581</phrase>
2775<phrase> 2582<phrase>
@@ -2845,7 +2652,7 @@
2845 *: "Пример за наÑтройката на ÑкороÑтта на Ñкролиране" 2652 *: "Пример за наÑтройката на ÑкороÑтта на Ñкролиране"
2846 </dest> 2653 </dest>
2847 <voice> 2654 <voice>
2848 *: "Пример за наÑтройката на ÑкороÑтта на Ñкролиране" 2655 *: ""
2849 </voice> 2656 </voice>
2850</phrase> 2657</phrase>
2851<phrase> 2658<phrase>
@@ -2901,7 +2708,7 @@
2901 *: "Пример за наÑтройката на Ñтъпката на Ñкролиране" 2708 *: "Пример за наÑтройката на Ñтъпката на Ñкролиране"
2902 </dest> 2709 </dest>
2903 <voice> 2710 <voice>
2904 *: "Пример за наÑтройката на Ñтъпката на Ñкролиране" 2711 *: ""
2905 </voice> 2712 </voice>
2906</phrase> 2713</phrase>
2907<phrase> 2714<phrase>
@@ -3054,23 +2861,6 @@
3054 </voice> 2861 </voice>
3055</phrase> 2862</phrase>
3056<phrase> 2863<phrase>
3057 id: LANG_BUTTON_BAR
3058 desc: in settings menu
3059 user: core
3060 <source>
3061 *: none
3062 recorder_pad: "Button Bar"
3063 </source>
3064 <dest>
3065 *: none
3066 recorder_pad: "Лента Ñ Ð±ÑƒÑ‚Ð¾Ð½Ð¸"
3067 </dest>
3068 <voice>
3069 *: none
3070 recorder_pad: "Лента Ñ Ð±ÑƒÑ‚Ð¾Ð½Ð¸"
3071 </voice>
3072</phrase>
3073<phrase>
3074 id: LANG_VOLUME_DISPLAY 2864 id: LANG_VOLUME_DISPLAY
3075 desc: Volume type title 2865 desc: Volume type title
3076 user: core 2866 user: core
@@ -3132,15 +2922,12 @@
3132 user: core 2922 user: core
3133 <source> 2923 <source>
3134 *: "Peak Meter" 2924 *: "Peak Meter"
3135 masd: none
3136 </source> 2925 </source>
3137 <dest> 2926 <dest>
3138 *: "Пик метър" 2927 *: "Пик метър"
3139 masd: none
3140 </dest> 2928 </dest>
3141 <voice> 2929 <voice>
3142 *: "Пик метър" 2930 *: "Пик метър"
3143 masd: none
3144 </voice> 2931 </voice>
3145</phrase> 2932</phrase>
3146<phrase> 2933<phrase>
@@ -3149,15 +2936,12 @@
3149 user: core 2936 user: core
3150 <source> 2937 <source>
3151 *: "Clip Hold Time" 2938 *: "Clip Hold Time"
3152 masd: none
3153 </source> 2939 </source>
3154 <dest> 2940 <dest>
3155 *: "Задържане на индикатора за ÑмущениÑ" 2941 *: "Задържане на индикатора за ÑмущениÑ"
3156 masd: none
3157 </dest> 2942 </dest>
3158 <voice> 2943 <voice>
3159 *: "Задържане на индикатора за ÑмущениÑ" 2944 *: "Задържане на индикатора за ÑмущениÑ"
3160 masd: none
3161 </voice> 2945 </voice>
3162</phrase> 2946</phrase>
3163<phrase> 2947<phrase>
@@ -3166,15 +2950,12 @@
3166 user: core 2950 user: core
3167 <source> 2951 <source>
3168 *: "Peak Hold Time" 2952 *: "Peak Hold Time"
3169 masd: none
3170 </source> 2953 </source>
3171 <dest> 2954 <dest>
3172 *: "Задържане на пик индикатора" 2955 *: "Задържане на пик индикатора"
3173 masd: none
3174 </dest> 2956 </dest>
3175 <voice> 2957 <voice>
3176 *: "Задържане на пик индикатора" 2958 *: "Задържане на пик индикатора"
3177 masd: none
3178 </voice> 2959 </voice>
3179</phrase> 2960</phrase>
3180<phrase> 2961<phrase>
@@ -3183,15 +2964,12 @@
3183 user: core 2964 user: core
3184 <source> 2965 <source>
3185 *: "Eternal" 2966 *: "Eternal"
3186 masd: none
3187 </source> 2967 </source>
3188 <dest> 2968 <dest>
3189 *: "Вечно" 2969 *: "Вечно"
3190 masd: none
3191 </dest> 2970 </dest>
3192 <voice> 2971 <voice>
3193 *: "Вечно" 2972 *: "Вечно"
3194 masd: none
3195 </voice> 2973 </voice>
3196</phrase> 2974</phrase>
3197<phrase> 2975<phrase>
@@ -3200,15 +2978,12 @@
3200 user: core 2978 user: core
3201 <source> 2979 <source>
3202 *: "Peak Release" 2980 *: "Peak Release"
3203 masd: none
3204 </source> 2981 </source>
3205 <dest> 2982 <dest>
3206 *: "ОÑвобождаване на пик индикатора" 2983 *: "ОÑвобождаване на пик индикатора"
3207 masd: none
3208 </dest> 2984 </dest>
3209 <voice> 2985 <voice>
3210 *: "ОÑвобождаване на пик индикатора" 2986 *: "ОÑвобождаване на пик индикатора"
3211 masd: none
3212 </voice> 2987 </voice>
3213</phrase> 2988</phrase>
3214<phrase> 2989<phrase>
@@ -3217,15 +2992,12 @@
3217 user: core 2992 user: core
3218 <source> 2993 <source>
3219 *: "Scale" 2994 *: "Scale"
3220 masd: none
3221 </source> 2995 </source>
3222 <dest> 2996 <dest>
3223 *: "Скала" 2997 *: "Скала"
3224 masd: none
3225 </dest> 2998 </dest>
3226 <voice> 2999 <voice>
3227 *: "Скала" 3000 *: "Скала"
3228 masd: none
3229 </voice> 3001 </voice>
3230</phrase> 3002</phrase>
3231<phrase> 3003<phrase>
@@ -3234,15 +3006,12 @@
3234 user: core 3006 user: core
3235 <source> 3007 <source>
3236 *: "Logarithmic (dB)" 3008 *: "Logarithmic (dB)"
3237 masd: none
3238 </source> 3009 </source>
3239 <dest> 3010 <dest>
3240 *: "Логаритмична (dB)" 3011 *: "Логаритмична (dB)"
3241 masd: none
3242 </dest> 3012 </dest>
3243 <voice> 3013 <voice>
3244 *: "Логаритмична в децибели" 3014 *: "Логаритмична в децибели"
3245 masd: none
3246 </voice> 3015 </voice>
3247</phrase> 3016</phrase>
3248<phrase> 3017<phrase>
@@ -3251,15 +3020,12 @@
3251 user: core 3020 user: core
3252 <source> 3021 <source>
3253 *: "Linear (%)" 3022 *: "Linear (%)"
3254 masd: none
3255 </source> 3023 </source>
3256 <dest> 3024 <dest>
3257 *: "Линейна (%)" 3025 *: "Линейна (%)"
3258 masd: none
3259 </dest> 3026 </dest>
3260 <voice> 3027 <voice>
3261 *: "Линейна в проценти" 3028 *: "Линейна в проценти"
3262 masd: none
3263 </voice> 3029 </voice>
3264</phrase> 3030</phrase>
3265<phrase> 3031<phrase>
@@ -3268,15 +3034,12 @@
3268 user: core 3034 user: core
3269 <source> 3035 <source>
3270 *: "Minimum Of Range" 3036 *: "Minimum Of Range"
3271 masd: none
3272 </source> 3037 </source>
3273 <dest> 3038 <dest>
3274 *: "Минимален обхват" 3039 *: "Минимален обхват"
3275 masd: none
3276 </dest> 3040 </dest>
3277 <voice> 3041 <voice>
3278 *: "Минимален обхват" 3042 *: "Минимален обхват"
3279 masd: none
3280 </voice> 3043 </voice>
3281</phrase> 3044</phrase>
3282<phrase> 3045<phrase>
@@ -3285,15 +3048,12 @@
3285 user: core 3048 user: core
3286 <source> 3049 <source>
3287 *: "Maximum Of Range" 3050 *: "Maximum Of Range"
3288 masd: none
3289 </source> 3051 </source>
3290 <dest> 3052 <dest>
3291 *: "МакÑимален обхват" 3053 *: "МакÑимален обхват"
3292 masd: none
3293 </dest> 3054 </dest>
3294 <voice> 3055 <voice>
3295 *: "МакÑимален обхват" 3056 *: "МакÑимален обхват"
3296 masd: none
3297 </voice> 3057 </voice>
3298</phrase> 3058</phrase>
3299<phrase> 3059<phrase>
@@ -3486,10 +3246,10 @@
3486 *: "Unicode (UTF-8)" 3246 *: "Unicode (UTF-8)"
3487 </source> 3247 </source>
3488 <dest> 3248 <dest>
3489 *: "Unicode (UTF-8)" 3249 *: "Уникод (UTF-8)"
3490 </dest> 3250 </dest>
3491 <voice> 3251 <voice>
3492 *: "Unicode (UTF-8)" 3252 *: "Уникод (UTF-8)"
3493 </voice> 3253 </voice>
3494</phrase> 3254</phrase>
3495<phrase> 3255<phrase>
@@ -3623,14 +3383,17 @@
3623 <source> 3383 <source>
3624 *: none 3384 *: none
3625 battery_types: "Alkaline" 3385 battery_types: "Alkaline"
3386 xduoox3: "Newer (2000 mAh)"
3626 </source> 3387 </source>
3627 <dest> 3388 <dest>
3628 *: none 3389 *: none
3629 battery_types: "Ðлкална" 3390 battery_types: "Ðлкална"
3391 xduoox3: "Ðов модел (2000 mAh)"
3630 </dest> 3392 </dest>
3631 <voice> 3393 <voice>
3632 *: none 3394 *: none
3633 battery_types: "Ðлкална" 3395 battery_types: "Ðлкална"
3396 xduoox3: "Ðов модел 2000 милиампер чаÑа"
3634 </voice> 3397 </voice>
3635</phrase> 3398</phrase>
3636<phrase> 3399<phrase>
@@ -3640,14 +3403,17 @@
3640 <source> 3403 <source>
3641 *: none 3404 *: none
3642 battery_types: "NiMH" 3405 battery_types: "NiMH"
3406 xduoox3: "Older (1500 mAh)"
3643 </source> 3407 </source>
3644 <dest> 3408 <dest>
3645 *: none 3409 *: none
3646 battery_types: "NiMH " 3410 battery_types: "NiMH "
3411 xduoox3: "Стар модел (1500 mAh)"
3647 </dest> 3412 </dest>
3648 <voice> 3413 <voice>
3649 *: none 3414 *: none
3650 battery_types: "Ðикел Метал Хидридна" 3415 battery_types: "Ðикел Метал Хидридна"
3416 xduoox3: "Стар модел 1500 милиампер чаÑа"
3651 </voice> 3417 </voice>
3652</phrase> 3418</phrase>
3653<phrase> 3419<phrase>
@@ -3837,7 +3603,7 @@
3837 </dest> 3603 </dest>
3838 <voice> 3604 <voice>
3839 *: none 3605 *: none
3840 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,sansac200*,sansae200*: "" 3606 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
3841 </voice> 3607 </voice>
3842</phrase> 3608</phrase>
3843<phrase> 3609<phrase>
@@ -3854,7 +3620,7 @@
3854 </dest> 3620 </dest>
3855 <voice> 3621 <voice>
3856 *: none 3622 *: none
3857 rtc: "Ðед" 3623 rtc: ""
3858 </voice> 3624 </voice>
3859</phrase> 3625</phrase>
3860<phrase> 3626<phrase>
@@ -3871,7 +3637,7 @@
3871 </dest> 3637 </dest>
3872 <voice> 3638 <voice>
3873 *: none 3639 *: none
3874 rtc: "Пон" 3640 rtc: ""
3875 </voice> 3641 </voice>
3876</phrase> 3642</phrase>
3877<phrase> 3643<phrase>
@@ -3888,7 +3654,7 @@
3888 </dest> 3654 </dest>
3889 <voice> 3655 <voice>
3890 *: none 3656 *: none
3891 rtc: "Ð’Ñ‚" 3657 rtc: ""
3892 </voice> 3658 </voice>
3893</phrase> 3659</phrase>
3894<phrase> 3660<phrase>
@@ -3905,7 +3671,7 @@
3905 </dest> 3671 </dest>
3906 <voice> 3672 <voice>
3907 *: none 3673 *: none
3908 rtc: "Ср" 3674 rtc: ""
3909 </voice> 3675 </voice>
3910</phrase> 3676</phrase>
3911<phrase> 3677<phrase>
@@ -3922,7 +3688,7 @@
3922 </dest> 3688 </dest>
3923 <voice> 3689 <voice>
3924 *: none 3690 *: none
3925 rtc: "Чет" 3691 rtc: ""
3926 </voice> 3692 </voice>
3927</phrase> 3693</phrase>
3928<phrase> 3694<phrase>
@@ -3939,7 +3705,7 @@
3939 </dest> 3705 </dest>
3940 <voice> 3706 <voice>
3941 *: none 3707 *: none
3942 rtc: "Пет" 3708 rtc: ""
3943 </voice> 3709 </voice>
3944</phrase> 3710</phrase>
3945<phrase> 3711<phrase>
@@ -3956,7 +3722,7 @@
3956 </dest> 3722 </dest>
3957 <voice> 3723 <voice>
3958 *: none 3724 *: none
3959 rtc: "Съб" 3725 rtc: ""
3960 </voice> 3726 </voice>
3961</phrase> 3727</phrase>
3962<phrase> 3728<phrase>
@@ -4262,7 +4028,7 @@
4262 </dest> 4028 </dest>
4263 <voice> 4029 <voice>
4264 *: none 4030 *: none
4265 alarm: "Време на алармата" 4031 alarm: ""
4266 </voice> 4032 </voice>
4267</phrase> 4033</phrase>
4268<phrase> 4034<phrase>
@@ -4283,23 +4049,6 @@
4283 </voice> 4049 </voice>
4284</phrase> 4050</phrase>
4285<phrase> 4051<phrase>
4286 id: LANG_ALARM_MOD_SHUTDOWN
4287 desc: The text that tells the user that the alarm time is ok and the device shuts off (for the RTC alarm mod).
4288 user: core
4289 <source>
4290 *: none
4291 alarm: "Alarm Set"
4292 </source>
4293 <dest>
4294 *: none
4295 alarm: "ÐаÑтройка на алармата"
4296 </dest>
4297 <voice>
4298 *: none
4299 alarm: "ÐаÑтройка на алармата"
4300 </voice>
4301</phrase>
4302<phrase>
4303 id: LANG_ALARM_MOD_ERROR 4052 id: LANG_ALARM_MOD_ERROR
4304 desc: The text that tells that the time is incorrect (for the RTC alarm mod). 4053 desc: The text that tells that the time is incorrect (for the RTC alarm mod).
4305 user: core 4054 user: core
@@ -4317,35 +4066,6 @@
4317 </voice> 4066 </voice>
4318</phrase> 4067</phrase>
4319<phrase> 4068<phrase>
4320 id: LANG_ALARM_MOD_KEYS
4321 desc: Shown key functions in alarm menu (for the RTC alarm mod).
4322 user: core
4323 <source>
4324 *: none
4325 alarm: "PLAY=Set OFF=Cancel"
4326 gigabeats: "SELECT=Set POWER=Cancel"
4327 ipod*: "SELECT=Set MENU=Cancel"
4328 iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
4329 mpiohd300: "ENTER=Set MENU=Cancel"
4330 sansafuzeplus: "SELECT=Set BACK=Cancel"
4331 vibe500: "OK=Set C=Cancel"
4332 </source>
4333 <dest>
4334 *: none
4335 alarm: "PLAY=УÑтановÑване OFF=Отказ"
4336 gigabeats: "SELECT=УÑтановÑване POWER=Отказ"
4337 ipod*: "SELECT=УÑтановÑване MENU=Отказ"
4338 iriverh10,iriverh10_5gb: "SELECT=УÑтановÑване PREV=Отказ"
4339 mpiohd300: "ENTER=УÑтановÑване MENU=Отказ"
4340 sansafuzeplus: "SELECT=УÑтановÑване BACK=Отказ"
4341 vibe500: "OK=УÑтановÑване C=Отказ"
4342 </dest>
4343 <voice>
4344 *: none
4345 alarm,ipod*: ""
4346 </voice>
4347</phrase>
4348<phrase>
4349 id: LANG_ALARM_MOD_DISABLE 4069 id: LANG_ALARM_MOD_DISABLE
4350 desc: Announce that the RTC alarm has been turned off 4070 desc: Announce that the RTC alarm has been turned off
4351 user: core 4071 user: core
@@ -4447,20 +4167,6 @@
4447 </voice> 4167 </voice>
4448</phrase> 4168</phrase>
4449<phrase> 4169<phrase>
4450 id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
4451 desc: Save only on bookmark for each playlist in recent bookmarks
4452 user: core
4453 <source>
4454 *: "Unique only"
4455 </source>
4456 <dest>
4457 *: "Само уникални"
4458 </dest>
4459 <voice>
4460 *: "Само уникални"
4461 </voice>
4462</phrase>
4463<phrase>
4464 id: LANG_LANGUAGE 4170 id: LANG_LANGUAGE
4465 desc: in settings_menu 4171 desc: in settings_menu
4466 user: core 4172 user: core
@@ -4877,57 +4583,6 @@
4877 </voice> 4583 </voice>
4878</phrase> 4584</phrase>
4879<phrase> 4585<phrase>
4880 id: LANG_BUTTONBAR_MENU
4881 desc: in button bar
4882 user: core
4883 <source>
4884 *: none
4885 radio_screen_button_bar: "Menu"
4886 </source>
4887 <dest>
4888 *: none
4889 radio_screen_button_bar: "Меню"
4890 </dest>
4891 <voice>
4892 *: none
4893 radio_screen_button_bar: ""
4894 </voice>
4895</phrase>
4896<phrase>
4897 id: LANG_FM_BUTTONBAR_EXIT
4898 desc: in radio screen
4899 user: core
4900 <source>
4901 *: none
4902 radio_screen_button_bar: "Exit"
4903 </source>
4904 <dest>
4905 *: none
4906 radio_screen_button_bar: "Изход"
4907 </dest>
4908 <voice>
4909 *: none
4910 radio_screen_button_bar: ""
4911 </voice>
4912</phrase>
4913<phrase>
4914 id: LANG_FM_BUTTONBAR_ACTION
4915 desc: in radio screen
4916 user: core
4917 <source>
4918 *: none
4919 radio_screen_button_bar: "Action"
4920 </source>
4921 <dest>
4922 *: none
4923 radio_screen_button_bar: "ДейÑтвие"
4924 </dest>
4925 <voice>
4926 *: none
4927 radio_screen_button_bar: ""
4928 </voice>
4929</phrase>
4930<phrase>
4931 id: LANG_PRESET 4586 id: LANG_PRESET
4932 desc: in button bar and radio screen / menu 4587 desc: in button bar and radio screen / menu
4933 user: core 4588 user: core
@@ -4945,40 +4600,6 @@
4945 </voice> 4600 </voice>
4946</phrase> 4601</phrase>
4947<phrase> 4602<phrase>
4948 id: LANG_FM_BUTTONBAR_ADD
4949 desc: in radio screen
4950 user: core
4951 <source>
4952 *: none
4953 radio_screen_button_bar: "Add"
4954 </source>
4955 <dest>
4956 *: none
4957 radio_screen_button_bar: "Добави"
4958 </dest>
4959 <voice>
4960 *: none
4961 radio_screen_button_bar: ""
4962 </voice>
4963</phrase>
4964<phrase>
4965 id: LANG_FM_BUTTONBAR_RECORD
4966 desc: in radio screen
4967 user: core
4968 <source>
4969 *: none
4970 radio_screen_button_bar: "Record"
4971 </source>
4972 <dest>
4973 *: none
4974 radio_screen_button_bar: "ЗапиÑ"
4975 </dest>
4976 <voice>
4977 *: none
4978 radio_screen_button_bar: ""
4979 </voice>
4980</phrase>
4981<phrase>
4982 id: LANG_FM_MONO_MODE 4603 id: LANG_FM_MONO_MODE
4983 desc: in radio screen 4604 desc: in radio screen
4984 user: core 4605 user: core
@@ -5009,7 +4630,7 @@
5009 </dest> 4630 </dest>
5010 <voice> 4631 <voice>
5011 *: none 4632 *: none
5012 radio: "Екранът е замразен!" 4633 radio: ""
5013 </voice> 4634 </voice>
5014</phrase> 4635</phrase>
5015<phrase> 4636<phrase>
@@ -5060,7 +4681,7 @@
5060 </dest> 4681 </dest>
5061 <voice> 4682 <voice>
5062 *: none 4683 *: none
5063 radio: "Сканиране" 4684 radio: ""
5064 </voice> 4685 </voice>
5065</phrase> 4686</phrase>
5066<phrase> 4687<phrase>
@@ -5308,11 +4929,11 @@
5308 </source> 4929 </source>
5309 <dest> 4930 <dest>
5310 *: none 4931 *: none
5311 recording: "PCM Wave" 4932 recording: "~PCM Wave"
5312 </dest> 4933 </dest>
5313 <voice> 4934 <voice>
5314 *: none 4935 *: none
5315 recording: "PCM Wave" 4936 recording: "~PCM Wave"
5316 </voice> 4937 </voice>
5317</phrase> 4938</phrase>
5318<phrase> 4939<phrase>
@@ -5401,23 +5022,6 @@
5401 </voice> 5022 </voice>
5402</phrase> 5023</phrase>
5403<phrase> 5024<phrase>
5404 id: LANG_RECORDING_QUALITY
5405 desc: in the recording settings
5406 user: core
5407 <source>
5408 *: none
5409 recording_hwcodec: "Quality"
5410 </source>
5411 <dest>
5412 *: none
5413 recording_hwcodec: "КачеÑтво"
5414 </dest>
5415 <voice>
5416 *: none
5417 recording_hwcodec: "КачеÑтво"
5418 </voice>
5419</phrase>
5420<phrase>
5421 id: LANG_FREQUENCY 5025 id: LANG_FREQUENCY
5422 desc: in recording and playback settings 5026 desc: in recording and playback settings
5423 user: core 5027 user: core
@@ -5523,23 +5127,6 @@
5523 </voice> 5127 </voice>
5524</phrase> 5128</phrase>
5525<phrase> 5129<phrase>
5526 id: LANG_RECORDING_EDITABLE
5527 desc: Editable recordings setting
5528 user: core
5529 <source>
5530 *: none
5531 recording_hwcodec: "Independent Frames"
5532 </source>
5533 <dest>
5534 *: none
5535 recording_hwcodec: "ÐезавиÑими кадри"
5536 </dest>
5537 <voice>
5538 *: none
5539 recording_hwcodec: "ÐезавиÑими кадри"
5540 </voice>
5541</phrase>
5542<phrase>
5543 id: LANG_RECORD_TIMESPLIT 5130 id: LANG_RECORD_TIMESPLIT
5544 desc: Record split menu 5131 desc: Record split menu
5545 user: core 5132 user: core
@@ -5676,40 +5263,6 @@
5676 </voice> 5263 </voice>
5677</phrase> 5264</phrase>
5678<phrase> 5265<phrase>
5679 id: LANG_RECORD_DIRECTORY
5680 desc: in recording settings_menu
5681 user: core
5682 <source>
5683 *: none
5684 recording: "Directory"
5685 </source>
5686 <dest>
5687 *: none
5688 recording: "Папка"
5689 </dest>
5690 <voice>
5691 *: none
5692 recording: "Папка"
5693 </voice>
5694</phrase>
5695<phrase>
5696 id: LANG_SET_AS_REC_DIR
5697 desc: used in the onplay menu to set a recording dir
5698 user: core
5699 <source>
5700 *: none
5701 recording: "Set As Recording Directory"
5702 </source>
5703 <dest>
5704 *: none
5705 recording: "УÑтановÑване като папка за запиÑ"
5706 </dest>
5707 <voice>
5708 *: none
5709 recording: "УÑтановÑване като папка за запиÑ"
5710 </voice>
5711</phrase>
5712<phrase>
5713 id: LANG_CLEAR_REC_DIR 5266 id: LANG_CLEAR_REC_DIR
5714 desc: 5267 desc:
5715 user: core 5268 user: core
@@ -5978,7 +5531,7 @@
5978 </dest> 5531 </dest>
5979 <voice> 5532 <voice>
5980 *: none 5533 *: none
5981 recording: "Предварителен запиÑ" 5534 recording: ""
5982 </voice> 5535 </voice>
5983</phrase> 5536</phrase>
5984<phrase> 5537<phrase>
@@ -6097,7 +5650,7 @@
6097 </dest> 5650 </dest>
6098 <voice> 5651 <voice>
6099 *: none 5652 *: none
6100 remote: "(Vol- : активиране отново)" 5653 remote: "(Volume minus за активиране отново)"
6101 </voice> 5654 </voice>
6102</phrase> 5655</phrase>
6103<phrase> 5656<phrase>
@@ -6311,23 +5864,6 @@
6311 </voice> 5864 </voice>
6312</phrase> 5865</phrase>
6313<phrase> 5866<phrase>
6314 id: LANG_SHUTDOWN
6315 desc: in main menu
6316 user: core
6317 <source>
6318 *: none
6319 soft_shutdown: "Shut down"
6320 </source>
6321 <dest>
6322 *: none
6323 soft_shutdown: "Изключване"
6324 </dest>
6325 <voice>
6326 *: none
6327 soft_shutdown: "Изключване"
6328 </voice>
6329</phrase>
6330<phrase>
6331 id: LANG_ROCKBOX_INFO 5867 id: LANG_ROCKBOX_INFO
6332 desc: displayed topmost on the info screen and in the info menu 5868 desc: displayed topmost on the info screen and in the info menu
6333 user: core 5869 user: core
@@ -6347,11 +5883,9 @@
6347 user: core 5883 user: core
6348 <source> 5884 <source>
6349 *: "Buffer:" 5885 *: "Buffer:"
6350 archosplayer: "Buf:"
6351 </source> 5886 </source>
6352 <dest> 5887 <dest>
6353 *: "Буфер:" 5888 *: "Буфер:"
6354 archosplayer: "Буф:"
6355 </dest> 5889 </dest>
6356 <voice> 5890 <voice>
6357 *: "Размер на буфера" 5891 *: "Размер на буфера"
@@ -6364,12 +5898,10 @@
6364 <source> 5898 <source>
6365 *: "Battery: %d%% %dh %dm" 5899 *: "Battery: %d%% %dh %dm"
6366 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm" 5900 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
6367 iriverifp7xx: "%d%% %dh %dm"
6368 </source> 5901 </source>
6369 <dest> 5902 <dest>
6370 *: "БатериÑ: %d%% %dh %dm" 5903 *: "БатериÑ: %d%% %dh %dm"
6371 ipodmini1g,ipodmini2g,iriverh10: "Бат: %d%% %dh %dm" 5904 ipodmini1g,ipodmini2g,iriverh10: "Бат: %d%% %dh %dm"
6372 iriverifp7xx: "%d%% %dh %dm"
6373 </dest> 5905 </dest>
6374 <voice> 5906 <voice>
6375 *: "ЗареденоÑÑ‚ на батериÑта" 5907 *: "ЗареденоÑÑ‚ на батериÑта"
@@ -6400,7 +5932,7 @@
6400 *: "Свободно:" 5932 *: "Свободно:"
6401 </dest> 5933 </dest>
6402 <voice> 5934 <voice>
6403 *: "Свободно диÑково проÑтранÑтво:" 5935 *: "Свободно диÑково проÑтранÑтво"
6404 </voice> 5936 </voice>
6405</phrase> 5937</phrase>
6406<phrase> 5938<phrase>
@@ -6409,41 +5941,44 @@
6409 user: core 5941 user: core
6410 <source> 5942 <source>
6411 *: "Int:" 5943 *: "Int:"
5944 hibylinux: "mSD:"
6412 xduoox3: "mSD1:" 5945 xduoox3: "mSD1:"
6413 </source> 5946 </source>
6414 <dest> 5947 <dest>
6415 *: "вгр:" 5948 *: "вгр:"
6416 xduoox3: "MSD1:" 5949 hibylinux: "~mSD:"
5950 xduoox3: "~mSD1:"
6417 </dest> 5951 </dest>
6418 <voice> 5952 <voice>
6419 *: "Вградена памет" 5953 *: "Вградена памет"
6420 xduoox3: "micro S D 1" 5954 hibylinux: "микро S D"
5955 xduoox3: "микро S D 1"
6421 </voice> 5956 </voice>
6422</phrase> 5957</phrase>
6423<phrase> 5958<phrase>
6424 id: LANG_DISK_NAME_MMC 5959 id: LANG_DISK_NAME_MMC
6425 desc: in info menu; name for external disk with multivolume (Ondio; keep short!) 5960 desc: in info menu; name for external disk with multivolume (keep short!)
6426 user: core 5961 user: core
6427 <source> 5962 <source>
6428 *: none 5963 *: none
6429 archosondio*: "MMC:" 5964 hibylinux: "USB:"
6430 multivolume: "HD1" 5965 multivolume: "HD1:"
6431 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 5966 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
6432 xduoox3: "mSD2:" 5967 xduoox3: "mSD2:"
6433 </source> 5968 </source>
6434 <dest> 5969 <dest>
6435 *: none 5970 *: none
6436 archosondio*: "MMC:" 5971 hibylinux: "~USB:"
6437 multivolume: "HD1" 5972 multivolume: "~HD1:"
6438 sansac200*,sansaclipplus,sansae200*,sansafuze*: "MSD:" 5973 sansac200*,sansaclipplus,sansae200*,sansafuze*: "~mSD:"
6439 xduoox3: "MSD2:" 5974 xduoox3: "~mSD2:"
6440 </dest> 5975 </dest>
6441 <voice> 5976 <voice>
6442 *: none 5977 *: none
6443 archosondio*: "M M C" 5978 hibylinux: "~U S B"
6444 multivolume: "H D 1" 5979 multivolume: "~H D 1"
6445 sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D" 5980 sansac200*,sansaclipplus,sansae200*,sansafuze*: "микро S D"
6446 xduoox3: "micro S D 2" 5981 xduoox3: "микро S D 2"
6447 </voice> 5982 </voice>
6448</phrase> 5983</phrase>
6449<phrase> 5984<phrase>
@@ -6531,62 +6066,6 @@
6531 </voice> 6066 </voice>
6532</phrase> 6067</phrase>
6533<phrase> 6068<phrase>
6534 id: LANG_INSERT
6535 desc: in onplay menu. insert a track/playlist into dynamic playlist.
6536 user: core
6537 <source>
6538 *: "Insert"
6539 </source>
6540 <dest>
6541 *: "Вмъкване"
6542 </dest>
6543 <voice>
6544 *: "Вмъкване"
6545 </voice>
6546</phrase>
6547<phrase>
6548 id: LANG_INSERT_FIRST
6549 desc: in onplay menu. insert a track/playlist into dynamic playlist.
6550 user: core
6551 <source>
6552 *: "Insert Next"
6553 </source>
6554 <dest>
6555 *: "Вмъкване като Ñледващ"
6556 </dest>
6557 <voice>
6558 *: "Вмъкване като Ñледващ"
6559 </voice>
6560</phrase>
6561<phrase>
6562 id: LANG_INSERT_LAST
6563 desc: in onplay menu. append a track/playlist into dynamic playlist.
6564 user: core
6565 <source>
6566 *: "Insert Last"
6567 </source>
6568 <dest>
6569 *: "Вмъкване като поÑледен"
6570 </dest>
6571 <voice>
6572 *: "Вмъкване като поÑледен"
6573 </voice>
6574</phrase>
6575<phrase>
6576 id: LANG_INSERT_SHUFFLED
6577 desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
6578 user: core
6579 <source>
6580 *: "Insert Shuffled"
6581 </source>
6582 <dest>
6583 *: "Вмъкване разбъркано"
6584 </dest>
6585 <voice>
6586 *: "Вмъкване разбъркано"
6587 </voice>
6588</phrase>
6589<phrase>
6590 id: LANG_QUEUE 6069 id: LANG_QUEUE
6591 desc: The verb/action Queue 6070 desc: The verb/action Queue
6592 user: core 6071 user: core
@@ -6643,20 +6122,6 @@
6643 </voice> 6122 </voice>
6644</phrase> 6123</phrase>
6645<phrase> 6124<phrase>
6646 id: LANG_REPLACE
6647 desc: in onplay menu. Replace the current playlist with a new one.
6648 user: core
6649 <source>
6650 *: "Play Next"
6651 </source>
6652 <dest>
6653 *: "Свирене като Ñледваща"
6654 </dest>
6655 <voice>
6656 *: "Свирене като Ñледваща"
6657 </voice>
6658</phrase>
6659<phrase>
6660 id: LANG_PLAYLIST_INSERT_COUNT 6125 id: LANG_PLAYLIST_INSERT_COUNT
6661 desc: splash number of tracks inserted 6126 desc: splash number of tracks inserted
6662 user: core 6127 user: core
@@ -6723,7 +6188,7 @@
6723 *: "ТърÑене... %d намерени (%s)" 6188 *: "ТърÑене... %d намерени (%s)"
6724 </dest> 6189 </dest>
6725 <voice> 6190 <voice>
6726 *: "" 6191 *: "Ðамерени"
6727 </voice> 6192 </voice>
6728</phrase> 6193</phrase>
6729<phrase> 6194<phrase>
@@ -6741,34 +6206,6 @@
6741 </voice> 6206 </voice>
6742</phrase> 6207</phrase>
6743<phrase> 6208<phrase>
6744 id: LANG_CATALOG_VIEW
6745 desc: in onplay playlist catalogue submenu
6746 user: core
6747 <source>
6748 *: "View Catalogue"
6749 </source>
6750 <dest>
6751 *: "Разглеждане на каталога"
6752 </dest>
6753 <voice>
6754 *: "Разглеждане на каталога"
6755 </voice>
6756</phrase>
6757<phrase>
6758 id: LANG_CATALOG_ADD_TO
6759 desc: in onplay playlist catalogue submenu
6760 user: core
6761 <source>
6762 *: "Add to Playlist"
6763 </source>
6764 <dest>
6765 *: "ДобавÑне към плейлиÑта"
6766 </dest>
6767 <voice>
6768 *: "ДобавÑне към плейлиÑта"
6769 </voice>
6770</phrase>
6771<phrase>
6772 id: LANG_CATALOG_ADD_TO_NEW 6209 id: LANG_CATALOG_ADD_TO_NEW
6773 desc: in onplay playlist catalogue submenu 6210 desc: in onplay playlist catalogue submenu
6774 user: core 6211 user: core
@@ -7119,20 +6556,6 @@
7119 </voice> 6556 </voice>
7120</phrase> 6557</phrase>
7121<phrase> 6558<phrase>
7122 id: LANG_ID3_ALBUM_GAIN
7123 desc: in tag viewer
7124 user: core
7125 <source>
7126 *: "Album Gain"
7127 </source>
7128 <dest>
7129 *: "Сила на албума"
7130 </dest>
7131 <voice>
7132 *: "Сила на албума"
7133 </voice>
7134</phrase>
7135<phrase>
7136 id: LANG_ID3_PATH 6559 id: LANG_ID3_PATH
7137 desc: in tag viewer 6560 desc: in tag viewer
7138 user: core 6561 user: core
@@ -7432,7 +6855,7 @@
7432 </dest> 6855 </dest>
7433 <voice> 6856 <voice>
7434 *: none 6857 *: none
7435 pitchscreen: "УÑилване на виÑочината и ÑкороÑтта" 6858 pitchscreen: ""
7436 </voice> 6859 </voice>
7437</phrase> 6860</phrase>
7438<phrase> 6861<phrase>
@@ -7449,7 +6872,7 @@
7449 </dest> 6872 </dest>
7450 <voice> 6873 <voice>
7451 *: none 6874 *: none
7452 pitchscreen: "ÐамалÑване на виÑочината и ÑкороÑтта" 6875 pitchscreen: ""
7453 </voice> 6876 </voice>
7454</phrase> 6877</phrase>
7455<phrase> 6878<phrase>
@@ -7466,7 +6889,7 @@
7466 </dest> 6889 </dest>
7467 <voice> 6890 <voice>
7468 *: none 6891 *: none
7469 pitchscreen: "Полутон нагоре" 6892 pitchscreen: ""
7470 </voice> 6893 </voice>
7471</phrase> 6894</phrase>
7472<phrase> 6895<phrase>
@@ -7483,7 +6906,7 @@
7483 </dest> 6906 </dest>
7484 <voice> 6907 <voice>
7485 *: none 6908 *: none
7486 pitchscreen: "Полутон надолу" 6909 pitchscreen: ""
7487 </voice> 6910 </voice>
7488</phrase> 6911</phrase>
7489<phrase> 6912<phrase>
@@ -7506,11 +6929,9 @@
7506 user: core 6929 user: core
7507 <source> 6930 <source>
7508 *: "End of Song List" 6931 *: "End of Song List"
7509 archosplayer: "End of List"
7510 </source> 6932 </source>
7511 <dest> 6933 <dest>
7512 *: "Край на ÑпиÑъка Ñ Ð¿ÐµÑни" 6934 *: "Край на ÑпиÑъка Ñ Ð¿ÐµÑни"
7513 archosplayer: "Край на ÑпиÑъка"
7514 </dest> 6935 </dest>
7515 <voice> 6936 <voice>
7516 *: "Край на ÑпиÑъка Ñ Ð¿ÐµÑни" 6937 *: "Край на ÑпиÑъка Ñ Ð¿ÐµÑни"
@@ -7527,7 +6948,7 @@
7527 *: "Създаване" 6948 *: "Създаване"
7528 </dest> 6949 </dest>
7529 <voice> 6950 <voice>
7530 *: "Създаване" 6951 *: ""
7531 </voice> 6952 </voice>
7532</phrase> 6953</phrase>
7533<phrase> 6954<phrase>
@@ -7545,20 +6966,6 @@
7545 </voice> 6966 </voice>
7546</phrase> 6967</phrase>
7547<phrase> 6968<phrase>
7548 id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR
7549 desc: Playlist error
7550 user: core
7551 <source>
7552 *: "Error updating playlist control file"
7553 </source>
7554 <dest>
7555 *: "Грешка при обновÑването на ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» на плейлиÑта"
7556 </dest>
7557 <voice>
7558 *: "Грешка при обновÑването на ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» на плейлиÑта"
7559 </voice>
7560</phrase>
7561<phrase>
7562 id: LANG_PLAYLIST_ACCESS_ERROR 6969 id: LANG_PLAYLIST_ACCESS_ERROR
7563 desc: Playlist error 6970 desc: Playlist error
7564 user: core 6971 user: core
@@ -7639,7 +7046,7 @@
7639 *: "Режим:" 7046 *: "Режим:"
7640 </dest> 7047 </dest>
7641 <voice> 7048 <voice>
7642 *: "Режим:" 7049 *: "Режим"
7643 </voice> 7050 </voice>
7644</phrase> 7051</phrase>
7645<phrase> 7052<phrase>
@@ -7684,7 +7091,7 @@
7684 *: "Клавиатурата е заключена" 7091 *: "Клавиатурата е заключена"
7685 </dest> 7092 </dest>
7686 <voice> 7093 <voice>
7687 *: "Клавиатурата е заключена" 7094 *: ""
7688 </voice> 7095 </voice>
7689</phrase> 7096</phrase>
7690<phrase> 7097<phrase>
@@ -7698,7 +7105,7 @@
7698 *: "Клавиатурата е отключена" 7105 *: "Клавиатурата е отключена"
7699 </dest> 7106 </dest>
7700 <voice> 7107 <voice>
7701 *: "Клавиатурата е отключена" 7108 *: ""
7702 </voice> 7109 </voice>
7703</phrase> 7110</phrase>
7704<phrase> 7111<phrase>
@@ -7740,58 +7147,7 @@
7740 </dest> 7147 </dest>
7741 <voice> 7148 <voice>
7742 *: none 7149 *: none
7743 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,sansac200*,sansae200*: "" 7150 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
7744 </voice>
7745</phrase>
7746<phrase>
7747 id: LANG_DB_INF
7748 desc: -inf db for values below measurement
7749 user: core
7750 <source>
7751 *: none
7752 recording: "-inf"
7753 </source>
7754 <dest>
7755 *: none
7756 recording: "-безкр"
7757 </dest>
7758 <voice>
7759 *: none
7760 recording: "ÐœÐ¸Ð½ÑƒÑ Ð±ÐµÐ·ÐºÑ€Ð°Ð¹Ð½Ð¾ÑÑ‚"
7761 </voice>
7762</phrase>
7763<phrase>
7764 id: LANG_CONFIRM_SHUTDOWN
7765 desc: in shutdown screen
7766 user: core
7767 <source>
7768 *: none
7769 soft_shutdown: "Press OFF to shut down"
7770 </source>
7771 <dest>
7772 *: none
7773 soft_shutdown: "ÐатиÑнете Включване за Изключване"
7774 </dest>
7775 <voice>
7776 *: none
7777 soft_shutdown: "ÐатиÑнете Включване за Изключване"
7778 </voice>
7779</phrase>
7780<phrase>
7781 id: LANG_REMOVE_MMC
7782 desc: before acknowledging usb in case an MMC is inserted (Ondio)
7783 user: core
7784 <source>
7785 *: none
7786 archosondio*: "Please remove inserted MMC"
7787 </source>
7788 <dest>
7789 *: none
7790 archosondio*: "ÐœÐ¾Ð»Ñ Ð¸Ð·Ð²Ð°Ð´ÐµÑ‚Ðµ MMC"
7791 </dest>
7792 <voice>
7793 *: none
7794 archosondio*: "ÐœÐ¾Ð»Ñ Ð¸Ð·Ð²Ð°Ð´ÐµÑ‚Ðµ картата памет"
7795 </voice> 7151 </voice>
7796</phrase> 7152</phrase>
7797<phrase> 7153<phrase>
@@ -7824,7 +7180,7 @@
7824</phrase> 7180</phrase>
7825<phrase> 7181<phrase>
7826 id: LANG_OFF_ABORT 7182 id: LANG_OFF_ABORT
7827 desc: Used on archosrecorder models 7183 desc: Used on many models
7828 user: core 7184 user: core
7829 <source> 7185 <source>
7830 *: "OFF to abort" 7186 *: "OFF to abort"
@@ -7906,7 +7262,7 @@
7906</phrase> 7262</phrase>
7907<phrase> 7263<phrase>
7908 id: LANG_PLUGIN_WRONG_MODEL 7264 id: LANG_PLUGIN_WRONG_MODEL
7909 desc: The plugin is not compatible with the archos model trying to run it 7265 desc: The plugin is not compatible with the player model trying to run it
7910 user: core 7266 user: core
7911 <source> 7267 <source>
7912 *: "Incompatible model" 7268 *: "Incompatible model"
@@ -7915,7 +7271,7 @@
7915 *: "ÐеÑъвмеÑтим модел" 7271 *: "ÐеÑъвмеÑтим модел"
7916 </dest> 7272 </dest>
7917 <voice> 7273 <voice>
7918 *: "" 7274 *: "ÐеÑъвмеÑтим модел"
7919 </voice> 7275 </voice>
7920</phrase> 7276</phrase>
7921<phrase> 7277<phrase>
@@ -7929,7 +7285,7 @@
7929 *: "ÐеÑъвмеÑтима верÑиÑ" 7285 *: "ÐеÑъвмеÑтима верÑиÑ"
7930 </dest> 7286 </dest>
7931 <voice> 7287 <voice>
7932 *: "" 7288 *: "ÐеÑъвмеÑтима верÑиÑ"
7933 </voice> 7289 </voice>
7934</phrase> 7290</phrase>
7935<phrase> 7291<phrase>
@@ -7943,7 +7299,7 @@
7943 *: "Плъгинът върна грешка" 7299 *: "Плъгинът върна грешка"
7944 </dest> 7300 </dest>
7945 <voice> 7301 <voice>
7946 *: "" 7302 *: "Плъгинът върна грешка"
7947 </voice> 7303 </voice>
7948</phrase> 7304</phrase>
7949<phrase> 7305<phrase>
@@ -8016,41 +7372,7 @@
8016 </dest> 7372 </dest>
8017 <voice> 7373 <voice>
8018 *: none 7374 *: none
8019 charging: "БатериÑ: Зареждане" 7375 charging: "БатериÑта Ñе зарежда"
8020 </voice>
8021</phrase>
8022<phrase>
8023 id: LANG_BATTERY_TOPOFF_CHARGE
8024 desc: in info display, shows that top off charge is running Only for V1 archosrecorder
8025 user: core
8026 <source>
8027 *: none
8028 archosrecorder: "Battery: Top-Off Chg"
8029 </source>
8030 <dest>
8031 *: none
8032 archosrecorder: "БатериÑ: нерегулирано зареждане"
8033 </dest>
8034 <voice>
8035 *: none
8036 archosrecorder: "БатериÑ: нерегулирано зареждане"
8037 </voice>
8038</phrase>
8039<phrase>
8040 id: LANG_BATTERY_TRICKLE_CHARGE
8041 desc: in info display, shows that trickle charge is running
8042 user: core
8043 <source>
8044 *: none
8045 charging: "Battery: Trickle Chg"
8046 </source>
8047 <dest>
8048 *: none
8049 charging: "БатериÑ: регулирано зареждане"
8050 </dest>
8051 <voice>
8052 *: none
8053 charging: "БатериÑ: регулирано зареждане"
8054 </voice> 7376 </voice>
8055</phrase> 7377</phrase>
8056<phrase> 7378<phrase>
@@ -8092,49 +7414,7 @@
8092 *: "B" 7414 *: "B"
8093 </dest> 7415 </dest>
8094 <voice> 7416 <voice>
8095 *: "байта" 7417 *: ""
8096 </voice>
8097</phrase>
8098<phrase>
8099 id: LANG_KILOBYTE
8100 desc: a unit postfix, also voiced
8101 user: core
8102 <source>
8103 *: "KB"
8104 </source>
8105 <dest>
8106 *: "KB"
8107 </dest>
8108 <voice>
8109 *: "килобайта"
8110 </voice>
8111</phrase>
8112<phrase>
8113 id: LANG_MEGABYTE
8114 desc: a unit postfix, also voiced
8115 user: core
8116 <source>
8117 *: "MB"
8118 </source>
8119 <dest>
8120 *: "MB"
8121 </dest>
8122 <voice>
8123 *: "мегабайта"
8124 </voice>
8125</phrase>
8126<phrase>
8127 id: LANG_GIGABYTE
8128 desc: a unit postfix, also voiced
8129 user: core
8130 <source>
8131 *: "GB"
8132 </source>
8133 <dest>
8134 *: "GB"
8135 </dest>
8136 <voice>
8137 *: "гигабайта"
8138 </voice> 7418 </voice>
8139</phrase> 7419</phrase>
8140<phrase> 7420<phrase>
@@ -8145,7 +7425,7 @@
8145 *: "." 7425 *: "."
8146 </source> 7426 </source>
8147 <dest> 7427 <dest>
8148 *: "." 7428 *: "~."
8149 </dest> 7429 </dest>
8150 <voice> 7430 <voice>
8151 *: "цÑло и" 7431 *: "цÑло и"
@@ -9397,7 +8677,7 @@
9397 *: "" 8677 *: ""
9398 </dest> 8678 </dest>
9399 <voice> 8679 <voice>
9400 *: "cuesheet" 8680 *: "ÑпиÑък Ñ Ð¼Ð°Ñ€ÐºÐµÑ€Ð¸"
9401 </voice> 8681 </voice>
9402</phrase> 8682</phrase>
9403<phrase> 8683<phrase>
@@ -9428,7 +8708,7 @@
9428 </dest> 8708 </dest>
9429 <voice> 8709 <voice>
9430 *: none 8710 *: none
9431 rtc: "ЧаÑÑŠÑ‚ е:" 8711 rtc: "ЧаÑÑŠÑ‚ е"
9432 </voice> 8712 </voice>
9433</phrase> 8713</phrase>
9434<phrase> 8714<phrase>
@@ -9474,241 +8754,6 @@
9474 </voice> 8754 </voice>
9475</phrase> 8755</phrase>
9476<phrase> 8756<phrase>
9477 id: LANG_SYSFONT_MODE
9478 desc: in wps F2 pressed
9479 user: core
9480 <source>
9481 *: "Mode:"
9482 </source>
9483 <dest>
9484 *: "Режим:"
9485 </dest>
9486 <voice>
9487 *: ""
9488 </voice>
9489</phrase>
9490<phrase>
9491 id: LANG_SYSFONT_DIRBROWSE_F1
9492 desc: in dir browser, F1 button bar text
9493 user: core
9494 <source>
9495 *: none
9496 recorder_pad: "Menu"
9497 </source>
9498 <dest>
9499 *: none
9500 recorder_pad: "Меню"
9501 </dest>
9502 <voice>
9503 *: none
9504 recorder_pad: ""
9505 </voice>
9506</phrase>
9507<phrase>
9508 id: LANG_SYSFONT_DIRBROWSE_F2
9509 desc: in dir browser, F2 button bar text
9510 user: core
9511 <source>
9512 *: none
9513 recorder_pad: "Option"
9514 </source>
9515 <dest>
9516 *: none
9517 recorder_pad: "ОпциÑ"
9518 </dest>
9519 <voice>
9520 *: none
9521 recorder_pad: ""
9522 </voice>
9523</phrase>
9524<phrase>
9525 id: LANG_SYSFONT_DIRBROWSE_F3
9526 desc: in dir browser, F3 button bar text
9527 user: core
9528 <source>
9529 *: none
9530 recorder_pad: "LCD"
9531 </source>
9532 <dest>
9533 *: none
9534 recorder_pad: "LCD"
9535 </dest>
9536 <voice>
9537 *: none
9538 recorder_pad: ""
9539 </voice>
9540</phrase>
9541<phrase>
9542 id: LANG_SYSFONT_CHANNEL_STEREO
9543 desc: in sound_settings
9544 user: core
9545 <source>
9546 *: none
9547 recording: "Stereo"
9548 </source>
9549 <dest>
9550 *: none
9551 recording: "Стерео"
9552 </dest>
9553 <voice>
9554 *: none
9555 recording: "Стерео"
9556 </voice>
9557</phrase>
9558<phrase>
9559 id: LANG_SYSFONT_CHANNEL_MONO
9560 desc: in sound_settings
9561 user: core
9562 <source>
9563 *: none
9564 recording: "Mono"
9565 </source>
9566 <dest>
9567 *: none
9568 recording: "Моно"
9569 </dest>
9570 <voice>
9571 *: none
9572 recording: "Моно"
9573 </voice>
9574</phrase>
9575<phrase>
9576 id: LANG_SYSFONT_RECORDING_QUALITY
9577 desc: in the recording settings
9578 user: core
9579 <source>
9580 *: none
9581 recording_hwcodec: "Quality"
9582 </source>
9583 <dest>
9584 *: none
9585 recording_hwcodec: "КачеÑтво"
9586 </dest>
9587 <voice>
9588 *: none
9589 recording_hwcodec: "КачеÑтво"
9590 </voice>
9591</phrase>
9592<phrase>
9593 id: LANG_SYSFONT_RECORDING_FREQUENCY
9594 desc: in the recording settings
9595 user: core
9596 <source>
9597 *: none
9598 recording: "Frequency"
9599 </source>
9600 <dest>
9601 *: none
9602 recording: "ЧеÑтота"
9603 </dest>
9604 <voice>
9605 *: none
9606 recording: "ЧеÑтота"
9607 </voice>
9608</phrase>
9609<phrase>
9610 id: LANG_SYSFONT_RECORDING_SOURCE
9611 desc: in the recording settings
9612 user: core
9613 <source>
9614 *: none
9615 recording: "Source"
9616 </source>
9617 <dest>
9618 *: none
9619 recording: "Източник"
9620 </dest>
9621 <voice>
9622 *: none
9623 recording: "Източник"
9624 </voice>
9625</phrase>
9626<phrase>
9627 id: LANG_SYSFONT_RECORDING_SRC_MIC
9628 desc: in the recording settings
9629 user: core
9630 <source>
9631 *: none
9632 recording: "Int. Mic"
9633 </source>
9634 <dest>
9635 *: none
9636 recording: "Вграден микрофон"
9637 </dest>
9638 <voice>
9639 *: none
9640 recording: "Вграден микрофон"
9641 </voice>
9642</phrase>
9643<phrase>
9644 id: LANG_SYSFONT_LINE_IN
9645 desc: in the recording settings
9646 user: core
9647 <source>
9648 *: none
9649 recording: "Line In"
9650 </source>
9651 <dest>
9652 *: none
9653 recording: "Линеен вход"
9654 </dest>
9655 <voice>
9656 *: none
9657 recording: "Линеен вход"
9658 </voice>
9659</phrase>
9660<phrase>
9661 id: LANG_SYSFONT_RECORDING_SRC_DIGITAL
9662 desc: in the recording settings
9663 user: core
9664 <source>
9665 *: none
9666 recording: "Digital"
9667 </source>
9668 <dest>
9669 *: none
9670 recording: "Цифров"
9671 </dest>
9672 <voice>
9673 *: none
9674 recording: "Цифров"
9675 </voice>
9676</phrase>
9677<phrase>
9678 id: LANG_SYSFONT_CHANNELS
9679 desc: in the recording settings
9680 user: core
9681 <source>
9682 *: none
9683 recording: "Channels"
9684 </source>
9685 <dest>
9686 *: none
9687 recording: "Канали"
9688 </dest>
9689 <voice>
9690 *: none
9691 recording: "Канали"
9692 </voice>
9693</phrase>
9694<phrase>
9695 id: LANG_SYSFONT_RECORD_TRIGGER
9696 desc: in recording settings_menu
9697 user: core
9698 <source>
9699 *: none
9700 recording: "Trigger"
9701 </source>
9702 <dest>
9703 *: none
9704 recording: "Тригер"
9705 </dest>
9706 <voice>
9707 *: none
9708 recording: "Тригер"
9709 </voice>
9710</phrase>
9711<phrase>
9712 id: VOICE_OF 8757 id: VOICE_OF
9713 desc: spoken only, as in 3/8 => 3 of 8 8758 desc: spoken only, as in 3/8 => 3 of 8
9714 user: core 8759 user: core
@@ -10287,7 +9332,7 @@
10287 *: "" 9332 *: ""
10288 </dest> 9333 </dest>
10289 <voice> 9334 <voice>
10290 *: "OK" 9335 *: "окей"
10291 </voice> 9336 </voice>
10292</phrase> 9337</phrase>
10293<phrase> 9338<phrase>
@@ -10606,7 +9651,7 @@
10606 </dest> 9651 </dest>
10607 <voice> 9652 <voice>
10608 *: none 9653 *: none
10609 agc: "Ðвтоматично регулиране на уÑилването: време на ÑмущениÑ" 9654 agc: "Време на ÑÐ¼ÑƒÑ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ автоматично регулиране на уÑилването"
10610 </voice> 9655 </voice>
10611</phrase> 9656</phrase>
10612<phrase> 9657<phrase>
@@ -10623,7 +9668,7 @@
10623 </dest> 9668 </dest>
10624 <voice> 9669 <voice>
10625 *: none 9670 *: none
10626 agc: "Ðвтоматично регулиране на уÑилването: МакÑимални ÑмущениÑ" 9671 agc: "МакÑимални ÑÐ¼ÑƒÑ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ автоматично регулиране на уÑилването"
10627 </voice> 9672 </voice>
10628</phrase> 9673</phrase>
10629<phrase> 9674<phrase>
@@ -11231,20 +10276,6 @@
11231 </voice> 10276 </voice>
11232</phrase> 10277</phrase>
11233<phrase> 10278<phrase>
11234 id: LANG_SCROLLBAR_POSITION
11235 desc: in Settings -> General -> Display -> Status-/Scrollbar
11236 user: core
11237 <source>
11238 *: "Scroll Bar Position"
11239 </source>
11240 <dest>
11241 *: "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð½Ð° лентата за позициониране"
11242 </dest>
11243 <voice>
11244 *: "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð½Ð° лентата за позициониране"
11245 </voice>
11246</phrase>
11247<phrase>
11248 id: LANG_COMPRESSOR 10279 id: LANG_COMPRESSOR
11249 desc: in sound settings 10280 desc: in sound settings
11250 user: core 10281 user: core
@@ -11506,20 +10537,6 @@
11506 </voice> 10537 </voice>
11507</phrase> 10538</phrase>
11508<phrase> 10539<phrase>
11509 id: LANG_STATUSBAR_CUSTOM
11510 desc: if this translation is compatible with LANG_CHANNEL_CUSTOM, then please use the same translation. it can be combined later then
11511 user: core
11512 <source>
11513 *: "Custom"
11514 </source>
11515 <dest>
11516 *: "ПотребителÑки"
11517 </dest>
11518 <voice>
11519 *: "ПотребителÑки"
11520 </voice>
11521</phrase>
11522<phrase>
11523 id: VOICE_EXT_SBS 10540 id: VOICE_EXT_SBS
11524 desc: spoken only, for file extension 10541 desc: spoken only, for file extension
11525 user: core 10542 user: core
@@ -11568,20 +10585,6 @@
11568 </voice> 10585 </voice>
11569</phrase> 10586</phrase>
11570<phrase> 10587<phrase>
11571 id: LANG_INSERT_LAST_SHUFFLED
11572 desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
11573 user: core
11574 <source>
11575 *: "Insert Last Shuffled"
11576 </source>
11577 <dest>
11578 *: "Вмъкване като поÑледна разбъркано"
11579 </dest>
11580 <voice>
11581 *: "Вмъкване като поÑледна разбъркано"
11582 </voice>
11583</phrase>
11584<phrase>
11585 id: LANG_QUEUE_LAST_SHUFFLED 10588 id: LANG_QUEUE_LAST_SHUFFLED
11586 desc: in onplay menu. queue a playlist randomly at end of dynamic playlist 10589 desc: in onplay menu. queue a playlist randomly at end of dynamic playlist
11587 user: core 10590 user: core
@@ -11640,7 +10643,7 @@
11640 *: "Следващ запиÑ:" 10643 *: "Следващ запиÑ:"
11641 </dest> 10644 </dest>
11642 <voice> 10645 <voice>
11643 *: "Следващ запиÑ:" 10646 *: "Следващ запиÑ"
11644 </voice> 10647 </voice>
11645</phrase> 10648</phrase>
11646<phrase> 10649<phrase>
@@ -11654,7 +10657,7 @@
11654 *: "Следващ:" 10657 *: "Следващ:"
11655 </dest> 10658 </dest>
11656 <voice> 10659 <voice>
11657 *: "Следващ:" 10660 *: "Следващ"
11658 </voice> 10661 </voice>
11659</phrase> 10662</phrase>
11660<phrase> 10663<phrase>
@@ -12119,11 +11122,11 @@
12119 </source> 11122 </source>
12120 <dest> 11123 <dest>
12121 *: none 11124 *: none
12122 touchscreen: "OK" 11125 touchscreen: "ОК"
12123 </dest> 11126 </dest>
12124 <voice> 11127 <voice>
12125 *: none 11128 *: none
12126 touchscreen: "OK" 11129 touchscreen: "окей"
12127 </voice> 11130 </voice>
12128</phrase> 11131</phrase>
12129<phrase> 11132<phrase>
@@ -12136,11 +11139,11 @@
12136 </source> 11139 </source>
12137 <dest> 11140 <dest>
12138 *: none 11141 *: none
12139 touchscreen: "Del" 11142 touchscreen: "~Del"
12140 </dest> 11143 </dest>
12141 <voice> 11144 <voice>
12142 *: none 11145 *: none
12143 touchscreen: "Del" 11146 touchscreen: "изтриване"
12144 </voice> 11147 </voice>
12145</phrase> 11148</phrase>
12146<phrase> 11149<phrase>
@@ -12175,20 +11178,6 @@
12175 </voice> 11178 </voice>
12176</phrase> 11179</phrase>
12177<phrase> 11180<phrase>
12178 id: LANG_SET_AS_START_DIR
12179 desc: used in the onplay menu to set a starting browser dir
12180 user: core
12181 <source>
12182 *: "Start File Browser Here"
12183 </source>
12184 <dest>
12185 *: "ОтварÑне на Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÑŠÑ€ тук"
12186 </dest>
12187 <voice>
12188 *: "ОтварÑне на Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÑŠÑ€ тук"
12189 </voice>
12190</phrase>
12191<phrase>
12192 id: LANG_RESET_START_DIR 11181 id: LANG_RESET_START_DIR
12193 desc: reset the browser start directory 11182 desc: reset the browser start directory
12194 user: core 11183 user: core
@@ -12216,7 +11205,7 @@
12216 </dest> 11205 </dest>
12217 <voice> 11206 <voice>
12218 *: none 11207 *: none
12219 radio: "Сила на Ñигнала:" 11208 radio: "Сила на Ñигнала"
12220 </voice> 11209 </voice>
12221</phrase> 11210</phrase>
12222<phrase> 11211<phrase>
@@ -12290,20 +11279,6 @@
12290 </voice> 11279 </voice>
12291</phrase> 11280</phrase>
12292<phrase> 11281<phrase>
12293 id: LANG_SET_AS_PLAYLISTCAT_DIR
12294 desc: used in the onplay menu to set a playlist catalogue dir
12295 user: core
12296 <source>
12297 *: "Set As Playlist Catalogue Directory"
12298 </source>
12299 <dest>
12300 *: "Задай като папка за каталога на плейлиÑти"
12301 </dest>
12302 <voice>
12303 *: "Задай като папка за каталога на плейлиÑти"
12304 </voice>
12305</phrase>
12306<phrase>
12307 id: LANG_RESET_PLAYLISTCAT_DIR 11282 id: LANG_RESET_PLAYLISTCAT_DIR
12308 desc: 11283 desc:
12309 user: core 11284 user: core
@@ -12380,20 +11355,6 @@
12380 </voice> 11355 </voice>
12381</phrase> 11356</phrase>
12382<phrase> 11357<phrase>
12383 id: LANG_AUTOMATIC
12384 desc: generic automatic
12385 user: core
12386 <source>
12387 *: "Automatic"
12388 </source>
12389 <dest>
12390 *: "Ðвтоматично"
12391 </dest>
12392 <voice>
12393 *: "Ðвтоматично"
12394 </voice>
12395</phrase>
12396<phrase>
12397 id: LANG_SLEEP_TIMER_DURATION 11358 id: LANG_SLEEP_TIMER_DURATION
12398 desc: default sleep timer duration in minutes 11359 desc: default sleep timer duration in minutes
12399 user: core 11360 user: core
@@ -12786,10 +11747,10 @@
12786 *: "Android Debug Bridge" 11747 *: "Android Debug Bridge"
12787 </source> 11748 </source>
12788 <dest> 11749 <dest>
12789 *: "Android Debug Bridge" 11750 *: "ОтÑтранÑване на грешки в Ðндроид"
12790 </dest> 11751 </dest>
12791 <voice> 11752 <voice>
12792 *: "Android Debug Bridge" 11753 *: "ОтÑтранÑване на грешки в Ðндроид"
12793 </voice> 11754 </voice>
12794</phrase> 11755</phrase>
12795<phrase> 11756<phrase>
@@ -12943,9 +11904,4511 @@
12943 *: "Haas Surround" 11904 *: "Haas Surround"
12944 </source> 11905 </source>
12945 <dest> 11906 <dest>
12946 *: "Haas Surround" 11907 *: "Ð¥Ð°Ð°Ñ Ñъраунд"
12947 </dest> 11908 </dest>
12948 <voice> 11909 <voice>
12949 *: "Haas Surround" 11910 *: "Ð¥Ð°Ð°Ñ Ñъраунд"
11911 </voice>
11912</phrase>
11913<phrase>
11914 id: LANG_SINGLE_MODE
11915 desc: single mode
11916 user: core
11917 <source>
11918 *: "Single Mode"
11919 </source>
11920 <dest>
11921 *: "Единичен режим"
11922 </dest>
11923 <voice>
11924 *: "Единичен режим"
11925 </voice>
11926</phrase>
11927<phrase>
11928 id: LANG_TALK_MIXER_LEVEL
11929 desc: Relative volume of voice prompts
11930 user: core
11931 <source>
11932 *: "Voice prompt volume"
11933 </source>
11934 <dest>
11935 *: "Сила на глаÑовите ÑъобщениÑ"
11936 </dest>
11937 <voice>
11938 *: "Сила на глаÑовите ÑъобщениÑ"
11939 </voice>
11940</phrase>
11941<phrase>
11942 id: LANG_FILTER_SHORT_SHARP
11943 desc: in sound settings
11944 user: core
11945 <source>
11946 *: none
11947 filter_roll_off: "Short Sharp"
11948 </source>
11949 <dest>
11950 *: none
11951 filter_roll_off: "ÐšÑŠÑ Ð¸ Ñ€Ñзък"
11952 </dest>
11953 <voice>
11954 *: none
11955 filter_roll_off: "ÐšÑŠÑ Ð¸ Ñ€Ñзък"
11956 </voice>
11957</phrase>
11958<phrase>
11959 id: LANG_FILTER_SHORT_SLOW
11960 desc: in sound settings
11961 user: core
11962 <source>
11963 *: none
11964 filter_roll_off: "Short Slow"
11965 </source>
11966 <dest>
11967 *: none
11968 filter_roll_off: "ÐšÑŠÑ Ð¸ бавен"
11969 </dest>
11970 <voice>
11971 *: none
11972 filter_roll_off: "ÐšÑŠÑ Ð¸ бавен"
11973 </voice>
11974</phrase>
11975<phrase>
11976 id: LANG_FILTER_SUPER_SLOW
11977 desc: in sound settings
11978 user: core
11979 <source>
11980 *: none
11981 filter_roll_off: "Super Slow"
11982 </source>
11983 <dest>
11984 *: none
11985 filter_roll_off: "Супер бавен"
11986 </dest>
11987 <voice>
11988 *: none
11989 filter_roll_off: "Супер бавен"
11990 </voice>
11991</phrase>
11992<phrase>
11993 id: LANG_FILTER_SHORT
11994 desc: in sound settings
11995 user: core
11996 <source>
11997 *: none
11998 es9018: "Short"
11999 </source>
12000 <dest>
12001 *: none
12002 es9018: "КъÑ"
12003 </dest>
12004 <voice>
12005 *: none
12006 es9018: "КъÑ"
12007 </voice>
12008</phrase>
12009<phrase>
12010 id: LANG_FILTER_BYPASS
12011 desc: in sound settings
12012 user: core
12013 <source>
12014 *: none
12015 es9018: "Bypass"
12016 </source>
12017 <dest>
12018 *: none
12019 es9018: "ПропуÑкане"
12020 </dest>
12021 <voice>
12022 *: none
12023 es9018: "ПропуÑкане"
12024 </voice>
12025</phrase>
12026<phrase>
12027 id: LANG_FILTER_LINEAR_FAST
12028 desc: in sound settings
12029 user: core
12030 <source>
12031 *: none
12032 es9218: "Linear Fast"
12033 </source>
12034 <dest>
12035 *: none
12036 es9218: "Линеен бърз"
12037 </dest>
12038 <voice>
12039 *: none
12040 es9218: "Линеен бърз"
12041 </voice>
12042</phrase>
12043<phrase>
12044 id: LANG_FILTER_LINEAR_SLOW
12045 desc: in sound settings
12046 user: core
12047 <source>
12048 *: none
12049 es9218: "Linear Slow"
12050 </source>
12051 <dest>
12052 *: none
12053 es9218: "Линеен бавен"
12054 </dest>
12055 <voice>
12056 *: none
12057 es9218: "Линеен бавен"
12058 </voice>
12059</phrase>
12060<phrase>
12061 id: LANG_FILTER_MINIMUM_FAST
12062 desc: in sound settings
12063 user: core
12064 <source>
12065 *: none
12066 es9218: "Minimum Fast"
12067 </source>
12068 <dest>
12069 *: none
12070 es9218: "Минимален бърз"
12071 </dest>
12072 <voice>
12073 *: none
12074 es9218: "Минимален бърз"
12075 </voice>
12076</phrase>
12077<phrase>
12078 id: LANG_FILTER_MINIMUM_SLOW
12079 desc: in sound settings
12080 user: core
12081 <source>
12082 *: none
12083 es9218: "Minimum Slow"
12084 </source>
12085 <dest>
12086 *: none
12087 es9218: "Минимален бавен"
12088 </dest>
12089 <voice>
12090 *: none
12091 es9218: "Минимален бавен"
12092 </voice>
12093</phrase>
12094<phrase>
12095 id: LANG_FILTER_APODIZING_1
12096 desc: in sound settings
12097 user: core
12098 <source>
12099 *: none
12100 es9218: "Apodizing type 1"
12101 </source>
12102 <dest>
12103 *: none
12104 es9218: "Ðподизиращ тип 1"
12105 </dest>
12106 <voice>
12107 *: none
12108 es9218: "Ðподизиращ тип 1"
12109 </voice>
12110</phrase>
12111<phrase>
12112 id: LANG_FILTER_APODIZING_2
12113 desc: in sound settings
12114 user: core
12115 <source>
12116 *: none
12117 es9218: "Apodizing type 2"
12118 </source>
12119 <dest>
12120 *: none
12121 es9218: "Ðподизиращ тип 2"
12122 </dest>
12123 <voice>
12124 *: none
12125 es9218: "Ðподизиращ тип 2"
12126 </voice>
12127</phrase>
12128<phrase>
12129 id: LANG_FILTER_HYBRID_FAST
12130 desc: in sound settings
12131 user: core
12132 <source>
12133 *: none
12134 es9218: "Hybrid Fast"
12135 </source>
12136 <dest>
12137 *: none
12138 es9218: "Хибриден бърз"
12139 </dest>
12140 <voice>
12141 *: none
12142 es9218: "Хибриден бърз"
12143 </voice>
12144</phrase>
12145<phrase>
12146 id: LANG_FILTER_BRICK_WALL
12147 desc: in sound settings
12148 user: core
12149 <source>
12150 *: none
12151 es9218: "Brick Wall"
12152 </source>
12153 <dest>
12154 *: none
12155 es9218: "Тухлена Ñтена"
12156 </dest>
12157 <voice>
12158 *: none
12159 es9218: "Тухлена Ñтена"
12160 </voice>
12161</phrase>
12162<phrase>
12163 id: LANG_DAC_POWER_MODE
12164 desc: in sound settings
12165 user: core
12166 <source>
12167 *: none
12168 dac_power_mode: "DAC power mode"
12169 es9218: "DAC output level"
12170 </source>
12171 <dest>
12172 *: none
12173 dac_power_mode: "Режим на ЦÐП"
12174 es9218: "Изходно ниво на ЦÐП"
12175 </dest>
12176 <voice>
12177 *: none
12178 dac_power_mode: "Режим на ЦÐП"
12179 es9218: "Изходно ниво на ЦÐП"
12180 </voice>
12181</phrase>
12182<phrase>
12183 id: LANG_DAC_POWER_HIGH
12184 desc: in sound settings
12185 user: core
12186 <source>
12187 *: none
12188 dac_power_mode: "High performance"
12189 es9218: "High Gain (2 Vrms)"
12190 </source>
12191 <dest>
12192 *: none
12193 dac_power_mode: "ВиÑока производителноÑÑ‚"
12194 es9218: "ГолÑмо уÑилване (2 Vrms)"
12195 </dest>
12196 <voice>
12197 *: none
12198 dac_power_mode: "ВиÑока производителноÑÑ‚"
12199 es9218: "ГолÑмо уÑилване (2 Vrms)"
12200 </voice>
12201</phrase>
12202<phrase>
12203 id: LANG_DAC_POWER_LOW
12204 desc: in sound settings
12205 user: core
12206 <source>
12207 *: none
12208 dac_power_mode: "Save battery"
12209 es9218: "Low Gain (1 Vrms)"
12210 </source>
12211 <dest>
12212 *: none
12213 dac_power_mode: "ПеÑтене на батериÑ"
12214 es9218: "Малко уÑилване (1 Vrms)"
12215 </dest>
12216 <voice>
12217 *: none
12218 dac_power_mode: "ПеÑтене на батериÑ"
12219 es9218: "Малко уÑилване (1 Vrms)"
12220 </voice>
12221</phrase>
12222<phrase>
12223 id: LANG_ACTION_ENABLED
12224 desc: Selective Actions
12225 user: core
12226 <source>
12227 *: "Enabled"
12228 </source>
12229 <dest>
12230 *: "Включено"
12231 </dest>
12232 <voice>
12233 *: "Включено"
12234 </voice>
12235</phrase>
12236<phrase>
12237 id: LANG_ACTION_PLAY
12238 desc: Selective Actions
12239 user: core
12240 <source>
12241 *: "Exempt Play"
12242 </source>
12243 <dest>
12244 *: "С изключение на възпроизвеждане"
12245 </dest>
12246 <voice>
12247 *: "С изключение на възпроизвеждане"
12248 </voice>
12249</phrase>
12250<phrase>
12251 id: LANG_ACTION_SEEK
12252 desc: Selective Actions
12253 user: core
12254 <source>
12255 *: "Exempt Seek"
12256 </source>
12257 <dest>
12258 *: "С изключение на превъртане"
12259 </dest>
12260 <voice>
12261 *: "С изключение на превъртане"
12262 </voice>
12263</phrase>
12264<phrase>
12265 id: LANG_ACTION_SKIP
12266 desc: Selective Actions
12267 user: core
12268 <source>
12269 *: "Exempt Skip"
12270 </source>
12271 <dest>
12272 *: "С изключение на пропуÑкане"
12273 </dest>
12274 <voice>
12275 *: "С изключение на пропуÑкане"
12276 </voice>
12277</phrase>
12278<phrase>
12279 id: LANG_BACKLIGHT_SELECTIVE
12280 desc: Backlight behaviour setting
12281 user: core
12282 <source>
12283 *: "Backlight Exemptions"
12284 </source>
12285 <dest>
12286 *: "Ð˜Ð·ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð·Ð° подÑветка"
12287 </dest>
12288 <voice>
12289 *: "Ð˜Ð·ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð·Ð° подÑветка"
12290 </voice>
12291</phrase>
12292<phrase>
12293 id: LANG_ACTION_DISABLE_EXT_POWER
12294 desc: Backlight behaviour setting
12295 user: core
12296 <source>
12297 *: "Disable on External Power"
12298 </source>
12299 <dest>
12300 *: "Изключи при външно захранване"
12301 </dest>
12302 <voice>
12303 *: "Изключи при външно захранване"
12304 </voice>
12305</phrase>
12306<phrase>
12307 id: LANG_ACTION_DISABLE_UNMAPPED
12308 desc: Backlight behaviour setting
12309 user: core
12310 <source>
12311 *: "Disable Unmapped Keys"
12312 </source>
12313 <dest>
12314 *: "Изключи неприÑвоените бутони"
12315 </dest>
12316 <voice>
12317 *: "Изключи неприÑвоените бутони"
12318 </voice>
12319</phrase>
12320<phrase>
12321 id: LANG_SOFTLOCK_SELECTIVE
12322 desc: Softlock behaviour setting
12323 user: core
12324 <source>
12325 *: "Advanced Key Lock"
12326 </source>
12327 <dest>
12328 *: "Разширено заключване"
12329 </dest>
12330 <voice>
12331 *: "Разширено заключване"
12332 </voice>
12333</phrase>
12334<phrase>
12335 id: LANG_ACTION_AUTOLOCK_ON
12336 desc: Softlock behaviour setting
12337 user: core
12338 <source>
12339 *: "Autolock On"
12340 </source>
12341 <dest>
12342 *: "Ðвтоматично заключване активно"
12343 </dest>
12344 <voice>
12345 *: "Ðвтоматично заключване активно"
12346 </voice>
12347</phrase>
12348<phrase>
12349 id: LANG_ACTION_AUTOLOCK_OFF
12350 desc: Softlock behaviour setting
12351 user: core
12352 <source>
12353 *: "Autolock Off"
12354 </source>
12355 <dest>
12356 *: "Ðвтоматично заключване неактивно"
12357 </dest>
12358 <voice>
12359 *: "Ðвтоматично заключване неактивно"
12360 </voice>
12361</phrase>
12362<phrase>
12363 id: LANG_ACTION_DISABLE_NOTIFY
12364 desc: Softlock behaviour setting
12365 user: core
12366 <source>
12367 *: "Disable Locked Reminders"
12368 </source>
12369 <dest>
12370 *: "Изключване на напомнÑниÑта"
12371 </dest>
12372 <voice>
12373 *: "Изключване на напомнÑниÑта"
12374 </voice>
12375</phrase>
12376<phrase>
12377 id: LANG_ACTION_DISABLE_TOUCH
12378 desc: Softlock behaviour setting
12379 user: core
12380 <source>
12381 *: "Disable Touch"
12382 </source>
12383 <dest>
12384 *: "Изключване на тъч"
12385 </dest>
12386 <voice>
12387 *: "Изключване на тъч"
12388 </voice>
12389</phrase>
12390<phrase>
12391 id: LANG_KIBIBYTE
12392 desc: a unit postfix, also voiced
12393 user: core
12394 <source>
12395 *: "KiB"
12396 </source>
12397 <dest>
12398 *: "~KiB"
12399 </dest>
12400 <voice>
12401 *: "кибибайта"
12402 </voice>
12403</phrase>
12404<phrase>
12405 id: LANG_MEBIBYTE
12406 desc: a unit postfix, also voiced
12407 user: core
12408 <source>
12409 *: "MiB"
12410 </source>
12411 <dest>
12412 *: "~MiB"
12413 </dest>
12414 <voice>
12415 *: "мебибайта"
12416 </voice>
12417</phrase>
12418<phrase>
12419 id: LANG_GIBIBYTE
12420 desc: a unit postfix, also voiced
12421 user: core
12422 <source>
12423 *: "GiB"
12424 </source>
12425 <dest>
12426 *: "~GiB"
12427 </dest>
12428 <voice>
12429 *: "гибибайта"
12430 </voice>
12431</phrase>
12432<phrase>
12433 id: LANG_BOOKMARK_SETTINGS_ONE_PER_PLAYLIST
12434 desc: Save only one bookmark for a playlist in recent bookmarks
12435 user: core
12436 <source>
12437 *: "One per playlist"
12438 </source>
12439 <dest>
12440 *: "По една за плейлиÑта"
12441 </dest>
12442 <voice>
12443 *: "По една за плейлиÑта"
12444 </voice>
12445</phrase>
12446<phrase>
12447 id: LANG_BOOKMARK_SETTINGS_ONE_PER_TRACK
12448 desc: Save only one bookmark for a combination (playlist,track) in recent bookmarks
12449 user: core
12450 <source>
12451 *: "One per track"
12452 </source>
12453 <dest>
12454 *: "По една за запиÑ"
12455 </dest>
12456 <voice>
12457 *: "По една за запиÑ"
12458 </voice>
12459</phrase>
12460<phrase>
12461 id: VOICE_TRACK_TO_MOVE
12462 desc: playlist viewer
12463 user: core
12464 <source>
12465 *: ""
12466 </source>
12467 <dest>
12468 *: ""
12469 </dest>
12470 <voice>
12471 *: "Ð—Ð°Ð¿Ð¸Ñ Ð·Ð° премеÑтване"
12472 </voice>
12473</phrase>
12474<phrase>
12475 id: VOICE_QUEUED
12476 desc: playlist viewer
12477 user: core
12478 <source>
12479 *: ""
12480 </source>
12481 <dest>
12482 *: ""
12483 </dest>
12484 <voice>
12485 *: "Ðа опашката"
12486 </voice>
12487</phrase>
12488<phrase>
12489 id: VOICE_BAD_TRACK
12490 desc: playlist viewer
12491 user: core
12492 <source>
12493 *: ""
12494 </source>
12495 <dest>
12496 *: ""
12497 </dest>
12498 <voice>
12499 *: "Повреден запиÑ"
12500 </voice>
12501</phrase>
12502<phrase>
12503 id: VOICE_MOVING_TRACK
12504 desc: playlist viewer
12505 user: core
12506 <source>
12507 *: ""
12508 </source>
12509 <dest>
12510 *: ""
12511 </dest>
12512 <voice>
12513 *: "ПремеÑтване на запиÑ"
12514 </voice>
12515</phrase>
12516<phrase>
12517 id: LANG_PLAYTIME_ELAPSED
12518 desc: playing time screen
12519 user: core
12520 <source>
12521 *: "Playlist elapsed:"
12522 </source>
12523 <dest>
12524 *: "Изминало време в плейлиÑтата:"
12525 </dest>
12526 <voice>
12527 *: "Изминало време в плейлиÑтата"
12528 </voice>
12529</phrase>
12530<phrase>
12531 id: LANG_PLAYTIME_TRK_ELAPSED
12532 desc: playing time screen
12533 user: core
12534 <source>
12535 *: "Track elapsed:"
12536 </source>
12537 <dest>
12538 *: "Изминало време в запиÑа:"
12539 </dest>
12540 <voice>
12541 *: "Изминало време в запиÑа"
12542 </voice>
12543</phrase>
12544<phrase>
12545 id: LANG_PLAYTIME_REMAINING
12546 desc: playing time screen
12547 user: core
12548 <source>
12549 *: "Playlist remaining:"
12550 </source>
12551 <dest>
12552 *: "ОÑтаващо време в плейлиÑтата:"
12553 </dest>
12554 <voice>
12555 *: "ОÑтаващо време в плейлиÑтата"
12556 </voice>
12557</phrase>
12558<phrase>
12559 id: LANG_PLAYTIME_TRK_REMAINING
12560 desc: playing time screen
12561 user: core
12562 <source>
12563 *: "Track remaining:"
12564 </source>
12565 <dest>
12566 *: "ОÑтаващо време в запиÑа:"
12567 </dest>
12568 <voice>
12569 *: "ОÑтаващо време в запиÑа"
12570 </voice>
12571</phrase>
12572<phrase>
12573 id: LANG_PLAYTIME_TRACK
12574 desc: playing time screen
12575 user: core
12576 <source>
12577 *: "Track:"
12578 </source>
12579 <dest>
12580 *: "ЗапиÑ:"
12581 </dest>
12582 <voice>
12583 *: "ЗапиÑ"
12584 </voice>
12585</phrase>
12586<phrase>
12587 id: LANG_PLAYTIME_STORAGE
12588 desc: playing time screen
12589 user: core
12590 <source>
12591 *: "Storage (Done / Remaining):"
12592 </source>
12593 <dest>
12594 *: "Памет (Завършена / ОÑтаваща):"
12595 </dest>
12596 <voice>
12597 *: "Памет"
12598 </voice>
12599</phrase>
12600<phrase>
12601 id: VOICE_PLAYTIME_DONE
12602 desc: playing time screen
12603 user: core
12604 <source>
12605 *: ""
12606 </source>
12607 <dest>
12608 *: ""
12609 </dest>
12610 <voice>
12611 *: "Завършено"
12612 </voice>
12613</phrase>
12614<phrase>
12615 id: LANG_PLAYTIME_AVG_TRACK_SIZE
12616 desc: playing time screen
12617 user: core
12618 <source>
12619 *: "Average track size:"
12620 </source>
12621 <dest>
12622 *: "Среден размер на запиÑа:"
12623 </dest>
12624 <voice>
12625 *: "Среден размер на запиÑа"
12626 </voice>
12627</phrase>
12628<phrase>
12629 id: LANG_PLAYTIME_AVG_BITRATE
12630 desc: playing time screen
12631 user: core
12632 <source>
12633 *: "Average bitrate:"
12634 </source>
12635 <dest>
12636 *: "Средна побитова ÑкороÑÑ‚:"
12637 </dest>
12638 <voice>
12639 *: "Средна побитова ÑкороÑÑ‚"
12640 </voice>
12641</phrase>
12642<phrase>
12643 id: LANG_PLAYTIME_ERROR
12644 desc: playing time screen
12645 user: core
12646 <source>
12647 *: "Error while gathering info"
12648 </source>
12649 <dest>
12650 *: "Грешка при Ñъбирането на информациÑ"
12651 </dest>
12652 <voice>
12653 *: "Грешка при Ñъбирането на информациÑ"
12654 </voice>
12655</phrase>
12656<phrase>
12657 id: LANG_PLAYING_TIME
12658 desc: onplay menu
12659 user: core
12660 <source>
12661 *: "Playing time"
12662 </source>
12663 <dest>
12664 *: "Време на възпроизвеждане"
12665 </dest>
12666 <voice>
12667 *: "Време на възпроизвеждане"
12668 </voice>
12669</phrase>
12670<phrase>
12671 id: LANG_CAR_ADAPTER_MODE_DELAY
12672 desc: Displayed for setting car adapter mode delay
12673 user: core
12674 <source>
12675 *: none
12676 charging: "Delay Before Resume"
12677 </source>
12678 <dest>
12679 *: none
12680 charging: "Изчакване преди възобновÑване"
12681 </dest>
12682 <voice>
12683 *: none
12684 charging: "Изчакване преди възобновÑване"
12685 </voice>
12686</phrase>
12687<phrase>
12688 id: VOICE_PITCH_ABSOLUTE_MODE
12689 desc: spoken only
12690 user: core
12691 <source>
12692 *: none
12693 pitchscreen: ""
12694 </source>
12695 <dest>
12696 *: none
12697 pitchscreen: ""
12698 </dest>
12699 <voice>
12700 *: none
12701 pitchscreen: "ÐбÑолютен режим"
12702 </voice>
12703</phrase>
12704<phrase>
12705 id: VOICE_PITCH_SEMITONE_MODE
12706 desc: spoken only
12707 user: core
12708 <source>
12709 *: none
12710 pitchscreen: ""
12711 </source>
12712 <dest>
12713 *: none
12714 pitchscreen: ""
12715 </dest>
12716 <voice>
12717 *: none
12718 pitchscreen: "Полутонен режим"
12719 </voice>
12720</phrase>
12721<phrase>
12722 id: VOICE_PITCH_TIMESTRETCH_MODE
12723 desc: spoken only
12724 user: core
12725 <source>
12726 *: none
12727 pitchscreen: ""
12728 </source>
12729 <dest>
12730 *: none
12731 pitchscreen: ""
12732 </dest>
12733 <voice>
12734 *: none
12735 pitchscreen: "СгъÑÑ‚Ñване на времето"
12736 </voice>
12737</phrase>
12738<phrase>
12739 id: LANG_REMOTE_CONTROL
12740 desc: Item for menus
12741 user: core
12742 <source>
12743 *: "Remote Control"
12744 </source>
12745 <dest>
12746 *: "ДиÑтанционно управление"
12747 </dest>
12748 <voice>
12749 *: "ДиÑтанционно управление"
12750 </voice>
12751</phrase>
12752<phrase>
12753 id: LANG_NO_REM_CONTROL
12754 desc: Item for menus
12755 user: core
12756 <source>
12757 *: "No Rem. Control"
12758 </source>
12759 <dest>
12760 *: "ÐÑма диÑÑ‚. управление"
12761 </dest>
12762 <voice>
12763 *: "ÐÑма диÑтанционно управление"
12764 </voice>
12765</phrase>
12766<phrase>
12767 id: LANG_OUT_OF_CONTROL
12768 desc: Item for menus
12769 user: core
12770 <source>
12771 *: "Out of Control"
12772 </source>
12773 <dest>
12774 *: "Без управление"
12775 </dest>
12776 <voice>
12777 *: "Без управление"
12778 </voice>
12779</phrase>
12780<phrase>
12781 id: LANG_2_KEY_CONTROL
12782 desc: Item for menus
12783 user: core
12784 <source>
12785 *: "2 Key Control"
12786 </source>
12787 <dest>
12788 *: "Управление Ñ 2 бутона"
12789 </dest>
12790 <voice>
12791 *: "Управление Ñ 2 бутона"
12792 </voice>
12793</phrase>
12794<phrase>
12795 id: LANG_4_KEY_CONTROL
12796 desc: Item for menus
12797 user: core
12798 <source>
12799 *: "4 Key Control"
12800 </source>
12801 <dest>
12802 *: "Управление Ñ 4 бутона"
12803 </dest>
12804 <voice>
12805 *: "Управление Ñ 4 бутона"
12806 </voice>
12807</phrase>
12808<phrase>
12809 id: LANG_PLAY_WORMLET
12810 desc: For wormlet menu
12811 user: core
12812 <source>
12813 *: "Play Wormlet!"
12814 </source>
12815 <dest>
12816 *: "Играй Wormlet!"
12817 </dest>
12818 <voice>
12819 *: "Играй Wormlet!"
12820 </voice>
12821</phrase>
12822<phrase>
12823 id: LANG_NUMBER_OF_WORMS
12824 desc: For wormlet menu
12825 user: core
12826 <source>
12827 *: "Number of Worms"
12828 </source>
12829 <dest>
12830 *: "Брой червеи"
12831 </dest>
12832 <voice>
12833 *: "Брой червеи"
12834 </voice>
12835</phrase>
12836<phrase>
12837 id: LANG_WORM_GROWTH_PER_FOOD
12838 desc: For wormlet menu
12839 user: core
12840 <source>
12841 *: "Worm Growth Per Food"
12842 </source>
12843 <dest>
12844 *: "РаÑтеж на червеите при хранене"
12845 </dest>
12846 <voice>
12847 *: "РаÑтеж на червеите при хранене"
12848 </voice>
12849</phrase>
12850<phrase>
12851 id: LANG_WORM_SPEED
12852 desc: For wormlet menu
12853 user: core
12854 <source>
12855 *: "Worm Speed"
12856 </source>
12857 <dest>
12858 *: "СкороÑÑ‚ на червеите"
12859 </dest>
12860 <voice>
12861 *: "СкороÑÑ‚ на червеите"
12862 </voice>
12863</phrase>
12864<phrase>
12865 id: LANG_ARGHS_PER_FOOD
12866 desc: For wormlet menu
12867 user: core
12868 <source>
12869 *: "Arghs Per Food"
12870 </source>
12871 <dest>
12872 *: "Argh-ове при хранене"
12873 </dest>
12874 <voice>
12875 *: "Argh-ове при хранене"
12876 </voice>
12877</phrase>
12878<phrase>
12879 id: LANG_ARGH_SIZE
12880 desc: For wormlet menu
12881 user: core
12882 <source>
12883 *: "Argh Size"
12884 </source>
12885 <dest>
12886 *: "Размер на Argh"
12887 </dest>
12888 <voice>
12889 *: "Размер на Argh"
12890 </voice>
12891</phrase>
12892<phrase>
12893 id: LANG_FOOD_SIZE
12894 desc: For wormlet menu
12895 user: core
12896 <source>
12897 *: "Food Size"
12898 </source>
12899 <dest>
12900 *: "Размер на храната"
12901 </dest>
12902 <voice>
12903 *: "Размер на храната"
12904 </voice>
12905</phrase>
12906<phrase>
12907 id: LANG_NUMBER_OF_PLAYERS
12908 desc: For game menus
12909 user: core
12910 <source>
12911 *: "Number of Players"
12912 </source>
12913 <dest>
12914 *: "Брой играчи"
12915 </dest>
12916 <voice>
12917 *: "Брой играчи"
12918 </voice>
12919</phrase>
12920<phrase>
12921 id: LANG_CONTROL_STYLE
12922 desc: In various menus
12923 user: core
12924 <source>
12925 *: "Control Style"
12926 </source>
12927 <dest>
12928 *: "Стил на управление"
12929 </dest>
12930 <voice>
12931 *: "Стил на управление"
12932 </voice>
12933</phrase>
12934<phrase>
12935 id: LANG_REVERT_TO_DEFAULT_SETTINGS
12936 desc: In various menus
12937 user: core
12938 <source>
12939 *: "Revert to Default Settings"
12940 </source>
12941 <dest>
12942 *: "Върни наÑтройките по подразбиране"
12943 </dest>
12944 <voice>
12945 *: "Върни наÑтройките по подразбиране"
12946 </voice>
12947</phrase>
12948<phrase>
12949 id: LANG_MENU_QUIT
12950 desc: in various menus
12951 user: core
12952 <source>
12953 *: "Quit"
12954 </source>
12955 <dest>
12956 *: "Изход"
12957 </dest>
12958 <voice>
12959 *: "Изход"
12960 </voice>
12961</phrase>
12962<phrase>
12963 id: LANG_MENU_DISPLAY_OPTIONS
12964 desc: in various menus
12965 user: core
12966 <source>
12967 *: "Display Options"
12968 </source>
12969 <dest>
12970 *: "Опции на диÑплеÑ"
12971 </dest>
12972 <voice>
12973 *: "Опции на диÑплеÑ"
12974 </voice>
12975</phrase>
12976<phrase>
12977 id: LANG_PREVTRACK
12978 desc: in playback control menu
12979 user: core
12980 <source>
12981 *: "Previous Track"
12982 </source>
12983 <dest>
12984 *: "Предишен запиÑ"
12985 </dest>
12986 <voice>
12987 *: "Предишен запиÑ"
12988 </voice>
12989</phrase>
12990<phrase>
12991 id: LANG_PLAYPAUSE
12992 desc: in playback control menu
12993 user: core
12994 <source>
12995 *: "Pause / Play"
12996 </source>
12997 <dest>
12998 *: "Пауза / Възпроизвеждане"
12999 </dest>
13000 <voice>
13001 *: "Пауза / Възпроизвеждане"
13002 </voice>
13003</phrase>
13004<phrase>
13005 id: LANG_STOP_PLAYBACK
13006 desc: in playback control menu
13007 user: core
13008 <source>
13009 *: "Stop Playback"
13010 </source>
13011 <dest>
13012 *: "Спри възпроизвеждането"
13013 </dest>
13014 <voice>
13015 *: "Спри възпроизвеждането"
13016 </voice>
13017</phrase>
13018<phrase>
13019 id: LANG_NEXTTRACK
13020 desc: in playback control menu
13021 user: core
13022 <source>
13023 *: "Next Track"
13024 </source>
13025 <dest>
13026 *: "Следващ запиÑ"
13027 </dest>
13028 <voice>
13029 *: "Следващ запиÑ"
13030 </voice>
13031</phrase>
13032<phrase>
13033 id: LANG_CHANGE_VOLUME
13034 desc: in playback control menu
13035 user: core
13036 <source>
13037 *: "Change Volume"
13038 </source>
13039 <dest>
13040 *: "ПромÑна нивото на звука"
13041 </dest>
13042 <voice>
13043 *: "ПромÑна нивото на звука"
13044 </voice>
13045</phrase>
13046<phrase>
13047 id: LANG_CHANGE_SHUFFLE_MODE
13048 desc: in playback control menu
13049 user: core
13050 <source>
13051 *: "Shuffle Mode"
13052 </source>
13053 <dest>
13054 *: "Режим разбъркване"
13055 </dest>
13056 <voice>
13057 *: "Режим разбъркване"
13058 </voice>
13059</phrase>
13060<phrase>
13061 id: LANG_CHANGE_REPEAT_MODE
13062 desc: in playback control menu
13063 user: core
13064 <source>
13065 *: "Change Repeat Mode"
13066 </source>
13067 <dest>
13068 *: "ПромÑна режима на повторение"
13069 </dest>
13070 <voice>
13071 *: "ПромÑна режима на повторение"
13072 </voice>
13073</phrase>
13074<phrase>
13075 id: LANG_PLAYBACK_CONTROL
13076 desc: in playback control menu
13077 user: core
13078 <source>
13079 *: "Playback Control"
13080 </source>
13081 <dest>
13082 *: "Управление на възпроизвеждането"
13083 </dest>
13084 <voice>
13085 *: "Управление на възпроизвеждането"
13086 </voice>
13087</phrase>
13088<phrase>
13089 id: LANG_CHESSBOX_CHECKMATE
13090 desc: in chessbox
13091 user: core
13092 <source>
13093 *: "Checkmate!"
13094 </source>
13095 <dest>
13096 *: "Шах и мат!"
13097 </dest>
13098 <voice>
13099 *: "Шах и мат!"
13100 </voice>
13101</phrase>
13102<phrase>
13103 id: LANG_CHESSBOX_ILLEGAL_MOVE
13104 desc: in chessbox
13105 user: core
13106 <source>
13107 *: "Illegal move!"
13108 </source>
13109 <dest>
13110 *: "Ðевалиден ход!"
13111 </dest>
13112 <voice>
13113 *: "Ðевалиден ход!"
13114 </voice>
13115</phrase>
13116<phrase>
13117 id: LANG_CHESSBOX_MENU_NEW_GAME
13118 desc: in the chessbox menu
13119 user: core
13120 <source>
13121 *: "New Game"
13122 </source>
13123 <dest>
13124 *: "Ðова игра"
13125 </dest>
13126 <voice>
13127 *: "Ðова игра"
13128 </voice>
13129</phrase>
13130<phrase>
13131 id: LANG_CHESSBOX_MENU_RESUME_GAME
13132 desc: in the chessbox menu
13133 user: core
13134 <source>
13135 *: "Resume Game"
13136 </source>
13137 <dest>
13138 *: "Поднови игра"
13139 </dest>
13140 <voice>
13141 *: "Поднови игра"
13142 </voice>
13143</phrase>
13144<phrase>
13145 id: LANG_CHESSBOX_MENU_SAVE_GAME
13146 desc: in the chessbox menu
13147 user: core
13148 <source>
13149 *: "Save Game"
13150 </source>
13151 <dest>
13152 *: "Запиши игра"
13153 </dest>
13154 <voice>
13155 *: "Запиши игра"
13156 </voice>
13157</phrase>
13158<phrase>
13159 id: LANG_CHESSBOX_MENU_RESTORE_GAME
13160 desc: in the chessbox menu
13161 user: core
13162 <source>
13163 *: "Restore Game"
13164 </source>
13165 <dest>
13166 *: "Зареди игра"
13167 </dest>
13168 <voice>
13169 *: "Зареди игра"
13170 </voice>
13171</phrase>
13172<phrase>
13173 id: LANG_CHESSBOX_MENU_RESTART_GAME
13174 desc: in the chessbox menu
13175 user: core
13176 <source>
13177 *: "Restart Game"
13178 </source>
13179 <dest>
13180 *: "РеÑтартирай игра"
13181 </dest>
13182 <voice>
13183 *: "РеÑтартирай игра"
13184 </voice>
13185</phrase>
13186<phrase>
13187 id: LANG_CHESSBOX_MENU_SELECT_OTHER_GAME
13188 desc: in the chessbox menu
13189 user: core
13190 <source>
13191 *: "Select Other Game"
13192 </source>
13193 <dest>
13194 *: "Избери друга игра"
13195 </dest>
13196 <voice>
13197 *: "Избери друга игра"
13198 </voice>
13199</phrase>
13200<phrase>
13201 id: LANG_CHESSBOX_LEVEL_1
13202 desc: in the chessbox game level selection
13203 user: core
13204 <source>
13205 *: "Level 1: 60 moves / 5 min"
13206 </source>
13207 <dest>
13208 *: "Ðиво 1: 60 хода / 5 мин"
13209 </dest>
13210 <voice>
13211 *: "Ðиво 1, 60 хода за 5 минути"
13212 </voice>
13213</phrase>
13214<phrase>
13215 id: LANG_CHESSBOX_LEVEL_2
13216 desc: in the chessbox game level selection
13217 user: core
13218 <source>
13219 *: "Level 2: 60 moves / 15 min"
13220 </source>
13221 <dest>
13222 *: "Ðиво 2: 60 хода / 15 мин"
13223 </dest>
13224 <voice>
13225 *: "Ðиво 2, 60 хода за 15 минути"
13226 </voice>
13227</phrase>
13228<phrase>
13229 id: LANG_CHESSBOX_LEVEL_3
13230 desc: in the chessbox game level selection
13231 user: core
13232 <source>
13233 *: "Level 3: 60 moves / 30 min"
13234 </source>
13235 <dest>
13236 *: "Ðиво 3: 60 хода / 30 мин"
13237 </dest>
13238 <voice>
13239 *: "Ðиво 3, 60 хода за 30 минути"
13240 </voice>
13241</phrase>
13242<phrase>
13243 id: LANG_CHESSBOX_LEVEL_4
13244 desc: in the chessbox game level selection
13245 user: core
13246 <source>
13247 *: "Level 4: 40 moves / 30 min"
13248 </source>
13249 <dest>
13250 *: "Ðиво 4: 40 хода / 30 мин"
13251 </dest>
13252 <voice>
13253 *: "Ðиво 4, 40 хода за 30 минути"
13254 </voice>
13255</phrase>
13256<phrase>
13257 id: LANG_CHESSBOX_LEVEL_5
13258 desc: in the chessbox game level selection
13259 user: core
13260 <source>
13261 *: "Level 5: 40 moves / 60 min"
13262 </source>
13263 <dest>
13264 *: "Ðиво 5: 40 хода / 60 мин"
13265 </dest>
13266 <voice>
13267 *: "Ðиво 5, 40 хода за 60 минути"
13268 </voice>
13269</phrase>
13270<phrase>
13271 id: LANG_CHESSBOX_LEVEL_6
13272 desc: in the chessbox game level selection
13273 user: core
13274 <source>
13275 *: "Level 6: 40 moves / 120 min"
13276 </source>
13277 <dest>
13278 *: "Ðиво 6: 40 хода / 120 мин"
13279 </dest>
13280 <voice>
13281 *: "Ðиво 6, 40 хода за 120 минути"
13282 </voice>
13283</phrase>
13284<phrase>
13285 id: LANG_CHESSBOX_LEVEL_7
13286 desc: in the chessbox game level selection
13287 user: core
13288 <source>
13289 *: "Level 7: 40 moves / 240 min"
13290 </source>
13291 <dest>
13292 *: "Ðиво 7: 40 хода / 240 мин"
13293 </dest>
13294 <voice>
13295 *: "Ðиво 7, 40 хода за 240 минути"
13296 </voice>
13297</phrase>
13298<phrase>
13299 id: LANG_CHESSBOX_LEVEL_8
13300 desc: in the chessbox game level selection
13301 user: core
13302 <source>
13303 *: "Level 8: 1 move / 15 min"
13304 </source>
13305 <dest>
13306 *: "Ðиво 8: 1 ход / 15 мин"
13307 </dest>
13308 <voice>
13309 *: "Ðиво 8, 1 ход за 15 минути"
13310 </voice>
13311</phrase>
13312<phrase>
13313 id: LANG_CHESSBOX_LEVEL_9
13314 desc: in the chessbox game level selection
13315 user: core
13316 <source>
13317 *: "Level 9: 1 move / 60 min"
13318 </source>
13319 <dest>
13320 *: "Ðиво 9: 1 ход / 60 мин"
13321 </dest>
13322 <voice>
13323 *: "Ðиво 9, 1 ход за 60 минути"
13324 </voice>
13325</phrase>
13326<phrase>
13327 id: LANG_CHESSBOX_LEVEL_10
13328 desc: in the chessbox game level selection
13329 user: core
13330 <source>
13331 *: "Level 10: 1 move / 600 min"
13332 </source>
13333 <dest>
13334 *: "Ðиво 10: 1 ход / 600 мин"
13335 </dest>
13336 <voice>
13337 *: "Ðиво 10, 1 ход за 600 минути"
13338 </voice>
13339</phrase>
13340<phrase>
13341 id: LANG_CHESSBOX_PGN_PARSE_ERROR
13342 desc: in the chessbox game viewer
13343 user: core
13344 <source>
13345 *: "Error parsing game !"
13346 </source>
13347 <dest>
13348 *: "Грешка при разчитане на играта !"
13349 </dest>
13350 <voice>
13351 *: "Грешка при разчитане на играта !"
13352 </voice>
13353</phrase>
13354<phrase>
13355 id: LANG_CHESSBOX_NO_GAMES
13356 desc: in the chessbox game viewer
13357 user: core
13358 <source>
13359 *: "No games found !"
13360 </source>
13361 <dest>
13362 *: "Ðе Ñа намерени игри !"
13363 </dest>
13364 <voice>
13365 *: "Ðе Ñа намерени игри!"
13366 </voice>
13367</phrase>
13368<phrase>
13369 id: LANG_CHESSBOX_GAME_BEGINNING
13370 desc: in the chessbox game viewer
13371 user: core
13372 <source>
13373 *: "At the beginning of the game"
13374 </source>
13375 <dest>
13376 *: "В началото на играта"
13377 </dest>
13378 <voice>
13379 *: "В началото на играта"
13380 </voice>
13381</phrase>
13382<phrase>
13383 id: LANG_CHESSBOX_GAME_END
13384 desc: in the chessbox game viewer
13385 user: core
13386 <source>
13387 *: "At the end of the game"
13388 </source>
13389 <dest>
13390 *: "Ð’ ÐºÑ€Ð°Ñ Ð½Ð° играта"
13391 </dest>
13392 <voice>
13393 *: "Ð’ ÐºÑ€Ð°Ñ Ð½Ð° играта"
13394 </voice>
13395</phrase>
13396<phrase>
13397 id: VOICE_PLAYER
13398 desc: spoken only, for announcing player's id
13399 user: core
13400 <source>
13401 *: ""
13402 </source>
13403 <dest>
13404 *: ""
13405 </dest>
13406 <voice>
13407 *: "Играч"
13408 </voice>
13409</phrase>
13410<phrase>
13411 id: VOICE_GNUCHESS
13412 desc: spoken only, for announcing player's id
13413 user: core
13414 <source>
13415 *: ""
13416 </source>
13417 <dest>
13418 *: ""
13419 </dest>
13420 <voice>
13421 *: "Програма GNU Chess"
13422 </voice>
13423</phrase>
13424<phrase>
13425 id: VOICE_MARKED
13426 desc: spoken only, for announcing chess piece marking
13427 user: core
13428 <source>
13429 *: ""
13430 </source>
13431 <dest>
13432 *: ""
13433 </dest>
13434 <voice>
13435 *: "Маркирано"
13436 </voice>
13437</phrase>
13438<phrase>
13439 id: VOICE_UNMARKED
13440 desc: spoken only, for announcing chess piece unmarking
13441 user: core
13442 <source>
13443 *: ""
13444 </source>
13445 <dest>
13446 *: ""
13447 </dest>
13448 <voice>
13449 *: "Ðемаркирано"
13450 </voice>
13451</phrase>
13452<phrase>
13453 id: VOICE_WHITE
13454 desc: spoken only, for announcing chess piece color
13455 user: core
13456 <source>
13457 *: ""
13458 </source>
13459 <dest>
13460 *: ""
13461 </dest>
13462 <voice>
13463 *: "БÑло"
13464 </voice>
13465</phrase>
13466<phrase>
13467 id: VOICE_BLACK
13468 desc: spoken only, for announcing chess piece color
13469 user: core
13470 <source>
13471 *: ""
13472 </source>
13473 <dest>
13474 *: ""
13475 </dest>
13476 <voice>
13477 *: "Черно"
13478 </voice>
13479</phrase>
13480<phrase>
13481 id: VOICE_CHESSBOX_CHECK
13482 desc: spoken only, for announcing chess moves
13483 user: core
13484 <source>
13485 *: ""
13486 </source>
13487 <dest>
13488 *: ""
13489 </dest>
13490 <voice>
13491 *: "Шах!"
13492 </voice>
13493</phrase>
13494<phrase>
13495 id: VOICE_CHESSBOX_CAPTURES
13496 desc: spoken only, for announcing chess moves
13497 user: core
13498 <source>
13499 *: ""
13500 </source>
13501 <dest>
13502 *: ""
13503 </dest>
13504 <voice>
13505 *: "взема"
13506 </voice>
13507</phrase>
13508<phrase>
13509 id: VOICE_CHESSBOX_CASTLE
13510 desc: spoken only, for announcing chess moves
13511 user: core
13512 <source>
13513 *: ""
13514 </source>
13515 <dest>
13516 *: ""
13517 </dest>
13518 <voice>
13519 *: "рокада"
13520 </voice>
13521</phrase>
13522<phrase>
13523 id: VOICE_CHESSBOX_KINGSIDE
13524 desc: spoken only, for announcing chess moves
13525 user: core
13526 <source>
13527 *: ""
13528 </source>
13529 <dest>
13530 *: ""
13531 </dest>
13532 <voice>
13533 *: "от Ñтраната на царÑ"
13534 </voice>
13535</phrase>
13536<phrase>
13537 id: VOICE_CHESSBOX_QUEENSIDE
13538 desc: spoken only, for announcing chess moves
13539 user: core
13540 <source>
13541 *: ""
13542 </source>
13543 <dest>
13544 *: ""
13545 </dest>
13546 <voice>
13547 *: "от Ñтраната на царицата"
13548 </voice>
13549</phrase>
13550<phrase>
13551 id: VOICE_PAWN
13552 desc: spoken only, for announcing chess piece names
13553 user: core
13554 <source>
13555 *: ""
13556 </source>
13557 <dest>
13558 *: ""
13559 </dest>
13560 <voice>
13561 *: "Пешка"
13562 </voice>
13563</phrase>
13564<phrase>
13565 id: VOICE_KNIGHT
13566 desc: spoken only, for announcing chess piece names
13567 user: core
13568 <source>
13569 *: ""
13570 </source>
13571 <dest>
13572 *: ""
13573 </dest>
13574 <voice>
13575 *: "Кон"
13576 </voice>
13577</phrase>
13578<phrase>
13579 id: VOICE_BISHOP
13580 desc: spoken only, for announcing chess piece names
13581 user: core
13582 <source>
13583 *: ""
13584 </source>
13585 <dest>
13586 *: ""
13587 </dest>
13588 <voice>
13589 *: "Офицер"
13590 </voice>
13591</phrase>
13592<phrase>
13593 id: VOICE_ROOK
13594 desc: spoken only, for announcing chess piece names
13595 user: core
13596 <source>
13597 *: ""
13598 </source>
13599 <dest>
13600 *: ""
13601 </dest>
13602 <voice>
13603 *: "Топ"
13604 </voice>
13605</phrase>
13606<phrase>
13607 id: VOICE_QUEEN
13608 desc: spoken only, for announcing chess piece names
13609 user: core
13610 <source>
13611 *: ""
13612 </source>
13613 <dest>
13614 *: ""
13615 </dest>
13616 <voice>
13617 *: "Царица"
13618 </voice>
13619</phrase>
13620<phrase>
13621 id: VOICE_KING
13622 desc: spoken only, for announcing chess piece names
13623 user: core
13624 <source>
13625 *: ""
13626 </source>
13627 <dest>
13628 *: ""
13629 </dest>
13630 <voice>
13631 *: "Цар"
13632 </voice>
13633</phrase>
13634<phrase>
13635 id: LANG_CHESSBOX_GAMES
13636 desc: in chessbox
13637 user: core
13638 <source>
13639 *: "Games"
13640 </source>
13641 <dest>
13642 *: "Игри"
13643 </dest>
13644 <voice>
13645 *: ""
13646 </voice>
13647</phrase>
13648<phrase>
13649 id: LANG_CHESSBOX_SAVING_POSITION
13650 desc: in chessbox
13651 user: core
13652 <source>
13653 *: "Saving position"
13654 </source>
13655 <dest>
13656 *: "ЗапиÑване на позициÑта"
13657 </dest>
13658 <voice>
13659 *: "ЗапиÑване на позициÑта"
13660 </voice>
13661</phrase>
13662<phrase>
13663 id: LANG_CHESSBOX_LOADING_POSITION
13664 desc: in chessbox
13665 user: core
13666 <source>
13667 *: "Loading position"
13668 </source>
13669 <dest>
13670 *: "Зареждане на позициÑта"
13671 </dest>
13672 <voice>
13673 *: "Зареждане на позициÑта"
13674 </voice>
13675</phrase>
13676<phrase>
13677 id: LANG_CHESSBOX_THINKING
13678 desc: in chessbox
13679 user: core
13680 <source>
13681 *: "Thinking..."
13682 </source>
13683 <dest>
13684 *: "МиÑлене..."
13685 </dest>
13686 <voice>
13687 *: "МиÑлене"
13688 </voice>
13689</phrase>
13690<phrase>
13691 id: VOICE_BATTERY_BENCH_IS_ALREADY_RUNNING
13692 desc: Spoken if battery bench is already running
13693 user: core
13694 <source>
13695 *: ""
13696 </source>
13697 <dest>
13698 *: ""
13699 </dest>
13700 <voice>
13701 *: "ТеÑта на батериÑта вече работи"
13702 </voice>
13703</phrase>
13704<phrase>
13705 id: VOICE_BAT_BENCH_KEYS
13706 desc: Battery bench start up message
13707 user: core
13708 <source>
13709 *: ""
13710 </source>
13711 <dest>
13712 *: ""
13713 </dest>
13714 <voice>
13715 *: "ÐатиÑнете възпроизвеждане за да започнете теÑÑ‚ на батериÑта или Ñпиране за отказ"
13716 </voice>
13717</phrase>
13718<phrase>
13719 id: LANG_CANNOT_RESTART_PLAYBACK
13720 desc: cannot restart playback splash in imageviewer
13721 user: core
13722 <source>
13723 *: "Cannot restart playback"
13724 </source>
13725 <dest>
13726 *: "Ðе може да Ñе реÑтартира възпроизвеждането"
13727 </dest>
13728 <voice>
13729 *: "Ðе може да Ñе реÑтартира възпроизвеждането"
13730 </voice>
13731</phrase>
13732<phrase>
13733 id: LANG_ORDERED
13734 desc: in the imageviewer settings menu
13735 user: core
13736 <source>
13737 *: "Ordered"
13738 </source>
13739 <dest>
13740 *: "Подредено"
13741 </dest>
13742 <voice>
13743 *: "Подредено"
13744 </voice>
13745</phrase>
13746<phrase>
13747 id: LANG_DIFFUSION
13748 desc: in the imageviewer settings menu
13749 user: core
13750 <source>
13751 *: "Diffusion"
13752 </source>
13753 <dest>
13754 *: "РазпръÑнато"
13755 </dest>
13756 <voice>
13757 *: "РазпръÑнато"
13758 </voice>
13759</phrase>
13760<phrase>
13761 id: LANG_GRAYSCALE
13762 desc: in the imageviewer settings menu
13763 user: core
13764 <source>
13765 *: "Grayscale"
13766 </source>
13767 <dest>
13768 *: "Черно-бÑло"
13769 </dest>
13770 <voice>
13771 *: "Черно-бÑло"
13772 </voice>
13773</phrase>
13774<phrase>
13775 id: LANG_SLIDESHOW_MODE
13776 desc: in the imageviewer settings menu
13777 user: core
13778 <source>
13779 *: "Toggle Slideshow Mode"
13780 </source>
13781 <dest>
13782 *: "Превключи режим галериÑ"
13783 </dest>
13784 <voice>
13785 *: "Превключи режим галериÑ"
13786 </voice>
13787</phrase>
13788<phrase>
13789 id: LANG_SLIDESHOW_TIME
13790 desc: in the imageviewer settings menu
13791 user: core
13792 <source>
13793 *: "Slideshow Time"
13794 </source>
13795 <dest>
13796 *: "Време за показване"
13797 </dest>
13798 <voice>
13799 *: "Време за показване"
13800 </voice>
13801</phrase>
13802<phrase>
13803 id: LANG_RETURN
13804 desc: in various plugin menus
13805 user: core
13806 <source>
13807 *: "Return"
13808 </source>
13809 <dest>
13810 *: "Връщане"
13811 </dest>
13812 <voice>
13813 *: "Връщане"
13814 </voice>
13815</phrase>
13816<phrase>
13817 id: LANG_REC_DIR
13818 desc: used in the info screen to show a recording dir
13819 user: core
13820 <source>
13821 *: none
13822 recording: "Recording Directory"
13823 </source>
13824 <dest>
13825 *: none
13826 recording: "Папка за запиÑи"
13827 </dest>
13828 <voice>
13829 *: none
13830 recording: "Папка за запиÑи"
13831 </voice>
13832</phrase>
13833<phrase>
13834 id: LANG_CHESSBOX_MENU_VIEW_GAMES
13835 desc: in the chessbox menu
13836 user: core
13837 <source>
13838 *: "View Played Games"
13839 </source>
13840 <dest>
13841 *: "Виж изиграните игри"
13842 </dest>
13843 <voice>
13844 *: "Виж изиграните игри"
13845 </voice>
13846</phrase>
13847<phrase>
13848 id: LANG_MENU_AUDIO_OPTIONS
13849 desc: in mpegplayer menus
13850 user: core
13851 <source>
13852 *: "Audio Options"
13853 lowmem: none
13854 </source>
13855 <dest>
13856 *: "ÐаÑтройки на звука"
13857 lowmem: none
13858 </dest>
13859 <voice>
13860 *: "ÐаÑтройки на звука"
13861 lowmem: none
13862 </voice>
13863</phrase>
13864<phrase>
13865 id: LANG_MENU_RESUME_OPTIONS
13866 desc: in mpegplayer menus
13867 user: core
13868 <source>
13869 *: "Resume Options"
13870 lowmem: none
13871 </source>
13872 <dest>
13873 *: "ÐаÑтройки на подновÑването"
13874 lowmem: none
13875 </dest>
13876 <voice>
13877 *: "ÐаÑтройки на подновÑването"
13878 lowmem: none
13879 </voice>
13880</phrase>
13881<phrase>
13882 id: LANG_MENU_PLAY_MODE
13883 desc: in mpegplayer menus
13884 user: core
13885 <source>
13886 *: "Play Mode"
13887 lowmem: none
13888 </source>
13889 <dest>
13890 *: "Режим на възпроизвеждане"
13891 lowmem: none
13892 </dest>
13893 <voice>
13894 *: "Режим на възпроизвеждане"
13895 lowmem: none
13896 </voice>
13897</phrase>
13898<phrase>
13899 id: LANG_SINGLE
13900 desc: in mpegplayer menus
13901 user: core
13902 <source>
13903 *: "Single"
13904 lowmem: none
13905 </source>
13906 <dest>
13907 *: "Само един"
13908 lowmem: none
13909 </dest>
13910 <voice>
13911 *: "Само един"
13912 lowmem: none
13913 </voice>
13914</phrase>
13915<phrase>
13916 id: LANG_USE_SOUND_SETTING
13917 desc: in mpegplayer menus
13918 user: core
13919 <source>
13920 *: "Use sound setting"
13921 lowmem: none
13922 </source>
13923 <dest>
13924 *: "Позлвай наÑтройките за звука"
13925 lowmem: none
13926 </dest>
13927 <voice>
13928 *: "Позлвай наÑтройките за звука"
13929 lowmem: none
13930 </voice>
13931</phrase>
13932<phrase>
13933 id: LANG_RESTART_PLAYBACK
13934 desc: in the mpegplayer settings menu
13935 user: core
13936 <source>
13937 *: "Play from beginning"
13938 lowmem: none
13939 </source>
13940 <dest>
13941 *: "Възпроизвеждане от началото"
13942 lowmem: none
13943 </dest>
13944 <voice>
13945 *: "Възпроизвеждане от началото"
13946 lowmem: none
13947 </voice>
13948</phrase>
13949<phrase>
13950 id: LANG_SET_RESUME_TIME
13951 desc: in the mpegplayer settings menu
13952 user: core
13953 <source>
13954 *: "Set resume time (min)"
13955 lowmem: none
13956 </source>
13957 <dest>
13958 *: "Продължаване от (минути)"
13959 lowmem: none
13960 </dest>
13961 <voice>
13962 *: "Избор на време за продължаване"
13963 lowmem: none
13964 </voice>
13965</phrase>
13966<phrase>
13967 id: LANG_DISPLAY_FPS
13968 desc: in the mpegplayer and pictureflow settings menus
13969 user: core
13970 <source>
13971 *: "Display FPS"
13972 </source>
13973 <dest>
13974 *: "Кадри в Ñекунда на диÑплеÑ"
13975 </dest>
13976 <voice>
13977 *: "Кадри в Ñекунда на диÑплеÑ"
13978 </voice>
13979</phrase>
13980<phrase>
13981 id: LANG_LIMIT_FPS
13982 desc: in the mpegplayer settings menu
13983 user: core
13984 <source>
13985 *: "Limit FPS"
13986 lowmem: none
13987 </source>
13988 <dest>
13989 *: "Ограничи кадрите в Ñекунда"
13990 lowmem: none
13991 </dest>
13992 <voice>
13993 *: "Ограничи кадрите в Ñекунда"
13994 lowmem: none
13995 </voice>
13996</phrase>
13997<phrase>
13998 id: LANG_SKIP_FRAMES
13999 desc: in the mpegplayer settings menu
14000 user: core
14001 <source>
14002 *: "Skip frames"
14003 lowmem: none
14004 </source>
14005 <dest>
14006 *: "ПропуÑкане на кадри"
14007 lowmem: none
14008 </dest>
14009 <voice>
14010 *: "ПропуÑкане на кадри"
14011 lowmem: none
14012 </voice>
14013</phrase>
14014<phrase>
14015 id: LANG_BACKLIGHT_BRIGHTNESS
14016 desc: in the mpegplayer settings menu
14017 user: core
14018 <source>
14019 *: "Backlight brightness"
14020 lowmem: none
14021 </source>
14022 <dest>
14023 *: "ЯркоÑÑ‚ на подÑветката"
14024 lowmem: none
14025 </dest>
14026 <voice>
14027 *: "ЯркоÑÑ‚ на подÑветката"
14028 lowmem: none
14029 </voice>
14030</phrase>
14031<phrase>
14032 id: LANG_USE_COMMON_SETTING
14033 desc: in the mpegplayer settings menu
14034 user: core
14035 <source>
14036 *: "Use common setting"
14037 lowmem: none
14038 </source>
14039 <dest>
14040 *: "Ползвай общата наÑтройка"
14041 lowmem: none
14042 </dest>
14043 <voice>
14044 *: "Ползвай общата наÑтройка"
14045 lowmem: none
14046 </voice>
14047</phrase>
14048<phrase>
14049 id: LANG_TONE_CONTROLS
14050 desc: in the mpegplayer settings menu
14051 user: core
14052 <source>
14053 *: "Tone controls"
14054 lowmem: none
14055 </source>
14056 <dest>
14057 *: "ÐаÑтройка на тона"
14058 lowmem: none
14059 </dest>
14060 <voice>
14061 *: "ÐаÑтройка на тона"
14062 lowmem: none
14063 </voice>
14064</phrase>
14065<phrase>
14066 id: LANG_FORCE_START_MENU
14067 desc: in mpegplayer menus
14068 user: core
14069 <source>
14070 *: "Start menu"
14071 lowmem: none
14072 </source>
14073 <dest>
14074 *: "Покажи менюто"
14075 lowmem: none
14076 </dest>
14077 <voice>
14078 *: "Покажи менюто"
14079 lowmem: none
14080 </voice>
14081</phrase>
14082<phrase>
14083 id: LANG_CONDITIONAL_START_MENU
14084 desc: in mpegplayer menus
14085 user: core
14086 <source>
14087 *: "Start menu if not completed"
14088 lowmem: none
14089 </source>
14090 <dest>
14091 *: "Покажи менюто ако не е завършено"
14092 lowmem: none
14093 </dest>
14094 <voice>
14095 *: "Покажи менюто ако не е завършено"
14096 lowmem: none
14097 </voice>
14098</phrase>
14099<phrase>
14100 id: LANG_AUTO_RESUME
14101 desc: in mpegplayer menus
14102 user: core
14103 <source>
14104 *: "Resume automatically"
14105 lowmem: none
14106 </source>
14107 <dest>
14108 *: "Продължавай автоматично"
14109 lowmem: none
14110 </dest>
14111 <voice>
14112 *: "Продължавай автоматично"
14113 lowmem: none
14114 </voice>
14115</phrase>
14116<phrase>
14117 id: LANG_CLEAR_ALL_RESUMES
14118 desc: in the mpegplayer settings menu
14119 user: core
14120 <source>
14121 *: "Clear all resumes"
14122 lowmem: none
14123 </source>
14124 <dest>
14125 *: "ИзчиÑти вÑички недовършени"
14126 lowmem: none
14127 </dest>
14128 <voice>
14129 *: "ИзчиÑти вÑички недовършени"
14130 lowmem: none
14131 </voice>
14132</phrase>
14133<phrase>
14134 id: LANG_UNAVAILABLE
14135 desc: in mpegplayer settings
14136 user: core
14137 <source>
14138 *: "Unavailable"
14139 lowmem: none
14140 </source>
14141 <dest>
14142 *: "Ðе е налично"
14143 lowmem: none
14144 </dest>
14145 <voice>
14146 *: "Ðе е налично"
14147 lowmem: none
14148 </voice>
14149</phrase>
14150<phrase>
14151 id: LANG_TOGGLE_ITEM
14152 desc: in main_menu_config
14153 user: core
14154 <source>
14155 *: "Toggle Item"
14156 </source>
14157 <dest>
14158 *: "Превключи елемента"
14159 </dest>
14160 <voice>
14161 *: "Превключи елемента"
14162 </voice>
14163</phrase>
14164<phrase>
14165 id: LANG_MOVE_ITEM_UP
14166 desc: in main_menu_config
14167 user: core
14168 <source>
14169 *: "Move Item Up"
14170 </source>
14171 <dest>
14172 *: "ПремеÑти елемента по-нагоре"
14173 </dest>
14174 <voice>
14175 *: "ПремеÑти елемента по-нагоре"
14176 </voice>
14177</phrase>
14178<phrase>
14179 id: LANG_MOVE_ITEM_DOWN
14180 desc: in main_menu_config
14181 user: core
14182 <source>
14183 *: "Move Item Down"
14184 </source>
14185 <dest>
14186 *: "ПремеÑти елемента по-надолу"
14187 </dest>
14188 <voice>
14189 *: "ПремеÑти елемента по-надолу"
14190 </voice>
14191</phrase>
14192<phrase>
14193 id: LANG_LOAD_DEFAULT_CONFIGURATION
14194 desc: in main_menu_config
14195 user: core
14196 <source>
14197 *: "Load Default Configuration"
14198 </source>
14199 <dest>
14200 *: "Зареди ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¿Ð¾ подразбиране"
14201 </dest>
14202 <voice>
14203 *: "Зареди ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¿Ð¾ подразбиране"
14204 </voice>
14205</phrase>
14206<phrase>
14207 id: LANG_SAVE_EXIT
14208 desc: in main_menu_config
14209 user: core
14210 <source>
14211 *: "Save and Exit"
14212 </source>
14213 <dest>
14214 *: "Ð—Ð°Ð¿Ð¸Ñ Ð¸ изход"
14215 </dest>
14216 <voice>
14217 *: "Ð—Ð°Ð¿Ð¸Ñ Ð¸ изход"
14218 </voice>
14219</phrase>
14220<phrase>
14221 id: LANG_MAIN_MENU_ORDER
14222 desc: main_menu_config plugin title
14223 user: core
14224 <source>
14225 *: "Rockbox Main Menu Order"
14226 </source>
14227 <dest>
14228 *: "Ред на оÑновното меню на Rockbox"
14229 </dest>
14230 <voice>
14231 *: ""
14232 </voice>
14233</phrase>
14234<phrase>
14235 id: LANG_PROPERTIES_PATH
14236 desc: in properties plugin
14237 user: core
14238 <source>
14239 *: "[Path]"
14240 </source>
14241 <dest>
14242 *: "[Път]"
14243 </dest>
14244 <voice>
14245 *: "Път"
14246 </voice>
14247</phrase>
14248<phrase>
14249 id: LANG_PROPERTIES_FILENAME
14250 desc: in properties plugin
14251 user: core
14252 <source>
14253 *: "[Filename]"
14254 </source>
14255 <dest>
14256 *: "[Име на файл]"
14257 </dest>
14258 <voice>
14259 *: "Име на файл"
14260 </voice>
14261</phrase>
14262<phrase>
14263 id: LANG_PROPERTIES_SIZE
14264 desc: in properties plugin
14265 user: core
14266 <source>
14267 *: "[Size]"
14268 </source>
14269 <dest>
14270 *: "[Размер]"
14271 </dest>
14272 <voice>
14273 *: "Размер"
14274 </voice>
14275</phrase>
14276<phrase>
14277 id: LANG_PROPERTIES_DATE
14278 desc: in properties plugin
14279 user: core
14280 <source>
14281 *: "[Date]"
14282 </source>
14283 <dest>
14284 *: "[Дата]"
14285 </dest>
14286 <voice>
14287 *: "Дата"
14288 </voice>
14289</phrase>
14290<phrase>
14291 id: LANG_PROPERTIES_TIME
14292 desc: in properties plugin
14293 user: core
14294 <source>
14295 *: "[Time]"
14296 </source>
14297 <dest>
14298 *: "[ЧаÑ]"
14299 </dest>
14300 <voice>
14301 *: "ЧаÑ"
14302 </voice>
14303</phrase>
14304<phrase>
14305 id: LANG_PROPERTIES_SUBDIRS
14306 desc: in properties plugin
14307 user: core
14308 <source>
14309 *: "[Subdirs]"
14310 </source>
14311 <dest>
14312 *: "[Подпапки]"
14313 </dest>
14314 <voice>
14315 *: "Подпапки"
14316 </voice>
14317</phrase>
14318<phrase>
14319 id: LANG_PROPERTIES_FILES
14320 desc: in properties plugin
14321 user: core
14322 <source>
14323 *: "[Files]"
14324 </source>
14325 <dest>
14326 *: "[Файлове]"
14327 </dest>
14328 <voice>
14329 *: "Файлове"
14330 </voice>
14331</phrase>
14332<phrase>
14333 id: LANG_PROPERTIES_DIRECTORY_PROPERTIES
14334 desc: in properties plugin
14335 user: core
14336 <source>
14337 *: "Directory properties"
14338 </source>
14339 <dest>
14340 *: "СвойÑтва на папката"
14341 </dest>
14342 <voice>
14343 *: ""
14344 </voice>
14345</phrase>
14346<phrase>
14347 id: LANG_PROPERTIES_FILE_PROPERTIES
14348 desc: in properties plugin
14349 user: core
14350 <source>
14351 *: "File properties"
14352 </source>
14353 <dest>
14354 *: "СвойÑтва на файла"
14355 </dest>
14356 <voice>
14357 *: ""
14358 </voice>
14359</phrase>
14360<phrase>
14361 id: LANG_PROPERTIES_FAIL
14362 desc: in properties plugin
14363 user: core
14364 <source>
14365 *: "Failed to gather information"
14366 </source>
14367 <dest>
14368 *: "ÐеуÑпешно Ñъбиране на информациÑ"
14369 </dest>
14370 <voice>
14371 *: "ÐеуÑпешно Ñъбиране на информациÑ"
14372 </voice>
14373</phrase>
14374<phrase>
14375 id: LANG_SWAP_CHANNELS
14376 desc: in sound_settings
14377 user: core
14378 <source>
14379 *: "Swap Channels"
14380 </source>
14381 <dest>
14382 *: "РазмÑна на каналите"
14383 </dest>
14384 <voice>
14385 *: "РазмÑна на каналите"
14386 </voice>
14387</phrase>
14388<phrase>
14389 id: LANG_PANNING_SEPARATION
14390 desc: in mikmod settings menu
14391 user: core
14392 <source>
14393 *: "Panning Separation"
14394 lowmem: none
14395 </source>
14396 <dest>
14397 *: "Панорамно разделÑне"
14398 lowmem: none
14399 </dest>
14400 <voice>
14401 *: "Панорамно разделÑне"
14402 lowmem: none
14403 </voice>
14404</phrase>
14405<phrase>
14406 id: LANG_REVERBERATION
14407 desc: in mikmod settings menu
14408 user: core
14409 <source>
14410 *: "Reverberation"
14411 lowmem: none
14412 </source>
14413 <dest>
14414 *: "РеверберациÑ"
14415 lowmem: none
14416 </dest>
14417 <voice>
14418 *: "РеверберациÑ"
14419 lowmem: none
14420 </voice>
14421</phrase>
14422<phrase>
14423 id: LANG_INTERPOLATION
14424 desc: in mikmod settings menu
14425 user: core
14426 <source>
14427 *: "Interpolation"
14428 lowmem: none
14429 </source>
14430 <dest>
14431 *: "ИнтерполациÑ"
14432 lowmem: none
14433 </dest>
14434 <voice>
14435 *: "ИнтерполациÑ"
14436 lowmem: none
14437 </voice>
14438</phrase>
14439<phrase>
14440 id: LANG_MIKMOD_SURROUND
14441 desc: in mikmod settings menu
14442 user: core
14443 <source>
14444 *: "Surround"
14445 lowmem: none
14446 </source>
14447 <dest>
14448 *: "Съраунд"
14449 lowmem: none
14450 </dest>
14451 <voice>
14452 *: "Съраунд"
14453 lowmem: none
14454 </voice>
14455</phrase>
14456<phrase>
14457 id: LANG_MIKMOD_HQMIXER
14458 desc: in mikmod settings menu
14459 user: core
14460 <source>
14461 *: "HQ Mixer"
14462 lowmem: none
14463 </source>
14464 <dest>
14465 *: "HQ микÑер"
14466 lowmem: none
14467 </dest>
14468 <voice>
14469 *: "ВиÑококачеÑтвен микÑер"
14470 lowmem: none
14471 </voice>
14472</phrase>
14473<phrase>
14474 id: LANG_MIKMOD_SAMPLERATE
14475 desc: in mikmod settings menu
14476 user: core
14477 <source>
14478 *: "Sample Rate"
14479 lowmem: none
14480 </source>
14481 <dest>
14482 *: "ЧеÑтота на диÑкретизациÑ"
14483 lowmem: none
14484 </dest>
14485 <voice>
14486 *: "ЧеÑтота на диÑкретизациÑ"
14487 lowmem: none
14488 </voice>
14489</phrase>
14490<phrase>
14491 id: LANG_CPU_BOOST
14492 desc: in mikmod settings menu
14493 user: core
14494 <source>
14495 *: "CPU Boost"
14496 lowmem: none
14497 </source>
14498 <dest>
14499 *: "ПодÑилване на процеÑора"
14500 lowmem: none
14501 </dest>
14502 <voice>
14503 *: "ПодÑилване на процеÑора"
14504 lowmem: none
14505 </voice>
14506</phrase>
14507<phrase>
14508 id: LANG_SPACING
14509 desc: in the pictureflow settings menu
14510 user: core
14511 <source>
14512 *: "Spacing"
14513 </source>
14514 <dest>
14515 *: "Раздалечаване"
14516 </dest>
14517 <voice>
14518 *: "Раздалечаване"
14519 </voice>
14520</phrase>
14521<phrase>
14522 id: LANG_CENTRE_MARGIN
14523 desc: in the pictureflow settings menu
14524 user: core
14525 <source>
14526 *: "Centre margin"
14527 </source>
14528 <dest>
14529 *: "Централно поле"
14530 </dest>
14531 <voice>
14532 *: "Централно поле"
14533 </voice>
14534</phrase>
14535<phrase>
14536 id: LANG_NUMBER_OF_SLIDES
14537 desc: in the pictureflow settings menu
14538 user: core
14539 <source>
14540 *: "Number of slides"
14541 </source>
14542 <dest>
14543 *: "Брой Ñлайдове"
14544 </dest>
14545 <voice>
14546 *: "Брой Ñлайдове"
14547 </voice>
14548</phrase>
14549<phrase>
14550 id: LANG_ZOOM
14551 desc: in the pictureflow settings menu
14552 user: core
14553 <source>
14554 *: "Zoom"
14555 </source>
14556 <dest>
14557 *: "Увеличение"
14558 </dest>
14559 <voice>
14560 *: "Увеличение"
14561 </voice>
14562</phrase>
14563<phrase>
14564 id: LANG_SHOW_ALBUM_TITLE
14565 desc: in the pictureflow settings menu
14566 user: core
14567 <source>
14568 *: "Show album title"
14569 </source>
14570 <dest>
14571 *: "Показване заглавието на албума"
14572 </dest>
14573 <voice>
14574 *: "Показване заглавието на албума"
14575 </voice>
14576</phrase>
14577<phrase>
14578 id: LANG_RESIZE_COVERS
14579 desc: in the pictureflow settings menu
14580 user: core
14581 <source>
14582 *: "Resize Covers"
14583 </source>
14584 <dest>
14585 *: "ПреоразмерÑване на обложките"
14586 </dest>
14587 <voice>
14588 *: "ПреоразмерÑване на обложките"
14589 </voice>
14590</phrase>
14591<phrase>
14592 id: LANG_REBUILD_CACHE
14593 desc: in the pictureflow settings menu
14594 user: core
14595 <source>
14596 *: "Rebuild cache"
14597 </source>
14598 <dest>
14599 *: "Прегенериране на кеша"
14600 </dest>
14601 <voice>
14602 *: "Прегенериране на кеша"
14603 </voice>
14604</phrase>
14605<phrase>
14606 id: LANG_WPS_INTEGRATION
14607 desc: in the pictureflow settings menu
14608 user: core
14609 <source>
14610 *: "WPS Integration"
14611 </source>
14612 <dest>
14613 *: "WPS интеграциÑ"
14614 </dest>
14615 <voice>
14616 *: "WPS интеграциÑ"
14617 </voice>
14618</phrase>
14619<phrase>
14620 id: LANG_GOTO_WPS
14621 desc: in the pictureflow main menu
14622 user: core
14623 <source>
14624 *: "Go to WPS"
14625 </source>
14626 <dest>
14627 *: "Отиди към WPS"
14628 </dest>
14629 <voice>
14630 *: "Отиди към WPS"
14631 </voice>
14632</phrase>
14633<phrase>
14634 id: LANG_HIDE_ALBUM_TITLE
14635 desc: in the pictureflow settings
14636 user: core
14637 <source>
14638 *: "Hide album title"
14639 </source>
14640 <dest>
14641 *: "Скриване заглавието на албума"
14642 </dest>
14643 <voice>
14644 *: "Скриване заглавието на албума"
14645 </voice>
14646</phrase>
14647<phrase>
14648 id: LANG_SHOW_AT_THE_BOTTOM
14649 desc: in the pictureflow settings
14650 user: core
14651 <source>
14652 *: "Show at the bottom"
14653 </source>
14654 <dest>
14655 *: "Показване най-отдолу"
14656 </dest>
14657 <voice>
14658 *: "Показване най-отдолу"
14659 </voice>
14660</phrase>
14661<phrase>
14662 id: LANG_SHOW_AT_THE_TOP
14663 desc: in the pictureflow settings
14664 user: core
14665 <source>
14666 *: "Show at the top"
14667 </source>
14668 <dest>
14669 *: "Показване най-отгоре"
14670 </dest>
14671 <voice>
14672 *: "Показване най-отгоре"
14673 </voice>
14674</phrase>
14675<phrase>
14676 id: LANG_DIRECT
14677 desc: in the pictureflow settings, also a volume adjustment mode
14678 user: core
14679 <source>
14680 *: "Direct"
14681 </source>
14682 <dest>
14683 *: "Директно"
14684 </dest>
14685 <voice>
14686 *: "Директно"
14687 </voice>
14688</phrase>
14689<phrase>
14690 id: LANG_VIA_TRACK_LIST
14691 desc: in the pictureflow settings
14692 user: core
14693 <source>
14694 *: "Via Track list"
14695 </source>
14696 <dest>
14697 *: "Чрез ÑпиÑъка ÑÑŠÑ Ð·Ð°Ð¿Ð¸Ñи"
14698 </dest>
14699 <voice>
14700 *: "Чрез ÑпиÑъка ÑÑŠÑ Ð·Ð°Ð¿Ð¸Ñи"
14701 </voice>
14702</phrase>
14703<phrase>
14704 id: LANG_ALWAYS_ON
14705 desc: in the pictureflow settings menu
14706 user: core
14707 <source>
14708 *: "Always On"
14709 </source>
14710 <dest>
14711 *: "Винаги включено"
14712 </dest>
14713 <voice>
14714 *: "Винаги включено"
14715 </voice>
14716</phrase>
14717<phrase>
14718 id: LANG_NO_ALBUMART_FOUND
14719 desc: in the pictureflow splash messages
14720 user: core
14721 <source>
14722 *: "No album art found"
14723 </source>
14724 <dest>
14725 *: "Ðе е намерена обложка"
14726 </dest>
14727 <voice>
14728 *: "Ðе е намерена обложка"
14729 </voice>
14730</phrase>
14731<phrase>
14732 id: LANG_CACHE_REBUILT_NEXT_RESTART
14733 desc: in the pictureflow splash messages
14734 user: core
14735 <source>
14736 *: "Cache will be rebuilt on next restart"
14737 </source>
14738 <dest>
14739 *: "Кешът ще Ñе прегенерира при ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ñ€ÐµÑтарт"
14740 </dest>
14741 <voice>
14742 *: "Кешът ще Ñе прегенерира при ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ñ€ÐµÑтарт"
14743 </voice>
14744</phrase>
14745<phrase>
14746 id: LANG_ERROR_WRITING_CONFIG
14747 desc: in the pictureflow splash messages
14748 user: core
14749 <source>
14750 *: "Error writing config"
14751 </source>
14752 <dest>
14753 *: "Грешка при запиÑване на конфигурациÑта"
14754 </dest>
14755 <voice>
14756 *: "Грешка при запиÑване на конфигурациÑта"
14757 </voice>
14758</phrase>
14759<phrase>
14760 id: LANG_NOT_A_VBR_FILE
14761 desc: in vbrfix plugin
14762 user: core
14763 <source>
14764 *: "Not a VBR file"
14765 </source>
14766 <dest>
14767 *: "Файлът не е VBR"
14768 </dest>
14769 <voice>
14770 *: "Файлът не е VBR"
14771 </voice>
14772</phrase>
14773<phrase>
14774 id: LANG_FILE_ERROR
14775 desc: in vbrfix plugin
14776 user: core
14777 <source>
14778 *: "File error: %d"
14779 </source>
14780 <dest>
14781 *: "Файлова грешка: %d"
14782 </dest>
14783 <voice>
14784 *: "Файлова грешка"
14785 </voice>
14786</phrase>
14787<phrase>
14788 id: LANG_UPDATE_CACHE
14789 desc: in pictureflow
14790 user: core
14791 <source>
14792 *: "Update cache"
14793 </source>
14794 <dest>
14795 *: "ОбновÑване на кеша"
14796 </dest>
14797 <voice>
14798 *: "ОбновÑване на кеша"
14799 </voice>
14800</phrase>
14801<phrase>
14802 id: LANG_HIDE_ALBUM_TITLE_NEW
14803 desc: in the pictureflow settings
14804 user: core
14805 <source>
14806 *: "Hide information"
14807 </source>
14808 <dest>
14809 *: "Скриване на информациÑта"
14810 </dest>
14811 <voice>
14812 *: "Скриване на информациÑта"
14813 </voice>
14814</phrase>
14815<phrase>
14816 id: LANG_SHOW_AT_THE_BOTTOM_NEW
14817 desc: in the pictureflow settings
14818 user: core
14819 <source>
14820 *: "Show album at the bottom"
14821 </source>
14822 <dest>
14823 *: "Показване на албума най-долу"
14824 </dest>
14825 <voice>
14826 *: "Показване на албума най-долу"
14827 </voice>
14828</phrase>
14829<phrase>
14830 id: LANG_SHOW_AT_THE_TOP_NEW
14831 desc: in the pictureflow settings
14832 user: core
14833 <source>
14834 *: "Show album at the top"
14835 </source>
14836 <dest>
14837 *: "Показване на албум най-горе"
14838 </dest>
14839 <voice>
14840 *: "Показване на албум най-горе"
14841 </voice>
14842</phrase>
14843<phrase>
14844 id: LANG_SHOW_ALL_AT_THE_TOP
14845 desc: in the pictureflow settings
14846 user: core
14847 <source>
14848 *: "Show album and artist at the top"
14849 </source>
14850 <dest>
14851 *: "Показване на албум и изпълнител най-горе"
14852 </dest>
14853 <voice>
14854 *: "Показване на албум и изпълнител най-горе"
14855 </voice>
14856</phrase>
14857<phrase>
14858 id: LANG_SHOW_ALL_AT_THE_BOTTOM
14859 desc: in the pictureflow settings
14860 user: core
14861 <source>
14862 *: "Show album and artist at the bottom"
14863 </source>
14864 <dest>
14865 *: "Показване на албум и изпълнител най-долу"
14866 </dest>
14867 <voice>
14868 *: "Показване на албум и изпълнител най-долу"
14869 </voice>
14870</phrase>
14871<phrase>
14872 id: LANG_ACTION_STD_CANCEL
14873 desc: standard press x to cancel string
14874 user: core
14875 <source>
14876 *: "Press LEFT to cancel."
14877 android,hifietma*,zenvision: "Press BACK to cancel."
14878 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
14879 ihifi760,ihifi960: "Double tap RETURN to cancel."
14880 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
14881 iriverh10,samsungyh*: "Double tap LEFT to cancel."
14882 mpiohd200: "Double tap REC to cancel."
14883 mpiohd300: "Double tap MENU to cancel."
14884 rx27generic: "Press VOLUME to cancel."
14885 sonynwza860: "Keymaps incomplete."
14886 touchscreen: "Press Middle Left to cancel."
14887 vibe500: "Press PREV to cancel."
14888 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
14889 </source>
14890 <dest>
14891 *: "ÐатиÑнете LEFT за отказ."
14892 android,hifietma*,zenvision: "ÐатиÑнете BACK за отказ."
14893 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "ÐатиÑнете POWER за отказ."
14894 ihifi760,ihifi960: "ÐатиÑнете двукратно RETURN за отказ."
14895 ihifi770,ihifi770c,ihifi800: "ÐатиÑнете HOME за отказ."
14896 iriverh10,samsungyh*: "ÐатиÑнете двукратно LEFT за отказ."
14897 mpiohd200: "ÐатиÑнете двукратно REC за отказ."
14898 mpiohd300: "ÐатиÑнете двукратно MENU за отказ."
14899 rx27generic: "ÐатиÑнете VOLUME за отказ."
14900 sonynwza860: "ÐаÑтройките за клавиши Ñа непълни."
14901 touchscreen: "ÐатиÑнете по Ñредата отлÑво за отказ."
14902 vibe500: "ÐатиÑнете PREV за отказ."
14903 xduoox20,xduoox3,xduoox3ii: "ÐатиÑнете двукратно HOME за отказ."
14904 </dest>
14905 <voice>
14906 *: "ÐатиÑнете LEFT за отказ."
14907 android,hifietma*,zenvision: "ÐатиÑнете BACK за отказ."
14908 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "ÐатиÑнете POWER за отказ."
14909 ihifi760,ihifi960: "ÐатиÑнете двукратно RETURN за отказ."
14910 ihifi770,ihifi770c,ihifi800: "ÐатиÑнете HOME за отказ."
14911 iriverh10,samsungyh*: "ÐатиÑнете двукратно LEFT за отказ."
14912 mpiohd200: "ÐатиÑнете двукратно REC за отказ."
14913 mpiohd300: "ÐатиÑнете двукратно MENU за отказ."
14914 rx27generic: "ÐатиÑнете VOLUME за отказ."
14915 touchscreen: "ÐатиÑнете по Ñредата отлÑво за отказ."
14916 vibe500: "ÐатиÑнете PREV за отказ."
14917 xduoox20,xduoox3,xduoox3ii: "ÐатиÑнете двукратно HOME за отказ."
14918 </voice>
14919</phrase>
14920<phrase>
14921 id: LANG_DATE
14922 desc: for constructing time and date announcements
14923 user: core
14924 <source>
14925 *: "Date"
14926 </source>
14927 <dest>
14928 *: "Дата"
14929 </dest>
14930 <voice>
14931 *: "Дата"
14932 </voice>
14933</phrase>
14934<phrase>
14935 id: LANG_CLEAR_ALL
14936 desc:
14937 user: core
14938 <source>
14939 *: "Clear all"
14940 </source>
14941 <dest>
14942 *: "ИзчиÑти вÑички"
14943 </dest>
14944 <voice>
14945 *: "ИзчиÑти вÑички"
14946 </voice>
14947</phrase>
14948<phrase>
14949 id: LANG_CANCEL_0
14950 desc: CANCEL.
14951 user: core
14952 <source>
14953 *: "Cancel"
14954 </source>
14955 <dest>
14956 *: "Отказ"
14957 </dest>
14958 <voice>
14959 *: "Отказ"
14960 </voice>
14961</phrase>
14962<phrase>
14963 id: LANG_SAVE
14964 desc:
14965 user: core
14966 <source>
14967 *: "Save"
14968 </source>
14969 <dest>
14970 *: "ЗапиÑване"
14971 </dest>
14972 <voice>
14973 *: "ЗапиÑване"
14974 </voice>
14975</phrase>
14976<phrase>
14977 id: LANG_TIMEOUT
14978 desc:
14979 user: core
14980 <source>
14981 *: "Timeout"
14982 </source>
14983 <dest>
14984 *: "Таймаут"
14985 </dest>
14986 <voice>
14987 *: "Таймаут"
14988 </voice>
14989</phrase>
14990<phrase>
14991 id: LANG_TRACK
14992 desc: used in track x of y constructs
14993 user: core
14994 <source>
14995 *: "Track"
14996 </source>
14997 <dest>
14998 *: "ЗапиÑ"
14999 </dest>
15000 <voice>
15001 *: "ЗапиÑ"
15002 </voice>
15003</phrase>
15004<phrase>
15005 id: LANG_ELAPSED
15006 desc: prefix for elapsed playtime announcement
15007 user: core
15008 <source>
15009 *: "Elapsed"
15010 </source>
15011 <dest>
15012 *: "Изминало"
15013 </dest>
15014 <voice>
15015 *: "Изминало"
15016 </voice>
15017</phrase>
15018<phrase>
15019 id: LANG_ANNOUNCEMENT_FMT
15020 desc: format for wps hotkey announcement
15021 user: core
15022 <source>
15023 *: none
15024 hotkey: "Announcement format"
15025 </source>
15026 <dest>
15027 *: none
15028 hotkey: "Формат на анонÑ"
15029 </dest>
15030 <voice>
15031 *: none
15032 hotkey: "Формат на анонÑ"
15033 </voice>
15034</phrase>
15035<phrase>
15036 id: LANG_REMAIN
15037 desc: for constructs such as number of tracks remaining etc
15038 user: core
15039 <source>
15040 *: none
15041 hotkey: "Remain"
15042 </source>
15043 <dest>
15044 *: none
15045 hotkey: "ОÑтаващо"
15046 </dest>
15047 <voice>
15048 *: none
15049 hotkey: "ОÑтаващо"
15050 </voice>
15051</phrase>
15052<phrase>
15053 id: LANG_GROUPING
15054 desc:
15055 user: core
15056 <source>
15057 *: none
15058 hotkey: "Grouping"
15059 </source>
15060 <dest>
15061 *: none
15062 hotkey: "Групиране"
15063 </dest>
15064 <voice>
15065 *: none
15066 hotkey: "Групиране"
15067 </voice>
15068</phrase>
15069<phrase>
15070 id: LANG_ANNOUNCE_ON
15071 desc:
15072 user: core
15073 <source>
15074 *: none
15075 hotkey: "Announce on"
15076 </source>
15077 <dest>
15078 *: none
15079 hotkey: "ÐÐ½Ð¾Ð½Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½"
15080 </dest>
15081 <voice>
15082 *: none
15083 hotkey: "ÐÐ½Ð¾Ð½Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½"
15084 </voice>
15085</phrase>
15086<phrase>
15087 id: LANG_TRACK_CHANGE
15088 desc:
15089 user: core
15090 <source>
15091 *: none
15092 hotkey: "Track change"
15093 </source>
15094 <dest>
15095 *: none
15096 hotkey: "СмÑна на запиÑ"
15097 </dest>
15098 <voice>
15099 *: none
15100 hotkey: "СмÑна на запиÑ"
15101 </voice>
15102</phrase>
15103<phrase>
15104 id: LANG_HOLD_FOR_SETTINGS
15105 desc:
15106 user: core
15107 <source>
15108 *: none
15109 hotkey: "Hold for settings"
15110 </source>
15111 <dest>
15112 *: none
15113 hotkey: "Задръж за наÑтройки"
15114 </dest>
15115 <voice>
15116 *: none
15117 hotkey: "Задръж за наÑтройки"
15118 </voice>
15119</phrase>
15120<phrase>
15121 id: LANG_OPEN_PLUGIN
15122 desc: onplay open plugin
15123 user: core
15124 <source>
15125 *: "Open Plugin"
15126 </source>
15127 <dest>
15128 *: "Отвори плъгин"
15129 </dest>
15130 <voice>
15131 *: "Отвори плъгин"
15132 </voice>
15133</phrase>
15134<phrase>
15135 id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
15136 desc: open plugin module
15137 user: core
15138 <source>
15139 *: "Not a plugin: %s"
15140 </source>
15141 <dest>
15142 *: "Ðе е плъгин: %s"
15143 </dest>
15144 <voice>
15145 *: "Ðе е плъгин"
15146 </voice>
15147</phrase>
15148<phrase>
15149 id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
15150 desc: open plugin module
15151 user: core
15152 <source>
15153 *: "Set Wps Context Plugin"
15154 </source>
15155 <dest>
15156 *: "Избери плъгин за WPS контекÑÑ‚"
15157 </dest>
15158 <voice>
15159 *: "Избери плъгин за WPS контекÑÑ‚"
15160 </voice>
15161</phrase>
15162<phrase>
15163 id: LANG_PARAMETER
15164 desc:
15165 user: core
15166 <source>
15167 *: "Parameter"
15168 </source>
15169 <dest>
15170 *: "Параметър"
15171 </dest>
15172 <voice>
15173 *: "Параметър"
15174 </voice>
15175</phrase>
15176<phrase>
15177 id: LANG_NAME
15178 desc:
15179 user: core
15180 <source>
15181 *: "Name"
15182 </source>
15183 <dest>
15184 *: "Име"
15185 </dest>
15186 <voice>
15187 *: "Име"
15188 </voice>
15189</phrase>
15190<phrase>
15191 id: LANG_ADD
15192 desc:
15193 user: core
15194 <source>
15195 *: "Add"
15196 </source>
15197 <dest>
15198 *: "Добави"
15199 </dest>
15200 <voice>
15201 *: "Добави"
15202 </voice>
15203</phrase>
15204<phrase>
15205 id: LANG_BACK
15206 desc:
15207 user: core
15208 <source>
15209 *: "Back"
15210 </source>
15211 <dest>
15212 *: "Ðазад"
15213 </dest>
15214 <voice>
15215 *: "Ðазад"
15216 </voice>
15217</phrase>
15218<phrase>
15219 id: LANG_EDIT
15220 desc:
15221 user: core
15222 <source>
15223 *: "Edit"
15224 </source>
15225 <dest>
15226 *: "Редактирай"
15227 </dest>
15228 <voice>
15229 *: "Редактирай"
15230 </voice>
15231</phrase>
15232<phrase>
15233 id: LANG_RUN
15234 desc:
15235 user: core
15236 <source>
15237 *: "Run"
15238 </source>
15239 <dest>
15240 *: "Изпълни"
15241 </dest>
15242 <voice>
15243 *: "Изпълни"
15244 </voice>
15245</phrase>
15246<phrase>
15247 id: LANG_EXPORT
15248 desc:
15249 user: core
15250 <source>
15251 *: "Export"
15252 </source>
15253 <dest>
15254 *: "ЕкÑпортирай"
15255 </dest>
15256 <voice>
15257 *: "ЕкÑпортирай"
15258 </voice>
15259</phrase>
15260<phrase>
15261 id: LANG_BROWSE
15262 desc:
15263 user: core
15264 <source>
15265 *: "Browse"
15266 </source>
15267 <dest>
15268 *: "Разгледай"
15269 </dest>
15270 <voice>
15271 *: "Разгледай"
15272 </voice>
15273</phrase>
15274<phrase>
15275 id: LANG_ENTER_USB_STORAGE_MODE_QUERY
15276 desc: upon plugging in USB
15277 user: core
15278 <source>
15279 *: "Enter USB mass storage mode?"
15280 </source>
15281 <dest>
15282 *: "Влез в режим USB памет?"
15283 </dest>
15284 <voice>
15285 *: "Влез в режим USB памет?"
15286 </voice>
15287</phrase>
15288<phrase>
15289 id: LANG_QUEUE_MENU
15290 desc: in onplay menu
15291 user: core
15292 <source>
15293 *: "Queue..."
15294 </source>
15295 <dest>
15296 *: "Ðа опашката..."
15297 </dest>
15298 <voice>
15299 *: "Ðа опашката"
15300 </voice>
15301</phrase>
15302<phrase>
15303 id: LANG_SHOW_QUEUE_OPTIONS
15304 desc: in Current Playlist settings
15305 user: core
15306 <source>
15307 *: "Show Queue Options"
15308 </source>
15309 <dest>
15310 *: "Покажи опциите на опашката"
15311 </dest>
15312 <voice>
15313 *: "Покажи опциите на опашката"
15314 </voice>
15315</phrase>
15316<phrase>
15317 id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
15318 desc: in Current Playlist settings
15319 user: core
15320 <source>
15321 *: "Show Shuffled Adding Options"
15322 </source>
15323 <dest>
15324 *: "Покажи опциите за разбъркано добавÑне"
15325 </dest>
15326 <voice>
15327 *: "Покажи опциите за разбъркано добавÑне"
15328 </voice>
15329</phrase>
15330<phrase>
15331 id: LANG_IN_SUBMENU
15332 desc: in Settings
15333 user: core
15334 <source>
15335 *: "In Submenu"
15336 </source>
15337 <dest>
15338 *: "В подменю"
15339 </dest>
15340 <voice>
15341 *: "В подменю"
15342 </voice>
15343</phrase>
15344<phrase>
15345 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
15346 desc: disable all softlock notifications
15347 user: core
15348 <source>
15349 *: "Disable All Lock Notifications"
15350 </source>
15351 <dest>
15352 *: "Спри вÑички извеÑÑ‚Ð¸Ñ Ð·Ð° заключване"
15353 </dest>
15354 <voice>
15355 *: "Спри вÑички извеÑÑ‚Ð¸Ñ Ð·Ð° заключване"
15356 </voice>
15357</phrase>
15358<phrase>
15359 id: LANG_ACTION_VOLUME
15360 desc: exempt volume from softlock
15361 user: core
15362 <source>
15363 *: "Exempt Volume"
15364 </source>
15365 <dest>
15366 *: "С изключение на нивото на звука"
15367 </dest>
15368 <voice>
15369 *: "С изключение на нивото на звука"
15370 </voice>
15371</phrase>
15372<phrase>
15373 id: LANG_ACTION_ALWAYSAUTOLOCK
15374 desc: always prime autolock
15375 user: core
15376 <source>
15377 *: "Always Autolock"
15378 </source>
15379 <dest>
15380 *: "Винаги заключвай автоматично"
15381 </dest>
15382 <voice>
15383 *: "Винаги заключвай автоматично"
15384 </voice>
15385</phrase>
15386<phrase>
15387 id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
15388 desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
15389 user: core
15390 <source>
15391 *: ""
15392 </source>
15393 <dest>
15394 *: ""
15395 </dest>
15396 <voice>
15397 *: ""
15398 </voice>
15399</phrase>
15400<phrase>
15401 id: LANG_VOICED_DATE_FORMAT
15402 desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
15403 user: core
15404 <source>
15405 *: "dAY"
15406 </source>
15407 <dest>
15408 *: "~dAY"
15409 </dest>
15410 <voice>
15411 *: ""
15412 </voice>
15413</phrase>
15414<phrase>
15415 id: LANG_LIST_WRAPAROUND
15416 desc: in Settings
15417 user: core
15418 <source>
15419 *: "List Wraparound"
15420 </source>
15421 <dest>
15422 *: "ЦиркулÑрноÑÑ‚ на ÑпиÑъците"
15423 </dest>
15424 <voice>
15425 *: "ЦиркулÑрноÑÑ‚ на ÑпиÑъците"
15426 </voice>
15427</phrase>
15428<phrase>
15429 id: LANG_SHOW_SHUTDOWN_MESSAGE
15430 desc: in Settings
15431 user: core
15432 <source>
15433 *: "Show Shutdown Message"
15434 </source>
15435 <dest>
15436 *: "Показване на Ñъобщение при изключване"
15437 </dest>
15438 <voice>
15439 *: "Показване на Ñъобщение при изключване"
15440 </voice>
15441</phrase>
15442<phrase>
15443 id: LANG_LIST_ORDER
15444 desc: in Settings
15445 user: core
15446 <source>
15447 *: "List Order"
15448 </source>
15449 <dest>
15450 *: "Ред на ÑпиÑъците"
15451 </dest>
15452 <voice>
15453 *: "Ред на ÑпиÑъците"
15454 </voice>
15455</phrase>
15456<phrase>
15457 id: LANG_ASCENDING
15458 desc: in Settings
15459 user: core
15460 <source>
15461 *: "Ascending"
15462 </source>
15463 <dest>
15464 *: "ВъзходÑщ"
15465 </dest>
15466 <voice>
15467 *: "ВъзходÑщ"
15468 </voice>
15469</phrase>
15470<phrase>
15471 id: LANG_DESCENDING
15472 desc: in Settings
15473 user: core
15474 <source>
15475 *: "Descending"
15476 </source>
15477 <dest>
15478 *: "ÐизходÑщ"
15479 </dest>
15480 <voice>
15481 *: "ÐизходÑщ"
15482 </voice>
15483</phrase>
15484<phrase>
15485 id: LANG_ALBUM_ART
15486 desc: in Settings
15487 user: core
15488 <source>
15489 *: "Album Art"
15490 </source>
15491 <dest>
15492 *: "Обложка"
15493 </dest>
15494 <voice>
15495 *: "Обложка"
15496 </voice>
15497</phrase>
15498<phrase>
15499 id: LANG_PREFER_EMBEDDED
15500 desc: in Settings
15501 user: core
15502 <source>
15503 *: "Prefer Embedded"
15504 </source>
15505 <dest>
15506 *: "Предпочитай вградените"
15507 </dest>
15508 <voice>
15509 *: "Предпочитай вградените"
15510 </voice>
15511</phrase>
15512<phrase>
15513 id: LANG_PREFER_IMAGE_FILE
15514 desc: in Settings
15515 user: core
15516 <source>
15517 *: "Prefer Image File"
15518 </source>
15519 <dest>
15520 *: "Предпочитай файл Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ"
15521 </dest>
15522 <voice>
15523 *: "Предпочитай файл Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ"
15524 </voice>
15525</phrase>
15526<phrase>
15527 id: LANG_FM_SYNC_RDS_TIME
15528 desc: in radio screen and Settings
15529 user: core
15530 <source>
15531 *: none
15532 rds: "Sync RDS Time"
15533 </source>
15534 <dest>
15535 *: none
15536 rds: "Свери чаÑовника от RDS"
15537 </dest>
15538 <voice>
15539 *: none
15540 rds: "Свери чаÑовника от RDS"
15541 </voice>
15542</phrase>
15543<phrase>
15544 id: LANG_SORT_ALBUMS_BY
15545 desc: in Settings
15546 user: core
15547 <source>
15548 *: "Sort albums by"
15549 </source>
15550 <dest>
15551 *: "Подреди албумите по"
15552 </dest>
15553 <voice>
15554 *: "Подреди албумите по"
15555 </voice>
15556</phrase>
15557<phrase>
15558 id: LANG_ARTIST_PLUS_NAME
15559 desc: in Settings
15560 user: core
15561 <source>
15562 *: "Artist + Name"
15563 </source>
15564 <dest>
15565 *: "Изпълнител + Заглавие"
15566 </dest>
15567 <voice>
15568 *: "Изпълнител и заглавие"
15569 </voice>
15570</phrase>
15571<phrase>
15572 id: LANG_ARTIST_PLUS_YEAR
15573 desc: in Settings
15574 user: core
15575 <source>
15576 *: "Artist + Year"
15577 </source>
15578 <dest>
15579 *: "Изпълнител + Година"
15580 </dest>
15581 <voice>
15582 *: "Изпълнител и година"
15583 </voice>
15584</phrase>
15585<phrase>
15586 id: LANG_YEAR_SORT_ORDER
15587 desc: in Settings
15588 user: core
15589 <source>
15590 *: "Year sort order"
15591 </source>
15592 <dest>
15593 *: "Ред на Ñортиране на годините"
15594 </dest>
15595 <voice>
15596 *: "Ред на Ñортиране на годините"
15597 </voice>
15598</phrase>
15599<phrase>
15600 id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
15601 desc: in Settings
15602 user: core
15603 <source>
15604 *: "Show year in album title"
15605 </source>
15606 <dest>
15607 *: "Покажи година в заглавието на албума"
15608 </dest>
15609 <voice>
15610 *: "Покажи година в заглавието на албума"
15611 </voice>
15612</phrase>
15613<phrase>
15614 id: LANG_WAIT_FOR_CACHE
15615 desc: in Settings
15616 user: core
15617 <source>
15618 *: "Cache needs to finish updating first!"
15619 </source>
15620 <dest>
15621 *: "Кешът Ñ‚Ñ€Ñбва първо да приключи обновÑване!"
15622 </dest>
15623 <voice>
15624 *: "Кешът Ñ‚Ñ€Ñбва първо да приключи обновÑване!"
15625 </voice>
15626</phrase>
15627<phrase>
15628 id: LANG_TRACK_INFO
15629 desc: Track Info Title
15630 user: core
15631 <source>
15632 *: "Track Info"
15633 </source>
15634 <dest>
15635 *: "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° запиÑа"
15636 </dest>
15637 <voice>
15638 *: "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° запиÑа"
15639 </voice>
15640</phrase>
15641<phrase>
15642 id: LANG_PLAY
15643 desc: play selected file/directory, in playlist context menu
15644 user: core
15645 <source>
15646 *: "Play"
15647 </source>
15648 <dest>
15649 *: "Възпроизвеждане"
15650 </dest>
15651 <voice>
15652 *: "Възпроизвеждане"
15653 </voice>
15654</phrase>
15655<phrase>
15656 id: LANG_PLAY_SHUFFLED
15657 desc: play selected files in shuffled order, in playlist context menu
15658 user: core
15659 <source>
15660 *: "Play Shuffled"
15661 </source>
15662 <dest>
15663 *: "Разбъркано възпроизвеждане"
15664 </dest>
15665 <voice>
15666 *: "Разбъркано възпроизвеждане"
15667 </voice>
15668</phrase>
15669<phrase>
15670 id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
15671 desc: used in the playlist settings menu
15672 user: core
15673 <source>
15674 *: "Keep Current Track When Replacing Playlist"
15675 </source>
15676 <dest>
15677 *: "Запази Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¸ презапиÑване на плейлиÑта"
15678 </dest>
15679 <voice>
15680 *: "Запази Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¸ презапиÑване на плейлиÑта"
15681 </voice>
15682</phrase>
15683<phrase>
15684 id: LANG_CLEAR_SETTINGS_ON_HOLD
15685 desc: in the system sub menu
15686 user: core
15687 <source>
15688 *: none
15689 clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
15690 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
15691 </source>
15692 <dest>
15693 *: none
15694 clear_settings_on_hold,iriverh10: "ИзчиÑти наÑтройките когато бутонът за нулиране е задържан при Ñтартиране"
15695 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "ИзчиÑти наÑтройките когато бутонът за заключване е активен при Ñтартиране"
15696 </dest>
15697 <voice>
15698 *: none
15699 clear_settings_on_hold,iriverh10: "ИзчиÑти наÑтройките когато бутонът за нулиране е задържан при Ñтартиране"
15700 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "ИзчиÑти наÑтройките когато бутонът за заключване е активен при Ñтартиране"
15701 </voice>
15702</phrase>
15703<phrase>
15704 id: LANG_REWIND_ACROSS_TRACKS
15705 desc: in playback settings menu
15706 user: core
15707 <source>
15708 *: "Rewind Across Tracks"
15709 </source>
15710 <dest>
15711 *: "Превъртане между запиÑите"
15712 </dest>
15713 <voice>
15714 *: "Превъртане между запиÑите"
15715 </voice>
15716</phrase>
15717<phrase>
15718 id: LANG_SET_AS
15719 desc: used in the onplay menu
15720 user: core
15721 <source>
15722 *: "Set As..."
15723 </source>
15724 <dest>
15725 *: "Задай като..."
15726 </dest>
15727 <voice>
15728 *: "Задай като"
15729 </voice>
15730</phrase>
15731<phrase>
15732 id: LANG_PLAYLIST_DIR
15733 desc: used in the onplay menu
15734 user: core
15735 <source>
15736 *: "Playlist Directory"
15737 </source>
15738 <dest>
15739 *: "Папка за плейлиÑти"
15740 </dest>
15741 <voice>
15742 *: "Папка за плейлиÑти"
15743 </voice>
15744</phrase>
15745<phrase>
15746 id: LANG_START_DIR
15747 desc: used in the onplay menu
15748 user: core
15749 <source>
15750 *: "Start Directory"
15751 </source>
15752 <dest>
15753 *: "Ðачална папка"
15754 </dest>
15755 <voice>
15756 *: "Ðачална папка"
15757 </voice>
15758</phrase>
15759<phrase>
15760 id: LANG_RECORDING_DIR
15761 desc: used in the onplay menu
15762 user: core
15763 <source>
15764 *: none
15765 recording: "Recording Directory"
15766 </source>
15767 <dest>
15768 *: none
15769 recording: "Папка за запиÑи"
15770 </dest>
15771 <voice>
15772 *: none
15773 recording: "Папка за запиÑи"
15774 </voice>
15775</phrase>
15776<phrase>
15777 id: LANG_ADD_TO_PL
15778 desc: used in the onplay menu
15779 user: core
15780 <source>
15781 *: "Add to Playlist..."
15782 </source>
15783 <dest>
15784 *: "Добави в плейлиÑта..."
15785 </dest>
15786 <voice>
15787 *: "Добави в плейлиÑта"
15788 </voice>
15789</phrase>
15790<phrase>
15791 id: LANG_ADD_TO_EXISTING_PL
15792 desc: used in the onplay menu
15793 user: core
15794 <source>
15795 *: "Add to Existing Playlist"
15796 </source>
15797 <dest>
15798 *: "Добави в ÑъщеÑтвуваща плейлиÑта"
15799 </dest>
15800 <voice>
15801 *: "Добави в ÑъщеÑтвуваща плейлиÑта"
15802 </voice>
15803</phrase>
15804<phrase>
15805 id: LANG_PLAYING_NEXT
15806 desc: used in the onplay menu
15807 user: core
15808 <source>
15809 *: "Playing Next..."
15810 </source>
15811 <dest>
15812 *: "Възпроизвеждане на Ñледващ..."
15813 </dest>
15814 <voice>
15815 *: "Възпроизвеждане на Ñледващ"
15816 </voice>
15817</phrase>
15818<phrase>
15819 id: LANG_PLAY_NEXT
15820 desc: used in the onplay menu
15821 user: core
15822 <source>
15823 *: "Play Next"
15824 </source>
15825 <dest>
15826 *: "Възпроизведи Ñледващ"
15827 </dest>
15828 <voice>
15829 *: "Възпроизведи Ñледващ"
15830 </voice>
15831</phrase>
15832<phrase>
15833 id: LANG_ADD_SHUFFLED
15834 desc: used in the onplay menu
15835 user: core
15836 <source>
15837 *: "Add Shuffled"
15838 </source>
15839 <dest>
15840 *: "Добави разбъркано"
15841 </dest>
15842 <voice>
15843 *: "Добави разбъркано"
15844 </voice>
15845</phrase>
15846<phrase>
15847 id: LANG_PLAY_LAST
15848 desc: used in the onplay menu
15849 user: core
15850 <source>
15851 *: "Play Last"
15852 </source>
15853 <dest>
15854 *: "Възпроизведи поÑледен"
15855 </dest>
15856 <voice>
15857 *: "Възпроизведи поÑледен"
15858 </voice>
15859</phrase>
15860<phrase>
15861 id: LANG_PLAY_LAST_SHUFFLED
15862 desc: used in the onplay menu
15863 user: core
15864 <source>
15865 *: "Play Last Shuffled"
15866 </source>
15867 <dest>
15868 *: "Възпроизведи поÑледен разбъркано"
15869 </dest>
15870 <voice>
15871 *: "Възпроизведи поÑледен разбъркано"
15872 </voice>
15873</phrase>
15874<phrase>
15875 id: LANG_VOLUME_ADJUST_MODE
15876 desc: in system settings
15877 user: core
15878 <source>
15879 *: none
15880 perceptual_volume: "Volume Adjustment Mode"
15881 </source>
15882 <dest>
15883 *: none
15884 perceptual_volume: "Режим на наÑтройка Ñилата на звука"
15885 </dest>
15886 <voice>
15887 *: none
15888 perceptual_volume: "Режим на наÑтройка Ñилата на звука"
15889 </voice>
15890</phrase>
15891<phrase>
15892 id: LANG_VOLUME_ADJUST_NORM_STEPS
15893 desc: in system settings
15894 user: core
15895 <source>
15896 *: none
15897 perceptual_volume: "Number of Volume Steps"
15898 </source>
15899 <dest>
15900 *: none
15901 perceptual_volume: "Брой Ñтъпки на Ñилата на звука"
15902 </dest>
15903 <voice>
15904 *: none
15905 perceptual_volume: "Брой Ñтъпки на Ñилата на звука"
15906 </voice>
15907</phrase>
15908<phrase>
15909 id: LANG_PERCEPTUAL
15910 desc: in system settings -> volume adjustment mode
15911 user: core
15912 <source>
15913 *: none
15914 perceptual_volume: "Perceptual"
15915 </source>
15916 <dest>
15917 *: none
15918 perceptual_volume: "По възприÑтие"
15919 </dest>
15920 <voice>
15921 *: none
15922 perceptual_volume: "По възприÑтие"
15923 </voice>
15924</phrase>
15925<phrase>
15926 id: LANG_SHOW_TRACKS_WHILE_BROWSING
15927 desc: in PictureFlow Main Menu
15928 user: core
15929 <source>
15930 *: "Show Tracks While Browsing"
15931 </source>
15932 <dest>
15933 *: "Покажи запиÑите по време на разглеждане"
15934 </dest>
15935 <voice>
15936 *: "Покажи запиÑите по време на разглеждане"
15937 </voice>
15938</phrase>
15939<phrase>
15940 id: LANG_GOTO_LAST_ALBUM
15941 desc: in PictureFlow Main Menu
15942 user: core
15943 <source>
15944 *: "Go to Last Album"
15945 </source>
15946 <dest>
15947 *: "Отиди към поÑледниÑÑ‚ албум"
15948 </dest>
15949 <voice>
15950 *: "Отиди към поÑледниÑÑ‚ албум"
15951 </voice>
15952</phrase>
15953<phrase>
15954 id: LANG_DATABASE_DIR
15955 desc: in database settings menu
15956 user: core
15957 <source>
15958 *: "Database Directory"
15959 </source>
15960 <dest>
15961 *: "Папка за базата данни"
15962 </dest>
15963 <voice>
15964 *: "Папка за базата данни"
15965 </voice>
15966</phrase>
15967<phrase>
15968 id: LANG_REMOVE_QUEUED_TRACKS
15969 desc: Confirmation dialog
15970 user: core
15971 <source>
15972 *: "Remove Queued Tracks?"
15973 </source>
15974 <dest>
15975 *: "Изтрий запиÑите от опашката?"
15976 </dest>
15977 <voice>
15978 *: "Изтрий запиÑите от опашката?"
15979 </voice>
15980</phrase>
15981<phrase>
15982 id: LANG_QUICK_IGNORE_DIRACHE
15983 desc: in Settings
15984 user: core
15985 <source>
15986 *: "Quick (Ignore Directory Cache)"
15987 </source>
15988 <dest>
15989 *: "Бърз (игнориране кеша на папките)"
15990 </dest>
15991 <voice>
15992 *: "Бърз (игнориране кеша на папките)"
15993 </voice>
15994</phrase>
15995<phrase>
15996 id: LANG_WPS
15997 desc: in Settings
15998 user: core
15999 <source>
16000 *: "What's Playing Screen"
16001 </source>
16002 <dest>
16003 *: "Екран по време на проÑвирване"
16004 </dest>
16005 <voice>
16006 *: "Екран по време на проÑвирване"
16007 </voice>
16008</phrase>
16009<phrase>
16010 id: LANG_DEFAULT_BROWSER
16011 desc: in Settings
16012 user: core
16013 <source>
16014 *: "Default Browser"
16015 </source>
16016 <dest>
16017 *: "Браузър по подразбиране"
16018 </dest>
16019 <voice>
16020 *: "Браузър по подразбиране"
16021 </voice>
16022</phrase>
16023<phrase>
16024 id: LANG_AMAZE_MENU
16025 desc: Amaze game
16026 user: core
16027 <source>
16028 *: "Amaze Main Menu"
16029 </source>
16030 <dest>
16031 *: "Главно меню на Amaze"
16032 </dest>
16033 <voice>
16034 *: "Главно меню на Amaze"
16035 </voice>
16036</phrase>
16037<phrase>
16038 id: LANG_SET_MAZE_SIZE
16039 desc: Maze size in Amaze game
16040 user: core
16041 <source>
16042 *: "Set Maze Size"
16043 </source>
16044 <dest>
16045 *: "Размер на лабиринта"
16046 </dest>
16047 <voice>
16048 *: "Размер на лабиринта"
16049 </voice>
16050</phrase>
16051<phrase>
16052 id: LANG_VIEW_MAP
16053 desc: Map in Amaze game
16054 user: core
16055 <source>
16056 *: "View Map"
16057 </source>
16058 <dest>
16059 *: "Виж картата"
16060 </dest>
16061 <voice>
16062 *: "Виж картата"
16063 </voice>
16064</phrase>
16065<phrase>
16066 id: LANG_SHOW_COMPASS
16067 desc: Compass in Amaze game
16068 user: core
16069 <source>
16070 *: "Show Compass"
16071 </source>
16072 <dest>
16073 *: "Покажи компаÑ"
16074 </dest>
16075 <voice>
16076 *: "Покажи компаÑ"
16077 </voice>
16078</phrase>
16079<phrase>
16080 id: LANG_SHOW_MAP
16081 desc: Map in Amaze game
16082 user: core
16083 <source>
16084 *: "Show Map"
16085 </source>
16086 <dest>
16087 *: "Покажи картата"
16088 </dest>
16089 <voice>
16090 *: "Покажи картата"
16091 </voice>
16092</phrase>
16093<phrase>
16094 id: LANG_REMEMBER_PATH
16095 desc: Map in Amaze game
16096 user: core
16097 <source>
16098 *: "Remember Path"
16099 </source>
16100 <dest>
16101 *: "Запомни пътÑ"
16102 </dest>
16103 <voice>
16104 *: "Запомни пътÑ"
16105 </voice>
16106</phrase>
16107<phrase>
16108 id: LANG_USE_LARGE_TILES
16109 desc: Map in Amaze game
16110 user: core
16111 <source>
16112 *: "Use Large Tiles"
16113 </source>
16114 <dest>
16115 *: "Ползвай големи плочки"
16116 </dest>
16117 <voice>
16118 *: "Ползвай големи плочки"
16119 </voice>
16120</phrase>
16121<phrase>
16122 id: LANG_SHOW_SOLUTION
16123 desc: Map in Amaze game
16124 user: core
16125 <source>
16126 *: "Show Solution"
16127 </source>
16128 <dest>
16129 *: "Покажи решението"
16130 </dest>
16131 <voice>
16132 *: "Покажи решението"
16133 </voice>
16134</phrase>
16135<phrase>
16136 id: LANG_QUIT_WITHOUT_SAVING
16137 desc:
16138 user: core
16139 <source>
16140 *: "Quit without saving"
16141 </source>
16142 <dest>
16143 *: "Изход без запиÑване"
16144 </dest>
16145 <voice>
16146 *: "Изход без запиÑване"
16147 </voice>
16148</phrase>
16149<phrase>
16150 id: LANG_GENERATING_MAZE
16151 desc: Amaze game
16152 user: core
16153 <source>
16154 *: "Generating maze..."
16155 </source>
16156 <dest>
16157 *: "Генериране на лабиринт..."
16158 </dest>
16159 <voice>
16160 *: "Генериране на лабиринт"
16161 </voice>
16162</phrase>
16163<phrase>
16164 id: LANG_YOU_WIN
16165 desc: Success in game
16166 user: core
16167 <source>
16168 *: "You win!"
16169 </source>
16170 <dest>
16171 *: "Спечелихте играта!"
16172 </dest>
16173 <voice>
16174 *: "Спечелихте играта!"
16175 </voice>
16176</phrase>
16177<phrase>
16178 id: LANG_YOU_CHEATED
16179 desc: Cheated in game
16180 user: core
16181 <source>
16182 *: "You cheated!"
16183 </source>
16184 <dest>
16185 *: "Измамихте в играта!"
16186 </dest>
16187 <voice>
16188 *: "Измамихте в играта!"
16189 </voice>
16190</phrase>
16191<phrase>
16192 id: LANG_DIFFICULTY_EASY
16193 desc: Game difficulty
16194 user: core
16195 <source>
16196 *: "Easy"
16197 </source>
16198 <dest>
16199 *: "ЛеÑно"
16200 </dest>
16201 <voice>
16202 *: "ЛеÑно"
16203 </voice>
16204</phrase>
16205<phrase>
16206 id: LANG_DIFFICULTY_MEDIUM
16207 desc: Game difficulty
16208 user: core
16209 <source>
16210 *: "Medium"
16211 </source>
16212 <dest>
16213 *: "Средно"
16214 </dest>
16215 <voice>
16216 *: "Средно"
16217 </voice>
16218</phrase>
16219<phrase>
16220 id: LANG_DIFFICULTY_HARD
16221 desc: Game difficulty
16222 user: core
16223 <source>
16224 *: "Hard"
16225 </source>
16226 <dest>
16227 *: "Трудно"
16228 </dest>
16229 <voice>
16230 *: "Трудно"
16231 </voice>
16232</phrase>
16233<phrase>
16234 id: LANG_DIFFICULTY_EXPERT
16235 desc: Game difficulty
16236 user: core
16237 <source>
16238 *: "Expert"
16239 </source>
16240 <dest>
16241 *: "ЕкÑпертно"
16242 </dest>
16243 <voice>
16244 *: "ЕкÑпертно"
16245 </voice>
16246</phrase>
16247<phrase>
16248 id: LANG_STEREOSW_MODE
16249 desc: Stereo Switch Mode
16250 user: core
16251 <source>
16252 *: "Stereo Switch Mode"
16253 </source>
16254 <dest>
16255 *: "РазменÑне на Ñтерео"
16256 </dest>
16257 <voice>
16258 *: "РазменÑне на Ñтерео"
16259 </voice>
16260</phrase>
16261<phrase>
16262 id: LANG_REVERSE
16263 desc: in settings_menu
16264 user: core
16265 <source>
16266 *: "Reverse"
16267 </source>
16268 <dest>
16269 *: "Обърнато"
16270 </dest>
16271 <voice>
16272 *: "Обърнато"
16273 </voice>
16274</phrase>
16275<phrase>
16276 id: LANG_ALWAYS_ZERO
16277 desc: in settings_menu
16278 user: core
16279 <source>
16280 *: "Always 0"
16281 </source>
16282 <dest>
16283 *: "Винаги 0"
16284 </dest>
16285 <voice>
16286 *: "Винаги 0"
16287 </voice>
16288</phrase>
16289<phrase>
16290 id: LANG_ALWAYS_ONE
16291 desc: in settings_menu
16292 user: core
16293 <source>
16294 *: "Always 1"
16295 </source>
16296 <dest>
16297 *: "Винаги 1"
16298 </dest>
16299 <voice>
16300 *: "Винаги 1"
16301 </voice>
16302</phrase>
16303<phrase>
16304 id: LANG_LEGAL_NOTICES
16305 desc: in system menu
16306 user: core
16307 <source>
16308 *: "Legal Notices"
16309 </source>
16310 <dest>
16311 *: "Правни бележки"
16312 </dest>
16313 <voice>
16314 *: "Правни бележки"
16315 </voice>
16316</phrase>
16317<phrase>
16318 id: LANG_ERROR_FORMATSTR
16319 desc: for general use
16320 user: core
16321 <source>
16322 *: "Error: %s"
16323 </source>
16324 <dest>
16325 *: "Грешка: %s"
16326 </dest>
16327 <voice>
16328 *: "Грешка"
16329 </voice>
16330</phrase>
16331<phrase>
16332 id: LANG_MIKMOD_SETTINGS
16333 desc: mikmod plugin
16334 user: core
16335 <source>
16336 *: "Mikmod Settings"
16337 </source>
16338 <dest>
16339 *: "ÐаÑтройки на Mikmod"
16340 </dest>
16341 <voice>
16342 *: "ÐаÑтройки на Mik mod"
16343 </voice>
16344</phrase>
16345<phrase>
16346 id: LANG_MIKMOD_MENU
16347 desc: mikmod plugin
16348 user: core
16349 <source>
16350 *: "Mikmod Menu"
16351 </source>
16352 <dest>
16353 *: "Меню на Mikmod"
16354 </dest>
16355 <voice>
16356 *: "Меню на Mik mod"
16357 </voice>
16358</phrase>
16359<phrase>
16360 id: LANG_CHESSBOX_MENU
16361 desc: chessbox plugin
16362 user: core
16363 <source>
16364 *: "Chessbox Menu"
16365 </source>
16366 <dest>
16367 *: "Меню на Chessbox"
16368 </dest>
16369 <voice>
16370 *: "Меню на Chess box"
16371 </voice>
16372</phrase>
16373<phrase>
16374 id: VOICE_INVALID_VOICE_FILE
16375 desc: played if the voice file fails to load
16376 user: core
16377 <source>
16378 *: ""
16379 </source>
16380 <dest>
16381 *: ""
16382 </dest>
16383 <voice>
16384 *: "Ðевалиден глаÑов файл"
16385 </voice>
16386</phrase>
16387<phrase>
16388 id: VOICE_LANG_NAME
16389 desc: Spoken name of the language
16390 user: core
16391 <source>
16392 *: ""
16393 </source>
16394 <dest>
16395 *: ""
16396 </dest>
16397 <voice>
16398 *: "БългарÑки"
16399 </voice>
16400</phrase>
16401<phrase>
16402 id: LANG_PERCENT_FORMAT
16403 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
16404 user: core
16405 <source>
16406 *: "%ld%%"
16407 </source>
16408 <dest>
16409 *: "~%ld%%"
16410 </dest>
16411 <voice>
16412 *: none
12950 </voice> 16413 </voice>
12951</phrase> 16414</phrase>
diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang
index 54440c54b6..d018271645 100644
--- a/apps/lang/chinese-simp.lang
+++ b/apps/lang/chinese-simp.lang
@@ -21,6 +21,7 @@
21# - Harry Tu 21# - Harry Tu
22# - Jun Gu 22# - Jun Gu
23# - Purling Nayuki 23# - Purling Nayuki
24# - çŽ‹å‰ (Medu Hedan)
24#------ 25#------
25<phrase> 26<phrase>
26 id: LANG_LIST_SEPARATOR 27 id: LANG_LIST_SEPARATOR
@@ -201,7 +202,7 @@
201 *: "常规设置" 202 *: "常规设置"
202 </dest> 203 </dest>
203 <voice> 204 <voice>
204 *: "ä¸èˆ¬è®¾ç½®" 205 *: "常规设置"
205 </voice> 206 </voice>
206</phrase> 207</phrase>
207<phrase> 208<phrase>
@@ -338,7 +339,7 @@
338</phrase> 339</phrase>
339<phrase> 340<phrase>
340 id: LANG_CHANNEL_STEREO 341 id: LANG_CHANNEL_STEREO
341 desc: in sound_settings 342 desc: in sound_settings and radio screen
342 user: core 343 user: core
343 <source> 344 <source>
344 *: "Stereo" 345 *: "Stereo"
@@ -352,7 +353,7 @@
352</phrase> 353</phrase>
353<phrase> 354<phrase>
354 id: LANG_CHANNEL_MONO 355 id: LANG_CHANNEL_MONO
355 desc: in sound_settings 356 desc: in sound_settings and radio screen
356 user: core 357 user: core
357 <source> 358 <source>
358 *: "Mono" 359 *: "Mono"
@@ -732,20 +733,6 @@
732 </voice> 733 </voice>
733</phrase> 734</phrase>
734<phrase> 735<phrase>
735 id: LANG_EQUALIZER_EDIT_MODE
736 desc: in the equalizer settings menu
737 user: core
738 <source>
739 *: "Edit mode: %s"
740 </source>
741 <dest>
742 *: "编辑:%s"
743 </dest>
744 <voice>
745 *: ""
746 </voice>
747</phrase>
748<phrase>
749 id: LANG_EQUALIZER_GAIN_ITEM 736 id: LANG_EQUALIZER_GAIN_ITEM
750 desc: in the equalizer settings menu 737 desc: in the equalizer settings menu
751 user: core 738 user: core
@@ -756,7 +743,7 @@
756 *: "%dHz频段增益" 743 *: "%dHz频段增益"
757 </dest> 744 </dest>
758 <voice> 745 <voice>
759 *: "%d赫兹频段增益" 746 *: "赫兹频段增益"
760 </voice> 747 </voice>
761</phrase> 748</phrase>
762<phrase> 749<phrase>
@@ -784,7 +771,7 @@
784 *: "带通滤波器%d" 771 *: "带通滤波器%d"
785 </dest> 772 </dest>
786 <voice> 773 <voice>
787 *: "带通滤波器%d" 774 *: "带通滤波器"
788 </voice> 775 </voice>
789</phrase> 776</phrase>
790<phrase> 777<phrase>
@@ -949,10 +936,10 @@
949 *: "Shuffle" 936 *: "Shuffle"
950 </source> 937 </source>
951 <dest> 938 <dest>
952 *: "ä¹±åºæ’­æ”¾" 939 *: "éšæœºæ’­æ”¾"
953 </dest> 940 </dest>
954 <voice> 941 <voice>
955 *: "ä¹±åºæ’­æ”¾" 942 *: "éšæœºæ’­æ”¾"
956 </voice> 943 </voice>
957</phrase> 944</phrase>
958<phrase> 945<phrase>
@@ -991,10 +978,10 @@
991 *: "A-B" 978 *: "A-B"
992 </source> 979 </source>
993 <dest> 980 <dest>
994 *: "A-Bé‡å¤æ®µ" 981 *: "A-Bé‡å¤"
995 </dest> 982 </dest>
996 <voice> 983 <voice>
997 *: "AB段" 984 *: "区段é‡å¤"
998 </voice> 985 </voice>
999</phrase> 986</phrase>
1000<phrase> 987<phrase>
@@ -1244,7 +1231,7 @@
1244 *: "åŽå°æ›´æ–°ä¸­..." 1231 *: "åŽå°æ›´æ–°ä¸­..."
1245 </dest> 1232 </dest>
1246 <voice> 1233 <voice>
1247 *: "åŽå°æ›´æ–°ä¸­..." 1234 *: "åŽå°æ›´æ–°ä¸­"
1248 </voice> 1235 </voice>
1249</phrase> 1236</phrase>
1250<phrase> 1237<phrase>
@@ -1395,7 +1382,7 @@
1395 *: "Supported" 1382 *: "Supported"
1396 </source> 1383 </source>
1397 <dest> 1384 <dest>
1398 *: "仅显示Rockbox支æŒçš„" 1385 *: "仅显示Rockbox支æŒçš„文件"
1399 </dest> 1386 </dest>
1400 <voice> 1387 <voice>
1401 *: "仅显示Rockbox支æŒçš„文件" 1388 *: "仅显示Rockbox支æŒçš„文件"
@@ -2155,23 +2142,6 @@
2155 </voice> 2142 </voice>
2156</phrase> 2143</phrase>
2157<phrase> 2144<phrase>
2158 id: LANG_RECORD_DIRECTORY
2159 desc: in recording settings_menu
2160 user: core
2161 <source>
2162 *: none
2163 recording: "Directory"
2164 </source>
2165 <dest>
2166 *: none
2167 recording: "目录"
2168 </dest>
2169 <voice>
2170 *: none
2171 recording: "目录"
2172 </voice>
2173</phrase>
2174<phrase>
2175 id: LANG_RECORD_TRIGGER 2145 id: LANG_RECORD_TRIGGER
2176 desc: in recording settings_menu 2146 desc: in recording settings_menu
2177 user: core 2147 user: core
@@ -3105,7 +3075,7 @@
3105 *: "Linear (%)" 3075 *: "Linear (%)"
3106 </source> 3076 </source>
3107 <dest> 3077 <dest>
3108 *: "线性(%)åæ ‡" 3078 *: "线性 (%)"
3109 </dest> 3079 </dest>
3110 <voice> 3080 <voice>
3111 *: "线性åæ ‡" 3081 *: "线性åæ ‡"
@@ -3177,14 +3147,17 @@
3177 <source> 3147 <source>
3178 *: none 3148 *: none
3179 battery_types: "Alkaline" 3149 battery_types: "Alkaline"
3150 xduoox3: "Newer (2000 mAh)"
3180 </source> 3151 </source>
3181 <dest> 3152 <dest>
3182 *: none 3153 *: none
3183 battery_types: "碱性" 3154 battery_types: "碱性"
3155 xduoox3: "新电池 (2000 mAh)"
3184 </dest> 3156 </dest>
3185 <voice> 3157 <voice>
3186 *: none 3158 *: none
3187 battery_types: "碱性" 3159 battery_types: "碱性"
3160 xduoox3: "新电池"
3188 </voice> 3161 </voice>
3189</phrase> 3162</phrase>
3190<phrase> 3163<phrase>
@@ -3194,14 +3167,17 @@
3194 <source> 3167 <source>
3195 *: none 3168 *: none
3196 battery_types: "NiMH" 3169 battery_types: "NiMH"
3170 xduoox3: "Older (1500 mAh)"
3197 </source> 3171 </source>
3198 <dest> 3172 <dest>
3199 *: none 3173 *: none
3200 battery_types: "锂é•æ°¢" 3174 battery_types: "锂é•æ°¢"
3175 xduoox3: "旧电池 (1500 mAh)"
3201 </dest> 3176 </dest>
3202 <voice> 3177 <voice>
3203 *: none 3178 *: none
3204 battery_types: "锂é•æ°¢" 3179 battery_types: "锂é•æ°¢"
3180 xduoox3: "旧电池"
3205 </voice> 3181 </voice>
3206</phrase> 3182</phrase>
3207<phrase> 3183<phrase>
@@ -3595,62 +3571,6 @@
3595 </voice> 3571 </voice>
3596</phrase> 3572</phrase>
3597<phrase> 3573<phrase>
3598 id: LANG_INSERT
3599 desc: in onplay menu. insert a track/playlist into dynamic playlist.
3600 user: core
3601 <source>
3602 *: "Insert"
3603 </source>
3604 <dest>
3605 *: "æ’å…¥"
3606 </dest>
3607 <voice>
3608 *: "æ’å…¥"
3609 </voice>
3610</phrase>
3611<phrase>
3612 id: LANG_INSERT_FIRST
3613 desc: in onplay menu. insert a track/playlist into dynamic playlist.
3614 user: core
3615 <source>
3616 *: "Insert Next"
3617 </source>
3618 <dest>
3619 *: "æ’入为下一个"
3620 </dest>
3621 <voice>
3622 *: "æ’入为下一个"
3623 </voice>
3624</phrase>
3625<phrase>
3626 id: LANG_INSERT_LAST
3627 desc: in onplay menu. append a track/playlist into dynamic playlist.
3628 user: core
3629 <source>
3630 *: "Insert Last"
3631 </source>
3632 <dest>
3633 *: "æ’入到最åŽ"
3634 </dest>
3635 <voice>
3636 *: "æ’入到最åŽ"
3637 </voice>
3638</phrase>
3639<phrase>
3640 id: LANG_INSERT_SHUFFLED
3641 desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
3642 user: core
3643 <source>
3644 *: "Insert Shuffled"
3645 </source>
3646 <dest>
3647 *: "ä¹±åºæ’å…¥"
3648 </dest>
3649 <voice>
3650 *: "ä¹±åºæ’å…¥"
3651 </voice>
3652</phrase>
3653<phrase>
3654 id: LANG_QUEUE 3574 id: LANG_QUEUE
3655 desc: The verb/action Queue 3575 desc: The verb/action Queue
3656 user: core 3576 user: core
@@ -3731,7 +3651,7 @@
3731 *: "æœç´¢ä¸­...已找到%d个(%s)" 3651 *: "æœç´¢ä¸­...已找到%d个(%s)"
3732 </dest> 3652 </dest>
3733 <voice> 3653 <voice>
3734 *: "æœç´¢ä¸­...已找到%d个(%s)" 3654 *: "æœç´¢ä¸­"
3735 </voice> 3655 </voice>
3736</phrase> 3656</phrase>
3737<phrase> 3657<phrase>
@@ -3794,23 +3714,6 @@
3794 </voice> 3714 </voice>
3795</phrase> 3715</phrase>
3796<phrase> 3716<phrase>
3797 id: LANG_BATTERY_TRICKLE_CHARGE
3798 desc: in info display, shows that trickle charge is running
3799 user: core
3800 <source>
3801 *: none
3802 charging: "Battery: Trickle Chg"
3803 </source>
3804 <dest>
3805 *: none
3806 charging: "电池:æ’压涓æµå……电"
3807 </dest>
3808 <voice>
3809 *: none
3810 charging: "电池 æ’压涓æµå……电"
3811 </voice>
3812</phrase>
3813<phrase>
3814 id: LANG_BATTERY_TIME 3717 id: LANG_BATTERY_TIME
3815 desc: battery level in % and estimated time remaining 3718 desc: battery level in % and estimated time remaining
3816 user: core 3719 user: core
@@ -3865,12 +3768,12 @@
3865 </source> 3768 </source>
3866 <dest> 3769 <dest>
3867 *: "内部:" 3770 *: "内部:"
3868 hibylinux: "mSD:" 3771 hibylinux: "~mSD:"
3869 xduoox3: "mSD1:" 3772 xduoox3: "~mSD1:"
3870 </dest> 3773 </dest>
3871 <voice> 3774 <voice>
3872 *: "内部大å°ä¸º" 3775 *: "内部大å°ä¸º"
3873 hibylinux: "micro S D" 3776 hibylinux: "~micro S D"
3874 xduoox3: "mSDå¡1" 3777 xduoox3: "mSDå¡1"
3875 </voice> 3778 </voice>
3876</phrase> 3779</phrase>
@@ -3880,18 +3783,21 @@
3880 user: core 3783 user: core
3881 <source> 3784 <source>
3882 *: none 3785 *: none
3883 multivolume: "HD1" 3786 hibylinux: "USB:"
3787 multivolume: "HD1:"
3884 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 3788 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
3885 xduoox3: "mSD2:" 3789 xduoox3: "mSD2:"
3886 </source> 3790 </source>
3887 <dest> 3791 <dest>
3888 *: none 3792 *: none
3793 hibylinux: "~USB:"
3889 multivolume: "ç£ç›˜1" 3794 multivolume: "ç£ç›˜1"
3890 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 3795 sansac200*,sansaclipplus,sansae200*,sansafuze*: "~mSD:"
3891 xduoox3: "mSD2:" 3796 xduoox3: "~mSD2:"
3892 </dest> 3797 </dest>
3893 <voice> 3798 <voice>
3894 *: none 3799 *: none
3800 hibylinux: "~U S B"
3895 multivolume: "ç£ç›˜1" 3801 multivolume: "ç£ç›˜1"
3896 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSDå¡" 3802 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSDå¡"
3897 xduoox3: "mSDå¡2" 3803 xduoox3: "mSDå¡2"
@@ -4052,8 +3958,8 @@
4052 vibe500: "CANCEL:å–消" 3958 vibe500: "CANCEL:å–消"
4053 </dest> 3959 </dest>
4054 <voice> 3960 <voice>
4055 *: "" 3961 *: none
4056 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: "" 3962 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
4057 </voice> 3963 </voice>
4058</phrase> 3964</phrase>
4059<phrase> 3965<phrase>
@@ -4184,23 +4090,6 @@
4184 </voice> 4090 </voice>
4185</phrase> 4091</phrase>
4186<phrase> 4092<phrase>
4187 id: LANG_DB_INF
4188 desc: -inf db for values below measurement
4189 user: core
4190 <source>
4191 *: none
4192 recording: "-inf"
4193 </source>
4194 <dest>
4195 *: none
4196 recording: "负无穷大"
4197 </dest>
4198 <voice>
4199 *: none
4200 recording: "负无穷大"
4201 </voice>
4202</phrase>
4203<phrase>
4204 id: LANG_ALARM_MOD_TIME 4093 id: LANG_ALARM_MOD_TIME
4205 desc: The current alarm time shown in the alarm menu for the RTC alarm mod. 4094 desc: The current alarm time shown in the alarm menu for the RTC alarm mod.
4206 user: core 4095 user: core
@@ -4231,24 +4120,7 @@
4231 </dest> 4120 </dest>
4232 <voice> 4121 <voice>
4233 *: none 4122 *: none
4234 alarm: "离闹钟å“起还有%d:%02d" 4123 alarm: "离闹钟å“起还有"
4235 </voice>
4236</phrase>
4237<phrase>
4238 id: LANG_ALARM_MOD_SHUTDOWN
4239 desc: The text that tells the user that the alarm time is ok and the device shuts off (for the RTC alarm mod).
4240 user: core
4241 <source>
4242 *: none
4243 alarm: "Alarm Set"
4244 </source>
4245 <dest>
4246 *: none
4247 alarm: "已设置闹铃"
4248 </dest>
4249 <voice>
4250 *: none
4251 alarm: "闹铃已设"
4252 </voice> 4124 </voice>
4253</phrase> 4125</phrase>
4254<phrase> 4126<phrase>
@@ -4269,35 +4141,6 @@
4269 </voice> 4141 </voice>
4270</phrase> 4142</phrase>
4271<phrase> 4143<phrase>
4272 id: LANG_ALARM_MOD_KEYS
4273 desc: Shown key functions in alarm menu (for the RTC alarm mod).
4274 user: core
4275 <source>
4276 *: none
4277 alarm: "PLAY=Set OFF=Cancel"
4278 gigabeats: "SELECT=Set POWER=Cancel"
4279 ipod*: "SELECT=Set MENU=Cancel"
4280 iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
4281 mpiohd300: "ENTER=Set MENU=Cancel"
4282 sansafuzeplus: "SELECT=Set BACK=Cancel"
4283 vibe500: "OK=Set C=Cancel"
4284 </source>
4285 <dest>
4286 *: none
4287 alarm: "PLAY=设置 OFF=å–消"
4288 gigabeats: "SELECT=设置 POWER=å–消"
4289 ipod*: "SELECT=设置 MENU=å–消"
4290 iriverh10,iriverh10_5gb: "SELECT=设置 PREV=å–消"
4291 mpiohd300: "ENTER=设置 MENU=å–消"
4292 sansafuzeplus: "SELECT=设置 BACK=å–消"
4293 vibe500: "OK=设置 C=å–消"
4294 </dest>
4295 <voice>
4296 *: none
4297 alarm,ipod*: ""
4298 </voice>
4299</phrase>
4300<phrase>
4301 id: LANG_ALARM_MOD_DISABLE 4144 id: LANG_ALARM_MOD_DISABLE
4302 desc: Announce that the RTC alarm has been turned off 4145 desc: Announce that the RTC alarm has been turned off
4303 user: core 4146 user: core
@@ -4534,20 +4377,6 @@
4534 </voice> 4377 </voice>
4535</phrase> 4378</phrase>
4536<phrase> 4379<phrase>
4537 id: LANG_ID3_ALBUM_GAIN
4538 desc: in tag viewer
4539 user: core
4540 <source>
4541 *: "Album Gain"
4542 </source>
4543 <dest>
4544 *: "专辑增益"
4545 </dest>
4546 <voice>
4547 *: "专辑增益"
4548 </voice>
4549</phrase>
4550<phrase>
4551 id: LANG_ID3_PATH 4380 id: LANG_ID3_PATH
4552 desc: in tag viewer 4381 desc: in tag viewer
4553 user: core 4382 user: core
@@ -5556,7 +5385,7 @@
5556 *: "." 5385 *: "."
5557 </source> 5386 </source>
5558 <dest> 5387 <dest>
5559 *: "." 5388 *: "~."
5560 </dest> 5389 </dest>
5561 <voice> 5390 <voice>
5562 *: "点" 5391 *: "点"
@@ -6150,7 +5979,7 @@
6150 *: "æ’入了%d首曲目(%s)" 5979 *: "æ’入了%d首曲目(%s)"
6151 </dest> 5980 </dest>
6152 <voice> 5981 <voice>
6153 *: "æ’入了%d首曲目%s" 5982 *: "æ’入了曲目"
6154 </voice> 5983 </voice>
6155</phrase> 5984</phrase>
6156<phrase> 5985<phrase>
@@ -6164,7 +5993,7 @@
6164 *: "列入了%d首曲目(%s)" 5993 *: "列入了%d首曲目(%s)"
6165 </dest> 5994 </dest>
6166 <voice> 5995 <voice>
6167 *: "列入了%d首曲目%s" 5996 *: "列入了曲目"
6168 </voice> 5997 </voice>
6169</phrase> 5998</phrase>
6170<phrase> 5999<phrase>
@@ -6178,7 +6007,7 @@
6178 *: "ä¿å­˜äº†%d首曲目(%s)" 6007 *: "ä¿å­˜äº†%d首曲目(%s)"
6179 </dest> 6008 </dest>
6180 <voice> 6009 <voice>
6181 *: "ä¿å­˜äº†%d首曲目%s" 6010 *: "ä¿å­˜äº†æ›²ç›®"
6182 </voice> 6011 </voice>
6183</phrase> 6012</phrase>
6184<phrase> 6013<phrase>
@@ -6224,20 +6053,6 @@
6224 </voice> 6053 </voice>
6225</phrase> 6054</phrase>
6226<phrase> 6055<phrase>
6227 id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR
6228 desc: Playlist error
6229 user: core
6230 <source>
6231 *: "Error updating playlist control file"
6232 </source>
6233 <dest>
6234 *: "更新播放列表控制文件错误"
6235 </dest>
6236 <voice>
6237 *: "更新播放列表控制文件错误"
6238 </voice>
6239</phrase>
6240<phrase>
6241 id: LANG_PLAYLIST_ACCESS_ERROR 6056 id: LANG_PLAYLIST_ACCESS_ERROR
6242 desc: Playlist error 6057 desc: Playlist error
6243 user: core 6058 user: core
@@ -6614,12 +6429,11 @@
6614 <dest> 6429 <dest>
6615 *: "按OFFé”®å–消" 6430 *: "按OFFé”®å–消"
6616 gigabeatfx: "按POWERé”®å–消" 6431 gigabeatfx: "按POWERé”®å–消"
6617 gigabeats: "按BACKé”®å–消" 6432 gigabeats,sansafuzeplus: "按BACKé”®å–消"
6618 iaudiom5,iaudiox5: "长按PLAYé”®å–消" 6433 iaudiom5,iaudiox5: "长按PLAYé”®å–消"
6619 ipod*: "按PLAY/PAUSEé”®å–消" 6434 ipod*: "按PLAY/PAUSEé”®å–消"
6620 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "按PREVé”®å–消" 6435 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "按PREVé”®å–消"
6621 iriverh100,iriverh120,iriverh300: "按STOPé”®å–消" 6436 iriverh100,iriverh120,iriverh300: "按STOPé”®å–消"
6622 sansafuzeplus: "按BACKé”®å–消"
6623 </dest> 6437 </dest>
6624 <voice> 6438 <voice>
6625 *: "" 6439 *: ""
@@ -7616,8 +7430,7 @@
7616 <source> 7430 <source>
7617 *: "PLAY = Yes" 7431 *: "PLAY = Yes"
7618 cowond2*: "MENU, or top-right = Yes" 7432 cowond2*: "MENU, or top-right = Yes"
7619 creativezen*: "SELECT = Yes" 7433 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
7620 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
7621 iriverh100,iriverh120,iriverh300: "NAVI = Yes" 7434 iriverh100,iriverh120,iriverh300: "NAVI = Yes"
7622 mrobe500: "PLAY, POWER, or top-right = Yes" 7435 mrobe500: "PLAY, POWER, or top-right = Yes"
7623 vibe500: "OK = Yes" 7436 vibe500: "OK = Yes"
@@ -7660,7 +7473,7 @@
7660 </dest> 7473 </dest>
7661 <voice> 7474 <voice>
7662 *: none 7475 *: none
7663 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: "" 7476 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
7664 </voice> 7477 </voice>
7665</phrase> 7478</phrase>
7666<phrase> 7479<phrase>
@@ -8088,7 +7901,7 @@
8088 *: "模å¼:" 7901 *: "模å¼:"
8089 </dest> 7902 </dest>
8090 <voice> 7903 <voice>
8091 *: "模å¼:" 7904 *: "模å¼"
8092 </voice> 7905 </voice>
8093</phrase> 7906</phrase>
8094<phrase> 7907<phrase>
@@ -8174,20 +7987,6 @@
8174 </voice> 7987 </voice>
8175</phrase> 7988</phrase>
8176<phrase> 7989<phrase>
8177 id: LANG_REPLACE
8178 desc: deprecated
8179 user: core
8180 <source>
8181 *: ""
8182 </source>
8183 <dest>
8184 *: ""
8185 </dest>
8186 <voice>
8187 *: ""
8188 </voice>
8189</phrase>
8190<phrase>
8191 id: LANG_CATALOG 7990 id: LANG_CATALOG
8192 desc: in main menu and onplay menu 7991 desc: in main menu and onplay menu
8193 user: core 7992 user: core
@@ -8447,20 +8246,6 @@
8447 </voice> 8246 </voice>
8448</phrase> 8247</phrase>
8449<phrase> 8248<phrase>
8450 id: LANG_CATALOG_ADD_TO
8451 desc: in onplay playlist catalogue submenu
8452 user: core
8453 <source>
8454 *: "Add to Playlist"
8455 </source>
8456 <dest>
8457 *: "添加到播放列表"
8458 </dest>
8459 <voice>
8460 *: "添加到播放列表"
8461 </voice>
8462</phrase>
8463<phrase>
8464 id: LANG_BUTTONLIGHT_BRIGHTNESS 8249 id: LANG_BUTTONLIGHT_BRIGHTNESS
8465 desc: in settings_menu 8250 desc: in settings_menu
8466 user: core 8251 user: core
@@ -8530,7 +8315,7 @@
8530 *: "读å–中...已读å–%d%%(%s)" 8315 *: "读å–中...已读å–%d%%(%s)"
8531 </dest> 8316 </dest>
8532 <voice> 8317 <voice>
8533 *: "读å–中...已读å–%d%%(%s)" 8318 *: "读å–中"
8534 </voice> 8319 </voice>
8535</phrase> 8320</phrase>
8536<phrase> 8321<phrase>
@@ -8596,20 +8381,6 @@
8596 </voice> 8381 </voice>
8597</phrase> 8382</phrase>
8598<phrase> 8383<phrase>
8599 id: LANG_BOOKMARK_CONTEXT_DELETE
8600 desc: bookmark context menu, delete this bookmark
8601 user: core
8602 <source>
8603 *: "Delete"
8604 </source>
8605 <dest>
8606 *: "删除"
8607 </dest>
8608 <voice>
8609 *: "删除"
8610 </voice>
8611</phrase>
8612<phrase>
8613 id: LANG_FM_JAPAN 8384 id: LANG_FM_JAPAN
8614 desc: fm region japan 8385 desc: fm region japan
8615 user: core 8386 user: core
@@ -8742,20 +8513,6 @@
8742 </voice> 8513 </voice>
8743</phrase> 8514</phrase>
8744<phrase> 8515<phrase>
8745 id: LANG_CATALOG_VIEW
8746 desc: in onplay playlist catalogue submenu
8747 user: core
8748 <source>
8749 *: "View Catalogue"
8750 </source>
8751 <dest>
8752 *: "查看所有播放列表"
8753 </dest>
8754 <voice>
8755 *: "查看所有播放列表"
8756 </voice>
8757</phrase>
8758<phrase>
8759 id: VOICE_EXT_CUESHEET 8516 id: VOICE_EXT_CUESHEET
8760 desc: 8517 desc:
8761 user: core 8518 user: core
@@ -8934,10 +8691,10 @@
8934</phrase> 8691</phrase>
8935<phrase> 8692<phrase>
8936 id: LANG_AUDIOSCROBBLER 8693 id: LANG_AUDIOSCROBBLER
8937 desc: "Last.fm Log" in the playback menu 8694 desc: "Last.fm Logger" in Plugin/apps/scrobbler
8938 user: core 8695 user: core
8939 <source> 8696 <source>
8940 *: "Last.fm Log" 8697 *: "Last.fm Logger"
8941 </source> 8698 </source>
8942 <dest> 8699 <dest>
8943 *: "Last.fm日志" 8700 *: "Last.fm日志"
@@ -8951,15 +8708,15 @@
8951 desc: in lcd settings 8708 desc: in lcd settings
8952 user: core 8709 user: core
8953 <source> 8710 <source>
8954 *: none 8711 *: "Backlight on Lock"
8955 hold_button: "Backlight on Hold" 8712 hold_button: "Backlight on Hold"
8956 </source> 8713 </source>
8957 <dest> 8714 <dest>
8958 *: none 8715 *: "背光(按键é”定时)"
8959 hold_button: "背光(按键é”定时)" 8716 hold_button: "背光(按键é”定时)"
8960 </dest> 8717 </dest>
8961 <voice> 8718 <voice>
8962 *: none 8719 *: "按键é”定时的背光"
8963 hold_button: "按键é”定时的背光" 8720 hold_button: "按键é”定时的背光"
8964 </voice> 8721 </voice>
8965</phrase> 8722</phrase>
@@ -9002,7 +8759,7 @@
9002 *: "" 8759 *: ""
9003 </dest> 8760 </dest>
9004 <voice> 8761 <voice>
9005 *: "of" 8762 *: "å…±"
9006 </voice> 8763 </voice>
9007</phrase> 8764</phrase>
9008<phrase> 8765<phrase>
@@ -9030,7 +8787,7 @@
9030 *: "%sä¸å­˜åœ¨" 8787 *: "%sä¸å­˜åœ¨"
9031 </dest> 8788 </dest>
9032 <voice> 8789 <voice>
9033 *: "%sä¸å­˜åœ¨" 8790 *: "目录ä¸å­˜åœ¨"
9034 </voice> 8791 </voice>
9035</phrase> 8792</phrase>
9036<phrase> 8793<phrase>
@@ -9079,23 +8836,6 @@
9079 </voice> 8836 </voice>
9080</phrase> 8837</phrase>
9081<phrase> 8838<phrase>
9082 id: LANG_SET_AS_REC_DIR
9083 desc: used in the onplay menu to set a recording dir
9084 user: core
9085 <source>
9086 *: none
9087 recording: "Set As Recording Directory"
9088 </source>
9089 <dest>
9090 *: none
9091 recording: "设为录音目录"
9092 </dest>
9093 <voice>
9094 *: none
9095 recording: "设为录音目录"
9096 </voice>
9097</phrase>
9098<phrase>
9099 id: LANG_FM_MENU 8839 id: LANG_FM_MENU
9100 desc: fm menu title 8840 desc: fm menu title
9101 user: core 8841 user: core
@@ -10322,20 +10062,6 @@
10322 </voice> 10062 </voice>
10323</phrase> 10063</phrase>
10324<phrase> 10064<phrase>
10325 id: LANG_SCROLLBAR_POSITION
10326 desc: in Settings -> General -> Display -> Status-/Scrollbar
10327 user: core
10328 <source>
10329 *: "Scroll Bar Position"
10330 </source>
10331 <dest>
10332 *: "滚动æ ä½ç½®"
10333 </dest>
10334 <voice>
10335 *: "滚动æ ä½ç½®"
10336 </voice>
10337</phrase>
10338<phrase>
10339 id: LANG_REMOTE_STATUSBAR 10065 id: LANG_REMOTE_STATUSBAR
10340 desc: in Settings -> General -> Display -> statusbar 10066 desc: in Settings -> General -> Display -> statusbar
10341 user: core 10067 user: core
@@ -10366,7 +10092,7 @@
10366 </dest> 10092 </dest>
10367 <voice> 10093 <voice>
10368 *: none 10094 *: none
10369 remote: "remote statusbar skin" 10095 remote: "控制器状æ€æ çš®è‚¤"
10370 </voice> 10096 </voice>
10371</phrase> 10097</phrase>
10372<phrase> 10098<phrase>
@@ -10398,20 +10124,6 @@
10398 </voice> 10124 </voice>
10399</phrase> 10125</phrase>
10400<phrase> 10126<phrase>
10401 id: LANG_INSERT_LAST_SHUFFLED
10402 desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
10403 user: core
10404 <source>
10405 *: "Insert Last Shuffled"
10406 </source>
10407 <dest>
10408 *: "éšæœºæ’入到播放列表最åŽ"
10409 </dest>
10410 <voice>
10411 *: "éšæœºæ’入到播放列表的最åŽä½ç½®"
10412 </voice>
10413</phrase>
10414<phrase>
10415 id: LANG_COMPRESSOR_SOFT_KNEE 10127 id: LANG_COMPRESSOR_SOFT_KNEE
10416 desc: in sound settings 10128 desc: in sound settings
10417 user: core 10129 user: core
@@ -10436,7 +10148,7 @@
10436 *: "下一曲:" 10148 *: "下一曲:"
10437 </dest> 10149 </dest>
10438 <voice> 10150 <voice>
10439 *: "下一曲:" 10151 *: "下一曲"
10440 </voice> 10152 </voice>
10441</phrase> 10153</phrase>
10442<phrase> 10154<phrase>
@@ -10511,11 +10223,11 @@
10511 </source> 10223 </source>
10512 <dest> 10224 <dest>
10513 *: none 10225 *: none
10514 pitchscreen: "Rate" 10226 pitchscreen: "速率"
10515 </dest> 10227 </dest>
10516 <voice> 10228 <voice>
10517 *: none 10229 *: none
10518 pitchscreen: "Rate" 10230 pitchscreen: "速率"
10519 </voice> 10231 </voice>
10520</phrase> 10232</phrase>
10521<phrase> 10233<phrase>
@@ -10574,10 +10286,10 @@
10574 *: "Right" 10286 *: "Right"
10575 </source> 10287 </source>
10576 <dest> 10288 <dest>
10577 *: "Right" 10289 *: "å³é¢"
10578 </dest> 10290 </dest>
10579 <voice> 10291 <voice>
10580 *: "Right" 10292 *: "å³é¢"
10581 </voice> 10293 </voice>
10582</phrase> 10294</phrase>
10583<phrase> 10295<phrase>
@@ -10590,11 +10302,11 @@
10590 </source> 10302 </source>
10591 <dest> 10303 <dest>
10592 *: none 10304 *: none
10593 pitchscreen: "Semitone" 10305 pitchscreen: "åŠéŸ³"
10594 </dest> 10306 </dest>
10595 <voice> 10307 <voice>
10596 *: none 10308 *: none
10597 pitchscreen: "Semitone" 10309 pitchscreen: "åŠéŸ³"
10598 </voice> 10310 </voice>
10599</phrase> 10311</phrase>
10600<phrase> 10312<phrase>
@@ -10624,11 +10336,11 @@
10624 </source> 10336 </source>
10625 <dest> 10337 <dest>
10626 *: none 10338 *: none
10627 pitchscreen: "Limit" 10339 pitchscreen: "é™åˆ¶"
10628 </dest> 10340 </dest>
10629 <voice> 10341 <voice>
10630 *: none 10342 *: none
10631 pitchscreen: "Limit" 10343 pitchscreen: "é™åˆ¶"
10632 </voice> 10344 </voice>
10633</phrase> 10345</phrase>
10634<phrase> 10346<phrase>
@@ -10776,20 +10488,6 @@
10776 </voice> 10488 </voice>
10777</phrase> 10489</phrase>
10778<phrase> 10490<phrase>
10779 id: LANG_STATUSBAR_CUSTOM
10780 desc: if this translation is compatible with LANG_CHANNEL_CUSTOM, then please use the same translation. it can be combined later then
10781 user: core
10782 <source>
10783 *: "Custom"
10784 </source>
10785 <dest>
10786 *: "自定义"
10787 </dest>
10788 <voice>
10789 *: "自定义"
10790 </voice>
10791</phrase>
10792<phrase>
10793 id: LANG_SCROLLBAR_WIDTH 10491 id: LANG_SCROLLBAR_WIDTH
10794 desc: in Settings -> General -> Display -> Status-/Scrollbar 10492 desc: in Settings -> General -> Display -> Status-/Scrollbar
10795 user: core 10493 user: core
@@ -10811,10 +10509,10 @@
10811 *: "Skip to Outro" 10509 *: "Skip to Outro"
10812 </source> 10510 </source>
10813 <dest> 10511 <dest>
10814 *: "Skip to Outro" 10512 *: "跳转至尾声"
10815 </dest> 10513 </dest>
10816 <voice> 10514 <voice>
10817 *: "Skip to Outro" 10515 *: "跳转至尾声"
10818 </voice> 10516 </voice>
10819</phrase> 10517</phrase>
10820<phrase> 10518<phrase>
@@ -10898,10 +10596,10 @@
10898 *: "Left" 10596 *: "Left"
10899 </source> 10597 </source>
10900 <dest> 10598 <dest>
10901 *: "Left" 10599 *: "å·¦é¢"
10902 </dest> 10600 </dest>
10903 <voice> 10601 <voice>
10904 *: "Left" 10602 *: "å·¦é¢"
10905 </voice> 10603 </voice>
10906</phrase> 10604</phrase>
10907<phrase> 10605<phrase>
@@ -11002,10 +10700,10 @@
11002 *: "of" 10700 *: "of"
11003 </source> 10701 </source>
11004 <dest> 10702 <dest>
11005 *: "of" 10703 *: "~of"
11006 </dest> 10704 </dest>
11007 <voice> 10705 <voice>
11008 *: "of" 10706 *: "å…±"
11009 </voice> 10707 </voice>
11010</phrase> 10708</phrase>
11011<phrase> 10709<phrase>
@@ -11107,7 +10805,7 @@
11107 </dest> 10805 </dest>
11108 <voice> 10806 <voice>
11109 *: none 10807 *: none
11110 gigabeats,samsungypr1: "频段 %d 增益" 10808 gigabeats,samsungypr1: "频段增益"
11111 </voice> 10809 </voice>
11112</phrase> 10810</phrase>
11113<phrase> 10811<phrase>
@@ -11279,10 +10977,10 @@
11279 *: "Composer" 10977 *: "Composer"
11280 </source> 10978 </source>
11281 <dest> 10979 <dest>
11282 *: "Composer" 10980 *: "曲师"
11283 </dest> 10981 </dest>
11284 <voice> 10982 <voice>
11285 *: "Composer" 10983 *: "曲师"
11286 </voice> 10984 </voice>
11287</phrase> 10985</phrase>
11288<phrase> 10986<phrase>
@@ -11299,7 +10997,7 @@
11299 </dest> 10997 </dest>
11300 <voice> 10998 <voice>
11301 *: none 10999 *: none
11302 gigabeats,samsungypr1: "频段 %d 的频率" 11000 gigabeats,samsungypr1: "频率"
11303 </voice> 11001 </voice>
11304</phrase> 11002</phrase>
11305<phrase> 11003<phrase>
@@ -11316,7 +11014,7 @@
11316 </dest> 11014 </dest>
11317 <voice> 11015 <voice>
11318 *: none 11016 *: none
11319 gigabeats,samsungypr1: "频段 %d 的宽度" 11017 gigabeats,samsungypr1: "宽度"
11320 </voice> 11018 </voice>
11321</phrase> 11019</phrase>
11322<phrase> 11020<phrase>
@@ -11361,7 +11059,7 @@
11361 *: "PictureFlow" 11059 *: "PictureFlow"
11362 </source> 11060 </source>
11363 <dest> 11061 <dest>
11364 *: "PictureFlow" 11062 *: "~PictureFlow"
11365 </dest> 11063 </dest>
11366 <voice> 11064 <voice>
11367 *: "è¿è¡Œpicture flow" 11065 *: "è¿è¡Œpicture flow"
@@ -11505,7 +11203,7 @@
11505 </dest> 11203 </dest>
11506 <voice> 11204 <voice>
11507 *: none 11205 *: none
11508 radio: "ä¿¡å·å¼ºåº¦:" 11206 radio: "ä¿¡å·å¼ºåº¦"
11509 </voice> 11207 </voice>
11510</phrase> 11208</phrase>
11511<phrase> 11209<phrase>
@@ -11554,20 +11252,6 @@
11554 </voice> 11252 </voice>
11555</phrase> 11253</phrase>
11556<phrase> 11254<phrase>
11557 id: LANG_SET_AS_START_DIR
11558 desc: used in the onplay menu to set a starting browser dir
11559 user: core
11560 <source>
11561 *: "Start File Browser Here"
11562 </source>
11563 <dest>
11564 *: "设置为文件æµè§ˆèµ·å§‹ç›®å½•"
11565 </dest>
11566 <voice>
11567 *: "设置本文件夹为文件æµè§ˆèµ·å§‹ç›®å½•"
11568 </voice>
11569</phrase>
11570<phrase>
11571 id: LANG_PAUSE_REWIND 11255 id: LANG_PAUSE_REWIND
11572 desc: Seconds to rewind when rewind on pause is enabled. 11256 desc: Seconds to rewind when rewind on pause is enabled.
11573 user: core 11257 user: core
@@ -11706,20 +11390,6 @@
11706 </voice> 11390 </voice>
11707</phrase> 11391</phrase>
11708<phrase> 11392<phrase>
11709 id: LANG_SET_AS_PLAYLISTCAT_DIR
11710 desc: used in the onplay menu to set a playlist catalogue dir
11711 user: core
11712 <source>
11713 *: "Set As Playlist Catalogue Directory"
11714 </source>
11715 <dest>
11716 *: "设置为播放列表目录ä½ç½®"
11717 </dest>
11718 <voice>
11719 *: "设置为播放列表目录ä½ç½®"
11720 </voice>
11721</phrase>
11722<phrase>
11723 id: LANG_CODEPAGE_WESTERN_EUROPEAN 11393 id: LANG_CODEPAGE_WESTERN_EUROPEAN
11724 desc: in codepage setting menu 11394 desc: in codepage setting menu
11725 user: core 11395 user: core
@@ -11765,20 +11435,6 @@
11765 </voice> 11435 </voice>
11766</phrase> 11436</phrase>
11767<phrase> 11437<phrase>
11768 id: LANG_AUTOMATIC
11769 desc: generic automatic
11770 user: core
11771 <source>
11772 *: "Automatic"
11773 </source>
11774 <dest>
11775 *: "自动"
11776 </dest>
11777 <voice>
11778 *: "自动"
11779 </voice>
11780</phrase>
11781<phrase>
11782 id: LANG_CROSSFEED_CUSTOM 11438 id: LANG_CROSSFEED_CUSTOM
11783 desc: in sound settings 11439 desc: in sound settings
11784 user: core 11440 user: core
@@ -12092,10 +11748,10 @@
12092 *: "SIDE ONLY" 11748 *: "SIDE ONLY"
12093 </source> 11749 </source>
12094 <dest> 11750 <dest>
12095 *: "SIDE ONLY" 11751 *: "å•ä¾§æ¨¡å¼"
12096 </dest> 11752 </dest>
12097 <voice> 11753 <voice>
12098 *: "SIDE ONLY" 11754 *: "å•ä¾§æ¨¡å¼"
12099 </voice> 11755 </voice>
12100</phrase> 11756</phrase>
12101<phrase> 11757<phrase>
@@ -12185,3 +11841,4663 @@
12185 *: "ADB调试" 11841 *: "ADB调试"
12186 </voice> 11842 </voice>
12187</phrase> 11843</phrase>
11844<phrase>
11845 id: LANG_SINGLE_MODE
11846 desc: single mode
11847 user: core
11848 <source>
11849 *: "Single Mode"
11850 </source>
11851 <dest>
11852 *: "å•æ›²æ¨¡å¼"
11853 </dest>
11854 <voice>
11855 *: "å•æ›²æ¨¡å¼"
11856 </voice>
11857</phrase>
11858<phrase>
11859 id: LANG_SYSFONT_EQUALIZER_EDIT_MODE
11860 desc: in the equalizer settings menu
11861 user: core
11862 <source>
11863 *: "Edit mode: %s %s"
11864 </source>
11865 <dest>
11866 *: "~Edit mode: %s %s"
11867 </dest>
11868 <voice>
11869 *: ""
11870 </voice>
11871</phrase>
11872<phrase>
11873 id: LANG_SYSFONT_EQUALIZER_BAND_CUTOFF
11874 desc: in the equalizer settings menu
11875 user: core
11876 <source>
11877 *: "Cutoff"
11878 </source>
11879 <dest>
11880 *: "~Cutoff"
11881 </dest>
11882 <voice>
11883 *: "~Cutoff Frequency"
11884 </voice>
11885</phrase>
11886<phrase>
11887 id: LANG_SYSFONT_GAIN
11888 desc: in the equalizer settings menu
11889 user: core
11890 <source>
11891 *: "Gain"
11892 </source>
11893 <dest>
11894 *: "~Gain"
11895 </dest>
11896 <voice>
11897 *: "~Gain"
11898 </voice>
11899</phrase>
11900<phrase>
11901 id: LANG_TALK_MIXER_LEVEL
11902 desc: Relative volume of voice prompts
11903 user: core
11904 <source>
11905 *: "Voice prompt volume"
11906 </source>
11907 <dest>
11908 *: "语音æ示音é‡"
11909 </dest>
11910 <voice>
11911 *: "语音æ示音é‡"
11912 </voice>
11913</phrase>
11914<phrase>
11915 id: LANG_DEADZONE
11916 desc: touchpad deadzone setting
11917 user: core
11918 <source>
11919 *: none
11920 sansafuzeplus: "Touchpad Dead Zone"
11921 </source>
11922 <dest>
11923 *: none
11924 sansafuzeplus: "触摸æ¿æ­»åŒº"
11925 </dest>
11926 <voice>
11927 *: none
11928 sansafuzeplus: "触摸æ¿æ­»åŒº"
11929 </voice>
11930</phrase>
11931<phrase>
11932 id: LANG_FILTER_SHORT_SHARP
11933 desc: in sound settings
11934 user: core
11935 <source>
11936 *: none
11937 filter_roll_off: "Short Sharp"
11938 </source>
11939 <dest>
11940 *: none
11941 filter_roll_off: "短é”(Short Sharp)"
11942 </dest>
11943 <voice>
11944 *: none
11945 filter_roll_off: "短é”"
11946 </voice>
11947</phrase>
11948<phrase>
11949 id: LANG_FILTER_SHORT_SLOW
11950 desc: in sound settings
11951 user: core
11952 <source>
11953 *: none
11954 filter_roll_off: "Short Slow"
11955 </source>
11956 <dest>
11957 *: none
11958 filter_roll_off: "短慢"
11959 </dest>
11960 <voice>
11961 *: none
11962 filter_roll_off: "短慢"
11963 </voice>
11964</phrase>
11965<phrase>
11966 id: LANG_FILTER_SUPER_SLOW
11967 desc: in sound settings
11968 user: core
11969 <source>
11970 *: none
11971 filter_roll_off: "Super Slow"
11972 </source>
11973 <dest>
11974 *: none
11975 filter_roll_off: "特别慢"
11976 </dest>
11977 <voice>
11978 *: none
11979 filter_roll_off: "特别慢"
11980 </voice>
11981</phrase>
11982<phrase>
11983 id: LANG_FILTER_SHORT
11984 desc: in sound settings
11985 user: core
11986 <source>
11987 *: none
11988 es9018: "Short"
11989 </source>
11990 <dest>
11991 *: none
11992 es9018: "短"
11993 </dest>
11994 <voice>
11995 *: none
11996 es9018: "短"
11997 </voice>
11998</phrase>
11999<phrase>
12000 id: LANG_FILTER_BYPASS
12001 desc: in sound settings
12002 user: core
12003 <source>
12004 *: none
12005 es9018: "Bypass"
12006 </source>
12007 <dest>
12008 *: none
12009 es9018: "跳过(Bypass)"
12010 </dest>
12011 <voice>
12012 *: none
12013 es9018: "跳过"
12014 </voice>
12015</phrase>
12016<phrase>
12017 id: LANG_FILTER_LINEAR_FAST
12018 desc: in sound settings
12019 user: core
12020 <source>
12021 *: none
12022 es9218: "Linear Fast"
12023 </source>
12024 <dest>
12025 *: none
12026 es9218: "线性快速"
12027 </dest>
12028 <voice>
12029 *: none
12030 es9218: "线性快速"
12031 </voice>
12032</phrase>
12033<phrase>
12034 id: LANG_FILTER_LINEAR_SLOW
12035 desc: in sound settings
12036 user: core
12037 <source>
12038 *: none
12039 es9218: "Linear Slow"
12040 </source>
12041 <dest>
12042 *: none
12043 es9218: "线性慢速"
12044 </dest>
12045 <voice>
12046 *: none
12047 es9218: "线性慢速"
12048 </voice>
12049</phrase>
12050<phrase>
12051 id: LANG_FILTER_MINIMUM_FAST
12052 desc: in sound settings
12053 user: core
12054 <source>
12055 *: none
12056 es9218: "Minimum Fast"
12057 </source>
12058 <dest>
12059 *: none
12060 es9218: "最å°åŒ–快速"
12061 </dest>
12062 <voice>
12063 *: none
12064 es9218: "最å°åŒ–快速"
12065 </voice>
12066</phrase>
12067<phrase>
12068 id: LANG_FILTER_MINIMUM_SLOW
12069 desc: in sound settings
12070 user: core
12071 <source>
12072 *: none
12073 es9218: "Minimum Slow"
12074 </source>
12075 <dest>
12076 *: none
12077 es9218: "最å°åŒ–慢速"
12078 </dest>
12079 <voice>
12080 *: none
12081 es9218: "最å°åŒ–慢速"
12082 </voice>
12083</phrase>
12084<phrase>
12085 id: LANG_FILTER_APODIZING_1
12086 desc: in sound settings
12087 user: core
12088 <source>
12089 *: none
12090 es9218: "Apodizing type 1"
12091 </source>
12092 <dest>
12093 *: none
12094 es9218: "切趾类型1"
12095 </dest>
12096 <voice>
12097 *: none
12098 es9218: "切趾类型一"
12099 </voice>
12100</phrase>
12101<phrase>
12102 id: LANG_FILTER_APODIZING_2
12103 desc: in sound settings
12104 user: core
12105 <source>
12106 *: none
12107 es9218: "Apodizing type 2"
12108 </source>
12109 <dest>
12110 *: none
12111 es9218: "切趾类型2"
12112 </dest>
12113 <voice>
12114 *: none
12115 es9218: "切趾类型二"
12116 </voice>
12117</phrase>
12118<phrase>
12119 id: LANG_FILTER_HYBRID_FAST
12120 desc: in sound settings
12121 user: core
12122 <source>
12123 *: none
12124 es9218: "Hybrid Fast"
12125 </source>
12126 <dest>
12127 *: none
12128 es9218: "æ··åˆå¿«é€Ÿ"
12129 </dest>
12130 <voice>
12131 *: none
12132 es9218: "æ··åˆå¿«é€Ÿ"
12133 </voice>
12134</phrase>
12135<phrase>
12136 id: LANG_FILTER_BRICK_WALL
12137 desc: in sound settings
12138 user: core
12139 <source>
12140 *: none
12141 es9218: "Brick Wall"
12142 </source>
12143 <dest>
12144 *: none
12145 es9218: "砖墙"
12146 </dest>
12147 <voice>
12148 *: none
12149 es9218: "砖墙"
12150 </voice>
12151</phrase>
12152<phrase>
12153 id: LANG_DAC_POWER_MODE
12154 desc: in sound settings
12155 user: core
12156 <source>
12157 *: none
12158 dac_power_mode: "DAC power mode"
12159 es9218: "DAC output level"
12160 </source>
12161 <dest>
12162 *: none
12163 dac_power_mode: "DAC电æºæ¨¡å¼"
12164 es9218: "DAC输出等级"
12165 </dest>
12166 <voice>
12167 *: none
12168 dac_power_mode: "DAC电æºæ¨¡å¼"
12169 es9218: "DAC输出等级"
12170 </voice>
12171</phrase>
12172<phrase>
12173 id: LANG_DAC_POWER_HIGH
12174 desc: in sound settings
12175 user: core
12176 <source>
12177 *: none
12178 dac_power_mode: "High performance"
12179 es9218: "High Gain (2 Vrms)"
12180 </source>
12181 <dest>
12182 *: none
12183 dac_power_mode: "高å“è´¨"
12184 es9218: "高增益 (2 Vrms)"
12185 </dest>
12186 <voice>
12187 *: none
12188 dac_power_mode: "高å“è´¨"
12189 es9218: "高增益"
12190 </voice>
12191</phrase>
12192<phrase>
12193 id: LANG_DAC_POWER_LOW
12194 desc: in sound settings
12195 user: core
12196 <source>
12197 *: none
12198 dac_power_mode: "Save battery"
12199 es9218: "Low Gain (1 Vrms)"
12200 </source>
12201 <dest>
12202 *: none
12203 dac_power_mode: "çœç”µ"
12204 es9218: "低增益 (1 Vrms)"
12205 </dest>
12206 <voice>
12207 *: none
12208 dac_power_mode: "çœç”µ"
12209 es9218: "低增益 (1 Vrms)"
12210 </voice>
12211</phrase>
12212<phrase>
12213 id: LANG_SYSFONT_EQUALIZER_BAND_Q
12214 desc: in the equalizer settings menu
12215 user: core
12216 <source>
12217 *: "Q"
12218 </source>
12219 <dest>
12220 *: "~Q"
12221 </dest>
12222 <voice>
12223 *: "~Q"
12224 </voice>
12225</phrase>
12226<phrase>
12227 id: LANG_ACTION_ENABLED
12228 desc: Selective Actions
12229 user: core
12230 <source>
12231 *: "Enabled"
12232 </source>
12233 <dest>
12234 *: "å¯ç”¨"
12235 </dest>
12236 <voice>
12237 *: "å¯ç”¨"
12238 </voice>
12239</phrase>
12240<phrase>
12241 id: LANG_ACTION_PLAY
12242 desc: Selective Actions
12243 user: core
12244 <source>
12245 *: "Exempt Play"
12246 </source>
12247 <dest>
12248 *: "播放时è±å…"
12249 </dest>
12250 <voice>
12251 *: "播放时è±å…"
12252 </voice>
12253</phrase>
12254<phrase>
12255 id: LANG_ACTION_SEEK
12256 desc: Selective Actions
12257 user: core
12258 <source>
12259 *: "Exempt Seek"
12260 </source>
12261 <dest>
12262 *: "å¿«è¿›/快退时è±å…"
12263 </dest>
12264 <voice>
12265 *: "快进快退时è±å…"
12266 </voice>
12267</phrase>
12268<phrase>
12269 id: LANG_ACTION_SKIP
12270 desc: Selective Actions
12271 user: core
12272 <source>
12273 *: "Exempt Skip"
12274 </source>
12275 <dest>
12276 *: "曲目跳过时è±å…"
12277 </dest>
12278 <voice>
12279 *: "曲目跳过时è±å…"
12280 </voice>
12281</phrase>
12282<phrase>
12283 id: LANG_BACKLIGHT_SELECTIVE
12284 desc: Backlight behaviour setting
12285 user: core
12286 <source>
12287 *: "Backlight Exemptions"
12288 </source>
12289 <dest>
12290 *: "背光开å¯ä¾‹å¤–规则"
12291 </dest>
12292 <voice>
12293 *: "背光开å¯ä¾‹å¤–规则"
12294 </voice>
12295</phrase>
12296<phrase>
12297 id: LANG_ACTION_DISABLE_EXT_POWER
12298 desc: Backlight behaviour setting
12299 user: core
12300 <source>
12301 *: "Disable on External Power"
12302 </source>
12303 <dest>
12304 *: "外接电æºæ—¶ç¦ç”¨"
12305 </dest>
12306 <voice>
12307 *: "外接电æºæ—¶ç¦ç”¨"
12308 </voice>
12309</phrase>
12310<phrase>
12311 id: LANG_ACTION_DISABLE_UNMAPPED
12312 desc: Backlight behaviour setting
12313 user: core
12314 <source>
12315 *: "Disable Unmapped Keys"
12316 </source>
12317 <dest>
12318 *: "ç¦ç”¨æœªå®šä½çš„é”®"
12319 </dest>
12320 <voice>
12321 *: "ç¦ç”¨æœªå®šä½çš„é”®"
12322 </voice>
12323</phrase>
12324<phrase>
12325 id: LANG_SOFTLOCK_SELECTIVE
12326 desc: Softlock behaviour setting
12327 user: core
12328 <source>
12329 *: "Advanced Key Lock"
12330 </source>
12331 <dest>
12332 *: "高级按键é”"
12333 </dest>
12334 <voice>
12335 *: "高级按键é”"
12336 </voice>
12337</phrase>
12338<phrase>
12339 id: LANG_ACTION_AUTOLOCK_ON
12340 desc: Softlock behaviour setting
12341 user: core
12342 <source>
12343 *: "Autolock On"
12344 </source>
12345 <dest>
12346 *: "自动é”定å¯ç”¨"
12347 </dest>
12348 <voice>
12349 *: "自动é”定å¯ç”¨"
12350 </voice>
12351</phrase>
12352<phrase>
12353 id: LANG_ACTION_AUTOLOCK_OFF
12354 desc: Softlock behaviour setting
12355 user: core
12356 <source>
12357 *: "Autolock Off"
12358 </source>
12359 <dest>
12360 *: "自动é”定ç¦ç”¨"
12361 </dest>
12362 <voice>
12363 *: "自动é”定ç¦ç”¨"
12364 </voice>
12365</phrase>
12366<phrase>
12367 id: LANG_ACTION_DISABLE_NOTIFY
12368 desc: Softlock behaviour setting
12369 user: core
12370 <source>
12371 *: "Disable Locked Reminders"
12372 </source>
12373 <dest>
12374 *: "ç¦ç”¨å·²é”定的æ醒器"
12375 </dest>
12376 <voice>
12377 *: "ç¦ç”¨å·²é”定的æ醒器"
12378 </voice>
12379</phrase>
12380<phrase>
12381 id: LANG_ACTION_DISABLE_TOUCH
12382 desc: Softlock behaviour setting
12383 user: core
12384 <source>
12385 *: "Disable Touch"
12386 </source>
12387 <dest>
12388 *: "ç¦ç”¨è§¦æŽ§"
12389 </dest>
12390 <voice>
12391 *: "ç¦ç”¨è§¦æŽ§"
12392 </voice>
12393</phrase>
12394<phrase>
12395 id: LANG_KIBIBYTE
12396 desc: a unit postfix, also voiced
12397 user: core
12398 <source>
12399 *: "KiB"
12400 </source>
12401 <dest>
12402 *: "KB"
12403 </dest>
12404 <voice>
12405 *: "K B"
12406 </voice>
12407</phrase>
12408<phrase>
12409 id: LANG_MEBIBYTE
12410 desc: a unit postfix, also voiced
12411 user: core
12412 <source>
12413 *: "MiB"
12414 </source>
12415 <dest>
12416 *: "MB"
12417 </dest>
12418 <voice>
12419 *: "M B"
12420 </voice>
12421</phrase>
12422<phrase>
12423 id: LANG_GIBIBYTE
12424 desc: a unit postfix, also voiced
12425 user: core
12426 <source>
12427 *: "GiB"
12428 </source>
12429 <dest>
12430 *: "GB"
12431 </dest>
12432 <voice>
12433 *: "G B"
12434 </voice>
12435</phrase>
12436<phrase>
12437 id: LANG_BOOKMARK_SETTINGS_ONE_PER_PLAYLIST
12438 desc: Save only one bookmark for a playlist in recent bookmarks
12439 user: core
12440 <source>
12441 *: "One per playlist"
12442 </source>
12443 <dest>
12444 *: "æ¯ä¸ªæ’­æ”¾åˆ—表一个"
12445 </dest>
12446 <voice>
12447 *: "æ¯ä¸ªæ’­æ”¾åˆ—表一个"
12448 </voice>
12449</phrase>
12450<phrase>
12451 id: LANG_BOOKMARK_SETTINGS_ONE_PER_TRACK
12452 desc: Save only one bookmark for a combination (playlist,track) in recent bookmarks
12453 user: core
12454 <source>
12455 *: "One per track"
12456 </source>
12457 <dest>
12458 *: "æ¯ä¸ªæ›²ç›®ä¸€ä¸ª"
12459 </dest>
12460 <voice>
12461 *: "æ¯ä¸ªæ›²ç›®ä¸€ä¸ª"
12462 </voice>
12463</phrase>
12464<phrase>
12465 id: VOICE_TRACK_TO_MOVE
12466 desc: playlist viewer
12467 user: core
12468 <source>
12469 *: ""
12470 </source>
12471 <dest>
12472 *: ""
12473 </dest>
12474 <voice>
12475 *: "è¦ç§»åŠ¨çš„曲目"
12476 </voice>
12477</phrase>
12478<phrase>
12479 id: VOICE_QUEUED
12480 desc: playlist viewer
12481 user: core
12482 <source>
12483 *: ""
12484 </source>
12485 <dest>
12486 *: ""
12487 </dest>
12488 <voice>
12489 *: "已列队"
12490 </voice>
12491</phrase>
12492<phrase>
12493 id: VOICE_BAD_TRACK
12494 desc: playlist viewer
12495 user: core
12496 <source>
12497 *: ""
12498 </source>
12499 <dest>
12500 *: ""
12501 </dest>
12502 <voice>
12503 *: "æŸå曲目"
12504 </voice>
12505</phrase>
12506<phrase>
12507 id: VOICE_MOVING_TRACK
12508 desc: playlist viewer
12509 user: core
12510 <source>
12511 *: ""
12512 </source>
12513 <dest>
12514 *: ""
12515 </dest>
12516 <voice>
12517 *: "移动曲目"
12518 </voice>
12519</phrase>
12520<phrase>
12521 id: LANG_PLAYTIME_ELAPSED
12522 desc: playing time screen
12523 user: core
12524 <source>
12525 *: "Playlist elapsed:"
12526 </source>
12527 <dest>
12528 *: "播放列表已播放:"
12529 </dest>
12530 <voice>
12531 *: "播放列表已播放"
12532 </voice>
12533</phrase>
12534<phrase>
12535 id: LANG_PLAYTIME_TRK_ELAPSED
12536 desc: playing time screen
12537 user: core
12538 <source>
12539 *: "Track elapsed:"
12540 </source>
12541 <dest>
12542 *: "曲目已播放:"
12543 </dest>
12544 <voice>
12545 *: "曲目已播放"
12546 </voice>
12547</phrase>
12548<phrase>
12549 id: LANG_PLAYTIME_REMAINING
12550 desc: deprecated
12551 user: core
12552 <source>
12553 *: ""
12554 </source>
12555 <dest>
12556 *: ""
12557 </dest>
12558 <voice>
12559 *: ""
12560 </voice>
12561</phrase>
12562<phrase>
12563 id: LANG_PLAYTIME_TRK_REMAINING
12564 desc: playing time screen
12565 user: core
12566 <source>
12567 *: "Track remaining:"
12568 </source>
12569 <dest>
12570 *: "曲目剩余:"
12571 </dest>
12572 <voice>
12573 *: "曲目剩余"
12574 </voice>
12575</phrase>
12576<phrase>
12577 id: LANG_PLAYTIME_TRACK
12578 desc: playing time screen
12579 user: core
12580 <source>
12581 *: "Track:"
12582 </source>
12583 <dest>
12584 *: "曲目:"
12585 </dest>
12586 <voice>
12587 *: "曲目"
12588 </voice>
12589</phrase>
12590<phrase>
12591 id: LANG_PLAYTIME_STORAGE
12592 desc: playing time screen
12593 user: core
12594 <source>
12595 *: "Storage (Done / Remaining):"
12596 </source>
12597 <dest>
12598 *: "存储 (å®Œæˆ / 剩余):"
12599 </dest>
12600 <voice>
12601 *: "存储"
12602 </voice>
12603</phrase>
12604<phrase>
12605 id: VOICE_PLAYTIME_DONE
12606 desc: playing time screen
12607 user: core
12608 <source>
12609 *: ""
12610 </source>
12611 <dest>
12612 *: ""
12613 </dest>
12614 <voice>
12615 *: "完æˆ"
12616 </voice>
12617</phrase>
12618<phrase>
12619 id: LANG_PLAYTIME_AVG_TRACK_SIZE
12620 desc: playing time screen
12621 user: core
12622 <source>
12623 *: "Average track size:"
12624 </source>
12625 <dest>
12626 *: "å¹³å‡æ›²ç›®å¤§å°ï¼š"
12627 </dest>
12628 <voice>
12629 *: "å¹³å‡æ›²ç›®å¤§å°"
12630 </voice>
12631</phrase>
12632<phrase>
12633 id: LANG_PLAYTIME_AVG_BITRATE
12634 desc: playing time screen
12635 user: core
12636 <source>
12637 *: "Average bitrate:"
12638 </source>
12639 <dest>
12640 *: "å¹³å‡æ¯”特率:"
12641 </dest>
12642 <voice>
12643 *: "å¹³å‡æ¯”特率"
12644 </voice>
12645</phrase>
12646<phrase>
12647 id: LANG_PLAYTIME_ERROR
12648 desc: playing time screen
12649 user: core
12650 <source>
12651 *: "Error while gathering info"
12652 </source>
12653 <dest>
12654 *: "收集信æ¯æ—¶å‡ºé”™"
12655 </dest>
12656 <voice>
12657 *: "收集信æ¯æ—¶å‡ºé”™"
12658 </voice>
12659</phrase>
12660<phrase>
12661 id: LANG_PLAYING_TIME
12662 desc: onplay menu
12663 user: core
12664 <source>
12665 *: "Playing time"
12666 </source>
12667 <dest>
12668 *: "播放时间"
12669 </dest>
12670 <voice>
12671 *: "播放时间"
12672 </voice>
12673</phrase>
12674<phrase>
12675 id: LANG_CAR_ADAPTER_MODE_DELAY
12676 desc: Displayed for setting car adapter mode delay
12677 user: core
12678 <source>
12679 *: none
12680 charging: "Delay Before Resume"
12681 </source>
12682 <dest>
12683 *: none
12684 charging: "æ¢å¤æ’­æ”¾å‰å»¶è¿Ÿ"
12685 </dest>
12686 <voice>
12687 *: none
12688 charging: "æ¢å¤æ’­æ”¾å‰å»¶è¿Ÿ"
12689 </voice>
12690</phrase>
12691<phrase>
12692 id: VOICE_PITCH_ABSOLUTE_MODE
12693 desc: spoken only
12694 user: core
12695 <source>
12696 *: none
12697 pitchscreen: ""
12698 </source>
12699 <dest>
12700 *: none
12701 pitchscreen: ""
12702 </dest>
12703 <voice>
12704 *: none
12705 pitchscreen: "ç»å¯¹æ¨¡å¼"
12706 </voice>
12707</phrase>
12708<phrase>
12709 id: VOICE_PITCH_SEMITONE_MODE
12710 desc: spoken only
12711 user: core
12712 <source>
12713 *: none
12714 pitchscreen: ""
12715 </source>
12716 <dest>
12717 *: none
12718 pitchscreen: ""
12719 </dest>
12720 <voice>
12721 *: none
12722 pitchscreen: "åŠéŸ³æ¨¡å¼"
12723 </voice>
12724</phrase>
12725<phrase>
12726 id: VOICE_PITCH_TIMESTRETCH_MODE
12727 desc: spoken only
12728 user: core
12729 <source>
12730 *: none
12731 pitchscreen: ""
12732 </source>
12733 <dest>
12734 *: none
12735 pitchscreen: ""
12736 </dest>
12737 <voice>
12738 *: none
12739 pitchscreen: "时间拉伸"
12740 </voice>
12741</phrase>
12742<phrase>
12743 id: LANG_REMOTE_CONTROL
12744 desc: Item for menus
12745 user: core
12746 <source>
12747 *: "Remote Control"
12748 </source>
12749 <dest>
12750 *: "远程控制"
12751 </dest>
12752 <voice>
12753 *: "远程控制"
12754 </voice>
12755</phrase>
12756<phrase>
12757 id: LANG_NO_REM_CONTROL
12758 desc: Item for menus
12759 user: core
12760 <source>
12761 *: "No Rem. Control"
12762 </source>
12763 <dest>
12764 *: "没有远程控制"
12765 </dest>
12766 <voice>
12767 *: "没有远程控制"
12768 </voice>
12769</phrase>
12770<phrase>
12771 id: LANG_OUT_OF_CONTROL
12772 desc: Item for menus
12773 user: core
12774 <source>
12775 *: "Out of Control"
12776 </source>
12777 <dest>
12778 *: "失控"
12779 </dest>
12780 <voice>
12781 *: "失控"
12782 </voice>
12783</phrase>
12784<phrase>
12785 id: LANG_2_KEY_CONTROL
12786 desc: Item for menus
12787 user: core
12788 <source>
12789 *: "2 Key Control"
12790 </source>
12791 <dest>
12792 *: "åŒé”®æŽ§åˆ¶"
12793 </dest>
12794 <voice>
12795 *: "åŒé”®æŽ§åˆ¶"
12796 </voice>
12797</phrase>
12798<phrase>
12799 id: LANG_4_KEY_CONTROL
12800 desc: Item for menus
12801 user: core
12802 <source>
12803 *: "4 Key Control"
12804 </source>
12805 <dest>
12806 *: "四键控制"
12807 </dest>
12808 <voice>
12809 *: "四键控制"
12810 </voice>
12811</phrase>
12812<phrase>
12813 id: LANG_PLAY_WORMLET
12814 desc: For wormlet menu
12815 user: core
12816 <source>
12817 *: "Play Wormlet!"
12818 </source>
12819 <dest>
12820 *: "播放蠕虫ï¼"
12821 </dest>
12822 <voice>
12823 *: "播放蠕虫"
12824 </voice>
12825</phrase>
12826<phrase>
12827 id: LANG_NUMBER_OF_WORMS
12828 desc: For wormlet menu
12829 user: core
12830 <source>
12831 *: "Number of Worms"
12832 </source>
12833 <dest>
12834 *: "蠕虫数é‡"
12835 </dest>
12836 <voice>
12837 *: "蠕虫数é‡"
12838 </voice>
12839</phrase>
12840<phrase>
12841 id: LANG_WORM_GROWTH_PER_FOOD
12842 desc: For wormlet menu
12843 user: core
12844 <source>
12845 *: "Worm Growth Per Food"
12846 </source>
12847 <dest>
12848 *: "æ¯ä¸ªé£Ÿç‰©çš„蠕虫增长速度"
12849 </dest>
12850 <voice>
12851 *: "æ¯ä¸ªé£Ÿç‰©çš„蠕虫增长速度"
12852 </voice>
12853</phrase>
12854<phrase>
12855 id: LANG_WORM_SPEED
12856 desc: For wormlet menu
12857 user: core
12858 <source>
12859 *: "Worm Speed"
12860 </source>
12861 <dest>
12862 *: "蠕虫速度"
12863 </dest>
12864 <voice>
12865 *: "蠕虫速度"
12866 </voice>
12867</phrase>
12868<phrase>
12869 id: LANG_ARGHS_PER_FOOD
12870 desc: For wormlet menu
12871 user: core
12872 <source>
12873 *: "Arghs Per Food"
12874 </source>
12875 <dest>
12876 *: "æ¯ä»½é£Ÿç‰©çš„ Arghs"
12877 </dest>
12878 <voice>
12879 *: "æ¯ä»½é£Ÿç‰©çš„ Arghs"
12880 </voice>
12881</phrase>
12882<phrase>
12883 id: LANG_ARGH_SIZE
12884 desc: For wormlet menu
12885 user: core
12886 <source>
12887 *: "Argh Size"
12888 </source>
12889 <dest>
12890 *: "Argh大å°"
12891 </dest>
12892 <voice>
12893 *: "大å°"
12894 </voice>
12895</phrase>
12896<phrase>
12897 id: LANG_FOOD_SIZE
12898 desc: For wormlet menu
12899 user: core
12900 <source>
12901 *: "Food Size"
12902 </source>
12903 <dest>
12904 *: "食物大å°"
12905 </dest>
12906 <voice>
12907 *: "食物大å°"
12908 </voice>
12909</phrase>
12910<phrase>
12911 id: LANG_NUMBER_OF_PLAYERS
12912 desc: For game menus
12913 user: core
12914 <source>
12915 *: "Number of Players"
12916 </source>
12917 <dest>
12918 *: "玩家数é‡"
12919 </dest>
12920 <voice>
12921 *: "玩家数é‡"
12922 </voice>
12923</phrase>
12924<phrase>
12925 id: LANG_CONTROL_STYLE
12926 desc: In various menus
12927 user: core
12928 <source>
12929 *: "Control Style"
12930 </source>
12931 <dest>
12932 *: "控制风格"
12933 </dest>
12934 <voice>
12935 *: "控制风格"
12936 </voice>
12937</phrase>
12938<phrase>
12939 id: LANG_REVERT_TO_DEFAULT_SETTINGS
12940 desc: In various menus
12941 user: core
12942 <source>
12943 *: "Revert to Default Settings"
12944 </source>
12945 <dest>
12946 *: "æ¢å¤é»˜è®¤è®¾ç½®"
12947 </dest>
12948 <voice>
12949 *: "æ¢å¤é»˜è®¤è®¾ç½®"
12950 </voice>
12951</phrase>
12952<phrase>
12953 id: LANG_MENU_QUIT
12954 desc: in various menus
12955 user: core
12956 <source>
12957 *: "Quit"
12958 </source>
12959 <dest>
12960 *: "退出"
12961 </dest>
12962 <voice>
12963 *: "退出"
12964 </voice>
12965</phrase>
12966<phrase>
12967 id: LANG_MENU_DISPLAY_OPTIONS
12968 desc: in various menus
12969 user: core
12970 <source>
12971 *: "Display Options"
12972 </source>
12973 <dest>
12974 *: "显示选项"
12975 </dest>
12976 <voice>
12977 *: "显示选项"
12978 </voice>
12979</phrase>
12980<phrase>
12981 id: LANG_PREVTRACK
12982 desc: in playback control menu
12983 user: core
12984 <source>
12985 *: "Previous Track"
12986 </source>
12987 <dest>
12988 *: "上一曲"
12989 </dest>
12990 <voice>
12991 *: "上一曲"
12992 </voice>
12993</phrase>
12994<phrase>
12995 id: LANG_PLAYPAUSE
12996 desc: in playback control menu
12997 user: core
12998 <source>
12999 *: "Pause / Play"
13000 </source>
13001 <dest>
13002 *: "æš‚åœ/播放"
13003 </dest>
13004 <voice>
13005 *: "æš‚åœ/播放"
13006 </voice>
13007</phrase>
13008<phrase>
13009 id: LANG_STOP_PLAYBACK
13010 desc: in playback control menu
13011 user: core
13012 <source>
13013 *: "Stop Playback"
13014 </source>
13015 <dest>
13016 *: "åœæ­¢æ’­æ”¾"
13017 </dest>
13018 <voice>
13019 *: "åœæ­¢æ’­æ”¾"
13020 </voice>
13021</phrase>
13022<phrase>
13023 id: LANG_NEXTTRACK
13024 desc: in playback control menu
13025 user: core
13026 <source>
13027 *: "Next Track"
13028 </source>
13029 <dest>
13030 *: "下一曲"
13031 </dest>
13032 <voice>
13033 *: "下一曲"
13034 </voice>
13035</phrase>
13036<phrase>
13037 id: LANG_CHANGE_VOLUME
13038 desc: in playback control menu
13039 user: core
13040 <source>
13041 *: "Change Volume"
13042 </source>
13043 <dest>
13044 *: "更改音é‡"
13045 </dest>
13046 <voice>
13047 *: "更改音é‡"
13048 </voice>
13049</phrase>
13050<phrase>
13051 id: LANG_CHANGE_SHUFFLE_MODE
13052 desc: in playback control menu
13053 user: core
13054 <source>
13055 *: "Shuffle Mode"
13056 </source>
13057 <dest>
13058 *: "éšæœºæ¨¡å¼"
13059 </dest>
13060 <voice>
13061 *: "éšæœºæ¨¡å¼"
13062 </voice>
13063</phrase>
13064<phrase>
13065 id: LANG_CHANGE_REPEAT_MODE
13066 desc: in playback control menu
13067 user: core
13068 <source>
13069 *: "Change Repeat Mode"
13070 </source>
13071 <dest>
13072 *: "é‡å¤æ¨¡å¼"
13073 </dest>
13074 <voice>
13075 *: "é‡å¤æ¨¡å¼"
13076 </voice>
13077</phrase>
13078<phrase>
13079 id: LANG_PLAYBACK_CONTROL
13080 desc: in playback control menu
13081 user: core
13082 <source>
13083 *: "Playback Control"
13084 </source>
13085 <dest>
13086 *: "播放器控制"
13087 </dest>
13088 <voice>
13089 *: "播放器控制"
13090 </voice>
13091</phrase>
13092<phrase>
13093 id: LANG_CHESSBOX_CHECKMATE
13094 desc: in chessbox
13095 user: core
13096 <source>
13097 *: "Checkmate!"
13098 </source>
13099 <dest>
13100 *: "将军ï¼ï¼"
13101 </dest>
13102 <voice>
13103 *: "将军"
13104 </voice>
13105</phrase>
13106<phrase>
13107 id: LANG_CHESSBOX_ILLEGAL_MOVE
13108 desc: in chessbox
13109 user: core
13110 <source>
13111 *: "Illegal move!"
13112 </source>
13113 <dest>
13114 *: "éžæ³•ç§»åŠ¨ï¼"
13115 </dest>
13116 <voice>
13117 *: "éžæ³•ç§»åŠ¨"
13118 </voice>
13119</phrase>
13120<phrase>
13121 id: LANG_CHESSBOX_MENU_NEW_GAME
13122 desc: in the chessbox menu
13123 user: core
13124 <source>
13125 *: "New Game"
13126 </source>
13127 <dest>
13128 *: "新游æˆ"
13129 </dest>
13130 <voice>
13131 *: "新游æˆ"
13132 </voice>
13133</phrase>
13134<phrase>
13135 id: LANG_CHESSBOX_MENU_RESUME_GAME
13136 desc: in the chessbox menu
13137 user: core
13138 <source>
13139 *: "Resume Game"
13140 </source>
13141 <dest>
13142 *: "继续游æˆ"
13143 </dest>
13144 <voice>
13145 *: "继续游æˆ"
13146 </voice>
13147</phrase>
13148<phrase>
13149 id: LANG_CHESSBOX_MENU_SAVE_GAME
13150 desc: in the chessbox menu
13151 user: core
13152 <source>
13153 *: "Save Game"
13154 </source>
13155 <dest>
13156 *: "ä¿å­˜æ¸¸æˆ"
13157 </dest>
13158 <voice>
13159 *: "ä¿å­˜æ¸¸æˆ"
13160 </voice>
13161</phrase>
13162<phrase>
13163 id: LANG_CHESSBOX_MENU_RESTORE_GAME
13164 desc: in the chessbox menu
13165 user: core
13166 <source>
13167 *: "Restore Game"
13168 </source>
13169 <dest>
13170 *: "æ¢å¤æ¸¸æˆ"
13171 </dest>
13172 <voice>
13173 *: "æ¢å¤æ¸¸æˆ"
13174 </voice>
13175</phrase>
13176<phrase>
13177 id: LANG_CHESSBOX_MENU_RESTART_GAME
13178 desc: in the chessbox menu
13179 user: core
13180 <source>
13181 *: "Restart Game"
13182 </source>
13183 <dest>
13184 *: "é‡æ–°å¼€å§‹æ¸¸æˆ"
13185 </dest>
13186 <voice>
13187 *: "é‡æ–°å¼€å§‹æ¸¸æˆ"
13188 </voice>
13189</phrase>
13190<phrase>
13191 id: LANG_CHESSBOX_MENU_SELECT_OTHER_GAME
13192 desc: in the chessbox menu
13193 user: core
13194 <source>
13195 *: "Select Other Game"
13196 </source>
13197 <dest>
13198 *: "选择å¦ä¸€ä¸ªæ¸¸æˆ"
13199 </dest>
13200 <voice>
13201 *: "选择å¦ä¸€ä¸ªæ¸¸æˆ"
13202 </voice>
13203</phrase>
13204<phrase>
13205 id: LANG_CHESSBOX_LEVEL_1
13206 desc: in the chessbox game level selection
13207 user: core
13208 <source>
13209 *: "Level 1: 60 moves / 5 min"
13210 </source>
13211 <dest>
13212 *: "Level 1: 60 步 / 5 分钟"
13213 </dest>
13214 <voice>
13215 *: "Level 1, 60 æ­¥ æ¯5 分钟"
13216 </voice>
13217</phrase>
13218<phrase>
13219 id: LANG_CHESSBOX_LEVEL_2
13220 desc: in the chessbox game level selection
13221 user: core
13222 <source>
13223 *: "Level 2: 60 moves / 15 min"
13224 </source>
13225 <dest>
13226 *: "Level 2: 60 步 / 15 分钟"
13227 </dest>
13228 <voice>
13229 *: "Level 2, 60 æ­¥ æ¯15 分钟"
13230 </voice>
13231</phrase>
13232<phrase>
13233 id: LANG_CHESSBOX_LEVEL_3
13234 desc: in the chessbox game level selection
13235 user: core
13236 <source>
13237 *: "Level 3: 60 moves / 30 min"
13238 </source>
13239 <dest>
13240 *: "Level 3: 60 步 / 30 分钟"
13241 </dest>
13242 <voice>
13243 *: "Level 3, 60 æ­¥ æ¯30 分钟"
13244 </voice>
13245</phrase>
13246<phrase>
13247 id: LANG_CHESSBOX_LEVEL_4
13248 desc: in the chessbox game level selection
13249 user: core
13250 <source>
13251 *: "Level 4: 40 moves / 30 min"
13252 </source>
13253 <dest>
13254 *: "Level 4: 40 步 / 30 分钟"
13255 </dest>
13256 <voice>
13257 *: "Level 4, 40 æ­¥ æ¯30 分钟"
13258 </voice>
13259</phrase>
13260<phrase>
13261 id: LANG_CHESSBOX_LEVEL_5
13262 desc: in the chessbox game level selection
13263 user: core
13264 <source>
13265 *: "Level 5: 40 moves / 60 min"
13266 </source>
13267 <dest>
13268 *: "Level 5: 40 步 / 60 分钟"
13269 </dest>
13270 <voice>
13271 *: "Level 5, 40 æ­¥ æ¯60 分钟"
13272 </voice>
13273</phrase>
13274<phrase>
13275 id: LANG_CHESSBOX_LEVEL_6
13276 desc: in the chessbox game level selection
13277 user: core
13278 <source>
13279 *: "Level 6: 40 moves / 120 min"
13280 </source>
13281 <dest>
13282 *: "Level 6: 40 步 / 120 分钟"
13283 </dest>
13284 <voice>
13285 *: "Level 6, 40 æ­¥ æ¯120 分钟"
13286 </voice>
13287</phrase>
13288<phrase>
13289 id: LANG_CHESSBOX_LEVEL_7
13290 desc: in the chessbox game level selection
13291 user: core
13292 <source>
13293 *: "Level 7: 40 moves / 240 min"
13294 </source>
13295 <dest>
13296 *: "Level 7: 40 步 / 240 分钟"
13297 </dest>
13298 <voice>
13299 *: "Level 7, 40 æ­¥ æ¯240 分钟"
13300 </voice>
13301</phrase>
13302<phrase>
13303 id: LANG_CHESSBOX_LEVEL_8
13304 desc: in the chessbox game level selection
13305 user: core
13306 <source>
13307 *: "Level 8: 1 move / 15 min"
13308 </source>
13309 <dest>
13310 *: "Level 8: 1 步 / 15 分钟"
13311 </dest>
13312 <voice>
13313 *: "Level 8, 1 æ­¥ æ¯15 分钟"
13314 </voice>
13315</phrase>
13316<phrase>
13317 id: LANG_CHESSBOX_LEVEL_9
13318 desc: in the chessbox game level selection
13319 user: core
13320 <source>
13321 *: "Level 9: 1 move / 60 min"
13322 </source>
13323 <dest>
13324 *: "Level 9: 1 步 / 60 分钟"
13325 </dest>
13326 <voice>
13327 *: "Level 9, 1 æ­¥ æ¯60 分钟"
13328 </voice>
13329</phrase>
13330<phrase>
13331 id: LANG_CHESSBOX_LEVEL_10
13332 desc: in the chessbox game level selection
13333 user: core
13334 <source>
13335 *: "Level 10: 1 move / 600 min"
13336 </source>
13337 <dest>
13338 *: "Level 10: 1 步 / 600 分钟"
13339 </dest>
13340 <voice>
13341 *: "Level 10, 1 æ­¥ æ¯ 600 分钟"
13342 </voice>
13343</phrase>
13344<phrase>
13345 id: LANG_CHESSBOX_PGN_PARSE_ERROR
13346 desc: in the chessbox game viewer
13347 user: core
13348 <source>
13349 *: "Error parsing game !"
13350 </source>
13351 <dest>
13352 *: "解æžæ¸¸æˆæ—¶å‡ºé”™ï¼"
13353 </dest>
13354 <voice>
13355 *: "解æžæ¸¸æˆæ—¶å‡ºé”™ï¼"
13356 </voice>
13357</phrase>
13358<phrase>
13359 id: LANG_CHESSBOX_NO_GAMES
13360 desc: in the chessbox game viewer
13361 user: core
13362 <source>
13363 *: "No games found !"
13364 </source>
13365 <dest>
13366 *: "未找到游æˆï¼"
13367 </dest>
13368 <voice>
13369 *: "未找到游æˆï¼"
13370 </voice>
13371</phrase>
13372<phrase>
13373 id: LANG_CHESSBOX_GAME_BEGINNING
13374 desc: in the chessbox game viewer
13375 user: core
13376 <source>
13377 *: "At the beginning of the game"
13378 </source>
13379 <dest>
13380 *: "游æˆåˆšå¼€å§‹"
13381 </dest>
13382 <voice>
13383 *: "游æˆåˆšå¼€å§‹"
13384 </voice>
13385</phrase>
13386<phrase>
13387 id: LANG_CHESSBOX_GAME_END
13388 desc: in the chessbox game viewer
13389 user: core
13390 <source>
13391 *: "At the end of the game"
13392 </source>
13393 <dest>
13394 *: "游æˆç»“æŸæ—¶"
13395 </dest>
13396 <voice>
13397 *: "游æˆç»“æŸæ—¶"
13398 </voice>
13399</phrase>
13400<phrase>
13401 id: VOICE_PLAYER
13402 desc: spoken only, for announcing player's id
13403 user: core
13404 <source>
13405 *: ""
13406 </source>
13407 <dest>
13408 *: ""
13409 </dest>
13410 <voice>
13411 *: "玩家"
13412 </voice>
13413</phrase>
13414<phrase>
13415 id: VOICE_GNUCHESS
13416 desc: spoken only, for announcing player's id
13417 user: core
13418 <source>
13419 *: ""
13420 </source>
13421 <dest>
13422 *: ""
13423 </dest>
13424 <voice>
13425 *: "GNU 象棋"
13426 </voice>
13427</phrase>
13428<phrase>
13429 id: VOICE_MARKED
13430 desc: spoken only, for announcing chess piece marking
13431 user: core
13432 <source>
13433 *: ""
13434 </source>
13435 <dest>
13436 *: ""
13437 </dest>
13438 <voice>
13439 *: "已标记"
13440 </voice>
13441</phrase>
13442<phrase>
13443 id: VOICE_UNMARKED
13444 desc: spoken only, for announcing chess piece unmarking
13445 user: core
13446 <source>
13447 *: ""
13448 </source>
13449 <dest>
13450 *: ""
13451 </dest>
13452 <voice>
13453 *: "å–消标记"
13454 </voice>
13455</phrase>
13456<phrase>
13457 id: VOICE_WHITE
13458 desc: spoken only, for announcing chess piece color
13459 user: core
13460 <source>
13461 *: ""
13462 </source>
13463 <dest>
13464 *: ""
13465 </dest>
13466 <voice>
13467 *: "白"
13468 </voice>
13469</phrase>
13470<phrase>
13471 id: VOICE_BLACK
13472 desc: spoken only, for announcing chess piece color
13473 user: core
13474 <source>
13475 *: ""
13476 </source>
13477 <dest>
13478 *: ""
13479 </dest>
13480 <voice>
13481 *: "黑"
13482 </voice>
13483</phrase>
13484<phrase>
13485 id: VOICE_CHESSBOX_CHECK
13486 desc: spoken only, for announcing chess moves
13487 user: core
13488 <source>
13489 *: ""
13490 </source>
13491 <dest>
13492 *: ""
13493 </dest>
13494 <voice>
13495 *: "将军ï¼"
13496 </voice>
13497</phrase>
13498<phrase>
13499 id: VOICE_CHESSBOX_CAPTURES
13500 desc: spoken only, for announcing chess moves
13501 user: core
13502 <source>
13503 *: ""
13504 </source>
13505 <dest>
13506 *: ""
13507 </dest>
13508 <voice>
13509 *: "åƒ"
13510 </voice>
13511</phrase>
13512<phrase>
13513 id: VOICE_CHESSBOX_CASTLE
13514 desc: spoken only, for announcing chess moves
13515 user: core
13516 <source>
13517 *: ""
13518 </source>
13519 <dest>
13520 *: ""
13521 </dest>
13522 <voice>
13523 *: "城堡"
13524 </voice>
13525</phrase>
13526<phrase>
13527 id: VOICE_CHESSBOX_KINGSIDE
13528 desc: spoken only, for announcing chess moves
13529 user: core
13530 <source>
13531 *: ""
13532 </source>
13533 <dest>
13534 *: ""
13535 </dest>
13536 <voice>
13537 *: "王翼"
13538 </voice>
13539</phrase>
13540<phrase>
13541 id: VOICE_CHESSBOX_QUEENSIDE
13542 desc: spoken only, for announcing chess moves
13543 user: core
13544 <source>
13545 *: ""
13546 </source>
13547 <dest>
13548 *: ""
13549 </dest>
13550 <voice>
13551 *: "åŽç¿¼"
13552 </voice>
13553</phrase>
13554<phrase>
13555 id: VOICE_PAWN
13556 desc: spoken only, for announcing chess piece names
13557 user: core
13558 <source>
13559 *: ""
13560 </source>
13561 <dest>
13562 *: ""
13563 </dest>
13564 <voice>
13565 *: "å…µ"
13566 </voice>
13567</phrase>
13568<phrase>
13569 id: VOICE_KNIGHT
13570 desc: spoken only, for announcing chess piece names
13571 user: core
13572 <source>
13573 *: ""
13574 </source>
13575 <dest>
13576 *: ""
13577 </dest>
13578 <voice>
13579 *: "马"
13580 </voice>
13581</phrase>
13582<phrase>
13583 id: VOICE_BISHOP
13584 desc: spoken only, for announcing chess piece names
13585 user: core
13586 <source>
13587 *: ""
13588 </source>
13589 <dest>
13590 *: ""
13591 </dest>
13592 <voice>
13593 *: "象"
13594 </voice>
13595</phrase>
13596<phrase>
13597 id: VOICE_ROOK
13598 desc: spoken only, for announcing chess piece names
13599 user: core
13600 <source>
13601 *: ""
13602 </source>
13603 <dest>
13604 *: ""
13605 </dest>
13606 <voice>
13607 *: "å±…"
13608 </voice>
13609</phrase>
13610<phrase>
13611 id: VOICE_QUEEN
13612 desc: spoken only, for announcing chess piece names
13613 user: core
13614 <source>
13615 *: ""
13616 </source>
13617 <dest>
13618 *: ""
13619 </dest>
13620 <voice>
13621 *: "åŽ"
13622 </voice>
13623</phrase>
13624<phrase>
13625 id: VOICE_KING
13626 desc: spoken only, for announcing chess piece names
13627 user: core
13628 <source>
13629 *: ""
13630 </source>
13631 <dest>
13632 *: ""
13633 </dest>
13634 <voice>
13635 *: "王"
13636 </voice>
13637</phrase>
13638<phrase>
13639 id: LANG_CHESSBOX_GAMES
13640 desc: in chessbox
13641 user: core
13642 <source>
13643 *: "Games"
13644 </source>
13645 <dest>
13646 *: "游æˆ"
13647 </dest>
13648 <voice>
13649 *: ""
13650 </voice>
13651</phrase>
13652<phrase>
13653 id: LANG_CHESSBOX_SAVING_POSITION
13654 desc: in chessbox
13655 user: core
13656 <source>
13657 *: "Saving position"
13658 </source>
13659 <dest>
13660 *: "ä¿å­˜ä½ç½®"
13661 </dest>
13662 <voice>
13663 *: "ä¿å­˜ä½ç½®"
13664 </voice>
13665</phrase>
13666<phrase>
13667 id: LANG_CHESSBOX_LOADING_POSITION
13668 desc: in chessbox
13669 user: core
13670 <source>
13671 *: "Loading position"
13672 </source>
13673 <dest>
13674 *: "加载ä½ç½®"
13675 </dest>
13676 <voice>
13677 *: "加载ä½ç½®"
13678 </voice>
13679</phrase>
13680<phrase>
13681 id: LANG_CHESSBOX_THINKING
13682 desc: in chessbox
13683 user: core
13684 <source>
13685 *: "Thinking..."
13686 </source>
13687 <dest>
13688 *: "æ€è€ƒä¸­â€¦â€¦"
13689 </dest>
13690 <voice>
13691 *: "æ€è€ƒä¸­"
13692 </voice>
13693</phrase>
13694<phrase>
13695 id: VOICE_BATTERY_BENCH_IS_ALREADY_RUNNING
13696 desc: Spoken if battery bench is already running
13697 user: core
13698 <source>
13699 *: ""
13700 </source>
13701 <dest>
13702 *: ""
13703 </dest>
13704 <voice>
13705 *: "电池检测已在è¿è¡Œä¸­"
13706 </voice>
13707</phrase>
13708<phrase>
13709 id: VOICE_BAT_BENCH_KEYS
13710 desc: Battery bench start up message
13711 user: core
13712 <source>
13713 *: ""
13714 </source>
13715 <dest>
13716 *: ""
13717 </dest>
13718 <voice>
13719 *: "请按播放键è¿è¡Œç”µæ± æ£€æµ‹æˆ–者按åœæ­¢é”®å–消"
13720 </voice>
13721</phrase>
13722<phrase>
13723 id: LANG_CANNOT_RESTART_PLAYBACK
13724 desc: cannot restart playback splash in imageviewer
13725 user: core
13726 <source>
13727 *: "Cannot restart playback"
13728 </source>
13729 <dest>
13730 *: "无法æ¢å¤æ’­æ”¾"
13731 </dest>
13732 <voice>
13733 *: "无法æ¢å¤æ’­æ”¾"
13734 </voice>
13735</phrase>
13736<phrase>
13737 id: LANG_ORDERED
13738 desc: in the imageviewer settings menu
13739 user: core
13740 <source>
13741 *: "Ordered"
13742 </source>
13743 <dest>
13744 *: "已排åº"
13745 </dest>
13746 <voice>
13747 *: "已排åº"
13748 </voice>
13749</phrase>
13750<phrase>
13751 id: LANG_DIFFUSION
13752 desc: in the imageviewer settings menu
13753 user: core
13754 <source>
13755 *: "Diffusion"
13756 </source>
13757 <dest>
13758 *: "弥散"
13759 </dest>
13760 <voice>
13761 *: "弥散"
13762 </voice>
13763</phrase>
13764<phrase>
13765 id: LANG_GRAYSCALE
13766 desc: in the imageviewer settings menu
13767 user: core
13768 <source>
13769 *: "Grayscale"
13770 </source>
13771 <dest>
13772 *: "ç°åº¦"
13773 </dest>
13774 <voice>
13775 *: "æ¢å¤"
13776 </voice>
13777</phrase>
13778<phrase>
13779 id: LANG_SLIDESHOW_MODE
13780 desc: in the imageviewer settings menu
13781 user: core
13782 <source>
13783 *: "Toggle Slideshow Mode"
13784 </source>
13785 <dest>
13786 *: "切æ¢å¹»ç¯ç‰‡æ”¾æ˜ æ¨¡å¼"
13787 </dest>
13788 <voice>
13789 *: "切æ¢å¹»ç¯ç‰‡æ”¾æ˜ æ¨¡å¼"
13790 </voice>
13791</phrase>
13792<phrase>
13793 id: LANG_SLIDESHOW_TIME
13794 desc: in the imageviewer settings menu
13795 user: core
13796 <source>
13797 *: "Slideshow Time"
13798 </source>
13799 <dest>
13800 *: "å¹»ç¯ç‰‡æ”¾æ˜ æ—¶é—´"
13801 </dest>
13802 <voice>
13803 *: "å¹»ç¯ç‰‡æ”¾æ˜ æ—¶é—´"
13804 </voice>
13805</phrase>
13806<phrase>
13807 id: LANG_RETURN
13808 desc: in various plugin menus
13809 user: core
13810 <source>
13811 *: "Return"
13812 </source>
13813 <dest>
13814 *: "返回"
13815 </dest>
13816 <voice>
13817 *: "返回"
13818 </voice>
13819</phrase>
13820<phrase>
13821 id: LANG_REC_DIR
13822 desc: used in the info screen to show a recording dir
13823 user: core
13824 <source>
13825 *: none
13826 recording: "Recording Directory"
13827 </source>
13828 <dest>
13829 *: none
13830 recording: "录制目录"
13831 </dest>
13832 <voice>
13833 *: none
13834 recording: "录制目录"
13835 </voice>
13836</phrase>
13837<phrase>
13838 id: LANG_CHESSBOX_MENU_VIEW_GAMES
13839 desc: in the chessbox menu
13840 user: core
13841 <source>
13842 *: "View Played Games"
13843 </source>
13844 <dest>
13845 *: "查看已游玩的游æˆ"
13846 </dest>
13847 <voice>
13848 *: "查看已游玩的游æˆ"
13849 </voice>
13850</phrase>
13851<phrase>
13852 id: LANG_MENU_AUDIO_OPTIONS
13853 desc: in mpegplayer menus
13854 user: core
13855 <source>
13856 *: "Audio Options"
13857 lowmem: none
13858 </source>
13859 <dest>
13860 *: "音频选项"
13861 lowmem: none
13862 </dest>
13863 <voice>
13864 *: "音频选项"
13865 lowmem: none
13866 </voice>
13867</phrase>
13868<phrase>
13869 id: LANG_MENU_RESUME_OPTIONS
13870 desc: in mpegplayer menus
13871 user: core
13872 <source>
13873 *: "Resume Options"
13874 lowmem: none
13875 </source>
13876 <dest>
13877 *: "æ¢å¤é€‰é¡¹"
13878 lowmem: none
13879 </dest>
13880 <voice>
13881 *: "æ¢å¤é€‰é¡¹"
13882 lowmem: none
13883 </voice>
13884</phrase>
13885<phrase>
13886 id: LANG_MENU_PLAY_MODE
13887 desc: in mpegplayer menus
13888 user: core
13889 <source>
13890 *: "Play Mode"
13891 lowmem: none
13892 </source>
13893 <dest>
13894 *: "播放模å¼"
13895 lowmem: none
13896 </dest>
13897 <voice>
13898 *: "播放模å¼"
13899 lowmem: none
13900 </voice>
13901</phrase>
13902<phrase>
13903 id: LANG_SINGLE
13904 desc: in mpegplayer menus
13905 user: core
13906 <source>
13907 *: "Single"
13908 lowmem: none
13909 </source>
13910 <dest>
13911 *: "å•æ›²æ’­æ”¾"
13912 lowmem: none
13913 </dest>
13914 <voice>
13915 *: "å•æ›²æ’­æ”¾"
13916 lowmem: none
13917 </voice>
13918</phrase>
13919<phrase>
13920 id: LANG_USE_SOUND_SETTING
13921 desc: in mpegplayer menus
13922 user: core
13923 <source>
13924 *: "Use sound setting"
13925 lowmem: none
13926 </source>
13927 <dest>
13928 *: "使用声音设置"
13929 lowmem: none
13930 </dest>
13931 <voice>
13932 *: "使用声音设置"
13933 lowmem: none
13934 </voice>
13935</phrase>
13936<phrase>
13937 id: LANG_RESTART_PLAYBACK
13938 desc: in the mpegplayer settings menu
13939 user: core
13940 <source>
13941 *: "Play from beginning"
13942 lowmem: none
13943 </source>
13944 <dest>
13945 *: "从头播放"
13946 lowmem: none
13947 </dest>
13948 <voice>
13949 *: "从头播放"
13950 lowmem: none
13951 </voice>
13952</phrase>
13953<phrase>
13954 id: LANG_SET_RESUME_TIME
13955 desc: in the mpegplayer settings menu
13956 user: core
13957 <source>
13958 *: "Set resume time (min)"
13959 lowmem: none
13960 </source>
13961 <dest>
13962 *: "设置æ¢å¤æ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰"
13963 lowmem: none
13964 </dest>
13965 <voice>
13966 *: "设置æ¢å¤æ—¶é—´"
13967 lowmem: none
13968 </voice>
13969</phrase>
13970<phrase>
13971 id: LANG_DISPLAY_FPS
13972 desc: in the mpegplayer and pictureflow settings menus
13973 user: core
13974 <source>
13975 *: "Display FPS"
13976 </source>
13977 <dest>
13978 *: "显示帧率(FPS)"
13979 </dest>
13980 <voice>
13981 *: "显示帧率"
13982 </voice>
13983</phrase>
13984<phrase>
13985 id: LANG_LIMIT_FPS
13986 desc: in the mpegplayer settings menu
13987 user: core
13988 <source>
13989 *: "Limit FPS"
13990 lowmem: none
13991 </source>
13992 <dest>
13993 *: "é™åˆ¶å¸§çŽ‡"
13994 lowmem: none
13995 </dest>
13996 <voice>
13997 *: "é™åˆ¶å¸§çŽ‡"
13998 lowmem: none
13999 </voice>
14000</phrase>
14001<phrase>
14002 id: LANG_SKIP_FRAMES
14003 desc: in the mpegplayer settings menu
14004 user: core
14005 <source>
14006 *: "Skip frames"
14007 lowmem: none
14008 </source>
14009 <dest>
14010 *: "跳帧"
14011 lowmem: none
14012 </dest>
14013 <voice>
14014 *: "跳帧"
14015 lowmem: none
14016 </voice>
14017</phrase>
14018<phrase>
14019 id: LANG_BACKLIGHT_BRIGHTNESS
14020 desc: in the mpegplayer settings menu
14021 user: core
14022 <source>
14023 *: "Backlight brightness"
14024 lowmem: none
14025 </source>
14026 <dest>
14027 *: "背光亮度"
14028 lowmem: none
14029 </dest>
14030 <voice>
14031 *: "背光亮度"
14032 lowmem: none
14033 </voice>
14034</phrase>
14035<phrase>
14036 id: LANG_USE_COMMON_SETTING
14037 desc: in the mpegplayer settings menu
14038 user: core
14039 <source>
14040 *: "Use common setting"
14041 lowmem: none
14042 </source>
14043 <dest>
14044 *: "使用通常设定"
14045 lowmem: none
14046 </dest>
14047 <voice>
14048 *: "使用通常设定"
14049 lowmem: none
14050 </voice>
14051</phrase>
14052<phrase>
14053 id: LANG_TONE_CONTROLS
14054 desc: in the mpegplayer settings menu
14055 user: core
14056 <source>
14057 *: "Tone controls"
14058 lowmem: none
14059 </source>
14060 <dest>
14061 *: "音调控制"
14062 lowmem: none
14063 </dest>
14064 <voice>
14065 *: "音调控制"
14066 lowmem: none
14067 </voice>
14068</phrase>
14069<phrase>
14070 id: LANG_FORCE_START_MENU
14071 desc: in mpegplayer menus
14072 user: core
14073 <source>
14074 *: "Start menu"
14075 lowmem: none
14076 </source>
14077 <dest>
14078 *: "显示èœå•"
14079 lowmem: none
14080 </dest>
14081 <voice>
14082 *: "显示èœå•"
14083 lowmem: none
14084 </voice>
14085</phrase>
14086<phrase>
14087 id: LANG_CONDITIONAL_START_MENU
14088 desc: in mpegplayer menus
14089 user: core
14090 <source>
14091 *: "Start menu if not completed"
14092 lowmem: none
14093 </source>
14094 <dest>
14095 *: "显示èœå•ï¼ˆå¦‚果未播完)"
14096 lowmem: none
14097 </dest>
14098 <voice>
14099 *: "未播完时显示èœå•"
14100 lowmem: none
14101 </voice>
14102</phrase>
14103<phrase>
14104 id: LANG_AUTO_RESUME
14105 desc: in mpegplayer menus
14106 user: core
14107 <source>
14108 *: "Resume automatically"
14109 lowmem: none
14110 </source>
14111 <dest>
14112 *: "自动æ¢å¤æ’­æ”¾"
14113 lowmem: none
14114 </dest>
14115 <voice>
14116 *: "自动æ¢å¤æ’­æ”¾"
14117 lowmem: none
14118 </voice>
14119</phrase>
14120<phrase>
14121 id: LANG_CLEAR_ALL_RESUMES
14122 desc: in the mpegplayer settings menu
14123 user: core
14124 <source>
14125 *: "Clear all resumes"
14126 lowmem: none
14127 </source>
14128 <dest>
14129 *: "清除全部æ¢å¤ç‚¹"
14130 lowmem: none
14131 </dest>
14132 <voice>
14133 *: "清除全部æ¢å¤ç‚¹"
14134 lowmem: none
14135 </voice>
14136</phrase>
14137<phrase>
14138 id: LANG_UNAVAILABLE
14139 desc: in mpegplayer settings
14140 user: core
14141 <source>
14142 *: "Unavailable"
14143 lowmem: none
14144 </source>
14145 <dest>
14146 *: "ä¸å¯ç”¨"
14147 lowmem: none
14148 </dest>
14149 <voice>
14150 *: "ä¸å¯ç”¨"
14151 lowmem: none
14152 </voice>
14153</phrase>
14154<phrase>
14155 id: LANG_TOGGLE_ITEM
14156 desc: in main_menu_config
14157 user: core
14158 <source>
14159 *: "Toggle Item"
14160 </source>
14161 <dest>
14162 *: "切æ¢é¡¹ç›®"
14163 </dest>
14164 <voice>
14165 *: "切æ¢é¡¹ç›®"
14166 </voice>
14167</phrase>
14168<phrase>
14169 id: LANG_MOVE_ITEM_UP
14170 desc: in main_menu_config
14171 user: core
14172 <source>
14173 *: "Move Item Up"
14174 </source>
14175 <dest>
14176 *: "上移项目"
14177 </dest>
14178 <voice>
14179 *: "上移项目"
14180 </voice>
14181</phrase>
14182<phrase>
14183 id: LANG_MOVE_ITEM_DOWN
14184 desc: in main_menu_config
14185 user: core
14186 <source>
14187 *: "Move Item Down"
14188 </source>
14189 <dest>
14190 *: "下移项目"
14191 </dest>
14192 <voice>
14193 *: "下移项目"
14194 </voice>
14195</phrase>
14196<phrase>
14197 id: LANG_LOAD_DEFAULT_CONFIGURATION
14198 desc: in main_menu_config
14199 user: core
14200 <source>
14201 *: "Load Default Configuration"
14202 </source>
14203 <dest>
14204 *: "加载默认设定"
14205 </dest>
14206 <voice>
14207 *: "加载默认设定"
14208 </voice>
14209</phrase>
14210<phrase>
14211 id: LANG_SAVE_EXIT
14212 desc: in main_menu_config
14213 user: core
14214 <source>
14215 *: "Save and Exit"
14216 </source>
14217 <dest>
14218 *: "ä¿å­˜å¹¶é€€å‡º"
14219 </dest>
14220 <voice>
14221 *: "ä¿å­˜å¹¶é€€å‡º"
14222 </voice>
14223</phrase>
14224<phrase>
14225 id: LANG_MAIN_MENU_ORDER
14226 desc: main_menu_config plugin title
14227 user: core
14228 <source>
14229 *: "Rockbox Main Menu Order"
14230 </source>
14231 <dest>
14232 *: "Rockbox主å±å¹•èœå•å®šåˆ¶"
14233 </dest>
14234 <voice>
14235 *: ""
14236 </voice>
14237</phrase>
14238<phrase>
14239 id: LANG_PROPERTIES_PATH
14240 desc: in properties plugin
14241 user: core
14242 <source>
14243 *: "[Path]"
14244 </source>
14245 <dest>
14246 *: "[路径]"
14247 </dest>
14248 <voice>
14249 *: "路径"
14250 </voice>
14251</phrase>
14252<phrase>
14253 id: LANG_PROPERTIES_FILENAME
14254 desc: in properties plugin
14255 user: core
14256 <source>
14257 *: "[Filename]"
14258 </source>
14259 <dest>
14260 *: "[文件å]"
14261 </dest>
14262 <voice>
14263 *: "文件å"
14264 </voice>
14265</phrase>
14266<phrase>
14267 id: LANG_PROPERTIES_SIZE
14268 desc: in properties plugin
14269 user: core
14270 <source>
14271 *: "[Size]"
14272 </source>
14273 <dest>
14274 *: "[大å°]"
14275 </dest>
14276 <voice>
14277 *: "大å°"
14278 </voice>
14279</phrase>
14280<phrase>
14281 id: LANG_PROPERTIES_DATE
14282 desc: in properties plugin
14283 user: core
14284 <source>
14285 *: "[Date]"
14286 </source>
14287 <dest>
14288 *: "[日期]"
14289 </dest>
14290 <voice>
14291 *: "日期"
14292 </voice>
14293</phrase>
14294<phrase>
14295 id: LANG_PROPERTIES_TIME
14296 desc: in properties plugin
14297 user: core
14298 <source>
14299 *: "[Time]"
14300 </source>
14301 <dest>
14302 *: "[时间]"
14303 </dest>
14304 <voice>
14305 *: "时间"
14306 </voice>
14307</phrase>
14308<phrase>
14309 id: LANG_PROPERTIES_SUBDIRS
14310 desc: in properties plugin
14311 user: core
14312 <source>
14313 *: "[Subdirs]"
14314 </source>
14315 <dest>
14316 *: "[å­ç›®å½•æ•°]"
14317 </dest>
14318 <voice>
14319 *: "å­ç›®å½•æ•°"
14320 </voice>
14321</phrase>
14322<phrase>
14323 id: LANG_PROPERTIES_FILES
14324 desc: in properties plugin
14325 user: core
14326 <source>
14327 *: "[Files]"
14328 </source>
14329 <dest>
14330 *: "[文件数]"
14331 </dest>
14332 <voice>
14333 *: "文件数"
14334 </voice>
14335</phrase>
14336<phrase>
14337 id: LANG_PROPERTIES_DIRECTORY_PROPERTIES
14338 desc: in properties plugin
14339 user: core
14340 <source>
14341 *: "Directory properties"
14342 </source>
14343 <dest>
14344 *: "目录属性"
14345 </dest>
14346 <voice>
14347 *: ""
14348 </voice>
14349</phrase>
14350<phrase>
14351 id: LANG_PROPERTIES_FILE_PROPERTIES
14352 desc: in properties plugin
14353 user: core
14354 <source>
14355 *: "File properties"
14356 </source>
14357 <dest>
14358 *: "文件属性"
14359 </dest>
14360 <voice>
14361 *: ""
14362 </voice>
14363</phrase>
14364<phrase>
14365 id: LANG_PROPERTIES_FAIL
14366 desc: in properties plugin
14367 user: core
14368 <source>
14369 *: "Failed to gather information"
14370 </source>
14371 <dest>
14372 *: "收集数æ®å¤±è´¥"
14373 </dest>
14374 <voice>
14375 *: "收集数æ®å¤±è´¥"
14376 </voice>
14377</phrase>
14378<phrase>
14379 id: LANG_SWAP_CHANNELS
14380 desc: in sound_settings
14381 user: core
14382 <source>
14383 *: "Swap Channels"
14384 </source>
14385 <dest>
14386 *: "交æ¢é¢‘é“"
14387 </dest>
14388 <voice>
14389 *: "交æ¢é¢‘é“"
14390 </voice>
14391</phrase>
14392<phrase>
14393 id: LANG_PANNING_SEPARATION
14394 desc: in mikmod settings menu
14395 user: core
14396 <source>
14397 *: "Panning Separation"
14398 lowmem: none
14399 </source>
14400 <dest>
14401 *: "平移分离"
14402 lowmem: none
14403 </dest>
14404 <voice>
14405 *: "平移分离"
14406 lowmem: none
14407 </voice>
14408</phrase>
14409<phrase>
14410 id: LANG_REVERBERATION
14411 desc: in mikmod settings menu
14412 user: core
14413 <source>
14414 *: "Reverberation"
14415 lowmem: none
14416 </source>
14417 <dest>
14418 *: "æ··å“"
14419 lowmem: none
14420 </dest>
14421 <voice>
14422 *: "æ··å“"
14423 lowmem: none
14424 </voice>
14425</phrase>
14426<phrase>
14427 id: LANG_INTERPOLATION
14428 desc: in mikmod settings menu
14429 user: core
14430 <source>
14431 *: "Interpolation"
14432 lowmem: none
14433 </source>
14434 <dest>
14435 *: "æ’值"
14436 lowmem: none
14437 </dest>
14438 <voice>
14439 *: "æ’值"
14440 lowmem: none
14441 </voice>
14442</phrase>
14443<phrase>
14444 id: LANG_MIKMOD_SURROUND
14445 desc: in mikmod settings menu
14446 user: core
14447 <source>
14448 *: "Surround"
14449 lowmem: none
14450 </source>
14451 <dest>
14452 *: "环绕"
14453 lowmem: none
14454 </dest>
14455 <voice>
14456 *: "环绕"
14457 lowmem: none
14458 </voice>
14459</phrase>
14460<phrase>
14461 id: LANG_MIKMOD_HQMIXER
14462 desc: in mikmod settings menu
14463 user: core
14464 <source>
14465 *: "HQ Mixer"
14466 lowmem: none
14467 </source>
14468 <dest>
14469 *: "HQæ··åˆå™¨"
14470 lowmem: none
14471 </dest>
14472 <voice>
14473 *: "高å“æ··åˆå™¨"
14474 lowmem: none
14475 </voice>
14476</phrase>
14477<phrase>
14478 id: LANG_MIKMOD_SAMPLERATE
14479 desc: in mikmod settings menu
14480 user: core
14481 <source>
14482 *: "Sample Rate"
14483 lowmem: none
14484 </source>
14485 <dest>
14486 *: "采样率"
14487 lowmem: none
14488 </dest>
14489 <voice>
14490 *: "采样率"
14491 lowmem: none
14492 </voice>
14493</phrase>
14494<phrase>
14495 id: LANG_CPU_BOOST
14496 desc: in mikmod settings menu
14497 user: core
14498 <source>
14499 *: "CPU Boost"
14500 lowmem: none
14501 </source>
14502 <dest>
14503 *: "CPU加速"
14504 lowmem: none
14505 </dest>
14506 <voice>
14507 *: "C P U加速"
14508 lowmem: none
14509 </voice>
14510</phrase>
14511<phrase>
14512 id: LANG_SPACING
14513 desc: in the pictureflow settings menu
14514 user: core
14515 <source>
14516 *: "Spacing"
14517 </source>
14518 <dest>
14519 *: "é—´è·"
14520 </dest>
14521 <voice>
14522 *: "é—´è·"
14523 </voice>
14524</phrase>
14525<phrase>
14526 id: LANG_CENTRE_MARGIN
14527 desc: in the pictureflow settings menu
14528 user: core
14529 <source>
14530 *: "Centre margin"
14531 </source>
14532 <dest>
14533 *: "中心边è·"
14534 </dest>
14535 <voice>
14536 *: "中心边è·"
14537 </voice>
14538</phrase>
14539<phrase>
14540 id: LANG_NUMBER_OF_SLIDES
14541 desc: in the pictureflow settings menu
14542 user: core
14543 <source>
14544 *: "Number of slides"
14545 </source>
14546 <dest>
14547 *: "å¹»ç¯ç‰‡æ•°é‡"
14548 </dest>
14549 <voice>
14550 *: "å¹»ç¯ç‰‡æ•°é‡"
14551 </voice>
14552</phrase>
14553<phrase>
14554 id: LANG_ZOOM
14555 desc: in the pictureflow settings menu
14556 user: core
14557 <source>
14558 *: "Zoom"
14559 </source>
14560 <dest>
14561 *: "缩放"
14562 </dest>
14563 <voice>
14564 *: "缩放"
14565 </voice>
14566</phrase>
14567<phrase>
14568 id: LANG_SHOW_ALBUM_TITLE
14569 desc: in the pictureflow settings menu
14570 user: core
14571 <source>
14572 *: "Show album title"
14573 </source>
14574 <dest>
14575 *: "显示专辑标题"
14576 </dest>
14577 <voice>
14578 *: "显示专辑标题"
14579 </voice>
14580</phrase>
14581<phrase>
14582 id: LANG_RESIZE_COVERS
14583 desc: in the pictureflow settings menu
14584 user: core
14585 <source>
14586 *: "Resize Covers"
14587 </source>
14588 <dest>
14589 *: "é‡è®¾å°é¢å¤§å°"
14590 </dest>
14591 <voice>
14592 *: "é‡è®¾å°é¢å¤§å°"
14593 </voice>
14594</phrase>
14595<phrase>
14596 id: LANG_REBUILD_CACHE
14597 desc: in the pictureflow settings menu
14598 user: core
14599 <source>
14600 *: "Rebuild cache"
14601 </source>
14602 <dest>
14603 *: "é‡å»ºç¼“å­˜"
14604 </dest>
14605 <voice>
14606 *: "é‡å»ºç¼“å­˜"
14607 </voice>
14608</phrase>
14609<phrase>
14610 id: LANG_WPS_INTEGRATION
14611 desc: in the pictureflow settings menu
14612 user: core
14613 <source>
14614 *: "WPS Integration"
14615 </source>
14616 <dest>
14617 *: "播放时å±å¹•é›†æˆ"
14618 </dest>
14619 <voice>
14620 *: "播放时å±å¹•é›†æˆ"
14621 </voice>
14622</phrase>
14623<phrase>
14624 id: LANG_GOTO_WPS
14625 desc: in the pictureflow main menu
14626 user: core
14627 <source>
14628 *: "Go to WPS"
14629 </source>
14630 <dest>
14631 *: "去 播放å±å¹•"
14632 </dest>
14633 <voice>
14634 *: "去 播放å±å¹•"
14635 </voice>
14636</phrase>
14637<phrase>
14638 id: LANG_HIDE_ALBUM_TITLE
14639 desc: in the pictureflow settings
14640 user: core
14641 <source>
14642 *: "Hide album title"
14643 </source>
14644 <dest>
14645 *: "éšè—专辑标题"
14646 </dest>
14647 <voice>
14648 *: "éšè—专辑标题"
14649 </voice>
14650</phrase>
14651<phrase>
14652 id: LANG_SHOW_AT_THE_BOTTOM
14653 desc: in the pictureflow settings
14654 user: core
14655 <source>
14656 *: "Show at the bottom"
14657 </source>
14658 <dest>
14659 *: "在底部显示"
14660 </dest>
14661 <voice>
14662 *: "在底部显示"
14663 </voice>
14664</phrase>
14665<phrase>
14666 id: LANG_SHOW_AT_THE_TOP
14667 desc: in the pictureflow settings
14668 user: core
14669 <source>
14670 *: "Show at the top"
14671 </source>
14672 <dest>
14673 *: "在顶部显示"
14674 </dest>
14675 <voice>
14676 *: "在顶部显示"
14677 </voice>
14678</phrase>
14679<phrase>
14680 id: LANG_DIRECT
14681 desc: in the pictureflow settings, also a volume adjustment mode
14682 user: core
14683 <source>
14684 *: "Direct"
14685 </source>
14686 <dest>
14687 *: "直接"
14688 </dest>
14689 <voice>
14690 *: "直接"
14691 </voice>
14692</phrase>
14693<phrase>
14694 id: LANG_VIA_TRACK_LIST
14695 desc: in the pictureflow settings
14696 user: core
14697 <source>
14698 *: "Via Track list"
14699 </source>
14700 <dest>
14701 *: "通过曲目列表"
14702 </dest>
14703 <voice>
14704 *: "通过曲目列表"
14705 </voice>
14706</phrase>
14707<phrase>
14708 id: LANG_ALWAYS_ON
14709 desc: in the pictureflow settings menu
14710 user: core
14711 <source>
14712 *: "Always On"
14713 </source>
14714 <dest>
14715 *: "始终开å¯"
14716 </dest>
14717 <voice>
14718 *: "始终开å¯"
14719 </voice>
14720</phrase>
14721<phrase>
14722 id: LANG_NO_ALBUMART_FOUND
14723 desc: in the pictureflow splash messages
14724 user: core
14725 <source>
14726 *: "No album art found"
14727 </source>
14728 <dest>
14729 *: "未找到专辑艺术"
14730 </dest>
14731 <voice>
14732 *: "未找到专辑艺术"
14733 </voice>
14734</phrase>
14735<phrase>
14736 id: LANG_CACHE_REBUILT_NEXT_RESTART
14737 desc: in the pictureflow splash messages
14738 user: core
14739 <source>
14740 *: "Cache will be rebuilt on next restart"
14741 </source>
14742 <dest>
14743 *: "下次å¯åŠ¨æ—¶é‡å»ºç¼“å­˜"
14744 </dest>
14745 <voice>
14746 *: "下次å¯åŠ¨æ—¶é‡å»ºç¼“å­˜"
14747 </voice>
14748</phrase>
14749<phrase>
14750 id: LANG_ERROR_WRITING_CONFIG
14751 desc: in the pictureflow splash messages
14752 user: core
14753 <source>
14754 *: "Error writing config"
14755 </source>
14756 <dest>
14757 *: "写入é…置时出错"
14758 </dest>
14759 <voice>
14760 *: "写入é…置时出错"
14761 </voice>
14762</phrase>
14763<phrase>
14764 id: LANG_NOT_A_VBR_FILE
14765 desc: in vbrfix plugin
14766 user: core
14767 <source>
14768 *: "Not a VBR file"
14769 </source>
14770 <dest>
14771 *: "ä¸æ˜¯VBR文件"
14772 </dest>
14773 <voice>
14774 *: "ä¸æ˜¯VBR文件"
14775 </voice>
14776</phrase>
14777<phrase>
14778 id: LANG_FILE_ERROR
14779 desc: in vbrfix plugin
14780 user: core
14781 <source>
14782 *: "File error: %d"
14783 </source>
14784 <dest>
14785 *: "文件错误: %d"
14786 </dest>
14787 <voice>
14788 *: "文件错误"
14789 </voice>
14790</phrase>
14791<phrase>
14792 id: LANG_UPDATE_CACHE
14793 desc: in pictureflow
14794 user: core
14795 <source>
14796 *: "Update cache"
14797 </source>
14798 <dest>
14799 *: "更新缓存"
14800 </dest>
14801 <voice>
14802 *: "更新缓存"
14803 </voice>
14804</phrase>
14805<phrase>
14806 id: LANG_HIDE_ALBUM_TITLE_NEW
14807 desc: in the pictureflow settings
14808 user: core
14809 <source>
14810 *: "Hide information"
14811 </source>
14812 <dest>
14813 *: "éšè—ä¿¡æ¯"
14814 </dest>
14815 <voice>
14816 *: "éšè—ä¿¡æ¯"
14817 </voice>
14818</phrase>
14819<phrase>
14820 id: LANG_SHOW_AT_THE_BOTTOM_NEW
14821 desc: in the pictureflow settings
14822 user: core
14823 <source>
14824 *: "Show album at the bottom"
14825 </source>
14826 <dest>
14827 *: "在底部显示专辑"
14828 </dest>
14829 <voice>
14830 *: "在底部显示专辑"
14831 </voice>
14832</phrase>
14833<phrase>
14834 id: LANG_SHOW_AT_THE_TOP_NEW
14835 desc: in the pictureflow settings
14836 user: core
14837 <source>
14838 *: "Show album at the top"
14839 </source>
14840 <dest>
14841 *: "在顶部显示专辑"
14842 </dest>
14843 <voice>
14844 *: "在顶部显示专辑"
14845 </voice>
14846</phrase>
14847<phrase>
14848 id: LANG_SHOW_ALL_AT_THE_TOP
14849 desc: in the pictureflow settings
14850 user: core
14851 <source>
14852 *: "Show album and artist at the top"
14853 </source>
14854 <dest>
14855 *: "在顶部显示专辑和艺术家"
14856 </dest>
14857 <voice>
14858 *: "在顶部显示专辑和艺术家"
14859 </voice>
14860</phrase>
14861<phrase>
14862 id: LANG_SHOW_ALL_AT_THE_BOTTOM
14863 desc: in the pictureflow settings
14864 user: core
14865 <source>
14866 *: "Show album and artist at the bottom"
14867 </source>
14868 <dest>
14869 *: "在底部显示专辑和艺术家"
14870 </dest>
14871 <voice>
14872 *: "在底部显示专辑和艺术家"
14873 </voice>
14874</phrase>
14875<phrase>
14876 id: LANG_ACTION_STD_CANCEL
14877 desc: standard press x to cancel string
14878 user: core
14879 <source>
14880 *: "Press LEFT to cancel."
14881 android,hifietma*,zenvision: "Press BACK to cancel."
14882 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
14883 ihifi760,ihifi960: "Double tap RETURN to cancel."
14884 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
14885 iriverh10,samsungyh*: "Double tap LEFT to cancel."
14886 mpiohd200: "Double tap REC to cancel."
14887 mpiohd300: "Double tap MENU to cancel."
14888 rx27generic: "Press VOLUME to cancel."
14889 sonynwza860: "Keymaps incomplete."
14890 touchscreen: "Press Middle Left to cancel."
14891 vibe500: "Press PREV to cancel."
14892 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
14893 </source>
14894 <dest>
14895 *: "请按LEFTé”®å–消。"
14896 android,hifietma*,zenvision: "请按BACKé”®å–消。"
14897 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWERé”®å–消。"
14898 ihifi760,ihifi960: "åŒå‡»RETURNé”®å–消。"
14899 ihifi770,ihifi770c,ihifi800: "请按HOMEé”®å–消。"
14900 iriverh10,samsungyh*: "åŒå‡»LEFTé”®å–消。"
14901 mpiohd200: "åŒå‡»RECé”®å–消。"
14902 mpiohd300: "åŒå‡»MENUé”®å–消。"
14903 rx27generic: "请按VOLUMEé”®å–消。"
14904 sonynwza860: "键盘映射ä¸å®Œæ•´ã€‚"
14905 touchscreen: "请按Middle Lefté”®å–消。"
14906 vibe500: "请按PREVé”®å–消。"
14907 xduoox20,xduoox3ii: "åŒå‡»HOMEé”®å–消。"
14908 xduoox3: "åŒå‡» HOMEé”®å–消。"
14909 </dest>
14910 <voice>
14911 *: "请按LEFTé”®å–消"
14912 android,hifietma*,zenvision: "请按BACKé”®å–消"
14913 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWERé”®å–消"
14914 ihifi760,ihifi960: "åŒå‡»RETURNé”®å–消"
14915 ihifi770,ihifi770c,ihifi800: "请按HOMEé”®å–消"
14916 iriverh10,samsungyh*: "åŒå‡»LEFTé”®å–消"
14917 mpiohd200: "åŒå‡»RECé”®å–消。"
14918 mpiohd300: "åŒå‡»MENUé”®å–消。"
14919 rx27generic: "请按VOLUMEé”®å–消。"
14920 touchscreen: "请按Middle Lefté”®å–消"
14921 vibe500: "请按PREVé”®å–消"
14922 xduoox20,xduoox3,xduoox3ii: "åŒå‡»HOMEé”®å–消"
14923 </voice>
14924</phrase>
14925<phrase>
14926 id: LANG_DATE
14927 desc: for constructing time and date announcements
14928 user: core
14929 <source>
14930 *: "Date"
14931 </source>
14932 <dest>
14933 *: "日期"
14934 </dest>
14935 <voice>
14936 *: "日期"
14937 </voice>
14938</phrase>
14939<phrase>
14940 id: LANG_CLEAR_ALL
14941 desc:
14942 user: core
14943 <source>
14944 *: "Clear all"
14945 </source>
14946 <dest>
14947 *: "全清除"
14948 </dest>
14949 <voice>
14950 *: "全清除"
14951 </voice>
14952</phrase>
14953<phrase>
14954 id: LANG_CANCEL_0
14955 desc: CANCEL.
14956 user: core
14957 <source>
14958 *: "Cancel"
14959 </source>
14960 <dest>
14961 *: "å–消"
14962 </dest>
14963 <voice>
14964 *: "å–消"
14965 </voice>
14966</phrase>
14967<phrase>
14968 id: LANG_SAVE
14969 desc:
14970 user: core
14971 <source>
14972 *: "Save"
14973 </source>
14974 <dest>
14975 *: "ä¿å­˜"
14976 </dest>
14977 <voice>
14978 *: "ä¿å­˜"
14979 </voice>
14980</phrase>
14981<phrase>
14982 id: LANG_TIMEOUT
14983 desc:
14984 user: core
14985 <source>
14986 *: "Timeout"
14987 </source>
14988 <dest>
14989 *: "超时"
14990 </dest>
14991 <voice>
14992 *: "超时"
14993 </voice>
14994</phrase>
14995<phrase>
14996 id: LANG_TRACK
14997 desc: used in track x of y constructs
14998 user: core
14999 <source>
15000 *: "Track"
15001 </source>
15002 <dest>
15003 *: "曲目"
15004 </dest>
15005 <voice>
15006 *: "曲目"
15007 </voice>
15008</phrase>
15009<phrase>
15010 id: LANG_ELAPSED
15011 desc: prefix for elapsed playtime announcement
15012 user: core
15013 <source>
15014 *: "Elapsed"
15015 </source>
15016 <dest>
15017 *: "过去了"
15018 </dest>
15019 <voice>
15020 *: "过去了"
15021 </voice>
15022</phrase>
15023<phrase>
15024 id: LANG_ANNOUNCEMENT_FMT
15025 desc: format for wps hotkey announcement
15026 user: core
15027 <source>
15028 *: none
15029 hotkey: "Announcement format"
15030 </source>
15031 <dest>
15032 *: none
15033 hotkey: "通知格å¼"
15034 </dest>
15035 <voice>
15036 *: none
15037 hotkey: "通知格å¼"
15038 </voice>
15039</phrase>
15040<phrase>
15041 id: LANG_REMAIN
15042 desc: for constructs such as number of tracks remaining etc
15043 user: core
15044 <source>
15045 *: none
15046 hotkey: "Remain"
15047 </source>
15048 <dest>
15049 *: none
15050 hotkey: "剩余"
15051 </dest>
15052 <voice>
15053 *: none
15054 hotkey: "剩余"
15055 </voice>
15056</phrase>
15057<phrase>
15058 id: LANG_GROUPING
15059 desc:
15060 user: core
15061 <source>
15062 *: none
15063 hotkey: "Grouping"
15064 </source>
15065 <dest>
15066 *: none
15067 hotkey: "组"
15068 </dest>
15069 <voice>
15070 *: none
15071 hotkey: "组"
15072 </voice>
15073</phrase>
15074<phrase>
15075 id: LANG_ANNOUNCE_ON
15076 desc:
15077 user: core
15078 <source>
15079 *: none
15080 hotkey: "Announce on"
15081 </source>
15082 <dest>
15083 *: none
15084 hotkey: "通知å¯ç”¨"
15085 </dest>
15086 <voice>
15087 *: none
15088 hotkey: "通知å¯ç”¨"
15089 </voice>
15090</phrase>
15091<phrase>
15092 id: LANG_TRACK_CHANGE
15093 desc:
15094 user: core
15095 <source>
15096 *: none
15097 hotkey: "Track change"
15098 </source>
15099 <dest>
15100 *: none
15101 hotkey: "曲目å˜æ›´"
15102 </dest>
15103 <voice>
15104 *: none
15105 hotkey: "曲目å˜æ›´"
15106 </voice>
15107</phrase>
15108<phrase>
15109 id: LANG_HOLD_FOR_SETTINGS
15110 desc:
15111 user: core
15112 <source>
15113 *: none
15114 hotkey: "Hold for settings"
15115 </source>
15116 <dest>
15117 *: none
15118 hotkey: "按HOLD键设置"
15119 </dest>
15120 <voice>
15121 *: none
15122 hotkey: "按HOLD键设置"
15123 </voice>
15124</phrase>
15125<phrase>
15126 id: LANG_OPEN_PLUGIN
15127 desc: onplay open plugin
15128 user: core
15129 <source>
15130 *: "Open Plugin"
15131 </source>
15132 <dest>
15133 *: "打开æ’件"
15134 </dest>
15135 <voice>
15136 *: "打开æ’件"
15137 </voice>
15138</phrase>
15139<phrase>
15140 id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
15141 desc: open plugin module
15142 user: core
15143 <source>
15144 *: "Not a plugin: %s"
15145 </source>
15146 <dest>
15147 *: "ä¸æ˜¯æ’件: %s"
15148 </dest>
15149 <voice>
15150 *: "ä¸æ˜¯æ’件"
15151 </voice>
15152</phrase>
15153<phrase>
15154 id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
15155 desc: open plugin module
15156 user: core
15157 <source>
15158 *: "Set Wps Context Plugin"
15159 </source>
15160 <dest>
15161 *: "设置播放å±å¹•ä¸Šä¸‹æ–‡æ’件"
15162 </dest>
15163 <voice>
15164 *: "设置播放å±å¹•ä¸Šä¸‹æ–‡æ’件"
15165 </voice>
15166</phrase>
15167<phrase>
15168 id: LANG_PARAMETER
15169 desc:
15170 user: core
15171 <source>
15172 *: "Parameter"
15173 </source>
15174 <dest>
15175 *: "å‚æ•°"
15176 </dest>
15177 <voice>
15178 *: "å‚æ•°"
15179 </voice>
15180</phrase>
15181<phrase>
15182 id: LANG_NAME
15183 desc:
15184 user: core
15185 <source>
15186 *: "Name"
15187 </source>
15188 <dest>
15189 *: "åå­—"
15190 </dest>
15191 <voice>
15192 *: "åå­—"
15193 </voice>
15194</phrase>
15195<phrase>
15196 id: LANG_ADD
15197 desc:
15198 user: core
15199 <source>
15200 *: "Add"
15201 </source>
15202 <dest>
15203 *: "添加"
15204 </dest>
15205 <voice>
15206 *: "添加"
15207 </voice>
15208</phrase>
15209<phrase>
15210 id: LANG_BACK
15211 desc:
15212 user: core
15213 <source>
15214 *: "Back"
15215 </source>
15216 <dest>
15217 *: "返回"
15218 </dest>
15219 <voice>
15220 *: "返回"
15221 </voice>
15222</phrase>
15223<phrase>
15224 id: LANG_EDIT
15225 desc:
15226 user: core
15227 <source>
15228 *: "Edit"
15229 </source>
15230 <dest>
15231 *: "编辑"
15232 </dest>
15233 <voice>
15234 *: "编辑"
15235 </voice>
15236</phrase>
15237<phrase>
15238 id: LANG_RUN
15239 desc:
15240 user: core
15241 <source>
15242 *: "Run"
15243 </source>
15244 <dest>
15245 *: "è¿è¡Œ"
15246 </dest>
15247 <voice>
15248 *: "è¿è¡Œ"
15249 </voice>
15250</phrase>
15251<phrase>
15252 id: LANG_EXPORT
15253 desc:
15254 user: core
15255 <source>
15256 *: "Export"
15257 </source>
15258 <dest>
15259 *: "导出"
15260 </dest>
15261 <voice>
15262 *: "导出"
15263 </voice>
15264</phrase>
15265<phrase>
15266 id: LANG_BROWSE
15267 desc:
15268 user: core
15269 <source>
15270 *: "Browse"
15271 </source>
15272 <dest>
15273 *: "æµè§ˆ"
15274 </dest>
15275 <voice>
15276 *: "æµè§ˆ"
15277 </voice>
15278</phrase>
15279<phrase>
15280 id: LANG_ENTER_USB_STORAGE_MODE_QUERY
15281 desc: upon plugging in USB
15282 user: core
15283 <source>
15284 *: "Enter USB mass storage mode?"
15285 </source>
15286 <dest>
15287 *: "进入USB大容é‡å­˜å‚¨æ¨¡å¼å—?"
15288 </dest>
15289 <voice>
15290 *: "是å¦è¿›å…¥USB大容é‡å­˜å‚¨æ¨¡å¼"
15291 </voice>
15292</phrase>
15293<phrase>
15294 id: LANG_QUEUE_MENU
15295 desc: in onplay menu
15296 user: core
15297 <source>
15298 *: "Queue..."
15299 </source>
15300 <dest>
15301 *: "队列..."
15302 </dest>
15303 <voice>
15304 *: "队列"
15305 </voice>
15306</phrase>
15307<phrase>
15308 id: LANG_SHOW_QUEUE_OPTIONS
15309 desc: in Current Playlist settings
15310 user: core
15311 <source>
15312 *: "Show Queue Options"
15313 </source>
15314 <dest>
15315 *: "显示队列选项"
15316 </dest>
15317 <voice>
15318 *: "显示队列选项"
15319 </voice>
15320</phrase>
15321<phrase>
15322 id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
15323 desc: in Current Playlist settings
15324 user: core
15325 <source>
15326 *: "Show Shuffled Adding Options"
15327 </source>
15328 <dest>
15329 *: "显示éšæœºæ·»åŠ é€‰é¡¹"
15330 </dest>
15331 <voice>
15332 *: "显示éšæœºæ·»åŠ é€‰é¡¹"
15333 </voice>
15334</phrase>
15335<phrase>
15336 id: LANG_IN_SUBMENU
15337 desc: in Settings
15338 user: core
15339 <source>
15340 *: "In Submenu"
15341 </source>
15342 <dest>
15343 *: "å­ç›®å½•ä¸­"
15344 </dest>
15345 <voice>
15346 *: "å­ç›®å½•ä¸­"
15347 </voice>
15348</phrase>
15349<phrase>
15350 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
15351 desc: disable all softlock notifications
15352 user: core
15353 <source>
15354 *: "Disable All Lock Notifications"
15355 </source>
15356 <dest>
15357 *: "ç¦ç”¨æ‰€æœ‰é”定通知"
15358 </dest>
15359 <voice>
15360 *: "ç¦ç”¨æ‰€æœ‰é”定通知"
15361 </voice>
15362</phrase>
15363<phrase>
15364 id: LANG_ACTION_VOLUME
15365 desc: exempt volume from softlock
15366 user: core
15367 <source>
15368 *: "Exempt Volume"
15369 </source>
15370 <dest>
15371 *: "调节音é‡æ—¶è±å…"
15372 </dest>
15373 <voice>
15374 *: "调节音é‡æ—¶è±å…"
15375 </voice>
15376</phrase>
15377<phrase>
15378 id: LANG_ACTION_ALWAYSAUTOLOCK
15379 desc: always prime autolock
15380 user: core
15381 <source>
15382 *: "Always Autolock"
15383 </source>
15384 <dest>
15385 *: "永远自动é”定"
15386 </dest>
15387 <voice>
15388 *: "永远自动é”定"
15389 </voice>
15390</phrase>
15391<phrase>
15392 id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
15393 desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
15394 user: core
15395 <source>
15396 *: ""
15397 </source>
15398 <dest>
15399 *: ""
15400 </dest>
15401 <voice>
15402 *: ""
15403 </voice>
15404</phrase>
15405<phrase>
15406 id: LANG_VOICED_DATE_FORMAT
15407 desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
15408 user: core
15409 <source>
15410 *: "dAY"
15411 </source>
15412 <dest>
15413 *: "Ymd"
15414 </dest>
15415 <voice>
15416 *: ""
15417 </voice>
15418</phrase>
15419<phrase>
15420 id: LANG_LIST_WRAPAROUND
15421 desc: in Settings
15422 user: core
15423 <source>
15424 *: "List Wraparound"
15425 </source>
15426 <dest>
15427 *: "列表环绕"
15428 </dest>
15429 <voice>
15430 *: "列表环绕"
15431 </voice>
15432</phrase>
15433<phrase>
15434 id: LANG_SHOW_SHUTDOWN_MESSAGE
15435 desc: in Settings
15436 user: core
15437 <source>
15438 *: "Show Shutdown Message"
15439 </source>
15440 <dest>
15441 *: "显示关机信æ¯"
15442 </dest>
15443 <voice>
15444 *: "显示关机信æ¯"
15445 </voice>
15446</phrase>
15447<phrase>
15448 id: LANG_LIST_ORDER
15449 desc: in Settings
15450 user: core
15451 <source>
15452 *: "List Order"
15453 </source>
15454 <dest>
15455 *: "列表顺åº"
15456 </dest>
15457 <voice>
15458 *: "列表顺åº"
15459 </voice>
15460</phrase>
15461<phrase>
15462 id: LANG_ASCENDING
15463 desc: in Settings
15464 user: core
15465 <source>
15466 *: "Ascending"
15467 </source>
15468 <dest>
15469 *: "上å‡"
15470 </dest>
15471 <voice>
15472 *: "上å‡"
15473 </voice>
15474</phrase>
15475<phrase>
15476 id: LANG_DESCENDING
15477 desc: in Settings
15478 user: core
15479 <source>
15480 *: "Descending"
15481 </source>
15482 <dest>
15483 *: "下é™"
15484 </dest>
15485 <voice>
15486 *: "下é™"
15487 </voice>
15488</phrase>
15489<phrase>
15490 id: LANG_ALBUM_ART
15491 desc: in Settings
15492 user: core
15493 <source>
15494 *: "Album Art"
15495 </source>
15496 <dest>
15497 *: "专辑å°é¢æ¥æº"
15498 </dest>
15499 <voice>
15500 *: "专辑å°é¢æ¥æº"
15501 </voice>
15502</phrase>
15503<phrase>
15504 id: LANG_PREFER_EMBEDDED
15505 desc: in Settings
15506 user: core
15507 <source>
15508 *: "Prefer Embedded"
15509 </source>
15510 <dest>
15511 *: "内置优先"
15512 </dest>
15513 <voice>
15514 *: "内置优先"
15515 </voice>
15516</phrase>
15517<phrase>
15518 id: LANG_PREFER_IMAGE_FILE
15519 desc: in Settings
15520 user: core
15521 <source>
15522 *: "Prefer Image File"
15523 </source>
15524 <dest>
15525 *: "图片文件优先"
15526 </dest>
15527 <voice>
15528 *: "图片文件优先"
15529 </voice>
15530</phrase>
15531<phrase>
15532 id: LANG_FM_SYNC_RDS_TIME
15533 desc: in radio screen and Settings
15534 user: core
15535 <source>
15536 *: none
15537 rds: "Sync RDS Time"
15538 </source>
15539 <dest>
15540 *: none
15541 rds: "åŒæ­¥RDS时间"
15542 </dest>
15543 <voice>
15544 *: none
15545 rds: "åŒæ­¥RDS时间"
15546 </voice>
15547</phrase>
15548<phrase>
15549 id: LANG_SORT_ALBUMS_BY
15550 desc: in Settings
15551 user: core
15552 <source>
15553 *: "Sort albums by"
15554 </source>
15555 <dest>
15556 *: "排åºä¸“辑通过…"
15557 </dest>
15558 <voice>
15559 *: "专辑排åºæ–¹å¼"
15560 </voice>
15561</phrase>
15562<phrase>
15563 id: LANG_ARTIST_PLUS_NAME
15564 desc: in Settings
15565 user: core
15566 <source>
15567 *: "Artist + Name"
15568 </source>
15569 <dest>
15570 *: "艺术家 + 曲å"
15571 </dest>
15572 <voice>
15573 *: "艺术家 + 曲å"
15574 </voice>
15575</phrase>
15576<phrase>
15577 id: LANG_ARTIST_PLUS_YEAR
15578 desc: in Settings
15579 user: core
15580 <source>
15581 *: "Artist + Year"
15582 </source>
15583 <dest>
15584 *: "艺术家 + 年份"
15585 </dest>
15586 <voice>
15587 *: "艺术家 + 年份"
15588 </voice>
15589</phrase>
15590<phrase>
15591 id: LANG_YEAR_SORT_ORDER
15592 desc: in Settings
15593 user: core
15594 <source>
15595 *: "Year sort order"
15596 </source>
15597 <dest>
15598 *: "年份顺åº"
15599 </dest>
15600 <voice>
15601 *: "年份顺åº"
15602 </voice>
15603</phrase>
15604<phrase>
15605 id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
15606 desc: in Settings
15607 user: core
15608 <source>
15609 *: "Show year in album title"
15610 </source>
15611 <dest>
15612 *: "在专辑标题中显示年份"
15613 </dest>
15614 <voice>
15615 *: "在专辑标题中显示年份"
15616 </voice>
15617</phrase>
15618<phrase>
15619 id: LANG_WAIT_FOR_CACHE
15620 desc: in Settings
15621 user: core
15622 <source>
15623 *: "Cache needs to finish updating first!"
15624 </source>
15625 <dest>
15626 *: "缓存需è¦å…ˆå®Œæˆæ›´æ–°ï¼"
15627 </dest>
15628 <voice>
15629 *: "缓存需è¦å…ˆå®Œæˆæ›´æ–°ï¼"
15630 </voice>
15631</phrase>
15632<phrase>
15633 id: LANG_TRACK_INFO
15634 desc: Track Info Title
15635 user: core
15636 <source>
15637 *: "Track Info"
15638 </source>
15639 <dest>
15640 *: "曲目信æ¯"
15641 </dest>
15642 <voice>
15643 *: "曲目信æ¯"
15644 </voice>
15645</phrase>
15646<phrase>
15647 id: LANG_PLAY
15648 desc: play selected file/directory, in playlist context menu
15649 user: core
15650 <source>
15651 *: "Play"
15652 </source>
15653 <dest>
15654 *: "播放"
15655 </dest>
15656 <voice>
15657 *: "播放"
15658 </voice>
15659</phrase>
15660<phrase>
15661 id: LANG_PLAY_SHUFFLED
15662 desc: play selected files in shuffled order, in playlist context menu
15663 user: core
15664 <source>
15665 *: "Play Shuffled"
15666 </source>
15667 <dest>
15668 *: "éšæœºæ’­æ”¾"
15669 </dest>
15670 <voice>
15671 *: "éšæœºæ’­æ”¾"
15672 </voice>
15673</phrase>
15674<phrase>
15675 id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
15676 desc: used in the playlist settings menu
15677 user: core
15678 <source>
15679 *: "Keep Current Track When Replacing Playlist"
15680 </source>
15681 <dest>
15682 *: "替æ¢æ’­æ”¾åˆ—表时ä¿æŒå½“å‰æ›²ç›®"
15683 </dest>
15684 <voice>
15685 *: "替æ¢æ’­æ”¾åˆ—表时ä¿æŒå½“å‰æ›²ç›®"
15686 </voice>
15687</phrase>
15688<phrase>
15689 id: LANG_CLEAR_SETTINGS_ON_HOLD
15690 desc: in the system sub menu
15691 user: core
15692 <source>
15693 *: none
15694 clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
15695 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
15696 </source>
15697 <dest>
15698 *: none
15699 clear_settings_on_hold,iriverh10: "å¯åŠ¨æ—¶æŒ‰ä½RESET按钮清除设置"
15700 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "å¯åŠ¨æ—¶æ‰“å¼€HOLD开关清除设置"
15701 </dest>
15702 <voice>
15703 *: none
15704 clear_settings_on_hold,iriverh10: "å¯åŠ¨æ—¶æŒ‰ä½RESET按钮清除设置"
15705 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "å¯åŠ¨æ—¶æ‰“å¼€HOLD开关清除设置"
15706 </voice>
15707</phrase>
15708<phrase>
15709 id: LANG_REWIND_ACROSS_TRACKS
15710 desc: in playback settings menu
15711 user: core
15712 <source>
15713 *: "Rewind Across Tracks"
15714 </source>
15715 <dest>
15716 *: "跨轨é“倒带"
15717 </dest>
15718 <voice>
15719 *: "跨轨é“倒带"
15720 </voice>
15721</phrase>
15722<phrase>
15723 id: LANG_SET_AS
15724 desc: used in the onplay menu
15725 user: core
15726 <source>
15727 *: "Set As..."
15728 </source>
15729 <dest>
15730 *: "设置为…"
15731 </dest>
15732 <voice>
15733 *: "设置为"
15734 </voice>
15735</phrase>
15736<phrase>
15737 id: LANG_PLAYLIST_DIR
15738 desc: used in the onplay menu
15739 user: core
15740 <source>
15741 *: "Playlist Directory"
15742 </source>
15743 <dest>
15744 *: "播放列表目录"
15745 </dest>
15746 <voice>
15747 *: "播放列表目录"
15748 </voice>
15749</phrase>
15750<phrase>
15751 id: LANG_START_DIR
15752 desc: used in the onplay menu
15753 user: core
15754 <source>
15755 *: "Start Directory"
15756 </source>
15757 <dest>
15758 *: "开始目录"
15759 </dest>
15760 <voice>
15761 *: "开始目录"
15762 </voice>
15763</phrase>
15764<phrase>
15765 id: LANG_RECORDING_DIR
15766 desc: used in the onplay menu
15767 user: core
15768 <source>
15769 *: none
15770 recording: "Recording Directory"
15771 </source>
15772 <dest>
15773 *: none
15774 recording: "录制目录"
15775 </dest>
15776 <voice>
15777 *: none
15778 recording: "录制目录"
15779 </voice>
15780</phrase>
15781<phrase>
15782 id: LANG_ADD_TO_PL
15783 desc: used in the onplay menu
15784 user: core
15785 <source>
15786 *: "Add to Playlist..."
15787 </source>
15788 <dest>
15789 *: "添加到播放列表…"
15790 </dest>
15791 <voice>
15792 *: "添加到播放列表…"
15793 </voice>
15794</phrase>
15795<phrase>
15796 id: LANG_ADD_TO_EXISTING_PL
15797 desc: used in the onplay menu
15798 user: core
15799 <source>
15800 *: "Add to Existing Playlist"
15801 </source>
15802 <dest>
15803 *: "添加到现存播放列表"
15804 </dest>
15805 <voice>
15806 *: "添加到现存播放列表"
15807 </voice>
15808</phrase>
15809<phrase>
15810 id: LANG_PLAYING_NEXT
15811 desc: used in the onplay menu
15812 user: core
15813 <source>
15814 *: "Playing Next..."
15815 </source>
15816 <dest>
15817 *: "下一首播放…"
15818 </dest>
15819 <voice>
15820 *: "下一首播放…"
15821 </voice>
15822</phrase>
15823<phrase>
15824 id: LANG_PLAY_NEXT
15825 desc: used in the onplay menu
15826 user: core
15827 <source>
15828 *: "Play Next"
15829 </source>
15830 <dest>
15831 *: "下一首播放"
15832 </dest>
15833 <voice>
15834 *: "下一首播放"
15835 </voice>
15836</phrase>
15837<phrase>
15838 id: LANG_ADD_SHUFFLED
15839 desc: used in the onplay menu
15840 user: core
15841 <source>
15842 *: "Add Shuffled"
15843 </source>
15844 <dest>
15845 *: "添加éšæœº"
15846 </dest>
15847 <voice>
15848 *: "添加éšæœº"
15849 </voice>
15850</phrase>
15851<phrase>
15852 id: LANG_PLAY_LAST
15853 desc: used in the onplay menu
15854 user: core
15855 <source>
15856 *: "Play Last"
15857 </source>
15858 <dest>
15859 *: "最åŽæ’­æ”¾"
15860 </dest>
15861 <voice>
15862 *: "最åŽæ’­æ”¾"
15863 </voice>
15864</phrase>
15865<phrase>
15866 id: LANG_PLAY_LAST_SHUFFLED
15867 desc: used in the onplay menu
15868 user: core
15869 <source>
15870 *: "Play Last Shuffled"
15871 </source>
15872 <dest>
15873 *: "播放列表éšæœº"
15874 </dest>
15875 <voice>
15876 *: "播放列表éšæœº"
15877 </voice>
15878</phrase>
15879<phrase>
15880 id: LANG_VOLUME_ADJUST_MODE
15881 desc: in system settings
15882 user: core
15883 <source>
15884 *: none
15885 perceptual_volume: "Volume Adjustment Mode"
15886 </source>
15887 <dest>
15888 *: none
15889 perceptual_volume: "音é‡è°ƒèŠ‚模å¼"
15890 </dest>
15891 <voice>
15892 *: none
15893 perceptual_volume: "音é‡è°ƒèŠ‚模å¼"
15894 </voice>
15895</phrase>
15896<phrase>
15897 id: LANG_VOLUME_ADJUST_NORM_STEPS
15898 desc: in system settings
15899 user: core
15900 <source>
15901 *: none
15902 perceptual_volume: "Number of Volume Steps"
15903 </source>
15904 <dest>
15905 *: none
15906 perceptual_volume: "音é‡è°ƒèŠ‚档数"
15907 </dest>
15908 <voice>
15909 *: none
15910 perceptual_volume: "音é‡è°ƒèŠ‚档数"
15911 </voice>
15912</phrase>
15913<phrase>
15914 id: LANG_PERCEPTUAL
15915 desc: in system settings -> volume adjustment mode
15916 user: core
15917 <source>
15918 *: none
15919 perceptual_volume: "Perceptual"
15920 </source>
15921 <dest>
15922 *: none
15923 perceptual_volume: "感性"
15924 </dest>
15925 <voice>
15926 *: none
15927 perceptual_volume: "感性"
15928 </voice>
15929</phrase>
15930<phrase>
15931 id: LANG_SHOW_TRACKS_WHILE_BROWSING
15932 desc: in PictureFlow Main Menu
15933 user: core
15934 <source>
15935 *: "Show Tracks While Browsing"
15936 </source>
15937 <dest>
15938 *: "æµè§ˆæ—¶æ˜¾ç¤ºæ›²ç›®"
15939 </dest>
15940 <voice>
15941 *: "æµè§ˆæ—¶æ˜¾ç¤ºæ›²ç›®"
15942 </voice>
15943</phrase>
15944<phrase>
15945 id: LANG_GOTO_LAST_ALBUM
15946 desc: in PictureFlow Main Menu
15947 user: core
15948 <source>
15949 *: "Go to Last Album"
15950 </source>
15951 <dest>
15952 *: "去最åŽä¸€ä¸ªæ›²ç›®"
15953 </dest>
15954 <voice>
15955 *: "去最åŽä¸€ä¸ªæ›²ç›®"
15956 </voice>
15957</phrase>
15958<phrase>
15959 id: LANG_DATABASE_DIR
15960 desc: in database settings menu
15961 user: core
15962 <source>
15963 *: "Database Directory"
15964 </source>
15965 <dest>
15966 *: "æ•°æ®åº“目录"
15967 </dest>
15968 <voice>
15969 *: "æ•°æ®åº“目录"
15970 </voice>
15971</phrase>
15972<phrase>
15973 id: LANG_REMOVE_QUEUED_TRACKS
15974 desc: Confirmation dialog
15975 user: core
15976 <source>
15977 *: "Remove Queued Tracks?"
15978 </source>
15979 <dest>
15980 *: "删除排队的轨é“?"
15981 </dest>
15982 <voice>
15983 *: "删除排队的轨é“?"
15984 </voice>
15985</phrase>
15986<phrase>
15987 id: LANG_QUICK_IGNORE_DIRACHE
15988 desc: in Settings
15989 user: core
15990 <source>
15991 *: "Quick (Ignore Directory Cache)"
15992 </source>
15993 <dest>
15994 *: "快速(忽略目录缓存)"
15995 </dest>
15996 <voice>
15997 *: "快速(忽略目录缓存)"
15998 </voice>
15999</phrase>
16000<phrase>
16001 id: LANG_WPS
16002 desc: in Settings
16003 user: core
16004 <source>
16005 *: "What's Playing Screen"
16006 </source>
16007 <dest>
16008 *: "正在播放å±å¹•"
16009 </dest>
16010 <voice>
16011 *: "正在播放å±å¹•"
16012 </voice>
16013</phrase>
16014<phrase>
16015 id: LANG_DEFAULT_BROWSER
16016 desc: in Settings
16017 user: core
16018 <source>
16019 *: "Default Browser"
16020 </source>
16021 <dest>
16022 *: "默认æµè§ˆå™¨"
16023 </dest>
16024 <voice>
16025 *: "默认æµè§ˆå™¨"
16026 </voice>
16027</phrase>
16028<phrase>
16029 id: LANG_AMAZE_MENU
16030 desc: Amaze game
16031 user: core
16032 <source>
16033 *: "Amaze Main Menu"
16034 </source>
16035 <dest>
16036 *: "Amaze 主èœå•"
16037 </dest>
16038 <voice>
16039 *: "Amaze 主èœå•"
16040 </voice>
16041</phrase>
16042<phrase>
16043 id: LANG_SET_MAZE_SIZE
16044 desc: Maze size in Amaze game
16045 user: core
16046 <source>
16047 *: "Set Maze Size"
16048 </source>
16049 <dest>
16050 *: "设置迷宫大å°"
16051 </dest>
16052 <voice>
16053 *: "设置迷宫大å°"
16054 </voice>
16055</phrase>
16056<phrase>
16057 id: LANG_VIEW_MAP
16058 desc: Map in Amaze game
16059 user: core
16060 <source>
16061 *: "View Map"
16062 </source>
16063 <dest>
16064 *: "设置迷宫大å°"
16065 </dest>
16066 <voice>
16067 *: "设置迷宫大å°"
16068 </voice>
16069</phrase>
16070<phrase>
16071 id: LANG_SHOW_COMPASS
16072 desc: Compass in Amaze game
16073 user: core
16074 <source>
16075 *: "Show Compass"
16076 </source>
16077 <dest>
16078 *: "显示指å—é’ˆ"
16079 </dest>
16080 <voice>
16081 *: "显示指å—é’ˆ"
16082 </voice>
16083</phrase>
16084<phrase>
16085 id: LANG_SHOW_MAP
16086 desc: Map in Amaze game
16087 user: core
16088 <source>
16089 *: "Show Map"
16090 </source>
16091 <dest>
16092 *: "显示地图"
16093 </dest>
16094 <voice>
16095 *: "显示地图"
16096 </voice>
16097</phrase>
16098<phrase>
16099 id: LANG_REMEMBER_PATH
16100 desc: Map in Amaze game
16101 user: core
16102 <source>
16103 *: "Remember Path"
16104 </source>
16105 <dest>
16106 *: "è®°ä½è·¯å¾„"
16107 </dest>
16108 <voice>
16109 *: "è®°ä½è·¯å¾„"
16110 </voice>
16111</phrase>
16112<phrase>
16113 id: LANG_USE_LARGE_TILES
16114 desc: Map in Amaze game
16115 user: core
16116 <source>
16117 *: "Use Large Tiles"
16118 </source>
16119 <dest>
16120 *: "使用大瓷砖"
16121 </dest>
16122 <voice>
16123 *: "使用大瓷砖"
16124 </voice>
16125</phrase>
16126<phrase>
16127 id: LANG_SHOW_SOLUTION
16128 desc: Map in Amaze game
16129 user: core
16130 <source>
16131 *: "Show Solution"
16132 </source>
16133 <dest>
16134 *: "显示解决方案"
16135 </dest>
16136 <voice>
16137 *: "显示解决方案"
16138 </voice>
16139</phrase>
16140<phrase>
16141 id: LANG_QUIT_WITHOUT_SAVING
16142 desc:
16143 user: core
16144 <source>
16145 *: "Quit without saving"
16146 </source>
16147 <dest>
16148 *: "ä¸ä¿å­˜å°±é€€å‡º"
16149 </dest>
16150 <voice>
16151 *: "ä¸ä¿å­˜å°±é€€å‡º"
16152 </voice>
16153</phrase>
16154<phrase>
16155 id: LANG_GENERATING_MAZE
16156 desc: Amaze game
16157 user: core
16158 <source>
16159 *: "Generating maze..."
16160 </source>
16161 <dest>
16162 *: "生æˆè¿·å®«â€¦"
16163 </dest>
16164 <voice>
16165 *: "生æˆè¿·å®«â€¦"
16166 </voice>
16167</phrase>
16168<phrase>
16169 id: LANG_YOU_WIN
16170 desc: Success in game
16171 user: core
16172 <source>
16173 *: "You win!"
16174 </source>
16175 <dest>
16176 *: "你赢了ï¼"
16177 </dest>
16178 <voice>
16179 *: "你过关ï¼"
16180 </voice>
16181</phrase>
16182<phrase>
16183 id: LANG_YOU_CHEATED
16184 desc: Cheated in game
16185 user: core
16186 <source>
16187 *: "You cheated!"
16188 </source>
16189 <dest>
16190 *: "你作弊ï¼"
16191 </dest>
16192 <voice>
16193 *: "你作弊ï¼"
16194 </voice>
16195</phrase>
16196<phrase>
16197 id: LANG_DIFFICULTY_EASY
16198 desc: Game difficulty
16199 user: core
16200 <source>
16201 *: "Easy"
16202 </source>
16203 <dest>
16204 *: "简å•"
16205 </dest>
16206 <voice>
16207 *: "简å•"
16208 </voice>
16209</phrase>
16210<phrase>
16211 id: LANG_DIFFICULTY_MEDIUM
16212 desc: Game difficulty
16213 user: core
16214 <source>
16215 *: "Medium"
16216 </source>
16217 <dest>
16218 *: "中等"
16219 </dest>
16220 <voice>
16221 *: "中等"
16222 </voice>
16223</phrase>
16224<phrase>
16225 id: LANG_DIFFICULTY_HARD
16226 desc: Game difficulty
16227 user: core
16228 <source>
16229 *: "Hard"
16230 </source>
16231 <dest>
16232 *: "å›°éš¾"
16233 </dest>
16234 <voice>
16235 *: "å›°éš¾"
16236 </voice>
16237</phrase>
16238<phrase>
16239 id: LANG_DIFFICULTY_EXPERT
16240 desc: Game difficulty
16241 user: core
16242 <source>
16243 *: "Expert"
16244 </source>
16245 <dest>
16246 *: "专家"
16247 </dest>
16248 <voice>
16249 *: "专家"
16250 </voice>
16251</phrase>
16252<phrase>
16253 id: LANG_STEREOSW_MODE
16254 desc: Stereo Switch Mode
16255 user: core
16256 <source>
16257 *: "Stereo Switch Mode"
16258 </source>
16259 <dest>
16260 *: "立体声切æ¢æ¨¡å¼"
16261 </dest>
16262 <voice>
16263 *: "立体声切æ¢æ¨¡å¼"
16264 </voice>
16265</phrase>
16266<phrase>
16267 id: LANG_REVERSE
16268 desc: in settings_menu
16269 user: core
16270 <source>
16271 *: "Reverse"
16272 </source>
16273 <dest>
16274 *: "å转"
16275 </dest>
16276 <voice>
16277 *: "å转"
16278 </voice>
16279</phrase>
16280<phrase>
16281 id: LANG_ALWAYS_ZERO
16282 desc: in settings_menu
16283 user: core
16284 <source>
16285 *: "Always 0"
16286 </source>
16287 <dest>
16288 *: "总是0"
16289 </dest>
16290 <voice>
16291 *: "总是0"
16292 </voice>
16293</phrase>
16294<phrase>
16295 id: LANG_ALWAYS_ONE
16296 desc: in settings_menu
16297 user: core
16298 <source>
16299 *: "Always 1"
16300 </source>
16301 <dest>
16302 *: "总是1"
16303 </dest>
16304 <voice>
16305 *: "总是1"
16306 </voice>
16307</phrase>
16308<phrase>
16309 id: LANG_LEGAL_NOTICES
16310 desc: in system menu
16311 user: core
16312 <source>
16313 *: "Legal Notices"
16314 </source>
16315 <dest>
16316 *: "法律声明"
16317 </dest>
16318 <voice>
16319 *: "法律声明"
16320 </voice>
16321</phrase>
16322<phrase>
16323 id: LANG_ERROR_FORMATSTR
16324 desc: for general use
16325 user: core
16326 <source>
16327 *: "Error: %s"
16328 </source>
16329 <dest>
16330 *: "错误: %s"
16331 </dest>
16332 <voice>
16333 *: "错误"
16334 </voice>
16335</phrase>
16336<phrase>
16337 id: LANG_MIKMOD_SETTINGS
16338 desc: mikmod plugin
16339 user: core
16340 <source>
16341 *: "Mikmod Settings"
16342 </source>
16343 <dest>
16344 *: "Mikmod 设置"
16345 </dest>
16346 <voice>
16347 *: "Mikmod 设置"
16348 </voice>
16349</phrase>
16350<phrase>
16351 id: LANG_MIKMOD_MENU
16352 desc: mikmod plugin
16353 user: core
16354 <source>
16355 *: "Mikmod Menu"
16356 </source>
16357 <dest>
16358 *: "Mikmod èœå•"
16359 </dest>
16360 <voice>
16361 *: "Mikmod èœå•"
16362 </voice>
16363</phrase>
16364<phrase>
16365 id: LANG_CHESSBOX_MENU
16366 desc: chessbox plugin
16367 user: core
16368 <source>
16369 *: "Chessbox Menu"
16370 </source>
16371 <dest>
16372 *: "Chessbox èœå•"
16373 </dest>
16374 <voice>
16375 *: "Chess box èœå•"
16376 </voice>
16377</phrase>
16378<phrase>
16379 id: VOICE_INVALID_VOICE_FILE
16380 desc: played if the voice file fails to load
16381 user: core
16382 <source>
16383 *: ""
16384 </source>
16385 <dest>
16386 *: ""
16387 </dest>
16388 <voice>
16389 *: "无效语音文件"
16390 </voice>
16391</phrase>
16392<phrase>
16393 id: VOICE_LANG_NAME
16394 desc: Spoken name of the language
16395 user: core
16396 <source>
16397 *: ""
16398 </source>
16399 <dest>
16400 *: ""
16401 </dest>
16402 <voice>
16403 *: "简体中文"
16404 </voice>
16405</phrase>
16406<phrase>
16407 id: LANG_PERCENT_FORMAT
16408 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
16409 user: core
16410 <source>
16411 *: "%ld%%"
16412 </source>
16413 <dest>
16414 *: "~%ld%%"
16415 </dest>
16416 <voice>
16417 *: none
16418 </voice>
16419</phrase>
16420<phrase>
16421 id: LANG_CHOOSE_FILE
16422 desc: file_picker plugin ask user to select a file
16423 user: core
16424 <source>
16425 *: "Choose File"
16426 </source>
16427 <dest>
16428 *: "请选择文件"
16429 </dest>
16430 <voice>
16431 *: "请选择文件"
16432 </voice>
16433</phrase>
16434<phrase>
16435 id: LANG_REMAINING
16436 desc: Playing Time
16437 user: core
16438 <source>
16439 *: "Remaining"
16440 </source>
16441 <dest>
16442 *: "剩余"
16443 </dest>
16444 <voice>
16445 *: "剩余"
16446 </voice>
16447</phrase>
16448<phrase>
16449 id: LANG_DISABLE_MAINMENU_SCROLLING
16450 desc: Disable main menu scrolling
16451 user: core
16452 <source>
16453 *: "Disable main menu scrolling"
16454 </source>
16455 <dest>
16456 *: "ç¦ç”¨ä¸»èœå•æ»šåŠ¨"
16457 </dest>
16458 <voice>
16459 *: "ç¦ç”¨ä¸»èœå•æ»šåŠ¨"
16460 </voice>
16461</phrase>
16462<phrase>
16463 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
16464 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
16465 user: core
16466 <source>
16467 *: "Selection too big, %d random tracks will be selected"
16468 </source>
16469 <dest>
16470 *: "选择了太多,将从中éšæœºé€‰å–%d首"
16471 </dest>
16472 <voice>
16473 *: "选择了太多,将从中选择更少曲目"
16474 </voice>
16475</phrase>
16476<phrase>
16477 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
16478 desc: track display options
16479 user: core
16480 <source>
16481 *: "Title & Album from ID3 tags"
16482 </source>
16483 <dest>
16484 *: "ID3 标题&专辑信æ¯"
16485 </dest>
16486 <voice>
16487 *: "I D 3 标题与专辑信æ¯"
16488 </voice>
16489</phrase>
16490<phrase>
16491 id: LANG_DISPLAY_TITLE_FROMTAGS
16492 desc: track display options
16493 user: core
16494 <source>
16495 *: "Title from ID3 tags"
16496 </source>
16497 <dest>
16498 *: "ID3标题信æ¯"
16499 </dest>
16500 <voice>
16501 *: "I D 3 标题信æ¯"
16502 </voice>
16503</phrase>
diff --git a/apps/lang/czech.lang b/apps/lang/czech.lang
index 63224db4f3..e3d79c91fa 100644
--- a/apps/lang/czech.lang
+++ b/apps/lang/czech.lang
@@ -12926,16 +12926,13 @@
12926 desc: prefix for elapsed playtime announcement 12926 desc: prefix for elapsed playtime announcement
12927 user: core 12927 user: core
12928 <source> 12928 <source>
12929 *: none 12929 *: "Elapsed"
12930 hotkey: "Elapsed"
12931 </source> 12930 </source>
12932 <dest> 12931 <dest>
12933 *: none 12932 *: "Uplynulo"
12934 hotkey: "Uplynulo"
12935 </dest> 12933 </dest>
12936 <voice> 12934 <voice>
12937 *: none 12935 *: "Uplynulo"
12938 hotkey: "Uplynulo"
12939 </voice> 12936 </voice>
12940</phrase> 12937</phrase>
12941<phrase> 12938<phrase>
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index 096a945bd9..3ea025b1ac 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -3271,7 +3271,7 @@
3271 </dest> 3271 </dest>
3272 <voice> 3272 <voice>
3273 *: none 3273 *: none
3274 buttonlight_brightness: "Tastenbeleuchtung: Helligkeit" 3274 buttonlight_brightness: "Tastenbeleuchtung Helligkeit"
3275 </voice> 3275 </voice>
3276</phrase> 3276</phrase>
3277<phrase> 3277<phrase>
@@ -5917,7 +5917,7 @@
5917 *: "Frei:" 5917 *: "Frei:"
5918 </dest> 5918 </dest>
5919 <voice> 5919 <voice>
5920 *: "Freier Plattenplatz:" 5920 *: "Freier Plattenplatz"
5921 </voice> 5921 </voice>
5922</phrase> 5922</phrase>
5923<phrase> 5923<phrase>
@@ -7031,7 +7031,7 @@
7031 *: "Modus:" 7031 *: "Modus:"
7032 </dest> 7032 </dest>
7033 <voice> 7033 <voice>
7034 *: "Modus:" 7034 *: "Modus"
7035 </voice> 7035 </voice>
7036</phrase> 7036</phrase>
7037<phrase> 7037<phrase>
@@ -8693,7 +8693,7 @@
8693 </dest> 8693 </dest>
8694 <voice> 8694 <voice>
8695 *: none 8695 *: none
8696 rtc: "Uhrzeit:" 8696 rtc: "Uhrzeit"
8697 </voice> 8697 </voice>
8698</phrase> 8698</phrase>
8699<phrase> 8699<phrase>
@@ -10363,7 +10363,7 @@
10363 *: "Danach:" 10363 *: "Danach:"
10364 </dest> 10364 </dest>
10365 <voice> 10365 <voice>
10366 *: "Danach:" 10366 *: "Danach"
10367 </voice> 10367 </voice>
10368</phrase> 10368</phrase>
10369<phrase> 10369<phrase>
@@ -10628,7 +10628,7 @@
10628 *: "Nächster Titel:" 10628 *: "Nächster Titel:"
10629 </dest> 10629 </dest>
10630 <voice> 10630 <voice>
10631 *: "Nächster Titel:" 10631 *: "Nächster Titel"
10632 </voice> 10632 </voice>
10633</phrase> 10633</phrase>
10634<phrase> 10634<phrase>
@@ -11221,7 +11221,7 @@
11221 </dest> 11221 </dest>
11222 <voice> 11222 <voice>
11223 *: none 11223 *: none
11224 radio: "Signalstärke:" 11224 radio: "Signalstärke"
11225 </voice> 11225 </voice>
11226</phrase> 11226</phrase>
11227<phrase> 11227<phrase>
@@ -12091,7 +12091,7 @@
12091 *: "Level 5: 40 Züge/ 60 min" 12091 *: "Level 5: 40 Züge/ 60 min"
12092 </dest> 12092 </dest>
12093 <voice> 12093 <voice>
12094 *: "Level 5: 40 Züge pro 60 Minuten" 12094 *: "Level 5, 40 Züge pro 60 Minuten"
12095 </voice> 12095 </voice>
12096</phrase> 12096</phrase>
12097<phrase> 12097<phrase>
@@ -12391,7 +12391,7 @@
12391 *: "Level 1: 60 Züge/ 5 min" 12391 *: "Level 1: 60 Züge/ 5 min"
12392 </dest> 12392 </dest>
12393 <voice> 12393 <voice>
12394 *: "Level 1: 60 Züge pro 5 Minuten" 12394 *: "Level 1, 60 Züge pro 5 Minuten"
12395 </voice> 12395 </voice>
12396</phrase> 12396</phrase>
12397<phrase> 12397<phrase>
@@ -12565,7 +12565,7 @@
12565 *: "Level 2: 60 Züge / 15 min" 12565 *: "Level 2: 60 Züge / 15 min"
12566 </dest> 12566 </dest>
12567 <voice> 12567 <voice>
12568 *: "Level 2: 60 Züge pro 15 Minuten" 12568 *: "Level 2, 60 Züge pro 15 Minuten"
12569 </voice> 12569 </voice>
12570</phrase> 12570</phrase>
12571<phrase> 12571<phrase>
@@ -12773,7 +12773,7 @@
12773 *: "Level 7: 40 Züge / 240 min" 12773 *: "Level 7: 40 Züge / 240 min"
12774 </dest> 12774 </dest>
12775 <voice> 12775 <voice>
12776 *: "Level 7: 40 Züge pro 240 Minuten" 12776 *: "Level 7, 40 Züge pro 240 Minuten"
12777 </voice> 12777 </voice>
12778</phrase> 12778</phrase>
12779<phrase> 12779<phrase>
@@ -13068,7 +13068,7 @@
13068 *: "Level 9: 1 Züge / 60 min" 13068 *: "Level 9: 1 Züge / 60 min"
13069 </dest> 13069 </dest>
13070 <voice> 13070 <voice>
13071 *: "Level 9: 1 Züge pro 60 Minuten" 13071 *: "Level 9, 1 Züge pro 60 Minuten"
13072 </voice> 13072 </voice>
13073</phrase> 13073</phrase>
13074<phrase> 13074<phrase>
@@ -13183,7 +13183,7 @@
13183 *: "Level 3: 60 Züge / 30 min" 13183 *: "Level 3: 60 Züge / 30 min"
13184 </dest> 13184 </dest>
13185 <voice> 13185 <voice>
13186 *: "Level 3: 60 Züge pro 30 Minuten" 13186 *: "Level 3, 60 Züge pro 30 Minuten"
13187 </voice> 13187 </voice>
13188</phrase> 13188</phrase>
13189<phrase> 13189<phrase>
@@ -13708,7 +13708,7 @@
13708 *: "Level 4: 40 Züge / 30 min" 13708 *: "Level 4: 40 Züge / 30 min"
13709 </dest> 13709 </dest>
13710 <voice> 13710 <voice>
13711 *: "Level 4: 40 Züge pro 30 Minuten" 13711 *: "Level 4, 40 Züge pro 30 Minuten"
13712 </voice> 13712 </voice>
13713</phrase> 13713</phrase>
13714<phrase> 13714<phrase>
@@ -14084,7 +14084,7 @@
14084 *: "Level 6: 40 Züge / 120 min" 14084 *: "Level 6: 40 Züge / 120 min"
14085 </dest> 14085 </dest>
14086 <voice> 14086 <voice>
14087 *: "Level 6: 40 Züge pro 120 Minuten" 14087 *: "Level 6, 40 Züge pro 120 Minuten"
14088 </voice> 14088 </voice>
14089</phrase> 14089</phrase>
14090<phrase> 14090<phrase>
@@ -14334,7 +14334,7 @@
14334 *: "Level 10: 1 Zug / 600 min" 14334 *: "Level 10: 1 Zug / 600 min"
14335 </dest> 14335 </dest>
14336 <voice> 14336 <voice>
14337 *: "Level 10: 1 Zug pro 600 Minuten" 14337 *: "Level 10, 1 Zug pro 600 Minuten"
14338 </voice> 14338 </voice>
14339</phrase> 14339</phrase>
14340<phrase> 14340<phrase>
@@ -14404,7 +14404,7 @@
14404 *: "Level 8: 1 Zug / 15 min" 14404 *: "Level 8: 1 Zug / 15 min"
14405 </dest> 14405 </dest>
14406 <voice> 14406 <voice>
14407 *: "Level 8: 1 Zug pro 15 Minuten" 14407 *: "Level 8, 1 Zug pro 15 Minuten"
14408 </voice> 14408 </voice>
14409</phrase> 14409</phrase>
14410<phrase> 14410<phrase>
@@ -15020,16 +15020,13 @@
15020 desc: prefix for elapsed playtime announcement 15020 desc: prefix for elapsed playtime announcement
15021 user: core 15021 user: core
15022 <source> 15022 <source>
15023 *: none 15023 *: "Elapsed"
15024 hotkey: "Elapsed"
15025 </source> 15024 </source>
15026 <dest> 15025 <dest>
15027 *: none 15026 *: "Vergangen"
15028 hotkey: "Vergangen"
15029 </dest> 15027 </dest>
15030 <voice> 15028 <voice>
15031 *: none 15029 *: "Vergangen"
15032 hotkey: "Vergangen"
15033 </voice> 15030 </voice>
15034</phrase> 15031</phrase>
15035<phrase> 15032<phrase>
diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang
index f3dba7008c..4cc52ede38 100644
--- a/apps/lang/english-us.lang
+++ b/apps/lang/english-us.lang
@@ -5644,7 +5644,7 @@
5644 </dest> 5644 </dest>
5645 <voice> 5645 <voice>
5646 *: none 5646 *: none
5647 remote: "(Vol- : Re-enable)" 5647 remote: "(Volume minus to Re-enable)"
5648 </voice> 5648 </voice>
5649</phrase> 5649</phrase>
5650<phrase> 5650<phrase>
@@ -5926,7 +5926,7 @@
5926 *: "Free:" 5926 *: "Free:"
5927 </dest> 5927 </dest>
5928 <voice> 5928 <voice>
5929 *: "Free diskspace:" 5929 *: "Free disk space"
5930 </voice> 5930 </voice>
5931</phrase> 5931</phrase>
5932<phrase> 5932<phrase>
@@ -7040,7 +7040,7 @@
7040 *: "Mode:" 7040 *: "Mode:"
7041 </dest> 7041 </dest>
7042 <voice> 7042 <voice>
7043 *: "Mode:" 7043 *: "Mode"
7044 </voice> 7044 </voice>
7045</phrase> 7045</phrase>
7046<phrase> 7046<phrase>
@@ -7366,7 +7366,7 @@
7366 </dest> 7366 </dest>
7367 <voice> 7367 <voice>
7368 *: none 7368 *: none
7369 charging: "Battery: Charging" 7369 charging: "Battery is Charging"
7370 </voice> 7370 </voice>
7371</phrase> 7371</phrase>
7372<phrase> 7372<phrase>
@@ -8702,7 +8702,7 @@
8702 </dest> 8702 </dest>
8703 <voice> 8703 <voice>
8704 *: none 8704 *: none
8705 rtc: "Current time:" 8705 rtc: "Current time"
8706 </voice> 8706 </voice>
8707</phrase> 8707</phrase>
8708<phrase> 8708<phrase>
@@ -10682,7 +10682,7 @@
10682 *: "Next Track:" 10682 *: "Next Track:"
10683 </dest> 10683 </dest>
10684 <voice> 10684 <voice>
10685 *: "Next Track:" 10685 *: "Next Track"
10686 </voice> 10686 </voice>
10687</phrase> 10687</phrase>
10688<phrase> 10688<phrase>
@@ -10696,7 +10696,7 @@
10696 *: "Next:" 10696 *: "Next:"
10697 </dest> 10697 </dest>
10698 <voice> 10698 <voice>
10699 *: "Next:" 10699 *: "Next"
10700 </voice> 10700 </voice>
10701</phrase> 10701</phrase>
10702<phrase> 10702<phrase>
@@ -11244,7 +11244,7 @@
11244 </dest> 11244 </dest>
11245 <voice> 11245 <voice>
11246 *: none 11246 *: none
11247 radio: "Signal strength:" 11247 radio: "Signal strength"
11248 </voice> 11248 </voice>
11249</phrase> 11249</phrase>
11250<phrase> 11250<phrase>
@@ -12537,16 +12537,16 @@
12537</phrase> 12537</phrase>
12538<phrase> 12538<phrase>
12539 id: LANG_PLAYTIME_REMAINING 12539 id: LANG_PLAYTIME_REMAINING
12540 desc: playing time screen 12540 desc: deprecated
12541 user: core 12541 user: core
12542 <source> 12542 <source>
12543 *: "Playlist remaining:" 12543 *: ""
12544 </source> 12544 </source>
12545 <dest> 12545 <dest>
12546 *: "Playlist remaining:" 12546 *: ""
12547 </dest> 12547 </dest>
12548 <voice> 12548 <voice>
12549 *: "Playlist remaining" 12549 *: ""
12550 </voice> 12550 </voice>
12551</phrase> 12551</phrase>
12552<phrase> 12552<phrase>
@@ -13202,7 +13202,7 @@
13202 *: "Level 1: 60 moves / 5 min" 13202 *: "Level 1: 60 moves / 5 min"
13203 </dest> 13203 </dest>
13204 <voice> 13204 <voice>
13205 *: "Level 1: 60 moves per 5 minutes" 13205 *: "Level 1, 60 moves per 5 minutes"
13206 </voice> 13206 </voice>
13207</phrase> 13207</phrase>
13208<phrase> 13208<phrase>
@@ -13216,7 +13216,7 @@
13216 *: "Level 2: 60 moves / 15 min" 13216 *: "Level 2: 60 moves / 15 min"
13217 </dest> 13217 </dest>
13218 <voice> 13218 <voice>
13219 *: "Level 2: 60 moves per 15 minutes" 13219 *: "Level 2, 60 moves per 15 minutes"
13220 </voice> 13220 </voice>
13221</phrase> 13221</phrase>
13222<phrase> 13222<phrase>
@@ -13230,7 +13230,7 @@
13230 *: "Level 3: 60 moves / 30 min" 13230 *: "Level 3: 60 moves / 30 min"
13231 </dest> 13231 </dest>
13232 <voice> 13232 <voice>
13233 *: "Level 3: 60 moves per 30 minutes" 13233 *: "Level 3, 60 moves per 30 minutes"
13234 </voice> 13234 </voice>
13235</phrase> 13235</phrase>
13236<phrase> 13236<phrase>
@@ -13244,7 +13244,7 @@
13244 *: "Level 4: 40 moves / 30 min" 13244 *: "Level 4: 40 moves / 30 min"
13245 </dest> 13245 </dest>
13246 <voice> 13246 <voice>
13247 *: "Level 4: 40 moves per 30 minutes" 13247 *: "Level 4, 40 moves per 30 minutes"
13248 </voice> 13248 </voice>
13249</phrase> 13249</phrase>
13250<phrase> 13250<phrase>
@@ -13258,7 +13258,7 @@
13258 *: "Level 5: 40 moves / 60 min" 13258 *: "Level 5: 40 moves / 60 min"
13259 </dest> 13259 </dest>
13260 <voice> 13260 <voice>
13261 *: "Level 5: 40 moves per 60 minutes" 13261 *: "Level 5, 40 moves per 60 minutes"
13262 </voice> 13262 </voice>
13263</phrase> 13263</phrase>
13264<phrase> 13264<phrase>
@@ -13272,7 +13272,7 @@
13272 *: "Level 6: 40 moves / 120 min" 13272 *: "Level 6: 40 moves / 120 min"
13273 </dest> 13273 </dest>
13274 <voice> 13274 <voice>
13275 *: "Level 6: 40 moves per 120 minutes" 13275 *: "Level 6, 40 moves per 120 minutes"
13276 </voice> 13276 </voice>
13277</phrase> 13277</phrase>
13278<phrase> 13278<phrase>
@@ -13286,7 +13286,7 @@
13286 *: "Level 7: 40 moves / 240 min" 13286 *: "Level 7: 40 moves / 240 min"
13287 </dest> 13287 </dest>
13288 <voice> 13288 <voice>
13289 *: "Level 7: 40 moves per 240 minutes" 13289 *: "Level 7, 40 moves per 240 minutes"
13290 </voice> 13290 </voice>
13291</phrase> 13291</phrase>
13292<phrase> 13292<phrase>
@@ -13300,7 +13300,7 @@
13300 *: "Level 8: 1 move / 15 min" 13300 *: "Level 8: 1 move / 15 min"
13301 </dest> 13301 </dest>
13302 <voice> 13302 <voice>
13303 *: "Level 8: 1 move per 15 minutes" 13303 *: "Level 8, 1 move per 15 minutes"
13304 </voice> 13304 </voice>
13305</phrase> 13305</phrase>
13306<phrase> 13306<phrase>
@@ -13314,7 +13314,7 @@
13314 *: "Level 9: 1 move / 60 min" 13314 *: "Level 9: 1 move / 60 min"
13315 </dest> 13315 </dest>
13316 <voice> 13316 <voice>
13317 *: "Level 9: 1 move per 60 minutes" 13317 *: "Level 9, 1 move per 60 minutes"
13318 </voice> 13318 </voice>
13319</phrase> 13319</phrase>
13320<phrase> 13320<phrase>
@@ -13328,7 +13328,7 @@
13328 *: "Level 10: 1 move / 600 min" 13328 *: "Level 10: 1 move / 600 min"
13329 </dest> 13329 </dest>
13330 <voice> 13330 <voice>
13331 *: "Level 10: 1 move per 600 minutes" 13331 *: "Level 10, 1 move per 600 minutes"
13332 </voice> 13332 </voice>
13333</phrase> 13333</phrase>
13334<phrase> 13334<phrase>
@@ -15000,16 +15000,13 @@
15000 desc: prefix for elapsed playtime announcement 15000 desc: prefix for elapsed playtime announcement
15001 user: core 15001 user: core
15002 <source> 15002 <source>
15003 *: none 15003 *: "Elapsed"
15004 hotkey: "Elapsed"
15005 </source> 15004 </source>
15006 <dest> 15005 <dest>
15007 *: none 15006 *: "Elapsed"
15008 hotkey: "Elapsed"
15009 </dest> 15007 </dest>
15010 <voice> 15008 <voice>
15011 *: none 15009 *: "Elapsed"
15012 hotkey: "Elapsed"
15013 </voice> 15010 </voice>
15014</phrase> 15011</phrase>
15015<phrase> 15012<phrase>
@@ -16409,3 +16406,87 @@
16409 *: none 16406 *: none
16410 </voice> 16407 </voice>
16411</phrase> 16408</phrase>
16409<phrase>
16410 id: LANG_CHOOSE_FILE
16411 desc: file_picker plugin ask user to select a file
16412 user: core
16413 <source>
16414 *: "Choose File"
16415 </source>
16416 <dest>
16417 *: "Choose File"
16418 </dest>
16419 <voice>
16420 *: "Choose File"
16421 </voice>
16422</phrase>
16423<phrase>
16424 id: LANG_REMAINING
16425 desc: Playing Time
16426 user: core
16427 <source>
16428 *: "Remaining"
16429 </source>
16430 <dest>
16431 *: "Remaining"
16432 </dest>
16433 <voice>
16434 *: "Remaining"
16435 </voice>
16436</phrase>
16437<phrase>
16438 id: LANG_DISABLE_MAINMENU_SCROLLING
16439 desc: Disable main menu scrolling
16440 user: core
16441 <source>
16442 *: "Disable main menu scrolling"
16443 </source>
16444 <dest>
16445 *: "Disable main menu scrolling"
16446 </dest>
16447 <voice>
16448 *: "Disable main menu scrolling"
16449 </voice>
16450</phrase>
16451<phrase>
16452 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
16453 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
16454 user: core
16455 <source>
16456 *: "Selection too big, %d random tracks will be selected"
16457 </source>
16458 <dest>
16459 *: "Selection too big, %d random tracks will be selected"
16460 </dest>
16461 <voice>
16462 *: "Selection too big, fewer random tracks will be selected"
16463 </voice>
16464</phrase>
16465<phrase>
16466 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
16467 desc: track display options
16468 user: core
16469 <source>
16470 *: "Title & Album from ID3 tags"
16471 </source>
16472 <dest>
16473 *: "Title & Album from ID3 tags"
16474 </dest>
16475 <voice>
16476 *: "Title and Album from tags"
16477 </voice>
16478</phrase>
16479<phrase>
16480 id: LANG_DISPLAY_TITLE_FROMTAGS
16481 desc: track display options
16482 user: core
16483 <source>
16484 *: "Title from ID3 tags"
16485 </source>
16486 <dest>
16487 *: "Title from ID3 tags"
16488 </dest>
16489 <voice>
16490 *: "Title from tags"
16491 </voice>
16492</phrase>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 532f05baf7..1ee1ffac0f 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -2032,6 +2032,34 @@
2032 </voice> 2032 </voice>
2033</phrase> 2033</phrase>
2034<phrase> 2034<phrase>
2035 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
2036 desc: track display options
2037 user: core
2038 <source>
2039 *: "Title & Album from ID3 tags"
2040 </source>
2041 <dest>
2042 *: "Title & Album from ID3 tags"
2043 </dest>
2044 <voice>
2045 *: "Title and Album from tags"
2046 </voice>
2047</phrase>
2048<phrase>
2049 id: LANG_DISPLAY_TITLE_FROMTAGS
2050 desc: track display options
2051 user: core
2052 <source>
2053 *: "Title from ID3 tags"
2054 </source>
2055 <dest>
2056 *: "Title from ID3 tags"
2057 </dest>
2058 <voice>
2059 *: "Title from tags"
2060 </voice>
2061</phrase>
2062<phrase>
2035 id: LANG_BUILDING_DATABASE 2063 id: LANG_BUILDING_DATABASE
2036 desc: splash database building progress 2064 desc: splash database building progress
2037 user: core 2065 user: core
@@ -2054,6 +2082,20 @@
2054 </voice> 2082 </voice>
2055</phrase> 2083</phrase>
2056<phrase> 2084<phrase>
2085 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
2086 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
2087 user: core
2088 <source>
2089 *: "Selection too big, %d random tracks will be selected"
2090 </source>
2091 <dest>
2092 *: "Selection too big, %d random tracks will be selected"
2093 </dest>
2094 <voice>
2095 *: "Selection too big, fewer random tracks will be selected"
2096 </voice>
2097</phrase>
2098<phrase>
2057 id: LANG_TAGCACHE_RAM 2099 id: LANG_TAGCACHE_RAM
2058 desc: in tag cache settings 2100 desc: in tag cache settings
2059 user: core 2101 user: core
@@ -5721,7 +5763,7 @@
5721 </dest> 5763 </dest>
5722 <voice> 5764 <voice>
5723 *: none 5765 *: none
5724 remote: "(Vol- : Re-enable)" 5766 remote: "(Volume minus to Re-enable)"
5725 </voice> 5767 </voice>
5726</phrase> 5768</phrase>
5727<phrase> 5769<phrase>
@@ -6003,7 +6045,7 @@
6003 *: "Free:" 6045 *: "Free:"
6004 </dest> 6046 </dest>
6005 <voice> 6047 <voice>
6006 *: "Free diskspace:" 6048 *: "Free disk space"
6007 </voice> 6049 </voice>
6008</phrase> 6050</phrase>
6009<phrase> 6051<phrase>
@@ -7117,7 +7159,7 @@
7117 *: "Mode:" 7159 *: "Mode:"
7118 </dest> 7160 </dest>
7119 <voice> 7161 <voice>
7120 *: "Mode:" 7162 *: "Mode"
7121 </voice> 7163 </voice>
7122</phrase> 7164</phrase>
7123<phrase> 7165<phrase>
@@ -7443,7 +7485,7 @@
7443 </dest> 7485 </dest>
7444 <voice> 7486 <voice>
7445 *: none 7487 *: none
7446 charging: "Battery: Charging" 7488 charging: "Battery is Charging"
7447 </voice> 7489 </voice>
7448</phrase> 7490</phrase>
7449<phrase> 7491<phrase>
@@ -8779,7 +8821,7 @@
8779 </dest> 8821 </dest>
8780 <voice> 8822 <voice>
8781 *: none 8823 *: none
8782 rtc: "Current time:" 8824 rtc: "Current time"
8783 </voice> 8825 </voice>
8784</phrase> 8826</phrase>
8785<phrase> 8827<phrase>
@@ -10759,7 +10801,7 @@
10759 *: "Next Track:" 10801 *: "Next Track:"
10760 </dest> 10802 </dest>
10761 <voice> 10803 <voice>
10762 *: "Next Track:" 10804 *: "Next Track"
10763 </voice> 10805 </voice>
10764</phrase> 10806</phrase>
10765<phrase> 10807<phrase>
@@ -10773,7 +10815,7 @@
10773 *: "Next:" 10815 *: "Next:"
10774 </dest> 10816 </dest>
10775 <voice> 10817 <voice>
10776 *: "Next:" 10818 *: "Next"
10777 </voice> 10819 </voice>
10778</phrase> 10820</phrase>
10779<phrase> 10821<phrase>
@@ -11321,7 +11363,7 @@
11321 </dest> 11363 </dest>
11322 <voice> 11364 <voice>
11323 *: none 11365 *: none
11324 radio: "Signal strength:" 11366 radio: "Signal strength"
11325 </voice> 11367 </voice>
11326</phrase> 11368</phrase>
11327<phrase> 11369<phrase>
@@ -12614,16 +12656,16 @@
12614</phrase> 12656</phrase>
12615<phrase> 12657<phrase>
12616 id: LANG_PLAYTIME_REMAINING 12658 id: LANG_PLAYTIME_REMAINING
12617 desc: playing time screen 12659 desc: deprecated
12618 user: core 12660 user: core
12619 <source> 12661 <source>
12620 *: "Playlist remaining:" 12662 *: ""
12621 </source> 12663 </source>
12622 <dest> 12664 <dest>
12623 *: "Playlist remaining:" 12665 *: ""
12624 </dest> 12666 </dest>
12625 <voice> 12667 <voice>
12626 *: "Playlist remaining" 12668 *: ""
12627 </voice> 12669 </voice>
12628</phrase> 12670</phrase>
12629<phrase> 12671<phrase>
@@ -13279,7 +13321,7 @@
13279 *: "Level 1: 60 moves / 5 min" 13321 *: "Level 1: 60 moves / 5 min"
13280 </dest> 13322 </dest>
13281 <voice> 13323 <voice>
13282 *: "Level 1: 60 moves per 5 minutes" 13324 *: "Level 1, 60 moves per 5 minutes"
13283 </voice> 13325 </voice>
13284</phrase> 13326</phrase>
13285<phrase> 13327<phrase>
@@ -13293,7 +13335,7 @@
13293 *: "Level 2: 60 moves / 15 min" 13335 *: "Level 2: 60 moves / 15 min"
13294 </dest> 13336 </dest>
13295 <voice> 13337 <voice>
13296 *: "Level 2: 60 moves per 15 minutes" 13338 *: "Level 2, 60 moves per 15 minutes"
13297 </voice> 13339 </voice>
13298</phrase> 13340</phrase>
13299<phrase> 13341<phrase>
@@ -13307,7 +13349,7 @@
13307 *: "Level 3: 60 moves / 30 min" 13349 *: "Level 3: 60 moves / 30 min"
13308 </dest> 13350 </dest>
13309 <voice> 13351 <voice>
13310 *: "Level 3: 60 moves per 30 minutes" 13352 *: "Level 3, 60 moves per 30 minutes"
13311 </voice> 13353 </voice>
13312</phrase> 13354</phrase>
13313<phrase> 13355<phrase>
@@ -13321,7 +13363,7 @@
13321 *: "Level 4: 40 moves / 30 min" 13363 *: "Level 4: 40 moves / 30 min"
13322 </dest> 13364 </dest>
13323 <voice> 13365 <voice>
13324 *: "Level 4: 40 moves per 30 minutes" 13366 *: "Level 4, 40 moves per 30 minutes"
13325 </voice> 13367 </voice>
13326</phrase> 13368</phrase>
13327<phrase> 13369<phrase>
@@ -13335,7 +13377,7 @@
13335 *: "Level 5: 40 moves / 60 min" 13377 *: "Level 5: 40 moves / 60 min"
13336 </dest> 13378 </dest>
13337 <voice> 13379 <voice>
13338 *: "Level 5: 40 moves per 60 minutes" 13380 *: "Level 5, 40 moves per 60 minutes"
13339 </voice> 13381 </voice>
13340</phrase> 13382</phrase>
13341<phrase> 13383<phrase>
@@ -13349,7 +13391,7 @@
13349 *: "Level 6: 40 moves / 120 min" 13391 *: "Level 6: 40 moves / 120 min"
13350 </dest> 13392 </dest>
13351 <voice> 13393 <voice>
13352 *: "Level 6: 40 moves per 120 minutes" 13394 *: "Level 6, 40 moves per 120 minutes"
13353 </voice> 13395 </voice>
13354</phrase> 13396</phrase>
13355<phrase> 13397<phrase>
@@ -13363,7 +13405,7 @@
13363 *: "Level 7: 40 moves / 240 min" 13405 *: "Level 7: 40 moves / 240 min"
13364 </dest> 13406 </dest>
13365 <voice> 13407 <voice>
13366 *: "Level 7: 40 moves per 240 minutes" 13408 *: "Level 7, 40 moves per 240 minutes"
13367 </voice> 13409 </voice>
13368</phrase> 13410</phrase>
13369<phrase> 13411<phrase>
@@ -13377,7 +13419,7 @@
13377 *: "Level 8: 1 move / 15 min" 13419 *: "Level 8: 1 move / 15 min"
13378 </dest> 13420 </dest>
13379 <voice> 13421 <voice>
13380 *: "Level 8: 1 move per 15 minutes" 13422 *: "Level 8, 1 move per 15 minutes"
13381 </voice> 13423 </voice>
13382</phrase> 13424</phrase>
13383<phrase> 13425<phrase>
@@ -13391,7 +13433,7 @@
13391 *: "Level 9: 1 move / 60 min" 13433 *: "Level 9: 1 move / 60 min"
13392 </dest> 13434 </dest>
13393 <voice> 13435 <voice>
13394 *: "Level 9: 1 move per 60 minutes" 13436 *: "Level 9, 1 move per 60 minutes"
13395 </voice> 13437 </voice>
13396</phrase> 13438</phrase>
13397<phrase> 13439<phrase>
@@ -13405,7 +13447,7 @@
13405 *: "Level 10: 1 move / 600 min" 13447 *: "Level 10: 1 move / 600 min"
13406 </dest> 13448 </dest>
13407 <voice> 13449 <voice>
13408 *: "Level 10: 1 move per 600 minutes" 13450 *: "Level 10, 1 move per 600 minutes"
13409 </voice> 13451 </voice>
13410</phrase> 13452</phrase>
13411<phrase> 13453<phrase>
@@ -15077,16 +15119,13 @@
15077 desc: prefix for elapsed playtime announcement 15119 desc: prefix for elapsed playtime announcement
15078 user: core 15120 user: core
15079 <source> 15121 <source>
15080 *: none 15122 *: "Elapsed"
15081 hotkey: "Elapsed"
15082 </source> 15123 </source>
15083 <dest> 15124 <dest>
15084 *: none 15125 *: "Elapsed"
15085 hotkey: "Elapsed"
15086 </dest> 15126 </dest>
15087 <voice> 15127 <voice>
15088 *: none 15128 *: "Elapsed"
15089 hotkey: "Elapsed"
15090 </voice> 15129 </voice>
15091</phrase> 15130</phrase>
15092<phrase> 15131<phrase>
@@ -16486,3 +16525,45 @@
16486 *: none 16525 *: none
16487 </voice> 16526 </voice>
16488</phrase> 16527</phrase>
16528<phrase>
16529 id: LANG_CHOOSE_FILE
16530 desc: file_picker plugin ask user to select a file
16531 user: core
16532 <source>
16533 *: "Choose File"
16534 </source>
16535 <dest>
16536 *: "Choose File"
16537 </dest>
16538 <voice>
16539 *: "Choose File"
16540 </voice>
16541</phrase>
16542<phrase>
16543 id: LANG_DISABLE_MAINMENU_SCROLLING
16544 desc: Disable main menu scrolling
16545 user: core
16546 <source>
16547 *: "Disable main menu scrolling"
16548 </source>
16549 <dest>
16550 *: "Disable main menu scrolling"
16551 </dest>
16552 <voice>
16553 *: "Disable main menu scrolling"
16554 </voice>
16555</phrase>
16556<phrase>
16557 id: LANG_REMAINING
16558 desc: Playing Time
16559 user: core
16560 <source>
16561 *: "Remaining"
16562 </source>
16563 <dest>
16564 *: "Remaining"
16565 </dest>
16566 <voice>
16567 *: "Remaining"
16568 </voice>
16569</phrase>
diff --git a/apps/lang/francais.lang b/apps/lang/francais.lang
index 55b7ae9f63..45f1fc55db 100644
--- a/apps/lang/francais.lang
+++ b/apps/lang/francais.lang
@@ -32,6 +32,7 @@
32# - Clément Pit-Claudel 32# - Clément Pit-Claudel
33# - Michaël Burtin 33# - Michaël Burtin
34# - Olivier Kaloudoff 34# - Olivier Kaloudoff
35# - Jaussoin Timothée
35# 36#
36# Original comments below: 37# Original comments below:
37# 38#
@@ -182,7 +183,7 @@
182 *: "Chargement... %d%% fait (%s)" 183 *: "Chargement... %d%% fait (%s)"
183 </dest> 184 </dest>
184 <voice> 185 <voice>
185 *: "" 186 *: "Chargement... %d%% fait (%s)"
186 </voice> 187 </voice>
187</phrase> 188</phrase>
188<phrase> 189<phrase>
@@ -276,18 +277,15 @@
276 <source> 277 <source>
277 *: "PLAY = Yes" 278 *: "PLAY = Yes"
278 cowond2*: "MENU, or top-right = Yes" 279 cowond2*: "MENU, or top-right = Yes"
279 creativezen*: "SELECT = Yes" 280 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
280 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
281 iriverh100,iriverh120,iriverh300: "NAVI = Yes" 281 iriverh100,iriverh120,iriverh300: "NAVI = Yes"
282 mrobe500: "PLAY, POWER, or top-right = Yes" 282 mrobe500: "PLAY, POWER, or top-right = Yes"
283 vibe500: "OK = Yes" 283 vibe500: "OK = Yes"
284 </source> 284 </source>
285 <dest> 285 <dest>
286 *: "PLAY = Oui" 286 *: "PLAY = Oui"
287 archosplayer: "(PLAY/STOP)"
288 cowond2*: "MENU ou en haut à gauche = Oui" 287 cowond2*: "MENU ou en haut à gauche = Oui"
289 creativezen*: "SELECT = Oui" 288 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Oui"
290 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Oui"
291 iriverh100,iriverh120,iriverh300: "NAVI = Oui" 289 iriverh100,iriverh120,iriverh300: "NAVI = Oui"
292 mrobe500: "PLAY, POWER, ou en haut à gauche = Oui" 290 mrobe500: "PLAY, POWER, ou en haut à gauche = Oui"
293 vibe500: "OK = Oui" 291 vibe500: "OK = Oui"
@@ -305,11 +303,9 @@
305 </source> 303 </source>
306 <dest> 304 <dest>
307 *: "Autres = Non" 305 *: "Autres = Non"
308 archosplayer: none
309 </dest> 306 </dest>
310 <voice> 307 <voice>
311 *: "" 308 *: ""
312 archosplayer: none
313 </voice> 309 </voice>
314</phrase> 310</phrase>
315<phrase> 311<phrase>
@@ -571,20 +567,6 @@
571 </voice> 567 </voice>
572</phrase> 568</phrase>
573<phrase> 569<phrase>
574 id: LANG_BOOKMARK_CONTEXT_DELETE
575 desc: bookmark context menu, delete this bookmark
576 user: core
577 <source>
578 *: "Delete"
579 </source>
580 <dest>
581 *: "Supprimer"
582 </dest>
583 <voice>
584 *: "Supprimer"
585 </voice>
586</phrase>
587<phrase>
588 id: LANG_AUTO_BOOKMARK_QUERY 570 id: LANG_AUTO_BOOKMARK_QUERY
589 desc: prompt for user to decide to create an bookmark 571 desc: prompt for user to decide to create an bookmark
590 user: core 572 user: core
@@ -726,7 +708,7 @@
726</phrase> 708</phrase>
727<phrase> 709<phrase>
728 id: LANG_CHANNEL_STEREO 710 id: LANG_CHANNEL_STEREO
729 desc: in sound_settings 711 desc: in sound_settings and radio screen
730 user: core 712 user: core
731 <source> 713 <source>
732 *: "Stereo" 714 *: "Stereo"
@@ -740,7 +722,7 @@
740</phrase> 722</phrase>
741<phrase> 723<phrase>
742 id: LANG_CHANNEL_MONO 724 id: LANG_CHANNEL_MONO
743 desc: in sound_settings 725 desc: in sound_settings and radio screen
744 user: core 726 user: core
745 <source> 727 <source>
746 *: "Mono" 728 *: "Mono"
@@ -1022,20 +1004,6 @@
1022 </voice> 1004 </voice>
1023</phrase> 1005</phrase>
1024<phrase> 1006<phrase>
1025 id: LANG_EQUALIZER_EDIT_MODE
1026 desc: in the equalizer settings menu
1027 user: core
1028 <source>
1029 *: "Edit mode: %s"
1030 </source>
1031 <dest>
1032 *: "Mode édition : %s"
1033 </dest>
1034 <voice>
1035 *: ""
1036 </voice>
1037</phrase>
1038<phrase>
1039 id: LANG_EQUALIZER_GAIN_ITEM 1007 id: LANG_EQUALIZER_GAIN_ITEM
1040 desc: in the equalizer settings menu 1008 desc: in the equalizer settings menu
1041 user: core 1009 user: core
@@ -1527,7 +1495,7 @@
1527 *: "ReplayGain" 1495 *: "ReplayGain"
1528 </dest> 1496 </dest>
1529 <voice> 1497 <voice>
1530 *: "Replaygain" 1498 *: "ReplayGain"
1531 </voice> 1499 </voice>
1532</phrase> 1500</phrase>
1533<phrase> 1501<phrase>
@@ -1717,16 +1685,16 @@
1717</phrase> 1685</phrase>
1718<phrase> 1686<phrase>
1719 id: LANG_AUDIOSCROBBLER 1687 id: LANG_AUDIOSCROBBLER
1720 desc: "Last.fm Log" in the playback menu 1688 desc: "Last.fm Logger" in Plugin/apps/scrobbler
1721 user: core 1689 user: core
1722 <source> 1690 <source>
1723 *: "Last.fm Log" 1691 *: "Last.fm Logger"
1724 </source> 1692 </source>
1725 <dest> 1693 <dest>
1726 *: "Log Last.fm" 1694 *: "Journaux Last.fm"
1727 </dest> 1695 </dest>
1728 <voice> 1696 <voice>
1729 *: "Log Last.fm" 1697 *: "Journaux Last.fm"
1730 </voice> 1698 </voice>
1731</phrase> 1699</phrase>
1732<phrase> 1700<phrase>
@@ -2005,6 +1973,34 @@
2005 </voice> 1973 </voice>
2006</phrase> 1974</phrase>
2007<phrase> 1975<phrase>
1976 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
1977 desc: track display options
1978 user: core
1979 <source>
1980 *: "Title & Album from ID3 tags"
1981 </source>
1982 <dest>
1983 *: "Titre & Album depuis les tags ID3"
1984 </dest>
1985 <voice>
1986 *: "Titre et Album depuis les tags"
1987 </voice>
1988</phrase>
1989<phrase>
1990 id: LANG_DISPLAY_TITLE_FROMTAGS
1991 desc: track display options
1992 user: core
1993 <source>
1994 *: "Title from ID3 tags"
1995 </source>
1996 <dest>
1997 *: "Titre depuis les tags ID3"
1998 </dest>
1999 <voice>
2000 *: "Titre depuis les tags"
2001 </voice>
2002</phrase>
2003<phrase>
2008 id: LANG_BUILDING_DATABASE 2004 id: LANG_BUILDING_DATABASE
2009 desc: splash database building progress 2005 desc: splash database building progress
2010 user: core 2006 user: core
@@ -2017,7 +2013,6 @@
2017 </source> 2013 </source>
2018 <dest> 2014 <dest>
2019 *: "Création base de données... %d trouvés (OFF = retour)" 2015 *: "Création base de données... %d trouvés (OFF = retour)"
2020 archosplayer: "Création BD %d trouvés"
2021 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Création base de données... %d trouvés (LEFT = retour)" 2016 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Création base de données... %d trouvés (LEFT = retour)"
2022 gogearsa9200: "Création base de données... %d trouvés (REW = retour)" 2017 gogearsa9200: "Création base de données... %d trouvés (REW = retour)"
2023 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Création base de données... %d trouvés (PREV = retour)" 2018 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Création base de données... %d trouvés (PREV = retour)"
@@ -2028,6 +2023,20 @@
2028 </voice> 2023 </voice>
2029</phrase> 2024</phrase>
2030<phrase> 2025<phrase>
2026 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
2027 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
2028 user: core
2029 <source>
2030 *: "Selection too big, %d random tracks will be selected"
2031 </source>
2032 <dest>
2033 *: "Selection trop grande, %d pistes seront sélectionnées aléatoirement"
2034 </dest>
2035 <voice>
2036 *: "Selection trop grande, donc des pistes seront sélectionnées aléatoirement"
2037 </voice>
2038</phrase>
2039<phrase>
2031 id: LANG_TAGCACHE_RAM 2040 id: LANG_TAGCACHE_RAM
2032 desc: in tag cache settings 2041 desc: in tag cache settings
2033 user: core 2042 user: core
@@ -2293,15 +2302,15 @@
2293 desc: in lcd settings 2302 desc: in lcd settings
2294 user: core 2303 user: core
2295 <source> 2304 <source>
2296 *: none 2305 *: "Backlight on Lock"
2297 hold_button: "Backlight on Hold" 2306 hold_button: "Backlight on Hold"
2298 </source> 2307 </source>
2299 <dest> 2308 <dest>
2300 *: none 2309 *: "Backlight on Lock"
2301 hold_button: "Rétro-éclairage quand touches verrouillées" 2310 hold_button: "Rétro-éclairage quand touches verrouillées"
2302 </dest> 2311 </dest>
2303 <voice> 2312 <voice>
2304 *: none 2313 *: "Backlight on Lock"
2305 hold_button: "Rétro-éclairage quand touches verrouillées" 2314 hold_button: "Rétro-éclairage quand touches verrouillées"
2306 </voice> 2315 </voice>
2307</phrase> 2316</phrase>
@@ -2970,11 +2979,9 @@
2970 </source> 2979 </source>
2971 <dest> 2980 <dest>
2972 *: "Indicateur de niveau des pics" 2981 *: "Indicateur de niveau des pics"
2973 masd: none
2974 </dest> 2982 </dest>
2975 <voice> 2983 <voice>
2976 *: "Indicateur de niveau des pics" 2984 *: "Indicateur de niveau des pics"
2977 masd: none
2978 </voice> 2985 </voice>
2979</phrase> 2986</phrase>
2980<phrase> 2987<phrase>
@@ -2986,11 +2993,9 @@
2986 </source> 2993 </source>
2987 <dest> 2994 <dest>
2988 *: "Persistance marqueur seuil max." 2995 *: "Persistance marqueur seuil max."
2989 masd: none
2990 </dest> 2996 </dest>
2991 <voice> 2997 <voice>
2992 *: "Persistance marqueur seuil max." 2998 *: "Persistance marqueur seuil max."
2993 masd: none
2994 </voice> 2999 </voice>
2995</phrase> 3000</phrase>
2996<phrase> 3001<phrase>
@@ -3002,11 +3007,9 @@
3002 </source> 3007 </source>
3003 <dest> 3008 <dest>
3004 *: "Temps de persistance du pic" 3009 *: "Temps de persistance du pic"
3005 masd: none
3006 </dest> 3010 </dest>
3007 <voice> 3011 <voice>
3008 *: "Temps de persistance du pic" 3012 *: "Temps de persistance du pic"
3009 masd: none
3010 </voice> 3013 </voice>
3011</phrase> 3014</phrase>
3012<phrase> 3015<phrase>
@@ -3018,11 +3021,9 @@
3018 </source> 3021 </source>
3019 <dest> 3022 <dest>
3020 *: "Infini" 3023 *: "Infini"
3021 masd: none
3022 </dest> 3024 </dest>
3023 <voice> 3025 <voice>
3024 *: "Infini" 3026 *: "Infini"
3025 masd: none
3026 </voice> 3027 </voice>
3027</phrase> 3028</phrase>
3028<phrase> 3029<phrase>
@@ -3034,11 +3035,9 @@
3034 </source> 3035 </source>
3035 <dest> 3036 <dest>
3036 *: "Taux de baisse de l'indicateur" 3037 *: "Taux de baisse de l'indicateur"
3037 masd: none
3038 </dest> 3038 </dest>
3039 <voice> 3039 <voice>
3040 *: "Taux de baisse de l'indicateur" 3040 *: "Taux de baisse de l'indicateur"
3041 masd: none
3042 </voice> 3041 </voice>
3043</phrase> 3042</phrase>
3044<phrase> 3043<phrase>
@@ -3050,11 +3049,9 @@
3050 </source> 3049 </source>
3051 <dest> 3050 <dest>
3052 *: "Echelle" 3051 *: "Echelle"
3053 masd: none
3054 </dest> 3052 </dest>
3055 <voice> 3053 <voice>
3056 *: "échelle" 3054 *: "échelle"
3057 masd: none
3058 </voice> 3055 </voice>
3059</phrase> 3056</phrase>
3060<phrase> 3057<phrase>
@@ -3066,11 +3063,9 @@
3066 </source> 3063 </source>
3067 <dest> 3064 <dest>
3068 *: "Logarithmique (dB)" 3065 *: "Logarithmique (dB)"
3069 masd: none
3070 </dest> 3066 </dest>
3071 <voice> 3067 <voice>
3072 *: "Logarithmique en décibel" 3068 *: "Logarithmique en décibel"
3073 masd: none
3074 </voice> 3069 </voice>
3075</phrase> 3070</phrase>
3076<phrase> 3071<phrase>
@@ -3082,11 +3077,9 @@
3082 </source> 3077 </source>
3083 <dest> 3078 <dest>
3084 *: "Linéaire (%)" 3079 *: "Linéaire (%)"
3085 masd: none
3086 </dest> 3080 </dest>
3087 <voice> 3081 <voice>
3088 *: "Linéaire en pourcentage" 3082 *: "Linéaire en pourcentage"
3089 masd: none
3090 </voice> 3083 </voice>
3091</phrase> 3084</phrase>
3092<phrase> 3085<phrase>
@@ -3098,11 +3091,9 @@
3098 </source> 3091 </source>
3099 <dest> 3092 <dest>
3100 *: "Minimum de l'intervalle" 3093 *: "Minimum de l'intervalle"
3101 masd: none
3102 </dest> 3094 </dest>
3103 <voice> 3095 <voice>
3104 *: "Minimum de l'intervalle" 3096 *: "Minimum de l'intervalle"
3105 masd: none
3106 </voice> 3097 </voice>
3107</phrase> 3098</phrase>
3108<phrase> 3099<phrase>
@@ -3114,11 +3105,9 @@
3114 </source> 3105 </source>
3115 <dest> 3106 <dest>
3116 *: "Maximum de l'intervalle" 3107 *: "Maximum de l'intervalle"
3117 masd: none
3118 </dest> 3108 </dest>
3119 <voice> 3109 <voice>
3120 *: "Maximum de l'intervalle" 3110 *: "Maximum de l'intervalle"
3121 masd: none
3122 </voice> 3111 </voice>
3123</phrase> 3112</phrase>
3124<phrase> 3113<phrase>
@@ -3448,14 +3437,17 @@
3448 <source> 3437 <source>
3449 *: none 3438 *: none
3450 battery_types: "Alkaline" 3439 battery_types: "Alkaline"
3440 xduoox3: "Newer (2000 mAh)"
3451 </source> 3441 </source>
3452 <dest> 3442 <dest>
3453 *: none 3443 *: none
3454 battery_types: "Alcaline" 3444 battery_types: "Alcaline"
3445 xduoox3: "Nouveau (2000 mAh)"
3455 </dest> 3446 </dest>
3456 <voice> 3447 <voice>
3457 *: none 3448 *: none
3458 battery_types: "Alcaline" 3449 battery_types: "Alcaline"
3450 xduoox3: "Nouveau 2000 milliampère heure"
3459 </voice> 3451 </voice>
3460</phrase> 3452</phrase>
3461<phrase> 3453<phrase>
@@ -3465,14 +3457,17 @@
3465 <source> 3457 <source>
3466 *: none 3458 *: none
3467 battery_types: "NiMH" 3459 battery_types: "NiMH"
3460 xduoox3: "Older (1500 mAh)"
3468 </source> 3461 </source>
3469 <dest> 3462 <dest>
3470 *: none 3463 *: none
3471 battery_types: "NiMH" 3464 battery_types: "NiMH"
3465 xduoox3: "Nouveau (1500 mAh)"
3472 </dest> 3466 </dest>
3473 <voice> 3467 <voice>
3474 *: none 3468 *: none
3475 battery_types: "Nickel metal hydride" 3469 battery_types: "Nickel metal hydride"
3470 xduoox3: "Nouveau 1500 milliampère heure"
3476 </voice> 3471 </voice>
3477</phrase> 3472</phrase>
3478<phrase> 3473<phrase>
@@ -3662,7 +3657,7 @@
3662 </dest> 3657 </dest>
3663 <voice> 3658 <voice>
3664 *: none 3659 *: none
3665 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,sansac200*,sansae200*: "" 3660 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
3666 </voice> 3661 </voice>
3667</phrase> 3662</phrase>
3668<phrase> 3663<phrase>
@@ -4108,23 +4103,6 @@
4108 </voice> 4103 </voice>
4109</phrase> 4104</phrase>
4110<phrase> 4105<phrase>
4111 id: LANG_ALARM_MOD_SHUTDOWN
4112 desc: The text that tells the user that the alarm time is ok and the device shuts off (for the RTC alarm mod).
4113 user: core
4114 <source>
4115 *: none
4116 alarm: "Alarm Set"
4117 </source>
4118 <dest>
4119 *: none
4120 alarm: "Réveil activé"
4121 </dest>
4122 <voice>
4123 *: none
4124 alarm: "Réveil activé"
4125 </voice>
4126</phrase>
4127<phrase>
4128 id: LANG_ALARM_MOD_ERROR 4106 id: LANG_ALARM_MOD_ERROR
4129 desc: The text that tells that the time is incorrect (for the RTC alarm mod). 4107 desc: The text that tells that the time is incorrect (for the RTC alarm mod).
4130 user: core 4108 user: core
@@ -4142,35 +4120,6 @@
4142 </voice> 4120 </voice>
4143</phrase> 4121</phrase>
4144<phrase> 4122<phrase>
4145 id: LANG_ALARM_MOD_KEYS
4146 desc: Shown key functions in alarm menu (for the RTC alarm mod).
4147 user: core
4148 <source>
4149 *: none
4150 alarm: "PLAY=Set OFF=Cancel"
4151 gigabeats: "SELECT=Set POWER=Cancel"
4152 ipod*: "SELECT=Set MENU=Cancel"
4153 iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
4154 mpiohd300: "ENTER=Set MENU=Cancel"
4155 sansafuzeplus: "SELECT=Set BACK=Cancel"
4156 vibe500: "OK=Set C=Cancel"
4157 </source>
4158 <dest>
4159 *: none
4160 alarm: "PLAY=Valider OFF=Annuler"
4161 gigabeats: "SELECT=Valider POWER=Annuler"
4162 ipod*: "SELECT=Valider MENU=Annuler"
4163 iriverh10,iriverh10_5gb: "SELECT=Valider PREV=Annuler"
4164 mpiohd300: "ENTER=Valider MENU=Annuler"
4165 sansafuzeplus: "SELECT=Valider BACK=Annuler"
4166 vibe500: "OK=Valider C=Annuler"
4167 </dest>
4168 <voice>
4169 *: none
4170 alarm,ipod*: ""
4171 </voice>
4172</phrase>
4173<phrase>
4174 id: LANG_ALARM_MOD_DISABLE 4123 id: LANG_ALARM_MOD_DISABLE
4175 desc: Announce that the RTC alarm has been turned off 4124 desc: Announce that the RTC alarm has been turned off
4176 user: core 4125 user: core
@@ -5366,40 +5315,6 @@
5366 </voice> 5315 </voice>
5367</phrase> 5316</phrase>
5368<phrase> 5317<phrase>
5369 id: LANG_RECORD_DIRECTORY
5370 desc: in recording settings_menu
5371 user: core
5372 <source>
5373 *: none
5374 recording: "Directory"
5375 </source>
5376 <dest>
5377 *: none
5378 recording: "Répertoire"
5379 </dest>
5380 <voice>
5381 *: none
5382 recording: "Répertoire"
5383 </voice>
5384</phrase>
5385<phrase>
5386 id: LANG_SET_AS_REC_DIR
5387 desc: used in the onplay menu to set a recording dir
5388 user: core
5389 <source>
5390 *: none
5391 recording: "Set As Recording Directory"
5392 </source>
5393 <dest>
5394 *: none
5395 recording: "Choisir comme dossier d'enreg."
5396 </dest>
5397 <voice>
5398 *: none
5399 recording: "Choisir comme dossier d'enregistrement"
5400 </voice>
5401</phrase>
5402<phrase>
5403 id: LANG_CLEAR_REC_DIR 5318 id: LANG_CLEAR_REC_DIR
5404 desc: 5319 desc:
5405 user: core 5320 user: core
@@ -6023,7 +5938,6 @@
6023 </source> 5938 </source>
6024 <dest> 5939 <dest>
6025 *: "Tampon:" 5940 *: "Tampon:"
6026 archosplayer: "Tamp.:"
6027 </dest> 5941 </dest>
6028 <voice> 5942 <voice>
6029 *: "Tampon" 5943 *: "Tampon"
@@ -6036,12 +5950,10 @@
6036 <source> 5950 <source>
6037 *: "Battery: %d%% %dh %dm" 5951 *: "Battery: %d%% %dh %dm"
6038 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm" 5952 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
6039 iriverifp7xx: "%d%% %dh %dm"
6040 </source> 5953 </source>
6041 <dest> 5954 <dest>
6042 *: "Batterie: %d%% %dh %dm" 5955 *: "Batterie: %d%% %dh %dm"
6043 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm" 5956 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
6044 iriverifp7xx: "%d%% %dh %dm"
6045 </dest> 5957 </dest>
6046 <voice> 5958 <voice>
6047 *: "Niveau de la batterie" 5959 *: "Niveau de la batterie"
@@ -6081,14 +5993,17 @@
6081 user: core 5993 user: core
6082 <source> 5994 <source>
6083 *: "Int:" 5995 *: "Int:"
5996 hibylinux: "mSD:"
6084 xduoox3: "mSD1:" 5997 xduoox3: "mSD1:"
6085 </source> 5998 </source>
6086 <dest> 5999 <dest>
6087 *: "Int:" 6000 *: "Int:"
6001 hibylinux: "mSD:"
6088 xduoox3: "mSD1:" 6002 xduoox3: "mSD1:"
6089 </dest> 6003 </dest>
6090 <voice> 6004 <voice>
6091 *: "Interne" 6005 *: "Interne"
6006 hibylinux: "micro S D"
6092 xduoox3: "micro S D 1" 6007 xduoox3: "micro S D 1"
6093 </voice> 6008 </voice>
6094</phrase> 6009</phrase>
@@ -6098,20 +6013,21 @@
6098 user: core 6013 user: core
6099 <source> 6014 <source>
6100 *: none 6015 *: none
6101 multivolume: "HD1" 6016 hibylinux: "USB:"
6017 multivolume: "HD1:"
6102 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 6018 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
6103 xduoox3: "mSD2:" 6019 xduoox3: "mSD2:"
6104 </source> 6020 </source>
6105 <dest> 6021 <dest>
6106 *: none 6022 *: none
6107 archosondio*: "MMC:" 6023 hibylinux: "USB:"
6108 multivolume: "DD1" 6024 multivolume: "DD1"
6109 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 6025 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
6110 xduoox3: "mSD2:" 6026 xduoox3: "mSD2:"
6111 </dest> 6027 </dest>
6112 <voice> 6028 <voice>
6113 *: none 6029 *: none
6114 archosondio*: "M M C" 6030 hibylinux: "U S B"
6115 multivolume: "D D 1" 6031 multivolume: "D D 1"
6116 sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D" 6032 sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
6117 xduoox3: "micro S D 2" 6033 xduoox3: "micro S D 2"
@@ -6202,62 +6118,6 @@
6202 </voice> 6118 </voice>
6203</phrase> 6119</phrase>
6204<phrase> 6120<phrase>
6205 id: LANG_INSERT
6206 desc: in onplay menu. insert a track/playlist into dynamic playlist.
6207 user: core
6208 <source>
6209 *: "Insert"
6210 </source>
6211 <dest>
6212 *: "Insérer"
6213 </dest>
6214 <voice>
6215 *: "Insérer"
6216 </voice>
6217</phrase>
6218<phrase>
6219 id: LANG_INSERT_FIRST
6220 desc: in onplay menu. insert a track/playlist into dynamic playlist.
6221 user: core
6222 <source>
6223 *: "Insert Next"
6224 </source>
6225 <dest>
6226 *: "Insérer suivant"
6227 </dest>
6228 <voice>
6229 *: "Insérer suivant"
6230 </voice>
6231</phrase>
6232<phrase>
6233 id: LANG_INSERT_LAST
6234 desc: in onplay menu. append a track/playlist into dynamic playlist.
6235 user: core
6236 <source>
6237 *: "Insert Last"
6238 </source>
6239 <dest>
6240 *: "Insérer en dernier"
6241 </dest>
6242 <voice>
6243 *: "Insérer en dernier"
6244 </voice>
6245</phrase>
6246<phrase>
6247 id: LANG_INSERT_SHUFFLED
6248 desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
6249 user: core
6250 <source>
6251 *: "Insert Shuffled"
6252 </source>
6253 <dest>
6254 *: "Insérer mélangé"
6255 </dest>
6256 <voice>
6257 *: "Insérer mélangé"
6258 </voice>
6259</phrase>
6260<phrase>
6261 id: LANG_QUEUE 6121 id: LANG_QUEUE
6262 desc: The verb/action Queue 6122 desc: The verb/action Queue
6263 user: core 6123 user: core
@@ -6314,20 +6174,6 @@
6314 </voice> 6174 </voice>
6315</phrase> 6175</phrase>
6316<phrase> 6176<phrase>
6317 id: LANG_REPLACE
6318 desc: in onplay menu. Replace the current playlist with a new one.
6319 user: core
6320 <source>
6321 *: "Play Next"
6322 </source>
6323 <dest>
6324 *: "Lire suivant (remplacer)"
6325 </dest>
6326 <voice>
6327 *: "Lire suivant (remplacer)"
6328 </voice>
6329</phrase>
6330<phrase>
6331 id: LANG_PLAYLIST_INSERT_COUNT 6177 id: LANG_PLAYLIST_INSERT_COUNT
6332 desc: splash number of tracks inserted 6178 desc: splash number of tracks inserted
6333 user: core 6179 user: core
@@ -6394,7 +6240,7 @@
6394 *: "Recherche... %d trouvé (%s)" 6240 *: "Recherche... %d trouvé (%s)"
6395 </dest> 6241 </dest>
6396 <voice> 6242 <voice>
6397 *: "" 6243 *: "Recherche... %d trouvé (%s)"
6398 </voice> 6244 </voice>
6399</phrase> 6245</phrase>
6400<phrase> 6246<phrase>
@@ -6412,34 +6258,6 @@
6412 </voice> 6258 </voice>
6413</phrase> 6259</phrase>
6414<phrase> 6260<phrase>
6415 id: LANG_CATALOG_VIEW
6416 desc: in onplay playlist catalogue submenu
6417 user: core
6418 <source>
6419 *: "View Catalogue"
6420 </source>
6421 <dest>
6422 *: "Afficher le catalogue"
6423 </dest>
6424 <voice>
6425 *: "Afficher le catalogue"
6426 </voice>
6427</phrase>
6428<phrase>
6429 id: LANG_CATALOG_ADD_TO
6430 desc: in onplay playlist catalogue submenu
6431 user: core
6432 <source>
6433 *: "Add to Playlist"
6434 </source>
6435 <dest>
6436 *: "Ajouter à la liste de lecture"
6437 </dest>
6438 <voice>
6439 *: "Ajouter à la liste de lecture"
6440 </voice>
6441</phrase>
6442<phrase>
6443 id: LANG_CATALOG_ADD_TO_NEW 6261 id: LANG_CATALOG_ADD_TO_NEW
6444 desc: in onplay playlist catalogue submenu 6262 desc: in onplay playlist catalogue submenu
6445 user: core 6263 user: core
@@ -6790,20 +6608,6 @@
6790 </voice> 6608 </voice>
6791</phrase> 6609</phrase>
6792<phrase> 6610<phrase>
6793 id: LANG_ID3_ALBUM_GAIN
6794 desc: in tag viewer
6795 user: core
6796 <source>
6797 *: "Album Gain"
6798 </source>
6799 <dest>
6800 *: "Gain par album"
6801 </dest>
6802 <voice>
6803 *: "Gain par album"
6804 </voice>
6805</phrase>
6806<phrase>
6807 id: LANG_ID3_PATH 6611 id: LANG_ID3_PATH
6808 desc: in tag viewer 6612 desc: in tag viewer
6809 user: core 6613 user: core
@@ -7180,7 +6984,6 @@
7180 </source> 6984 </source>
7181 <dest> 6985 <dest>
7182 *: "Fin de la liste de lecture" 6986 *: "Fin de la liste de lecture"
7183 archosplayer: "Fin de la liste"
7184 </dest> 6987 </dest>
7185 <voice> 6988 <voice>
7186 *: "Fin de la liste de lecture" 6989 *: "Fin de la liste de lecture"
@@ -7215,20 +7018,6 @@
7215 </voice> 7018 </voice>
7216</phrase> 7019</phrase>
7217<phrase> 7020<phrase>
7218 id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR
7219 desc: Playlist error
7220 user: core
7221 <source>
7222 *: "Error updating playlist control file"
7223 </source>
7224 <dest>
7225 *: "Erreur mise à jour fichier de contrôle de liste de lecture"
7226 </dest>
7227 <voice>
7228 *: "Erreur mise à jour fichier de contrôle de liste de lecture"
7229 </voice>
7230</phrase>
7231<phrase>
7232 id: LANG_PLAYLIST_ACCESS_ERROR 7021 id: LANG_PLAYLIST_ACCESS_ERROR
7233 desc: Playlist error 7022 desc: Playlist error
7234 user: core 7023 user: core
@@ -7309,7 +7098,7 @@
7309 *: "Mode:" 7098 *: "Mode:"
7310 </dest> 7099 </dest>
7311 <voice> 7100 <voice>
7312 *: "Mode:" 7101 *: "Mode"
7313 </voice> 7102 </voice>
7314</phrase> 7103</phrase>
7315<phrase> 7104<phrase>
@@ -7410,24 +7199,7 @@
7410 </dest> 7199 </dest>
7411 <voice> 7200 <voice>
7412 *: none 7201 *: none
7413 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,sansac200*,sansae200*: "" 7202 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
7414 </voice>
7415</phrase>
7416<phrase>
7417 id: LANG_DB_INF
7418 desc: -inf db for values below measurement
7419 user: core
7420 <source>
7421 *: none
7422 recording: "-inf"
7423 </source>
7424 <dest>
7425 *: none
7426 recording: "-inf"
7427 </dest>
7428 <voice>
7429 *: none
7430 recording: "Inifini négatif"
7431 </voice> 7203 </voice>
7432</phrase> 7204</phrase>
7433<phrase> 7205<phrase>
@@ -7551,7 +7323,7 @@
7551 *: "Modèle incompatible" 7323 *: "Modèle incompatible"
7552 </dest> 7324 </dest>
7553 <voice> 7325 <voice>
7554 *: "" 7326 *: "Modèle incompatible"
7555 </voice> 7327 </voice>
7556</phrase> 7328</phrase>
7557<phrase> 7329<phrase>
@@ -7565,7 +7337,7 @@
7565 *: "Version incompatible" 7337 *: "Version incompatible"
7566 </dest> 7338 </dest>
7567 <voice> 7339 <voice>
7568 *: "" 7340 *: "Version incompatible"
7569 </voice> 7341 </voice>
7570</phrase> 7342</phrase>
7571<phrase> 7343<phrase>
@@ -7576,10 +7348,10 @@
7576 *: "Plugin returned error" 7348 *: "Plugin returned error"
7577 </source> 7349 </source>
7578 <dest> 7350 <dest>
7579 *: "Erreur Retournée par le module d'extension" 7351 *: "Erreur retournée par le module"
7580 </dest> 7352 </dest>
7581 <voice> 7353 <voice>
7582 *: "" 7354 *: "Erreur retournée par le module"
7583 </voice> 7355 </voice>
7584</phrase> 7356</phrase>
7585<phrase> 7357<phrase>
@@ -7656,23 +7428,6 @@
7656 </voice> 7428 </voice>
7657</phrase> 7429</phrase>
7658<phrase> 7430<phrase>
7659 id: LANG_BATTERY_TRICKLE_CHARGE
7660 desc: in info display, shows that trickle charge is running
7661 user: core
7662 <source>
7663 *: none
7664 charging: "Battery: Trickle Chg"
7665 </source>
7666 <dest>
7667 *: none
7668 charging: "Batterie: charge persistante"
7669 </dest>
7670 <voice>
7671 *: none
7672 charging: "Charge persistante de la batterie"
7673 </voice>
7674</phrase>
7675<phrase>
7676 id: LANG_WARNING_BATTERY_LOW 7431 id: LANG_WARNING_BATTERY_LOW
7677 desc: general warning 7432 desc: general warning
7678 user: core 7433 user: core
@@ -10573,20 +10328,6 @@
10573 </voice> 10328 </voice>
10574</phrase> 10329</phrase>
10575<phrase> 10330<phrase>
10576 id: LANG_SCROLLBAR_POSITION
10577 desc: in Settings -> General -> Display -> Status-/Scrollbar
10578 user: core
10579 <source>
10580 *: "Scroll Bar Position"
10581 </source>
10582 <dest>
10583 *: "Position barre de défilement"
10584 </dest>
10585 <voice>
10586 *: "Position de la barre de défilement"
10587 </voice>
10588</phrase>
10589<phrase>
10590 id: LANG_COMPRESSOR 10331 id: LANG_COMPRESSOR
10591 desc: in sound settings 10332 desc: in sound settings
10592 user: core 10333 user: core
@@ -10848,20 +10589,6 @@
10848 </voice> 10589 </voice>
10849</phrase> 10590</phrase>
10850<phrase> 10591<phrase>
10851 id: LANG_STATUSBAR_CUSTOM
10852 desc: if this translation is compatible with LANG_CHANNEL_CUSTOM, then please use the same translation. it can be combined later then
10853 user: core
10854 <source>
10855 *: "Custom"
10856 </source>
10857 <dest>
10858 *: "Personnalisé"
10859 </dest>
10860 <voice>
10861 *: "Personnalisé"
10862 </voice>
10863</phrase>
10864<phrase>
10865 id: VOICE_EXT_SBS 10592 id: VOICE_EXT_SBS
10866 desc: spoken only, for file extension 10593 desc: spoken only, for file extension
10867 user: core 10594 user: core
@@ -10910,20 +10637,6 @@
10910 </voice> 10637 </voice>
10911</phrase> 10638</phrase>
10912<phrase> 10639<phrase>
10913 id: LANG_INSERT_LAST_SHUFFLED
10914 desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
10915 user: core
10916 <source>
10917 *: "Insert Last Shuffled"
10918 </source>
10919 <dest>
10920 *: "Insérer mélangé en fin"
10921 </dest>
10922 <voice>
10923 *: "Insérer mélangé en fin"
10924 </voice>
10925</phrase>
10926<phrase>
10927 id: LANG_QUEUE_LAST_SHUFFLED 10640 id: LANG_QUEUE_LAST_SHUFFLED
10928 desc: in onplay menu. queue a playlist randomly at end of dynamic playlist 10641 desc: in onplay menu. queue a playlist randomly at end of dynamic playlist
10929 user: core 10642 user: core
@@ -10982,7 +10695,7 @@
10982 *: "Titre suivant:" 10695 *: "Titre suivant:"
10983 </dest> 10696 </dest>
10984 <voice> 10697 <voice>
10985 *: "Titre suivant:" 10698 *: "Titre suivant"
10986 </voice> 10699 </voice>
10987</phrase> 10700</phrase>
10988<phrase> 10701<phrase>
@@ -10996,7 +10709,7 @@
10996 *: "Suivant:" 10709 *: "Suivant:"
10997 </dest> 10710 </dest>
10998 <voice> 10711 <voice>
10999 *: "Suivant:" 10712 *: "Suivant"
11000 </voice> 10713 </voice>
11001</phrase> 10714</phrase>
11002<phrase> 10715<phrase>
@@ -11517,20 +11230,6 @@
11517 </voice> 11230 </voice>
11518</phrase> 11231</phrase>
11519<phrase> 11232<phrase>
11520 id: LANG_SET_AS_START_DIR
11521 desc: used in the onplay menu to set a starting browser dir
11522 user: core
11523 <source>
11524 *: "Start File Browser Here"
11525 </source>
11526 <dest>
11527 *: "Démarrer navigation fichiers ici"
11528 </dest>
11529 <voice>
11530 *: "Démarrer navigation des fichiers ici"
11531 </voice>
11532</phrase>
11533<phrase>
11534 id: LANG_RESET_START_DIR 11233 id: LANG_RESET_START_DIR
11535 desc: reset the browser start directory 11234 desc: reset the browser start directory
11536 user: core 11235 user: core
@@ -11663,20 +11362,6 @@
11663 </voice> 11362 </voice>
11664</phrase> 11363</phrase>
11665<phrase> 11364<phrase>
11666 id: LANG_SET_AS_PLAYLISTCAT_DIR
11667 desc: used in the onplay menu to set a playlist catalogue dir
11668 user: core
11669 <source>
11670 *: "Set As Playlist Catalogue Directory"
11671 </source>
11672 <dest>
11673 *: "Sélectionner comme répertoire des catalogues de listes de lecture"
11674 </dest>
11675 <voice>
11676 *: "Sélectionner comme répertoire des catalogues de listes de lecture"
11677 </voice>
11678</phrase>
11679<phrase>
11680 id: LANG_LIST_LINE_PADDING 11365 id: LANG_LIST_LINE_PADDING
11681 desc: list padding, in display settings 11366 desc: list padding, in display settings
11682 user: core 11367 user: core
@@ -11708,20 +11393,6 @@
11708 </voice> 11393 </voice>
11709</phrase> 11394</phrase>
11710<phrase> 11395<phrase>
11711 id: LANG_AUTOMATIC
11712 desc: generic automatic
11713 user: core
11714 <source>
11715 *: "Automatic"
11716 </source>
11717 <dest>
11718 *: "Automatique"
11719 </dest>
11720 <voice>
11721 *: "Automatique"
11722 </voice>
11723</phrase>
11724<phrase>
11725 id: LANG_SLEEP_TIMER_CANCEL_CURRENT 11396 id: LANG_SLEEP_TIMER_CANCEL_CURRENT
11726 desc: shown instead of sleep timer when it's running 11397 desc: shown instead of sleep timer when it's running
11727 user: core 11398 user: core
@@ -12296,7 +11967,7 @@
12296 desc: Selective Actions 11967 desc: Selective Actions
12297 user: core 11968 user: core
12298 <source> 11969 <source>
12299 *: "Seek" 11970 *: "Exempt Seek"
12300 </source> 11971 </source>
12301 <dest> 11972 <dest>
12302 *: "Chercher" 11973 *: "Chercher"
@@ -12310,7 +11981,7 @@
12310 desc: Selective Actions 11981 desc: Selective Actions
12311 user: core 11982 user: core
12312 <source> 11983 <source>
12313 *: "Play" 11984 *: "Exempt Play"
12314 </source> 11985 </source>
12315 <dest> 11986 <dest>
12316 *: "Jouer" 11987 *: "Jouer"
@@ -12352,7 +12023,7 @@
12352 desc: Softlock behaviour setting 12023 desc: Softlock behaviour setting
12353 user: core 12024 user: core
12354 <source> 12025 <source>
12355 *: "Disable Notify" 12026 *: "Disable Locked Reminders"
12356 </source> 12027 </source>
12357 <dest> 12028 <dest>
12358 *: "Désactiver les notifications" 12029 *: "Désactiver les notifications"
@@ -12422,7 +12093,7 @@
12422 desc: Selective Actions 12093 desc: Selective Actions
12423 user: core 12094 user: core
12424 <source> 12095 <source>
12425 *: "Skip" 12096 *: "Exempt Skip"
12426 </source> 12097 </source>
12427 <dest> 12098 <dest>
12428 *: "Passer" 12099 *: "Passer"
@@ -12481,10 +12152,10 @@
12481 desc: playing time screen 12152 desc: playing time screen
12482 user: core 12153 user: core
12483 <source> 12154 <source>
12484 *: "Playlist elapsed: %s / %s %ld%%" 12155 *: "Playlist elapsed:"
12485 </source> 12156 </source>
12486 <dest> 12157 <dest>
12487 *: "Liste de lecture, temps écoulé: %s / %s %ld%%" 12158 *: "Liste de lecture, temps écoulé :"
12488 </dest> 12159 </dest>
12489 <voice> 12160 <voice>
12490 *: "Liste de lecture, temps écoulé" 12161 *: "Liste de lecture, temps écoulé"
@@ -12495,10 +12166,10 @@
12495 desc: playing time screen 12166 desc: playing time screen
12496 user: core 12167 user: core
12497 <source> 12168 <source>
12498 *: "Track remaining: %s" 12169 *: "Track remaining:"
12499 </source> 12170 </source>
12500 <dest> 12171 <dest>
12501 *: "Durée de piste restante: %s" 12172 *: "Durée de piste restante :"
12502 </dest> 12173 </dest>
12503 <voice> 12174 <voice>
12504 *: "Durée de piste restante" 12175 *: "Durée de piste restante"
@@ -12509,10 +12180,10 @@
12509 desc: playing time screen 12180 desc: playing time screen
12510 user: core 12181 user: core
12511 <source> 12182 <source>
12512 *: "Track elapsed: %s / %s %ld%%" 12183 *: "Track elapsed:"
12513 </source> 12184 </source>
12514 <dest> 12185 <dest>
12515 *: "Temps écoulé de la piste: %s / %s %ld%%" 12186 *: "Temps écoulé de la piste :"
12516 </dest> 12187 </dest>
12517 <voice> 12188 <voice>
12518 *: "Temps écoulé de la piste" 12189 *: "Temps écoulé de la piste"
@@ -12735,20 +12406,6 @@
12735 </voice> 12406 </voice>
12736</phrase> 12407</phrase>
12737<phrase> 12408<phrase>
12738 id: LANG_PROPERTIES_TITLE
12739 desc: in properties plugin
12740 user: core
12741 <source>
12742 *: "[Title]"
12743 </source>
12744 <dest>
12745 *: "[Titre]"
12746 </dest>
12747 <voice>
12748 *: "Titre"
12749 </voice>
12750</phrase>
12751<phrase>
12752 id: VOICE_PITCH_TIMESTRETCH_MODE 12409 id: VOICE_PITCH_TIMESTRETCH_MODE
12753 desc: spoken only 12410 desc: spoken only
12754 user: core 12411 user: core
@@ -12897,7 +12554,7 @@
12897 *: "Réflexion en cours..." 12554 *: "Réflexion en cours..."
12898 </dest> 12555 </dest>
12899 <voice> 12556 <voice>
12900 *: "" 12557 *: "Réflexion en cours..."
12901 </voice> 12558 </voice>
12902</phrase> 12559</phrase>
12903<phrase> 12560<phrase>
@@ -12933,16 +12590,16 @@
12933</phrase> 12590</phrase>
12934<phrase> 12591<phrase>
12935 id: LANG_PLAYTIME_REMAINING 12592 id: LANG_PLAYTIME_REMAINING
12936 desc: playing time screen 12593 desc: deprecated
12937 user: core 12594 user: core
12938 <source> 12595 <source>
12939 *: "Playlist remaining: %s" 12596 *: ""
12940 </source> 12597 </source>
12941 <dest> 12598 <dest>
12942 *: "Playlist remaining: %s" 12599 *: ""
12943 </dest> 12600 </dest>
12944 <voice> 12601 <voice>
12945 *: "Playlist remaining" 12602 *: ""
12946 </voice> 12603 </voice>
12947</phrase> 12604</phrase>
12948<phrase> 12605<phrase>
@@ -12995,10 +12652,10 @@
12995 *: "Display FPS" 12652 *: "Display FPS"
12996 </source> 12653 </source>
12997 <dest> 12654 <dest>
12998 *: "Display FPS" 12655 *: "Afficher les FPS"
12999 </dest> 12656 </dest>
13000 <voice> 12657 <voice>
13001 *: "Display FPS" 12658 *: "Afficher les FPS"
13002 </voice> 12659 </voice>
13003</phrase> 12660</phrase>
13004<phrase> 12661<phrase>
@@ -13009,10 +12666,10 @@
13009 *: "Remote Control" 12666 *: "Remote Control"
13010 </source> 12667 </source>
13011 <dest> 12668 <dest>
13012 *: "Remote Control" 12669 *: "Télécommande"
13013 </dest> 12670 </dest>
13014 <voice> 12671 <voice>
13015 *: "Remote Control" 12672 *: "Télécommande"
13016 </voice> 12673 </voice>
13017</phrase> 12674</phrase>
13018<phrase> 12675<phrase>
@@ -13141,10 +12798,10 @@
13141 *: "[Subdirs]" 12798 *: "[Subdirs]"
13142 </source> 12799 </source>
13143 <dest> 12800 <dest>
13144 *: "[Subdirs]" 12801 *: "[Sous-rep]"
13145 </dest> 12802 </dest>
13146 <voice> 12803 <voice>
13147 *: "Subdirs" 12804 *: "[Sous-rep]"
13148 </voice> 12805 </voice>
13149</phrase> 12806</phrase>
13150<phrase> 12807<phrase>
@@ -13183,10 +12840,10 @@
13183 *: "Go to WPS" 12840 *: "Go to WPS"
13184 </source> 12841 </source>
13185 <dest> 12842 <dest>
13186 *: "Go to WPS" 12843 *: "Aller au WPS"
13187 </dest> 12844 </dest>
13188 <voice> 12845 <voice>
13189 *: "Go to WPS" 12846 *: "Aller au WPS"
13190 </voice> 12847 </voice>
13191</phrase> 12848</phrase>
13192<phrase> 12849<phrase>
@@ -13197,24 +12854,10 @@
13197 *: "Not a VBR file" 12854 *: "Not a VBR file"
13198 </source> 12855 </source>
13199 <dest> 12856 <dest>
13200 *: "Not a VBR file" 12857 *: "Ce n'est pas un fichier VBR"
13201 </dest>
13202 <voice>
13203 *: "Not a VBR file"
13204 </voice>
13205</phrase>
13206<phrase>
13207 id: LANG_PROPERTIES_ALBUM
13208 desc: in properties plugin
13209 user: core
13210 <source>
13211 *: "[Album]"
13212 </source>
13213 <dest>
13214 *: "[Album]"
13215 </dest> 12858 </dest>
13216 <voice> 12859 <voice>
13217 *: "Album" 12860 *: "Ce n'est pas un fichier VBR"
13218 </voice> 12861 </voice>
13219</phrase> 12862</phrase>
13220<phrase> 12863<phrase>
@@ -13239,10 +12882,10 @@
13239 *: "Show album title" 12882 *: "Show album title"
13240 </source> 12883 </source>
13241 <dest> 12884 <dest>
13242 *: "Show album title" 12885 *: "Afficher le titre de l'album"
13243 </dest> 12886 </dest>
13244 <voice> 12887 <voice>
13245 *: "Show album title" 12888 *: "Afficher le titre de l'album"
13246 </voice> 12889 </voice>
13247</phrase> 12890</phrase>
13248<phrase> 12891<phrase>
@@ -13250,13 +12893,13 @@
13250 desc: playing time screen 12893 desc: playing time screen
13251 user: core 12894 user: core
13252 <source> 12895 <source>
13253 *: "Average bitrate: %ld kbps" 12896 *: "Average bitrate:"
13254 </source> 12897 </source>
13255 <dest> 12898 <dest>
13256 *: "Average bitrate: %ld kbps" 12899 *: "Débit binaire moyen :"
13257 </dest> 12900 </dest>
13258 <voice> 12901 <voice>
13259 *: "Average bit rate" 12902 *: "Débit binaire moyen :"
13260 </voice> 12903 </voice>
13261</phrase> 12904</phrase>
13262<phrase> 12905<phrase>
@@ -13322,20 +12965,6 @@
13322 </voice> 12965 </voice>
13323</phrase> 12966</phrase>
13324<phrase> 12967<phrase>
13325 id: LANG_CLEAR_PLAYLIST
13326 desc: in the pictureflow main menu
13327 user: core
13328 <source>
13329 *: "Clear playlist"
13330 </source>
13331 <dest>
13332 *: "Effacer la liste de lecture"
13333 </dest>
13334 <voice>
13335 *: "Effacer la liste de lecture"
13336 </voice>
13337</phrase>
13338<phrase>
13339 id: VOICE_CHESSBOX_QUEENSIDE 12968 id: VOICE_CHESSBOX_QUEENSIDE
13340 desc: spoken only, for announcing chess moves 12969 desc: spoken only, for announcing chess moves
13341 user: core 12970 user: core
@@ -13371,10 +13000,10 @@
13371 *: "Playback Control" 13000 *: "Playback Control"
13372 </source> 13001 </source>
13373 <dest> 13002 <dest>
13374 *: "Playback Control" 13003 *: "Contrôle de lecture"
13375 </dest> 13004 </dest>
13376 <voice> 13005 <voice>
13377 *: "Playback Control" 13006 *: "Contrôle de lecture"
13378 </voice> 13007 </voice>
13379</phrase> 13008</phrase>
13380<phrase> 13009<phrase>
@@ -13475,7 +13104,7 @@
13475 *: "File properties" 13104 *: "File properties"
13476 </source> 13105 </source>
13477 <dest> 13106 <dest>
13478 *: "File properties" 13107 *: "Propriétés du fichier"
13479 </dest> 13108 </dest>
13480 <voice> 13109 <voice>
13481 *: "" 13110 *: ""
@@ -13562,10 +13191,10 @@
13562 *: "Move Item Down" 13191 *: "Move Item Down"
13563 </source> 13192 </source>
13564 <dest> 13193 <dest>
13565 *: "Move Item Down" 13194 *: "Déplacer vers le bas"
13566 </dest> 13195 </dest>
13567 <voice> 13196 <voice>
13568 *: "Move Item Down" 13197 *: "Déplacer vers le bas"
13569 </voice> 13198 </voice>
13570</phrase> 13199</phrase>
13571<phrase> 13200<phrase>
@@ -13642,20 +13271,6 @@
13642 </voice> 13271 </voice>
13643</phrase> 13272</phrase>
13644<phrase> 13273<phrase>
13645 id: LANG_PLAYLIST_CLEARED
13646 desc: in the pictureflow splash messages
13647 user: core
13648 <source>
13649 *: "Playlist Cleared"
13650 </source>
13651 <dest>
13652 *: "Playlist Cleared"
13653 </dest>
13654 <voice>
13655 *: "Playlist Cleared"
13656 </voice>
13657</phrase>
13658<phrase>
13659 id: LANG_REC_DIR 13274 id: LANG_REC_DIR
13660 desc: used in the info screen to show a recording dir 13275 desc: used in the info screen to show a recording dir
13661 user: core 13276 user: core
@@ -13680,10 +13295,10 @@
13680 *: "Shuffle Mode" 13295 *: "Shuffle Mode"
13681 </source> 13296 </source>
13682 <dest> 13297 <dest>
13683 *: "Shuffle Mode" 13298 *: "Mode mélangé"
13684 </dest> 13299 </dest>
13685 <voice> 13300 <voice>
13686 *: "Shuffle Mode" 13301 *: "Mode mélangé"
13687 </voice> 13302 </voice>
13688</phrase> 13303</phrase>
13689<phrase> 13304<phrase>
@@ -13812,7 +13427,7 @@
13812 *: "" 13427 *: ""
13813 </dest> 13428 </dest>
13814 <voice> 13429 <voice>
13815 *: "Moving track" 13430 *: "Déplacement de la piste"
13816 </voice> 13431 </voice>
13817</phrase> 13432</phrase>
13818<phrase> 13433<phrase>
@@ -13920,20 +13535,6 @@
13920 </voice> 13535 </voice>
13921</phrase> 13536</phrase>
13922<phrase> 13537<phrase>
13923 id: LANG_ADDED_TO_PLAYLIST
13924 desc: in the pictureflow splash messages
13925 user: core
13926 <source>
13927 *: "Added to playlist"
13928 </source>
13929 <dest>
13930 *: "Ajouté à la liste de lecture"
13931 </dest>
13932 <voice>
13933 *: "Ajouté à la liste de lecture"
13934 </voice>
13935</phrase>
13936<phrase>
13937 id: VOICE_PITCH_ABSOLUTE_MODE 13538 id: VOICE_PITCH_ABSOLUTE_MODE
13938 desc: spoken only 13539 desc: spoken only
13939 user: core 13540 user: core
@@ -14039,7 +13640,7 @@
14039</phrase> 13640</phrase>
14040<phrase> 13641<phrase>
14041 id: LANG_DIRECT 13642 id: LANG_DIRECT
14042 desc: in the pictureflow settings 13643 desc: in the pictureflow settings, also a volume adjustment mode
14043 user: core 13644 user: core
14044 <source> 13645 <source>
14045 *: "Direct" 13646 *: "Direct"
@@ -14073,10 +13674,10 @@
14073 *: "Change Repeat Mode" 13674 *: "Change Repeat Mode"
14074 </source> 13675 </source>
14075 <dest> 13676 <dest>
14076 *: "Change Repeat Mode" 13677 *: "Changer le mode de répétition"
14077 </dest> 13678 </dest>
14078 <voice> 13679 <voice>
14079 *: "Change Repeat Mode" 13680 *: "Changer le mode de répétition"
14080 </voice> 13681 </voice>
14081</phrase> 13682</phrase>
14082<phrase> 13683<phrase>
@@ -14174,10 +13775,10 @@
14174 *: "Cache will be rebuilt on next restart" 13775 *: "Cache will be rebuilt on next restart"
14175 </source> 13776 </source>
14176 <dest> 13777 <dest>
14177 *: "Cache will be rebuilt on next restart" 13778 *: "Le cache sera reconstruit au prochain redémarrage"
14178 </dest> 13779 </dest>
14179 <voice> 13780 <voice>
14180 *: "Cache will be rebuilt on next restart" 13781 *: "Le cache sera reconstruit au prochain redémarrage"
14181 </voice> 13782 </voice>
14182</phrase> 13783</phrase>
14183<phrase> 13784<phrase>
@@ -14188,10 +13789,10 @@
14188 *: "Change Volume" 13789 *: "Change Volume"
14189 </source> 13790 </source>
14190 <dest> 13791 <dest>
14191 *: "Change Volume" 13792 *: "Changer le volume"
14192 </dest> 13793 </dest>
14193 <voice> 13794 <voice>
14194 *: "Change Volume" 13795 *: "Changer le volume"
14195 </voice> 13796 </voice>
14196</phrase> 13797</phrase>
14197<phrase> 13798<phrase>
@@ -14268,20 +13869,6 @@
14268 </voice> 13869 </voice>
14269</phrase> 13870</phrase>
14270<phrase> 13871<phrase>
14271 id: LANG_PROPERTIES_ARTIST
14272 desc: in properties plugin
14273 user: core
14274 <source>
14275 *: "[Artist]"
14276 </source>
14277 <dest>
14278 *: "[Interprète]"
14279 </dest>
14280 <voice>
14281 *: "Interprète"
14282 </voice>
14283</phrase>
14284<phrase>
14285 id: LANG_PROPERTIES_SIZE 13872 id: LANG_PROPERTIES_SIZE
14286 desc: in properties plugin 13873 desc: in properties plugin
14287 user: core 13874 user: core
@@ -14303,10 +13890,10 @@
14303 *: "Via Track list" 13890 *: "Via Track list"
14304 </source> 13891 </source>
14305 <dest> 13892 <dest>
14306 *: "Via Track list" 13893 *: "Via la liste des pistes"
14307 </dest> 13894 </dest>
14308 <voice> 13895 <voice>
14309 *: "Via Track list" 13896 *: "Via la liste des pistes"
14310 </voice> 13897 </voice>
14311</phrase> 13898</phrase>
14312<phrase> 13899<phrase>
@@ -14359,10 +13946,10 @@
14359 *: "Load Default Configuration" 13946 *: "Load Default Configuration"
14360 </source> 13947 </source>
14361 <dest> 13948 <dest>
14362 *: "Load Default Configuration" 13949 *: "Charger la configuration par défaut"
14363 </dest> 13950 </dest>
14364 <voice> 13951 <voice>
14365 *: "Load Default Configuration" 13952 *: "Charger la configuration par défaut"
14366 </voice> 13953 </voice>
14367</phrase> 13954</phrase>
14368<phrase> 13955<phrase>
@@ -14373,10 +13960,10 @@
14373 *: "Checkmate!" 13960 *: "Checkmate!"
14374 </source> 13961 </source>
14375 <dest> 13962 <dest>
14376 *: "Checkmate!" 13963 *: "Échec et mat !"
14377 </dest> 13964 </dest>
14378 <voice> 13965 <voice>
14379 *: "Checkmate!" 13966 *: "Échec et mat !"
14380 </voice> 13967 </voice>
14381</phrase> 13968</phrase>
14382<phrase> 13969<phrase>
@@ -14454,13 +14041,13 @@
14454 desc: playing time screen 14041 desc: playing time screen
14455 user: core 14042 user: core
14456 <source> 14043 <source>
14457 *: "Storage: %s (done %s, remaining %s)" 14044 *: "Storage (Done / Remaining):"
14458 </source> 14045 </source>
14459 <dest> 14046 <dest>
14460 *: "Storage: %s (done %s, remaining %s)" 14047 *: "Stockage (Terminé / Restant) :"
14461 </dest> 14048 </dest>
14462 <voice> 14049 <voice>
14463 *: "Storage" 14050 *: "Stockage"
14464 </voice> 14051 </voice>
14465</phrase> 14052</phrase>
14466<phrase> 14053<phrase>
@@ -14471,10 +14058,10 @@
14471 *: "Pause / Play" 14058 *: "Pause / Play"
14472 </source> 14059 </source>
14473 <dest> 14060 <dest>
14474 *: "Pause / Play" 14061 *: "Pause / Lecture"
14475 </dest> 14062 </dest>
14476 <voice> 14063 <voice>
14477 *: "Pause / Play" 14064 *: "Pause / Lecture"
14478 </voice> 14065 </voice>
14479</phrase> 14066</phrase>
14480<phrase> 14067<phrase>
@@ -14485,10 +14072,10 @@
14485 *: "Previous Track" 14072 *: "Previous Track"
14486 </source> 14073 </source>
14487 <dest> 14074 <dest>
14488 *: "Previous Track" 14075 *: "Piste précédente"
14489 </dest> 14076 </dest>
14490 <voice> 14077 <voice>
14491 *: "Previous Track" 14078 *: "Piste précédente"
14492 </voice> 14079 </voice>
14493</phrase> 14080</phrase>
14494<phrase> 14081<phrase>
@@ -14520,20 +14107,6 @@
14520 </voice> 14107 </voice>
14521</phrase> 14108</phrase>
14522<phrase> 14109<phrase>
14523 id: LANG_PROPERTIES_DURATION
14524 desc: in properties plugin
14525 user: core
14526 <source>
14527 *: "[Duration]"
14528 </source>
14529 <dest>
14530 *: "[Durée]"
14531 </dest>
14532 <voice>
14533 *: "Durée"
14534 </voice>
14535</phrase>
14536<phrase>
14537 id: LANG_PROPERTIES_FAIL 14110 id: LANG_PROPERTIES_FAIL
14538 desc: in properties plugin 14111 desc: in properties plugin
14539 user: core 14112 user: core
@@ -14541,10 +14114,10 @@
14541 *: "Failed to gather information" 14114 *: "Failed to gather information"
14542 </source> 14115 </source>
14543 <dest> 14116 <dest>
14544 *: "Failed to gather information" 14117 *: "Échec de la collecte d'informations"
14545 </dest> 14118 </dest>
14546 <voice> 14119 <voice>
14547 *: "Failed to gather information" 14120 *: "Échec de la collecte d'informations"
14548 </voice> 14121 </voice>
14549</phrase> 14122</phrase>
14550<phrase> 14123<phrase>
@@ -14570,11 +14143,11 @@
14570 lowmem: none 14143 lowmem: none
14571 </source> 14144 </source>
14572 <dest> 14145 <dest>
14573 *: "Resume automatically" 14146 *: "Reprendre automatiquement"
14574 lowmem: none 14147 lowmem: none
14575 </dest> 14148 </dest>
14576 <voice> 14149 <voice>
14577 *: "Resume automatically" 14150 *: "Reprendre automatiquement"
14578 lowmem: none 14151 lowmem: none
14579 </voice> 14152 </voice>
14580</phrase> 14153</phrase>
@@ -14587,11 +14160,11 @@
14587 lowmem: none 14160 lowmem: none
14588 </source> 14161 </source>
14589 <dest> 14162 <dest>
14590 *: "Reverberation" 14163 *: "Réverbération"
14591 lowmem: none 14164 lowmem: none
14592 </dest> 14165 </dest>
14593 <voice> 14166 <voice>
14594 *: "Reverberation" 14167 *: "Réverbération"
14595 lowmem: none 14168 lowmem: none
14596 </voice> 14169 </voice>
14597</phrase> 14170</phrase>
@@ -14642,10 +14215,10 @@
14642 desc: playing time screen 14215 desc: playing time screen
14643 user: core 14216 user: core
14644 <source> 14217 <source>
14645 *: "Average track size: %s" 14218 *: "Average track size:"
14646 </source> 14219 </source>
14647 <dest> 14220 <dest>
14648 *: "Taille moyenne des pistes: %s" 14221 *: "Taille moyenne des pistes :"
14649 </dest> 14222 </dest>
14650 <voice> 14223 <voice>
14651 *: "Taille moyenne des pistes" 14224 *: "Taille moyenne des pistes"
@@ -14656,13 +14229,13 @@
14656 desc: playing time screen 14229 desc: playing time screen
14657 user: core 14230 user: core
14658 <source> 14231 <source>
14659 *: "Track %d / %d %d%%" 14232 *: "Track:"
14660 </source> 14233 </source>
14661 <dest> 14234 <dest>
14662 *: "Track %d / %d %d%%" 14235 *: "Piste :"
14663 </dest> 14236 </dest>
14664 <voice> 14237 <voice>
14665 *: "Track" 14238 *: "Piste :"
14666 </voice> 14239 </voice>
14667</phrase> 14240</phrase>
14668<phrase> 14241<phrase>
@@ -14725,20 +14298,6 @@
14725 </voice> 14298 </voice>
14726</phrase> 14299</phrase>
14727<phrase> 14300<phrase>
14728 id: LANG_NO_VIEWERS
14729 desc: text for splash to indicate that no viewers are available
14730 user: core
14731 <source>
14732 *: "No viewers found"
14733 </source>
14734 <dest>
14735 *: "Aucune visionneuse trouvée"
14736 </dest>
14737 <voice>
14738 *: "Aucune visionneuse trouvée"
14739 </voice>
14740</phrase>
14741<phrase>
14742 id: VOICE_BISHOP 14301 id: VOICE_BISHOP
14743 desc: spoken only, for announcing chess piece names 14302 desc: spoken only, for announcing chess piece names
14744 user: core 14303 user: core
@@ -14959,10 +14518,10 @@
14959 *: "Hide album title" 14518 *: "Hide album title"
14960 </source> 14519 </source>
14961 <dest> 14520 <dest>
14962 *: "Hide album title" 14521 *: "Cacher le titre de l'album"
14963 </dest> 14522 </dest>
14964 <voice> 14523 <voice>
14965 *: "Hide album title" 14524 *: "Cacher le titre de l'album"
14966 </voice> 14525 </voice>
14967</phrase> 14526</phrase>
14968<phrase> 14527<phrase>
@@ -14974,11 +14533,11 @@
14974 lowmem: none 14533 lowmem: none
14975 </source> 14534 </source>
14976 <dest> 14535 <dest>
14977 *: "Set resume time (min)" 14536 *: "Définir le temps de reprise (min)"
14978 lowmem: none 14537 lowmem: none
14979 </dest> 14538 </dest>
14980 <voice> 14539 <voice>
14981 *: "Set resume time" 14540 *: "Définir le temps de reprise (min)"
14982 lowmem: none 14541 lowmem: none
14983 </voice> 14542 </voice>
14984</phrase> 14543</phrase>
@@ -15080,3 +14639,1882 @@
15080 *: "Français" 14639 *: "Français"
15081 </voice> 14640 </voice>
15082</phrase> 14641</phrase>
14642<phrase>
14643 id: LANG_DISABLE_MAINMENU_SCROLLING
14644 desc: Disable main menu scrolling
14645 user: core
14646 <source>
14647 *: "Disable main menu scrolling"
14648 </source>
14649 <dest>
14650 *: "Désactiver défilement dans le menu principal"
14651 </dest>
14652 <voice>
14653 *: "Désactiver défilement dans le menu principal"
14654 </voice>
14655</phrase>
14656<phrase>
14657 id: LANG_SINGLE_MODE
14658 desc: single mode
14659 user: core
14660 <source>
14661 *: "Single Mode"
14662 </source>
14663 <dest>
14664 *: "Mode lecture seule"
14665 </dest>
14666 <voice>
14667 *: "Mode lecture seule"
14668 </voice>
14669</phrase>
14670<phrase>
14671 id: LANG_TALK_MIXER_LEVEL
14672 desc: Relative volume of voice prompts
14673 user: core
14674 <source>
14675 *: "Voice prompt volume"
14676 </source>
14677 <dest>
14678 *: "Volume des messages vocaux"
14679 </dest>
14680 <voice>
14681 *: "Volume des messages vocaux"
14682 </voice>
14683</phrase>
14684<phrase>
14685 id: LANG_FILTER_SHORT_SHARP
14686 desc: in sound settings
14687 user: core
14688 <source>
14689 *: none
14690 filter_roll_off: "Short Sharp"
14691 </source>
14692 <dest>
14693 *: none
14694 filter_roll_off: "Short Sharp"
14695 </dest>
14696 <voice>
14697 *: none
14698 filter_roll_off: "Short Sharp"
14699 </voice>
14700</phrase>
14701<phrase>
14702 id: LANG_FILTER_SHORT_SLOW
14703 desc: in sound settings
14704 user: core
14705 <source>
14706 *: none
14707 filter_roll_off: "Short Slow"
14708 </source>
14709 <dest>
14710 *: none
14711 filter_roll_off: "Short Slow"
14712 </dest>
14713 <voice>
14714 *: none
14715 filter_roll_off: "Short Slow"
14716 </voice>
14717</phrase>
14718<phrase>
14719 id: LANG_FILTER_SUPER_SLOW
14720 desc: in sound settings
14721 user: core
14722 <source>
14723 *: none
14724 filter_roll_off: "Super Slow"
14725 </source>
14726 <dest>
14727 *: none
14728 filter_roll_off: "Super Slow"
14729 </dest>
14730 <voice>
14731 *: none
14732 filter_roll_off: "Super Slow"
14733 </voice>
14734</phrase>
14735<phrase>
14736 id: LANG_FILTER_LINEAR_FAST
14737 desc: in sound settings
14738 user: core
14739 <source>
14740 *: none
14741 es9218: "Linear Fast"
14742 </source>
14743 <dest>
14744 *: none
14745 es9218: "Linear Fast"
14746 </dest>
14747 <voice>
14748 *: none
14749 es9218: "Linear Fast"
14750 </voice>
14751</phrase>
14752<phrase>
14753 id: LANG_FILTER_LINEAR_SLOW
14754 desc: in sound settings
14755 user: core
14756 <source>
14757 *: none
14758 es9218: "Linear Slow"
14759 </source>
14760 <dest>
14761 *: none
14762 es9218: "Linear Slow"
14763 </dest>
14764 <voice>
14765 *: none
14766 es9218: "Linear Slow"
14767 </voice>
14768</phrase>
14769<phrase>
14770 id: LANG_FILTER_MINIMUM_FAST
14771 desc: in sound settings
14772 user: core
14773 <source>
14774 *: none
14775 es9218: "Minimum Fast"
14776 </source>
14777 <dest>
14778 *: none
14779 es9218: "Minimum Fast"
14780 </dest>
14781 <voice>
14782 *: none
14783 es9218: "Minimum Fast"
14784 </voice>
14785</phrase>
14786<phrase>
14787 id: LANG_FILTER_MINIMUM_SLOW
14788 desc: in sound settings
14789 user: core
14790 <source>
14791 *: none
14792 es9218: "Minimum Slow"
14793 </source>
14794 <dest>
14795 *: none
14796 es9218: "Minimum Slow"
14797 </dest>
14798 <voice>
14799 *: none
14800 es9218: "Minimum Slow"
14801 </voice>
14802</phrase>
14803<phrase>
14804 id: LANG_FILTER_APODIZING_1
14805 desc: in sound settings
14806 user: core
14807 <source>
14808 *: none
14809 es9218: "Apodizing type 1"
14810 </source>
14811 <dest>
14812 *: none
14813 es9218: "Apodizing type 1"
14814 </dest>
14815 <voice>
14816 *: none
14817 es9218: "Apodizing type 1"
14818 </voice>
14819</phrase>
14820<phrase>
14821 id: LANG_FILTER_APODIZING_2
14822 desc: in sound settings
14823 user: core
14824 <source>
14825 *: none
14826 es9218: "Apodizing type 2"
14827 </source>
14828 <dest>
14829 *: none
14830 es9218: "Apodizing type 2"
14831 </dest>
14832 <voice>
14833 *: none
14834 es9218: "Apodizing type 2"
14835 </voice>
14836</phrase>
14837<phrase>
14838 id: LANG_FILTER_HYBRID_FAST
14839 desc: in sound settings
14840 user: core
14841 <source>
14842 *: none
14843 es9218: "Hybrid Fast"
14844 </source>
14845 <dest>
14846 *: none
14847 es9218: "Hybrid Fast"
14848 </dest>
14849 <voice>
14850 *: none
14851 es9218: "Hybrid Fast"
14852 </voice>
14853</phrase>
14854<phrase>
14855 id: LANG_FILTER_BRICK_WALL
14856 desc: in sound settings
14857 user: core
14858 <source>
14859 *: none
14860 es9218: "Brick Wall"
14861 </source>
14862 <dest>
14863 *: none
14864 es9218: "Brick Wall"
14865 </dest>
14866 <voice>
14867 *: none
14868 es9218: "Brick Wall"
14869 </voice>
14870</phrase>
14871<phrase>
14872 id: LANG_DAC_POWER_MODE
14873 desc: in sound settings
14874 user: core
14875 <source>
14876 *: none
14877 dac_power_mode: "DAC power mode"
14878 es9218: "DAC output level"
14879 </source>
14880 <dest>
14881 *: none
14882 dac_power_mode: "DAC power mode"
14883 es9218: "DAC output level"
14884 </dest>
14885 <voice>
14886 *: none
14887 dac_power_mode: "DAC power mode"
14888 es9218: "DAC output level"
14889 </voice>
14890</phrase>
14891<phrase>
14892 id: LANG_DAC_POWER_HIGH
14893 desc: in sound settings
14894 user: core
14895 <source>
14896 *: none
14897 dac_power_mode: "High performance"
14898 es9218: "High Gain (2 Vrms)"
14899 </source>
14900 <dest>
14901 *: none
14902 dac_power_mode: "High performance"
14903 es9218: "High Gain (2 Vrms)"
14904 </dest>
14905 <voice>
14906 *: none
14907 dac_power_mode: "High performance"
14908 es9218: "High Gain (2 Vrms)"
14909 </voice>
14910</phrase>
14911<phrase>
14912 id: LANG_DAC_POWER_LOW
14913 desc: in sound settings
14914 user: core
14915 <source>
14916 *: none
14917 dac_power_mode: "Save battery"
14918 es9218: "Low Gain (1 Vrms)"
14919 </source>
14920 <dest>
14921 *: none
14922 dac_power_mode: "Save battery"
14923 es9218: "Low Gain (1 Vrms)"
14924 </dest>
14925 <voice>
14926 *: none
14927 dac_power_mode: "Save battery"
14928 es9218: "Low Gain (1 Vrms)"
14929 </voice>
14930</phrase>
14931<phrase>
14932 id: LANG_MIKMOD_HQMIXER
14933 desc: in mikmod settings menu
14934 user: core
14935 <source>
14936 *: "HQ Mixer"
14937 lowmem: none
14938 </source>
14939 <dest>
14940 *: "HQ Mixer"
14941 lowmem: none
14942 </dest>
14943 <voice>
14944 *: "High Quality Mixer"
14945 lowmem: none
14946 </voice>
14947</phrase>
14948<phrase>
14949 id: LANG_MIKMOD_SAMPLERATE
14950 desc: in mikmod settings menu
14951 user: core
14952 <source>
14953 *: "Sample Rate"
14954 lowmem: none
14955 </source>
14956 <dest>
14957 *: "Sample Rate"
14958 lowmem: none
14959 </dest>
14960 <voice>
14961 *: "Sample Rate"
14962 lowmem: none
14963 </voice>
14964</phrase>
14965<phrase>
14966 id: LANG_ACTION_STD_CANCEL
14967 desc: standard press x to cancel string
14968 user: core
14969 <source>
14970 *: "Press LEFT to cancel."
14971 android,hifietma*,zenvision: "Press BACK to cancel."
14972 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
14973 ihifi760,ihifi960: "Double tap RETURN to cancel."
14974 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
14975 iriverh10,samsungyh*: "Double tap LEFT to cancel."
14976 mpiohd200: "Double tap REC to cancel."
14977 mpiohd300: "Double tap MENU to cancel."
14978 rx27generic: "Press VOLUME to cancel."
14979 sonynwza860: "Keymaps incomplete."
14980 touchscreen: "Press Middle Left to cancel."
14981 vibe500: "Press PREV to cancel."
14982 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
14983 </source>
14984 <dest>
14985 *: "Press LEFT to cancel."
14986 android,hifietma*,zenvision: "Press BACK to cancel."
14987 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
14988 ihifi760,ihifi960: "Double tap RETURN to cancel."
14989 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
14990 iriverh10,samsungyh*: "Double tap LEFT to cancel."
14991 mpiohd200: "Double tap REC to cancel."
14992 mpiohd300: "Double tap MENU to cancel."
14993 rx27generic: "Press VOLUME to cancel."
14994 sonynwza860: "Keymaps incomplete."
14995 touchscreen: "Press Middle Left to cancel."
14996 vibe500: "Press PREV to cancel."
14997 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
14998 </dest>
14999 <voice>
15000 *: "Press LEFT to cancel."
15001 android,hifietma*,zenvision: "Press BACK to cancel."
15002 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
15003 ihifi760,ihifi960: "Double tap RETURN to cancel."
15004 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
15005 iriverh10,samsungyh*: "Double tap LEFT to cancel."
15006 mpiohd200: "Double tap REC to cancel."
15007 mpiohd300: "Double tap MENU to cancel."
15008 rx27generic: "Press VOLUME to cancel."
15009 touchscreen: "Press Middle Left to cancel."
15010 vibe500: "Press PREV to cancel."
15011 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
15012 </voice>
15013</phrase>
15014<phrase>
15015 id: LANG_DATE
15016 desc: for constructing time and date announcements
15017 user: core
15018 <source>
15019 *: "Date"
15020 </source>
15021 <dest>
15022 *: "Tout effacer"
15023 </dest>
15024 <voice>
15025 *: "Tout effacer"
15026 </voice>
15027</phrase>
15028<phrase>
15029 id: LANG_CLEAR_ALL
15030 desc:
15031 user: core
15032 <source>
15033 *: "Clear all"
15034 </source>
15035 <dest>
15036 *: "Clear all"
15037 </dest>
15038 <voice>
15039 *: "Clear all"
15040 </voice>
15041</phrase>
15042<phrase>
15043 id: LANG_CANCEL_0
15044 desc: CANCEL.
15045 user: core
15046 <source>
15047 *: "Cancel"
15048 </source>
15049 <dest>
15050 *: "Annuler"
15051 </dest>
15052 <voice>
15053 *: "Annuler"
15054 </voice>
15055</phrase>
15056<phrase>
15057 id: LANG_SAVE
15058 desc:
15059 user: core
15060 <source>
15061 *: "Save"
15062 </source>
15063 <dest>
15064 *: "Sauvegarder"
15065 </dest>
15066 <voice>
15067 *: "Sauvegarder"
15068 </voice>
15069</phrase>
15070<phrase>
15071 id: LANG_TIMEOUT
15072 desc:
15073 user: core
15074 <source>
15075 *: "Timeout"
15076 </source>
15077 <dest>
15078 *: "Temps expiré"
15079 </dest>
15080 <voice>
15081 *: "Temps expiré"
15082 </voice>
15083</phrase>
15084<phrase>
15085 id: LANG_TRACK
15086 desc: used in track x of y constructs
15087 user: core
15088 <source>
15089 *: "Track"
15090 </source>
15091 <dest>
15092 *: "Piste"
15093 </dest>
15094 <voice>
15095 *: "Piste"
15096 </voice>
15097</phrase>
15098<phrase>
15099 id: LANG_ELAPSED
15100 desc: prefix for elapsed playtime announcement
15101 user: core
15102 <source>
15103 *: "Elapsed"
15104 </source>
15105 <dest>
15106 *: "Écoulé"
15107 </dest>
15108 <voice>
15109 *: "Écoulé"
15110 </voice>
15111</phrase>
15112<phrase>
15113 id: LANG_ANNOUNCEMENT_FMT
15114 desc: format for wps hotkey announcement
15115 user: core
15116 <source>
15117 *: none
15118 hotkey: "Announcement format"
15119 </source>
15120 <dest>
15121 *: none
15122 hotkey: "Format des annonces"
15123 </dest>
15124 <voice>
15125 *: none
15126 hotkey: "Format des annonces"
15127 </voice>
15128</phrase>
15129<phrase>
15130 id: LANG_REMAIN
15131 desc: for constructs such as number of tracks remaining etc
15132 user: core
15133 <source>
15134 *: none
15135 hotkey: "Remain"
15136 </source>
15137 <dest>
15138 *: none
15139 hotkey: "Restant"
15140 </dest>
15141 <voice>
15142 *: none
15143 hotkey: "Restant"
15144 </voice>
15145</phrase>
15146<phrase>
15147 id: LANG_GROUPING
15148 desc:
15149 user: core
15150 <source>
15151 *: none
15152 hotkey: "Grouping"
15153 </source>
15154 <dest>
15155 *: none
15156 hotkey: "Regroupement"
15157 </dest>
15158 <voice>
15159 *: none
15160 hotkey: "Regroupement"
15161 </voice>
15162</phrase>
15163<phrase>
15164 id: LANG_ANNOUNCE_ON
15165 desc:
15166 user: core
15167 <source>
15168 *: none
15169 hotkey: "Announce on"
15170 </source>
15171 <dest>
15172 *: none
15173 hotkey: "Annoncer sur"
15174 </dest>
15175 <voice>
15176 *: none
15177 hotkey: "Annoncer sur"
15178 </voice>
15179</phrase>
15180<phrase>
15181 id: LANG_TRACK_CHANGE
15182 desc:
15183 user: core
15184 <source>
15185 *: none
15186 hotkey: "Track change"
15187 </source>
15188 <dest>
15189 *: none
15190 hotkey: "Changement de piste"
15191 </dest>
15192 <voice>
15193 *: none
15194 hotkey: "Changement de piste"
15195 </voice>
15196</phrase>
15197<phrase>
15198 id: LANG_HOLD_FOR_SETTINGS
15199 desc:
15200 user: core
15201 <source>
15202 *: none
15203 hotkey: "Hold for settings"
15204 </source>
15205 <dest>
15206 *: none
15207 hotkey: "Maintenir pour accéder aux paramètres"
15208 </dest>
15209 <voice>
15210 *: none
15211 hotkey: "Maintenir pour accéder aux paramètres"
15212 </voice>
15213</phrase>
15214<phrase>
15215 id: LANG_OPEN_PLUGIN
15216 desc: onplay open plugin
15217 user: core
15218 <source>
15219 *: "Open Plugin"
15220 </source>
15221 <dest>
15222 *: "Lancer le module"
15223 </dest>
15224 <voice>
15225 *: "Lancer le module"
15226 </voice>
15227</phrase>
15228<phrase>
15229 id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
15230 desc: open plugin module
15231 user: core
15232 <source>
15233 *: "Not a plugin: %s"
15234 </source>
15235 <dest>
15236 *: "N'est pas un module: %s"
15237 </dest>
15238 <voice>
15239 *: "N'est pas un module: %s"
15240 </voice>
15241</phrase>
15242<phrase>
15243 id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
15244 desc: open plugin module
15245 user: core
15246 <source>
15247 *: "Set Wps Context Plugin"
15248 </source>
15249 <dest>
15250 *: "Set Wps Context Plugin"
15251 </dest>
15252 <voice>
15253 *: "Set WPS Context Plugin"
15254 </voice>
15255</phrase>
15256<phrase>
15257 id: LANG_PARAMETER
15258 desc:
15259 user: core
15260 <source>
15261 *: "Parameter"
15262 </source>
15263 <dest>
15264 *: "Paramètre"
15265 </dest>
15266 <voice>
15267 *: "Paramètre"
15268 </voice>
15269</phrase>
15270<phrase>
15271 id: LANG_NAME
15272 desc:
15273 user: core
15274 <source>
15275 *: "Name"
15276 </source>
15277 <dest>
15278 *: "Nom"
15279 </dest>
15280 <voice>
15281 *: "Nom"
15282 </voice>
15283</phrase>
15284<phrase>
15285 id: LANG_ADD
15286 desc:
15287 user: core
15288 <source>
15289 *: "Add"
15290 </source>
15291 <dest>
15292 *: "Ajouter"
15293 </dest>
15294 <voice>
15295 *: "Ajouter"
15296 </voice>
15297</phrase>
15298<phrase>
15299 id: LANG_BACK
15300 desc:
15301 user: core
15302 <source>
15303 *: "Back"
15304 </source>
15305 <dest>
15306 *: "Revenir"
15307 </dest>
15308 <voice>
15309 *: "Éditer"
15310 </voice>
15311</phrase>
15312<phrase>
15313 id: LANG_EDIT
15314 desc:
15315 user: core
15316 <source>
15317 *: "Edit"
15318 </source>
15319 <dest>
15320 *: "Éditer"
15321 </dest>
15322 <voice>
15323 *: "Éditer"
15324 </voice>
15325</phrase>
15326<phrase>
15327 id: LANG_RUN
15328 desc:
15329 user: core
15330 <source>
15331 *: "Run"
15332 </source>
15333 <dest>
15334 *: "Exécuter"
15335 </dest>
15336 <voice>
15337 *: "Exécuter"
15338 </voice>
15339</phrase>
15340<phrase>
15341 id: LANG_EXPORT
15342 desc:
15343 user: core
15344 <source>
15345 *: "Export"
15346 </source>
15347 <dest>
15348 *: "Exporter"
15349 </dest>
15350 <voice>
15351 *: "Exporter"
15352 </voice>
15353</phrase>
15354<phrase>
15355 id: LANG_BROWSE
15356 desc:
15357 user: core
15358 <source>
15359 *: "Browse"
15360 </source>
15361 <dest>
15362 *: "Parcourir"
15363 </dest>
15364 <voice>
15365 *: "Parcourir"
15366 </voice>
15367</phrase>
15368<phrase>
15369 id: LANG_ENTER_USB_STORAGE_MODE_QUERY
15370 desc: upon plugging in USB
15371 user: core
15372 <source>
15373 *: "Enter USB mass storage mode?"
15374 </source>
15375 <dest>
15376 *: "Passer en mode stockage de masse USB ?"
15377 </dest>
15378 <voice>
15379 *: "Passer en mode stockage de masse USB ?"
15380 </voice>
15381</phrase>
15382<phrase>
15383 id: LANG_QUEUE_MENU
15384 desc: in onplay menu
15385 user: core
15386 <source>
15387 *: "Queue..."
15388 </source>
15389 <dest>
15390 *: "File d'attente"
15391 </dest>
15392 <voice>
15393 *: "File d'attente"
15394 </voice>
15395</phrase>
15396<phrase>
15397 id: LANG_SHOW_QUEUE_OPTIONS
15398 desc: in Current Playlist settings
15399 user: core
15400 <source>
15401 *: "Show Queue Options"
15402 </source>
15403 <dest>
15404 *: "Afficher les options de la file d'attente"
15405 </dest>
15406 <voice>
15407 *: "Afficher les options de la file d'attente"
15408 </voice>
15409</phrase>
15410<phrase>
15411 id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
15412 desc: in Current Playlist settings
15413 user: core
15414 <source>
15415 *: "Show Shuffled Adding Options"
15416 </source>
15417 <dest>
15418 *: "Afficher les options d'ajout aléatoire"
15419 </dest>
15420 <voice>
15421 *: "Afficher les options d'ajout aléatoire"
15422 </voice>
15423</phrase>
15424<phrase>
15425 id: LANG_IN_SUBMENU
15426 desc: in Settings
15427 user: core
15428 <source>
15429 *: "In Submenu"
15430 </source>
15431 <dest>
15432 *: "En sous-menu"
15433 </dest>
15434 <voice>
15435 *: "En sous-menu"
15436 </voice>
15437</phrase>
15438<phrase>
15439 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
15440 desc: disable all softlock notifications
15441 user: core
15442 <source>
15443 *: "Disable All Lock Notifications"
15444 </source>
15445 <dest>
15446 *: "Désactiver toutes les notifications de verrouillage"
15447 </dest>
15448 <voice>
15449 *: "Désactiver toutes les notifications de verrouillage"
15450 </voice>
15451</phrase>
15452<phrase>
15453 id: LANG_ACTION_VOLUME
15454 desc: exempt volume from softlock
15455 user: core
15456 <source>
15457 *: "Exempt Volume"
15458 </source>
15459 <dest>
15460 *: "À l'exception du volume"
15461 </dest>
15462 <voice>
15463 *: "À l'exception du volume"
15464 </voice>
15465</phrase>
15466<phrase>
15467 id: LANG_ACTION_ALWAYSAUTOLOCK
15468 desc: always prime autolock
15469 user: core
15470 <source>
15471 *: "Always Autolock"
15472 </source>
15473 <dest>
15474 *: "Always Autolock"
15475 </dest>
15476 <voice>
15477 *: "Always Autolock"
15478 </voice>
15479</phrase>
15480<phrase>
15481 id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
15482 desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
15483 user: core
15484 <source>
15485 *: ""
15486 </source>
15487 <dest>
15488 *: ""
15489 </dest>
15490 <voice>
15491 *: ""
15492 </voice>
15493</phrase>
15494<phrase>
15495 id: LANG_VOICED_DATE_FORMAT
15496 desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
15497 user: core
15498 <source>
15499 *: "dAY"
15500 </source>
15501 <dest>
15502 *: "dAY"
15503 </dest>
15504 <voice>
15505 *: ""
15506 </voice>
15507</phrase>
15508<phrase>
15509 id: LANG_LIST_WRAPAROUND
15510 desc: in Settings
15511 user: core
15512 <source>
15513 *: "List Wraparound"
15514 </source>
15515 <dest>
15516 *: "List Wraparound"
15517 </dest>
15518 <voice>
15519 *: "List Wraparound"
15520 </voice>
15521</phrase>
15522<phrase>
15523 id: LANG_SHOW_SHUTDOWN_MESSAGE
15524 desc: in Settings
15525 user: core
15526 <source>
15527 *: "Show Shutdown Message"
15528 </source>
15529 <dest>
15530 *: "Afficher le message d'arrêt"
15531 </dest>
15532 <voice>
15533 *: "Afficher le message d'arrêt"
15534 </voice>
15535</phrase>
15536<phrase>
15537 id: LANG_LIST_ORDER
15538 desc: in Settings
15539 user: core
15540 <source>
15541 *: "List Order"
15542 </source>
15543 <dest>
15544 *: "Ordre de la liste"
15545 </dest>
15546 <voice>
15547 *: "Ordre de la liste"
15548 </voice>
15549</phrase>
15550<phrase>
15551 id: LANG_ASCENDING
15552 desc: in Settings
15553 user: core
15554 <source>
15555 *: "Ascending"
15556 </source>
15557 <dest>
15558 *: "Croissante"
15559 </dest>
15560 <voice>
15561 *: "Croissante"
15562 </voice>
15563</phrase>
15564<phrase>
15565 id: LANG_DESCENDING
15566 desc: in Settings
15567 user: core
15568 <source>
15569 *: "Descending"
15570 </source>
15571 <dest>
15572 *: "Décroissante"
15573 </dest>
15574 <voice>
15575 *: "Décroissante"
15576 </voice>
15577</phrase>
15578<phrase>
15579 id: LANG_ALBUM_ART
15580 desc: in Settings
15581 user: core
15582 <source>
15583 *: "Album Art"
15584 </source>
15585 <dest>
15586 *: "Pochette d'album"
15587 </dest>
15588 <voice>
15589 *: "Pochette d'album"
15590 </voice>
15591</phrase>
15592<phrase>
15593 id: LANG_PREFER_EMBEDDED
15594 desc: in Settings
15595 user: core
15596 <source>
15597 *: "Prefer Embedded"
15598 </source>
15599 <dest>
15600 *: "Privilégier les images intégrées"
15601 </dest>
15602 <voice>
15603 *: "Privilégier les images intégrées"
15604 </voice>
15605</phrase>
15606<phrase>
15607 id: LANG_PREFER_IMAGE_FILE
15608 desc: in Settings
15609 user: core
15610 <source>
15611 *: "Prefer Image File"
15612 </source>
15613 <dest>
15614 *: "Privilégier les images en fichier"
15615 </dest>
15616 <voice>
15617 *: "Privilégier les images en fichier"
15618 </voice>
15619</phrase>
15620<phrase>
15621 id: LANG_FM_SYNC_RDS_TIME
15622 desc: in radio screen and Settings
15623 user: core
15624 <source>
15625 *: none
15626 rds: "Sync RDS Time"
15627 </source>
15628 <dest>
15629 *: none
15630 rds: "Sync RDS Time"
15631 </dest>
15632 <voice>
15633 *: none
15634 rds: "Sync RDS Time"
15635 </voice>
15636</phrase>
15637<phrase>
15638 id: LANG_SORT_ALBUMS_BY
15639 desc: in Settings
15640 user: core
15641 <source>
15642 *: "Sort albums by"
15643 </source>
15644 <dest>
15645 *: "Trier les albums par"
15646 </dest>
15647 <voice>
15648 *: "Trier les albums par"
15649 </voice>
15650</phrase>
15651<phrase>
15652 id: LANG_ARTIST_PLUS_NAME
15653 desc: in Settings
15654 user: core
15655 <source>
15656 *: "Artist + Name"
15657 </source>
15658 <dest>
15659 *: "Artiste + nom"
15660 </dest>
15661 <voice>
15662 *: "Artiste + nom"
15663 </voice>
15664</phrase>
15665<phrase>
15666 id: LANG_ARTIST_PLUS_YEAR
15667 desc: in Settings
15668 user: core
15669 <source>
15670 *: "Artist + Year"
15671 </source>
15672 <dest>
15673 *: "Artiste + année"
15674 </dest>
15675 <voice>
15676 *: "Artiste + année"
15677 </voice>
15678</phrase>
15679<phrase>
15680 id: LANG_YEAR_SORT_ORDER
15681 desc: in Settings
15682 user: core
15683 <source>
15684 *: "Year sort order"
15685 </source>
15686 <dest>
15687 *: "Order du tri par années"
15688 </dest>
15689 <voice>
15690 *: "Order du tri par années"
15691 </voice>
15692</phrase>
15693<phrase>
15694 id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
15695 desc: in Settings
15696 user: core
15697 <source>
15698 *: "Show year in album title"
15699 </source>
15700 <dest>
15701 *: "Afficher l'année avec le titre de l'album"
15702 </dest>
15703 <voice>
15704 *: "Afficher l'année avec le titre de l'album"
15705 </voice>
15706</phrase>
15707<phrase>
15708 id: LANG_WAIT_FOR_CACHE
15709 desc: in Settings
15710 user: core
15711 <source>
15712 *: "Cache needs to finish updating first!"
15713 </source>
15714 <dest>
15715 *: "La mise à jour du cache doit finir au préalable !"
15716 </dest>
15717 <voice>
15718 *: "La mise à jour du cache doit finir au préalable !"
15719 </voice>
15720</phrase>
15721<phrase>
15722 id: LANG_TRACK_INFO
15723 desc: Track Info Title
15724 user: core
15725 <source>
15726 *: "Track Info"
15727 </source>
15728 <dest>
15729 *: "Informations sur la piste"
15730 </dest>
15731 <voice>
15732 *: "Informations sur la piste"
15733 </voice>
15734</phrase>
15735<phrase>
15736 id: LANG_PLAY
15737 desc: play selected file/directory, in playlist context menu
15738 user: core
15739 <source>
15740 *: "Play"
15741 </source>
15742 <dest>
15743 *: "Lire"
15744 </dest>
15745 <voice>
15746 *: "Lire"
15747 </voice>
15748</phrase>
15749<phrase>
15750 id: LANG_PLAY_SHUFFLED
15751 desc: play selected files in shuffled order, in playlist context menu
15752 user: core
15753 <source>
15754 *: "Play Shuffled"
15755 </source>
15756 <dest>
15757 *: "Lire mélangé"
15758 </dest>
15759 <voice>
15760 *: "Lire mélangé"
15761 </voice>
15762</phrase>
15763<phrase>
15764 id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
15765 desc: used in the playlist settings menu
15766 user: core
15767 <source>
15768 *: "Keep Current Track When Replacing Playlist"
15769 </source>
15770 <dest>
15771 *: "Garder la piste courante lors du remplacement de la liste de lecture"
15772 </dest>
15773 <voice>
15774 *: "Garder la piste courante lors du remplacement de la liste de lecture"
15775 </voice>
15776</phrase>
15777<phrase>
15778 id: LANG_CLEAR_SETTINGS_ON_HOLD
15779 desc: in the system sub menu
15780 user: core
15781 <source>
15782 *: none
15783 clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
15784 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
15785 </source>
15786 <dest>
15787 *: none
15788 clear_settings_on_hold,iriverh10,ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Effacer les paramètres lorsque le bouton de réinitialisation est maintenu enfoncé pendant le démarrage"
15789 </dest>
15790 <voice>
15791 *: none
15792 clear_settings_on_hold,iriverh10,ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Effacer les paramètres lorsque le bouton de réinitialisation est maintenu enfoncé pendant le démarrage"
15793 </voice>
15794</phrase>
15795<phrase>
15796 id: LANG_REWIND_ACROSS_TRACKS
15797 desc: in playback settings menu
15798 user: core
15799 <source>
15800 *: "Rewind Across Tracks"
15801 </source>
15802 <dest>
15803 *: "Rembobiner entre les pistes"
15804 </dest>
15805 <voice>
15806 *: "Rembobiner entre les pistes"
15807 </voice>
15808</phrase>
15809<phrase>
15810 id: LANG_SET_AS
15811 desc: used in the onplay menu
15812 user: core
15813 <source>
15814 *: "Set As..."
15815 </source>
15816 <dest>
15817 *: "Définir comme..."
15818 </dest>
15819 <voice>
15820 *: "Définir comme"
15821 </voice>
15822</phrase>
15823<phrase>
15824 id: LANG_PLAYLIST_DIR
15825 desc: used in the onplay menu
15826 user: core
15827 <source>
15828 *: "Playlist Directory"
15829 </source>
15830 <dest>
15831 *: "Répertoire des listes de lecture"
15832 </dest>
15833 <voice>
15834 *: "Répertoire des listes de lecture"
15835 </voice>
15836</phrase>
15837<phrase>
15838 id: LANG_START_DIR
15839 desc: used in the onplay menu
15840 user: core
15841 <source>
15842 *: "Start Directory"
15843 </source>
15844 <dest>
15845 *: "Répertoire de démarrage"
15846 </dest>
15847 <voice>
15848 *: "Répertoire de démarrage"
15849 </voice>
15850</phrase>
15851<phrase>
15852 id: LANG_RECORDING_DIR
15853 desc: used in the onplay menu
15854 user: core
15855 <source>
15856 *: none
15857 recording: "Recording Directory"
15858 </source>
15859 <dest>
15860 *: none
15861 recording: "Répertoire des enregistrements"
15862 </dest>
15863 <voice>
15864 *: none
15865 recording: "Répertoire des enregistrements"
15866 </voice>
15867</phrase>
15868<phrase>
15869 id: LANG_ADD_TO_PL
15870 desc: used in the onplay menu
15871 user: core
15872 <source>
15873 *: "Add to Playlist..."
15874 </source>
15875 <dest>
15876 *: "Ajouter à la liste de lecture"
15877 </dest>
15878 <voice>
15879 *: "Ajouter à la liste de lecture"
15880 </voice>
15881</phrase>
15882<phrase>
15883 id: LANG_ADD_TO_EXISTING_PL
15884 desc: used in the onplay menu
15885 user: core
15886 <source>
15887 *: "Add to Existing Playlist"
15888 </source>
15889 <dest>
15890 *: "Ajouter à la liste de lecture existante"
15891 </dest>
15892 <voice>
15893 *: "Ajouter à la liste de lecture existante"
15894 </voice>
15895</phrase>
15896<phrase>
15897 id: LANG_PLAYING_NEXT
15898 desc: used in the onplay menu
15899 user: core
15900 <source>
15901 *: "Playing Next..."
15902 </source>
15903 <dest>
15904 *: "Ajouter à la suite..."
15905 </dest>
15906 <voice>
15907 *: "Ajouter à la suite"
15908 </voice>
15909</phrase>
15910<phrase>
15911 id: LANG_PLAY_NEXT
15912 desc: used in the onplay menu
15913 user: core
15914 <source>
15915 *: "Play Next"
15916 </source>
15917 <dest>
15918 *: "Ajouter à la suite"
15919 </dest>
15920 <voice>
15921 *: "Ajouter à la suite"
15922 </voice>
15923</phrase>
15924<phrase>
15925 id: LANG_ADD_SHUFFLED
15926 desc: used in the onplay menu
15927 user: core
15928 <source>
15929 *: "Add Shuffled"
15930 </source>
15931 <dest>
15932 *: "Ajouter mélangé"
15933 </dest>
15934 <voice>
15935 *: "Ajouter mélangé"
15936 </voice>
15937</phrase>
15938<phrase>
15939 id: LANG_PLAY_LAST
15940 desc: used in the onplay menu
15941 user: core
15942 <source>
15943 *: "Play Last"
15944 </source>
15945 <dest>
15946 *: "Lire en dernier"
15947 </dest>
15948 <voice>
15949 *: "Lire en dernier"
15950 </voice>
15951</phrase>
15952<phrase>
15953 id: LANG_PLAY_LAST_SHUFFLED
15954 desc: used in the onplay menu
15955 user: core
15956 <source>
15957 *: "Play Last Shuffled"
15958 </source>
15959 <dest>
15960 *: "Lire en dernier mélangé"
15961 </dest>
15962 <voice>
15963 *: "Lire en dernier mélangé"
15964 </voice>
15965</phrase>
15966<phrase>
15967 id: LANG_VOLUME_ADJUST_MODE
15968 desc: in system settings
15969 user: core
15970 <source>
15971 *: none
15972 perceptual_volume: "Volume Adjustment Mode"
15973 </source>
15974 <dest>
15975 *: none
15976 perceptual_volume: "Méthode d'ajustement du volume"
15977 </dest>
15978 <voice>
15979 *: none
15980 perceptual_volume: "Méthode d'ajustement du volume"
15981 </voice>
15982</phrase>
15983<phrase>
15984 id: LANG_VOLUME_ADJUST_NORM_STEPS
15985 desc: in system settings
15986 user: core
15987 <source>
15988 *: none
15989 perceptual_volume: "Number of Volume Steps"
15990 </source>
15991 <dest>
15992 *: none
15993 perceptual_volume: "Nombre de niveaux de volume"
15994 </dest>
15995 <voice>
15996 *: none
15997 perceptual_volume: "Nombre de niveaux de volume"
15998 </voice>
15999</phrase>
16000<phrase>
16001 id: LANG_PERCEPTUAL
16002 desc: in system settings -> volume adjustment mode
16003 user: core
16004 <source>
16005 *: none
16006 perceptual_volume: "Perceptual"
16007 </source>
16008 <dest>
16009 *: none
16010 perceptual_volume: "Perceptual"
16011 </dest>
16012 <voice>
16013 *: none
16014 perceptual_volume: "Perceptual"
16015 </voice>
16016</phrase>
16017<phrase>
16018 id: LANG_SHOW_TRACKS_WHILE_BROWSING
16019 desc: in PictureFlow Main Menu
16020 user: core
16021 <source>
16022 *: "Show Tracks While Browsing"
16023 </source>
16024 <dest>
16025 *: "Afficher les pistes lors de la navigation"
16026 </dest>
16027 <voice>
16028 *: "Afficher les pistes lors de la navigation"
16029 </voice>
16030</phrase>
16031<phrase>
16032 id: LANG_GOTO_LAST_ALBUM
16033 desc: in PictureFlow Main Menu
16034 user: core
16035 <source>
16036 *: "Go to Last Album"
16037 </source>
16038 <dest>
16039 *: "Aller au dernier album"
16040 </dest>
16041 <voice>
16042 *: "Aller au dernier album"
16043 </voice>
16044</phrase>
16045<phrase>
16046 id: LANG_DATABASE_DIR
16047 desc: in database settings menu
16048 user: core
16049 <source>
16050 *: "Database Directory"
16051 </source>
16052 <dest>
16053 *: "Répertoire de la base de données"
16054 </dest>
16055 <voice>
16056 *: "Répertoire de la base de données"
16057 </voice>
16058</phrase>
16059<phrase>
16060 id: LANG_REMOVE_QUEUED_TRACKS
16061 desc: Confirmation dialog
16062 user: core
16063 <source>
16064 *: "Remove Queued Tracks?"
16065 </source>
16066 <dest>
16067 *: "Retirer les pistes en file attente ?"
16068 </dest>
16069 <voice>
16070 *: "Retirer les pistes en file attente ?"
16071 </voice>
16072</phrase>
16073<phrase>
16074 id: LANG_QUICK_IGNORE_DIRACHE
16075 desc: in Settings
16076 user: core
16077 <source>
16078 *: "Quick (Ignore Directory Cache)"
16079 </source>
16080 <dest>
16081 *: "Rapide (Ignorer le cache des répertoires)"
16082 </dest>
16083 <voice>
16084 *: "Rapide (Ignorer le cache des répertoires)"
16085 </voice>
16086</phrase>
16087<phrase>
16088 id: LANG_WPS
16089 desc: in Settings
16090 user: core
16091 <source>
16092 *: "What's Playing Screen"
16093 </source>
16094 <dest>
16095 *: "Écran En Lecture"
16096 </dest>
16097 <voice>
16098 *: "Écran En Lecture"
16099 </voice>
16100</phrase>
16101<phrase>
16102 id: LANG_DEFAULT_BROWSER
16103 desc: in Settings
16104 user: core
16105 <source>
16106 *: "Default Browser"
16107 </source>
16108 <dest>
16109 *: "Navigateur par défaut"
16110 </dest>
16111 <voice>
16112 *: "Navigateur par défaut"
16113 </voice>
16114</phrase>
16115<phrase>
16116 id: LANG_AMAZE_MENU
16117 desc: Amaze game
16118 user: core
16119 <source>
16120 *: "Amaze Main Menu"
16121 </source>
16122 <dest>
16123 *: "Amaze Main Menu"
16124 </dest>
16125 <voice>
16126 *: "Amaze Main Menu"
16127 </voice>
16128</phrase>
16129<phrase>
16130 id: LANG_SET_MAZE_SIZE
16131 desc: Maze size in Amaze game
16132 user: core
16133 <source>
16134 *: "Set Maze Size"
16135 </source>
16136 <dest>
16137 *: "Set Maze Size"
16138 </dest>
16139 <voice>
16140 *: "Set Maze Size"
16141 </voice>
16142</phrase>
16143<phrase>
16144 id: LANG_VIEW_MAP
16145 desc: Map in Amaze game
16146 user: core
16147 <source>
16148 *: "View Map"
16149 </source>
16150 <dest>
16151 *: "View Map"
16152 </dest>
16153 <voice>
16154 *: "View Map"
16155 </voice>
16156</phrase>
16157<phrase>
16158 id: LANG_SHOW_COMPASS
16159 desc: Compass in Amaze game
16160 user: core
16161 <source>
16162 *: "Show Compass"
16163 </source>
16164 <dest>
16165 *: "Show Compass"
16166 </dest>
16167 <voice>
16168 *: "Show Compass"
16169 </voice>
16170</phrase>
16171<phrase>
16172 id: LANG_SHOW_MAP
16173 desc: Map in Amaze game
16174 user: core
16175 <source>
16176 *: "Show Map"
16177 </source>
16178 <dest>
16179 *: "Show Map"
16180 </dest>
16181 <voice>
16182 *: "Show Map"
16183 </voice>
16184</phrase>
16185<phrase>
16186 id: LANG_REMEMBER_PATH
16187 desc: Map in Amaze game
16188 user: core
16189 <source>
16190 *: "Remember Path"
16191 </source>
16192 <dest>
16193 *: "Remember Path"
16194 </dest>
16195 <voice>
16196 *: "Remember Path"
16197 </voice>
16198</phrase>
16199<phrase>
16200 id: LANG_USE_LARGE_TILES
16201 desc: Map in Amaze game
16202 user: core
16203 <source>
16204 *: "Use Large Tiles"
16205 </source>
16206 <dest>
16207 *: "Use Large Tiles"
16208 </dest>
16209 <voice>
16210 *: "Use Large Tiles"
16211 </voice>
16212</phrase>
16213<phrase>
16214 id: LANG_SHOW_SOLUTION
16215 desc: Map in Amaze game
16216 user: core
16217 <source>
16218 *: "Show Solution"
16219 </source>
16220 <dest>
16221 *: "Show Solution"
16222 </dest>
16223 <voice>
16224 *: "Show Solution"
16225 </voice>
16226</phrase>
16227<phrase>
16228 id: LANG_QUIT_WITHOUT_SAVING
16229 desc:
16230 user: core
16231 <source>
16232 *: "Quit without saving"
16233 </source>
16234 <dest>
16235 *: "Quit without saving"
16236 </dest>
16237 <voice>
16238 *: "Quit without saving"
16239 </voice>
16240</phrase>
16241<phrase>
16242 id: LANG_GENERATING_MAZE
16243 desc: Amaze game
16244 user: core
16245 <source>
16246 *: "Generating maze..."
16247 </source>
16248 <dest>
16249 *: "Generating maze..."
16250 </dest>
16251 <voice>
16252 *: "Generating maze"
16253 </voice>
16254</phrase>
16255<phrase>
16256 id: LANG_YOU_WIN
16257 desc: Success in game
16258 user: core
16259 <source>
16260 *: "You win!"
16261 </source>
16262 <dest>
16263 *: "You win!"
16264 </dest>
16265 <voice>
16266 *: "You win!"
16267 </voice>
16268</phrase>
16269<phrase>
16270 id: LANG_YOU_CHEATED
16271 desc: Cheated in game
16272 user: core
16273 <source>
16274 *: "You cheated!"
16275 </source>
16276 <dest>
16277 *: "You cheated!"
16278 </dest>
16279 <voice>
16280 *: "You cheated!"
16281 </voice>
16282</phrase>
16283<phrase>
16284 id: LANG_DIFFICULTY_EASY
16285 desc: Game difficulty
16286 user: core
16287 <source>
16288 *: "Easy"
16289 </source>
16290 <dest>
16291 *: "Easy"
16292 </dest>
16293 <voice>
16294 *: "Easy"
16295 </voice>
16296</phrase>
16297<phrase>
16298 id: LANG_DIFFICULTY_MEDIUM
16299 desc: Game difficulty
16300 user: core
16301 <source>
16302 *: "Medium"
16303 </source>
16304 <dest>
16305 *: "Medium"
16306 </dest>
16307 <voice>
16308 *: "Medium"
16309 </voice>
16310</phrase>
16311<phrase>
16312 id: LANG_DIFFICULTY_HARD
16313 desc: Game difficulty
16314 user: core
16315 <source>
16316 *: "Hard"
16317 </source>
16318 <dest>
16319 *: "Hard"
16320 </dest>
16321 <voice>
16322 *: "Hard"
16323 </voice>
16324</phrase>
16325<phrase>
16326 id: LANG_DIFFICULTY_EXPERT
16327 desc: Game difficulty
16328 user: core
16329 <source>
16330 *: "Expert"
16331 </source>
16332 <dest>
16333 *: "Expert"
16334 </dest>
16335 <voice>
16336 *: "Expert"
16337 </voice>
16338</phrase>
16339<phrase>
16340 id: LANG_STEREOSW_MODE
16341 desc: Stereo Switch Mode
16342 user: core
16343 <source>
16344 *: "Stereo Switch Mode"
16345 </source>
16346 <dest>
16347 *: "Stereo Switch Mode"
16348 </dest>
16349 <voice>
16350 *: "Stereo Switch Mode"
16351 </voice>
16352</phrase>
16353<phrase>
16354 id: LANG_REVERSE
16355 desc: in settings_menu
16356 user: core
16357 <source>
16358 *: "Reverse"
16359 </source>
16360 <dest>
16361 *: "Inversé"
16362 </dest>
16363 <voice>
16364 *: "Inversé"
16365 </voice>
16366</phrase>
16367<phrase>
16368 id: LANG_ALWAYS_ZERO
16369 desc: in settings_menu
16370 user: core
16371 <source>
16372 *: "Always 0"
16373 </source>
16374 <dest>
16375 *: "Toujours 0"
16376 </dest>
16377 <voice>
16378 *: "Toujours 0"
16379 </voice>
16380</phrase>
16381<phrase>
16382 id: LANG_ALWAYS_ONE
16383 desc: in settings_menu
16384 user: core
16385 <source>
16386 *: "Always 1"
16387 </source>
16388 <dest>
16389 *: "Toujours 1"
16390 </dest>
16391 <voice>
16392 *: "Toujours 1"
16393 </voice>
16394</phrase>
16395<phrase>
16396 id: LANG_LEGAL_NOTICES
16397 desc: in system menu
16398 user: core
16399 <source>
16400 *: "Legal Notices"
16401 </source>
16402 <dest>
16403 *: "Mentions Légales"
16404 </dest>
16405 <voice>
16406 *: "Mentions Légales"
16407 </voice>
16408</phrase>
16409<phrase>
16410 id: LANG_ERROR_FORMATSTR
16411 desc: for general use
16412 user: core
16413 <source>
16414 *: "Error: %s"
16415 </source>
16416 <dest>
16417 *: "Erreur: %s"
16418 </dest>
16419 <voice>
16420 *: "Erreur"
16421 </voice>
16422</phrase>
16423<phrase>
16424 id: LANG_MIKMOD_SETTINGS
16425 desc: mikmod plugin
16426 user: core
16427 <source>
16428 *: "Mikmod Settings"
16429 </source>
16430 <dest>
16431 *: "Mikmod Settings"
16432 </dest>
16433 <voice>
16434 *: "Mik mod Settings"
16435 </voice>
16436</phrase>
16437<phrase>
16438 id: LANG_MIKMOD_MENU
16439 desc: mikmod plugin
16440 user: core
16441 <source>
16442 *: "Mikmod Menu"
16443 </source>
16444 <dest>
16445 *: "Mikmod Menu"
16446 </dest>
16447 <voice>
16448 *: "Mik mod Menu"
16449 </voice>
16450</phrase>
16451<phrase>
16452 id: LANG_CHESSBOX_MENU
16453 desc: chessbox plugin
16454 user: core
16455 <source>
16456 *: "Chessbox Menu"
16457 </source>
16458 <dest>
16459 *: "Chessbox Menu"
16460 </dest>
16461 <voice>
16462 *: "Chess box Menu"
16463 </voice>
16464</phrase>
16465<phrase>
16466 id: VOICE_INVALID_VOICE_FILE
16467 desc: played if the voice file fails to load
16468 user: core
16469 <source>
16470 *: ""
16471 </source>
16472 <dest>
16473 *: ""
16474 </dest>
16475 <voice>
16476 *: "Fichier Voix non valide"
16477 </voice>
16478</phrase>
16479<phrase>
16480 id: LANG_PERCENT_FORMAT
16481 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
16482 user: core
16483 <source>
16484 *: "%ld%%"
16485 </source>
16486 <dest>
16487 *: "%ld%%"
16488 </dest>
16489 <voice>
16490 *: none
16491 </voice>
16492</phrase>
16493<phrase>
16494 id: LANG_CHOOSE_FILE
16495 desc: file_picker plugin ask user to select a file
16496 user: core
16497 <source>
16498 *: "Choose File"
16499 </source>
16500 <dest>
16501 *: "Choisir un fichier"
16502 </dest>
16503 <voice>
16504 *: "Choisir un fichier"
16505 </voice>
16506</phrase>
16507<phrase>
16508 id: LANG_REMAINING
16509 desc: Playing Time
16510 user: core
16511 <source>
16512 *: "Remaining"
16513 </source>
16514 <dest>
16515 *: "Restant"
16516 </dest>
16517 <voice>
16518 *: "Restant"
16519 </voice>
16520</phrase>
diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang
index a7d1eaa9a7..1d791f0a1d 100644
--- a/apps/lang/italiano.lang
+++ b/apps/lang/italiano.lang
@@ -5141,7 +5141,7 @@
5141 </dest> 5141 </dest>
5142 <voice> 5142 <voice>
5143 *: none 5143 *: none
5144 recording: "Suddivisione per: " 5144 recording: "Suddivisione per "
5145 </voice> 5145 </voice>
5146</phrase> 5146</phrase>
5147<phrase> 5147<phrase>
@@ -5209,7 +5209,7 @@
5209 </dest> 5209 </dest>
5210 <voice> 5210 <voice>
5211 *: none 5211 *: none
5212 recording: "Tempo: " 5212 recording: "Tempo "
5213 </voice> 5213 </voice>
5214</phrase> 5214</phrase>
5215<phrase> 5215<phrase>
@@ -5634,7 +5634,7 @@
5634 </dest> 5634 </dest>
5635 <voice> 5635 <voice>
5636 *: none 5636 *: none
5637 remote: "(Vol- : Re-Attivato)" 5637 remote: "(Volume, meno per riattivare)"
5638 </voice> 5638 </voice>
5639</phrase> 5639</phrase>
5640<phrase> 5640<phrase>
@@ -5916,7 +5916,7 @@
5916 *: "Liberi:" 5916 *: "Liberi:"
5917 </dest> 5917 </dest>
5918 <voice> 5918 <voice>
5919 *: "Spazio Libero su Disco:" 5919 *: "Spazio Libero su Disco"
5920 </voice> 5920 </voice>
5921</phrase> 5921</phrase>
5922<phrase> 5922<phrase>
@@ -7030,7 +7030,7 @@
7030 *: "Modo:" 7030 *: "Modo:"
7031 </dest> 7031 </dest>
7032 <voice> 7032 <voice>
7033 *: "Modo:" 7033 *: "Modo"
7034 </voice> 7034 </voice>
7035</phrase> 7035</phrase>
7036<phrase> 7036<phrase>
@@ -8692,7 +8692,7 @@
8692 </dest> 8692 </dest>
8693 <voice> 8693 <voice>
8694 *: none 8694 *: none
8695 rtc: "ora attuale:" 8695 rtc: "ora attuale"
8696 </voice> 8696 </voice>
8697</phrase> 8697</phrase>
8698<phrase> 8698<phrase>
@@ -10627,7 +10627,7 @@
10627 *: "Prossima Traccia:" 10627 *: "Prossima Traccia:"
10628 </dest> 10628 </dest>
10629 <voice> 10629 <voice>
10630 *: "Prossima Traccia:" 10630 *: "Prossima Traccia"
10631 </voice> 10631 </voice>
10632</phrase> 10632</phrase>
10633<phrase> 10633<phrase>
@@ -10641,7 +10641,7 @@
10641 *: "Prossima:" 10641 *: "Prossima:"
10642 </dest> 10642 </dest>
10643 <voice> 10643 <voice>
10644 *: "Prossima:" 10644 *: "Prossima"
10645 </voice> 10645 </voice>
10646</phrase> 10646</phrase>
10647<phrase> 10647<phrase>
@@ -11189,7 +11189,7 @@
11189 </dest> 11189 </dest>
11190 <voice> 11190 <voice>
11191 *: none 11191 *: none
11192 radio: "Potenza segnale:" 11192 radio: "Potenza segnale"
11193 </voice> 11193 </voice>
11194</phrase> 11194</phrase>
11195<phrase> 11195<phrase>
@@ -12463,7 +12463,7 @@
12463 *: "Livello 9: 1 mossa / 60 min" 12463 *: "Livello 9: 1 mossa / 60 min"
12464 </dest> 12464 </dest>
12465 <voice> 12465 <voice>
12466 *: "Livello 9: 1 mossa ogni 60 minuti" 12466 *: "Livello 9, 1 mossa ogni 60 minuti"
12467 </voice> 12467 </voice>
12468</phrase> 12468</phrase>
12469<phrase> 12469<phrase>
@@ -12477,7 +12477,7 @@
12477 *: "Livello 8: 1 mossa / 15 min" 12477 *: "Livello 8: 1 mossa / 15 min"
12478 </dest> 12478 </dest>
12479 <voice> 12479 <voice>
12480 *: "Livello 8: 1 mossa ogni 15 minuti" 12480 *: "Livello 8, 1 mossa ogni 15 minuti"
12481 </voice> 12481 </voice>
12482</phrase> 12482</phrase>
12483<phrase> 12483<phrase>
@@ -12991,7 +12991,7 @@
12991 *: "Livello 2: 60 mosse / 15 min" 12991 *: "Livello 2: 60 mosse / 15 min"
12992 </dest> 12992 </dest>
12993 <voice> 12993 <voice>
12994 *: "Livello 2: 60 mosse ogni 15 minuti" 12994 *: "Livello 2, 60 mosse ogni 15 minuti"
12995 </voice> 12995 </voice>
12996</phrase> 12996</phrase>
12997<phrase> 12997<phrase>
@@ -13095,7 +13095,7 @@
13095 *: "Livello 5: 40 mosse / 60 min" 13095 *: "Livello 5: 40 mosse / 60 min"
13096 </dest> 13096 </dest>
13097 <voice> 13097 <voice>
13098 *: "Livello 5: 40 mosse ogni 60 minuti" 13098 *: "Livello 5, 40 mosse ogni 60 minuti"
13099 </voice> 13099 </voice>
13100</phrase> 13100</phrase>
13101<phrase> 13101<phrase>
@@ -13272,7 +13272,7 @@
13272 *: "Livello 10: 1 mossa / 600 min" 13272 *: "Livello 10: 1 mossa / 600 min"
13273 </dest> 13273 </dest>
13274 <voice> 13274 <voice>
13275 *: "Livello 10: 1 mossa ogni 600 minuti" 13275 *: "Livello 10, 1 mossa ogni 600 minuti"
13276 </voice> 13276 </voice>
13277</phrase> 13277</phrase>
13278<phrase> 13278<phrase>
@@ -13572,21 +13572,21 @@
13572 *: "Livello 7: 40 mosse / 240 min" 13572 *: "Livello 7: 40 mosse / 240 min"
13573 </dest> 13573 </dest>
13574 <voice> 13574 <voice>
13575 *: "Livello 7: 40 mosse ogni 240 minuti" 13575 *: "Livello 7, 40 mosse ogni 240 minuti"
13576 </voice> 13576 </voice>
13577</phrase> 13577</phrase>
13578<phrase> 13578<phrase>
13579 id: LANG_PLAYTIME_REMAINING 13579 id: LANG_PLAYTIME_REMAINING
13580 desc: playing time screen 13580 desc: deprecated
13581 user: core 13581 user: core
13582 <source> 13582 <source>
13583 *: "Playlist remaining:" 13583 *: ""
13584 </source> 13584 </source>
13585 <dest> 13585 <dest>
13586 *: "Playlist rimanente:" 13586 *: ""
13587 </dest> 13587 </dest>
13588 <voice> 13588 <voice>
13589 *: "Playlist rimanente" 13589 *: ""
13590 </voice> 13590 </voice>
13591</phrase> 13591</phrase>
13592<phrase> 13592<phrase>
@@ -13875,7 +13875,7 @@
13875 *: "Livello 3: 60 mosse / 30 min" 13875 *: "Livello 3: 60 mosse / 30 min"
13876 </dest> 13876 </dest>
13877 <voice> 13877 <voice>
13878 *: "Livello 3: 60 mosse ogni 30 minuti" 13878 *: "Livello 3, 60 mosse ogni 30 minuti"
13879 </voice> 13879 </voice>
13880</phrase> 13880</phrase>
13881<phrase> 13881<phrase>
@@ -14027,7 +14027,7 @@
14027 *: "Livello 6: 40 mosse / 120 min" 14027 *: "Livello 6: 40 mosse / 120 min"
14028 </dest> 14028 </dest>
14029 <voice> 14029 <voice>
14030 *: "Livello 6: 40 mosse ogni 120 minuti" 14030 *: "Livello 6, 40 mosse ogni 120 minuti"
14031 </voice> 14031 </voice>
14032</phrase> 14032</phrase>
14033<phrase> 14033<phrase>
@@ -14134,7 +14134,7 @@
14134 *: "Livello 1: 60 mosse / 5 min" 14134 *: "Livello 1: 60 mosse / 5 min"
14135 </dest> 14135 </dest>
14136 <voice> 14136 <voice>
14137 *: "Livello 1: 60 mosse ogni 5 minuti" 14137 *: "Livello 1, 60 mosse ogni 5 minuti"
14138 </voice> 14138 </voice>
14139</phrase> 14139</phrase>
14140<phrase> 14140<phrase>
@@ -14260,7 +14260,7 @@
14260 *: "Livello 4: 40 mosse / 30 min" 14260 *: "Livello 4: 40 mosse / 30 min"
14261 </dest> 14261 </dest>
14262 <voice> 14262 <voice>
14263 *: "Livello 4: 40 mosse ogni 30 minuti" 14263 *: "Livello 4, 40 mosse ogni 30 minuti"
14264 </voice> 14264 </voice>
14265</phrase> 14265</phrase>
14266<phrase> 14266<phrase>
@@ -14695,16 +14695,13 @@
14695 desc: prefix for elapsed playtime announcement 14695 desc: prefix for elapsed playtime announcement
14696 user: core 14696 user: core
14697 <source> 14697 <source>
14698 *: none 14698 *: "Elapsed"
14699 hotkey: "Elapsed"
14700 </source> 14699 </source>
14701 <dest> 14700 <dest>
14702 *: none 14701 *: "Trascorso"
14703 hotkey: "Trascorso"
14704 </dest> 14702 </dest>
14705 <voice> 14703 <voice>
14706 *: none 14704 *: "Trascorso"
14707 hotkey: "Trascorso"
14708 </voice> 14705 </voice>
14709</phrase> 14706</phrase>
14710<phrase> 14707<phrase>
@@ -16413,3 +16410,59 @@
16413 *: none 16410 *: none
16414 </voice> 16411 </voice>
16415</phrase> 16412</phrase>
16413<phrase>
16414 id: LANG_CHOOSE_FILE
16415 desc: file_picker plugin ask user to select a file
16416 user: core
16417 <source>
16418 *: "Choose File"
16419 </source>
16420 <dest>
16421 *: "Scegli file"
16422 </dest>
16423 <voice>
16424 *: "Scegli file"
16425 </voice>
16426</phrase>
16427<phrase>
16428 id: LANG_REMAINING
16429 desc: Playing Time
16430 user: core
16431 <source>
16432 *: "Remaining"
16433 </source>
16434 <dest>
16435 *: "Rimanente"
16436 </dest>
16437 <voice>
16438 *: "Rimanente"
16439 </voice>
16440</phrase>
16441<phrase>
16442 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
16443 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
16444 user: core
16445 <source>
16446 *: "Selection too big, %d random tracks will be selected"
16447 </source>
16448 <dest>
16449 *: "Selezione troppo grande, verranno prese solamente %d tracce casuali dalla selezione"
16450 </dest>
16451 <voice>
16452 *: "Selezione troppo grande, verranno prese solamente alcune tracce casuali dalla selezione"
16453 </voice>
16454</phrase>
16455<phrase>
16456 id: LANG_DISABLE_MAINMENU_SCROLLING
16457 desc: Disable main menu scrolling
16458 user: core
16459 <source>
16460 *: "Disable main menu scrolling"
16461 </source>
16462 <dest>
16463 *: "Disabilita lo scorrimento del menu principale"
16464 </dest>
16465 <voice>
16466 *: "Disabilita lo scorrimento del menu principale"
16467 </voice>
16468</phrase>
diff --git a/apps/lang/korean.lang b/apps/lang/korean.lang
index 3656675b19..dee9f8a022 100644
--- a/apps/lang/korean.lang
+++ b/apps/lang/korean.lang
@@ -19,6 +19,7 @@
19# - Kong Hui Sug 19# - Kong Hui Sug
20# - Jeong Taek In 20# - Jeong Taek In
21# - Sanggon Lee 21# - Sanggon Lee
22# - Hoseok Seo
22# 23#
23# ì´ íŒŒì¼ì€ 한국어 언어 파ì¼ìž…니다. 24# ì´ íŒŒì¼ì€ 한국어 언어 파ì¼ìž…니다.
24# 25#
@@ -66,10 +67,10 @@
66 *: "On" 67 *: "On"
67 </source> 68 </source>
68 <dest> 69 <dest>
69 *: "ì¬ìš©" 70 *: "켬"
70 </dest> 71 </dest>
71 <voice> 72 <voice>
72 *: "ì¬ìš©" 73 *: "켬"
73 </voice> 74 </voice>
74</phrase> 75</phrase>
75<phrase> 76<phrase>
@@ -80,10 +81,24 @@
80 *: "Off" 81 *: "Off"
81 </source> 82 </source>
82 <dest> 83 <dest>
83 *: "사용안함" 84 *: "ë”"
84 </dest> 85 </dest>
85 <voice> 86 <voice>
86 *: "사용안함" 87 *: "ë”"
88 </voice>
89</phrase>
90<phrase>
91 id: LANG_ASK
92 desc: in settings_menu
93 user: core
94 <source>
95 *: "Ask"
96 </source>
97 <dest>
98 *: "요청"
99 </dest>
100 <voice>
101 *: "요청"
87 </voice> 102 </voice>
88</phrase> 103</phrase>
89<phrase> 104<phrase>
@@ -101,6 +116,34 @@
101 </voice> 116 </voice>
102</phrase> 117</phrase>
103<phrase> 118<phrase>
119 id: LANG_NORMAL
120 desc: in settings_menu
121 user: core
122 <source>
123 *: "Normal"
124 </source>
125 <dest>
126 *: "보통"
127 </dest>
128 <voice>
129 *: "보통"
130 </voice>
131</phrase>
132<phrase>
133 id: LANG_GAIN
134 desc: Generic string for gain used in EQ menu and recording screen
135 user: core
136 <source>
137 *: "Gain"
138 </source>
139 <dest>
140 *: "게ì¸"
141 </dest>
142 <voice>
143 *: "게ì¸"
144 </voice>
145</phrase>
146<phrase>
104 id: LANG_WAIT 147 id: LANG_WAIT
105 desc: general please wait splash 148 desc: general please wait splash
106 user: core 149 user: core
@@ -108,27 +151,38 @@
108 *: "Loading..." 151 *: "Loading..."
109 </source> 152 </source>
110 <dest> 153 <dest>
111 *: "불러오고 있습니다..." 154 *: "로딩 ì¤..."
112 </dest> 155 </dest>
113 <voice> 156 <voice>
114 *: "" 157 *: "로딩 중"
115 </voice> 158 </voice>
116</phrase> 159</phrase>
117<phrase> 160<phrase>
118 id: LANG_CONFIRM_SHUTDOWN 161 id: LANG_LOADING_PERCENT
119 desc: in shutdown screen 162 desc: splash number of percents loaded
120 user: core 163 user: core
121 <source> 164 <source>
122 *: none 165 *: "Loading... %d%% done (%s)"
123 soft_shutdown: "Press OFF to shut down"
124 </source> 166 </source>
125 <dest> 167 <dest>
126 *: none 168 *: "로딩 중... %d%% 완료 (%s)"
127 soft_shutdown: "OFF버튼으로 ì „ì›ë„기"
128 </dest> 169 </dest>
129 <voice> 170 <voice>
130 *: none 171 *: "로딩 중"
131 soft_shutdown: "" 172 </voice>
173</phrase>
174<phrase>
175 id: LANG_SCANNING_DISK
176 desc: when booting up and rebuilding the cache and calculating free space
177 user: core
178 <source>
179 *: "Scanning disk..."
180 </source>
181 <dest>
182 *: "ë””ìŠ¤í¬ ê²€ì‚¬ 중..."
183 </dest>
184 <voice>
185 *: "ë””ìŠ¤í¬ ê²€ì‚¬ 중"
132 </voice> 186 </voice>
133</phrase> 187</phrase>
134<phrase> 188<phrase>
@@ -139,27 +193,24 @@
139 *: "Shutting down..." 193 *: "Shutting down..."
140 </source> 194 </source>
141 <dest> 195 <dest>
142 *: "ì‹œìŠ¤í…œì´ ì¢…ë£Œë©ë‹ˆë‹¤." 196 *: "종료 ì¤..."
143 </dest> 197 </dest>
144 <voice> 198 <voice>
145 *: "" 199 *: "종료 중"
146 </voice> 200 </voice>
147</phrase> 201</phrase>
148<phrase> 202<phrase>
149 id: LANG_REMOVE_MMC 203 id: LANG_CANCEL
150 desc: before acknowledging usb in case an MMC is inserted (Ondio) 204 desc: Visual confirmation of canceling a changed setting
151 user: core 205 user: core
152 <source> 206 <source>
153 *: none 207 *: "Cancelled"
154 archosondio*: "Please remove inserted MMC"
155 </source> 208 </source>
156 <dest> 209 <dest>
157 *: none 210 *: "취소ë¨"
158 archosondio*: "ì‚½ìž…ëœ MMC를 제거해주세요"
159 </dest> 211 </dest>
160 <voice> 212 <voice>
161 *: none 213 *: "취소ë¨"
162 archosondio*: "ì‚½ìž…ëœ MMC를 제거해주세요"
163 </voice> 214 </voice>
164</phrase> 215</phrase>
165<phrase> 216<phrase>
@@ -170,651 +221,673 @@
170 *: "Failed" 221 *: "Failed"
171 </source> 222 </source>
172 <dest> 223 <dest>
173 *: "실패하였습니다." 224 *: "실패함"
225 </dest>
226 <voice>
227 *: "실패함"
228 </voice>
229</phrase>
230<phrase>
231 id: LANG_CHANNELS
232 desc: in sound_settings
233 user: core
234 <source>
235 *: "Channels"
236 </source>
237 <dest>
238 *: "채ë„"
239 </dest>
240 <voice>
241 *: "채ë„"
242 </voice>
243</phrase>
244<phrase>
245 id: LANG_RESET_ASK
246 desc: confirm to reset settings
247 user: core
248 <source>
249 *: "Are You Sure?"
250 </source>
251 <dest>
252 *: "확실하나요?"
253 </dest>
254 <voice>
255 *: "확실하나요?"
256 </voice>
257</phrase>
258<phrase>
259 id: LANG_CONFIRM_WITH_BUTTON
260 desc: Generic string to use to confirm
261 user: core
262 <source>
263 *: "PLAY = Yes"
264 cowond2*: "MENU, or top-right = Yes"
265 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
266 iriverh100,iriverh120,iriverh300: "NAVI = Yes"
267 mrobe500: "PLAY, POWER, or top-right = Yes"
268 vibe500: "OK = Yes"
269 </source>
270 <dest>
271 *: "ìž¬ìƒ = 예"
272 cowond2*: "메뉴, ë˜ëŠ” 오른쪽 ìƒë‹¨ = 예"
273 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "ì„ íƒ = 예"
274 iriverh100,iriverh120,iriverh300: "íƒìƒ‰ = 예"
275 mrobe500: "재ìƒ, ì „ì› ë˜ëŠ” 오른쪽 ìƒë‹¨ = 예"
276 vibe500: "í™•ì¸ = 예"
174 </dest> 277 </dest>
175 <voice> 278 <voice>
176 *: "" 279 *: ""
177 </voice> 280 </voice>
178</phrase> 281</phrase>
179<phrase> 282<phrase>
180 id: LANG_BOOKMARK_MENU_RECENT_BOOKMARKS 283 id: LANG_CANCEL_WITH_ANY
181 desc: in the main menu 284 desc: Generic string to use to cancel
182 user: core 285 user: core
183 <source> 286 <source>
184 *: "Recent Bookmarks" 287 *: "Any Other = No"
185 </source> 288 </source>
186 <dest> 289 <dest>
187 *: "최근 ë¶ë§ˆí¬" 290 *: "기타 = ì•„ëˆì˜¤"
188 </dest> 291 </dest>
189 <voice> 292 <voice>
190 *: "최근 ë¶ë§ˆí¬" 293 *: ""
191 </voice> 294 </voice>
192</phrase> 295</phrase>
193<phrase> 296<phrase>
194 id: LANG_SOUND_SETTINGS 297 id: LANG_ROCKBOX_TITLE
195 desc: in the main menu 298 desc: main menu title
196 user: core 299 user: core
197 <source> 300 <source>
198 *: "Sound Settings" 301 *: "Rockbox"
199 </source> 302 </source>
200 <dest> 303 <dest>
201 *: "ìŒí–¥ ì¤ì •" 304 *: "ë¡ë°•ìŠ¤"
202 </dest> 305 </dest>
203 <voice> 306 <voice>
204 *: "ìŒí–¥ ì¤ì •" 307 *: "ë¡ë°•ìŠ¤"
205 </voice> 308 </voice>
206</phrase> 309</phrase>
207<phrase> 310<phrase>
208 id: LANG_GENERAL_SETTINGS 311 id: LANG_BOOKMARK_MENU_RECENT_BOOKMARKS
209 desc: in the main menu 312 desc: in the main menu
210 user: core 313 user: core
211 <source> 314 <source>
212 *: "General Settings" 315 *: "Recent Bookmarks"
213 </source> 316 </source>
214 <dest> 317 <dest>
215 *: "ì¼ë°˜ 설정" 318 *: "최근 ë¶ë§ˆí¬"
216 </dest> 319 </dest>
217 <voice> 320 <voice>
218 *: "ì¼ë°˜ 설정" 321 *: "최근 ë¶ë§ˆí¬"
219 </voice> 322 </voice>
220</phrase> 323</phrase>
221<phrase> 324<phrase>
222 id: LANG_MANAGE_MENU 325 id: LANG_DIR_BROWSER
223 desc: in the main menu 326 desc: main menu title
224 user: core 327 user: core
225 <source> 328 <source>
226 *: "Manage Settings" 329 *: "Files"
227 </source> 330 </source>
228 <dest> 331 <dest>
229 *: "설정 관리" 332 *: "파ì¼"
230 </dest> 333 </dest>
231 <voice> 334 <voice>
232 *: "설정 관리" 335 *: "파ì¼"
233 </voice> 336 </voice>
234</phrase> 337</phrase>
235<phrase> 338<phrase>
236 id: LANG_CUSTOM_THEME 339 id: LANG_TAGCACHE
237 desc: in the main menu 340 desc: in the main menu and the settings menu
238 user: core 341 user: core
239 <source> 342 <source>
240 *: "Browse Themes" 343 *: "Database"
241 </source> 344 </source>
242 <dest> 345 <dest>
243 *: "테마 ì„ íƒ" 346 *: "ë°ì´í°ë² ì´ìŠ¤"
244 </dest> 347 </dest>
245 <voice> 348 <voice>
246 *: "테마 ì„ íƒ" 349 *: "ë°ì´í°ë² ì´ìŠ¤"
247 </voice> 350 </voice>
248</phrase> 351</phrase>
249<phrase> 352<phrase>
250 id: LANG_FM_RADIO 353 id: LANG_NOW_PLAYING
251 desc: in the main menu 354 desc: in the main menu
252 user: core 355 user: core
253 <source> 356 <source>
254 *: none 357 *: "Now Playing"
255 radio: "FM Radio"
256 </source> 358 </source>
257 <dest> 359 <dest>
258 *: none 360 *: "지금 ìž¬ìƒ ì¤‘"
259 radio: "FM ë¼ë””오"
260 </dest> 361 </dest>
261 <voice> 362 <voice>
262 *: none 363 *: "지금 ìž¬ìƒ ì¤‘"
263 radio: "FM ë¼ë””오"
264 </voice> 364 </voice>
265</phrase> 365</phrase>
266<phrase> 366<phrase>
267 id: LANG_RECORDING 367 id: LANG_RESUME_PLAYBACK
268 desc: in the main menu 368 desc: in the main menu
269 user: core 369 user: core
270 <source> 370 <source>
271 *: none 371 *: "Resume Playback"
272 recording: "Recording"
273 </source> 372 </source>
274 <dest> 373 <dest>
275 *: none 374 *: "ìž¬ìƒ ìž¬ì‹œìž‘"
276 recording: "ë…¹ìŒ"
277 </dest> 375 </dest>
278 <voice> 376 <voice>
279 *: none 377 *: "ìž¬ìƒ ìž¬ì‹œìž‘"
280 recording: "ë…¹ìŒ"
281 </voice> 378 </voice>
282</phrase> 379</phrase>
283<phrase> 380<phrase>
284 id: LANG_PLUGINS 381 id: LANG_SETTINGS
285 desc: in the main menu 382 desc: in main menu and visual confirmation after settings reset
286 user: core 383 user: core
287 <source> 384 <source>
288 *: "Plugins" 385 *: "Settings"
289 </source> 386 </source>
290 <dest> 387 <dest>
291 *: "í”ŒëŸ¬ê·¸ì¸ ë³´ê¸°" 388 *: "설정"
292 </dest> 389 </dest>
293 <voice> 390 <voice>
294 *: "í”ŒëŸ¬ê·¸ì¸ ë³´ê¸°" 391 *: "설정"
295 </voice> 392 </voice>
296</phrase> 393</phrase>
297<phrase> 394<phrase>
298 id: LANG_SHUTDOWN 395 id: LANG_RECORDING
299 desc: in main menu 396 desc: in the main menu
300 user: core 397 user: core
301 <source> 398 <source>
302 *: none 399 *: none
303 soft_shutdown: "Shut down" 400 recording: "Recording"
304 </source> 401 </source>
305 <dest> 402 <dest>
306 *: none 403 *: none
307 soft_shutdown: "ë„기" 404 recording: "ë…¹ìŒ"
308 </dest> 405 </dest>
309 <voice> 406 <voice>
310 *: none 407 *: none
311 soft_shutdown: "ë„기" 408 recording: "ë…¹ìŒ"
312 </voice> 409 </voice>
313</phrase> 410</phrase>
314<phrase> 411<phrase>
315 id: LANG_VOLUME 412 id: LANG_FM_RADIO
316 desc: in sound_settings 413 desc: in the main menu
317 user: core 414 user: core
318 <source> 415 <source>
319 *: "Volume" 416 *: none
417 radio: "FM Radio"
320 </source> 418 </source>
321 <dest> 419 <dest>
322 *: "볼륨 조절" 420 *: none
421 radio: "FM ë¼ë””오"
323 </dest> 422 </dest>
324 <voice> 423 <voice>
325 *: "볼륨 조절" 424 *: none
425 radio: "FM ë¼ë””오"
326 </voice> 426 </voice>
327</phrase> 427</phrase>
328<phrase> 428<phrase>
329 id: LANG_BASS 429 id: LANG_PLAYLISTS
330 desc: in sound_settings 430 desc: in the file view setting
331 user: core 431 user: core
332 <source> 432 <source>
333 *: "Bass" 433 *: "Playlists"
334 </source> 434 </source>
335 <dest> 435 <dest>
336 *: "ì €ìŒ ì„¤ì •" 436 *: "재ìƒëª©ë¡"
337 </dest> 437 </dest>
338 <voice> 438 <voice>
339 *: "ì €ìŒ ì„¤ì •" 439 *: "재ìƒëª©ë¡"
340 </voice> 440 </voice>
341</phrase> 441</phrase>
342<phrase> 442<phrase>
343 id: LANG_TREBLE 443 id: LANG_PLUGINS
344 desc: in sound_settings 444 desc: in the main menu
345 user: core 445 user: core
346 <source> 446 <source>
347 *: "Treble" 447 *: "Plugins"
348 </source> 448 </source>
349 <dest> 449 <dest>
350 *: "ê³ ìŒ ì„¤ì •" 450 *: "플러그ì¸"
351 </dest> 451 </dest>
352 <voice> 452 <voice>
353 *: "ê³ ìŒ ì„¤ì •" 453 *: "플러그ì¸"
354 </voice> 454 </voice>
355</phrase> 455</phrase>
356<phrase> 456<phrase>
357 id: LANG_BALANCE 457 id: LANG_SYSTEM
358 desc: in sound_settings 458 desc: in the main menu and settings menu
359 user: core 459 user: core
360 <source> 460 <source>
361 *: "Balance" 461 *: "System"
362 </source> 462 </source>
363 <dest> 463 <dest>
364 *: "좌우 밸런스" 464 *: "시스템"
365 </dest> 465 </dest>
366 <voice> 466 <voice>
367 *: "좌우 밸런스" 467 *: "시스템"
368 </voice> 468 </voice>
369</phrase> 469</phrase>
370<phrase> 470<phrase>
371 id: LANG_CHANNEL_STEREO 471 id: LANG_BOOKMARK_SELECT_BOOKMARK
372 desc: in sound_settings 472 desc: bookmark selection list title
373 user: core 473 user: core
374 <source> 474 <source>
375 *: "Stereo" 475 *: "Select Bookmark"
376 </source> 476 </source>
377 <dest> 477 <dest>
378 *: "스테레오" 478 *: "ë¶ë§ˆí¬ ì„ íƒ"
379 </dest> 479 </dest>
380 <voice> 480 <voice>
381 *: "스테레오" 481 *: "ë¶ë§ˆí¬ ì„ íƒ"
382 </voice> 482 </voice>
383</phrase> 483</phrase>
384<phrase> 484<phrase>
385 id: LANG_CHANNEL_MONO 485 id: LANG_BOOKMARK_DONT_RESUME
386 desc: in sound_settings 486 desc: top item in the list when asking user about bookmark auto load
387 user: core 487 user: core
388 <source> 488 <source>
389 *: "Mono" 489 *: "<Don't Resume>"
390 </source> 490 </source>
391 <dest> 491 <dest>
392 *: "모노" 492 *: "<재시작하지 ì•ŠìŒ>"
393 </dest> 493 </dest>
394 <voice> 494 <voice>
395 *: "모노" 495 *: "재시작하지 ì•ŠìŒ"
396 </voice> 496 </voice>
397</phrase> 497</phrase>
398<phrase> 498<phrase>
399 id: LANG_CHANNEL_CUSTOM 499 id: LANG_BOOKMARK_SHUFFLE
400 desc: in sound_settings 500 desc: bookmark selection list, bookmark enables shuffle
401 user: core 501 user: core
402 <source> 502 <source>
403 *: "Custom" 503 *: ", Shuffle"
404 </source> 504 </source>
405 <dest> 505 <dest>
406 *: "ì¼ë°˜" 506 *: ", 셔플"
407 </dest> 507 </dest>
408 <voice> 508 <voice>
409 *: "ì¼ë°˜" 509 *: ""
410 </voice> 510 </voice>
411</phrase> 511</phrase>
412<phrase> 512<phrase>
413 id: LANG_CHANNEL_LEFT 513 id: LANG_BOOKMARK_INVALID
414 desc: in sound_settings 514 desc: bookmark selection list, bookmark couldn't be parsed
415 user: core 515 user: core
416 <source> 516 <source>
417 *: "Mono Left" 517 *: "<Invalid Bookmark>"
418 </source> 518 </source>
419 <dest> 519 <dest>
420 *: "왼쪽 모노" 520 *: "<ìž˜ëª»ëœ ë마í¬>"
421 </dest> 521 </dest>
422 <voice> 522 <voice>
423 *: "왼쪽 모노" 523 *: "ìž˜ëª»ëœ ë마í¬"
424 </voice> 524 </voice>
425</phrase> 525</phrase>
426<phrase> 526<phrase>
427 id: LANG_CHANNEL_RIGHT 527 id: LANG_BOOKMARK_CONTEXT_MENU
428 desc: in sound_settings 528 desc: bookmark selection list context menu
429 user: core 529 user: core
430 <source> 530 <source>
431 *: "Mono Right" 531 *: "Bookmark Actions"
432 </source> 532 </source>
433 <dest> 533 <dest>
434 *: "오른쪽 모ë¸" 534 *: "ëë§ˆí¬ ìž‘ì—…"
435 </dest> 535 </dest>
436 <voice> 536 <voice>
437 *: "오른쪽 모ë¸" 537 *: "ëë§ˆí¬ ìž‘ì—…"
438 </voice> 538 </voice>
439</phrase> 539</phrase>
440<phrase> 540<phrase>
441 id: LANG_CHANNEL_KARAOKE 541 id: LANG_BOOKMARK_CONTEXT_RESUME
442 desc: in sound_settings 542 desc: bookmark context menu, resume this bookmark
443 user: core 543 user: core
444 <source> 544 <source>
445 *: "Karaoke" 545 *: "Resume"
446 </source> 546 </source>
447 <dest> 547 <dest>
448 *: "ìŒì„± 제거" 548 *: "재ìœìž‘"
449 </dest> 549 </dest>
450 <voice> 550 <voice>
451 *: "ìŒì„± 제거" 551 *: "재ìœìž‘"
452 </voice> 552 </voice>
453</phrase> 553</phrase>
454<phrase> 554<phrase>
455 id: LANG_STEREO_WIDTH 555 id: LANG_AUTO_BOOKMARK_QUERY
456 desc: in sound_settings 556 desc: prompt for user to decide to create an bookmark
457 user: core 557 user: core
458 <source> 558 <source>
459 *: "Stereo Width" 559 *: "Create a Bookmark?"
460 </source> 560 </source>
461 <dest> 561 <dest>
462 *: "스íŒë ˆì˜¤ ëˆë¹" 562 *: "ë¶ë§ˆí¬ë¥¼ 만들겠습ëˆê¹Œ?"
463 </dest> 563 </dest>
464 <voice> 564 <voice>
465 *: "스íŒë ˆì˜¤ ëˆë¹" 565 *: "ë¶ë§ˆí¬ë¥¼ 만들겠습ëˆê¹Œ?"
466 </voice> 566 </voice>
467</phrase> 567</phrase>
468<phrase> 568<phrase>
469 id: LANG_LOUDNESS 569 id: LANG_BOOKMARK_CREATE_SUCCESS
470 desc: in sound_settings 570 desc: Indicates bookmark was successfully created
471 user: core 571 user: core
472 <source> 572 <source>
473 *: none 573 *: "Bookmark Created"
474 masf: "Loudness"
475 </source> 574 </source>
476 <dest> 575 <dest>
477 *: none 576 *: "ë¶ë§ˆí¬ ìƒì„±ë¨"
478 masf: "소리í¬ê¸°"
479 </dest> 577 </dest>
480 <voice> 578 <voice>
481 *: none 579 *: "ë¶ë§ˆí¬ ìƒì„±ë¨"
482 masf: "소리í¬ê¸°"
483 </voice> 580 </voice>
484</phrase> 581</phrase>
485<phrase> 582<phrase>
486 id: LANG_AUTOVOL 583 id: LANG_BOOKMARK_CREATE_FAILURE
487 desc: in sound_settings 584 desc: Indicates bookmark was not created
488 user: core 585 user: core
489 <source> 586 <source>
490 *: none 587 *: "Bookmark Failed!"
491 masf: "Auto Volume"
492 </source> 588 </source>
493 <dest> 589 <dest>
494 *: none 590 *: "ë¶ë§ˆí¬ 실패함!"
495 masf: "ìžë™ 볼륨설정"
496 </dest> 591 </dest>
497 <voice> 592 <voice>
498 *: none 593 *: "ë¶ë§ˆí¬ 실패함!"
499 masf: "ìžë™ 볼륨설정"
500 </voice> 594 </voice>
501</phrase> 595</phrase>
502<phrase> 596<phrase>
503 id: LANG_DECAY 597 id: LANG_BOOKMARK_LOAD_EMPTY
504 desc: in sound_settings 598 desc: Indicates bookmark was empty
505 user: core 599 user: core
506 <source> 600 <source>
507 *: none 601 *: "Bookmark Empty"
508 masf: "AV Decay Time"
509 </source> 602 </source>
510 <dest> 603 <dest>
511 *: none 604 *: "ë¶ë§ˆí¬ 비어 있ìŒ"
512 masf: "AV ê°ì† 시간"
513 </dest> 605 </dest>
514 <voice> 606 <voice>
515 *: none 607 *: "ë¶ë§ˆí¬ 비어 있ìŒ"
516 masf: ""
517 </voice> 608 </voice>
518</phrase> 609</phrase>
519<phrase> 610<phrase>
520 id: LANG_SUPERBASS 611 id: LANG_SOUND_SETTINGS
521 desc: in sound settings 612 desc: in the main menu
522 user: core 613 user: core
523 <source> 614 <source>
524 *: none 615 *: "Sound Settings"
525 masf: "Super Bass"
526 </source> 616 </source>
527 <dest> 617 <dest>
528 *: none 618 *: "사운드 설정"
529 masf: "ì €ìŒì—­ ê°•í™”"
530 </dest> 619 </dest>
531 <voice> 620 <voice>
532 *: none 621 *: "사운드 설정"
533 masf: "ì €ìŒì—­ ê°•í™”"
534 </voice> 622 </voice>
535</phrase> 623</phrase>
536<phrase> 624<phrase>
537 id: LANG_MDB_ENABLE 625 id: LANG_VOLUME
538 desc: in sound settings 626 desc: in sound_settings
539 user: core 627 user: core
540 <source> 628 <source>
541 *: none 629 *: "Volume"
542 masf: "MDB Enable"
543 </source> 630 </source>
544 <dest> 631 <dest>
545 *: none 632 *: "볼륨"
546 masf: "MDB 켬"
547 </dest> 633 </dest>
548 <voice> 634 <voice>
549 *: none 635 *: "볼륨"
550 masf: "MDB 켬"
551 </voice> 636 </voice>
552</phrase> 637</phrase>
553<phrase> 638<phrase>
554 id: LANG_MDB_STRENGTH 639 id: LANG_BASS
555 desc: in sound settings 640 desc: in sound_settings
556 user: core 641 user: core
557 <source> 642 <source>
558 *: none 643 *: "Bass"
559 masf: "MDB Strength"
560 </source> 644 </source>
561 <dest> 645 <dest>
562 *: none 646 *: "ì €ìŒ"
563 masf: "MDB 세기"
564 </dest> 647 </dest>
565 <voice> 648 <voice>
566 *: none 649 *: "ì €ìŒ"
567 masf: "MDB 세기"
568 </voice> 650 </voice>
569</phrase> 651</phrase>
570<phrase> 652<phrase>
571 id: LANG_MDB_HARMONICS 653 id: LANG_TREBLE
572 desc: in sound settings 654 desc: in sound_settings
573 user: core 655 user: core
574 <source> 656 <source>
575 *: none 657 *: "Treble"
576 masf: "MDB Harmonics"
577 </source> 658 </source>
578 <dest> 659 <dest>
579 *: none 660 *: "ê³ ìŒ"
580 masf: "MDB 하모닉스"
581 </dest> 661 </dest>
582 <voice> 662 <voice>
583 *: none 663 *: "ê³ ìŒ"
584 masf: "MDB 하모닉스"
585 </voice> 664 </voice>
586</phrase> 665</phrase>
587<phrase> 666<phrase>
588 id: LANG_MDB_CENTER 667 id: LANG_BALANCE
589 desc: in sound settings 668 desc: in sound_settings
590 user: core 669 user: core
591 <source> 670 <source>
592 *: none 671 *: "Balance"
593 masf: "MDB Centre Frequency"
594 </source> 672 </source>
595 <dest> 673 <dest>
596 *: none 674 *: "균형"
597 masf: "MDB 센터 주파수"
598 </dest> 675 </dest>
599 <voice> 676 <voice>
600 *: none 677 *: "균형"
601 masf: "MDB 센터 주파수"
602 </voice> 678 </voice>
603</phrase> 679</phrase>
604<phrase> 680<phrase>
605 id: LANG_MDB_SHAPE 681 id: LANG_CHANNEL_CONFIGURATION
606 desc: in sound settings 682 desc: in sound_settings
607 user: core 683 user: core
608 <source> 684 <source>
609 *: none 685 *: "Channel Configuration"
610 masf: "MDB Shape"
611 </source> 686 </source>
612 <dest> 687 <dest>
613 *: none 688 *: "ì±„ë„ êµ¬ì„±"
614 masf: "MDB 모양"
615 </dest> 689 </dest>
616 <voice> 690 <voice>
617 *: none 691 *: "ì±„ë„ êµ¬ì„±"
618 masf: "MDB 모양"
619 </voice> 692 </voice>
620</phrase> 693</phrase>
621<phrase> 694<phrase>
622 id: LANG_CROSSFEED 695 id: LANG_CHANNEL_STEREO
623 desc: in sound settings 696 desc: in sound_settings and radio screen
624 user: core 697 user: core
625 <source> 698 <source>
626 *: "Crossfeed" 699 *: "Stereo"
627 </source> 700 </source>
628 <dest> 701 <dest>
629 *: "í¬ë¡œìŠ¤í”¼ë“œ" 702 *: "스테레오"
630 </dest> 703 </dest>
631 <voice> 704 <voice>
632 *: "í¬ë¡œìŠ¤í”¼ë“œ" 705 *: "스테레오"
633 </voice> 706 </voice>
634</phrase> 707</phrase>
635<phrase> 708<phrase>
636 id: LANG_EQUALIZER 709 id: LANG_CHANNEL_MONO
637 desc: in the sound settings menu 710 desc: in sound_settings and radio screen
638 user: core 711 user: core
639 <source> 712 <source>
640 *: "Equalizer" 713 *: "Mono"
641 </source> 714 </source>
642 <dest> 715 <dest>
643 *: "ì´í€„ë¼ì´ì ¸" 716 *: "모노"
644 </dest> 717 </dest>
645 <voice> 718 <voice>
646 *: "ì´í€„ë¼ì´ì ¸" 719 *: "모노"
647 </voice> 720 </voice>
648</phrase> 721</phrase>
649<phrase> 722<phrase>
650 id: LANG_PLAYBACK 723 id: LANG_CHANNEL_CUSTOM
651 desc: in settings_menu() 724 desc: in sound_settings
652 user: core 725 user: core
653 <source> 726 <source>
654 *: "Playback Settings" 727 *: "Custom"
655 </source> 728 </source>
656 <dest> 729 <dest>
657 *: "ìž¬ìƒ ì„¤ì •" 730 *: "커스텀"
658 </dest> 731 </dest>
659 <voice> 732 <voice>
660 *: "ìž¬ìƒ ì„¤ì •" 733 *: "커스텀"
661 </voice> 734 </voice>
662</phrase> 735</phrase>
663<phrase> 736<phrase>
664 id: LANG_FILE 737 id: LANG_CHANNEL_LEFT
665 desc: in settings_menu() 738 desc: in sound_settings
666 user: core 739 user: core
667 <source> 740 <source>
668 *: "File View" 741 *: "Mono Left"
669 </source> 742 </source>
670 <dest> 743 <dest>
671 *: "보기 설정" 744 *: "모노 왼쪽"
672 </dest> 745 </dest>
673 <voice> 746 <voice>
674 *: "보기 설정" 747 *: "모노 왼쪽"
675 </voice> 748 </voice>
676</phrase> 749</phrase>
677<phrase> 750<phrase>
678 id: LANG_DISPLAY 751 id: LANG_CHANNEL_RIGHT
679 desc: in settings_menu() 752 desc: in sound_settings
680 user: core 753 user: core
681 <source> 754 <source>
682 *: "Display" 755 *: "Mono Right"
683 </source> 756 </source>
684 <dest> 757 <dest>
685 *: "화면 ì¤ì •" 758 *: "모노 오른쪽"
686 </dest> 759 </dest>
687 <voice> 760 <voice>
688 *: "화면 ì¤ì •" 761 *: "모노 오른쪽"
689 </voice> 762 </voice>
690</phrase> 763</phrase>
691<phrase> 764<phrase>
692 id: LANG_SYSTEM 765 id: LANG_CHANNEL_LEFTRIGHT
693 desc: in the main menu and settings menu 766 desc: in sound_settings
694 user: core 767 user: core
695 <source> 768 <source>
696 *: "System" 769 *: none
770 recording: "Mono Left + Right"
697 </source> 771 </source>
698 <dest> 772 <dest>
699 *: "시스템 설정" 773 *: none
774 recording: "모노 왼쪽 + 오른쪽"
700 </dest> 775 </dest>
701 <voice> 776 <voice>
702 *: "시스템 설정" 777 *: none
778 recording: "모노 왼쪽과 오른쪽"
703 </voice> 779 </voice>
704</phrase> 780</phrase>
705<phrase> 781<phrase>
706 id: LANG_BOOKMARK_SETTINGS 782 id: LANG_CHANNEL_KARAOKE
707 desc: in general settings 783 desc: in sound_settings
708 user: core 784 user: core
709 <source> 785 <source>
710 *: "Bookmarking" 786 *: "Karaoke"
711 </source> 787 </source>
712 <dest> 788 <dest>
713 *: "ë¶ë§ˆí¬ ì¤ì •" 789 *: "ê°€ë¼ì˜¤ì¼€"
714 </dest> 790 </dest>
715 <voice> 791 <voice>
716 *: "ë¶ë§ˆí¬ ì¤ì •" 792 *: "ê°€ë¼ì˜¤ì¼€"
717 </voice> 793 </voice>
718</phrase> 794</phrase>
719<phrase> 795<phrase>
720 id: LANG_LANGUAGE 796 id: LANG_STEREO_WIDTH
721 desc: in settings_menu 797 desc: in sound_settings
722 user: core 798 user: core
723 <source> 799 <source>
724 *: "Language" 800 *: "Stereo Width"
725 </source> 801 </source>
726 <dest> 802 <dest>
727 *: "언어 ì„ íƒ" 803 *: "스테레오 í­"
728 </dest> 804 </dest>
729 <voice> 805 <voice>
730 *: "언어 ì„ íƒ" 806 *: "스테레오 í­"
731 </voice> 807 </voice>
732</phrase> 808</phrase>
733<phrase> 809<phrase>
734 id: LANG_VOICE 810 id: LANG_CROSSFEED
735 desc: root of voice menu 811 desc: in sound settings
736 user: core 812 user: core
737 <source> 813 <source>
738 *: "Voice" 814 *: "Crossfeed"
739 </source> 815 </source>
740 <dest> 816 <dest>
741 *: "ìŒì„± 안내" 817 *: "í¬ë¡œìŠ¤í”¼ë“œ"
742 </dest> 818 </dest>
743 <voice> 819 <voice>
744 *: "ìŒì„± 안내" 820 *: "í¬ë¡œìŠ¤í”¼ë“œ"
745 </voice> 821 </voice>
746</phrase> 822</phrase>
747<phrase> 823<phrase>
748 id: LANG_CUSTOM_CFG 824 id: LANG_CROSSFEED_DIRECT_GAIN
749 desc: in setting_menu() 825 desc: in crossfeed settings
750 user: core 826 user: core
751 <source> 827 <source>
752 *: "Browse .cfg Files" 828 *: "Direct Gain"
753 </source> 829 </source>
754 <dest> 830 <dest>
755 *: "설ì íŒŒì¼ 보기" 831 *: "다ì´ë íŠ¸ 게ì¸"
756 </dest> 832 </dest>
757 <voice> 833 <voice>
758 *: "설ì íŒŒì¼ 보기" 834 *: "다ì´ë íŠ¸ 게ì¸"
759 </voice> 835 </voice>
760</phrase> 836</phrase>
761<phrase> 837<phrase>
762 id: LANG_RESET 838 id: LANG_CROSSFEED_CROSS_GAIN
763 desc: in system_settings_menu() 839 desc: in crossfeed settings
764 user: core 840 user: core
765 <source> 841 <source>
766 *: "Reset Settings" 842 *: "Cross Gain"
767 </source> 843 </source>
768 <dest> 844 <dest>
769 *: "ì¤ì • 초기화" 845 *: "í¬ë¡œìŠ¤ 게ì¸"
770 </dest> 846 </dest>
771 <voice> 847 <voice>
772 *: "ì¤ì • 초기화" 848 *: "í¬ë¡œìŠ¤ 게ì¸"
773 </voice> 849 </voice>
774</phrase> 850</phrase>
775<phrase> 851<phrase>
776 id: LANG_RESET_DONE_CLEAR 852 id: LANG_CROSSFEED_HF_ATTENUATION
777 desc: visual confirmation after settings reset 853 desc: in crossfeed settings
778 user: core 854 user: core
779 <source> 855 <source>
780 *: "Cleared" 856 *: "High-Frequency Attenuation"
781 </source> 857 </source>
782 <dest> 858 <dest>
783 *: "초기화 ë˜ì—ˆìŠµë‹ˆë‹¤." 859 *: "고주파 ê°ì‡ "
784 </dest> 860 </dest>
785 <voice> 861 <voice>
786 *: "" 862 *: "고주파 ê°ì‡ "
787 </voice> 863 </voice>
788</phrase> 864</phrase>
789<phrase> 865<phrase>
790 id: LANG_SAVE_SETTINGS 866 id: LANG_CROSSFEED_HF_CUTOFF
791 desc: in system_settings_menu() 867 desc: in crossfeed settings
792 user: core 868 user: core
793 <source> 869 <source>
794 *: "Save .cfg File" 870 *: "High-Frequency Cutoff"
795 </source> 871 </source>
796 <dest> 872 <dest>
797 *: "설ì íŒŒì¼ 만들기" 873 *: "고주파 차단"
798 </dest> 874 </dest>
799 <voice> 875 <voice>
800 *: "설ì íŒŒì¼ 만들기" 876 *: "고주파 차단"
801 </voice> 877 </voice>
802</phrase> 878</phrase>
803<phrase> 879<phrase>
804 id: LANG_RECORDING_SETTINGS 880 id: LANG_EQUALIZER
805 desc: in the main menu 881 desc: in the sound settings menu
806 user: core 882 user: core
807 <source> 883 <source>
808 *: none 884 *: "Equalizer"
809 recording: "Recording Settings"
810 </source> 885 </source>
811 <dest> 886 <dest>
812 *: none 887 *: "ì´í€„ë¼ì´ì €"
813 recording: "ë…¹ìŒ ì„¤ì •"
814 </dest> 888 </dest>
815 <voice> 889 <voice>
816 *: none 890 *: "ì´í€„ë¼ì´ì €"
817 recording: "ë…¹ìŒ ì„¤ì •"
818 </voice> 891 </voice>
819</phrase> 892</phrase>
820<phrase> 893<phrase>
@@ -825,10 +898,10 @@
825 *: "Enable EQ" 898 *: "Enable EQ"
826 </source> 899 </source>
827 <dest> 900 <dest>
828 *: "ì´í€„ë¼ì´ì ¸ 켜기" 901 *: "EQ 활성화"
829 </dest> 902 </dest>
830 <voice> 903 <voice>
831 *: "ì´í€„ë¼ì´ì ¸ 켜기" 904 *: "ì´í€„ë¼ì´ì  활성화"
832 </voice> 905 </voice>
833</phrase> 906</phrase>
834<phrase> 907<phrase>
@@ -842,7 +915,7 @@
842 *: "그래픽 EQ" 915 *: "그래픽 EQ"
843 </dest> 916 </dest>
844 <voice> 917 <voice>
845 *: "그래픽 EQ" 918 *: "그래픽 ì´í€„ë¼ì´ì €"
846 </voice> 919 </voice>
847</phrase> 920</phrase>
848<phrase> 921<phrase>
@@ -853,10 +926,10 @@
853 *: "Precut" 926 *: "Precut"
854 </source> 927 </source>
855 <dest> 928 <dest>
856 *: "미리 줄임" 929 *: "프리컷"
857 </dest> 930 </dest>
858 <voice> 931 <voice>
859 *: "미리 줄임" 932 *: "프리컷"
860 </voice> 933 </voice>
861</phrase> 934</phrase>
862<phrase> 935<phrase>
@@ -867,10 +940,10 @@
867 *: "Simple EQ Settings" 940 *: "Simple EQ Settings"
868 </source> 941 </source>
869 <dest> 942 <dest>
870 *: "간단한 EQ설정" 943 *: "간단한 EQ 설정"
871 </dest> 944 </dest>
872 <voice> 945 <voice>
873 *: "간단한 EQ설정" 946 *: "간단한 ì´í€ë¼ì´ì € 설정"
874 </voice> 947 </voice>
875</phrase> 948</phrase>
876<phrase> 949<phrase>
@@ -881,10 +954,10 @@
881 *: "Advanced EQ Settings" 954 *: "Advanced EQ Settings"
882 </source> 955 </source>
883 <dest> 956 <dest>
884 *: "고급 EQ설정" 957 *: "고급 EQ 설정"
885 </dest> 958 </dest>
886 <voice> 959 <voice>
887 *: "고급 EQ설정" 960 *: "고급 ì´í€ë¼ì´ì € 설정"
888 </voice> 961 </voice>
889</phrase> 962</phrase>
890<phrase> 963<phrase>
@@ -895,10 +968,10 @@
895 *: "Save EQ Preset" 968 *: "Save EQ Preset"
896 </source> 969 </source>
897 <dest> 970 <dest>
898 *: "EQ 프리셋 저장" 971 *: "EQ 사전설정 저장"
899 </dest> 972 </dest>
900 <voice> 973 <voice>
901 *: "EQ í„리셋 저장" 974 *: "ì´í„ë¼ì´ì € 사전설정 저장"
902 </voice> 975 </voice>
903</phrase> 976</phrase>
904<phrase> 977<phrase>
@@ -909,24 +982,10 @@
909 *: "Browse EQ Presets" 982 *: "Browse EQ Presets"
910 </source> 983 </source>
911 <dest> 984 <dest>
912 *: "EQ 프리셋 íƒìƒ‰" 985 *: "EQ 사전설정 찾아보기"
913 </dest>
914 <voice>
915 *: "EQ 프리셋 íƒìƒ‰"
916 </voice>
917</phrase>
918<phrase>
919 id: LANG_EQUALIZER_EDIT_MODE
920 desc: in the equalizer settings menu
921 user: core
922 <source>
923 *: "Edit mode: %s"
924 </source>
925 <dest>
926 *: "편집 모드: %s"
927 </dest> 986 </dest>
928 <voice> 987 <voice>
929 *: "" 988 *: "ì´í€„ë¼ì´ì € 사전설정 찾아보기"
930 </voice> 989 </voice>
931</phrase> 990</phrase>
932<phrase> 991<phrase>
@@ -937,10 +996,10 @@
937 *: "%d Hz Band Gain" 996 *: "%d Hz Band Gain"
938 </source> 997 </source>
939 <dest> 998 <dest>
940 *: "%d Hz ë°´ë“œ 게ì¸" 999 *: "%d Hz 대역 게ì¸"
941 </dest> 1000 </dest>
942 <voice> 1001 <voice>
943 *: "Hertz ë°´ë“œ 게ì¸" 1002 *: "헤르츠 대역 게ì¸"
944 </voice> 1003 </voice>
945</phrase> 1004</phrase>
946<phrase> 1005<phrase>
@@ -951,10 +1010,10 @@
951 *: "Low Shelf Filter" 1010 *: "Low Shelf Filter"
952 </source> 1011 </source>
953 <dest> 1012 <dest>
954 *: "로우 셀프 필터" 1013 *: "Low Shelf 필터"
955 </dest> 1014 </dest>
956 <voice> 1015 <voice>
957 *: "로우 셀프 필터" 1016 *: "로우 쉘프 필터"
958 </voice> 1017 </voice>
959</phrase> 1018</phrase>
960<phrase> 1019<phrase>
@@ -979,10 +1038,10 @@
979 *: "High Shelf Filter" 1038 *: "High Shelf Filter"
980 </source> 1039 </source>
981 <dest> 1040 <dest>
982 *: "í•˜ì´ ì…€í”„ í•„í„°" 1041 *: "í•˜ì´ ì‰˜í”„ í•„í„°"
983 </dest> 1042 </dest>
984 <voice> 1043 <voice>
985 *: "í•˜ì´ ì…€í”„ í•„í„°" 1044 *: "í•˜ì´ ì‰˜í”„ í•„í„°"
986 </voice> 1045 </voice>
987</phrase> 1046</phrase>
988<phrase> 1047<phrase>
@@ -1007,10 +1066,10 @@
1007 *: "Centre Frequency" 1066 *: "Centre Frequency"
1008 </source> 1067 </source>
1009 <dest> 1068 <dest>
1010 *: "센터 주파수" 1069 *: "중심 주파수"
1011 </dest> 1070 </dest>
1012 <voice> 1071 <voice>
1013 *: "센터 주파수" 1072 *: "중심 주파수"
1014 </voice> 1073 </voice>
1015</phrase> 1074</phrase>
1016<phrase> 1075<phrase>
@@ -1021,192 +1080,178 @@
1021 *: "Q" 1080 *: "Q"
1022 </source> 1081 </source>
1023 <dest> 1082 <dest>
1024 *: "Qê°’" 1083 *: "Q"
1025 </dest> 1084 </dest>
1026 <voice> 1085 <voice>
1027 *: "Qê°’" 1086 *: "Q"
1028 </voice> 1087 </voice>
1029</phrase> 1088</phrase>
1030<phrase> 1089<phrase>
1031 id: LANG_CREATE_PLAYLIST 1090 id: LANG_DITHERING
1032 desc: Menu option for creating a playlist 1091 desc: in the sound settings and some other menus
1033 user: core 1092 user: core
1034 <source> 1093 <source>
1035 *: "Create Playlist" 1094 *: "Dithering"
1036 </source> 1095 </source>
1037 <dest> 1096 <dest>
1038 *: "재ìƒëª©ë¡ 만들기" 1097 *: "ë””ëë§"
1039 </dest> 1098 </dest>
1040 <voice> 1099 <voice>
1041 *: "재ìƒëª©ë¡ 만들기" 1100 *: "ë””ëë§"
1042 </voice> 1101 </voice>
1043</phrase> 1102</phrase>
1044<phrase> 1103<phrase>
1045 id: LANG_VIEW_DYNAMIC_PLAYLIST 1104 id: LANG_GENERAL_SETTINGS
1046 desc: in playlist menu. 1105 desc: in the main menu
1047 user: core 1106 user: core
1048 <source> 1107 <source>
1049 *: "View Current Playlist" 1108 *: "General Settings"
1050 </source> 1109 </source>
1051 <dest> 1110 <dest>
1052 *: "현재 재ìƒëª©ë¡ 보기" 1111 *: "ì¼ë°˜ 설정"
1053 </dest> 1112 </dest>
1054 <voice> 1113 <voice>
1055 *: "현재 재ìƒëª©ë¡ 보기" 1114 *: "ì¼ë°˜ 설정"
1056 </voice> 1115 </voice>
1057</phrase> 1116</phrase>
1058<phrase> 1117<phrase>
1059 id: LANG_SAVE_DYNAMIC_PLAYLIST 1118 id: LANG_PLAYBACK
1060 desc: in playlist menu. 1119 desc: in settings_menu()
1061 user: core 1120 user: core
1062 <source> 1121 <source>
1063 *: "Save Current Playlist" 1122 *: "Playback Settings"
1064 </source> 1123 </source>
1065 <dest> 1124 <dest>
1066 *: "현재 재ìƒëª©ë¡ 저장" 1125 *: "ìž¬ìƒ ì„¤ì •"
1067 </dest> 1126 </dest>
1068 <voice> 1127 <voice>
1069 *: "현재 재ìƒëª©ë¡ 저장" 1128 *: "ìž¬ìƒ ì„¤ì •"
1070 </voice> 1129 </voice>
1071</phrase> 1130</phrase>
1072<phrase> 1131<phrase>
1073 id: LANG_RECURSE_DIRECTORY 1132 id: LANG_SHUFFLE
1074 desc: In playlist menu 1133 desc: in settings_menu
1075 user: core 1134 user: core
1076 <source> 1135 <source>
1077 *: "Recursively Insert Directories" 1136 *: "Shuffle"
1078 </source> 1137 </source>
1079 <dest> 1138 <dest>
1080 *: "ìŒì• ì¶ê°€ì‹œ 하위 í´ëë„ ì¶”ê°€" 1139 *: "셔플"
1081 </dest> 1140 </dest>
1082 <voice> 1141 <voice>
1083 *: "ìŒì• ì¶ê°€ì‹œ 하위 í´ëë„ ì¶”ê°€" 1142 *: "셔플"
1084 </voice> 1143 </voice>
1085</phrase> 1144</phrase>
1086<phrase> 1145<phrase>
1087 id: LANG_WARN_ERASEDYNPLAYLIST_MENU 1146 id: LANG_REPEAT
1088 desc: in playlist options menu, option to warn when erasing dynamic playlist 1147 desc: in settings_menu
1089 user: core 1148 user: core
1090 <source> 1149 <source>
1091 *: "Warn When Erasing Dynamic Playlist" 1150 *: "Repeat"
1092 </source> 1151 </source>
1093 <dest> 1152 <dest>
1094 *: "현재 재ìƒëª©ë¡ 삭제시 알림" 1153 *: "반복"
1095 </dest> 1154 </dest>
1096 <voice> 1155 <voice>
1097 *: "현재 재ìƒëª©ë¡ 삭제시 알림" 1156 *: "반복"
1098 </voice> 1157 </voice>
1099</phrase> 1158</phrase>
1100<phrase> 1159<phrase>
1101 id: LANG_VERSION 1160 id: LANG_ALL
1102 desc: in the info menu 1161 desc: generic string used both in dir file filter and repeat mode selection
1103 user: core 1162 user: core
1104 <source> 1163 <source>
1105 *: "Version" 1164 *: "All"
1106 </source> 1165 </source>
1107 <dest> 1166 <dest>
1108 *: "Rockbox ì •ë³´" 1167 *: "모ë‘"
1109 </dest> 1168 </dest>
1110 <voice> 1169 <voice>
1111 *: "Rockbox ì •ë³´" 1170 *: "모ë‘"
1112 </voice> 1171 </voice>
1113</phrase> 1172</phrase>
1114<phrase> 1173<phrase>
1115 id: LANG_DEBUG 1174 id: LANG_REPEAT_ONE
1116 desc: in the info menu 1175 desc: repeat one song
1117 user: core 1176 user: core
1118 <source> 1177 <source>
1119 *: "Debug (Keep Out!)" 1178 *: "One"
1120 </source> 1179 </source>
1121 <dest> 1180 <dest>
1122 *: "디버깅 모드 (접근금지)" 1181 *: "한 곡"
1123 </dest> 1182 </dest>
1124 <voice> 1183 <voice>
1125 *: "디버깅 모드 (접근금지)" 1184 *: "한 곡"
1126 </voice> 1185 </voice>
1127</phrase> 1186</phrase>
1128<phrase> 1187<phrase>
1129 id: LANG_SHUFFLE 1188 id: LANG_REPEAT_AB
1130 desc: in settings_menu 1189 desc: repeat range from point A to B
1131 user: core 1190 user: core
1132 <source> 1191 <source>
1133 *: "Shuffle" 1192 *: "A-B"
1134 </source> 1193 </source>
1135 <dest> 1194 <dest>
1136 *: "ìž„ì˜ ìž¬ìƒ" 1195 *: "A-B"
1137 </dest> 1196 </dest>
1138 <voice> 1197 <voice>
1139 *: "ìž„ì˜ ìž¬ìƒ" 1198 *: "A-B"
1140 </voice> 1199 </voice>
1141</phrase> 1200</phrase>
1142<phrase> 1201<phrase>
1143 id: LANG_REPEAT 1202 id: LANG_PLAY_SELECTED
1144 desc: in settings_menu 1203 desc: in settings_menu
1145 user: core 1204 user: core
1146 <source> 1205 <source>
1147 *: "Repeat" 1206 *: "Play Selected First"
1148 </source>
1149 <dest>
1150 *: "반복 재ìƒ"
1151 </dest>
1152 <voice>
1153 *: "반복 재ìƒ"
1154 </voice>
1155</phrase>
1156<phrase>
1157 id: LANG_REPEAT_ONE
1158 desc: repeat one song
1159 user: core
1160 <source>
1161 *: "One"
1162 </source> 1207 </source>
1163 <dest> 1208 <dest>
1164 *: "í•œ 곡" 1209 *: "ì„ íƒí•œ 곡 먼저 재ìƒ"
1165 </dest> 1210 </dest>
1166 <voice> 1211 <voice>
1167 *: "í•œ 곡" 1212 *: "ì„ íƒí•œ 곡 먼저 재ìƒ"
1168 </voice> 1213 </voice>
1169</phrase> 1214</phrase>
1170<phrase> 1215<phrase>
1171 id: LANG_REPEAT_AB 1216 id: LANG_WIND_MENU
1172 desc: repeat range from point A to B 1217 desc: in the playback sub menu
1173 user: core 1218 user: core
1174 <source> 1219 <source>
1175 *: "A-B" 1220 *: "Fast-Forward/Rewind"
1176 </source> 1221 </source>
1177 <dest> 1222 <dest>
1178 *: "A-B 구간반복" 1223 *: "빨리ê°ê¸°/ë˜ê°ê¸°"
1179 </dest> 1224 </dest>
1180 <voice> 1225 <voice>
1181 *: "A-B 구간반복" 1226 *: "빨리ê°ê¸°ì™€ ë˜ê°ê¸°"
1182 </voice> 1227 </voice>
1183</phrase> 1228</phrase>
1184<phrase> 1229<phrase>
1185 id: LANG_PLAY_SELECTED 1230 id: LANG_FFRW_STEP
1186 desc: in settings_menu 1231 desc: in settings_menu
1187 user: core 1232 user: core
1188 <source> 1233 <source>
1189 *: "Play Selected First" 1234 *: "FF/RW Min Step"
1190 </source> 1235 </source>
1191 <dest> 1236 <dest>
1192 *: "ì„ íƒí•œ 곡 먼저 재ìƒ" 1237 *: "빨리ê°ê¸°/ë˜ê°ê¸° ë¨ê³„"
1193 </dest> 1238 </dest>
1194 <voice> 1239 <voice>
1195 *: "ì„ íƒí•œ 곡 먼저 재ìƒ" 1240 *: "최소 ë¨ê³„"
1196 </voice> 1241 </voice>
1197</phrase> 1242</phrase>
1198<phrase> 1243<phrase>
1199 id: LANG_WIND_MENU 1244 id: LANG_FFRW_ACCEL
1200 desc: in the playback sub menu 1245 desc: in settings_menu
1201 user: core 1246 user: core
1202 <source> 1247 <source>
1203 *: "Fast-Forward/Rewind" 1248 *: "FF/RW Accel"
1204 </source> 1249 </source>
1205 <dest> 1250 <dest>
1206 *: "ë˜/빨리ê°ê¸° 설정" 1251 *: "빨리ê°ê¸°/ë˜ê°ê¸° ê°€ì"
1207 </dest> 1252 </dest>
1208 <voice> 1253 <voice>
1209 *: "ë˜/빨리ê°ê¸° 설정" 1254 *: "ê°ì"
1210 </voice> 1255 </voice>
1211</phrase> 1256</phrase>
1212<phrase> 1257<phrase>
@@ -1218,11 +1263,11 @@
1218 flash_storage: none 1263 flash_storage: none
1219 </source> 1264 </source>
1220 <dest> 1265 <dest>
1221 *: "ëŠê¹€ë°©ì§€ ë²„í¼ ì„¤ì •" 1266 *: "건너뛰기 방지 버í¼"
1222 flash_storage: none 1267 flash_storage: none
1223 </dest> 1268 </dest>
1224 <voice> 1269 <voice>
1225 *: "ëŠê¹€ë°©ì§€ ë²„í¼ ì„¤ì •" 1270 *: "건너뛰기 방지 버í¼"
1226 flash_storage: none 1271 flash_storage: none
1227 </voice> 1272 </voice>
1228</phrase> 1273</phrase>
@@ -1234,10 +1279,24 @@
1234 *: "Fade on Stop/Pause" 1279 *: "Fade on Stop/Pause"
1235 </source> 1280 </source>
1236 <dest> 1281 <dest>
1237 *: "ìŒì•… 정지시 페ì´ë“œ ì¸" 1282 *: "정지/ì¼ì‹œ 정지 ì‹œ 페ì´ë“œ"
1283 </dest>
1284 <voice>
1285 *: "정지와 ì¼ì‹œ 정지 ì‹œ 페ì´ë“œ"
1286 </voice>
1287</phrase>
1288<phrase>
1289 id: LANG_SINGLE_MODE
1290 desc: single mode
1291 user: core
1292 <source>
1293 *: "Single Mode"
1294 </source>
1295 <dest>
1296 *: "싱글 모드"
1238 </dest> 1297 </dest>
1239 <voice> 1298 <voice>
1240 *: "ìŒì•… ì •ì§€ìœ íŽ˜ì´ë“œ ì¸" 1299 *: "싱글 모드"
1241 </voice> 1300 </voice>
1242</phrase> 1301</phrase>
1243<phrase> 1302<phrase>
@@ -1248,10 +1307,10 @@
1248 *: "Party Mode" 1307 *: "Party Mode"
1249 </source> 1308 </source>
1250 <dest> 1309 <dest>
1251 *: "예약 모드 사용" 1310 *: "파티 모드"
1252 </dest> 1311 </dest>
1253 <voice> 1312 <voice>
1254 *: "예약 모드 사용" 1313 *: "파티 모드"
1255 </voice> 1314 </voice>
1256</phrase> 1315</phrase>
1257<phrase> 1316<phrase>
@@ -1272,6 +1331,159 @@
1272 </voice> 1331 </voice>
1273</phrase> 1332</phrase>
1274<phrase> 1333<phrase>
1334 id: LANG_CROSSFADE_ENABLE
1335 desc: in crossfade settings menu
1336 user: core
1337 <source>
1338 *: none
1339 crossfade: "Enable Crossfade"
1340 </source>
1341 <dest>
1342 *: none
1343 crossfade: "í¬ë¡œìŠ¤íŽ˜ì´ë“œ 활성화"
1344 </dest>
1345 <voice>
1346 *: none
1347 crossfade: "í¬ë¡œìŠ¤íŽ˜ì´ë“œ 활성화"
1348 </voice>
1349</phrase>
1350<phrase>
1351 id: LANG_MANTRACKSKIP
1352 desc: in crossfade settings
1353 user: core
1354 <source>
1355 *: none
1356 crossfade: "Manual Track Skip Only"
1357 </source>
1358 <dest>
1359 *: none
1360 crossfade: "ìˆ˜ë™ íŠ¸ëž™ 건너뛰기 ì „ìš©"
1361 </dest>
1362 <voice>
1363 *: none
1364 crossfade: "ìˆ˜ë™ íŠ¸ëž™ 건너뛰기 ì „ìš©"
1365 </voice>
1366</phrase>
1367<phrase>
1368 id: LANG_SHUFFLE_TRACKSKIP
1369 desc: in settings_menu
1370 user: core
1371 <source>
1372 *: none
1373 crossfade: "Shuffle or Manual Track Skip"
1374 </source>
1375 <dest>
1376 *: none
1377 crossfade: "셔플 ë˜ëŠ” ìˆ˜ë™ íŠ¸ëž™ 건너뛰기"
1378 </dest>
1379 <voice>
1380 *: none
1381 crossfade: "셔플 ë˜ëŠ” ìˆ˜ë™ íŠ¸ëž™ 건너뛰기"
1382 </voice>
1383</phrase>
1384<phrase>
1385 id: LANG_CROSSFADE_FADE_IN_DELAY
1386 desc: in crossfade settings menu
1387 user: core
1388 <source>
1389 *: none
1390 crossfade: "Fade-In Delay"
1391 </source>
1392 <dest>
1393 *: none
1394 crossfade: "페ì´ë“œì¸ 지연"
1395 </dest>
1396 <voice>
1397 *: none
1398 crossfade: "페ì´ë“œì¸ 지연"
1399 </voice>
1400</phrase>
1401<phrase>
1402 id: LANG_CROSSFADE_FADE_IN_DURATION
1403 desc: in crossfade settings menu
1404 user: core
1405 <source>
1406 *: none
1407 crossfade: "Fade-In Duration"
1408 </source>
1409 <dest>
1410 *: none
1411 crossfade: "페ì´ë“œì¸ ì§€ì† ì‹œê°„"
1412 </dest>
1413 <voice>
1414 *: none
1415 crossfade: "페ì´ë“œì¸ ì§€ì† ì‹œê°„"
1416 </voice>
1417</phrase>
1418<phrase>
1419 id: LANG_CROSSFADE_FADE_OUT_DELAY
1420 desc: in crossfade settings menu
1421 user: core
1422 <source>
1423 *: none
1424 crossfade: "Fade-Out Delay"
1425 </source>
1426 <dest>
1427 *: none
1428 crossfade: "페ì´ë“œì•„웃 지연"
1429 </dest>
1430 <voice>
1431 *: none
1432 crossfade: "페ì´ë“œì•„웃 지연"
1433 </voice>
1434</phrase>
1435<phrase>
1436 id: LANG_CROSSFADE_FADE_OUT_DURATION
1437 desc: in crossfade settings menu
1438 user: core
1439 <source>
1440 *: none
1441 crossfade: "Fade-Out Duration"
1442 </source>
1443 <dest>
1444 *: none
1445 crossfade: "페ì´ë“œì•„웃 ì§€ì† ì‹œê°„"
1446 </dest>
1447 <voice>
1448 *: none
1449 crossfade: "페ì´ë“œì•„웃 ì§€ì† ì‹œê°„"
1450 </voice>
1451</phrase>
1452<phrase>
1453 id: LANG_CROSSFADE_FADE_OUT_MODE
1454 desc: in crossfade settings menu
1455 user: core
1456 <source>
1457 *: none
1458 crossfade: "Fade-Out Mode"
1459 </source>
1460 <dest>
1461 *: none
1462 crossfade: "페ì´ë“œì•„웃 모드"
1463 </dest>
1464 <voice>
1465 *: none
1466 crossfade: "페ì´ë“œì•„웃 모드"
1467 </voice>
1468</phrase>
1469<phrase>
1470 id: LANG_MIX
1471 desc: in playback settings, crossfade option
1472 user: core
1473 <source>
1474 *: none
1475 crossfade: "Mix"
1476 </source>
1477 <dest>
1478 *: none
1479 crossfade: "혼합"
1480 </dest>
1481 <voice>
1482 *: none
1483 crossfade: "혼합"
1484 </voice>
1485</phrase>
1486<phrase>
1275 id: LANG_REPLAYGAIN 1487 id: LANG_REPLAYGAIN
1276 desc: in replaygain 1488 desc: in replaygain
1277 user: core 1489 user: core
@@ -1286,17 +1498,101 @@
1286 </voice> 1498 </voice>
1287</phrase> 1499</phrase>
1288<phrase> 1500<phrase>
1501 id: LANG_REPLAYGAIN_NOCLIP
1502 desc: in replaygain
1503 user: core
1504 <source>
1505 *: "Prevent Clipping"
1506 </source>
1507 <dest>
1508 *: "í´ë¦¬í•‘ 방지"
1509 </dest>
1510 <voice>
1511 *: "í´ë¦¬í•‘ 방지"
1512 </voice>
1513</phrase>
1514<phrase>
1515 id: LANG_REPLAYGAIN_MODE
1516 desc: in replaygain
1517 user: core
1518 <source>
1519 *: "Replaygain Type"
1520 </source>
1521 <dest>
1522 *: "리플레ì´ê²Œì¸ 유형"
1523 </dest>
1524 <voice>
1525 *: "리플레ì´ê²Œì¸ 유형"
1526 </voice>
1527</phrase>
1528<phrase>
1529 id: LANG_ALBUM_GAIN
1530 desc: in replaygain
1531 user: core
1532 <source>
1533 *: "Album Gain"
1534 </source>
1535 <dest>
1536 *: "앨범 게ì¸"
1537 </dest>
1538 <voice>
1539 *: "앨범 게ì¸"
1540 </voice>
1541</phrase>
1542<phrase>
1543 id: LANG_TRACK_GAIN
1544 desc: in replaygain
1545 user: core
1546 <source>
1547 *: "Track Gain"
1548 </source>
1549 <dest>
1550 *: "트랙 게ì¸"
1551 </dest>
1552 <voice>
1553 *: "트랙 게ì¸"
1554 </voice>
1555</phrase>
1556<phrase>
1557 id: LANG_SHUFFLE_GAIN
1558 desc: use track gain if shuffle mode is on, album gain otherwise
1559 user: core
1560 <source>
1561 *: "Track Gain if Shuffling"
1562 </source>
1563 <dest>
1564 *: "셔플 ì‹œ 트랙 게ì¸"
1565 </dest>
1566 <voice>
1567 *: "셔플 ì‹œ 트랙 게ì¸"
1568 </voice>
1569</phrase>
1570<phrase>
1571 id: LANG_REPLAYGAIN_PREAMP
1572 desc: in replaygain settings
1573 user: core
1574 <source>
1575 *: "Pre-amp"
1576 </source>
1577 <dest>
1578 *: "프리앰프"
1579 </dest>
1580 <voice>
1581 *: "프리앰프"
1582 </voice>
1583</phrase>
1584<phrase>
1289 id: LANG_BEEP 1585 id: LANG_BEEP
1290 desc: in playback settings 1586 desc: in playback settings
1291 user: core 1587 user: core
1292 <source> 1588 <source>
1293 *: "Beep Volume" 1589 *: "Track Skip Beep"
1294 </source> 1590 </source>
1295 <dest> 1591 <dest>
1296 *: "ì‚ ì†Œë¦¬í¬ê¸°" 1592 *: "트랙 건너뛰기 경고ìŒ"
1297 </dest> 1593 </dest>
1298 <voice> 1594 <voice>
1299 *: "ì‚ ì†Œë¦¬í¬ê¸°" 1595 *: "트랙 건너뛰기 경고ìŒ"
1300 </voice> 1596 </voice>
1301</phrase> 1597</phrase>
1302<phrase> 1598<phrase>
@@ -1307,10 +1603,10 @@
1307 *: "Weak" 1603 *: "Weak"
1308 </source> 1604 </source>
1309 <dest> 1605 <dest>
1310 *: "ìž‘ìŒ" 1606 *: "약함"
1311 </dest> 1607 </dest>
1312 <voice> 1608 <voice>
1313 *: "ìž‘ìŒ" 1609 *: "약함"
1314 </voice> 1610 </voice>
1315</phrase> 1611</phrase>
1316<phrase> 1612<phrase>
@@ -1321,10 +1617,10 @@
1321 *: "Moderate" 1617 *: "Moderate"
1322 </source> 1618 </source>
1323 <dest> 1619 <dest>
1324 *: "중간" 1620 *: "보통"
1325 </dest> 1621 </dest>
1326 <voice> 1622 <voice>
1327 *: "중간" 1623 *: "보통"
1328 </voice> 1624 </voice>
1329</phrase> 1625</phrase>
1330<phrase> 1626<phrase>
@@ -1335,10 +1631,10 @@
1335 *: "Strong" 1631 *: "Strong"
1336 </source> 1632 </source>
1337 <dest> 1633 <dest>
1338 *: "í¼" 1634 *: "강함"
1339 </dest> 1635 </dest>
1340 <voice> 1636 <voice>
1341 *: "" 1637 *: "강함"
1342 </voice> 1638 </voice>
1343</phrase> 1639</phrase>
1344<phrase> 1640<phrase>
@@ -1366,97 +1662,117 @@
1366 *: "Auto-Change Directory" 1662 *: "Auto-Change Directory"
1367 </source> 1663 </source>
1368 <dest> 1664 <dest>
1369 *: "í´ëê°„ ìžë™ ì´ë" 1665 *: "디렉토리 ìžë™ 변경"
1370 </dest> 1666 </dest>
1371 <voice> 1667 <voice>
1372 *: "í´ëê°„ ìžë™ ì´ë" 1668 *: "디렉토리 ìžë™ 변경"
1373 </voice> 1669 </voice>
1374</phrase> 1670</phrase>
1375<phrase> 1671<phrase>
1376 id: LANG_TAGCACHE 1672 id: LANG_RANDOM
1377 desc: in the main menu and the settings menu 1673 desc: random folder
1378 user: core 1674 user: core
1379 <source> 1675 <source>
1380 *: "Database" 1676 *: "Random"
1381 </source> 1677 </source>
1382 <dest> 1678 <dest>
1383 *: "ìŒì•… DB" 1679 *: "ëžœë¤"
1384 </dest> 1680 </dest>
1385 <voice> 1681 <voice>
1386 *: "ìŒì•… DB" 1682 *: "ëžœë¤"
1387 </voice> 1683 </voice>
1388</phrase> 1684</phrase>
1389<phrase> 1685<phrase>
1390 id: LANG_TAGCACHE_RAM 1686 id: LANG_AUDIOSCROBBLER
1391 desc: in tag cache settings 1687 desc: "Last.fm Logger" in Plugin/apps/scrobbler
1392 user: core 1688 user: core
1393 <source> 1689 <source>
1394 *: none 1690 *: "Last.fm Logger"
1395 tc_ramcache: "Load to RAM"
1396 </source> 1691 </source>
1397 <dest> 1692 <dest>
1398 *: none 1693 *: "Last.fm 로거"
1399 tc_ramcache: "ë©”ëª¨ë¦¬ì— ì½ê¸°"
1400 </dest> 1694 </dest>
1401 <voice> 1695 <voice>
1402 *: none 1696 *: "Last.fm 로거"
1403 tc_ramcache: "ë©”ëª¨ë¦¬ì— ì½ê¸°"
1404 </voice> 1697 </voice>
1405</phrase> 1698</phrase>
1406<phrase> 1699<phrase>
1407 id: LANG_TAGCACHE_FORCE_UPDATE 1700 id: LANG_CUESHEET_ENABLE
1408 desc: in tag cache settings 1701 desc: cuesheet support option
1409 user: core 1702 user: core
1410 <source> 1703 <source>
1411 *: "Initialize Now" 1704 *: "Cuesheet Support"
1412 </source> 1705 </source>
1413 <dest> 1706 <dest>
1414 *: "지금 초기화하기" 1707 *: "í시트 지ì›"
1415 </dest> 1708 </dest>
1416 <voice> 1709 <voice>
1417 *: "지금 초기화하기" 1710 *: "í시트 지ì›"
1418 </voice> 1711 </voice>
1419</phrase> 1712</phrase>
1420<phrase> 1713<phrase>
1421 id: LANG_TAGCACHE_FORCE_UPDATE_SPLASH 1714 id: LANG_HEADPHONE_UNPLUG
1422 desc: in tag cache settings 1715 desc: in settings_menu.
1423 user: core 1716 user: core
1424 <source> 1717 <source>
1425 *: "Updating in background" 1718 *: none
1719 headphone_detection: "Pause on Headphone Unplug"
1426 </source> 1720 </source>
1427 <dest> 1721 <dest>
1428 *: "ì—…ë°ì´íŠ¸ 중입니다..." 1722 *: none
1723 headphone_detection: "í—¤ë“œí° ë¶„ë¦¬ ì‹œ ì¼ì‹œ 중지"
1429 </dest> 1724 </dest>
1430 <voice> 1725 <voice>
1431 *: "" 1726 *: none
1727 headphone_detection: "í—¤ë“œí° ë¶„ë¦¬ ì‹œ ì¼ì‹œ 중지"
1432 </voice> 1728 </voice>
1433</phrase> 1729</phrase>
1434<phrase> 1730<phrase>
1435 id: LANG_TAGCACHE_INIT 1731 id: LANG_HEADPHONE_UNPLUG_RESUME
1436 desc: while initializing tagcache on boot 1732 desc: in pause_phones_menu.
1437 user: core 1733 user: core
1438 <source> 1734 <source>
1439 *: "Committing database" 1735 *: none
1736 headphone_detection: "Pause and Resume"
1440 </source> 1737 </source>
1441 <dest> 1738 <dest>
1442 *: "ìŒì•… DB ì—…ë°ì´íŠ¸ 중..." 1739 *: none
1740 headphone_detection: "ì¼ì‹œ 중지 ë° ìž¬ì‹œìž‘"
1443 </dest> 1741 </dest>
1444 <voice> 1742 <voice>
1445 *: "" 1743 *: none
1744 headphone_detection: "ì¼ì‹œ 중지 ë° ìž¬ì‹œìž‘"
1446 </voice> 1745 </voice>
1447</phrase> 1746</phrase>
1448<phrase> 1747<phrase>
1449 id: LANG_RUNTIMEDB_ACTIVE 1748 id: LANG_HEADPHONE_UNPLUG_DISABLE_AUTORESUME
1450 desc: in settings_menu. 1749 desc: in pause_phones_menu.
1451 user: core 1750 user: core
1452 <source> 1751 <source>
1453 *: "Gather Runtime Data" 1752 *: none
1753 headphone_detection: "Disable resume on startup if phones unplugged"
1454 </source> 1754 </source>
1455 <dest> 1755 <dest>
1456 *: "런타임 ë°ì´í„° 수집" 1756 *: none
1757 headphone_detection: "휴대í°ì´ ë¶„ë¦¬ëœ ê²½ìš° 시작 ì‹œ 재시작 비활성화"
1457 </dest> 1758 </dest>
1458 <voice> 1759 <voice>
1459 *: "런타임 ë°ì´í„° 수집" 1760 *: none
1761 headphone_detection: "휴대í°ì´ ë¶„ë¦¬ëœ ê²½ìš° 시작 ì‹œ 재시작 비활성화"
1762 </voice>
1763</phrase>
1764<phrase>
1765 id: LANG_FILE
1766 desc: in settings_menu()
1767 user: core
1768 <source>
1769 *: "File View"
1770 </source>
1771 <dest>
1772 *: "íŒŒì¼ ë³´ê¸°"
1773 </dest>
1774 <voice>
1775 *: "íŒŒì¼ ë³´ê¸°"
1460 </voice> 1776 </voice>
1461</phrase> 1777</phrase>
1462<phrase> 1778<phrase>
@@ -1467,10 +1783,10 @@
1467 *: "Sort Case Sensitive" 1783 *: "Sort Case Sensitive"
1468 </source> 1784 </source>
1469 <dest> 1785 <dest>
1470 *: "대/ì†Œë¬¸ìž ìˆœì„œë¡œ ì •ë ¬" 1786 *: "ëŒ€ì†Œë¬¸ìž êµ¬ë¶„ ì •ë ¬"
1471 </dest> 1787 </dest>
1472 <voice> 1788 <voice>
1473 *: "대/ì†Œë¬¸ìž ìˆœì„œë¡œ ì •ë ¬" 1789 *: "ëŒ€ì†Œë¬¸ìž êµ¬ë¶„ ì •ë ¬"
1474 </voice> 1790 </voice>
1475</phrase> 1791</phrase>
1476<phrase> 1792<phrase>
@@ -1481,10 +1797,10 @@
1481 *: "Sort Directories" 1797 *: "Sort Directories"
1482 </source> 1798 </source>
1483 <dest> 1799 <dest>
1484 *: "í´ë” ì •ë ¬ 순서" 1800 *: "디렉토리 ì •ë ¬"
1485 </dest> 1801 </dest>
1486 <voice> 1802 <voice>
1487 *: "í´ë” ì •ë ¬ 순서" 1803 *: "디렉토리 ì •ë ¬"
1488 </voice> 1804 </voice>
1489</phrase> 1805</phrase>
1490<phrase> 1806<phrase>
@@ -1495,10 +1811,10 @@
1495 *: "Sort Files" 1811 *: "Sort Files"
1496 </source> 1812 </source>
1497 <dest> 1813 <dest>
1498 *: "íŒŒì¼ ì •ë ¬ 순서" 1814 *: "íŒŒì¼ ì •ë ¬"
1499 </dest> 1815 </dest>
1500 <voice> 1816 <voice>
1501 *: "íŒŒì¼ ì •ë ¬ 순서" 1817 *: "íŒŒì¼ ì •ë ¬"
1502 </voice> 1818 </voice>
1503</phrase> 1819</phrase>
1504<phrase> 1820<phrase>
@@ -1509,10 +1825,10 @@
1509 *: "Alphabetical" 1825 *: "Alphabetical"
1510 </source> 1826 </source>
1511 <dest> 1827 <dest>
1512 *: "ì´ë¦„" 1828 *: "알파벳순"
1513 </dest> 1829 </dest>
1514 <voice> 1830 <voice>
1515 *: "ì´ë¦„" 1831 *: "알파벳순"
1516 </voice> 1832 </voice>
1517</phrase> 1833</phrase>
1518<phrase> 1834<phrase>
@@ -1523,10 +1839,10 @@
1523 *: "By Date" 1839 *: "By Date"
1524 </source> 1840 </source>
1525 <dest> 1841 <dest>
1526 *: "날짜" 1842 *: "날짜별"
1527 </dest> 1843 </dest>
1528 <voice> 1844 <voice>
1529 *: "날짜" 1845 *: "날짜별"
1530 </voice> 1846 </voice>
1531</phrase> 1847</phrase>
1532<phrase> 1848<phrase>
@@ -1537,10 +1853,10 @@
1537 *: "By Newest Date" 1853 *: "By Newest Date"
1538 </source> 1854 </source>
1539 <dest> 1855 <dest>
1540 *: "수정한 날짜" 1856 *: "ìµœì  ë‚ ì§œë³„"
1541 </dest> 1857 </dest>
1542 <voice> 1858 <voice>
1543 *: "수정한 날짜" 1859 *: "ìµœì  ë‚ ì§œë³„"
1544 </voice> 1860 </voice>
1545</phrase> 1861</phrase>
1546<phrase> 1862<phrase>
@@ -1551,10 +1867,10 @@
1551 *: "By Type" 1867 *: "By Type"
1552 </source> 1868 </source>
1553 <dest> 1869 <dest>
1554 *: "형ì‹" 1870 *: "유형별"
1555 </dest> 1871 </dest>
1556 <voice> 1872 <voice>
1557 *: "형ì‹" 1873 *: "유형별"
1558 </voice> 1874 </voice>
1559</phrase> 1875</phrase>
1560<phrase> 1876<phrase>
@@ -1565,10 +1881,10 @@
1565 *: "Show Files" 1881 *: "Show Files"
1566 </source> 1882 </source>
1567 <dest> 1883 <dest>
1568 *: "íŒŒì¼ í‘œì‹œ 설정" 1884 *: "íŒŒì¼ í‘œì‹œ"
1569 </dest> 1885 </dest>
1570 <voice> 1886 <voice>
1571 *: "íŒŒì¼ í‘œì‹œ 설정" 1887 *: "íŒŒì¼ í‘œì‹œ"
1572 </voice> 1888 </voice>
1573</phrase> 1889</phrase>
1574<phrase> 1890<phrase>
@@ -1579,10 +1895,10 @@
1579 *: "Supported" 1895 *: "Supported"
1580 </source> 1896 </source>
1581 <dest> 1897 <dest>
1582 *: "ì§€ì› íŒŒì¼" 1898 *: "지ì›ë¨"
1583 </dest> 1899 </dest>
1584 <voice> 1900 <voice>
1585 *: "ì§€ì› íŒŒì¼" 1901 *: "지ì›ë¨"
1586 </voice> 1902 </voice>
1587</phrase> 1903</phrase>
1588<phrase> 1904<phrase>
@@ -1593,10 +1909,10 @@
1593 *: "Music" 1909 *: "Music"
1594 </source> 1910 </source>
1595 <dest> 1911 <dest>
1596 *: "ìŒì•… 파ì¼" 1912 *: "ìŒì•…"
1597 </dest> 1913 </dest>
1598 <voice> 1914 <voice>
1599 *: "ìŒì•… 파ì¼" 1915 *: "ìŒì•…"
1600 </voice> 1916 </voice>
1601</phrase> 1917</phrase>
1602<phrase> 1918<phrase>
@@ -1607,24 +1923,245 @@
1607 *: "Follow Playlist" 1923 *: "Follow Playlist"
1608 </source> 1924 </source>
1609 <dest> 1925 <dest>
1610 *: "재ìƒì¤‘ì¸ í´ë”ë¡œ ì´ë™" 1926 *: "재ìƒëª©ë¡ 팔로우"
1611 </dest> 1927 </dest>
1612 <voice> 1928 <voice>
1613 *: "재ìƒì¤‘ì¸ í´ë”ë¡œ ì´ë™" 1929 *: "재ìƒëª©ë¡ 팔로우"
1614 </voice> 1930 </voice>
1615</phrase> 1931</phrase>
1616<phrase> 1932<phrase>
1617 id: LANG_SHOW_ICONS 1933 id: LANG_SHOW_PATH
1618 desc: in settings_menu 1934 desc: in settings_menu
1619 user: core 1935 user: core
1620 <source> 1936 <source>
1621 *: "Show Icons" 1937 *: "Show Path"
1622 </source> 1938 </source>
1623 <dest> 1939 <dest>
1624 *: "ì•„ì´ì½˜ 표시" 1940 *: "경로 표시"
1625 </dest> 1941 </dest>
1626 <voice> 1942 <voice>
1627 *: "ì•„ì´ì½˜ 표시" 1943 *: "경로 표시"
1944 </voice>
1945</phrase>
1946<phrase>
1947 id: LANG_SHOW_PATH_CURRENT
1948 desc: in show path menu
1949 user: core
1950 <source>
1951 *: "Current Directory Only"
1952 </source>
1953 <dest>
1954 *: "현재 디렉토리만"
1955 </dest>
1956 <voice>
1957 *: "현재 디렉토리만"
1958 </voice>
1959</phrase>
1960<phrase>
1961 id: LANG_DISPLAY_FULL_PATH
1962 desc: track display options
1963 user: core
1964 <source>
1965 *: "Full Path"
1966 </source>
1967 <dest>
1968 *: "전체 경로"
1969 </dest>
1970 <voice>
1971 *: "전체 경로"
1972 </voice>
1973</phrase>
1974<phrase>
1975 id: LANG_BUILDING_DATABASE
1976 desc: splash database building progress
1977 user: core
1978 <source>
1979 *: "Building database... %d found (OFF to return)"
1980 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
1981 gogearsa9200: "Building database... %d found (REW to return)"
1982 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
1983 iriverh100,iriverh120,iriverh300: "Building database... %d found (STOP to return)"
1984 </source>
1985 <dest>
1986 *: "ë°ì´í„°ë² ì´ìŠ¤ 구축하는 중... %d ì°¾ì•˜ìŒ (ëŒì•„오려면 OFF)"
1987 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "ë°ì´í„°ë² ì´ìŠ¤ 구축하는 중... %d found (ëŒì•„오려면 LEFT)"
1988 gogearsa9200: "ë°ì´í„°ë² ì´ìŠ¤ 구축하는 중... %d ì°¾ì•˜ìŒ (ëŒì•„오려면 REW)"
1989 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "ë°ì´í„°ë² ì´ìŠ¤ 구축하는 중... %d ì°¾ì•˜ìŒ (ëŒì•„오려면 PREV)"
1990 iriverh100,iriverh120,iriverh300: "ë°ì´í„°ë² ì´ìŠ¤ 구축하는 중... %d ì°¾ì•˜ìŒ (ëŒì•„오려면 STOP)"
1991 </dest>
1992 <voice>
1993 *: "ë°ì´í„°ë² ì´ìŠ¤ì—ì„œ ì°¾ì€ í•­ëª©"
1994 </voice>
1995</phrase>
1996<phrase>
1997 id: LANG_TAGCACHE_RAM
1998 desc: in tag cache settings
1999 user: core
2000 <source>
2001 *: none
2002 tc_ramcache: "Load to RAM"
2003 </source>
2004 <dest>
2005 *: none
2006 tc_ramcache: "RAMì— ë¡œë“œ"
2007 </dest>
2008 <voice>
2009 *: none
2010 tc_ramcache: "RAMì— ë¡œë“œ"
2011 </voice>
2012</phrase>
2013<phrase>
2014 id: LANG_TAGCACHE_AUTOUPDATE
2015 desc: in tag cache settings
2016 user: core
2017 <source>
2018 *: "Auto Update"
2019 </source>
2020 <dest>
2021 *: "ìžë™ ì—…ë°ì´íŠ¸"
2022 </dest>
2023 <voice>
2024 *: "ìžë™ ì—…ë°ì´íŠ¸"
2025 </voice>
2026</phrase>
2027<phrase>
2028 id: LANG_TAGCACHE_FORCE_UPDATE
2029 desc: in tag cache settings
2030 user: core
2031 <source>
2032 *: "Initialize Now"
2033 </source>
2034 <dest>
2035 *: "지금 초기화"
2036 </dest>
2037 <voice>
2038 *: "지금 초기화"
2039 </voice>
2040</phrase>
2041<phrase>
2042 id: LANG_TAGCACHE_UPDATE
2043 desc: in tag cache settings
2044 user: core
2045 <source>
2046 *: "Update Now"
2047 </source>
2048 <dest>
2049 *: "지금 ì—…ë°ì´íŠ¸"
2050 </dest>
2051 <voice>
2052 *: "지금 ì—…ë°ì´íŠ¸"
2053 </voice>
2054</phrase>
2055<phrase>
2056 id: LANG_RUNTIMEDB_ACTIVE
2057 desc: in settings_menu.
2058 user: core
2059 <source>
2060 *: "Gather Runtime Data"
2061 </source>
2062 <dest>
2063 *: "런타임 ë°ì´í„° 수집"
2064 </dest>
2065 <voice>
2066 *: "런타임 ë°ì´í„° 수집"
2067 </voice>
2068</phrase>
2069<phrase>
2070 id: LANG_TAGCACHE_EXPORT
2071 desc: in tag cache settings
2072 user: core
2073 <source>
2074 *: "Export Modifications"
2075 </source>
2076 <dest>
2077 *: "수정사항 내보내기"
2078 </dest>
2079 <voice>
2080 *: "수정사항 내보내기"
2081 </voice>
2082</phrase>
2083<phrase>
2084 id: LANG_TAGCACHE_IMPORT
2085 desc: in tag cache settings
2086 user: core
2087 <source>
2088 *: "Import Modifications"
2089 </source>
2090 <dest>
2091 *: "수정사항 가져오기"
2092 </dest>
2093 <voice>
2094 *: "수정사항 가져오기"
2095 </voice>
2096</phrase>
2097<phrase>
2098 id: LANG_TAGCACHE_FORCE_UPDATE_SPLASH
2099 desc: in tag cache settings
2100 user: core
2101 <source>
2102 *: "Updating in background"
2103 </source>
2104 <dest>
2105 *: "백그ë¼ìš´ë“œì—ì„œ ì—…ë°ì´íŠ¸ 중"
2106 </dest>
2107 <voice>
2108 *: "백그ë¼ìš´ë“œì—ì„œ ì—…ë°ì´íŠ¸ 중"
2109 </voice>
2110</phrase>
2111<phrase>
2112 id: LANG_TAGCACHE_INIT
2113 desc: while initializing tagcache on boot
2114 user: core
2115 <source>
2116 *: "Committing database"
2117 </source>
2118 <dest>
2119 *: "ë°ì´í„°ë² ì´ìŠ¤ 커밋 중"
2120 </dest>
2121 <voice>
2122 *: "ë°ì´í„°ë² ì´ìŠ¤ 커밋 중"
2123 </voice>
2124</phrase>
2125<phrase>
2126 id: LANG_TAGCACHE_BUSY
2127 desc: when trying to shutdown and tagcache is committing
2128 user: core
2129 <source>
2130 *: "Database is not ready"
2131 </source>
2132 <dest>
2133 *: "ë°ì´í„°ë² ì´ìŠ¤ê°€ 준비ë˜ì§€ 않았ìŒ"
2134 </dest>
2135 <voice>
2136 *: "ë°ì´í„°ë² ì´ìŠ¤ê°€ 준비ë˜ì§€ 않았ìŒ"
2137 </voice>
2138</phrase>
2139<phrase>
2140 id: LANG_TAGNAVI_ALL_TRACKS
2141 desc: "<All tracks>" entry in tag browser
2142 user: core
2143 <source>
2144 *: "<All tracks>"
2145 </source>
2146 <dest>
2147 *: "<모든 트랙>"
2148 </dest>
2149 <voice>
2150 *: "모든 트랙"
2151 </voice>
2152</phrase>
2153<phrase>
2154 id: LANG_DISPLAY
2155 desc: in settings_menu()
2156 user: core
2157 <source>
2158 *: "Display"
2159 </source>
2160 <dest>
2161 *: "화면"
2162 </dest>
2163 <voice>
2164 *: "화면"
1628 </voice> 2165 </voice>
1629</phrase> 2166</phrase>
1630<phrase> 2167<phrase>
@@ -1632,13 +2169,13 @@
1632 desc: in setting_menu() 2169 desc: in setting_menu()
1633 user: core 2170 user: core
1634 <source> 2171 <source>
1635 *: "Browse Fonts" 2172 *: "Font"
1636 </source> 2173 </source>
1637 <dest> 2174 <dest>
1638 *: "í°íŠ¸ 찾기" 2175 *: "글꼴"
1639 </dest> 2176 </dest>
1640 <voice> 2177 <voice>
1641 *: "í°íŠ¸ 찾기" 2178 *: "글꼴"
1642 </voice> 2179 </voice>
1643</phrase> 2180</phrase>
1644<phrase> 2181<phrase>
@@ -1646,13 +2183,13 @@
1646 desc: in settings_menu() 2183 desc: in settings_menu()
1647 user: core 2184 user: core
1648 <source> 2185 <source>
1649 *: "Browse .wps files" 2186 *: "While Playing Screen"
1650 </source> 2187 </source>
1651 <dest> 2188 <dest>
1652 *: "WPS ì„ íƒ" 2189 *: "화면ì 재ìƒí•˜ëŠ” ë™ì•ˆ"
1653 </dest> 2190 </dest>
1654 <voice> 2191 <voice>
1655 *: "재ìƒí™”ë©´ ì„ íƒ" 2192 *: "화면ì 재ìƒí•˜ëŠ” ë™ì•ˆ"
1656 </voice> 2193 </voice>
1657</phrase> 2194</phrase>
1658<phrase> 2195<phrase>
@@ -1661,15 +2198,15 @@
1661 user: core 2198 user: core
1662 <source> 2199 <source>
1663 *: none 2200 *: none
1664 remote: "Browse .rwps files" 2201 remote: "Remote While Playing Screen"
1665 </source> 2202 </source>
1666 <dest> 2203 <dest>
1667 *: none 2204 *: none
1668 remote: "리모트 WPS 찾기" 2205 remote: "í™”ë©´ì„ ìž¬ìƒí•˜ëŠ ë™ì•ˆ ì격"
1669 </dest> 2206 </dest>
1670 <voice> 2207 <voice>
1671 *: none 2208 *: none
1672 remote: "리모트 WPS 찾기" 2209 remote: "í™”ë©´ì„ ìž¬ìƒí•˜ëŠ ë™ì•ˆ ì격"
1673 </voice> 2210 </voice>
1674</phrase> 2211</phrase>
1675<phrase> 2212<phrase>
@@ -1680,10 +2217,377 @@
1680 *: "LCD Settings" 2217 *: "LCD Settings"
1681 </source> 2218 </source>
1682 <dest> 2219 <dest>
1683 *: "본체 LCD 설정" 2220 *: "LCD 설정"
2221 </dest>
2222 <voice>
2223 *: "LCD 설정"
2224 </voice>
2225</phrase>
2226<phrase>
2227 id: LANG_BACKLIGHT
2228 desc: in settings_menu
2229 user: core
2230 <source>
2231 *: "Backlight"
2232 </source>
2233 <dest>
2234 *: "ë°±ë¼ì´íŠ¸"
2235 </dest>
2236 <voice>
2237 *: "ë°±ë¼ì´íŠ¸"
2238 </voice>
2239</phrase>
2240<phrase>
2241 id: LANG_BACKLIGHT_ON_WHEN_CHARGING
2242 desc: in display_settings_menu, backlight timeout with charger connected
2243 user: core
2244 <source>
2245 *: none
2246 charging: "Backlight (While Plugged In)"
2247 </source>
2248 <dest>
2249 *: none
2250 charging: "ë°±ë¼ì´íŠ¸ (ì—°ê²°ëœ ë™ì•ˆ)"
2251 </dest>
2252 <voice>
2253 *: none
2254 charging: "ë°±ë¼ì´íŠ¸ (ì—°ê²°ëœ ë™ì•ˆ)"
2255 </voice>
2256</phrase>
2257<phrase>
2258 id: LANG_BACKLIGHT_ON_BUTTON_HOLD
2259 desc: in lcd settings
2260 user: core
2261 <source>
2262 *: "Backlight on Lock"
2263 hold_button: "Backlight on Hold"
2264 </source>
2265 <dest>
2266 *: "ìž ê¶œì„ ì‹œ ë°±ë¼ì´íŠ¸"
2267 hold_button: "홀드 ì‹œ ë°±ë¼ì´íŠ¸"
2268 </dest>
2269 <voice>
2270 *: "ìž ê¶œì„ ì‹œ ë°±ë¼ì´íŠ¸"
2271 hold_button: "홀드 ì‹œ ë°±ë¼ì´íŠ¸"
2272 </voice>
2273</phrase>
2274<phrase>
2275 id: LANG_CAPTION_BACKLIGHT
2276 desc: in settings_menu
2277 user: core
2278 <source>
2279 *: "Caption Backlight"
2280 </source>
2281 <dest>
2282 *: "캡션 ë°±ë¼ì´íŠ¸"
2283 </dest>
2284 <voice>
2285 *: "캡션 ë°±ë¼ì´íŠ¸"
2286 </voice>
2287</phrase>
2288<phrase>
2289 id: LANG_BACKLIGHT_FADE_IN
2290 desc: in settings_menu
2291 user: core
2292 <source>
2293 *: none
2294 backlight_fade*: "Backlight Fade In"
2295 </source>
2296 <dest>
2297 *: none
2298 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œ ì¸"
2299 </dest>
2300 <voice>
2301 *: none
2302 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œ ì¸"
2303 </voice>
2304</phrase>
2305<phrase>
2306 id: LANG_BACKLIGHT_FADE_OUT
2307 desc: in settings_menu
2308 user: core
2309 <source>
2310 *: none
2311 backlight_fade*: "Backlight Fade Out"
2312 </source>
2313 <dest>
2314 *: none
2315 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œ 아웃"
2316 </dest>
2317 <voice>
2318 *: none
2319 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œ 아웃"
2320 </voice>
2321</phrase>
2322<phrase>
2323 id: LANG_BACKLIGHT_FILTER_FIRST_KEYPRESS
2324 desc: Backlight behaviour setting
2325 user: core
2326 <source>
2327 *: "First Buttonpress Enables Backlight Only"
2328 </source>
2329 <dest>
2330 *: "첫 번째 ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ ë°±ë¼ì´íŠ¸ë§Œ 활성화"
2331 </dest>
2332 <voice>
2333 *: "첫 번째 ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ ë°±ë¼ì´íŠ¸ë§Œ 활성화"
2334 </voice>
2335</phrase>
2336<phrase>
2337 id: LANG_LCD_SLEEP_AFTER_BACKLIGHT_OFF
2338 desc: In display settings, time to switch LCD chip into power saving state
2339 user: core
2340 <source>
2341 *: none
2342 lcd_sleep: "Sleep (After Backlight Off)"
2343 </source>
2344 <dest>
2345 *: none
2346 lcd_sleep: "절전 (ë°±ë¼ì´íŠ¸ê°€ 꺼진 후)"
2347 </dest>
2348 <voice>
2349 *: none
2350 lcd_sleep: "절전(ë°±ë¼ì´íŠ¸ê°€ 꺼진 후)"
2351 </voice>
2352</phrase>
2353<phrase>
2354 id: LANG_BRIGHTNESS
2355 desc: in settings_menu
2356 user: core
2357 <source>
2358 *: none
2359 backlight_brightness: "Brightness"
2360 </source>
2361 <dest>
2362 *: none
2363 backlight_brightness: "ë°ê¸°"
2364 </dest>
2365 <voice>
2366 *: none
2367 backlight_brightness: "ë°ê¸°"
2368 </voice>
2369</phrase>
2370<phrase>
2371 id: LANG_CONTRAST
2372 desc: in settings_menu
2373 user: core
2374 <source>
2375 *: "Contrast"
2376 </source>
2377 <dest>
2378 *: "대비"
2379 </dest>
2380 <voice>
2381 *: "대비"
2382 </voice>
2383</phrase>
2384<phrase>
2385 id: LANG_INVERT
2386 desc: in settings_menu
2387 user: core
2388 <source>
2389 *: none
2390 lcd_invert,remote_lcd_invert: "LCD Mode"
2391 </source>
2392 <dest>
2393 *: none
2394 lcd_invert,remote_lcd_invert: "LCD 모드"
2395 </dest>
2396 <voice>
2397 *: none
2398 lcd_invert,remote_lcd_invert: "LCD 모드"
2399 </voice>
2400</phrase>
2401<phrase>
2402 id: LANG_INVERT_LCD_INVERSE
2403 desc: in settings_menu
2404 user: core
2405 <source>
2406 *: none
2407 lcd_invert,remote_lcd_invert: "Inverse"
2408 </source>
2409 <dest>
2410 *: none
2411 lcd_invert,remote_lcd_invert: "반전"
2412 </dest>
2413 <voice>
2414 *: none
2415 lcd_invert,remote_lcd_invert: "반전"
2416 </voice>
2417</phrase>
2418<phrase>
2419 id: LANG_FLIP_DISPLAY
2420 desc: in settings_menu, option to turn display+buttos by 180 degrees
2421 user: core
2422 <source>
2423 *: "Upside Down"
2424 </source>
2425 <dest>
2426 *: "ìƒí•˜ 반전"
2427 </dest>
2428 <voice>
2429 *: "ìƒí•˜ 반전"
2430 </voice>
2431</phrase>
2432<phrase>
2433 id: LANG_INVERT_CURSOR
2434 desc: in settings_menu
2435 user: core
2436 <source>
2437 *: "Line Selector Type"
2438 </source>
2439 <dest>
2440 *: "ë¼ì¸ ì„ íƒê¸° 유형"
2441 </dest>
2442 <voice>
2443 *: "ë¼ì¸ ì„ íƒê¸° 유형"
2444 </voice>
2445</phrase>
2446<phrase>
2447 id: LANG_INVERT_CURSOR_POINTER
2448 desc: in settings_menu
2449 user: core
2450 <source>
2451 *: "Pointer"
2452 </source>
2453 <dest>
2454 *: "í¬ì¸í„°"
2455 </dest>
2456 <voice>
2457 *: "í¬ì¸í„°"
2458 </voice>
2459</phrase>
2460<phrase>
2461 id: LANG_INVERT_CURSOR_BAR
2462 desc: in settings_menu
2463 user: core
2464 <source>
2465 *: "Bar (Inverse)"
2466 </source>
2467 <dest>
2468 *: "바 (반전)"
2469 </dest>
2470 <voice>
2471 *: "반전바"
2472 </voice>
2473</phrase>
2474<phrase>
2475 id: LANG_CLEAR_BACKDROP
2476 desc: text for LCD settings menu
2477 user: core
2478 <source>
2479 *: none
2480 lcd_non-mono: "Clear Backdrop"
2481 </source>
2482 <dest>
2483 *: none
2484 lcd_non-mono: "배경화면 지우기"
1684 </dest> 2485 </dest>
1685 <voice> 2486 <voice>
1686 *: "본체 LCD 설정" 2487 *: none
2488 lcd_non-mono: "배경화면 지우기"
2489 </voice>
2490</phrase>
2491<phrase>
2492 id: LANG_BACKGROUND_COLOR
2493 desc: menu entry to set the background color
2494 user: core
2495 <source>
2496 *: none
2497 lcd_color: "Background Colour"
2498 </source>
2499 <dest>
2500 *: none
2501 lcd_color: "배경색"
2502 </dest>
2503 <voice>
2504 *: none
2505 lcd_color: "배경색"
2506 </voice>
2507</phrase>
2508<phrase>
2509 id: LANG_FOREGROUND_COLOR
2510 desc: menu entry to set the foreground color
2511 user: core
2512 <source>
2513 *: none
2514 lcd_color: "Foreground Colour"
2515 </source>
2516 <dest>
2517 *: none
2518 lcd_color: "전경색"
2519 </dest>
2520 <voice>
2521 *: none
2522 lcd_color: "전경색"
2523 </voice>
2524</phrase>
2525<phrase>
2526 id: LANG_RESET_COLORS
2527 desc: menu
2528 user: core
2529 <source>
2530 *: none
2531 lcd_color: "Reset Colours"
2532 </source>
2533 <dest>
2534 *: none
2535 lcd_color: "ìƒ‰ìƒ ìž¬ì„¤ì •"
2536 </dest>
2537 <voice>
2538 *: none
2539 lcd_color: "ìƒ‰ìƒ ìž¬ì„¤ì •"
2540 </voice>
2541</phrase>
2542<phrase>
2543 id: LANG_COLOR_RGB_LABELS
2544 desc: what to show for the 'R' 'G' 'B' ONE LETTER EACH
2545 user: core
2546 <source>
2547 *: none
2548 lcd_color: "RGB"
2549 </source>
2550 <dest>
2551 *: none
2552 lcd_color: "RGB"
2553 </dest>
2554 <voice>
2555 *: none
2556 lcd_color: ""
2557 </voice>
2558</phrase>
2559<phrase>
2560 id: LANG_COLOR_RGB_VALUE
2561 desc: in color screen
2562 user: core
2563 <source>
2564 *: none
2565 lcd_color: "RGB: %02X%02X%02X"
2566 </source>
2567 <dest>
2568 *: none
2569 lcd_color: "RGB: %02X%02X%02X"
2570 </dest>
2571 <voice>
2572 *: none
2573 lcd_color: ""
2574 </voice>
2575</phrase>
2576<phrase>
2577 id: LANG_COLOR_UNACCEPTABLE
2578 desc: splash when user selects an invalid colour
2579 user: core
2580 <source>
2581 *: none
2582 lcd_color: "Invalid colour"
2583 </source>
2584 <dest>
2585 *: none
2586 lcd_color: "ìž˜ëª»ëœ ìƒ‰ìƒ"
2587 </dest>
2588 <voice>
2589 *: none
2590 lcd_color: ""
1687 </voice> 2591 </voice>
1688</phrase> 2592</phrase>
1689<phrase> 2593<phrase>
@@ -1696,11 +2600,42 @@
1696 </source> 2600 </source>
1697 <dest> 2601 <dest>
1698 *: none 2602 *: none
1699 remote: "리모트 LCD 설정" 2603 remote: "ì›ê²© LCD 설정"
2604 </dest>
2605 <voice>
2606 *: none
2607 remote: "ì›ê²© LCD 설정"
2608 </voice>
2609</phrase>
2610<phrase>
2611 id: LANG_REDUCE_TICKING
2612 desc: in remote lcd settings menu
2613 user: core
2614 <source>
2615 *: none
2616 remote_ticking: "Reduce Ticking"
2617 </source>
2618 <dest>
2619 *: none
2620 remote_ticking: "티킹 ê°ì†Œ"
1700 </dest> 2621 </dest>
1701 <voice> 2622 <voice>
1702 *: none 2623 *: none
1703 remote: "리모트 LCD 설정" 2624 remote_ticking: "티킹 ê°ì†Œ"
2625 </voice>
2626</phrase>
2627<phrase>
2628 id: LANG_SHOW_ICONS
2629 desc: in settings_menu
2630 user: core
2631 <source>
2632 *: "Show Icons"
2633 </source>
2634 <dest>
2635 *: "ì•„ì´ì½˜ 표시"
2636 </dest>
2637 <voice>
2638 *: "ì•„ì´ì½˜ 표시"
1704 </voice> 2639 </voice>
1705</phrase> 2640</phrase>
1706<phrase> 2641<phrase>
@@ -1711,10 +2646,187 @@
1711 *: "Scrolling" 2646 *: "Scrolling"
1712 </source> 2647 </source>
1713 <dest> 2648 <dest>
1714 *: "ë¬¸ìž í름 설정" 2649 *: "스í¬ë¡¤"
1715 </dest> 2650 </dest>
1716 <voice> 2651 <voice>
1717 *: "ë¬¸ìž í름 설정" 2652 *: "스í¬ë¡¤"
2653 </voice>
2654</phrase>
2655<phrase>
2656 id: LANG_SCROLL
2657 desc: in settings_menu
2658 user: core
2659 <source>
2660 *: "Scroll Speed Setting Example"
2661 </source>
2662 <dest>
2663 *: "스í¬ë¡¤ ì†ë„ 설정 예제"
2664 </dest>
2665 <voice>
2666 *: ""
2667 </voice>
2668</phrase>
2669<phrase>
2670 id: LANG_SCROLL_SPEED
2671 desc: in display_settings_menu()
2672 user: core
2673 <source>
2674 *: "Scroll Speed"
2675 </source>
2676 <dest>
2677 *: "스í¬ë¡¤ ì†ë„"
2678 </dest>
2679 <voice>
2680 *: "스í¬ë¡¤ ì†ë„"
2681 </voice>
2682</phrase>
2683<phrase>
2684 id: LANG_SCROLL_DELAY
2685 desc: Delay before scrolling
2686 user: core
2687 <source>
2688 *: "Scroll Start Delay"
2689 </source>
2690 <dest>
2691 *: "스í¬ë¡¤ 시작 지연"
2692 </dest>
2693 <voice>
2694 *: "스í¬ë¡¤ 시작 지연"
2695 </voice>
2696</phrase>
2697<phrase>
2698 id: LANG_SCROLL_STEP
2699 desc: Pixels to advance per scroll
2700 user: core
2701 <source>
2702 *: "Scroll Step Size"
2703 </source>
2704 <dest>
2705 *: "스í¬ë¡¤ 단계 í¬ê¸°"
2706 </dest>
2707 <voice>
2708 *: "스í¬ë¡¤ 단계 í¬ê¸°"
2709 </voice>
2710</phrase>
2711<phrase>
2712 id: LANG_SCROLL_STEP_EXAMPLE
2713 desc: Pixels to advance per scroll
2714 user: core
2715 <source>
2716 *: "Scroll Step Size Setting Example Text"
2717 </source>
2718 <dest>
2719 *: "스í¬ë¡¤ 단계 í¬ê¸° 설정 예제 í…스트"
2720 </dest>
2721 <voice>
2722 *: ""
2723 </voice>
2724</phrase>
2725<phrase>
2726 id: LANG_BIDIR_SCROLL
2727 desc: Bidirectional scroll limit
2728 user: core
2729 <source>
2730 *: "Bidirectional Scroll Limit"
2731 </source>
2732 <dest>
2733 *: "ì–‘ë°©í–¥ 스í¬ë¡¤ 제한"
2734 </dest>
2735 <voice>
2736 *: "ì–‘ë°©í–¥ 스í¬ë¡¤ 제한"
2737 </voice>
2738</phrase>
2739<phrase>
2740 id: LANG_REMOTE_SCROLL_SETS
2741 desc: "Remote Scrolling Options" Submenu in "Scrolling Options" menu
2742 user: core
2743 <source>
2744 *: none
2745 remote: "Remote Scrolling Options"
2746 </source>
2747 <dest>
2748 *: none
2749 remote: "ì›ê²© 스í¬ë¡¤ 옵션"
2750 </dest>
2751 <voice>
2752 *: none
2753 remote: "ì›ê²© 스í¬ë¡¤ 옵션"
2754 </voice>
2755</phrase>
2756<phrase>
2757 id: LANG_SCREEN_SCROLL_VIEW
2758 desc: should lines scroll out of the screen
2759 user: core
2760 <source>
2761 *: "Screen Scrolls Out Of View"
2762 </source>
2763 <dest>
2764 *: "화면 스í¬ë¡¤ì´ ë³´ì´ì§€ ì•ŠìŒ"
2765 </dest>
2766 <voice>
2767 *: "화면 스í¬ë¡¤ì´ ë³´ì´ì§€ ì•ŠìŒ"
2768 </voice>
2769</phrase>
2770<phrase>
2771 id: LANG_SCREEN_SCROLL_STEP
2772 desc: Pixels to advance per Screen scroll
2773 user: core
2774 <source>
2775 *: "Screen Scroll Step Size"
2776 </source>
2777 <dest>
2778 *: "화면 스í¬ë¡¤ 단계 í¬ê¸°"
2779 </dest>
2780 <voice>
2781 *: "화면 스í¬ë¡¤ 단계 í¬ê¸°"
2782 </voice>
2783</phrase>
2784<phrase>
2785 id: LANG_SCROLL_PAGINATED
2786 desc: jump to new page when scrolling
2787 user: core
2788 <source>
2789 *: "Paged Scrolling"
2790 </source>
2791 <dest>
2792 *: "페ì´ì§€ 스í¬ë¡¤"
2793 </dest>
2794 <voice>
2795 *: "페ì´ì§€ 스í¬ë¡¤"
2796 </voice>
2797</phrase>
2798<phrase>
2799 id: LANG_LISTACCEL_START_DELAY
2800 desc: Delay before list starts accelerating
2801 user: core
2802 <source>
2803 *: "List Acceleration Start Delay"
2804 wheel_acceleration: none
2805 </source>
2806 <dest>
2807 *: "ëª©ë¡ ê°€ì† ì‹œìž‘ 지연"
2808 wheel_acceleration: none
2809 </dest>
2810 <voice>
2811 *: "ëª©ë¡ ê°€ì† ì‹œìž‘ 지연"
2812 wheel_acceleration: none
2813 </voice>
2814</phrase>
2815<phrase>
2816 id: LANG_LISTACCEL_ACCEL_SPEED
2817 desc: list acceleration speed
2818 user: core
2819 <source>
2820 *: "List Acceleration Speed"
2821 wheel_acceleration: none
2822 </source>
2823 <dest>
2824 *: "ëª©ë¡ ê°€ì† ì†ë„"
2825 wheel_acceleration: none
2826 </dest>
2827 <voice>
2828 *: "ëª©ë¡ ê°€ì† ì†ë„"
2829 wheel_acceleration: none
1718 </voice> 2830 </voice>
1719</phrase> 2831</phrase>
1720<phrase> 2832<phrase>
@@ -1725,10 +2837,94 @@
1725 *: "Status-/Scrollbar" 2837 *: "Status-/Scrollbar"
1726 </source> 2838 </source>
1727 <dest> 2839 <dest>
1728 *: "ìƒíƒœ/스í¬ë¡¤ ë°”" 2840 *: "ìƒíƒœ-/스í¬ë¡¤ë°”"
2841 </dest>
2842 <voice>
2843 *: "ìƒíƒœ-와 스í¬ë¡¤ë°”"
2844 </voice>
2845</phrase>
2846<phrase>
2847 id: LANG_SCROLL_BAR
2848 desc: display menu, F3 substitute
2849 user: core
2850 <source>
2851 *: "Scroll Bar"
2852 </source>
2853 <dest>
2854 *: "스í¬ë¡¤ë°”"
2855 </dest>
2856 <voice>
2857 *: "스í¬ë¡¤ë°”"
2858 </voice>
2859</phrase>
2860<phrase>
2861 id: LANG_STATUS_BAR
2862 desc: display menu, F3 substitute
2863 user: core
2864 <source>
2865 *: "Status Bar"
2866 </source>
2867 <dest>
2868 *: "ìƒíƒœë°”"
2869 </dest>
2870 <voice>
2871 *: "ìƒíƒœë°”"
2872 </voice>
2873</phrase>
2874<phrase>
2875 id: LANG_VOLUME_DISPLAY
2876 desc: Volume type title
2877 user: core
2878 <source>
2879 *: "Volume Display"
2880 </source>
2881 <dest>
2882 *: "볼륨 표시"
2883 </dest>
2884 <voice>
2885 *: "볼륨 표시"
2886 </voice>
2887</phrase>
2888<phrase>
2889 id: LANG_BATTERY_DISPLAY
2890 desc: Battery type title
2891 user: core
2892 <source>
2893 *: "Battery Display"
2894 </source>
2895 <dest>
2896 *: "배터리 표시"
2897 </dest>
2898 <voice>
2899 *: "배터리 표시"
2900 </voice>
2901</phrase>
2902<phrase>
2903 id: LANG_DISPLAY_GRAPHIC
2904 desc: Label for type of icon display
2905 user: core
2906 <source>
2907 *: "Graphic"
2908 </source>
2909 <dest>
2910 *: "그래픽"
2911 </dest>
2912 <voice>
2913 *: "그래픽"
2914 </voice>
2915</phrase>
2916<phrase>
2917 id: LANG_DISPLAY_NUMERIC
2918 desc: Label for type of icon display
2919 user: core
2920 <source>
2921 *: "Numeric"
2922 </source>
2923 <dest>
2924 *: "숫ìž"
1729 </dest> 2925 </dest>
1730 <voice> 2926 <voice>
1731 *: "ìƒíƒœ/스í¬ë¡¤ ë°”" 2927 *: "숫ìž"
1732 </voice> 2928 </voice>
1733</phrase> 2929</phrase>
1734<phrase> 2930<phrase>
@@ -1737,15 +2933,138 @@
1737 user: core 2933 user: core
1738 <source> 2934 <source>
1739 *: "Peak Meter" 2935 *: "Peak Meter"
1740 masd: none
1741 </source> 2936 </source>
1742 <dest> 2937 <dest>
1743 *: "í”¼í¬ ë¯¸í„° 설정" 2938 *: "í”¼í¬ ë¯¸í„°"
1744 masd: none 2939 </dest>
2940 <voice>
2941 *: "í”¼í¬ ë¯¸í„°"
2942 </voice>
2943</phrase>
2944<phrase>
2945 id: LANG_PM_CLIP_HOLD
2946 desc: in the peak meter menu
2947 user: core
2948 <source>
2949 *: "Clip Hold Time"
2950 </source>
2951 <dest>
2952 *: "í´ë¦½ 유지 시간"
2953 </dest>
2954 <voice>
2955 *: "í´ë¦½ 유지 시간"
2956 </voice>
2957</phrase>
2958<phrase>
2959 id: LANG_PM_PEAK_HOLD
2960 desc: in the peak meter menu
2961 user: core
2962 <source>
2963 *: "Peak Hold Time"
2964 </source>
2965 <dest>
2966 *: "í”¼í¬ ìœ ì§€ 시간"
1745 </dest> 2967 </dest>
1746 <voice> 2968 <voice>
1747 *: "í”¼í¬ ë¯¸í„° 설정" 2969 *: "í”¼í¬ ìœ ì§€ 시간"
1748 masd: none 2970 </voice>
2971</phrase>
2972<phrase>
2973 id: LANG_PM_ETERNAL
2974 desc: in the peak meter menu
2975 user: core
2976 <source>
2977 *: "Eternal"
2978 </source>
2979 <dest>
2980 *: "ì´í„°ë„"
2981 </dest>
2982 <voice>
2983 *: "ì´í„°ë„"
2984 </voice>
2985</phrase>
2986<phrase>
2987 id: LANG_PM_RELEASE
2988 desc: in the peak meter menu
2989 user: core
2990 <source>
2991 *: "Peak Release"
2992 </source>
2993 <dest>
2994 *: "í”¼í¬ ë¦´ë¦¬ìŠ¤"
2995 </dest>
2996 <voice>
2997 *: "í”¼í¬ ë¦´ë¦¬ìŠ¤"
2998 </voice>
2999</phrase>
3000<phrase>
3001 id: LANG_PM_SCALE
3002 desc: in the peak meter menu
3003 user: core
3004 <source>
3005 *: "Scale"
3006 </source>
3007 <dest>
3008 *: "스케ì¼"
3009 </dest>
3010 <voice>
3011 *: "스케ì¼"
3012 </voice>
3013</phrase>
3014<phrase>
3015 id: LANG_PM_DBFS
3016 desc: in the peak meter menu
3017 user: core
3018 <source>
3019 *: "Logarithmic (dB)"
3020 </source>
3021 <dest>
3022 *: "대수 (dB)"
3023 </dest>
3024 <voice>
3025 *: "대수 ë°ì‹œë²¨"
3026 </voice>
3027</phrase>
3028<phrase>
3029 id: LANG_PM_LINEAR
3030 desc: in the peak meter menu
3031 user: core
3032 <source>
3033 *: "Linear (%)"
3034 </source>
3035 <dest>
3036 *: "선형 (%)"
3037 </dest>
3038 <voice>
3039 *: "선형 í¼ì„¼íŠ¸"
3040 </voice>
3041</phrase>
3042<phrase>
3043 id: LANG_PM_MIN
3044 desc: in the peak meter menu
3045 user: core
3046 <source>
3047 *: "Minimum Of Range"
3048 </source>
3049 <dest>
3050 *: "최소 범위"
3051 </dest>
3052 <voice>
3053 *: "최소 범위"
3054 </voice>
3055</phrase>
3056<phrase>
3057 id: LANG_PM_MAX
3058 desc: in the peak meter menu
3059 user: core
3060 <source>
3061 *: "Maximum Of Range"
3062 </source>
3063 <dest>
3064 *: "최대 범위"
3065 </dest>
3066 <voice>
3067 *: "최대 범위"
1749 </voice> 3068 </voice>
1750</phrase> 3069</phrase>
1751<phrase> 3070<phrase>
@@ -1770,10 +3089,10 @@
1770 *: "Latin1 (ISO-8859-1)" 3089 *: "Latin1 (ISO-8859-1)"
1771 </source> 3090 </source>
1772 <dest> 3091 <dest>
1773 *: "ë¼í‹´ì–´ (ISO-8859-1)" 3092 *: "ë¼í‹´ì–´1 (ISO-8859-1)"
1774 </dest> 3093 </dest>
1775 <voice> 3094 <voice>
1776 *: "ë¼í‹´ì–´" 3095 *: "ë¼í‹´ì–´ 1"
1777 </voice> 3096 </voice>
1778</phrase> 3097</phrase>
1779<phrase> 3098<phrase>
@@ -1812,10 +3131,10 @@
1812 *: "Cyrillic (CP1251)" 3131 *: "Cyrillic (CP1251)"
1813 </source> 3132 </source>
1814 <dest> 3133 <dest>
1815 *: "í‚¤ë¦´ë¬¸ìž (CP1251)" 3134 *: "키릴어 (CP1251)"
1816 </dest> 3135 </dest>
1817 <voice> 3136 <voice>
1818 *: "키릴문ìž" 3137 *: "키릴어"
1819 </voice> 3138 </voice>
1820</phrase> 3139</phrase>
1821<phrase> 3140<phrase>
@@ -1826,10 +3145,10 @@
1826 *: "Thai (ISO-8859-11)" 3145 *: "Thai (ISO-8859-11)"
1827 </source> 3146 </source>
1828 <dest> 3147 <dest>
1829 *: "태국어 (ISO-8859-11)" 3148 *: "íƒì´ì–´ (ISO-8859-11)"
1830 </dest> 3149 </dest>
1831 <voice> 3150 <voice>
1832 *: "태국어" 3151 *: "íƒì´ì–´"
1833 </voice> 3152 </voice>
1834</phrase> 3153</phrase>
1835<phrase> 3154<phrase>
@@ -1854,10 +3173,10 @@
1854 *: "Turkish (ISO-8859-9)" 3173 *: "Turkish (ISO-8859-9)"
1855 </source> 3174 </source>
1856 <dest> 3175 <dest>
1857 *: "터키어 (ISO-8859-9)" 3176 *: "튀르키예어 (ISO-8859-9)"
1858 </dest> 3177 </dest>
1859 <voice> 3178 <voice>
1860 *: "터키어" 3179 *: "튀르키예어"
1861 </voice> 3180 </voice>
1862</phrase> 3181</phrase>
1863<phrase> 3182<phrase>
@@ -1868,10 +3187,10 @@
1868 *: "Latin Extended (ISO-8859-2)" 3187 *: "Latin Extended (ISO-8859-2)"
1869 </source> 3188 </source>
1870 <dest> 3189 <dest>
1871 *: "추가 ë¼í‹´ì–´ (ISO-8859-2)" 3190 *: "ë¼í‹´ì–´ 확장 (ISO-8859-2)"
1872 </dest> 3191 </dest>
1873 <voice> 3192 <voice>
1874 *: "추가 ë¼í‹´ì–´" 3193 *: "ë¼í‹´ì–´ 확장"
1875 </voice> 3194 </voice>
1876</phrase> 3195</phrase>
1877<phrase> 3196<phrase>
@@ -1896,10 +3215,10 @@
1896 *: "Simp. Chinese (GB2312)" 3215 *: "Simp. Chinese (GB2312)"
1897 </source> 3216 </source>
1898 <dest> 3217 <dest>
1899 *: "중국어 간체 (GB2312)" 3218 *: "간체 중국어 (GB2312)"
1900 </dest> 3219 </dest>
1901 <voice> 3220 <voice>
1902 *: "중국어 간체" 3221 *: "간체 중국어"
1903 </voice> 3222 </voice>
1904</phrase> 3223</phrase>
1905<phrase> 3224<phrase>
@@ -1913,7 +3232,7 @@
1913 *: "한국어 (KSX1001)" 3232 *: "한국어 (KSX1001)"
1914 </dest> 3233 </dest>
1915 <voice> 3234 <voice>
1916 *: "한국어" 3235 *: "항국어"
1917 </voice> 3236 </voice>
1918</phrase> 3237</phrase>
1919<phrase> 3238<phrase>
@@ -1924,10 +3243,10 @@
1924 *: "Trad. Chinese (BIG5)" 3243 *: "Trad. Chinese (BIG5)"
1925 </source> 3244 </source>
1926 <dest> 3245 <dest>
1927 *: "중국어 번체 (BIG5)" 3246 *: "번체 중국어 (BIG5)"
1928 </dest> 3247 </dest>
1929 <voice> 3248 <voice>
1930 *: "중국어 번체" 3249 *: "번체 중국어"
1931 </voice> 3250 </voice>
1932</phrase> 3251</phrase>
1933<phrase> 3252<phrase>
@@ -1945,6 +3264,85 @@
1945 </voice> 3264 </voice>
1946</phrase> 3265</phrase>
1947<phrase> 3266<phrase>
3267 id: LANG_BUTTONLIGHT_TIMEOUT
3268 desc: in settings_menu
3269 user: core
3270 <source>
3271 *: none
3272 button_light: "Button Light Timeout"
3273 sansae200*,sansafuze*: "Wheel Light Timeout"
3274 </source>
3275 <dest>
3276 *: none
3277 button_light: "버튼 ë¼ì´íŠ¸ 시간 초과"
3278 sansae200*,sansafuze*: "휠 ë¼ì´íŠ¸ 시간 초과"
3279 </dest>
3280 <voice>
3281 *: none
3282 button_light: "버튼 ë¼ì´íŠ¸ 시간 초과"
3283 sansae200*,sansafuze*: "휠 ë¼ì´íŠ¸ 시간 초과"
3284 </voice>
3285</phrase>
3286<phrase>
3287 id: LANG_BUTTONLIGHT_BRIGHTNESS
3288 desc: in settings_menu
3289 user: core
3290 <source>
3291 *: none
3292 buttonlight_brightness: "Button Light Brightness"
3293 </source>
3294 <dest>
3295 *: none
3296 buttonlight_brightness: "버튼 ë¼ì´íŠ¸ ë°ê¸°"
3297 </dest>
3298 <voice>
3299 *: none
3300 buttonlight_brightness: "버튼 ë¼ì´íŠ¸ ë°ê¸°"
3301 </voice>
3302</phrase>
3303<phrase>
3304 id: LANG_START_SCREEN
3305 desc: in the system sub menu
3306 user: core
3307 <source>
3308 *: "Start Screen"
3309 </source>
3310 <dest>
3311 *: "시작 화면"
3312 </dest>
3313 <voice>
3314 *: "시작 화면"
3315 </voice>
3316</phrase>
3317<phrase>
3318 id: LANG_MAIN_MENU
3319 desc: in start screen setting
3320 user: core
3321 <source>
3322 *: "Main Menu"
3323 </source>
3324 <dest>
3325 *: "ë©”ì¸ ë©”ë‰´"
3326 </dest>
3327 <voice>
3328 *: "ë©”ì¸ ë©”ë‰´"
3329 </voice>
3330</phrase>
3331<phrase>
3332 id: LANG_PREVIOUS_SCREEN
3333 desc: in start screen setting
3334 user: core
3335 <source>
3336 *: "Previous Screen"
3337 </source>
3338 <dest>
3339 *: "ì´ì „ 화면"
3340 </dest>
3341 <voice>
3342 *: "ì´ì „ 화면"
3343 </voice>
3344</phrase>
3345<phrase>
1948 id: LANG_BATTERY_MENU 3346 id: LANG_BATTERY_MENU
1949 desc: in the system sub menu 3347 desc: in the system sub menu
1950 user: core 3348 user: core
@@ -1952,10 +3350,81 @@
1952 *: "Battery" 3350 *: "Battery"
1953 </source> 3351 </source>
1954 <dest> 3352 <dest>
1955 *: "배터리 설정" 3353 *: "배터리"
3354 </dest>
3355 <voice>
3356 *: "배터리"
3357 </voice>
3358</phrase>
3359<phrase>
3360 id: LANG_BATTERY_CAPACITY
3361 desc: in settings_menu
3362 user: core
3363 <source>
3364 *: "Battery Capacity"
3365 </source>
3366 <dest>
3367 *: "배터리 용량"
3368 </dest>
3369 <voice>
3370 *: "배터리 용량"
3371 </voice>
3372</phrase>
3373<phrase>
3374 id: LANG_BATTERY_TYPE
3375 desc: in battery settings
3376 user: core
3377 <source>
3378 *: none
3379 battery_types: "Battery Type"
3380 </source>
3381 <dest>
3382 *: none
3383 battery_types: "배터리 유형"
3384 </dest>
3385 <voice>
3386 *: none
3387 battery_types: "배터리 유형"
3388 </voice>
3389</phrase>
3390<phrase>
3391 id: LANG_BATTERY_TYPE_1
3392 desc: in battery settings
3393 user: core
3394 <source>
3395 *: none
3396 battery_types: "Alkaline"
3397 xduoox3: "Newer (2000 mAh)"
3398 </source>
3399 <dest>
3400 *: none
3401 battery_types: "알카ë¼ì¸"
3402 xduoox3: "최신 (2000 mAh)"
3403 </dest>
3404 <voice>
3405 *: none
3406 battery_types: "알카ë¼ì¸"
3407 xduoox3: "최신 2000 밀리암페어 시간"
3408 </voice>
3409</phrase>
3410<phrase>
3411 id: LANG_BATTERY_TYPE_2
3412 desc: in battery settings
3413 user: core
3414 <source>
3415 *: none
3416 battery_types: "NiMH"
3417 xduoox3: "Older (1500 mAh)"
3418 </source>
3419 <dest>
3420 *: none
3421 battery_types: "~NiMH"
3422 xduoox3: "ì˜¤ëž˜ë¨ (1500 mAh)"
1956 </dest> 3423 </dest>
1957 <voice> 3424 <voice>
1958 *: "배터리 설정" 3425 *: none
3426 battery_types: "니켈 수소"
3427 xduoox3: "ì˜¤ëž˜ë¨ 1500 밀리암페어 시간"
1959 </voice> 3428 </voice>
1960</phrase> 3429</phrase>
1961<phrase> 3430<phrase>
@@ -1966,10 +3435,44 @@
1966 *: "Disk" 3435 *: "Disk"
1967 </source> 3436 </source>
1968 <dest> 3437 <dest>
1969 *: "ë””ìŠ¤í¬ ì„¤ì •" 3438 *: "디스í¬"
3439 </dest>
3440 <voice>
3441 *: "디스í¬"
3442 </voice>
3443</phrase>
3444<phrase>
3445 id: LANG_SPINDOWN
3446 desc: in settings_menu
3447 user: core
3448 <source>
3449 *: "Disk Spindown"
3450 flash_storage: none
3451 </source>
3452 <dest>
3453 *: "ë””ìŠ¤í¬ ìŠ¤í•€ë‹¤ìš´"
3454 flash_storage: none
3455 </dest>
3456 <voice>
3457 *: "ë””ìŠ¤í¬ ìŠ¤í•€ë‹¤ìš´"
3458 flash_storage: none
3459 </voice>
3460</phrase>
3461<phrase>
3462 id: LANG_DIRCACHE_ENABLE
3463 desc: in directory cache settings
3464 user: core
3465 <source>
3466 *: none
3467 dircache: "Directory Cache"
3468 </source>
3469 <dest>
3470 *: none
3471 dircache: "디렉토리 ìºì‹œ"
1970 </dest> 3472 </dest>
1971 <voice> 3473 <voice>
1972 *: "ë””ìŠ¤í¬ ì„¤ì •" 3474 *: none
3475 dircache: "디렉토리 ìºì‹œ"
1973 </voice> 3476 </voice>
1974</phrase> 3477</phrase>
1975<phrase> 3478<phrase>
@@ -1986,7 +3489,419 @@
1986 </dest> 3489 </dest>
1987 <voice> 3490 <voice>
1988 *: none 3491 *: none
1989 rtc: "시간 & 날짜" 3492 rtc: "시간과 날짜"
3493 </voice>
3494</phrase>
3495<phrase>
3496 id: LANG_SET_TIME
3497 desc: in settings_menu
3498 user: core
3499 <source>
3500 *: none
3501 rtc: "Set Time/Date"
3502 </source>
3503 <dest>
3504 *: none
3505 rtc: "시간/날짜 설정"
3506 </dest>
3507 <voice>
3508 *: none
3509 rtc: "시간과 날짜 설정"
3510 </voice>
3511</phrase>
3512<phrase>
3513 id: LANG_TIMEFORMAT
3514 desc: select the time format of time in status bar
3515 user: core
3516 <source>
3517 *: "Time Format"
3518 </source>
3519 <dest>
3520 *: "시간 형ì‹"
3521 </dest>
3522 <voice>
3523 *: "시간 형ì‹"
3524 </voice>
3525</phrase>
3526<phrase>
3527 id: LANG_12_HOUR_CLOCK
3528 desc: option for 12 hour clock
3529 user: core
3530 <source>
3531 *: "12 Hour Clock"
3532 </source>
3533 <dest>
3534 *: "12 시간"
3535 </dest>
3536 <voice>
3537 *: "12 시간"
3538 </voice>
3539</phrase>
3540<phrase>
3541 id: LANG_24_HOUR_CLOCK
3542 desc: option for 24 hour clock
3543 user: core
3544 <source>
3545 *: "24 Hour Clock"
3546 </source>
3547 <dest>
3548 *: "24 시간"
3549 </dest>
3550 <voice>
3551 *: "24 시간"
3552 </voice>
3553</phrase>
3554<phrase>
3555 id: LANG_TIME_SET_BUTTON
3556 desc: used in set_time()
3557 user: core
3558 <source>
3559 *: none
3560 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Set"
3561 gogearsa9200,samsungyh*: "PLAY = Set"
3562 iriverh100,iriverh120,iriverh300: "NAVI = Set"
3563 mpiohd300: "ENTER = Set"
3564 mrobe500: "HEART = Set"
3565 rtc: "ON = Set"
3566 vibe500: "OK = Set"
3567 </source>
3568 <dest>
3569 *: none
3570 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "ì„ íƒ ë²„íŠ¼ = 설정"
3571 gogearsa9200,samsungyh*: "ìž¬ìƒ ë²„íŠ¼ = 설정"
3572 iriverh100,iriverh120,iriverh300: "íƒìƒ‰ 버튼 = 설정"
3573 mpiohd300: "진입 버튼 = 설정"
3574 mrobe500: "하트 버튼 = 설정"
3575 rtc: "켬 버튼 = 설정"
3576 vibe500: "í™•ì¸ ë²„íŠ¼ = 설정"
3577 </dest>
3578 <voice>
3579 *: none
3580 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
3581 </voice>
3582</phrase>
3583<phrase>
3584 id: LANG_TIME_REVERT
3585 desc: used in set_time()
3586 user: core
3587 <source>
3588 *: none
3589 gigabeatfx,mrobe500: "POWER = Revert"
3590 gigabeats,sansafuzeplus: "BACK = Revert"
3591 gogearsa9200: "LEFT = Revert"
3592 iaudiom5,iaudiox5: "RECORD = Revert"
3593 ipod*,mpiohd300,sansac200*: "MENU = Revert"
3594 iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
3595 iriverh100,iriverh120,iriverh300: "STOP = Revert"
3596 mrobe100: "DISPLAY = Revert"
3597 rtc: "OFF = Revert"
3598 samsungyh*: "REW = Revert"
3599 vibe500: "CANCEL = Revert"
3600 </source>
3601 <dest>
3602 *: none
3603 gigabeatfx,mrobe500: "ì „ì› ë²„í„°ëŠ = 반복"
3604 gigabeats,sansafuzeplus: "뒤로가기 버튼 = 반복"
3605 gogearsa9200: "왼쪽 버튼 = 반복"
3606 iaudiom5,iaudiox5: "ë…¹ìŒ ë²„íŠ¼ = 반복"
3607 ipod*,mpiohd300,sansac200*: "메뉴 버튼 = 반복"
3608 iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "ì´ì „ 버튼 = 반복"
3609 iriverh100,iriverh120,iriverh300: "정지 버튼 = 반복"
3610 mrobe100: "화면 버튼 = 반복"
3611 rtc: "ë” ë²„íŠ¼ = 반복"
3612 samsungyh*: "ë˜ê°ê¸° 버튼 = 반복"
3613 vibe500: "취소 버튼 = 반복"
3614 </dest>
3615 <voice>
3616 *: none
3617 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
3618 </voice>
3619</phrase>
3620<phrase>
3621 id: LANG_WEEKDAY_SUNDAY
3622 desc: Maximum 3-letter abbreviation for weekday
3623 user: core
3624 <source>
3625 *: none
3626 rtc: "Sun"
3627 </source>
3628 <dest>
3629 *: none
3630 rtc: "ì¼"
3631 </dest>
3632 <voice>
3633 *: none
3634 rtc: ""
3635 </voice>
3636</phrase>
3637<phrase>
3638 id: LANG_WEEKDAY_MONDAY
3639 desc: Maximum 3-letter abbreviation for weekday
3640 user: core
3641 <source>
3642 *: none
3643 rtc: "Mon"
3644 </source>
3645 <dest>
3646 *: none
3647 rtc: "ì›”"
3648 </dest>
3649 <voice>
3650 *: none
3651 rtc: ""
3652 </voice>
3653</phrase>
3654<phrase>
3655 id: LANG_WEEKDAY_TUESDAY
3656 desc: Maximum 3-letter abbreviation for weekday
3657 user: core
3658 <source>
3659 *: none
3660 rtc: "Tue"
3661 </source>
3662 <dest>
3663 *: none
3664 rtc: "í™”"
3665 </dest>
3666 <voice>
3667 *: none
3668 rtc: ""
3669 </voice>
3670</phrase>
3671<phrase>
3672 id: LANG_WEEKDAY_WEDNESDAY
3673 desc: Maximum 3-letter abbreviation for weekday
3674 user: core
3675 <source>
3676 *: none
3677 rtc: "Wed"
3678 </source>
3679 <dest>
3680 *: none
3681 rtc: "수"
3682 </dest>
3683 <voice>
3684 *: none
3685 rtc: ""
3686 </voice>
3687</phrase>
3688<phrase>
3689 id: LANG_WEEKDAY_THURSDAY
3690 desc: Maximum 3-letter abbreviation for weekday
3691 user: core
3692 <source>
3693 *: none
3694 rtc: "Thu"
3695 </source>
3696 <dest>
3697 *: none
3698 rtc: "목"
3699 </dest>
3700 <voice>
3701 *: none
3702 rtc: ""
3703 </voice>
3704</phrase>
3705<phrase>
3706 id: LANG_WEEKDAY_FRIDAY
3707 desc: Maximum 3-letter abbreviation for weekday
3708 user: core
3709 <source>
3710 *: none
3711 rtc: "Fri"
3712 </source>
3713 <dest>
3714 *: none
3715 rtc: "금"
3716 </dest>
3717 <voice>
3718 *: none
3719 rtc: ""
3720 </voice>
3721</phrase>
3722<phrase>
3723 id: LANG_WEEKDAY_SATURDAY
3724 desc: Maximum 3-letter abbreviation for weekday
3725 user: core
3726 <source>
3727 *: none
3728 rtc: "Sat"
3729 </source>
3730 <dest>
3731 *: none
3732 rtc: "토"
3733 </dest>
3734 <voice>
3735 *: none
3736 rtc: ""
3737 </voice>
3738</phrase>
3739<phrase>
3740 id: LANG_MONTH_JANUARY
3741 desc: Maximum 3-letter abbreviation for monthname
3742 user: core
3743 <source>
3744 *: "Jan"
3745 </source>
3746 <dest>
3747 *: "1ì›”"
3748 </dest>
3749 <voice>
3750 *: "ì¼ì›”"
3751 </voice>
3752</phrase>
3753<phrase>
3754 id: LANG_MONTH_FEBRUARY
3755 desc: Maximum 3-letter abbreviation for monthname
3756 user: core
3757 <source>
3758 *: "Feb"
3759 </source>
3760 <dest>
3761 *: "2ì›”"
3762 </dest>
3763 <voice>
3764 *: "ì´ì›”"
3765 </voice>
3766</phrase>
3767<phrase>
3768 id: LANG_MONTH_MARCH
3769 desc: Maximum 3-letter abbreviation for monthname
3770 user: core
3771 <source>
3772 *: "Mar"
3773 </source>
3774 <dest>
3775 *: "3ì›”"
3776 </dest>
3777 <voice>
3778 *: "삼월"
3779 </voice>
3780</phrase>
3781<phrase>
3782 id: LANG_MONTH_APRIL
3783 desc: Maximum 3-letter abbreviation for monthname
3784 user: core
3785 <source>
3786 *: "Apr"
3787 </source>
3788 <dest>
3789 *: "4ì›”"
3790 </dest>
3791 <voice>
3792 *: "사월"
3793 </voice>
3794</phrase>
3795<phrase>
3796 id: LANG_MONTH_MAY
3797 desc: Maximum 3-letter abbreviation for monthname
3798 user: core
3799 <source>
3800 *: "May"
3801 </source>
3802 <dest>
3803 *: "5ì›”"
3804 </dest>
3805 <voice>
3806 *: "오월"
3807 </voice>
3808</phrase>
3809<phrase>
3810 id: LANG_MONTH_JUNE
3811 desc: Maximum 3-letter abbreviation for monthname
3812 user: core
3813 <source>
3814 *: "Jun"
3815 </source>
3816 <dest>
3817 *: "6ì›”"
3818 </dest>
3819 <voice>
3820 *: "유월"
3821 </voice>
3822</phrase>
3823<phrase>
3824 id: LANG_MONTH_JULY
3825 desc: Maximum 3-letter abbreviation for monthname
3826 user: core
3827 <source>
3828 *: "Jul"
3829 </source>
3830 <dest>
3831 *: "7ì›”"
3832 </dest>
3833 <voice>
3834 *: "ì¹ ì›”"
3835 </voice>
3836</phrase>
3837<phrase>
3838 id: LANG_MONTH_AUGUST
3839 desc: Maximum 3-letter abbreviation for monthname
3840 user: core
3841 <source>
3842 *: "Aug"
3843 </source>
3844 <dest>
3845 *: "8ì›”"
3846 </dest>
3847 <voice>
3848 *: "팔월"
3849 </voice>
3850</phrase>
3851<phrase>
3852 id: LANG_MONTH_SEPTEMBER
3853 desc: Maximum 3-letter abbreviation for monthname
3854 user: core
3855 <source>
3856 *: "Sep"
3857 </source>
3858 <dest>
3859 *: "9ì›”"
3860 </dest>
3861 <voice>
3862 *: "구월"
3863 </voice>
3864</phrase>
3865<phrase>
3866 id: LANG_MONTH_OCTOBER
3867 desc: Maximum 3-letter abbreviation for monthname
3868 user: core
3869 <source>
3870 *: "Oct"
3871 </source>
3872 <dest>
3873 *: "10ì›”"
3874 </dest>
3875 <voice>
3876 *: "시월"
3877 </voice>
3878</phrase>
3879<phrase>
3880 id: LANG_MONTH_NOVEMBER
3881 desc: Maximum 3-letter abbreviation for monthname
3882 user: core
3883 <source>
3884 *: "Nov"
3885 </source>
3886 <dest>
3887 *: "11ì›”"
3888 </dest>
3889 <voice>
3890 *: "ì‹­ì¼ì›”"
3891 </voice>
3892</phrase>
3893<phrase>
3894 id: LANG_MONTH_DECEMBER
3895 desc: Maximum 3-letter abbreviation for monthname
3896 user: core
3897 <source>
3898 *: "Dec"
3899 </source>
3900 <dest>
3901 *: "12ì›”"
3902 </dest>
3903 <voice>
3904 *: "ì‹­ì´ì›”"
1990 </voice> 3905 </voice>
1991</phrase> 3906</phrase>
1992<phrase> 3907<phrase>
@@ -1997,10 +3912,10 @@
1997 *: "Idle Poweroff" 3912 *: "Idle Poweroff"
1998 </source> 3913 </source>
1999 <dest> 3914 <dest>
2000 *: "대기ìœê°„ 설정" 3915 *: "유휴 ì „ì› ë„기"
2001 </dest> 3916 </dest>
2002 <voice> 3917 <voice>
2003 *: "대기ìœê°„ 설정" 3918 *: "유휴 ì „ì› ë„기"
2004 </voice> 3919 </voice>
2005</phrase> 3920</phrase>
2006<phrase> 3921<phrase>
@@ -2011,10 +3926,69 @@
2011 *: "Sleep Timer" 3926 *: "Sleep Timer"
2012 </source> 3927 </source>
2013 <dest> 3928 <dest>
2014 *: "취침 예약" 3929 *: "절전 타ì´ë¨¸"
2015 </dest> 3930 </dest>
2016 <voice> 3931 <voice>
2017 *: "취침 예약" 3932 *: "절전 타ì´ë¨¸"
3933 </voice>
3934</phrase>
3935<phrase>
3936 id: LANG_LIMITS_MENU
3937 desc: in the system sub menu
3938 user: core
3939 <source>
3940 *: "Limits"
3941 </source>
3942 <dest>
3943 *: "제한"
3944 </dest>
3945 <voice>
3946 *: "제한"
3947 </voice>
3948</phrase>
3949<phrase>
3950 id: LANG_MAX_FILES_IN_DIR
3951 desc: in settings_menu
3952 user: core
3953 <source>
3954 *: "Max Entries in File Browser"
3955 </source>
3956 <dest>
3957 *: "íŒŒì¼ ë¸Œë¼ìš°ì €ì˜ 최대 항목"
3958 </dest>
3959 <voice>
3960 *: "íŒŒì¼ ë¸Œë¼ìš°ì €ì˜ 최대 항목"
3961 </voice>
3962</phrase>
3963<phrase>
3964 id: LANG_MAX_FILES_IN_PLAYLIST
3965 desc: in settings_menu
3966 user: core
3967 <source>
3968 *: "Max Playlist Size"
3969 </source>
3970 <dest>
3971 *: "최대 ìž¬ìƒ ëª©ë¡ í¬ê¸°"
3972 </dest>
3973 <voice>
3974 *: "최대 ìž¬ìƒ ëª©ë¡ í¬ê¸°"
3975 </voice>
3976</phrase>
3977<phrase>
3978 id: LANG_CAR_ADAPTER_MODE
3979 desc: Displayed for setting car adapter mode to on/off
3980 user: core
3981 <source>
3982 *: none
3983 charging: "Car Adapter Mode"
3984 </source>
3985 <dest>
3986 *: none
3987 charging: "차량용 어댑터 모드"
3988 </dest>
3989 <voice>
3990 *: none
3991 charging: "차량용 어댑터 모드"
2018 </voice> 3992 </voice>
2019</phrase> 3993</phrase>
2020<phrase> 3994<phrase>
@@ -2027,59 +4001,110 @@
2027 </source> 4001 </source>
2028 <dest> 4002 <dest>
2029 *: none 4003 *: none
2030 alarm: "켜지는 ì‹œê°ì„¤ì •" 4004 alarm: "ê¸°ìƒ ì•ŒëžŒ"
2031 </dest> 4005 </dest>
2032 <voice> 4006 <voice>
2033 *: none 4007 *: none
2034 alarm: "켜지는 ì‹œê°ì„¤ì •" 4008 alarm: "ê¸°ìƒ ì•ŒëžŒ"
2035 </voice> 4009 </voice>
2036</phrase> 4010</phrase>
2037<phrase> 4011<phrase>
2038 id: LANG_LIMITS_MENU 4012 id: LANG_ALARM_WAKEUP_SCREEN
2039 desc: in the system sub menu 4013 desc: in alarm menu setting
2040 user: core 4014 user: core
2041 <source> 4015 <source>
2042 *: "Limits" 4016 *: none
4017 alarm: "Alarm Wake up Screen"
2043 </source> 4018 </source>
2044 <dest> 4019 <dest>
2045 *: "제한 설정" 4020 *: none
4021 alarm: "알람 ê¸°ìƒ í™”ë©´"
2046 </dest> 4022 </dest>
2047 <voice> 4023 <voice>
2048 *: "제한 설정" 4024 *: none
4025 alarm: "알람 ê¸°ìƒ í™”ë©´"
2049 </voice> 4026 </voice>
2050</phrase> 4027</phrase>
2051<phrase> 4028<phrase>
2052 id: LANG_LINE_IN 4029 id: LANG_ALARM_MOD_TIME
2053 desc: in the recording settings 4030 desc: The current alarm time shown in the alarm menu for the RTC alarm mod.
2054 user: core 4031 user: core
2055 <source> 4032 <source>
2056 *: none 4033 *: none
2057 recording: "Line In" 4034 alarm: "Alarm Time:"
2058 </source> 4035 </source>
2059 <dest> 4036 <dest>
2060 *: none 4037 *: none
2061 recording: "ë¼ì¸ ì¸" 4038 alarm: "알람 시간:"
2062 </dest> 4039 </dest>
2063 <voice> 4040 <voice>
2064 *: none 4041 *: none
2065 recording: "ë¼ì¸ ì¸" 4042 alarm: ""
2066 </voice> 4043 </voice>
2067</phrase> 4044</phrase>
2068<phrase> 4045<phrase>
2069 id: LANG_CAR_ADAPTER_MODE 4046 id: LANG_ALARM_MOD_TIME_TO_GO
2070 desc: Displayed for setting car adapter mode to on/off 4047 desc: The time until the alarm will go off shown in the alarm menu for the RTC alarm mod.
2071 user: core 4048 user: core
2072 <source> 4049 <source>
2073 *: none 4050 *: none
2074 charging: "Car Adapter Mode" 4051 alarm: "Waking Up In %d:%02d"
4052 </source>
4053 <dest>
4054 *: none
4055 alarm: "%d:%02dì— ê¹¨ì–´ë‚¨"
4056 </dest>
4057 <voice>
4058 *: none
4059 alarm: "ìž ì—ì„œ 깨어나기"
4060 </voice>
4061</phrase>
4062<phrase>
4063 id: LANG_ALARM_MOD_ERROR
4064 desc: The text that tells that the time is incorrect (for the RTC alarm mod).
4065 user: core
4066 <source>
4067 *: none
4068 alarm: "Alarm Time Is Too Soon!"
4069 </source>
4070 <dest>
4071 *: none
4072 alarm: "알람 ì‹œê°„ì´ ë„ˆë¬´ ì´ë¦…니다!"
4073 </dest>
4074 <voice>
4075 *: none
4076 alarm: "알람 ì‹œê°„ì´ ë„ˆë¬´ ì´ë¦…니다!"
4077 </voice>
4078</phrase>
4079<phrase>
4080 id: LANG_ALARM_MOD_DISABLE
4081 desc: Announce that the RTC alarm has been turned off
4082 user: core
4083 <source>
4084 *: none
4085 alarm: "Alarm Disabled"
2075 </source> 4086 </source>
2076 <dest> 4087 <dest>
2077 *: none 4088 *: none
2078 charging: "카 어댑터 모드" 4089 alarm: "알람 비활성화"
2079 </dest> 4090 </dest>
2080 <voice> 4091 <voice>
2081 *: none 4092 *: none
2082 charging: "카 어댑터 모드" 4093 alarm: "알람 비활성화"
4094 </voice>
4095</phrase>
4096<phrase>
4097 id: LANG_BOOKMARK_SETTINGS
4098 desc: in general settings
4099 user: core
4100 <source>
4101 *: "Bookmarking"
4102 </source>
4103 <dest>
4104 *: "ë¶ë§ˆí¬"
4105 </dest>
4106 <voice>
4107 *: "ë¶ë§ˆí¬"
2083 </voice> 4108 </voice>
2084</phrase> 4109</phrase>
2085<phrase> 4110<phrase>
@@ -2090,10 +4115,10 @@
2090 *: "Bookmark on Stop" 4115 *: "Bookmark on Stop"
2091 </source> 4116 </source>
2092 <dest> 4117 <dest>
2093 *: "정지시 ìžë™ ë¶ë§ˆí¬" 4118 *: "중지 ì‹œ ë¶ë§ˆí¬"
2094 </dest> 4119 </dest>
2095 <voice> 4120 <voice>
2096 *: "정지시 ìžë™ ë¶ë§ˆí¬" 4121 *: "중지 ì‹œ ë¶ë§ˆí¬"
2097 </voice> 4122 </voice>
2098</phrase> 4123</phrase>
2099<phrase> 4124<phrase>
@@ -2104,10 +4129,10 @@
2104 *: "Yes - Recent only" 4129 *: "Yes - Recent only"
2105 </source> 4130 </source>
2106 <dest> 4131 <dest>
2107 *: "ìµœê·¼ì˜ ë¶ë§ˆí¬ë§Œ 저장" 4132 *: "예 - 최근만"
2108 </dest> 4133 </dest>
2109 <voice> 4134 <voice>
2110 *: "ìµœê·¼ì˜ ë¶ë§ˆí¬ë§Œ 저장" 4135 *: "예 - 최근만"
2111 </voice> 4136 </voice>
2112</phrase> 4137</phrase>
2113<phrase> 4138<phrase>
@@ -2118,10 +4143,10 @@
2118 *: "Ask - Recent only" 4143 *: "Ask - Recent only"
2119 </source> 4144 </source>
2120 <dest> 4145 <dest>
2121 *: "ìµœê·¼ì˜ ë¶ë§ˆí¬ë§Œ 매번 확ì¸" 4146 *: "요청 - 최근만"
2122 </dest> 4147 </dest>
2123 <voice> 4148 <voice>
2124 *: "ìµœê·¼ì˜ ë¶ë§ˆí¬ë§Œ 매번 확ì¸" 4149 *: "요청 - 최근만"
2125 </voice> 4150 </voice>
2126</phrase> 4151</phrase>
2127<phrase> 4152<phrase>
@@ -2132,10 +4157,10 @@
2132 *: "Load Last Bookmark" 4157 *: "Load Last Bookmark"
2133 </source> 4158 </source>
2134 <dest> 4159 <dest>
2135 *: "최근 ë¶ë§ˆí¬ 열기" 4160 *: "마지막 ë¶ë§ˆí¬ 로드"
2136 </dest> 4161 </dest>
2137 <voice> 4162 <voice>
2138 *: "최근 ë¶ë§ˆí¬ 열기" 4163 *: "마지막 ë¶ë§ˆí¬ 로드"
2139 </voice> 4164 </voice>
2140</phrase> 4165</phrase>
2141<phrase> 4166<phrase>
@@ -2146,24 +4171,52 @@
2146 *: "Maintain a List of Recent Bookmarks?" 4171 *: "Maintain a List of Recent Bookmarks?"
2147 </source> 4172 </source>
2148 <dest> 4173 <dest>
2149 *: "최근 ë¶ë§ˆí¬ì˜ ëª©ë¡ ìœ ì§€" 4174 *: "최근 ë¶ë§ˆí¬ 목ë¡ì„ 유지할까요?"
4175 </dest>
4176 <voice>
4177 *: "최근 ë¶ë§ˆí¬ 목ë¡ì„ 유지할까요?"
4178 </voice>
4179</phrase>
4180<phrase>
4181 id: LANG_LANGUAGE
4182 desc: in settings_menu
4183 user: core
4184 <source>
4185 *: "Language"
4186 </source>
4187 <dest>
4188 *: "언어"
2150 </dest> 4189 </dest>
2151 <voice> 4190 <voice>
2152 *: "최근 ë¶ë§ˆí¬ì˜ ëª©ë¡ ìœ ì§€" 4191 *: "언어"
2153 </voice> 4192 </voice>
2154</phrase> 4193</phrase>
2155<phrase> 4194<phrase>
2156 id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY 4195 id: LANG_LANGUAGE_LOADED
2157 desc: Save only on bookmark for each playlist in recent bookmarks 4196 desc: shown when a language has been loaded from the dir browser
2158 user: core 4197 user: core
2159 <source> 4198 <source>
2160 *: "Unique only" 4199 *: "New Language"
2161 </source> 4200 </source>
2162 <dest> 4201 <dest>
2163 *: "특별한 것만" 4202 *: "새 언어"
2164 </dest> 4203 </dest>
2165 <voice> 4204 <voice>
2166 *: "특별한 것만" 4205 *: "새 언어"
4206 </voice>
4207</phrase>
4208<phrase>
4209 id: LANG_VOICE
4210 desc: root of voice menu
4211 user: core
4212 <source>
4213 *: "Voice"
4214 </source>
4215 <dest>
4216 *: "ìŒì„¬"
4217 </dest>
4218 <voice>
4219 *: "ìŒì„±"
2167 </voice> 4220 </voice>
2168</phrase> 4221</phrase>
2169<phrase> 4222<phrase>
@@ -2174,10 +4227,10 @@
2174 *: "Voice Menus" 4227 *: "Voice Menus"
2175 </source> 4228 </source>
2176 <dest> 4229 <dest>
2177 *: "ìŒì„± 안내 사용" 4230 *: "ìŒì„± 메뉴"
2178 </dest> 4231 </dest>
2179 <voice> 4232 <voice>
2180 *: "ìŒì„± 안내 사용" 4233 *: "ìŒì„± 메뉴"
2181 </voice> 4234 </voice>
2182</phrase> 4235</phrase>
2183<phrase> 4236<phrase>
@@ -2188,10 +4241,24 @@
2188 *: "Voice Directories" 4241 *: "Voice Directories"
2189 </source> 4242 </source>
2190 <dest> 4243 <dest>
2191 *: "í´ë” ì´ë¦„ 안내" 4244 *: "ìŒì„± 디렉토리"
4245 </dest>
4246 <voice>
4247 *: "ìŒì„± 디렉토리"
4248 </voice>
4249</phrase>
4250<phrase>
4251 id: LANG_VOICE_DIR_TALK
4252 desc: Item of voice menu, whether to use directory .talk clips
4253 user: core
4254 <source>
4255 *: "Use Directory .talk Clips"
4256 </source>
4257 <dest>
4258 *: "디렉토리 .talk í´ë¦½ 사용"
2192 </dest> 4259 </dest>
2193 <voice> 4260 <voice>
2194 *: "í´ëì´ë¦ 안ë´" 4261 *: "디렉토리 .talk í´ë¦½ 사용"
2195 </voice> 4262 </voice>
2196</phrase> 4263</phrase>
2197<phrase> 4264<phrase>
@@ -2202,10 +4269,24 @@
2202 *: "Voice Filenames" 4269 *: "Voice Filenames"
2203 </source> 4270 </source>
2204 <dest> 4271 <dest>
2205 *: "íŒŒì¼ ì´ë¦„ 안내" 4272 *: "ìŒì„± 파ì¼ì´ë¦„"
4273 </dest>
4274 <voice>
4275 *: "ìŒì„± 파ì¼ì´ë¦„"
4276 </voice>
4277</phrase>
4278<phrase>
4279 id: LANG_VOICE_FILE_TALK
4280 desc: Item of voice menu, whether to use file .talk clips
4281 user: core
4282 <source>
4283 *: "Use File .talk Clips"
4284 </source>
4285 <dest>
4286 *: "íŒŒì¼ .talk í´ë¦½ 사용"
2206 </dest> 4287 </dest>
2207 <voice> 4288 <voice>
2208 *: "íŒŒì¼ ì´ë¦ 안ë´" 4289 *: "íŒŒì¼ .talk í´ë¦½ 사용"
2209 </voice> 4290 </voice>
2210</phrase> 4291</phrase>
2211<phrase> 4292<phrase>
@@ -2216,10 +4297,10 @@
2216 *: "Numbers" 4297 *: "Numbers"
2217 </source> 4298 </source>
2218 <dest> 4299 <dest>
2219 *: "숫ìž" 4300 *: "번호"
2220 </dest> 4301 </dest>
2221 <voice> 4302 <voice>
2222 *: "숫ìž" 4303 *: "번호"
2223 </voice> 4304 </voice>
2224</phrase> 4305</phrase>
2225<phrase> 4306<phrase>
@@ -2230,10 +4311,10 @@
2230 *: "Spell" 4311 *: "Spell"
2231 </source> 4312 </source>
2232 <dest> 4313 <dest>
2233 *: "ì² ìž" 4314 *: "ì² ìž ë§í•˜ê¸°"
2234 </dest> 4315 </dest>
2235 <voice> 4316 <voice>
2236 *: "ì² ìž" 4317 *: "ì² ìž ë§í•˜ê¸°"
2237 </voice> 4318 </voice>
2238</phrase> 4319</phrase>
2239<phrase> 4320<phrase>
@@ -2241,1608 +4322,1842 @@
2241 desc: "talkbox" mode for directories + files 4322 desc: "talkbox" mode for directories + files
2242 user: core 4323 user: core
2243 <source> 4324 <source>
2244 *: ".talk mp3 clip" 4325 *: ".talk Clip"
2245 </source> 4326 </source>
2246 <dest> 4327 <dest>
2247 *: "MP3 íŒŒì¼ ì‚¬ìš©" 4328 *: ".talk í´ë¦½"
2248 </dest> 4329 </dest>
2249 <voice> 4330 <voice>
2250 *: "MP3 íŒŒì¼ ì‚¬ìš©" 4331 *: "í† í¬ í´ë¦½"
2251 </voice> 4332 </voice>
2252</phrase> 4333</phrase>
2253<phrase> 4334<phrase>
2254 id: LANG_RECORDING_QUALITY 4335 id: LANG_MANAGE_MENU
2255 desc: in the recording settings 4336 desc: in the main menu
4337 user: core
4338 <source>
4339 *: "Manage Settings"
4340 </source>
4341 <dest>
4342 *: "설정 관리"
4343 </dest>
4344 <voice>
4345 *: "설정 관리"
4346 </voice>
4347</phrase>
4348<phrase>
4349 id: LANG_CUSTOM_CFG
4350 desc: in setting_menu()
4351 user: core
4352 <source>
4353 *: "Browse .cfg Files"
4354 </source>
4355 <dest>
4356 *: ".cfg íŒŒì¼ ì°¾ì•„ë³´ê¸°"
4357 </dest>
4358 <voice>
4359 *: "구성 íŒŒì¼ ì°¾ì•„ë³´ê¸°"
4360 </voice>
4361</phrase>
4362<phrase>
4363 id: LANG_SETTINGS_LOADED
4364 desc: Feedback shown when a .cfg file is loaded
4365 user: core
4366 <source>
4367 *: "Settings Loaded"
4368 </source>
4369 <dest>
4370 *: "설정 로드ë¨"
4371 </dest>
4372 <voice>
4373 *: "설정 로드ë¨"
4374 </voice>
4375</phrase>
4376<phrase>
4377 id: LANG_RESET
4378 desc: in system_settings_menu()
4379 user: core
4380 <source>
4381 *: "Reset Settings"
4382 </source>
4383 <dest>
4384 *: "설정 초기화"
4385 </dest>
4386 <voice>
4387 *: "설정 초기화"
4388 </voice>
4389</phrase>
4390<phrase>
4391 id: LANG_RESET_DONE_CLEAR
4392 desc: visual confirmation after settings reset
4393 user: core
4394 <source>
4395 *: "Cleared"
4396 </source>
4397 <dest>
4398 *: "지워ì§"
4399 </dest>
4400 <voice>
4401 *: "설정 지워ì§"
4402 </voice>
4403</phrase>
4404<phrase>
4405 id: LANG_SAVE_SETTINGS
4406 desc: in system_settings_menu()
4407 user: core
4408 <source>
4409 *: "Save .cfg File"
4410 </source>
4411 <dest>
4412 *: ".cfg íŒŒì¼ ì €ìž¥"
4413 </dest>
4414 <voice>
4415 *: "구성 íŒŒì¼ ì €ìž¥"
4416 </voice>
4417</phrase>
4418<phrase>
4419 id: LANG_SETTINGS_SAVED
4420 desc: Feedback shown when a .cfg file is saved
4421 user: core
4422 <source>
4423 *: "Settings Saved"
4424 </source>
4425 <dest>
4426 *: "설정 저장ë¨"
4427 </dest>
4428 <voice>
4429 *: "설정 저장ë¨"
4430 </voice>
4431</phrase>
4432<phrase>
4433 id: LANG_SAVE_THEME
4434 desc: save a theme file
4435 user: core
4436 <source>
4437 *: "Save Theme Settings"
4438 </source>
4439 <dest>
4440 *: "테마 설정 저장"
4441 </dest>
4442 <voice>
4443 *: "테마 설정 저장"
4444 </voice>
4445</phrase>
4446<phrase>
4447 id: LANG_CUSTOM_THEME
4448 desc: in the main menu
4449 user: core
4450 <source>
4451 *: "Browse Theme Files"
4452 </source>
4453 <dest>
4454 *: "테마 íŒŒì¼ ì°¾ì•„ë³´ê¸°"
4455 </dest>
4456 <voice>
4457 *: "테마 íŒŒì¼ ì°¾ì•„ë³´ê¸°"
4458 </voice>
4459</phrase>
4460<phrase>
4461 id: LANG_RECORDING_SETTINGS
4462 desc: in the main menu
2256 user: core 4463 user: core
2257 <source> 4464 <source>
2258 *: none 4465 *: none
2259 recording_hwcodec: "Quality" 4466 recording: "Recording Settings"
2260 </source> 4467 </source>
2261 <dest> 4468 <dest>
2262 *: none 4469 *: none
2263 recording_hwcodec: "ìŒì§ˆ" 4470 recording: "ë…¹ìŒ ì„¤ì •"
2264 </dest> 4471 </dest>
2265 <voice> 4472 <voice>
2266 *: none 4473 *: none
2267 recording_hwcodec: "ìŒì§ˆ" 4474 recording: "ë…¹ìŒ ì„¤ì •"
2268 </voice> 4475 </voice>
2269</phrase> 4476</phrase>
2270<phrase> 4477<phrase>
2271 id: LANG_FREQUENCY 4478 id: LANG_FM_MENU
2272 desc: in recording and playback settings 4479 desc: fm menu title
2273 user: core 4480 user: core
2274 <source> 4481 <source>
2275 *: none 4482 *: none
2276 play_frequency,recording: "Frequency" 4483 radio: "FM Radio Menu"
2277 </source> 4484 </source>
2278 <dest> 4485 <dest>
2279 *: none 4486 *: none
2280 play_frequency,recording: "주파수" 4487 radio: "FM ë¼ë””오 메뉴"
2281 </dest> 4488 </dest>
2282 <voice> 4489 <voice>
2283 *: none 4490 *: none
2284 play_frequency,recording: "주파수" 4491 radio: "FM ë¼ë””오 메뉴"
2285 </voice> 4492 </voice>
2286</phrase> 4493</phrase>
2287<phrase> 4494<phrase>
2288 id: LANG_RECORDING_SOURCE 4495 id: LANG_FM_NO_PRESETS
2289 desc: in the recording settings 4496 desc: error when preset list is empty
2290 user: core 4497 user: core
2291 <source> 4498 <source>
2292 *: none 4499 *: none
2293 recording: "Source" 4500 radio: "No presets"
2294 </source> 4501 </source>
2295 <dest> 4502 <dest>
2296 *: none 4503 *: none
2297 recording: "소스" 4504 radio: "사전 설정 ì—†ìŒ"
2298 </dest> 4505 </dest>
2299 <voice> 4506 <voice>
2300 *: none 4507 *: none
2301 recording: "소스" 4508 radio: "사전 설정 ì—†ìŒ"
2302 </voice> 4509 </voice>
2303</phrase> 4510</phrase>
2304<phrase> 4511<phrase>
2305 id: LANG_RECORDING_SRC_MIC 4512 id: LANG_FM_ADD_PRESET
2306 desc: in the recording settings 4513 desc: in radio menu
2307 user: core 4514 user: core
2308 <source> 4515 <source>
2309 *: none 4516 *: none
2310 iriverh100,iriverh120,iriverh300: "Internal Microphone" 4517 radio: "Add Preset"
2311 recording: "Microphone"
2312 </source> 4518 </source>
2313 <dest> 4519 <dest>
2314 *: none 4520 *: none
2315 iriverh100,iriverh120,iriverh300: "내장 마ì´í¬" 4521 radio: "사전 설정 추가"
2316 recording: "외부 마ì´í¬"
2317 </dest> 4522 </dest>
2318 <voice> 4523 <voice>
2319 *: none 4524 *: none
2320 iriverh100,iriverh120,iriverh300: "내장 마ì´í¬" 4525 radio: "사전 설정 추가"
2321 recording: "외부 마ì´í¬"
2322 </voice> 4526 </voice>
2323</phrase> 4527</phrase>
2324<phrase> 4528<phrase>
2325 id: LANG_RECORDING_SRC_DIGITAL 4529 id: LANG_FM_EDIT_PRESET
2326 desc: in the recording settings 4530 desc: in radio screen
2327 user: core 4531 user: core
2328 <source> 4532 <source>
2329 *: none 4533 *: none
2330 recording: "Digital" 4534 radio: "Edit Preset"
2331 </source> 4535 </source>
2332 <dest> 4536 <dest>
2333 *: none 4537 *: none
2334 recording: "디지털" 4538 radio: "ì‚¬ì  ì„¤ì • 편집"
2335 </dest> 4539 </dest>
2336 <voice> 4540 <voice>
2337 *: none 4541 *: none
2338 recording: "디지털" 4542 radio: "ì‚¬ì  ì„¤ì • 편집"
2339 </voice> 4543 </voice>
2340</phrase> 4544</phrase>
2341<phrase> 4545<phrase>
2342 id: LANG_RECORDING_EDITABLE 4546 id: LANG_FM_DELETE_PRESET
2343 desc: Editable recordings setting 4547 desc: in radio screen
2344 user: core 4548 user: core
2345 <source> 4549 <source>
2346 *: none 4550 *: none
2347 recording_hwcodec: "Independent Frames" 4551 radio: "Remove Preset"
2348 </source> 4552 </source>
2349 <dest> 4553 <dest>
2350 *: none 4554 *: none
2351 recording_hwcodec: "ë…ë¦½ëœ í”레임" 4555 radio: "사전 ì¤ì ì œê±°"
2352 </dest> 4556 </dest>
2353 <voice> 4557 <voice>
2354 *: none 4558 *: none
2355 recording_hwcodec: "ë…ë¦½ëœ í”레임" 4559 radio: "사전 ì¤ì ì œê±°"
2356 </voice> 4560 </voice>
2357</phrase> 4561</phrase>
2358<phrase> 4562<phrase>
2359 id: LANG_RECORD_TIMESPLIT 4563 id: LANG_FM_PRESET_SAVE_FAILED
2360 desc: Record split menu 4564 desc: in radio screen
2361 user: core 4565 user: core
2362 <source> 4566 <source>
2363 *: none 4567 *: none
2364 recording: "File Split Options" 4568 radio: "Preset Save Failed"
2365 </source> 4569 </source>
2366 <dest> 4570 <dest>
2367 *: none 4571 *: none
2368 recording: "íŒŒì¼ ë¶„ë¦¬ 옵션" 4572 radio: "í„리셋 저장 실패함"
2369 </dest> 4573 </dest>
2370 <voice> 4574 <voice>
2371 *: none 4575 *: none
2372 recording: "íŒŒì¼ ë¶„ë¦¬ 옵션" 4576 radio: "í„리셋 저장 실패함"
2373 </voice> 4577 </voice>
2374</phrase> 4578</phrase>
2375<phrase> 4579<phrase>
2376 id: LANG_RECORD_PRERECORD_TIME 4580 id: LANG_FM_NO_FREE_PRESETS
2377 desc: in recording settings_menu 4581 desc: in radio screen
2378 user: core 4582 user: core
2379 <source> 4583 <source>
2380 *: none 4584 *: none
2381 recording: "Prerecord Time" 4585 radio: "The Preset List is Full"
2382 </source> 4586 </source>
2383 <dest> 4587 <dest>
2384 *: none 4588 *: none
2385 recording: "프리 ë ˆì½”ë© íƒ€ìž„" 4589 radio: "사ì ì„¤ì • ëª©ë¡ ê°ë“ ì°¸"
2386 </dest> 4590 </dest>
2387 <voice> 4591 <voice>
2388 *: none 4592 *: none
2389 recording: "프리 ë ˆì½”ë© íƒ€ìž„" 4593 radio: "사ì ì„¤ì • ëª©ë¡ ê°ë“ ì°¸"
2390 </voice> 4594 </voice>
2391</phrase> 4595</phrase>
2392<phrase> 4596<phrase>
2393 id: LANG_RECORD_DIRECTORY 4597 id: LANG_PRESET
2394 desc: in recording settings_menu 4598 desc: in button bar and radio screen / menu
2395 user: core 4599 user: core
2396 <source> 4600 <source>
2397 *: none 4601 *: none
2398 recording: "Directory" 4602 radio: "Preset"
2399 </source> 4603 </source>
2400 <dest> 4604 <dest>
2401 *: none 4605 *: none
2402 recording: "디렉토리" 4606 radio: "사전 설ì "
2403 </dest> 4607 </dest>
2404 <voice> 4608 <voice>
2405 *: none 4609 *: none
2406 recording: "디렉토리" 4610 radio: "사전 설ì "
2407 </voice> 4611 </voice>
2408</phrase> 4612</phrase>
2409<phrase> 4613<phrase>
2410 id: LANG_RECORD_TRIGGER 4614 id: LANG_FM_MONO_MODE
2411 desc: in recording settings_menu 4615 desc: in radio screen
2412 user: core 4616 user: core
2413 <source> 4617 <source>
2414 *: none 4618 *: none
2415 recording: "Trigger" 4619 radio: "Force Mono"
2416 </source> 4620 </source>
2417 <dest> 4621 <dest>
2418 *: none 4622 *: none
2419 recording: "트리거" 4623 radio: "강제 모노"
2420 </dest> 4624 </dest>
2421 <voice> 4625 <voice>
2422 *: none 4626 *: none
2423 recording: "트리거" 4627 radio: "강제 모노"
2424 </voice> 4628 </voice>
2425</phrase> 4629</phrase>
2426<phrase> 4630<phrase>
2427 id: LANG_CLIP_LIGHT 4631 id: LANG_FM_FREEZE
2428 desc: in record settings menu. 4632 desc: splash screen during freeze in radio mode
2429 user: core 4633 user: core
2430 <source> 4634 <source>
2431 *: none 4635 *: none
2432 recording: "Clipping Light" 4636 radio: "Screen frozen!"
2433 </source> 4637 </source>
2434 <dest> 4638 <dest>
2435 *: none 4639 *: none
2436 recording: "í´ë¦¬í•‘ 램프" 4640 radio: "í™”ë©´ì´ ë©ˆì·ìŠµë‹ˆë‹¤!"
2437 </dest> 4641 </dest>
2438 <voice> 4642 <voice>
2439 *: none 4643 *: none
2440 recording: "í´ë¦¬í•‘ 램프" 4644 radio: ""
2441 </voice> 4645 </voice>
2442</phrase> 4646</phrase>
2443<phrase> 4647<phrase>
2444 id: LANG_MAIN_UNIT 4648 id: LANG_FM_SCAN_PRESETS
2445 desc: in record settings menu. 4649 desc: in radio menu
2446 user: core 4650 user: core
2447 <source> 4651 <source>
2448 *: none 4652 *: none
2449 remote: "Main Unit Only" 4653 radio: "Auto-Scan Presets"
2450 </source> 4654 </source>
2451 <dest> 4655 <dest>
2452 *: none 4656 *: none
2453 remote: "본체만" 4657 radio: "사전 설정 ìžë™ 검색"
2454 </dest> 4658 </dest>
2455 <voice> 4659 <voice>
2456 *: none 4660 *: none
2457 remote: "본체만" 4661 radio: "사전 설정 ìžë™ ê²€"
2458 </voice> 4662 </voice>
2459</phrase> 4663</phrase>
2460<phrase> 4664<phrase>
2461 id: LANG_REMOTE_UNIT 4665 id: LANG_FM_CLEAR_PRESETS
2462 desc: in record settings menu. 4666 desc: confirmation if presets can be cleared
2463 user: core 4667 user: core
2464 <source> 4668 <source>
2465 *: none 4669 *: none
2466 remote: "Remote Unit Only" 4670 radio: "Clear Current Presets?"
2467 </source> 4671 </source>
2468 <dest> 4672 <dest>
2469 *: none 4673 *: none
2470 remote: "리모트만" 4674 radio: "현재 사전 ì„¤ì •ì„ ì§€ì›ë‹ˆê¹Œ?"
2471 </dest> 4675 </dest>
2472 <voice> 4676 <voice>
2473 *: none 4677 *: none
2474 remote: "리모트만" 4678 radio: "현재 사전 ì„¤ì •ì„ ì§€ì›ë‹ˆê¹Œ?"
2475 </voice> 4679 </voice>
2476</phrase> 4680</phrase>
2477<phrase> 4681<phrase>
2478 id: LANG_REMOTE_MAIN 4682 id: LANG_FM_SCANNING
2479 desc: in record settings menu. 4683 desc: during auto scan
2480 user: core 4684 user: core
2481 <source> 4685 <source>
2482 *: none 4686 *: none
2483 remote: "Main and Remote Unit" 4687 radio: "Scanning %d.%02d MHz"
2484 </source> 4688 </source>
2485 <dest> 4689 <dest>
2486 *: none 4690 *: none
2487 remote: "본체ì™ë¦¬ëª¨íŠ¸" 4691 radio: "%d.%02d MHz 검색 중"
2488 </dest> 4692 </dest>
2489 <voice> 4693 <voice>
2490 *: none 4694 *: none
2491 remote: "본체와 리모트" 4695 radio: ""
2492 </voice> 4696 </voice>
2493</phrase> 4697</phrase>
2494<phrase> 4698<phrase>
2495 id: LANG_FFRW_STEP 4699 id: LANG_FM_DEFAULT_PRESET_NAME
2496 desc: in settings_menu 4700 desc: default preset name for auto scan mode
2497 user: core 4701 user: core
2498 <source> 4702 <source>
2499 *: "FF/RW Min Step" 4703 *: none
4704 radio: "%d.%02d MHz"
2500 </source> 4705 </source>
2501 <dest> 4706 <dest>
2502 *: "ë˜/빨리ê°ê¸° 범위" 4707 *: none
4708 radio: "%d.%02d MHz"
2503 </dest> 4709 </dest>
2504 <voice> 4710 <voice>
2505 *: "최소 범위" 4711 *: none
4712 radio: ""
2506 </voice> 4713 </voice>
2507</phrase> 4714</phrase>
2508<phrase> 4715<phrase>
2509 id: LANG_FFRW_ACCEL 4716 id: LANG_RADIO_SCAN_MODE
2510 desc: in settings_menu 4717 desc: in radio screen / menu
2511 user: core 4718 user: core
2512 <source> 4719 <source>
2513 *: "FF/RW Accel" 4720 *: none
4721 radio: "Scan"
2514 </source> 4722 </source>
2515 <dest> 4723 <dest>
2516 *: "ë˜/빨리ê°ê¸° ì†ë„" 4724 *: none
4725 radio: "검색"
2517 </dest> 4726 </dest>
2518 <voice> 4727 <voice>
2519 *: "ì†ë„" 4728 *: none
4729 radio: "검색"
2520 </voice> 4730 </voice>
2521</phrase> 4731</phrase>
2522<phrase> 4732<phrase>
2523 id: LANG_CROSSFADE_ENABLE 4733 id: LANG_FM_PRESET_LOAD
2524 desc: in crossfade settings menu 4734 desc: load preset list in fm radio
2525 user: core 4735 user: core
2526 <source> 4736 <source>
2527 *: none 4737 *: none
2528 crossfade: "Enable Crossfade" 4738 radio: "Load Preset List"
2529 </source> 4739 </source>
2530 <dest> 4740 <dest>
2531 *: none 4741 *: none
2532 crossfade: "í¬ë¡œìŠ¤íŽ˜ì´ëœ 켜기" 4742 radio: "사전 설정 ëª©ë¡ ë¡œë“œ"
2533 </dest> 4743 </dest>
2534 <voice> 4744 <voice>
2535 *: none 4745 *: none
2536 crossfade: "í¬ë¡œìŠ¤íŽ˜ì´ëœ 켜기" 4746 radio: "사전설정 ëª©ë¡ ë¡œë“œ"
2537 </voice> 4747 </voice>
2538</phrase> 4748</phrase>
2539<phrase> 4749<phrase>
2540 id: LANG_MANTRACKSKIP 4750 id: LANG_FM_PRESET_SAVE
2541 desc: in crossfade settings 4751 desc: Save preset list in fm radio
2542 user: core 4752 user: core
2543 <source> 4753 <source>
2544 *: none 4754 *: none
2545 crossfade: "Manual Track Skip Only" 4755 radio: "Save Preset List"
2546 </source> 4756 </source>
2547 <dest> 4757 <dest>
2548 *: none 4758 *: none
2549 crossfade: "트랙 스킵만" 4759 radio: "사전 ì¤ì • ëª©ë¡ ì €ìž¥"
2550 </dest> 4760 </dest>
2551 <voice> 4761 <voice>
2552 *: none 4762 *: none
2553 crossfade: "트랙 스킵만" 4763 radio: "사전 ì¤ì • ëª©ë¡ ì €ìž¥"
2554 </voice> 4764 </voice>
2555</phrase> 4765</phrase>
2556<phrase> 4766<phrase>
2557 id: LANG_CROSSFADE_FADE_IN_DELAY 4767 id: LANG_FM_PRESET_CLEAR
2558 desc: in crossfade settings menu 4768 desc: clear preset list in fm radio
2559 user: core 4769 user: core
2560 <source> 4770 <source>
2561 *: none 4771 *: none
2562 crossfade: "Fade-In Delay" 4772 radio: "Clear Preset List"
2563 </source> 4773 </source>
2564 <dest> 4774 <dest>
2565 *: none 4775 *: none
2566 crossfade: "페ì´ë“œì¸ 지ì°ì‹œê°" 4776 radio: "사전 설정 ëª©ë¡ ì§€ìš°ê¸°"
2567 </dest> 4777 </dest>
2568 <voice> 4778 <voice>
2569 *: none 4779 *: none
2570 crossfade: "페ì´ë“œì¸ 지ì°ì‹œê°" 4780 radio: "사전 설정 ëª©ë¡ ì§€ìš°ê¸°"
2571 </voice> 4781 </voice>
2572</phrase> 4782</phrase>
2573<phrase> 4783<phrase>
2574 id: LANG_CROSSFADE_FADE_IN_DURATION 4784 id: LANG_FMR
2575 desc: in crossfade settings menu 4785 desc: Used when you need to say Preset List, also voiced
2576 user: core 4786 user: core
2577 <source> 4787 <source>
2578 *: none 4788 *: none
2579 crossfade: "Fade-In Duration" 4789 radio: "Preset List"
2580 </source> 4790 </source>
2581 <dest> 4791 <dest>
2582 *: none 4792 *: none
2583 crossfade: "페ì´ë“œì¸ 시간" 4793 radio: "사전 설정 목ë¡"
2584 </dest> 4794 </dest>
2585 <voice> 4795 <voice>
2586 *: none 4796 *: none
2587 crossfade: "페ì´ë“œì¸ 시간" 4797 radio: "사전 설정 목ë¡"
2588 </voice> 4798 </voice>
2589</phrase> 4799</phrase>
2590<phrase> 4800<phrase>
2591 id: LANG_CROSSFADE_FADE_OUT_DELAY 4801 id: LANG_FM_FIRST_AUTOSCAN
2592 desc: in crossfade settings menu 4802 desc: When you run the radio without an fmr file in settings
2593 user: core 4803 user: core
2594 <source> 4804 <source>
2595 *: none 4805 *: none
2596 crossfade: "Fade-Out Delay" 4806 radio: "No settings found. Autoscan?"
2597 </source> 4807 </source>
2598 <dest> 4808 <dest>
2599 *: none 4809 *: none
2600 crossfade: "페ì´ë“œì•„웃 지연시간" 4810 radio: "설ì ì ì°¾ì„ ìˆ˜ ì—습ëˆë‹¤. ìžë™ 검색ì 할까요?"
2601 </dest> 4811 </dest>
2602 <voice> 4812 <voice>
2603 *: none 4813 *: none
2604 crossfade: "페ì´ë“œì•„웃 지연시간" 4814 radio: "설ì ì ì°¾ì„ ìˆ˜ ì—습ëˆë‹¤. ìžë™ 검색ì 할까요?"
2605 </voice> 4815 </voice>
2606</phrase> 4816</phrase>
2607<phrase> 4817<phrase>
2608 id: LANG_CROSSFADE_FADE_OUT_DURATION 4818 id: LANG_FM_REGION
2609 desc: in crossfade settings menu 4819 desc: fm tuner region setting
2610 user: core 4820 user: core
2611 <source> 4821 <source>
2612 *: none 4822 *: none
2613 crossfade: "Fade-Out Duration" 4823 radio: "Region"
2614 </source> 4824 </source>
2615 <dest> 4825 <dest>
2616 *: none 4826 *: none
2617 crossfade: "페ì´ë“œì•„웃 시간" 4827 radio: "지역"
2618 </dest> 4828 </dest>
2619 <voice> 4829 <voice>
2620 *: none 4830 *: none
2621 crossfade: "페ì´ë“œì•„웃 시간" 4831 radio: "지역"
2622 </voice> 4832 </voice>
2623</phrase> 4833</phrase>
2624<phrase> 4834<phrase>
2625 id: LANG_CROSSFADE_FADE_OUT_MODE 4835 id: LANG_FM_EUROPE
2626 desc: in crossfade settings menu 4836 desc: fm tuner region europe
2627 user: core 4837 user: core
2628 <source> 4838 <source>
2629 *: none 4839 *: none
2630 crossfade: "Fade-Out Mode" 4840 radio: "Europe"
2631 </source> 4841 </source>
2632 <dest> 4842 <dest>
2633 *: none 4843 *: none
2634 crossfade: "페ì´ë“œì•„웃 모드" 4844 radio: "유럽"
2635 </dest> 4845 </dest>
2636 <voice> 4846 <voice>
2637 *: none 4847 *: none
2638 crossfade: "페ì´ë“œì•„웃 모드" 4848 radio: "유럽"
2639 </voice> 4849 </voice>
2640</phrase> 4850</phrase>
2641<phrase> 4851<phrase>
2642 id: LANG_MIX 4852 id: LANG_FM_US
2643 desc: in playback settings, crossfade option 4853 desc: fm region us / canada
2644 user: core 4854 user: core
2645 <source> 4855 <source>
2646 *: none 4856 *: none
2647 crossfade: "Mix" 4857 radio: "US / Canada"
2648 </source> 4858 </source>
2649 <dest> 4859 <dest>
2650 *: none 4860 *: none
2651 crossfade: "혼합" 4861 radio: "미국 / ìºë‚˜ë‹¤"
2652 </dest> 4862 </dest>
2653 <voice> 4863 <voice>
2654 *: none 4864 *: none
2655 crossfade: "혼합" 4865 radio: "미국과 ìºë‚˜ë‹¤"
2656 </voice> 4866 </voice>
2657</phrase> 4867</phrase>
2658<phrase> 4868<phrase>
2659 id: LANG_REPLAYGAIN_NOCLIP 4869 id: LANG_FM_JAPAN
2660 desc: in replaygain 4870 desc: fm region japan
2661 user: core 4871 user: core
2662 <source> 4872 <source>
2663 *: "Prevent Clipping" 4873 *: none
4874 radio: "Japan"
2664 </source> 4875 </source>
2665 <dest> 4876 <dest>
2666 *: "í´ë¦¬í•‘ 방지" 4877 *: none
4878 radio: "ì¼ë³¸"
2667 </dest> 4879 </dest>
2668 <voice> 4880 <voice>
2669 *: "í´ë¦¬í•‘ 방지" 4881 *: none
4882 radio: "ì¼ë³¸"
2670 </voice> 4883 </voice>
2671</phrase> 4884</phrase>
2672<phrase> 4885<phrase>
2673 id: LANG_REPLAYGAIN_MODE 4886 id: LANG_FM_KOREA
2674 desc: in replaygain 4887 desc: fm region korea
2675 user: core 4888 user: core
2676 <source> 4889 <source>
2677 *: "Replaygain Type" 4890 *: none
4891 radio: "Korea"
2678 </source> 4892 </source>
2679 <dest> 4893 <dest>
2680 *: "리플레ì´ê²Œì¸ 종류" 4894 *: none
4895 radio: "대한민국"
2681 </dest> 4896 </dest>
2682 <voice> 4897 <voice>
2683 *: "리플레ì´ê²Œì¸ 종류" 4898 *: none
4899 radio: "대한민국"
2684 </voice> 4900 </voice>
2685</phrase> 4901</phrase>
2686<phrase> 4902<phrase>
2687 id: LANG_ALBUM_GAIN 4903 id: LANG_FORMAT
2688 desc: in replaygain 4904 desc: audio format
2689 user: core 4905 user: core
2690 <source> 4906 <source>
2691 *: "Album Gain" 4907 *: "Format"
2692 </source> 4908 </source>
2693 <dest> 4909 <dest>
2694 *: "ì¨ë²” 게ì¸" 4910 *: "형ì"
2695 </dest> 4911 </dest>
2696 <voice> 4912 <voice>
2697 *: "ì¨ë²” 게ì¸" 4913 *: "형ì"
2698 </voice> 4914 </voice>
2699</phrase> 4915</phrase>
2700<phrase> 4916<phrase>
2701 id: LANG_TRACK_GAIN 4917 id: LANG_AFMT_MPA_L3
2702 desc: in replaygain 4918 desc: audio format description
2703 user: core 4919 user: core
2704 <source> 4920 <source>
2705 *: "Track Gain" 4921 *: none
4922 recording: "MPEG Layer 3"
2706 </source> 4923 </source>
2707 <dest> 4924 <dest>
2708 *: "트랙 게ì¸" 4925 *: none
4926 recording: "MPEG ë ˆì´ì–´ 3"
2709 </dest> 4927 </dest>
2710 <voice> 4928 <voice>
2711 *: "트랙 게ì¸" 4929 *: none
4930 recording: "MPEG ë ˆì´ì–´ 3"
2712 </voice> 4931 </voice>
2713</phrase> 4932</phrase>
2714<phrase> 4933<phrase>
2715 id: LANG_SHUFFLE_GAIN 4934 id: LANG_AFMT_PCM_WAV
2716 desc: use track gain if shuffle mode is on, album gain otherwise 4935 desc: audio format description
2717 user: core 4936 user: core
2718 <source> 4937 <source>
2719 *: "Track Gain if Shuffling" 4938 *: none
4939 recording: "PCM Wave"
2720 </source> 4940 </source>
2721 <dest> 4941 <dest>
2722 *: "ìž„ì˜ ìž¬ìƒì‹œ 트랙 ê²Œì¸ ì‚¬ìš©" 4942 *: none
4943 recording: "~PCM Wave"
2723 </dest> 4944 </dest>
2724 <voice> 4945 <voice>
2725 *: "ìž„ì˜ ìž¬ìƒì‹œ 트랙 ê²Œì¸ ì‚¬ìš©" 4946 *: none
4947 recording: "PCM 웨ì´ë¸Œ"
2726 </voice> 4948 </voice>
2727</phrase> 4949</phrase>
2728<phrase> 4950<phrase>
2729 id: LANG_REPLAYGAIN_PREAMP 4951 id: LANG_AFMT_WAVPACK
2730 desc: in replaygain settings 4952 desc: audio format description
2731 user: core 4953 user: core
2732 <source> 4954 <source>
2733 *: "Pre-amp" 4955 *: none
4956 recording: "WavPack"
2734 </source> 4957 </source>
2735 <dest> 4958 <dest>
2736 *: "미리 ì¦í­" 4959 *: none
4960 recording: "WavPack"
2737 </dest> 4961 </dest>
2738 <voice> 4962 <voice>
2739 *: "미리 ì¦í­" 4963 *: none
4964 recording: "WavPack"
2740 </voice> 4965 </voice>
2741</phrase> 4966</phrase>
2742<phrase> 4967<phrase>
2743 id: LANG_BACKLIGHT 4968 id: LANG_AFMT_AIFF
2744 desc: in settings_menu 4969 desc: audio format description
2745 user: core 4970 user: core
2746 <source> 4971 <source>
2747 *: "Backlight" 4972 *: none
4973 recording: "AIFF"
2748 </source> 4974 </source>
2749 <dest> 4975 <dest>
2750 *: "배터리 조명 시간" 4976 *: none
4977 recording: "AIFF"
2751 </dest> 4978 </dest>
2752 <voice> 4979 <voice>
2753 *: "배터리 조명 시간" 4980 *: none
4981 recording: "AIFF"
2754 </voice> 4982 </voice>
2755</phrase> 4983</phrase>
2756<phrase> 4984<phrase>
2757 id: LANG_BACKLIGHT_ON_WHEN_CHARGING 4985 id: LANG_ENCODER_SETTINGS
2758 desc: in display_settings_menu, backlight timeout with charger connected 4986 desc: encoder settings
2759 user: core 4987 user: core
2760 <source> 4988 <source>
2761 *: none 4989 *: none
2762 charging: "Backlight (While Plugged In)" 4990 recording: "Encoder Settings"
2763 </source> 4991 </source>
2764 <dest> 4992 <dest>
2765 *: none 4993 *: none
2766 charging: "플러그 ì—°ê²°ì‹œ ë°±ë¼ì´íŠ¸ 설정" 4994 recording: "ì¸ì½”ë” ì„¤ì •"
2767 </dest> 4995 </dest>
2768 <voice> 4996 <voice>
2769 *: none 4997 *: none
2770 charging: "플러그 ì—°ê²°ì‹œ ë°±ë¼ì´íŠ¸ 설정" 4998 recording: "ì¸ì½”ë” ì„¤ì •"
2771 </voice> 4999 </voice>
2772</phrase> 5000</phrase>
2773<phrase> 5001<phrase>
2774 id: LANG_CAPTION_BACKLIGHT 5002 id: LANG_BITRATE
2775 desc: in settings_menu 5003 desc: bits-kilobits per unit time
2776 user: core 5004 user: core
2777 <source> 5005 <source>
2778 *: "Caption Backlight" 5006 *: none
5007 recording: "Bitrate"
2779 </source> 5008 </source>
2780 <dest> 5009 <dest>
2781 *: "ìŒì•… 시작/종료시 조명 켜기" 5010 *: none
5011 recording: "비트전송률"
2782 </dest> 5012 </dest>
2783 <voice> 5013 <voice>
2784 *: "ìŒì•… 시작/종료시 조명 켜기" 5014 *: none
5015 recording: "비트전송률"
2785 </voice> 5016 </voice>
2786</phrase> 5017</phrase>
2787<phrase> 5018<phrase>
2788 id: LANG_BACKLIGHT_FADE_IN 5019 id: LANG_NO_SETTINGS
2789 desc: in settings_menu 5020 desc: when something has settings in a certain context
2790 user: core 5021 user: core
2791 <source> 5022 <source>
2792 *: none 5023 *: none
2793 backlight_fade*: "Backlight Fade In" 5024 recording: "(No Settings)"
2794 </source> 5025 </source>
2795 <dest> 5026 <dest>
2796 *: none 5027 *: none
2797 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œì¸" 5028 recording: "(설정 ì—†ìŒ)"
2798 </dest> 5029 </dest>
2799 <voice> 5030 <voice>
2800 *: none 5031 *: none
2801 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œì¸" 5032 recording: "사용 ê°ëŠ¥í•œ ì„¤ì •ì´ ì—†ìŒ"
2802 </voice> 5033 </voice>
2803</phrase> 5034</phrase>
2804<phrase> 5035<phrase>
2805 id: LANG_BACKLIGHT_FADE_OUT 5036 id: LANG_FREQUENCY
2806 desc: in settings_menu 5037 desc: in recording and playback settings
2807 user: core 5038 user: core
2808 <source> 5039 <source>
2809 *: none 5040 *: none
2810 backlight_fade*: "Backlight Fade Out" 5041 play_frequency,recording: "Frequency"
2811 </source> 5042 </source>
2812 <dest> 5043 <dest>
2813 *: none 5044 *: none
2814 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œì•„웃" 5045 play_frequency,recording: "주파수"
2815 </dest> 5046 </dest>
2816 <voice> 5047 <voice>
2817 *: none 5048 *: none
2818 backlight_fade*: "ë°±ë¼ì´íŠ¸ 페ì´ë“œì•„웃" 5049 play_frequency,recording: "주파수"
2819 </voice> 5050 </voice>
2820</phrase> 5051</phrase>
2821<phrase> 5052<phrase>
2822 id: LANG_BRIGHTNESS 5053 id: LANG_SOURCE_FREQUENCY
2823 desc: in settings_menu 5054 desc: when recording source frequency setting must follow source
2824 user: core 5055 user: core
2825 <source> 5056 <source>
2826 *: none 5057 *: none
2827 backlight_brightness: "Brightness" 5058 recording: "(Same As Source)"
2828 </source> 5059 </source>
2829 <dest> 5060 <dest>
2830 *: none 5061 *: none
2831 backlight_brightness: "화면ë°ê¸°" 5062 recording: "(ì›ë³¸ê³¼ ê°™ì€)"
2832 </dest> 5063 </dest>
2833 <voice> 5064 <voice>
2834 *: none 5065 *: none
2835 backlight_brightness: "화면ë°ê¸°" 5066 recording: "ì›ë³¸ê³¼ ê°™ì€"
2836 </voice> 5067 </voice>
2837</phrase> 5068</phrase>
2838<phrase> 5069<phrase>
2839 id: LANG_CONTRAST 5070 id: LANG_RECORDING_SOURCE
2840 desc: in settings_menu 5071 desc: in the recording settings
2841 user: core 5072 user: core
2842 <source> 5073 <source>
2843 *: "Contrast" 5074 *: none
5075 recording: "Source"
2844 </source> 5076 </source>
2845 <dest> 5077 <dest>
2846 *: "명암 조절" 5078 *: none
5079 recording: "소스"
2847 </dest> 5080 </dest>
2848 <voice> 5081 <voice>
2849 *: "명암 조절" 5082 *: none
5083 recording: "소스"
2850 </voice> 5084 </voice>
2851</phrase> 5085</phrase>
2852<phrase> 5086<phrase>
2853 id: LANG_BACKLIGHT_FILTER_FIRST_KEYPRESS 5087 id: LANG_RECORDING_SRC_MIC
2854 desc: Backlight behaviour setting 5088 desc: in the recording settings
2855 user: core 5089 user: core
2856 <source> 5090 <source>
2857 *: "First Buttonpress Enables Backlight Only" 5091 *: none
5092 iriverh100,iriverh120,iriverh300: "Internal Microphone"
5093 recording: "Microphone"
2858 </source> 5094 </source>
2859 <dest> 5095 <dest>
2860 *: "버튼 한번 누르면 조명 켜기" 5096 *: none
5097 iriverh100,iriverh120,iriverh300: "내장 마ì´í¬"
5098 recording: "마ì´í¬"
2861 </dest> 5099 </dest>
2862 <voice> 5100 <voice>
2863 *: "버튼 한번 누르면 조명 켜기" 5101 *: none
5102 iriverh100,iriverh120,iriverh300: "내장 마ì´í¬"
5103 recording: "마ì´í¬"
2864 </voice> 5104 </voice>
2865</phrase> 5105</phrase>
2866<phrase> 5106<phrase>
2867 id: LANG_INVERT 5107 id: LANG_RECORDING_SRC_DIGITAL
2868 desc: in settings_menu 5108 desc: in the recording settings
2869 user: core 5109 user: core
2870 <source> 5110 <source>
2871 *: none 5111 *: none
2872 lcd_invert,remote_lcd_invert: "LCD Mode" 5112 recording: "Digital"
2873 </source> 5113 </source>
2874 <dest> 5114 <dest>
2875 *: none 5115 *: none
2876 lcd_invert,remote_lcd_invert: "LCD모드" 5116 recording: "디지털"
2877 </dest> 5117 </dest>
2878 <voice> 5118 <voice>
2879 *: none 5119 *: none
2880 lcd_invert,remote_lcd_invert: "LCD모드" 5120 recording: "디지털"
2881 </voice> 5121 </voice>
2882</phrase> 5122</phrase>
2883<phrase> 5123<phrase>
2884 id: LANG_INVERT_LCD_INVERSE 5124 id: LANG_LINE_IN
2885 desc: in settings_menu 5125 desc: in the recording settings
2886 user: core 5126 user: core
2887 <source> 5127 <source>
2888 *: none 5128 *: none
2889 lcd_invert,remote_lcd_invert: "Inverse" 5129 recording: "Line In"
2890 </source> 5130 </source>
2891 <dest> 5131 <dest>
2892 *: none 5132 *: none
2893 lcd_invert,remote_lcd_invert: "반전" 5133 recording: "ë¼ì¸ ì¸"
2894 </dest> 5134 </dest>
2895 <voice> 5135 <voice>
2896 *: none 5136 *: none
2897 lcd_invert,remote_lcd_invert: "반전" 5137 recording: "ë¼ì¸ ì¸"
2898 </voice> 5138 </voice>
2899</phrase> 5139</phrase>
2900<phrase> 5140<phrase>
2901 id: LANG_FLIP_DISPLAY 5141 id: LANG_RECORD_TIMESPLIT
2902 desc: in settings_menu, option to turn display+buttos by 180 degrees 5142 desc: Record split menu
2903 user: core 5143 user: core
2904 <source> 5144 <source>
2905 *: "Upside Down" 5145 *: none
5146 recording: "File Split Options"
2906 </source> 5147 </source>
2907 <dest> 5148 <dest>
2908 *: "뒤집기" 5149 *: none
5150 recording: "íŒŒì¼ ë¶„í•  옵션"
2909 </dest> 5151 </dest>
2910 <voice> 5152 <voice>
2911 *: "뒤집기" 5153 *: none
5154 recording: "íŒŒì¼ ë¶„í•  옵션"
2912 </voice> 5155 </voice>
2913</phrase> 5156</phrase>
2914<phrase> 5157<phrase>
2915 id: LANG_INVERT_CURSOR 5158 id: LANG_SPLIT_MEASURE
2916 desc: in settings_menu 5159 desc: in record timesplit options
2917 user: core 5160 user: core
2918 <source> 5161 <source>
2919 *: "Line Selector Type" 5162 *: none
5163 recording: "Split Measure"
2920 </source> 5164 </source>
2921 <dest> 5165 <dest>
2922 *: "ë¼ì¸ ì„ íƒê¸° 타입" 5166 *: none
5167 recording: "분할 측정"
2923 </dest> 5168 </dest>
2924 <voice> 5169 <voice>
2925 *: "ë¼ì¸ ì„ íƒê¸° 타입" 5170 *: none
5171 recording: "분할 측정"
2926 </voice> 5172 </voice>
2927</phrase> 5173</phrase>
2928<phrase> 5174<phrase>
2929 id: LANG_INVERT_CURSOR_POINTER 5175 id: LANG_SPLIT_TYPE
2930 desc: in settings_menu 5176 desc: in record timesplit options
2931 user: core 5177 user: core
2932 <source> 5178 <source>
2933 *: "Pointer" 5179 *: none
5180 recording: "What to do when Splitting"
2934 </source> 5181 </source>
2935 <dest> 5182 <dest>
2936 *: "í¬ì¸í„°" 5183 *: none
5184 recording: "분할 ì‹œ 해야 í•  ì¼"
2937 </dest> 5185 </dest>
2938 <voice> 5186 <voice>
2939 *: "í¬ì¸í„°" 5187 *: none
5188 recording: "분할 ì‹œ 해야 í•  ì¼"
2940 </voice> 5189 </voice>
2941</phrase> 5190</phrase>
2942<phrase> 5191<phrase>
2943 id: LANG_INVERT_CURSOR_BAR 5192 id: LANG_START_NEW_FILE
2944 desc: in settings_menu 5193 desc: in record timesplit options
2945 user: core 5194 user: core
2946 <source> 5195 <source>
2947 *: "Bar (Inverse)" 5196 *: none
5197 recording: "Start new file"
2948 </source> 5198 </source>
2949 <dest> 5199 <dest>
2950 *: "바(반전)" 5200 *: none
5201 recording: "새 íŒŒì¼ ì‹œìž‘"
2951 </dest> 5202 </dest>
2952 <voice> 5203 <voice>
2953 *: "바(반전)" 5204 *: none
5205 recording: "새 íŒŒì¼ ì‹œìž‘"
2954 </voice> 5206 </voice>
2955</phrase> 5207</phrase>
2956<phrase> 5208<phrase>
2957 id: LANG_CLEAR_BACKDROP 5209 id: LANG_STOP_RECORDING
2958 desc: text for LCD settings menu 5210 desc: in record timesplit options
2959 user: core 5211 user: core
2960 <source> 5212 <source>
2961 *: none 5213 *: none
2962 lcd_non-mono: "Clear Backdrop" 5214 recording: "Stop recording"
2963 </source> 5215 </source>
2964 <dest> 5216 <dest>
2965 *: none 5217 *: none
2966 lcd_non-mono: "배경화면 ì—†ì ê¸°" 5218 recording: "ë…¹ìŒ ì •ì§€"
2967 </dest> 5219 </dest>
2968 <voice> 5220 <voice>
2969 *: none 5221 *: none
2970 lcd_non-mono: "배경화면 ì—†ì ê¸°" 5222 recording: "ë…¹ìŒ ì •ì§€"
2971 </voice> 5223 </voice>
2972</phrase> 5224</phrase>
2973<phrase> 5225<phrase>
2974 id: LANG_BACKGROUND_COLOR 5226 id: LANG_SPLIT_TIME
2975 desc: menu entry to set the background color 5227 desc: in record timesplit options
2976 user: core 5228 user: core
2977 <source> 5229 <source>
2978 *: none 5230 *: none
2979 lcd_color: "Background Colour" 5231 recording: "Split Time"
2980 </source> 5232 </source>
2981 <dest> 5233 <dest>
2982 *: none 5234 *: none
2983 lcd_color: "배경색" 5235 recording: "분할 시간"
2984 </dest> 5236 </dest>
2985 <voice> 5237 <voice>
2986 *: none 5238 *: none
2987 lcd_color: "배경색" 5239 recording: "분할 시간"
2988 </voice> 5240 </voice>
2989</phrase> 5241</phrase>
2990<phrase> 5242<phrase>
2991 id: LANG_FOREGROUND_COLOR 5243 id: LANG_SPLIT_SIZE
2992 desc: menu entry to set the foreground color 5244 desc: in record timesplit options
2993 user: core 5245 user: core
2994 <source> 5246 <source>
2995 *: none 5247 *: none
2996 lcd_color: "Foreground Colour" 5248 recording: "Split Filesize"
2997 </source> 5249 </source>
2998 <dest> 5250 <dest>
2999 *: none 5251 *: none
3000 lcd_color: "전면색" 5252 recording: "분할 파ì¼í¬ê¸°"
3001 </dest> 5253 </dest>
3002 <voice> 5254 <voice>
3003 *: none 5255 *: none
3004 lcd_color: "전면색" 5256 recording: "분할 파ì¼í¬ê¸°"
3005 </voice> 5257 </voice>
3006</phrase> 5258</phrase>
3007<phrase> 5259<phrase>
3008 id: LANG_RESET_COLORS 5260 id: LANG_RECORD_PRERECORD_TIME
3009 desc: menu 5261 desc: in recording settings_menu
3010 user: core 5262 user: core
3011 <source> 5263 <source>
3012 *: none 5264 *: none
3013 lcd_color: "Reset Colours" 5265 recording: "Prerecord Time"
3014 </source> 5266 </source>
3015 <dest> 5267 <dest>
3016 *: none 5268 *: none
3017 lcd_color: "ìƒ‰ê¹ ì´ˆê¸°í™”" 5269 recording: "사전 ë…¹ìŒ ì‹œê°"
3018 </dest> 5270 </dest>
3019 <voice> 5271 <voice>
3020 *: none 5272 *: none
3021 lcd_color: "ìƒ‰ê¹ ì´ˆê¸°í™”" 5273 recording: "사전 ë…¹ìŒ ì‹œê°"
3022 </voice> 5274 </voice>
3023</phrase> 5275</phrase>
3024<phrase> 5276<phrase>
3025 id: LANG_REDUCE_TICKING 5277 id: LANG_CLEAR_REC_DIR
3026 desc: in remote lcd settings menu 5278 desc:
3027 user: core 5279 user: core
3028 <source> 5280 <source>
3029 *: none 5281 *: none
3030 remote_ticking: "Reduce Ticking" 5282 recording: "Clear Recording Directory"
3031 </source> 5283 </source>
3032 <dest> 5284 <dest>
3033 *: none 5285 *: none
3034 remote_ticking: "Ticking ê°ìŒì‹œí‚¤ê¸°" 5286 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ 지우기"
3035 </dest> 5287 </dest>
3036 <voice> 5288 <voice>
3037 *: none 5289 *: none
3038 remote_ticking: "Ticking ê°ìŒì‹œí‚¤ê¸°" 5290 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ 지우기"
3039 </voice> 5291 </voice>
3040</phrase> 5292</phrase>
3041<phrase> 5293<phrase>
3042 id: LANG_SCROLL_SPEED 5294 id: LANG_REC_DIR_NOT_WRITABLE
3043 desc: in display_settings_menu() 5295 desc:
3044 user: core 5296 user: core
3045 <source> 5297 <source>
3046 *: "Scroll Speed" 5298 *: none
5299 recording: "Can't write to recording directory"
3047 </source> 5300 </source>
3048 <dest> 5301 <dest>
3049 *: "ë¬¸ìž í름 ì†ë„" 5302 *: none
5303 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ì— ì“¸ 수 ì—†ìŒ"
3050 </dest> 5304 </dest>
3051 <voice> 5305 <voice>
3052 *: "ë¬¸ìž í름 ì†ë„" 5306 *: none
5307 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ì— ì“¸ 수 ì—†ìŒ"
3053 </voice> 5308 </voice>
3054</phrase> 5309</phrase>
3055<phrase> 5310<phrase>
3056 id: LANG_SCROLL 5311 id: LANG_CLIP_LIGHT
3057 desc: in settings_menu 5312 desc: in record settings menu.
3058 user: core 5313 user: core
3059 <source> 5314 <source>
3060 *: "Scroll Speed Setting Example" 5315 *: none
5316 recording: "Clipping Light"
3061 </source> 5317 </source>
3062 <dest> 5318 <dest>
3063 *: "ë¬¸ìž í름 ì†ë„ ì„¤ì •ì„ ë¯¸ë¦¬ë³´ê¸° 위한 예시입니다" 5319 *: none
5320 recording: "í´ë¦¬í•‘ ë¼ì´íŠ¸"
3064 </dest> 5321 </dest>
3065 <voice> 5322 <voice>
3066 *: "" 5323 *: none
5324 recording: "í´ë¦¬í•‘ ë¼ì´íŠ¸"
3067 </voice> 5325 </voice>
3068</phrase> 5326</phrase>
3069<phrase> 5327<phrase>
3070 id: LANG_SCROLL_DELAY 5328 id: LANG_MAIN_UNIT
3071 desc: Delay before scrolling 5329 desc: in record settings menu.
3072 user: core 5330 user: core
3073 <source> 5331 <source>
3074 *: "Scroll Start Delay" 5332 *: none
5333 remote: "Main Unit Only"
3075 </source> 5334 </source>
3076 <dest> 5335 <dest>
3077 *: "ë¬¸ìž í름 지연시간" 5336 *: none
5337 remote: "본체만"
3078 </dest> 5338 </dest>
3079 <voice> 5339 <voice>
3080 *: "ë¬¸ìž í름 지연시간" 5340 *: none
5341 remote: "본체만"
3081 </voice> 5342 </voice>
3082</phrase> 5343</phrase>
3083<phrase> 5344<phrase>
3084 id: LANG_SCROLL_STEP 5345 id: LANG_REMOTE_UNIT
3085 desc: Pixels to advance per scroll 5346 desc: in record settings menu.
3086 user: core 5347 user: core
3087 <source> 5348 <source>
3088 *: "Scroll Step Size" 5349 *: none
5350 remote: "Remote Unit Only"
3089 </source> 5351 </source>
3090 <dest> 5352 <dest>
3091 *: "ë¬¸ìž í름 í¬ê¸°" 5353 *: none
5354 remote: "ì›ê²© 장치만"
3092 </dest> 5355 </dest>
3093 <voice> 5356 <voice>
3094 *: "ë¬¸ìž í름 í¬ê¸°" 5357 *: none
5358 remote: "ì›ê²© 장치만"
3095 </voice> 5359 </voice>
3096</phrase> 5360</phrase>
3097<phrase> 5361<phrase>
3098 id: LANG_SCROLL_STEP_EXAMPLE 5362 id: LANG_REMOTE_MAIN
3099 desc: Pixels to advance per scroll 5363 desc: in record settings menu.
3100 user: core 5364 user: core
3101 <source> 5365 <source>
3102 *: "Scroll Step Size Setting Example Text" 5366 *: none
5367 remote: "Main and Remote Unit"
3103 </source> 5368 </source>
3104 <dest> 5369 <dest>
3105 *: "ë¬¸ìž í름 í¬ê¸° ì„¤ì •ì„ ë¯¸ë¦¬ë³´ê¸° 위한 예시입니다" 5370 *: none
5371 remote: "본체 ë° ì›ê²© 장치"
3106 </dest> 5372 </dest>
3107 <voice> 5373 <voice>
3108 *: "" 5374 *: none
5375 remote: "본체 ë° ì›ê²© 장치"
3109 </voice> 5376 </voice>
3110</phrase> 5377</phrase>
3111<phrase> 5378<phrase>
3112 id: LANG_BIDIR_SCROLL 5379 id: LANG_RECORD_TRIGGER
3113 desc: Bidirectional scroll limit 5380 desc: in recording settings_menu
3114 user: core 5381 user: core
3115 <source> 5382 <source>
3116 *: "Bidirectional Scroll Limit" 5383 *: none
5384 recording: "Trigger"
3117 </source> 5385 </source>
3118 <dest> 5386 <dest>
3119 *: "ëŒì•„오는 ë¬¸ìž í름 제한" 5387 *: none
5388 recording: "트리거"
3120 </dest> 5389 </dest>
3121 <voice> 5390 <voice>
3122 *: "ëŒì•„오는 ë¬¸ìž í름 제한" 5391 *: none
5392 recording: "트리거"
3123 </voice> 5393 </voice>
3124</phrase> 5394</phrase>
3125<phrase> 5395<phrase>
3126 id: LANG_SCREEN_SCROLL_VIEW 5396 id: LANG_RECORD_TRIG_NOREARM
3127 desc: should lines scroll out of the screen 5397 desc: in recording settings_menu
3128 user: core 5398 user: core
3129 <source> 5399 <source>
3130 *: "Screen Scrolls Out Of View" 5400 *: none
5401 recording: "Once"
3131 </source> 5402 </source>
3132 <dest> 5403 <dest>
3133 *: "í™”ë©´ì„ ì´ë™í•´ 긴 ë¬¸ìž ë³´ê¸°" 5404 *: none
5405 recording: "즉시"
3134 </dest> 5406 </dest>
3135 <voice> 5407 <voice>
3136 *: "í™”ë©´ì„ ì´ë™í•´ 긴 ë¬¸ìž ë³´ê¸°" 5408 *: none
5409 recording: "즉시"
3137 </voice> 5410 </voice>
3138</phrase> 5411</phrase>
3139<phrase> 5412<phrase>
3140 id: LANG_SCREEN_SCROLL_STEP 5413 id: LANG_RECORD_TRIGGER_TYPE
3141 desc: Pixels to advance per Screen scroll 5414 desc: in recording trigger menu
3142 user: core 5415 user: core
3143 <source> 5416 <source>
3144 *: "Screen Scroll Step Size" 5417 *: none
5418 recording: "Trigtype"
3145 </source> 5419 </source>
3146 <dest> 5420 <dest>
3147 *: "화면 ì´ë™ í¬ê¸°" 5421 *: none
5422 recording: "트리거유형"
3148 </dest> 5423 </dest>
3149 <voice> 5424 <voice>
3150 *: "화면 ì´ë™ í¬ê¸°" 5425 *: none
5426 recording: "트리거 유형"
3151 </voice> 5427 </voice>
3152</phrase> 5428</phrase>
3153<phrase> 5429<phrase>
3154 id: LANG_SCROLL_PAGINATED 5430 id: LANG_RECORD_TRIGGER_NEWFILESTP
3155 desc: jump to new page when scrolling 5431 desc: trigger types
3156 user: core 5432 user: core
3157 <source> 5433 <source>
3158 *: "Paged Scrolling" 5434 *: none
5435 recording: "New file"
3159 </source> 5436 </source>
3160 <dest> 5437 <dest>
3161 *: "빠른 페ì´ì§€ 넘김 사용" 5438 *: none
5439 recording: "새 파ì¼"
3162 </dest> 5440 </dest>
3163 <voice> 5441 <voice>
3164 *: "빠른 페ì´ì§€ 넘김 사용" 5442 *: none
5443 recording: "새 파ì¼"
3165 </voice> 5444 </voice>
3166</phrase> 5445</phrase>
3167<phrase> 5446<phrase>
3168 id: LANG_SCROLL_BAR 5447 id: LANG_RECORD_TRIGGER_STOP
3169 desc: display menu, F3 substitute 5448 desc: trigger types
3170 user: core 5449 user: core
3171 <source> 5450 <source>
3172 *: "Scroll Bar" 5451 *: none
5452 recording: "Stop"
3173 </source> 5453 </source>
3174 <dest> 5454 <dest>
3175 *: "스í¬ë¡¤ ë°”" 5455 *: none
5456 recording: "정지"
3176 </dest> 5457 </dest>
3177 <voice> 5458 <voice>
3178 *: "스í¬ë¡¤ ë°”" 5459 *: none
5460 recording: "정지"
3179 </voice> 5461 </voice>
3180</phrase> 5462</phrase>
3181<phrase> 5463<phrase>
3182 id: LANG_STATUS_BAR 5464 id: LANG_RECORD_START_THRESHOLD
3183 desc: display menu, F3 substitute 5465 desc: in recording settings_menu
3184 user: core 5466 user: core
3185 <source> 5467 <source>
3186 *: "Status Bar" 5468 *: none
5469 recording: "Start Above"
3187 </source> 5470 </source>
3188 <dest> 5471 <dest>
3189 *: "ìƒíƒœ ë°”" 5472 *: none
5473 recording: "위ì—ì„œ 시작"
3190 </dest> 5474 </dest>
3191 <voice> 5475 <voice>
3192 *: "ìƒíƒœ ë°”" 5476 *: none
5477 recording: "위ì—ì„œ 시작"
3193 </voice> 5478 </voice>
3194</phrase> 5479</phrase>
3195<phrase> 5480<phrase>
3196 id: LANG_BUTTON_BAR 5481 id: LANG_MIN_DURATION
3197 desc: in settings menu 5482 desc: in recording settings_menu
3198 user: core 5483 user: core
3199 <source> 5484 <source>
3200 *: none 5485 *: none
3201 recorder_pad: "Button Bar" 5486 recording: "for at least"
3202 </source> 5487 </source>
3203 <dest> 5488 <dest>
3204 *: none 5489 *: none
3205 recorder_pad: "버튼 바" 5490 recording: "최소한"
3206 </dest> 5491 </dest>
3207 <voice> 5492 <voice>
3208 *: none 5493 *: none
3209 recorder_pad: "버튼 바" 5494 recording: "최소한"
3210 </voice> 5495 </voice>
3211</phrase> 5496</phrase>
3212<phrase> 5497<phrase>
3213 id: LANG_VOLUME_DISPLAY 5498 id: LANG_RECORD_STOP_THRESHOLD
3214 desc: Volume type title 5499 desc: in recording settings_menu
3215 user: core 5500 user: core
3216 <source> 5501 <source>
3217 *: "Volume Display" 5502 *: none
5503 recording: "Stop Below"
3218 </source> 5504 </source>
3219 <dest> 5505 <dest>
3220 *: "볼륨 표시" 5506 *: none
5507 recording: "아래ì—ì„œ 중지"
3221 </dest> 5508 </dest>
3222 <voice> 5509 <voice>
3223 *: "볼륨 표시" 5510 *: none
5511 recording: "아래ì—ì„œ 중지"
3224 </voice> 5512 </voice>
3225</phrase> 5513</phrase>
3226<phrase> 5514<phrase>
3227 id: LANG_BATTERY_DISPLAY 5515 id: LANG_RECORD_STOP_GAP
3228 desc: Battery type title 5516 desc: in recording settings_menu
3229 user: core 5517 user: core
3230 <source> 5518 <source>
3231 *: "Battery Display" 5519 *: none
5520 recording: "Presplit Gap"
3232 </source> 5521 </source>
3233 <dest> 5522 <dest>
3234 *: "배터리 표시" 5523 *: none
5524 recording: "사전 분할 간격"
3235 </dest> 5525 </dest>
3236 <voice> 5526 <voice>
3237 *: "배터리 표시" 5527 *: none
5528 recording: "사전 분할 간격"
3238 </voice> 5529 </voice>
3239</phrase> 5530</phrase>
3240<phrase> 5531<phrase>
3241 id: LANG_DISPLAY_GRAPHIC 5532 id: LANG_RECORD_PRERECORD
3242 desc: Label for type of icon display 5533 desc: in recording and radio screen
3243 user: core 5534 user: core
3244 <source> 5535 <source>
3245 *: "Graphic" 5536 *: none
5537 recording: "Pre-Recording"
3246 </source> 5538 </source>
3247 <dest> 5539 <dest>
3248 *: "그래픽" 5540 *: none
5541 recording: "사전 ë…¹ìŒ"
3249 </dest> 5542 </dest>
3250 <voice> 5543 <voice>
3251 *: "그래픽" 5544 *: none
5545 recording: ""
3252 </voice> 5546 </voice>
3253</phrase> 5547</phrase>
3254<phrase> 5548<phrase>
3255 id: LANG_DISPLAY_NUMERIC 5549 id: LANG_AGC_SAFETY
3256 desc: Label for type of icon display 5550 desc: AGC preset
3257 user: core 5551 user: core
3258 <source> 5552 <source>
3259 *: "Numeric" 5553 *: none
5554 agc: "Safety (clip)"
3260 </source> 5555 </source>
3261 <dest> 5556 <dest>
3262 *: "숫ìž" 5557 *: none
5558 agc: "안전하게 (í´ë¦½)"
3263 </dest> 5559 </dest>
3264 <voice> 5560 <voice>
3265 *: "숫ìž" 5561 *: none
5562 agc: "안전하게 (í´ë¦½)"
3266 </voice> 5563 </voice>
3267</phrase> 5564</phrase>
3268<phrase> 5565<phrase>
3269 id: LANG_PM_RELEASE 5566 id: LANG_AGC_LIVE
3270 desc: in the peak meter menu 5567 desc: AGC preset
3271 user: core 5568 user: core
3272 <source> 5569 <source>
3273 *: "Peak Release" 5570 *: none
3274 masd: none 5571 agc: "Live (slow)"
3275 </source> 5572 </source>
3276 <dest> 5573 <dest>
3277 *: "í”¼í¬ ë¯¸í„° ì†ë„" 5574 *: none
3278 masd: none 5575 agc: "ë¼ì´ë¸Œ (ëŠë¦¼)"
3279 </dest> 5576 </dest>
3280 <voice> 5577 <voice>
3281 *: "í”¼í¬ ë¯¸í„° ì†ë„" 5578 *: none
3282 masd: none 5579 agc: "ë¼ì´ë¸Œ (ëŠë¦¼)"
3283 </voice> 5580 </voice>
3284</phrase> 5581</phrase>
3285<phrase> 5582<phrase>
3286 id: LANG_PM_PEAK_HOLD 5583 id: LANG_AGC_DJSET
3287 desc: in the peak meter menu 5584 desc: AGC preset
3288 user: core 5585 user: core
3289 <source> 5586 <source>
3290 *: "Peak Hold Time" 5587 *: none
3291 masd: none 5588 agc: "DJ-Set (slow)"
3292 </source> 5589 </source>
3293 <dest> 5590 <dest>
3294 *: "í”¼í¬ ë¯¸í„° 시간" 5591 *: none
3295 masd: none 5592 agc: "DJ 설정 (ëŠë¦¼)"
3296 </dest> 5593 </dest>
3297 <voice> 5594 <voice>
3298 *: "í”¼í¬ ë¯¸í„° 시간" 5595 *: none
3299 masd: none 5596 agc: "DJ 설정 (ëŠë¦¼)"
3300 </voice> 5597 </voice>
3301</phrase> 5598</phrase>
3302<phrase> 5599<phrase>
3303 id: LANG_PM_CLIP_HOLD 5600 id: LANG_AGC_MEDIUM
3304 desc: in the peak meter menu 5601 desc: AGC preset
3305 user: core 5602 user: core
3306 <source> 5603 <source>
3307 *: "Clip Hold Time" 5604 *: none
3308 masd: none 5605 agc: "Medium"
3309 </source> 5606 </source>
3310 <dest> 5607 <dest>
3311 *: "ê³ ìŒ í‘œì‹œ 시간" 5608 *: none
3312 masd: none 5609 agc: "중간"
3313 </dest> 5610 </dest>
3314 <voice> 5611 <voice>
3315 *: "ê³ ìŒ í‘œì‹œ 시간" 5612 *: none
3316 masd: none 5613 agc: "중간"
3317 </voice> 5614 </voice>
3318</phrase> 5615</phrase>
3319<phrase> 5616<phrase>
3320 id: LANG_PM_ETERNAL 5617 id: LANG_AGC_VOICE
3321 desc: in the peak meter menu 5618 desc: AGC preset
3322 user: core 5619 user: core
3323 <source> 5620 <source>
3324 *: "Eternal" 5621 *: none
3325 masd: none 5622 agc: "Voice (fast)"
3326 </source> 5623 </source>
3327 <dest> 5624 <dest>
3328 *: "내려오지 않게 하기" 5625 *: none
3329 masd: none 5626 agc: "ìŒì„± (빠름)"
3330 </dest> 5627 </dest>
3331 <voice> 5628 <voice>
3332 *: "내려오지 않게 하기" 5629 *: none
3333 masd: none 5630 agc: "ìŒì„± (빠름)"
3334 </voice> 5631 </voice>
3335</phrase> 5632</phrase>
3336<phrase> 5633<phrase>
3337 id: LANG_PM_SCALE 5634 id: LANG_REMOTE_LCD_OFF
3338 desc: in the peak meter menu 5635 desc: Remote lcd off splash in recording screen
3339 user: core 5636 user: core
3340 <source> 5637 <source>
3341 *: "Scale" 5638 *: none
3342 masd: none 5639 remote: "Remote Display OFF"
3343 </source> 5640 </source>
3344 <dest> 5641 <dest>
3345 *: "표시 단위" 5642 *: none
3346 masd: none 5643 remote: "ì›ê²© 화면 꺼ì§"
3347 </dest> 5644 </dest>
3348 <voice> 5645 <voice>
3349 *: "표시 단위" 5646 *: none
3350 masd: none 5647 remote: "ì›ê²© 화면 꺼ì§"
3351 </voice> 5648 </voice>
3352</phrase> 5649</phrase>
3353<phrase> 5650<phrase>
3354 id: LANG_PM_DBFS 5651 id: LANG_REMOTE_LCD_ON
3355 desc: in the peak meter menu 5652 desc: Remote lcd off splash in recording screen
3356 user: core 5653 user: core
3357 <source> 5654 <source>
3358 *: "Logarithmic (dB)" 5655 *: none
3359 masd: none 5656 remote: "(Vol- : Re-enable)"
3360 </source> 5657 </source>
3361 <dest> 5658 <dest>
3362 *: "ë°ì‹œë²¨ (dB)" 5659 *: none
3363 masd: none 5660 remote: "(볼륨- : 다시 켜기)"
3364 </dest> 5661 </dest>
3365 <voice> 5662 <voice>
3366 *: "ë°ì‹œë²¨" 5663 *: none
3367 masd: none 5664 remote: "(재활성화하려면 볼륨 마ì´ë„ˆìŠ¤)"
3368 </voice> 5665 </voice>
3369</phrase> 5666</phrase>
3370<phrase> 5667<phrase>
3371 id: LANG_PM_LINEAR 5668 id: LANG_CREATE_PLAYLIST
3372 desc: in the peak meter menu 5669 desc: Menu option for creating a playlist
3373 user: core 5670 user: core
3374 <source> 5671 <source>
3375 *: "Linear (%)" 5672 *: "Create Playlist"
3376 masd: none
3377 </source> 5673 </source>
3378 <dest> 5674 <dest>
3379 *: "í¼ì„¼íŠ¸ (%)" 5675 *: "재ìƒëª©ë¡ ìƒì„±"
3380 masd: none
3381 </dest> 5676 </dest>
3382 <voice> 5677 <voice>
3383 *: "í¼ì„¼íŠ¸" 5678 *: "재ìƒëª©ë¡ ìƒì„±"
3384 masd: none
3385 </voice> 5679 </voice>
3386</phrase> 5680</phrase>
3387<phrase> 5681<phrase>
3388 id: LANG_PM_MIN 5682 id: LANG_PLAYLISTVIEWER_SETTINGS
3389 desc: in the peak meter menu 5683 desc: title for the playlist viewer settings menus
3390 user: core 5684 user: core
3391 <source> 5685 <source>
3392 *: "Minimum Of Range" 5686 *: "Playlist Viewer Settings"
3393 masd: none
3394 </source> 5687 </source>
3395 <dest> 5688 <dest>
3396 *: "최소 범위" 5689 *: "재ìƒëª©ë¡ ë·°ì–´ 설정"
3397 masd: none
3398 </dest> 5690 </dest>
3399 <voice> 5691 <voice>
3400 *: "최소 범위" 5692 *: "재ìƒëª©ë¡ ë·°ì–´ 설정"
3401 masd: none
3402 </voice> 5693 </voice>
3403</phrase> 5694</phrase>
3404<phrase> 5695<phrase>
3405 id: LANG_PM_MAX 5696 id: LANG_VIEW_DYNAMIC_PLAYLIST
3406 desc: in the peak meter menu 5697 desc: in playlist menu.
3407 user: core 5698 user: core
3408 <source> 5699 <source>
3409 *: "Maximum Of Range" 5700 *: "View Current Playlist"
3410 masd: none
3411 </source> 5701 </source>
3412 <dest> 5702 <dest>
3413 *: "최대 범위" 5703 *: "현재 재ìƒëª©ë¡ 보기"
3414 masd: none
3415 </dest> 5704 </dest>
3416 <voice> 5705 <voice>
3417 *: "최대 범위" 5706 *: "현재 재ìƒëª©ë¡ 보기"
3418 masd: none
3419 </voice> 5707 </voice>
3420</phrase> 5708</phrase>
3421<phrase> 5709<phrase>
3422 id: LANG_BATTERY_CAPACITY 5710 id: LANG_MOVE
3423 desc: in settings_menu 5711 desc: The verb/action Move
3424 user: core 5712 user: core
3425 <source> 5713 <source>
3426 *: "Battery Capacity" 5714 *: "Move"
3427 </source> 5715 </source>
3428 <dest> 5716 <dest>
3429 *: "배터리 용량" 5717 *: "ì´ë"
3430 </dest> 5718 </dest>
3431 <voice> 5719 <voice>
3432 *: "배터리 용량" 5720 *: "ì´ë"
3433 </voice> 5721 </voice>
3434</phrase> 5722</phrase>
3435<phrase> 5723<phrase>
3436 id: LANG_BATTERY_TYPE 5724 id: LANG_SHOW_INDICES
3437 desc: in battery settings 5725 desc: in playlist viewer menu
3438 user: core 5726 user: core
3439 <source> 5727 <source>
3440 *: none 5728 *: "Show Indices"
3441 battery_types: "Battery Type"
3442 </source> 5729 </source>
3443 <dest> 5730 <dest>
3444 *: none 5731 *: "ì¸ë±ìŠ¤ 표시"
3445 battery_types: "배터리 타입"
3446 </dest> 5732 </dest>
3447 <voice> 5733 <voice>
3448 *: none 5734 *: "ì¸ë±ìŠ¤ 표시"
3449 battery_types: "배터리 타입"
3450 </voice> 5735 </voice>
3451</phrase> 5736</phrase>
3452<phrase> 5737<phrase>
3453 id: LANG_BATTERY_TYPE_1 5738 id: LANG_TRACK_DISPLAY
3454 desc: in battery settings 5739 desc: in playlist viewer on+play menu
3455 user: core 5740 user: core
3456 <source> 5741 <source>
3457 *: none 5742 *: "Track Display"
3458 battery_types: "Alkaline"
3459 </source> 5743 </source>
3460 <dest> 5744 <dest>
3461 *: none 5745 *: "트랙 화면"
3462 battery_types: "알카ë¼ì¸"
3463 </dest> 5746 </dest>
3464 <voice> 5747 <voice>
3465 *: none 5748 *: "트랙 화면"
3466 battery_types: "알카ë¼ì¸"
3467 </voice> 5749 </voice>
3468</phrase> 5750</phrase>
3469<phrase> 5751<phrase>
3470 id: LANG_BATTERY_TYPE_2 5752 id: LANG_DISPLAY_TRACK_NAME_ONLY
3471 desc: in battery settings 5753 desc: track display options
3472 user: core 5754 user: core
3473 <source> 5755 <source>
3474 *: none 5756 *: "Track Name Only"
3475 battery_types: "NiMH"
3476 </source> 5757 </source>
3477 <dest> 5758 <dest>
3478 *: none 5759 *: "트랙 ì´ë¦„만"
3479 battery_types: "니켈 수소"
3480 </dest> 5760 </dest>
3481 <voice> 5761 <voice>
3482 *: none 5762 *: "트랙 ì´ë¦„만"
3483 battery_types: "니켈 수소"
3484 </voice> 5763 </voice>
3485</phrase> 5764</phrase>
3486<phrase> 5765<phrase>
3487 id: LANG_SPINDOWN 5766 id: LANG_REMOVE
3488 desc: in settings_menu 5767 desc: in playlist viewer on+play menu
3489 user: core 5768 user: core
3490 <source> 5769 <source>
3491 *: "Disk Spindown" 5770 *: "Remove"
3492 flash_storage: none
3493 </source> 5771 </source>
3494 <dest> 5772 <dest>
3495 *: "ë””ìŠ¤í¬ íšŒì „ì‹œê°„" 5773 *: "제거"
3496 flash_storage: none
3497 </dest> 5774 </dest>
3498 <voice> 5775 <voice>
3499 *: "ë””ìŠ¤í¬ íšŒì „ì‹œê°„" 5776 *: "제거"
3500 flash_storage: none
3501 </voice> 5777 </voice>
3502</phrase> 5778</phrase>
3503<phrase> 5779<phrase>
3504 id: LANG_DIRCACHE_ENABLE 5780 id: LANG_SAVE_DYNAMIC_PLAYLIST
3505 desc: in directory cache settings 5781 desc: in playlist menu.
3506 user: core 5782 user: core
3507 <source> 5783 <source>
3508 *: none 5784 *: "Save Current Playlist"
3509 dircache: "Directory Cache"
3510 </source> 5785 </source>
3511 <dest> 5786 <dest>
3512 *: none 5787 *: "현재 재ìƒëª©ë¡ 저장"
3513 dircache: "디렉토리 ìºì‰¬"
3514 </dest> 5788 </dest>
3515 <voice> 5789 <voice>
3516 *: none 5790 *: "현재 재ìƒëª©ë¡ 저장"
3517 dircache: "디렉토리 ìºì‰¬"
3518 </voice> 5791 </voice>
3519</phrase> 5792</phrase>
3520<phrase> 5793<phrase>
3521 id: LANG_TIME 5794 id: LANG_PLAYLIST_SAVE_COUNT
3522 desc: Used on the bookmark select window to label elapsed time 5795 desc: splash number of tracks saved
3523 user: core 5796 user: core
3524 <source> 5797 <source>
3525 *: "Time" 5798 *: "Saved %d tracks (%s)"
3526 </source> 5799 </source>
3527 <dest> 5800 <dest>
3528 *: "í˜„ìž¬ì‹œê° ì„¤ì •" 5801 *: "%d ê°œ 트랙 ì €ìž¥í¨ (%s)"
3529 </dest> 5802 </dest>
3530 <voice> 5803 <voice>
3531 *: "í˜„ìž¬ì‹œê° ì„¤ì " 5804 *: "트ëžì´ 저장ë˜ì—ˆìŒ"
3532 </voice> 5805 </voice>
3533</phrase> 5806</phrase>
3534<phrase> 5807<phrase>
3535 id: LANG_TIMEFORMAT 5808 id: LANG_CATALOG
3536 desc: select the time format of time in status bar 5809 desc: in main menu and onplay menu
3537 user: core 5810 user: core
3538 <source> 5811 <source>
3539 *: "Time Format" 5812 *: "Playlist Catalogue"
3540 </source> 5813 </source>
3541 <dest> 5814 <dest>
3542 *: "시간 형ì" 5815 *: "재ìƒëª©ë¡ 카탈로그"
3543 </dest> 5816 </dest>
3544 <voice> 5817 <voice>
3545 *: "시간 형ì" 5818 *: "재ìƒëª©ë¡ 카탈로그"
3546 </voice> 5819 </voice>
3547</phrase> 5820</phrase>
3548<phrase> 5821<phrase>
3549 id: LANG_12_HOUR_CLOCK 5822 id: LANG_RECURSE_DIRECTORY
3550 desc: option for 12 hour clock 5823 desc: In playlist menu
3551 user: core 5824 user: core
3552 <source> 5825 <source>
3553 *: "12 Hour Clock" 5826 *: "Recursively Insert Directories"
3554 </source> 5827 </source>
3555 <dest> 5828 <dest>
3556 *: "12ì‹œê°" 5829 *: "ìŒì•… 추가시 하위 í´ë”ë„ ì¶”ê°"
3557 </dest> 5830 </dest>
3558 <voice> 5831 <voice>
3559 *: "12ì‹œê°" 5832 *: "ìŒì•… 추가시 하위 í´ë”ë„ ì¶”ê°"
3560 </voice> 5833 </voice>
3561</phrase> 5834</phrase>
3562<phrase> 5835<phrase>
3563 id: LANG_24_HOUR_CLOCK 5836 id: LANG_RECURSE_DIRECTORY_QUESTION
3564 desc: option for 24 hour clock 5837 desc: Asked from onplay screen
3565 user: core 5838 user: core
3566 <source> 5839 <source>
3567 *: "24 Hour Clock" 5840 *: "Recursively?"
3568 </source> 5841 </source>
3569 <dest> 5842 <dest>
3570 *: "24ìœê°" 5843 *: "하위 í´ë”ë„ ì¶”ê°€í• ê¹Œìš”?"
3571 </dest> 5844 </dest>
3572 <voice> 5845 <voice>
3573 *: "24ìœê°" 5846 *: "하위 í´ë”ë„ ì¶”ê°€í• ê¹Œìš”?"
3574 </voice> 5847 </voice>
3575</phrase> 5848</phrase>
3576<phrase> 5849<phrase>
3577 id: LANG_MAX_FILES_IN_DIR 5850 id: LANG_WARN_ERASEDYNPLAYLIST_MENU
3578 desc: in settings_menu 5851 desc: in playlist options menu, option to warn when erasing dynamic playlist
3579 user: core 5852 user: core
3580 <source> 5853 <source>
3581 *: "Max Entries in File Browser" 5854 *: "Warn When Erasing Dynamic Playlist"
3582 </source> 5855 </source>
3583 <dest> 5856 <dest>
3584 *: "í´ë” ë‚´ 최대 íŒŒì¼ ìˆ˜" 5857 *: "현재 재ìƒëª©ë¡ 삭제시 경고"
3585 </dest> 5858 </dest>
3586 <voice> 5859 <voice>
3587 *: "í´ë” ë‚´ 최대 íŒŒì¼ ìˆ˜" 5860 *: "현재 재ìƒëª©ë¡ 삭제시 경고"
3588 </voice> 5861 </voice>
3589</phrase> 5862</phrase>
3590<phrase> 5863<phrase>
3591 id: LANG_MAX_FILES_IN_PLAYLIST 5864 id: LANG_WARN_ERASEDYNPLAYLIST_PROMPT
3592 desc: in settings_menu 5865 desc: prompt shown when about to erase a modified dynamic playlist
3593 user: core 5866 user: core
3594 <source> 5867 <source>
3595 *: "Max Playlist Size" 5868 *: "Erase dynamic playlist?"
3596 </source> 5869 </source>
3597 <dest> 5870 <dest>
3598 *: "재ìƒëª©ë¡ì˜ 최대 í¬ê¸°" 5871 *: "현재 재ìƒëª©ë¡ì 삭제할까요?"
3599 </dest> 5872 </dest>
3600 <voice> 5873 <voice>
3601 *: "재ìƒëª©ë¡ì˜ 최대 í¬ê¸°" 5874 *: "현재 재ìƒëª©ë¡ì 삭제할까요?"
3602 </voice> 5875 </voice>
3603</phrase> 5876</phrase>
3604<phrase> 5877<phrase>
3605 id: LANG_PLAYLIST 5878 id: LANG_ROCKBOX_INFO
3606 desc: Used when you need to say playlist, also voiced 5879 desc: displayed topmost on the info screen and in the info menu
3607 user: core 5880 user: core
3608 <source> 5881 <source>
3609 *: "Playlist" 5882 *: "Rockbox Info"
3610 </source> 5883 </source>
3611 <dest> 5884 <dest>
3612 *: "현재 재ìƒëª©ë¡ 관리" 5885 *: "ë¡ë°•ìŠ¤ ì •ë³´"
3613 </dest> 5886 </dest>
3614 <voice> 5887 <voice>
3615 *: "현재 재ìƒëª©ë¡ 관리" 5888 *: "ë¡ë°•ìŠ¤ ì •ë³´"
3616 </voice> 5889 </voice>
3617</phrase> 5890</phrase>
3618<phrase> 5891<phrase>
3619 id: LANG_BOOKMARK_MENU 5892 id: LANG_BUFFER_STAT
3620 desc: Text on main menu to get to bookmark commands 5893 desc: the buffer size, %d MB %d fraction of MB
3621 user: core 5894 user: core
3622 <source> 5895 <source>
3623 *: "Bookmarks" 5896 *: "Buffer:"
3624 </source> 5897 </source>
3625 <dest> 5898 <dest>
3626 *: "ë¶ë§ˆí¬ 관리" 5899 *: "버í¼:"
3627 </dest> 5900 </dest>
3628 <voice> 5901 <voice>
3629 *: "ë¶ë§ˆí¬ 관리" 5902 *: "ë²„í¼ í¬ê¸°"
3630 </voice> 5903 </voice>
3631</phrase> 5904</phrase>
3632<phrase> 5905<phrase>
3633 id: LANG_MENU_SHOW_ID3_INFO 5906 id: LANG_BATTERY_TIME
3634 desc: Menu option to start tag viewer 5907 desc: battery level in % and estimated time remaining
3635 user: core 5908 user: core
3636 <source> 5909 <source>
3637 *: "Show Track Info" 5910 *: "Battery: %d%% %dh %dm"
5911 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
3638 </source> 5912 </source>
3639 <dest> 5913 <dest>
3640 *: "ID3 정보 보기" 5914 *: "배터리: %d%% %dh %dm"
5915 ipodmini1g,ipodmini2g,iriverh10: "배터리: %d%% %dh %dm"
3641 </dest> 5916 </dest>
3642 <voice> 5917 <voice>
3643 *: "ID3 정보 보기" 5918 *: "배터리 잔량"
3644 </voice> 5919 </voice>
3645</phrase> 5920</phrase>
3646<phrase> 5921<phrase>
3647 id: LANG_MENU_SET_RATING 5922 id: LANG_DISK_SIZE_INFO
3648 desc: Set the rating of a file in the wps context menu 5923 desc: disk size info
3649 user: core 5924 user: core
3650 <source> 5925 <source>
3651 *: "Set Song Rating" 5926 *: "Disk:"
3652 </source> 5927 </source>
3653 <dest> 5928 <dest>
3654 *: "ìŒì•… í‰ì  주기" 5929 *: "디스í¬:"
3655 </dest> 5930 </dest>
3656 <voice> 5931 <voice>
3657 *: "ìŒì•… í‰ì  주기" 5932 *: "ë””ìŠ¤í¬ í¬ê¸°"
3658 </voice> 5933 </voice>
3659</phrase> 5934</phrase>
3660<phrase> 5935<phrase>
3661 id: LANG_RENAME 5936 id: LANG_DISK_FREE_INFO
3662 desc: The verb/action Rename 5937 desc: disk size info
3663 user: core 5938 user: core
3664 <source> 5939 <source>
3665 *: "Rename" 5940 *: "Free:"
3666 </source> 5941 </source>
3667 <dest> 5942 <dest>
3668 *: "ì´ë¦„ 바꾸기" 5943 *: "여유:"
3669 </dest> 5944 </dest>
3670 <voice> 5945 <voice>
3671 *: "ì´ë¦„ 바꾸기" 5946 *: "여유 디스í¬ê³µê°"
3672 </voice> 5947 </voice>
3673</phrase> 5948</phrase>
3674<phrase> 5949<phrase>
3675 id: LANG_CUT 5950 id: LANG_DISK_NAME_INTERNAL
3676 desc: The verb/action Cut 5951 desc: in info menu; name for internal disk with multivolume (keep short!)
3677 user: core 5952 user: core
3678 <source> 5953 <source>
3679 *: "Cut" 5954 *: "Int:"
5955 hibylinux: "mSD:"
5956 xduoox3: "mSD1:"
3680 </source> 5957 </source>
3681 <dest> 5958 <dest>
3682 *: "잘ë¼ë‚´ê¸°" 5959 *: "내부:"
5960 hibylinux: "마ì´í¬ë¡œSD:"
5961 xduoox3: "마ì´í¬ë¡œSD1:"
3683 </dest> 5962 </dest>
3684 <voice> 5963 <voice>
3685 *: "잘ë¼ë‚´ê¸°" 5964 *: "내부"
5965 hibylinux: "마ì´í¬ë¡œ S D"
5966 xduoox3: "마ì´í¬ë¡œ S D 1"
3686 </voice> 5967 </voice>
3687</phrase> 5968</phrase>
3688<phrase> 5969<phrase>
3689 id: LANG_COPY 5970 id: LANG_DISK_NAME_MMC
3690 desc: The verb/action Copy 5971 desc: in info menu; name for external disk with multivolume (keep short!)
3691 user: core 5972 user: core
3692 <source> 5973 <source>
3693 *: "Copy" 5974 *: none
5975 hibylinux: "USB:"
5976 multivolume: "HD1:"
5977 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
5978 xduoox3: "mSD2:"
3694 </source> 5979 </source>
3695 <dest> 5980 <dest>
3696 *: "복사하기" 5981 *: none
5982 hibylinux: "~USB:"
5983 multivolume: "~HD1:"
5984 sansac200*,sansaclipplus,sansae200*,sansafuze*: "마ì´í¬ë¡œSD:"
5985 xduoox3: "마ì´í¬ë¡œSD2:"
3697 </dest> 5986 </dest>
3698 <voice> 5987 <voice>
3699 *: "복사하기" 5988 *: none
5989 hibylinux: "~U S B"
5990 multivolume: "~H D 1"
5991 sansac200*,sansaclipplus,sansae200*,sansafuze*: "마ì´í¬ë¡œ S D"
5992 xduoox3: "마ì´í¬ë¡œ S D 2"
3700 </voice> 5993 </voice>
3701</phrase> 5994</phrase>
3702<phrase> 5995<phrase>
3703 id: LANG_PASTE 5996 id: LANG_VERSION
3704 desc: The verb/action Paste 5997 desc: in the Rockbox Info screen
3705 user: core 5998 user: core
3706 <source> 5999 <source>
3707 *: "Paste" 6000 *: "Version"
3708 </source> 6001 </source>
3709 <dest> 6002 <dest>
3710 *: "붙여ë£ê¸°" 6003 *: "버전"
3711 </dest> 6004 </dest>
3712 <voice> 6005 <voice>
3713 *: "붙여ë£ê¸°" 6006 *: "버전"
3714 </voice> 6007 </voice>
3715</phrase> 6008</phrase>
3716<phrase> 6009<phrase>
3717 id: LANG_REALLY_OVERWRITE 6010 id: LANG_RUNNING_TIME
3718 desc: The verb/action Paste 6011 desc: in run time screen
3719 user: core 6012 user: core
3720 <source> 6013 <source>
3721 *: "File/directory exists. Overwrite?" 6014 *: "Running Time"
3722 </source> 6015 </source>
3723 <dest> 6016 <dest>
3724 *: "ê°™ì€ ì´ë¦„ì´ ì¡´ìž¬í•©ë‹ˆë‹¤. ë®ì–´ 쓰시겠습니까?" 6017 *: "ìžë 시간"
3725 </dest> 6018 </dest>
3726 <voice> 6019 <voice>
3727 *: "" 6020 *: "ìž‘ë™ ì‹œê°„"
3728 </voice> 6021 </voice>
3729</phrase> 6022</phrase>
3730<phrase> 6023<phrase>
3731 id: LANG_DELETE 6024 id: LANG_TOP_TIME
3732 desc: The verb/action Delete 6025 desc: in run time screen
3733 user: core 6026 user: core
3734 <source> 6027 <source>
3735 *: "Delete" 6028 *: "Top Time"
3736 </source> 6029 </source>
3737 <dest> 6030 <dest>
3738 *: "삭제하기" 6031 *: "ëˆ„ì  ì‹œê°"
3739 </dest> 6032 </dest>
3740 <voice> 6033 <voice>
3741 *: "삭제하기" 6034 *: "ëˆ„ì  ì‹œê°"
3742 </voice> 6035 </voice>
3743</phrase> 6036</phrase>
3744<phrase> 6037<phrase>
3745 id: LANG_SET_AS_BACKDROP 6038 id: LANG_CLEAR_TIME
3746 desc: text for onplay menu entry 6039 desc: in run time screen
3747 user: core 6040 user: core
3748 <source> 6041 <source>
3749 *: none 6042 *: "Clear Time?"
3750 lcd_non-mono: "Set As Backdrop"
3751 </source> 6043 </source>
3752 <dest> 6044 <dest>
3753 *: none 6045 *: "ì‹œê°„ì„ ì§€ìš¸ê¹Œìš”?"
3754 lcd_non-mono: "배경화면으로 설정"
3755 </dest> 6046 </dest>
3756 <voice> 6047 <voice>
3757 *: none 6048 *: "ì‹œê°„ì„ ì§€ìš¸ê¹Œìš”?"
3758 lcd_non-mono: "배경화면으로 설정"
3759 </voice> 6049 </voice>
3760</phrase> 6050</phrase>
3761<phrase> 6051<phrase>
3762 id: LANG_DELETE_DIR 6052 id: LANG_DEBUG
3763 desc: in on+play menu 6053 desc: in the info menu
3764 user: core 6054 user: core
3765 <source> 6055 <source>
3766 *: "Delete Directory" 6056 *: "Debug (Keep Out!)"
3767 </source> 6057 </source>
3768 <dest> 6058 <dest>
3769 *: "삭제하기" 6059 *: "디버깅 모드 (ì ê·¼ê¸ˆì§€!)"
3770 </dest> 6060 </dest>
3771 <voice> 6061 <voice>
3772 *: "삭제하기" 6062 *: "디버깅 모드, ì ê·¼ê¸ˆì§€!"
3773 </voice> 6063 </voice>
3774</phrase> 6064</phrase>
3775<phrase> 6065<phrase>
3776 id: LANG_REALLY_DELETE 6066 id: LANG_PLAYLIST
3777 desc: Really Delete? 6067 desc: Used when you need to say playlist, also voiced
3778 user: core 6068 user: core
3779 <source> 6069 <source>
3780 *: "Delete?" 6070 *: "Playlist"
3781 </source> 6071 </source>
3782 <dest> 6072 <dest>
3783 *: "삭제하시겠습니까?" 6073 *: "재ìƒëª©ë¡"
3784 </dest> 6074 </dest>
3785 <voice> 6075 <voice>
3786 *: "" 6076 *: "재ìƒëª©ë¡"
3787 </voice> 6077 </voice>
3788</phrase> 6078</phrase>
3789<phrase> 6079<phrase>
3790 id: LANG_DELETED 6080 id: LANG_QUEUE
3791 desc: A file has beed deleted 6081 desc: The verb/action Queue
3792 user: core 6082 user: core
3793 <source> 6083 <source>
3794 *: "Deleted" 6084 *: "Queue"
3795 </source> 6085 </source>
3796 <dest> 6086 <dest>
3797 *: "ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤." 6087 *: "대기열"
3798 </dest> 6088 </dest>
3799 <voice> 6089 <voice>
3800 *: "" 6090 *: "대기열"
3801 </voice> 6091 </voice>
3802</phrase> 6092</phrase>
3803<phrase> 6093<phrase>
3804 id: LANG_ONPLAY_OPEN_WITH 6094 id: LANG_QUEUE_FIRST
3805 desc: Onplay open with 6095 desc: in onplay menu. queue a track/playlist into dynamic playlist.
3806 user: core 6096 user: core
3807 <source> 6097 <source>
3808 *: "Open With..." 6098 *: "Queue Next"
3809 </source> 6099 </source>
3810 <dest> 6100 <dest>
3811 *: "플러그ì¸ìœ¼ë¡œ 열기" 6101 *: "대기열 다ìŒ"
3812 </dest> 6102 </dest>
3813 <voice> 6103 <voice>
3814 *: "플러그ì¸ìœ¼ë¡œ 열기" 6104 *: "대기열 다ìŒ"
3815 </voice> 6105 </voice>
3816</phrase> 6106</phrase>
3817<phrase> 6107<phrase>
3818 id: LANG_CREATE_DIR 6108 id: LANG_QUEUE_LAST
3819 desc: in main menu 6109 desc: in onplay menu. queue a track/playlist at end of playlist.
3820 user: core 6110 user: core
3821 <source> 6111 <source>
3822 *: "Create Directory" 6112 *: "Queue Last"
3823 </source> 6113 </source>
3824 <dest> 6114 <dest>
3825 *: "í´ë” 만들기" 6115 *: "ëŒê¸°ì—´ 마지막"
3826 </dest> 6116 </dest>
3827 <voice> 6117 <voice>
3828 *: "í´ë” 만들기" 6118 *: "ëŒê¸°ì—´ 마지막"
3829 </voice> 6119 </voice>
3830</phrase> 6120</phrase>
3831<phrase> 6121<phrase>
3832 id: LANG_PITCH 6122 id: LANG_QUEUE_SHUFFLED
3833 desc: "pitch" in the pitch screen 6123 desc: in onplay menu. queue a track/playlist randomly into dynamic playlist
3834 user: core 6124 user: core
3835 <source> 6125 <source>
3836 *: none 6126 *: "Queue Shuffled"
3837 pitchscreen: "Pitch"
3838 </source> 6127 </source>
3839 <dest> 6128 <dest>
3840 *: none 6129 *: "대기열 ì„žìŒ"
3841 pitchscreen: "피치"
3842 </dest> 6130 </dest>
3843 <voice> 6131 <voice>
3844 *: none 6132 *: "대기열 ì„žìŒ"
3845 pitchscreen: "피치" 6133 </voice>
6134</phrase>
6135<phrase>
6136 id: LANG_PLAYLIST_INSERT_COUNT
6137 desc: splash number of tracks inserted
6138 user: core
6139 <source>
6140 *: "Inserted %d tracks (%s)"
6141 </source>
6142 <dest>
6143 *: "%d ê°œ 트랙 ì‚½ìž…ë¨ (%s)"
6144 </dest>
6145 <voice>
6146 *: "ì‚½ìž…ëœ íŠ¸ëž™"
6147 </voice>
6148</phrase>
6149<phrase>
6150 id: LANG_PLAYLIST_QUEUE_COUNT
6151 desc: splash number of tracks queued
6152 user: core
6153 <source>
6154 *: "Queued %d tracks (%s)"
6155 </source>
6156 <dest>
6157 *: "%d ê°œ 트랙 ëŒ€ê¸°ë¨ (%s)"
6158 </dest>
6159 <voice>
6160 *: "ëŒ€ê¸°ëœ íŠ¸ëž™"
3846 </voice> 6161 </voice>
3847</phrase> 6162</phrase>
3848<phrase> 6163<phrase>
@@ -3860,6 +6175,34 @@
3860 </voice> 6175 </voice>
3861</phrase> 6176</phrase>
3862<phrase> 6177<phrase>
6178 id: LANG_SEARCH_IN_PLAYLIST
6179 desc: in playlist menu.
6180 user: core
6181 <source>
6182 *: "Search In Playlist"
6183 </source>
6184 <dest>
6185 *: "재ìƒëª©ë¡ì—ì„œ 검색"
6186 </dest>
6187 <voice>
6188 *: "재ìƒëª©ë¡ì—ì„œ 검색"
6189 </voice>
6190</phrase>
6191<phrase>
6192 id: LANG_PLAYLIST_SEARCH_MSG
6193 desc: splash number of tracks inserted
6194 user: core
6195 <source>
6196 *: "Searching... %d found (%s)"
6197 </source>
6198 <dest>
6199 *: "검색 중... %d ê°œ ì°¾ì•˜ìŒ (%s)"
6200 </dest>
6201 <voice>
6202 *: "찾았ìŒ"
6203 </voice>
6204</phrase>
6205<phrase>
3863 id: LANG_SHUFFLE_PLAYLIST 6206 id: LANG_SHUFFLE_PLAYLIST
3864 desc: in playlist menu, reshuffles the order in which songs are played 6207 desc: in playlist menu, reshuffles the order in which songs are played
3865 user: core 6208 user: core
@@ -3867,1293 +6210,1236 @@
3867 *: "Reshuffle" 6210 *: "Reshuffle"
3868 </source> 6211 </source>
3869 <dest> 6212 <dest>
3870 *: "재ìƒìˆœì„œ 다시섞기" 6213 *: "리셔플"
3871 </dest> 6214 </dest>
3872 <voice> 6215 <voice>
3873 *: "재ìƒìˆœì„œ 다시섞기" 6216 *: "리셔플"
3874 </voice> 6217 </voice>
3875</phrase> 6218</phrase>
3876<phrase> 6219<phrase>
3877 id: LANG_INSERT 6220 id: LANG_CATALOG_ADD_TO_NEW
3878 desc: in onplay menu. insert a track/playlist into dynamic playlist. 6221 desc: in onplay playlist catalogue submenu
3879 user: core 6222 user: core
3880 <source> 6223 <source>
3881 *: "Insert" 6224 *: "Add to New Playlist"
3882 </source> 6225 </source>
3883 <dest> 6226 <dest>
3884 *: "재ìƒëª©ë¡ì— 삽입" 6227 *: "새 재ìƒëª©ë¡ì— 추가"
3885 </dest> 6228 </dest>
3886 <voice> 6229 <voice>
3887 *: "재ìƒëª©ë¡ì— 삽입" 6230 *: "새 재ìƒëª©ë¡ì— 추가"
3888 </voice> 6231 </voice>
3889</phrase> 6232</phrase>
3890<phrase> 6233<phrase>
3891 id: LANG_INSERT_FIRST 6234 id: LANG_CATALOG_NO_DIRECTORY
3892 desc: in onplay menu. insert a track/playlist into dynamic playlist. 6235 desc: error message when playlist catalogue directory doesn't exist
3893 user: core 6236 user: core
3894 <source> 6237 <source>
3895 *: "Insert Next" 6238 *: "%s doesn't exist"
3896 </source> 6239 </source>
3897 <dest> 6240 <dest>
3898 *: "다ìŒê³¡ìœ¼ë¡œ 삽입" 6241 *: "%sì´(ê°€) 존재하지 ì•ŠìŒ"
3899 </dest> 6242 </dest>
3900 <voice> 6243 <voice>
3901 *: "다ìŒê³¡ìœ¼ë¡œ 삽ìž" 6244 *: "ìž¬ìƒ ëª©ë¡ ë””ë ‰í† ë¦¬ê°€ 존재하지 ì•ŠìŒ"
3902 </voice> 6245 </voice>
3903</phrase> 6246</phrase>
3904<phrase> 6247<phrase>
3905 id: LANG_INSERT_LAST 6248 id: LANG_CATALOG_NO_PLAYLISTS
3906 desc: in onplay menu. append a track/playlist into dynamic playlist. 6249 desc: error message when no playlists for playlist catalogue
3907 user: core 6250 user: core
3908 <source> 6251 <source>
3909 *: "Insert Last" 6252 *: "No Playlists"
3910 </source> 6253 </source>
3911 <dest> 6254 <dest>
3912 *: "ë§ˆì§€ë§‰ê³¡ì— ì‚½ìž…" 6255 *: "재ìƒëª©ë¡ ì—†ìŒ"
3913 </dest> 6256 </dest>
3914 <voice> 6257 <voice>
3915 *: "ë§ˆì§€ë§‰ê³¡ì— ì‚½ìž…" 6258 *: "재ìƒëª©ë¡ ì—†ìŒ"
3916 </voice> 6259 </voice>
3917</phrase> 6260</phrase>
3918<phrase> 6261<phrase>
3919 id: LANG_INSERT_SHUFFLED 6262 id: LANG_BOOKMARK_MENU
3920 desc: in onplay menu. insert a track/playlist randomly into dynamic playlist 6263 desc: Text on main menu to get to bookmark commands
3921 user: core 6264 user: core
3922 <source> 6265 <source>
3923 *: "Insert Shuffled" 6266 *: "Bookmarks"
3924 </source> 6267 </source>
3925 <dest> 6268 <dest>
3926 *: "ìž„ì˜ìˆœì„œë¡œ 삽입" 6269 *: "ë¶ë§ˆí¬"
3927 </dest> 6270 </dest>
3928 <voice> 6271 <voice>
3929 *: "ìž„ì˜ìˆœì„œë¡œ 삽입" 6272 *: "ë¶ë§ˆí¬"
3930 </voice> 6273 </voice>
3931</phrase> 6274</phrase>
3932<phrase> 6275<phrase>
3933 id: LANG_QUEUE 6276 id: LANG_BOOKMARK_MENU_CREATE
3934 desc: The verb/action Queue 6277 desc: Used off of the bookmark menu to create a bookmark
3935 user: core 6278 user: core
3936 <source> 6279 <source>
3937 *: "Queue" 6280 *: "Create Bookmark"
3938 </source> 6281 </source>
3939 <dest> 6282 <dest>
3940 *: "재ìƒëª©ë¡ì— 예약" 6283 *: "ëë§ˆí¬ ìƒì„±"
3941 </dest> 6284 </dest>
3942 <voice> 6285 <voice>
3943 *: "재ìƒëª©ë¡ì— 예약" 6286 *: "ëë§ˆí¬ ìƒì„±"
3944 </voice> 6287 </voice>
3945</phrase> 6288</phrase>
3946<phrase> 6289<phrase>
3947 id: LANG_QUEUE_FIRST 6290 id: LANG_BOOKMARK_MENU_LIST
3948 desc: in onplay menu. queue a track/playlist into dynamic playlist. 6291 desc: Used off of the bookmark menu to list available bookmarks for the currently playing directory or M3U
3949 user: core 6292 user: core
3950 <source> 6293 <source>
3951 *: "Queue Next" 6294 *: "List Bookmarks"
3952 </source> 6295 </source>
3953 <dest> 6296 <dest>
3954 *: "다ìŒê³¡ìœ¼ë¡œ 예약" 6297 *: "ëë§ˆí¬ ëª©ë¡"
3955 </dest> 6298 </dest>
3956 <voice> 6299 <voice>
3957 *: "다ìŒê³¡ìœ¼ë¡œ 예약" 6300 *: "ëë§ˆí¬ ëª©ë¡"
3958 </voice> 6301 </voice>
3959</phrase> 6302</phrase>
3960<phrase> 6303<phrase>
3961 id: LANG_QUEUE_LAST 6304 id: LANG_ONPLAY_MENU_TITLE
3962 desc: in onplay menu. queue a track/playlist at end of playlist. 6305 desc: title for the onplay menus
3963 user: core 6306 user: core
3964 <source> 6307 <source>
3965 *: "Queue Last" 6308 *: "Context Menu"
3966 </source> 6309 </source>
3967 <dest> 6310 <dest>
3968 *: "ë§ˆì§€ë§‰ê³¡ì— ì˜ˆì•½" 6311 *: "콘í…스트 메뉴"
3969 </dest> 6312 </dest>
3970 <voice> 6313 <voice>
3971 *: "ë§ˆì§€ë§‰ê³¡ì— ì˜ˆì•½" 6314 *: "콘í…스트 메뉴"
3972 </voice> 6315 </voice>
3973</phrase> 6316</phrase>
3974<phrase> 6317<phrase>
3975 id: LANG_QUEUE_SHUFFLED 6318 id: LANG_MENU_SET_RATING
3976 desc: in onplay menu. queue a track/playlist randomly into dynamic playlist 6319 desc: Set the rating of a file in the wps context menu
3977 user: core 6320 user: core
3978 <source> 6321 <source>
3979 *: "Queue Shuffled" 6322 *: "Set Song Rating"
3980 </source> 6323 </source>
3981 <dest> 6324 <dest>
3982 *: "ìž„ì˜ìˆœì„œë¡œ 예약" 6325 *: "노래 등급 설정"
3983 </dest> 6326 </dest>
3984 <voice> 6327 <voice>
3985 *: "ìž„ì˜ìˆœì„œë¡œ 예약" 6328 *: "노래 등급 설정"
3986 </voice> 6329 </voice>
3987</phrase> 6330</phrase>
3988<phrase> 6331<phrase>
3989 id: LANG_SEARCH_IN_PLAYLIST 6332 id: LANG_BROWSE_CUESHEET
3990 desc: in playlist menu. 6333 desc:
3991 user: core 6334 user: core
3992 <source> 6335 <source>
3993 *: "Search In Playlist" 6336 *: "Browse Cuesheet"
3994 </source> 6337 </source>
3995 <dest> 6338 <dest>
3996 *: "재ìƒëª©ë¡ì—ìœ ìŒì•ê²€ìƒ‰" 6339 *: "íìœíŠ¸ 찾아보기"
3997 </dest> 6340 </dest>
3998 <voice> 6341 <voice>
3999 *: "재ìƒëª©ë¡ì—ìœ ìŒì•ê²€ìƒ‰" 6342 *: "íìœíŠ¸ 찾아보기"
4000 </voice> 6343 </voice>
4001</phrase> 6344</phrase>
4002<phrase> 6345<phrase>
4003 id: LANG_PLAYLIST_SEARCH_MSG 6346 id: LANG_MENU_SHOW_ID3_INFO
4004 desc: splash number of tracks inserted 6347 desc: Menu option to start tag viewer
4005 user: core 6348 user: core
4006 <source> 6349 <source>
4007 *: "Searching... %d found (%s)" 6350 *: "Show Track Info"
4008 </source> 6351 </source>
4009 <dest> 6352 <dest>
4010 *: "ì°¾ëŠ ì¤‘... %dê°œ ê²€ìƒ‰ë¨ (%s)" 6353 *: "트랙 ì •ë³´ 표시"
4011 </dest> 6354 </dest>
4012 <voice> 6355 <voice>
4013 *: "" 6356 *: "트랙 정보 표시"
4014 </voice> 6357 </voice>
4015</phrase> 6358</phrase>
4016<phrase> 6359<phrase>
4017 id: LANG_BOOKMARK_MENU_CREATE 6360 id: LANG_ID3_TITLE
4018 desc: Used off of the bookmark menu to create a bookmark 6361 desc: in tag viewer
4019 user: core 6362 user: core
4020 <source> 6363 <source>
4021 *: "Create Bookmark" 6364 *: "Title"
4022 </source> 6365 </source>
4023 <dest> 6366 <dest>
4024 *: "ë¶ë§ˆí¬ 만들기" 6367 *: "제목"
4025 </dest> 6368 </dest>
4026 <voice> 6369 <voice>
4027 *: "ë¶ë§ˆí¬ 만들기" 6370 *: "제목"
4028 </voice> 6371 </voice>
4029</phrase> 6372</phrase>
4030<phrase> 6373<phrase>
4031 id: LANG_BOOKMARK_MENU_LIST 6374 id: LANG_ID3_ARTIST
4032 desc: Used off of the bookmark menu to list available bookmarks for the currently playing directory or M3U 6375 desc: in tag viewer
4033 user: core 6376 user: core
4034 <source> 6377 <source>
4035 *: "List Bookmarks" 6378 *: "Artist"
4036 </source> 6379 </source>
4037 <dest> 6380 <dest>
4038 *: "ë¶ë§ˆí¬ 목ë¡" 6381 *: "아티스트"
4039 </dest> 6382 </dest>
4040 <voice> 6383 <voice>
4041 *: "ë¶ë§ˆí¬ 목ë¡" 6384 *: "아티스트"
4042 </voice> 6385 </voice>
4043</phrase> 6386</phrase>
4044<phrase> 6387<phrase>
4045 id: LANG_ROCKBOX_INFO 6388 id: LANG_ID3_ALBUM
4046 desc: displayed topmost on the info screen and in the info menu 6389 desc: in tag viewer
4047 user: core 6390 user: core
4048 <source> 6391 <source>
4049 *: "Rockbox Info" 6392 *: "Album"
4050 </source> 6393 </source>
4051 <dest> 6394 <dest>
4052 *: "시스템 정보" 6395 *: "앨범"
4053 </dest> 6396 </dest>
4054 <voice> 6397 <voice>
4055 *: "시스템 정보" 6398 *: "앨범"
4056 </voice> 6399 </voice>
4057</phrase> 6400</phrase>
4058<phrase> 6401<phrase>
4059 id: LANG_BATTERY_CHARGE 6402 id: LANG_ID3_TRACKNUM
4060 desc: tells that the battery is charging, instead of battery level 6403 desc: in tag viewer
4061 user: core 6404 user: core
4062 <source> 6405 <source>
4063 *: none 6406 *: "Tracknum"
4064 charging: "Battery: Charging"
4065 </source> 6407 </source>
4066 <dest> 6408 <dest>
4067 *: none 6409 *: "트랙번호"
4068 charging: "배터리: 충전"
4069 </dest> 6410 </dest>
4070 <voice> 6411 <voice>
4071 *: none 6412 *: "트랙 번호"
4072 charging: "배터리: 충전"
4073 </voice> 6413 </voice>
4074</phrase> 6414</phrase>
4075<phrase> 6415<phrase>
4076 id: LANG_BATTERY_TOPOFF_CHARGE 6416 id: LANG_ID3_GENRE
4077 desc: in info display, shows that top off charge is running Only for V1 archosrecorder 6417 desc: in tag viewer
4078 user: core 6418 user: core
4079 <source> 6419 <source>
4080 *: none 6420 *: "Genre"
4081 archosrecorder: "Battery: Top-Off Chg"
4082 </source> 6421 </source>
4083 <dest> 6422 <dest>
4084 *: none 6423 *: "장르"
4085 archosrecorder: "배터리: Top-Off Chg"
4086 </dest> 6424 </dest>
4087 <voice> 6425 <voice>
4088 *: none 6426 *: "장르"
4089 archosrecorder: "배터리: Top-Off Chg"
4090 </voice> 6427 </voice>
4091</phrase> 6428</phrase>
4092<phrase> 6429<phrase>
4093 id: LANG_BATTERY_TRICKLE_CHARGE 6430 id: LANG_ID3_YEAR
4094 desc: in info display, shows that trickle charge is running 6431 desc: in tag viewer
4095 user: core 6432 user: core
4096 <source> 6433 <source>
4097 *: none 6434 *: "Year"
4098 charging: "Battery: Trickle Chg"
4099 </source> 6435 </source>
4100 <dest> 6436 <dest>
4101 *: none 6437 *: "ë…„ë„"
4102 charging: "배터리: Trickle Chg"
4103 </dest> 6438 </dest>
4104 <voice> 6439 <voice>
4105 *: none 6440 *: "ë…„ë„"
4106 charging: "배터리: Trickle Chg"
4107 </voice> 6441 </voice>
4108</phrase> 6442</phrase>
4109<phrase> 6443<phrase>
4110 id: LANG_BATTERY_TIME 6444 id: LANG_ID3_LENGTH
4111 desc: battery level in % and estimated time remaining 6445 desc: in tag viewer
4112 user: core 6446 user: core
4113 <source> 6447 <source>
4114 *: "Battery: %d%% %dh %dm" 6448 *: "Length"
4115 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
4116 iriverifp7xx: "%d%% %dh %dm"
4117 </source> 6449 </source>
4118 <dest> 6450 <dest>
4119 *: "배터리: %d%% (%d시간 %d분)" 6451 *: "길ì´"
4120 ipodmini1g,ipodmini2g,iriverh10: "배터리: %d%% %d시간 %d분"
4121 iriverifp7xx: "%d%% %d시간 %d분"
4122 </dest> 6452 </dest>
4123 <voice> 6453 <voice>
4124 *: "배터리 레벨" 6454 *: "길ì´"
4125 </voice> 6455 </voice>
4126</phrase> 6456</phrase>
4127<phrase> 6457<phrase>
4128 id: LANG_DISK_SIZE_INFO 6458 id: LANG_ID3_PLAYLIST
4129 desc: disk size info 6459 desc: in tag viewer
4130 user: core 6460 user: core
4131 <source> 6461 <source>
4132 *: "Disk:" 6462 *: "Playlist"
4133 </source> 6463 </source>
4134 <dest> 6464 <dest>
4135 *: "ì „ì²´ 용량:" 6465 *: "재ìƒëª©ë¡"
4136 </dest> 6466 </dest>
4137 <voice> 6467 <voice>
4138 *: "" 6468 *: "재ìƒëª©ë¡"
4139 </voice> 6469 </voice>
4140</phrase> 6470</phrase>
4141<phrase> 6471<phrase>
4142 id: LANG_DISK_FREE_INFO 6472 id: LANG_ID3_BITRATE
4143 desc: disk size info 6473 desc: in tag viewer
4144 user: core 6474 user: core
4145 <source> 6475 <source>
4146 *: "Free:" 6476 *: "Bitrate"
4147 </source> 6477 </source>
4148 <dest> 6478 <dest>
4149 *: "남ì용량:" 6479 *: "비트전송률"
4150 </dest> 6480 </dest>
4151 <voice> 6481 <voice>
4152 *: "ë‚¨ì€ ìš©ëŸ‰" 6482 *: "비트 전송률"
4153 </voice> 6483 </voice>
4154</phrase> 6484</phrase>
4155<phrase> 6485<phrase>
4156 id: LANG_DISK_NAME_INTERNAL 6486 id: LANG_ID3_ALBUMARTIST
4157 desc: in info menu; name for internal disk with multivolume (keep short!) 6487 desc: in tag viewer
4158 user: core 6488 user: core
4159 <source> 6489 <source>
4160 *: "Int:" 6490 *: "Album Artist"
4161 xduoox3: "mSD1:"
4162 </source> 6491 </source>
4163 <dest> 6492 <dest>
4164 *: "내부:" 6493 *: "앨범 아티스트"
4165 xduoox3: "mSD1:"
4166 </dest> 6494 </dest>
4167 <voice> 6495 <voice>
4168 *: "내부" 6496 *: "앨범 아티스트"
4169 xduoox3: "micro S D 1"
4170 </voice> 6497 </voice>
4171</phrase> 6498</phrase>
4172<phrase> 6499<phrase>
4173 id: LANG_DISK_NAME_MMC 6500 id: LANG_ID3_DISCNUM
4174 desc: in info menu; name for external disk with multivolume (Ondio; keep short!) 6501 desc: in tag viewer
4175 user: core 6502 user: core
4176 <source> 6503 <source>
4177 *: none 6504 *: "Discnum"
4178 archosondio*: "MMC:"
4179 multivolume: "HD1"
4180 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
4181 xduoox3: "mSD2:"
4182 </source> 6505 </source>
4183 <dest> 6506 <dest>
4184 *: none 6507 *: "디스í¬ë²ˆí˜¸"
4185 archosondio*: "MMC:"
4186 multivolume: "HD1"
4187 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
4188 xduoox3: "mSD2:"
4189 </dest> 6508 </dest>
4190 <voice> 6509 <voice>
4191 *: none 6510 *: "ë””ìŠ¤í¬ ë²ˆí˜¸"
4192 archosondio*: "M M C"
4193 multivolume: "H D 1 "
4194 sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
4195 xduoox3: "micro S D 2"
4196 </voice> 6511 </voice>
4197</phrase> 6512</phrase>
4198<phrase> 6513<phrase>
4199 id: VOICE_CURRENT_TIME 6514 id: LANG_ID3_COMMENT
4200 desc: spoken only, for wall clock announce 6515 desc: in tag viewer
4201 user: core 6516 user: core
4202 <source> 6517 <source>
4203 *: none 6518 *: "Comment"
4204 rtc: ""
4205 </source> 6519 </source>
4206 <dest> 6520 <dest>
4207 *: none 6521 *: "설명"
4208 rtc: ""
4209 </dest> 6522 </dest>
4210 <voice> 6523 <voice>
4211 *: none 6524 *: "설명"
4212 rtc: "현재 시간:"
4213 </voice> 6525 </voice>
4214</phrase> 6526</phrase>
4215<phrase> 6527<phrase>
4216 id: LANG_PITCH_UP 6528 id: LANG_ID3_VBR
4217 desc: in wps 6529 desc: in browse_id3
4218 user: core 6530 user: core
4219 <source> 6531 <source>
4220 *: none 6532 *: " (VBR)"
4221 pitchscreen: "Pitch Up"
4222 </source> 6533 </source>
4223 <dest> 6534 <dest>
4224 *: none 6535 *: " (VBR)"
4225 pitchscreen: "피치 올리기"
4226 </dest> 6536 </dest>
4227 <voice> 6537 <voice>
4228 *: none 6538 *: "VBR"
4229 pitchscreen: ""
4230 </voice> 6539 </voice>
4231</phrase> 6540</phrase>
4232<phrase> 6541<phrase>
4233 id: LANG_PITCH_DOWN 6542 id: LANG_ID3_FREQUENCY
4234 desc: in wps 6543 desc: in tag viewer
4235 user: core 6544 user: core
4236 <source> 6545 <source>
4237 *: none 6546 *: "Frequency"
4238 pitchscreen: "Pitch Down"
4239 </source> 6547 </source>
4240 <dest> 6548 <dest>
4241 *: none 6549 *: "주파수"
4242 pitchscreen: "피치 내리기"
4243 </dest> 6550 </dest>
4244 <voice> 6551 <voice>
4245 *: none 6552 *: "주파수"
4246 pitchscreen: ""
4247 </voice> 6553 </voice>
4248</phrase> 6554</phrase>
4249<phrase> 6555<phrase>
4250 id: LANG_PAUSE 6556 id: LANG_ID3_TRACK_GAIN
4251 desc: in wps and recording trigger menu 6557 desc: in tag viewer
4252 user: core 6558 user: core
4253 <source> 6559 <source>
4254 *: "Pause" 6560 *: "Track Gain"
4255 </source> 6561 </source>
4256 <dest> 6562 <dest>
4257 *: "ì¼ì‹œì •ì§€" 6563 *: "트랙 게ì¸"
4258 </dest> 6564 </dest>
4259 <voice> 6565 <voice>
4260 *: "ì¼ì‹œ 정지" 6566 *: "트랙 게ì¸"
4261 </voice> 6567 </voice>
4262</phrase> 6568</phrase>
4263<phrase> 6569<phrase>
4264 id: LANG_AUTO_BOOKMARK_QUERY 6570 id: LANG_ID3_PATH
4265 desc: prompt for user to decide to create an bookmark 6571 desc: in tag viewer
4266 user: core 6572 user: core
4267 <source> 6573 <source>
4268 *: "Create a Bookmark?" 6574 *: "Path"
4269 </source> 6575 </source>
4270 <dest> 6576 <dest>
4271 *: "ë¶ë§ˆí¬ë¥¼ 만드시겠습니까?" 6577 *: "경로"
4272 </dest> 6578 </dest>
4273 <voice> 6579 <voice>
4274 *: "" 6580 *: "경로"
4275 </voice> 6581 </voice>
4276</phrase> 6582</phrase>
4277<phrase> 6583<phrase>
4278 id: LANG_BOOKMARK_CREATE_SUCCESS 6584 id: LANG_ID3_NO_INFO
4279 desc: Indicates bookmark was successfully created 6585 desc: in tag viewer
4280 user: core 6586 user: core
4281 <source> 6587 <source>
4282 *: "Bookmark Created" 6588 *: "<No Info>"
4283 </source> 6589 </source>
4284 <dest> 6590 <dest>
4285 *: "ë¶ë§ˆí¬ê°€ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤." 6591 *: "<ì •ë³´ ì—ìŒ>"
4286 </dest> 6592 </dest>
4287 <voice> 6593 <voice>
4288 *: "" 6594 *: "ì •ë³´ ì—†ìŒ"
4289 </voice> 6595 </voice>
4290</phrase> 6596</phrase>
4291<phrase> 6597<phrase>
4292 id: LANG_BOOKMARK_CREATE_FAILURE 6598 id: LANG_RENAME
4293 desc: Indicates bookmark was not created 6599 desc: The verb/action Rename
4294 user: core 6600 user: core
4295 <source> 6601 <source>
4296 *: "Bookmark Failed!" 6602 *: "Rename"
4297 </source> 6603 </source>
4298 <dest> 6604 <dest>
4299 *: "ë¶ë§ˆí¬ ìƒì±ì— 실패하였습니다." 6605 *: "ì´ë¦„ 바꾸기"
4300 </dest> 6606 </dest>
4301 <voice> 6607 <voice>
4302 *: "" 6608 *: "ì´ë¦„ 바꾸기"
4303 </voice> 6609 </voice>
4304</phrase> 6610</phrase>
4305<phrase> 6611<phrase>
4306 id: LANG_BOOKMARK_LOAD_EMPTY 6612 id: LANG_CUT
4307 desc: Indicates bookmark was empty 6613 desc: The verb/action Cut
4308 user: core 6614 user: core
4309 <source> 6615 <source>
4310 *: "Bookmark Empty" 6616 *: "Cut"
4311 </source> 6617 </source>
4312 <dest> 6618 <dest>
4313 *: "ë마í¬ê°€ 없습니다." 6619 *: "잘ë¼ë‚´ê¸°"
4314 </dest> 6620 </dest>
4315 <voice> 6621 <voice>
4316 *: "" 6622 *: "잘ë¼ë‚´ê¸°"
4317 </voice> 6623 </voice>
4318</phrase> 6624</phrase>
4319<phrase> 6625<phrase>
4320 id: LANG_TIME_REVERT 6626 id: LANG_COPY
4321 desc: used in set_time() 6627 desc: The verb/action Copy
4322 user: core 6628 user: core
4323 <source> 6629 <source>
4324 *: none 6630 *: "Copy"
4325 gigabeatfx: "A = Revert"
4326 gigabeats: "BACK = Revert"
4327 gogearsa9200,ipod*,mpiohd300,sansac200*: "MENU = Revert"
4328 iaudiom5,iaudiox5: "RECORD = Revert"
4329 iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
4330 iriverh100,iriverh120,iriverh300: "STOP = Revert"
4331 mrobe100: "DISPLAY = Revert"
4332 rtc: "OFF = Revert"
4333 samsungyh*: "REW = Revert"
4334 vibe500: "CANCEL = Revert"
4335 </source> 6631 </source>
4336 <dest> 6632 <dest>
4337 *: none 6633 *: "복사"
4338 gigabeatfx: "[ì „ì›]: ì›ìƒíƒœ 복구"
4339 gigabeats: "[ë’¤]: ì›ìƒíƒœ 복구"
4340 gogearsa9200,ipod*,mpiohd300,sansac200*: "[메뉴]: ì›ìƒíƒœ 복구"
4341 iaudiom5,iaudiox5: "[ë…¹ìŒ]: ì›ìƒíƒœ 복구"
4342 iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "[ì´ì „]: ì›ìƒíƒœ 복구"
4343 iriverh100,iriverh120,iriverh300: "[정지]: ì›ìƒíƒœ 복구"
4344 mrobe100: "[화면]: ì›ìƒíƒœ 복구"
4345 rtc: "[ë”]: ì›ìƒíƒœ 복구"
4346 samsungyh*: "REW: ì›ìƒíƒœ 복구"
4347 vibe500: "C: ì›ìƒíƒœ 복구"
4348 </dest> 6634 </dest>
4349 <voice> 6635 <voice>
4350 *: none 6636 *: "복사"
4351 </voice> 6637 </voice>
4352</phrase> 6638</phrase>
4353<phrase> 6639<phrase>
4354 id: LANG_RECORDING_TIME 6640 id: LANG_PASTE
4355 desc: Display of recorded time 6641 desc: The verb/action Paste
4356 user: core 6642 user: core
4357 <source> 6643 <source>
4358 *: none 6644 *: "Paste"
4359 recording: "Time:"
4360 </source> 6645 </source>
4361 <dest> 6646 <dest>
4362 *: none 6647 *: "붙여넣기"
4363 recording: "시간:"
4364 </dest> 6648 </dest>
4365 <voice> 6649 <voice>
4366 *: none 6650 *: "붙여넣기"
4367 recording: ""
4368 </voice> 6651 </voice>
4369</phrase> 6652</phrase>
4370<phrase> 6653<phrase>
4371 id: LANG_RECORDING_SIZE 6654 id: LANG_REALLY_OVERWRITE
4372 desc: Display of recorded file size 6655 desc: The verb/action Paste
4373 user: core 6656 user: core
4374 <source> 6657 <source>
4375 *: none 6658 *: "File/directory exists. Overwrite?"
4376 recording: "Size:"
4377 </source> 6659 </source>
4378 <dest> 6660 <dest>
4379 *: none 6661 *: "파ì¼/디렉토리가 존재합니다. ë®ì–´ì“¸ê¹Œìš”?"
4380 recording: "í¬ê¸°:"
4381 </dest> 6662 </dest>
4382 <voice> 6663 <voice>
4383 *: none 6664 *: "파ì¼/디렉토리가 존재합니다. ë®ì–´ì“¸ê¹Œìš”?"
4384 recording: ""
4385 </voice> 6665 </voice>
4386</phrase> 6666</phrase>
4387<phrase> 6667<phrase>
4388 id: LANG_RECORD_PRERECORD 6668 id: LANG_DELETE
4389 desc: in recording and radio screen 6669 desc: The verb/action Delete
4390 user: core 6670 user: core
4391 <source> 6671 <source>
4392 *: none 6672 *: "Delete"
4393 recording: "Pre-Recording"
4394 </source> 6673 </source>
4395 <dest> 6674 <dest>
4396 *: none 6675 *: "삭제"
4397 recording: "사전 ë…¹ìŒ"
4398 </dest> 6676 </dest>
4399 <voice> 6677 <voice>
4400 *: none 6678 *: "삭제"
4401 recording: ""
4402 </voice> 6679 </voice>
4403</phrase> 6680</phrase>
4404<phrase> 6681<phrase>
4405 id: LANG_DISK_FULL 6682 id: LANG_DELETE_DIR
4406 desc: in recording screen 6683 desc: in on+play menu
4407 user: core 6684 user: core
4408 <source> 6685 <source>
4409 *: none 6686 *: "Delete Directory"
4410 iaudiom5,iaudiox5: "The disk is full. Press POWER to continue."
4411 iriverh100,iriverh120,iriverh300: "The disk is full. Press STOP to continue."
4412 recording: "The disk is full. Press OFF to continue."
4413 samsungyh*: "Disk full. Press LEFT to continue."
4414 sansac200*,sansae200*,vibe500: "The disk is full. Press UP to continue."
4415 </source> 6687 </source>
4416 <dest> 6688 <dest>
4417 *: none 6689 *: "디렉토리 삭제"
4418 iaudiom5,iaudiox5: "ë””ìŠ¤í¬ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. [ì „ì›] ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4419 iriverh100,iriverh120,iriverh300: "ë””ìŠ¤í¬ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. [정지] ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4420 recording: "ë””ìŠ¤í¬ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. [ë”] ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4421 samsungyh*: "ë””ìŠ¤í¬ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. LEFT ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4422 sansac200*,sansae200*,vibe500: "ë””ìŠ¤í¬ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. [위] ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4423 </dest> 6690 </dest>
4424 <voice> 6691 <voice>
4425 *: none 6692 *: "디렉토리 삭제"
4426 </voice> 6693 </voice>
4427</phrase> 6694</phrase>
4428<phrase> 6695<phrase>
4429 id: LANG_RECORD_TRIG_NOREARM 6696 id: LANG_REALLY_DELETE
4430 desc: in recording settings_menu 6697 desc: Really Delete?
4431 user: core 6698 user: core
4432 <source> 6699 <source>
4433 *: none 6700 *: "Delete?"
4434 recording: "Once"
4435 </source> 6701 </source>
4436 <dest> 6702 <dest>
4437 *: none 6703 *: "삭제할까요?"
4438 recording: "한번"
4439 </dest> 6704 </dest>
4440 <voice> 6705 <voice>
4441 *: none 6706 *: "ì •ë§ ì‚­ì œí• ê¹Œìš”?"
4442 recording: "한번"
4443 </voice> 6707 </voice>
4444</phrase> 6708</phrase>
4445<phrase> 6709<phrase>
4446 id: LANG_RECORD_START_THRESHOLD 6710 id: LANG_COPYING
4447 desc: in recording settings_menu 6711 desc:
4448 user: core 6712 user: core
4449 <source> 6713 <source>
4450 *: none 6714 *: "Copying..."
4451 recording: "Start Above"
4452 </source> 6715 </source>
4453 <dest> 6716 <dest>
4454 *: none 6717 *: "복사 중..."
4455 recording: "위ì—ì„œ 시작"
4456 </dest> 6718 </dest>
4457 <voice> 6719 <voice>
4458 *: none 6720 *: "복사 중"
4459 recording: "위ì—ì„œ 시작"
4460 </voice> 6721 </voice>
4461</phrase> 6722</phrase>
4462<phrase> 6723<phrase>
4463 id: LANG_RECORD_STOP_THRESHOLD 6724 id: LANG_DELETING
4464 desc: in recording settings_menu 6725 desc:
4465 user: core 6726 user: core
4466 <source> 6727 <source>
4467 *: none 6728 *: "Deleting..."
4468 recording: "Stop Below"
4469 </source> 6729 </source>
4470 <dest> 6730 <dest>
4471 *: none 6731 *: "삭제 중..."
4472 recording: "아래ì—ì„œ 멈춤"
4473 </dest> 6732 </dest>
4474 <voice> 6733 <voice>
4475 *: none 6734 *: "삭제 중"
4476 recording: "아래ì—ì„œ 멈춤"
4477 </voice> 6735 </voice>
4478</phrase> 6736</phrase>
4479<phrase> 6737<phrase>
4480 id: LANG_RECORD_STOP_GAP 6738 id: LANG_MOVING
4481 desc: in recording settings_menu 6739 desc:
4482 user: core 6740 user: core
4483 <source> 6741 <source>
4484 *: none 6742 *: "Moving..."
4485 recording: "Presplit Gap"
4486 </source> 6743 </source>
4487 <dest> 6744 <dest>
4488 *: none 6745 *: "ì´ë™ 중..."
4489 recording: "미리 나눔 갭"
4490 </dest> 6746 </dest>
4491 <voice> 6747 <voice>
4492 *: none 6748 *: "ì´ë™ 중"
4493 recording: "미리 나눔 갭"
4494 </voice> 6749 </voice>
4495</phrase> 6750</phrase>
4496<phrase> 6751<phrase>
4497 id: LANG_DB_INF 6752 id: LANG_DELETED
4498 desc: -inf db for values below measurement 6753 desc: A file has beed deleted
4499 user: core 6754 user: core
4500 <source> 6755 <source>
4501 *: none 6756 *: "Deleted"
4502 recording: "-inf"
4503 </source> 6757 </source>
4504 <dest> 6758 <dest>
4505 *: none 6759 *: "ì‚­ì œë¨"
4506 recording: "마ì´ë„ˆìŠ¤ 무한대"
4507 </dest> 6760 </dest>
4508 <voice> 6761 <voice>
4509 *: none 6762 *: "ì‚­ì œë¨"
4510 recording: "마ì´ë„ˆìŠ¤ 무한대"
4511 </voice> 6763 </voice>
4512</phrase> 6764</phrase>
4513<phrase> 6765<phrase>
4514 id: LANG_ALARM_MOD_TIME 6766 id: LANG_SET_AS_BACKDROP
4515 desc: The current alarm time shown in the alarm menu for the RTC alarm mod. 6767 desc: text for onplay menu entry
4516 user: core 6768 user: core
4517 <source> 6769 <source>
4518 *: none 6770 *: none
4519 alarm: "Alarm Time:" 6771 lcd_non-mono: "Set As Backdrop"
4520 </source> 6772 </source>
4521 <dest> 6773 <dest>
4522 *: none 6774 *: none
4523 alarm: "알람 ì‹œê°:" 6775 lcd_non-mono: "배경화면으로 설정"
4524 </dest> 6776 </dest>
4525 <voice> 6777 <voice>
4526 *: none 6778 *: none
4527 alarm: "" 6779 lcd_non-mono: "배경화면으로 설정"
4528 </voice> 6780 </voice>
4529</phrase> 6781</phrase>
4530<phrase> 6782<phrase>
4531 id: LANG_ALARM_MOD_TIME_TO_GO 6783 id: LANG_ONPLAY_OPEN_WITH
4532 desc: The time until the alarm will go off shown in the alarm menu for the RTC alarm mod. 6784 desc: Onplay open with
4533 user: core 6785 user: core
4534 <source> 6786 <source>
4535 *: none 6787 *: "Open With..."
4536 alarm: "Waking Up In %d:%02d"
4537 </source> 6788 </source>
4538 <dest> 6789 <dest>
4539 *: none 6790 *: "다ìŒìœ¼ë¡œ 열기..."
4540 alarm: "%d:%02d ì— ì¼œê¸°"
4541 </dest> 6791 </dest>
4542 <voice> 6792 <voice>
4543 *: none 6793 *: "다ìŒìœ¼ë¡œ 열기"
4544 alarm: "ì§€ì •ëœ ì‹œê°„ì— ì¼œê¸°"
4545 </voice> 6794 </voice>
4546</phrase> 6795</phrase>
4547<phrase> 6796<phrase>
4548 id: LANG_ALARM_MOD_SHUTDOWN 6797 id: LANG_CREATE_DIR
4549 desc: The text that tells the user that the alarm time is ok and the device shuts off (for the RTC alarm mod). 6798 desc: in main menu
4550 user: core 6799 user: core
4551 <source> 6800 <source>
4552 *: none 6801 *: "Create Directory"
4553 alarm: "Alarm Set"
4554 </source> 6802 </source>
4555 <dest> 6803 <dest>
4556 *: none 6804 *: "디렉토리 ìƒì„±"
4557 alarm: "알람 설정"
4558 </dest> 6805 </dest>
4559 <voice> 6806 <voice>
4560 *: none 6807 *: "디렉토리 ìƒì„±"
4561 alarm: "알람 설정"
4562 </voice> 6808 </voice>
4563</phrase> 6809</phrase>
4564<phrase> 6810<phrase>
4565 id: LANG_ALARM_MOD_ERROR 6811 id: LANG_PROPERTIES
4566 desc: The text that tells that the time is incorrect (for the RTC alarm mod). 6812 desc: browser file/dir properties
4567 user: core 6813 user: core
4568 <source> 6814 <source>
4569 *: none 6815 *: "Properties"
4570 alarm: "Alarm Time Is Too Soon!"
4571 </source> 6816 </source>
4572 <dest> 6817 <dest>
4573 *: none 6818 *: "ì†ì„±"
4574 alarm: "알람 ì‹œê°„ì´ ë„ˆë¬´ 짧습니다"
4575 </dest> 6819 </dest>
4576 <voice> 6820 <voice>
4577 *: none 6821 *: "ì†ì„±"
4578 alarm: "알람 ì‹œê°„ì´ ë„ˆë¬´ 짧습니다"
4579 </voice> 6822 </voice>
4580</phrase> 6823</phrase>
4581<phrase> 6824<phrase>
4582 id: LANG_ALARM_MOD_KEYS 6825 id: LANG_ADD_TO_FAVES
4583 desc: Shown key functions in alarm menu (for the RTC alarm mod). 6826 desc:
6827 user: core
6828 <source>
6829 *: "Add to Shortcuts"
6830 </source>
6831 <dest>
6832 *: "ë°”ë¡œê°€ê¸°ì— ì¶”ê°€"
6833 </dest>
6834 <voice>
6835 *: "ë°”ë¡œê°€ê¸°ì— ì¶”ê°€"
6836 </voice>
6837</phrase>
6838<phrase>
6839 id: LANG_PITCH
6840 desc: "pitch" in the pitch screen
4584 user: core 6841 user: core
4585 <source> 6842 <source>
4586 *: none 6843 *: none
4587 alarm: "PLAY=Set OFF=Cancel" 6844 pitchscreen: "Pitch"
4588 gigabeats: "SELECT=Set POWER=Cancel"
4589 ipod*: "SELECT=Set MENU=Cancel"
4590 iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
4591 mpiohd300: "ENTER=Set MENU=Cancel"
4592 sansafuzeplus: "SELECT=Set BACK=Cancel"
4593 vibe500: "OK=Set C=Cancel"
4594 </source> 6845 </source>
4595 <dest> 6846 <dest>
4596 *: none 6847 *: none
4597 alarm: "[재ìƒ]: 설정 [꺼ì§]: 취소" 6848 pitchscreen: "피치"
4598 gigabeats: "[ì„ íƒ]: 설정 [꺼ì§]: 취소"
4599 ipod*: "[ì„ íƒ]: 설정 [메뉴]: 취소"
4600 iriverh10,iriverh10_5gb: "[ì„ íƒ]: 설정 [ì´ì „]: 취소"
4601 mpiohd300: "ENTER: 설정 MENU: 취소"
4602 vibe500: "OK: 설정 C: 취소"
4603 </dest> 6849 </dest>
4604 <voice> 6850 <voice>
4605 *: none 6851 *: none
6852 pitchscreen: "피치"
4606 </voice> 6853 </voice>
4607</phrase> 6854</phrase>
4608<phrase> 6855<phrase>
4609 id: LANG_ALARM_MOD_DISABLE 6856 id: LANG_PITCH_UP
4610 desc: Announce that the RTC alarm has been turned off 6857 desc: in wps
4611 user: core 6858 user: core
4612 <source> 6859 <source>
4613 *: none 6860 *: none
4614 alarm: "Alarm Disabled" 6861 pitchscreen: "Pitch Up"
4615 </source> 6862 </source>
4616 <dest> 6863 <dest>
4617 *: none 6864 *: none
4618 alarm: "알람 ë„기" 6865 pitchscreen: "피치 ì—…"
4619 </dest> 6866 </dest>
4620 <voice> 6867 <voice>
4621 *: none 6868 *: none
4622 alarm: "알람 ë„기" 6869 pitchscreen: ""
4623 </voice> 6870 </voice>
4624</phrase> 6871</phrase>
4625<phrase> 6872<phrase>
4626 id: LANG_COLOR_RGB_LABELS 6873 id: LANG_PITCH_DOWN
4627 desc: what to show for the 'R' 'G' 'B' ONE LETTER EACH 6874 desc: in wps
4628 user: core 6875 user: core
4629 <source> 6876 <source>
4630 *: none 6877 *: none
4631 lcd_color: "RGB" 6878 pitchscreen: "Pitch Down"
4632 </source> 6879 </source>
4633 <dest> 6880 <dest>
4634 *: none 6881 *: none
4635 lcd_color: "RGB" 6882 pitchscreen: "피치 다운"
4636 </dest> 6883 </dest>
4637 <voice> 6884 <voice>
4638 *: none 6885 *: none
4639 lcd_color: "RGB" 6886 pitchscreen: ""
4640 </voice> 6887 </voice>
4641</phrase> 6888</phrase>
4642<phrase> 6889<phrase>
4643 id: LANG_COLOR_RGB_VALUE 6890 id: LANG_PITCH_UP_SEMITONE
4644 desc: in color screen 6891 desc: in wps
4645 user: core 6892 user: core
4646 <source> 6893 <source>
4647 *: none 6894 *: none
4648 lcd_color: "RGB: %02X%02X%02X" 6895 pitchscreen: "Semitone Up"
4649 </source> 6896 </source>
4650 <dest> 6897 <dest>
4651 *: none 6898 *: none
4652 lcd_color: "RGB Code: %02X%02X%02X" 6899 pitchscreen: "ë°˜ìŒ ì˜¬ë¦¬ê¸°"
4653 </dest> 6900 </dest>
4654 <voice> 6901 <voice>
4655 *: none 6902 *: none
4656 lcd_color: "" 6903 pitchscreen: ""
4657 </voice> 6904 </voice>
4658</phrase> 6905</phrase>
4659<phrase> 6906<phrase>
4660 id: LANG_COLOR_UNACCEPTABLE 6907 id: LANG_PITCH_DOWN_SEMITONE
4661 desc: splash when user selects an invalid colour 6908 desc: in wps
4662 user: core 6909 user: core
4663 <source> 6910 <source>
4664 *: none 6911 *: none
4665 lcd_color: "Invalid colour" 6912 pitchscreen: "Semitone Down"
4666 </source> 6913 </source>
4667 <dest> 6914 <dest>
4668 *: none 6915 *: none
4669 lcd_color: "ìž˜ëª»ëœ ìƒ‰ê¹”ìž…ë‹ˆë‹¤." 6916 pitchscreen: "ë°˜ìŒ ë‚´ë¦¬ê¸°"
4670 </dest> 6917 </dest>
4671 <voice> 6918 <voice>
4672 *: none 6919 *: none
4673 lcd_color: "" 6920 pitchscreen: ""
4674 </voice> 6921 </voice>
4675</phrase> 6922</phrase>
4676<phrase> 6923<phrase>
4677 id: LANG_ID3_TITLE 6924 id: LANG_PLAYLIST_BUFFER_FULL
4678 desc: in tag viewer 6925 desc: in playlist.indices() when playlist is full
4679 user: core 6926 user: core
4680 <source> 6927 <source>
4681 *: "Title" 6928 *: "Playlist Buffer Full"
4682 </source> 6929 </source>
4683 <dest> 6930 <dest>
4684 *: "제목" 6931 *: "재ìƒëª©ë¡ 버í¼ê°€ ê°€ë“ ì°¸"
4685 </dest> 6932 </dest>
4686 <voice> 6933 <voice>
4687 *: "제목" 6934 *: "재ìƒëª©ë¡ 버í¼ê°€ ê°€ë“ ì°¸"
4688 </voice> 6935 </voice>
4689</phrase> 6936</phrase>
4690<phrase> 6937<phrase>
4691 id: LANG_ID3_ARTIST 6938 id: LANG_END_PLAYLIST
4692 desc: in tag viewer 6939 desc: when playlist has finished
4693 user: core 6940 user: core
4694 <source> 6941 <source>
4695 *: "Artist" 6942 *: "End of Song List"
4696 </source> 6943 </source>
4697 <dest> 6944 <dest>
4698 *: "ìŒì•ê°€" 6945 *: "ë¸ëž˜ ëª©ë¡ ë"
4699 </dest> 6946 </dest>
4700 <voice> 6947 <voice>
4701 *: "ìŒì•ê°€" 6948 *: "ë¸ëž˜ ëª©ë¡ ë"
4702 </voice> 6949 </voice>
4703</phrase> 6950</phrase>
4704<phrase> 6951<phrase>
4705 id: LANG_ID3_ALBUM 6952 id: LANG_CREATING
4706 desc: in tag viewer 6953 desc: Screen feedback during playlist creation
4707 user: core 6954 user: core
4708 <source> 6955 <source>
4709 *: "Album" 6956 *: "Creating"
4710 </source> 6957 </source>
4711 <dest> 6958 <dest>
4712 *: "앨범" 6959 *: "만들기"
4713 </dest> 6960 </dest>
4714 <voice> 6961 <voice>
4715 *: "앨범" 6962 *: ""
4716 </voice> 6963 </voice>
4717</phrase> 6964</phrase>
4718<phrase> 6965<phrase>
4719 id: LANG_ID3_TRACKNUM 6966 id: LANG_NOTHING_TO_RESUME
4720 desc: in tag viewer 6967 desc: Error message displayed when resume button pressed but no playlist
4721 user: core 6968 user: core
4722 <source> 6969 <source>
4723 *: "Tracknum" 6970 *: "Nothing to resume"
4724 </source> 6971 </source>
4725 <dest> 6972 <dest>
4726 *: "íŠ¸ëž ë²ˆí˜¸" 6973 *: "재시작할 파ì¼ì´ ì—†ìŒ"
4727 </dest> 6974 </dest>
4728 <voice> 6975 <voice>
4729 *: "íŠ¸ëž ë²ˆí˜¸" 6976 *: "재시작할 파ì¼ì´ ì—†ìŒ"
4730 </voice> 6977 </voice>
4731</phrase> 6978</phrase>
4732<phrase> 6979<phrase>
4733 id: LANG_ID3_GENRE 6980 id: LANG_PLAYLIST_ACCESS_ERROR
4734 desc: in tag viewer 6981 desc: Playlist error
4735 user: core 6982 user: core
4736 <source> 6983 <source>
4737 *: "Genre" 6984 *: "Error accessing playlist file"
4738 </source> 6985 </source>
4739 <dest> 6986 <dest>
4740 *: "장르" 6987 *: "재ìƒëª©ë¡ 파ì¼ì— ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4741 </dest> 6988 </dest>
4742 <voice> 6989 <voice>
4743 *: "장르" 6990 *: "재ìƒëª©ë¡ 파ì¼ì— ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4744 </voice> 6991 </voice>
4745</phrase> 6992</phrase>
4746<phrase> 6993<phrase>
4747 id: LANG_ID3_YEAR 6994 id: LANG_PLAYLIST_CONTROL_ACCESS_ERROR
4748 desc: in tag viewer 6995 desc: Playlist error
4749 user: core 6996 user: core
4750 <source> 6997 <source>
4751 *: "Year" 6998 *: "Error accessing playlist control file"
4752 </source> 6999 </source>
4753 <dest> 7000 <dest>
4754 *: "ì—°ë" 7001 *: "재ìƒëª©ë¡ 제어 파ì¼ì ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4755 </dest> 7002 </dest>
4756 <voice> 7003 <voice>
4757 *: "ì—°ë" 7004 *: "재ìƒëª©ë¡ 제어 파ì¼ì ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4758 </voice> 7005 </voice>
4759</phrase> 7006</phrase>
4760<phrase> 7007<phrase>
4761 id: LANG_ID3_LENGTH 7008 id: LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
4762 desc: in tag viewer 7009 desc: Playlist error
4763 user: core 7010 user: core
4764 <source> 7011 <source>
4765 *: "Length" 7012 *: "Error accessing directory"
4766 </source> 7013 </source>
4767 <dest> 7014 <dest>
4768 *: "길ì´" 7015 *: "디렉토리 ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4769 </dest> 7016 </dest>
4770 <voice> 7017 <voice>
4771 *: "길ì´" 7018 *: "디렉토리 ì ‘ì†í•˜ëŠ” 중 오류 ë°œìƒ"
4772 </voice> 7019 </voice>
4773</phrase> 7020</phrase>
4774<phrase> 7021<phrase>
4775 id: LANG_ID3_PLAYLIST 7022 id: LANG_PLAYLIST_CONTROL_INVALID
4776 desc: in tag viewer 7023 desc: Playlist resume error
4777 user: core 7024 user: core
4778 <source> 7025 <source>
4779 *: "Playlist" 7026 *: "Playlist control file is invalid"
4780 </source> 7027 </source>
4781 <dest> 7028 <dest>
4782 *: "재ìƒìˆœì„œ" 7029 *: "재ìƒëª©ë¡ 제어 파ì¼ì´ 잘못ë¨"
4783 </dest> 7030 </dest>
4784 <voice> 7031 <voice>
4785 *: "재ìƒìˆœì„œ" 7032 *: "재ìƒëª©ë¡ 제어 파ì¼ì´ 잘못ë¨"
4786 </voice> 7033 </voice>
4787</phrase> 7034</phrase>
4788<phrase> 7035<phrase>
4789 id: LANG_ID3_BITRATE 7036 id: LANG_PAUSE
4790 desc: in tag viewer 7037 desc: in wps and recording trigger menu
4791 user: core 7038 user: core
4792 <source> 7039 <source>
4793 *: "Bitrate" 7040 *: "Pause"
4794 </source> 7041 </source>
4795 <dest> 7042 <dest>
4796 *: "비트 ì ì†¡ë¥ " 7043 *: "ì¼ì‹œ ì ì§€"
4797 </dest> 7044 </dest>
4798 <voice> 7045 <voice>
4799 *: "비트 ì ì†¡ë¥ " 7046 *: "ì¼ì‹œ ì ì§€"
4800 </voice> 7047 </voice>
4801</phrase> 7048</phrase>
4802<phrase> 7049<phrase>
4803 id: LANG_ID3_VBR 7050 id: LANG_MODE
4804 desc: in browse_id3 7051 desc: in wps F2 pressed and radio screen
4805 user: core 7052 user: core
4806 <source> 7053 <source>
4807 *: " (VBR)" 7054 *: "Mode:"
4808 </source> 7055 </source>
4809 <dest> 7056 <dest>
4810 *: " (VBR)" 7057 *: "모드:"
4811 </dest> 7058 </dest>
4812 <voice> 7059 <voice>
4813 *: "VBR" 7060 *: "모드"
4814 </voice> 7061 </voice>
4815</phrase> 7062</phrase>
4816<phrase> 7063<phrase>
4817 id: LANG_ID3_FREQUENCY 7064 id: LANG_TIME
4818 desc: in tag viewer 7065 desc: Used on the bookmark select window to label elapsed time
4819 user: core 7066 user: core
4820 <source> 7067 <source>
4821 *: "Frequency" 7068 *: "Time"
4822 </source> 7069 </source>
4823 <dest> 7070 <dest>
4824 *: "오디오 샘플 ì†ë„" 7071 *: "시간"
4825 </dest> 7072 </dest>
4826 <voice> 7073 <voice>
4827 *: "오디오 샘플 ì†ë„" 7074 *: "시간"
4828 </voice> 7075 </voice>
4829</phrase> 7076</phrase>
4830<phrase> 7077<phrase>
4831 id: LANG_ID3_TRACK_GAIN 7078 id: LANG_USB_CHARGING
4832 desc: in tag viewer 7079 desc: in Battery menu
4833 user: core 7080 user: core
4834 <source> 7081 <source>
4835 *: "Track Gain" 7082 *: none
7083 usb_charging_enable: "Charge During USB Connection"
4836 </source> 7084 </source>
4837 <dest> 7085 <dest>
4838 *: "트랙 게ì¸" 7086 *: none
7087 usb_charging_enable: "USB 연결 중 충전"
4839 </dest> 7088 </dest>
4840 <voice> 7089 <voice>
4841 *: "트랙 게ì¸" 7090 *: none
7091 usb_charging_enable: "U S B 연결 중 충전"
4842 </voice> 7092 </voice>
4843</phrase> 7093</phrase>
4844<phrase> 7094<phrase>
4845 id: LANG_ID3_ALBUM_GAIN 7095 id: LANG_KEYLOCK_ON
4846 desc: in tag viewer 7096 desc: displayed when key lock is on
4847 user: core 7097 user: core
4848 <source> 7098 <source>
4849 *: "Album Gain" 7099 *: "Buttons Locked"
4850 </source> 7100 </source>
4851 <dest> 7101 <dest>
4852 *: "앨범 게ì¸" 7102 *: "ë²„íŠ¼ì´ ìž ê¸ˆ"
4853 </dest> 7103 </dest>
4854 <voice> 7104 <voice>
4855 *: "앨범 게ì¸" 7105 *: ""
4856 </voice> 7106 </voice>
4857</phrase> 7107</phrase>
4858<phrase> 7108<phrase>
4859 id: LANG_ID3_PATH 7109 id: LANG_KEYLOCK_OFF
4860 desc: in tag viewer 7110 desc: displayed when key lock is turned off
4861 user: core 7111 user: core
4862 <source> 7112 <source>
4863 *: "Path" 7113 *: "Buttons Unlocked"
4864 </source> 7114 </source>
4865 <dest> 7115 <dest>
4866 *: "íŒŒì¼ ìœ„ì¹˜" 7116 *: "버튼 잠금 í•´ì œ"
4867 </dest> 7117 </dest>
4868 <voice> 7118 <voice>
4869 *: "íŒŒì¼ ìœ„ì¹˜" 7119 *: ""
4870 </voice> 7120 </voice>
4871</phrase> 7121</phrase>
4872<phrase> 7122<phrase>
4873 id: LANG_WEEKDAY_SUNDAY 7123 id: LANG_RECORDING_TIME
4874 desc: Maximum 3-letter abbreviation for weekday 7124 desc: Display of recorded time
4875 user: core 7125 user: core
4876 <source> 7126 <source>
4877 *: none 7127 *: none
4878 rtc: "Sun" 7128 recording: "Time:"
4879 </source> 7129 </source>
4880 <dest> 7130 <dest>
4881 *: none 7131 *: none
4882 rtc: "ì¼" 7132 recording: "시간:"
4883 </dest> 7133 </dest>
4884 <voice> 7134 <voice>
4885 *: none 7135 *: none
4886 rtc: "" 7136 recording: ""
4887 </voice> 7137 </voice>
4888</phrase> 7138</phrase>
4889<phrase> 7139<phrase>
4890 id: LANG_WEEKDAY_MONDAY 7140 id: LANG_DISK_FULL
4891 desc: Maximum 3-letter abbreviation for weekday 7141 desc: in recording screen
4892 user: core 7142 user: core
4893 <source> 7143 <source>
4894 *: none 7144 *: none
4895 rtc: "Mon" 7145 iaudiom5,iaudiox5: "The disk is full. Press POWER to continue."
7146 iriverh100,iriverh120,iriverh300: "The disk is full. Press STOP to continue."
7147 recording: "The disk is full. Press OFF to continue."
7148 samsungyh*: "Disk full. Press LEFT to continue."
7149 sansac200*,sansae200*,vibe500: "The disk is full. Press PREV to continue."
4896 </source> 7150 </source>
4897 <dest> 7151 <dest>
4898 *: none 7152 *: none
4899 rtc: "ì›”" 7153 iaudiom5,iaudiox5: "디스í¬ê°€ 꽉 찼습니다. 계ì†í•˜ë ¤ë©´ ì „ì› ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
7154 iriverh100,iriverh120,iriverh300: "디스í¬ê°€ 꽉 찼습니다. 계ì†í•˜ë ¤ë©´ 정지 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
7155 recording: "디스í¬ê°€ 꽉 찼습니다. 계ì†í•˜ë ¤ë©´ ë” ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
7156 samsungyh*: "디스í¬ê°€ 꽉 찼습니다. 계ì†í•˜ë ¤ë©´ 왼쪽 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
7157 sansac200*,sansae200*,vibe500: "디스í¬ê°€ 꽉 찼습니다. 계ì†í•˜ë ¤ë©´ ì´ì „ ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
4900 </dest> 7158 </dest>
4901 <voice> 7159 <voice>
4902 *: none 7160 *: none
4903 rtc: "" 7161 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
4904 </voice> 7162 </voice>
4905</phrase> 7163</phrase>
4906<phrase> 7164<phrase>
4907 id: LANG_WEEKDAY_TUESDAY 7165 id: LANG_BOOT_CHANGED
4908 desc: Maximum 3-letter abbreviation for weekday 7166 desc: File browser discovered the boot file was changed
4909 user: core 7167 user: core
4910 <source> 7168 <source>
4911 *: none 7169 *: "Boot changed"
4912 rtc: "Tue"
4913 </source> 7170 </source>
4914 <dest> 7171 <dest>
4915 *: none 7172 *: "부팅 변경ë¨"
4916 rtc: "í™”"
4917 </dest> 7173 </dest>
4918 <voice> 7174 <voice>
4919 *: none 7175 *: "부팅 변경ë¨"
4920 rtc: ""
4921 </voice> 7176 </voice>
4922</phrase> 7177</phrase>
4923<phrase> 7178<phrase>
4924 id: LANG_WEEKDAY_WEDNESDAY 7179 id: LANG_REBOOT_NOW
4925 desc: Maximum 3-letter abbreviation for weekday 7180 desc: Do you want to reboot?
4926 user: core 7181 user: core
4927 <source> 7182 <source>
4928 *: none 7183 *: "Reboot now?"
4929 rtc: "Wed"
4930 </source> 7184 </source>
4931 <dest> 7185 <dest>
4932 *: none 7186 *: "지금 재부팅할까요?"
4933 rtc: "수"
4934 </dest> 7187 </dest>
4935 <voice> 7188 <voice>
4936 *: none 7189 *: "지금 재부팅할까요?"
4937 rtc: ""
4938 </voice> 7190 </voice>
4939</phrase> 7191</phrase>
4940<phrase> 7192<phrase>
4941 id: LANG_WEEKDAY_THURSDAY 7193 id: LANG_OFF_ABORT
4942 desc: Maximum 3-letter abbreviation for weekday 7194 desc: Used on many models
4943 user: core 7195 user: core
4944 <source> 7196 <source>
4945 *: none 7197 *: "OFF to abort"
4946 rtc: "Thu" 7198 gigabeatfx: "POWER to abort"
7199 gigabeats,sansafuzeplus: "BACK to abort"
7200 iaudiom5,iaudiox5: "Long PLAY to abort"
7201 ipod*: "PLAY/PAUSE to abort"
7202 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "PREV to abort"
7203 iriverh100,iriverh120,iriverh300: "STOP to abort"
4947 </source> 7204 </source>
4948 <dest> 7205 <dest>
4949 *: none 7206 *: "중단하려면 ë” ë²„íŠ¼"
4950 rtc: "목" 7207 gigabeatfx: "중단하려면 ì „ì› ë²„íŠ¼"
7208 gigabeats,sansafuzeplus: "중단하려면 뒤로가기 버튼"
7209 iaudiom5,iaudiox5: "중단하려면 길게 ìž¬ìƒ ë²„íŠ¼"
7210 ipod*: "중단하려면 재ìƒ/ì¼ì‹œì •ì§€ 버튼"
7211 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "중단하려면 ì´ì „ 버튼"
7212 iriverh100,iriverh120,iriverh300: "중단하려면 중지 버튼"
4951 </dest> 7213 </dest>
4952 <voice> 7214 <voice>
4953 *: none 7215 *: ""
4954 rtc: ""
4955 </voice> 7216 </voice>
4956</phrase> 7217</phrase>
4957<phrase> 7218<phrase>
4958 id: LANG_WEEKDAY_FRIDAY 7219 id: LANG_NO_FILES
4959 desc: Maximum 3-letter abbreviation for weekday 7220 desc: in settings_menu
4960 user: core 7221 user: core
4961 <source> 7222 <source>
4962 *: none 7223 *: "No files"
4963 rtc: "Fri"
4964 </source> 7224 </source>
4965 <dest> 7225 <dest>
4966 *: none 7226 *: "íŒŒì¼ ì—†ìŒ"
4967 rtc: "금"
4968 </dest> 7227 </dest>
4969 <voice> 7228 <voice>
4970 *: none 7229 *: "íŒŒì¼ ì—†ìŒ"
4971 rtc: ""
4972 </voice> 7230 </voice>
4973</phrase> 7231</phrase>
4974<phrase> 7232<phrase>
4975 id: LANG_WEEKDAY_SATURDAY 7233 id: LANG_KEYBOARD_LOADED
4976 desc: Maximum 3-letter abbreviation for weekday 7234 desc: shown when a keyboard has been loaded from the dir browser
4977 user: core 7235 user: core
4978 <source> 7236 <source>
4979 *: none 7237 *: "New Keyboard"
4980 rtc: "Sat"
4981 </source> 7238 </source>
4982 <dest> 7239 <dest>
4983 *: none 7240 *: "새 키보드"
4984 rtc: "토"
4985 </dest> 7241 </dest>
4986 <voice> 7242 <voice>
4987 *: none 7243 *: "새 키보드"
4988 rtc: ""
4989 </voice> 7244 </voice>
4990</phrase> 7245</phrase>
4991<phrase> 7246<phrase>
4992 id: LANG_MONTH_JANUARY 7247 id: LANG_PLUGIN_CANT_OPEN
4993 desc: Maximum 3-letter abbreviation for monthname 7248 desc: Plugin open error message
4994 user: core 7249 user: core
4995 <source> 7250 <source>
4996 *: "Jan" 7251 *: "Can't open %s"
4997 </source> 7252 </source>
4998 <dest> 7253 <dest>
4999 *: "1ì›”" 7254 *: "%s ì„(를) ì—´ 수 ì—†ìŒ"
5000 </dest> 7255 </dest>
5001 <voice> 7256 <voice>
5002 *: "1ì›”" 7257 *: ""
5003 </voice> 7258 </voice>
5004</phrase> 7259</phrase>
5005<phrase> 7260<phrase>
5006 id: LANG_MONTH_FEBRUARY 7261 id: LANG_READ_FAILED
5007 desc: Maximum 3-letter abbreviation for monthname 7262 desc: There was an error reading a file
5008 user: core 7263 user: core
5009 <source> 7264 <source>
5010 *: "Feb" 7265 *: "Failed reading %s"
5011 </source> 7266 </source>
5012 <dest> 7267 <dest>
5013 *: "2ì›”" 7268 *: "%sì„(를) ì½ê¸° 실패함"
5014 </dest> 7269 </dest>
5015 <voice> 7270 <voice>
5016 *: "2ì›”" 7271 *: ""
5017 </voice> 7272 </voice>
5018</phrase> 7273</phrase>
5019<phrase> 7274<phrase>
5020 id: LANG_MONTH_MARCH 7275 id: LANG_PLUGIN_WRONG_MODEL
5021 desc: Maximum 3-letter abbreviation for monthname 7276 desc: The plugin is not compatible with the player model trying to run it
5022 user: core 7277 user: core
5023 <source> 7278 <source>
5024 *: "Mar" 7279 *: "Incompatible model"
5025 </source> 7280 </source>
5026 <dest> 7281 <dest>
5027 *: "3ì”" 7282 *: "호환ë˜ì§€ ì•ŠëŠ ëª¨ë¸"
5028 </dest> 7283 </dest>
5029 <voice> 7284 <voice>
5030 *: "3ì”" 7285 *: "호환ë˜ì§€ ì•ŠëŠ ëª¨ë¸"
5031 </voice> 7286 </voice>
5032</phrase> 7287</phrase>
5033<phrase> 7288<phrase>
5034 id: LANG_MONTH_APRIL 7289 id: LANG_PLUGIN_WRONG_VERSION
5035 desc: Maximum 3-letter abbreviation for monthname 7290 desc: The plugin is not compatible with the rockbox version trying to run it
5036 user: core 7291 user: core
5037 <source> 7292 <source>
5038 *: "Apr" 7293 *: "Incompatible version"
5039 </source> 7294 </source>
5040 <dest> 7295 <dest>
5041 *: "4ì”" 7296 *: "호환ë˜ì§€ ì•ŠëŠ ë²„ì „"
5042 </dest> 7297 </dest>
5043 <voice> 7298 <voice>
5044 *: "4ì”" 7299 *: "호환ë˜ì§€ ì•ŠëŠ ë²„ì „"
5045 </voice> 7300 </voice>
5046</phrase> 7301</phrase>
5047<phrase> 7302<phrase>
5048 id: LANG_MONTH_MAY 7303 id: LANG_PLUGIN_ERROR
5049 desc: Maximum 3-letter abbreviation for monthname 7304 desc: The plugin return an error code
5050 user: core 7305 user: core
5051 <source> 7306 <source>
5052 *: "May" 7307 *: "Plugin returned error"
5053 </source> 7308 </source>
5054 <dest> 7309 <dest>
5055 *: "5ì›”" 7310 *: "íŒëŸ¬ê·¸ì¸ì—ì„œ 오류가 반환ë¨"
5056 </dest> 7311 </dest>
5057 <voice> 7312 <voice>
5058 *: "5ì›”" 7313 *: "íŒëŸ¬ê·¸ì¸ì—ì„œ 오류가 반환ë¨"
5059 </voice> 7314 </voice>
5060</phrase> 7315</phrase>
5061<phrase> 7316<phrase>
5062 id: LANG_MONTH_JUNE 7317 id: LANG_FILETYPES_FULL
5063 desc: Maximum 3-letter abbreviation for monthname 7318 desc: Filetype array full
5064 user: core 7319 user: core
5065 <source> 7320 <source>
5066 *: "Jun" 7321 *: "Filetype array full"
5067 </source> 7322 </source>
5068 <dest> 7323 <dest>
5069 *: "6ì›”" 7324 *: "파ì¼í˜•ì‹ ë°°ì—´ì´ ê°€ë“ ì°¸"
5070 </dest> 7325 </dest>
5071 <voice> 7326 <voice>
5072 *: "6ì›”" 7327 *: "파ì¼í˜•ì‹ ë°°ì—´ì´ ê°€ë“ ì°¸"
5073 </voice> 7328 </voice>
5074</phrase> 7329</phrase>
5075<phrase> 7330<phrase>
5076 id: LANG_MONTH_JULY 7331 id: LANG_SHOWDIR_BUFFER_FULL
5077 desc: Maximum 3-letter abbreviation for monthname 7332 desc: in showdir(), displayed on screen when you reach buffer limit
5078 user: core 7333 user: core
5079 <source> 7334 <source>
5080 *: "Jul" 7335 *: "Dir Buffer is Full!"
5081 </source> 7336 </source>
5082 <dest> 7337 <dest>
5083 *: "7ì›”" 7338 *: "ë”렉토리 버í¼ê°€ ê°€ë“ ì°¼ìŠµë‹ˆë‹¤!"
5084 </dest> 7339 </dest>
5085 <voice> 7340 <voice>
5086 *: "7ì›”" 7341 *: "ë”렉토리 버í¼ê°€ ê°€ë“ ì°¼ìŠµë‹ˆë‹¤!"
5087 </voice> 7342 </voice>
5088</phrase> 7343</phrase>
5089<phrase> 7344<phrase>
5090 id: LANG_MONTH_AUGUST 7345 id: LANG_INVALID_FILENAME
5091 desc: Maximum 3-letter abbreviation for monthname 7346 desc: "invalid filename entered" error message
5092 user: core 7347 user: core
5093 <source> 7348 <source>
5094 *: "Aug" 7349 *: "Invalid Filename!"
5095 </source> 7350 </source>
5096 <dest> 7351 <dest>
5097 *: "8ì›”" 7352 *: "ìž˜ëª»ëœ íŒŒì¼ì´ë¦„입니다!"
5098 </dest> 7353 </dest>
5099 <voice> 7354 <voice>
5100 *: "8ì›”" 7355 *: "ìž˜ëª»ëœ íŒŒì¼ì´ë¦„!"
5101 </voice> 7356 </voice>
5102</phrase> 7357</phrase>
5103<phrase> 7358<phrase>
5104 id: LANG_MONTH_SEPTEMBER 7359 id: LANG_PLEASE_REBOOT
5105 desc: Maximum 3-letter abbreviation for monthname 7360 desc: when activating an option that requires a reboot
5106 user: core 7361 user: core
5107 <source> 7362 <source>
5108 *: "Sep" 7363 *: "Please reboot to enable"
5109 </source> 7364 </source>
5110 <dest> 7365 <dest>
5111 *: "9ì”" 7366 *: "활성í™í•˜ë ¤ë©´ 재부팅"
5112 </dest> 7367 </dest>
5113 <voice> 7368 <voice>
5114 *: "9ì”" 7369 *: "활성í™í•˜ë ¤ë©´ 재부팅"
5115 </voice> 7370 </voice>
5116</phrase> 7371</phrase>
5117<phrase> 7372<phrase>
5118 id: LANG_MONTH_OCTOBER 7373 id: LANG_BATTERY_CHARGE
5119 desc: Maximum 3-letter abbreviation for monthname 7374 desc: tells that the battery is charging, instead of battery level
5120 user: core 7375 user: core
5121 <source> 7376 <source>
5122 *: "Oct" 7377 *: none
7378 charging: "Battery: Charging"
5123 </source> 7379 </source>
5124 <dest> 7380 <dest>
5125 *: "10ì›”" 7381 *: none
7382 charging: "배터리: 충전중"
5126 </dest> 7383 </dest>
5127 <voice> 7384 <voice>
5128 *: "10ì›”" 7385 *: none
7386 charging: "배터리 충전 중"
5129 </voice> 7387 </voice>
5130</phrase> 7388</phrase>
5131<phrase> 7389<phrase>
5132 id: LANG_MONTH_NOVEMBER 7390 id: LANG_WARNING_BATTERY_LOW
5133 desc: Maximum 3-letter abbreviation for monthname 7391 desc: general warning
5134 user: core 7392 user: core
5135 <source> 7393 <source>
5136 *: "Nov" 7394 *: "WARNING! Low Battery!"
5137 </source> 7395 </source>
5138 <dest> 7396 <dest>
5139 *: "11월" 7397 *: "경고! 배터리 부족합니다!"
5140 </dest> 7398 </dest>
5141 <voice> 7399 <voice>
5142 *: "11월" 7400 *: "경고! 배터리 부족합니다!"
5143 </voice> 7401 </voice>
5144</phrase> 7402</phrase>
5145<phrase> 7403<phrase>
5146 id: LANG_MONTH_DECEMBER 7404 id: LANG_WARNING_BATTERY_EMPTY
5147 desc: Maximum 3-letter abbreviation for monthname 7405 desc: general warning
5148 user: core 7406 user: core
5149 <source> 7407 <source>
5150 *: "Dec" 7408 *: "Battery empty! RECHARGE!"
5151 </source> 7409 </source>
5152 <dest> 7410 <dest>
5153 *: "12ì”" 7411 *: "배터리가 ë°©ì „ë˜ì–´ê°‘니다! 재충전하세ìš!"
5154 </dest> 7412 </dest>
5155 <voice> 7413 <voice>
5156 *: "12ì›”" 7414 *: "배터리가 ë°©ì „ë˜ì–´ê°‘니다! 재충전하세요!"
7415 </voice>
7416</phrase>
7417<phrase>
7418 id: LANG_BYTE
7419 desc: a unit postfix
7420 user: core
7421 <source>
7422 *: "B"
7423 </source>
7424 <dest>
7425 *: "B"
7426 </dest>
7427 <voice>
7428 *: ""
7429 </voice>
7430</phrase>
7431<phrase>
7432 id: LANG_POINT
7433 desc: decimal separator for composing numbers
7434 user: core
7435 <source>
7436 *: "."
7437 </source>
7438 <dest>
7439 *: "~."
7440 </dest>
7441 <voice>
7442 *: "í¬ì¸íŠ¸"
5157 </voice> 7443 </voice>
5158</phrase> 7444</phrase>
5159<phrase> 7445<phrase>
@@ -5587,7 +7873,7 @@
5587 *: "" 7873 *: ""
5588 </dest> 7874 </dest>
5589 <voice> 7875 <voice>
5590 *: "백만" 7876 *: "만"
5591 </voice> 7877 </voice>
5592</phrase> 7878</phrase>
5593<phrase> 7879<phrase>
@@ -5601,7 +7887,7 @@
5601 *: "" 7887 *: ""
5602 </dest> 7888 </dest>
5603 <voice> 7889 <voice>
5604 *: "십억" 7890 *: "십만"
5605 </voice> 7891 </voice>
5606</phrase> 7892</phrase>
5607<phrase> 7893<phrase>
@@ -5643,7 +7929,7 @@
5643 *: "" 7929 *: ""
5644 </dest> 7930 </dest>
5645 <voice> 7931 <voice>
5646 *: "밀리세컨드" 7932 *: "밀리초"
5647 </voice> 7933 </voice>
5648</phrase> 7934</phrase>
5649<phrase> 7935<phrase>
@@ -5713,7 +7999,7 @@
5713 *: "" 7999 *: ""
5714 </dest> 8000 </dest>
5715 <voice> 8001 <voice>
5716 *: "시간" 8002 *: "시"
5717 </voice> 8003 </voice>
5718</phrase> 8004</phrase>
5719<phrase> 8005<phrase>
@@ -5727,7 +8013,7 @@
5727 *: "" 8013 *: ""
5728 </dest> 8014 </dest>
5729 <voice> 8015 <voice>
5730 *: "시간" 8016 *: "시"
5731 </voice> 8017 </voice>
5732</phrase> 8018</phrase>
5733<phrase> 8019<phrase>
@@ -5829,73 +8115,17 @@
5829 </voice> 8115 </voice>
5830</phrase> 8116</phrase>
5831<phrase> 8117<phrase>
5832 id: LANG_BYTE 8118 id: VOICE_KBIT_PER_SEC
5833 desc: a unit postfix 8119 desc: spoken only, a unit postfix
5834 user: core 8120 user: core
5835 <source> 8121 <source>
5836 *: "B"
5837 </source>
5838 <dest>
5839 *: "B"
5840 </dest>
5841 <voice>
5842 *: "" 8122 *: ""
5843 </voice>
5844</phrase>
5845<phrase>
5846 id: LANG_KILOBYTE
5847 desc: a unit postfix, also voiced
5848 user: core
5849 <source>
5850 *: "KB"
5851 </source>
5852 <dest>
5853 *: "KB"
5854 </dest>
5855 <voice>
5856 *: "킬로바ì´íŠ¸"
5857 </voice>
5858</phrase>
5859<phrase>
5860 id: LANG_MEGABYTE
5861 desc: a unit postfix, also voiced
5862 user: core
5863 <source>
5864 *: "MB"
5865 </source>
5866 <dest>
5867 *: "MB"
5868 </dest>
5869 <voice>
5870 *: "메가바ì´íŠ¸"
5871 </voice>
5872</phrase>
5873<phrase>
5874 id: LANG_GIGABYTE
5875 desc: a unit postfix, also voiced
5876 user: core
5877 <source>
5878 *: "GB"
5879 </source>
5880 <dest>
5881 *: "GB"
5882 </dest>
5883 <voice>
5884 *: "기가바ì´íŠ¸"
5885 </voice>
5886</phrase>
5887<phrase>
5888 id: LANG_POINT
5889 desc: decimal separator for composing numbers
5890 user: core
5891 <source>
5892 *: "."
5893 </source> 8123 </source>
5894 <dest> 8124 <dest>
5895 *: "." 8125 *: ""
5896 </dest> 8126 </dest>
5897 <voice> 8127 <voice>
5898 *: "ì " 8128 *: "초당 킬로비트"
5899 </voice> 8129 </voice>
5900</phrase> 8130</phrase>
5901<phrase> 8131<phrase>
@@ -6273,7 +8503,7 @@
6273 *: "" 8503 *: ""
6274 </dest> 8504 </dest>
6275 <voice> 8505 <voice>
6276 *: "ì " 8506 *: "ë‹·"
6277 </voice> 8507 </voice>
6278</phrase> 8508</phrase>
6279<phrase> 8509<phrase>
@@ -6329,7 +8559,7 @@
6329 *: "" 8559 *: ""
6330 </dest> 8560 </dest>
6331 <voice> 8561 <voice>
6332 *: "오디오 파ì¼" 8562 *: "오디오"
6333 </voice> 8563 </voice>
6334</phrase> 8564</phrase>
6335<phrase> 8565<phrase>
@@ -6343,7 +8573,7 @@
6343 *: "" 8573 *: ""
6344 </dest> 8574 </dest>
6345 <voice> 8575 <voice>
6346 *: "설정 파ì¼" 8576 *: "구성"
6347 </voice> 8577 </voice>
6348</phrase> 8578</phrase>
6349<phrase> 8579<phrase>
@@ -6357,7 +8587,7 @@
6357 *: "" 8587 *: ""
6358 </dest> 8588 </dest>
6359 <voice> 8589 <voice>
6360 *: "ìž¬ìƒ í™”ë©´ 파ì¼" 8590 *: "ìž¬ìƒ ì¤‘ 화면"
6361 </voice> 8591 </voice>
6362</phrase> 8592</phrase>
6363<phrase> 8593<phrase>
@@ -6371,7 +8601,7 @@
6371 *: "" 8601 *: ""
6372 </dest> 8602 </dest>
6373 <voice> 8603 <voice>
6374 *: "í”ŒëŸ¬ê·¸ì¸ íŒŒì¼" 8604 *: "플러그ì¸"
6375 </voice> 8605 </voice>
6376</phrase> 8606</phrase>
6377<phrase> 8607<phrase>
@@ -6385,7 +8615,7 @@
6385 *: "" 8615 *: ""
6386 </dest> 8616 </dest>
6387 <voice> 8617 <voice>
6388 *: "í°íŠ¸ 파ì¼" 8618 *: "글꼴"
6389 </voice> 8619 </voice>
6390</phrase> 8620</phrase>
6391<phrase> 8621<phrase>
@@ -6399,7 +8629,7 @@
6399 *: "" 8629 *: ""
6400 </dest> 8630 </dest>
6401 <voice> 8631 <voice>
6402 *: "ë¶ë§ˆí¬ 파ì¼" 8632 *: "ë¶ë§ˆí¬"
6403 </voice> 8633 </voice>
6404</phrase> 8634</phrase>
6405<phrase> 8635<phrase>
@@ -6413,7 +8643,7 @@
6413 *: "" 8643 *: ""
6414 </dest> 8644 </dest>
6415 <voice> 8645 <voice>
6416 *: "펌웨어 파ì¼" 8646 *: "펌웨어"
6417 </voice> 8647 </voice>
6418</phrase> 8648</phrase>
6419<phrase> 8649<phrase>
@@ -6430,7 +8660,7 @@
6430 </dest> 8660 </dest>
6431 <voice> 8661 <voice>
6432 *: none 8662 *: none
6433 remote: "리모트 ìž¬ìƒ ì¤‘ 화면" 8663 remote: "ìž¬ìƒ ì¤‘ 화면 ì›ê²©"
6434 </voice> 8664 </voice>
6435</phrase> 8665</phrase>
6436<phrase> 8666<phrase>
@@ -6444,2523 +8674,3789 @@
6444 *: "" 8674 *: ""
6445 </dest> 8675 </dest>
6446 <voice> 8676 <voice>
6447 *: "키보드 파ì¼" 8677 *: "키보드"
6448 </voice> 8678 </voice>
6449</phrase> 8679</phrase>
6450<phrase> 8680<phrase>
6451 id: LANG_PLAYLIST_BUFFER_FULL 8681 id: VOICE_EXT_CUESHEET
6452 desc: in playlist.indices() when playlist is full 8682 desc:
6453 user: core 8683 user: core
6454 <source> 8684 <source>
6455 *: "Playlist Buffer Full" 8685 *: ""
6456 </source> 8686 </source>
6457 <dest> 8687 <dest>
6458 *: "재ìƒëª©ë¡ 버í¼ì˜ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤." 8688 *: ""
6459 </dest> 8689 </dest>
6460 <voice> 8690 <voice>
8691 *: "í시트"
8692 </voice>
8693</phrase>
8694<phrase>
8695 id: VOICE_BOOKMARK_SELECT_INDEX_TEXT
8696 desc: voice only, used in the bookmark list to label index number
8697 user: core
8698 <source>
8699 *: ""
8700 </source>
8701 <dest>
6461 *: "" 8702 *: ""
8703 </dest>
8704 <voice>
8705 *: "ì¸ë±ìŠ¤"
6462 </voice> 8706 </voice>
6463</phrase> 8707</phrase>
6464<phrase> 8708<phrase>
6465 id: LANG_CREATING 8709 id: VOICE_CURRENT_TIME
6466 desc: Screen feedback during playlist creation 8710 desc: spoken only, for wall clock announce
6467 user: core 8711 user: core
6468 <source> 8712 <source>
6469 *: "Creating" 8713 *: none
8714 rtc: ""
6470 </source> 8715 </source>
6471 <dest> 8716 <dest>
6472 *: "ìƒì„±í•˜ê³  있습니다..." 8717 *: none
8718 rtc: ""
6473 </dest> 8719 </dest>
6474 <voice> 8720 <voice>
6475 *: "" 8721 *: none
8722 rtc: "현재 시간"
6476 </voice> 8723 </voice>
6477</phrase> 8724</phrase>
6478<phrase> 8725<phrase>
6479 id: LANG_PLAYLIST_INSERT_COUNT 8726 id: LANG_SYSFONT_EQUALIZER_EDIT_MODE
6480 desc: splash number of tracks inserted 8727 desc: in the equalizer settings menu
6481 user: core 8728 user: core
6482 <source> 8729 <source>
6483 *: "Inserted %d tracks (%s)" 8730 *: "Edit mode: %s %s"
6484 </source> 8731 </source>
6485 <dest> 8732 <dest>
6486 *: "%dê°œì˜ ìŒì•… 추가 (%s)" 8733 *: "편집 모드: %s %s"
6487 </dest> 8734 </dest>
6488 <voice> 8735 <voice>
6489 *: "" 8736 *: ""
6490 </voice> 8737 </voice>
6491</phrase> 8738</phrase>
6492<phrase> 8739<phrase>
6493 id: LANG_PLAYLIST_QUEUE_COUNT 8740 id: LANG_SYSFONT_EQUALIZER_BAND_CUTOFF
6494 desc: splash number of tracks queued 8741 desc: in the equalizer settings menu
6495 user: core 8742 user: core
6496 <source> 8743 <source>
6497 *: "Queued %d tracks (%s)" 8744 *: "Cutoff"
6498 </source> 8745 </source>
6499 <dest> 8746 <dest>
6500 *: "%dê°œì˜ ìŒì•… 예약 (%s)" 8747 *: "컷오프"
6501 </dest> 8748 </dest>
6502 <voice> 8749 <voice>
6503 *: "" 8750 *: "컷오프 주파수"
6504 </voice> 8751 </voice>
6505</phrase> 8752</phrase>
6506<phrase> 8753<phrase>
6507 id: LANG_PLAYLIST_SAVE_COUNT 8754 id: LANG_SYSFONT_GAIN
6508 desc: splash number of tracks saved 8755 desc: in the equalizer settings menu
6509 user: core 8756 user: core
6510 <source> 8757 <source>
6511 *: "Saved %d tracks (%s)" 8758 *: "Gain"
6512 </source> 8759 </source>
6513 <dest> 8760 <dest>
6514 *: "%dê°œì˜ ìŒì•… 저장 (%s)" 8761 *: "게ì¸"
6515 </dest> 8762 </dest>
6516 <voice> 8763 <voice>
8764 *: "게ì¸"
8765 </voice>
8766</phrase>
8767<phrase>
8768 id: VOICE_OF
8769 desc: spoken only, as in 3/8 => 3 of 8
8770 user: core
8771 <source>
6517 *: "" 8772 *: ""
8773 </source>
8774 <dest>
8775 *: ""
8776 </dest>
8777 <voice>
8778 *: "ì˜"
6518 </voice> 8779 </voice>
6519</phrase> 8780</phrase>
6520<phrase> 8781<phrase>
6521 id: LANG_RECURSE_DIRECTORY_QUESTION 8782 id: LANG_PLUGIN_GAMES
6522 desc: Asked from onplay screen 8783 desc: in the main menu
6523 user: core 8784 user: core
6524 <source> 8785 <source>
6525 *: "Recursively?" 8786 *: "Games"
6526 </source> 8787 </source>
6527 <dest> 8788 <dest>
6528 *: "하위 í´ë”ë„ ì¶”ê°€í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" 8789 *: "게임"
6529 </dest> 8790 </dest>
6530 <voice> 8791 <voice>
6531 *: "" 8792 *: "게임"
6532 </voice> 8793 </voice>
6533</phrase> 8794</phrase>
6534<phrase> 8795<phrase>
6535 id: LANG_WARN_ERASEDYNPLAYLIST_PROMPT 8796 id: LANG_PLUGIN_APPS
6536 desc: prompt shown when about to erase a modified dynamic playlist 8797 desc: in the main menu
6537 user: core 8798 user: core
6538 <source> 8799 <source>
6539 *: "Erase dynamic playlist?" 8800 *: "Applications"
6540 </source> 8801 </source>
6541 <dest> 8802 <dest>
6542 *: "현재 재ìƒëª©ë¡ì 삭제하시겠습니까?" 8803 *: "ììš© 프로그램"
6543 </dest> 8804 </dest>
6544 <voice> 8805 <voice>
6545 *: "" 8806 *: "ì‘ìš© 프로그램"
6546 </voice> 8807 </voice>
6547</phrase> 8808</phrase>
6548<phrase> 8809<phrase>
6549 id: LANG_NOTHING_TO_RESUME 8810 id: LANG_PLUGIN_DEMOS
6550 desc: Error message displayed when resume button pressed but no playlist 8811 desc: in the main menu
6551 user: core 8812 user: core
6552 <source> 8813 <source>
6553 *: "Nothing to resume" 8814 *: "Demos"
6554 </source> 8815 </source>
6555 <dest> 8816 <dest>
6556 *: "재ìƒí•  파ì¼ì´ 없습니다." 8817 *: "ë°ëª¨"
6557 </dest> 8818 </dest>
6558 <voice> 8819 <voice>
6559 *: "" 8820 *: "ë°ëª¨"
6560 </voice> 8821 </voice>
6561</phrase> 8822</phrase>
6562<phrase> 8823<phrase>
6563 id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR 8824 id: LANG_ID3_GROUPING
6564 desc: Playlist error 8825 desc: in tag viewer
6565 user: core 8826 user: core
6566 <source> 8827 <source>
6567 *: "Error updating playlist control file" 8828 *: "Work"
6568 </source> 8829 </source>
6569 <dest> 8830 <dest>
6570 *: "재ìƒëª©ë¡ 제어 íŒŒì¼ ì—…ë°ì´íŠ¸ ì—러" 8831 *: "ìžì—…"
6571 </dest> 8832 </dest>
6572 <voice> 8833 <voice>
6573 *: "" 8834 *: "ìž‘ì—…"
6574 </voice> 8835 </voice>
6575</phrase> 8836</phrase>
6576<phrase> 8837<phrase>
6577 id: LANG_PLAYLIST_ACCESS_ERROR 8838 id: LANG_SHOW_FILENAME_EXT
6578 desc: Playlist error 8839 desc: in settings_menu
6579 user: core 8840 user: core
6580 <source> 8841 <source>
6581 *: "Error accessing playlist file" 8842 *: "Show Filename Extensions"
6582 </source> 8843 </source>
6583 <dest> 8844 <dest>
6584 *: "재ìƒëª©ë¡ íŒŒì¼ ì ‘ê·¼ ì러" 8845 *: "파ì¼ì´ë¦„ í™•ìž¥ìž í‘œì‹œ"
6585 </dest> 8846 </dest>
6586 <voice> 8847 <voice>
6587 *: "" 8848 *: "파ì¼ì´ë¦„ í™•ìž¥ìž í‘œì‹œ"
6588 </voice> 8849 </voice>
6589</phrase> 8850</phrase>
6590<phrase> 8851<phrase>
6591 id: LANG_PLAYLIST_CONTROL_ACCESS_ERROR 8852 id: LANG_UNKNOWN_TYPES
6592 desc: Playlist error 8853 desc: in settings_menu
6593 user: core 8854 user: core
6594 <source> 8855 <source>
6595 *: "Error accessing playlist control file" 8856 *: "Only Unknown Types"
8857 </source>
8858 <dest>
8859 *: "알 수 없는 유형만"
8860 </dest>
8861 <voice>
8862 *: "알 수 없는 유형만"
8863 </voice>
8864</phrase>
8865<phrase>
8866 id: LANG_EXT_ONLY_VIEW_ALL
8867 desc: in settings_menu
8868 user: core
8869 <source>
8870 *: "Only When Viewing All Types"
6596 </source> 8871 </source>
6597 <dest> 8872 <dest>
6598 *: "재ìƒëª©ë¡ 제어 íŒŒì¼ ì ‘ê·¼ ì—러" 8873 *: "모든 유형ì ë³¼ 때만"
6599 </dest> 8874 </dest>
6600 <voice> 8875 <voice>
8876 *: "모든 ìœ í˜•ì„ ë³¼ 때만"
8877 </voice>
8878</phrase>
8879<phrase>
8880 id: VOICE_PM_UNITS_PER_TICK
8881 desc: spoken only, peak meter release unit
8882 user: core
8883 <source>
8884 *: ""
8885 </source>
8886 <dest>
6601 *: "" 8887 *: ""
8888 </dest>
8889 <voice>
8890 *: "틱당 단위"
6602 </voice> 8891 </voice>
6603</phrase> 8892</phrase>
6604<phrase> 8893<phrase>
6605 id: LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR 8894 id: VOICE_OCLOCK
6606 desc: Playlist error 8895 desc: spoken only, for wall clock announce
6607 user: core 8896 user: core
6608 <source> 8897 <source>
6609 *: "Error accessing directory" 8898 *: ""
6610 </source> 8899 </source>
6611 <dest> 8900 <dest>
6612 *: "í´ë” ì ‘ê·¼ ì—러" 8901 *: ""
6613 </dest> 8902 </dest>
6614 <voice> 8903 <voice>
8904 *: "ì •ê°"
8905 </voice>
8906</phrase>
8907<phrase>
8908 id: VOICE_PM
8909 desc: spoken only, for wall clock announce
8910 user: core
8911 <source>
6615 *: "" 8912 *: ""
8913 </source>
8914 <dest>
8915 *: ""
8916 </dest>
8917 <voice>
8918 *: "오후"
6616 </voice> 8919 </voice>
6617</phrase> 8920</phrase>
6618<phrase> 8921<phrase>
6619 id: LANG_PLAYLIST_CONTROL_INVALID 8922 id: VOICE_AM
6620 desc: Playlist resume error 8923 desc: spoken only, for wall clock announce
6621 user: core 8924 user: core
6622 <source> 8925 <source>
6623 *: "Playlist control file is invalid" 8926 *: ""
6624 </source> 8927 </source>
6625 <dest> 8928 <dest>
6626 *: "재ìƒëª©ë¡ 제어 파ì¼ì´ 유효하지 않습니다." 8929 *: ""
6627 </dest> 8930 </dest>
6628 <voice> 8931 <voice>
8932 *: "오전"
8933 </voice>
8934</phrase>
8935<phrase>
8936 id: VOICE_OH
8937 desc: spoken only, for wall clock announce
8938 user: core
8939 <source>
8940 *: ""
8941 </source>
8942 <dest>
6629 *: "" 8943 *: ""
8944 </dest>
8945 <voice>
8946 *: "오"
6630 </voice> 8947 </voice>
6631</phrase> 8948</phrase>
6632<phrase> 8949<phrase>
6633 id: LANG_FM_NO_PRESETS 8950 id: LANG_PM_CLIPCOUNTER
6634 desc: error when preset list is empty 8951 desc: in settings, for recording peak meter
6635 user: core 8952 user: core
6636 <source> 8953 <source>
6637 *: none 8954 *: none
6638 radio: "No presets" 8955 recording: "Clip Counter"
6639 </source> 8956 </source>
6640 <dest> 8957 <dest>
6641 *: none 8958 *: none
6642 radio: "프리셋 ì—†ìŒ" 8959 recording: "í´ë¦½ ì¹´ìš´í„°"
6643 </dest> 8960 </dest>
6644 <voice> 8961 <voice>
6645 *: none 8962 *: none
6646 radio: "프리셋 ì—†ìŒ" 8963 recording: "í´ë¦½ ì¹´ìš´í„°"
6647 </voice> 8964 </voice>
6648</phrase> 8965</phrase>
6649<phrase> 8966<phrase>
6650 id: LANG_FM_ADD_PRESET 8967 id: LANG_SELECTOR_START_COLOR
6651 desc: in radio menu 8968 desc: line selector color option
6652 user: core 8969 user: core
6653 <source> 8970 <source>
6654 *: none 8971 *: none
6655 radio: "Add Preset" 8972 lcd_color: "Primary Colour"
6656 </source> 8973 </source>
6657 <dest> 8974 <dest>
6658 *: none 8975 *: none
6659 radio: "프리셋 추가" 8976 lcd_color: "기본 색ìƒ"
6660 </dest> 8977 </dest>
6661 <voice> 8978 <voice>
6662 *: none 8979 *: none
6663 radio: "프리셋 추가" 8980 lcd_color: "기본 색ìƒ"
6664 </voice> 8981 </voice>
6665</phrase> 8982</phrase>
6666<phrase> 8983<phrase>
6667 id: LANG_FM_EDIT_PRESET 8984 id: LANG_SELECTOR_END_COLOR
6668 desc: in radio screen 8985 desc: line selector color option
6669 user: core 8986 user: core
6670 <source> 8987 <source>
6671 *: none 8988 *: none
6672 radio: "Edit Preset" 8989 lcd_color: "Secondary Colour"
6673 </source> 8990 </source>
6674 <dest> 8991 <dest>
6675 *: none 8992 *: none
6676 radio: "프리셋 편집" 8993 lcd_color: "ë³´ì¡° 색ìƒ"
6677 </dest> 8994 </dest>
6678 <voice> 8995 <voice>
6679 *: none 8996 *: none
6680 radio: "프리셋 편집" 8997 lcd_color: "ë³´ì¡° 색ìƒ"
6681 </voice> 8998 </voice>
6682</phrase> 8999</phrase>
6683<phrase> 9000<phrase>
6684 id: LANG_FM_DELETE_PRESET 9001 id: LANG_SELECTOR_TEXT_COLOR
6685 desc: in radio screen 9002 desc: line selector text color option
6686 user: core 9003 user: core
6687 <source> 9004 <source>
6688 *: none 9005 *: none
6689 radio: "Remove Preset" 9006 lcd_color: "Text Colour"
6690 </source> 9007 </source>
6691 <dest> 9008 <dest>
6692 *: none 9009 *: none
6693 radio: "프리셋 제거" 9010 lcd_color: "ë¬¸ìž ìƒ‰ìƒ"
6694 </dest> 9011 </dest>
6695 <voice> 9012 <voice>
6696 *: none 9013 *: none
6697 radio: "프리셋 제거" 9014 lcd_color: "ë¬¸ìž ìƒ‰ìƒ"
6698 </voice> 9015 </voice>
6699</phrase> 9016</phrase>
6700<phrase> 9017<phrase>
6701 id: LANG_FM_PRESET_SAVE_FAILED 9018 id: LANG_INVERT_CURSOR_COLOR
6702 desc: in radio screen 9019 desc: in settings_menu
6703 user: core 9020 user: core
6704 <source> 9021 <source>
6705 *: none 9022 *: none
6706 radio: "Preset Save Failed" 9023 lcd_color: "Bar (Solid Colour)"
6707 </source> 9024 </source>
6708 <dest> 9025 <dest>
6709 *: none 9026 *: none
6710 radio: "프리셋 저장 ì¤íŒ¨" 9027 lcd_color: "ë°” ‹¨ìƒ‰)"
6711 </dest> 9028 </dest>
6712 <voice> 9029 <voice>
6713 *: none 9030 *: none
6714 radio: "프리셋 저장 ì¤íŒ¨" 9031 lcd_color: "ë°” ‹¨ìƒ‰)"
6715 </voice> 9032 </voice>
6716</phrase> 9033</phrase>
6717<phrase> 9034<phrase>
6718 id: LANG_FM_NO_FREE_PRESETS 9035 id: LANG_INVERT_CURSOR_GRADIENT
6719 desc: in radio screen 9036 desc: in settings_menu
6720 user: core 9037 user: core
6721 <source> 9038 <source>
6722 *: none 9039 *: none
6723 radio: "The Preset List is Full" 9040 lcd_color: "Bar (Gradient Colour)"
6724 </source> 9041 </source>
6725 <dest> 9042 <dest>
6726 *: none 9043 *: none
6727 radio: "íë¦¬ì… ë¦¬ìŠ¤íŠ¸ê°€ ê½ ì°¼ìŠµë‹ˆë‹¤" 9044 lcd_color: "ë° (ê·¸ë¼ë°ì´ì˜ 색ìƒ)"
6728 </dest> 9045 </dest>
6729 <voice> 9046 <voice>
6730 *: none 9047 *: none
6731 radio: "프리셋 리스트가 꽉 찼습니다" 9048 lcd_color: "ë°” (ê·¸ë¼ë°ì´ì…˜ 색ìƒ)"
9049 </voice>
9050</phrase>
9051<phrase>
9052 id: LANG_CODEPAGE_CENTRAL_EUROPEAN
9053 desc: in codepage setting menu
9054 user: core
9055 <source>
9056 *: "Central European (CP1250)"
9057 </source>
9058 <dest>
9059 *: "중앙 유럽어 (CP1250)"
9060 </dest>
9061 <voice>
9062 *: "중앙 유럽어"
9063 </voice>
9064</phrase>
9065<phrase>
9066 id: LANG_THEME_MENU
9067 desc: in the settings menu
9068 user: core
9069 <source>
9070 *: "Theme Settings"
9071 </source>
9072 <dest>
9073 *: "테마 설정"
9074 </dest>
9075 <voice>
9076 *: "테마 설정"
6732 </voice> 9077 </voice>
6733</phrase> 9078</phrase>
6734<phrase> 9079<phrase>
6735 id: LANG_BUTTONBAR_MENU 9080 id: LANG_COLORS_MENU
6736 desc: in button bar 9081 desc: colours menu under theme settings
6737 user: core 9082 user: core
6738 <source> 9083 <source>
6739 *: none 9084 *: none
6740 radio_screen_button_bar: "Menu" 9085 lcd_color: "Colours"
6741 </source> 9086 </source>
6742 <dest> 9087 <dest>
6743 *: none 9088 *: none
6744 radio_screen_button_bar: "ë©”ë´" 9089 lcd_color: "ìƒìƒ"
6745 </dest> 9090 </dest>
6746 <voice> 9091 <voice>
6747 *: none 9092 *: none
6748 radio_screen_button_bar: "" 9093 lcd_color: "색ìƒ"
6749 </voice> 9094 </voice>
6750</phrase> 9095</phrase>
6751<phrase> 9096<phrase>
6752 id: LANG_FM_BUTTONBAR_EXIT 9097 id: LANG_SELECTOR_COLOR_MENU
6753 desc: in radio screen 9098 desc: line selector color menu title
6754 user: core 9099 user: core
6755 <source> 9100 <source>
6756 *: none 9101 *: none
6757 radio_screen_button_bar: "Exit" 9102 lcd_color: "Line Selector Colours"
6758 </source> 9103 </source>
6759 <dest> 9104 <dest>
6760 *: none 9105 *: none
6761 radio_screen_button_bar: "나가기" 9106 lcd_color: "ë¼ì¸ ì„ íƒê¸° 색ìƒ"
6762 </dest> 9107 </dest>
6763 <voice> 9108 <voice>
6764 *: none 9109 *: none
6765 radio_screen_button_bar: "" 9110 lcd_color: "ë¼ì¸ ì„ íƒê¸° 색ìƒ"
6766 </voice> 9111 </voice>
6767</phrase> 9112</phrase>
6768<phrase> 9113<phrase>
6769 id: LANG_FM_BUTTONBAR_ACTION 9114 id: VOICE_EDIT
6770 desc: in radio screen 9115 desc: keyboard
9116 user: core
9117 <source>
9118 *: ""
9119 </source>
9120 <dest>
9121 *: ""
9122 </dest>
9123 <voice>
9124 *: "편집"
9125 </voice>
9126</phrase>
9127<phrase>
9128 id: VOICE_BLANK
9129 desc: keyboard
9130 user: core
9131 <source>
9132 *: ""
9133 </source>
9134 <dest>
9135 *: ""
9136 </dest>
9137 <voice>
9138 *: "공백"
9139 </voice>
9140</phrase>
9141<phrase>
9142 id: VOICE_EMPTY_LIST
9143 desc: spoken only, when a list dialog contains no elements
9144 user: core
9145 <source>
9146 *: ""
9147 </source>
9148 <dest>
9149 *: ""
9150 </dest>
9151 <voice>
9152 *: "빈 목ë¡"
9153 </voice>
9154</phrase>
9155<phrase>
9156 id: LANG_NOT_PRESENT
9157 desc: when external memory is not present
6771 user: core 9158 user: core
6772 <source> 9159 <source>
6773 *: none 9160 *: none
6774 radio_screen_button_bar: "Action" 9161 multivolume: "Not present"
6775 </source> 9162 </source>
6776 <dest> 9163 <dest>
6777 *: none 9164 *: none
6778 radio_screen_button_bar: "하기" 9165 multivolume: "존재하지 ì•ŠìŒ"
6779 </dest> 9166 </dest>
6780 <voice> 9167 <voice>
6781 *: none 9168 *: none
6782 radio_screen_button_bar: "" 9169 multivolume: "존재하지 ì•ŠìŒ"
6783 </voice> 9170 </voice>
6784</phrase> 9171</phrase>
6785<phrase> 9172<phrase>
6786 id: LANG_FM_BUTTONBAR_ADD 9173 id: LANG_TALK_BATTERY_LEVEL
6787 desc: in radio screen 9174 desc: Setting for spontaneous battery level announcement
9175 user: core
9176 <source>
9177 *: "Announce Battery Level"
9178 </source>
9179 <dest>
9180 *: "배터리 잔량 알리기"
9181 </dest>
9182 <voice>
9183 *: "배터리 잔량 알리기"
9184 </voice>
9185</phrase>
9186<phrase>
9187 id: LANG_TALK_MIXER_LEVEL
9188 desc: Relative volume of voice prompts
9189 user: core
9190 <source>
9191 *: "Voice prompt volume"
9192 </source>
9193 <dest>
9194 *: "ìŒì„± 안내 볼륨"
9195 </dest>
9196 <voice>
9197 *: "ìŒì„± 안내 볼륨"
9198 </voice>
9199</phrase>
9200<phrase>
9201 id: LANG_VOICE_FILETYPE
9202 desc: voice settings menu
9203 user: core
9204 <source>
9205 *: "Say File Type"
9206 </source>
9207 <dest>
9208 *: "íŒŒì¼ ìœ í˜• ë§í•˜ê¸°"
9209 </dest>
9210 <voice>
9211 *: "íŒŒì¼ ìœ í˜• ë§í•˜ê¸°"
9212 </voice>
9213</phrase>
9214<phrase>
9215 id: LANG_BASS_CUTOFF
9216 desc: Bass setting cut-off frequency
6788 user: core 9217 user: core
6789 <source> 9218 <source>
6790 *: none 9219 *: none
6791 radio_screen_button_bar: "Add" 9220 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
6792 </source> 9221 </source>
6793 <dest> 9222 <dest>
6794 *: none 9223 *: none
6795 radio_screen_button_bar: "추가" 9224 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ì ìŒ 컷오프"
6796 </dest> 9225 </dest>
6797 <voice> 9226 <voice>
6798 *: none 9227 *: none
6799 radio_screen_button_bar: "" 9228 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ì €ìŒ ì»·ì˜¤í”„"
6800 </voice> 9229 </voice>
6801</phrase> 9230</phrase>
6802<phrase> 9231<phrase>
6803 id: LANG_FM_BUTTONBAR_RECORD 9232 id: LANG_TREBLE_CUTOFF
6804 desc: in radio screen 9233 desc: Treble setting cut-off frequency
6805 user: core 9234 user: core
6806 <source> 9235 <source>
6807 *: none 9236 *: none
6808 radio_screen_button_bar: "Record" 9237 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
6809 </source> 9238 </source>
6810 <dest> 9239 <dest>
6811 *: none 9240 *: none
6812 radio_screen_button_bar: "ë…¹ìŒ" 9241 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ê³ ìŒ ì»·ì˜¤í”„"
6813 </dest> 9242 </dest>
6814 <voice> 9243 <voice>
6815 *: none 9244 *: none
6816 radio_screen_button_bar: "" 9245 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ê³ ìŒ ì»·ì˜¤í”„"
6817 </voice> 9246 </voice>
6818</phrase> 9247</phrase>
6819<phrase> 9248<phrase>
6820 id: LANG_FM_MONO_MODE 9249 id: LANG_TAGNAVI_RANDOM
6821 desc: in radio screen 9250 desc: "<Random>" entry in tag browser
9251 user: core
9252 <source>
9253 *: "<Random>"
9254 </source>
9255 <dest>
9256 *: "<ëžœë¤>"
9257 </dest>
9258 <voice>
9259 *: "ëžœë¤"
9260 </voice>
9261</phrase>
9262<phrase>
9263 id: LANG_SAVE_SOUND
9264 desc: save a sound config file
9265 user: core
9266 <source>
9267 *: "Save Sound Settings"
9268 </source>
9269 <dest>
9270 *: "사운드 설정 저장"
9271 </dest>
9272 <voice>
9273 *: "사운드 설정 저장"
9274 </voice>
9275</phrase>
9276<phrase>
9277 id: LANG_KEYCLICK
9278 desc: in keyclick settings menu
9279 user: core
9280 <source>
9281 *: "Keyclick"
9282 </source>
9283 <dest>
9284 *: "키í´ë¦­"
9285 </dest>
9286 <voice>
9287 *: "키í´ë¦­"
9288 </voice>
9289</phrase>
9290<phrase>
9291 id: LANG_KEYCLICK_REPEATS
9292 desc: in keyclick settings menu
9293 user: core
9294 <source>
9295 *: "Keyclick Repeats"
9296 </source>
9297 <dest>
9298 *: "키í´ë¦­ 반복"
9299 </dest>
9300 <voice>
9301 *: "키í´ë¦­ 반복"
9302 </voice>
9303</phrase>
9304<phrase>
9305 id: LANG_ACCESSORY_SUPPLY
9306 desc: in system settings menu
6822 user: core 9307 user: core
6823 <source> 9308 <source>
6824 *: none 9309 *: none
6825 radio: "Force Mono" 9310 accessory_supply: "Accessory Power Supply"
6826 </source> 9311 </source>
6827 <dest> 9312 <dest>
6828 *: none 9313 *: none
6829 radio: "ê°•ì œ 모노" 9314 accessory_supply: "ì¡ì„¸ì„œë¦¬ ì „ì› ê³µê¸‰ 장치"
6830 </dest> 9315 </dest>
6831 <voice> 9316 <voice>
6832 *: none 9317 *: none
6833 radio: "ê°•ì œ 모노" 9318 accessory_supply: "ì¡ì„¸ì„œë¦¬ ì „ì› ê³µê¸‰ 장치"
6834 </voice> 9319 </voice>
6835</phrase> 9320</phrase>
6836<phrase> 9321<phrase>
6837 id: LANG_FM_FREEZE 9322 id: LANG_UNKNOWN
6838 desc: splash screen during freeze in radio mode 9323 desc: generic string for unknown states, such as an unset clock
9324 user: core
9325 <source>
9326 *: "Unknown"
9327 </source>
9328 <dest>
9329 *: "ì•Œ 수 ì—†ìŒ"
9330 </dest>
9331 <voice>
9332 *: "ì•Œ 수 ì—†ìŒ"
9333 </voice>
9334</phrase>
9335<phrase>
9336 id: VOICE_QUICKSCREEN
9337 desc: spoken only, Announces entering the "quick screen"
9338 user: core
9339 <source>
9340 *: ""
9341 </source>
9342 <dest>
9343 *: ""
9344 </dest>
9345 <voice>
9346 *: "빠른 화면"
9347 </voice>
9348</phrase>
9349<phrase>
9350 id: VOICE_OK
9351 desc: spoken only, On exiting a context, specifically the quick screen
9352 user: core
9353 <source>
9354 *: ""
9355 </source>
9356 <dest>
9357 *: ""
9358 </dest>
9359 <voice>
9360 *: "확ì¸"
9361 </voice>
9362</phrase>
9363<phrase>
9364 id: LANG_STOP_RECORDING_AND_SHUTDOWN
9365 desc: in record timesplit options
6839 user: core 9366 user: core
6840 <source> 9367 <source>
6841 *: none 9368 *: none
6842 radio: "Screen frozen!" 9369 recording: "Stop Recording And Shutdown"
6843 </source> 9370 </source>
6844 <dest> 9371 <dest>
6845 *: none 9372 *: none
6846 radio: "화면 멈ì¤!" 9373 recording: "녹화 중지 ë° ì¢…ë£Œ"
6847 </dest> 9374 </dest>
6848 <voice> 9375 <voice>
6849 *: none 9376 *: none
6850 radio: "" 9377 recording: "녹화 중지 ë° ì¢…ë£Œ"
6851 </voice> 9378 </voice>
6852</phrase> 9379</phrase>
6853<phrase> 9380<phrase>
6854 id: LANG_FM_SCAN_PRESETS 9381 id: LANG_TOUCHPAD_SENSITIVITY
6855 desc: in radio menu 9382 desc: touchpad sensitivity setting
6856 user: core 9383 user: core
6857 <source> 9384 <source>
6858 *: none 9385 *: none
6859 radio: "Auto-Scan Presets" 9386 fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
6860 </source> 9387 </source>
6861 <dest> 9388 <dest>
6862 *: none 9389 *: none
6863 radio: "주파수 ìžë™ 검색" 9390 fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë"
6864 </dest> 9391 </dest>
6865 <voice> 9392 <voice>
6866 *: none 9393 *: none
6867 radio: "주파수 ìžë™ 검색" 9394 fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë"
6868 </voice> 9395 </voice>
6869</phrase> 9396</phrase>
6870<phrase> 9397<phrase>
6871 id: LANG_FM_CLEAR_PRESETS 9398 id: LANG_DEADZONE
6872 desc: confirmation if presets can be cleared 9399 desc: touchpad deadzone setting
6873 user: core 9400 user: core
6874 <source> 9401 <source>
6875 *: none 9402 *: none
6876 radio: "Clear Current Presets?" 9403 sansafuzeplus: "Touchpad Dead Zone"
6877 </source> 9404 </source>
6878 <dest> 9405 <dest>
6879 *: none 9406 *: none
6880 radio: "현재 í”„ë¦¬ì…‹ì„ ì œê±°í• ê¹Œìš”?" 9407 sansafuzeplus: "í„°ì¹˜íŒ¨ëœ ë°ë“œì¡´"
6881 </dest> 9408 </dest>
6882 <voice> 9409 <voice>
6883 *: none 9410 *: none
6884 radio: "현재 í”„ë¦¬ì…‹ì„ ì œê±°í• ê¹Œìš”?" 9411 sansafuzeplus: "í„°ì¹˜íŒ¨ëœ ë°ë“œì¡´"
6885 </voice> 9412 </voice>
6886</phrase> 9413</phrase>
6887<phrase> 9414<phrase>
6888 id: LANG_FM_SCANNING 9415 id: LANG_HIGH
6889 desc: during auto scan 9416 desc: in settings_menu
6890 user: core 9417 user: core
6891 <source> 9418 <source>
6892 *: none 9419 *: none
6893 radio: "Scanning %d.%02d MHz" 9420 gigabeatfx: "High"
6894 </source> 9421 </source>
6895 <dest> 9422 <dest>
6896 *: none 9423 *: none
6897 radio: "검색 %d.%02d MHz" 9424 gigabeatfx: "높ìŒ"
6898 </dest> 9425 </dest>
6899 <voice> 9426 <voice>
6900 *: none 9427 *: none
6901 radio: "" 9428 gigabeatfx: "높ìŒ"
6902 </voice> 9429 </voice>
6903</phrase> 9430</phrase>
6904<phrase> 9431<phrase>
6905 id: LANG_FM_DEFAULT_PRESET_NAME 9432 id: LANG_SERIAL_BITRATE
6906 desc: default preset name for auto scan mode 9433 desc: in system settings menu
6907 user: core 9434 user: core
6908 <source> 9435 <source>
6909 *: none 9436 *: none
6910 radio: "%d.%02d MHz" 9437 serial_port: "Serial Bitrate"
6911 </source> 9438 </source>
6912 <dest> 9439 <dest>
6913 *: none 9440 *: none
6914 radio: "%d.%02d MHz" 9441 serial_port: "ì§ë ¬ 비트 전송률"
6915 </dest> 9442 </dest>
6916 <voice> 9443 <voice>
6917 *: none 9444 *: none
6918 radio: "" 9445 serial_port: "ì§ë ¬ 비트 전송률"
6919 </voice> 9446 </voice>
6920</phrase> 9447</phrase>
6921<phrase> 9448<phrase>
6922 id: LANG_RADIO_SCAN_MODE 9449 id: LANG_SERIAL_BITRATE_AUTO
6923 desc: in radio screen / menu 9450 desc: in system settings menu
6924 user: core 9451 user: core
6925 <source> 9452 <source>
6926 *: none 9453 *: none
6927 radio: "Scan" 9454 serial_port: "Auto"
6928 </source> 9455 </source>
6929 <dest> 9456 <dest>
6930 *: none 9457 *: none
6931 radio: "검색" 9458 serial_port: "ìžë™"
6932 </dest> 9459 </dest>
6933 <voice> 9460 <voice>
6934 *: none 9461 *: none
6935 radio: "검색" 9462 serial_port: "ìžë™"
6936 </voice> 9463 </voice>
6937</phrase> 9464</phrase>
6938<phrase> 9465<phrase>
6939 id: LANG_SHOWDIR_BUFFER_FULL 9466 id: LANG_SERIAL_BITRATE_9600
6940 desc: in showdir(), displayed on screen when you reach buffer limit 9467 desc: in system settings menu
6941 user: core 9468 user: core
6942 <source> 9469 <source>
6943 *: "Dir Buffer is Full!" 9470 *: none
9471 serial_port: "9600"
6944 </source> 9472 </source>
6945 <dest> 9473 <dest>
6946 *: "í´ë” 버í¼ì˜ ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤." 9474 *: none
9475 serial_port: "9600"
6947 </dest> 9476 </dest>
6948 <voice> 9477 <voice>
6949 *: "" 9478 *: none
9479 serial_port: "9600"
6950 </voice> 9480 </voice>
6951</phrase> 9481</phrase>
6952<phrase> 9482<phrase>
6953 id: LANG_LANGUAGE_LOADED 9483 id: LANG_SERIAL_BITRATE_19200
6954 desc: shown when a language has been loaded from the dir browser 9484 desc: in system settings menu
6955 user: core 9485 user: core
6956 <source> 9486 <source>
6957 *: "New Language" 9487 *: none
9488 serial_port: "19200"
6958 </source> 9489 </source>
6959 <dest> 9490 <dest>
6960 *: "새 언어가 ì ìš©ë˜ì—ˆìŠµë‹ˆë‹¤." 9491 *: none
9492 serial_port: "19200"
6961 </dest> 9493 </dest>
6962 <voice> 9494 <voice>
6963 *: "" 9495 *: none
9496 serial_port: "19200"
6964 </voice> 9497 </voice>
6965</phrase> 9498</phrase>
6966<phrase> 9499<phrase>
6967 id: LANG_SETTINGS_LOADED 9500 id: LANG_SERIAL_BITRATE_38400
6968 desc: Feedback shown when a .cfg file is loaded 9501 desc: in system settings menu
6969 user: core 9502 user: core
6970 <source> 9503 <source>
6971 *: "Settings Loaded" 9504 *: none
9505 serial_port: "38400"
6972 </source> 9506 </source>
6973 <dest> 9507 <dest>
6974 *: "ì„¤ì •ì´ ì ìš©ë˜ì—ˆìŠµë‹ˆë‹¤." 9508 *: none
9509 serial_port: "38400"
6975 </dest> 9510 </dest>
6976 <voice> 9511 <voice>
6977 *: "" 9512 *: none
9513 serial_port: "38400"
6978 </voice> 9514 </voice>
6979</phrase> 9515</phrase>
6980<phrase> 9516<phrase>
6981 id: LANG_SETTINGS_SAVED 9517 id: LANG_SERIAL_BITRATE_57600
6982 desc: Feedback shown when a .cfg file is saved 9518 desc: in system settings menu
6983 user: core 9519 user: core
6984 <source> 9520 <source>
6985 *: "Settings Saved" 9521 *: none
9522 serial_port: "57600"
6986 </source> 9523 </source>
6987 <dest> 9524 <dest>
6988 *: "ì„¤ì •ì´ ì €ìž¥ë˜ì—ˆìŠµë‹ˆë‹¤." 9525 *: none
9526 serial_port: "57600"
6989 </dest> 9527 </dest>
6990 <voice> 9528 <voice>
6991 *: "" 9529 *: none
9530 serial_port: "57600"
6992 </voice> 9531 </voice>
6993</phrase> 9532</phrase>
6994<phrase> 9533<phrase>
6995 id: LANG_BOOT_CHANGED 9534 id: LANG_VERY_SLOW
6996 desc: File browser discovered the boot file was changed 9535 desc: in settings_menu
6997 user: core 9536 user: core
6998 <source> 9537 <source>
6999 *: "Boot changed" 9538 *: "Very slow"
7000 </source> 9539 </source>
7001 <dest> 9540 <dest>
7002 *: "부트 파ì¼ì´ 변경ë˜ì—ˆìŠµë‹ˆë‹¤." 9541 *: "매우 ëŠë¦¼"
7003 </dest> 9542 </dest>
7004 <voice> 9543 <voice>
7005 *: "" 9544 *: "매우 ëŠë¦¼"
7006 </voice> 9545 </voice>
7007</phrase> 9546</phrase>
7008<phrase> 9547<phrase>
7009 id: LANG_REBOOT_NOW 9548 id: LANG_SLOW
7010 desc: Do you want to reboot? 9549 desc: in settings_menu
7011 user: core 9550 user: core
7012 <source> 9551 <source>
7013 *: "Reboot now?" 9552 *: "Slow"
7014 </source> 9553 </source>
7015 <dest> 9554 <dest>
7016 *: "지금 재부팅 하시겠습니까?" 9555 *: "ëŠë¦¼"
7017 </dest> 9556 </dest>
7018 <voice> 9557 <voice>
7019 *: "" 9558 *: "ëŠë¦¼"
7020 </voice> 9559 </voice>
7021</phrase> 9560</phrase>
7022<phrase> 9561<phrase>
7023 id: LANG_OFF_ABORT 9562 id: LANG_VERY_FAST
7024 desc: Used on archosrecorder models 9563 desc: in settings_menu
7025 user: core 9564 user: core
7026 <source> 9565 <source>
7027 *: "OFF to abort" 9566 *: "Very fast"
7028 gigabeatfx: "POWER to abort"
7029 gigabeats,sansafuzeplus: "BACK to abort"
7030 iaudiom5,iaudiox5: "Long PLAY to abort"
7031 ipod*: "PLAY/PAUSE to abort"
7032 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "PREV to abort"
7033 iriverh100,iriverh120,iriverh300: "STOP to abort"
7034 </source> 9567 </source>
7035 <dest> 9568 <dest>
7036 *: "[꺼ì§]: 중지" 9569 *: "매우 빠름"
7037 gigabeatfx: "[ì „ì›]: 중지"
7038 gigabeats: "[뒤]: 중지"
7039 iaudiom5,iaudiox5: "[ìž¬ìƒ ê¸¸ê²Œëˆ„ë¦„]: 중지"
7040 ipod*: "[재ìƒ/ì¼ì‹œì •ì§€]: 중지"
7041 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "[ì´ì „]: 중지"
7042 iriverh100,iriverh120,iriverh300: "[정지]: 중지"
7043 </dest> 9570 </dest>
7044 <voice> 9571 <voice>
7045 *: "" 9572 *: "매우 빠름"
7046 </voice> 9573 </voice>
7047</phrase> 9574</phrase>
7048<phrase> 9575<phrase>
7049 id: LANG_NO_FILES 9576 id: LANG_FAST
7050 desc: in settings_menu 9577 desc: in settings_menu
7051 user: core 9578 user: core
7052 <source> 9579 <source>
7053 *: "No files" 9580 *: "Fast"
7054 </source> 9581 </source>
7055 <dest> 9582 <dest>
7056 *: "파ì¼ì´ 없습니다." 9583 *: "빠름"
7057 </dest> 9584 </dest>
7058 <voice> 9585 <voice>
7059 *: "" 9586 *: "빠름"
7060 </voice> 9587 </voice>
7061</phrase> 9588</phrase>
7062<phrase> 9589<phrase>
7063 id: LANG_KEYBOARD_LOADED 9590 id: LANG_SKIP_LENGTH
7064 desc: shown when a keyboard has been loaded from the dir browser 9591 desc: playback settings menu
7065 user: core 9592 user: core
7066 <source> 9593 <source>
7067 *: "New Keyboard" 9594 *: "Skip Length"
9595 </source>
9596 <dest>
9597 *: "ê¸¸ì´ ê±´ë„ˆë›°ê¸°"
9598 </dest>
9599 <voice>
9600 *: "ê¸¸ì´ ê±´ë„ˆë›°ê¸°"
9601 </voice>
9602</phrase>
9603<phrase>
9604 id: LANG_SKIP_TRACK
9605 desc: skip length setting entry 0
9606 user: core
9607 <source>
9608 *: "Skip Track"
7068 </source> 9609 </source>
7069 <dest> 9610 <dest>
7070 *: "새 키보드가 ì ìš©ë˜ì—ˆìŠµë‹ˆë‹¤." 9611 *: "트랙 ê±´ëˆë›°ê¸°"
7071 </dest> 9612 </dest>
7072 <voice> 9613 <voice>
9614 *: "트랙 건너뛰기"
9615 </voice>
9616</phrase>
9617<phrase>
9618 id: VOICE_CHAR_SLASH
9619 desc: spoken only, for spelling
9620 user: core
9621 <source>
9622 *: ""
9623 </source>
9624 <dest>
7073 *: "" 9625 *: ""
9626 </dest>
9627 <voice>
9628 *: "슬래쉬"
7074 </voice> 9629 </voice>
7075</phrase> 9630</phrase>
7076<phrase> 9631<phrase>
7077 id: LANG_MOVE 9632 id: LANG_GAIN_LEFT
7078 desc: The verb/action Move 9633 desc: in the recording screen
7079 user: core 9634 user: core
7080 <source> 9635 <source>
7081 *: "Move" 9636 *: none
9637 recording: "Gain L"
7082 </source> 9638 </source>
7083 <dest> 9639 <dest>
7084 *: "ì´ë™í•˜ê¸°" 9640 *: none
9641 recording: "ê²Œì¸ L"
7085 </dest> 9642 </dest>
7086 <voice> 9643 <voice>
7087 *: "ì´ë™í•˜ê¸°" 9644 *: none
9645 recording: "ê²Œì¸ ì™¼ìª½"
7088 </voice> 9646 </voice>
7089</phrase> 9647</phrase>
7090<phrase> 9648<phrase>
7091 id: LANG_SHOW_INDICES 9649 id: LANG_GAIN_RIGHT
7092 desc: in playlist viewer menu 9650 desc: in the recording screen
7093 user: core 9651 user: core
7094 <source> 9652 <source>
7095 *: "Show Indices" 9653 *: none
9654 recording: "Gain R"
7096 </source> 9655 </source>
7097 <dest> 9656 <dest>
7098 *: "순서 보기" 9657 *: none
9658 recording: "ê²Œì¸ R"
7099 </dest> 9659 </dest>
7100 <voice> 9660 <voice>
7101 *: "순서 보기" 9661 *: none
9662 recording: "ê²Œì¸ ì˜¤ë¥¸ìª½"
7102 </voice> 9663 </voice>
7103</phrase> 9664</phrase>
7104<phrase> 9665<phrase>
7105 id: LANG_TRACK_DISPLAY 9666 id: LANG_RECORDING_AGC_PRESET
7106 desc: in playlist viewer on+play menu 9667 desc: automatic gain control in record settings and screen
7107 user: core 9668 user: core
7108 <source> 9669 <source>
7109 *: "Track Display" 9670 *: none
9671 agc: "AGC"
7110 </source> 9672 </source>
7111 <dest> 9673 <dest>
7112 *: "트랙 보기" 9674 *: none
9675 agc: "ì§€ë™ ê²Œì¸ ì œì–´"
7113 </dest> 9676 </dest>
7114 <voice> 9677 <voice>
7115 *: "트랙 보기" 9678 *: none
9679 agc: "ìžë™ ê²Œì¸ ì œì–´"
7116 </voice> 9680 </voice>
7117</phrase> 9681</phrase>
7118<phrase> 9682<phrase>
7119 id: LANG_DISPLAY_TRACK_NAME_ONLY 9683 id: LANG_RECORDING_AGC_CLIPTIME
7120 desc: track display options 9684 desc: in record settings
7121 user: core 9685 user: core
7122 <source> 9686 <source>
7123 *: "Track Name Only" 9687 *: none
9688 agc: "AGC clip time"
7124 </source> 9689 </source>
7125 <dest> 9690 <dest>
7126 *: "트랙 ì´ë¦„만 보기" 9691 *: none
9692 agc: "AGC í´ë¦½ 시간"
7127 </dest> 9693 </dest>
7128 <voice> 9694 <voice>
7129 *: "트랙 ì´ë¦„만 보기" 9695 *: none
9696 agc: "ìžë™ ê²Œì¸ ì œì–´ í´ë¦½ 시간"
7130 </voice> 9697 </voice>
7131</phrase> 9698</phrase>
7132<phrase> 9699<phrase>
7133 id: LANG_DISPLAY_FULL_PATH 9700 id: LANG_RECORDING_AGC_MAXGAIN
7134 desc: track display options 9701 desc: AGC maximum gain in recording screen
7135 user: core 9702 user: core
7136 <source> 9703 <source>
7137 *: "Full Path" 9704 *: none
9705 agc: "AGC max. gain"
7138 </source> 9706 </source>
7139 <dest> 9707 <dest>
7140 *: "전체 경로" 9708 *: none
9709 agc: "AGC 최대 게ì¸"
7141 </dest> 9710 </dest>
7142 <voice> 9711 <voice>
7143 *: "전체 경로" 9712 *: none
9713 agc: "ìžë™ ê²Œì¸ ì œì–´ 최대 게ì¸"
7144 </voice> 9714 </voice>
7145</phrase> 9715</phrase>
7146<phrase> 9716<phrase>
7147 id: LANG_REMOVE 9717 id: LANG_RECORDING_FILENAME
7148 desc: in playlist viewer on+play menu 9718 desc: Filename header in recording screen
7149 user: core 9719 user: core
7150 <source> 9720 <source>
7151 *: "Remove" 9721 *: none
9722 recording: "Filename:"
7152 </source> 9723 </source>
7153 <dest> 9724 <dest>
7154 *: "제거하기" 9725 *: none
9726 recording: "파ì¼ì´ë¦„:"
7155 </dest> 9727 </dest>
7156 <voice> 9728 <voice>
7157 *: "제거하기" 9729 *: none
9730 recording: ""
7158 </voice> 9731 </voice>
7159</phrase> 9732</phrase>
7160<phrase> 9733<phrase>
7161 id: LANG_PLUGIN_CANT_OPEN 9734 id: LANG_PM_CLIPCOUNT
7162 desc: Plugin open error message 9735 desc: in recording GUI, for recording peak meter. MAX 5 characters!
7163 user: core 9736 user: core
7164 <source> 9737 <source>
7165 *: "Can't open %s" 9738 *: none
9739 recording: "CLIP:"
7166 </source> 9740 </source>
7167 <dest> 9741 <dest>
7168 *: "%sì„(를) ì—´ 수 없습니다." 9742 *: none
9743 recording: "í´ë¦½:"
7169 </dest> 9744 </dest>
7170 <voice> 9745 <voice>
7171 *: "" 9746 *: none
9747 recording: ""
7172 </voice> 9748 </voice>
7173</phrase> 9749</phrase>
7174<phrase> 9750<phrase>
7175 id: LANG_READ_FAILED 9751 id: LANG_RECORDING_TIMESPLIT_REC
7176 desc: There was an error reading a file 9752 desc: Display of record timer interval setting, on the record screen
7177 user: core 9753 user: core
7178 <source> 9754 <source>
7179 *: "Failed reading %s" 9755 *: none
9756 recording: "Split Time:"
7180 </source> 9757 </source>
7181 <dest> 9758 <dest>
7182 *: "%sì„(를) ì½ì§€ 못하였습니다." 9759 *: none
9760 recording: "분할 시간:"
7183 </dest> 9761 </dest>
7184 <voice> 9762 <voice>
7185 *: "" 9763 *: none
9764 recording: ""
7186 </voice> 9765 </voice>
7187</phrase> 9766</phrase>
7188<phrase> 9767<phrase>
7189 id: LANG_PLUGIN_WRONG_MODEL 9768 id: LANG_RECORDING_SIZE
7190 desc: The plugin is not compatible with the archos model trying to run it 9769 desc: Display of recorded file size
7191 user: core 9770 user: core
7192 <source> 9771 <source>
7193 *: "Incompatible model" 9772 *: none
9773 recording: "Size:"
7194 </source> 9774 </source>
7195 <dest> 9775 <dest>
7196 *: "ì´ ëª¨ë¸ì€ 지ì›í•˜ì§€ 않습니다." 9776 *: none
9777 recording: "í¬ê¸°:"
7197 </dest> 9778 </dest>
7198 <voice> 9779 <voice>
7199 *: "" 9780 *: none
9781 recording: ""
7200 </voice> 9782 </voice>
7201</phrase> 9783</phrase>
7202<phrase> 9784<phrase>
7203 id: LANG_PLUGIN_WRONG_VERSION 9785 id: LANG_RECORDING_MONO_MODE
7204 desc: The plugin is not compatible with the rockbox version trying to run it 9786 desc: in the recording settings
7205 user: core 9787 user: core
7206 <source> 9788 <source>
7207 *: "Incompatible version" 9789 *: none
9790 recording: "Mono mode"
7208 </source> 9791 </source>
7209 <dest> 9792 <dest>
7210 *: "ì´ ë²„ì „ì€ ì§€ì›í•˜ì§€ 않습니다." 9793 *: none
9794 recording: "모노 모드"
7211 </dest> 9795 </dest>
7212 <voice> 9796 <voice>
7213 *: "" 9797 *: none
9798 recording: "모노 모드"
7214 </voice> 9799 </voice>
7215</phrase> 9800</phrase>
7216<phrase> 9801<phrase>
7217 id: LANG_PLUGIN_ERROR 9802 id: LANG_SEARCH_RESULTS
7218 desc: The plugin return an error code 9803 desc: title for the list of results displayed after searching in a playlist
7219 user: core 9804 user: core
7220 <source> 9805 <source>
7221 *: "Plugin returned error" 9806 *: "Search Results"
7222 </source> 9807 </source>
7223 <dest> 9808 <dest>
7224 *: "플러그ì¸ì—ì„œ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤." 9809 *: "검색 ê²°ê³¼"
7225 </dest> 9810 </dest>
7226 <voice> 9811 <voice>
7227 *: "" 9812 *: "검색 결과"
7228 </voice> 9813 </voice>
7229</phrase> 9814</phrase>
7230<phrase> 9815<phrase>
7231 id: LANG_FILETYPES_FULL 9816 id: LANG_LEFT
7232 desc: Filetype array full 9817 desc: Generic use of 'left'
7233 user: core 9818 user: core
7234 <source> 9819 <source>
7235 *: "Filetype array full" 9820 *: "Left"
7236 </source> 9821 </source>
7237 <dest> 9822 <dest>
7238 *: "확장ìžì˜ 길ì´ê°€ 너무 ê¹ë‹ˆë‹¤." 9823 *: "왼쪽"
7239 </dest> 9824 </dest>
7240 <voice> 9825 <voice>
7241 *: "" 9826 *: "왼쪽"
7242 </voice> 9827 </voice>
7243</phrase> 9828</phrase>
7244<phrase> 9829<phrase>
7245 id: LANG_FM_PRESET_LOAD 9830 id: LANG_RIGHT
7246 desc: load preset list in fm radio 9831 desc: Generic use of 'right'
7247 user: core 9832 user: core
7248 <source> 9833 <source>
7249 *: none 9834 *: "Right"
7250 radio: "Load Preset List"
7251 </source> 9835 </source>
7252 <dest> 9836 <dest>
7253 *: none 9837 *: "오른쪽"
7254 radio: "프리셋 리스트 불러오기"
7255 </dest> 9838 </dest>
7256 <voice> 9839 <voice>
7257 *: none 9840 *: "오른쪽"
7258 radio: "프리셋 리스트 불러오기"
7259 </voice> 9841 </voice>
7260</phrase> 9842</phrase>
7261<phrase> 9843<phrase>
7262 id: LANG_FM_PRESET_SAVE 9844 id: LANG_RESET_SETTING
7263 desc: Save preset list in fm radio 9845 desc: used in the settings context menu
7264 user: core 9846 user: core
7265 <source> 9847 <source>
7266 *: none 9848 *: "Reset Setting"
7267 radio: "Save Preset List"
7268 </source> 9849 </source>
7269 <dest> 9850 <dest>
7270 *: none 9851 *: "설정 초기화"
7271 radio: "프리셋 리스트 저장"
7272 </dest> 9852 </dest>
7273 <voice> 9853 <voice>
7274 *: none 9854 *: "설정 초기화"
7275 radio: "프리셋 리스트 저장"
7276 </voice> 9855 </voice>
7277</phrase> 9856</phrase>
7278<phrase> 9857<phrase>
7279 id: LANG_FM_PRESET_CLEAR 9858 id: LANG_LEFT_QS_ITEM
7280 desc: clear preset list in fm radio 9859 desc: used for the submenu name for the quickscreen items
7281 user: core 9860 user: core
7282 <source> 9861 <source>
7283 *: none 9862 *: none
7284 radio: "Clear Preset List" 9863 quickscreen: "Set as Left Quickscreen Item"
7285 </source> 9864 </source>
7286 <dest> 9865 <dest>
7287 *: none 9866 *: none
7288 radio: "í”리셋 리스트 지움" 9867 quickscreen: "왼쪽 빠른í”ë©´ 항목으로 설정"
7289 </dest> 9868 </dest>
7290 <voice> 9869 <voice>
7291 *: none 9870 *: none
7292 radio: "í”리셋 리스트 지움" 9871 quickscreen: "왼쪽 빠른í”ë©´ 항목으로 설정"
7293 </voice> 9872 </voice>
7294</phrase> 9873</phrase>
7295<phrase> 9874<phrase>
7296 id: LANG_FMR 9875 id: LANG_RIGHT_QS_ITEM
7297 desc: Used when you need to say Preset List, also voiced 9876 desc: used for the submenu name for the quickscreen items
7298 user: core 9877 user: core
7299 <source> 9878 <source>
7300 *: none 9879 *: none
7301 radio: "Preset List" 9880 quickscreen: "Set as Right Quickscreen Item"
7302 </source> 9881 </source>
7303 <dest> 9882 <dest>
7304 *: none 9883 *: none
7305 radio: "í”리셋 리스트" 9884 quickscreen: "오른쪽 빠른í”ë©´ 항목으로 설정"
7306 </dest> 9885 </dest>
7307 <voice> 9886 <voice>
7308 *: none 9887 *: none
7309 radio: "í”리셋 리스트" 9888 quickscreen: "오른쪽 빠른í”ë©´ 항목으로 설정"
7310 </voice> 9889 </voice>
7311</phrase> 9890</phrase>
7312<phrase> 9891<phrase>
7313 id: LANG_FM_FIRST_AUTOSCAN 9892 id: LANG_BOTTOM_QS_ITEM
7314 desc: When you run the radio without an fmr file in settings 9893 desc: used for the submenu name for the quickscreen items
7315 user: core 9894 user: core
7316 <source> 9895 <source>
7317 *: none 9896 *: none
7318 radio: "No settings found. Autoscan?" 9897 quickscreen: "Set as Bottom Quickscreen Item"
7319 </source> 9898 </source>
7320 <dest> 9899 <dest>
7321 *: none 9900 *: none
7322 radio: "설ì ì„ 찾지 못했습니다. ìžë 검색ì 할까요?" 9901 quickscreen: "í˜ë‹¨ 빠른화면 항목으로 설정"
7323 </dest> 9902 </dest>
7324 <voice> 9903 <voice>
7325 *: none 9904 *: none
7326 radio: "설ì ì„ 찾지 못했습니다. ìžë 검색ì 할까요?" 9905 quickscreen: "í˜ë‹¨ 빠른화면 항목으로 설정"
7327 </voice> 9906 </voice>
7328</phrase> 9907</phrase>
7329<phrase> 9908<phrase>
7330 id: LANG_CROSSFEED_DIRECT_GAIN 9909 id: LANG_CREDITS
7331 desc: in crossfeed settings 9910 desc: in the Main Menu -> System screen
7332 user: core 9911 user: core
7333 <source> 9912 <source>
7334 *: "Direct Gain" 9913 *: "Credits"
7335 </source> 9914 </source>
7336 <dest> 9915 <dest>
7337 *: "다ì´ë ‰íŠ¸ 게ì¸" 9916 *: "í¬ë ˆë”§"
7338 </dest> 9917 </dest>
7339 <voice> 9918 <voice>
7340 *: "다ì´ë ‰íŠ¸ 게ì¸" 9919 *: "í¬ë ˆë”§"
7341 </voice> 9920 </voice>
7342</phrase> 9921</phrase>
7343<phrase> 9922<phrase>
7344 id: LANG_CROSSFEED_CROSS_GAIN 9923 id: LANG_SORT_INTERPRET_NUMBERS
7345 desc: in crossfeed settings 9924 desc: in Settings -> File view
7346 user: core 9925 user: core
7347 <source> 9926 <source>
7348 *: "Cross Gain" 9927 *: "Interpret numbers when sorting"
7349 </source> 9928 </source>
7350 <dest> 9929 <dest>
7351 *: "í¬ë¡œìŠ¤ 게ì¸" 9930 *: "ì •ë ¬ ì‹œ ìˆ«ìž í•´ì"
7352 </dest> 9931 </dest>
7353 <voice> 9932 <voice>
7354 *: "í¬ë¡œìŠ¤ 게ì¸" 9933 *: "ì •ë ¬ ì‹œ ìˆ«ìž í•´ì"
7355 </voice> 9934 </voice>
7356</phrase> 9935</phrase>
7357<phrase> 9936<phrase>
7358 id: LANG_CROSSFEED_HF_ATTENUATION 9937 id: LANG_SORT_INTERPRET_AS_DIGIT
7359 desc: in crossfeed settings 9938 desc: in Settings -> File view
7360 user: core 9939 user: core
7361 <source> 9940 <source>
7362 *: "High-Frequency Attenuation" 9941 *: "As digits"
7363 </source> 9942 </source>
7364 <dest> 9943 <dest>
7365 *: "고주파수 ê°ì‡„율" 9944 *: "숫ìžë¡œ"
7366 </dest> 9945 </dest>
7367 <voice> 9946 <voice>
7368 *: "고주파수 ê°ì‡„율" 9947 *: "숫ìžë¡œ"
7369 </voice> 9948 </voice>
7370</phrase> 9949</phrase>
7371<phrase> 9950<phrase>
7372 id: LANG_CROSSFEED_HF_CUTOFF 9951 id: LANG_SORT_INTERPRET_AS_NUMBERS
7373 desc: in crossfeed settings 9952 desc: in Settings -> File view
7374 user: core 9953 user: core
7375 <source> 9954 <source>
7376 *: "High-Frequency Cutoff" 9955 *: "As whole numbers"
7377 </source> 9956 </source>
7378 <dest> 9957 <dest>
7379 *: "고주파수 컷오프" 9958 *: "정수로"
7380 </dest> 9959 </dest>
7381 <voice> 9960 <voice>
7382 *: "고주파수 컷오프" 9961 *: "정수로"
7383 </voice> 9962 </voice>
7384</phrase> 9963</phrase>
7385<phrase> 9964<phrase>
7386 id: LANG_TAGCACHE_BUSY 9965 id: LANG_ENABLE_SPEAKER
7387 desc: when trying to shutdown and tagcache is committing 9966 desc: in Settings -> Sound Settings
7388 user: core 9967 user: core
7389 <source> 9968 <source>
7390 *: "Database is not ready" 9969 *: none
9970 speaker: "Enable Speaker"
7391 </source> 9971 </source>
7392 <dest> 9972 <dest>
7393 *: "ìŒì•… DBê°€ 없습니다." 9973 *: none
9974 speaker: "스피커 활성화"
7394 </dest> 9975 </dest>
7395 <voice> 9976 <voice>
7396 *: "ìŒì•… DBê°€ 없습니다." 9977 *: none
9978 speaker: "스피커 활성화"
7397 </voice> 9979 </voice>
7398</phrase> 9980</phrase>
7399<phrase> 9981<phrase>
7400 id: LANG_TAGNAVI_ALL_TRACKS 9982 id: LANG_TOUCHSCREEN_MODE
7401 desc: "<All tracks>" entry in tag browser 9983 desc: in Settings -> General -> Display -> Touchscreen Settings
7402 user: core 9984 user: core
7403 <source> 9985 <source>
7404 *: "<All tracks>" 9986 *: none
9987 touchscreen: "Touchscreen Mode"
7405 </source> 9988 </source>
7406 <dest> 9989 <dest>
7407 *: "<모든 ìŒì•…>" 9990 *: none
9991 touchscreen: "터치스í¬ë¦° 모드"
7408 </dest> 9992 </dest>
7409 <voice> 9993 <voice>
7410 *: "모든 ìŒì•…" 9994 *: none
9995 touchscreen: "터치스í¬ë¦° 모드"
7411 </voice> 9996 </voice>
7412</phrase> 9997</phrase>
7413<phrase> 9998<phrase>
7414 id: LANG_INVALID_FILENAME 9999 id: LANG_TOUCHSCREEN_GRID
7415 desc: "invalid filename entered" error message 10000 desc: in Settings -> General -> Display -> Touchscreen Settings
7416 user: core 10001 user: core
7417 <source> 10002 <source>
7418 *: "Invalid Filename!" 10003 *: none
10004 touchscreen: "3x3 Grid"
7419 </source> 10005 </source>
7420 <dest> 10006 <dest>
7421 *: "ìž˜ëª»ëœ íŒŒì¼ëª…입니다." 10007 *: none
10008 touchscreen: "3x3 그리드"
7422 </dest> 10009 </dest>
7423 <voice> 10010 <voice>
7424 *: "ìž˜ëª»ëœ íŒŒì¼ëª…입니다." 10011 *: none
10012 touchscreen: "3 곱하기 3 그리드"
7425 </voice> 10013 </voice>
7426</phrase> 10014</phrase>
7427<phrase> 10015<phrase>
7428 id: LANG_REMOTE_SCROLL_SETS 10016 id: LANG_TOUCHSCREEN_POINT
7429 desc: "Remote Scrolling Options" Submenu in "Scrolling Options" menu 10017 desc: in Settings -> General -> Display -> Touchscreen Settings
7430 user: core 10018 user: core
7431 <source> 10019 <source>
7432 *: none 10020 *: none
7433 remote: "Remote Scrolling Options" 10021 touchscreen: "Absolute Point"
7434 </source> 10022 </source>
7435 <dest> 10023 <dest>
7436 *: none 10024 *: none
7437 remote: "리모트 스í¬ë¡¤ 옵션" 10025 touchscreen: "절대ì "
7438 </dest> 10026 </dest>
7439 <voice> 10027 <voice>
7440 *: none 10028 *: none
7441 remote: "리모트 스í¬ë¡¤ 옵션" 10029 touchscreen: "절대ì "
7442 </voice> 10030 </voice>
7443</phrase> 10031</phrase>
7444<phrase> 10032<phrase>
7445 id: LANG_TAGCACHE_UPDATE 10033 id: LANG_PREVENT_SKIPPING
7446 desc: in tag cache settings 10034 desc: in Settings -> Playback Settings
7447 user: core 10035 user: core
7448 <source> 10036 <source>
7449 *: "Update Now" 10037 *: "Prevent Track Skipping"
7450 </source> 10038 </source>
7451 <dest> 10039 <dest>
7452 *: "지금 ì—…ë°ì´íŠ¸í•˜ê¸°" 10040 *: "트랙 건너뛰기 방지"
7453 </dest> 10041 </dest>
7454 <voice> 10042 <voice>
7455 *: "지금 ì—…ë°ì´íŠ¸í•˜ê¸°" 10043 *: "트랙 건너뛰기 방지"
7456 </voice> 10044 </voice>
7457</phrase> 10045</phrase>
7458<phrase> 10046<phrase>
7459 id: LANG_TAGCACHE_AUTOUPDATE 10047 id: LANG_TIMESTRETCH
7460 desc: in tag cache settings 10048 desc: timestretch enable
7461 user: core 10049 user: core
7462 <source> 10050 <source>
7463 *: "Auto Update" 10051 *: "Timestretch"
7464 </source> 10052 </source>
7465 <dest> 10053 <dest>
7466 *: "ìžë™ ì—…ë°ì´íŠ¸" 10054 *: "타ìžìŠ¤íŠ¸ë ˆì¹˜"
7467 </dest> 10055 </dest>
7468 <voice> 10056 <voice>
7469 *: "ìžë™ ì—…ë°ì´íŠ¸" 10057 *: "íƒ€ìž ìŠ¤íŠ¸ë ˆì¹˜"
7470 </voice> 10058 </voice>
7471</phrase> 10059</phrase>
7472<phrase> 10060<phrase>
7473 id: LANG_TAGCACHE_EXPORT 10061 id: LANG_SPEED
7474 desc: in tag cache settings 10062 desc: timestretch speed
7475 user: core 10063 user: core
7476 <source> 10064 <source>
7477 *: "Export Modifications" 10065 *: "Speed"
7478 </source> 10066 </source>
7479 <dest> 10067 <dest>
7480 *: "변경 사항 내보내기" 10068 *: "ìë"
7481 </dest> 10069 </dest>
7482 <voice> 10070 <voice>
7483 *: "변경 사항 내보내기" 10071 *: "ìë"
7484 </voice> 10072 </voice>
7485</phrase> 10073</phrase>
7486<phrase> 10074<phrase>
7487 id: LANG_CATALOG 10075 id: LANG_TOUCHSCREEN_SETTINGS
7488 desc: in onplay menu 10076 desc: in Settings -> General -> Display menu
7489 user: core 10077 user: core
7490 <source> 10078 <source>
7491 *: "Playlist Catalogue" 10079 *: none
10080 touchscreen: "Touchscreen Settings"
7492 </source> 10081 </source>
7493 <dest> 10082 <dest>
7494 *: "재ìƒëª©ë¡ 모ìŒ" 10083 *: none
10084 touchscreen: "터치스í¬ë¦° 설정"
7495 </dest> 10085 </dest>
7496 <voice> 10086 <voice>
7497 *: "재ìƒëª©ë¡ 모ìŒ" 10087 *: none
10088 touchscreen: "터치스í¬ë¦° 설정"
7498 </voice> 10089 </voice>
7499</phrase> 10090</phrase>
7500<phrase> 10091<phrase>
7501 id: LANG_CATALOG_ADD_TO 10092 id: LANG_TOUCHSCREEN_CALIBRATE
7502 desc: in onplay playlist catalogue submenu 10093 desc: in Settings -> General -> Display -> Touchscreen Settings
7503 user: core 10094 user: core
7504 <source> 10095 <source>
7505 *: "Add to Playlist" 10096 *: none
10097 touchscreen: "Calibrate"
7506 </source> 10098 </source>
7507 <dest> 10099 <dest>
7508 *: "재ìƒëª©ë¡ì— 추가" 10100 *: none
10101 touchscreen: "êµì •"
7509 </dest> 10102 </dest>
7510 <voice> 10103 <voice>
7511 *: "재ìƒëª©ë¡ì— 추가" 10104 *: none
10105 touchscreen: "êµì •"
7512 </voice> 10106 </voice>
7513</phrase> 10107</phrase>
7514<phrase> 10108<phrase>
7515 id: LANG_CATALOG_ADD_TO_NEW 10109 id: LANG_TOUCHSCREEN_RESET_CALIBRATION
7516 desc: in onplay playlist catalogue submenu 10110 desc: in Settings -> General -> Display -> Touchscreen Settings
7517 user: core 10111 user: core
7518 <source> 10112 <source>
7519 *: "Add to New Playlist" 10113 *: none
10114 touchscreen: "Reset Calibration"
7520 </source> 10115 </source>
7521 <dest> 10116 <dest>
7522 *: "새 재ìƒëª©ë¡ì— 추가" 10117 *: none
10118 touchscreen: "êµì • 재설정"
7523 </dest> 10119 </dest>
7524 <voice> 10120 <voice>
7525 *: "새 재ìƒëª©ë¡ì— 추가" 10121 *: none
10122 touchscreen: "êµì • 재설정"
7526 </voice> 10123 </voice>
7527</phrase> 10124</phrase>
7528<phrase> 10125<phrase>
7529 id: LANG_CATALOG_VIEW 10126 id: LANG_STATUSBAR_TOP
7530 desc: in onplay playlist catalogue submenu 10127 desc: in Settings -> General -> Display -> statusbar
7531 user: core 10128 user: core
7532 <source> 10129 <source>
7533 *: "View Catalogue" 10130 *: "Top"
7534 </source> 10131 </source>
7535 <dest> 10132 <dest>
7536 *: "재ìƒëª©ë¡ë“¤ 보기" 10133 *: "ìƒë‹¨"
7537 </dest> 10134 </dest>
7538 <voice> 10135 <voice>
7539 *: "재ìƒëª©ë¡ë“¤ 보기" 10136 *: "ìƒë‹¨"
7540 </voice> 10137 </voice>
7541</phrase> 10138</phrase>
7542<phrase> 10139<phrase>
7543 id: LANG_CATALOG_NO_DIRECTORY 10140 id: LANG_STATUSBAR_BOTTOM
7544 desc: error message when playlist catalogue directory doesn't exist 10141 desc: in Settings -> General -> Display -> statusbar
7545 user: core 10142 user: core
7546 <source> 10143 <source>
7547 *: "%s doesn't exist" 10144 *: "Bottom"
7548 </source> 10145 </source>
7549 <dest> 10146 <dest>
7550 *: "%s가 존재하지 않습니다." 10147 *: "하단"
7551 </dest> 10148 </dest>
7552 <voice> 10149 <voice>
7553 *: "" 10150 *: "하단"
7554 </voice> 10151 </voice>
7555</phrase> 10152</phrase>
7556<phrase> 10153<phrase>
7557 id: LANG_CATALOG_NO_PLAYLISTS 10154 id: LANG_REMOTE_STATUSBAR
7558 desc: error message when no playlists for playlist catalogue 10155 desc: in Settings -> General -> Display -> statusbar
7559 user: core 10156 user: core
7560 <source> 10157 <source>
7561 *: "No Playlists" 10158 *: none
10159 remote: "Remote Statusbar"
7562 </source> 10160 </source>
7563 <dest> 10161 <dest>
7564 *: "재ìƒëª©ë¡ì´ 없습니다." 10162 *: none
10163 remote: "ì›ê²© ìƒíƒœí‘œì‹œì¤„"
7565 </dest> 10164 </dest>
7566 <voice> 10165 <voice>
7567 *: "" 10166 *: none
10167 remote: "ì›ê²© ìƒíƒœí‘œì‹œì¤„"
7568 </voice> 10168 </voice>
7569</phrase> 10169</phrase>
7570<phrase> 10170<phrase>
7571 id: LANG_TAGCACHE_IMPORT 10171 id: LANG_SEMITONE
7572 desc: in tag cache settings 10172 desc:
7573 user: core 10173 user: core
7574 <source> 10174 <source>
7575 *: "Import Modifications" 10175 *: none
10176 pitchscreen: "Semitone"
7576 </source> 10177 </source>
7577 <dest> 10178 <dest>
7578 *: "변경 사항 들여오기" 10179 *: none
10180 pitchscreen: "ë°˜ìŒ"
7579 </dest> 10181 </dest>
7580 <voice> 10182 <voice>
7581 *: "변경 사항 들여오기" 10183 *: none
10184 pitchscreen: "ë°˜ìŒ"
7582 </voice> 10185 </voice>
7583</phrase> 10186</phrase>
7584<phrase> 10187<phrase>
7585 id: LANG_SPLIT_MEASURE 10188 id: LANG_STRETCH_LIMIT
7586 desc: in record timesplit options 10189 desc: "limit" in pitch screen
7587 user: core 10190 user: core
7588 <source> 10191 <source>
7589 *: none 10192 *: none
7590 recording: "Split Measure" 10193 pitchscreen: "Limit"
7591 </source> 10194 </source>
7592 <dest> 10195 <dest>
7593 *: none 10196 *: none
7594 recording: "분리 기준" 10197 pitchscreen: "제한"
7595 </dest> 10198 </dest>
7596 <voice> 10199 <voice>
7597 *: none 10200 *: none
7598 recording: "분리 기준" 10201 pitchscreen: "제한"
7599 </voice> 10202 </voice>
7600</phrase> 10203</phrase>
7601<phrase> 10204<phrase>
7602 id: LANG_SPLIT_TYPE 10205 id: LANG_PLAYBACK_RATE
7603 desc: in record timesplit options 10206 desc: "rate" in pitch screen
7604 user: core 10207 user: core
7605 <source> 10208 <source>
7606 *: none 10209 *: none
7607 recording: "What to do when Splitting" 10210 pitchscreen: "Rate"
7608 </source> 10211 </source>
7609 <dest> 10212 <dest>
7610 *: none 10213 *: none
7611 recording: "ë¶„ë¦¬ì¤‘ì— í•˜ëŠ” ë™ìž‘" 10214 pitchscreen: "전송률"
7612 </dest> 10215 </dest>
7613 <voice> 10216 <voice>
7614 *: none 10217 *: none
7615 recording: "ë¶„ë¦¬ì¤‘ì— í•˜ëŠ” ë™ìž‘" 10218 pitchscreen: "전송률"
7616 </voice> 10219 </voice>
7617</phrase> 10220</phrase>
7618<phrase> 10221<phrase>
7619 id: LANG_SPLIT_TIME 10222 id: LANG_USB_KEYPAD_MODE
7620 desc: in record timesplit options 10223 desc: in settings_menu
7621 user: core 10224 user: core
7622 <source> 10225 <source>
7623 *: none 10226 *: none
7624 recording: "Split Time" 10227 usb_hid: "USB Keypad Mode"
7625 </source> 10228 </source>
7626 <dest> 10229 <dest>
7627 *: none 10230 *: none
7628 recording: "ì‹œê°„ì— ë”°ë¥¸ 분리" 10231 usb_hid: "USB 키패드 모드"
7629 </dest> 10232 </dest>
7630 <voice> 10233 <voice>
7631 *: none 10234 *: none
7632 recording: "ì‹œê°„ì— ë”°ë¥¸ 분리" 10235 usb_hid: "USB 키패드 모드"
7633 </voice> 10236 </voice>
7634</phrase> 10237</phrase>
7635<phrase> 10238<phrase>
7636 id: LANG_SPLIT_SIZE 10239 id: LANG_MULTIMEDIA_MODE
7637 desc: in record timesplit options 10240 desc: in settings_menu
7638 user: core 10241 user: core
7639 <source> 10242 <source>
7640 *: none 10243 *: none
7641 recording: "Split Filesize" 10244 usb_hid: "Multimedia"
7642 </source> 10245 </source>
7643 <dest> 10246 <dest>
7644 *: none 10247 *: none
7645 recording: "파ì¼í¬ê¸°ì— ë°ë¼ 분리" 10248 usb_hid: "멀티미디어"
7646 </dest> 10249 </dest>
7647 <voice> 10250 <voice>
7648 *: none 10251 *: none
7649 recording: "파ì¼í¬ê¸°ì— ë°ë¼ 분리" 10252 usb_hid: "멀티미디어"
7650 </voice> 10253 </voice>
7651</phrase> 10254</phrase>
7652<phrase> 10255<phrase>
7653 id: LANG_START_NEW_FILE 10256 id: LANG_PRESENTATION_MODE
7654 desc: in record timesplit options 10257 desc: in settings_menu
7655 user: core 10258 user: core
7656 <source> 10259 <source>
7657 *: none 10260 *: none
7658 recording: "Start new file" 10261 usb_hid: "Presentation"
7659 </source> 10262 </source>
7660 <dest> 10263 <dest>
7661 *: none 10264 *: none
7662 recording: "새 íŒŒì¼ ì‹œìž‘" 10265 usb_hid: "프레젠íŒì´ì…˜"
7663 </dest> 10266 </dest>
7664 <voice> 10267 <voice>
7665 *: none 10268 *: none
7666 recording: "새 íŒŒì¼ ì‹œìž‘" 10269 usb_hid: "프레젠íŒì´ì…˜"
7667 </voice> 10270 </voice>
7668</phrase> 10271</phrase>
7669<phrase> 10272<phrase>
7670 id: LANG_STOP_RECORDING 10273 id: LANG_BROWSER_MODE
7671 desc: in record timesplit options 10274 desc: in settings_menu
7672 user: core 10275 user: core
7673 <source> 10276 <source>
7674 *: none 10277 *: none
7675 recording: "Stop recording" 10278 usb_hid: "Browser"
7676 </source> 10279 </source>
7677 <dest> 10280 <dest>
7678 *: none 10281 *: none
7679 recording: "ë…¹ìŒ ì¤‘ì§€" 10282 usb_hid: "íƒìƒ‰ê¸°"
7680 </dest> 10283 </dest>
7681 <voice> 10284 <voice>
7682 *: none 10285 *: none
7683 recording: "ë…¹ìŒ ì¤‘ì§€" 10286 usb_hid: "íƒìƒ‰ê¸°"
7684 </voice> 10287 </voice>
7685</phrase> 10288</phrase>
7686<phrase> 10289<phrase>
7687 id: LANG_REMOTE_LCD_OFF 10290 id: LANG_MOUSE_MODE
7688 desc: Remote lcd off splash in recording screen 10291 desc: in settings_menu
7689 user: core 10292 user: core
7690 <source> 10293 <source>
7691 *: none 10294 *: none
7692 remote: "Remote Display OFF" 10295 usb_hid: "Mouse"
7693 </source> 10296 </source>
7694 <dest> 10297 <dest>
7695 *: none 10298 *: none
7696 remote: "리모트 화면 ë„기" 10299 usb_hid: "마우스"
7697 </dest> 10300 </dest>
7698 <voice> 10301 <voice>
7699 *: none 10302 *: none
7700 remote: "리모트 화면 ë„기" 10303 usb_hid: "마우스"
7701 </voice> 10304 </voice>
7702</phrase> 10305</phrase>
7703<phrase> 10306<phrase>
7704 id: LANG_REMOTE_LCD_ON 10307 id: LANG_SCROLLBAR_WIDTH
7705 desc: Remote lcd off splash in recording screen 10308 desc: in Settings -> General -> Display -> Status-/Scrollbar
10309 user: core
10310 <source>
10311 *: "Scroll Bar Width"
10312 </source>
10313 <dest>
10314 *: "스í¬ë¡¤ë°” í­"
10315 </dest>
10316 <voice>
10317 *: "스í¬ë¡¤ë°” í­"
10318 </voice>
10319</phrase>
10320<phrase>
10321 id: LANG_COMPRESSOR
10322 desc: in sound settings
10323 user: core
10324 <source>
10325 *: "Compressor"
10326 </source>
10327 <dest>
10328 *: "압축기"
10329 </dest>
10330 <voice>
10331 *: "압축기"
10332 </voice>
10333</phrase>
10334<phrase>
10335 id: LANG_TOP_QS_ITEM
10336 desc: used for the submenu name for the quickscreen items
7706 user: core 10337 user: core
7707 <source> 10338 <source>
7708 *: none 10339 *: none
7709 remote: "(Vol- : Re-enable)" 10340 quickscreen: "Set as Top Quickscreen Item"
7710 </source> 10341 </source>
7711 <dest> 10342 <dest>
7712 *: none 10343 *: none
7713 remote: "(볼륨- : ë‹¤ìœ ì¼œê¸°)" 10344 quickscreen: "ìƒë¨ 빠른화면 항목으로 설정"
7714 </dest> 10345 </dest>
7715 <voice> 10346 <voice>
7716 *: none 10347 *: none
7717 remote: "(볼륨- : ë‹¤ìœ ì¼œê¸°)" 10348 quickscreen: "ìƒë¨ 빠른화면 항목으로 설정"
7718 </voice> 10349 </voice>
7719</phrase> 10350</phrase>
7720<phrase> 10351<phrase>
7721 id: LANG_BACKLIGHT_ON_BUTTON_HOLD 10352 id: LANG_FM_ITALY
7722 desc: in lcd settings 10353 desc: fm region Italy
7723 user: core 10354 user: core
7724 <source> 10355 <source>
7725 *: none 10356 *: none
7726 hold_button: "Backlight on Hold" 10357 radio: "Italy"
7727 </source> 10358 </source>
7728 <dest> 10359 <dest>
7729 *: none 10360 *: none
7730 hold_button: "홀드 ë²„íŠ¼ì´ ì¼œì¡Œì„ë•Œ ë°±ë¼ì´íŠ¸ 설정" 10361 radio: "ì´íƒˆë¦¬ì•"
7731 </dest> 10362 </dest>
7732 <voice> 10363 <voice>
7733 *: none 10364 *: none
7734 hold_button: "홀드 ë²„íŠ¼ì´ ì¼œì¡Œì„ë•Œ ë°±ë¼ì´íŠ¸ 설정" 10365 radio: "ì´íƒˆë¦¬ì•"
7735 </voice> 10366 </voice>
7736</phrase> 10367</phrase>
7737<phrase> 10368<phrase>
7738 id: LANG_NEVER 10369 id: LANG_FM_OTHER
7739 desc: in lcd settings 10370 desc: Catch-all FM region. Select if none of the others work
7740 user: core 10371 user: core
7741 <source> 10372 <source>
7742 *: none 10373 *: none
7743 lcd_sleep: "Never" 10374 radio: "Other"
7744 </source> 10375 </source>
7745 <dest> 10376 <dest>
7746 *: none 10377 *: none
7747 lcd_sleep: "ì¼œì§ ì•ŠìŒ" 10378 radio: "기타"
7748 </dest> 10379 </dest>
7749 <voice> 10380 <voice>
7750 *: none 10381 *: none
7751 lcd_sleep: "ì¼œì§ ì•ŠìŒ" 10382 radio: "기타"
7752 </voice> 10383 </voice>
7753</phrase> 10384</phrase>
7754<phrase> 10385<phrase>
7755 id: LANG_LCD_SLEEP_AFTER_BACKLIGHT_OFF 10386 id: LANG_COMPRESSOR_THRESHOLD
7756 desc: In display settings, time to switch LCD chip into power saving state 10387 desc: in sound settings
10388 user: core
10389 <source>
10390 *: "Threshold"
10391 </source>
10392 <dest>
10393 *: "한계ì "
10394 </dest>
10395 <voice>
10396 *: "한계ì "
10397 </voice>
10398</phrase>
10399<phrase>
10400 id: LANG_COMPRESSOR_RATIO
10401 desc: in sound settings
10402 user: core
10403 <source>
10404 *: "Ratio"
10405 </source>
10406 <dest>
10407 *: "비율"
10408 </dest>
10409 <voice>
10410 *: "비율"
10411 </voice>
10412</phrase>
10413<phrase>
10414 id: LANG_COMPRESSOR_RATIO_2
10415 desc: in sound settings
10416 user: core
10417 <source>
10418 *: "2:1"
10419 </source>
10420 <dest>
10421 *: "2:1"
10422 </dest>
10423 <voice>
10424 *: "2 대 1"
10425 </voice>
10426</phrase>
10427<phrase>
10428 id: LANG_COMPRESSOR_RATIO_4
10429 desc: in sound settings
10430 user: core
10431 <source>
10432 *: "4:1"
10433 </source>
10434 <dest>
10435 *: "4:1"
10436 </dest>
10437 <voice>
10438 *: "4 대 1"
10439 </voice>
10440</phrase>
10441<phrase>
10442 id: LANG_COMPRESSOR_RATIO_6
10443 desc: in sound settings
10444 user: core
10445 <source>
10446 *: "6:1"
10447 </source>
10448 <dest>
10449 *: "6:1"
10450 </dest>
10451 <voice>
10452 *: "6 대 1"
10453 </voice>
10454</phrase>
10455<phrase>
10456 id: LANG_COMPRESSOR_RATIO_10
10457 desc: in sound settings
10458 user: core
10459 <source>
10460 *: "10:1"
10461 </source>
10462 <dest>
10463 *: "10:1"
10464 </dest>
10465 <voice>
10466 *: "10 대 1"
10467 </voice>
10468</phrase>
10469<phrase>
10470 id: LANG_COMPRESSOR_RATIO_LIMIT
10471 desc: in sound settings
10472 user: core
10473 <source>
10474 *: "Limit"
10475 </source>
10476 <dest>
10477 *: "제한"
10478 </dest>
10479 <voice>
10480 *: "제한"
10481 </voice>
10482</phrase>
10483<phrase>
10484 id: LANG_COMPRESSOR_GAIN
10485 desc: in sound settings
10486 user: core
10487 <source>
10488 *: "Makeup Gain"
10489 </source>
10490 <dest>
10491 *: "ë©”ì´í¬ì—… 게ì¸"
10492 </dest>
10493 <voice>
10494 *: "ë©”ì´í¬ì—… 게ì¸"
10495 </voice>
10496</phrase>
10497<phrase>
10498 id: LANG_AUTO
10499 desc: in sound settings
10500 user: core
10501 <source>
10502 *: "Auto"
10503 </source>
10504 <dest>
10505 *: "ìžë™"
10506 </dest>
10507 <voice>
10508 *: "ìžë™"
10509 </voice>
10510</phrase>
10511<phrase>
10512 id: LANG_COMPRESSOR_KNEE
10513 desc: in sound settings
10514 user: core
10515 <source>
10516 *: "Knee"
10517 </source>
10518 <dest>
10519 *: "니"
10520 </dest>
10521 <voice>
10522 *: "니"
10523 </voice>
10524</phrase>
10525<phrase>
10526 id: LANG_COMPRESSOR_HARD_KNEE
10527 desc: in sound settings
10528 user: core
10529 <source>
10530 *: "Hard Knee"
10531 </source>
10532 <dest>
10533 *: "하드 니"
10534 </dest>
10535 <voice>
10536 *: "하드 니"
10537 </voice>
10538</phrase>
10539<phrase>
10540 id: LANG_COMPRESSOR_SOFT_KNEE
10541 desc: in sound settings
10542 user: core
10543 <source>
10544 *: "Soft Knee"
10545 </source>
10546 <dest>
10547 *: "소프트 니"
10548 </dest>
10549 <voice>
10550 *: "소프트 니"
10551 </voice>
10552</phrase>
10553<phrase>
10554 id: LANG_COMPRESSOR_ATTACK
10555 desc: in sound settings
10556 user: core
10557 <source>
10558 *: "Attack Time"
10559 </source>
10560 <dest>
10561 *: "ì–´íƒ íƒ€ìž„"
10562 </dest>
10563 <voice>
10564 *: "ì–´íƒ íƒ€ìž„"
10565 </voice>
10566</phrase>
10567<phrase>
10568 id: LANG_COMPRESSOR_RELEASE
10569 desc: in sound settings
10570 user: core
10571 <source>
10572 *: "Release Time"
10573 </source>
10574 <dest>
10575 *: "릴리즈 타임"
10576 </dest>
10577 <voice>
10578 *: "릴리즈 타임"
10579 </voice>
10580</phrase>
10581<phrase>
10582 id: LANG_SKIP_OUTRO
10583 desc: skipping to the 5 seconds before the end of a track
10584 user: core
10585 <source>
10586 *: "Skip to Outro"
10587 </source>
10588 <dest>
10589 *: "아웃트로로 건너뛰기"
10590 </dest>
10591 <voice>
10592 *: "아웃트로로 건너뛰기"
10593 </voice>
10594</phrase>
10595<phrase>
10596 id: VOICE_EXT_SBS
10597 desc: spoken only, for file extension
10598 user: core
10599 <source>
10600 *: ""
10601 </source>
10602 <dest>
10603 *: ""
10604 </dest>
10605 <voice>
10606 *: "ìƒíƒœí‘œì‹œì¤„ 스킨"
10607 </voice>
10608</phrase>
10609<phrase>
10610 id: VOICE_EXT_RSBS
10611 desc: spoken only, for file extension
7757 user: core 10612 user: core
7758 <source> 10613 <source>
7759 *: none 10614 *: none
7760 lcd_sleep: "Sleep (After Backlight Off)" 10615 remote: ""
7761 </source> 10616 </source>
7762 <dest> 10617 <dest>
7763 *: none 10618 *: none
7764 lcd_sleep: "ë°±ë¼ì´íŠ¸ 꺼진 후 슬립모드 진입시간" 10619 remote: ""
7765 </dest> 10620 </dest>
7766 <voice> 10621 <voice>
7767 *: none 10622 *: none
7768 lcd_sleep: "ë°±ë¼ì´íŠ¸ 꺼진 í 슬립모드 진입시간" 10623 remote: "ì›ê²© ìƒíƒœí‘œì‹œì¤ 스킨"
7769 </voice> 10624 </voice>
7770</phrase> 10625</phrase>
7771<phrase> 10626<phrase>
7772 id: LANG_SYSFONT_CHANNEL_STEREO 10627 id: LANG_USB_HID
7773 desc: in sound_settings 10628 desc: in settings_menu
7774 user: core 10629 user: core
7775 <source> 10630 <source>
7776 *: none 10631 *: none
7777 recording: "Stereo" 10632 usb_hid: "USB HID"
7778 </source> 10633 </source>
7779 <dest> 10634 <dest>
7780 *: none 10635 *: none
7781 recording: "스테레오" 10636 usb_hid: "USB HID"
7782 </dest> 10637 </dest>
7783 <voice> 10638 <voice>
7784 *: none 10639 *: none
7785 recording: "스테레오" 10640 usb_hid: "USB 휴먼 ì¸í„°íŽ˜ì´ìŠ¤ 장치"
7786 </voice> 10641 </voice>
7787</phrase> 10642</phrase>
7788<phrase> 10643<phrase>
7789 id: LANG_SYSFONT_CHANNEL_MONO 10644 id: LANG_QUEUE_LAST_SHUFFLED
7790 desc: in sound_settings 10645 desc: in onplay menu. queue a playlist randomly at end of dynamic playlist
10646 user: core
10647 <source>
10648 *: "Queue Last Shuffled"
10649 </source>
10650 <dest>
10651 *: "마지막으로 ì„žì€ ëŒ€ê¸°ì—´"
10652 </dest>
10653 <voice>
10654 *: "마지막으로 ì„žì€ ëŒ€ê¸°ì—´"
10655 </voice>
10656</phrase>
10657<phrase>
10658 id: LANG_MORSE_INPUT
10659 desc: in Settings -> System
7791 user: core 10660 user: core
7792 <source> 10661 <source>
7793 *: none 10662 *: none
7794 recording: "Mono" 10663 morse_input: "Use Morse Code Input"
7795 </source> 10664 </source>
7796 <dest> 10665 <dest>
7797 *: none 10666 *: none
7798 recording: "모ë¸" 10667 morse_input: "모스 부호 ìž…ë ¥ 사용"
7799 </dest> 10668 </dest>
7800 <voice> 10669 <voice>
7801 *: none 10670 *: none
7802 recording: "모ë¸" 10671 morse_input: "모스 부호 ìž…ë ¥ 사용"
7803 </voice> 10672 </voice>
7804</phrase> 10673</phrase>
7805<phrase> 10674<phrase>
7806 id: LANG_SYSFONT_EQUALIZER_EDIT_MODE 10675 id: LANG_AUTOTRACKSKIP
7807 desc: in the equalizer settings menu 10676 desc: in crossfade settings
7808 user: core 10677 user: core
7809 <source> 10678 <source>
7810 *: "Edit mode: %s %s" 10679 *: none
10680 crossfade: "Automatic Track Change Only"
7811 </source> 10681 </source>
7812 <dest> 10682 <dest>
7813 *: "편집 모드: %s %s" 10683 *: none
10684 crossfade: "ìžë™ 트랙 변경만 가능"
7814 </dest> 10685 </dest>
7815 <voice> 10686 <voice>
7816 *: "" 10687 *: none
10688 crossfade: "ìžë™ 트랙 변경만 가능"
7817 </voice> 10689 </voice>
7818</phrase> 10690</phrase>
7819<phrase> 10691<phrase>
7820 id: LANG_SYSFONT_EQUALIZER_BAND_CUTOFF 10692 id: LANG_NEXT_TRACK
7821 desc: in the equalizer settings menu 10693 desc: Shown in WPS
7822 user: core 10694 user: core
7823 <source> 10695 <source>
7824 *: "Cutoff" 10696 *: "Next Track:"
7825 </source> 10697 </source>
7826 <dest> 10698 <dest>
7827 *: "컷오프 주파수" 10699 *: "ë‹¤ìŒ íŠ¸ëž™:"
7828 </dest> 10700 </dest>
7829 <voice> 10701 <voice>
7830 *: "컷오프 주파수" 10702 *: "ë‹¤ìŒ íŠ¸ëž™"
7831 </voice> 10703 </voice>
7832</phrase> 10704</phrase>
7833<phrase> 10705<phrase>
7834 id: LANG_SYSFONT_RECORDING_QUALITY 10706 id: LANG_NEXT
7835 desc: in the recording settings 10707 desc: Shown in WPS (short form of Next Track)
10708 user: core
10709 <source>
10710 *: "Next:"
10711 </source>
10712 <dest>
10713 *: "다ìŒ:"
10714 </dest>
10715 <voice>
10716 *: "다ìŒ"
10717 </voice>
10718</phrase>
10719<phrase>
10720 id: LANG_OF
10721 desc: Shown in WPS: X of Y (tracks)
10722 user: core
10723 <source>
10724 *: "of"
10725 </source>
10726 <dest>
10727 *: "ì˜"
10728 </dest>
10729 <voice>
10730 *: "ì˜"
10731 </voice>
10732</phrase>
10733<phrase>
10734 id: LANG_BASE_SKIN
10735 desc: browse for the base skin in theme settings
10736 user: core
10737 <source>
10738 *: "Base Skin"
10739 </source>
10740 <dest>
10741 *: "기본 스킨"
10742 </dest>
10743 <voice>
10744 *: "기본 스킨"
10745 </voice>
10746</phrase>
10747<phrase>
10748 id: LANG_REMOTE_BASE_SKIN
10749 desc: browse for the base skin in theme settings
10750 user: core
10751 <source>
10752 *: "Remote Base Skin"
10753 </source>
10754 <dest>
10755 *: "기본 스킨 ì›ê²©"
10756 </dest>
10757 <voice>
10758 *: "기본 스킨 ì›ê²©"
10759 </voice>
10760</phrase>
10761<phrase>
10762 id: LANG_MAIN_SCREEN
10763 desc: in the main menu
7836 user: core 10764 user: core
7837 <source> 10765 <source>
7838 *: none 10766 *: none
7839 recording_hwcodec: "Quality" 10767 remote: "Main Screen"
7840 </source> 10768 </source>
7841 <dest> 10769 <dest>
7842 *: none 10770 *: none
7843 recording_hwcodec: "ìŒì§ˆ" 10771 remote: "ë©”ì¸ í™”ë©´"
7844 </dest> 10772 </dest>
7845 <voice> 10773 <voice>
7846 *: none 10774 *: none
7847 recording_hwcodec: "ìŒì§ˆ" 10775 remote: "ë©”ì¸ í™”ë©´"
7848 </voice> 10776 </voice>
7849</phrase> 10777</phrase>
7850<phrase> 10778<phrase>
7851 id: LANG_SYSFONT_RECORDING_FREQUENCY 10779 id: LANG_REMOTE_SCREEN
7852 desc: in the recording settings 10780 desc: in the main menu
7853 user: core 10781 user: core
7854 <source> 10782 <source>
7855 *: none 10783 *: none
7856 recording: "Frequency" 10784 remote: "Remote Screen"
7857 </source> 10785 </source>
7858 <dest> 10786 <dest>
7859 *: none 10787 *: none
7860 recording: "주파수" 10788 remote: "ì›ê²© 화면"
7861 </dest> 10789 </dest>
7862 <voice> 10790 <voice>
7863 *: none 10791 *: none
7864 recording: "주파수" 10792 remote: "ì›ê²© 화면"
7865 </voice> 10793 </voice>
7866</phrase> 10794</phrase>
7867<phrase> 10795<phrase>
7868 id: LANG_SYSFONT_RECORDING_SOURCE 10796 id: LANG_HISTOGRAM_INTERVAL
7869 desc: in the recording settings 10797 desc: in record settings menu
7870 user: core 10798 user: core
7871 <source> 10799 <source>
7872 *: none 10800 *: none
7873 recording: "Source" 10801 histogram: "Histogram interval"
7874 </source> 10802 </source>
7875 <dest> 10803 <dest>
7876 *: none 10804 *: none
7877 recording: "소스" 10805 histogram: "히스토그램 간격"
7878 </dest> 10806 </dest>
7879 <voice> 10807 <voice>
7880 *: none 10808 *: none
7881 recording: "소스" 10809 histogram: "히스토그램 간격"
7882 </voice> 10810 </voice>
7883</phrase> 10811</phrase>
7884<phrase> 10812<phrase>
7885 id: LANG_SYSFONT_RECORDING_SRC_MIC 10813 id: LANG_LINEOUT_ONOFF
7886 desc: in the recording settings 10814 desc: in system settings menu
7887 user: core 10815 user: core
7888 <source> 10816 <source>
7889 *: none 10817 *: none
7890 recording: "Int. Mic" 10818 lineout_poweroff: "Line Out"
7891 </source> 10819 </source>
7892 <dest> 10820 <dest>
7893 *: none 10821 *: none
7894 recording: "내부 마ì´í¬" 10822 lineout_poweroff: "ë¼ì¸ 아웃"
7895 </dest> 10823 </dest>
7896 <voice> 10824 <voice>
7897 *: none 10825 *: none
7898 recording: "내부 마ì´í¬" 10826 lineout_poweroff: "ë¼ì¸ 아웃"
7899 </voice> 10827 </voice>
7900</phrase> 10828</phrase>
7901<phrase> 10829<phrase>
7902 id: LANG_SYSFONT_RECORDING_SRC_DIGITAL 10830 id: LANG_HOTKEY
7903 desc: in the recording settings 10831 desc: hotkey menu
7904 user: core 10832 user: core
7905 <source> 10833 <source>
7906 *: none 10834 *: none
7907 recording: "Digital" 10835 hotkey: "Hotkey"
7908 </source> 10836 </source>
7909 <dest> 10837 <dest>
7910 *: none 10838 *: none
7911 recording: "디지털" 10839 hotkey: "단축키"
7912 </dest> 10840 </dest>
7913 <voice> 10841 <voice>
7914 *: none 10842 *: none
7915 recording: "디지털" 10843 hotkey: "단축키"
7916 </voice> 10844 </voice>
7917</phrase> 10845</phrase>
7918<phrase> 10846<phrase>
7919 id: LANG_SYSFONT_RECORD_TRIGGER 10847 id: LANG_HOTKEY_WPS
7920 desc: in recording settings_menu 10848 desc: hotkey menu
7921 user: core 10849 user: core
7922 <source> 10850 <source>
7923 *: none 10851 *: none
7924 recording: "Trigger" 10852 hotkey: "WPS Hotkey"
7925 </source> 10853 </source>
7926 <dest> 10854 <dest>
7927 *: none 10855 *: none
7928 recording: "트리거" 10856 hotkey: "WPS 단축키"
7929 </dest> 10857 </dest>
7930 <voice> 10858 <voice>
7931 *: none 10859 *: none
7932 recording: "트리거" 10860 hotkey: "WPS 단축키"
7933 </voice> 10861 </voice>
7934</phrase> 10862</phrase>
7935<phrase> 10863<phrase>
7936 id: LANG_SYSFONT_DIRBROWSE_F1 10864 id: LANG_HOTKEY_FILE_BROWSER
7937 desc: in dir browser, F1 button bar text 10865 desc: hotkey menu
7938 user: core 10866 user: core
7939 <source> 10867 <source>
7940 *: none 10868 *: none
7941 recorder_pad: "Menu" 10869 hotkey: "File Browser Hotkey"
7942 </source> 10870 </source>
7943 <dest> 10871 <dest>
7944 *: none 10872 *: none
7945 recorder_pad: "ë©”ë´" 10873 hotkey: "íŒŒì¼ íƒìƒê¸° 단축키"
7946 </dest> 10874 </dest>
7947 <voice> 10875 <voice>
7948 *: none 10876 *: none
7949 recorder_pad: "" 10877 hotkey: "íŒŒì¼ íƒìƒ‰ê¸° 단축키"
10878 </voice>
10879</phrase>
10880<phrase>
10881 id: LANG_RESUME_REWIND
10882 desc: in playback settings menu
10883 user: core
10884 <source>
10885 *: "Rewind Before Resume"
10886 </source>
10887 <dest>
10888 *: "재시작 ì „ ë˜ê°ê¸°"
10889 </dest>
10890 <voice>
10891 *: "재시작 ì „ ë˜ê°ê¸°"
7950 </voice> 10892 </voice>
7951</phrase> 10893</phrase>
7952<phrase> 10894<phrase>
7953 id: LANG_SYSFONT_DIRBROWSE_F2 10895 id: LANG_REMOTE_RADIOSCREEN
7954 desc: in dir browser, F2 button bar text 10896 desc: in the theme menu
7955 user: core 10897 user: core
7956 <source> 10898 <source>
7957 *: none 10899 *: none
7958 recorder_pad: "Option" 10900 radio_remote: "Remote Radio Screen"
7959 </source> 10901 </source>
7960 <dest> 10902 <dest>
7961 *: none 10903 *: none
7962 recorder_pad: "옵ì˜" 10904 radio_remote: "ì›ê²© ë¼ë””오 화면"
7963 </dest> 10905 </dest>
7964 <voice> 10906 <voice>
7965 *: none 10907 *: none
7966 recorder_pad: "" 10908 radio_remote: "ì›ê²© ë¼ë””오 화면"
7967 </voice> 10909 </voice>
7968</phrase> 10910</phrase>
7969<phrase> 10911<phrase>
7970 id: LANG_SYSFONT_DIRBROWSE_F3 10912 id: VOICE_EXT_FMS
7971 desc: in dir browser, F3 button bar text 10913 desc: spoken only, for file extension
7972 user: core 10914 user: core
7973 <source> 10915 <source>
7974 *: none 10916 *: none
7975 recorder_pad: "LCD" 10917 radio: ""
7976 </source> 10918 </source>
7977 <dest> 10919 <dest>
7978 *: none 10920 *: none
7979 recorder_pad: "ì•¡ì •" 10921 radio: ""
7980 </dest> 10922 </dest>
7981 <voice> 10923 <voice>
7982 *: none 10924 *: none
7983 recorder_pad: "" 10925 radio: "ë¼ë””오 화면 스킨"
7984 </voice> 10926 </voice>
7985</phrase> 10927</phrase>
7986<phrase> 10928<phrase>
7987 id: LANG_LOADING_PERCENT 10929 id: VOICE_EXT_RFMS
7988 desc: splash number of percents loaded 10930 desc: spoken only, for file extension
7989 user: core 10931 user: core
7990 <source> 10932 <source>
7991 *: "Loading... %d%% done (%s)" 10933 *: none
10934 radio_remote: ""
7992 </source> 10935 </source>
7993 <dest> 10936 <dest>
7994 *: "불러오는 중... %d%% 완료 (%s)" 10937 *: none
10938 radio_remote: ""
7995 </dest> 10939 </dest>
7996 <voice> 10940 <voice>
7997 *: "" 10941 *: none
10942 radio_remote: "ì›ê²© ë¼ë””오 화면 스킨"
7998 </voice> 10943 </voice>
7999</phrase> 10944</phrase>
8000<phrase> 10945<phrase>
8001 id: LANG_SHOW_PATH 10946 id: LANG_FM_STATION_HEADER
8002 desc: in settings_menu 10947 desc: in radio screen
8003 user: core 10948 user: core
8004 <source> 10949 <source>
8005 *: "Show Path" 10950 *: none
10951 radio: "Station:"
8006 </source> 10952 </source>
8007 <dest> 10953 <dest>
8008 *: "경로 표시" 10954 *: none
10955 radio: "스테ì´ì…˜:"
8009 </dest> 10956 </dest>
8010 <voice> 10957 <voice>
8011 *: "경로 표시" 10958 *: none
10959 radio: ""
8012 </voice> 10960 </voice>
8013</phrase> 10961</phrase>
8014<phrase> 10962<phrase>
8015 id: LANG_SHOW_PATH_CURRENT 10963 id: LANG_HW_EQ_TONE_CONTROLS
8016 desc: in show path menu 10964 desc: in sound_menu, hardware equalizer tone controls
8017 user: core 10965 user: core
8018 <source> 10966 <source>
8019 *: "Current Directory Only" 10967 *: none
10968 gigabeats,samsungypr1: "Tone Controls"
8020 </source> 10969 </source>
8021 <dest> 10970 <dest>
8022 *: "현재 í´ë”명만" 10971 *: none
10972 gigabeats,samsungypr1: "톤 제어"
8023 </dest> 10973 </dest>
8024 <voice> 10974 <voice>
8025 *: "현재 í´ë”명만" 10975 *: none
10976 gigabeats,samsungypr1: "톤 제어"
8026 </voice> 10977 </voice>
8027</phrase> 10978</phrase>
8028<phrase> 10979<phrase>
8029 id: LANG_AGC_SAFETY 10980 id: LANG_HW_EQ_TONE_CONTROLS_ADVANCED
8030 desc: AGC preset 10981 desc: in sound_menu, advanced settings for hardware equalizer tone controls
8031 user: core 10982 user: core
8032 <source> 10983 <source>
8033 *: none 10984 *: none
8034 agc: "Safety (clip)" 10985 gigabeats,samsungypr1: "Advanced Tone Control Settings"
8035 </source> 10986 </source>
8036 <dest> 10987 <dest>
8037 *: none 10988 *: none
8038 agc: "안전하게 (í´ë¦¬í‘)" 10989 gigabeats,samsungypr1: "고급 톤 제어 설정"
8039 </dest> 10990 </dest>
8040 <voice> 10991 <voice>
8041 *: none 10992 *: none
8042 agc: "안전하게 (í´ë¦¬í‘)" 10993 gigabeats,samsungypr1: "고급 톤 제어 설정"
8043 </voice> 10994 </voice>
8044</phrase> 10995</phrase>
8045<phrase> 10996<phrase>
8046 id: LANG_AGC_LIVE 10997 id: LANG_HW_EQ_GAIN
8047 desc: AGC preset 10998 desc: in sound_menu, hardware equalizer tone controls filter gain
8048 user: core 10999 user: core
8049 <source> 11000 <source>
8050 *: none 11001 *: none
8051 agc: "Live (slow)" 11002 gigabeats,samsungypr1: "Band %d Gain"
8052 </source> 11003 </source>
8053 <dest> 11004 <dest>
8054 *: none 11005 *: none
8055 agc: "ë¼ì´ë¸Œ (ëŠë¦¼)" 11006 gigabeats,samsungypr1: "대역 %d 게ì¸"
8056 </dest> 11007 </dest>
8057 <voice> 11008 <voice>
8058 *: none 11009 *: none
8059 agc: "ë¼ì´ë¸Œ (ëŠë¦¼)" 11010 gigabeats,samsungypr1: "대역 게ì¸"
8060 </voice> 11011 </voice>
8061</phrase> 11012</phrase>
8062<phrase> 11013<phrase>
8063 id: LANG_AGC_DJSET 11014 id: LANG_HW_EQ_FREQUENCY
8064 desc: AGC preset 11015 desc: in sound_menu, hardware equalizer tone controls shelf filter cutoff frequency
8065 user: core 11016 user: core
8066 <source> 11017 <source>
8067 *: none 11018 *: none
8068 agc: "DJ-Set (slow)" 11019 gigabeats,samsungypr1: "Band %d Frequency"
8069 </source> 11020 </source>
8070 <dest> 11021 <dest>
8071 *: none 11022 *: none
8072 agc: "DJ-ì„¸íŒ (ëŠë¦¼)" 11023 gigabeats,samsungypr1: "대역 %d 주파수"
8073 </dest> 11024 </dest>
8074 <voice> 11025 <voice>
8075 *: none 11026 *: none
8076 agc: "DJ-ì„¸íŒ (ëŠë¦¼)" 11027 gigabeats,samsungypr1: "대역 주파수"
8077 </voice> 11028 </voice>
8078</phrase> 11029</phrase>
8079<phrase> 11030<phrase>
8080 id: LANG_AGC_MEDIUM 11031 id: LANG_HW_EQ_WIDTH
8081 desc: AGC preset 11032 desc: in sound_menu, hardware equalizer tone controls peak bandwith setting
8082 user: core 11033 user: core
8083 <source> 11034 <source>
8084 *: none 11035 *: none
8085 agc: "Medium" 11036 gigabeats,samsungypr1: "Band %d Width"
8086 </source> 11037 </source>
8087 <dest> 11038 <dest>
8088 *: none 11039 *: none
8089 agc: "중간" 11040 gigabeats,samsungypr1: "대역 %d í­"
8090 </dest> 11041 </dest>
8091 <voice> 11042 <voice>
8092 *: none 11043 *: none
8093 agc: "중간" 11044 gigabeats,samsungypr1: "대역í­"
8094 </voice> 11045 </voice>
8095</phrase> 11046</phrase>
8096<phrase> 11047<phrase>
8097 id: LANG_AGC_VOICE 11048 id: LANG_HW_EQ_WIDTH_NARROW
8098 desc: AGC preset 11049 desc: in sound_menu, hardware equalizer tone controls narrow bandwith setting
8099 user: core 11050 user: core
8100 <source> 11051 <source>
8101 *: none 11052 *: none
8102 agc: "Voice (fast)" 11053 gigabeats,samsungypr1: "Narrow"
8103 </source> 11054 </source>
8104 <dest> 11055 <dest>
8105 *: none 11056 *: none
8106 agc: "ìŒì„± (빠름)" 11057 gigabeats,samsungypr1: "ì¢ìŒ"
8107 </dest> 11058 </dest>
8108 <voice> 11059 <voice>
8109 *: none 11060 *: none
8110 agc: "ìŒì„± (빠름)" 11061 gigabeats,samsungypr1: "ì¢ìŒ"
8111 </voice> 11062 </voice>
8112</phrase> 11063</phrase>
8113<phrase> 11064<phrase>
8114 id: LANG_RECORDING_AGC_MAXGAIN 11065 id: LANG_HW_EQ_WIDTH_WIDE
8115 desc: AGC maximum gain in recording screen 11066 desc: in sound_menu, hardware equalizer tone controls wide bandwidth setting
8116 user: core 11067 user: core
8117 <source> 11068 <source>
8118 *: none 11069 *: none
8119 agc: "AGC max. gain" 11070 gigabeats,samsungypr1: "Wide"
8120 </source> 11071 </source>
8121 <dest> 11072 <dest>
8122 *: none 11073 *: none
8123 agc: "AGC 최대 게ì¸" 11074 gigabeats,samsungypr1: "ë„“ìŒ"
8124 </dest> 11075 </dest>
8125 <voice> 11076 <voice>
8126 *: none 11077 *: none
8127 agc: "AGC 최대 게ì¸" 11078 gigabeats,samsungypr1: "ë„“ìŒ"
8128 </voice> 11079 </voice>
8129</phrase> 11080</phrase>
8130<phrase> 11081<phrase>
8131 id: VOICE_KBIT_PER_SEC 11082 id: LANG_DEPTH_3D
8132 desc: spoken only, a unit postfix 11083 desc: in sound_menu, amount of 3D enhancement effect
8133 user: core 11084 user: core
8134 <source> 11085 <source>
8135 *: "" 11086 *: none
11087 depth_3d: "3-D Enhancement"
8136 </source> 11088 </source>
8137 <dest> 11089 <dest>
8138 *: "" 11090 *: none
11091 depth_3d: "3-D í–¥ìƒ"
8139 </dest> 11092 </dest>
8140 <voice> 11093 <voice>
8141 *: "초당 킬로바ì´íŠ¸" 11094 *: none
11095 depth_3d: "3-D í–¥ìƒ"
8142 </voice> 11096 </voice>
8143</phrase> 11097</phrase>
8144<phrase> 11098<phrase>
8145 id: LANG_FM_REGION 11099 id: LANG_TAGNAVI_UNTAGGED
8146 desc: fm tuner region setting 11100 desc: "<untagged>" entry in tag browser
11101 user: core
11102 <source>
11103 *: "<Untagged>"
11104 </source>
11105 <dest>
11106 *: "<태그가 지정ë˜ì§€ ì•ŠìŒ>"
11107 </dest>
11108 <voice>
11109 *: "태그가 지정ë˜ì§€ ì•ŠìŒ"
11110 </voice>
11111</phrase>
11112<phrase>
11113 id: LANG_RADIOSCREEN
11114 desc: in the theme menu
8147 user: core 11115 user: core
8148 <source> 11116 <source>
8149 *: none 11117 *: none
8150 radio: "Region" 11118 radio: "Radio Screen"
8151 </source> 11119 </source>
8152 <dest> 11120 <dest>
8153 *: none 11121 *: none
8154 radio: "지역" 11122 radio: "ë¼ë””오 화면"
8155 </dest> 11123 </dest>
8156 <voice> 11124 <voice>
8157 *: none 11125 *: none
8158 radio: "지역" 11126 radio: "ë¼ë””오 화면"
8159 </voice> 11127 </voice>
8160</phrase> 11128</phrase>
8161<phrase> 11129<phrase>
8162 id: LANG_FM_EUROPE 11130 id: LANG_ID3_COMPOSER
8163 desc: fm tuner region europe 11131 desc: in tag viewer
11132 user: core
11133 <source>
11134 *: "Composer"
11135 </source>
11136 <dest>
11137 *: "작곡가"
11138 </dest>
11139 <voice>
11140 *: "작곡가"
11141 </voice>
11142</phrase>
11143<phrase>
11144 id: LANG_FORCE
11145 desc: alternative to yes/no for tristate settings
11146 user: core
11147 <source>
11148 *: "Force"
11149 </source>
11150 <dest>
11151 *: "강제"
11152 </dest>
11153 <voice>
11154 *: "강제"
11155 </voice>
11156</phrase>
11157<phrase>
11158 id: LANG_ONPLAY_PICTUREFLOW
11159 desc: Onplay pictureflow
11160 user: core
11161 <source>
11162 *: "PictureFlow"
11163 </source>
11164 <dest>
11165 *: "픽ì³í”Œë¡œìš°"
11166 </dest>
11167 <voice>
11168 *: "í”½ì³ í”Œë¡œìš° 열기"
11169 </voice>
11170</phrase>
11171<phrase>
11172 id: LANG_KBD_OK
11173 desc: in keyboard
8164 user: core 11174 user: core
8165 <source> 11175 <source>
8166 *: none 11176 *: none
8167 radio: "Europe" 11177 touchscreen: "OK"
8168 </source> 11178 </source>
8169 <dest> 11179 <dest>
8170 *: none 11180 *: none
8171 radio: "유럽" 11181 touchscreen: "확ì¸"
8172 </dest> 11182 </dest>
8173 <voice> 11183 <voice>
8174 *: none 11184 *: none
8175 radio: "유럽" 11185 touchscreen: "확ì¸"
8176 </voice> 11186 </voice>
8177</phrase> 11187</phrase>
8178<phrase> 11188<phrase>
8179 id: LANG_FM_US 11189 id: LANG_KBD_DELETE
8180 desc: fm region us / canada 11190 desc: in keyboard
8181 user: core 11191 user: core
8182 <source> 11192 <source>
8183 *: none 11193 *: none
8184 radio: "US / Canada" 11194 touchscreen: "Del"
8185 </source> 11195 </source>
8186 <dest> 11196 <dest>
8187 *: none 11197 *: none
8188 radio: "미국 / ì¼€ë˜ë‹¤" 11198 touchscreen: "ì‚­ì œ"
8189 </dest> 11199 </dest>
8190 <voice> 11200 <voice>
8191 *: none 11201 *: none
8192 radio: "미국 / ì¼€ë˜ë‹¤" 11202 touchscreen: "ì‚­ì œ"
8193 </voice> 11203 </voice>
8194</phrase> 11204</phrase>
8195<phrase> 11205<phrase>
8196 id: LANG_FM_JAPAN 11206 id: LANG_KBD_CANCEL
8197 desc: fm region japan 11207 desc: in keyboard
8198 user: core 11208 user: core
8199 <source> 11209 <source>
8200 *: none 11210 *: none
8201 radio: "Japan" 11211 touchscreen: "Cancel"
8202 </source> 11212 </source>
8203 <dest> 11213 <dest>
8204 *: none 11214 *: none
8205 radio: "ì¼ë³¸" 11215 touchscreen: "취소"
8206 </dest> 11216 </dest>
8207 <voice> 11217 <voice>
8208 *: none 11218 *: none
8209 radio: "ì¼ë³¸" 11219 touchscreen: "취소"
8210 </voice> 11220 </voice>
8211</phrase> 11221</phrase>
8212<phrase> 11222<phrase>
8213 id: LANG_FM_KOREA 11223 id: LANG_BOOKMARK_SETTINGS_AUTOUPDATE
8214 desc: fm region korea 11224 desc: prompt for user to decide whether to update bookmarks
11225 user: core
11226 <source>
11227 *: "Update on Stop"
11228 </source>
11229 <dest>
11230 *: "중지 ì‹œ ì—…ë°ì´íŠ¸"
11231 </dest>
11232 <voice>
11233 *: "중지 ì‹œ ì—…ë°ì´íŠ¸"
11234 </voice>
11235</phrase>
11236<phrase>
11237 id: LANG_RESET_START_DIR
11238 desc: reset the browser start directory
11239 user: core
11240 <source>
11241 *: "Start File Browser at /"
11242 </source>
11243 <dest>
11244 *: "/ ì—ì„œ íŒŒì¼ ë¸Œë¼ìš°ì € 시작"
11245 </dest>
11246 <voice>
11247 *: "루트ì—ì„œ íŒŒì¼ ë¸Œë¼ìš°ì € 시작"
11248 </voice>
11249</phrase>
11250<phrase>
11251 id: LANG_FM_RSSI
11252 desc: Signal strength of a received FM station
8215 user: core 11253 user: core
8216 <source> 11254 <source>
8217 *: none 11255 *: none
8218 radio: "Korea" 11256 radio: "Signal strength:"
8219 </source> 11257 </source>
8220 <dest> 11258 <dest>
8221 *: none 11259 *: none
8222 radio: "한국" 11260 radio: "신호 ê°•ë„:"
8223 </dest> 11261 </dest>
8224 <voice> 11262 <voice>
8225 *: none 11263 *: none
8226 radio: "한국" 11264 radio: "신호 ê°•ë„"
8227 </voice> 11265 </voice>
8228</phrase> 11266</phrase>
8229<phrase> 11267<phrase>
8230 id: LANG_RANDOM 11268 id: LANG_FILESIZE
8231 desc: random folder 11269 desc: in record timesplit options and in track information viewer
8232 user: core 11270 user: core
8233 <source> 11271 <source>
8234 *: "Random" 11272 *: "Filesize"
8235 </source> 11273 </source>
8236 <dest> 11274 <dest>
8237 *: "무작위" 11275 *: "파ì¼í¬ê¸°"
8238 </dest> 11276 </dest>
8239 <voice> 11277 <voice>
8240 *: "무작위" 11278 *: "파ì¼í¬ê¸°"
8241 </voice> 11279 </voice>
8242</phrase> 11280</phrase>
8243<phrase> 11281<phrase>
8244 id: LANG_AUDIOSCROBBLER 11282 id: LANG_AUTORESUME
8245 desc: "Last.fm Log" in the playback menu 11283 desc: resume settings menu
8246 user: core 11284 user: core
8247 <source> 11285 <source>
8248 *: "Last.fm Log" 11286 *: "Automatic resume"
8249 </source> 11287 </source>
8250 <dest> 11288 <dest>
8251 *: "Last.fm 로그 기ë¡" 11289 *: "ìžë 재시작"
8252 </dest> 11290 </dest>
8253 <voice> 11291 <voice>
8254 *: "Last.fm 로그 기ë¡" 11292 *: "ìžë 재시작"
8255 </voice> 11293 </voice>
8256</phrase> 11294</phrase>
8257<phrase> 11295<phrase>
8258 id: LANG_PLEASE_REBOOT 11296 id: LANG_AUTORESUME_AUTOMATIC
8259 desc: when activating an option that requires a reboot 11297 desc: resume on automatic track change
8260 user: core 11298 user: core
8261 <source> 11299 <source>
8262 *: "Please reboot to enable" 11300 *: "Resume on automatic track change"
8263 </source> 11301 </source>
8264 <dest> 11302 <dest>
8265 *: "사용하려면 다시 시작해주세요." 11303 *: "ìžë™ 트랙 변경 재시작"
8266 </dest> 11304 </dest>
8267 <voice> 11305 <voice>
8268 *: "" 11306 *: "ìžë™ 트랙 변경 재시작"
8269 </voice> 11307 </voice>
8270</phrase> 11308</phrase>
8271<phrase> 11309<phrase>
8272 id: LANG_DITHERING 11310 id: LANG_AUTORESUME_CUSTOM
8273 desc: in the sound settings menu 11311 desc: enable customization of resume on automatic track change
8274 user: core 11312 user: core
8275 <source> 11313 <source>
8276 *: "Dithering" 11314 *: "In custom directories only"
8277 </source> 11315 </source>
8278 <dest> 11316 <dest>
8279 *: "ë””ëë§" 11317 *: "커스텀 디렉토리ì—서만"
8280 </dest> 11318 </dest>
8281 <voice> 11319 <voice>
8282 *: "ë””ëë§" 11320 *: "커스텀 디렉토리ì—서만"
8283 </voice> 11321 </voice>
8284</phrase> 11322</phrase>
8285<phrase> 11323<phrase>
8286 id: LANG_FORMAT 11324 id: LANG_PAUSE_REWIND
8287 desc: audio format 11325 desc: Seconds to rewind when rewind on pause is enabled.
8288 user: core 11326 user: core
8289 <source> 11327 <source>
8290 *: "Format" 11328 *: "Rewind on Pause"
8291 </source> 11329 </source>
8292 <dest> 11330 <dest>
8293 *: "íŒŒì¼ í˜•ì‹" 11331 *: "ì¼ì‹œì •ì§€ ìœ ë˜ê°ê¸°"
8294 </dest> 11332 </dest>
8295 <voice> 11333 <voice>
8296 *: "íŒŒì¼ í˜•ì‹" 11334 *: "ì¼ì‹œì •ì§€ ìœ ë˜ê°ê¸°"
8297 </voice> 11335 </voice>
8298</phrase> 11336</phrase>
8299<phrase> 11337<phrase>
8300 id: LANG_AFMT_MPA_L3 11338 id: LANG_RESET_PLAYLISTCAT_DIR
8301 desc: audio format description 11339 desc:
8302 user: core 11340 user: core
8303 <source> 11341 <source>
8304 *: none 11342 *: "Reset Playlist Catalogue Directory"
8305 recording: "MPEG Layer 3"
8306 </source> 11343 </source>
8307 <dest> 11344 <dest>
8308 *: none 11345 *: "ìž¬ìƒ ëª©ë¡ ì¹´íƒˆë¡œê·¸ 디렉토리 재설정"
8309 recording: "MPEG Layer 3"
8310 </dest> 11346 </dest>
8311 <voice> 11347 <voice>
8312 *: none 11348 *: "ìž¬ìƒ ëª©ë¡ ì¹´íƒˆë¡œê·¸ 디렉토리 재설정"
8313 recording: "MPEG Layer 3"
8314 </voice> 11349 </voice>
8315</phrase> 11350</phrase>
8316<phrase> 11351<phrase>
8317 id: LANG_AFMT_PCM_WAV 11352 id: LANG_CURRENT_PLAYLIST
8318 desc: audio format description 11353 desc: Used when you need to say playlist, also voiced
11354 user: core
11355 <source>
11356 *: "Current Playlist"
11357 </source>
11358 <dest>
11359 *: "현재 재ìƒëª©ë¡"
11360 </dest>
11361 <voice>
11362 *: "현재 재ìƒëª©ë¡"
11363 </voice>
11364</phrase>
11365<phrase>
11366 id: LANG_SAVE_CHANGES
11367 desc: When you try to exit screens to confirm save
11368 user: core
11369 <source>
11370 *: "Save Changes?"
11371 </source>
11372 <dest>
11373 *: "변경 사항 저장할까요?"
11374 </dest>
11375 <voice>
11376 *: "변경 사항 저장할까요?"
11377 </voice>
11378</phrase>
11379<phrase>
11380 id: LANG_USB_SKIP_FIRST_DRIVE
11381 desc: in settings_menu
8319 user: core 11382 user: core
8320 <source> 11383 <source>
8321 *: none 11384 *: none
8322 recording: "PCM Wave" 11385 multidrive_usb: "USB Hide Internal Drive"
8323 </source> 11386 </source>
8324 <dest> 11387 <dest>
8325 *: none 11388 *: none
8326 recording: "PCM Wave" 11389 multidrive_usb: "USB 내부 ë“œë¼ì´ë¸Œ 숨기기"
8327 </dest> 11390 </dest>
8328 <voice> 11391 <voice>
8329 *: none 11392 *: none
8330 recording: "PCM Wave" 11393 multidrive_usb: "USB 내부 ë“œë¼ì´ë¸Œ 숨기기"
8331 </voice> 11394 </voice>
8332</phrase> 11395</phrase>
8333<phrase> 11396<phrase>
8334 id: LANG_AFMT_WAVPACK 11397 id: LANG_LIST_LINE_PADDING
8335 desc: audio format description 11398 desc: list padding, in display settings
8336 user: core 11399 user: core
8337 <source> 11400 <source>
8338 *: none 11401 *: none
8339 recording: "WavPack" 11402 touchscreen: "Line Padding in Lists"
8340 </source> 11403 </source>
8341 <dest> 11404 <dest>
8342 *: none 11405 *: none
8343 recording: "WavPack" 11406 touchscreen: "목ë¡ì˜ ë¼ì¸ 패딩"
8344 </dest> 11407 </dest>
8345 <voice> 11408 <voice>
8346 *: none 11409 *: none
8347 recording: "WavPack" 11410 touchscreen: "목ë¡ì˜ ë¼ì¸ 패딩"
8348 </voice> 11411 </voice>
8349</phrase> 11412</phrase>
8350<phrase> 11413<phrase>
8351 id: LANG_ENCODER_SETTINGS 11414 id: LANG_SLEEP_TIMER_DURATION
8352 desc: encoder settings 11415 desc: default sleep timer duration in minutes
11416 user: core
11417 <source>
11418 *: "Default Sleep Timer Duration"
11419 </source>
11420 <dest>
11421 *: "기본 절전 타ì´ë¨¸ 기간"
11422 </dest>
11423 <voice>
11424 *: "기본 절전 타ì´ë¨¸ 기간"
11425 </voice>
11426</phrase>
11427<phrase>
11428 id: LANG_SLEEP_TIMER_ON_POWER_UP
11429 desc: whether sleep timer starts on power up
11430 user: core
11431 <source>
11432 *: "Start Sleep Timer On Boot"
11433 </source>
11434 <dest>
11435 *: "부팅 ì‹œ 절전 타ì´ë¨¸ 시작"
11436 </dest>
11437 <voice>
11438 *: "부팅 ì‹œ 절전 타ì´ë¨¸ 시작"
11439 </voice>
11440</phrase>
11441<phrase>
11442 id: LANG_SLEEP_TIMER_CANCEL_CURRENT
11443 desc: shown instead of sleep timer when it's running
11444 user: core
11445 <source>
11446 *: "Cancel Sleep Timer"
11447 </source>
11448 <dest>
11449 *: "절전 타ì´ë¨¸ 취소"
11450 </dest>
11451 <voice>
11452 *: "절전 타ì´ë¨¸ 취소"
11453 </voice>
11454</phrase>
11455<phrase>
11456 id: LANG_LIST_SEPARATOR
11457 desc: line between lines in lists
11458 user: core
11459 <source>
11460 *: "Line Separator"
11461 </source>
11462 <dest>
11463 *: "ë¼ì¸ 분리기"
11464 </dest>
11465 <voice>
11466 *: "ë¼ì¸ 분리기"
11467 </voice>
11468</phrase>
11469<phrase>
11470 id: LANG_LIST_SEPARATOR_COLOR
11471 desc: line between lines in lists
11472 user: core
11473 <source>
11474 *: "Line Separator Colour"
11475 </source>
11476 <dest>
11477 *: "ë¼ì¸ 분리기 색ìƒ"
11478 </dest>
11479 <voice>
11480 *: "ë¼ì¸ 분리기 색ìƒ"
11481 </voice>
11482</phrase>
11483<phrase>
11484 id: LANG_SHORTCUTS
11485 desc: Title in the shortcuts menu
11486 user: core
11487 <source>
11488 *: "Shortcuts"
11489 </source>
11490 <dest>
11491 *: "바로가기"
11492 </dest>
11493 <voice>
11494 *: "바로가기"
11495 </voice>
11496</phrase>
11497<phrase>
11498 id: LANG_KEYCLICK_SOFTWARE
11499 desc: in keyclick settings menu
8353 user: core 11500 user: core
8354 <source> 11501 <source>
8355 *: none 11502 *: none
8356 recording: "Encoder Settings" 11503 hardware_click: "Headphone Keyclick"
8357 </source> 11504 </source>
8358 <dest> 11505 <dest>
8359 *: none 11506 *: none
8360 recording: "ì¸ì½”ë 설정" 11507 hardware_click: "í—¤ë“œí° í‚¤í´ë¦­"
8361 </dest> 11508 </dest>
8362 <voice> 11509 <voice>
8363 *: none 11510 *: none
8364 recording: "ì¸ì½”ë 설정" 11511 hardware_click: "í—¤ë“œí° í‚¤í´ë¦­"
8365 </voice> 11512 </voice>
8366</phrase> 11513</phrase>
8367<phrase> 11514<phrase>
8368 id: LANG_NO_SETTINGS 11515 id: LANG_KEYCLICK_HARDWARE
8369 desc: when something has settings in a certain context 11516 desc: in keyclick settings menu
8370 user: core 11517 user: core
8371 <source> 11518 <source>
8372 *: none 11519 *: none
8373 recording: "(No Settings)" 11520 hardware_click: "Speaker Keyclick"
8374 </source> 11521 </source>
8375 <dest> 11522 <dest>
8376 *: none 11523 *: none
8377 recording: "(가능한 ì¤ì • ì—†ìŒ)" 11524 hardware_click: "스피커 키í´ë¦­"
8378 </dest> 11525 </dest>
8379 <voice> 11526 <voice>
8380 *: none 11527 *: none
8381 recording: "가능한 ì¤ì • ì—†ìŒ" 11528 hardware_click: "스피커 키í´ë¦­"
8382 </voice> 11529 </voice>
8383</phrase> 11530</phrase>
8384<phrase> 11531<phrase>
8385 id: LANG_SOURCE_FREQUENCY 11532 id: LANG_GLYPHS
8386 desc: when recording source frequency setting must follow source 11533 desc: in settings_menu
11534 user: core
11535 <source>
11536 *: "Glyphs To Cache"
11537 </source>
11538 <dest>
11539 *: "ìºì‹œí•  문ìž"
11540 </dest>
11541 <voice>
11542 *: "ìºì‹œí•  문ìž"
11543 </voice>
11544</phrase>
11545<phrase>
11546 id: LANG_STARTUP_SHUTDOWN
11547 desc: in the general settings menu
11548 user: core
11549 <source>
11550 *: "Startup/Shutdown"
11551 </source>
11552 <dest>
11553 *: "시작/종료"
11554 </dest>
11555 <voice>
11556 *: "시작/종료"
11557 </voice>
11558</phrase>
11559<phrase>
11560 id: LANG_KEYPRESS_RESTARTS_SLEEP_TIMER
11561 desc: whether to restart running sleep timer on keypress
11562 user: core
11563 <source>
11564 *: "Restart Sleep Timer On Keypress"
11565 </source>
11566 <dest>
11567 *: "키를 누를 ë•Œ 절전 타ì´ë¨¸ 다시 시작"
11568 </dest>
11569 <voice>
11570 *: "키를 누를 ë•Œ 절전 타ì´ë¨¸ 다시 시작"
11571 </voice>
11572</phrase>
11573<phrase>
11574 id: LANG_CONSTRAIN_NEXT_FOLDER
11575 desc: in settings_menu. Whether LANG_NEXT_FOLDER should be constrained to directories within LANG_SET_AS_START_DIR
11576 user: core
11577 <source>
11578 *: "Constrain Auto-Change"
11579 </source>
11580 <dest>
11581 *: "ìžë™ 변경 제한"
11582 </dest>
11583 <voice>
11584 *: "ìžë™ 변경 제한"
11585 </voice>
11586</phrase>
11587<phrase>
11588 id: LANG_USE_SHORTCUTS_INSTEAD_OF_QS
11589 desc: in settings_menu.
8387 user: core 11590 user: core
8388 <source> 11591 <source>
8389 *: none 11592 *: none
8390 recording: "(Same As Source)" 11593 quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
8391 </source> 11594 </source>
8392 <dest> 11595 <dest>
8393 *: none 11596 *: none
8394 recording: "(소스ì ëì¼í•˜ê²Œ 설정)" 11597 quickscreen: "빠른 í”ë©´ 대신 바로가기 메뉴 사용"
8395 </dest> 11598 </dest>
8396 <voice> 11599 <voice>
8397 *: none 11600 *: none
8398 recording: "소스ì ëì¼í•˜ê²Œ 설정" 11601 quickscreen: "빠른 í”ë©´ 대신 바로가기 메뉴 사용"
8399 </voice> 11602 </voice>
8400</phrase> 11603</phrase>
8401<phrase> 11604<phrase>
8402 id: LANG_BITRATE 11605 id: LANG_SLEEP_TIMER_START_CURRENT
8403 desc: bits-kilobits per unit time 11606 desc: shown when a sleep timer isn't running
11607 user: core
11608 <source>
11609 *: "Start Sleep Timer"
11610 </source>
11611 <dest>
11612 *: "절전 타ì´ë¨¸ 시작"
11613 </dest>
11614 <voice>
11615 *: "절전 타ì´ë¨¸ 시작"
11616 </voice>
11617</phrase>
11618<phrase>
11619 id: LANG_CODEPAGE_WESTERN_EUROPEAN
11620 desc: in codepage setting menu
11621 user: core
11622 <source>
11623 *: "Western European (CP1252)"
11624 </source>
11625 <dest>
11626 *: "서유럽어 (CP1252)"
11627 </dest>
11628 <voice>
11629 *: "서유럽어"
11630 </voice>
11631</phrase>
11632<phrase>
11633 id: LANG_CROSSFEED_MEIER
11634 desc: in sound settings
11635 user: core
11636 <source>
11637 *: "Simple (Meier)"
11638 </source>
11639 <dest>
11640 *: "간단한 (마ì´ì–´)"
11641 </dest>
11642 <voice>
11643 *: "간단함"
11644 </voice>
11645</phrase>
11646<phrase>
11647 id: LANG_CROSSFEED_CUSTOM
11648 desc: in sound settings
11649 user: core
11650 <source>
11651 *: "Custom"
11652 </source>
11653 <dest>
11654 *: "커스텀"
11655 </dest>
11656 <voice>
11657 *: "커스텀"
11658 </voice>
11659</phrase>
11660<phrase>
11661 id: LANG_SELECT_FOLDER
11662 desc: in settings_menu
11663 user: core
11664 <source>
11665 *: "Select one or more directories"
11666 </source>
11667 <dest>
11668 *: "하나 ì´ìƒì˜ 디렉터리 ì„ íƒ"
11669 </dest>
11670 <voice>
11671 *: "하나 ì´ìƒì˜ 디렉터리 ì„ íƒ"
11672 </voice>
11673</phrase>
11674<phrase>
11675 id: LANG_SELECT_DATABASE_DIRS
11676 desc: in settings_menu
11677 user: core
11678 <source>
11679 *: "Select directories to scan"
11680 </source>
11681 <dest>
11682 *: "검색할 디렉토리 ì„ íƒ"
11683 </dest>
11684 <voice>
11685 *: "검색할 디렉토리 ì„ íƒ"
11686 </voice>
11687</phrase>
11688<phrase>
11689 id: LANG_FILTER_ROLL_OFF
11690 desc: in sound settings
8404 user: core 11691 user: core
8405 <source> 11692 <source>
8406 *: none 11693 *: none
8407 recording: "Bitrate" 11694 filter_roll_off: "DAC filter roll-off"
8408 </source> 11695 </source>
8409 <dest> 11696 <dest>
8410 *: none 11697 *: none
8411 recording: "비트레ì´íŠ¸" 11698 filter_roll_off: "DAC í•„í„° 롤오프"
8412 </dest> 11699 </dest>
8413 <voice> 11700 <voice>
8414 *: none 11701 *: none
8415 recording: "비트레ì´íŠ¸" 11702 filter_roll_off: "DAC í•„í„° 롤오프"
8416 </voice> 11703 </voice>
8417</phrase> 11704</phrase>
8418<phrase> 11705<phrase>
8419 id: LANG_RECORD_TRIGGER_TYPE 11706 id: LANG_FILTER_SHARP
8420 desc: in recording trigger menu 11707 desc: in sound settings
8421 user: core 11708 user: core
8422 <source> 11709 <source>
8423 *: none 11710 *: none
8424 recording: "Trigtype" 11711 filter_roll_off: "Sharp"
8425 </source> 11712 </source>
8426 <dest> 11713 <dest>
8427 *: none 11714 *: none
8428 recording: "트리거 타입" 11715 filter_roll_off: "ì„ ëªí•¨"
8429 </dest> 11716 </dest>
8430 <voice> 11717 <voice>
8431 *: none 11718 *: none
8432 recording: "트리거 타입" 11719 filter_roll_off: "ì„ ëªí•¨"
8433 </voice> 11720 </voice>
8434</phrase> 11721</phrase>
8435<phrase> 11722<phrase>
8436 id: LANG_RECORD_TRIGGER_STOP 11723 id: LANG_FILTER_SLOW
8437 desc: trigger types 11724 desc: in sound settings
8438 user: core 11725 user: core
8439 <source> 11726 <source>
8440 *: none 11727 *: none
8441 recording: "Stop" 11728 filter_roll_off: "Slow"
8442 </source> 11729 </source>
8443 <dest> 11730 <dest>
8444 *: none 11731 *: none
8445 recording: "정지" 11732 filter_roll_off: "ëŠë¦¼"
8446 </dest> 11733 </dest>
8447 <voice> 11734 <voice>
8448 *: none 11735 *: none
8449 recording: "정지" 11736 filter_roll_off: "ëŠë¦¼"
8450 </voice> 11737 </voice>
8451</phrase> 11738</phrase>
8452<phrase> 11739<phrase>
8453 id: LANG_RECORD_TRIGGER_NEWFILESTP 11740 id: LANG_FILTER_SHORT_SHARP
8454 desc: trigger types 11741 desc: in sound settings
8455 user: core 11742 user: core
8456 <source> 11743 <source>
8457 *: none 11744 *: none
8458 recording: "New file" 11745 filter_roll_off: "Short Sharp"
8459 </source> 11746 </source>
8460 <dest> 11747 <dest>
8461 *: none 11748 *: none
8462 recording: "새 파ì¼" 11749 filter_roll_off: "약간 부족한 선명함"
8463 </dest> 11750 </dest>
8464 <voice> 11751 <voice>
8465 *: none 11752 *: none
8466 recording: "새 파ì¼" 11753 filter_roll_off: "약간 부족한 선명함"
8467 </voice> 11754 </voice>
8468</phrase> 11755</phrase>
8469<phrase> 11756<phrase>
8470 id: LANG_WARNING_BATTERY_LOW 11757 id: LANG_FILTER_SHORT_SLOW
8471 desc: general warning 11758 desc: in sound settings
8472 user: core 11759 user: core
8473 <source> 11760 <source>
8474 *: "WARNING! Low Battery!" 11761 *: none
11762 filter_roll_off: "Short Slow"
8475 </source> 11763 </source>
8476 <dest> 11764 <dest>
8477 *: "주ì˜! 배터리가 부족합니다!" 11765 *: none
11766 filter_roll_off: "약간 ëŠë¦¼"
8478 </dest> 11767 </dest>
8479 <voice> 11768 <voice>
8480 *: "" 11769 *: none
11770 filter_roll_off: "약간 ëŠë¦¼"
8481 </voice> 11771 </voice>
8482</phrase> 11772</phrase>
8483<phrase> 11773<phrase>
8484 id: LANG_WARNING_BATTERY_EMPTY 11774 id: LANG_FILTER_SUPER_SLOW
8485 desc: general warning 11775 desc: in sound settings
8486 user: core 11776 user: core
8487 <source> 11777 <source>
8488 *: "Battery empty! RECHARGE!" 11778 *: none
11779 filter_roll_off: "Super Slow"
8489 </source> 11780 </source>
8490 <dest> 11781 <dest>
8491 *: "배터리가 없습니다! 충전해주세요!" 11782 *: none
11783 filter_roll_off: "ì—„ì²­ ëŠë¦¼"
8492 </dest> 11784 </dest>
8493 <voice> 11785 <voice>
8494 *: "" 11786 *: none
11787 filter_roll_off: "ì—„ì²­ ëŠë¦¼"
8495 </voice> 11788 </voice>
8496</phrase> 11789</phrase>
8497<phrase> 11790<phrase>
8498 id: LANG_AFMT_AIFF 11791 id: LANG_FILTER_SHORT
8499 desc: audio format description 11792 desc: in sound settings
8500 user: core 11793 user: core
8501 <source> 11794 <source>
8502 *: none 11795 *: none
8503 recording: "AIFF" 11796 es9018: "Short"
8504 </source> 11797 </source>
8505 <dest> 11798 <dest>
8506 *: none 11799 *: none
8507 recording: "AIFF" 11800 es9018: "짧ìŒ"
8508 </dest> 11801 </dest>
8509 <voice> 11802 <voice>
8510 *: none 11803 *: none
8511 recording: "AIFF" 11804 es9018: "짧ìŒ"
8512 </voice> 11805 </voice>
8513</phrase> 11806</phrase>
8514<phrase> 11807<phrase>
8515 id: LANG_PROPERTIES 11808 id: LANG_FILTER_BYPASS
8516 desc: browser file/dir properties 11809 desc: in sound settings
8517 user: core 11810 user: core
8518 <source> 11811 <source>
8519 *: "Properties" 11812 *: none
11813 es9018: "Bypass"
8520 </source> 11814 </source>
8521 <dest> 11815 <dest>
8522 *: "ì†ì„±" 11816 *: none
11817 es9018: "우회"
8523 </dest> 11818 </dest>
8524 <voice> 11819 <voice>
8525 *: "ì†ì„±" 11820 *: none
11821 es9018: "우회"
8526 </voice> 11822 </voice>
8527</phrase> 11823</phrase>
8528<phrase> 11824<phrase>
8529 id: LANG_SHUFFLE_TRACKSKIP 11825 id: LANG_FILTER_LINEAR_FAST
8530 desc: in settings_menu 11826 desc: in sound settings
8531 user: core 11827 user: core
8532 <source> 11828 <source>
8533 *: none 11829 *: none
8534 crossfade: "Shuffle or Manual Track Skip" 11830 es9218: "Linear Fast"
8535 </source> 11831 </source>
8536 <dest> 11832 <dest>
8537 *: none 11833 *: none
8538 crossfade: "ë¬´ìž‘ìœ & 트랙 스킵" 11834 es9218: "선형 빠름"
8539 </dest> 11835 </dest>
8540 <voice> 11836 <voice>
8541 *: none 11837 *: none
8542 crossfade: "ë¬´ìž‘ìœ & 트랙 스킵" 11838 es9218: "선형 빠름"
8543 </voice> 11839 </voice>
8544</phrase> 11840</phrase>
8545<phrase> 11841<phrase>
8546 id: LANG_RUNNING_TIME 11842 id: LANG_FILTER_LINEAR_SLOW
8547 desc: in run time screen 11843 desc: in sound settings
8548 user: core 11844 user: core
8549 <source> 11845 <source>
8550 *: "Running Time" 11846 *: none
11847 es9218: "Linear Slow"
8551 </source> 11848 </source>
8552 <dest> 11849 <dest>
8553 *: "ìž‘ë™ ì‹œê°„" 11850 *: none
11851 es9218: "선형 ëŠë¦¼"
8554 </dest> 11852 </dest>
8555 <voice> 11853 <voice>
8556 *: "ìž‘ë™ ì‹œê°„" 11854 *: none
11855 es9218: "선형 ëŠë¦¼"
8557 </voice> 11856 </voice>
8558</phrase> 11857</phrase>
8559<phrase> 11858<phrase>
8560 id: LANG_TOP_TIME 11859 id: LANG_FILTER_MINIMUM_FAST
8561 desc: in run time screen 11860 desc: in sound settings
8562 user: core 11861 user: core
8563 <source> 11862 <source>
8564 *: "Top Time" 11863 *: none
11864 es9218: "Minimum Fast"
8565 </source> 11865 </source>
8566 <dest> 11866 <dest>
8567 *: "ëˆ„ì  ì‹œê°„" 11867 *: none
11868 es9218: "최소한 빠름"
8568 </dest> 11869 </dest>
8569 <voice> 11870 <voice>
8570 *: "ëˆ„ì  ì‹œê°„" 11871 *: none
11872 es9218: "최소한 빠름"
8571 </voice> 11873 </voice>
8572</phrase> 11874</phrase>
8573<phrase> 11875<phrase>
8574 id: LANG_CLEAR_TIME 11876 id: LANG_FILTER_MINIMUM_SLOW
8575 desc: in run time screen 11877 desc: in sound settings
8576 user: core 11878 user: core
8577 <source> 11879 <source>
8578 *: "Clear Time?" 11880 *: none
11881 es9218: "Minimum Slow"
8579 </source> 11882 </source>
8580 <dest> 11883 <dest>
8581 *: "초기화 하시겠습니까?" 11884 *: none
11885 es9218: "최소한 ëŠë¦¼"
8582 </dest> 11886 </dest>
8583 <voice> 11887 <voice>
8584 *: "초기화 하시겠습니까?" 11888 *: none
11889 es9218: "최소한 ëŠë¦¼"
8585 </voice> 11890 </voice>
8586</phrase> 11891</phrase>
8587<phrase> 11892<phrase>
8588 id: LANG_REPLACE 11893 id: LANG_FILTER_APODIZING_1
8589 desc: in onplay menu. Replace the current playlist with a new one. 11894 desc: in sound settings
8590 user: core 11895 user: core
8591 <source> 11896 <source>
8592 *: "Play Next" 11897 *: none
11898 es9218: "Apodizing type 1"
8593 </source> 11899 </source>
8594 <dest> 11900 <dest>
8595 *: "다ìŒê³¡ìœ¼ë¡œ 재ìƒ" 11901 *: none
11902 es9218: "ì•„í¬ë‹¤ì´ì§• 유형 1"
8596 </dest> 11903 </dest>
8597 <voice> 11904 <voice>
8598 *: "다ìŒê³¡ìœ¼ë¡œ 재ìƒ" 11905 *: none
11906 es9218: "ì•„í¬ë‹¤ì´ì§• 유형 1"
8599 </voice> 11907 </voice>
8600</phrase> 11908</phrase>
8601<phrase> 11909<phrase>
8602 id: LANG_SAVE_THEME 11910 id: LANG_FILTER_APODIZING_2
8603 desc: save a theme file 11911 desc: in sound settings
8604 user: core 11912 user: core
8605 <source> 11913 <source>
8606 *: "Save Theme Settings" 11914 *: none
11915 es9218: "Apodizing type 2"
8607 </source> 11916 </source>
8608 <dest> 11917 <dest>
8609 *: "테마 설정 저장" 11918 *: none
11919 es9218: "ì•„í¬ë‹¤ì´ì§• 유형 2"
8610 </dest> 11920 </dest>
8611 <voice> 11921 <voice>
8612 *: "테마 설정 저장" 11922 *: none
11923 es9218: "ì•„í¬ë‹¤ì´ì§• 유형 2"
8613 </voice> 11924 </voice>
8614</phrase> 11925</phrase>
8615<phrase> 11926<phrase>
8616 id: LANG_USB_CHARGING 11927 id: LANG_FILTER_HYBRID_FAST
8617 desc: in Battery menu 11928 desc: in sound settings
8618 user: core 11929 user: core
8619 <source> 11930 <source>
8620 *: none 11931 *: none
8621 usb_charging_enable: "Charge During USB Connection" 11932 es9218: "Hybrid Fast"
8622 </source> 11933 </source>
8623 <dest> 11934 <dest>
8624 *: none 11935 *: none
8625 usb_charging_enable: "USB ì—°ê²° 중 충전" 11936 es9218: "ë³µí•©ì  ë¹ ë¦„"
8626 </dest> 11937 </dest>
8627 <voice> 11938 <voice>
8628 *: none 11939 *: none
8629 usb_charging_enable: "USB ì—°ê²° 중 충전" 11940 es9218: "ë³µí•©ì  ë¹ ë¦„"
8630 </voice> 11941 </voice>
8631</phrase> 11942</phrase>
8632<phrase> 11943<phrase>
8633 id: LANG_ID3_ALBUMARTIST 11944 id: LANG_FILTER_BRICK_WALL
8634 desc: in tag viewer 11945 desc: in sound settings
8635 user: core 11946 user: core
8636 <source> 11947 <source>
8637 *: "Album Artist" 11948 *: none
11949 es9218: "Brick Wall"
8638 </source> 11950 </source>
8639 <dest> 11951 <dest>
8640 *: "앨범 ìŒì•…ê°€" 11952 *: none
11953 es9218: "ë²½ëŒ"
8641 </dest> 11954 </dest>
8642 <voice> 11955 <voice>
8643 *: "앨범 ìŒì•…ê°€" 11956 *: none
11957 es9218: "ë²½ëŒ"
8644 </voice> 11958 </voice>
8645</phrase> 11959</phrase>
8646<phrase> 11960<phrase>
8647 id: LANG_ID3_COMMENT 11961 id: LANG_DAC_POWER_MODE
8648 desc: in tag viewer 11962 desc: in sound settings
8649 user: core 11963 user: core
8650 <source> 11964 <source>
8651 *: "Comment" 11965 *: none
11966 dac_power_mode: "DAC power mode"
11967 es9218: "DAC output level"
8652 </source> 11968 </source>
8653 <dest> 11969 <dest>
8654 *: "설명" 11970 *: none
11971 dac_power_mode: "DAC 파워 모드"
11972 es9218: "DAC 출력 레벨"
8655 </dest> 11973 </dest>
8656 <voice> 11974 <voice>
8657 *: "설명" 11975 *: none
11976 dac_power_mode: "DAC 파워 모드"
11977 es9218: "DAC 출력 레벨"
8658 </voice> 11978 </voice>
8659</phrase> 11979</phrase>
8660<phrase> 11980<phrase>
8661 id: LANG_CUESHEET_ENABLE 11981 id: LANG_DAC_POWER_HIGH
8662 desc: cuesheet support option 11982 desc: in sound settings
8663 user: core 11983 user: core
8664 <source> 11984 <source>
8665 *: "Cuesheet Support" 11985 *: none
11986 dac_power_mode: "High performance"
11987 es9218: "High Gain (2 Vrms)"
8666 </source> 11988 </source>
8667 <dest> 11989 <dest>
8668 *: "í 시트 지ì›" 11990 *: none
11991 dac_power_mode: "고성능"
11992 es9218: "ë†’ì€ ê²Œì¸ (2 Vrms)"
8669 </dest> 11993 </dest>
8670 <voice> 11994 <voice>
8671 *: "í 시트 지ì›" 11995 *: none
11996 dac_power_mode: "고성능"
11997 es9218: "ë†’ì€ ê²Œì¸ (2 Vrms)"
8672 </voice> 11998 </voice>
8673</phrase> 11999</phrase>
8674<phrase> 12000<phrase>
8675 id: LANG_FM_MENU 12001 id: LANG_DAC_POWER_LOW
8676 desc: fm menu title 12002 desc: in sound settings
8677 user: core 12003 user: core
8678 <source> 12004 <source>
8679 *: none 12005 *: none
8680 radio: "FM Radio Menu" 12006 dac_power_mode: "Save battery"
12007 es9218: "Low Gain (1 Vrms)"
8681 </source> 12008 </source>
8682 <dest> 12009 <dest>
8683 *: none 12010 *: none
8684 radio: "FM ë¼ë””오 메뉴" 12011 dac_power_mode: "배터리 저장"
12012 es9218: "ë‚®ì€ ê²Œì¸ (1 Vrms)"
8685 </dest> 12013 </dest>
8686 <voice> 12014 <voice>
8687 *: none 12015 *: none
8688 radio: "FM ë¼ë””오 메뉴" 12016 dac_power_mode: "배터리 저장"
12017 es9218: "ë‚®ì€ ê²Œì¸ (1 Vrms)"
8689 </voice> 12018 </voice>
8690</phrase> 12019</phrase>
8691<phrase> 12020<phrase>
8692 id: LANG_DIR_BROWSER 12021 id: LANG_VOLUME_LIMIT
8693 desc: main menu title 12022 desc: in sound_settings
8694 user: core 12023 user: core
8695 <source> 12024 <source>
8696 *: "Files" 12025 *: "Maximum Volume Limit"
8697 </source> 12026 </source>
8698 <dest> 12027 <dest>
8699 *: "íŒŒì¼ ë³´ê¸°" 12028 *: "최대 볼륨 제한"
8700 </dest> 12029 </dest>
8701 <voice> 12030 <voice>
8702 *: "íŒŒì¼ ë³´ê¸°" 12031 *: "최대 볼륨 제한"
8703 </voice> 12032 </voice>
8704</phrase> 12033</phrase>
8705<phrase> 12034<phrase>
8706 id: LANG_NOW_PLAYING 12035 id: LANG_SYSFONT_EQUALIZER_BAND_Q
8707 desc: in the main menu 12036 desc: in the equalizer settings menu
8708 user: core 12037 user: core
8709 <source> 12038 <source>
8710 *: "Now Playing" 12039 *: "Q"
8711 </source> 12040 </source>
8712 <dest> 12041 <dest>
8713 *: "지금 ìž¬ìƒ ì¤‘" 12042 *: "Q"
8714 </dest> 12043 </dest>
8715 <voice> 12044 <voice>
8716 *: "지금 ìž¬ìƒ ì¤‘" 12045 *: "Q"
8717 </voice> 12046 </voice>
8718</phrase> 12047</phrase>
8719<phrase> 12048<phrase>
8720 id: LANG_RESUME_PLAYBACK 12049 id: LANG_PBE
8721 desc: in the main menu 12050 desc: in sound settings
8722 user: core 12051 user: core
8723 <source> 12052 <source>
8724 *: "Resume Playback" 12053 *: "Perceptual Bass Enhancement"
8725 </source> 12054 </source>
8726 <dest> 12055 <dest>
8727 *: "ìž¬ìƒ ê³„ì†í•˜ê¸°" 12056 *: "ì§€ê° ì €ìŒ ê°•í™”"
8728 </dest> 12057 </dest>
8729 <voice> 12058 <voice>
8730 *: "ìž¬ìƒ ê³„ì†í•˜ê¸°" 12059 *: "ì§€ê° ì €ìŒ ê°•í™”"
8731 </voice> 12060 </voice>
8732</phrase> 12061</phrase>
8733<phrase> 12062<phrase>
8734 id: LANG_START_SCREEN 12063 id: LANG_AFR
8735 desc: in the system sub menu 12064 desc: in sound settings
8736 user: core 12065 user: core
8737 <source> 12066 <source>
8738 *: "Start Screen" 12067 *: "Auditory Fatigue Reduction"
8739 </source> 12068 </source>
8740 <dest> 12069 <dest>
8741 *: "시작í”ë©´ 설정" 12070 *: "ì²­ê° í”¼ë¡œ ê°ì†Œ"
8742 </dest> 12071 </dest>
8743 <voice> 12072 <voice>
8744 *: "시작í”ë©´ 설정" 12073 *: "ì²­ê° í”¼ë¡œ ê°ì†Œ"
8745 </voice> 12074 </voice>
8746</phrase> 12075</phrase>
8747<phrase> 12076<phrase>
8748 id: LANG_ROCKBOX_TITLE 12077 id: LANG_SURROUND
8749 desc: main menu title 12078 desc: in the sound settings menu
8750 user: core 12079 user: core
8751 <source> 12080 <source>
8752 *: "Rockbox" 12081 *: "Haas Surround"
8753 </source> 12082 </source>
8754 <dest> 12083 <dest>
8755 *: "Rockbox 메뉴" 12084 *: "하스 ì„œë¼ìš´ë“œ"
8756 </dest> 12085 </dest>
8757 <voice> 12086 <voice>
8758 *: "Rockbox 메뉴" 12087 *: "하스 ì„œë¼ìš´ë“œ"
8759 </voice> 12088 </voice>
8760</phrase> 12089</phrase>
8761<phrase> 12090<phrase>
8762 id: LANG_MAIN_MENU 12091 id: LANG_SURROUND_FX1
8763 desc: in start screen setting 12092 desc: in sound settings
8764 user: core 12093 user: core
8765 <source> 12094 <source>
8766 *: "Main Menu" 12095 *: "f(x1)"
8767 </source> 12096 </source>
8768 <dest> 12097 <dest>
8769 *: "ë©”ì¸ ë©”ë‰´" 12098 *: "f(x1)"
8770 </dest> 12099 </dest>
8771 <voice> 12100 <voice>
8772 *: "ë©”ì¸ ë©”ë‰´" 12101 *: "f(x1)"
8773 </voice> 12102 </voice>
8774</phrase> 12103</phrase>
8775<phrase> 12104<phrase>
8776 id: LANG_PREVIOUS_SCREEN 12105 id: LANG_SURROUND_FX2
8777 desc: in start screen setting 12106 desc: in sound settings
8778 user: core 12107 user: core
8779 <source> 12108 <source>
8780 *: "Previous Screen" 12109 *: "f(x2)"
8781 </source> 12110 </source>
8782 <dest> 12111 <dest>
8783 *: "ì´ì „ 화면" 12112 *: "f(x2)"
8784 </dest> 12113 </dest>
8785 <voice> 12114 <voice>
8786 *: "ì´ì „ 화면" 12115 *: "f(x2)"
8787 </voice> 12116 </voice>
8788</phrase> 12117</phrase>
8789<phrase> 12118<phrase>
8790 id: LANG_ALARM_WAKEUP_SCREEN 12119 id: LANG_SURROUND_METHOD2
8791 desc: in alarm menu setting 12120 desc: in sound settings
12121 user: core
12122 <source>
12123 *: "SIDE ONLY"
12124 </source>
12125 <dest>
12126 *: "측면만"
12127 </dest>
12128 <voice>
12129 *: "측면만"
12130 </voice>
12131</phrase>
12132<phrase>
12133 id: LANG_SURROUND_MIX
12134 desc: in sound settings
12135 user: core
12136 <source>
12137 *: "Dry / Wet Mix"
12138 </source>
12139 <dest>
12140 *: "ë“œë¼ì´ / ì›» 믹스"
12141 </dest>
12142 <voice>
12143 *: "ë“œë¼ì´ / ì›» 믹스"
12144 </voice>
12145</phrase>
12146<phrase>
12147 id: LANG_IBASSO_FREQ_SCALING_GOVERNOR
12148 desc: in Settings -> General -> System -> Freq Scaling Governor
8792 user: core 12149 user: core
8793 <source> 12150 <source>
8794 *: none 12151 *: none
8795 alarm: "Alarm Wake up Screen" 12152 ibassodx50,ibassodx90: "Freq Scaling Governor"
8796 </source> 12153 </source>
8797 <dest> 12154 <dest>
8798 *: none 12155 *: none
8799 alarm: "ìŒëžŒìœ¼ë¡œ 켜졌ìë•Œ 화면" 12156 ibassodx50,ibassodx90: "주파수 스케ì¼ë§ 거버너"
8800 </dest> 12157 </dest>
8801 <voice> 12158 <voice>
8802 *: none 12159 *: none
8803 alarm: "ìŒëžŒìœ¼ë¡œ 켜졌ìë•Œ 화면" 12160 ibassodx50,ibassodx90: "주파수 스케ì¼ë§ 거버너"
8804 </voice> 12161 </voice>
8805</phrase> 12162</phrase>
8806<phrase> 12163<phrase>
8807 id: LANG_BUILDING_DATABASE 12164 id: LANG_USB_MODE
8808 desc: splash database building progress 12165 desc: in Settings -> General -> System -> USB Mode
8809 user: core 12166 user: core
8810 <source> 12167 <source>
8811 *: "Building database... %d found (OFF to return)" 12168 *: "USB Mode"
8812 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
8813 gogearsa9200,ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "Building database... %d found (PREV to return)"
8814 iriverh100,iriverh120,iriverh300: "Building database... %d found (STOP to return)"
8815 </source> 12169 </source>
8816 <dest> 12170 <dest>
8817 *: "ìŒì•… DB ìƒì„± 중... %dê°œ 완료 ([꺼ì§]: ëŒì•„가기)" 12171 *: "USB 모드"
8818 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "ìŒì•… DB ìƒì„± 중... %dê°œ 완료 ([왼쪽]: ëŒì•„가기)"
8819 gogearsa9200,ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "ìŒì•… DB ìƒì„± 중... %dê°œ 완료 ([ì´ì „]: ëŒì•„가기)"
8820 iriverh100,iriverh120,iriverh300: "ìŒì•… DB ìƒì„± 중... %dê°œ 완료 ([정지]: ëŒì•„가기)"
8821 </dest> 12172 </dest>
8822 <voice> 12173 <voice>
8823 *: "ìŒì•… DBì˜ ìš”ì†Œë¥¼ ì°¾ìŒ" 12174 *: "USB 모드"
8824 </voice> 12175 </voice>
8825</phrase> 12176</phrase>
8826<phrase> 12177<phrase>
8827 id: LANG_ONPLAY_MENU_TITLE 12178 id: LANG_USB_MODE_MASS_STORAGE
8828 desc: title for the onplay menus 12179 desc: in Settings -> General -> System -> USB Mode: Mass Storage
8829 user: core 12180 user: core
8830 <source> 12181 <source>
8831 *: "Context Menu" 12182 *: "Mass Storage"
8832 </source> 12183 </source>
8833 <dest> 12184 <dest>
8834 *: "ìž¬ìƒ ë©ë‰´" 12185 *: "ëŒ€ìš©ëŸ ì €ìž¥ 장치"
8835 </dest> 12186 </dest>
8836 <voice> 12187 <voice>
8837 *: "ìž¬ìƒ ë©ë‰´" 12188 *: "ëŒ€ìš©ëŸ ì €ìž¥ 장치"
8838 </voice> 12189 </voice>
8839</phrase> 12190</phrase>
8840<phrase> 12191<phrase>
8841 id: LANG_BUTTONLIGHT_TIMEOUT 12192 id: LANG_USB_MODE_CHARGE
8842 desc: in settings_menu 12193 desc: in Settings -> General -> System -> USB Mode: Charge Only
8843 user: core 12194 user: core
8844 <source> 12195 <source>
8845 *: none 12196 *: "Charge Only"
8846 button_light: "Button Light Timeout"
8847 sansae200*,sansafuze*: "Wheel Light Timeout"
8848 </source> 12197 </source>
8849 <dest> 12198 <dest>
8850 *: none 12199 *: "충전만"
8851 button_light: "버튼 조명 시간"
8852 sansae200*,sansafuze*: "휠 조명 시간"
8853 </dest> 12200 </dest>
8854 <voice> 12201 <voice>
8855 *: none 12202 *: "충전만"
8856 button_light: "버튼 조명 시간"
8857 sansae200*,sansafuze*: "휠 조명 시간"
8858 </voice> 12203 </voice>
8859</phrase> 12204</phrase>
8860<phrase> 12205<phrase>
8861 id: LANG_BUTTONLIGHT_BRIGHTNESS 12206 id: LANG_USB_MODE_ADB
8862 desc: in settings_menu 12207 desc: in Settings -> General -> System -> USB Mode: Android Debug Bridge
8863 user: core 12208 user: core
8864 <source> 12209 <source>
8865 *: none 12210 *: "Android Debug Bridge"
8866 buttonlight_brightness: "Button Light Brightness"
8867 </source> 12211 </source>
8868 <dest> 12212 <dest>
8869 *: none 12213 *: "안드로ì´ë“œ 디버그 브리지"
8870 buttonlight_brightness: "버튼 조명 ë°ê¸°"
8871 </dest> 12214 </dest>
8872 <voice> 12215 <voice>
8873 *: none 12216 *: "안드로ì´ë“œ 디버그 브리지"
8874 buttonlight_brightness: "버튼 조명 ë°ê¸°"
8875 </voice> 12217 </voice>
8876</phrase> 12218</phrase>
8877<phrase> 12219<phrase>
8878 id: LANG_PLAYLISTVIEWER_SETTINGS 12220 id: LANG_ACTION_ENABLED
8879 desc: title for the playlist viewer settings menus 12221 desc: Selective Actions
8880 user: core 12222 user: core
8881 <source> 12223 <source>
8882 *: "Playlist Viewer Settings" 12224 *: "Enabled"
8883 </source> 12225 </source>
8884 <dest> 12226 <dest>
8885 *: "재ìƒëª©ë¡ 보기 ì¤ì •" 12227 *: "활성화"
8886 </dest> 12228 </dest>
8887 <voice> 12229 <voice>
8888 *: "재ìƒëª©ë¡ 보기 ì¤ì •" 12230 *: "활성화"
8889 </voice> 12231 </voice>
8890</phrase> 12232</phrase>
8891<phrase> 12233<phrase>
8892 id: LANG_BROWSE_CUESHEET 12234 id: LANG_ACTION_PLAY
8893 desc: 12235 desc: Selective Actions
8894 user: core 12236 user: core
8895 <source> 12237 <source>
8896 *: "Browse Cuesheet" 12238 *: "Exempt Play"
8897 </source> 12239 </source>
8898 <dest> 12240 <dest>
8899 *: "í 시트 보기" 12241 *: "예외 재ìƒ"
8900 </dest> 12242 </dest>
8901 <voice> 12243 <voice>
8902 *: "í 시트 보기" 12244 *: "ì—ì¸ ìž¬ìƒ"
8903 </voice> 12245 </voice>
8904</phrase> 12246</phrase>
8905<phrase> 12247<phrase>
8906 id: LANG_COPYING 12248 id: LANG_ACTION_SEEK
8907 desc: 12249 desc: Selective Actions
8908 user: core 12250 user: core
8909 <source> 12251 <source>
8910 *: "Copying..." 12252 *: "Exempt Seek"
8911 </source> 12253 </source>
8912 <dest> 12254 <dest>
8913 *: "복사하고 있습니다..." 12255 *: "예외 íƒìƒ‰"
8914 </dest> 12256 </dest>
8915 <voice> 12257 <voice>
8916 *: "복사 중입니다." 12258 *: "예외 íƒìƒ‰"
8917 </voice> 12259 </voice>
8918</phrase> 12260</phrase>
8919<phrase> 12261<phrase>
8920 id: LANG_DELETING 12262 id: LANG_ACTION_SKIP
8921 desc: 12263 desc: Selective Actions
8922 user: core 12264 user: core
8923 <source> 12265 <source>
8924 *: "Deleting..." 12266 *: "Exempt Skip"
8925 </source> 12267 </source>
8926 <dest> 12268 <dest>
8927 *: "삭제하고 있습ëˆë‹¤..." 12269 *: "예외 ê±´ëˆë›°ê¸°"
8928 </dest> 12270 </dest>
8929 <voice> 12271 <voice>
8930 *: "ì‚­ì œ 중입ëˆë‹¤." 12272 *: "예외 ê±´ëˆë›°ê¸°"
8931 </voice> 12273 </voice>
8932</phrase> 12274</phrase>
8933<phrase> 12275<phrase>
8934 id: LANG_MOVING 12276 id: LANG_BACKLIGHT_SELECTIVE
8935 desc: 12277 desc: Backlight behaviour setting
8936 user: core 12278 user: core
8937 <source> 12279 <source>
8938 *: "Moving..." 12280 *: "Backlight Exemptions"
8939 </source> 12281 </source>
8940 <dest> 12282 <dest>
8941 *: "ì´ë™í•˜ê³  있습니다..." 12283 *: "ë°±ë¼ì´íŠ¸ 예외"
8942 </dest> 12284 </dest>
8943 <voice> 12285 <voice>
8944 *: "ì´ë™ 중입니다." 12286 *: "ë°±ë¼ì´íŠ¸ 예외"
8945 </voice> 12287 </voice>
8946</phrase> 12288</phrase>
8947<phrase> 12289<phrase>
8948 id: LANG_VOICE_FILETYPE 12290 id: LANG_ACTION_DISABLE_EXT_POWER
8949 desc: voice settings menu 12291 desc: Backlight behaviour setting
8950 user: core 12292 user: core
8951 <source> 12293 <source>
8952 *: "Say File Type" 12294 *: "Disable on External Power"
8953 </source> 12295 </source>
8954 <dest> 12296 <dest>
8955 *: "ìŒì„± íŒŒì¼ ì¢…ë¥˜" 12297 *: "외부 ì „ì 비활성화"
8956 </dest> 12298 </dest>
8957 <voice> 12299 <voice>
8958 *: "ìŒì„± íŒŒì¼ ì¢…ë¥˜" 12300 *: "외부 ì „ì 비활성화"
8959 </voice> 12301 </voice>
8960</phrase> 12302</phrase>
8961<phrase> 12303<phrase>
8962 id: VOICE_PM_UNITS_PER_TICK 12304 id: LANG_ACTION_DISABLE_UNMAPPED
8963 desc: spoken only, peak meter release unit 12305 desc: Backlight behaviour setting
12306 user: core
12307 <source>
12308 *: "Disable Unmapped Keys"
12309 </source>
12310 <dest>
12311 *: "매핑ë˜ì§€ ì•Šì€ í‚¤ 비활성화"
12312 </dest>
12313 <voice>
12314 *: "매핑ë˜ì§€ ì•Šì€ í‚¤ 비활성화"
12315 </voice>
12316</phrase>
12317<phrase>
12318 id: LANG_SOFTLOCK_SELECTIVE
12319 desc: Softlock behaviour setting
12320 user: core
12321 <source>
12322 *: "Advanced Key Lock"
12323 </source>
12324 <dest>
12325 *: "고급 키 잠금"
12326 </dest>
12327 <voice>
12328 *: "고급 키 잠금"
12329 </voice>
12330</phrase>
12331<phrase>
12332 id: LANG_ACTION_AUTOLOCK_ON
12333 desc: Softlock behaviour setting
12334 user: core
12335 <source>
12336 *: "Autolock On"
12337 </source>
12338 <dest>
12339 *: "ìžë™ìž ê¸ˆ 켬"
12340 </dest>
12341 <voice>
12342 *: "ìžë™ìž ê¸ˆ 켬"
12343 </voice>
12344</phrase>
12345<phrase>
12346 id: LANG_ACTION_AUTOLOCK_OFF
12347 desc: Softlock behaviour setting
12348 user: core
12349 <source>
12350 *: "Autolock Off"
12351 </source>
12352 <dest>
12353 *: "ìžë™ìž ê¸ˆ ë”"
12354 </dest>
12355 <voice>
12356 *: "ìžë™ìž ê¸ˆ ë”"
12357 </voice>
12358</phrase>
12359<phrase>
12360 id: LANG_ACTION_DISABLE_NOTIFY
12361 desc: Softlock behaviour setting
12362 user: core
12363 <source>
12364 *: "Disable Locked Reminders"
12365 </source>
12366 <dest>
12367 *: "잠긴 알림 비활성화"
12368 </dest>
12369 <voice>
12370 *: "잠긴 알림 비활성화"
12371 </voice>
12372</phrase>
12373<phrase>
12374 id: LANG_ACTION_DISABLE_TOUCH
12375 desc: Softlock behaviour setting
12376 user: core
12377 <source>
12378 *: "Disable Touch"
12379 </source>
12380 <dest>
12381 *: "터치 비활성화"
12382 </dest>
12383 <voice>
12384 *: "터치 비활성화"
12385 </voice>
12386</phrase>
12387<phrase>
12388 id: LANG_KIBIBYTE
12389 desc: a unit postfix, also voiced
12390 user: core
12391 <source>
12392 *: "KiB"
12393 </source>
12394 <dest>
12395 *: "KiB"
12396 </dest>
12397 <voice>
12398 *: "키비바ì´íŠ¸"
12399 </voice>
12400</phrase>
12401<phrase>
12402 id: LANG_MEBIBYTE
12403 desc: a unit postfix, also voiced
12404 user: core
12405 <source>
12406 *: "MiB"
12407 </source>
12408 <dest>
12409 *: "MiB"
12410 </dest>
12411 <voice>
12412 *: "메비바ì´íŠ¸"
12413 </voice>
12414</phrase>
12415<phrase>
12416 id: LANG_GIBIBYTE
12417 desc: a unit postfix, also voiced
12418 user: core
12419 <source>
12420 *: "GiB"
12421 </source>
12422 <dest>
12423 *: "GiB"
12424 </dest>
12425 <voice>
12426 *: "기비바ì´íŠ¸"
12427 </voice>
12428</phrase>
12429<phrase>
12430 id: LANG_BOOKMARK_SETTINGS_ONE_PER_PLAYLIST
12431 desc: Save only one bookmark for a playlist in recent bookmarks
12432 user: core
12433 <source>
12434 *: "One per playlist"
12435 </source>
12436 <dest>
12437 *: "재ìƒëª©ë¡ë‹¹ 하나"
12438 </dest>
12439 <voice>
12440 *: "재ìƒëª©ë¡ë‹¹ 하나"
12441 </voice>
12442</phrase>
12443<phrase>
12444 id: LANG_BOOKMARK_SETTINGS_ONE_PER_TRACK
12445 desc: Save only one bookmark for a combination (playlist,track) in recent bookmarks
12446 user: core
12447 <source>
12448 *: "One per track"
12449 </source>
12450 <dest>
12451 *: "트랙당 하나"
12452 </dest>
12453 <voice>
12454 *: "트랙당 하나"
12455 </voice>
12456</phrase>
12457<phrase>
12458 id: VOICE_TRACK_TO_MOVE
12459 desc: playlist viewer
8964 user: core 12460 user: core
8965 <source> 12461 <source>
8966 *: "" 12462 *: ""
@@ -8969,648 +12465,1116 @@
8969 *: "" 12465 *: ""
8970 </dest> 12466 </dest>
8971 <voice> 12467 <voice>
12468 *: "ì´ë™í•  트랙"
12469 </voice>
12470</phrase>
12471<phrase>
12472 id: VOICE_QUEUED
12473 desc: playlist viewer
12474 user: core
12475 <source>
8972 *: "" 12476 *: ""
12477 </source>
12478 <dest>
12479 *: ""
12480 </dest>
12481 <voice>
12482 *: "대기함"
8973 </voice> 12483 </voice>
8974</phrase> 12484</phrase>
8975<phrase> 12485<phrase>
8976 id: LANG_NOT_PRESENT 12486 id: VOICE_BAD_TRACK
8977 desc: when external memory is not present 12487 desc: playlist viewer
12488 user: core
12489 <source>
12490 *: ""
12491 </source>
12492 <dest>
12493 *: ""
12494 </dest>
12495 <voice>
12496 *: "ë‚˜ìœ íŠ¸ëž™"
12497 </voice>
12498</phrase>
12499<phrase>
12500 id: VOICE_MOVING_TRACK
12501 desc: playlist viewer
12502 user: core
12503 <source>
12504 *: ""
12505 </source>
12506 <dest>
12507 *: ""
12508 </dest>
12509 <voice>
12510 *: "ì´ë™ 트랙"
12511 </voice>
12512</phrase>
12513<phrase>
12514 id: LANG_NEVER
12515 desc: in lcd settings
12516 user: core
12517 <source>
12518 *: "Never"
12519 </source>
12520 <dest>
12521 *: "절대 안 함"
12522 </dest>
12523 <voice>
12524 *: "절대 안 함"
12525 </voice>
12526</phrase>
12527<phrase>
12528 id: LANG_PLAYTIME_ELAPSED
12529 desc: playing time screen
12530 user: core
12531 <source>
12532 *: "Playlist elapsed:"
12533 </source>
12534 <dest>
12535 *: "재ìƒëª©ë¡ 경과ë¨:"
12536 </dest>
12537 <voice>
12538 *: "재ìƒëª©ë¡ 경과ë¨"
12539 </voice>
12540</phrase>
12541<phrase>
12542 id: LANG_PLAYTIME_TRK_ELAPSED
12543 desc: playing time screen
12544 user: core
12545 <source>
12546 *: "Track elapsed:"
12547 </source>
12548 <dest>
12549 *: "트랙 경과ë¨:"
12550 </dest>
12551 <voice>
12552 *: "트랙 경과ë¨"
12553 </voice>
12554</phrase>
12555<phrase>
12556 id: LANG_PLAYTIME_REMAINING
12557 desc: deprecated
12558 user: core
12559 <source>
12560 *: ""
12561 </source>
12562 <dest>
12563 *: ""
12564 </dest>
12565 <voice>
12566 *: ""
12567 </voice>
12568</phrase>
12569<phrase>
12570 id: LANG_PLAYTIME_TRK_REMAINING
12571 desc: playing time screen
12572 user: core
12573 <source>
12574 *: "Track remaining:"
12575 </source>
12576 <dest>
12577 *: "ë‚¨ì€ íŠ¸ëž™:"
12578 </dest>
12579 <voice>
12580 *: "ë‚¨ì€ íŠ¸ëž™"
12581 </voice>
12582</phrase>
12583<phrase>
12584 id: LANG_PLAYTIME_TRACK
12585 desc: playing time screen
12586 user: core
12587 <source>
12588 *: "Track:"
12589 </source>
12590 <dest>
12591 *: "트랙:"
12592 </dest>
12593 <voice>
12594 *: "트랙"
12595 </voice>
12596</phrase>
12597<phrase>
12598 id: LANG_PLAYTIME_STORAGE
12599 desc: playing time screen
12600 user: core
12601 <source>
12602 *: "Storage (Done / Remaining):"
12603 </source>
12604 <dest>
12605 *: "저장용량 (완료 / 남ìŒ):"
12606 </dest>
12607 <voice>
12608 *: "저장용량"
12609 </voice>
12610</phrase>
12611<phrase>
12612 id: VOICE_PLAYTIME_DONE
12613 desc: playing time screen
12614 user: core
12615 <source>
12616 *: ""
12617 </source>
12618 <dest>
12619 *: ""
12620 </dest>
12621 <voice>
12622 *: "완료"
12623 </voice>
12624</phrase>
12625<phrase>
12626 id: LANG_PLAYTIME_AVG_TRACK_SIZE
12627 desc: playing time screen
12628 user: core
12629 <source>
12630 *: "Average track size:"
12631 </source>
12632 <dest>
12633 *: "í‰ê·  트랙 í¬ê¸°:"
12634 </dest>
12635 <voice>
12636 *: "í‰ê·  트랙 í¬ê¸°"
12637 </voice>
12638</phrase>
12639<phrase>
12640 id: LANG_PLAYTIME_AVG_BITRATE
12641 desc: playing time screen
12642 user: core
12643 <source>
12644 *: "Average bitrate:"
12645 </source>
12646 <dest>
12647 *: "í‰ê·  비트전송률:"
12648 </dest>
12649 <voice>
12650 *: "í‰ê·  비트 전송률"
12651 </voice>
12652</phrase>
12653<phrase>
12654 id: LANG_PLAYTIME_ERROR
12655 desc: playing time screen
12656 user: core
12657 <source>
12658 *: "Error while gathering info"
12659 </source>
12660 <dest>
12661 *: "정보를 수집하는 중 오류 ë°œìƒ"
12662 </dest>
12663 <voice>
12664 *: "정보를 수집하는 중 오류 ë°œìƒ"
12665 </voice>
12666</phrase>
12667<phrase>
12668 id: LANG_PLAYING_TIME
12669 desc: onplay menu
12670 user: core
12671 <source>
12672 *: "Playing time"
12673 </source>
12674 <dest>
12675 *: "ìž¬ìƒ ì‹œê°„"
12676 </dest>
12677 <voice>
12678 *: "ìž¬ìƒ ì‹œê°„"
12679 </voice>
12680</phrase>
12681<phrase>
12682 id: LANG_CAR_ADAPTER_MODE_DELAY
12683 desc: Displayed for setting car adapter mode delay
8978 user: core 12684 user: core
8979 <source> 12685 <source>
8980 *: none 12686 *: none
8981 multivolume: "Not present" 12687 charging: "Delay Before Resume"
8982 </source> 12688 </source>
8983 <dest> 12689 <dest>
8984 *: none 12690 *: none
8985 multivolume: "ì—†ìŒ" 12691 charging: "재시작 ì „ 지연"
8986 </dest> 12692 </dest>
8987 <voice> 12693 <voice>
8988 *: none 12694 *: none
8989 multivolume: "ì—†ìŒ" 12695 charging: "재시작 ì „ 지연"
8990 </voice> 12696 </voice>
8991</phrase> 12697</phrase>
8992<phrase> 12698<phrase>
8993 id: LANG_BASS_CUTOFF 12699 id: VOICE_PITCH_ABSOLUTE_MODE
8994 desc: Bass setting cut-off frequency 12700 desc: spoken only
8995 user: core 12701 user: core
8996 <source> 12702 <source>
8997 *: none 12703 *: none
8998 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff" 12704 pitchscreen: ""
8999 </source> 12705 </source>
9000 <dest> 12706 <dest>
9001 *: none 12707 *: none
9002 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ì¤‘ì €ìŒ ì»·ì˜¤í”„ 주파수" 12708 pitchscreen: ""
9003 </dest> 12709 </dest>
9004 <voice> 12710 <voice>
9005 *: none 12711 *: none
9006 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ì¤‘ì €ìŒ ì»·ì˜¤í”„ 주파수" 12712 pitchscreen: "ì ˆëŒ ëª¨ë“œ"
9007 </voice> 12713 </voice>
9008</phrase> 12714</phrase>
9009<phrase> 12715<phrase>
9010 id: LANG_SELECTOR_START_COLOR 12716 id: VOICE_PITCH_SEMITONE_MODE
9011 desc: line selector color option 12717 desc: spoken only
9012 user: core 12718 user: core
9013 <source> 12719 <source>
9014 *: none 12720 *: none
9015 lcd_color: "Primary Colour" 12721 pitchscreen: ""
9016 </source> 12722 </source>
9017 <dest> 12723 <dest>
9018 *: none 12724 *: none
9019 lcd_color: "주 색깔" 12725 pitchscreen: ""
9020 </dest> 12726 </dest>
9021 <voice> 12727 <voice>
9022 *: none 12728 *: none
9023 lcd_color: "주 색깔" 12729 pitchscreen: "ë°˜ìŒ ëª¨ë“œ"
9024 </voice> 12730 </voice>
9025</phrase> 12731</phrase>
9026<phrase> 12732<phrase>
9027 id: LANG_SYSFONT_LINE_IN 12733 id: VOICE_PITCH_TIMESTRETCH_MODE
9028 desc: in the recording settings 12734 desc: spoken only
9029 user: core 12735 user: core
9030 <source> 12736 <source>
9031 *: none 12737 *: none
9032 recording: "Line In" 12738 pitchscreen: ""
9033 </source> 12739 </source>
9034 <dest> 12740 <dest>
9035 *: none 12741 *: none
9036 recording: "ë¼ì¸ ì¸" 12742 pitchscreen: ""
9037 </dest> 12743 </dest>
9038 <voice> 12744 <voice>
9039 *: none 12745 *: none
9040 recording: "ë¼ì¸ ì¸" 12746 pitchscreen: "타임 스í¬ë ˆì¹˜"
9041 </voice> 12747 </voice>
9042</phrase> 12748</phrase>
9043<phrase> 12749<phrase>
9044 id: LANG_SCANNING_DISK 12750 id: LANG_REMOTE_CONTROL
9045 desc: when booting up and rebuilding the cache and calculating free space 12751 desc: Item for menus
9046 user: core 12752 user: core
9047 <source> 12753 <source>
9048 *: "Scanning disk..." 12754 *: "Remote Control"
9049 </source> 12755 </source>
9050 <dest> 12756 <dest>
9051 *: "ë””ìŠ¤í¬ ê²€ìƒ‰ 중..." 12757 *: "리모컨"
9052 </dest> 12758 </dest>
9053 <voice> 12759 <voice>
9054 *: "ë””ìŠ¤í¬ ê²€ìƒ‰ 중..." 12760 *: "리모컨"
9055 </voice> 12761 </voice>
9056</phrase> 12762</phrase>
9057<phrase> 12763<phrase>
9058 id: VOICE_BLANK 12764 id: LANG_NO_REM_CONTROL
9059 desc: keyboard 12765 desc: Item for menus
9060 user: core 12766 user: core
9061 <source> 12767 <source>
9062 *: "" 12768 *: "No Rem. Control"
9063 </source> 12769 </source>
9064 <dest> 12770 <dest>
9065 *: "" 12771 *: "리모컨 ì—†ìŒ"
9066 </dest> 12772 </dest>
9067 <voice> 12773 <voice>
9068 *: "비어있ìŒ" 12774 *: "리모컨 ì—†ìŒ"
9069 </voice> 12775 </voice>
9070</phrase> 12776</phrase>
9071<phrase> 12777<phrase>
9072 id: LANG_PM_CLIPCOUNTER 12778 id: LANG_OUT_OF_CONTROL
9073 desc: in settings, for recording peak meter 12779 desc: Item for menus
9074 user: core 12780 user: core
9075 <source> 12781 <source>
9076 *: none 12782 *: "Out of Control"
9077 recording: "Clip Counter"
9078 </source> 12783 </source>
9079 <dest> 12784 <dest>
9080 *: none 12785 *: "제어 불능"
9081 recording: "í´ë¦½ ì¹´ìš´í„°"
9082 </dest> 12786 </dest>
9083 <voice> 12787 <voice>
9084 *: none 12788 *: "제어 불능"
9085 recording: "í´ë¦½ ì¹´ìš´í„°"
9086 </voice> 12789 </voice>
9087</phrase> 12790</phrase>
9088<phrase> 12791<phrase>
9089 id: LANG_GAIN 12792 id: LANG_2_KEY_CONTROL
9090 desc: Generic string for gain used in EQ menu and recording screen 12793 desc: Item for menus
9091 user: core 12794 user: core
9092 <source> 12795 <source>
9093 *: "Gain" 12796 *: "2 Key Control"
9094 </source> 12797 </source>
9095 <dest> 12798 <dest>
9096 *: "게ì¸" 12799 *: "2 키 제어"
9097 </dest> 12800 </dest>
9098 <voice> 12801 <voice>
9099 *: "게ì¸" 12802 *: "2 키 제어"
9100 </voice> 12803 </voice>
9101</phrase> 12804</phrase>
9102<phrase> 12805<phrase>
9103 id: LANG_CHANNEL_LEFTRIGHT 12806 id: LANG_4_KEY_CONTROL
9104 desc: in sound_settings 12807 desc: Item for menus
9105 user: core 12808 user: core
9106 <source> 12809 <source>
9107 *: none 12810 *: "4 Key Control"
9108 recording: "Mono Left + Right"
9109 </source> 12811 </source>
9110 <dest> 12812 <dest>
9111 *: none 12813 *: "4 키 제어"
9112 recording: "모노 (좌 + 우)"
9113 </dest> 12814 </dest>
9114 <voice> 12815 <voice>
9115 *: none 12816 *: "4 키 제어"
9116 recording: "좌 우 합한 모노"
9117 </voice> 12817 </voice>
9118</phrase> 12818</phrase>
9119<phrase> 12819<phrase>
9120 id: LANG_ID3_NO_INFO 12820 id: LANG_PLAY_WORMLET
9121 desc: in tag viewer 12821 desc: For wormlet menu
9122 user: core 12822 user: core
9123 <source> 12823 <source>
9124 *: "<No Info>" 12824 *: "Play Wormlet!"
9125 </source> 12825 </source>
9126 <dest> 12826 <dest>
9127 *: "<ì •ë³´ ì—†ìŒ>" 12827 *: "Wormletì„ í”Œë ˆì´í´ë³´ì„¸ìš”!"
9128 </dest> 12828 </dest>
9129 <voice> 12829 <voice>
9130 *: "ì •ë³´ ì—†ìŒ" 12830 *: "Wormletì„ í”Œë ˆì´í´ë³´ì„¸ìš”!"
9131 </voice> 12831 </voice>
9132</phrase> 12832</phrase>
9133<phrase> 12833<phrase>
9134 id: LANG_TOUCHPAD_SENSITIVITY 12834 id: LANG_NUMBER_OF_WORMS
9135 desc: touchpad sensitivity setting 12835 desc: For wormlet menu
9136 user: core 12836 user: core
9137 <source> 12837 <source>
9138 *: none 12838 *: "Number of Worms"
9139 fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
9140 </source> 12839 </source>
9141 <dest> 12840 <dest>
9142 *: none 12841 *: "벌레 수"
9143 fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
9144 </dest> 12842 </dest>
9145 <voice> 12843 <voice>
9146 *: none 12844 *: "벌레 수"
9147 fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
9148 </voice> 12845 </voice>
9149</phrase> 12846</phrase>
9150<phrase> 12847<phrase>
9151 id: LANG_HEADPHONE_UNPLUG_DISABLE_AUTORESUME 12848 id: LANG_WORM_GROWTH_PER_FOOD
9152 desc: in pause_phones_menu. 12849 desc: For wormlet menu
9153 user: core 12850 user: core
9154 <source> 12851 <source>
9155 *: none 12852 *: "Worm Growth Per Food"
9156 headphone_detection: "Disable resume on startup if phones unplugged"
9157 </source> 12853 </source>
9158 <dest> 12854 <dest>
9159 *: none 12855 *: "먹ì´ë³„ 벌레 성장"
9160 headphone_detection: "ì´ì–´í°/í—¤ë“œí° ì—°ê²°ì´ ë˜ì–´ìžˆì§€ì•Šìœ¼ë©´ 시작할때 ì´ì–´ì„œ ìžë™ìž¬ìƒí•˜ì§€ ì•ŠìŒ"
9161 </dest> 12856 </dest>
9162 <voice> 12857 <voice>
9163 *: none 12858 *: "먹ì´ë³„ 벌레 성장"
9164 headphone_detection: "ì´ì–´í°/í—¤ë“œí° ì—°ê²°ì´ ë˜ì–´ìžˆì§€ì•Šìœ¼ë©´ 시작할때 ì´ì–´ì„œ ìžë™ìž¬ìƒí•˜ì§€ ì•ŠìŒ"
9165 </voice> 12859 </voice>
9166</phrase> 12860</phrase>
9167<phrase> 12861<phrase>
9168 id: LANG_BOOKMARK_CONTEXT_RESUME 12862 id: LANG_WORM_SPEED
9169 desc: bookmark context menu, resume this bookmark 12863 desc: For wormlet menu
9170 user: core 12864 user: core
9171 <source> 12865 <source>
9172 *: "Resume" 12866 *: "Worm Speed"
9173 </source> 12867 </source>
9174 <dest> 12868 <dest>
9175 *: "ì´ì–´ì„œ 재ìƒ" 12869 *: "벌레 ì†ë„"
9176 </dest> 12870 </dest>
9177 <voice> 12871 <voice>
9178 *: "ì´ì–´ì„œ 재ìƒ" 12872 *: "벌레 ì†ë„"
9179 </voice> 12873 </voice>
9180</phrase> 12874</phrase>
9181<phrase> 12875<phrase>
9182 id: LANG_TALK_BATTERY_LEVEL 12876 id: LANG_ARGHS_PER_FOOD
9183 desc: Setting for spontaneous battery level announcement 12877 desc: For wormlet menu
9184 user: core 12878 user: core
9185 <source> 12879 <source>
9186 *: "Announce Battery Level" 12880 *: "Arghs Per Food"
9187 </source> 12881 </source>
9188 <dest> 12882 <dest>
9189 *: "배터리 레밸 표시" 12883 *: "먹ì´ë³„ 외마디 비명"
9190 </dest> 12884 </dest>
9191 <voice> 12885 <voice>
9192 *: "배터리 레밸 표시" 12886 *: "먹ì´ë³„ 외마디 비명"
9193 </voice> 12887 </voice>
9194</phrase> 12888</phrase>
9195<phrase> 12889<phrase>
9196 id: LANG_SELECTOR_COLOR_MENU 12890 id: LANG_ARGH_SIZE
9197 desc: line selector color menu title 12891 desc: For wormlet menu
9198 user: core 12892 user: core
9199 <source> 12893 <source>
9200 *: none 12894 *: "Argh Size"
9201 lcd_color: "Line Selector Colours"
9202 </source> 12895 </source>
9203 <dest> 12896 <dest>
9204 *: none 12897 *: "외마디 비명 í¬ê¸°"
9205 lcd_color: "ë¼ì¸ ì„ íƒê¸° 색깔"
9206 </dest> 12898 </dest>
9207 <voice> 12899 <voice>
9208 *: none 12900 *: "외마디 비명 í¬ê¸°"
9209 lcd_color: "ë¼ì¸ ì„ íƒê¸° 색깔"
9210 </voice> 12901 </voice>
9211</phrase> 12902</phrase>
9212<phrase> 12903<phrase>
9213 id: LANG_SET_TIME 12904 id: LANG_FOOD_SIZE
9214 desc: in settings_menu 12905 desc: For wormlet menu
9215 user: core 12906 user: core
9216 <source> 12907 <source>
9217 *: none 12908 *: "Food Size"
9218 rtc: "Set Time/Date"
9219 </source> 12909 </source>
9220 <dest> 12910 <dest>
9221 *: none 12911 *: "ë¨¹ì´ í¬ê¸°"
9222 rtc: "시간/날짜 설정"
9223 </dest> 12912 </dest>
9224 <voice> 12913 <voice>
9225 *: none 12914 *: "ë¨¹ì´ í¬ê¸°"
9226 rtc: "시간과 날짜 설정"
9227 </voice> 12915 </voice>
9228</phrase> 12916</phrase>
9229<phrase> 12917<phrase>
9230 id: LANG_FAST 12918 id: LANG_NUMBER_OF_PLAYERS
9231 desc: in settings_menu 12919 desc: For game menus
9232 user: core 12920 user: core
9233 <source> 12921 <source>
9234 *: "Fast" 12922 *: "Number of Players"
9235 </source> 12923 </source>
9236 <dest> 12924 <dest>
9237 *: "빠름" 12925 *: "플레ì´ì–´ 수"
9238 </dest> 12926 </dest>
9239 <voice> 12927 <voice>
9240 *: "빠름" 12928 *: "플레ì´ì–´ 수"
9241 </voice> 12929 </voice>
9242</phrase> 12930</phrase>
9243<phrase> 12931<phrase>
9244 id: VOICE_EMPTY_LIST 12932 id: LANG_CONTROL_STYLE
9245 desc: spoken only, when a list dialog contains no elements 12933 desc: In various menus
9246 user: core 12934 user: core
9247 <source> 12935 <source>
9248 *: "" 12936 *: "Control Style"
9249 </source> 12937 </source>
9250 <dest> 12938 <dest>
9251 *: "" 12939 *: "제어 스타ì¼"
9252 </dest> 12940 </dest>
9253 <voice> 12941 <voice>
9254 *: "빈 목ë¡" 12942 *: "제어 스타ì¼"
9255 </voice> 12943 </voice>
9256</phrase> 12944</phrase>
9257<phrase> 12945<phrase>
9258 id: LANG_LISTACCEL_ACCEL_SPEED 12946 id: LANG_REVERT_TO_DEFAULT_SETTINGS
9259 desc: list acceleration speed 12947 desc: In various menus
9260 user: core 12948 user: core
9261 <source> 12949 <source>
9262 *: "List Acceleration Speed" 12950 *: "Revert to Default Settings"
9263 wheel_acceleration: none
9264 </source> 12951 </source>
9265 <dest> 12952 <dest>
9266 *: "ëª©ë¡ ê°€ì† ì†ë„" 12953 *: "기본 설정으로 ë˜ëŒë¦¬ê¸°"
9267 wheel_acceleration: none
9268 </dest> 12954 </dest>
9269 <voice> 12955 <voice>
9270 *: "ëª©ë¡ ê°€ì† ì†ë„" 12956 *: "기본 설정으로 ë˜ëŒë¦¬ê¸°"
9271 wheel_acceleration: none
9272 </voice> 12957 </voice>
9273</phrase> 12958</phrase>
9274<phrase> 12959<phrase>
9275 id: LANG_BOOKMARK_CONTEXT_MENU 12960 id: LANG_MENU_QUIT
9276 desc: bookmark selection list context menu 12961 desc: in various menus
9277 user: core 12962 user: core
9278 <source> 12963 <source>
9279 *: "Bookmark Actions" 12964 *: "Quit"
9280 </source> 12965 </source>
9281 <dest> 12966 <dest>
9282 *: "ë¶ë§ˆí¬ í–‰ë™" 12967 *: "종료"
9283 </dest> 12968 </dest>
9284 <voice> 12969 <voice>
9285 *: "ë¶ë§ˆí¬ í–‰ë™" 12970 *: "종료"
9286 </voice> 12971 </voice>
9287</phrase> 12972</phrase>
9288<phrase> 12973<phrase>
9289 id: LANG_NORMAL 12974 id: LANG_MENU_DISPLAY_OPTIONS
9290 desc: in settings_menu 12975 desc: in various menus
9291 user: core 12976 user: core
9292 <source> 12977 <source>
9293 *: "Normal" 12978 *: "Display Options"
9294 </source> 12979 </source>
9295 <dest> 12980 <dest>
9296 *: "ë…¸ë©" 12981 *: "화면 옵션"
9297 </dest> 12982 </dest>
9298 <voice> 12983 <voice>
9299 *: "ë…¸ë©" 12984 *: "화면 옵션"
9300 </voice> 12985 </voice>
9301</phrase> 12986</phrase>
9302<phrase> 12987<phrase>
9303 id: VOICE_AM 12988 id: LANG_PREVTRACK
9304 desc: spoken only, for wall clock announce 12989 desc: in playback control menu
9305 user: core 12990 user: core
9306 <source> 12991 <source>
9307 *: "" 12992 *: "Previous Track"
9308 </source> 12993 </source>
9309 <dest> 12994 <dest>
9310 *: "" 12995 *: "ì´ì „ 트랙"
9311 </dest> 12996 </dest>
9312 <voice> 12997 <voice>
9313 *: "A M" 12998 *: "ì´ì „ 트랙"
9314 </voice> 12999 </voice>
9315</phrase> 13000</phrase>
9316<phrase> 13001<phrase>
9317 id: LANG_BUFFER_STAT 13002 id: LANG_PLAYPAUSE
9318 desc: the buffer size, %d MB %d fraction of MB 13003 desc: in playback control menu
9319 user: core 13004 user: core
9320 <source> 13005 <source>
9321 *: "Buffer:" 13006 *: "Pause / Play"
9322 archosplayer: "Buf:"
9323 </source> 13007 </source>
9324 <dest> 13008 <dest>
9325 *: "버í¼:" 13009 *: "ì¼ì‹œì •ì§€ / 재ìƒ"
9326 archosplayer: "버í¼:"
9327 </dest> 13010 </dest>
9328 <voice> 13011 <voice>
9329 *: "ë²„í¼ í¬ê¸°" 13012 *: "ì¼ì‹œì •ì§€ / 재ìƒ"
9330 </voice> 13013 </voice>
9331</phrase> 13014</phrase>
9332<phrase> 13015<phrase>
9333 id: LANG_LEFT_QS_ITEM 13016 id: LANG_STOP_PLAYBACK
9334 desc: used for the submenu name for the quickscreen items 13017 desc: in playback control menu
9335 user: core 13018 user: core
9336 <source> 13019 <source>
9337 *: none 13020 *: "Stop Playback"
9338 quickscreen: "Set as Left Quickscreen Item"
9339 </source> 13021 </source>
9340 <dest> 13022 <dest>
9341 *: none 13023 *: "ìž¬ìƒ ì¤‘ì§€"
9342 quickscreen: "왼쪽 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 지정"
9343 </dest> 13024 </dest>
9344 <voice> 13025 <voice>
9345 *: none 13026 *: "ìž¬ìƒ ì¤‘ì§€"
9346 quickscreen: "왼쪽 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 지정"
9347 </voice> 13027 </voice>
9348</phrase> 13028</phrase>
9349<phrase> 13029<phrase>
9350 id: LANG_RESET_SETTING 13030 id: LANG_NEXTTRACK
9351 desc: used in the settings context menu 13031 desc: in playback control menu
9352 user: core 13032 user: core
9353 <source> 13033 <source>
9354 *: "Reset Setting" 13034 *: "Next Track"
9355 </source> 13035 </source>
9356 <dest> 13036 <dest>
9357 *: "ì„¸íŒ ì´ˆê¸°í™" 13037 *: "ë‹¤ìŒ íŠ¸ëž™"
9358 </dest> 13038 </dest>
9359 <voice> 13039 <voice>
9360 *: "ì„¸íŒ ì´ˆê¸°í™" 13040 *: "ë‹¤ìŒ íŠ¸ëž™"
9361 </voice> 13041 </voice>
9362</phrase> 13042</phrase>
9363<phrase> 13043<phrase>
9364 id: VOICE_CHAR_SLASH 13044 id: LANG_CHANGE_VOLUME
9365 desc: spoken only, for spelling 13045 desc: in playback control menu
9366 user: core 13046 user: core
9367 <source> 13047 <source>
9368 *: "" 13048 *: "Change Volume"
9369 </source> 13049 </source>
9370 <dest> 13050 <dest>
9371 *: "" 13051 *: "볼륨 변경"
9372 </dest> 13052 </dest>
9373 <voice> 13053 <voice>
9374 *: "slash" 13054 *: "볼륨 변경"
9375 </voice> 13055 </voice>
9376</phrase> 13056</phrase>
9377<phrase> 13057<phrase>
9378 id: LANG_HEADPHONE_UNPLUG_RESUME 13058 id: LANG_CHANGE_SHUFFLE_MODE
9379 desc: in pause_phones_menu. 13059 desc: in playback control menu
9380 user: core 13060 user: core
9381 <source> 13061 <source>
9382 *: none 13062 *: "Shuffle Mode"
9383 headphone_detection: "Pause and Resume"
9384 </source> 13063 </source>
9385 <dest> 13064 <dest>
9386 *: none 13065 *: "셔플 모드"
9387 headphone_detection: "정지 / ì´ì–´ 재ìƒ"
9388 </dest> 13066 </dest>
9389 <voice> 13067 <voice>
9390 *: none 13068 *: "셔플 모드"
9391 headphone_detection: "정지와 ì´ì–´ìž¬ìƒ"
9392 </voice> 13069 </voice>
9393</phrase> 13070</phrase>
9394<phrase> 13071<phrase>
9395 id: LANG_REC_DIR_NOT_WRITABLE 13072 id: LANG_CHANGE_REPEAT_MODE
9396 desc: 13073 desc: in playback control menu
9397 user: core 13074 user: core
9398 <source> 13075 <source>
9399 *: none 13076 *: "Change Repeat Mode"
9400 recording: "Can't write to recording directory"
9401 </source> 13077 </source>
9402 <dest> 13078 <dest>
9403 *: none 13079 *: "반복 모드 변경"
9404 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ì— ê¸°ë¡í•  수 없습니다."
9405 </dest> 13080 </dest>
9406 <voice> 13081 <voice>
9407 *: none 13082 *: "반복 모드 변경"
9408 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ì— ê¸°ë¡í•  수 없습니다."
9409 </voice> 13083 </voice>
9410</phrase> 13084</phrase>
9411<phrase> 13085<phrase>
9412 id: LANG_CHANNEL_CONFIGURATION 13086 id: LANG_PLAYBACK_CONTROL
9413 desc: in sound_settings 13087 desc: in playback control menu
9414 user: core 13088 user: core
9415 <source> 13089 <source>
9416 *: "Channel Configuration" 13090 *: "Playback Control"
9417 </source> 13091 </source>
9418 <dest> 13092 <dest>
9419 *: "ì±„ë„ ì„¤ì " 13093 *: "ìž¬ìƒ ì œì–´"
9420 </dest> 13094 </dest>
9421 <voice> 13095 <voice>
9422 *: "ì±„ë„ ì„¤ì " 13096 *: "ìž¬ìƒ ì œì–´"
9423 </voice> 13097 </voice>
9424</phrase> 13098</phrase>
9425<phrase> 13099<phrase>
9426 id: LANG_CONFIRM_WITH_BUTTON 13100 id: LANG_CHESSBOX_CHECKMATE
9427 desc: Generic string to use to confirm 13101 desc: in chessbox
9428 user: core 13102 user: core
9429 <source> 13103 <source>
9430 *: "PLAY = Yes" 13104 *: "Checkmate!"
9431 archosplayer: "(PLAY/STOP)" 13105 </source>
9432 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "SELECT = Yes" 13106 <dest>
9433 iriverh100,iriverh120,iriverh300: "NAVI = Yes" 13107 *: "ì²´í¬ë©”ì´íŠ¸!"
9434 vibe500: "OK = Yes" 13108 </dest>
13109 <voice>
13110 *: "ì²´í¬ë©”ì´íŠ¸!"
13111 </voice>
13112</phrase>
13113<phrase>
13114 id: LANG_CHESSBOX_ILLEGAL_MOVE
13115 desc: in chessbox
13116 user: core
13117 <source>
13118 *: "Illegal move!"
13119 </source>
13120 <dest>
13121 *: "불법 ì´ë™ìž…니다!"
13122 </dest>
13123 <voice>
13124 *: "불법 ì´ë™ìž…니다!"
13125 </voice>
13126</phrase>
13127<phrase>
13128 id: LANG_CHESSBOX_MENU_NEW_GAME
13129 desc: in the chessbox menu
13130 user: core
13131 <source>
13132 *: "New Game"
13133 </source>
13134 <dest>
13135 *: "새 게임"
13136 </dest>
13137 <voice>
13138 *: "새 게임"
13139 </voice>
13140</phrase>
13141<phrase>
13142 id: LANG_CHESSBOX_MENU_RESUME_GAME
13143 desc: in the chessbox menu
13144 user: core
13145 <source>
13146 *: "Resume Game"
13147 </source>
13148 <dest>
13149 *: "게임 재시작"
13150 </dest>
13151 <voice>
13152 *: "게임 재시작"
13153 </voice>
13154</phrase>
13155<phrase>
13156 id: LANG_CHESSBOX_MENU_SAVE_GAME
13157 desc: in the chessbox menu
13158 user: core
13159 <source>
13160 *: "Save Game"
13161 </source>
13162 <dest>
13163 *: "게임 저장"
13164 </dest>
13165 <voice>
13166 *: "게임 저장"
13167 </voice>
13168</phrase>
13169<phrase>
13170 id: LANG_CHESSBOX_MENU_RESTORE_GAME
13171 desc: in the chessbox menu
13172 user: core
13173 <source>
13174 *: "Restore Game"
9435 </source> 13175 </source>
9436 <dest> 13176 <dest>
9437 *: "[재ìƒ] = 예" 13177 *: "게임 ë³µì›"
9438 archosplayer: "(재ìƒ/정지)"
9439 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "[ì„ íƒ] = 예"
9440 iriverh100,iriverh120,iriverh300: "[네비] = 예"
9441 vibe500: "OK = 예"
9442 </dest> 13178 </dest>
9443 <voice> 13179 <voice>
13180 *: "게임 ë³µì›"
13181 </voice>
13182</phrase>
13183<phrase>
13184 id: LANG_CHESSBOX_MENU_RESTART_GAME
13185 desc: in the chessbox menu
13186 user: core
13187 <source>
13188 *: "Restart Game"
13189 </source>
13190 <dest>
13191 *: "게임 재시작"
13192 </dest>
13193 <voice>
13194 *: "게임 재시작"
13195 </voice>
13196</phrase>
13197<phrase>
13198 id: LANG_CHESSBOX_MENU_SELECT_OTHER_GAME
13199 desc: in the chessbox menu
13200 user: core
13201 <source>
13202 *: "Select Other Game"
13203 </source>
13204 <dest>
13205 *: "다른 게임 ì„ íƒ"
13206 </dest>
13207 <voice>
13208 *: "다른 게임 ì„ íƒ"
13209 </voice>
13210</phrase>
13211<phrase>
13212 id: LANG_CHESSBOX_LEVEL_1
13213 desc: in the chessbox game level selection
13214 user: core
13215 <source>
13216 *: "Level 1: 60 moves / 5 min"
13217 </source>
13218 <dest>
13219 *: "레벨 1: 60 ì´ë™ / 5 분"
13220 </dest>
13221 <voice>
13222 *: "레벨 1, 5 분당 60 ì´ë™"
13223 </voice>
13224</phrase>
13225<phrase>
13226 id: LANG_CHESSBOX_LEVEL_2
13227 desc: in the chessbox game level selection
13228 user: core
13229 <source>
13230 *: "Level 2: 60 moves / 15 min"
13231 </source>
13232 <dest>
13233 *: "레벨 2: 60 ì´ë™ / 15 분"
13234 </dest>
13235 <voice>
13236 *: "레벨 2, 15 분당 60 ì´ë™"
13237 </voice>
13238</phrase>
13239<phrase>
13240 id: LANG_CHESSBOX_LEVEL_3
13241 desc: in the chessbox game level selection
13242 user: core
13243 <source>
13244 *: "Level 3: 60 moves / 30 min"
13245 </source>
13246 <dest>
13247 *: "레벨 3: 60 ì´ë™ / 30 분"
13248 </dest>
13249 <voice>
13250 *: "레벨 3, 30 분당 60 ì´ë™"
13251 </voice>
13252</phrase>
13253<phrase>
13254 id: LANG_CHESSBOX_LEVEL_4
13255 desc: in the chessbox game level selection
13256 user: core
13257 <source>
13258 *: "Level 4: 40 moves / 30 min"
13259 </source>
13260 <dest>
13261 *: "레벨 4: 40 ì´ë™ / 30 분"
13262 </dest>
13263 <voice>
13264 *: "레벨 4, 30 분당 40 ì´ë™"
13265 </voice>
13266</phrase>
13267<phrase>
13268 id: LANG_CHESSBOX_LEVEL_5
13269 desc: in the chessbox game level selection
13270 user: core
13271 <source>
13272 *: "Level 5: 40 moves / 60 min"
13273 </source>
13274 <dest>
13275 *: "레벨 5: 40 ì´ë™ / 60 분"
13276 </dest>
13277 <voice>
13278 *: "레벨 5, 60 분당 40 ì´ë™"
13279 </voice>
13280</phrase>
13281<phrase>
13282 id: LANG_CHESSBOX_LEVEL_6
13283 desc: in the chessbox game level selection
13284 user: core
13285 <source>
13286 *: "Level 6: 40 moves / 120 min"
13287 </source>
13288 <dest>
13289 *: "레벨 6: 40 ì´ë™ / 120 분"
13290 </dest>
13291 <voice>
13292 *: "레벨 6, 120 분당 40 ì´ë™"
13293 </voice>
13294</phrase>
13295<phrase>
13296 id: LANG_CHESSBOX_LEVEL_7
13297 desc: in the chessbox game level selection
13298 user: core
13299 <source>
13300 *: "Level 7: 40 moves / 240 min"
13301 </source>
13302 <dest>
13303 *: "레벨 7: 40 ì´ë™ / 240 분"
13304 </dest>
13305 <voice>
13306 *: "레벨 7, 240 분당 40 ì´ë™"
13307 </voice>
13308</phrase>
13309<phrase>
13310 id: LANG_CHESSBOX_LEVEL_8
13311 desc: in the chessbox game level selection
13312 user: core
13313 <source>
13314 *: "Level 8: 1 move / 15 min"
13315 </source>
13316 <dest>
13317 *: "레벨 8: 1 ì´ë™ / 15 분"
13318 </dest>
13319 <voice>
13320 *: "레벨 8, 15 분당 1 ì´ë™"
13321 </voice>
13322</phrase>
13323<phrase>
13324 id: LANG_CHESSBOX_LEVEL_9
13325 desc: in the chessbox game level selection
13326 user: core
13327 <source>
13328 *: "Level 9: 1 move / 60 min"
13329 </source>
13330 <dest>
13331 *: "레벨 9: 1 ì´ë™ / 60 분"
13332 </dest>
13333 <voice>
13334 *: "레벨 9, 60 분당 1 ì´ë™"
13335 </voice>
13336</phrase>
13337<phrase>
13338 id: LANG_CHESSBOX_LEVEL_10
13339 desc: in the chessbox game level selection
13340 user: core
13341 <source>
13342 *: "Level 10: 1 move / 600 min"
13343 </source>
13344 <dest>
13345 *: "레벨 10: 1 ì´ë™ / 600 분"
13346 </dest>
13347 <voice>
13348 *: "레벨 10, 600 분당 1 ì´ë™"
13349 </voice>
13350</phrase>
13351<phrase>
13352 id: LANG_CHESSBOX_PGN_PARSE_ERROR
13353 desc: in the chessbox game viewer
13354 user: core
13355 <source>
13356 *: "Error parsing game !"
13357 </source>
13358 <dest>
13359 *: "게임 분ì„하는 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤!"
13360 </dest>
13361 <voice>
13362 *: "게임 분ì„하는 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤!"
13363 </voice>
13364</phrase>
13365<phrase>
13366 id: LANG_CHESSBOX_NO_GAMES
13367 desc: in the chessbox game viewer
13368 user: core
13369 <source>
13370 *: "No games found !"
13371 </source>
13372 <dest>
13373 *: "ê²Œìž„ì„ ì°¾ì„ ìˆ˜ 없습니다!"
13374 </dest>
13375 <voice>
13376 *: "ê²Œìž„ì„ ì°¾ì„ ìˆ˜ 없습니다!"
13377 </voice>
13378</phrase>
13379<phrase>
13380 id: LANG_CHESSBOX_GAME_BEGINNING
13381 desc: in the chessbox game viewer
13382 user: core
13383 <source>
13384 *: "At the beginning of the game"
13385 </source>
13386 <dest>
13387 *: "게임 시작시"
13388 </dest>
13389 <voice>
13390 *: "게임 시작시"
13391 </voice>
13392</phrase>
13393<phrase>
13394 id: LANG_CHESSBOX_GAME_END
13395 desc: in the chessbox game viewer
13396 user: core
13397 <source>
13398 *: "At the end of the game"
13399 </source>
13400 <dest>
13401 *: "ê²Œìž„ì´ ë나면"
13402 </dest>
13403 <voice>
13404 *: "ê²Œìž„ì´ ë나면"
13405 </voice>
13406</phrase>
13407<phrase>
13408 id: VOICE_PLAYER
13409 desc: spoken only, for announcing player's id
13410 user: core
13411 <source>
13412 *: ""
13413 </source>
13414 <dest>
9444 *: "" 13415 *: ""
13416 </dest>
13417 <voice>
13418 *: "플레ì´ì–´"
9445 </voice> 13419 </voice>
9446</phrase> 13420</phrase>
9447<phrase> 13421<phrase>
9448 id: LANG_TIME_SET_BUTTON 13422 id: VOICE_GNUCHESS
9449 desc: used in set_time() 13423 desc: spoken only, for announcing player's id
9450 user: core 13424 user: core
9451 <source> 13425 <source>
9452 *: none 13426 *: ""
9453 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "SELECT = Set"
9454 gogearsa9200,samsungyh*: "PLAY = Set"
9455 iriverh100,iriverh120,iriverh300: "NAVI = Set"
9456 mpiohd300: "ENTER = Set"
9457 rtc: "ON = Set"
9458 vibe500: "OK = Set"
9459 </source> 13427 </source>
9460 <dest> 13428 <dest>
9461 *: none 13429 *: ""
9462 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "[ì„ íƒ] = 설정"
9463 gogearsa9200,samsungyh*: "PLAY = 설정"
9464 iriverh100,iriverh120,iriverh300: "[네비] = 설정"
9465 mpiohd300: "ENTER = 설정"
9466 rtc: "[켬] = 설정"
9467 vibe500: "OK = 설정"
9468 </dest> 13430 </dest>
9469 <voice> 13431 <voice>
9470 *: none 13432 *: "GNU 체스"
9471 </voice> 13433 </voice>
9472</phrase> 13434</phrase>
9473<phrase> 13435<phrase>
9474 id: LANG_CANCEL_WITH_ANY 13436 id: VOICE_MARKED
9475 desc: Generic string to use to cancel 13437 desc: spoken only, for announcing chess piece marking
9476 user: core 13438 user: core
9477 <source> 13439 <source>
9478 *: "Any Other = No" 13440 *: ""
9479 archosplayer: none
9480 </source> 13441 </source>
9481 <dest> 13442 <dest>
9482 *: "다른 버튼 = 아니오" 13443 *: ""
9483 archosplayer: none
9484 </dest> 13444 </dest>
9485 <voice> 13445 <voice>
13446 *: "표시ë¨"
13447 </voice>
13448</phrase>
13449<phrase>
13450 id: VOICE_UNMARKED
13451 desc: spoken only, for announcing chess piece unmarking
13452 user: core
13453 <source>
9486 *: "" 13454 *: ""
9487 archosplayer: none 13455 </source>
13456 <dest>
13457 *: ""
13458 </dest>
13459 <voice>
13460 *: "표시ë˜ì§€ ì•ŠìŒ"
9488 </voice> 13461 </voice>
9489</phrase> 13462</phrase>
9490<phrase> 13463<phrase>
9491 id: LANG_PLUGIN_DEMOS 13464 id: VOICE_WHITE
9492 desc: in the main menu 13465 desc: spoken only, for announcing chess piece color
9493 user: core 13466 user: core
9494 <source> 13467 <source>
9495 *: "Demos" 13468 *: ""
9496 </source> 13469 </source>
9497 <dest> 13470 <dest>
9498 *: "ë°ëª¨" 13471 *: ""
9499 </dest> 13472 </dest>
9500 <voice> 13473 <voice>
9501 *: "ë°ëª¨" 13474 *: "í°"
9502 </voice> 13475 </voice>
9503</phrase> 13476</phrase>
9504<phrase> 13477<phrase>
9505 id: LANG_TAGNAVI_RANDOM 13478 id: VOICE_BLACK
9506 desc: "<Random>" entry in tag browser 13479 desc: spoken only, for announcing chess piece color
9507 user: core 13480 user: core
9508 <source> 13481 <source>
9509 *: "<Random>" 13482 *: ""
9510 </source> 13483 </source>
9511 <dest> 13484 <dest>
9512 *: "<무작위>" 13485 *: ""
9513 </dest> 13486 </dest>
9514 <voice> 13487 <voice>
9515 *: "무작위" 13488 *: "검정"
9516 </voice> 13489 </voice>
9517</phrase> 13490</phrase>
9518<phrase> 13491<phrase>
9519 id: LANG_VOICE_FILE_TALK 13492 id: VOICE_CHESSBOX_CHECK
9520 desc: Item of voice menu, whether to use file .talk clips 13493 desc: spoken only, for announcing chess moves
9521 user: core 13494 user: core
9522 <source> 13495 <source>
9523 *: "Use File .talk Clips" 13496 *: ""
9524 </source> 13497 </source>
9525 <dest> 13498 <dest>
9526 *: ".talk Clip 파ì¼ì„ 사용" 13499 *: ""
9527 </dest> 13500 </dest>
9528 <voice> 13501 <voice>
9529 *: "talk Clip 파ì¼ì„ 사용" 13502 *: "ì²´í¬!"
9530 </voice> 13503 </voice>
9531</phrase> 13504</phrase>
9532<phrase> 13505<phrase>
9533 id: LANG_BOTTOM_QS_ITEM 13506 id: VOICE_CHESSBOX_CAPTURES
9534 desc: used for the submenu name for the quickscreen items 13507 desc: spoken only, for announcing chess moves
9535 user: core 13508 user: core
9536 <source> 13509 <source>
9537 *: none 13510 *: ""
9538 quickscreen: "Set as Bottom Quickscreen Item"
9539 </source> 13511 </source>
9540 <dest> 13512 <dest>
9541 *: none 13513 *: ""
9542 quickscreen: "아래 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 설정"
9543 </dest> 13514 </dest>
9544 <voice> 13515 <voice>
9545 *: none 13516 *: "í¬ë¡œ"
9546 quickscreen: "아래 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 설정"
9547 </voice> 13517 </voice>
9548</phrase> 13518</phrase>
9549<phrase> 13519<phrase>
9550 id: LANG_SELECTOR_TEXT_COLOR 13520 id: VOICE_CHESSBOX_CASTLE
9551 desc: line selector text color option 13521 desc: spoken only, for announcing chess moves
9552 user: core 13522 user: core
9553 <source> 13523 <source>
9554 *: none 13524 *: ""
9555 lcd_color: "Text Colour"
9556 </source> 13525 </source>
9557 <dest> 13526 <dest>
9558 *: none 13527 *: ""
9559 lcd_color: "ë¬¸ìž ìƒ‰ê¹”"
9560 </dest> 13528 </dest>
9561 <voice> 13529 <voice>
9562 *: none 13530 *: "성"
9563 lcd_color: "ë¬¸ìž ìƒ‰ê¹”"
9564 </voice> 13531 </voice>
9565</phrase> 13532</phrase>
9566<phrase> 13533<phrase>
9567 id: LANG_THEME_MENU 13534 id: VOICE_CHESSBOX_KINGSIDE
9568 desc: in the settings menu 13535 desc: spoken only, for announcing chess moves
9569 user: core 13536 user: core
9570 <source> 13537 <source>
9571 *: "Theme Settings" 13538 *: ""
9572 </source> 13539 </source>
9573 <dest> 13540 <dest>
9574 *: "테마 설정" 13541 *: ""
9575 </dest> 13542 </dest>
9576 <voice> 13543 <voice>
9577 *: "테마 설정" 13544 *: "킹 사ì´ë“œ"
9578 </voice> 13545 </voice>
9579</phrase> 13546</phrase>
9580<phrase> 13547<phrase>
9581 id: LANG_ALL 13548 id: VOICE_CHESSBOX_QUEENSIDE
9582 desc: generic string used both in dir file filter and repeat mode selection 13549 desc: spoken only, for announcing chess moves
9583 user: core 13550 user: core
9584 <source> 13551 <source>
9585 *: "All" 13552 *: ""
9586 </source> 13553 </source>
9587 <dest> 13554 <dest>
9588 *: "모ë‘" 13555 *: ""
9589 </dest> 13556 </dest>
9590 <voice> 13557 <voice>
9591 *: "모ë‘" 13558 *: "퀸 사ì´ë“œ"
9592 </voice> 13559 </voice>
9593</phrase> 13560</phrase>
9594<phrase> 13561<phrase>
9595 id: LANG_ACCESSORY_SUPPLY 13562 id: VOICE_PAWN
9596 desc: in system settings menu 13563 desc: spoken only, for announcing chess piece names
9597 user: core 13564 user: core
9598 <source> 13565 <source>
9599 *: none 13566 *: ""
9600 accessory_supply: "Accessory Power Supply"
9601 </source> 13567 </source>
9602 <dest> 13568 <dest>
9603 *: none 13569 *: ""
9604 accessory_supply: "악세서리 ì „ì› ê³µê¸‰"
9605 </dest> 13570 </dest>
9606 <voice> 13571 <voice>
9607 *: none 13572 *: "í°(보병)"
9608 accessory_supply: "악세서리 ì „ì› ê³µê¸‰"
9609 </voice> 13573 </voice>
9610</phrase> 13574</phrase>
9611<phrase> 13575<phrase>
9612 id: VOICE_OH 13576 id: VOICE_KNIGHT
9613 desc: spoken only, for wall clock announce 13577 desc: spoken only, for announcing chess piece names
9614 user: core 13578 user: core
9615 <source> 13579 <source>
9616 *: "" 13580 *: ""
@@ -9619,43 +13583,68 @@
9619 *: "" 13583 *: ""
9620 </dest> 13584 </dest>
9621 <voice> 13585 <voice>
13586 *: "나ì´íŠ¸(기사)"
13587 </voice>
13588</phrase>
13589<phrase>
13590 id: VOICE_BISHOP
13591 desc: spoken only, for announcing chess piece names
13592 user: core
13593 <source>
9622 *: "" 13594 *: ""
13595 </source>
13596 <dest>
13597 *: ""
13598 </dest>
13599 <voice>
13600 *: "비ìˆ(주êµ)"
9623 </voice> 13601 </voice>
9624</phrase> 13602</phrase>
9625<phrase> 13603<phrase>
9626 id: LANG_ADD_TO_FAVES 13604 id: VOICE_ROOK
9627 desc: 13605 desc: spoken only, for announcing chess piece names
9628 user: core 13606 user: core
9629 <source> 13607 <source>
9630 *: "Add to Shortcuts" 13608 *: ""
9631 </source> 13609 </source>
9632 <dest> 13610 <dest>
9633 *: "ì¦ê²¨ì°¾ê¸°ì— 추가" 13611 *: ""
9634 </dest> 13612 </dest>
9635 <voice> 13613 <voice>
9636 *: "ì¦ê²¨ì°¾ê¸°ì— 추가" 13614 *: "룩(성)"
9637 </voice> 13615 </voice>
9638</phrase> 13616</phrase>
9639<phrase> 13617<phrase>
9640 id: LANG_PRESET 13618 id: VOICE_QUEEN
9641 desc: in button bar and radio screen / menu 13619 desc: spoken only, for announcing chess piece names
9642 user: core 13620 user: core
9643 <source> 13621 <source>
9644 *: none 13622 *: ""
9645 radio: "Preset"
9646 </source> 13623 </source>
9647 <dest> 13624 <dest>
9648 *: none 13625 *: ""
9649 radio: "프리셋"
9650 </dest> 13626 </dest>
9651 <voice> 13627 <voice>
9652 *: none 13628 *: "퀸(여왕)"
9653 radio: "프리셋"
9654 </voice> 13629 </voice>
9655</phrase> 13630</phrase>
9656<phrase> 13631<phrase>
9657 id: LANG_PLUGIN_GAMES 13632 id: VOICE_KING
9658 desc: in the main menu 13633 desc: spoken only, for announcing chess piece names
13634 user: core
13635 <source>
13636 *: ""
13637 </source>
13638 <dest>
13639 *: ""
13640 </dest>
13641 <voice>
13642 *: "킹(왕)"
13643 </voice>
13644</phrase>
13645<phrase>
13646 id: LANG_CHESSBOX_GAMES
13647 desc: in chessbox
9659 user: core 13648 user: core
9660 <source> 13649 <source>
9661 *: "Games" 13650 *: "Games"
@@ -9664,74 +13653,68 @@
9664 *: "게임" 13653 *: "게임"
9665 </dest> 13654 </dest>
9666 <voice> 13655 <voice>
9667 *: "게임" 13656 *: ""
9668 </voice> 13657 </voice>
9669</phrase> 13658</phrase>
9670<phrase> 13659<phrase>
9671 id: LANG_END_PLAYLIST 13660 id: LANG_CHESSBOX_SAVING_POSITION
9672 desc: when playlist has finished 13661 desc: in chessbox
9673 user: core 13662 user: core
9674 <source> 13663 <source>
9675 *: "End of Song List" 13664 *: "Saving position"
9676 archosplayer: "End of List"
9677 </source> 13665 </source>
9678 <dest> 13666 <dest>
9679 *: "노래 목ë¡ì˜ 마지막" 13667 *: "위치 저장"
9680 archosplayer: "목ë¡ì˜ 마지막"
9681 </dest> 13668 </dest>
9682 <voice> 13669 <voice>
9683 *: "노래 목ë¡ì˜ 마지막" 13670 *: "위치 저장"
9684 archosplayer: "목ë¡ì˜ 마지막"
9685 </voice> 13671 </voice>
9686</phrase> 13672</phrase>
9687<phrase> 13673<phrase>
9688 id: LANG_SYSFONT_GAIN 13674 id: LANG_CHESSBOX_LOADING_POSITION
9689 desc: in the equalizer settings menu 13675 desc: in chessbox
9690 user: core 13676 user: core
9691 <source> 13677 <source>
9692 *: "Gain" 13678 *: "Loading position"
9693 </source> 13679 </source>
9694 <dest> 13680 <dest>
9695 *: "게ì¸" 13681 *: "위치 로드"
9696 </dest> 13682 </dest>
9697 <voice> 13683 <voice>
9698 *: "게ì¸" 13684 *: "위치 로드"
9699 </voice> 13685 </voice>
9700</phrase> 13686</phrase>
9701<phrase> 13687<phrase>
9702 id: LANG_CLEAR_REC_DIR 13688 id: LANG_CHESSBOX_THINKING
9703 desc: 13689 desc: in chessbox
9704 user: core 13690 user: core
9705 <source> 13691 <source>
9706 *: none 13692 *: "Thinking..."
9707 recording: "Clear Recording Directory"
9708 </source> 13693 </source>
9709 <dest> 13694 <dest>
9710 *: none 13695 *: "ìƒê°í•˜ëŠ” 중..."
9711 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ 비우기"
9712 </dest> 13696 </dest>
9713 <voice> 13697 <voice>
9714 *: none 13698 *: "ìƒê°í•˜ëŠ” 중"
9715 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ 비우기"
9716 </voice> 13699 </voice>
9717</phrase> 13700</phrase>
9718<phrase> 13701<phrase>
9719 id: LANG_KEYLOCK_OFF 13702 id: VOICE_BATTERY_BENCH_IS_ALREADY_RUNNING
9720 desc: displayed when key lock is turned off 13703 desc: Spoken if battery bench is already running
9721 user: core 13704 user: core
9722 <source> 13705 <source>
9723 *: "Buttons Unlocked" 13706 *: ""
9724 </source> 13707 </source>
9725 <dest> 13708 <dest>
9726 *: "버튼 잠김 해제" 13709 *: ""
9727 </dest> 13710 </dest>
9728 <voice> 13711 <voice>
9729 *: "" 13712 *: "배터리 벤치가 ì´ë¯¸ 실행 중"
9730 </voice> 13713 </voice>
9731</phrase> 13714</phrase>
9732<phrase> 13715<phrase>
9733 id: VOICE_PM 13716 id: VOICE_BAT_BENCH_KEYS
9734 desc: spoken only, for wall clock announce 13717 desc: Battery bench start up message
9735 user: core 13718 user: core
9736 <source> 13719 <source>
9737 *: "" 13720 *: ""
@@ -9740,561 +13723,1680 @@
9740 *: "" 13723 *: ""
9741 </dest> 13724 </dest>
9742 <voice> 13725 <voice>
9743 *: "P M" 13726 *: "ìž¬ìƒ ë²„íŠ¼ì„ ëˆŒëŸ¬ 배터리 벤치를 실행하거나 중지하여 취소"
9744 </voice> 13727 </voice>
9745</phrase> 13728</phrase>
9746<phrase> 13729<phrase>
9747 id: LANG_STOP_RECORDING_AND_SHUTDOWN 13730 id: LANG_CANNOT_RESTART_PLAYBACK
9748 desc: in record timesplit options 13731 desc: cannot restart playback splash in imageviewer
13732 user: core
13733 <source>
13734 *: "Cannot restart playback"
13735 </source>
13736 <dest>
13737 *: "재ìƒì„ 다시 시작할 수 ì—†ìŒ"
13738 </dest>
13739 <voice>
13740 *: "재ìƒì„ 다시 시작할 수 ì—†ìŒ"
13741 </voice>
13742</phrase>
13743<phrase>
13744 id: LANG_ORDERED
13745 desc: in the imageviewer settings menu
13746 user: core
13747 <source>
13748 *: "Ordered"
13749 </source>
13750 <dest>
13751 *: "지시함"
13752 </dest>
13753 <voice>
13754 *: "지시함"
13755 </voice>
13756</phrase>
13757<phrase>
13758 id: LANG_DIFFUSION
13759 desc: in the imageviewer settings menu
13760 user: core
13761 <source>
13762 *: "Diffusion"
13763 </source>
13764 <dest>
13765 *: "확산"
13766 </dest>
13767 <voice>
13768 *: "확산"
13769 </voice>
13770</phrase>
13771<phrase>
13772 id: LANG_GRAYSCALE
13773 desc: in the imageviewer settings menu
13774 user: core
13775 <source>
13776 *: "Grayscale"
13777 </source>
13778 <dest>
13779 *: "그레ì´ìŠ¤ì¼€ì¼"
13780 </dest>
13781 <voice>
13782 *: "그레ì´ìŠ¤ì¼€ì¼"
13783 </voice>
13784</phrase>
13785<phrase>
13786 id: LANG_SLIDESHOW_MODE
13787 desc: in the imageviewer settings menu
13788 user: core
13789 <source>
13790 *: "Toggle Slideshow Mode"
13791 </source>
13792 <dest>
13793 *: "슬ë¼ì´ë“œì‡¼ 모드 전환"
13794 </dest>
13795 <voice>
13796 *: "슬ë¼ì´ë“œ 쇼 모드 전환"
13797 </voice>
13798</phrase>
13799<phrase>
13800 id: LANG_SLIDESHOW_TIME
13801 desc: in the imageviewer settings menu
13802 user: core
13803 <source>
13804 *: "Slideshow Time"
13805 </source>
13806 <dest>
13807 *: "슬ë¼ì´ë“œì‡¼ 시간"
13808 </dest>
13809 <voice>
13810 *: "슬ë¼ì´ë“œ 쇼 시간"
13811 </voice>
13812</phrase>
13813<phrase>
13814 id: LANG_RETURN
13815 desc: in various plugin menus
13816 user: core
13817 <source>
13818 *: "Return"
13819 </source>
13820 <dest>
13821 *: "ë˜ëŒì•„가기"
13822 </dest>
13823 <voice>
13824 *: "ë˜ëŒì•„가기"
13825 </voice>
13826</phrase>
13827<phrase>
13828 id: LANG_REC_DIR
13829 desc: used in the info screen to show a recording dir
9749 user: core 13830 user: core
9750 <source> 13831 <source>
9751 *: none 13832 *: none
9752 recording: "Stop Recording And Shutdown" 13833 recording: "Recording Directory"
9753 </source> 13834 </source>
9754 <dest> 13835 <dest>
9755 *: none 13836 *: none
9756 recording: "ë…¹ìŒ ë©ˆì¶ê³  ë„기" 13837 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬"
9757 </dest> 13838 </dest>
9758 <voice> 13839 <voice>
9759 *: none 13840 *: none
9760 recording: "ë…¹ìŒ ë©ˆì¶ê³  ë„기" 13841 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬"
9761 </voice> 13842 </voice>
9762</phrase> 13843</phrase>
9763<phrase> 13844<phrase>
9764 id: LANG_SYSFONT_MODE 13845 id: LANG_CHESSBOX_MENU_VIEW_GAMES
9765 desc: in wps F2 pressed 13846 desc: in the chessbox menu
9766 user: core 13847 user: core
9767 <source> 13848 <source>
9768 *: "Mode:" 13849 *: "View Played Games"
9769 </source> 13850 </source>
9770 <dest> 13851 <dest>
9771 *: "모드:" 13852 *: "ì¦ê²¼ë˜ 게임 보기"
13853 </dest>
13854 <voice>
13855 *: "ì¦ê²¼ë˜ 게임 보기"
13856 </voice>
13857</phrase>
13858<phrase>
13859 id: LANG_MENU_AUDIO_OPTIONS
13860 desc: in mpegplayer menus
13861 user: core
13862 <source>
13863 *: "Audio Options"
13864 lowmem: none
13865 </source>
13866 <dest>
13867 *: "오디오 옵션"
13868 lowmem: none
13869 </dest>
13870 <voice>
13871 *: "오디오 옵션"
13872 lowmem: none
13873 </voice>
13874</phrase>
13875<phrase>
13876 id: LANG_MENU_RESUME_OPTIONS
13877 desc: in mpegplayer menus
13878 user: core
13879 <source>
13880 *: "Resume Options"
13881 lowmem: none
13882 </source>
13883 <dest>
13884 *: "옵션 재시작"
13885 lowmem: none
13886 </dest>
13887 <voice>
13888 *: "옵션 재시작"
13889 lowmem: none
13890 </voice>
13891</phrase>
13892<phrase>
13893 id: LANG_MENU_PLAY_MODE
13894 desc: in mpegplayer menus
13895 user: core
13896 <source>
13897 *: "Play Mode"
13898 lowmem: none
13899 </source>
13900 <dest>
13901 *: "ìž¬ìƒ ëª¨ë“œ"
13902 lowmem: none
13903 </dest>
13904 <voice>
13905 *: "ìž¬ìƒ ëª¨ë“œ"
13906 lowmem: none
13907 </voice>
13908</phrase>
13909<phrase>
13910 id: LANG_SINGLE
13911 desc: in mpegplayer menus
13912 user: core
13913 <source>
13914 *: "Single"
13915 lowmem: none
13916 </source>
13917 <dest>
13918 *: "싱글"
13919 lowmem: none
13920 </dest>
13921 <voice>
13922 *: "싱글"
13923 lowmem: none
13924 </voice>
13925</phrase>
13926<phrase>
13927 id: LANG_USE_SOUND_SETTING
13928 desc: in mpegplayer menus
13929 user: core
13930 <source>
13931 *: "Use sound setting"
13932 lowmem: none
13933 </source>
13934 <dest>
13935 *: "사운드 설정 사용"
13936 lowmem: none
13937 </dest>
13938 <voice>
13939 *: "사운드 설정 사용"
13940 lowmem: none
13941 </voice>
13942</phrase>
13943<phrase>
13944 id: LANG_RESTART_PLAYBACK
13945 desc: in the mpegplayer settings menu
13946 user: core
13947 <source>
13948 *: "Play from beginning"
13949 lowmem: none
13950 </source>
13951 <dest>
13952 *: "처ìŒë¶€í„° 재ìƒ"
13953 lowmem: none
13954 </dest>
13955 <voice>
13956 *: "처ìŒë¶€í„° 재ìƒ"
13957 lowmem: none
13958 </voice>
13959</phrase>
13960<phrase>
13961 id: LANG_SET_RESUME_TIME
13962 desc: in the mpegplayer settings menu
13963 user: core
13964 <source>
13965 *: "Set resume time (min)"
13966 lowmem: none
13967 </source>
13968 <dest>
13969 *: "재시작 시간 설정 (분)"
13970 lowmem: none
13971 </dest>
13972 <voice>
13973 *: "재시작 시간 설정"
13974 lowmem: none
13975 </voice>
13976</phrase>
13977<phrase>
13978 id: LANG_DISPLAY_FPS
13979 desc: in the mpegplayer and pictureflow settings menus
13980 user: core
13981 <source>
13982 *: "Display FPS"
13983 </source>
13984 <dest>
13985 *: "FPS 보기"
13986 </dest>
13987 <voice>
13988 *: "FPS 보기"
13989 </voice>
13990</phrase>
13991<phrase>
13992 id: LANG_LIMIT_FPS
13993 desc: in the mpegplayer settings menu
13994 user: core
13995 <source>
13996 *: "Limit FPS"
13997 lowmem: none
13998 </source>
13999 <dest>
14000 *: "FPS 제한"
14001 lowmem: none
14002 </dest>
14003 <voice>
14004 *: "FPS 제한"
14005 lowmem: none
14006 </voice>
14007</phrase>
14008<phrase>
14009 id: LANG_SKIP_FRAMES
14010 desc: in the mpegplayer settings menu
14011 user: core
14012 <source>
14013 *: "Skip frames"
14014 lowmem: none
14015 </source>
14016 <dest>
14017 *: "프레임 건너뛰기"
14018 lowmem: none
14019 </dest>
14020 <voice>
14021 *: "프레임 건너뛰기"
14022 lowmem: none
14023 </voice>
14024</phrase>
14025<phrase>
14026 id: LANG_BACKLIGHT_BRIGHTNESS
14027 desc: in the mpegplayer settings menu
14028 user: core
14029 <source>
14030 *: "Backlight brightness"
14031 lowmem: none
14032 </source>
14033 <dest>
14034 *: "ë°±ë¼ì´íŠ¸ ë°ê¸°"
14035 lowmem: none
14036 </dest>
14037 <voice>
14038 *: "ë°±ë¼ì´íŠ¸ ë°ê¸°"
14039 lowmem: none
14040 </voice>
14041</phrase>
14042<phrase>
14043 id: LANG_USE_COMMON_SETTING
14044 desc: in the mpegplayer settings menu
14045 user: core
14046 <source>
14047 *: "Use common setting"
14048 lowmem: none
14049 </source>
14050 <dest>
14051 *: "공통 설정 사용"
14052 lowmem: none
14053 </dest>
14054 <voice>
14055 *: "공통 설정 사용"
14056 lowmem: none
14057 </voice>
14058</phrase>
14059<phrase>
14060 id: LANG_TONE_CONTROLS
14061 desc: in the mpegplayer settings menu
14062 user: core
14063 <source>
14064 *: "Tone controls"
14065 lowmem: none
14066 </source>
14067 <dest>
14068 *: "톤 제어"
14069 lowmem: none
14070 </dest>
14071 <voice>
14072 *: "톤 제어"
14073 lowmem: none
14074 </voice>
14075</phrase>
14076<phrase>
14077 id: LANG_FORCE_START_MENU
14078 desc: in mpegplayer menus
14079 user: core
14080 <source>
14081 *: "Start menu"
14082 lowmem: none
14083 </source>
14084 <dest>
14085 *: "시작 메뉴"
14086 lowmem: none
14087 </dest>
14088 <voice>
14089 *: "시작 메뉴"
14090 lowmem: none
14091 </voice>
14092</phrase>
14093<phrase>
14094 id: LANG_CONDITIONAL_START_MENU
14095 desc: in mpegplayer menus
14096 user: core
14097 <source>
14098 *: "Start menu if not completed"
14099 lowmem: none
14100 </source>
14101 <dest>
14102 *: "완료ë˜ì§€ ì•Šì€ ê²½ìš° 시작 메뉴"
14103 lowmem: none
14104 </dest>
14105 <voice>
14106 *: "완료ë˜ì§€ ì•Šì€ ê²½ìš° 시작 메뉴"
14107 lowmem: none
14108 </voice>
14109</phrase>
14110<phrase>
14111 id: LANG_AUTO_RESUME
14112 desc: in mpegplayer menus
14113 user: core
14114 <source>
14115 *: "Resume automatically"
14116 lowmem: none
14117 </source>
14118 <dest>
14119 *: "ìžë™ìœ¼ë¡œ 재시작"
14120 lowmem: none
14121 </dest>
14122 <voice>
14123 *: "ìžë™ìœ¼ë¡œ 재시작"
14124 lowmem: none
14125 </voice>
14126</phrase>
14127<phrase>
14128 id: LANG_CLEAR_ALL_RESUMES
14129 desc: in the mpegplayer settings menu
14130 user: core
14131 <source>
14132 *: "Clear all resumes"
14133 lowmem: none
14134 </source>
14135 <dest>
14136 *: "모든 ì´ë ¥ 지우기"
14137 lowmem: none
14138 </dest>
14139 <voice>
14140 *: "모든 ì´ë ¥ 지우기"
14141 lowmem: none
14142 </voice>
14143</phrase>
14144<phrase>
14145 id: LANG_UNAVAILABLE
14146 desc: in mpegplayer settings
14147 user: core
14148 <source>
14149 *: "Unavailable"
14150 lowmem: none
14151 </source>
14152 <dest>
14153 *: "사용할 수 ì—†ìŒ"
14154 lowmem: none
14155 </dest>
14156 <voice>
14157 *: "사용할 수 ì—†ìŒ"
14158 lowmem: none
14159 </voice>
14160</phrase>
14161<phrase>
14162 id: LANG_TOGGLE_ITEM
14163 desc: in main_menu_config
14164 user: core
14165 <source>
14166 *: "Toggle Item"
14167 </source>
14168 <dest>
14169 *: "전환 항목"
14170 </dest>
14171 <voice>
14172 *: "전환 항목"
14173 </voice>
14174</phrase>
14175<phrase>
14176 id: LANG_MOVE_ITEM_UP
14177 desc: in main_menu_config
14178 user: core
14179 <source>
14180 *: "Move Item Up"
14181 </source>
14182 <dest>
14183 *: "í•­ëª©ì„ ìœ„ë¡œ ì´ë™"
14184 </dest>
14185 <voice>
14186 *: "í•­ëª©ì„ ìœ„ë¡œ ì´ë™"
14187 </voice>
14188</phrase>
14189<phrase>
14190 id: LANG_MOVE_ITEM_DOWN
14191 desc: in main_menu_config
14192 user: core
14193 <source>
14194 *: "Move Item Down"
14195 </source>
14196 <dest>
14197 *: "í•­ëª©ì„ ì•„ëž˜ë¡œ ì´ë™"
14198 </dest>
14199 <voice>
14200 *: "í•­ëª©ì„ ì•„ëž˜ë¡œ ì´ë™"
14201 </voice>
14202</phrase>
14203<phrase>
14204 id: LANG_LOAD_DEFAULT_CONFIGURATION
14205 desc: in main_menu_config
14206 user: core
14207 <source>
14208 *: "Load Default Configuration"
14209 </source>
14210 <dest>
14211 *: "기본 구성 로드"
14212 </dest>
14213 <voice>
14214 *: "기본 구성 로드"
14215 </voice>
14216</phrase>
14217<phrase>
14218 id: LANG_SAVE_EXIT
14219 desc: in main_menu_config
14220 user: core
14221 <source>
14222 *: "Save and Exit"
14223 </source>
14224 <dest>
14225 *: "저장 ë° ì¢…ë£Œ"
14226 </dest>
14227 <voice>
14228 *: "저장 ë° ì¢…ë£Œ"
14229 </voice>
14230</phrase>
14231<phrase>
14232 id: LANG_MAIN_MENU_ORDER
14233 desc: main_menu_config plugin title
14234 user: core
14235 <source>
14236 *: "Rockbox Main Menu Order"
14237 </source>
14238 <dest>
14239 *: "ë¡ë°•ìŠ¤ ë©”ì¸ ë©”ë‰´ 순서"
9772 </dest> 14240 </dest>
9773 <voice> 14241 <voice>
9774 *: "" 14242 *: ""
9775 </voice> 14243 </voice>
9776</phrase> 14244</phrase>
9777<phrase> 14245<phrase>
9778 id: LANG_RECORDING_TIMESPLIT_REC 14246 id: LANG_PROPERTIES_PATH
9779 desc: Display of record timer interval setting, on the record screen 14247 desc: in properties plugin
9780 user: core 14248 user: core
9781 <source> 14249 <source>
9782 *: none 14250 *: "[Path]"
9783 recording: "Split Time:"
9784 </source> 14251 </source>
9785 <dest> 14252 <dest>
9786 *: none 14253 *: "[경로]"
9787 recording: "분리 시간:"
9788 </dest> 14254 </dest>
9789 <voice> 14255 <voice>
9790 *: none 14256 *: "경로"
9791 recording: ""
9792 </voice> 14257 </voice>
9793</phrase> 14258</phrase>
9794<phrase> 14259<phrase>
9795 id: LANG_MIN_DURATION 14260 id: LANG_PROPERTIES_FILENAME
9796 desc: in recording settings_menu 14261 desc: in properties plugin
9797 user: core 14262 user: core
9798 <source> 14263 <source>
9799 *: none 14264 *: "[Filename]"
9800 recording: "for at least"
9801 </source> 14265 </source>
9802 <dest> 14266 <dest>
9803 *: none 14267 *: "[파ì¼ì´ë¦„]"
9804 recording: "최소한"
9805 </dest> 14268 </dest>
9806 <voice> 14269 <voice>
9807 *: none 14270 *: "파ì¼ì´ë¦„"
9808 recording: "최소한"
9809 </voice> 14271 </voice>
9810</phrase> 14272</phrase>
9811<phrase> 14273<phrase>
9812 id: LANG_CHANNELS 14274 id: LANG_PROPERTIES_SIZE
9813 desc: in sound_settings 14275 desc: in properties plugin
9814 user: core 14276 user: core
9815 <source> 14277 <source>
9816 *: "Channels" 14278 *: "[Size]"
9817 </source> 14279 </source>
9818 <dest> 14280 <dest>
9819 *: "채ë„" 14281 *: "[í¬ê¸°]"
9820 </dest> 14282 </dest>
9821 <voice> 14283 <voice>
9822 *: "채ë„" 14284 *: "í¬ê¸°"
9823 </voice> 14285 </voice>
9824</phrase> 14286</phrase>
9825<phrase> 14287<phrase>
9826 id: LANG_ASK 14288 id: LANG_PROPERTIES_DATE
9827 desc: in settings_menu 14289 desc: in properties plugin
9828 user: core 14290 user: core
9829 <source> 14291 <source>
9830 *: "Ask" 14292 *: "[Date]"
9831 </source> 14293 </source>
9832 <dest> 14294 <dest>
9833 *: "묻기" 14295 *: "[날짜]"
9834 </dest> 14296 </dest>
9835 <voice> 14297 <voice>
9836 *: "묻기" 14298 *: "날짜"
9837 </voice> 14299 </voice>
9838</phrase> 14300</phrase>
9839<phrase> 14301<phrase>
9840 id: LANG_MODE 14302 id: LANG_PROPERTIES_TIME
9841 desc: in wps F2 pressed and radio screen 14303 desc: in properties plugin
9842 user: core 14304 user: core
9843 <source> 14305 <source>
9844 *: "Mode:" 14306 *: "[Time]"
9845 </source> 14307 </source>
9846 <dest> 14308 <dest>
9847 *: "모드:" 14309 *: "[시간]"
9848 </dest> 14310 </dest>
9849 <voice> 14311 <voice>
9850 *: "모드:" 14312 *: "시간"
9851 </voice> 14313 </voice>
9852</phrase> 14314</phrase>
9853<phrase> 14315<phrase>
9854 id: LANG_PITCH_DOWN_SEMITONE 14316 id: LANG_PROPERTIES_SUBDIRS
9855 desc: in wps 14317 desc: in properties plugin
9856 user: core 14318 user: core
9857 <source> 14319 <source>
9858 *: none 14320 *: "[Subdirs]"
9859 pitchscreen: "Semitone Down"
9860 </source> 14321 </source>
9861 <dest> 14322 <dest>
9862 *: none 14323 *: "[하위 디렉토리]"
9863 pitchscreen: "ë°˜ìŒìœ¼ë¡œ 내리기"
9864 </dest> 14324 </dest>
9865 <voice> 14325 <voice>
9866 *: none 14326 *: "하위 디렉토리"
9867 pitchscreen: ""
9868 </voice> 14327 </voice>
9869</phrase> 14328</phrase>
9870<phrase> 14329<phrase>
9871 id: LANG_SAVE_SOUND 14330 id: LANG_PROPERTIES_FILES
9872 desc: save a sound config file 14331 desc: in properties plugin
9873 user: core 14332 user: core
9874 <source> 14333 <source>
9875 *: "Save Sound Settings" 14334 *: "[Files]"
9876 </source> 14335 </source>
9877 <dest> 14336 <dest>
9878 *: "소리 설정 저장" 14337 *: "ŒŒì¼]"
9879 </dest> 14338 </dest>
9880 <voice> 14339 <voice>
9881 *: "소리 설정 저장" 14340 *: "파ì¼"
9882 </voice> 14341 </voice>
9883</phrase> 14342</phrase>
9884<phrase> 14343<phrase>
9885 id: VOICE_EDIT 14344 id: LANG_PROPERTIES_DIRECTORY_PROPERTIES
9886 desc: keyboard 14345 desc: in properties plugin
9887 user: core 14346 user: core
9888 <source> 14347 <source>
14348 *: "Directory properties"
14349 </source>
14350 <dest>
14351 *: "디렉토리 ì†ì„±"
14352 </dest>
14353 <voice>
9889 *: "" 14354 *: ""
14355 </voice>
14356</phrase>
14357<phrase>
14358 id: LANG_PROPERTIES_FILE_PROPERTIES
14359 desc: in properties plugin
14360 user: core
14361 <source>
14362 *: "File properties"
9890 </source> 14363 </source>
9891 <dest> 14364 <dest>
14365 *: "íŒŒì¼ ì†ì„±"
14366 </dest>
14367 <voice>
9892 *: "" 14368 *: ""
14369 </voice>
14370</phrase>
14371<phrase>
14372 id: LANG_PROPERTIES_FAIL
14373 desc: in properties plugin
14374 user: core
14375 <source>
14376 *: "Failed to gather information"
14377 </source>
14378 <dest>
14379 *: "정보수집 실패함"
9893 </dest> 14380 </dest>
9894 <voice> 14381 <voice>
9895 *: "편집" 14382 *: "정보수집 실패함"
9896 </voice> 14383 </voice>
9897</phrase> 14384</phrase>
9898<phrase> 14385<phrase>
9899 id: LANG_HEADPHONE_UNPLUG 14386 id: LANG_SWAP_CHANNELS
9900 desc: in settings_menu. 14387 desc: in sound_settings
9901 user: core 14388 user: core
9902 <source> 14389 <source>
9903 *: none 14390 *: "Swap Channels"
9904 headphone_detection: "Pause on Headphone Unplug"
9905 </source> 14391 </source>
9906 <dest> 14392 <dest>
9907 *: none 14393 *: "ì±„ë„ êµí™˜"
9908 headphone_detection: "ì´ì–´í°/í—¤ë“œí° ì—°ê²°ì‹œ 정지"
9909 </dest> 14394 </dest>
9910 <voice> 14395 <voice>
9911 *: none 14396 *: "ì±„ë„ êµí™˜"
9912 headphone_detection: "ì´ì–´í°ì´ë‚˜ í—¤ë“œí° ì—°ê²°ì‹œ 정지"
9913 </voice> 14397 </voice>
9914</phrase> 14398</phrase>
9915<phrase> 14399<phrase>
9916 id: LANG_ID3_GROUPING 14400 id: LANG_PANNING_SEPARATION
9917 desc: in tag viewer 14401 desc: in mikmod settings menu
9918 user: core 14402 user: core
9919 <source> 14403 <source>
9920 *: "Work" 14404 *: "Panning Separation"
14405 lowmem: none
9921 </source> 14406 </source>
9922 <dest> 14407 <dest>
9923 *: "ìž‘ì—…" 14408 *: "íŒ¨ë‹ ë¶„ë¦¬"
14409 lowmem: none
9924 </dest> 14410 </dest>
9925 <voice> 14411 <voice>
9926 *: "ìž‘ì—…" 14412 *: "íŒ¨ë‹ ë¶„ë¦¬"
14413 lowmem: none
9927 </voice> 14414 </voice>
9928</phrase> 14415</phrase>
9929<phrase> 14416<phrase>
9930 id: LANG_RIGHT_QS_ITEM 14417 id: LANG_REVERBERATION
9931 desc: used for the submenu name for the quickscreen items 14418 desc: in mikmod settings menu
9932 user: core 14419 user: core
9933 <source> 14420 <source>
9934 *: none 14421 *: "Reverberation"
9935 quickscreen: "Set as Right Quickscreen Item" 14422 lowmem: none
9936 </source> 14423 </source>
9937 <dest> 14424 <dest>
9938 *: none 14425 *: "잔향"
9939 quickscreen: "오른쪽 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 지정" 14426 lowmem: none
9940 </dest> 14427 </dest>
9941 <voice> 14428 <voice>
9942 *: none 14429 *: "잔향"
9943 quickscreen: "오른쪽 퀵스í¬ë¦° ì•„ì´í…œìœ¼ë¡œ 지정" 14430 lowmem: none
9944 </voice> 14431 </voice>
9945</phrase> 14432</phrase>
9946<phrase> 14433<phrase>
9947 id: LANG_VERY_SLOW 14434 id: LANG_INTERPOLATION
9948 desc: in settings_menu 14435 desc: in mikmod settings menu
9949 user: core 14436 user: core
9950 <source> 14437 <source>
9951 *: "Very slow" 14438 *: "Interpolation"
14439 lowmem: none
9952 </source> 14440 </source>
9953 <dest> 14441 <dest>
9954 *: "매우 ëŠë¦¼" 14442 *: "ë³´ê°„"
14443 lowmem: none
9955 </dest> 14444 </dest>
9956 <voice> 14445 <voice>
9957 *: "매우 ëŠë¦¼" 14446 *: "ë³´ê°„"
14447 lowmem: none
9958 </voice> 14448 </voice>
9959</phrase> 14449</phrase>
9960<phrase> 14450<phrase>
9961 id: LANG_KEYCLICK 14451 id: LANG_MIKMOD_SURROUND
9962 desc: in keyclick settings menu 14452 desc: in mikmod settings menu
9963 user: core 14453 user: core
9964 <source> 14454 <source>
9965 *: "Keyclick" 14455 *: "Surround"
14456 lowmem: none
9966 </source> 14457 </source>
9967 <dest> 14458 <dest>
9968 *: "키í´ë¦­" 14459 *: "ì„œë¼ìš´ë“œ"
14460 lowmem: none
9969 </dest> 14461 </dest>
9970 <voice> 14462 <voice>
9971 *: "키í´ë¦­" 14463 *: "ì„œë¼ìš´ë“œ"
14464 lowmem: none
9972 </voice> 14465 </voice>
9973</phrase> 14466</phrase>
9974<phrase> 14467<phrase>
9975 id: LANG_PM_CLIPCOUNT 14468 id: LANG_MIKMOD_HQMIXER
9976 desc: in recording GUI, for recording peak meter. MAX 5 characters! 14469 desc: in mikmod settings menu
14470 user: core
14471 <source>
14472 *: "HQ Mixer"
14473 lowmem: none
14474 </source>
14475 <dest>
14476 *: "고품질 믹서"
14477 lowmem: none
14478 </dest>
14479 <voice>
14480 *: "고품질 믹서"
14481 lowmem: none
14482 </voice>
14483</phrase>
14484<phrase>
14485 id: LANG_MIKMOD_SAMPLERATE
14486 desc: in mikmod settings menu
14487 user: core
14488 <source>
14489 *: "Sample Rate"
14490 lowmem: none
14491 </source>
14492 <dest>
14493 *: "샘플 전송률"
14494 lowmem: none
14495 </dest>
14496 <voice>
14497 *: "샘플 전송률"
14498 lowmem: none
14499 </voice>
14500</phrase>
14501<phrase>
14502 id: LANG_CPU_BOOST
14503 desc: in mikmod settings menu
14504 user: core
14505 <source>
14506 *: "CPU Boost"
14507 lowmem: none
14508 </source>
14509 <dest>
14510 *: "CPU 부스트"
14511 lowmem: none
14512 </dest>
14513 <voice>
14514 *: "CPU 부스트"
14515 lowmem: none
14516 </voice>
14517</phrase>
14518<phrase>
14519 id: LANG_SPACING
14520 desc: in the pictureflow settings menu
14521 user: core
14522 <source>
14523 *: "Spacing"
14524 </source>
14525 <dest>
14526 *: "간격"
14527 </dest>
14528 <voice>
14529 *: "간격"
14530 </voice>
14531</phrase>
14532<phrase>
14533 id: LANG_CENTRE_MARGIN
14534 desc: in the pictureflow settings menu
14535 user: core
14536 <source>
14537 *: "Centre margin"
14538 </source>
14539 <dest>
14540 *: "중앙 여백"
14541 </dest>
14542 <voice>
14543 *: "중앙 여백"
14544 </voice>
14545</phrase>
14546<phrase>
14547 id: LANG_NUMBER_OF_SLIDES
14548 desc: in the pictureflow settings menu
14549 user: core
14550 <source>
14551 *: "Number of slides"
14552 </source>
14553 <dest>
14554 *: "슬ë¼ì´ë“œ 수"
14555 </dest>
14556 <voice>
14557 *: "슬ë¼ì´ë“œ 수"
14558 </voice>
14559</phrase>
14560<phrase>
14561 id: LANG_ZOOM
14562 desc: in the pictureflow settings menu
14563 user: core
14564 <source>
14565 *: "Zoom"
14566 </source>
14567 <dest>
14568 *: "확대/축소"
14569 </dest>
14570 <voice>
14571 *: "확대 ë° ì¶•ì†Œ"
14572 </voice>
14573</phrase>
14574<phrase>
14575 id: LANG_SHOW_ALBUM_TITLE
14576 desc: in the pictureflow settings menu
14577 user: core
14578 <source>
14579 *: "Show album title"
14580 </source>
14581 <dest>
14582 *: "앨범 제목 표시"
14583 </dest>
14584 <voice>
14585 *: "앨범 제목 표시"
14586 </voice>
14587</phrase>
14588<phrase>
14589 id: LANG_RESIZE_COVERS
14590 desc: in the pictureflow settings menu
14591 user: core
14592 <source>
14593 *: "Resize Covers"
14594 </source>
14595 <dest>
14596 *: "표지 í¬ê¸° ì¡°ì •"
14597 </dest>
14598 <voice>
14599 *: "표지 í¬ê¸° ì¡°ì •"
14600 </voice>
14601</phrase>
14602<phrase>
14603 id: LANG_REBUILD_CACHE
14604 desc: in the pictureflow settings menu
14605 user: core
14606 <source>
14607 *: "Rebuild cache"
14608 </source>
14609 <dest>
14610 *: "ìºì‹œ 리빌드"
14611 </dest>
14612 <voice>
14613 *: "ìºì‹œ 리빌드"
14614 </voice>
14615</phrase>
14616<phrase>
14617 id: LANG_WPS_INTEGRATION
14618 desc: in the pictureflow settings menu
14619 user: core
14620 <source>
14621 *: "WPS Integration"
14622 </source>
14623 <dest>
14624 *: "WPS 통합"
14625 </dest>
14626 <voice>
14627 *: "WPS 통합"
14628 </voice>
14629</phrase>
14630<phrase>
14631 id: LANG_GOTO_WPS
14632 desc: in the pictureflow main menu
14633 user: core
14634 <source>
14635 *: "Go to WPS"
14636 </source>
14637 <dest>
14638 *: "WPSë¡œ ì´ë™"
14639 </dest>
14640 <voice>
14641 *: "WPSë¡œ ì´ë™"
14642 </voice>
14643</phrase>
14644<phrase>
14645 id: LANG_HIDE_ALBUM_TITLE
14646 desc: in the pictureflow settings
14647 user: core
14648 <source>
14649 *: "Hide album title"
14650 </source>
14651 <dest>
14652 *: "앨범 제목 숨기기"
14653 </dest>
14654 <voice>
14655 *: "앨범 제목 숨기기"
14656 </voice>
14657</phrase>
14658<phrase>
14659 id: LANG_SHOW_AT_THE_BOTTOM
14660 desc: in the pictureflow settings
14661 user: core
14662 <source>
14663 *: "Show at the bottom"
14664 </source>
14665 <dest>
14666 *: "í•˜ë‹¨ì— í‘œì‹œ"
14667 </dest>
14668 <voice>
14669 *: "í•˜ë‹¨ì— í‘œì‹œ"
14670 </voice>
14671</phrase>
14672<phrase>
14673 id: LANG_SHOW_AT_THE_TOP
14674 desc: in the pictureflow settings
14675 user: core
14676 <source>
14677 *: "Show at the top"
14678 </source>
14679 <dest>
14680 *: "ìƒë‹¨ì— 표시"
14681 </dest>
14682 <voice>
14683 *: "ìƒë‹¨ì— 표시"
14684 </voice>
14685</phrase>
14686<phrase>
14687 id: LANG_DIRECT
14688 desc: in the pictureflow settings, also a volume adjustment mode
14689 user: core
14690 <source>
14691 *: "Direct"
14692 </source>
14693 <dest>
14694 *: "ì§ì ‘"
14695 </dest>
14696 <voice>
14697 *: "ì§ì ‘"
14698 </voice>
14699</phrase>
14700<phrase>
14701 id: LANG_VIA_TRACK_LIST
14702 desc: in the pictureflow settings
14703 user: core
14704 <source>
14705 *: "Via Track list"
14706 </source>
14707 <dest>
14708 *: "트랙 목ë¡ì„ 통해"
14709 </dest>
14710 <voice>
14711 *: "트랙 목ë¡ì„ 통해"
14712 </voice>
14713</phrase>
14714<phrase>
14715 id: LANG_ALWAYS_ON
14716 desc: in the pictureflow settings menu
14717 user: core
14718 <source>
14719 *: "Always On"
14720 </source>
14721 <dest>
14722 *: "í•­ìƒ ì¼œì ¸ 있ìŒ"
14723 </dest>
14724 <voice>
14725 *: "í•­ìƒ ì¼œì ¸ 있ìŒ"
14726 </voice>
14727</phrase>
14728<phrase>
14729 id: LANG_NO_ALBUMART_FOUND
14730 desc: in the pictureflow splash messages
14731 user: core
14732 <source>
14733 *: "No album art found"
14734 </source>
14735 <dest>
14736 *: "앨범 아트를 ì°¾ì„ ìˆ˜ ì—†ìŒ"
14737 </dest>
14738 <voice>
14739 *: "앨범 아트를 ì°¾ì„ ìˆ˜ ì—†ìŒ"
14740 </voice>
14741</phrase>
14742<phrase>
14743 id: LANG_CACHE_REBUILT_NEXT_RESTART
14744 desc: in the pictureflow splash messages
14745 user: core
14746 <source>
14747 *: "Cache will be rebuilt on next restart"
14748 </source>
14749 <dest>
14750 *: "다ìŒì— 다시 시작할 ë•Œ ìºì‹œê°€ 다시 작성ë¨"
14751 </dest>
14752 <voice>
14753 *: "다ìŒì— 다시 시작할 ë•Œ ìºì‹œê°€ 다시 작성ë¨"
14754 </voice>
14755</phrase>
14756<phrase>
14757 id: LANG_ERROR_WRITING_CONFIG
14758 desc: in the pictureflow splash messages
14759 user: core
14760 <source>
14761 *: "Error writing config"
14762 </source>
14763 <dest>
14764 *: "êµ¬ì„±ì„ ì“°ëŠ” 중 오류 ë°œìƒ"
14765 </dest>
14766 <voice>
14767 *: "êµ¬ì„±ì„ ì“°ëŠ” 중 오류 ë°œìƒ"
14768 </voice>
14769</phrase>
14770<phrase>
14771 id: LANG_NOT_A_VBR_FILE
14772 desc: in vbrfix plugin
14773 user: core
14774 <source>
14775 *: "Not a VBR file"
14776 </source>
14777 <dest>
14778 *: "VBR 파ì¼ì´ 아님"
14779 </dest>
14780 <voice>
14781 *: "VBR 파ì¼ì´ 아님"
14782 </voice>
14783</phrase>
14784<phrase>
14785 id: LANG_FILE_ERROR
14786 desc: in vbrfix plugin
14787 user: core
14788 <source>
14789 *: "File error: %d"
14790 </source>
14791 <dest>
14792 *: "íŒŒì¼ ì˜¤ë¥˜: %d"
14793 </dest>
14794 <voice>
14795 *: "íŒŒì¼ ì˜¤ë¥˜"
14796 </voice>
14797</phrase>
14798<phrase>
14799 id: LANG_UPDATE_CACHE
14800 desc: in pictureflow
14801 user: core
14802 <source>
14803 *: "Update cache"
14804 </source>
14805 <dest>
14806 *: "ì—…ë°ì´íŠ¸ ìºì‹œ"
14807 </dest>
14808 <voice>
14809 *: "ì—…ë°ì´íŠ¸ ìºì‹œ"
14810 </voice>
14811</phrase>
14812<phrase>
14813 id: LANG_HIDE_ALBUM_TITLE_NEW
14814 desc: in the pictureflow settings
14815 user: core
14816 <source>
14817 *: "Hide information"
14818 </source>
14819 <dest>
14820 *: "정보 숨기기"
14821 </dest>
14822 <voice>
14823 *: "정보 숨기기"
14824 </voice>
14825</phrase>
14826<phrase>
14827 id: LANG_SHOW_AT_THE_BOTTOM_NEW
14828 desc: in the pictureflow settings
14829 user: core
14830 <source>
14831 *: "Show album at the bottom"
14832 </source>
14833 <dest>
14834 *: "í•˜ë‹¨ì— ì•¨ë²” 표시"
14835 </dest>
14836 <voice>
14837 *: "í•˜ë‹¨ì— ì•¨ë²” 표시"
14838 </voice>
14839</phrase>
14840<phrase>
14841 id: LANG_SHOW_AT_THE_TOP_NEW
14842 desc: in the pictureflow settings
14843 user: core
14844 <source>
14845 *: "Show album at the top"
14846 </source>
14847 <dest>
14848 *: "ìƒë‹¨ì— 앨범 표시"
14849 </dest>
14850 <voice>
14851 *: "ìƒë‹¨ì— 앨범 표시"
14852 </voice>
14853</phrase>
14854<phrase>
14855 id: LANG_SHOW_ALL_AT_THE_TOP
14856 desc: in the pictureflow settings
14857 user: core
14858 <source>
14859 *: "Show album and artist at the top"
14860 </source>
14861 <dest>
14862 *: "앨범 ë° ì•„í‹°ìŠ¤íŠ¸ë¥¼ ìƒë‹¨ì— 표시"
14863 </dest>
14864 <voice>
14865 *: "앨범 ë° ì•„í‹°ìŠ¤íŠ¸ë¥¼ ìƒë‹¨ì— 표시"
14866 </voice>
14867</phrase>
14868<phrase>
14869 id: LANG_SHOW_ALL_AT_THE_BOTTOM
14870 desc: in the pictureflow settings
14871 user: core
14872 <source>
14873 *: "Show album and artist at the bottom"
14874 </source>
14875 <dest>
14876 *: "앨범 ë° ì•„í‹°ìŠ¤íŠ¸ë¥¼ í•˜ë‹¨ì— í‘œì‹œ"
14877 </dest>
14878 <voice>
14879 *: "앨범 ë° ì•„í‹°ìŠ¤íŠ¸ë¥¼ í•˜ë‹¨ì— í‘œì‹œ"
14880 </voice>
14881</phrase>
14882<phrase>
14883 id: LANG_ACTION_STD_CANCEL
14884 desc: standard press x to cancel string
14885 user: core
14886 <source>
14887 *: "Press LEFT to cancel."
14888 android,hifietma*,zenvision: "Press BACK to cancel."
14889 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
14890 ihifi760,ihifi960: "Double tap RETURN to cancel."
14891 ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
14892 iriverh10,samsungyh*: "Double tap LEFT to cancel."
14893 mpiohd200: "Double tap REC to cancel."
14894 mpiohd300: "Double tap MENU to cancel."
14895 rx27generic: "Press VOLUME to cancel."
14896 sonynwza860: "Keymaps incomplete."
14897 touchscreen: "Press Middle Left to cancel."
14898 vibe500: "Press PREV to cancel."
14899 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
14900 </source>
14901 <dest>
14902 *: "취소하려면 왼쪽 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14903 android,hifietma*,zenvision: "취소하려면 뒤로가기 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14904 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "취소하려면 ì „ì› ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14905 ihifi760,ihifi960: "취소하려면 리턴 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14906 ihifi770,ihifi770c,ihifi800: "취소하려면 홈 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14907 iriverh10,samsungyh*: "취소하려면 왼쪽 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14908 mpiohd200: "취소하려면 녹화 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14909 mpiohd300: "취소하려면 메뉴 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14910 rx27generic: "취소하려면 볼륨 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14911 sonynwza860: "í‚¤ë§µì´ ë¶ˆì™„ì „í•©ë‹ˆë‹¤."
14912 touchscreen: "취소하려면 왼쪽 ê°€ìš´ë° ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14913 vibe500: "취소하려면 ì´ì „ ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14914 xduoox20,xduoox3,xduoox3ii: "취소하려면 홈 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14915 </dest>
14916 <voice>
14917 *: "취소하려면 왼쪽 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14918 android,hifietma*,zenvision: "취소하려면 뒤로가기 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14919 cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "취소하려면 ì „ì› ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14920 ihifi760,ihifi960: "취소하려면 리턴 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14921 ihifi770,ihifi770c,ihifi800: "취소하려면 홈 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14922 iriverh10,samsungyh*: "취소하려면 왼쪽 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14923 mpiohd200: "취소하려면 녹화 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14924 mpiohd300: "취소하려면 메뉴 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14925 rx27generic: "취소하려면 볼륨 ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14926 touchscreen: "취소하려면 왼쪽 ê°€ìš´ë° ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14927 vibe500: "취소하려면 ì´ì „ ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”."
14928 xduoox20,xduoox3,xduoox3ii: "취소하려면 홈 ë²„íŠ¼ì„ ë‘ ë²ˆ 탭하세요."
14929 </voice>
14930</phrase>
14931<phrase>
14932 id: LANG_DATE
14933 desc: for constructing time and date announcements
14934 user: core
14935 <source>
14936 *: "Date"
14937 </source>
14938 <dest>
14939 *: "날짜"
14940 </dest>
14941 <voice>
14942 *: "날짜"
14943 </voice>
14944</phrase>
14945<phrase>
14946 id: LANG_CLEAR_ALL
14947 desc:
14948 user: core
14949 <source>
14950 *: "Clear all"
14951 </source>
14952 <dest>
14953 *: "ëª¨ë‘ ì§€ìš°ê¸°"
14954 </dest>
14955 <voice>
14956 *: "ëª¨ë‘ ì§€ìš°ê¸°"
14957 </voice>
14958</phrase>
14959<phrase>
14960 id: LANG_CANCEL_0
14961 desc: CANCEL.
14962 user: core
14963 <source>
14964 *: "Cancel"
14965 </source>
14966 <dest>
14967 *: "취소"
14968 </dest>
14969 <voice>
14970 *: "취소"
14971 </voice>
14972</phrase>
14973<phrase>
14974 id: LANG_SAVE
14975 desc:
14976 user: core
14977 <source>
14978 *: "Save"
14979 </source>
14980 <dest>
14981 *: "저장"
14982 </dest>
14983 <voice>
14984 *: "저장"
14985 </voice>
14986</phrase>
14987<phrase>
14988 id: LANG_TIMEOUT
14989 desc:
14990 user: core
14991 <source>
14992 *: "Timeout"
14993 </source>
14994 <dest>
14995 *: "시간 초과"
14996 </dest>
14997 <voice>
14998 *: "시간 초과"
14999 </voice>
15000</phrase>
15001<phrase>
15002 id: LANG_TRACK
15003 desc: used in track x of y constructs
15004 user: core
15005 <source>
15006 *: "Track"
15007 </source>
15008 <dest>
15009 *: "트랙"
15010 </dest>
15011 <voice>
15012 *: "트랙"
15013 </voice>
15014</phrase>
15015<phrase>
15016 id: LANG_ELAPSED
15017 desc: prefix for elapsed playtime announcement
15018 user: core
15019 <source>
15020 *: "Elapsed"
15021 </source>
15022 <dest>
15023 *: "경과ë¨"
15024 </dest>
15025 <voice>
15026 *: "경과ë¨"
15027 </voice>
15028</phrase>
15029<phrase>
15030 id: LANG_ANNOUNCEMENT_FMT
15031 desc: format for wps hotkey announcement
9977 user: core 15032 user: core
9978 <source> 15033 <source>
9979 *: none 15034 *: none
9980 recording: "CLIP:" 15035 hotkey: "Announcement format"
9981 </source> 15036 </source>
9982 <dest> 15037 <dest>
9983 *: none 15038 *: none
9984 recording: "í´ë¦½:" 15039 hotkey: "발매 형ì‹"
9985 </dest> 15040 </dest>
9986 <voice> 15041 <voice>
9987 *: none 15042 *: none
9988 recording: "" 15043 hotkey: "발매 형ì‹"
9989 </voice> 15044 </voice>
9990</phrase> 15045</phrase>
9991<phrase> 15046<phrase>
9992 id: LANG_GAIN_RIGHT 15047 id: LANG_REMAIN
9993 desc: in the recording screen 15048 desc: for constructs such as number of tracks remaining etc
9994 user: core 15049 user: core
9995 <source> 15050 <source>
9996 *: none 15051 *: none
9997 recording: "Gain R" 15052 hotkey: "Remain"
9998 </source> 15053 </source>
9999 <dest> 15054 <dest>
10000 *: none 15055 *: none
10001 recording: "ê²Œì¸ R" 15056 hotkey: "남ìŒ"
10002 </dest> 15057 </dest>
10003 <voice> 15058 <voice>
10004 *: none 15059 *: none
10005 recording: "오른쪽 게ì¸" 15060 hotkey: "남ìŒ"
10006 </voice> 15061 </voice>
10007</phrase> 15062</phrase>
10008<phrase> 15063<phrase>
10009 id: LANG_RECORDING_FILENAME 15064 id: LANG_GROUPING
10010 desc: Filename header in recording screen 15065 desc:
10011 user: core 15066 user: core
10012 <source> 15067 <source>
10013 *: none 15068 *: none
10014 recording: "Filename:" 15069 hotkey: "Grouping"
10015 </source> 15070 </source>
10016 <dest> 15071 <dest>
10017 *: none 15072 *: none
10018 recording: "파ì¼ëª…:" 15073 hotkey: "그룹화"
10019 </dest> 15074 </dest>
10020 <voice> 15075 <voice>
10021 *: none 15076 *: none
10022 recording: "" 15077 hotkey: "그룹화"
10023 </voice> 15078 </voice>
10024</phrase> 15079</phrase>
10025<phrase> 15080<phrase>
10026 id: LANG_HEADPHONE_UNPLUG_RW 15081 id: LANG_ANNOUNCE_ON
10027 desc: in pause_phones_menu. 15082 desc:
10028 user: core 15083 user: core
10029 <source> 15084 <source>
10030 *: none 15085 *: none
10031 headphone_detection: "Duration to Rewind" 15086 hotkey: "Announce on"
10032 </source> 15087 </source>
10033 <dest> 15088 <dest>
10034 *: none 15089 *: none
10035 headphone_detection: "ë˜ê°ê¸° 시간" 15090 hotkey: "발매ì¼"
10036 </dest> 15091 </dest>
10037 <voice> 15092 <voice>
10038 *: none 15093 *: none
10039 headphone_detection: "ë˜ê°ê¸° 시간" 15094 hotkey: "발매ì¼"
10040 </voice> 15095 </voice>
10041</phrase> 15096</phrase>
10042<phrase> 15097<phrase>
10043 id: LANG_PITCH_UP_SEMITONE 15098 id: LANG_TRACK_CHANGE
10044 desc: in wps 15099 desc:
10045 user: core 15100 user: core
10046 <source> 15101 <source>
10047 *: none 15102 *: none
10048 pitchscreen: "Semitone Up" 15103 hotkey: "Track change"
10049 </source> 15104 </source>
10050 <dest> 15105 <dest>
10051 *: none 15106 *: none
10052 pitchscreen: "ë°˜ìŒ ì˜¬ë¦¼" 15107 hotkey: "트랙 변경"
10053 </dest> 15108 </dest>
10054 <voice> 15109 <voice>
10055 *: none 15110 *: none
10056 pitchscreen: "" 15111 hotkey: "트랙 변경"
10057 </voice> 15112 </voice>
10058</phrase> 15113</phrase>
10059<phrase> 15114<phrase>
10060 id: VOICE_BOOKMARK_SELECT_INDEX_TEXT 15115 id: LANG_HOLD_FOR_SETTINGS
10061 desc: voice only, used in the bookmark list to label index number 15116 desc:
10062 user: core 15117 user: core
10063 <source> 15118 <source>
10064 *: "" 15119 *: none
15120 hotkey: "Hold for settings"
10065 </source> 15121 </source>
10066 <dest> 15122 <dest>
10067 *: "" 15123 *: none
15124 hotkey: "ì„¤ì •ì„ ìœ„í•´ 유지"
10068 </dest> 15125 </dest>
10069 <voice> 15126 <voice>
10070 *: "ì¸ë±ìŠ¤" 15127 *: none
15128 hotkey: "ì„¤ì •ì„ ìœ„í•´ 유지"
10071 </voice> 15129 </voice>
10072</phrase> 15130</phrase>
10073<phrase> 15131<phrase>
10074 id: LANG_SLOW 15132 id: LANG_OPEN_PLUGIN
10075 desc: in settings_menu 15133 desc: onplay open plugin
10076 user: core 15134 user: core
10077 <source> 15135 <source>
10078 *: "Slow" 15136 *: "Open Plugin"
10079 </source> 15137 </source>
10080 <dest> 15138 <dest>
10081 *: "ëŠë¦¬ê²Œ" 15139 *: "í”ŒëŸ¬ê·¸ì¸ ì—´ê¸°"
10082 </dest> 15140 </dest>
10083 <voice> 15141 <voice>
10084 *: "ëŠë¦¬ê²Œ" 15142 *: "í”ŒëŸ¬ê·¸ì¸ ì—´ê¸°"
10085 </voice> 15143 </voice>
10086</phrase> 15144</phrase>
10087<phrase> 15145<phrase>
10088 id: LANG_INVERT_CURSOR_GRADIENT 15146 id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
10089 desc: in settings_menu 15147 desc: open plugin module
10090 user: core 15148 user: core
10091 <source> 15149 <source>
10092 *: none 15150 *: "Not a plugin: %s"
10093 lcd_color: "Bar (Gradient Colour)"
10094 </source> 15151 </source>
10095 <dest> 15152 <dest>
10096 *: none 15153 *: "í”ŒëŸ¬ê·¸ì¸ ì•„ë‹˜: %s"
10097 lcd_color: "ë°” (ê·¸ë¼ë””언트 색깔)"
10098 </dest> 15154 </dest>
10099 <voice> 15155 <voice>
10100 *: none 15156 *: "í”ŒëŸ¬ê·¸ì¸ ì•„ë‹™"
10101 lcd_color: "ë°” (ê·¸ë¼ë””언트 색깔)"
10102 </voice> 15157 </voice>
10103</phrase> 15158</phrase>
10104<phrase> 15159<phrase>
10105 id: LANG_GAIN_LEFT 15160 id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
10106 desc: in the recording screen 15161 desc: open plugin module
10107 user: core 15162 user: core
10108 <source> 15163 <source>
10109 *: none 15164 *: "Set Wps Context Plugin"
10110 recording: "Gain L"
10111 </source> 15165 </source>
10112 <dest> 15166 <dest>
10113 *: none 15167 *: "Wps 컨í…스트 í”ŒëŸ¬ê·¸ì¸ ì„¤ì •"
10114 recording: "ê²Œì¸ L"
10115 </dest> 15168 </dest>
10116 <voice> 15169 <voice>
10117 *: none 15170 *: "Wps 컨í…스트 í”ŒëŸ¬ê·¸ì¸ ì„¤ì •"
10118 recording: "왼쪽 게ì¸"
10119 </voice> 15171 </voice>
10120</phrase> 15172</phrase>
10121<phrase> 15173<phrase>
10122 id: LANG_ID3_DISCNUM 15174 id: LANG_PARAMETER
10123 desc: in tag viewer 15175 desc:
10124 user: core 15176 user: core
10125 <source> 15177 <source>
10126 *: "Discnum" 15178 *: "Parameter"
10127 </source> 15179 </source>
10128 <dest> 15180 <dest>
10129 *: "ë””ìŠ¤í¬ ë²ˆí˜¸" 15181 *: "매개변수"
10130 </dest> 15182 </dest>
10131 <voice> 15183 <voice>
10132 *: "ë””ìŠ¤í¬ ë²ˆí˜¸" 15184 *: "매개변수"
10133 </voice> 15185 </voice>
10134</phrase> 15186</phrase>
10135<phrase> 15187<phrase>
10136 id: LANG_SHOW_FILENAME_EXT 15188 id: LANG_NAME
10137 desc: in settings_menu 15189 desc:
10138 user: core 15190 user: core
10139 <source> 15191 <source>
10140 *: "Show Filename Extensions" 15192 *: "Name"
10141 </source> 15193 </source>
10142 <dest> 15194 <dest>
10143 *: "íŒŒì¼ í™•ìž¥ìž í‘œì‹œ" 15195 *: "ì´ë¦„"
10144 </dest> 15196 </dest>
10145 <voice> 15197 <voice>
10146 *: "íŒŒì¼ í™•ìž¥ìž í‘œì‹œ" 15198 *: "ì´ë¦„e"
10147 </voice> 15199 </voice>
10148</phrase> 15200</phrase>
10149<phrase> 15201<phrase>
10150 id: VOICE_QUICKSCREEN 15202 id: LANG_ADD
10151 desc: spoken only, Announces entering the "quick screen" 15203 desc:
10152 user: core 15204 user: core
10153 <source> 15205 <source>
10154 *: "" 15206 *: "Add"
10155 </source> 15207 </source>
10156 <dest> 15208 <dest>
10157 *: "" 15209 *: "추가"
10158 </dest> 15210 </dest>
10159 <voice> 15211 <voice>
10160 *: "퀵스í¬ë¦°" 15212 *: "추ê°"
10161 </voice> 15213 </voice>
10162</phrase> 15214</phrase>
10163<phrase> 15215<phrase>
10164 id: LANG_VERY_FAST 15216 id: LANG_BACK
10165 desc: in settings_menu 15217 desc:
10166 user: core 15218 user: core
10167 <source> 15219 <source>
10168 *: "Very fast" 15220 *: "Back"
10169 </source> 15221 </source>
10170 <dest> 15222 <dest>
10171 *: "매우 빠름" 15223 *: "ë¤ë¡œê°€ê¸°"
10172 </dest> 15224 </dest>
10173 <voice> 15225 <voice>
10174 *: "매우 빠름" 15226 *: "ë¤ë¡œê°€ê¸°"
10175 </voice> 15227 </voice>
10176</phrase> 15228</phrase>
10177<phrase> 15229<phrase>
10178 id: LANG_INVERT_CURSOR_COLOR 15230 id: LANG_EDIT
10179 desc: in settings_menu 15231 desc:
10180 user: core 15232 user: core
10181 <source> 15233 <source>
10182 *: none 15234 *: "Edit"
10183 lcd_color: "Bar (Solid Colour)"
10184 </source> 15235 </source>
10185 <dest> 15236 <dest>
10186 *: none 15237 *: "편집"
10187 lcd_color: "바 (고정 색깔)"
10188 </dest> 15238 </dest>
10189 <voice> 15239 <voice>
10190 *: none 15240 *: "편집"
10191 lcd_color: "바 (고정 색깔)"
10192 </voice> 15241 </voice>
10193</phrase> 15242</phrase>
10194<phrase> 15243<phrase>
10195 id: LANG_BOOKMARK_DONT_RESUME 15244 id: LANG_RUN
10196 desc: top item in the list when asking user about bookmark auto load 15245 desc:
10197 user: core 15246 user: core
10198 <source> 15247 <source>
10199 *: "<Don't Resume>" 15248 *: "Run"
10200 </source> 15249 </source>
10201 <dest> 15250 <dest>
10202 *: "<ì´ì–´ì„œ 재ìƒí•˜ì§€ ì•ŠìŒ>" 15251 *: "실행"
10203 </dest> 15252 </dest>
10204 <voice> 15253 <voice>
10205 *: "ì´ì–´ì„œ 재ìƒí•˜ì§€ ì•ŠìŒ" 15254 *: "실행"
10206 </voice> 15255 </voice>
10207</phrase> 15256</phrase>
10208<phrase> 15257<phrase>
10209 id: LANG_BOOKMARK_CONTEXT_DELETE 15258 id: LANG_EXPORT
10210 desc: bookmark context menu, delete this bookmark 15259 desc:
10211 user: core 15260 user: core
10212 <source> 15261 <source>
10213 *: "Delete" 15262 *: "Export"
10214 </source> 15263 </source>
10215 <dest> 15264 <dest>
10216 *: "지우기" 15265 *: "내보내기"
10217 </dest> 15266 </dest>
10218 <voice> 15267 <voice>
10219 *: "지우기" 15268 *: "내보내기"
10220 </voice> 15269 </voice>
10221</phrase> 15270</phrase>
10222<phrase> 15271<phrase>
10223 id: VOICE_OCLOCK 15272 id: LANG_BROWSE
10224 desc: spoken only, for wall clock announce 15273 desc:
10225 user: core 15274 user: core
10226 <source> 15275 <source>
10227 *: "" 15276 *: "Browse"
10228 </source> 15277 </source>
10229 <dest> 15278 <dest>
10230 *: "" 15279 *: "찾아보기"
10231 </dest> 15280 </dest>
10232 <voice> 15281 <voice>
10233 *: "" 15282 *: "찾아보기"
10234 </voice> 15283 </voice>
10235</phrase> 15284</phrase>
10236<phrase> 15285<phrase>
10237 id: LANG_BOOKMARK_SELECT_BOOKMARK 15286 id: LANG_ENTER_USB_STORAGE_MODE_QUERY
10238 desc: bookmark selection list title 15287 desc: upon plugging in USB
10239 user: core 15288 user: core
10240 <source> 15289 <source>
10241 *: "Select Bookmark" 15290 *: "Enter USB mass storage mode?"
10242 </source> 15291 </source>
10243 <dest> 15292 <dest>
10244 *: "ëë§ˆí¬ ì íƒ" 15293 *: "USB 대용량 저장 모드로 진입할까요?"
10245 </dest> 15294 </dest>
10246 <voice> 15295 <voice>
10247 *: "ëë§ˆí¬ ì íƒ" 15296 *: "USB 대용량 저장 모드로 진입할까요?"
10248 </voice> 15297 </voice>
10249</phrase> 15298</phrase>
10250<phrase> 15299<phrase>
10251 id: LANG_PLUGIN_APPS 15300 id: LANG_QUEUE_MENU
10252 desc: in the main menu 15301 desc: in onplay menu
10253 user: core 15302 user: core
10254 <source> 15303 <source>
10255 *: "Applications" 15304 *: "Queue..."
10256 </source> 15305 </source>
10257 <dest> 15306 <dest>
10258 *: "어플리케ì´ì…˜" 15307 *: "ëŒê¸°ì´..."
10259 </dest> 15308 </dest>
10260 <voice> 15309 <voice>
10261 *: "어플리케ì´ì…˜" 15310 *: "ëŒê¸°ì´"
10262 </voice> 15311 </voice>
10263</phrase> 15312</phrase>
10264<phrase> 15313<phrase>
10265 id: LANG_LISTACCEL_START_DELAY 15314 id: LANG_SHOW_QUEUE_OPTIONS
10266 desc: Delay before list starts accelerating 15315 desc: in Current Playlist settings
10267 user: core 15316 user: core
10268 <source> 15317 <source>
10269 *: "List Acceleration Start Delay" 15318 *: "Show Queue Options"
10270 wheel_acceleration: none
10271 </source> 15319 </source>
10272 <dest> 15320 <dest>
10273 *: "ëª©ë¡ ê°€ì† ì‹œìž‘ 지연시간" 15321 *: "대기열 옵션 표시"
10274 wheel_acceleration: none
10275 </dest> 15322 </dest>
10276 <voice> 15323 <voice>
10277 *: "ëª©ë¡ ê°€ì† ì‹œìž‘ 지연시간" 15324 *: "대기열 옵션 표시"
10278 wheel_acceleration: none
10279 </voice> 15325 </voice>
10280</phrase> 15326</phrase>
10281<phrase> 15327<phrase>
10282 id: LANG_VOICE_DIR_TALK 15328 id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
10283 desc: Item of voice menu, whether to use directory .talk clips 15329 desc: in Current Playlist settings
10284 user: core 15330 user: core
10285 <source> 15331 <source>
10286 *: "Use Directory .talk Clips" 15332 *: "Show Shuffled Adding Options"
10287 </source> 15333 </source>
10288 <dest> 15334 <dest>
10289 *: "ë”렉토리 .talk Clip 사용" 15335 *: "셔플 추가 옵션 표시"
10290 </dest> 15336 </dest>
10291 <voice> 15337 <voice>
10292 *: "ë”렉토리 talk Clip 사용" 15338 *: "셔플 추가 옵션 표시"
10293 </voice> 15339 </voice>
10294</phrase> 15340</phrase>
10295<phrase> 15341<phrase>
10296 id: VOICE_EXT_CUESHEET 15342 id: LANG_IN_SUBMENU
10297 desc: 15343 desc: in Settings
15344 user: core
15345 <source>
15346 *: "In Submenu"
15347 </source>
15348 <dest>
15349 *: "하위 메뉴"
15350 </dest>
15351 <voice>
15352 *: "하위 메뉴"
15353 </voice>
15354</phrase>
15355<phrase>
15356 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
15357 desc: disable all softlock notifications
15358 user: core
15359 <source>
15360 *: "Disable All Lock Notifications"
15361 </source>
15362 <dest>
15363 *: "모든 잠금 알림 비활성화"
15364 </dest>
15365 <voice>
15366 *: "모든 잠금 알림 비활성화"
15367 </voice>
15368</phrase>
15369<phrase>
15370 id: LANG_ACTION_VOLUME
15371 desc: exempt volume from softlock
15372 user: core
15373 <source>
15374 *: "Exempt Volume"
15375 </source>
15376 <dest>
15377 *: "예외 볼륨"
15378 </dest>
15379 <voice>
15380 *: "예외 볼륨"
15381 </voice>
15382</phrase>
15383<phrase>
15384 id: LANG_ACTION_ALWAYSAUTOLOCK
15385 desc: always prime autolock
15386 user: core
15387 <source>
15388 *: "Always Autolock"
15389 </source>
15390 <dest>
15391 *: "í•­ìƒ ìžë™ 잠금"
15392 </dest>
15393 <voice>
15394 *: "í•­ìƒ ìžë™ 잠금"
15395 </voice>
15396</phrase>
15397<phrase>
15398 id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
15399 desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
10298 user: core 15400 user: core
10299 <source> 15401 <source>
10300 *: "" 15402 *: ""
@@ -10307,309 +15409,981 @@
10307 </voice> 15409 </voice>
10308</phrase> 15410</phrase>
10309<phrase> 15411<phrase>
10310 id: LANG_BOOKMARK_SHUFFLE 15412 id: LANG_VOICED_DATE_FORMAT
10311 desc: bookmark selection list, bookmark enables shuffle 15413 desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
10312 user: core 15414 user: core
10313 <source> 15415 <source>
10314 *: ", Shuffle" 15416 *: "dAY"
10315 </source> 15417 </source>
10316 <dest> 15418 <dest>
10317 *: ", 무작위" 15419 *: "dAY"
10318 </dest> 15420 </dest>
10319 <voice> 15421 <voice>
10320 *: "" 15422 *: ""
10321 </voice> 15423 </voice>
10322</phrase> 15424</phrase>
10323<phrase> 15425<phrase>
10324 id: LANG_COLORS_MENU 15426 id: LANG_LIST_WRAPAROUND
10325 desc: colours menu under theme settings 15427 desc: in Settings
15428 user: core
15429 <source>
15430 *: "List Wraparound"
15431 </source>
15432 <dest>
15433 *: "ëª©ë¡ ëž©ì–´ë¼ìš´ë“œ"
15434 </dest>
15435 <voice>
15436 *: "ëª©ë¡ ëž©ì–´ë¼ìš´ë“œ"
15437 </voice>
15438</phrase>
15439<phrase>
15440 id: LANG_SHOW_SHUTDOWN_MESSAGE
15441 desc: in Settings
15442 user: core
15443 <source>
15444 *: "Show Shutdown Message"
15445 </source>
15446 <dest>
15447 *: "종료 메시지 표시"
15448 </dest>
15449 <voice>
15450 *: "종료 메시지 표시"
15451 </voice>
15452</phrase>
15453<phrase>
15454 id: LANG_LIST_ORDER
15455 desc: in Settings
15456 user: core
15457 <source>
15458 *: "List Order"
15459 </source>
15460 <dest>
15461 *: "ëª©ë¡ ìˆœì„œ"
15462 </dest>
15463 <voice>
15464 *: "ëª©ë¡ ìˆœì„œ"
15465 </voice>
15466</phrase>
15467<phrase>
15468 id: LANG_ASCENDING
15469 desc: in Settings
15470 user: core
15471 <source>
15472 *: "Ascending"
15473 </source>
15474 <dest>
15475 *: "오름차순"
15476 </dest>
15477 <voice>
15478 *: "오름차순"
15479 </voice>
15480</phrase>
15481<phrase>
15482 id: LANG_DESCENDING
15483 desc: in Settings
15484 user: core
15485 <source>
15486 *: "Descending"
15487 </source>
15488 <dest>
15489 *: "내림차순"
15490 </dest>
15491 <voice>
15492 *: "내림차순"
15493 </voice>
15494</phrase>
15495<phrase>
15496 id: LANG_ALBUM_ART
15497 desc: in Settings
15498 user: core
15499 <source>
15500 *: "Album Art"
15501 </source>
15502 <dest>
15503 *: "앨범 아트"
15504 </dest>
15505 <voice>
15506 *: "앨범 아트"
15507 </voice>
15508</phrase>
15509<phrase>
15510 id: LANG_PREFER_EMBEDDED
15511 desc: in Settings
15512 user: core
15513 <source>
15514 *: "Prefer Embedded"
15515 </source>
15516 <dest>
15517 *: "임베디드 선호"
15518 </dest>
15519 <voice>
15520 *: "임베디드 선호"
15521 </voice>
15522</phrase>
15523<phrase>
15524 id: LANG_PREFER_IMAGE_FILE
15525 desc: in Settings
15526 user: core
15527 <source>
15528 *: "Prefer Image File"
15529 </source>
15530 <dest>
15531 *: "ì´ë¯¸ì§€ íŒŒì¼ ì„ í˜¸"
15532 </dest>
15533 <voice>
15534 *: "ì´ë¯¸ì§€ íŒŒì¼ ì„ í˜¸"
15535 </voice>
15536</phrase>
15537<phrase>
15538 id: LANG_FM_SYNC_RDS_TIME
15539 desc: in radio screen and Settings
10326 user: core 15540 user: core
10327 <source> 15541 <source>
10328 *: none 15542 *: none
10329 lcd_color: "Colours" 15543 rds: "Sync RDS Time"
10330 </source> 15544 </source>
10331 <dest> 15545 <dest>
10332 *: none 15546 *: none
10333 lcd_color: "색깔" 15547 rds: "RDS 시간 ë™ê¸°í™”"
10334 </dest> 15548 </dest>
10335 <voice> 15549 <voice>
10336 *: none 15550 *: none
10337 lcd_color: "색깔" 15551 rds: "RDS 시간 ë™ê¸°í™”"
10338 </voice> 15552 </voice>
10339</phrase> 15553</phrase>
10340<phrase> 15554<phrase>
10341 id: LANG_BOOKMARK_INVALID 15555 id: LANG_SORT_ALBUMS_BY
10342 desc: bookmark selection list, bookmark couldn't be parsed 15556 desc: in Settings
10343 user: core 15557 user: core
10344 <source> 15558 <source>
10345 *: "<Invalid Bookmark>" 15559 *: "Sort albums by"
10346 </source> 15560 </source>
10347 <dest> 15561 <dest>
10348 *: "<ìž˜ëª»ëœ ë¶ë§ˆí¬>" 15562 *: "앨범 ì •ë ¬ 기준"
10349 </dest> 15563 </dest>
10350 <voice> 15564 <voice>
10351 *: "ìž˜ëª»ëœ ë¶ë§ˆí¬" 15565 *: "앨범 ì •ë ¬ 기준"
10352 </voice> 15566 </voice>
10353</phrase> 15567</phrase>
10354<phrase> 15568<phrase>
10355 id: LANG_SEARCH_RESULTS 15569 id: LANG_ARTIST_PLUS_NAME
10356 desc: title for the list of results displayed after searching in a playlist 15570 desc: in Settings
10357 user: core 15571 user: core
10358 <source> 15572 <source>
10359 *: "Search Results" 15573 *: "Artist + Name"
10360 </source> 15574 </source>
10361 <dest> 15575 <dest>
10362 *: "검색 ê²°ê³¼" 15576 *: "아티스트 + ì´ë¦„"
10363 </dest> 15577 </dest>
10364 <voice> 15578 <voice>
10365 *: "검색 ê²°ê³¼" 15579 *: "아티스트와 ì´ë¦„"
10366 </voice> 15580 </voice>
10367</phrase> 15581</phrase>
10368<phrase> 15582<phrase>
10369 id: LANG_RECORDING_AGC_PRESET 15583 id: LANG_ARTIST_PLUS_YEAR
10370 desc: automatic gain control in record settings and screen 15584 desc: in Settings
15585 user: core
15586 <source>
15587 *: "Artist + Year"
15588 </source>
15589 <dest>
15590 *: "아티스트 + ì—°ë„"
15591 </dest>
15592 <voice>
15593 *: "아티스트와 ì—°ë„"
15594 </voice>
15595</phrase>
15596<phrase>
15597 id: LANG_YEAR_SORT_ORDER
15598 desc: in Settings
15599 user: core
15600 <source>
15601 *: "Year sort order"
15602 </source>
15603 <dest>
15604 *: "ì—°ë„ ì •ë ¬ 순서"
15605 </dest>
15606 <voice>
15607 *: "ì—°ë„ ì •ë ¬ 순서"
15608 </voice>
15609</phrase>
15610<phrase>
15611 id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
15612 desc: in Settings
15613 user: core
15614 <source>
15615 *: "Show year in album title"
15616 </source>
15617 <dest>
15618 *: "앨범 ì œëª©ì— ì—°ë„ í‘œì‹œ"
15619 </dest>
15620 <voice>
15621 *: "앨범 ì œëª©ì— ì—°ë„ í‘œì‹œ"
15622 </voice>
15623</phrase>
15624<phrase>
15625 id: LANG_WAIT_FOR_CACHE
15626 desc: in Settings
15627 user: core
15628 <source>
15629 *: "Cache needs to finish updating first!"
15630 </source>
15631 <dest>
15632 *: "먼저 ìºì‹œ ì—…ë°ì´íŠ¸ë¥¼ 완료해야 합니다!"
15633 </dest>
15634 <voice>
15635 *: "먼저 ìºì‹œ ì—…ë°ì´íŠ¸ë¥¼ 완료해야 합니다!"
15636 </voice>
15637</phrase>
15638<phrase>
15639 id: LANG_TRACK_INFO
15640 desc: Track Info Title
15641 user: core
15642 <source>
15643 *: "Track Info"
15644 </source>
15645 <dest>
15646 *: "트랙 정보"
15647 </dest>
15648 <voice>
15649 *: "트랙 정보"
15650 </voice>
15651</phrase>
15652<phrase>
15653 id: LANG_PLAY
15654 desc: play selected file/directory, in playlist context menu
15655 user: core
15656 <source>
15657 *: "Play"
15658 </source>
15659 <dest>
15660 *: "재ìƒ"
15661 </dest>
15662 <voice>
15663 *: "재ìƒ"
15664 </voice>
15665</phrase>
15666<phrase>
15667 id: LANG_PLAY_SHUFFLED
15668 desc: play selected files in shuffled order, in playlist context menu
15669 user: core
15670 <source>
15671 *: "Play Shuffled"
15672 </source>
15673 <dest>
15674 *: "셔플 재ìƒ"
15675 </dest>
15676 <voice>
15677 *: "셔플 재ìƒ"
15678 </voice>
15679</phrase>
15680<phrase>
15681 id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
15682 desc: used in the playlist settings menu
15683 user: core
15684 <source>
15685 *: "Keep Current Track When Replacing Playlist"
15686 </source>
15687 <dest>
15688 *: "재ìƒëª©ë¡ êµì²´ ì‹œ 현재 트랙 유지"
15689 </dest>
15690 <voice>
15691 *: "재ìƒëª©ë¡ êµì²´ ì‹œ 현재 트랙 유지"
15692 </voice>
15693</phrase>
15694<phrase>
15695 id: LANG_CLEAR_SETTINGS_ON_HOLD
15696 desc: in the system sub menu
10371 user: core 15697 user: core
10372 <source> 15698 <source>
10373 *: none 15699 *: none
10374 agc: "AGC" 15700 clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
15701 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
10375 </source> 15702 </source>
10376 <dest> 15703 <dest>
10377 *: none 15704 *: none
10378 agc: "AGC" 15705 clear_settings_on_hold,iriverh10: "시작하는 ë™ì•ˆ 재설정 ë²„íŠ¼ì„ ëˆ„ë¥´ê³  있으면 설정 지우기"
15706 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "시작하는 ë™ì•ˆ 홀드 스위치가 켜져 있으면 설정 지우기"
10379 </dest> 15707 </dest>
10380 <voice> 15708 <voice>
10381 *: none 15709 *: none
10382 agc: "ìžë™ ê²Œì¸ ì¡°ìž‘" 15710 clear_settings_on_hold,iriverh10: "시작하는 ë™ì•ˆ 재설정 ë²„íŠ¼ì„ ëˆ„ë¥´ê³  있으면 ì„¤ì •ì´ ì§€ìš°ê¸°"
15711 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "시작하는 ë™ì•ˆ 홀드 스위치가 켜져 있으면 설정 지우기"
10383 </voice> 15712 </voice>
10384</phrase> 15713</phrase>
10385<phrase> 15714<phrase>
10386 id: LANG_SELECTOR_END_COLOR 15715 id: LANG_REWIND_ACROSS_TRACKS
10387 desc: line selector color option 15716 desc: in playback settings menu
15717 user: core
15718 <source>
15719 *: "Rewind Across Tracks"
15720 </source>
15721 <dest>
15722 *: "íŠ¸ëž™ì„ ê°€ë¡œì§ˆëŸ¬ ë˜ê°ê¸°"
15723 </dest>
15724 <voice>
15725 *: "íŠ¸ëž™ì„ ê°€ë¡œì§ˆëŸ¬ ë˜ê°ê¸°"
15726 </voice>
15727</phrase>
15728<phrase>
15729 id: LANG_SET_AS
15730 desc: used in the onplay menu
15731 user: core
15732 <source>
15733 *: "Set As..."
15734 </source>
15735 <dest>
15736 *: "다른 ì´ë¦„으로 설정..."
15737 </dest>
15738 <voice>
15739 *: "다른 ì´ë¦„으로 설정"
15740 </voice>
15741</phrase>
15742<phrase>
15743 id: LANG_PLAYLIST_DIR
15744 desc: used in the onplay menu
15745 user: core
15746 <source>
15747 *: "Playlist Directory"
15748 </source>
15749 <dest>
15750 *: "재ìƒëª©ë¡ 디렉토리"
15751 </dest>
15752 <voice>
15753 *: "재ìƒëª©ë¡ 디렉토리"
15754 </voice>
15755</phrase>
15756<phrase>
15757 id: LANG_START_DIR
15758 desc: used in the onplay menu
15759 user: core
15760 <source>
15761 *: "Start Directory"
15762 </source>
15763 <dest>
15764 *: "시작 디렉토리"
15765 </dest>
15766 <voice>
15767 *: "시작 디렉토리"
15768 </voice>
15769</phrase>
15770<phrase>
15771 id: LANG_RECORDING_DIR
15772 desc: used in the onplay menu
10388 user: core 15773 user: core
10389 <source> 15774 <source>
10390 *: none 15775 *: none
10391 lcd_color: "Secondary Colour" 15776 recording: "Recording Directory"
10392 </source> 15777 </source>
10393 <dest> 15778 <dest>
10394 *: none 15779 *: none
10395 lcd_color: "ë‘번째 색깔" 15780 recording: "ë…¹ìŒ ë”렉토리"
10396 </dest> 15781 </dest>
10397 <voice> 15782 <voice>
10398 *: none 15783 *: none
10399 lcd_color: "ë‘번째 색깔" 15784 recording: "ë…¹ìŒ ë”렉토리"
10400 </voice> 15785 </voice>
10401</phrase> 15786</phrase>
10402<phrase> 15787<phrase>
10403 id: LANG_TREBLE_CUTOFF 15788 id: LANG_ADD_TO_PL
10404 desc: Treble setting cut-off frequency 15789 desc: used in the onplay menu
15790 user: core
15791 <source>
15792 *: "Add to Playlist..."
15793 </source>
15794 <dest>
15795 *: "재ìƒëª©ë¡ì— 추가..."
15796 </dest>
15797 <voice>
15798 *: "재ìƒëª©ë¡ì— 추가"
15799 </voice>
15800</phrase>
15801<phrase>
15802 id: LANG_ADD_TO_EXISTING_PL
15803 desc: used in the onplay menu
15804 user: core
15805 <source>
15806 *: "Add to Existing Playlist"
15807 </source>
15808 <dest>
15809 *: "기존 재ìƒëª©ë¡ì— 추가"
15810 </dest>
15811 <voice>
15812 *: "기존 재ìƒëª©ë¡ì— 추가"
15813 </voice>
15814</phrase>
15815<phrase>
15816 id: LANG_PLAYING_NEXT
15817 desc: used in the onplay menu
15818 user: core
15819 <source>
15820 *: "Playing Next..."
15821 </source>
15822 <dest>
15823 *: "ë‹¤ìŒ ìž¬ìƒ ì¤‘..."
15824 </dest>
15825 <voice>
15826 *: "ë‹¤ìŒ ìž¬ìƒ ì¤‘"
15827 </voice>
15828</phrase>
15829<phrase>
15830 id: LANG_PLAY_NEXT
15831 desc: used in the onplay menu
15832 user: core
15833 <source>
15834 *: "Play Next"
15835 </source>
15836 <dest>
15837 *: "ë‹¤ìŒ ìž¬ìƒ"
15838 </dest>
15839 <voice>
15840 *: "ë‹¤ìŒ ìž¬ìƒ"
15841 </voice>
15842</phrase>
15843<phrase>
15844 id: LANG_ADD_SHUFFLED
15845 desc: used in the onplay menu
15846 user: core
15847 <source>
15848 *: "Add Shuffled"
15849 </source>
15850 <dest>
15851 *: "셔플 추가"
15852 </dest>
15853 <voice>
15854 *: "셔플 추가"
15855 </voice>
15856</phrase>
15857<phrase>
15858 id: LANG_PLAY_LAST
15859 desc: used in the onplay menu
15860 user: core
15861 <source>
15862 *: "Play Last"
15863 </source>
15864 <dest>
15865 *: "마지막 재ìƒ"
15866 </dest>
15867 <voice>
15868 *: "마지막 재ìƒ"
15869 </voice>
15870</phrase>
15871<phrase>
15872 id: LANG_PLAY_LAST_SHUFFLED
15873 desc: used in the onplay menu
15874 user: core
15875 <source>
15876 *: "Play Last Shuffled"
15877 </source>
15878 <dest>
15879 *: "마지막 셔플 재ìƒ"
15880 </dest>
15881 <voice>
15882 *: "마지막 셔플 재ìƒ"
15883 </voice>
15884</phrase>
15885<phrase>
15886 id: LANG_VOLUME_ADJUST_MODE
15887 desc: in system settings
10405 user: core 15888 user: core
10406 <source> 15889 <source>
10407 *: none 15890 *: none
10408 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff" 15891 perceptual_volume: "Volume Adjustment Mode"
10409 </source> 15892 </source>
10410 <dest> 15893 <dest>
10411 *: none 15894 *: none
10412 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ê³ ìŒì—­ 컷오프 주파수" 15895 perceptual_volume: "볼륨 ì¡°ì • 모드"
10413 </dest> 15896 </dest>
10414 <voice> 15897 <voice>
10415 *: none 15898 *: none
10416 gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "ê³ ìŒì—­ 컷오프 주파수" 15899 perceptual_volume: "볼륨 ì¡°ì • 모드"
10417 </voice> 15900 </voice>
10418</phrase> 15901</phrase>
10419<phrase> 15902<phrase>
10420 id: LANG_RECORDING_MONO_MODE 15903 id: LANG_VOLUME_ADJUST_NORM_STEPS
10421 desc: in the recording settings 15904 desc: in system settings
10422 user: core 15905 user: core
10423 <source> 15906 <source>
10424 *: none 15907 *: none
10425 recording: "Mono mode" 15908 perceptual_volume: "Number of Volume Steps"
10426 </source> 15909 </source>
10427 <dest> 15910 <dest>
10428 *: none 15911 *: none
10429 recording: "모노 모드" 15912 perceptual_volume: "볼륨 ë¨ê³„ 수"
10430 </dest> 15913 </dest>
10431 <voice> 15914 <voice>
10432 *: none 15915 *: none
10433 recording: "모노 모드" 15916 perceptual_volume: "볼륨 ë¨ê³„ 수"
10434 </voice> 15917 </voice>
10435</phrase> 15918</phrase>
10436<phrase> 15919<phrase>
10437 id: LANG_SYSFONT_CHANNELS 15920 id: LANG_PERCEPTUAL
10438 desc: in the recording settings 15921 desc: in system settings -> volume adjustment mode
10439 user: core 15922 user: core
10440 <source> 15923 <source>
10441 *: none 15924 *: none
10442 recording: "Channels" 15925 perceptual_volume: "Perceptual"
10443 </source> 15926 </source>
10444 <dest> 15927 <dest>
10445 *: none 15928 *: none
10446 recording: "채ë„" 15929 perceptual_volume: "지ê°ì "
10447 </dest> 15930 </dest>
10448 <voice> 15931 <voice>
10449 *: none 15932 *: none
10450 recording: "채ë„" 15933 perceptual_volume: "지ê°ì "
10451 </voice> 15934 </voice>
10452</phrase> 15935</phrase>
10453<phrase> 15936<phrase>
10454 id: LANG_PLAYLISTS 15937 id: LANG_SHOW_TRACKS_WHILE_BROWSING
10455 desc: in the main menu and file view setting 15938 desc: in PictureFlow Main Menu
10456 user: core 15939 user: core
10457 <source> 15940 <source>
10458 *: "Playlists" 15941 *: "Show Tracks While Browsing"
10459 </source> 15942 </source>
10460 <dest> 15943 <dest>
10461 *: "ìž¬ìƒ ëª©ë¡" 15944 *: "íƒìƒ‰í•˜ëŠ” ë™ì•ˆ 트랙 표시"
10462 </dest> 15945 </dest>
10463 <voice> 15946 <voice>
10464 *: "ìž¬ìƒ ëª©ë¡" 15947 *: "íƒìƒ‰í•˜ëŠ” ë™ì•ˆ 트랙 표시"
10465 </voice> 15948 </voice>
10466</phrase> 15949</phrase>
10467<phrase> 15950<phrase>
10468 id: LANG_EXT_ONLY_VIEW_ALL 15951 id: LANG_GOTO_LAST_ALBUM
10469 desc: in settings_menu 15952 desc: in PictureFlow Main Menu
10470 user: core 15953 user: core
10471 <source> 15954 <source>
10472 *: "Only When Viewing All Types" 15955 *: "Go to Last Album"
10473 </source> 15956 </source>
10474 <dest> 15957 <dest>
10475 *: "모든 종류 볼때만 보여주기" 15958 *: "마지막 앨범으로 ì´ë™"
10476 </dest> 15959 </dest>
10477 <voice> 15960 <voice>
10478 *: "모든 종류 볼때만 보여주기" 15961 *: "마지막 앨범으로 ì´ë™"
10479 </voice> 15962 </voice>
10480</phrase> 15963</phrase>
10481<phrase> 15964<phrase>
10482 id: LANG_UNKNOWN_TYPES 15965 id: LANG_DATABASE_DIR
10483 desc: in settings_menu 15966 desc: in database settings menu
10484 user: core 15967 user: core
10485 <source> 15968 <source>
10486 *: "Only Unknown Types" 15969 *: "Database Directory"
10487 </source> 15970 </source>
10488 <dest> 15971 <dest>
10489 *: "알려지지 ì•Šì€ ì¢…ë¥˜ë§Œ" 15972 *: "ë°ì´í„°ë² ì´ìŠ¤ 디렉토리"
10490 </dest> 15973 </dest>
10491 <voice> 15974 <voice>
10492 *: "알려지지 ì•Šì€ ì¢…ë¥˜ë§Œ" 15975 *: "ë°ì´í„°ë² ì´ìŠ¤ 디렉토리"
10493 </voice> 15976 </voice>
10494</phrase> 15977</phrase>
10495<phrase> 15978<phrase>
10496 id: LANG_UNKNOWN 15979 id: LANG_REMOVE_QUEUED_TRACKS
10497 desc: generic string for unknown states, such as an unset clock 15980 desc: Confirmation dialog
10498 user: core 15981 user: core
10499 <source> 15982 <source>
10500 *: "Unknown" 15983 *: "Remove Queued Tracks?"
10501 </source> 15984 </source>
10502 <dest> 15985 <dest>
10503 *: "모름" 15986 *: "대기 ì¤‘ì¸ íŠ¸ëž™ì 제거할까요?"
10504 </dest> 15987 </dest>
10505 <voice> 15988 <voice>
10506 *: "모름" 15989 *: "대기 ì¤‘ì¸ íŠ¸ëž™ì 제거할까요?"
10507 </voice> 15990 </voice>
10508</phrase> 15991</phrase>
10509<phrase> 15992<phrase>
10510 id: LANG_KEYLOCK_ON 15993 id: LANG_QUICK_IGNORE_DIRACHE
10511 desc: displayed when key lock is on 15994 desc: in Settings
10512 user: core 15995 user: core
10513 <source> 15996 <source>
10514 *: "Buttons Locked" 15997 *: "Quick (Ignore Directory Cache)"
10515 </source> 15998 </source>
10516 <dest> 15999 <dest>
10517 *: "버튼 ìž ê¹€" 16000 *: "ë¹ ë¦ (디렉토리 ìºì‹œ 무시)"
10518 </dest> 16001 </dest>
10519 <voice> 16002 <voice>
10520 *: "" 16003 *: "빠름 (디렉토리 ìºì‹œ 무시)"
10521 </voice> 16004 </voice>
10522</phrase> 16005</phrase>
10523<phrase> 16006<phrase>
10524 id: LANG_RECORDING_AGC_CLIPTIME 16007 id: LANG_WPS
10525 desc: in record settings 16008 desc: in Settings
10526 user: core 16009 user: core
10527 <source> 16010 <source>
10528 *: none 16011 *: "What's Playing Screen"
10529 agc: "AGC clip time"
10530 </source> 16012 </source>
10531 <dest> 16013 <dest>
10532 *: none 16014 *: "ìž¬ìƒ ì¤‘ 화면"
10533 agc: "AGC í´ë¦½ 시간"
10534 </dest> 16015 </dest>
10535 <voice> 16016 <voice>
10536 *: none 16017 *: "ìž¬ìƒ ì¤‘ 화면"
10537 agc: "AGC í´ë¦½ 시간"
10538 </voice> 16018 </voice>
10539</phrase> 16019</phrase>
10540<phrase> 16020<phrase>
10541 id: LANG_SKIP_TRACK 16021 id: LANG_DEFAULT_BROWSER
10542 desc: skip length setting entry 0 16022 desc: in Settings
10543 user: core 16023 user: core
10544 <source> 16024 <source>
10545 *: "Skip Track" 16025 *: "Default Browser"
10546 </source> 16026 </source>
10547 <dest> 16027 <dest>
10548 *: "트랙 건너뛰기" 16028 *: "기본 íƒìƒ‰ê¸°"
10549 </dest> 16029 </dest>
10550 <voice> 16030 <voice>
10551 *: "트랙 건너뛰기" 16031 *: "기본 íƒìƒ‰ê¸°"
10552 </voice> 16032 </voice>
10553</phrase> 16033</phrase>
10554<phrase> 16034<phrase>
10555 id: VOICE_OK 16035 id: LANG_AMAZE_MENU
10556 desc: spoken only, On exiting a context, specifically the quick screen 16036 desc: Amaze game
10557 user: core 16037 user: core
10558 <source> 16038 <source>
10559 *: "" 16039 *: "Amaze Main Menu"
10560 </source> 16040 </source>
10561 <dest> 16041 <dest>
10562 *: "" 16042 *: "Amaze ë©”ì¸ ë©”ë‰´"
10563 </dest> 16043 </dest>
10564 <voice> 16044 <voice>
10565 *: "" 16045 *: "Amaze ë©”ì¸ ë©”ë‰´"
10566 </voice> 16046 </voice>
10567</phrase> 16047</phrase>
10568<phrase> 16048<phrase>
10569 id: LANG_SETTINGS 16049 id: LANG_SET_MAZE_SIZE
10570 desc: in main menu and visual confirmation after settings reset 16050 desc: Maze size in Amaze game
10571 user: core 16051 user: core
10572 <source> 16052 <source>
10573 *: "Settings" 16053 *: "Set Maze Size"
10574 </source> 16054 </source>
10575 <dest> 16055 <dest>
10576 *: "설정" 16056 *: "미로 í¬ê¸° 설정"
10577 </dest> 16057 </dest>
10578 <voice> 16058 <voice>
10579 *: "설정" 16059 *: "미로 í¬ê¸° 설정"
10580 </voice> 16060 </voice>
10581</phrase> 16061</phrase>
10582<phrase> 16062<phrase>
10583 id: LANG_KEYCLICK_REPEATS 16063 id: LANG_VIEW_MAP
10584 desc: in keyclick settings menu 16064 desc: Map in Amaze game
10585 user: core 16065 user: core
10586 <source> 16066 <source>
10587 *: "Keyclick Repeats" 16067 *: "View Map"
10588 </source> 16068 </source>
10589 <dest> 16069 <dest>
10590 *: "반복 키í´ë¦­" 16070 *: "지ë보기"
10591 </dest> 16071 </dest>
10592 <voice> 16072 <voice>
10593 *: "반복 키í´ë¦­" 16073 *: "지ë보기"
10594 </voice> 16074 </voice>
10595</phrase> 16075</phrase>
10596<phrase> 16076<phrase>
10597 id: LANG_CANCEL 16077 id: LANG_SHOW_COMPASS
10598 desc: Visual confirmation of canceling a changed setting 16078 desc: Compass in Amaze game
10599 user: core 16079 user: core
10600 <source> 16080 <source>
10601 *: "Cancelled" 16081 *: "Show Compass"
10602 </source> 16082 </source>
10603 <dest> 16083 <dest>
10604 *: "취소ë¨" 16084 *: "나침반 표시"
10605 </dest> 16085 </dest>
10606 <voice> 16086 <voice>
10607 *: "취소ë¨" 16087 *: "나침반 표시"
10608 </voice> 16088 </voice>
10609</phrase> 16089</phrase>
10610<phrase> 16090<phrase>
10611 id: VOICE_OF 16091 id: LANG_SHOW_MAP
10612 desc: spoken only, as in 3/8 => 3 of 8 16092 desc: Map in Amaze game
16093 user: core
16094 <source>
16095 *: "Show Map"
16096 </source>
16097 <dest>
16098 *: "ì§€ë„ í‘œì‹œ"
16099 </dest>
16100 <voice>
16101 *: "ì§€ë„ í‘œì‹œ"
16102 </voice>
16103</phrase>
16104<phrase>
16105 id: LANG_REMEMBER_PATH
16106 desc: Map in Amaze game
16107 user: core
16108 <source>
16109 *: "Remember Path"
16110 </source>
16111 <dest>
16112 *: "경로 기억"
16113 </dest>
16114 <voice>
16115 *: "경로 기억"
16116 </voice>
16117</phrase>
16118<phrase>
16119 id: LANG_USE_LARGE_TILES
16120 desc: Map in Amaze game
16121 user: core
16122 <source>
16123 *: "Use Large Tiles"
16124 </source>
16125 <dest>
16126 *: "í° íƒ€ì¼ ì‚¬ìš©"
16127 </dest>
16128 <voice>
16129 *: "í° íƒ€ì¼ ì‚¬ìš©"
16130 </voice>
16131</phrase>
16132<phrase>
16133 id: LANG_SHOW_SOLUTION
16134 desc: Map in Amaze game
16135 user: core
16136 <source>
16137 *: "Show Solution"
16138 </source>
16139 <dest>
16140 *: "해결책 표시"
16141 </dest>
16142 <voice>
16143 *: "해결책 표시"
16144 </voice>
16145</phrase>
16146<phrase>
16147 id: LANG_QUIT_WITHOUT_SAVING
16148 desc:
16149 user: core
16150 <source>
16151 *: "Quit without saving"
16152 </source>
16153 <dest>
16154 *: "저장하지 않고 종료"
16155 </dest>
16156 <voice>
16157 *: "저장하지 않고 종료"
16158 </voice>
16159</phrase>
16160<phrase>
16161 id: LANG_GENERATING_MAZE
16162 desc: Amaze game
16163 user: core
16164 <source>
16165 *: "Generating maze..."
16166 </source>
16167 <dest>
16168 *: "미로 ìƒì„± 중..."
16169 </dest>
16170 <voice>
16171 *: "미로 ìƒì„± 중"
16172 </voice>
16173</phrase>
16174<phrase>
16175 id: LANG_YOU_WIN
16176 desc: Success in game
16177 user: core
16178 <source>
16179 *: "You win!"
16180 </source>
16181 <dest>
16182 *: "ì´ê²¼ìŠµë‹ˆë‹¤!"
16183 </dest>
16184 <voice>
16185 *: "ì´ê²¼ìŠµë‹ˆë‹¤!"
16186 </voice>
16187</phrase>
16188<phrase>
16189 id: LANG_YOU_CHEATED
16190 desc: Cheated in game
16191 user: core
16192 <source>
16193 *: "You cheated!"
16194 </source>
16195 <dest>
16196 *: "치트를 사용했습니다!"
16197 </dest>
16198 <voice>
16199 *: "치트를 사용했습니다!"
16200 </voice>
16201</phrase>
16202<phrase>
16203 id: LANG_DIFFICULTY_EASY
16204 desc: Game difficulty
16205 user: core
16206 <source>
16207 *: "Easy"
16208 </source>
16209 <dest>
16210 *: "쉬움"
16211 </dest>
16212 <voice>
16213 *: "쉬움"
16214 </voice>
16215</phrase>
16216<phrase>
16217 id: LANG_DIFFICULTY_MEDIUM
16218 desc: Game difficulty
16219 user: core
16220 <source>
16221 *: "Medium"
16222 </source>
16223 <dest>
16224 *: "중간"
16225 </dest>
16226 <voice>
16227 *: "중간"
16228 </voice>
16229</phrase>
16230<phrase>
16231 id: LANG_DIFFICULTY_HARD
16232 desc: Game difficulty
16233 user: core
16234 <source>
16235 *: "Hard"
16236 </source>
16237 <dest>
16238 *: "어려움"
16239 </dest>
16240 <voice>
16241 *: "어려움"
16242 </voice>
16243</phrase>
16244<phrase>
16245 id: LANG_DIFFICULTY_EXPERT
16246 desc: Game difficulty
16247 user: core
16248 <source>
16249 *: "Expert"
16250 </source>
16251 <dest>
16252 *: "전문가"
16253 </dest>
16254 <voice>
16255 *: "전문가"
16256 </voice>
16257</phrase>
16258<phrase>
16259 id: LANG_STEREOSW_MODE
16260 desc: Stereo Switch Mode
16261 user: core
16262 <source>
16263 *: "Stereo Switch Mode"
16264 </source>
16265 <dest>
16266 *: "스테레오 스위치 모드"
16267 </dest>
16268 <voice>
16269 *: "스테레오 스위치 모드"
16270 </voice>
16271</phrase>
16272<phrase>
16273 id: LANG_REVERSE
16274 desc: in settings_menu
16275 user: core
16276 <source>
16277 *: "Reverse"
16278 </source>
16279 <dest>
16280 *: "왕복"
16281 </dest>
16282 <voice>
16283 *: "왕복"
16284 </voice>
16285</phrase>
16286<phrase>
16287 id: LANG_ALWAYS_ZERO
16288 desc: in settings_menu
16289 user: core
16290 <source>
16291 *: "Always 0"
16292 </source>
16293 <dest>
16294 *: "í•­ìƒ 0"
16295 </dest>
16296 <voice>
16297 *: "í•­ìƒ 0"
16298 </voice>
16299</phrase>
16300<phrase>
16301 id: LANG_ALWAYS_ONE
16302 desc: in settings_menu
16303 user: core
16304 <source>
16305 *: "Always 1"
16306 </source>
16307 <dest>
16308 *: "í•­ìƒ 1"
16309 </dest>
16310 <voice>
16311 *: "í•­ìƒ 1"
16312 </voice>
16313</phrase>
16314<phrase>
16315 id: LANG_LEGAL_NOTICES
16316 desc: in system menu
16317 user: core
16318 <source>
16319 *: "Legal Notices"
16320 </source>
16321 <dest>
16322 *: "ë²•ì  ê³ ì§€"
16323 </dest>
16324 <voice>
16325 *: "ë²•ì  ê³ ì§€"
16326 </voice>
16327</phrase>
16328<phrase>
16329 id: LANG_ERROR_FORMATSTR
16330 desc: for general use
16331 user: core
16332 <source>
16333 *: "Error: %s"
16334 </source>
16335 <dest>
16336 *: "오류: %s"
16337 </dest>
16338 <voice>
16339 *: "오류"
16340 </voice>
16341</phrase>
16342<phrase>
16343 id: LANG_MIKMOD_SETTINGS
16344 desc: mikmod plugin
16345 user: core
16346 <source>
16347 *: "Mikmod Settings"
16348 </source>
16349 <dest>
16350 *: "Mik개조 설정"
16351 </dest>
16352 <voice>
16353 *: "Mik 개조 설정"
16354 </voice>
16355</phrase>
16356<phrase>
16357 id: LANG_MIKMOD_MENU
16358 desc: mikmod plugin
16359 user: core
16360 <source>
16361 *: "Mikmod Menu"
16362 </source>
16363 <dest>
16364 *: "Mik개조 메뉴"
16365 </dest>
16366 <voice>
16367 *: "Mik 개조 메뉴"
16368 </voice>
16369</phrase>
16370<phrase>
16371 id: LANG_CHESSBOX_MENU
16372 desc: chessbox plugin
16373 user: core
16374 <source>
16375 *: "Chessbox Menu"
16376 </source>
16377 <dest>
16378 *: "체스박스메뉴"
16379 </dest>
16380 <voice>
16381 *: "체스 박스 메뉴"
16382 </voice>
16383</phrase>
16384<phrase>
16385 id: VOICE_INVALID_VOICE_FILE
16386 desc: played if the voice file fails to load
10613 user: core 16387 user: core
10614 <source> 16388 <source>
10615 *: "" 16389 *: ""
@@ -10618,96 +16392,118 @@
10618 *: "" 16392 *: ""
10619 </dest> 16393 </dest>
10620 <voice> 16394 <voice>
10621 *: "ë„ì˜" 16395 *: "ìž˜ëª»ëœ ìŒì± 파ì¼"
10622 </voice> 16396 </voice>
10623</phrase> 16397</phrase>
10624<phrase> 16398<phrase>
10625 id: LANG_RESET_ASK 16399 id: VOICE_LANG_NAME
10626 desc: confirm to reset settings 16400 desc: Spoken name of the language
10627 user: core 16401 user: core
10628 <source> 16402 <source>
10629 *: "Are You Sure?" 16403 *: ""
10630 </source> 16404 </source>
10631 <dest> 16405 <dest>
10632 *: "ì •ë§ìž…니까?" 16406 *: ""
10633 </dest> 16407 </dest>
10634 <voice> 16408 <voice>
10635 *: "ì ë§ìž…니까?" 16409 *: "íœêµ­ì¸"
10636 </voice> 16410 </voice>
10637</phrase> 16411</phrase>
10638<phrase> 16412<phrase>
10639 id: LANG_HIGH 16413 id: LANG_PERCENT_FORMAT
10640 desc: in settings_menu 16414 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
10641 user: core 16415 user: core
10642 <source> 16416 <source>
10643 *: none 16417 *: "%ld%%"
10644 gigabeatfx: "High"
10645 </source> 16418 </source>
10646 <dest> 16419 <dest>
10647 *: none 16420 *: "~%ld%%"
10648 gigabeatfx: "높ìŒ"
10649 </dest> 16421 </dest>
10650 <voice> 16422 <voice>
10651 *: none 16423 *: none
10652 gigabeatfx: "높ìŒ"
10653 </voice> 16424 </voice>
10654</phrase> 16425</phrase>
10655<phrase> 16426<phrase>
10656 id: LANG_SET_AS_REC_DIR 16427 id: LANG_CHOOSE_FILE
10657 desc: used in the onplay menu to set a recording dir 16428 desc: file_picker plugin ask user to select a file
10658 user: core 16429 user: core
10659 <source> 16430 <source>
10660 *: none 16431 *: "Choose File"
10661 recording: "Set As Recording Directory"
10662 </source> 16432 </source>
10663 <dest> 16433 <dest>
10664 *: none 16434 *: "íŒŒì¼ ì„ íƒ"
10665 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ë¡œ 설정"
10666 </dest> 16435 </dest>
10667 <voice> 16436 <voice>
10668 *: none 16437 *: "íŒŒì¼ ì„ íƒ"
10669 recording: "ë…¹ìŒ ë””ë ‰í† ë¦¬ë¡œ 설정"
10670 </voice> 16438 </voice>
10671</phrase> 16439</phrase>
10672<phrase> 16440<phrase>
10673 id: LANG_SKIP_LENGTH 16441 id: LANG_REMAINING
10674 desc: playback settings menu 16442 desc: Playing Time
10675 user: core 16443 user: core
10676 <source> 16444 <source>
10677 *: "Skip Length" 16445 *: "Remaining"
10678 </source> 16446 </source>
10679 <dest> 16447 <dest>
10680 *: "스터디 모드" 16448 *: "ë¨ì€"
10681 </dest> 16449 </dest>
10682 <voice> 16450 <voice>
10683 *: "스터디 모드" 16451 *: "ë¨ì€"
10684 </voice> 16452 </voice>
10685</phrase> 16453</phrase>
10686<phrase> 16454<phrase>
10687 id: LANG_CODEPAGE_CENTRAL_EUROPEAN 16455 id: LANG_DISABLE_MAINMENU_SCROLLING
10688 desc: in codepage setting menu 16456 desc: Disable main menu scrolling
10689 user: core 16457 user: core
10690 <source> 16458 <source>
10691 *: "Central European (CP1250)" 16459 *: "Disable main menu scrolling"
10692 </source> 16460 </source>
10693 <dest> 16461 <dest>
10694 *: "중유럽 (CP1250)" 16462 *: "ë©”ì¸ ë©”ë‰´ 스í¬ë¡¤ 비활성화"
10695 </dest> 16463 </dest>
10696 <voice> 16464 <voice>
10697 *: "중유럽" 16465 *: "ë©”ì¸ ë©”ë‰´ 스í¬ë¡¤ 비활성화"
10698 </voice> 16466 </voice>
10699</phrase> 16467</phrase>
10700<phrase> 16468<phrase>
10701 id: LANG_FILESIZE 16469 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
10702 desc: in record timesplit options and in track information viewer 16470 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
10703 user: core 16471 user: core
10704 <source> 16472 <source>
10705 *: "Filesize" 16473 *: "Selection too big, %d random tracks will be selected"
10706 </source> 16474 </source>
10707 <dest> 16475 <dest>
10708 *: "파ì¼í¬ê¸°" 16476 *: "ì„ íƒì´ 너무 커서, %d ê°œì˜ ë¬´ìž‘ìœ„ íŠ¸ëž™ì´ ì„ íƒë¨"
10709 </dest> 16477 </dest>
10710 <voice> 16478 <voice>
10711 *: "파ì¼í¬ê¸°" 16479 *: "ì„ íƒì´ 너무 커서 무작위로 ì„ íƒë˜ëŠ” íŠ¸ëž™ì´ ì¤„ì–´ë“¬"
16480 </voice>
16481</phrase>
16482<phrase>
16483 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
16484 desc: track display options
16485 user: core
16486 <source>
16487 *: "Title & Album from ID3 tags"
16488 </source>
16489 <dest>
16490 *: "ID3 태그 제목 & 앨범"
16491 </dest>
16492 <voice>
16493 *: "íƒœê·¸ì˜ ì œëª© ë° ì•¨ë²”"
16494 </voice>
16495</phrase>
16496<phrase>
16497 id: LANG_DISPLAY_TITLE_FROMTAGS
16498 desc: track display options
16499 user: core
16500 <source>
16501 *: "Title from ID3 tags"
16502 </source>
16503 <dest>
16504 *: "ID3 태그 제목"
16505 </dest>
16506 <voice>
16507 *: "태그 제목"
10712 </voice> 16508 </voice>
10713</phrase> 16509</phrase>
diff --git a/apps/lang/nederlands.lang b/apps/lang/nederlands.lang
index 805eb0aa85..42ed25b897 100644
--- a/apps/lang/nederlands.lang
+++ b/apps/lang/nederlands.lang
@@ -15219,16 +15219,13 @@
15219 desc: prefix for elapsed playtime announcement 15219 desc: prefix for elapsed playtime announcement
15220 user: core 15220 user: core
15221 <source> 15221 <source>
15222 *: none 15222 *: "Elapsed"
15223 hotkey: "Elapsed"
15224 </source> 15223 </source>
15225 <dest> 15224 <dest>
15226 *: none 15225 *: "Verstreken"
15227 hotkey: "Verstreken"
15228 </dest> 15226 </dest>
15229 <voice> 15227 <voice>
15230 *: none 15228 *: "Verstreken"
15231 hotkey: "Verstreken"
15232 </voice> 15229 </voice>
15233</phrase> 15230</phrase>
15234<phrase> 15231<phrase>
diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang
index b7200a8830..9cbde96420 100644
--- a/apps/lang/polski.lang
+++ b/apps/lang/polski.lang
@@ -454,10 +454,10 @@
454 *: "System" 454 *: "System"
455 </source> 455 </source>
456 <dest> 456 <dest>
457 *: "System" 457 *: "~System"
458 </dest> 458 </dest>
459 <voice> 459 <voice>
460 *: "System" 460 *: "~System"
461 </voice> 461 </voice>
462</phrase> 462</phrase>
463<phrase> 463<phrase>
@@ -692,10 +692,10 @@
692 *: "Stereo" 692 *: "Stereo"
693 </source> 693 </source>
694 <dest> 694 <dest>
695 *: "Stereo" 695 *: "~Stereo"
696 </dest> 696 </dest>
697 <voice> 697 <voice>
698 *: "Stereofoniczny" 698 *: "~Stereofoniczny"
699 </voice> 699 </voice>
700</phrase> 700</phrase>
701<phrase> 701<phrase>
@@ -706,10 +706,10 @@
706 *: "Mono" 706 *: "Mono"
707 </source> 707 </source>
708 <dest> 708 <dest>
709 *: "Mono" 709 *: "~Mono"
710 </dest> 710 </dest>
711 <voice> 711 <voice>
712 *: "Monofoniczny" 712 *: "~Monofoniczny"
713 </voice> 713 </voice>
714</phrase> 714</phrase>
715<phrase> 715<phrase>
@@ -779,10 +779,10 @@
779 *: "Karaoke" 779 *: "Karaoke"
780 </source> 780 </source>
781 <dest> 781 <dest>
782 *: "Karaoke" 782 *: "~Karaoke"
783 </dest> 783 </dest>
784 <voice> 784 <voice>
785 *: "Karaoke" 785 *: "~Karaoke"
786 </voice> 786 </voice>
787</phrase> 787</phrase>
788<phrase> 788<phrase>
@@ -807,7 +807,7 @@
807 *: "Crossfeed" 807 *: "Crossfeed"
808 </source> 808 </source>
809 <dest> 809 <dest>
810 *: "Crossfeed" 810 *: "~Crossfeed"
811 </dest> 811 </dest>
812 <voice> 812 <voice>
813 *: "krosfid" 813 *: "krosfid"
@@ -1470,7 +1470,7 @@
1470 *: "Replaygain" 1470 *: "Replaygain"
1471 </source> 1471 </source>
1472 <dest> 1472 <dest>
1473 *: "Replaygain" 1473 *: "~Replaygain"
1474 </dest> 1474 </dest>
1475 <voice> 1475 <voice>
1476 *: "riplejgejn" 1476 *: "riplejgejn"
@@ -4871,11 +4871,11 @@
4871 </source> 4871 </source>
4872 <dest> 4872 <dest>
4873 *: none 4873 *: none
4874 radio: "Korea" 4874 radio: "~Korea"
4875 </dest> 4875 </dest>
4876 <voice> 4876 <voice>
4877 *: none 4877 *: none
4878 radio: "Korea" 4878 radio: "~Korea"
4879 </voice> 4879 </voice>
4880</phrase> 4880</phrase>
4881<phrase> 4881<phrase>
@@ -4886,10 +4886,10 @@
4886 *: "Format" 4886 *: "Format"
4887 </source> 4887 </source>
4888 <dest> 4888 <dest>
4889 *: "Format" 4889 *: "~Format"
4890 </dest> 4890 </dest>
4891 <voice> 4891 <voice>
4892 *: "Format" 4892 *: "~Format"
4893 </voice> 4893 </voice>
4894</phrase> 4894</phrase>
4895<phrase> 4895<phrase>
@@ -4902,11 +4902,11 @@
4902 </source> 4902 </source>
4903 <dest> 4903 <dest>
4904 *: none 4904 *: none
4905 recording: "MPEG Layer 3" 4905 recording: "~MPEG Layer 3"
4906 </dest> 4906 </dest>
4907 <voice> 4907 <voice>
4908 *: none 4908 *: none
4909 recording: "MPEG Layer 3" 4909 recording: "~MPEG Layer 3"
4910 </voice> 4910 </voice>
4911</phrase> 4911</phrase>
4912<phrase> 4912<phrase>
@@ -4919,11 +4919,11 @@
4919 </source> 4919 </source>
4920 <dest> 4920 <dest>
4921 *: none 4921 *: none
4922 recording: "PCM Wave" 4922 recording: "~PCM Wave"
4923 </dest> 4923 </dest>
4924 <voice> 4924 <voice>
4925 *: none 4925 *: none
4926 recording: "PCM Wave" 4926 recording: "~PCM Wave"
4927 </voice> 4927 </voice>
4928</phrase> 4928</phrase>
4929<phrase> 4929<phrase>
@@ -5636,11 +5636,11 @@
5636 </source> 5636 </source>
5637 <dest> 5637 <dest>
5638 *: none 5638 *: none
5639 remote: "(Vol- : Przywróć)" 5639 remote: "(Głośność- : Przywróć)"
5640 </dest> 5640 </dest>
5641 <voice> 5641 <voice>
5642 *: none 5642 *: none
5643 remote: "(Vol- : Przywróć)" 5643 remote: "Głośność minus przywróć"
5644 </voice> 5644 </voice>
5645</phrase> 5645</phrase>
5646<phrase> 5646<phrase>
@@ -5922,7 +5922,7 @@
5922 *: "Wolne miejsce:" 5922 *: "Wolne miejsce:"
5923 </dest> 5923 </dest>
5924 <voice> 5924 <voice>
5925 *: "Wolne miejsce na dysku:" 5925 *: "Wolne miejsce na dysku"
5926 </voice> 5926 </voice>
5927</phrase> 5927</phrase>
5928<phrase> 5928<phrase>
@@ -5965,7 +5965,7 @@
5965 </dest> 5965 </dest>
5966 <voice> 5966 <voice>
5967 *: none 5967 *: none
5968 hibylinux: "U S B" 5968 hibylinux: "u es be"
5969 multivolume: "ha de 1" 5969 multivolume: "ha de 1"
5970 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mikro es de" 5970 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mikro es de"
5971 xduoox3: "mikro es de 2" 5971 xduoox3: "mikro es de 2"
@@ -6371,10 +6371,10 @@
6371 *: "Album" 6371 *: "Album"
6372 </source> 6372 </source>
6373 <dest> 6373 <dest>
6374 *: "Album" 6374 *: "~Album"
6375 </dest> 6375 </dest>
6376 <voice> 6376 <voice>
6377 *: "Album" 6377 *: "~Album"
6378 </voice> 6378 </voice>
6379</phrase> 6379</phrase>
6380<phrase> 6380<phrase>
@@ -6892,7 +6892,7 @@
6892 </source> 6892 </source>
6893 <dest> 6893 <dest>
6894 *: none 6894 *: none
6895 pitchscreen: "Obniżenie Półtonu" 6895 pitchscreen: "Obniżenie półtonu"
6896 </dest> 6896 </dest>
6897 <voice> 6897 <voice>
6898 *: none 6898 *: none
@@ -7036,7 +7036,7 @@
7036 *: "Tryb:" 7036 *: "Tryb:"
7037 </dest> 7037 </dest>
7038 <voice> 7038 <voice>
7039 *: "Tryb:" 7039 *: "Tryb"
7040 </voice> 7040 </voice>
7041</phrase> 7041</phrase>
7042<phrase> 7042<phrase>
@@ -7880,7 +7880,7 @@
7880 *: "" 7880 *: ""
7881 </dest> 7881 </dest>
7882 <voice> 7882 <voice>
7883 *: "minus" 7883 *: "~minus"
7884 </voice> 7884 </voice>
7885</phrase> 7885</phrase>
7886<phrase> 7886<phrase>
@@ -7894,7 +7894,7 @@
7894 *: "" 7894 *: ""
7895 </dest> 7895 </dest>
7896 <voice> 7896 <voice>
7897 *: "plus" 7897 *: "~plus"
7898 </voice> 7898 </voice>
7899</phrase> 7899</phrase>
7900<phrase> 7900<phrase>
@@ -8922,7 +8922,7 @@
8922 *: "" 8922 *: ""
8923 </dest> 8923 </dest>
8924 <voice> 8924 <voice>
8925 *: "oh" 8925 *: "~oh"
8926 </voice> 8926 </voice>
8927</phrase> 8927</phrase>
8928<phrase> 8928<phrase>
@@ -10142,11 +10142,11 @@
10142 </source> 10142 </source>
10143 <dest> 10143 <dest>
10144 *: none 10144 *: none
10145 pitchscreen: "Limit" 10145 pitchscreen: "~Limit"
10146 </dest> 10146 </dest>
10147 <voice> 10147 <voice>
10148 *: none 10148 *: none
10149 pitchscreen: "Limit" 10149 pitchscreen: "~Limit"
10150 </voice> 10150 </voice>
10151</phrase> 10151</phrase>
10152<phrase> 10152<phrase>
@@ -10450,7 +10450,7 @@
10450 *: "Następny:" 10450 *: "Następny:"
10451 </dest> 10451 </dest>
10452 <voice> 10452 <voice>
10453 *: "Następny:" 10453 *: "Następny"
10454 </voice> 10454 </voice>
10455</phrase> 10455</phrase>
10456<phrase> 10456<phrase>
@@ -10814,10 +10814,10 @@
10814 *: "Limit" 10814 *: "Limit"
10815 </source> 10815 </source>
10816 <dest> 10816 <dest>
10817 *: "Limit" 10817 *: "~Limit"
10818 </dest> 10818 </dest>
10819 <voice> 10819 <voice>
10820 *: "Limit" 10820 *: "~Limit"
10821 </voice> 10821 </voice>
10822</phrase> 10822</phrase>
10823<phrase> 10823<phrase>
@@ -10830,11 +10830,11 @@
10830 </source> 10830 </source>
10831 <dest> 10831 <dest>
10832 *: none 10832 *: none
10833 usb_hid: "Multimedia" 10833 usb_hid: "~Multimedia"
10834 </dest> 10834 </dest>
10835 <voice> 10835 <voice>
10836 *: none 10836 *: none
10837 usb_hid: "Multimedia" 10837 usb_hid: "~Multimedia"
10838 </voice> 10838 </voice>
10839</phrase> 10839</phrase>
10840<phrase> 10840<phrase>
@@ -10961,7 +10961,7 @@
10961 *: "Następny:" 10961 *: "Następny:"
10962 </dest> 10962 </dest>
10963 <voice> 10963 <voice>
10964 *: "Następny:" 10964 *: "Następny"
10965 </voice> 10965 </voice>
10966</phrase> 10966</phrase>
10967<phrase> 10967<phrase>
@@ -11070,11 +11070,11 @@
11070 </source> 11070 </source>
11071 <dest> 11071 <dest>
11072 *: none 11072 *: none
11073 touchscreen: "OK" 11073 touchscreen: "~OK"
11074 </dest> 11074 </dest>
11075 <voice> 11075 <voice>
11076 *: none 11076 *: none
11077 touchscreen: "OK" 11077 touchscreen: "~OK"
11078 </voice> 11078 </voice>
11079</phrase> 11079</phrase>
11080<phrase> 11080<phrase>
@@ -11195,7 +11195,7 @@
11195 </dest> 11195 </dest>
11196 <voice> 11196 <voice>
11197 *: none 11197 *: none
11198 radio: "Poziom sygnału:" 11198 radio: "Poziom sygnału"
11199 </voice> 11199 </voice>
11200</phrase> 11200</phrase>
11201<phrase> 11201<phrase>
@@ -11632,11 +11632,11 @@
11632 </source> 11632 </source>
11633 <dest> 11633 <dest>
11634 *: none 11634 *: none
11635 sansafuzeplus: "Martwa strefa touchpada" 11635 sansafuzeplus: "Martwa strefa panelu dotykowego"
11636 </dest> 11636 </dest>
11637 <voice> 11637 <voice>
11638 *: none 11638 *: none
11639 sansafuzeplus: "Martwa strefa touchpada" 11639 sansafuzeplus: "Martwa strefa panelu dotykowego"
11640 </voice> 11640 </voice>
11641</phrase> 11641</phrase>
11642<phrase> 11642<phrase>
@@ -12283,16 +12283,16 @@
12283</phrase> 12283</phrase>
12284<phrase> 12284<phrase>
12285 id: LANG_PLAYTIME_REMAINING 12285 id: LANG_PLAYTIME_REMAINING
12286 desc: playing time screen 12286 desc: deprecated
12287 user: core 12287 user: core
12288 <source> 12288 <source>
12289 *: "Playlist remaining:" 12289 *: ""
12290 </source> 12290 </source>
12291 <dest> 12291 <dest>
12292 *: "Pozostały czas listy odtwarzania:" 12292 *: ""
12293 </dest> 12293 </dest>
12294 <voice> 12294 <voice>
12295 *: "Pozostały czas listy odtwarzania" 12295 *: ""
12296 </voice> 12296 </voice>
12297</phrase> 12297</phrase>
12298<phrase> 12298<phrase>
@@ -12334,7 +12334,7 @@
12334 *: "Poziom 3: 60 ruchów / 30 min" 12334 *: "Poziom 3: 60 ruchów / 30 min"
12335 </dest> 12335 </dest>
12336 <voice> 12336 <voice>
12337 *: "Poziom 3: 60 ruchów na 30 minut" 12337 *: "Poziom 3 60 ruchów na 30 minut"
12338 </voice> 12338 </voice>
12339</phrase> 12339</phrase>
12340<phrase> 12340<phrase>
@@ -12612,7 +12612,7 @@
12612 *: "Poziom 1: 60 ruchów / 5 min" 12612 *: "Poziom 1: 60 ruchów / 5 min"
12613 </dest> 12613 </dest>
12614 <voice> 12614 <voice>
12615 *: "Poziom 1: 60 ruchów na 5 minut" 12615 *: "Poziom 1 60 ruchów na 5 minut"
12616 </voice> 12616 </voice>
12617</phrase> 12617</phrase>
12618<phrase> 12618<phrase>
@@ -12643,7 +12643,7 @@
12643 *: "Poziom 8: 1 ruch / 15 min" 12643 *: "Poziom 8: 1 ruch / 15 min"
12644 </dest> 12644 </dest>
12645 <voice> 12645 <voice>
12646 *: "Poziom 8: 1 ruch na 15 minut" 12646 *: "Poziom 8 1 ruch na 15 minut"
12647 </voice> 12647 </voice>
12648</phrase> 12648</phrase>
12649<phrase> 12649<phrase>
@@ -12671,7 +12671,7 @@
12671 *: "Poziom 4: 40 ruchów / 30 min" 12671 *: "Poziom 4: 40 ruchów / 30 min"
12672 </dest> 12672 </dest>
12673 <voice> 12673 <voice>
12674 *: "Poziom 4: 40 ruchów na 30 minut" 12674 *: "Poziom 4 40 ruchów na 30 minut"
12675 </voice> 12675 </voice>
12676</phrase> 12676</phrase>
12677<phrase> 12677<phrase>
@@ -13199,7 +13199,7 @@
13199 *: "Poziom 7: 40 ruchów / 240 min" 13199 *: "Poziom 7: 40 ruchów / 240 min"
13200 </dest> 13200 </dest>
13201 <voice> 13201 <voice>
13202 *: "Poziom 7: 40 ruchów na 240 minut" 13202 *: "Poziom 7 40 ruchów na 240 minut"
13203 </voice> 13203 </voice>
13204</phrase> 13204</phrase>
13205<phrase> 13205<phrase>
@@ -13230,7 +13230,7 @@
13230 *: "Poziom 2: 60 ruchów / 15 min" 13230 *: "Poziom 2: 60 ruchów / 15 min"
13231 </dest> 13231 </dest>
13232 <voice> 13232 <voice>
13233 *: "Poziom 2: 60 ruchów na 15 minut" 13233 *: "Poziom 2 60 ruchów na 15 minut"
13234 </voice> 13234 </voice>
13235</phrase> 13235</phrase>
13236<phrase> 13236<phrase>
@@ -13401,7 +13401,7 @@
13401 *: "Poziom 6: 40 ruchów / 120 min" 13401 *: "Poziom 6: 40 ruchów / 120 min"
13402 </dest> 13402 </dest>
13403 <voice> 13403 <voice>
13404 *: "Poziom 6: 40 ruchów na 120 minut" 13404 *: "Poziom 6 40 ruchów na 120 minut"
13405 </voice> 13405 </voice>
13406</phrase> 13406</phrase>
13407<phrase> 13407<phrase>
@@ -13567,7 +13567,7 @@
13567 *: "Poziom 5: 40 ruchów / 60 min" 13567 *: "Poziom 5: 40 ruchów / 60 min"
13568 </dest> 13568 </dest>
13569 <voice> 13569 <voice>
13570 *: "Poziom 5: 40 ruchów na 60 minut" 13570 *: "Poziom 5 40 ruchów na 60 minut"
13571 </voice> 13571 </voice>
13572</phrase> 13572</phrase>
13573<phrase> 13573<phrase>
@@ -13814,7 +13814,7 @@
13814 *: "Poziom 10: 1 ruch / 600 min" 13814 *: "Poziom 10: 1 ruch / 600 min"
13815 </dest> 13815 </dest>
13816 <voice> 13816 <voice>
13817 *: "Poziom 10: 1 ruch na 600 minut" 13817 *: "Poziom 10 1 ruch na 600 minut"
13818 </voice> 13818 </voice>
13819</phrase> 13819</phrase>
13820<phrase> 13820<phrase>
@@ -14322,7 +14322,7 @@
14322 *: "Poziom 9: 1 ruch / 60 min" 14322 *: "Poziom 9: 1 ruch / 60 min"
14323 </dest> 14323 </dest>
14324 <voice> 14324 <voice>
14325 *: "Poziom 9: 1 ruch na 60 minut" 14325 *: "Poziom 9 1 ruch na 60 minut"
14326 </voice> 14326 </voice>
14327</phrase> 14327</phrase>
14328<phrase> 14328<phrase>
@@ -14701,16 +14701,13 @@
14701 desc: prefix for elapsed playtime announcement 14701 desc: prefix for elapsed playtime announcement
14702 user: core 14702 user: core
14703 <source> 14703 <source>
14704 *: none 14704 *: "Elapsed"
14705 hotkey: "Elapsed"
14706 </source> 14705 </source>
14707 <dest> 14706 <dest>
14708 *: none 14707 *: "Upłynęło"
14709 hotkey: "Upłynęło"
14710 </dest> 14708 </dest>
14711 <voice> 14709 <voice>
14712 *: none 14710 *: "Upłynęło"
14713 hotkey: "Upłynęło"
14714 </voice> 14711 </voice>
14715</phrase> 14712</phrase>
14716<phrase> 14713<phrase>
@@ -16419,3 +16416,87 @@
16419 *: none 16416 *: none
16420 </voice> 16417 </voice>
16421</phrase> 16418</phrase>
16419<phrase>
16420 id: LANG_CHOOSE_FILE
16421 desc: file_picker plugin ask user to select a file
16422 user: core
16423 <source>
16424 *: "Choose File"
16425 </source>
16426 <dest>
16427 *: "Wybierz plik"
16428 </dest>
16429 <voice>
16430 *: "Wybierz plik"
16431 </voice>
16432</phrase>
16433<phrase>
16434 id: LANG_REMAINING
16435 desc: Playing Time
16436 user: core
16437 <source>
16438 *: "Remaining"
16439 </source>
16440 <dest>
16441 *: "Pozostały"
16442 </dest>
16443 <voice>
16444 *: "Pozostały"
16445 </voice>
16446</phrase>
16447<phrase>
16448 id: LANG_DISABLE_MAINMENU_SCROLLING
16449 desc: Disable main menu scrolling
16450 user: core
16451 <source>
16452 *: "Disable main menu scrolling"
16453 </source>
16454 <dest>
16455 *: "Wyłącz przewijanie menu głównego"
16456 </dest>
16457 <voice>
16458 *: "Wyłącz przewijanie menu głównego"
16459 </voice>
16460</phrase>
16461<phrase>
16462 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
16463 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
16464 user: core
16465 <source>
16466 *: "Selection too big, %d random tracks will be selected"
16467 </source>
16468 <dest>
16469 *: "Wybór jest zbyt duży, zostanie z niego wybranych %d losowych utworów"
16470 </dest>
16471 <voice>
16472 *: "Wybór jest zbyt duży, zostanie z niego wybrana mniejsza liczba losowych utworów"
16473 </voice>
16474</phrase>
16475<phrase>
16476 id: LANG_DISPLAY_TITLEALBUM_FROMTAGS
16477 desc: track display options
16478 user: core
16479 <source>
16480 *: "Title & Album from ID3 tags"
16481 </source>
16482 <dest>
16483 *: "Tytuł i album ze znaczników ID3"
16484 </dest>
16485 <voice>
16486 *: "Tytuł i album ze znaczników i de trzy"
16487 </voice>
16488</phrase>
16489<phrase>
16490 id: LANG_DISPLAY_TITLE_FROMTAGS
16491 desc: track display options
16492 user: core
16493 <source>
16494 *: "Title from ID3 tags"
16495 </source>
16496 <dest>
16497 *: "Tytuł ze znaczników ID3"
16498 </dest>
16499 <voice>
16500 *: "Tytuł ze znaczników i de trzy"
16501 </voice>
16502</phrase>
diff --git a/apps/lang/russian.lang b/apps/lang/russian.lang
index cf4a762ae6..29c3eea147 100644
--- a/apps/lang/russian.lang
+++ b/apps/lang/russian.lang
@@ -317,7 +317,7 @@
317</phrase> 317</phrase>
318<phrase> 318<phrase>
319 id: LANG_CHANNEL_STEREO 319 id: LANG_CHANNEL_STEREO
320 desc: in sound_settings 320 desc: in sound_settings and radio screen
321 user: core 321 user: core
322 <source> 322 <source>
323 *: "Stereo" 323 *: "Stereo"
@@ -331,7 +331,7 @@
331</phrase> 331</phrase>
332<phrase> 332<phrase>
333 id: LANG_CHANNEL_MONO 333 id: LANG_CHANNEL_MONO
334 desc: in sound_settings 334 desc: in sound_settings and radio screen
335 user: core 335 user: core
336 <source> 336 <source>
337 *: "Mono" 337 *: "Mono"
@@ -697,20 +697,6 @@
697 </voice> 697 </voice>
698</phrase> 698</phrase>
699<phrase> 699<phrase>
700 id: LANG_EQUALIZER_EDIT_MODE
701 desc: in the equalizer settings menu
702 user: core
703 <source>
704 *: "Edit mode: %s"
705 </source>
706 <dest>
707 *: "Режим редактированиÑ: %s"
708 </dest>
709 <voice>
710 *: ""
711 </voice>
712</phrase>
713<phrase>
714 id: LANG_EQUALIZER_GAIN_ITEM 700 id: LANG_EQUALIZER_GAIN_ITEM
715 desc: in the equalizer settings menu 701 desc: in the equalizer settings menu
716 user: core 702 user: core
@@ -1431,15 +1417,12 @@
1431 user: core 1417 user: core
1432 <source> 1418 <source>
1433 *: "Peak Meter" 1419 *: "Peak Meter"
1434 masd: none
1435 </source> 1420 </source>
1436 <dest> 1421 <dest>
1437 *: "Уровень Ñигнала" 1422 *: "Уровень Ñигнала"
1438 masd: none
1439 </dest> 1423 </dest>
1440 <voice> 1424 <voice>
1441 *: "Уровень Ñигнала" 1425 *: "Уровень Ñигнала"
1442 masd: none
1443 </voice> 1426 </voice>
1444</phrase> 1427</phrase>
1445<phrase> 1428<phrase>
@@ -2036,23 +2019,6 @@
2036 </voice> 2019 </voice>
2037</phrase> 2020</phrase>
2038<phrase> 2021<phrase>
2039 id: LANG_RECORD_DIRECTORY
2040 desc: in recording settings_menu
2041 user: core
2042 <source>
2043 *: none
2044 recording: "Directory"
2045 </source>
2046 <dest>
2047 *: none
2048 recording: "Папка"
2049 </dest>
2050 <voice>
2051 *: none
2052 recording: "Папка"
2053 </voice>
2054</phrase>
2055<phrase>
2056 id: LANG_RECORD_TRIGGER 2022 id: LANG_RECORD_TRIGGER
2057 desc: in recording settings_menu 2023 desc: in recording settings_menu
2058 user: core 2024 user: core
@@ -2818,15 +2784,12 @@
2818 user: core 2784 user: core
2819 <source> 2785 <source>
2820 *: "Peak Release" 2786 *: "Peak Release"
2821 masd: none
2822 </source> 2787 </source>
2823 <dest> 2788 <dest>
2824 *: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð¸ÐºÐ¾Ð²" 2789 *: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð¸ÐºÐ¾Ð²"
2825 masd: none
2826 </dest> 2790 </dest>
2827 <voice> 2791 <voice>
2828 *: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð¸ÐºÐ¾Ð²" 2792 *: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð¸ÐºÐ¾Ð²"
2829 masd: none
2830 </voice> 2793 </voice>
2831</phrase> 2794</phrase>
2832<phrase> 2795<phrase>
@@ -2835,15 +2798,12 @@
2835 user: core 2798 user: core
2836 <source> 2799 <source>
2837 *: "Peak Hold Time" 2800 *: "Peak Hold Time"
2838 masd: none
2839 </source> 2801 </source>
2840 <dest> 2802 <dest>
2841 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¿Ð¸ÐºÐ¾Ð²" 2803 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¿Ð¸ÐºÐ¾Ð²"
2842 masd: none
2843 </dest> 2804 </dest>
2844 <voice> 2805 <voice>
2845 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¿Ð¸ÐºÐ¾Ð²" 2806 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¿Ð¸ÐºÐ¾Ð²"
2846 masd: none
2847 </voice> 2807 </voice>
2848</phrase> 2808</phrase>
2849<phrase> 2809<phrase>
@@ -2852,15 +2812,12 @@
2852 user: core 2812 user: core
2853 <source> 2813 <source>
2854 *: "Clip Hold Time" 2814 *: "Clip Hold Time"
2855 masd: none
2856 </source> 2815 </source>
2857 <dest> 2816 <dest>
2858 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°" 2817 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°"
2859 masd: none
2860 </dest> 2818 </dest>
2861 <voice> 2819 <voice>
2862 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°" 2820 *: "Ð’Ñ€ÐµÐ¼Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°"
2863 masd: none
2864 </voice> 2821 </voice>
2865</phrase> 2822</phrase>
2866<phrase> 2823<phrase>
@@ -2869,15 +2826,12 @@
2869 user: core 2826 user: core
2870 <source> 2827 <source>
2871 *: "Eternal" 2828 *: "Eternal"
2872 masd: none
2873 </source> 2829 </source>
2874 <dest> 2830 <dest>
2875 *: "ПоÑтоÑнно" 2831 *: "ПоÑтоÑнно"
2876 masd: none
2877 </dest> 2832 </dest>
2878 <voice> 2833 <voice>
2879 *: "ПоÑтоÑнно" 2834 *: "ПоÑтоÑнно"
2880 masd: none
2881 </voice> 2835 </voice>
2882</phrase> 2836</phrase>
2883<phrase> 2837<phrase>
@@ -2886,15 +2840,12 @@
2886 user: core 2840 user: core
2887 <source> 2841 <source>
2888 *: "Scale" 2842 *: "Scale"
2889 masd: none
2890 </source> 2843 </source>
2891 <dest> 2844 <dest>
2892 *: "Шкала" 2845 *: "Шкала"
2893 masd: none
2894 </dest> 2846 </dest>
2895 <voice> 2847 <voice>
2896 *: "Шкала" 2848 *: "Шкала"
2897 masd: none
2898 </voice> 2849 </voice>
2899</phrase> 2850</phrase>
2900<phrase> 2851<phrase>
@@ -2903,15 +2854,12 @@
2903 user: core 2854 user: core
2904 <source> 2855 <source>
2905 *: "Logarithmic (dB)" 2856 *: "Logarithmic (dB)"
2906 masd: none
2907 </source> 2857 </source>
2908 <dest> 2858 <dest>
2909 *: "ЛогарифмичеÑÐºÐ°Ñ (дБ)" 2859 *: "ЛогарифмичеÑÐºÐ°Ñ (дБ)"
2910 masd: none
2911 </dest> 2860 </dest>
2912 <voice> 2861 <voice>
2913 *: "ЛогарифмичеÑÐºÐ°Ñ Ð² децибелах" 2862 *: "ЛогарифмичеÑÐºÐ°Ñ Ð² децибелах"
2914 masd: none
2915 </voice> 2863 </voice>
2916</phrase> 2864</phrase>
2917<phrase> 2865<phrase>
@@ -2920,15 +2868,12 @@
2920 user: core 2868 user: core
2921 <source> 2869 <source>
2922 *: "Linear (%)" 2870 *: "Linear (%)"
2923 masd: none
2924 </source> 2871 </source>
2925 <dest> 2872 <dest>
2926 *: "Ð›Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ (%)" 2873 *: "Ð›Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ (%)"
2927 masd: none
2928 </dest> 2874 </dest>
2929 <voice> 2875 <voice>
2930 *: "Ð›Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ð² процентах" 2876 *: "Ð›Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ð² процентах"
2931 masd: none
2932 </voice> 2877 </voice>
2933</phrase> 2878</phrase>
2934<phrase> 2879<phrase>
@@ -2937,15 +2882,12 @@
2937 user: core 2882 user: core
2938 <source> 2883 <source>
2939 *: "Minimum Of Range" 2884 *: "Minimum Of Range"
2940 masd: none
2941 </source> 2885 </source>
2942 <dest> 2886 <dest>
2943 *: "ÐижнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона" 2887 *: "ÐижнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона"
2944 masd: none
2945 </dest> 2888 </dest>
2946 <voice> 2889 <voice>
2947 *: "ÐижнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона" 2890 *: "ÐижнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона"
2948 masd: none
2949 </voice> 2891 </voice>
2950</phrase> 2892</phrase>
2951<phrase> 2893<phrase>
@@ -2954,15 +2896,12 @@
2954 user: core 2896 user: core
2955 <source> 2897 <source>
2956 *: "Maximum Of Range" 2898 *: "Maximum Of Range"
2957 masd: none
2958 </source> 2899 </source>
2959 <dest> 2900 <dest>
2960 *: "ВерхнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона" 2901 *: "ВерхнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона"
2961 masd: none
2962 </dest> 2902 </dest>
2963 <voice> 2903 <voice>
2964 *: "ВерхнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона" 2904 *: "ВерхнÑÑ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ð° диапазона"
2965 masd: none
2966 </voice> 2905 </voice>
2967</phrase> 2906</phrase>
2968<phrase> 2907<phrase>
@@ -3354,62 +3293,6 @@
3354 </voice> 3293 </voice>
3355</phrase> 3294</phrase>
3356<phrase> 3295<phrase>
3357 id: LANG_INSERT
3358 desc: in onplay menu. insert a track/playlist into dynamic playlist.
3359 user: core
3360 <source>
3361 *: "Insert"
3362 </source>
3363 <dest>
3364 *: "Ð’Ñтавить"
3365 </dest>
3366 <voice>
3367 *: "Ð’Ñтавить"
3368 </voice>
3369</phrase>
3370<phrase>
3371 id: LANG_INSERT_FIRST
3372 desc: in onplay menu. insert a track/playlist into dynamic playlist.
3373 user: core
3374 <source>
3375 *: "Insert Next"
3376 </source>
3377 <dest>
3378 *: "Ð’Ñтавить Ñледующим"
3379 </dest>
3380 <voice>
3381 *: "Ð’Ñтавить Ñледующим"
3382 </voice>
3383</phrase>
3384<phrase>
3385 id: LANG_INSERT_LAST
3386 desc: in onplay menu. append a track/playlist into dynamic playlist.
3387 user: core
3388 <source>
3389 *: "Insert Last"
3390 </source>
3391 <dest>
3392 *: "Ð’Ñтавить в конец"
3393 </dest>
3394 <voice>
3395 *: "Ð’Ñтавить в конец"
3396 </voice>
3397</phrase>
3398<phrase>
3399 id: LANG_INSERT_SHUFFLED
3400 desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
3401 user: core
3402 <source>
3403 *: "Insert Shuffled"
3404 </source>
3405 <dest>
3406 *: "Ð’Ñтавить в Ñлучайное меÑто"
3407 </dest>
3408 <voice>
3409 *: "Ð’Ñтавить в Ñлучайное меÑто"
3410 </voice>
3411</phrase>
3412<phrase>
3413 id: LANG_QUEUE 3296 id: LANG_QUEUE
3414 desc: The verb/action Queue 3297 desc: The verb/action Queue
3415 user: core 3298 user: core
@@ -3525,34 +3408,16 @@
3525 </voice> 3408 </voice>
3526</phrase> 3409</phrase>
3527<phrase> 3410<phrase>
3528 id: LANG_BATTERY_TRICKLE_CHARGE
3529 desc: in info display, shows that trickle charge is running
3530 user: core
3531 <source>
3532 *: none
3533 charging: "Battery: Trickle Chg"
3534 </source>
3535 <dest>
3536 *: none
3537 charging: "ÐккумулÑтор: ИмпульÑн. зарÑд"
3538 </dest>
3539 <voice>
3540 *: none
3541 charging: "ÐккумулÑтор: ИмпульÑн. зарÑд"
3542 </voice>
3543</phrase>
3544<phrase>
3545 id: LANG_BATTERY_TIME 3411 id: LANG_BATTERY_TIME
3546 desc: battery level in % and estimated time remaining 3412 desc: battery level in % and estimated time remaining
3547 user: core 3413 user: core
3548 <source> 3414 <source>
3549 *: "Battery: %d%% %dh %dm" 3415 *: "Battery: %d%% %dh %dm"
3550 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm" 3416 ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
3551 iriverifp7xx: "%d%% %dh %dm"
3552 </source> 3417 </source>
3553 <dest> 3418 <dest>
3554 *: "%d%% %dч %dм" 3419 *: "%d%% %dч %dм"
3555 ipodmini1g,ipodmini2g,iriverh10,iriverifp7xx: "%d%% %dч %dм" 3420 ipodmini1g,ipodmini2g,iriverh10: "%d%% %dч %dм"
3556 </dest> 3421 </dest>
3557 <voice> 3422 <voice>
3558 *: "ЗарÑд аккумулÑтора" 3423 *: "ЗарÑд аккумулÑтора"
@@ -3592,38 +3457,41 @@
3592 user: core 3457 user: core
3593 <source> 3458 <source>
3594 *: "Int:" 3459 *: "Int:"
3595 xduoox3: "mSD1:"
3596 hibylinux: "mSD:" 3460 hibylinux: "mSD:"
3461 xduoox3: "mSD1:"
3597 </source> 3462 </source>
3598 <dest> 3463 <dest>
3599 *: "Внутр:" 3464 *: "Внутр:"
3600 xduoox3: "mSD1:"
3601 hibylinux: "mSD:" 3465 hibylinux: "mSD:"
3466 xduoox3: "mSD1:"
3602 </dest> 3467 </dest>
3603 <voice> 3468 <voice>
3604 *: "Внутр" 3469 *: "Внутр"
3605 xduoox3: "микро Ð­Ñ Ð”Ð¸ 1"
3606 hibylinux: "микро Ð­Ñ Ð”Ð¸" 3470 hibylinux: "микро Ð­Ñ Ð”Ð¸"
3471 xduoox3: "микро Ð­Ñ Ð”Ð¸ 1"
3607 </voice> 3472 </voice>
3608</phrase> 3473</phrase>
3609<phrase> 3474<phrase>
3610 id: LANG_DISK_NAME_MMC 3475 id: LANG_DISK_NAME_MMC
3611 desc: in info menu; name for external disk with multivolume (Ondio; keep short!) 3476 desc: in info menu; name for external disk with multivolume (keep short!)
3612 user: core 3477 user: core
3613 <source> 3478 <source>
3614 *: none 3479 *: none
3615 multivolume: "HD1" 3480 hibylinux: "USB:"
3481 multivolume: "HD1:"
3616 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 3482 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
3617 xduoox3: "mSD2:" 3483 xduoox3: "mSD2:"
3618 </source> 3484 </source>
3619 <dest> 3485 <dest>
3620 *: none 3486 *: none
3487 hibylinux: "USB:"
3621 multivolume: "HD1" 3488 multivolume: "HD1"
3622 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" 3489 sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
3623 xduoox3: "mSD2:" 3490 xduoox3: "mSD2:"
3624 </dest> 3491 </dest>
3625 <voice> 3492 <voice>
3626 *: none 3493 *: none
3494 hibylinux: "U S B"
3627 multivolume: "Эйч Ди 1" 3495 multivolume: "Эйч Ди 1"
3628 sansac200*,sansaclipplus,sansae200*,sansafuze*: "микро Ð­Ñ Ð”Ð¸" 3496 sansac200*,sansaclipplus,sansae200*,sansafuze*: "микро Ð­Ñ Ð”Ð¸"
3629 xduoox3: "микро Ð­Ñ Ð”Ð¸ 2" 3497 xduoox3: "микро Ð­Ñ Ð”Ð¸ 2"
@@ -3772,6 +3640,7 @@
3772 *: none 3640 *: none
3773 gigabeatfx,mrobe500,rtc: "ВЫКЛ. = Отмена" 3641 gigabeatfx,mrobe500,rtc: "ВЫКЛ. = Отмена"
3774 gigabeats: "ÐÐЗÐД = Отмена" 3642 gigabeats: "ÐÐЗÐД = Отмена"
3643 sansafuzeplus: "BACK = Revert"
3775 gogearsa9200: "ЛЕВО = Отмена" 3644 gogearsa9200: "ЛЕВО = Отмена"
3776 iaudiom5,iaudiox5: "ЗÐПИСЬ = Отмена" 3645 iaudiom5,iaudiox5: "ЗÐПИСЬ = Отмена"
3777 ipod*,mpiohd300,sansac200*: "МЕÐЮ = Отмена" 3646 ipod*,mpiohd300,sansac200*: "МЕÐЮ = Отмена"
@@ -3783,6 +3652,7 @@
3783 </dest> 3652 </dest>
3784 <voice> 3653 <voice>
3785 *: none 3654 *: none
3655 gogearsa9200,iaudiom5,iaudiox5,ipod*,sansac200*,iriverh10,iriverh10_5gb,sansae200*,iriverh100,iriverh120,iriverh300,mrobe100,rtc,samsungyh*: ""
3786 </voice> 3656 </voice>
3787</phrase> 3657</phrase>
3788<phrase> 3658<phrase>
@@ -3840,6 +3710,7 @@
3840 </dest> 3710 </dest>
3841 <voice> 3711 <voice>
3842 *: none 3712 *: none
3713 iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
3843 </voice> 3714 </voice>
3844</phrase> 3715</phrase>
3845<phrase> 3716<phrase>
@@ -3911,23 +3782,6 @@
3911 </voice> 3782 </voice>
3912</phrase> 3783</phrase>
3913<phrase> 3784<phrase>
3914 id: LANG_DB_INF
3915 desc: -inf db for values below measurement
3916 user: core
3917 <source>
3918 *: none
3919 recording: "-inf"
3920 </source>
3921 <dest>
3922 *: none
3923 recording: "-inf"
3924 </dest>
3925 <voice>
3926 *: none
3927 recording: "-inf"
3928 </voice>
3929</phrase>
3930<phrase>
3931 id: LANG_ALARM_MOD_TIME 3785 id: LANG_ALARM_MOD_TIME
3932 desc: The current alarm time shown in the alarm menu for the RTC alarm mod. 3786 desc: The current alarm time shown in the alarm menu for the RTC alarm mod.
3933 user: core 3787 user: core
@@ -3962,23 +3816,6 @@
3962 </voice> 3816 </voice>
3963</phrase> 3817</phrase>
3964<phrase> 3818<phrase>
3965 id: LANG_ALARM_MOD_SHUTDOWN
3966 desc: The text that tells the user that the alarm time is ok and the device shuts off (for the RTC alarm mod).
3967 user: core
3968 <source>
3969 *: none
3970 alarm: "Alarm Set"
3971 </source>
3972 <dest>
3973 *: none
3974 alarm: "УÑтановить будильник"
3975 </dest>
3976 <voice>
3977 *: none
3978 alarm: "УÑтановить будильник"
3979 </voice>
3980</phrase>
3981<phrase>
3982 id: LANG_ALARM_MOD_ERROR 3819 id: LANG_ALARM_MOD_ERROR
3983 desc: The text that tells that the time is incorrect (for the RTC alarm mod). 3820 desc: The text that tells that the time is incorrect (for the RTC alarm mod).
3984 user: core 3821 user: core
@@ -3996,35 +3833,6 @@
3996 </voice> 3833 </voice>
3997</phrase> 3834</phrase>
3998<phrase> 3835<phrase>
3999 id: LANG_ALARM_MOD_KEYS
4000 desc: Shown key functions in alarm menu (for the RTC alarm mod).
4001 user: core
4002 <source>
4003 *: none
4004 alarm: "PLAY=Set OFF=Cancel"
4005 gigabeats: "SELECT=Set POWER=Cancel"
4006 ipod*: "SELECT=Set MENU=Cancel"
4007 iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
4008 mpiohd300: "ENTER=Set MENU=Cancel"
4009 sansafuzeplus: "SELECT=Set BACK=Cancel"
4010 vibe500: "OK=Set C=Cancel"
4011 </source>
4012 <dest>
4013 *: none
4014 alarm: "ВОСПР.=УÑÑ‚., ВЫКЛ.=Отм."
4015 gigabeats: "ВЫБОР=УÑÑ‚., ВЫКЛ.=Отм."
4016 ipod*: "ВЫБОР=УÑÑ‚., МЕÐЮ=Отм."
4017 iriverh10,iriverh10_5gb: "ВЫБОР=УÑÑ‚., ПРЕД.=Отм."
4018 mpiohd300: "ENTER=УÑÑ‚., MENU=Отм."
4019 sansafuzeplus: "SELECT=УÑÑ‚., BACK=Отм."
4020 vibe500: "OK=УÑÑ‚., C=Отм."
4021 </dest>
4022 <voice>
4023 *: none
4024 alarm,gigabeats,ipod*,iriverh10,iriverh10_5gb: ""
4025 </voice>
4026</phrase>
4027<phrase>
4028 id: LANG_ALARM_MOD_DISABLE 3836 id: LANG_ALARM_MOD_DISABLE
4029 desc: Announce that the RTC alarm has been turned off 3837 desc: Announce that the RTC alarm has been turned off
4030 user: core 3838 user: core
@@ -4196,20 +4004,6 @@
4196 </voice> 4004 </voice>
4197</phrase> 4005</phrase>
4198<phrase> 4006<phrase>
4199 id: LANG_ID3_ALBUM_GAIN
4200 desc: in tag viewer
4201 user: core
4202 <source>
4203 *: "Album Gain"
4204 </source>
4205 <dest>
4206 *: "УÑиление альбома"
4207 </dest>
4208 <voice>
4209 *: "УÑиление альбома"
4210 </voice>
4211</phrase>
4212<phrase>
4213 id: LANG_ID3_PATH 4007 id: LANG_ID3_PATH
4214 desc: in tag viewer 4008 desc: in tag viewer
4215 user: core 4009 user: core
@@ -5823,7 +5617,7 @@
5823 *: "Saved %d tracks (%s)" 5617 *: "Saved %d tracks (%s)"
5824 </source> 5618 </source>
5825 <dest> 5619 <dest>
5826 *: "Сохранено %d треков (%d)" 5620 *: "Saved %d tracks (%s)"
5827 </dest> 5621 </dest>
5828 <voice> 5622 <voice>
5829 *: "ТрÑков Ñохранено" 5623 *: "ТрÑков Ñохранено"
@@ -5872,20 +5666,6 @@
5872 </voice> 5666 </voice>
5873</phrase> 5667</phrase>
5874<phrase> 5668<phrase>
5875 id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR
5876 desc: Playlist error
5877 user: core
5878 <source>
5879 *: "Error updating playlist control file"
5880 </source>
5881 <dest>
5882 *: "Ошибка при обновлении файла ÑпиÑка воÑпроизведениÑ"
5883 </dest>
5884 <voice>
5885 *: "Ошибка при обновлении файла ÑпиÑка воÑпроизведениÑ"
5886 </voice>
5887</phrase>
5888<phrase>
5889 id: LANG_PLAYLIST_ACCESS_ERROR 5669 id: LANG_PLAYLIST_ACCESS_ERROR
5890 desc: Playlist error 5670 desc: Playlist error
5891 user: core 5671 user: core
@@ -6044,91 +5824,6 @@
6044 </voice> 5824 </voice>
6045</phrase> 5825</phrase>
6046<phrase> 5826<phrase>
6047 id: LANG_BUTTONBAR_MENU
6048 desc: in button bar
6049 user: core
6050 <source>
6051 *: none
6052 radio_screen_button_bar: "Menu"
6053 </source>
6054 <dest>
6055 *: none
6056 radio_screen_button_bar: "Меню"
6057 </dest>
6058 <voice>
6059 *: none
6060 radio_screen_button_bar: ""
6061 </voice>
6062</phrase>
6063<phrase>
6064 id: LANG_FM_BUTTONBAR_EXIT
6065 desc: in radio screen
6066 user: core
6067 <source>
6068 *: none
6069 radio_screen_button_bar: "Exit"
6070 </source>
6071 <dest>
6072 *: none
6073 radio_screen_button_bar: "Выход"
6074 </dest>
6075 <voice>
6076 *: none
6077 radio_screen_button_bar: ""
6078 </voice>
6079</phrase>
6080<phrase>
6081 id: LANG_FM_BUTTONBAR_ACTION
6082 desc: in radio screen
6083 user: core
6084 <source>
6085 *: none
6086 radio_screen_button_bar: "Action"
6087 </source>
6088 <dest>
6089 *: none
6090 radio_screen_button_bar: "ДейÑтвие"
6091 </dest>
6092 <voice>
6093 *: none
6094 radio_screen_button_bar: ""
6095 </voice>
6096</phrase>
6097<phrase>
6098 id: LANG_FM_BUTTONBAR_ADD
6099 desc: in radio screen
6100 user: core
6101 <source>
6102 *: none
6103 radio_screen_button_bar: "Add"
6104 </source>
6105 <dest>
6106 *: none
6107 radio_screen_button_bar: "Добавить"
6108 </dest>
6109 <voice>
6110 *: none
6111 radio_screen_button_bar: ""
6112 </voice>
6113</phrase>
6114<phrase>
6115 id: LANG_FM_BUTTONBAR_RECORD
6116 desc: in radio screen
6117 user: core
6118 <source>
6119 *: none
6120 radio_screen_button_bar: "Record"
6121 </source>
6122 <dest>
6123 *: none
6124 radio_screen_button_bar: "ЗапиÑÑŒ"
6125 </dest>
6126 <voice>
6127 *: none
6128 radio_screen_button_bar: ""
6129 </voice>
6130</phrase>
6131<phrase>
6132 id: LANG_FM_MONO_MODE 5827 id: LANG_FM_MONO_MODE
6133 desc: in radio screen 5828 desc: in radio screen
6134 user: core 5829 user: core
@@ -6333,7 +6028,7 @@
6333</phrase> 6028</phrase>
6334<phrase> 6029<phrase>
6335 id: LANG_OFF_ABORT 6030 id: LANG_OFF_ABORT
6336 desc: Used on archosrecorder models 6031 desc: Used on many models
6337 user: core 6032 user: core
6338 <source> 6033 <source>
6339 *: "OFF to abort" 6034 *: "OFF to abort"
@@ -6348,6 +6043,7 @@
6348 *: "ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6043 *: "ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
6349 gigabeatfx: "ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6044 gigabeatfx: "ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
6350 gigabeats: "ÐÐЗÐД Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6045 gigabeats: "ÐÐЗÐД Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
6046 sansafuzeplus: "BACK to abort"
6351 iaudiom5,iaudiox5: "ВОСПР. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6047 iaudiom5,iaudiox5: "ВОСПР. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
6352 ipod*: "ПÐÐ£Ð—Ð Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6048 ipod*: "ПÐÐ£Ð—Ð Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
6353 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "ПРЕД. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" 6049 iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "ПРЕД. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹"
@@ -6485,7 +6181,7 @@
6485</phrase> 6181</phrase>
6486<phrase> 6182<phrase>
6487 id: LANG_PLUGIN_WRONG_MODEL 6183 id: LANG_PLUGIN_WRONG_MODEL
6488 desc: The plugin is not compatible with the archos model trying to run it 6184 desc: The plugin is not compatible with the player model trying to run it
6489 user: core 6185 user: core
6490 <source> 6186 <source>
6491 *: "Incompatible model" 6187 *: "Incompatible model"
@@ -6697,7 +6393,6 @@
6697 user: core 6393 user: core
6698 <source> 6394 <source>
6699 *: "Building database... %d found (OFF to return)" 6395 *: "Building database... %d found (OFF to return)"
6700 archosplayer: "Building DB %d found"
6701 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)" 6396 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
6702 gogearsa9200: "Building database... %d found (REW to return)" 6397 gogearsa9200: "Building database... %d found (REW to return)"
6703 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)" 6398 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
@@ -6705,7 +6400,6 @@
6705 </source> 6400 </source>
6706 <dest> 6401 <dest>
6707 *: "ПоÑтроение базы... %d найдено (ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)" 6402 *: "ПоÑтроение базы... %d найдено (ВЫКЛ. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)"
6708 archosplayer: "ПоÑтроение БД... %d найдено"
6709 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "ПоÑтроение базы... %d найдено (ВЛЕВО Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)" 6403 gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "ПоÑтроение базы... %d найдено (ВЛЕВО Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)"
6710 gogearsa9200: "ПоÑтроение базы... %d найдено (РЕВЕРС. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)" 6404 gogearsa9200: "ПоÑтроение базы... %d найдено (РЕВЕРС. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)"
6711 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "ПоÑтроение базы... %d найдено (ПРЕД. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)" 6405 ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "ПоÑтроение базы... %d найдено (ПРЕД. Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹)"
@@ -7318,11 +7012,9 @@
7318 user: core 7012 user: core
7319 <source> 7013 <source>
7320 *: "Buffer:" 7014 *: "Buffer:"
7321 archosplayer: "Buf:"
7322 </source> 7015 </source>
7323 <dest> 7016 <dest>
7324 *: "Буфер:" 7017 *: "Буфер:"
7325 archosplayer: "Буф:"
7326 </dest> 7018 </dest>
7327 <voice> 7019 <voice>
7328 *: "Буфер:" 7020 *: "Буфер:"
@@ -7396,17 +7088,14 @@
7396 user: core 7088 user: core
7397 <source> 7089 <source>
7398 *: "PLAY = Yes" 7090 *: "PLAY = Yes"
7399 archosplayer: "(PLAY/STOP)"
7400 cowond2*: "MENU, or top-right = Yes" 7091 cowond2*: "MENU, or top-right = Yes"
7401 creativezen*: "SELECT = Yes" 7092 creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
7402 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
7403 iriverh100,iriverh120,iriverh300: "NAVI = Yes" 7093 iriverh100,iriverh120,iriverh300: "NAVI = Yes"
7404 mrobe500: "PLAY, POWER, or top-right = Yes" 7094 mrobe500: "PLAY, POWER, or top-right = Yes"
7405 vibe500: "OK = Yes" 7095 vibe500: "OK = Yes"
7406 </source> 7096 </source>
7407 <dest> 7097 <dest>
7408 *: "ВОСПР. = Да" 7098 *: "ВОСПР. = Да"
7409 archosplayer: "(ВОСПР./СТОП)"
7410 cowond2*: "МЕÐЮ или прав. верх. = Да" 7099 cowond2*: "МЕÐЮ или прав. верх. = Да"
7411 creativezen*: "Выбрать = Да" 7100 creativezen*: "Выбрать = Да"
7412 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "ВЫБРÐТЬ = Да" 7101 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "ВЫБРÐТЬ = Да"
@@ -7434,7 +7123,7 @@
7434 </source> 7123 </source>
7435 <dest> 7124 <dest>
7436 *: none 7125 *: none
7437 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansae200*,sansafuze*: "ВЫБОР = УÑтановить" 7126 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "ВЫБОР = УÑтановить"
7438 gogearsa9200,samsungyh*: "ВОСПР. = УÑтановить" 7127 gogearsa9200,samsungyh*: "ВОСПР. = УÑтановить"
7439 iriverh100,iriverh120,iriverh300: "ÐÐВИГ. = УÑтановить" 7128 iriverh100,iriverh120,iriverh300: "ÐÐВИГ. = УÑтановить"
7440 mpiohd300: "ENTER = УÑтановить" 7129 mpiohd300: "ENTER = УÑтановить"
@@ -7444,7 +7133,7 @@
7444 </dest> 7133 </dest>
7445 <voice> 7134 <voice>
7446 *: none 7135 *: none
7447 gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,sansac200*,sansae200*: "" 7136 gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*,gogearsa9200,samsungyh*,iriverh100,iriverh120,iriverh300,rtc: ""
7448 </voice> 7137 </voice>
7449</phrase> 7138</phrase>
7450<phrase> 7139<phrase>
@@ -7453,15 +7142,12 @@
7453 user: core 7142 user: core
7454 <source> 7143 <source>
7455 *: "Any Other = No" 7144 *: "Any Other = No"
7456 archosplayer: none
7457 </source> 7145 </source>
7458 <dest> 7146 <dest>
7459 *: "Ð›ÑŽÐ±Ð°Ñ Ð´Ñ€ÑƒÐ³Ð°Ñ = Ðет" 7147 *: "Ð›ÑŽÐ±Ð°Ñ Ð´Ñ€ÑƒÐ³Ð°Ñ = Ðет"
7460 archosplayer: none
7461 </dest> 7148 </dest>
7462 <voice> 7149 <voice>
7463 *: "" 7150 *: ""
7464 archosplayer: none
7465 </voice> 7151 </voice>
7466</phrase> 7152</phrase>
7467<phrase> 7153<phrase>
@@ -7768,11 +7454,9 @@
7768 user: core 7454 user: core
7769 <source> 7455 <source>
7770 *: "End of Song List" 7456 *: "End of Song List"
7771 archosplayer: "End of List"
7772 </source> 7457 </source>
7773 <dest> 7458 <dest>
7774 *: "Конец ÑпиÑка" 7459 *: "Конец ÑпиÑка"
7775 archosplayer: "Конец ÑпиÑка"
7776 </dest> 7460 </dest>
7777 <voice> 7461 <voice>
7778 *: "Конец ÑпиÑка" 7462 *: "Конец ÑпиÑка"
@@ -8202,20 +7886,6 @@
8202 </voice> 7886 </voice>
8203</phrase> 7887</phrase>
8204<phrase> 7888<phrase>
8205 id: LANG_REPLACE
8206 desc: in onplay menu. Replace the current playlist with a new one.
8207 user: core
8208 <source>
8209 *: "Play Next"
8210 </source>
8211 <dest>
8212 *: "Играть Ñледующий"
8213 </dest>
8214 <voice>
8215 *: "Играть Ñледующий"
8216 </voice>
8217</phrase>
8218<phrase>
8219 id: LANG_CATALOG 7889 id: LANG_CATALOG
8220 desc: in main menu and onplay menu 7890 desc: in main menu and onplay menu
8221 user: core 7891 user: core
@@ -8720,20 +8390,6 @@
8720 </voice> 8390 </voice>
8721</phrase> 8391</phrase>
8722<phrase> 8392<phrase>
8723 id: LANG_CATALOG_ADD_TO
8724 desc: in onplay playlist catalogue submenu
8725 user: core
8726 <source>
8727 *: "Add to Playlist"
8728 </source>
8729 <dest>
8730 *: "Добавить в ÑпиÑок"
8731 </dest>
8732 <voice>
8733 *: "Добавить в ÑпиÑок"
8734 </voice>
8735</phrase>
8736<phrase>
8737 id: LANG_BUTTONLIGHT_BRIGHTNESS 8393 id: LANG_BUTTONLIGHT_BRIGHTNESS
8738 desc: in settings_menu 8394 desc: in settings_menu
8739 user: core 8395 user: core
@@ -8917,20 +8573,6 @@
8917 </voice> 8573 </voice>
8918</phrase> 8574</phrase>
8919<phrase> 8575<phrase>
8920 id: LANG_BOOKMARK_CONTEXT_DELETE
8921 desc: bookmark context menu, delete this bookmark
8922 user: core
8923 <source>
8924 *: "Delete"
8925 </source>
8926 <dest>
8927 *: "Удалить"
8928 </dest>
8929 <voice>
8930 *: "Удалить"
8931 </voice>
8932</phrase>
8933<phrase>
8934 id: LANG_CROSSFEED_CROSS_GAIN 8576 id: LANG_CROSSFEED_CROSS_GAIN
8935 desc: in crossfeed settings 8577 desc: in crossfeed settings
8936 user: core 8578 user: core
@@ -9139,20 +8781,6 @@
9139 </voice> 8781 </voice>
9140</phrase> 8782</phrase>
9141<phrase> 8783<phrase>
9142 id: LANG_CATALOG_VIEW
9143 desc: in onplay playlist catalogue submenu
9144 user: core
9145 <source>
9146 *: "View Catalogue"
9147 </source>
9148 <dest>
9149 *: "Каталог"
9150 </dest>
9151 <voice>
9152 *: "Каталог"
9153 </voice>
9154</phrase>
9155<phrase>
9156 id: VOICE_EXT_CUESHEET 8784 id: VOICE_EXT_CUESHEET
9157 desc: 8785 desc:
9158 user: core 8786 user: core
@@ -9410,10 +9038,10 @@
9410</phrase> 9038</phrase>
9411<phrase> 9039<phrase>
9412 id: LANG_AUDIOSCROBBLER 9040 id: LANG_AUDIOSCROBBLER
9413 desc: "Last.fm Log" in the playback menu 9041 desc: "Last.fm Logger" in Plugin/apps/scrobbler
9414 user: core 9042 user: core
9415 <source> 9043 <source>
9416 *: "Last.fm Log" 9044 *: "Last.fm Logger"
9417 </source> 9045 </source>
9418 <dest> 9046 <dest>
9419 *: "Отчёт Ð´Ð»Ñ Last.fm" 9047 *: "Отчёт Ð´Ð»Ñ Last.fm"
@@ -9534,7 +9162,7 @@
9534 *: "%s не ÑущеÑтвует" 9162 *: "%s не ÑущеÑтвует"
9535 </dest> 9163 </dest>
9536 <voice> 9164 <voice>
9537 *: "" 9165 *: "%s не ÑущеÑтвует"
9538 </voice> 9166 </voice>
9539</phrase> 9167</phrase>
9540<phrase> 9168<phrase>
@@ -9583,23 +9211,6 @@
9583 </voice> 9211 </voice>
9584</phrase> 9212</phrase>
9585<phrase> 9213<phrase>
9586 id: LANG_SET_AS_REC_DIR
9587 desc: used in the onplay menu to set a recording dir
9588 user: core
9589 <source>
9590 *: none
9591 recording: "Set As Recording Directory"
9592 </source>
9593 <dest>
9594 *: none
9595 recording: "УÑтановить как папку запиÑи"
9596 </dest>
9597 <voice>
9598 *: none
9599 recording: "УÑтановить как папку запиÑи"
9600 </voice>
9601</phrase>
9602<phrase>
9603 id: LANG_FM_MENU 9214 id: LANG_FM_MENU
9604 desc: fm menu title 9215 desc: fm menu title
9605 user: core 9216 user: core
@@ -10604,20 +10215,6 @@
10604 </voice> 10215 </voice>
10605</phrase> 10216</phrase>
10606<phrase> 10217<phrase>
10607 id: LANG_SCROLLBAR_POSITION
10608 desc: in Settings -> General -> Display -> Status-/Scrollbar
10609 user: core
10610 <source>
10611 *: "Scroll Bar Position"
10612 </source>
10613 <dest>
10614 *: "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾Ð»Ð¾ÑÑ‹ прокрутки"
10615 </dest>
10616 <voice>
10617 *: "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾Ð»Ð¾ÑÑ‹ прокрутки"
10618 </voice>
10619</phrase>
10620<phrase>
10621 id: LANG_REMOTE_STATUSBAR 10218 id: LANG_REMOTE_STATUSBAR
10622 desc: in Settings -> General -> Display -> statusbar 10219 desc: in Settings -> General -> Display -> statusbar
10623 user: core 10220 user: core
@@ -10694,20 +10291,6 @@
10694 </voice> 10291 </voice>
10695</phrase> 10292</phrase>
10696<phrase> 10293<phrase>
10697 id: LANG_INSERT_LAST_SHUFFLED
10698 desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
10699 user: core
10700 <source>
10701 *: "Insert Last Shuffled"
10702 </source>
10703 <dest>
10704 *: "Добавить Ñлучайный к концу"
10705 </dest>
10706 <voice>
10707 *: "Добавить Ñлучайный к концу"
10708 </voice>
10709</phrase>
10710<phrase>
10711 id: LANG_COMPRESSOR_SOFT_KNEE 10294 id: LANG_COMPRESSOR_SOFT_KNEE
10712 desc: in sound settings 10295 desc: in sound settings
10713 user: core 10296 user: core
@@ -11270,20 +10853,6 @@
11270 </voice> 10853 </voice>
11271</phrase> 10854</phrase>
11272<phrase> 10855<phrase>
11273 id: LANG_STATUSBAR_CUSTOM
11274 desc: if this translation is compatible with LANG_CHANNEL_CUSTOM, then please use the same translation. it can be combined later then
11275 user: core
11276 <source>
11277 *: "Custom"
11278 </source>
11279 <dest>
11280 *: "ПользовательÑкие"
11281 </dest>
11282 <voice>
11283 *: "ПользовательÑкие"
11284 </voice>
11285</phrase>
11286<phrase>
11287 id: LANG_SCROLLBAR_WIDTH 10856 id: LANG_SCROLLBAR_WIDTH
11288 desc: in Settings -> General -> Display -> Status-/Scrollbar 10857 desc: in Settings -> General -> Display -> Status-/Scrollbar
11289 user: core 10858 user: core
@@ -11619,20 +11188,6 @@
11619 </voice> 11188 </voice>
11620</phrase> 11189</phrase>
11621<phrase> 11190<phrase>
11622 id: LANG_SET_AS_START_DIR
11623 desc: used in the onplay menu to set a starting browser dir
11624 user: core
11625 <source>
11626 *: "Start File Browser Here"
11627 </source>
11628 <dest>
11629 *: "Ðачинать показ файлов здеÑÑŒ"
11630 </dest>
11631 <voice>
11632 *: "Ðачинать показ файлов здеÑÑŒ"
11633 </voice>
11634</phrase>
11635<phrase>
11636 id: LANG_FM_RSSI 11191 id: LANG_FM_RSSI
11637 desc: Signal strength of a received FM station 11192 desc: Signal strength of a received FM station
11638 user: core 11193 user: core
@@ -11737,20 +11292,6 @@
11737 </voice> 11292 </voice>
11738</phrase> 11293</phrase>
11739<phrase> 11294<phrase>
11740 id: LANG_SET_AS_PLAYLISTCAT_DIR
11741 desc: used in the onplay menu to set a playlist catalogue dir
11742 user: core
11743 <source>
11744 *: "Set As Playlist Catalogue Directory"
11745 </source>
11746 <dest>
11747 *: "УÑтановить как папку каталога ÑпиÑков"
11748 </dest>
11749 <voice>
11750 *: "УÑтановить как папку каталога ÑпиÑков"
11751 </voice>
11752</phrase>
11753<phrase>
11754 id: LANG_LIST_LINE_PADDING 11295 id: LANG_LIST_LINE_PADDING
11755 desc: list padding, in display settings 11296 desc: list padding, in display settings
11756 user: core 11297 user: core
@@ -11796,20 +11337,6 @@
11796 </voice> 11337 </voice>
11797</phrase> 11338</phrase>
11798<phrase> 11339<phrase>
11799 id: LANG_AUTOMATIC
11800 desc: generic automatic
11801 user: core
11802 <source>
11803 *: "Automatic"
11804 </source>
11805 <dest>
11806 *: "Ðвтоматич."
11807 </dest>
11808 <voice>
11809 *: "ÐвтоматичеÑки"
11810 </voice>
11811</phrase>
11812<phrase>
11813 id: LANG_SLEEP_TIMER_CANCEL_CURRENT 11340 id: LANG_SLEEP_TIMER_CANCEL_CURRENT
11814 desc: shown instead of sleep timer when it's running 11341 desc: shown instead of sleep timer when it's running
11815 user: core 11342 user: core
@@ -12342,7 +11869,7 @@
12342 desc: Selective Actions 11869 desc: Selective Actions
12343 user: core 11870 user: core
12344 <source> 11871 <source>
12345 *: "Seek" 11872 *: "Exempt Seek"
12346 </source> 11873 </source>
12347 <dest> 11874 <dest>
12348 *: "Перемотка" 11875 *: "Перемотка"
@@ -12401,7 +11928,7 @@
12401 desc: Softlock behaviour setting 11928 desc: Softlock behaviour setting
12402 user: core 11929 user: core
12403 <source> 11930 <source>
12404 *: "Disable Notify" 11931 *: "Disable Locked Reminders"
12405 </source> 11932 </source>
12406 <dest> 11933 <dest>
12407 *: "Запрет оповещениÑ" 11934 *: "Запрет оповещениÑ"
@@ -12516,7 +12043,7 @@
12516 desc: Selective Actions 12043 desc: Selective Actions
12517 user: core 12044 user: core
12518 <source> 12045 <source>
12519 *: "Skip" 12046 *: "Exempt Skip"
12520 </source> 12047 </source>
12521 <dest> 12048 <dest>
12522 *: "Смена трека" 12049 *: "Смена трека"
@@ -12596,20 +12123,6 @@
12596 </voice> 12123 </voice>
12597</phrase> 12124</phrase>
12598<phrase> 12125<phrase>
12599 id: LANG_NO_VIEWERS
12600 desc: text for splash to indicate that no viewers are available
12601 user: core
12602 <source>
12603 *: "No viewers found"
12604 </source>
12605 <dest>
12606 *: "ПроÑмотрщиков не найдено"
12607 </dest>
12608 <voice>
12609 *: "ПроÑмотрщиков не найдено"
12610 </voice>
12611</phrase>
12612<phrase>
12613 id: LANG_PBE 12126 id: LANG_PBE
12614 desc: in sound settings 12127 desc: in sound settings
12615 user: core 12128 user: core
@@ -12712,7 +12225,7 @@
12712 desc: Selective Actions 12225 desc: Selective Actions
12713 user: core 12226 user: core
12714 <source> 12227 <source>
12715 *: "Play" 12228 *: "Exempt Play"
12716 </source> 12229 </source>
12717 <dest> 12230 <dest>
12718 *: "ВоÑпроизведение или пауза" 12231 *: "ВоÑпроизведение или пауза"
@@ -13034,20 +12547,6 @@
13034 </voice> 12547 </voice>
13035</phrase> 12548</phrase>
13036<phrase> 12549<phrase>
13037 id: LANG_CLEAR_PLAYLIST
13038 desc: in the pictureflow main menu
13039 user: core
13040 <source>
13041 *: "Clear playlist"
13042 </source>
13043 <dest>
13044 *: "ОчиÑтить ÑпиÑок воÑпроизведениÑ"
13045 </dest>
13046 <voice>
13047 *: "ОчиÑтить ÑпиÑок воÑпроизведениÑ"
13048 </voice>
13049</phrase>
13050<phrase>
13051 id: LANG_HIDE_ALBUM_TITLE 12550 id: LANG_HIDE_ALBUM_TITLE
13052 desc: in the pictureflow settings 12551 desc: in the pictureflow settings
13053 user: core 12552 user: core
@@ -13091,7 +12590,7 @@
13091</phrase> 12590</phrase>
13092<phrase> 12591<phrase>
13093 id: LANG_DIRECT 12592 id: LANG_DIRECT
13094 desc: in the pictureflow settings 12593 desc: in the pictureflow settings, also a volume adjustment mode
13095 user: core 12594 user: core
13096 <source> 12595 <source>
13097 *: "Direct" 12596 *: "Direct"
@@ -13160,34 +12659,6 @@
13160 </voice> 12659 </voice>
13161</phrase> 12660</phrase>
13162<phrase> 12661<phrase>
13163 id: LANG_PLAYLIST_CLEARED
13164 desc: in the pictureflow splash messages
13165 user: core
13166 <source>
13167 *: "Playlist Cleared"
13168 </source>
13169 <dest>
13170 *: "СпиÑок воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½"
13171 </dest>
13172 <voice>
13173 *: "СпиÑок воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½"
13174 </voice>
13175</phrase>
13176<phrase>
13177 id: LANG_ADDED_TO_PLAYLIST
13178 desc: in the pictureflow splash messages
13179 user: core
13180 <source>
13181 *: "Added to playlist"
13182 </source>
13183 <dest>
13184 *: "Добавлено к ÑпиÑку воÑпроизведениÑ"
13185 </dest>
13186 <voice>
13187 *: "Добавлено к ÑпиÑку воÑпроизведениÑ"
13188 </voice>
13189</phrase>
13190<phrase>
13191 id: LANG_ERROR_WRITING_CONFIG 12662 id: LANG_ERROR_WRITING_CONFIG
13192 desc: in the pictureflow splash messages 12663 desc: in the pictureflow splash messages
13193 user: core 12664 user: core
@@ -13573,16 +13044,16 @@
13573</phrase> 13044</phrase>
13574<phrase> 13045<phrase>
13575 id: LANG_PLAYTIME_REMAINING 13046 id: LANG_PLAYTIME_REMAINING
13576 desc: playing time screen 13047 desc: deprecated
13577 user: core 13048 user: core
13578 <source> 13049 <source>
13579 *: "Playlist remaining:" 13050 *: ""
13580 </source> 13051 </source>
13581 <dest> 13052 <dest>
13582 *: "Ð’ ÑпиÑке оÑталоÑÑŒ:" 13053 *: ""
13583 </dest> 13054 </dest>
13584 <voice> 13055 <voice>
13585 *: "Ð’ ÑпиÑке оÑталоÑÑŒ" 13056 *: ""
13586 </voice> 13057 </voice>
13587</phrase> 13058</phrase>
13588<phrase> 13059<phrase>
@@ -13784,7 +13255,7 @@
13784 *: "Думаю..." 13255 *: "Думаю..."
13785 </dest> 13256 </dest>
13786 <voice> 13257 <voice>
13787 *: "" 13258 *: "Думаю..."
13788 </voice> 13259 </voice>
13789</phrase> 13260</phrase>
13790<phrase> 13261<phrase>
@@ -14945,62 +14416,6 @@
14945 </voice> 14416 </voice>
14946</phrase> 14417</phrase>
14947<phrase> 14418<phrase>
14948 id: LANG_PROPERTIES_ARTIST
14949 desc: in properties plugin
14950 user: core
14951 <source>
14952 *: "[Artist]"
14953 </source>
14954 <dest>
14955 *: "[ИÑполнитель]"
14956 </dest>
14957 <voice>
14958 *: "ИÑполнитель"
14959 </voice>
14960</phrase>
14961<phrase>
14962 id: LANG_PROPERTIES_TITLE
14963 desc: in properties plugin
14964 user: core
14965 <source>
14966 *: "[Title]"
14967 </source>
14968 <dest>
14969 *: "[Ðазвание]"
14970 </dest>
14971 <voice>
14972 *: "Ðазвание"
14973 </voice>
14974</phrase>
14975<phrase>
14976 id: LANG_PROPERTIES_ALBUM
14977 desc: in properties plugin
14978 user: core
14979 <source>
14980 *: "[Album]"
14981 </source>
14982 <dest>
14983 *: "[Ðльбом]"
14984 </dest>
14985 <voice>
14986 *: "Ðльбом"
14987 </voice>
14988</phrase>
14989<phrase>
14990 id: LANG_PROPERTIES_DURATION
14991 desc: in properties plugin
14992 user: core
14993 <source>
14994 *: "[Duration]"
14995 </source>
14996 <dest>
14997 *: "[ПродолжительноÑÑ‚ÑŒ]"
14998 </dest>
14999 <voice>
15000 *: "ПродолжительноÑÑ‚ÑŒ"
15001 </voice>
15002</phrase>
15003<phrase>
15004 id: LANG_PROPERTIES_SUBDIRS 14419 id: LANG_PROPERTIES_SUBDIRS
15005 desc: in properties plugin 14420 desc: in properties plugin
15006 user: core 14421 user: core
@@ -15292,16 +14707,13 @@
15292 desc: prefix for elapsed playtime announcement 14707 desc: prefix for elapsed playtime announcement
15293 user: core 14708 user: core
15294 <source> 14709 <source>
15295 *: none 14710 *: "Elapsed"
15296 hotkey: "Elapsed"
15297 </source> 14711 </source>
15298 <dest> 14712 <dest>
15299 *: none 14713 *: "Проиграно"
15300 hotkey: "Проиграно"
15301 </dest> 14714 </dest>
15302 <voice> 14715 <voice>
15303 *: none 14716 *: "Проиграно"
15304 hotkey: "Проиграно"
15305 </voice> 14717 </voice>
15306</phrase> 14718</phrase>
15307<phrase> 14719<phrase>
@@ -15649,20 +15061,6 @@
15649 </voice> 15061 </voice>
15650</phrase> 15062</phrase>
15651 <phrase> 15063 <phrase>
15652 id: LANG_CLEAR_LIST_AND_PLAY_NEXT
15653 desc: in onplay menu. Replace current playlist with selected tracks
15654 user: core
15655 <source>
15656 *: "Clear List & Play Next"
15657 </source>
15658 <dest>
15659 *: "ОчиÑтить ÑпиÑок и играть Ñледующий"
15660 </dest>
15661 <voice>
15662 *: "ОчиÑтить ÑпиÑок и играть Ñледующий"
15663 </voice>
15664</phrase>
15665<phrase>
15666 id: LANG_QUEUE_MENU 15064 id: LANG_QUEUE_MENU
15667 desc: in onplay menu 15065 desc: in onplay menu
15668 user: core 15066 user: core
@@ -15719,20 +15117,6 @@
15719 </voice> 15117 </voice>
15720</phrase> 15118</phrase>
15721<phrase> 15119<phrase>
15722 id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
15723 desc: in onplay menu. Replace current playlist with selected tracks in random order.
15724 user: core
15725 <source>
15726 *: "Clear List & Play Shuffled"
15727 </source>
15728 <dest>
15729 *: "ОчиÑтить ÑпиÑок и играть в Ñлучайном порÑдке"
15730 </dest>
15731 <voice>
15732 *: "ОчиÑтить ÑпиÑок и играть в Ñлучайном порÑдке"
15733 </voice>
15734</phrase>
15735<phrase>
15736 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY 15120 id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
15737 desc: disable all softlock notifications 15121 desc: disable all softlock notifications
15738 user: core 15122 user: core
@@ -15775,20 +15159,6 @@
15775 </voice> 15159 </voice>
15776</phrase> 15160</phrase>
15777<phrase> 15161<phrase>
15778 id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
15779 desc: reload playlist after saving
15780 user: core
15781 <source>
15782 *: "Reload After Saving"
15783 </source>
15784 <dest>
15785 *: "Перезагрузить поÑле ÑохранениÑ"
15786 </dest>
15787 <voice>
15788 *: "Перезагрузить поÑле ÑохранениÑ"
15789 </voice>
15790</phrase>
15791<phrase>
15792 id: LANG_FILTER_LINEAR_FAST 15162 id: LANG_FILTER_LINEAR_FAST
15793 desc: in sound settings 15163 desc: in sound settings
15794 user: core 15164 user: core
@@ -15942,132 +15312,6 @@
15942 </voice> 15312 </voice>
15943</phrase> 15313</phrase>
15944<phrase> 15314<phrase>
15945 id: LANG_PROPERTIES_ALBUMARTIST
15946 desc: in properties plugin
15947 user: core
15948 <source>
15949 *: "[Album Artist]"
15950 </source>
15951 <dest>
15952 *: "[ИÑполнитель альбома]"
15953 </dest>
15954 <voice>
15955 *: "ИÑполнитель альбома"
15956 </voice>
15957</phrase>
15958<phrase>
15959 id: LANG_PROPERTIES_GENRE
15960 desc: in properties plugin
15961 user: core
15962 <source>
15963 *: "[Genre]"
15964 </source>
15965 <dest>
15966 *: "[Жанр]"
15967 </dest>
15968 <voice>
15969 *: "Жанр"
15970 </voice>
15971</phrase>
15972<phrase>
15973 id: LANG_PROPERTIES_COMMENT
15974 desc: in properties plugin
15975 user: core
15976 <source>
15977 *: "[Comment]"
15978 </source>
15979 <dest>
15980 *: "[Комментарий]"
15981 </dest>
15982 <voice>
15983 *: "Комментарий"
15984 </voice>
15985</phrase>
15986<phrase>
15987 id: LANG_PROPERTIES_COMPOSER
15988 desc: in properties plugin
15989 user: core
15990 <source>
15991 *: "[Composer]"
15992 </source>
15993 <dest>
15994 *: "[Композитор]"
15995 </dest>
15996 <voice>
15997 *: "Композитор"
15998 </voice>
15999</phrase>
16000<phrase>
16001 id: LANG_PROPERTIES_YEAR
16002 desc: in properties plugin
16003 user: core
16004 <source>
16005 *: "[Year]"
16006 </source>
16007 <dest>
16008 *: "[Год]"
16009 </dest>
16010 <voice>
16011 *: "Год"
16012 </voice>
16013</phrase>
16014<phrase>
16015 id: LANG_PROPERTIES_TRACKNUM
16016 desc: in properties plugin
16017 user: core
16018 <source>
16019 *: "[Tracknum]"
16020 </source>
16021 <dest>
16022 *: "[Ðомер трека]"
16023 </dest>
16024 <voice>
16025 *: "Ðомер трека"
16026 </voice>
16027</phrase>
16028<phrase>
16029 id: LANG_PROPERTIES_DISCNUM
16030 desc: in properties plugin
16031 user: core
16032 <source>
16033 *: "[Discnum]"
16034 </source>
16035 <dest>
16036 *: "[Ðомер диÑка]"
16037 </dest>
16038 <voice>
16039 *: "Ðомер диÑка"
16040 </voice>
16041</phrase>
16042<phrase>
16043 id: LANG_PROPERTIES_FREQUENCY
16044 desc: in properties plugin
16045 user: core
16046 <source>
16047 *: "[Frequency]"
16048 </source>
16049 <dest>
16050 *: "[ЧаÑтота]"
16051 </dest>
16052 <voice>
16053 *: "ЧаÑтота"
16054 </voice>
16055</phrase>
16056<phrase>
16057 id: LANG_PROPERTIES_BITRATE
16058 desc: in properties plugin
16059 user: core
16060 <source>
16061 *: "[Bitrate]"
16062 </source>
16063 <dest>
16064 *: "[Битрейт]"
16065 </dest>
16066 <voice>
16067 *: "Битрейт"
16068 </voice>
16069</phrase>
16070<phrase>
16071 id: LANG_SINGLE_MODE 15315 id: LANG_SINGLE_MODE
16072 desc: single mode 15316 desc: single mode
16073 user: core 15317 user: core
@@ -16356,18 +15600,18 @@
16356 user: core 15600 user: core
16357 <source> 15601 <source>
16358 *: none 15602 *: none
16359 ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "Clear settings when hold switch is on during startup"
16360 clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup" 15603 clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup"
15604 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
16361 </source> 15605 </source>
16362 <dest> 15606 <dest>
16363 *: none 15607 *: none
16364 ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "СброÑить наÑтройки при включенной блокировке во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
16365 clear_settings_on_hold, iriverh10: "СброÑит наÑтройки при нажатой кнопке ÑброÑа во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸" 15608 clear_settings_on_hold, iriverh10: "СброÑит наÑтройки при нажатой кнопке ÑброÑа во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
15609 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "СброÑить наÑтройки при включенной блокировке во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
16366 </dest> 15610 </dest>
16367 <voice> 15611 <voice>
16368 *: none 15612 *: none
16369 ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "СброÑить наÑтройки при включенной блокировке во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
16370 clear_settings_on_hold, iriverh10: "СброÑит наÑтройки при нажатой кнопке ÑброÑа во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸" 15613 clear_settings_on_hold, iriverh10: "СброÑит наÑтройки при нажатой кнопке ÑброÑа во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
15614 ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "СброÑить наÑтройки при включенной блокировке во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
16371 </voice> 15615 </voice>
16372</phrase> 15616</phrase>
16373<phrase> 15617<phrase>
@@ -16634,3 +15878,603 @@
16634 *: "РуÑÑкий" 15878 *: "РуÑÑкий"
16635 </voice> 15879 </voice>
16636</phrase> 15880</phrase>
15881<phrase>
15882 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
15883 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
15884 user: core
15885 <source>
15886 *: "Selection too big, %d random tracks will be picked from it"
15887 </source>
15888 <dest>
15889 *: "Выбор Ñлишком велик, из него будут выбраны %d Ñлучайных треков"
15890 </dest>
15891 <voice>
15892 *: "Выбор Ñлишком велик, из него будет выбрано меньше Ñлучайных треков"
15893 </voice>
15894</phrase>
15895<phrase>
15896 id: LANG_FILTER_SHORT_SHARP
15897 desc: in sound settings
15898 user: core
15899 <source>
15900 *: none
15901 filter_roll_off: "Short Sharp"
15902 </source>
15903 <dest>
15904 *: none
15905 filter_roll_off: "Короткий ОÑтрый"
15906 </dest>
15907 <voice>
15908 *: none
15909 filter_roll_off: "Короткий ОÑтрый"
15910 </voice>
15911</phrase>
15912<phrase>
15913 id: LANG_FILTER_SHORT_SLOW
15914 desc: in sound settings
15915 user: core
15916 <source>
15917 *: none
15918 filter_roll_off: "Short Slow"
15919 </source>
15920 <dest>
15921 *: none
15922 filter_roll_off: "Короткий Медленный"
15923 </dest>
15924 <voice>
15925 *: none
15926 filter_roll_off: "Короткий Медленный"
15927 </voice>
15928</phrase>
15929<phrase>
15930 id: LANG_MIKMOD_HQMIXER
15931 desc: in mikmod settings menu
15932 user: core
15933 <source>
15934 *: "HQ Mixer"
15935 lowmem: none
15936 </source>
15937 <dest>
15938 *: "HQ Микшер"
15939 lowmem: none
15940 </dest>
15941 <voice>
15942 *: "High Quality Микшер"
15943 lowmem: none
15944 </voice>
15945</phrase>
15946<phrase>
15947 id: LANG_MIKMOD_SAMPLERATE
15948 desc: in mikmod settings menu
15949 user: core
15950 <source>
15951 *: "Sample Rate"
15952 lowmem: none
15953 </source>
15954 <dest>
15955 *: "ЧаÑтота диÑкретизации"
15956 lowmem: none
15957 </dest>
15958 <voice>
15959 *: "ЧаÑтота диÑкретизации"
15960 lowmem: none
15961 </voice>
15962</phrase>
15963<phrase>
15964 id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
15965 desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
15966 user: core
15967 <source>
15968 *: ""
15969 </source>
15970 <dest>
15971 *: ""
15972 </dest>
15973 <voice>
15974 *: ""
15975 </voice>
15976</phrase>
15977<phrase>
15978 id: LANG_VOICED_DATE_FORMAT
15979 desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
15980 user: core
15981 <source>
15982 *: "dAY"
15983 </source>
15984 <dest>
15985 *: "День"
15986 </dest>
15987 <voice>
15988 *: ""
15989 </voice>
15990</phrase>
15991<phrase>
15992 id: LANG_DATABASE_DIR
15993 desc: in database settings menu
15994 user: core
15995 <source>
15996 *: "Database Directory"
15997 </source>
15998 <dest>
15999 *: "Каталог базы данных"
16000 </dest>
16001 <voice>
16002 *: "Каталог базы данных"
16003 </voice>
16004</phrase>
16005<phrase>
16006 id: LANG_REMOVE_QUEUED_TRACKS
16007 desc: Confirmation dialog
16008 user: core
16009 <source>
16010 *: "Remove Queued Tracks?"
16011 </source>
16012 <dest>
16013 *: "УдалÑÑ‚ÑŒ треки, поÑтавленные в очередь?"
16014 </dest>
16015 <voice>
16016 *: "УдалÑÑ‚ÑŒ треки, поÑтавленные в очередь?"
16017 </voice>
16018</phrase>
16019<phrase>
16020 id: LANG_QUICK_IGNORE_DIRACHE
16021 desc: in Settings
16022 user: core
16023 <source>
16024 *: "Quick (Ignore Directory Cache)"
16025 </source>
16026 <dest>
16027 *: "Quick (игнорировать кÑш каталогов)"
16028 </dest>
16029 <voice>
16030 *: "Quick (игнорировать кÑш каталогов)"
16031 </voice>
16032</phrase>
16033<phrase>
16034 id: LANG_WPS
16035 desc: in Settings
16036 user: core
16037 <source>
16038 *: "What's Playing Screen"
16039 </source>
16040 <dest>
16041 *: "Что проиÑходит на Ñкране воÑпроизведениÑ"
16042 </dest>
16043 <voice>
16044 *: "Что проиÑходит на Ñкране воÑпроизведениÑ"
16045 </voice>
16046</phrase>
16047<phrase>
16048 id: LANG_DEFAULT_BROWSER
16049 desc: in Settings
16050 user: core
16051 <source>
16052 *: "Default Browser"
16053 </source>
16054 <dest>
16055 *: "Браузер по умолчанию"
16056 </dest>
16057 <voice>
16058 *: "Браузер по умолчанию"
16059 </voice>
16060</phrase>
16061<phrase>
16062 id: LANG_AMAZE_MENU
16063 desc: Amaze game
16064 user: core
16065 <source>
16066 *: "Amaze Main Menu"
16067 </source>
16068 <dest>
16069 *: "Главное меню Amaze"
16070 </dest>
16071 <voice>
16072 *: "Главное меню Amaze"
16073 </voice>
16074</phrase>
16075<phrase>
16076 id: LANG_SET_MAZE_SIZE
16077 desc: Maze size in Amaze game
16078 user: core
16079 <source>
16080 *: "Set Maze Size"
16081 </source>
16082 <dest>
16083 *: "УÑтановить размер Maze "
16084 </dest>
16085 <voice>
16086 *: "УÑтановить размер Maze "
16087 </voice>
16088</phrase>
16089<phrase>
16090 id: LANG_VIEW_MAP
16091 desc: Map in Amaze game
16092 user: core
16093 <source>
16094 *: "View Map"
16095 </source>
16096 <dest>
16097 *: "ПоÑмотреть карту"
16098 </dest>
16099 <voice>
16100 *: "ПоÑмотреть карту"
16101 </voice>
16102</phrase>
16103<phrase>
16104 id: LANG_SHOW_COMPASS
16105 desc: Compass in Amaze game
16106 user: core
16107 <source>
16108 *: "Show Compass"
16109 </source>
16110 <dest>
16111 *: "Показать компаÑ"
16112 </dest>
16113 <voice>
16114 *: "Показать компаÑ"
16115 </voice>
16116</phrase>
16117<phrase>
16118 id: LANG_SHOW_MAP
16119 desc: Map in Amaze game
16120 user: core
16121 <source>
16122 *: "Show Map"
16123 </source>
16124 <dest>
16125 *: "Показать карту"
16126 </dest>
16127 <voice>
16128 *: "Показать карту"
16129 </voice>
16130</phrase>
16131<phrase>
16132 id: LANG_REMEMBER_PATH
16133 desc: Map in Amaze game
16134 user: core
16135 <source>
16136 *: "Remember Path"
16137 </source>
16138 <dest>
16139 *: "Запомнить путь"
16140 </dest>
16141 <voice>
16142 *: "Запомнить путь"
16143 </voice>
16144</phrase>
16145<phrase>
16146 id: LANG_USE_LARGE_TILES
16147 desc: Map in Amaze game
16148 user: core
16149 <source>
16150 *: "Use Large Tiles"
16151 </source>
16152 <dest>
16153 *: "ИÑпользуйте большие Tiles"
16154 </dest>
16155 <voice>
16156 *: "ИÑпользуйте большие Tiles"
16157 </voice>
16158</phrase>
16159<phrase>
16160 id: LANG_SHOW_SOLUTION
16161 desc: Map in Amaze game
16162 user: core
16163 <source>
16164 *: "Show Solution"
16165 </source>
16166 <dest>
16167 *: "Показать решение"
16168 </dest>
16169 <voice>
16170 *: "Показать решение"
16171 </voice>
16172</phrase>
16173<phrase>
16174 id: LANG_QUIT_WITHOUT_SAVING
16175 desc:
16176 user: core
16177 <source>
16178 *: "Quit without saving"
16179 </source>
16180 <dest>
16181 *: "Выйти без ÑохранениÑ"
16182 </dest>
16183 <voice>
16184 *: "Выйти без ÑохранениÑ"
16185 </voice>
16186</phrase>
16187<phrase>
16188 id: LANG_GENERATING_MAZE
16189 desc: Amaze game
16190 user: core
16191 <source>
16192 *: "Generating maze..."
16193 </source>
16194 <dest>
16195 *: "Создать лабиринт"
16196 </dest>
16197 <voice>
16198 *: "Создать лабиринт"
16199 </voice>
16200</phrase>
16201<phrase>
16202 id: LANG_YOU_WIN
16203 desc: Success in game
16204 user: core
16205 <source>
16206 *: "You win!"
16207 </source>
16208 <dest>
16209 *: "Ты победил"
16210 </dest>
16211 <voice>
16212 *: "Ты победил"
16213 </voice>
16214</phrase>
16215<phrase>
16216 id: LANG_YOU_CHEATED
16217 desc: Cheated in game
16218 user: core
16219 <source>
16220 *: "You cheated!"
16221 </source>
16222 <dest>
16223 *: "Ты обманул"
16224 </dest>
16225 <voice>
16226 *: "Ты обманул"
16227 </voice>
16228</phrase>
16229<phrase>
16230 id: LANG_DIFFICULTY_EASY
16231 desc: Game difficulty
16232 user: core
16233 <source>
16234 *: "Easy"
16235 </source>
16236 <dest>
16237 *: "Легкий"
16238 </dest>
16239 <voice>
16240 *: "Легкий"
16241 </voice>
16242</phrase>
16243<phrase>
16244 id: LANG_DIFFICULTY_MEDIUM
16245 desc: Game difficulty
16246 user: core
16247 <source>
16248 *: "Medium"
16249 </source>
16250 <dest>
16251 *: "Средний"
16252 </dest>
16253 <voice>
16254 *: "Средний"
16255 </voice>
16256</phrase>
16257<phrase>
16258 id: LANG_DIFFICULTY_HARD
16259 desc: Game difficulty
16260 user: core
16261 <source>
16262 *: "Hard"
16263 </source>
16264 <dest>
16265 *: "Сложный"
16266 </dest>
16267 <voice>
16268 *: "Сложный"
16269 </voice>
16270</phrase>
16271<phrase>
16272 id: LANG_DIFFICULTY_EXPERT
16273 desc: Game difficulty
16274 user: core
16275 <source>
16276 *: "Expert"
16277 </source>
16278 <dest>
16279 *: "ЭкÑперт"
16280 </dest>
16281 <voice>
16282 *: "ЭкÑперт"
16283 </voice>
16284</phrase>
16285<phrase>
16286 id: LANG_STEREOSW_MODE
16287 desc: Stereo Switch Mode
16288 user: core
16289 <source>
16290 *: "Stereo Switch Mode"
16291 </source>
16292 <dest>
16293 *: "Режим Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñтерео"
16294 </dest>
16295 <voice>
16296 *: "Режим Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñтерео"
16297 </voice>
16298</phrase>
16299<phrase>
16300 id: LANG_REVERSE
16301 desc: in settings_menu
16302 user: core
16303 <source>
16304 *: "Reverse"
16305 </source>
16306 <dest>
16307 *: "Обратный / ÐžÐ±Ñ€Ð°Ñ‚Ð½Ð°Ñ Ñторона"
16308 </dest>
16309 <voice>
16310 *: "Обратный / ÐžÐ±Ñ€Ð°Ñ‚Ð½Ð°Ñ Ñторона"
16311 </voice>
16312</phrase>
16313<phrase>
16314 id: LANG_ALWAYS_ZERO
16315 desc: in settings_menu
16316 user: core
16317 <source>
16318 *: "Always 0"
16319 </source>
16320 <dest>
16321 *: "Ð’Ñегда 0"
16322 </dest>
16323 <voice>
16324 *: "Ð’Ñегда 0"
16325 </voice>
16326</phrase>
16327<phrase>
16328 id: LANG_ALWAYS_ONE
16329 desc: in settings_menu
16330 user: core
16331 <source>
16332 *: "Always 1"
16333 </source>
16334 <dest>
16335 *: "Ð’Ñегда 1"
16336 </dest>
16337 <voice>
16338 *: "Ð’Ñегда 1"
16339 </voice>
16340</phrase>
16341<phrase>
16342 id: LANG_LEGAL_NOTICES
16343 desc: in system menu
16344 user: core
16345 <source>
16346 *: "Legal Notices"
16347 </source>
16348 <dest>
16349 *: "официальное уведомление"
16350 </dest>
16351 <voice>
16352 *: "официальное уведомление"
16353 </voice>
16354</phrase>
16355<phrase>
16356 id: LANG_ERROR_FORMATSTR
16357 desc: for general use
16358 user: core
16359 <source>
16360 *: "Error: %s"
16361 </source>
16362 <dest>
16363 *: "Ошибка: %s"
16364 </dest>
16365 <voice>
16366 *: "Ошибка"
16367 </voice>
16368</phrase>
16369<phrase>
16370 id: LANG_MIKMOD_SETTINGS
16371 desc: mikmod plugin
16372 user: core
16373 <source>
16374 *: "Mikmod Settings"
16375 </source>
16376 <dest>
16377 *: "Mikmod ÐаÑтройки"
16378 </dest>
16379 <voice>
16380 *: "Mik mod ÐаÑтройки"
16381 </voice>
16382</phrase>
16383<phrase>
16384 id: LANG_MIKMOD_MENU
16385 desc: mikmod plugin
16386 user: core
16387 <source>
16388 *: "Mikmod Menu"
16389 </source>
16390 <dest>
16391 *: "Mikmod Меню"
16392 </dest>
16393 <voice>
16394 *: "Mik mod Меню"
16395 </voice>
16396</phrase>
16397<phrase>
16398 id: LANG_CHESSBOX_MENU
16399 desc: chessbox plugin
16400 user: core
16401 <source>
16402 *: "Chessbox Menu"
16403 </source>
16404 <dest>
16405 *: "Шахматы меню"
16406 </dest>
16407 <voice>
16408 *: "Шахматы меню"
16409 </voice>
16410</phrase>
16411<phrase>
16412 id: VOICE_INVALID_VOICE_FILE
16413 desc: played if the voice file fails to load
16414 user: core
16415 <source>
16416 *: ""
16417 </source>
16418 <dest>
16419 *: ""
16420 </dest>
16421 <voice>
16422 *: "ÐедопуÑтимый голоÑовой файл"
16423 </voice>
16424</phrase>
16425<phrase>
16426 id: LANG_PERCENT_FORMAT
16427 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
16428 user: core
16429 <source>
16430 *: "%ld%%"
16431 </source>
16432 <dest>
16433 *: "%ld%%"
16434 </dest>
16435 <voice>
16436 *: none
16437 </voice>
16438</phrase>
16439<phrase>
16440 id: LANG_CHOOSE_FILE
16441 desc: file_picker plugin ask user to select a file
16442 user: core
16443 <source>
16444 *: "Choose File"
16445 </source>
16446 <dest>
16447 *: "Выберите файл"
16448 </dest>
16449 <voice>
16450 *: "Выберите файл"
16451 </voice>
16452</phrase>
16453<phrase>
16454 id: LANG_DISABLE_MAINMENU_SCROLLING
16455 desc: Disable main menu scrolling
16456 user: core
16457 <source>
16458 *: "Disable main menu scrolling"
16459 </source>
16460 <dest>
16461 *: "Отключить прокрутку главного меню"
16462 </dest>
16463 <voice>
16464 *: "Отключить прокрутку главного меню"
16465 </voice>
16466</phrase>
16467<phrase>
16468 id: LANG_REMAINING
16469 desc: Playing Time
16470 user: core
16471 <source>
16472 *: "Remaining"
16473 </source>
16474 <dest>
16475 *: "ОÑтавшийÑÑ"
16476 </dest>
16477 <voice>
16478 *: "ОÑтавшийÑÑ"
16479 </voice>
16480</phrase>
diff --git a/apps/lang/slovak.lang b/apps/lang/slovak.lang
index 107e9d425c..b01f2a38dc 100644
--- a/apps/lang/slovak.lang
+++ b/apps/lang/slovak.lang
@@ -15176,16 +15176,13 @@
15176 desc: prefix for elapsed playtime announcement 15176 desc: prefix for elapsed playtime announcement
15177 user: core 15177 user: core
15178 <source> 15178 <source>
15179 *: none 15179 *: "Elapsed"
15180 hotkey: "Elapsed"
15181 </source> 15180 </source>
15182 <dest> 15181 <dest>
15183 *: none 15182 *: "Trvanie"
15184 hotkey: "Trvanie"
15185 </dest> 15183 </dest>
15186 <voice> 15184 <voice>
15187 *: none 15185 *: "Trvanie"
15188 hotkey: "Trvanie"
15189 </voice> 15186 </voice>
15190</phrase> 15187</phrase>
15191<phrase> 15188<phrase>
diff --git a/apps/lang/srpski.lang b/apps/lang/srpski.lang
index e738109bbc..da940e8311 100644
--- a/apps/lang/srpski.lang
+++ b/apps/lang/srpski.lang
@@ -15249,16 +15249,13 @@
15249 desc: prefix for elapsed playtime announcement 15249 desc: prefix for elapsed playtime announcement
15250 user: core 15250 user: core
15251 <source> 15251 <source>
15252 *: none 15252 *: "Elapsed"
15253 hotkey: "Elapsed"
15254 </source> 15253 </source>
15255 <dest> 15254 <dest>
15256 *: none 15255 *: "Протекло"
15257 hotkey: "Протекло"
15258 </dest> 15256 </dest>
15259 <voice> 15257 <voice>
15260 *: none 15258 *: "Протекло"
15261 hotkey: "Протекло"
15262 </voice> 15259 </voice>
15263</phrase> 15260</phrase>
15264<phrase> 15261<phrase>
diff --git a/apps/lang/turkce.lang b/apps/lang/turkce.lang
index e7515b5f8f..560c63f5ca 100644
--- a/apps/lang/turkce.lang
+++ b/apps/lang/turkce.lang
@@ -15452,16 +15452,13 @@
15452 desc: prefix for elapsed playtime announcement 15452 desc: prefix for elapsed playtime announcement
15453 user: core 15453 user: core
15454 <source> 15454 <source>
15455 *: none 15455 *: "Elapsed"
15456 hotkey: "Elapsed"
15457 </source> 15456 </source>
15458 <dest> 15457 <dest>
15459 *: none 15458 *: "geçen"
15460 hotkey: "geçen"
15461 </dest> 15459 </dest>
15462 <voice> 15460 <voice>
15463 *: none 15461 *: "geçen"
15464 hotkey: "geçen"
15465 </voice> 15462 </voice>
15466</phrase> 15463</phrase>
15467<phrase> 15464<phrase>
diff --git a/apps/main.c b/apps/main.c
index 20b9a0bc55..1e8e872296 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -78,6 +78,10 @@
78#include "bootchart.h" 78#include "bootchart.h"
79#include "logdiskf.h" 79#include "logdiskf.h"
80#include "bootdata.h" 80#include "bootdata.h"
81#if defined(HAVE_DEVICEDATA)
82#include "devicedata.h"
83#endif
84
81#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 85#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
82#include "notification.h" 86#include "notification.h"
83#endif 87#endif
@@ -131,6 +135,12 @@
131#define MAIN_NORETURN_ATTR 135#define MAIN_NORETURN_ATTR
132#endif 136#endif
133 137
138#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
139#ifdef HAVE_MULTIVOLUME
140#include "pathfuncs.h" /* for init_volume_names */
141#endif
142#endif
143
134#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) 144#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA))
135#ifdef SIMULATOR 145#ifdef SIMULATOR
136#include "sim_tasks.h" 146#include "sim_tasks.h"
@@ -170,6 +180,9 @@ int main(void)
170 } 180 }
171 list_init(); 181 list_init();
172 tree_init(); 182 tree_init();
183#if defined(HAVE_DEVICEDATA) && !defined(BOOTLOADER) /* SIMULATOR */
184 verify_device_data();
185#endif
173 /* Keep the order of this 3 186 /* Keep the order of this 3
174 * Must be done before any code uses the multi-screen API */ 187 * Must be done before any code uses the multi-screen API */
175#ifdef HAVE_USBSTACK 188#ifdef HAVE_USBSTACK
@@ -381,6 +394,9 @@ static void init(void)
381 powermgmt_init(); 394 powermgmt_init();
382 backlight_init(); 395 backlight_init();
383 unicode_init(); 396 unicode_init();
397#ifdef HAVE_MULTIVOLUME
398 init_volume_names();
399#endif
384#ifdef SIMULATOR 400#ifdef SIMULATOR
385 sim_tasks_init(); 401 sim_tasks_init();
386#endif 402#endif
@@ -450,6 +466,10 @@ static void init(void)
450 verify_boot_data(); 466 verify_boot_data();
451#endif 467#endif
452 468
469#if defined(HAVE_DEVICEDATA) && !defined(BOOTLOADER)
470 verify_device_data();
471#endif
472
453 /* early early early! */ 473 /* early early early! */
454 filesystem_init(); 474 filesystem_init();
455 475
@@ -604,7 +624,7 @@ static void init(void)
604 { 624 {
605 lcd_clear_display(); 625 lcd_clear_display();
606 lcd_puts(0, 0, "No partition"); 626 lcd_puts(0, 0, "No partition");
607 lcd_puts(0, 1, "found."); 627 lcd_putsf(0, 1, "found (%d).", rc);
608#ifndef USB_NONE 628#ifndef USB_NONE
609 lcd_puts(0, 2, "Insert USB cable"); 629 lcd_puts(0, 2, "Insert USB cable");
610 lcd_puts(0, 3, "and fix it."); 630 lcd_puts(0, 3, "and fix it.");
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
index c5c5e6d908..2ce566a888 100644
--- a/apps/menus/display_menu.c
+++ b/apps/menus/display_menu.c
@@ -331,6 +331,7 @@ MENUITEM_SETTING(list_accel_start_delay,
331MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL); 331MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL);
332#endif /* HAVE_WHEEL_ACCELERATION */ 332#endif /* HAVE_WHEEL_ACCELERATION */
333MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view, NULL); 333MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view, NULL);
334MENUITEM_SETTING(disable_mainmenu_scrolling, &global_settings.disable_mainmenu_scrolling, NULL);
334MENUITEM_SETTING(screen_scroll_step, &global_settings.screen_scroll_step, NULL); 335MENUITEM_SETTING(screen_scroll_step, &global_settings.screen_scroll_step, NULL);
335MENUITEM_SETTING(scroll_paginated, &global_settings.scroll_paginated, NULL); 336MENUITEM_SETTING(scroll_paginated, &global_settings.scroll_paginated, NULL);
336MENUITEM_SETTING(list_wraparound, &global_settings.list_wraparound, NULL); 337MENUITEM_SETTING(list_wraparound, &global_settings.list_wraparound, NULL);
@@ -343,7 +344,9 @@ MAKE_MENU(scroll_settings_menu, ID2P(LANG_SCROLL_MENU), 0, Icon_NOICON,
343#ifdef HAVE_REMOTE_LCD 344#ifdef HAVE_REMOTE_LCD
344 &remote_scroll_sets, 345 &remote_scroll_sets,
345#endif 346#endif
346 &offset_out_of_view, &screen_scroll_step, 347 &offset_out_of_view,
348 &disable_mainmenu_scrolling,
349 &screen_scroll_step,
347 &scroll_paginated, 350 &scroll_paginated,
348 &list_wraparound, 351 &list_wraparound,
349 &list_order, 352 &list_order,
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index a483a72eb0..2fa5be5623 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -134,8 +134,8 @@ static int show_legal(void)
134struct info_data 134struct info_data
135 135
136{ 136{
137 unsigned long size[NUM_VOLUMES]; 137 sector_t size[NUM_VOLUMES];
138 unsigned long free[NUM_VOLUMES]; 138 sector_t free[NUM_VOLUMES];
139 unsigned long name[NUM_VOLUMES]; 139 unsigned long name[NUM_VOLUMES];
140 bool new_data; 140 bool new_data;
141}; 141};
@@ -163,15 +163,18 @@ enum infoscreenorder
163static int refresh_data(struct info_data *info) 163static int refresh_data(struct info_data *info)
164{ 164{
165 int i = 0; 165 int i = 0;
166#ifdef HAVE_MULTIVOLUME
166#ifdef HAVE_MULTIDRIVE 167#ifdef HAVE_MULTIDRIVE
167 int drive;
168 int max = -1; 168 int max = -1;
169 169#endif
170 int drive = 0;
170 for (i = 0 ; CHECK_VOL(i) ; i++) { 171 for (i = 0 ; CHECK_VOL(i) ; i++) {
171#endif 172#endif
172 volume_size(IF_MV(i,) &info->size[i], &info->free[i]); 173 volume_size(IF_MV(i,) &info->size[i], &info->free[i]);
174#ifdef HAVE_MULTIVOLUME
173#ifdef HAVE_MULTIDRIVE 175#ifdef HAVE_MULTIDRIVE
174 drive = volume_drive(i); 176 drive = volume_drive(i);
177#endif
175 if (drive > 0 || info->size[i] == 0) 178 if (drive > 0 || info->size[i] == 0)
176 info->name[i] = LANG_DISK_NAME_MMC; 179 info->name[i] = LANG_DISK_NAME_MMC;
177 else 180 else
@@ -182,9 +185,12 @@ static int refresh_data(struct info_data *info)
182 max = drive; 185 max = drive;
183 else if (drive < max) 186 else if (drive < max)
184 break; 187 break;
188#elif defined(HAVE_MULTIVOLUME) && (defined(HAVE_HOTSWAP) || defined(HAVE_HOTSWAP) || defined(HAVE_DIRCACHE) || defined(HAVE_BOOTDATA))
189 if (volume_partition(i) == -1)
190 break;
191#endif
192#ifdef HAVE_MULTIVOLUME
185 } 193 }
186#else
187 i++;
188#endif 194#endif
189 195
190 info->new_data = false; 196 info->new_data = false;
diff --git a/apps/misc.c b/apps/misc.c
index d8caabd397..0bbaba965b 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -139,7 +139,7 @@ const unsigned char * const unit_strings_core[] =
139 * voiced.*/ 139 * voiced.*/
140char *output_dyn_value(char *buf, 140char *output_dyn_value(char *buf,
141 int buf_size, 141 int buf_size,
142 int value, 142 int64_t value,
143 const unsigned char * const *units, 143 const unsigned char * const *units,
144 unsigned int unit_count, 144 unsigned int unit_count,
145 bool binary_scale) 145 bool binary_scale)
@@ -147,8 +147,9 @@ char *output_dyn_value(char *buf,
147 unsigned int scale = binary_scale ? 1024 : 1000; 147 unsigned int scale = binary_scale ? 1024 : 1000;
148 unsigned int fraction = 0; 148 unsigned int fraction = 0;
149 unsigned int unit_no = 0; 149 unsigned int unit_no = 0;
150 unsigned int value_abs = (value < 0) ? -value : value; 150 uint64_t value_abs = (value < 0) ? -value : value;
151 char tbuf[5]; 151 char tbuf[5];
152 int value2;
152 153
153 while (value_abs >= scale && unit_no < (unit_count - 1)) 154 while (value_abs >= scale && unit_no < (unit_count - 1))
154 { 155 {
@@ -157,7 +158,7 @@ char *output_dyn_value(char *buf,
157 unit_no++; 158 unit_no++;
158 } 159 }
159 160
160 value = (value < 0) ? -value_abs : value_abs; /* preserve sign */ 161 value2 = (value < 0) ? -value_abs : value_abs; /* preserve sign */
161 fraction = (fraction * 1000 / scale) / 10; 162 fraction = (fraction * 1000 / scale) / 10;
162 163
163 if (value_abs >= 100 || fraction >= 100 || !unit_no) 164 if (value_abs >= 100 || fraction >= 100 || !unit_no)
@@ -170,10 +171,10 @@ char *output_dyn_value(char *buf,
170 if (buf) 171 if (buf)
171 { 172 {
172 if (*tbuf) 173 if (*tbuf)
173 snprintf(buf, buf_size, "%d%s%s%s", value, str(LANG_POINT), 174 snprintf(buf, buf_size, "%d%s%s%s", value2, str(LANG_POINT),
174 tbuf, P2STR(units[unit_no])); 175 tbuf, P2STR(units[unit_no]));
175 else 176 else
176 snprintf(buf, buf_size, "%d%s", value, P2STR(units[unit_no])); 177 snprintf(buf, buf_size, "%d%s", value2, P2STR(units[unit_no]));
177 } 178 }
178 else 179 else
179 { 180 {
@@ -200,7 +201,7 @@ bool warn_on_pl_erase(void)
200 return true; 201 return true;
201} 202}
202 203
203bool show_search_progress(bool init, int count) 204bool show_search_progress(bool init, int display_count, int current, int total)
204{ 205{
205 static int last_tick = 0; 206 static int last_tick = 0;
206 207
@@ -214,7 +215,15 @@ bool show_search_progress(bool init, int count)
214 /* Update progress every 1/10 of a second */ 215 /* Update progress every 1/10 of a second */
215 if (TIME_AFTER(current_tick, last_tick + HZ/10)) 216 if (TIME_AFTER(current_tick, last_tick + HZ/10))
216 { 217 {
217 splashf(0, str(LANG_PLAYLIST_SEARCH_MSG), count, str(LANG_OFF_ABORT)); 218 if (total != current)
219 {
220 splash_progress(current, total, str(LANG_PLAYLIST_SEARCH_MSG),
221 display_count, str(LANG_OFF_ABORT));
222 }
223 else
224 splashf(0, str(LANG_PLAYLIST_SEARCH_MSG),
225 display_count, str(LANG_OFF_ABORT));
226
218 if (action_userabort(TIMEOUT_NOBLOCK)) 227 if (action_userabort(TIMEOUT_NOBLOCK))
219 return false; 228 return false;
220 last_tick = current_tick; 229 last_tick = current_tick;
@@ -1843,7 +1852,7 @@ enum current_activity get_current_activity(void)
1843* ** Extended error info truth table ** 1852* ** Extended error info truth table **
1844* [ Handle ][buf_reqd] 1853* [ Handle ][buf_reqd]
1845* [ > 0 ][ > 0 ] buf_reqd indicates how many bytes were used 1854* [ > 0 ][ > 0 ] buf_reqd indicates how many bytes were used
1846* [ALOC_ERR][ > 0 ] buf_reqd indicates how many bytes are needed 1855* [ALOC_ERR][ > 0 ] buf_reqd indicates how many bytes are needed
1847* [ALOC_ERR][READ_ERR] there was an error reading the file or it is empty 1856* [ALOC_ERR][READ_ERR] there was an error reading the file or it is empty
1848*/ 1857*/
1849int core_load_bmp(const char * filename, struct bitmap *bm, const int bmformat, 1858int core_load_bmp(const char * filename, struct bitmap *bm, const int bmformat,
diff --git a/apps/misc.h b/apps/misc.h
index e5fb7a3d1f..87765b28b1 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -1,10 +1,10 @@
1/*************************************************************************** 1/***************************************************************************
2 * __________ __ ___. 2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 by Daniel Stenberg 10 * Copyright (C) 2002 by Daniel Stenberg
@@ -22,6 +22,7 @@
22#define MISC_H 22#define MISC_H
23 23
24#include <stdbool.h> 24#include <stdbool.h>
25#include <stdint.h>
25#include <inttypes.h> 26#include <inttypes.h>
26#include "config.h" 27#include "config.h"
27#include "screen_access.h" 28#include "screen_access.h"
@@ -36,7 +37,7 @@ extern const unsigned char * const unit_strings_core[];
36 * voiced.*/ 37 * voiced.*/
37char *output_dyn_value(char *buf, 38char *output_dyn_value(char *buf,
38 int buf_size, 39 int buf_size,
39 int value, 40 int64_t value,
40 const unsigned char * const *units, 41 const unsigned char * const *units,
41 unsigned int unit_count, 42 unsigned int unit_count,
42 bool binary_scale); 43 bool binary_scale);
@@ -110,12 +111,12 @@ void talk_timedate(void);
110 * returns true if the playlist should be replaced */ 111 * returns true if the playlist should be replaced */
111bool warn_on_pl_erase(void); 112bool warn_on_pl_erase(void);
112 113
113bool show_search_progress(bool init, int count); 114bool show_search_progress(bool init, int count, int current, int total);
114 115
115/* Read (up to) a line of text from fd into buffer and return number of bytes 116/* Read (up to) a line of text from fd into buffer and return number of bytes
116 * read (which may be larger than the number of bytes stored in buffer). If 117 * read (which may be larger than the number of bytes stored in buffer). If
117 * an error occurs, -1 is returned (and buffer contains whatever could be 118 * an error occurs, -1 is returned (and buffer contains whatever could be
118 * read). A line is terminated by a LF char. Neither LF nor CR chars are 119 * read). A line is terminated by a LF char. Neither LF nor CR chars are
119 * stored in buffer. 120 * stored in buffer.
120 */ 121 */
121int read_line(int fd, char* buffer, int buffer_size); 122int read_line(int fd, char* buffer, int buffer_size);
diff --git a/apps/onplay.c b/apps/onplay.c
index d468c0a545..ab507f08ac 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -138,7 +138,7 @@ static bool clipboard_clip(struct clipboard *clip, const char *path,
138static int bookmark_load_menu_wrapper(void) 138static int bookmark_load_menu_wrapper(void)
139{ 139{
140 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 140 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
141 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 141 pop_current_activity_without_refresh(); /* when called from ctxt menu */
142 142
143 return bookmark_load_menu(); 143 return bookmark_load_menu();
144} 144}
@@ -302,7 +302,7 @@ static int add_to_playlist(void* arg)
302 302
303 /* warn if replacing the playlist */ 303 /* warn if replacing the playlist */
304 if (new_playlist && !warn_on_pl_erase()) 304 if (new_playlist && !warn_on_pl_erase())
305 return 0; 305 return 1;
306 306
307 splash(0, ID2P(LANG_WAIT)); 307 splash(0, ID2P(LANG_WAIT));
308 308
@@ -340,7 +340,7 @@ static int add_to_playlist(void* arg)
340 } 340 }
341 341
342 playlist_set_modified(NULL, true); 342 playlist_set_modified(NULL, true);
343 return false; 343 return 0;
344} 344}
345 345
346static bool view_playlist(void) 346static bool view_playlist(void)
@@ -689,7 +689,7 @@ MENUITEM_FUNCTION(view_cue_item, 0, ID2P(LANG_BROWSE_CUESHEET),
689static int browse_id3_wrapper(void) 689static int browse_id3_wrapper(void)
690{ 690{
691 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 691 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
692 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 692 pop_current_activity_without_refresh(); /* when called from ctxt menu */
693 693
694 if (browse_id3(audio_current_track(), 694 if (browse_id3(audio_current_track(),
695 playlist_get_display_index(), 695 playlist_get_display_index(),
@@ -806,6 +806,10 @@ static bool onplay_load_plugin(void *param)
806 if (!prepare_database_sel(param)) 806 if (!prepare_database_sel(param))
807 return false; 807 return false;
808#endif 808#endif
809
810 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
811 pop_current_activity_without_refresh(); /* when called from ctxt menu */
812
809 int ret = filetype_load_plugin((const char*)param, selected_file.path); 813 int ret = filetype_load_plugin((const char*)param, selected_file.path);
810 if (ret == PLUGIN_USB_CONNECTED) 814 if (ret == PLUGIN_USB_CONNECTED)
811 onplay_result = ONPLAY_RELOAD_DIR; 815 onplay_result = ONPLAY_RELOAD_DIR;
@@ -1251,7 +1255,7 @@ static int execute_hotkey(bool is_wps)
1251} 1255}
1252#endif /* HOTKEY */ 1256#endif /* HOTKEY */
1253 1257
1254int onplay(char* file, int attr, int from_context, bool hotkey) 1258int onplay(char* file, int attr, int from_context, bool hotkey, int customaction)
1255{ 1259{
1256 const struct menu_item_ex *menu; 1260 const struct menu_item_ex *menu;
1257 onplay_result = ONPLAY_OK; 1261 onplay_result = ONPLAY_OK;
@@ -1290,6 +1294,14 @@ int onplay(char* file, int attr, int from_context, bool hotkey)
1290#else 1294#else
1291 (void)hotkey; 1295 (void)hotkey;
1292#endif 1296#endif
1297 if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS)
1298 {
1299 int returnCode = add_to_playlist(&addtopl_replace_shuffled);
1300 if (returnCode == 1)
1301 // User did not want to erase his current playlist, so let's show again the database main menu
1302 return ONPLAY_RELOAD_DIR;
1303 return ONPLAY_START_PLAY;
1304 }
1293 1305
1294 push_current_activity(ACTIVITY_CONTEXTMENU); 1306 push_current_activity(ACTIVITY_CONTEXTMENU);
1295 if (from_context == CONTEXT_WPS) 1307 if (from_context == CONTEXT_WPS)
@@ -1299,7 +1311,7 @@ int onplay(char* file, int attr, int from_context, bool hotkey)
1299 menu_selection = do_menu(menu, NULL, NULL, false); 1311 menu_selection = do_menu(menu, NULL, NULL, false);
1300 1312
1301 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */ 1313 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */
1302 pop_current_activity(); /* popped already by menu item */ 1314 pop_current_activity(); /* popped already by menu item */
1303 1315
1304 1316
1305 if (menu_selection == GO_TO_WPS) 1317 if (menu_selection == GO_TO_WPS)
diff --git a/apps/onplay.h b/apps/onplay.h
index 74dc045db3..03861e9cf6 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -25,7 +25,12 @@
25#include "menu.h" 25#include "menu.h"
26#endif 26#endif
27 27
28int onplay(char* file, int attr, int from_context, bool hotkey); 28enum {
29 ONPLAY_NO_CUSTOMACTION,
30 ONPLAY_CUSTOMACTION_SHUFFLE_SONGS,
31};
32
33int onplay(char* file, int attr, int from_context, bool hotkey, int customaction);
29int get_onplay_context(void); 34int get_onplay_context(void);
30 35
31enum { 36enum {
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 8718d730fb..773e97cce0 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -741,9 +741,6 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type)
741 } 741 }
742 } 742 }
743 743
744 if (auto_skip && global_settings.single_mode != SINGLE_MODE_OFF && !global_settings.party_mode)
745 crossfade = false;
746
747 if (crossfade) 744 if (crossfade)
748 { 745 {
749 logf("crossfade track change"); 746 logf("crossfade track change");
diff --git a/apps/playback.c b/apps/playback.c
index 8a30af5199..a284a1858d 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -2494,6 +2494,23 @@ static inline char* single_mode_get_id3_tag(struct mp3entry *id3)
2494 return NULL; 2494 return NULL;
2495} 2495}
2496 2496
2497static bool single_mode_do_pause(int id3_hid)
2498{
2499 if (global_settings.single_mode != SINGLE_MODE_OFF && global_settings.party_mode == 0 &&
2500 ((skip_pending == TRACK_SKIP_AUTO) || (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST))) {
2501
2502 if (global_settings.single_mode == SINGLE_MODE_TRACK)
2503 return true;
2504
2505 char *previous_tag = single_mode_get_id3_tag(id3_get(PLAYING_ID3));
2506 char *new_tag = single_mode_get_id3_tag(bufgetid3(id3_hid));
2507 return previous_tag == NULL ||
2508 new_tag == NULL ||
2509 strcmp(previous_tag, new_tag) != 0;
2510 }
2511 return false;
2512}
2513
2497/* Called to make an outstanding track skip the current track and to send the 2514/* Called to make an outstanding track skip the current track and to send the
2498 transition events */ 2515 transition events */
2499static void audio_finalise_track_change(void) 2516static void audio_finalise_track_change(void)
@@ -2540,43 +2557,28 @@ static void audio_finalise_track_change(void)
2540 bool have_info = track_list_current(0, &info); 2557 bool have_info = track_list_current(0, &info);
2541 struct mp3entry *track_id3 = NULL; 2558 struct mp3entry *track_id3 = NULL;
2542 2559
2543 id3_mutex_lock();
2544
2545 /* Update the current cuesheet if any and enabled */ 2560 /* Update the current cuesheet if any and enabled */
2546 if (have_info) 2561 if (have_info)
2547 { 2562 {
2548 buf_read_cuesheet(info.cuesheet_hid); 2563 buf_read_cuesheet(info.cuesheet_hid);
2549 track_id3 = bufgetid3(info.id3_hid); 2564 track_id3 = bufgetid3(info.id3_hid);
2550 }
2551
2552 if (SINGLE_MODE_OFF != global_settings.single_mode && global_settings.party_mode == 0 &&
2553 ((skip_pending == TRACK_SKIP_AUTO) || (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST)))
2554 {
2555 bool single_mode_do_pause = true;
2556 if (SINGLE_MODE_TRACK != global_settings.single_mode)
2557 {
2558 char *previous_tag = single_mode_get_id3_tag(id3_get(PLAYING_ID3));
2559 char *new_tag = single_mode_get_id3_tag(track_id3);
2560 single_mode_do_pause = previous_tag == NULL ||
2561 new_tag == NULL ||
2562 strcmp(previous_tag, new_tag) != 0;
2563 }
2564 2565
2565 if (single_mode_do_pause) 2566 if (single_mode_do_pause(info.id3_hid))
2566 { 2567 {
2567 play_status = PLAY_PAUSED; 2568 play_status = PLAY_PAUSED;
2568 pcmbuf_pause(true); 2569 pcmbuf_pause(true);
2569 } 2570 }
2570 } 2571 }
2572 /* Sync the next track information */
2573 have_info = track_list_current(1, &info);
2574
2575 id3_mutex_lock();
2571 2576
2572 id3_write(PLAYING_ID3, track_id3); 2577 id3_write(PLAYING_ID3, track_id3);
2573 2578
2574 /* The skip is technically over */ 2579 /* The skip is technically over */
2575 skip_pending = TRACK_SKIP_NONE; 2580 skip_pending = TRACK_SKIP_NONE;
2576 2581
2577 /* Sync the next track information */
2578 have_info = track_list_current(1, &info);
2579
2580 id3_write(NEXTTRACK_ID3, have_info ? bufgetid3(info.id3_hid) : 2582 id3_write(NEXTTRACK_ID3, have_info ? bufgetid3(info.id3_hid) :
2581 id3_get(UNBUFFERED_ID3)); 2583 id3_get(UNBUFFERED_ID3));
2582 2584
@@ -2641,54 +2643,12 @@ static void audio_monitor_end_of_playlist(void)
2641 pcmbuf_start_track_change(TRACK_CHANGE_END_OF_DATA); 2643 pcmbuf_start_track_change(TRACK_CHANGE_END_OF_DATA);
2642} 2644}
2643 2645
2644/* Codec has completed decoding the track 2646/* Does this track have an entry allocated? */
2645 (usually Q_AUDIO_CODEC_COMPLETE) */ 2647static bool audio_can_change_track(int *trackstat, int *id3_hid)
2646static void audio_on_codec_complete(int status)
2647{ 2648{
2648 logf("%s(%d)", __func__, status);
2649
2650 if (play_status == PLAY_STOPPED)
2651 return;
2652
2653 /* If it didn't notify us first, don't expect "seek complete" message
2654 since the codec can't post it now - do things like it would have
2655 done */
2656 audio_complete_codec_seek();
2657
2658 if (play_status == PLAY_PAUSED || skip_pending != TRACK_SKIP_NONE)
2659 {
2660 /* Old-hay on the ip-skay - codec has completed decoding
2661
2662 Paused: We're not sounding it, so just remember that it happened
2663 and the resume will begin the transition
2664
2665 Skipping: There was already a skip in progress, remember it and
2666 allow no further progress until the PCM from the previous
2667 song has finished
2668
2669 This function will be reentered upon completing the existing
2670 transition in order to do the one that was just tried (below)
2671 */
2672 codec_skip_pending = true;
2673 codec_skip_status = status;
2674
2675 /* PCM buffer must know; audio could still be filling and hasn't
2676 yet reached the play watermark */
2677 pcmbuf_start_track_change(TRACK_CHANGE_AUTO_PILEUP);
2678 return;
2679 }
2680
2681 codec_skip_pending = false;
2682
2683 int trackstat = LOAD_TRACK_OK;
2684
2685 track_event_flags = TEF_AUTO_SKIP;
2686 skip_pending = TRACK_SKIP_AUTO;
2687
2688 /* Does this track have an entry allocated? */
2689 struct track_info info; 2649 struct track_info info;
2690 bool have_track = track_list_advance_current(1, &info); 2650 bool have_track = track_list_advance_current(1, &info);
2691 2651 *id3_hid = info.id3_hid;
2692 if (!have_track || info.audio_hid < 0) 2652 if (!have_track || info.audio_hid < 0)
2693 { 2653 {
2694 bool end_of_playlist = false; 2654 bool end_of_playlist = false;
@@ -2697,8 +2657,8 @@ static void audio_on_codec_complete(int status)
2697 { 2657 {
2698 if (filling == STATE_STOPPED) 2658 if (filling == STATE_STOPPED)
2699 { 2659 {
2700 audio_begin_track_change(TRACK_CHANGE_END_OF_DATA, trackstat); 2660 audio_begin_track_change(TRACK_CHANGE_END_OF_DATA, *trackstat);
2701 return; 2661 return false;
2702 } 2662 }
2703 2663
2704 /* Track load is not complete - it might have stopped on a 2664 /* Track load is not complete - it might have stopped on a
@@ -2719,8 +2679,7 @@ static void audio_on_codec_complete(int status)
2719 { 2679 {
2720 /* Continue filling after this track */ 2680 /* Continue filling after this track */
2721 audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1); 2681 audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
2722 audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat); 2682 return true;
2723 return;
2724 } 2683 }
2725 /* else rebuffer at this track; status applies to the track we 2684 /* else rebuffer at this track; status applies to the track we
2726 want */ 2685 want */
@@ -2736,10 +2695,10 @@ static void audio_on_codec_complete(int status)
2736 2695
2737 if (!end_of_playlist) 2696 if (!end_of_playlist)
2738 { 2697 {
2739 trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, 2698 *trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL,
2740 skip_pending == TRACK_SKIP_AUTO ? 0 : -1); 2699 skip_pending == TRACK_SKIP_AUTO ? 0 : -1);
2741 2700
2742 if (trackstat == LOAD_TRACK_ERR_NO_MORE) 2701 if (*trackstat == LOAD_TRACK_ERR_NO_MORE)
2743 { 2702 {
2744 /* Failed to find anything after all - do playlist switchover 2703 /* Failed to find anything after all - do playlist switchover
2745 instead */ 2704 instead */
@@ -2751,11 +2710,64 @@ static void audio_on_codec_complete(int status)
2751 if (end_of_playlist) 2710 if (end_of_playlist)
2752 { 2711 {
2753 audio_monitor_end_of_playlist(); 2712 audio_monitor_end_of_playlist();
2754 return; 2713 return false;
2755 } 2714 }
2756 } 2715 }
2716 return true;
2717}
2718
2719/* Codec has completed decoding the track
2720 (usually Q_AUDIO_CODEC_COMPLETE) */
2721static void audio_on_codec_complete(int status)
2722{
2723 logf("%s(%d)", __func__, status);
2757 2724
2758 audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat); 2725 if (play_status == PLAY_STOPPED)
2726 return;
2727
2728 /* If it didn't notify us first, don't expect "seek complete" message
2729 since the codec can't post it now - do things like it would have
2730 done */
2731 audio_complete_codec_seek();
2732
2733 if (play_status == PLAY_PAUSED || skip_pending != TRACK_SKIP_NONE)
2734 {
2735 /* Old-hay on the ip-skay - codec has completed decoding
2736
2737 Paused: We're not sounding it, so just remember that it happened
2738 and the resume will begin the transition
2739
2740 Skipping: There was already a skip in progress, remember it and
2741 allow no further progress until the PCM from the previous
2742 song has finished
2743
2744 This function will be reentered upon completing the existing
2745 transition in order to do the one that was just tried (below)
2746 */
2747 codec_skip_pending = true;
2748 codec_skip_status = status;
2749
2750 /* PCM buffer must know; audio could still be filling and hasn't
2751 yet reached the play watermark */
2752 pcmbuf_start_track_change(TRACK_CHANGE_AUTO_PILEUP);
2753 return;
2754 }
2755
2756 codec_skip_pending = false;
2757
2758 int trackstat = LOAD_TRACK_OK;
2759
2760 track_event_flags = TEF_AUTO_SKIP;
2761 skip_pending = TRACK_SKIP_AUTO;
2762
2763 int id3_hid = 0;
2764 if (audio_can_change_track(&trackstat, &id3_hid))
2765 {
2766 audio_begin_track_change(
2767 single_mode_do_pause(id3_hid)
2768 ? TRACK_CHANGE_END_OF_DATA
2769 : TRACK_CHANGE_AUTO, trackstat);
2770 }
2759} 2771}
2760 2772
2761/* Called when codec completes seek operation 2773/* Called when codec completes seek operation
diff --git a/apps/playlist.c b/apps/playlist.c
index 70637b39a2..d23f3ec0d1 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -2519,6 +2519,7 @@ bool playlist_entries_iterate(const char *filename,
2519 bool ret = false; 2519 bool ret = false;
2520 int max; 2520 int max;
2521 char *dir; 2521 char *dir;
2522 off_t filesize;
2522 2523
2523 char temp_buf[MAX_PATH+1]; 2524 char temp_buf[MAX_PATH+1];
2524 char trackname[MAX_PATH+1]; 2525 char trackname[MAX_PATH+1];
@@ -2533,14 +2534,16 @@ bool playlist_entries_iterate(const char *filename,
2533 notify_access_error(); 2534 notify_access_error();
2534 goto out; 2535 goto out;
2535 } 2536 }
2536 2537 off_t start = lseek(fd, 0, SEEK_CUR);
2538 filesize = lseek(fd, 0, SEEK_END);
2539 lseek(fd, start, SEEK_SET);
2537 /* we need the directory name for formatting purposes */ 2540 /* we need the directory name for formatting purposes */
2538 size_t dirlen = path_dirname(filename, (const char **)&dir); 2541 size_t dirlen = path_dirname(filename, (const char **)&dir);
2539 //dir = strmemdupa(dir, dirlen); 2542 //dir = strmemdupa(dir, dirlen);
2540 2543
2541 2544
2542 if (action_cb) 2545 if (action_cb)
2543 show_search_progress(true, 0); 2546 show_search_progress(true, 0, 0, 0);
2544 2547
2545 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0) 2548 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2546 { 2549 {
@@ -2561,17 +2564,17 @@ bool playlist_entries_iterate(const char *filename,
2561 2564
2562 /* we need to format so that relative paths are correctly 2565 /* we need to format so that relative paths are correctly
2563 handled */ 2566 handled */
2564 if (format_track_path(trackname, temp_buf, 2567 if ((max = format_track_path(trackname, temp_buf,
2565 sizeof(trackname), dir, dirlen) < 0) 2568 sizeof(trackname), dir, dirlen)) < 0)
2566 { 2569 {
2567 goto out; 2570 goto out;
2568 } 2571 }
2569 2572 start += max;
2570 if (action_cb) 2573 if (action_cb)
2571 { 2574 {
2572 if (!action_cb(trackname)) 2575 if (!action_cb(trackname))
2573 goto out; 2576 goto out;
2574 else if (!show_search_progress(false, i)) 2577 else if (!show_search_progress(false, i, start, filesize))
2575 break; 2578 break;
2576 } 2579 }
2577 else if (playlist_insert_context_add(pl_context, trackname) < 0) 2580 else if (playlist_insert_context_add(pl_context, trackname) < 0)
diff --git a/apps/playlist.h b/apps/playlist.h
index f7426df9a3..2fb1ce100e 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -33,6 +33,8 @@
33#define PLAYLIST_ATTR_QUEUED 0x01 33#define PLAYLIST_ATTR_QUEUED 0x01
34#define PLAYLIST_ATTR_INSERTED 0x02 34#define PLAYLIST_ATTR_INSERTED 0x02
35#define PLAYLIST_ATTR_SKIPPED 0x04 35#define PLAYLIST_ATTR_SKIPPED 0x04
36#define PLAYLIST_ATTR_RETRIEVE_ID3_ATTEMPTED 0x08
37#define PLAYLIST_ATTR_RETRIEVE_ID3_SUCCEEDED 0x10
36 38
37#define PLAYLIST_DISPLAY_COUNT 10 39#define PLAYLIST_DISPLAY_COUNT 10
38 40
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index b0435365e2..d556f3b557 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -50,6 +50,7 @@
50#include "playlist_menu.h" 50#include "playlist_menu.h"
51#include "menus/exported_menus.h" 51#include "menus/exported_menus.h"
52#include "yesno.h" 52#include "yesno.h"
53#include "playback.h"
53 54
54/* Maximum number of tracks we can have loaded at one time */ 55/* Maximum number of tracks we can have loaded at one time */
55#define MAX_PLAYLIST_ENTRIES 200 56#define MAX_PLAYLIST_ENTRIES 200
@@ -142,7 +143,7 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
142 const char* filename, bool reload, 143 const char* filename, bool reload,
143 int *most_recent_selection); 144 int *most_recent_selection);
144 145
145static void format_line(const struct playlist_entry* track, char* str, 146static void format_line(struct playlist_entry* track, char* str,
146 int len); 147 int len);
147 148
148static bool update_playlist(bool force); 149static bool update_playlist(bool force);
@@ -159,6 +160,27 @@ static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer,
159 pb->num_loaded = 0; 160 pb->num_loaded = 0;
160} 161}
161 162
163static int playlist_buffer_get_index(struct playlist_buffer *pb, int index)
164{
165 int buffer_index;
166 if (pb->direction == FORWARD)
167 {
168 if (index >= pb->first_index)
169 buffer_index = index-pb->first_index;
170 else /* rotation : track0 in buffer + requested track */
171 buffer_index = viewer.num_tracks-pb->first_index+index;
172 }
173 else
174 {
175 if (index <= pb->first_index)
176 buffer_index = pb->first_index-index;
177 else /* rotation : track0 in buffer + dist from the last track
178 to the requested track (num_tracks-requested track) */
179 buffer_index = pb->first_index+viewer.num_tracks-index;
180 }
181 return buffer_index;
182}
183
162/* 184/*
163 * Loads the entries following 'index' in the playlist buffer 185 * Loads the entries following 'index' in the playlist buffer
164 */ 186 */
@@ -227,6 +249,25 @@ static void playlist_buffer_load_entries_screen(struct playlist_buffer * pb,
227 playlist_buffer_load_entries(pb, start, direction); 249 playlist_buffer_load_entries(pb, start, direction);
228} 250}
229 251
252static bool retrieve_id3_tags(const int index, const char* name, struct mp3entry *id3, int flags)
253{
254 bool id3_retrieval_successful = false;
255
256 if (!viewer.playlist &&
257 (audio_status() & AUDIO_STATUS_PLAY) &&
258 (playlist_get_resume_info(&viewer.current_playing_track) == index))
259 {
260 copy_mp3entry(id3, audio_current_track()); /* retrieve id3 from RAM */
261 id3_retrieval_successful = true;
262 }
263 else
264 {
265 /* Read from disk, the database, doesn't store frequency, file size or codec (g4470) ChrisS*/
266 id3_retrieval_successful = get_metadata_ex(id3, -1, name, flags);
267 }
268 return id3_retrieval_successful;
269}
270
230static int playlist_entry_load(struct playlist_entry *entry, int index, 271static int playlist_entry_load(struct playlist_entry *entry, int index,
231 char* name_buffer, int remaining_size) 272 char* name_buffer, int remaining_size)
232{ 273{
@@ -242,6 +283,13 @@ static int playlist_entry_load(struct playlist_entry *entry, int index,
242 283
243 len = strlcpy(name_buffer, info.filename, remaining_size) + 1; 284 len = strlcpy(name_buffer, info.filename, remaining_size) + 1;
244 285
286 if (global_settings.playlist_viewer_track_display >
287 PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH && len <= remaining_size)
288 {
289 /* Allocate space for the id3viewc if the option is enabled */
290 len += MAX_PATH + 1;
291 }
292
245 if (len <= remaining_size) 293 if (len <= remaining_size)
246 { 294 {
247 entry->name = name_buffer; 295 entry->name = name_buffer;
@@ -253,27 +301,6 @@ static int playlist_entry_load(struct playlist_entry *entry, int index,
253 return -1; 301 return -1;
254} 302}
255 303
256static int playlist_buffer_get_index(struct playlist_buffer *pb, int index)
257{
258 int buffer_index;
259 if (pb->direction == FORWARD)
260 {
261 if (index >= pb->first_index)
262 buffer_index = index-pb->first_index;
263 else /* rotation : track0 in buffer + requested track */
264 buffer_index = viewer.num_tracks-pb->first_index+index;
265 }
266 else
267 {
268 if (index <= pb->first_index)
269 buffer_index = pb->first_index-index;
270 else /* rotation : track0 in buffer + dist from the last track
271 to the requested track (num_tracks-requested track) */
272 buffer_index = pb->first_index+viewer.num_tracks-index;
273 }
274 return buffer_index;
275}
276
277#define distance(a, b) \ 304#define distance(a, b) \
278 a>b? (a) - (b) : (b) - (a) 305 a>b? (a) - (b) : (b) - (a)
279static bool playlist_buffer_needs_reload(struct playlist_buffer* pb, 306static bool playlist_buffer_needs_reload(struct playlist_buffer* pb,
@@ -440,7 +467,9 @@ static void format_name(char* dest, const char* src, size_t bufsz)
440{ 467{
441 switch (global_settings.playlist_viewer_track_display) 468 switch (global_settings.playlist_viewer_track_display)
442 { 469 {
443 case 0: 470 case PLAYLIST_VIEWER_ENTRY_SHOW_FILE_NAME:
471 case PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM: /* If loading from tags failed, only display the file name */
472 case PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE: /* If loading from tags failed, only display the file name */
444 default: 473 default:
445 { 474 {
446 /* Only display the filename */ 475 /* Only display the filename */
@@ -450,7 +479,7 @@ static void format_name(char* dest, const char* src, size_t bufsz)
450 strrsplt(dest, '.'); 479 strrsplt(dest, '.');
451 break; 480 break;
452 } 481 }
453 case 1: 482 case PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH:
454 /* Full path */ 483 /* Full path */
455 strlcpy(dest, src, bufsz); 484 strlcpy(dest, src, bufsz);
456 break; 485 break;
@@ -458,22 +487,99 @@ static void format_name(char* dest, const char* src, size_t bufsz)
458} 487}
459 488
460/* Format display line */ 489/* Format display line */
461static void format_line(const struct playlist_entry* track, char* str, 490static void format_line(struct playlist_entry* track, char* str,
462 int len) 491 int len)
463{ 492{
464 char name[MAX_PATH]; 493 char *id3viewc = NULL;
465 char *skipped = ""; 494 char *skipped = "";
466 format_name(name, track->name, sizeof(name));
467
468 if (track->attr & PLAYLIST_ATTR_SKIPPED) 495 if (track->attr & PLAYLIST_ATTR_SKIPPED)
469 skipped = "(ERR) "; 496 skipped = "(ERR) ";
470 497 if (!(track->attr & PLAYLIST_ATTR_RETRIEVE_ID3_ATTEMPTED) &&
471 if (global_settings.playlist_viewer_indices) 498 (global_settings.playlist_viewer_track_display ==
472 /* Display playlist index */ 499 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM ||
473 snprintf(str, len, "%d. %s%s", track->display_index, skipped, name); 500 global_settings.playlist_viewer_track_display ==
501 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE
502 ))
503 {
504 track->attr |= PLAYLIST_ATTR_RETRIEVE_ID3_ATTEMPTED;
505 struct mp3entry id3;
506 bool retrieve_success = retrieve_id3_tags(track->index, track->name,
507 &id3, METADATA_EXCLUDE_ID3_PATH);
508 if (retrieve_success)
509 {
510 if (!id3viewc)
511 {
512 id3viewc = track->name + strlen(track->name) + 1;
513 }
514 struct mp3entry * pid3 = &id3;
515 id3viewc[0] = '\0';
516 if (global_settings.playlist_viewer_track_display ==
517 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM)
518 {
519 /* Title & Album */
520 if (pid3->title && pid3->title[0] != '\0')
521 {
522 char* cur_str = id3viewc;
523 int title_len = strlen(pid3->title);
524 int rem_space = MAX_PATH;
525 for (int i = 0; i < title_len && rem_space > 0; i++)
526 {
527 cur_str[0] = pid3->title[i];
528 cur_str++;
529 rem_space--;
530 }
531 if (rem_space > 10)
532 {
533 cur_str[0] = (char) ' ';
534 cur_str[1] = (char) '-';
535 cur_str[2] = (char) ' ';
536 cur_str += 3;
537 rem_space -= 3;
538 cur_str = strmemccpy(cur_str, pid3->album && pid3->album[0] != '\0' ?
539 pid3->album : (char*) str(LANG_TAGNAVI_UNTAGGED), rem_space);
540 if (cur_str)
541 track->attr |= PLAYLIST_ATTR_RETRIEVE_ID3_SUCCEEDED;
542 }
543 }
544 }
545 else if (global_settings.playlist_viewer_track_display ==
546 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE)
547 {
548 /* Just the title */
549 if (pid3->title && pid3->title[0] != '\0' &&
550 strmemccpy(id3viewc, pid3->title, MAX_PATH)
551 )
552 track->attr |= PLAYLIST_ATTR_RETRIEVE_ID3_SUCCEEDED;
553 }
554 /* Yield to reduce as much as possible the perceived UI lag,
555 because retrieving id3 tags is an expensive operation */
556 yield();
557 }
558 }
559
560 if (!(track->attr & PLAYLIST_ATTR_RETRIEVE_ID3_SUCCEEDED))
561 {
562 /* Simply use a formatted file name */
563 char name[MAX_PATH];
564 format_name(name, track->name, sizeof(name));
565 if (global_settings.playlist_viewer_indices)
566 /* Display playlist index */
567 snprintf(str, len, "%d. %s%s", track->display_index, skipped, name);
568 else
569 snprintf(str, len, "%s%s", skipped, name);
570 }
474 else 571 else
475 snprintf(str, len, "%s%s", skipped, name); 572 {
476 573 if (!id3viewc)
574 {
575 id3viewc = track->name + strlen(track->name) + 1;
576 }
577 if (global_settings.playlist_viewer_indices)
578 /* Display playlist index */
579 snprintf(str, len, "%d. %s%s", track->display_index, skipped, id3viewc);
580 else
581 snprintf(str, len, "%s%s", skipped, id3viewc);
582 }
477} 583}
478 584
479/* Update playlist in case something has changed or forced */ 585/* Update playlist in case something has changed or forced */
@@ -512,20 +618,7 @@ static bool update_playlist(bool force)
512static enum pv_onplay_result show_track_info(const struct playlist_entry *current_track) 618static enum pv_onplay_result show_track_info(const struct playlist_entry *current_track)
513{ 619{
514 struct mp3entry id3; 620 struct mp3entry id3;
515 bool id3_retrieval_successful = false; 621 bool id3_retrieval_successful = retrieve_id3_tags(current_track->index, current_track->name, &id3, 0);
516
517 if (!viewer.playlist &&
518 (audio_status() & AUDIO_STATUS_PLAY) &&
519 (playlist_get_resume_info(&viewer.current_playing_track) == current_track->index))
520 {
521 copy_mp3entry(&id3, audio_current_track()); /* retrieve id3 from RAM */
522 id3_retrieval_successful = true;
523 }
524 else
525 {
526 /* Read from disk, the database, doesn't store frequency, file size or codec (g4470) ChrisS*/
527 id3_retrieval_successful = get_metadata(&id3, -1, current_track->name);
528 }
529 622
530 return id3_retrieval_successful && 623 return id3_retrieval_successful &&
531 browse_id3_ex(&id3, viewer.playlist, current_track->display_index, 624 browse_id3_ex(&id3, viewer.playlist, current_track->display_index,
@@ -790,11 +883,17 @@ static int playlist_callback_voice(int selected_item, void *data)
790 883
791 switch(global_settings.playlist_viewer_track_display) 884 switch(global_settings.playlist_viewer_track_display)
792 { 885 {
793 case 1: /*full path*/ 886 case PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH:
887 /*full path*/
794 talk_fullpath(track->name, true); 888 talk_fullpath(track->name, true);
795 break; 889 break;
796 default: 890 default:
797 case 0: /*filename only*/ 891 case PLAYLIST_VIEWER_ENTRY_SHOW_FILE_NAME:
892 /*filename only*/
893 case PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM:
894 /* If loading from tags failed, only talk the file name */
895 case PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE:
896 /* If loading from tags failed, only talk the file name */
798 talk_file_or_spell(NULL, track->name, NULL, true); 897 talk_file_or_spell(NULL, track->name, NULL, true);
799 break; 898 break;
800 } 899 }
@@ -1107,7 +1206,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename,
1107 } 1206 }
1108 } 1207 }
1109 else 1208 else
1110 onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true); 1209 onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true, ONPLAY_NO_CUSTOMACTION);
1111 break; 1210 break;
1112 } 1211 }
1113#endif /* HAVE_HOTKEY */ 1212#endif /* HAVE_HOTKEY */
@@ -1154,7 +1253,8 @@ static int say_search_item(int selected_item, void *data)
1154{ 1253{
1155 struct playlist_search_data *s_data = data; 1254 struct playlist_search_data *s_data = data;
1156 playlist_get_track_info(viewer.playlist, s_data->found_indicies[selected_item], s_data->track); 1255 playlist_get_track_info(viewer.playlist, s_data->found_indicies[selected_item], s_data->track);
1157 if(global_settings.playlist_viewer_track_display == 1) /* full path*/ 1256 if(global_settings.playlist_viewer_track_display == PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH)
1257 /* full path*/
1158 talk_fullpath(s_data->track->filename, false); 1258 talk_fullpath(s_data->track->filename, false);
1159 else talk_file_or_spell(NULL, s_data->track->filename, NULL, false); 1259 else talk_file_or_spell(NULL, s_data->track->filename, NULL, false);
1160 return 0; 1260 return 0;
@@ -1197,7 +1297,8 @@ bool search_playlist(void)
1197 1297
1198 playlist_get_track_info(viewer.playlist, i, &track); 1298 playlist_get_track_info(viewer.playlist, i, &track);
1199 const char *trackname = track.filename; 1299 const char *trackname = track.filename;
1200 if (track_display == 0) /* if we only display filename only search filename */ 1300 if (track_display != PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH)
1301 /* if we only display filename only search filename */
1201 trackname = strrchr(track.filename, '/'); 1302 trackname = strrchr(track.filename, '/');
1202 1303
1203 if (trackname && strcasestr(trackname, search_str)) 1304 if (trackname && strcasestr(trackname, search_str))
@@ -1219,7 +1320,6 @@ bool search_playlist(void)
1219 gui_synclist_init(&playlist_lists, playlist_search_callback_name, 1320 gui_synclist_init(&playlist_lists, playlist_search_callback_name,
1220 &s_data, false, 1, NULL); 1321 &s_data, false, 1, NULL);
1221 gui_synclist_set_title(&playlist_lists, str(LANG_SEARCH_RESULTS), NOICON); 1322 gui_synclist_set_title(&playlist_lists, str(LANG_SEARCH_RESULTS), NOICON);
1222 gui_synclist_set_icon_callback(&playlist_lists, NULL);
1223 if(global_settings.talk_file) 1323 if(global_settings.talk_file)
1224 gui_synclist_set_voice_callback(&playlist_lists, 1324 gui_synclist_set_voice_callback(&playlist_lists,
1225 global_settings.talk_file? 1325 global_settings.talk_file?
diff --git a/apps/plugin.c b/apps/plugin.c
index 7c80e5c6e1..adf207da79 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -352,7 +352,6 @@ static const struct plugin_api rockbox_api = {
352 yesno_pop, 352 yesno_pop,
353 353
354 /* action handling */ 354 /* action handling */
355 list_do_action,
356 get_custom_action, 355 get_custom_action,
357 get_action, 356 get_action,
358#ifdef HAVE_TOUCHSCREEN 357#ifdef HAVE_TOUCHSCREEN
@@ -422,6 +421,7 @@ static const struct plugin_api rockbox_api = {
422 crc_32, 421 crc_32,
423 crc_32r, 422 crc_32r,
424 filetype_get_attr, 423 filetype_get_attr,
424 filetype_get_plugin,
425 425
426 /* dir */ 426 /* dir */
427 FS_PREFIX(opendir), 427 FS_PREFIX(opendir),
@@ -582,6 +582,9 @@ static const struct plugin_api rockbox_api = {
582 utf8length, 582 utf8length,
583 utf8seek, 583 utf8seek,
584 584
585 /* language */
586 lang_is_rtl,
587
585 /* the buflib memory management library */ 588 /* the buflib memory management library */
586 buflib_init, 589 buflib_init,
587 buflib_available, 590 buflib_available,
@@ -665,7 +668,7 @@ static const struct plugin_api rockbox_api = {
665 668
666 /* metadata */ 669 /* metadata */
667 get_metadata, 670 get_metadata,
668 mp3info, 671 get_codec_string,
669 count_mp3_frames, 672 count_mp3_frames,
670 create_xing_header, 673 create_xing_header,
671#ifdef HAVE_TAGCACHE 674#ifdef HAVE_TAGCACHE
@@ -677,6 +680,7 @@ static const struct plugin_api rockbox_api = {
677 tagcache_search_finish, 680 tagcache_search_finish,
678 tagcache_get_numeric, 681 tagcache_get_numeric,
679 tagcache_get_stat, 682 tagcache_get_stat,
683 tagcache_commit_finalize,
680#if defined(HAVE_TC_RAMCACHE) 684#if defined(HAVE_TC_RAMCACHE)
681 tagcache_is_in_ram, 685 tagcache_is_in_ram,
682#if defined(HAVE_DIRCACHE) 686#if defined(HAVE_DIRCACHE)
@@ -694,6 +698,9 @@ static const struct plugin_api rockbox_api = {
694 playlist_get_current, 698 playlist_get_current,
695 playlist_get_resume_info, 699 playlist_get_resume_info,
696 playlist_get_track_info, 700 playlist_get_track_info,
701 playlist_get_first_index,
702 playlist_get_display_index,
703 playlist_entries_iterate,
697 playlist_amount, 704 playlist_amount,
698 playlist_resume, 705 playlist_resume,
699 playlist_resume_track, 706 playlist_resume_track,
@@ -833,14 +840,8 @@ static const struct plugin_api rockbox_api = {
833 840
834 /* new stuff at the end, sort into place next time 841 /* new stuff at the end, sort into place next time
835 the API gets incompatible */ 842 the API gets incompatible */
836#ifdef HAVE_TAGCACHE 843
837 tagcache_commit_finalize, 844 talk_fullpath,
838#endif
839 playlist_get_first_index,
840 playlist_get_display_index,
841 filetype_get_plugin,
842 playlist_entries_iterate,
843 lang_is_rtl,
844}; 845};
845 846
846static int plugin_buffer_handle; 847static int plugin_buffer_handle;
@@ -855,6 +856,10 @@ int plugin_load(const char* plugin, const void* parameter)
855 if (!plugin) 856 if (!plugin)
856 return PLUGIN_ERROR; 857 return PLUGIN_ERROR;
857 858
859 /* for some plugins, the SBS can be left enabled */
860 const char *sepch = strrchr(plugin, PATH_SEPCH);
861 bool theme_enabled = sepch && !strcmp("properties.rock", sepch + 1);
862
858 if (current_plugin_handle && pfn_tsr_exit) 863 if (current_plugin_handle && pfn_tsr_exit)
859 { /* if we have a resident old plugin and a callback */ 864 { /* if we have a resident old plugin and a callback */
860 bool reenter = (strcmp(current_plugin, plugin) == 0); 865 bool reenter = (strcmp(current_plugin, plugin) == 0);
@@ -922,7 +927,8 @@ int plugin_load(const char* plugin, const void* parameter)
922 927
923 *(p_hdr->api) = &rockbox_api; 928 *(p_hdr->api) = &rockbox_api;
924 lcd_set_viewport(NULL); 929 lcd_set_viewport(NULL);
925 lcd_clear_display(); 930 if (!theme_enabled)
931 lcd_clear_display();
926 932
927#ifdef HAVE_REMOTE_LCD 933#ifdef HAVE_REMOTE_LCD
928 lcd_remote_set_viewport(NULL); 934 lcd_remote_set_viewport(NULL);
@@ -937,8 +943,9 @@ int plugin_load(const char* plugin, const void* parameter)
937 * they should be fixed properly instead of this lock */ 943 * they should be fixed properly instead of this lock */
938 tree_lock_cache(tree_get_context()); 944 tree_lock_cache(tree_get_context());
939 945
940 FOR_NB_SCREENS(i) 946 if (!theme_enabled)
941 viewportmanager_theme_enable(i, false, NULL); 947 FOR_NB_SCREENS(i)
948 viewportmanager_theme_enable(i, false, NULL);
942 949
943#ifdef HAVE_TOUCHSCREEN 950#ifdef HAVE_TOUCHSCREEN
944 touchscreen_set_mode(TOUCHSCREEN_BUTTON); 951 touchscreen_set_mode(TOUCHSCREEN_BUTTON);
@@ -999,13 +1006,16 @@ int plugin_load(const char* plugin, const void* parameter)
999#endif 1006#endif
1000#endif 1007#endif
1001 1008
1002 lcd_clear_display();
1003#ifdef HAVE_REMOTE_LCD 1009#ifdef HAVE_REMOTE_LCD
1004 lcd_remote_clear_display(); 1010 lcd_remote_clear_display();
1005#endif 1011#endif
1006 1012
1007 FOR_NB_SCREENS(i) 1013 if (!theme_enabled)
1008 viewportmanager_theme_undo(i, true); 1014 {
1015 lcd_clear_display();
1016 FOR_NB_SCREENS(i)
1017 viewportmanager_theme_undo(i, true);
1018 }
1009 1019
1010 plugin_check_open_close__exit(); 1020 plugin_check_open_close__exit();
1011 1021
diff --git a/apps/plugin.h b/apps/plugin.h
index 4dbd9d04c0..31e1c77cb7 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -163,7 +163,7 @@ int plugin_open(const char *plugin, const char *parameter);
163 * when this happens please take the opportunity to sort in 163 * when this happens please take the opportunity to sort in
164 * any new functions "waiting" at the end of the list. 164 * any new functions "waiting" at the end of the list.
165 */ 165 */
166#define PLUGIN_API_VERSION 270 166#define PLUGIN_API_VERSION 271
167 167
168/* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ 168/* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */
169 169
@@ -405,8 +405,6 @@ struct plugin_api {
405 bool (*yesno_pop)(const char* text); 405 bool (*yesno_pop)(const char* text);
406 406
407 /* action handling */ 407 /* action handling */
408 bool (*list_do_action)(int context, int timeout,
409 struct gui_synclist *lists, int *action);
410 int (*get_custom_action)(int context,int timeout, 408 int (*get_custom_action)(int context,int timeout,
411 const struct button_mapping* (*get_context_map)(int)); 409 const struct button_mapping* (*get_context_map)(int));
412 int (*get_action)(int context, int timeout); 410 int (*get_action)(int context, int timeout);
@@ -479,6 +477,7 @@ struct plugin_api {
479 uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32); 477 uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32);
480 478
481 int (*filetype_get_attr)(const char* file); 479 int (*filetype_get_attr)(const char* file);
480 char* (*filetype_get_plugin)(int attr, char *buffer, size_t buffer_len);
482 481
483 /* dir */ 482 /* dir */
484 DIR * (*opendir)(const char *dirname); 483 DIR * (*opendir)(const char *dirname);
@@ -650,7 +649,7 @@ struct plugin_api {
650 int (*memcmp)(const void *s1, const void *s2, size_t n); 649 int (*memcmp)(const void *s1, const void *s2, size_t n);
651 char *(*strcasestr) (const char* phaystack, const char* pneedle); 650 char *(*strcasestr) (const char* phaystack, const char* pneedle);
652 char* (*strtok_r)(char *ptr, const char *sep, char **end); 651 char* (*strtok_r)(char *ptr, const char *sep, char **end);
653 char* (*output_dyn_value)(char *buf, int buf_size, int value, 652 char* (*output_dyn_value)(char *buf, int buf_size, int64_t value,
654 const unsigned char * const *units, 653 const unsigned char * const *units,
655 unsigned int unit_count, bool binary_scale); 654 unsigned int unit_count, bool binary_scale);
656 /* unicode stuff */ 655 /* unicode stuff */
@@ -662,6 +661,9 @@ struct plugin_api {
662 unsigned long (*utf8length)(const unsigned char *utf8); 661 unsigned long (*utf8length)(const unsigned char *utf8);
663 int (*utf8seek)(const unsigned char* utf8, int offset); 662 int (*utf8seek)(const unsigned char* utf8, int offset);
664 663
664 /* language */
665 int (*lang_is_rtl)(void);
666
665 /* the buflib memory management library */ 667 /* the buflib memory management library */
666 void (*buflib_init)(struct buflib_context* ctx, void* buf, size_t size); 668 void (*buflib_init)(struct buflib_context* ctx, void* buf, size_t size);
667 size_t (*buflib_available)(struct buflib_context* ctx); 669 size_t (*buflib_available)(struct buflib_context* ctx);
@@ -761,7 +763,7 @@ struct plugin_api {
761 763
762 /* metadata */ 764 /* metadata */
763 bool (*get_metadata)(struct mp3entry* id3, int fd, const char* trackname); 765 bool (*get_metadata)(struct mp3entry* id3, int fd, const char* trackname);
764 bool (*mp3info)(struct mp3entry *entry, const char *filename); 766 const char* (*get_codec_string)(int codectype);
765 int (*count_mp3_frames)(int fd, int startpos, int filesize, 767 int (*count_mp3_frames)(int fd, int startpos, int filesize,
766 void (*progressfunc)(int), 768 void (*progressfunc)(int),
767 unsigned char* buf, size_t buflen); 769 unsigned char* buf, size_t buflen);
@@ -782,6 +784,7 @@ struct plugin_api {
782 void (*tagcache_search_finish)(struct tagcache_search *tcs); 784 void (*tagcache_search_finish)(struct tagcache_search *tcs);
783 long (*tagcache_get_numeric)(const struct tagcache_search *tcs, int tag); 785 long (*tagcache_get_numeric)(const struct tagcache_search *tcs, int tag);
784 struct tagcache_stat* (*tagcache_get_stat)(void); 786 struct tagcache_stat* (*tagcache_get_stat)(void);
787 void (*tagcache_commit_finalize)(void);
785#if defined(HAVE_TC_RAMCACHE) 788#if defined(HAVE_TC_RAMCACHE)
786 bool (*tagcache_is_in_ram)(void); 789 bool (*tagcache_is_in_ram)(void);
787#if defined(HAVE_DIRCACHE) 790#if defined(HAVE_DIRCACHE)
@@ -801,6 +804,11 @@ struct plugin_api {
801 int (*playlist_get_resume_info)(int *resume_index); 804 int (*playlist_get_resume_info)(int *resume_index);
802 int (*playlist_get_track_info)(struct playlist_info* playlist, int index, 805 int (*playlist_get_track_info)(struct playlist_info* playlist, int index,
803 struct playlist_track_info* info); 806 struct playlist_track_info* info);
807 int (*playlist_get_first_index)(const struct playlist_info* playlist);
808 int (*playlist_get_display_index)(void);
809 bool (*playlist_entries_iterate)(const char *filename,
810 struct playlist_insert_context *pl_context,
811 bool (*action_cb)(const char *file_name));
804 int (*playlist_amount)(void); 812 int (*playlist_amount)(void);
805 int (*playlist_resume)(void); 813 int (*playlist_resume)(void);
806 void (*playlist_resume_track)(int start_index, unsigned int crc, 814 void (*playlist_resume_track)(int start_index, unsigned int crc,
@@ -969,16 +977,8 @@ struct plugin_api {
969#endif 977#endif
970 /* new stuff at the end, sort into place next time 978 /* new stuff at the end, sort into place next time
971 the API gets incompatible */ 979 the API gets incompatible */
972#ifdef HAVE_TAGCACHE 980
973 void (*tagcache_commit_finalize)(void); 981 int (*talk_fullpath)(const char* path, bool enqueue);
974#endif
975 int (*playlist_get_first_index)(const struct playlist_info* playlist);
976 int (*playlist_get_display_index)(void);
977 char* (*filetype_get_plugin)(int attr, char *buffer, size_t buffer_len);
978 bool (*playlist_entries_iterate)(const char *filename,
979 struct playlist_insert_context *pl_context,
980 bool (*action_cb)(const char *file_name));
981 int (*lang_is_rtl)(void);
982}; 982};
983 983
984/* plugin header */ 984/* plugin header */
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 5e22bea980..79186b7aed 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -23,6 +23,7 @@ clock,apps
23codebuster,games 23codebuster,games
24credits,viewers 24credits,viewers
25cube,demos 25cube,demos
26cue_playlist,viewers
26dart_scorer,apps 27dart_scorer,apps
27db_commit,apps 28db_commit,apps
28db_folder_select,viewers 29db_folder_select,viewers
@@ -34,6 +35,7 @@ doom,games
34duke3d,games 35duke3d,games
35euroconverter,apps 36euroconverter,apps
36fft,demos 37fft,demos
38file_picker,viewers
37fire,demos 39fire,demos
38fireworks,demos 40fireworks,demos
39firmware_flash,apps 41firmware_flash,apps
@@ -127,6 +129,7 @@ sgt-loopy,games
127sgt-magnets,games 129sgt-magnets,games
128sgt-map,games 130sgt-map,games
129sgt-mines,games 131sgt-mines,games
132sgt-mosaic,games
130sgt-net,games 133sgt-net,games
131sgt-netslide,games 134sgt-netslide,games
132sgt-palisade,games 135sgt-palisade,games
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 8028758ef0..a070637a77 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -9,8 +9,10 @@ tagcache/tagcache.c
9chessclock.c 9chessclock.c
10credits.c 10credits.c
11cube.c 11cube.c
12cue_playlist.c
12dart_scorer.c 13dart_scorer.c
13dict.c 14dict.c
15file_picker.c
14jackpot.c 16jackpot.c
15keybox.c 17keybox.c
16keyremap.c 18keyremap.c
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index bb35bec726..d6da369b9e 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -674,7 +674,6 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
674 674
675 rb->gui_synclist_init(&games_list, &get_game_text, first_game, false, 1, NULL); 675 rb->gui_synclist_init(&games_list, &get_game_text, first_game, false, 1, NULL);
676 rb->gui_synclist_set_title(&games_list, rb->str(LANG_CHESSBOX_GAMES), NOICON); 676 rb->gui_synclist_set_title(&games_list, rb->str(LANG_CHESSBOX_GAMES), NOICON);
677 rb->gui_synclist_set_icon_callback(&games_list, NULL);
678 if (rb->global_settings->talk_menu) 677 if (rb->global_settings->talk_menu)
679 rb->gui_synclist_set_voice_callback(&games_list, speak_game_selection); 678 rb->gui_synclist_set_voice_callback(&games_list, speak_game_selection);
680 rb->gui_synclist_set_nb_items(&games_list, i); 679 rb->gui_synclist_set_nb_items(&games_list, i);
diff --git a/apps/plugins/cue_playlist.c b/apps/plugins/cue_playlist.c
new file mode 100644
index 0000000000..d3f64fcc50
--- /dev/null
+++ b/apps/plugins/cue_playlist.c
@@ -0,0 +1,375 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2024 William Wilgus
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* convert supplied playlist file to a .cue file */
23
24#include "plugin.h"
25
26#if defined(DEBUG) || defined(SIMULATOR)
27 #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n")
28#elif defined(ROCKBOX_HAS_LOGF)
29 #define logf rb->logf
30#else
31 #define logf(...) do { } while(0)
32#endif
33
34#define CPS_MAX_ENTRY_SZ (4 *1024)
35#define TDINDENT " " /* prepend spaces for track data formatting */
36
37static struct cps
38{
39 char *buffer;
40 size_t buffer_sz;
41 size_t buffer_index;
42 int cue_fd;
43 int entries;
44} cps;
45
46static int sprfunc(void *ptr, int letter)
47{
48 /* callback for vuprintf */
49 (void) ptr;
50 if (cps.buffer_index < cps.buffer_sz - 1)
51 {
52 cps.buffer[cps.buffer_index++] = letter;
53 return 1;
54 }
55 return -1;
56}
57
58void cps_printf(const char *fmt, ...)
59{
60 /* NOTE! this is made for flushing the buffer to disk -- WARNING
61 * Nothing is NULL terminated here unless explicitly made so.. \0 */
62 va_list ap;
63 va_start(ap, fmt);
64 rb->vuprintf(sprfunc, NULL, fmt, ap);
65 va_end(ap);
66}
67
68static uint32_t write_metadata_tags(struct mp3entry *id3)
69{
70/* check an ID3 and write any numeric tags and valid string tags (non empty) */
71#define ISVALID(s) (s != NULL && s[0] != '\0')
72 uint32_t tag_flags = 0;
73
74 const char *performer = rb->str(LANG_TAGNAVI_UNTAGGED);
75 if (ISVALID(id3->artist))
76 performer = id3->artist;
77 else if (ISVALID(id3->albumartist))
78 performer = id3->albumartist;
79
80 const char *title = rb->str(LANG_TAGNAVI_UNTAGGED);
81 if (ISVALID(id3->title))
82 title = id3->title;
83
84#define PERFORMER_TITLE_SZ TDINDENT "PERFORMER \"%s\"\n" \
85 TDINDENT "TITLE \"%s\"\n" \
86 TDINDENT "SIZE_INFO %ld\n"
87
88 cps_printf(PERFORMER_TITLE_SZ, performer, title, id3->filesize);
89 if (ISVALID(id3->composer))
90 cps_printf(TDINDENT "COMPOSER \"%s\"\n", id3->composer);
91
92 cps_printf(TDINDENT "INDEX 01 00:00:00\n");
93
94#define ID3_TAG_NUM(theid3, TAGID, flag) \
95 {cps_printf(TDINDENT "REM %s %lu\n", TAGID, (unsigned long)theid3);tag_flags |= flag;}
96#define ID3_TAG_STR(theid3, TAGID, flag) if (ISVALID(theid3)) \
97 {cps_printf(TDINDENT "REM %s \"%s\"\n", TAGID, theid3);tag_flags |= flag;}
98
99 ID3_TAG_STR(id3->album, "ALBUM", 0x01);
100 ID3_TAG_STR(id3->albumartist, "ALBUMARTIST", 0x02);
101 ID3_TAG_STR(id3->comment, "COMMENT", 0x04);
102 ID3_TAG_STR(id3->genre_string, "GENRE", 0x08);
103 ID3_TAG_STR(id3->disc_string, "DISC", 0x10);
104 ID3_TAG_STR(id3->track_string, "TRACK", 0x20);
105 ID3_TAG_STR(id3->grouping, "GROUPING", 0x40);
106 ID3_TAG_STR(id3->mb_track_id, "MB_TRACK_ID", 0x80);
107 ID3_TAG_STR(rb->get_codec_string(id3->codectype), "ID3_CODEC", 0x100);
108
109 ID3_TAG_NUM(id3->discnum, "DISCNUM", 0x200);
110 ID3_TAG_NUM(id3->tracknum, "TRACKNUM", 0x400);
111 ID3_TAG_NUM(id3->length, "LENGTH", 0x800);
112 ID3_TAG_NUM(id3->bitrate, "BITRATE", 0x1000);
113 ID3_TAG_NUM(id3->frequency, "FREQUENCY", 0x2000);
114 ID3_TAG_NUM(id3->track_level, "TRACK_LEVEL",0x4000);
115 ID3_TAG_NUM(id3->album_level, "ALBUM_LEVEL", 0x8000);
116#undef ID3_TAG_STR
117#undef ID3_TAG_NUM
118#undef IS_VALID
119 return tag_flags;
120}
121
122static bool current_playlist_filename_cb(const char *filename, int attr, int index, int display_index)
123{
124 /* worker function for writing the actual cue data */
125 int szpos = 0; /* records position of the size string */
126 int namepos = 0; /* records position of the end of filename string */
127 struct mp3entry id3;
128
129 logf("found: %s", filename);
130
131 uint32_t id3_flags = 0;
132 bool have_metadata = rb->get_metadata(&id3, -1, filename);
133
134 if (!have_metadata && !rb->file_exists(filename))
135 return false;
136#define RB_ENTRY_DATA_FMT "REM RB_ENTRY_DATA " \
137 "\"DISPLAY_INDEX %012u " \
138 "PLAYLIST_INDEX %012u " \
139 "SIZE %n%012zu TAGS %012lu\"\n"
140
141 const char *audiotype = "WAVE"; /* everything except MP3 */
142 const char *skipped = "";;
143 const char *queued = "";
144
145 size_t entry_start = cps.buffer_index; /* get start to calculate final size */
146
147 cps_printf(RB_ENTRY_DATA_FMT, display_index, index, &szpos, 0, 0UL);
148
149 size_t file_start = cps.buffer_index;
150 cps_printf("FILE \"%s%n\"", filename, &namepos);
151
152 if (cps.buffer[file_start + namepos - 1] == '3')
153 audiotype = "MP3";
154
155 cps_printf(" %s\n", audiotype);
156
157 if (attr & PLAYLIST_ATTR_SKIPPED)
158 skipped = TDINDENT "REM SKIPPED\n";
159 if (attr & PLAYLIST_ATTR_QUEUED)
160 queued = TDINDENT "REM QUEUED\n";
161
162 cps_printf(" TRACK %d AUDIO\n%s%s", display_index, skipped, queued);
163
164 if (have_metadata)
165 id3_flags = write_metadata_tags(&id3);
166
167 if (cps.buffer_index - entry_start < CPS_MAX_ENTRY_SZ)
168 {
169 /* place the write pointer at the size entry so we can update size + tags*/
170 size_t index = cps.buffer_index;
171 cps.buffer_index = entry_start + szpos;
172 cps_printf("%012zu TAGS %012lu", (index - entry_start), id3_flags);
173 cps.buffer_index = index; /* set the write pointer back at the end */
174 }
175 else
176 {
177 rb->splashf(HZ * 3, "Entry too large %s", filename);
178 cps.buffer_index = entry_start;
179 return false;
180 }
181
182 cps.entries++;
183 return true;
184}
185
186static bool playlist_filename_cb(const char *filename)
187{
188 /* get entries from an on-disk playlist */
189 return current_playlist_filename_cb(filename, 0,
190 cps.entries, cps.entries);
191}
192
193static bool current_playlist_get_entries(void)
194{
195 /* get entries from a loaded playlist ( may have queued or skipped tracks ) */
196#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
197#define cpuboost(enable) rb->cpu_boost(enable);
198#else
199#define cpuboost(enable) do{ } while(0)
200#endif
201 struct playlist_track_info info;
202 int count = rb->playlist_amount();
203 int i, res = 0;
204 logf("current playlist contains %d entries", count);
205
206 cpuboost(true);
207
208 long next_progress_tick = *rb->current_tick;
209 for (i = 0; i < count; i++)
210 {
211 res = rb->playlist_get_track_info(NULL, i, &info);
212 int attr = info.attr;
213 int index = info.index;
214 int display_index = info.display_index;
215 if (res < 0 || !current_playlist_filename_cb(info.filename, attr, index, display_index))
216 break;
217
218 if (cps.buffer_index >= (cps.buffer_sz - CPS_MAX_ENTRY_SZ))
219 {
220 logf("Buffer full, writing to disk");
221 rb->write(cps.cue_fd, cps.buffer, cps.buffer_index);
222 cps.buffer_index = 0;
223 }
224
225 if (TIME_AFTER(*rb->current_tick, next_progress_tick))
226 {
227 rb->splash_progress(i, count, "Processing current playlist %d", i);
228 int action = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
229 if (action == ACTION_STD_CANCEL)
230 {
231 res = -10;
232 break;
233 }
234 if (rb->default_event_handler(action) == SYS_USB_CONNECTED)
235 {
236 cpuboost(false);
237 return PLUGIN_USB_CONNECTED;
238 }
239 next_progress_tick = *rb->current_tick + HZ / 2;
240 }
241 rb->yield();
242 }
243
244 cpuboost(false);
245
246 return res >= 0;
247#undef cpuboost
248}
249
250static void init_new_cue(const char *playlist_filename)
251{
252 if (cps.cue_fd >= 0)
253 {
254 rb->lseek(cps.cue_fd, 0, SEEK_SET);
255 rb->fdprintf(cps.cue_fd, "REM COMMENT \"generated by Rockbox version: " \
256 "%s\"\n", rb->rbversion);
257
258 rb->fdprintf(cps.cue_fd, "TITLE \"%s\"\n", playlist_filename); /* top level TITLE */
259 }
260}
261
262static void finalize_new_cue(void)
263{
264 rb->write(cps.cue_fd, "\n", 1);
265 rb->close(cps.cue_fd);
266}
267
268static int create_new_cue(const char *filename)
269{
270 char buf[MAX_PATH];
271 if (!filename)
272 filename = "/Playlists/current.cue";
273 const char *dot = rb->strrchr(filename, '.');
274 int dotpos = 0;
275 if (dot)
276 dotpos = dot - filename;
277 rb->snprintf(buf, sizeof(buf), "%.*s.cue", dotpos, filename);
278 cps.cue_fd = rb->open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0666);
279
280 init_new_cue(filename);
281
282 return cps.cue_fd;
283}
284
285enum plugin_status plugin_start(const void* parameter)
286{
287
288 bool res;
289 rb->splash(HZ*2, ID2P(LANG_WAIT));
290
291 const char *filename = parameter;
292
293 if (create_new_cue(filename) < 0)
294 {
295 rb->splashf(HZ, "creat() failed: %d", cps.cue_fd);
296 return PLUGIN_ERROR;
297 }
298
299 cps.buffer = rb->plugin_get_buffer(&cps.buffer_sz);
300 if (cps.buffer != NULL)
301 {
302 cps.buffer_index = 0;
303#ifdef STORAGE_WANTS_ALIGN
304 /* align start and length for DMA */
305 STORAGE_ALIGN_BUFFER(cps.buffer, cps.buffer_sz);
306#else
307 /* align start and length to 32 bit */
308 ALIGN_BUFFER(cps.buffer, cps.buffer_sz, 4);
309#endif
310 }
311 if (cps.buffer == NULL|| cps.buffer_sz < CPS_MAX_ENTRY_SZ)
312 {
313 rb->splashf(HZ, "No Buffers Available :( ");
314 return PLUGIN_ERROR;
315 }
316
317 if (filename && filename[0])
318 res = rb->playlist_entries_iterate(filename, NULL, &playlist_filename_cb);
319 else
320 res = current_playlist_get_entries();
321
322 if (res)
323 {
324
325 if (cps.buffer_index > 0)
326 {
327 rb->write(cps.cue_fd, cps.buffer, cps.buffer_index);
328 cps.buffer_index = 0;
329
330 }
331 rb->splashf(HZ * 2,
332 "Playist parsing SUCCESS %d entries written", cps.entries);
333 }
334 else
335 {
336 rb->splashf(HZ * 2, "Playist parsing FAILED after %d entries", cps.entries);
337 }
338
339 finalize_new_cue();
340
341 if (!res)
342 return PLUGIN_ERROR;
343 return PLUGIN_OK;
344}
345
346/*
347#CUE FORMAT
348 CATALOG
349 CDTEXTFILE
350 FILE
351 FLAGS
352 INDEX
353 ISRC
354 PERFORMER
355 POSTGAP
356 PREGAP
357 REM
358 SONGWRITER
359 TITLE
360 TRACK
361#CD-TEXT https://wyday.com/cuesharp/specification.php
362 ARRANGER
363 COMPOSER
364 DISC_ID
365 GENRE
366 ISRC
367 MESSAGE
368 PERFORMER
369 SONGWRITER
370 TITLE
371 TOC_INFO
372 TOC_INFO2
373 UPC_EAN
374 SIZE_INFO
375*/
diff --git a/apps/plugins/file_picker.c b/apps/plugins/file_picker.c
new file mode 100644
index 0000000000..ce9d81728e
--- /dev/null
+++ b/apps/plugins/file_picker.c
@@ -0,0 +1,295 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2024 William Wilgus
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21/*File Picker Plugin
22
23FPP accepts several arguments to help in your file picking adventure
24* NOTE: anything with spaces should be quoted, -f and -l can't be used together *
25 -r "<GOTO_PLUGIN_NAME>" doesn't need to be a full path demos/file.rock works
26 the file (if choosen, cancel exits) will be passed as a parameter
27 -t "<TITLE ( max 64 chars )>"
28 -s "<START DIR>"
29 -f "<.EXT (max 64 chars) >" the extension of the files you are looking for
30 must have the '.' and ".*" accepts any file
31 -l "<.EXT.EXT,.EXT .EXT (max 64 chars) >" list of extensions for files you are looking for
32 each must have the '.' spaces and commas are ignored
33 -a attrib flags eg FILE_ATTR_AUDIO
34 -d disallow changing directories (hide directories doesn't allow changing from start dir)
35*/
36#include "plugin.h"
37#include "lang_enum.h"
38#include "lib/arg_helper.h"
39
40#if defined(DEBUG) || defined(SIMULATOR)
41 #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n")
42#elif defined(ROCKBOX_HAS_LOGF)
43 #define logf rb->logf
44#else
45 #define logf(...) do { } while(0)
46#endif
47
48#define FIND_NODIRS 0x01
49#define FIND_ATTRIB 0x02
50#define FIND_WILDCARD 0x04
51#define FIND_EXT 0x08
52#define FIND_EXT_IN_LIST 0x10
53
54static struct fpp
55{
56 char return_plugin[MAX_PATH];
57 char start_dir[MAX_PATH];
58 char file_ext[64];
59 char title[64];
60 int tree_attr;
61 int flags;
62}fpp;
63
64static int arg_callback(char argchar, const char **parameter, void *userdata)
65{
66 struct fpp *pfp = userdata;
67 int ret;
68 long num;
69 const char* start = *parameter;
70 while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++;
71 switch (tolower(argchar))
72 {
73 case 'd' :
74 pfp->flags |= FIND_NODIRS;
75 logf ("Find no dirs");
76 break;
77 case 'r' : /*return_plugin*/
78 logf ("trying PLUGIN_DIR...");
79 size_t l = rb->strlcpy(pfp->return_plugin,
80 PLUGIN_DIR,
81 sizeof(pfp->return_plugin));
82
83 ret = string_parse(parameter,
84 pfp->return_plugin + l,
85 sizeof(pfp->return_plugin) - l);
86
87 if (ret && !rb->file_exists(pfp->return_plugin))
88 {
89 logf("Failed");
90 *parameter = start;
91 string_parse(parameter, pfp->return_plugin,
92 sizeof(pfp->return_plugin));
93 }
94
95 if (ret)
96 {
97 logf ("Ret plugin: Val: %s\n", pfp->return_plugin);
98 logf("ate %d chars\n", ret);
99 }
100 break;
101 case 't' : /* title */
102 ret = string_parse(parameter, pfp->title, sizeof(pfp->title));
103 if (ret)
104 {
105 logf ("Title: Val: %s\n", pfp->title);
106 logf("ate %d chars\n", ret);
107 }
108 break;
109 case 's' : /* start directory */
110 ret = string_parse(parameter, pfp->start_dir, sizeof(pfp->start_dir));
111 if (ret)
112 {
113 if (!rb->dir_exists(pfp->start_dir))
114 {
115 rb->strlcpy(pfp->start_dir, PATH_ROOTSTR, sizeof(pfp->start_dir));
116 }
117
118 logf ("Start dir: Val: %s\n", pfp->start_dir);
119 logf("ate %d chars\n", ret);
120 }
121 break;
122 case 'f' : /* file extension */
123 if (pfp->flags & FIND_EXT_IN_LIST)
124 {
125 rb->splash(HZ*5, "list extensions already active -f ignored");
126 break;
127 }
128 ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext));
129 if (ret)
130 {
131 if (pfp->file_ext[1] == '*')
132 pfp->flags |= FIND_WILDCARD;
133 else
134 pfp->flags |= FIND_EXT;
135 logf ("Extension: Val: %s\n", pfp->file_ext);
136 logf("ate %d chars\n", ret);
137 }
138 break;
139 case 'l' : /* file extension list */
140 if (pfp->flags & FIND_EXT)
141 {
142 rb->splash(HZ*5, "extension already active -l ignored");
143 break;
144 }
145 ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext));
146 if (ret)
147 {
148 char *wr = pfp->file_ext;
149 char *rd = pfp->file_ext;
150 while (*rd != '\0') /* copy the extensions */
151 {
152 if (*rd == ' ' || *rd == ',' || *rd == ';')
153 {
154 /* ignore spaces, commas, and semicolons */
155 rd++;
156 continue;
157 }
158 *wr++ = *rd++;
159 }
160 *wr = '\0';
161 pfp->flags |= FIND_EXT_IN_LIST;
162 logf ("Extension List: Val: %s\n", pfp->file_ext);
163 logf("ate %d chars\n", ret);
164 }
165 break;
166 case 'a' : /* tree attribute */
167 ret = longnum_parse(parameter, &num, NULL);
168 if (ret)
169 {
170 pfp->tree_attr = (num)&FILE_ATTR_MASK;
171 pfp->flags |= FIND_ATTRIB;
172 logf ("Attrib: Val: 0x%x\n", (uint32_t)num);
173 logf("ate %d chars\n", ret);
174 }
175 break;
176 default :
177 rb->splashf(HZ, "Unknown switch '%c'",argchar);
178 logf("Unknown switch '%c'",argchar);
179 //return 0;
180 }
181
182 return 1;
183}
184
185static bool cb_show_item(char *name, int attr, struct tree_context *tc)
186{
187 static int dirlevel = -1;
188 if(attr & ATTR_DIRECTORY)
189 {
190 if (fpp.flags & FIND_NODIRS)
191 {
192 if (tc->dirlevel == dirlevel)
193 return false;
194 dirlevel = tc->dirlevel;
195 if (rb->strcasestr(tc->currdir, fpp.start_dir) == NULL)
196 {
197 tc->is_browsing = false; /* exit immediately */
198 logf("exiting %d", tc->dirlevel);
199 }
200 }
201
202 return true;
203 }
204 if (fpp.flags & FIND_WILDCARD)
205 {
206 return true;
207 }
208 if ((fpp.flags & FIND_ATTRIB) && (fpp.tree_attr & attr) != 0)
209 {
210 return true;
211 }
212 if (fpp.flags & FIND_EXT)
213 {
214 const char *p = rb->strrchr(name, '.' );
215 if (p != NULL && !rb->strcasecmp( p, fpp.file_ext))
216 return true;
217 }
218
219 if (fpp.flags & FIND_EXT_IN_LIST)
220 {
221 const char *p = rb->strrchr(name, '.' );
222 if (p != NULL && rb->strcasestr(fpp.file_ext, p) != NULL)
223 return true;
224 }
225
226 logf("Excluded: %s", name);
227 return false;
228
229}
230
231static int browse_file_dir(struct fpp *pfp)
232{
233 char buf[MAX_PATH];
234 struct browse_context browse = {
235 .dirfilter = SHOW_ALL,
236 .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
237 .title = pfp->title,
238 .icon = Icon_Playlist,
239 .buf = buf,
240 .bufsize = sizeof(buf),
241 .root = pfp->start_dir,
242 .callback_show_item = &cb_show_item,
243 };
244
245 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
246 {
247 if (rb->file_exists(buf))
248 {
249 logf("Loading %s", buf);
250 return rb->plugin_open(pfp->return_plugin, buf);
251 }
252 else
253 {
254 logf("Error opening %s", buf);
255 rb->splashf(HZ *2, "Error Opening %s", buf);
256 return PLUGIN_ERROR;
257 }
258 }
259
260 return PLUGIN_OK;
261}
262
263enum plugin_status plugin_start(const void* parameter)
264{
265 if (!parameter)
266 {
267 rb->splash(HZ *2, "No Args");
268 return PLUGIN_ERROR;
269 }
270
271 argparse((const char*) parameter, -1, &fpp, &arg_callback);
272
273 if (fpp.title[0] == '\0')
274 {
275 if (rb->global_settings->talk_menu)
276 rb->talk_id(LANG_CHOOSE_FILE, true);
277
278 if ((fpp.flags & FIND_EXT) || (fpp.flags & FIND_EXT_IN_LIST))
279 {
280 rb->snprintf(fpp.title, sizeof(fpp.title),
281 "%s (%s)", rb->str(LANG_CHOOSE_FILE), fpp.file_ext);
282 if (rb->global_settings->talk_menu)
283 rb->talk_spell(fpp.file_ext, true);
284 }
285 else
286 {
287 rb->snprintf(fpp.title, sizeof(fpp.title), "%s", rb->str(LANG_CHOOSE_FILE));
288 }
289 rb->talk_force_enqueue_next();
290 }
291 if (fpp.start_dir[0] == '\0' || !rb->dir_exists(fpp.start_dir))
292 rb->strcpy(fpp.start_dir, PATH_ROOTSTR);
293
294 return browse_file_dir(&fpp);
295}
diff --git a/apps/plugins/keybox.c b/apps/plugins/keybox.c
index cb2e23a94a..749d76af74 100644
--- a/apps/plugins/keybox.c
+++ b/apps/plugins/keybox.c
@@ -657,7 +657,6 @@ enum plugin_status plugin_start(const void *parameter)
657 rb->gui_synclist_init(&kb_list, &kb_list_cb, NULL, false, 1, NULL); 657 rb->gui_synclist_init(&kb_list, &kb_list_cb, NULL, false, 1, NULL);
658 658
659 rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON); 659 rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
660 rb->gui_synclist_set_icon_callback(&kb_list, NULL);
661 rb->gui_synclist_set_nb_items(&kb_list, 0); 660 rb->gui_synclist_set_nb_items(&kb_list, 0);
662 rb->gui_synclist_select_item(&kb_list, 0); 661 rb->gui_synclist_select_item(&kb_list, 0);
663 662
diff --git a/apps/plugins/lib/arg_helper.c b/apps/plugins/lib/arg_helper.c
index 3ea5ba714d..63b7acbb2b 100644
--- a/apps/plugins/lib/arg_helper.c
+++ b/apps/plugins/lib/arg_helper.c
@@ -31,7 +31,7 @@
31#ifdef PLUGIN 31#ifdef PLUGIN
32 #define strchr rb->strchr 32 #define strchr rb->strchr
33#endif 33#endif
34int string_parse(const char **parameter, char* buf, size_t buf_sz) 34int string_parse(const char **parameter, char *buf, size_t buf_sz)
35{ 35{
36/* fills buf with a string upto buf_sz, null terminates the buffer 36/* fills buf with a string upto buf_sz, null terminates the buffer
37 * strings break on WS by default but can be enclosed in single or double quotes 37 * strings break on WS by default but can be enclosed in single or double quotes
@@ -44,6 +44,11 @@ int string_parse(const char **parameter, char* buf, size_t buf_sz)
44 char stopchars[] = "\'\""; 44 char stopchars[] = "\'\"";
45 int skipped = 0; 45 int skipped = 0;
46 int found = 0; 46 int found = 0;
47 if (!parameter || !*parameter)
48 {
49 *buf = '\0';
50 return 0;
51 }
47 const char* start = *parameter; 52 const char* start = *parameter;
48 53
49 if (strchr(stopchars, *start)) 54 if (strchr(stopchars, *start))
@@ -79,7 +84,7 @@ int string_parse(const char **parameter, char* buf, size_t buf_sz)
79 return found + skipped; 84 return found + skipped;
80} 85}
81 86
82int char_parse(const char **parameter, char* character) 87int char_parse(const char **parameter, char *character)
83{ 88{
84/* passes *character a single character eats remaining non-WS characters */ 89/* passes *character a single character eats remaining non-WS characters */
85 char buf[2]; 90 char buf[2];
@@ -95,6 +100,8 @@ int bool_parse(const char **parameter, bool *choice)
95/* determine true false using the first character the rest are skipped/ignored */ 100/* determine true false using the first character the rest are skipped/ignored */
96 int found = 0; 101 int found = 0;
97 const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */ 102 const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */
103 if (!parameter || !*parameter)
104 return 0;
98 const char* start = *parameter; 105 const char* start = *parameter;
99 106
100 107
@@ -133,7 +140,9 @@ int longnum_parse(const char **parameter, long *number, long *decimal)
133 int neg = 0; 140 int neg = 0;
134 int digits = 0; 141 int digits = 0;
135 //logf ("n: %s\n", *parameter); 142 //logf ("n: %s\n", *parameter);
136 const char *start = *parameter; 143 if (!parameter || !*parameter)
144 return 0;
145 const char* start = *parameter;
137 146
138 if (*start == '-') 147 if (*start == '-')
139 { 148 {
@@ -209,7 +218,8 @@ int num_parse(const char **parameter, int *number, int *decimal)
209* Note: WS at beginning is stripped, **parameter starts at the first NON WS char 218* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
210* return 0 for arg_callback to quit parsing immediately 219* return 0 for arg_callback to quit parsing immediately
211*/ 220*/
212void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char argchar, const char **parameter)) 221void argparse(const char *parameter, int parameter_len, void *userdata,
222 int (*arg_callback)(char argchar, const char **parameter, void *userdata))
213{ 223{
214 bool lastchr; 224 bool lastchr;
215 char argchar; 225 char argchar;
@@ -222,7 +232,10 @@ void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char
222 { 232 {
223 if ((*parameter) == '\0') 233 if ((*parameter) == '\0')
224 return; 234 return;
225 logf ("%s\n",parameter); 235
236 if (parameter_len < 0) { logf ("%s\n", parameter); }
237 else { logf ("%.*s\n", plen, parameter); }
238
226 argchar = *parameter; 239 argchar = *parameter;
227 lastchr = (*(parameter + 1) == '\0'); 240 lastchr = (*(parameter + 1) == '\0');
228 while (*++parameter || lastchr) 241 while (*++parameter || lastchr)
@@ -230,7 +243,7 @@ void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char
230 lastchr = false; 243 lastchr = false;
231 if (isspace(*parameter)) 244 if (isspace(*parameter))
232 continue; /* eat spaces at beginning */ 245 continue; /* eat spaces at beginning */
233 if (!arg_callback(argchar, &parameter)) 246 if (!arg_callback(argchar, &parameter, userdata))
234 return; 247 return;
235 break; 248 break;
236 } 249 }
diff --git a/apps/plugins/lib/arg_helper.h b/apps/plugins/lib/arg_helper.h
index 2cf94ba1dd..638279ee42 100644
--- a/apps/plugins/lib/arg_helper.h
+++ b/apps/plugins/lib/arg_helper.h
@@ -54,7 +54,7 @@ int num_parse(const char **parameter, int *number, int *decimal);
54* Note: WS at beginning is stripped, **parameter starts at the first NON WS char 54* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
55* return 0 for arg_callback to quit parsing immediately 55* return 0 for arg_callback to quit parsing immediately
56*/ 56*/
57void argparse(const char *parameter, int parameter_len, 57void argparse(const char *parameter, int parameter_len, void *userdata,
58 int (*arg_callback)(char argchar, const char **parameter)); 58 int (*arg_callback)(char argchar, const char **parameter, void *userdata));
59 59
60#endif /* _LIB_ARG_HELPER_H_ */ 60#endif /* _LIB_ARG_HELPER_H_ */
diff --git a/apps/plugins/lib/id3.c b/apps/plugins/lib/id3.c
index b0202b1d9c..6af715b1fb 100644
--- a/apps/plugins/lib/id3.c
+++ b/apps/plugins/lib/id3.c
@@ -35,5 +35,5 @@ bool retrieve_id3(struct mp3entry *id3, const char* file)
35 } 35 }
36#endif 36#endif
37 37
38 return !rb->mp3info(id3, file); 38 return rb->get_metadata(id3, -1, file);
39} 39}
diff --git a/apps/plugins/lib/mul_id3.c b/apps/plugins/lib/mul_id3.c
index edf44f7282..3ab3a438c8 100644
--- a/apps/plugins/lib/mul_id3.c
+++ b/apps/plugins/lib/mul_id3.c
@@ -40,6 +40,13 @@ struct multiple_tracks_id3 {
40 40
41static struct multiple_tracks_id3 mul_id3; 41static struct multiple_tracks_id3 mul_id3;
42 42
43static const int32_t units[] =
44{
45 LANG_BYTE,
46 LANG_KIBIBYTE,
47 LANG_MEBIBYTE,
48 LANG_GIBIBYTE
49};
43 50
44/* Calculate modified FNV hash of string 51/* Calculate modified FNV hash of string
45 * has good avalanche behaviour and uniform distribution 52 * has good avalanche behaviour and uniform distribution
@@ -130,9 +137,7 @@ void collect_id3(struct mp3entry *id3, bool is_first_track)
130 mul_id3.filesize += id3->filesize; 137 mul_id3.filesize += id3->filesize;
131} 138}
132 139
133/* (!) Note scale factor applied to returned metadata: 140/* (!) Note unit conversion below
134 * - Unit for filesize will be KiB instead of Bytes
135 * - Unit for length will be s instead of ms
136 * 141 *
137 * Use result only as input for browse_id3, 142 * Use result only as input for browse_id3,
138 * with the track_ct parameter set to > 1. 143 * with the track_ct parameter set to > 1.
@@ -159,8 +164,8 @@ void finalize_id3(struct mp3entry *id3)
159 id3->track_string = NULL; 164 id3->track_string = NULL;
160 id3->year_string = NULL; 165 id3->year_string = NULL;
161 id3->year = mul_id3.year; 166 id3->year = mul_id3.year;
162 mul_id3.length /= 1000; 167 mul_id3.length /= 1000; /* convert from ms to s */
163 mul_id3.filesize >>= 10; 168 mul_id3.filesize >>= 10; /* convert from B to KiB */
164 id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length; 169 id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length;
165 id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize; 170 id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize;
166 id3->frequency = mul_id3.frequency; 171 id3->frequency = mul_id3.frequency;
@@ -172,3 +177,163 @@ void finalize_id3(struct mp3entry *id3)
172 id3->track_level = 0; 177 id3->track_level = 0;
173 id3->album_level = 0; 178 id3->album_level = 0;
174} 179}
180
181unsigned long human_size(unsigned long long byte_count, int32_t *unit_lang_id)
182{
183 const size_t n = sizeof(units)/sizeof(units[0]);
184 unsigned int i;
185
186 /* margin set at 10K boundary: 10239 B +1 => 10 KB */
187 for(i = 0; i < n-1 && byte_count >= 10*1024; i++)
188 byte_count >>= 10; /* div by 1024 */
189
190 *unit_lang_id = units[i];
191 return (unsigned long)byte_count;
192}
193
194/* missing filetype attribute for images */
195static const char *image_exts[] = {"bmp","jpg","jpe","jpeg","png","ppm"};
196/* and videos */
197static const char *video_exts[] = {"mpg","mpeg","mpv","m2v"};
198
199static void display_dir_stats_vp(struct dir_stats *stats, struct viewport *vp,
200 struct screen *display)
201{
202 int32_t lang_size_unit;
203 unsigned long display_size = human_size(stats->byte_count, &lang_size_unit);
204 struct viewport *last_vp = display->set_viewport(vp);
205 display->clear_viewport();
206 display->putsf(0, 0, "Files: %d (%lu %s)", stats->file_count,
207 display_size, rb->str(lang_size_unit));
208 display->putsf(0, 1, "Audio: %d", stats->audio_file_count);
209 if (stats->count_all)
210 {
211 display->putsf(0, 2, "Playlists: %d", stats->m3u_file_count);
212 display->putsf(0, 3, "Images: %d", stats->img_file_count);
213 display->putsf(0, 4, "Videos: %d", stats->vid_file_count);
214 display->putsf(0, 5, "Directories: %d", stats->dir_count);
215 display->putsf(0, 6, "Max files in Dir: %d", stats->max_files_in_dir);
216 }
217 else
218 display->putsf(0, 2, "Directories: %d", stats->dir_count);
219
220 display->update_viewport();
221 display->set_viewport(last_vp);
222}
223
224void display_dir_stats(struct dir_stats *stats)
225{
226 struct viewport vps[NB_SCREENS];
227 FOR_NB_SCREENS(i)
228 {
229 rb->viewport_set_defaults(&vps[i], i);
230 display_dir_stats_vp(stats, &vps[i], rb->screens[i]);
231 }
232}
233
234/* Recursively scans directories in search of files
235 * and informs the user of the progress.
236 */
237bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*))
238{
239 bool result = true;
240 unsigned int files_in_dir = 0;
241 static unsigned int id3_count;
242 static unsigned long last_displayed, last_get_action;
243 struct dirent* entry;
244 int dirlen = rb->strlen(stats->dirname);
245 DIR* dir = rb->opendir(stats->dirname);
246 if (!dir)
247 {
248 rb->splashf(HZ*2, "open error: %s", stats->dirname);
249 return false;
250 }
251 else if (!stats->dirname[1]) /* root dir */
252 stats->dirname[0] = dirlen = 0;
253
254 /* walk through the directory content */
255 while(result && (0 != (entry = rb->readdir(dir))))
256 {
257 struct dirinfo info = rb->dir_get_info(dir, entry);
258 if (info.attribute & ATTR_DIRECTORY)
259 {
260 if (!rb->strcmp((char *)entry->d_name, ".") ||
261 !rb->strcmp((char *)entry->d_name, ".."))
262 continue; /* skip these */
263
264 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
265 "/%s", entry->d_name); /* append name to current directory */
266 if (!id3_cb)
267 {
268 stats->dir_count++; /* new directory */
269 if (*rb->current_tick - last_displayed > (HZ/2))
270 {
271 if (last_displayed)
272 display_dir_stats(stats);
273 last_displayed = *(rb->current_tick);
274 }
275 }
276 result = collect_dir_stats(stats, id3_cb); /* recursion */
277 }
278 else if (!id3_cb)
279 {
280 char *ptr;
281 stats->file_count++; /* new file */
282 files_in_dir++;
283 stats->byte_count += info.size;
284
285 int attr = rb->filetype_get_attr(entry->d_name);
286 if (attr == FILE_ATTR_AUDIO)
287 stats->audio_file_count++;
288 else if (attr == FILE_ATTR_M3U)
289 stats->m3u_file_count++;
290 /* image or video file attributes have to be compared manually */
291 else if (stats->count_all &&
292 (ptr = rb->strrchr(entry->d_name,'.')))
293 {
294 unsigned int i;
295 ptr++;
296 for(i = 0; i < ARRAYLEN(image_exts); i++)
297 {
298 if(!rb->strcasecmp(ptr, image_exts[i]))
299 {
300 stats->img_file_count++;
301 break;
302 }
303 }
304 if (i >= ARRAYLEN(image_exts)) {
305 for(i = 0; i < ARRAYLEN(video_exts); i++) {
306 if(!rb->strcasecmp(ptr, video_exts[i])) {
307 stats->vid_file_count++;
308 break;
309 }
310 }
311 }
312 }
313 }
314 else if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_AUDIO)
315 {
316 rb->splash_progress(id3_count++, stats->audio_file_count,
317 "%s (%s)",
318 rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
319 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
320 "/%s", entry->d_name); /* append name to current directory */
321 id3_cb(stats->dirname); /* allow metadata to be collected */
322 }
323
324 if (TIME_AFTER(*(rb->current_tick), last_get_action + HZ/8))
325 {
326 if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
327 {
328 stats->canceled = true;
329 result = false;
330 }
331 last_get_action = *(rb->current_tick);
332 }
333 rb->yield();
334 }
335 rb->closedir(dir);
336 if (stats->max_files_in_dir < files_in_dir)
337 stats->max_files_in_dir = files_in_dir;
338 return result;
339}
diff --git a/apps/plugins/lib/mul_id3.h b/apps/plugins/lib/mul_id3.h
index d08095de5c..1bb311c441 100644
--- a/apps/plugins/lib/mul_id3.h
+++ b/apps/plugins/lib/mul_id3.h
@@ -21,7 +21,39 @@
21#ifndef MUL_ID3_H 21#ifndef MUL_ID3_H
22#define MUL_ID3_H 22#define MUL_ID3_H
23 23
24struct dir_stats {
25 char dirname[MAX_PATH];
26 unsigned int dir_count;
27 unsigned int file_count;
28 unsigned int audio_file_count;
29 unsigned int m3u_file_count;
30 unsigned int img_file_count;
31 unsigned int vid_file_count;
32 unsigned int max_files_in_dir;
33 unsigned long long byte_count;
34 bool count_all;
35 bool canceled;
36};
37
38/* create mp3entry that contains matching metadata from multiple tracks */
24void collect_id3(struct mp3entry *id3, bool is_first_track); 39void collect_id3(struct mp3entry *id3, bool is_first_track);
25void finalize_id3(struct mp3entry *id3); 40void finalize_id3(struct mp3entry *id3);
26 41
42/* Traverse directory, collecting stats/track metadata.
43 *
44 * 1) If id3_cb is null, dir_properties calculates all dir stats, including the
45 * audio file count.
46 *
47 * 2) If id3_cb points to a function, dir_properties will call it for every audio
48 * file encountered, to allow the file's metadata to be collected. The displayed
49 * progress bar's maximum value is set to the audio file count.
50 * Stats are assumed to have already been generated by a preceding run.
51 *
52 * If the count_all parameter is set to false, images and videos are not counted,
53 * nor is the playlist, image, video or max file in dir count displayed.
54 */
55bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*));
56void display_dir_stats(struct dir_stats *stats);
57unsigned long human_size(unsigned long long byte_count, int32_t *unit_lang_id);
58
27#endif /* MUL_ID3_H */ 59#endif /* MUL_ID3_H */
diff --git a/apps/plugins/lua/lua.make b/apps/plugins/lua/lua.make
index c85182880b..a5d1813a8b 100644
--- a/apps/plugins/lua/lua.make
+++ b/apps/plugins/lua/lua.make
@@ -40,7 +40,7 @@ $(LUA_BUILDDIR)/settings.lua: $(LUA_OBJ) $(LUA_SRCDIR)/settings_helper.pl
40 40
41HOST_INCLUDES := $(filter-out %/libc/include,$(INCLUDES)) 41HOST_INCLUDES := $(filter-out %/libc/include,$(INCLUDES))
42$(LUA_BUILDDIR)/buttons.lua: $(LUA_OBJ) $(LUA_SRCDIR)/button_helper.pl 42$(LUA_BUILDDIR)/buttons.lua: $(LUA_OBJ) $(LUA_SRCDIR)/button_helper.pl
43 $(SILENT)$(CC) $(INCLUDES) $(TARGET) $(CFLAGS) -dM -E -P -include button-target.h - < /dev/null | $(LUA_SRCDIR)/button_helper.pl | $(HOSTCC) $(TARGET) -fno-builtin $(HOST_INCLUDES) -x c -o $(LUA_BUILDDIR)/button_helper - 43 $(SILENT)$(CC) $(INCLUDES) $(TARGET) $(CFLAGS) -dM -E -P -include button-target.h - < /dev/null | $(LUA_SRCDIR)/button_helper.pl | $(HOSTCC) $(TARGET) -fno-builtin $(HOST_INCLUDES) $(EXTRA_DEFINES) -x c -o $(LUA_BUILDDIR)/button_helper -
44 $(call PRINTS,GEN $(@F))$(LUA_BUILDDIR)/button_helper > $(LUA_BUILDDIR)/buttons.lua 44 $(call PRINTS,GEN $(@F))$(LUA_BUILDDIR)/button_helper > $(LUA_BUILDDIR)/buttons.lua
45 45
46$(LUA_BUILDDIR)/rb_defines.lua: $(LUA_OBJ) $(LUA_SRCDIR)/rbdefines_helper.pl 46$(LUA_BUILDDIR)/rb_defines.lua: $(LUA_OBJ) $(LUA_SRCDIR)/rbdefines_helper.pl
diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c
index 3cf0fce945..650c4d6dae 100644
--- a/apps/plugins/lua/rocklua.c
+++ b/apps/plugins/lua/rocklua.c
@@ -241,6 +241,20 @@ static void display_traceback(const char *errstr)
241 rb->splash(10 * HZ, errstr); 241 rb->splash(10 * HZ, errstr);
242#endif 242#endif
243} 243}
244
245int browse_scripts(void)
246{
247 static char buf[MAX_PATH];
248 const char *fname = rb->plugin_get_current_filename();
249 /* strip plugin dir to save space in the param buffer */
250 if (rb->strncmp(fname, PLUGIN_DIR, sizeof(PLUGIN_DIR) - 1) == 0)
251 fname += sizeof(PLUGIN_DIR) - 1; /* leave slash */
252 /* -r return to this plugin, -f looking for lua files,
253 -s start in lua_scripts, -d lock to that directory */
254 snprintf(buf, sizeof(buf), "-r'%s'-f'.lua'-s'%s'-d",
255 fname, PLUGIN_DEMOS_DIR"/lua_scripts/");
256 return rb->plugin_open(VIEWERS_DIR "/file_picker.rock", buf);
257}
244/***************** Plugin Entry Point *****************/ 258/***************** Plugin Entry Point *****************/
245enum plugin_status plugin_start(const void* parameter) 259enum plugin_status plugin_start(const void* parameter)
246{ 260{
@@ -249,7 +263,10 @@ enum plugin_status plugin_start(const void* parameter)
249 if (parameter == NULL) 263 if (parameter == NULL)
250 { 264 {
251 if (!Ls) 265 if (!Ls)
266 {
252 rb->splash(HZ, "Play a .lua file!"); 267 rb->splash(HZ, "Play a .lua file!");
268 return browse_scripts();
269 }
253 } 270 }
254 else 271 else
255 { 272 {
diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c
index 66200df8a5..7206fe2a49 100644
--- a/apps/plugins/open_plugins.c
+++ b/apps/plugins/open_plugins.c
@@ -643,7 +643,6 @@ static void synclist_set(char* menu_id, int selection, int items, int sel_size)
643 rb->gui_synclist_init(&lists,list_get_name_cb, 643 rb->gui_synclist_init(&lists,list_get_name_cb,
644 menu_id, false, sel_size, NULL); 644 menu_id, false, sel_size, NULL);
645 645
646 rb->gui_synclist_set_icon_callback(&lists,NULL);
647 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); 646 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
648 rb->gui_synclist_set_nb_items(&lists,items); 647 rb->gui_synclist_set_nb_items(&lists,items);
649 rb->gui_synclist_select_item(&lists, selection); 648 rb->gui_synclist_select_item(&lists, selection);
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index 87ad1a403f..e5492cf01c 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -4040,7 +4040,7 @@ static int show_id3_info(const char *selected_file)
4040 i = 0; 4040 i = 0;
4041 do { 4041 do {
4042 file_name = i == 0 ? selected_file : get_track_filename(i); 4042 file_name = i == 0 ? selected_file : get_track_filename(i);
4043 if (rb->mp3info(&id3, file_name)) 4043 if (!rb->get_metadata(&id3, -1, file_name))
4044 return 0; 4044 return 0;
4045 4045
4046 if (is_multiple_tracks) 4046 if (is_multiple_tracks)
diff --git a/apps/plugins/pitch_screen.c b/apps/plugins/pitch_screen.c
index e24e0240a2..4af34fed3b 100644
--- a/apps/plugins/pitch_screen.c
+++ b/apps/plugins/pitch_screen.c
@@ -1120,8 +1120,9 @@ int gui_syncpitchscreen_run(void)
1120 return 0; 1120 return 0;
1121} 1121}
1122 1122
1123static int arg_callback(char argchar, const char **parameter) 1123static int arg_callback(char argchar, const char **parameter, void *userdata)
1124{ 1124{
1125 (void)userdata;
1125 int ret; 1126 int ret;
1126 long num, dec; 1127 long num, dec;
1127 bool bret; 1128 bool bret;
@@ -1232,7 +1233,7 @@ enum plugin_status plugin_start(const void* parameter)
1232 struct pvars cur; 1233 struct pvars cur;
1233 fill_pitchvars(&cur); 1234 fill_pitchvars(&cur);
1234 fill_pitchvars(&pitch_vars); 1235 fill_pitchvars(&pitch_vars);
1235 argparse((const char*) parameter, -1, &arg_callback); 1236 argparse((const char*) parameter, -1, NULL, &arg_callback);
1236 if (pitch_vars.pitch != cur.pitch) 1237 if (pitch_vars.pitch != cur.pitch)
1237 { 1238 {
1238 rb->sound_set_pitch(pitch_vars.pitch); 1239 rb->sound_set_pitch(pitch_vars.pitch);
diff --git a/apps/plugins/playing_time.c b/apps/plugins/playing_time.c
index e465c35a18..47784cf85e 100644
--- a/apps/plugins/playing_time.c
+++ b/apps/plugins/playing_time.c
@@ -32,10 +32,10 @@ const unsigned char * const byte_units[] =
32}; 32};
33 33
34const int menu_items[] = { 34const int menu_items[] = {
35 LANG_PLAYTIME_ELAPSED, 35 LANG_REMAINING,
36 LANG_PLAYTIME_REMAINING, 36 LANG_ELAPSED,
37 LANG_PLAYTIME_TRK_ELAPSED,
38 LANG_PLAYTIME_TRK_REMAINING, 37 LANG_PLAYTIME_TRK_REMAINING,
38 LANG_PLAYTIME_TRK_ELAPSED,
39 LANG_PLAYTIME_TRACK, 39 LANG_PLAYTIME_TRACK,
40 LANG_PLAYTIME_STORAGE, 40 LANG_PLAYTIME_STORAGE,
41 LANG_PLAYTIME_AVG_TRACK_SIZE, 41 LANG_PLAYTIME_AVG_TRACK_SIZE,
@@ -44,212 +44,237 @@ const int menu_items[] = {
44 44
45const unsigned char * const * const kibyte_units = &byte_units[1]; 45const unsigned char * const * const kibyte_units = &byte_units[1];
46 46
47enum ePT_SECS { 47enum ePT_SUM {
48 ePT_SECS_TTL = 0,
49 ePT_SECS_BEF,
50 ePT_SECS_AFT,
51 ePT_SECS_COUNT
52};
53
54enum ePT_KBS {
55 /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */ 48 /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */
56 ePT_KBS_TTL = 0, 49 ePT_TOTAL = 0,
57 ePT_KBS_BEF, 50 ePT_ELAPSED,
58 ePT_KBS_AFT, 51 ePT_REMAINING,
59 ePT_KBS_COUNT 52
53 ePT_COUNT
60}; 54};
61 55
62/* playing_time screen context */
63struct playing_time_info { 56struct playing_time_info {
64 int curr_index; /* index of currently playing track in playlist */ 57 char single_mode_tag[MAX_PATH]; /* Relevant tag when single mode enabled */
65 int curr_display_index; /* display index of currently playing track in playlist */ 58 unsigned long long size[ePT_COUNT]; /* File size of tracks */
66 int nb_tracks; /* how many tracks in playlist */ 59 unsigned long long length[ePT_COUNT]; /* Length of tracks */
67 60 unsigned long curr_track_length[ePT_COUNT]; /* Current track length */
68 /* seconds total, before, and after current position. Datatype 61 int curr_track_index; /* Index of currently playing track in playlist */
69 allows for values up to 68years. If I had kept it in ms 62 int curr_display_index; /* Display index of currently playing track */
70 though, it would have overflowed at 24days, which takes 63 int actual_index; /* Display index in actually counted tracks */
71 something like 8.5GB at 32kbps, and so we could conceivably 64 int counted; /* Number of tracks already added up */
72 have playlists lasting longer than that. */ 65 int nb_tracks; /* Number of tracks in playlist */
73 long secs[ePT_SECS_COUNT]; 66 int error_count; /* Number of tracks whose data couldn't be retrieved */
74 long trk_secs[ePT_SECS_COUNT]; 67 bool remaining_only; /* Whether to ignore elapsed tracks */
75
76 /* kilobytes played total, before, and after current pos.
77 Kilobytes because bytes would overflow. Data type range is up
78 to 2TB. */
79 long kbs[ePT_KBS_COUNT];
80}; 68};
81 69
70static int32_t single_mode_lang(void)
71{
72 switch (rb->global_settings->single_mode)
73 {
74 case SINGLE_MODE_ALBUM:
75 return LANG_ID3_ALBUM;
76 case SINGLE_MODE_ALBUM_ARTIST:
77 return LANG_ID3_ALBUMARTIST;
78 case SINGLE_MODE_ARTIST:
79 return LANG_ID3_ARTIST;
80 case SINGLE_MODE_COMPOSER:
81 return LANG_ID3_COMPOSER;
82 case SINGLE_MODE_GROUPING:
83 return LANG_ID3_GROUPING;
84 case SINGLE_MODE_GENRE:
85 return LANG_ID3_GENRE;
86 case SINGLE_MODE_TRACK:
87 return LANG_TRACK;
88 }
89 return LANG_OFF;
90}
91
92static char* single_mode_id3_tag(struct mp3entry *id3)
93{
94 switch (rb->global_settings->single_mode)
95 {
96 case SINGLE_MODE_ALBUM:
97 return id3->album;
98 case SINGLE_MODE_ALBUM_ARTIST:
99 return id3->albumartist;
100 case SINGLE_MODE_ARTIST:
101 return id3->artist;
102 case SINGLE_MODE_COMPOSER:
103 return id3->composer;
104 case SINGLE_MODE_GROUPING:
105 return id3->grouping;
106 case SINGLE_MODE_GENRE:
107 return id3->genre_string;
108 }
109 return NULL;
110}
111
82static char* get_percent_str(long percents) 112static char* get_percent_str(long percents)
83{ 113{
84 static char val[10]; 114 static char val[10];
85 rb->snprintf(val, 10, rb->str(LANG_PERCENT_FORMAT), percents); 115 rb->snprintf(val, sizeof(val), rb->str(LANG_PERCENT_FORMAT), percents);
86 return val; 116 return val;
87} 117}
88 118
89static inline void prepare_time_string(char *buf, size_t buffer_len, long elapsed_pct, const char *timestr1, const char *timestr2) 119static inline void prepare_time_string(char *buf, size_t buffer_len,
120 long elapsed_pct, const char *timestr1,
121 const char *timestr2)
90{ 122{
91 if (rb->lang_is_rtl()) 123 if (rb->lang_is_rtl())
92 {
93 rb->snprintf(buf, buffer_len, "%s %s / %s", 124 rb->snprintf(buf, buffer_len, "%s %s / %s",
94 get_percent_str(elapsed_pct), timestr2, timestr1); 125 get_percent_str(elapsed_pct), timestr2, timestr1);
95 }
96 else 126 else
97 {
98 rb->snprintf(buf, buffer_len, "%s / %s %s", 127 rb->snprintf(buf, buffer_len, "%s / %s %s",
99 timestr1, timestr2, get_percent_str(elapsed_pct)); 128 timestr1, timestr2, get_percent_str(elapsed_pct));
100 }
101} 129}
102 130
103/* list callback for playing_time screen */ 131/* list callback for playing_time screen */
104static const char * playing_time_get_or_speak_info(int selected_item, void * data, 132static const char * pt_get_or_speak_info(int selected_item, void * data,
105 char *buf, size_t buffer_len, 133 char *buf, size_t buffer_len,
106 bool say_it) 134 bool say_it)
107{ 135{
108 long elapsed_pct; /* percentage of duration elapsed */ 136 long elapsed_pct; /* percentage of duration elapsed */
109 struct playing_time_info *pti = (struct playing_time_info *)data; 137 struct playing_time_info *pti = (struct playing_time_info *)data;
110 int info_no = selected_item/2; 138 int info_no = selected_item/2;
111 const int menu_name_id = menu_items[info_no]; 139 const int menu_name_id = menu_items[info_no];
112 140
113 if (!(selected_item%2)) 141 /* header */
114 {/* header */ 142 if (!say_it && !(selected_item % 2))
115 return rb->str(menu_name_id); 143 return rb->str(menu_name_id);
116 }
117 144
145 /* data */
118 switch(info_no) { 146 switch(info_no) {
119 case 0: { /* elapsed and total time */ 147 case 0: { /* playlist remaining time */
148 char timestr[25];
149 rb->format_time_auto(timestr, sizeof(timestr),
150 pti->length[ePT_REMAINING], UNIT_SEC, false);
151 rb->snprintf(buf, buffer_len, "%s", timestr);
152
153 if (say_it)
154 rb_talk_ids(false, menu_name_id,
155 TALK_ID(pti->length[ePT_REMAINING], UNIT_TIME));
156 break;
157 }
158 case 1: { /* elapsed and total time */
120 char timestr1[25], timestr2[25]; 159 char timestr1[25], timestr2[25];
121 rb->format_time_auto(timestr1, sizeof(timestr1), 160 rb->format_time_auto(timestr1, sizeof(timestr1),
122 pti->secs[ePT_SECS_BEF], UNIT_SEC, true); 161 pti->length[ePT_ELAPSED], UNIT_SEC, true);
123 162
124 rb->format_time_auto(timestr2, sizeof(timestr2), 163 rb->format_time_auto(timestr2, sizeof(timestr2),
125 pti->secs[ePT_SECS_TTL], UNIT_SEC, true); 164 pti->length[ePT_TOTAL], UNIT_SEC, true);
126 165
127 if (pti->secs[ePT_SECS_TTL] == 0) 166 if (pti->length[ePT_TOTAL] == 0)
128 elapsed_pct = 0; 167 elapsed_pct = 0;
129 else if (pti->secs[ePT_SECS_TTL] <= 0xFFFFFF) 168 else if (pti->length[ePT_TOTAL] <= 0xFFFFFF)
130 { 169 {
131 elapsed_pct = (pti->secs[ePT_SECS_BEF] * 100 170 elapsed_pct = (pti->length[ePT_ELAPSED] * 100
132 / pti->secs[ePT_SECS_TTL]); 171 / pti->length[ePT_TOTAL]);
133 } 172 }
134 else /* sacrifice some precision to avoid overflow */ 173 else /* sacrifice some precision to avoid overflow */
135 { 174 {
136 elapsed_pct = (pti->secs[ePT_SECS_BEF] >> 7) * 100 175 elapsed_pct = (pti->length[ePT_ELAPSED] >> 7) * 100
137 / (pti->secs[ePT_SECS_TTL] >> 7); 176 / (pti->length[ePT_TOTAL] >> 7);
138 } 177 }
139 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2); 178 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2);
140 179
141 if (say_it) 180 if (say_it)
142 rb_talk_ids(false, menu_name_id, 181 rb_talk_ids(false, menu_name_id,
143 TALK_ID(pti->secs[ePT_SECS_BEF], UNIT_TIME), 182 TALK_ID(pti->length[ePT_ELAPSED], UNIT_TIME),
144 VOICE_OF, 183 VOICE_OF,
145 TALK_ID(pti->secs[ePT_SECS_TTL], UNIT_TIME), 184 TALK_ID(pti->length[ePT_TOTAL], UNIT_TIME),
146 VOICE_PAUSE, 185 VOICE_PAUSE,
147 TALK_ID(elapsed_pct, UNIT_PERCENT)); 186 TALK_ID(elapsed_pct, UNIT_PERCENT));
148 break; 187 break;
149 } 188 }
150 case 1: { /* playlist remaining time */ 189 case 2: { /* track remaining time */
151 char timestr[25]; 190 char timestr[25];
152 rb->format_time_auto(timestr, sizeof(timestr), pti->secs[ePT_SECS_AFT], 191 rb->format_time_auto(timestr, sizeof(timestr),
153 UNIT_SEC, false); 192 pti->curr_track_length[ePT_REMAINING], UNIT_SEC, false);
154 rb->snprintf(buf, buffer_len, "%s", timestr); 193 rb->snprintf(buf, buffer_len, "%s", timestr);
155 194
156 if (say_it) 195 if (say_it)
157 rb_talk_ids(false, menu_name_id, 196 rb_talk_ids(false, menu_name_id,
158 TALK_ID(pti->secs[ePT_SECS_AFT], UNIT_TIME)); 197 TALK_ID(pti->curr_track_length[ePT_REMAINING], UNIT_TIME));
159 break; 198 break;
160 } 199 }
161 case 2: { /* track elapsed and duration */ 200 case 3: { /* track elapsed and duration */
162 char timestr1[25], timestr2[25]; 201 char timestr1[25], timestr2[25];
163 202
164 rb->format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs[ePT_SECS_BEF], 203 rb->format_time_auto(timestr1, sizeof(timestr1),
165 UNIT_SEC, true); 204 pti->curr_track_length[ePT_ELAPSED], UNIT_SEC, true);
166 rb->format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs[ePT_SECS_TTL], 205 rb->format_time_auto(timestr2, sizeof(timestr2),
167 UNIT_SEC, true); 206 pti->curr_track_length[ePT_TOTAL], UNIT_SEC, true);
168 207
169 if (pti->trk_secs[ePT_SECS_TTL] == 0) 208 if (pti->curr_track_length[ePT_TOTAL] == 0)
170 elapsed_pct = 0; 209 elapsed_pct = 0;
171 else if (pti->trk_secs[ePT_SECS_TTL] <= 0xFFFFFF) 210 else if (pti->curr_track_length[ePT_TOTAL] <= 0xFFFFFF)
172 { 211 {
173 elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] * 100 212 elapsed_pct = (pti->curr_track_length[ePT_ELAPSED] * 100
174 / pti->trk_secs[ePT_SECS_TTL]); 213 / pti->curr_track_length[ePT_TOTAL]);
175 } 214 }
176 else /* sacrifice some precision to avoid overflow */ 215 else /* sacrifice some precision to avoid overflow */
177 { 216 {
178 elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] >> 7) * 100 217 elapsed_pct = (pti->curr_track_length[ePT_ELAPSED] >> 7) * 100
179 / (pti->trk_secs[ePT_SECS_TTL] >> 7); 218 / (pti->curr_track_length[ePT_TOTAL] >> 7);
180 } 219 }
181 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2); 220 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2);
182 221
183 if (say_it) 222 if (say_it)
184 rb_talk_ids(false, menu_name_id, 223 rb_talk_ids(false, menu_name_id,
185 TALK_ID(pti->trk_secs[ePT_SECS_BEF], UNIT_TIME), 224 TALK_ID(pti->curr_track_length[ePT_ELAPSED], UNIT_TIME),
186 VOICE_OF, 225 VOICE_OF,
187 TALK_ID(pti->trk_secs[ePT_SECS_TTL], UNIT_TIME), 226 TALK_ID(pti->curr_track_length[ePT_TOTAL], UNIT_TIME),
188 VOICE_PAUSE, 227 VOICE_PAUSE,
189 TALK_ID(elapsed_pct, UNIT_PERCENT)); 228 TALK_ID(elapsed_pct, UNIT_PERCENT));
190 break;
191 }
192 case 3: { /* track remaining time */
193 char timestr[25];
194 rb->format_time_auto(timestr, sizeof(timestr), pti->trk_secs[ePT_SECS_AFT],
195 UNIT_SEC, false);
196 rb->snprintf(buf, buffer_len, "%s", timestr);
197
198 if (say_it)
199 rb_talk_ids(false, menu_name_id,
200 TALK_ID(pti->trk_secs[ePT_SECS_AFT], UNIT_TIME));
201 break; 229 break;
202 } 230 }
203 case 4: { /* track index */ 231 case 4: { /* track index */
204 int track_pct = pti->curr_display_index * 100 / pti->nb_tracks; 232 int track_pct = pti->actual_index * 100 / pti->counted;
205 233
206 if (rb->lang_is_rtl()) 234 if (rb->lang_is_rtl())
207 { 235 rb->snprintf(buf, buffer_len, "%s %d / %d", get_percent_str(track_pct),
208 rb->snprintf(buf, buffer_len, "%s %d / %d", 236 pti->counted, pti->actual_index);
209 get_percent_str(track_pct), pti->nb_tracks, pti->curr_display_index);
210 }
211 else 237 else
212 { 238 rb->snprintf(buf, buffer_len, "%d / %d %s", pti->actual_index,
213 rb->snprintf(buf, buffer_len, "%d / %d %s", 239 pti->counted, get_percent_str(track_pct));
214 pti->curr_display_index, pti->nb_tracks, get_percent_str(track_pct));
215 }
216 240
217 if (say_it) 241 if (say_it)
218 rb_talk_ids(false, menu_name_id, 242 rb_talk_ids(false, menu_name_id,
219 TALK_ID(pti->curr_display_index, UNIT_INT), 243 TALK_ID(pti->actual_index, UNIT_INT),
220 VOICE_OF, 244 VOICE_OF,
221 TALK_ID(pti->nb_tracks, UNIT_INT), 245 TALK_ID(pti->counted, UNIT_INT),
222 VOICE_PAUSE, 246 VOICE_PAUSE,
223 TALK_ID(track_pct, UNIT_PERCENT)); 247 TALK_ID(track_pct, UNIT_PERCENT));
224 break; 248 break;
225 } 249 }
226 case 5: { /* storage size */ 250 case 5: { /* storage size */
227 int i; 251 int i;
228 char kbstr[ePT_KBS_COUNT][20]; 252 char kbstr[ePT_COUNT][20];
229 253
230 for (i = 0; i < ePT_KBS_COUNT; i++) { 254 for (i = 0; i < ePT_COUNT; i++) {
231 rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]), 255 rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]),
232 pti->kbs[i], kibyte_units, 3, true); 256 pti->size[i], kibyte_units, 3, true);
233 } 257 }
234 rb->snprintf(buf, buffer_len, "%s (%s / %s)", 258 rb->snprintf(buf, buffer_len, "%s (%s / %s)", kbstr[ePT_TOTAL],
235 kbstr[ePT_KBS_TTL], kbstr[ePT_KBS_BEF],kbstr[ePT_KBS_AFT]); 259 kbstr[ePT_ELAPSED], kbstr[ePT_REMAINING]);
236 260
237 if (say_it) { 261 if (say_it) {
238 int32_t voice_ids[ePT_KBS_COUNT]; 262 int32_t voice_ids[ePT_COUNT];
239 voice_ids[ePT_KBS_TTL] = menu_name_id; 263 voice_ids[ePT_TOTAL] = menu_name_id;
240 voice_ids[ePT_KBS_BEF] = VOICE_PLAYTIME_DONE; 264 voice_ids[ePT_ELAPSED] = VOICE_PLAYTIME_DONE;
241 voice_ids[ePT_KBS_AFT] = LANG_PLAYTIME_REMAINING; 265 voice_ids[ePT_REMAINING] = LANG_REMAINING;
242 266
243 for (i = 0; i < ePT_KBS_COUNT; i++) { 267 for (i = 0; i < ePT_COUNT; i++)
268 {
244 rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]); 269 rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]);
245 rb->output_dyn_value(NULL, 0, pti->kbs[i], kibyte_units, 3, true); 270 rb->output_dyn_value(NULL, 0, pti->size[i], kibyte_units, 3, true);
246 } 271 }
247 } 272 }
248 break; 273 break;
249 } 274 }
250 case 6: { /* Average track file size */ 275 case 6: { /* Average track file size */
251 char str[20]; 276 char str[20];
252 long avg_track_size = pti->kbs[ePT_KBS_TTL] / pti->nb_tracks; 277 long avg_track_size = pti->size[ePT_TOTAL] / pti->counted;
253 rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true); 278 rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
254 rb->snprintf(buf, buffer_len, "%s", str); 279 rb->snprintf(buf, buffer_len, "%s", str);
255 280
@@ -261,163 +286,268 @@ static const char * playing_time_get_or_speak_info(int selected_item, void * dat
261 } 286 }
262 case 7: { /* Average bitrate */ 287 case 7: { /* Average bitrate */
263 /* Convert power of 2 kilobytes to power of 10 kilobits */ 288 /* Convert power of 2 kilobytes to power of 10 kilobits */
264 long avg_bitrate = (pti->kbs[ePT_KBS_TTL] / pti->secs[ePT_SECS_TTL] 289 long avg_bitrate = (pti->size[ePT_TOTAL] / pti->length[ePT_TOTAL]
265 * 1024 * 8 / 1000); 290 * 1024 * 8 / 1000);
266 rb->snprintf(buf, buffer_len, "%ld kbps", avg_bitrate); 291 rb->snprintf(buf, buffer_len, "%ld kbps", avg_bitrate);
267 292
268 if (say_it) 293 if (say_it)
269 rb_talk_ids(false, menu_name_id, 294 rb_talk_ids(false, menu_name_id,
270 TALK_ID(avg_bitrate, UNIT_KBIT)); 295 TALK_ID(avg_bitrate, UNIT_KBIT));
271 break; 296 break;
272 } 297 }
273 } 298 }
274 return buf; 299 return buf;
275} 300}
276 301
277static const char * playing_time_get_info(int selected_item, void * data, 302static const char * pt_get_info(int selected_item, void * data,
278 char *buffer, size_t buffer_len) 303 char *buffer, size_t buffer_len)
279{ 304{
280 return playing_time_get_or_speak_info(selected_item, data, 305 return pt_get_or_speak_info(selected_item, data,
281 buffer, buffer_len, false); 306 buffer, buffer_len, false);
282} 307}
283 308
284static int playing_time_speak_info(int selected_item, void * data) 309static int pt_speak_info(int selected_item, void * data)
285{ 310{
286 static char buffer[MAX_PATH]; 311 static char buffer[MAX_PATH];
287 playing_time_get_or_speak_info(selected_item, data, 312 pt_get_or_speak_info(selected_item, data, buffer, sizeof(buffer), true);
288 buffer, MAX_PATH, true);
289 return 0; 313 return 0;
290} 314}
291 315
292/* playing time screen: shows total and elapsed playlist duration and 316static bool pt_display_stats(struct playing_time_info *pti)
293 other stats */
294static bool playing_time(void)
295{ 317{
296 int error_count = 0; 318 struct gui_synclist pt_lists;
297 unsigned long talked_tick = *rb->current_tick; 319 rb->gui_synclist_init(&pt_lists, &pt_get_info, pti, true, 2, NULL);
298 struct playing_time_info pti; 320 if (rb->global_settings->talk_menu)
299 struct playlist_track_info pltrack; 321 rb->gui_synclist_set_voice_callback(&pt_lists, pt_speak_info);
300 struct mp3entry id3; 322 rb->gui_synclist_set_nb_items(&pt_lists, pti->remaining_only ? 2 : 8*2);
301 int i, index, fd; 323 rb->gui_synclist_set_title(&pt_lists, *pti->single_mode_tag ?
324 rb->str(single_mode_lang()) :
325 rb->str(LANG_PLAYLIST), NOICON);
326 rb->gui_synclist_draw(&pt_lists);
327 rb->gui_synclist_speak_item(&pt_lists);
328 while (true)
329 {
330 int action = rb->get_action(CONTEXT_LIST, HZ/2);
331 if (rb->gui_synclist_do_button(&pt_lists, &action) == 0
332 && action != ACTION_NONE && action != ACTION_UNKNOWN)
333 {
334 bool usb = rb->default_event_handler(action) == SYS_USB_CONNECTED;
302 335
303 pti.nb_tracks = rb->playlist_amount(); 336 if (!usb && IS_SYSEVENT(action))
304 rb->playlist_get_resume_info(&pti.curr_index); 337 continue;
305 struct mp3entry *curr_id3 = rb->audio_current_track(); 338
306 if (pti.curr_index == -1 || !curr_id3) 339 rb->talk_force_shutup();
307 return false; 340 return usb;
308 pti.curr_display_index = rb->playlist_get_display_index();
309
310 pti.secs[ePT_SECS_BEF] = pti.trk_secs[ePT_SECS_BEF] = curr_id3->elapsed / 1000;
311 pti.secs[ePT_SECS_AFT] = pti.trk_secs[ePT_SECS_AFT]
312 = (curr_id3->length -curr_id3->elapsed) / 1000;
313 pti.kbs[ePT_KBS_BEF] = curr_id3->offset / 1024;
314 pti.kbs[ePT_KBS_AFT] = (curr_id3->filesize -curr_id3->offset) / 1024;
315
316 rb->splash(0, ID2P(LANG_WAIT));
317 rb->splash_progress_set_delay(5 * HZ);
318 /* Go through each file in the playlist and get its stats. For
319 huge playlists this can take a while... The reference position
320 is the position at the moment this function was invoked,
321 although playback continues forward. */
322 index = rb->playlist_get_first_index(NULL);
323 for (i = 0; i < pti.nb_tracks; i++, index++) {
324
325 if (index == pti.nb_tracks)
326 index = 0;
327
328 /* Show a splash while we are loading. */
329 rb->splash_progress(i, pti.nb_tracks,
330 "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
331
332 /* Voice equivalent */
333 if (TIME_AFTER(*rb->current_tick, talked_tick + 5 * HZ)) {
334 talked_tick = *rb->current_tick;
335 rb_talk_ids(false, LANG_LOADING_PERCENT,
336 TALK_ID(i * 100 / pti.nb_tracks, UNIT_PERCENT));
337 } 341 }
338 if (rb->action_userabort(TIMEOUT_NOBLOCK)) 342 }
339 goto exit; 343 return false;
344}
340 345
341 if (index == pti.curr_index) 346static const char *pt_options_name(int selected_item, void * data,
342 continue; 347 char *buf, size_t buf_size)
348{
349 (void) data;
350 (void) buf;
351 (void) buf_size;
352 return selected_item == 0 ? rb->str(LANG_ALL) :
353 selected_item == 1 ? rb->str(LANG_REMAINING) :
354 rb->str(single_mode_lang());
355}
343 356
344 if (rb->playlist_get_track_info(NULL, index, &pltrack) >= 0) 357static int pt_options_speak(int selected_item, void * data)
345 { 358{
346 bool ret = false; 359 (void) data;
347 if ((fd = rb->open(pltrack.filename, O_RDONLY)) >= 0) 360 rb->talk_id(selected_item == 0 ? LANG_ALL :
348 { 361 selected_item == 1 ? LANG_REMAINING :
349 ret = rb->get_metadata(&id3, fd, pltrack.filename); 362 single_mode_lang(), false);
350 rb->close(fd); 363 return 0;
351 if (ret) 364}
352 {
353 if (pltrack.display_index < pti.curr_display_index) {
354 pti.secs[ePT_SECS_BEF] += id3.length / 1000;
355 pti.kbs[ePT_KBS_BEF] += id3.filesize / 1024;
356 } else {
357 pti.secs[ePT_SECS_AFT] += id3.length / 1000;
358 pti.kbs[ePT_KBS_AFT] += id3.filesize / 1024;
359 }
360 }
361 }
362 365
363 if (!ret) 366static int pt_options(struct playing_time_info *pti)
367{
368 struct gui_synclist pt_options;
369 rb->gui_synclist_init(&pt_options, &pt_options_name, NULL, true, 1, NULL);
370 if (rb->global_settings->talk_menu)
371 rb->gui_synclist_set_voice_callback(&pt_options, pt_options_speak);
372 rb->gui_synclist_set_nb_items(&pt_options, *pti->single_mode_tag ? 3 : 2);
373 rb->gui_synclist_set_title(&pt_options, rb->str(LANG_PLAYING_TIME), NOICON);
374 rb->gui_synclist_draw(&pt_options);
375 rb->gui_synclist_speak_item(&pt_options);
376
377 while(true)
378 {
379 int button = rb->get_action(CONTEXT_LIST, HZ);
380 if (rb->gui_synclist_do_button(&pt_options, &button))
381 continue;
382 switch(button)
383 {
384 case ACTION_STD_OK:
364 { 385 {
365 error_count++; 386 int sel = rb->gui_synclist_get_sel_pos(&pt_options);
366 continue; 387 if (sel < 2)
388 *pti->single_mode_tag = 0;
389 if (sel == 1)
390 pti->remaining_only = true;
391 return -1;
367 } 392 }
368 } 393 case ACTION_STD_CANCEL:
369 else 394 return 0;
370 { 395 default:
371 error_count++; 396 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
372 break; 397 return 1;
373 } 398 }
374 } 399 }
400}
401
402static void pt_store_converted_totals(struct playing_time_info *pti)
403{
404 /* convert units from ms to s */
405 pti->length[ePT_ELAPSED] /= 1000;
406 pti->length[ePT_REMAINING] /= 1000;
407 pti->curr_track_length[ePT_ELAPSED] /= 1000;
408 pti->curr_track_length[ePT_REMAINING] /= 1000;
409 /* convert units from Bytes to KiB */
410 pti->size[ePT_ELAPSED] >>= 10;
411 pti->size[ePT_REMAINING] >>= 10;
412
413 pti->length[ePT_TOTAL] = pti->length[ePT_ELAPSED] + pti->length[ePT_REMAINING];
414 pti->curr_track_length[ePT_TOTAL] = pti->curr_track_length[ePT_ELAPSED]
415 + pti->curr_track_length[ePT_REMAINING];
416 pti->size[ePT_TOTAL] = pti->size[ePT_ELAPSED] + pti->size[ePT_REMAINING];
417}
375 418
376 if (error_count > 0) 419static int pt_add_track(int i, enum ePT_SUM section, struct playing_time_info *pti)
420{
421 struct mp3entry id3;
422 struct playlist_track_info pl_track;
423 static unsigned long talked_tick;
424 int progress_total = pti->remaining_only ?
425 pti->nb_tracks - pti->curr_display_index :
426 pti->nb_tracks;
427
428 rb->splash_progress(pti->counted, progress_total, "%s (%s)",
429 rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
430
431 if (TIME_AFTER(*rb->current_tick, talked_tick + HZ*5))
377 { 432 {
378 rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR)); 433 talked_tick = *rb->current_tick;
434 rb_talk_ids(false, LANG_LOADING_PERCENT,
435 TALK_ID(pti->counted * 100 / progress_total,
436 UNIT_PERCENT));
379 } 437 }
380 438
381 pti.nb_tracks -= error_count; 439 if (rb->action_userabort(TIMEOUT_NOBLOCK))
382 pti.secs[ePT_SECS_TTL] = pti.secs[ePT_SECS_BEF] + pti.secs[ePT_SECS_AFT]; 440 return -1;
383 pti.trk_secs[ePT_SECS_TTL] = pti.trk_secs[ePT_SECS_BEF] + pti.trk_secs[ePT_SECS_AFT]; 441 else if (rb->playlist_get_track_info(NULL, i, &pl_track) < 0
384 pti.kbs[ePT_KBS_TTL] = pti.kbs[ePT_KBS_BEF] + pti.kbs[ePT_KBS_AFT]; 442 || !rb->get_metadata(&id3, -1, pl_track.filename))
443 {
444 pti->error_count++;
445 return -2;
446 }
447 else if(*pti->single_mode_tag && /* single mode tag doesn't match */
448 rb->strcmp(pti->single_mode_tag, single_mode_id3_tag(&id3) ?: ""))
449 return 1;
385 450
386 struct gui_synclist pt_lists; 451 pti->length[section] += id3.length;
387 int key; 452 pti->size[section] += id3.filesize;
453 pti->counted++;
454 return 0;
455}
388 456
389 rb->gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 2, NULL); 457static bool pt_add_remaining(struct playing_time_info *pti)
390 if (rb->global_settings->talk_menu) 458{
391 rb->gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info); 459 int display_index = pti->curr_display_index + 1;
392 rb->gui_synclist_set_nb_items(&pt_lists, 16); 460 for (int i = pti->curr_track_index + 1; display_index <= pti->nb_tracks; i++, display_index++)
393 rb->gui_synclist_set_title(&pt_lists, rb->str(LANG_PLAYING_TIME), NOICON); 461 {
394 rb->gui_synclist_draw(&pt_lists); 462 if (i == pti->nb_tracks)
395 rb->gui_synclist_speak_item(&pt_lists); 463 i = 0;
396 while (true) {
397 if (rb->list_do_action(CONTEXT_LIST, HZ/2, &pt_lists, &key) == 0
398 && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
399 {
400 bool usb = rb->default_event_handler(key) == SYS_USB_CONNECTED;
401 464
402 if (!usb && IS_SYSEVENT(key)) 465 int ret = pt_add_track(i, ePT_REMAINING, pti);
403 continue; 466 if (ret == 1)
467 break;
468 else if (ret == -1)
469 return false;
470 }
471 return true;
472}
404 473
405 rb->talk_force_shutup(); 474static bool pt_add_elapsed(struct playing_time_info *pti)
406 return usb; 475{
407 } 476 int display_index = pti->curr_display_index - 1;
477 for (int i = pti->curr_track_index - 1; display_index > 0; i--, display_index--)
478 {
479 if (i < 0)
480 i = pti->nb_tracks - 1;
408 481
482 int ret = pt_add_track(i, ePT_ELAPSED, pti);
483 if (ret == 1)
484 break;
485 else if (ret == -1)
486 return false;
487 else if (ret == 0)
488 pti->actual_index++;
409 } 489 }
490 return true;
491}
410 492
411 exit: 493static bool pt_add_curr_track(struct playing_time_info *pti)
412 return false; 494{
495 struct mp3entry *curr_id3 = rb->audio_current_track();
496 rb->playlist_get_resume_info(&pti->curr_track_index);
497
498 if (pti->curr_track_index == -1 || !curr_id3)
499 return false;
500
501 pti->curr_display_index = rb->playlist_get_display_index();
502 pti->length[ePT_ELAPSED] = pti->curr_track_length[ePT_ELAPSED]
503 = curr_id3->elapsed;
504 pti->length[ePT_REMAINING] = pti->curr_track_length[ePT_REMAINING]
505 = curr_id3->length - curr_id3->elapsed;
506 pti->size[ePT_ELAPSED] = curr_id3->offset;
507 pti->size[ePT_REMAINING] = curr_id3->filesize - curr_id3->offset;
508 pti->actual_index = pti->counted = 1;
509 rb->strlcpy(pti->single_mode_tag, single_mode_id3_tag(curr_id3) ?: "",
510 sizeof(pti->single_mode_tag));
511 return true;
512}
513
514/* playing time screen: shows total and elapsed playlist duration and
515 other stats */
516static bool playing_time(void)
517{
518 struct playing_time_info pti;
519 rb->memset(&pti, 0, sizeof(struct playing_time_info));
520
521 if (!pt_add_curr_track(&pti))
522 return false;
523
524 int opt = pt_options(&pti);
525 if (opt > -1)
526 return opt;
527
528#ifdef HAVE_ADJUSTABLE_CPU_FREQ
529 rb->cpu_boost(true);
530#endif
531 rb->splash_progress_set_delay(HZ/2);
532 pti.nb_tracks = rb->playlist_amount();
533 int success = (pti.remaining_only || pt_add_elapsed(&pti)) && pt_add_remaining(&pti);
534#ifdef HAVE_ADJUSTABLE_CPU_FREQ
535 rb->cpu_boost(false);
536#endif
537 if (!success)
538 return false;
539 if (pti.error_count > 0)
540 rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
541
542 pt_store_converted_totals(&pti);
543 return pt_display_stats(&pti);
413} 544}
414 545
415/* this is the plugin entry point */ 546/* this is the plugin entry point */
416enum plugin_status plugin_start(const void* parameter) 547enum plugin_status plugin_start(const void* parameter)
417{ 548{
418 enum plugin_status status = PLUGIN_OK;
419
420 (void)parameter; 549 (void)parameter;
550 enum plugin_status status = PLUGIN_OK;
421 551
422 if (!rb->audio_status()) 552 if (!rb->audio_status())
423 { 553 {
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index 32bc8b9150..dcd3d89edf 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -25,14 +25,6 @@
25 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) 25 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
26#endif 26#endif
27 27
28struct dir_stats {
29 char dirname[MAX_PATH];
30 int len;
31 unsigned int dir_count;
32 unsigned int file_count;
33 unsigned long long byte_count;
34};
35
36enum props_types { 28enum props_types {
37 PROPS_FILE = 0, 29 PROPS_FILE = 0,
38 PROPS_PLAYLIST, 30 PROPS_PLAYLIST,
@@ -41,26 +33,20 @@ enum props_types {
41 PROPS_DIR 33 PROPS_DIR
42}; 34};
43 35
44static int props_type = PROPS_FILE; 36static struct gui_synclist properties_lists;
45
46static struct mp3entry id3; 37static struct mp3entry id3;
47static int mul_id3_count; 38static struct tm tm;
48static int skipped_count; 39static unsigned long display_size;
40static int32_t lang_size_unit;
41static int props_type, mul_id3_count, skipped_count;
49 42
50static char str_filename[MAX_PATH]; 43static char str_filename[MAX_PATH], str_dirname[MAX_PATH],
51static char str_dirname[MAX_PATH]; 44 str_size[64], str_dircount[64], str_filecount[64],
52static char str_size[64]; 45 str_audio_filecount[64], str_date[64], str_time[64];
53static char str_dircount[64];
54static char str_filecount[64];
55static char str_date[64];
56static char str_time[64];
57 46
58static unsigned long nsize;
59static int32_t size_unit;
60static struct tm tm;
61 47
62#define NUM_FILE_PROPERTIES 5 48#define NUM_FILE_PROPERTIES 5
63#define NUM_PLAYLIST_PROPERTIES 1 + NUM_FILE_PROPERTIES 49#define NUM_PLAYLIST_PROPERTIES (1 + NUM_FILE_PROPERTIES)
64static const unsigned char* const props_file[] = 50static const unsigned char* const props_file[] =
65{ 51{
66 ID2P(LANG_PROPERTIES_PATH), str_dirname, 52 ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -68,170 +54,44 @@ static const unsigned char* const props_file[] =
68 ID2P(LANG_PROPERTIES_SIZE), str_size, 54 ID2P(LANG_PROPERTIES_SIZE), str_size,
69 ID2P(LANG_PROPERTIES_DATE), str_date, 55 ID2P(LANG_PROPERTIES_DATE), str_date,
70 ID2P(LANG_PROPERTIES_TIME), str_time, 56 ID2P(LANG_PROPERTIES_TIME), str_time,
57
71 ID2P(LANG_MENU_SHOW_ID3_INFO), "...", 58 ID2P(LANG_MENU_SHOW_ID3_INFO), "...",
72}; 59};
73 60
74#define NUM_DIR_PROPERTIES 4 61#define NUM_DIR_PROPERTIES 4
62#define NUM_AUDIODIR_PROPERTIES (1 + NUM_DIR_PROPERTIES)
75static const unsigned char* const props_dir[] = 63static const unsigned char* const props_dir[] =
76{ 64{
77 ID2P(LANG_PROPERTIES_PATH), str_dirname, 65 ID2P(LANG_PROPERTIES_PATH), str_dirname,
78 ID2P(LANG_PROPERTIES_SUBDIRS), str_dircount, 66 ID2P(LANG_PROPERTIES_SUBDIRS), str_dircount,
79 ID2P(LANG_PROPERTIES_FILES), str_filecount, 67 ID2P(LANG_PROPERTIES_FILES), str_filecount,
80 ID2P(LANG_PROPERTIES_SIZE), str_size, 68 ID2P(LANG_PROPERTIES_SIZE), str_size,
81};
82 69
83static const int32_t units[] = 70 ID2P(LANG_MENU_SHOW_ID3_INFO), str_audio_filecount,
84{
85 LANG_BYTE,
86 LANG_KIBIBYTE,
87 LANG_MEBIBYTE,
88 LANG_GIBIBYTE
89}; 71};
90 72
91static unsigned human_size_log(unsigned long long size)
92{
93 const size_t n = sizeof(units)/sizeof(units[0]);
94
95 unsigned i;
96 /* margin set at 10K boundary: 10239 B +1 => 10 KB */
97 for(i=0; i < n-1 && size >= 10*1024; i++)
98 size >>= 10; /* div by 1024 */
99
100 return i;
101}
102
103static bool file_properties(const char* selected_file)
104{
105 bool found = false;
106 DIR* dir;
107 struct dirent* entry;
108
109 dir = rb->opendir(str_dirname);
110 if (dir)
111 {
112 while(0 != (entry = rb->readdir(dir)))
113 {
114 struct dirinfo info = rb->dir_get_info(dir, entry);
115 if(!rb->strcmp(entry->d_name, str_filename))
116 {
117 unsigned log;
118 log = human_size_log((unsigned long)info.size);
119 nsize = ((unsigned long)info.size) >> (log*10);
120 size_unit = units[log];
121 rb->snprintf(str_size, sizeof str_size, "%lu %s",
122 nsize, rb->str(size_unit));
123 rb->gmtime_r(&info.mtime, &tm);
124 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
125 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
126 rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
127 tm.tm_hour, tm.tm_min, tm.tm_sec);
128
129 if (!rb->mp3info(&id3, selected_file))
130 props_type = PROPS_ID3;
131 found = true;
132 break;
133 }
134 }
135 rb->closedir(dir);
136 }
137 return found;
138}
139
140static bool _dir_properties(struct dir_stats *stats)
141{
142 /* recursively scan directories in search of files
143 and informs the user of the progress */
144 bool result;
145 static long lasttick=0;
146 int dirlen;
147 DIR* dir;
148 struct dirent* entry;
149
150 result = true;
151 dirlen = rb->strlen(stats->dirname);
152 dir = rb->opendir(stats->dirname);
153 if (!dir)
154 {
155 rb->splashf(HZ*2, "%s", stats->dirname);
156 return false; /* open error */
157 }
158
159 /* walk through the directory content */
160 while(result && (0 != (entry = rb->readdir(dir))))
161 {
162 struct dirinfo info = rb->dir_get_info(dir, entry);
163 /* append name to current directory */
164 rb->snprintf(stats->dirname+dirlen, stats->len-dirlen, "/%s",
165 entry->d_name);
166
167 if (info.attribute & ATTR_DIRECTORY)
168 {
169 if (!rb->strcmp((char *)entry->d_name, ".") ||
170 !rb->strcmp((char *)entry->d_name, ".."))
171 continue; /* skip these */
172
173 stats->dir_count++; /* new directory */
174 if (*rb->current_tick - lasttick > (HZ/8))
175 {
176 unsigned log;
177 lasttick = *rb->current_tick;
178 rb->lcd_clear_display();
179 rb->lcd_puts(0,0,"SCANNING...");
180 rb->lcd_puts(0,1,stats->dirname);
181 rb->lcd_putsf(0,2,"Directories: %d", stats->dir_count);
182 rb->lcd_putsf(0,3,"Files: %d", stats->file_count);
183 log = human_size_log(stats->byte_count);
184 rb->lcd_putsf(0,4,"Size: %lu %s",
185 (unsigned long)(stats->byte_count >> (10*log)),
186 rb->str(units[log]));
187 rb->lcd_update();
188 }
189
190 /* recursion */
191 result = _dir_properties(stats);
192 }
193 else
194 {
195 stats->file_count++; /* new file */
196 stats->byte_count += info.size;
197 }
198 if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
199 result = false;
200 rb->yield();
201 }
202 rb->closedir(dir);
203 return result;
204}
205
206static bool dir_properties(const char* selected_file, struct dir_stats *stats) 73static bool dir_properties(const char* selected_file, struct dir_stats *stats)
207{ 74{
208 unsigned log; 75 rb->strlcpy(stats->dirname, selected_file, sizeof(stats->dirname));
209
210 rb->strlcpy(stats->dirname, selected_file, MAX_PATH);
211 76
212#ifdef HAVE_ADJUSTABLE_CPU_FREQ 77#ifdef HAVE_ADJUSTABLE_CPU_FREQ
213 rb->cpu_boost(true); 78 rb->cpu_boost(true);
214#endif 79#endif
215 80 bool success = collect_dir_stats(stats, NULL);
216 if (!_dir_properties(stats))
217 {
218#ifdef HAVE_ADJUSTABLE_CPU_FREQ 81#ifdef HAVE_ADJUSTABLE_CPU_FREQ
219 rb->cpu_boost(false); 82 rb->cpu_boost(false);
220#endif 83#endif
84 if (!success)
221 return false; 85 return false;
222 }
223 86
224#ifdef HAVE_ADJUSTABLE_CPU_FREQ 87 rb->strlcpy(str_dirname, selected_file, sizeof(str_dirname));
225 rb->cpu_boost(false);
226#endif
227
228 rb->strlcpy(str_dirname, selected_file, MAX_PATH);
229 rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count); 88 rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count);
230 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count); 89 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
231 log = human_size_log(stats->byte_count); 90 rb->snprintf(str_audio_filecount, sizeof str_filecount, "%d",
232 nsize = (long) (stats->byte_count >> (log*10)); 91 stats->audio_file_count);
233 size_unit = units[log]; 92 display_size = human_size(stats->byte_count, &lang_size_unit);
234 rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit)); 93 rb->snprintf(str_size, sizeof str_size, "%lu %s", display_size,
94 rb->str(lang_size_unit));
235 return true; 95 return true;
236} 96}
237 97
@@ -246,12 +106,11 @@ static const char * get_props(int selected_item, void* data,
246{ 106{
247 (void)data; 107 (void)data;
248 if (PROPS_DIR == props_type) 108 if (PROPS_DIR == props_type)
249 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ? "ERROR" : 109 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ?
250 (char *) p2str(props_dir[selected_item]), buffer_len); 110 "ERROR" : (char *) p2str(props_dir[selected_item]), buffer_len);
251 else 111 else
252 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ? "ERROR" : 112 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ?
253 (char *) p2str(props_file[selected_item]), buffer_len); 113 "ERROR" : (char *) p2str(props_file[selected_item]), buffer_len);
254
255 return buffer; 114 return buffer;
256} 115}
257 116
@@ -264,32 +123,14 @@ static int speak_property_selection(int selected_item, void *data)
264 switch (id) 123 switch (id)
265 { 124 {
266 case LANG_PROPERTIES_PATH: 125 case LANG_PROPERTIES_PATH:
267 if (str_dirname[0] == '/') 126 rb->talk_fullpath(str_dirname, true);
268 {
269 char *start = str_dirname;
270 char *ptr;
271 while (0 != (ptr = rb->strchr(start, '/')))
272 {
273 *ptr = '\0';
274 rb->talk_dir_or_spell(str_dirname, NULL, true);
275 *ptr = '/';
276 rb->talk_id(VOICE_CHAR_SLASH, true);
277 start = ptr + 1;
278 }
279 if (*start)
280 rb->talk_dir_or_spell(str_dirname, NULL, true);
281 }
282 else
283 {
284 rb->talk_spell(str_dirname, true);
285 }
286 break; 127 break;
287 case LANG_PROPERTIES_FILENAME: 128 case LANG_PROPERTIES_FILENAME:
288 rb->talk_file_or_spell(str_dirname, str_filename, NULL, true); 129 rb->talk_file_or_spell(str_dirname, str_filename, NULL, true);
289 break; 130 break;
290 case LANG_PROPERTIES_SIZE: 131 case LANG_PROPERTIES_SIZE:
291 rb->talk_number(nsize, true); 132 rb->talk_number(display_size, true);
292 rb->talk_id(size_unit, true); 133 rb->talk_id(lang_size_unit, true);
293 break; 134 break;
294 case LANG_PROPERTIES_DATE: 135 case LANG_PROPERTIES_DATE:
295 rb->talk_date(&tm, true); 136 rb->talk_date(&tm, true);
@@ -310,40 +151,46 @@ static int speak_property_selection(int selected_item, void *data)
310 return 0; 151 return 0;
311} 152}
312 153
313static int browse_file_or_dir(struct dir_stats *stats) 154static void setup_properties_list(struct dir_stats *stats)
314{ 155{
315 struct gui_synclist properties_lists; 156 int nb_props;
316 int button; 157 if (props_type == PROPS_FILE)
158 nb_props = NUM_FILE_PROPERTIES;
159 else if (props_type == PROPS_PLAYLIST)
160 nb_props = NUM_PLAYLIST_PROPERTIES;
161 else
162 nb_props = NUM_DIR_PROPERTIES;
317 163
318 rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL); 164 rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL);
319 rb->gui_synclist_set_title(&properties_lists, 165 rb->gui_synclist_set_title(&properties_lists,
320 rb->str(props_type == PROPS_DIR ? 166 rb->str(props_type == PROPS_DIR ?
321 LANG_PROPERTIES_DIRECTORY_PROPERTIES : 167 LANG_PROPERTIES_DIRECTORY_PROPERTIES :
322 LANG_PROPERTIES_FILE_PROPERTIES), 168 LANG_PROPERTIES_FILE_PROPERTIES),
323 NOICON); 169 NOICON);
324 rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
325 if (rb->global_settings->talk_menu) 170 if (rb->global_settings->talk_menu)
326 rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection); 171 rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
327 rb->gui_synclist_set_nb_items(&properties_lists, 172 rb->gui_synclist_set_nb_items(&properties_lists, nb_props*2);
328 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES : 173}
329 props_type == PROPS_PLAYLIST ? 174
330 NUM_PLAYLIST_PROPERTIES : NUM_DIR_PROPERTIES)); 175static int browse_file_or_dir(struct dir_stats *stats)
331 rb->gui_synclist_select_item(&properties_lists, 0); 176{
177 if (props_type == PROPS_DIR && stats->audio_file_count)
178 rb->gui_synclist_set_nb_items(&properties_lists, NUM_AUDIODIR_PROPERTIES*2);
332 rb->gui_synclist_draw(&properties_lists); 179 rb->gui_synclist_draw(&properties_lists);
333 rb->gui_synclist_speak_item(&properties_lists); 180 rb->gui_synclist_speak_item(&properties_lists);
334
335 while(true) 181 while(true)
336 { 182 {
337 button = rb->get_action(CONTEXT_LIST, HZ); 183 int button = rb->get_action(CONTEXT_LIST, HZ);
338 /* HZ so the status bar redraws corectly */ 184 /* HZ so the status bar redraws corectly */
339 if (rb->gui_synclist_do_button(&properties_lists,&button)) 185 if (rb->gui_synclist_do_button(&properties_lists, &button))
340 continue; 186 continue;
341 switch(button) 187 switch(button)
342 { 188 {
343 case ACTION_STD_OK: 189 case ACTION_STD_OK:
344 if (props_type == PROPS_PLAYLIST && 190 if ((props_type == PROPS_PLAYLIST || props_type == PROPS_DIR) &&
345 rb->gui_synclist_get_sel_pos(&properties_lists) 191 rb->gui_synclist_get_sel_pos(&properties_lists)
346 == ARRAY_SIZE(props_file) - 2) 192 == (props_type == PROPS_DIR ?
193 ARRAY_SIZE(props_dir) : ARRAY_SIZE(props_file)) - 2)
347 return -1; 194 return -1;
348 break; 195 break;
349 case ACTION_STD_CANCEL: 196 case ACTION_STD_CANCEL:
@@ -356,60 +203,101 @@ static int browse_file_or_dir(struct dir_stats *stats)
356 } 203 }
357} 204}
358 205
359static bool determine_file_or_dir(void) 206static bool determine_props_type(const char *file)
360{ 207{
361 DIR* dir; 208 if (file[0] == PATH_SEPCH)
362 struct dirent* entry;
363
364 dir = rb->opendir(str_dirname);
365 if (dir)
366 { 209 {
210 const char* basename = rb->strrchr(file, PATH_SEPCH) + 1;
211 const int dir_len = (basename - file);
212 if ((int) sizeof(str_dirname) <= dir_len)
213 return false;
214 rb->strlcpy(str_dirname, file, dir_len + 1);
215 rb->strlcpy(str_filename, basename, sizeof str_filename);
216 struct dirent* entry;
217 DIR* dir = rb->opendir(str_dirname);
218 if (!dir)
219 return false;
367 while(0 != (entry = rb->readdir(dir))) 220 while(0 != (entry = rb->readdir(dir)))
368 { 221 {
369 if(!rb->strcmp(entry->d_name, str_filename)) 222 if(rb->strcmp(entry->d_name, str_filename))
223 continue;
224
225 struct dirinfo info = rb->dir_get_info(dir, entry);
226 if (info.attribute & ATTR_DIRECTORY)
227 props_type = PROPS_DIR;
228 else
370 { 229 {
371 struct dirinfo info = rb->dir_get_info(dir, entry); 230 display_size = human_size(info.size, &lang_size_unit);
372 props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE; 231 rb->snprintf(str_size, sizeof str_size, "%lu %s",
373 rb->closedir(dir); 232 display_size, rb->str(lang_size_unit));
374 return true; 233 rb->gmtime_r(&info.mtime, &tm);
234 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
235 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
236 rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
237 tm.tm_hour, tm.tm_min, tm.tm_sec);
238
239 if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_M3U)
240 props_type = PROPS_PLAYLIST;
241 else
242 props_type = rb->get_metadata(&id3, -1, file) ?
243 PROPS_ID3 : PROPS_FILE;
375 } 244 }
245 rb->closedir(dir);
246 return true;
376 } 247 }
377 rb->closedir(dir); 248 rb->closedir(dir);
378 } 249 }
250 else if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER)))
251 {
252 props_type = PROPS_MUL_ID3;
253 return true;
254 }
379 return false; 255 return false;
380} 256}
381 257
382bool mul_id3_add(const char *file_name) 258bool mul_id3_add(const char *file_name)
383{ 259{
384 if (!file_name || rb->mp3info(&id3, file_name)) 260 if (!file_name || !rb->get_metadata(&id3, -1, file_name))
385 skipped_count++; 261 skipped_count++;
386 else 262 else
387 { 263 {
388 collect_id3(&id3, mul_id3_count == 0); 264 collect_id3(&id3, mul_id3_count == 0);
389 mul_id3_count++; 265 mul_id3_count++;
390 } 266 }
391
392 return true; 267 return true;
393} 268}
394 269
395static bool has_pl_extension(const char* filename) 270/* Assemble track info from a dir, a playlist, or a database table */
396{ 271static bool assemble_track_info(const char *filename, struct dir_stats *stats)
397 char *dot = rb->strrchr(filename, '.');
398 return (dot && (!rb->strcasecmp(dot, ".m3u") || !rb->strcasecmp(dot, ".m3u8")));
399}
400
401/* Assemble track info from a database table or the contents of a playlist file */
402static bool assemble_track_info(const char *filename)
403{ 272{
404 props_type = PROPS_MUL_ID3; 273 if (props_type == PROPS_DIR)
405 mul_id3_count = skipped_count = 0; 274 {
406 275#ifdef HAVE_ADJUSTABLE_CPU_FREQ
407 if ( (filename && !rb->playlist_entries_iterate(filename, NULL, &mul_id3_add)) 276 rb->cpu_boost(true);
277#endif
278 rb->strlcpy(stats->dirname, filename, sizeof(stats->dirname));
279 rb->splash_progress_set_delay(HZ/2); /* hide progress bar for 0.5s */
280 bool success = collect_dir_stats(stats, &mul_id3_add);
281#ifdef HAVE_ADJUSTABLE_CPU_FREQ
282 rb->cpu_boost(false);
283#endif
284 if (!success)
285 return false;
286 }
287 else if(props_type == PROPS_PLAYLIST &&
288 !rb->playlist_entries_iterate(filename, NULL, &mul_id3_add))
289 return false;
408#ifdef HAVE_TAGCACHE 290#ifdef HAVE_TAGCACHE
409 || (!filename && !rb->tagtree_subentries_do_action(&mul_id3_add)) 291 else if (props_type == PROPS_MUL_ID3 &&
292 !rb->tagtree_subentries_do_action(&mul_id3_add))
293 return false;
410#endif 294#endif
411 || mul_id3_count == 0) 295
296 if (mul_id3_count == 0)
297 {
298 rb->splashf(HZ*2, "None found");
412 return false; 299 return false;
300 }
413 else if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */ 301 else if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */
414 finalize_id3(&id3); 302 finalize_id3(&id3);
415 303
@@ -421,78 +309,43 @@ static bool assemble_track_info(const char *filename)
421 309
422enum plugin_status plugin_start(const void* parameter) 310enum plugin_status plugin_start(const void* parameter)
423{ 311{
424 int ret; 312 static struct dir_stats stats;
425 static struct dir_stats stats =
426 {
427 .len = MAX_PATH,
428 .dir_count = 0,
429 .file_count = 0,
430 .byte_count = 0,
431 };
432
433 const char *file = parameter; 313 const char *file = parameter;
434 if(!parameter)
435 return PLUGIN_ERROR;
436
437#ifdef HAVE_TOUCHSCREEN 314#ifdef HAVE_TOUCHSCREEN
438 rb->touchscreen_set_mode(rb->global_settings->touch_mode); 315 rb->touchscreen_set_mode(rb->global_settings->touch_mode);
439#endif 316#endif
440 317 int ret = file && determine_props_type(file);
441 if (file[0] == '/') /* single file selected */ 318 if (!ret)
442 { 319 {
443 const char* file_name = rb->strrchr(file, '/') + 1; 320 rb->splashf(0, "Could not find: %s", file ?: "(NULL)");
444 int dirlen = (file_name - file); 321 rb->action_userabort(TIMEOUT_BLOCK);
445 322 return PLUGIN_OK;
446 rb->strlcpy(str_dirname, file, dirlen + 1); 323 }
447 rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
448
449 if(!determine_file_or_dir())
450 {
451 /* weird: we couldn't find the entry. This Should Never Happen (TM) */
452 rb->splashf(0, "File/Dir not found: %s", file);
453 rb->action_userabort(TIMEOUT_BLOCK);
454 return PLUGIN_OK;
455 }
456
457 if (props_type == PROPS_FILE && has_pl_extension(file))
458 props_type = PROPS_PLAYLIST;
459 324
460 /* get the info depending on its_a_dir */ 325 if (props_type == PROPS_MUL_ID3)
461 if(!(props_type == PROPS_DIR ? 326 ret = assemble_track_info(NULL, NULL);
462 dir_properties(file, &stats) : file_properties(file))) 327 else if (props_type != PROPS_ID3)
328 {
329 setup_properties_list(&stats); /* Show title during dir scan */
330 if (props_type == PROPS_DIR)
331 ret = dir_properties(file, &stats);
332 }
333 if (!ret)
334 {
335 if (!stats.canceled)
463 { 336 {
464 /* something went wrong (to do: tell user what it was (nesting,...) */ 337 rb->splash(0, ID2P(LANG_PROPERTIES_FAIL)); /* TODO: describe error */
465 rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
466 rb->action_userabort(TIMEOUT_BLOCK); 338 rb->action_userabort(TIMEOUT_BLOCK);
467 return PLUGIN_OK;
468 } 339 }
469 } 340 }
470 /* database table selected */ 341 else if (props_type == PROPS_ID3)
471 else if (rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))
472 || !assemble_track_info(NULL))
473 return PLUGIN_ERROR;
474
475 FOR_NB_SCREENS(i)
476 rb->viewportmanager_theme_enable(i, true, NULL);
477
478 if (props_type == PROPS_ID3)
479 ret = rb->browse_id3(&id3, 0, 0, &tm, 1); /* Track Info for single file */ 342 ret = rb->browse_id3(&id3, 0, 0, &tm, 1); /* Track Info for single file */
480 else if (props_type == PROPS_MUL_ID3) 343 else if (props_type == PROPS_MUL_ID3)
481 ret = rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count); /* database tracks */ 344 ret = rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count); /* database tracks */
482 else if ((ret = browse_file_or_dir(&stats)) < 0) 345 else if ((ret = browse_file_or_dir(&stats)) < 0)
483 ret = assemble_track_info(file) ? /* playlist tracks */ 346 ret = assemble_track_info(file, &stats) ? /* playlist or folder tracks */
484 rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) : -1; 347 rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) :
348 (stats.canceled ? 0 : -1);
485 349
486 FOR_NB_SCREENS(i) 350 return ret == -1 ? PLUGIN_ERROR : ret == 1 ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
487 rb->viewportmanager_theme_undo(i, false);
488
489 switch (ret)
490 {
491 case 1:
492 return PLUGIN_USB_CONNECTED;
493 case -1:
494 return PLUGIN_ERROR;
495 default:
496 return PLUGIN_OK;
497 }
498} 351}
diff --git a/apps/plugins/puzzles/README.rockbox b/apps/plugins/puzzles/README.rockbox
index e497c9794f..c7ecc540a1 100644
--- a/apps/plugins/puzzles/README.rockbox
+++ b/apps/plugins/puzzles/README.rockbox
@@ -4,11 +4,6 @@ Introduction
4This is the readme for the Rockbox port of Simon Tatham's Portable 4This is the readme for the Rockbox port of Simon Tatham's Portable
5Puzzle Collection. 5Puzzle Collection.
6 6
7The upstream version used is subject to change, as it should be
8relatively trivial to update it to a newer version. Simply copying the
9upstream repo's contents into src/ and running genhelp.sh ought to do
10it (watch out for API changes, though!).
11
12Source structure 7Source structure
13================ 8================
14 9
@@ -31,6 +26,46 @@ distribution. The compression is LZ4, implemented in lz4tiny.c (for
31decompression on target), and compress.c (for generation). genhelp.sh 26decompression on target), and compress.c (for generation). genhelp.sh
32should be run whenever the documentation is changed. 27should be run whenever the documentation is changed.
33 28
29Upstreams
30=========
31
32As of 2024, Simon's tree is located at:
33
34https://git.tartarus.org/?p=simon/puzzles.git
35
36For a long time (i.e. 2017-2024), our version of the puzzles tree
37contained several modifications by myself (notably cursor interfaces
38to Untangle and Palisade). These divergent changes complicated
39maintenance of this port, as merge conflicts often arose when upstream
40changes to these games conflicted with our changes. To remedy this, I
41sent most of these patches back upstream in summer 2024, and since
42then, Simon has merged them into his repo. We are now able to run with
43a fully unmodified puzzles source tree.
44
45Maintenance
46===========
47
48Simon's upstream tree sees continued development. The port is
49structured so that integrating new upstream versions is
50straightforward: all the upstream sources live in the src/
51subdirectory; all of the Rockbox frontend lives in the root
52apps/plugins/puzzles/ directory.
53
54The `resync.sh' shell script automates the resyncing process. It
55copies the upstream sources (point it to a local copy of the
56rockbox-devel branch above) into src/ and performs auto-generation of
57the help content. Note that a modified version of "halibut" (Simon's
58homegrown documentation processor) must be compiled from this source:
59
60https://github.com/built1n/halibut
61
62The LZ4 library and GCC are necessary as well.
63
64Wishlist
65========
66
67- Nothing!
68
34Kudos to Simon (duh), and Frank, for telling me about it. 69Kudos to Simon (duh), and Frank, for telling me about it.
35 70
36Franklin Wei (__builtin) 71Franklin Wei (__builtin)
@@ -64,3 +99,12 @@ May 2019: Resync to e2135d5.
64 99
65June 2020: Resync to 9aa7b7c. Fixed really embarrassing bug in loading 100June 2020: Resync to 9aa7b7c. Fixed really embarrassing bug in loading
66saved games. 101saved games.
102
103July 2024: Resync to 1c62dac (branched from Simon's fd304c5).
104Implement user preferences menu. Introduced "Mosaic".
105
106August 2024: Resync to ee5e327 (branched from Simon's
1071c1899e). Changes default Map stipple size to "Small".
108
109August 2024: Resync to Simon's 262f709 (unmodified). Uses new scanline
110polygon filling algorithm. Updates drawing API.
diff --git a/apps/plugins/puzzles/SOURCES b/apps/plugins/puzzles/SOURCES
index 5301ceef3d..dfbd184ba2 100644
--- a/apps/plugins/puzzles/SOURCES
+++ b/apps/plugins/puzzles/SOURCES
@@ -1,29 +1,31 @@
1/* Auto-generated by resync.sh */
1rockbox.c 2rockbox.c
2rbwrappers.c 3rbwrappers.c
3rbmalloc.c 4rbmalloc.c
4lz4tiny.c 5lz4tiny.c
6dummy/nullhelp.c
5 7
8/* puzzles core sources */
6src/combi.c 9src/combi.c
7src/divvy.c 10src/divvy.c
11src/draw-poly.c
8src/drawing.c 12src/drawing.c
9src/dsf.c 13src/dsf.c
10src/findloop.c 14src/findloop.c
11src/grid.c 15src/grid.c
16src/hat.c
12src/latin.c 17src/latin.c
13src/laydomino.c 18src/laydomino.c
14src/loopgen.c 19src/loopgen.c
15/*src/malloc.c*/ /* we have our own */
16src/matching.c 20src/matching.c
17src/midend.c 21src/midend.c
18src/misc.c 22src/misc.c
23src/penrose-legacy.c
19src/penrose.c 24src/penrose.c
20src/printing.c
21src/random.c 25src/random.c
22src/sort.c 26src/sort.c
27src/spectre.c
23src/tdq.c 28src/tdq.c
24src/tree234.c 29src/tree234.c
25src/version.c 30src/version.c
26 31src/printing.c
27#ifdef COMBINED
28src/list.c
29#endif
diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games
index 2b80ea3300..70f34f3334 100644
--- a/apps/plugins/puzzles/SOURCES.games
+++ b/apps/plugins/puzzles/SOURCES.games
@@ -1,5 +1,3 @@
1/* every game works! :) */
2
3src/blackbox.c 1src/blackbox.c
4src/bridges.c 2src/bridges.c
5src/cube.c 3src/cube.c
@@ -16,6 +14,7 @@ src/lightup.c
16src/magnets.c 14src/magnets.c
17src/map.c 15src/map.c
18src/mines.c 16src/mines.c
17src/mosaic.c
19src/net.c 18src/net.c
20src/netslide.c 19src/netslide.c
21src/palisade.c 20src/palisade.c
@@ -36,12 +35,8 @@ src/undead.c
36src/unequal.c 35src/unequal.c
37src/unruly.c 36src/unruly.c
38src/untangle.c 37src/untangle.c
39 38src/unfinished/slide.c
40/* disabled for now (fix puzzles.make and CATEGORIES to accomodate these) */ 39src/unfinished/sokoban.c
41/*src/unfinished/group.c*/
42/*src/unfinished/separate.c*/
43/*src/unfinished/slide.c*/
44/*src/unfinished/sokoban.c*/
45 40
46/* no c200v2 */ 41/* no c200v2 */
47#if PLUGIN_BUFFER_SIZE > 0x14000 42#if PLUGIN_BUFFER_SIZE > 0x14000
diff --git a/apps/plugins/puzzles/SOURCES.rockbox b/apps/plugins/puzzles/SOURCES.rockbox
new file mode 100644
index 0000000000..61ce4275ff
--- /dev/null
+++ b/apps/plugins/puzzles/SOURCES.rockbox
@@ -0,0 +1,5 @@
1rockbox.c
2rbwrappers.c
3rbmalloc.c
4lz4tiny.c
5dummy/nullhelp.c
diff --git a/apps/plugins/puzzles/compress.c b/apps/plugins/puzzles/compress.c
index 78b2aa4d4a..d127a9af17 100644
--- a/apps/plugins/puzzles/compress.c
+++ b/apps/plugins/puzzles/compress.c
@@ -72,7 +72,7 @@ int main()
72 int inlen = strlen(help_text) + 1, outlen; 72 int inlen = strlen(help_text) + 1, outlen;
73 int minsz, minlev; 73 int minsz, minlev;
74 74
75 printf("/* auto-generated on " __DATE__ " by genhelp.sh */\n"); 75 printf("/* auto-generated by genhelp.sh */\n");
76 printf("/* help text is compressed using LZ4; see compress.c for details */\n"); 76 printf("/* help text is compressed using LZ4; see compress.c for details */\n");
77 printf("/* DO NOT EDIT! */\n\n"); 77 printf("/* DO NOT EDIT! */\n\n");
78 78
@@ -158,6 +158,7 @@ int main()
158 printf("};\n\n"); 158 printf("};\n\n");
159 printf("const unsigned short help_text_len = %d;\n", help_text_len); 159 printf("const unsigned short help_text_len = %d;\n", help_text_len);
160 printf("const unsigned short help_text_words = %d;\n", word_idx); 160 printf("const unsigned short help_text_words = %d;\n", word_idx);
161 printf("const bool help_valid = true;\n");
161 162
162 return 0; 163 return 0;
163} 164}
diff --git a/apps/plugins/puzzles/dummy/nullhelp.c b/apps/plugins/puzzles/dummy/nullhelp.c
new file mode 100644
index 0000000000..79c36c902b
--- /dev/null
+++ b/apps/plugins/puzzles/dummy/nullhelp.c
@@ -0,0 +1,8 @@
1#include "help.h"
2
3const char help_text[] __attribute__((weak)) = "";
4const char quick_help_text[] __attribute__((weak)) = "";
5const unsigned short help_text_len __attribute__((weak)) = 0, quick_help_text_len __attribute__((weak)) = 0, help_text_words __attribute__((weak)) = 0;
6struct style_text help_text_style[] __attribute__((weak)) = {};
7
8const bool help_valid __attribute__((weak)) = false;
diff --git a/apps/plugins/puzzles/genhelp.sh b/apps/plugins/puzzles/genhelp.sh
index 3df7706b5d..588eff4c97 100755
--- a/apps/plugins/puzzles/genhelp.sh
+++ b/apps/plugins/puzzles/genhelp.sh
@@ -1,8 +1,8 @@
1#!/bin/bash 1#!/bin/bash
2# usage: ./genhelp.sh 2# usage: ./genhelp.sh
3# 3#
4# Expects halibut to be installed in $PATH: 4# Expects a modified version of `halibut' to be installed in $PATH:
5# https://www.fwei.tk/git/halibut 5# https://github.com/built1n/halibut
6# 6#
7# Also requires host CC and lz4 library to be available 7# Also requires host CC and lz4 library to be available
8 8
@@ -36,6 +36,9 @@ BEGIN {
36 if($3 ~ "Rectangles") 36 if($3 ~ "Rectangles")
37 file = "help/rect.c"; 37 file = "help/rect.c";
38 38
39 if($3 ~ "Train")
40 file = "help/tracks.c";
41
39 print "/* auto-generated by genhelp.sh (intermediate file) */" > file; 42 print "/* auto-generated by genhelp.sh (intermediate file) */" > file;
40 print "/* DO NOT EDIT! */" > file; 43 print "/* DO NOT EDIT! */" > file;
41 print "const char help_text[] = " > file; 44 print "const char help_text[] = " > file;
@@ -87,8 +90,24 @@ do
87 mv $f.tmp $f 90 mv $f.tmp $f
88done 91done
89 92
90# generate quick help from all the .R files 93# Generate quick help by parsing the CMakeLists.txt file to isolate
91cat src/*.R | awk 'print_next { print_next = 0; print; } /!begin/ && />/ && /gamedesc.txt/ { print_next = 1; }' | awk -F ":" '{print "const char quick_help_text[] = \""$5"\";" >> "help/"$1".c" }' 94# the "Objective" text for each puzzle.
95cat <<EOF > parsed_cmakelists.txt
96function(puzzle NAME)
97 cmake_parse_arguments(OPT
98 "" "DISPLAYNAME;DESCRIPTION;OBJECTIVE;WINDOWS_EXE_NAME" "" \${ARGN})
99
100 message("\${NAME}:\${OPT_OBJECTIVE}")
101endfunction()
102EOF
103
104# This parses out the puzzle(...) definitions from CMakeLists.
105
106# TODO: Perhaps ask Simon to include special header/footer comments to
107# make this less brittle?
108cat src/CMakeLists.txt | awk '/puzzle\(/{p=1} p{print} /\)/{p=0}' >> parsed_cmakelists.txt
109cmake -P parsed_cmakelists.txt 2>&1 | awk -F ":" '{print "const char quick_help_text[] = \""$2"\";" >> "help/"$1".c" }'
92 110
111rm parsed_cmakelists.txt
93rm puzzles.txt 112rm puzzles.txt
94rm compress 113rm compress
diff --git a/apps/plugins/puzzles/help.h b/apps/plugins/puzzles/help.h
index e9cac9b337..2f870393e8 100644
--- a/apps/plugins/puzzles/help.h
+++ b/apps/plugins/puzzles/help.h
@@ -1,3 +1,5 @@
1#include <stdbool.h>
2
1#ifdef ROCKBOX 3#ifdef ROCKBOX
2#include "lib/display_text.h" 4#include "lib/display_text.h"
3#endif 5#endif
@@ -12,3 +14,5 @@ extern const unsigned short help_text_len, quick_help_text_len, help_text_words;
12#if defined(ROCKBOX) 14#if defined(ROCKBOX)
13extern struct style_text help_text_style[]; 15extern struct style_text help_text_style[];
14#endif 16#endif
17
18extern const bool help_valid;
diff --git a/apps/plugins/puzzles/help/blackbox.c b/apps/plugins/puzzles/help/blackbox.c
index 0121ee3f93..90933604c1 100644
--- a/apps/plugins/puzzles/help/blackbox.c
+++ b/apps/plugins/puzzles/help/blackbox.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,337 +6,339 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 450, TEXT_UNDERLINE }, 9 { 451, TEXT_UNDERLINE },
10 { 476, TEXT_UNDERLINE }, 10 { 477, TEXT_UNDERLINE },
11 { 503, TEXT_CENTER | C_RED }, 11 { 504, TEXT_CENTER | C_RED },
12 { 869, TEXT_UNDERLINE }, 12 { 870, TEXT_UNDERLINE },
13 { 897, TEXT_CENTER | C_RED }, 13 { 898, TEXT_CENTER | C_RED },
14 { 914, TEXT_UNDERLINE },
15 { 915, TEXT_UNDERLINE }, 14 { 915, TEXT_UNDERLINE },
16 { 927, TEXT_UNDERLINE }, 15 { 916, TEXT_UNDERLINE },
17 { 929, TEXT_UNDERLINE }, 16 { 928, TEXT_UNDERLINE },
18 { 942, TEXT_UNDERLINE }, 17 { 930, TEXT_UNDERLINE },
18 { 943, TEXT_UNDERLINE },
19 LAST_STYLE_ITEM 19 LAST_STYLE_ITEM
20}; 20};
21 21
22/* orig 5458 comp 3139 ratio 0.575119 level 10 saved 2319 */ 22/* orig 5480 comp 3142 ratio 0.573358 level 10 saved 2338 */
23const char help_text[] = { 23const char help_text[] = {
240xf0, 0x37, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 240xff, 0x09, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
250x31, 0x39, 0x3a, 0x20, 0x42, 0x6c, 0x61, 0x63, 0x6b, 0x20, 250x31, 0x39, 0x3a, 0x20, 0x42, 0x6c, 0x61, 0x63, 0x6b, 0x20,
260x42, 0x6f, 0x78, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x6e, 260x42, 0x6f, 0x78, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x01, 0xf0,
270x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x00, 0x62, 270x21, 0x00, 0x00, 0x00, 0x41, 0x00, 0x6e, 0x75, 0x6d, 0x62,
280x61, 0x6c, 0x6c, 0x73, 0x00, 0x61, 0x72, 0x65, 0x00, 0x68, 280x65, 0x72, 0x00, 0x6f, 0x66, 0x00, 0x62, 0x61, 0x6c, 0x6c,
290x69, 0x64, 0x64, 0x65, 0x6e, 0x00, 0x69, 0x6e, 0x00, 0x61, 290x73, 0x00, 0x61, 0x72, 0x65, 0x00, 0x68, 0x69, 0x64, 0x64,
300x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x75, 0x6c, 300x65, 0x6e, 0x00, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x72, 0x65,
310x61, 0x72, 0x1c, 0x00, 0xf0, 0x15, 0x6e, 0x61, 0x2e, 0x00, 310x63, 0x74, 0x61, 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x1c,
320x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x74, 320x00, 0xf0, 0x15, 0x6e, 0x61, 0x2e, 0x00, 0x59, 0x6f, 0x75,
330x6f, 0x00, 0x64, 0x65, 0x64, 0x75, 0x63, 0x65, 0x00, 0x74, 330x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x74, 0x6f, 0x00, 0x64,
340x68, 0x65, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 340x65, 0x64, 0x75, 0x63, 0x65, 0x00, 0x74, 0x68, 0x65, 0x00,
350x6e, 0x73, 0x4d, 0x00, 0x00, 0x11, 0x00, 0x02, 0x51, 0x00, 350x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d,
360xf5, 0x01, 0x62, 0x79, 0x00, 0x66, 0x69, 0x72, 0x69, 0x6e, 360x00, 0x00, 0x11, 0x00, 0x02, 0x51, 0x00, 0xf5, 0x01, 0x62,
370x67, 0x00, 0x6c, 0x61, 0x73, 0x65, 0x72, 0x73, 0x28, 0x00, 370x79, 0x00, 0x66, 0x69, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x6c,
380x51, 0x65, 0x64, 0x00, 0x61, 0x74, 0x29, 0x00, 0x45, 0x65, 380x61, 0x73, 0x65, 0x72, 0x73, 0x28, 0x00, 0x51, 0x65, 0x64,
390x64, 0x67, 0x65, 0x36, 0x00, 0x01, 0x65, 0x00, 0xb0, 0x00, 390x00, 0x61, 0x74, 0x29, 0x00, 0x45, 0x65, 0x64, 0x67, 0x65,
400x61, 0x6e, 0x64, 0x00, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 400x36, 0x00, 0x01, 0x65, 0x00, 0xb0, 0x00, 0x61, 0x6e, 0x64,
410x3a, 0x00, 0x30, 0x68, 0x6f, 0x77, 0x1c, 0x00, 0x72, 0x69, 410x00, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x3a, 0x00, 0x30,
420x72, 0x00, 0x62, 0x65, 0x61, 0x6d, 0xa5, 0x00, 0xe1, 0x64, 420x68, 0x6f, 0x77, 0x1c, 0x00, 0x72, 0x69, 0x72, 0x00, 0x62,
430x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2e, 0x00, 430x65, 0x61, 0x6d, 0xa5, 0x00, 0xe1, 0x64, 0x65, 0x66, 0x6c,
440x00, 0x00, 0x42, 0x17, 0x00, 0xf3, 0x0a, 0x77, 0x69, 0x6c, 440x65, 0x63, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x00, 0x42,
450x6c, 0x00, 0x74, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x00, 0x73, 450x17, 0x00, 0xf3, 0x0a, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x74,
460x74, 0x72, 0x61, 0x69, 0x67, 0x68, 0x74, 0x00, 0x66, 0x72, 460x72, 0x61, 0x76, 0x65, 0x6c, 0x00, 0x73, 0x74, 0x72, 0x61,
470x6f, 0x6d, 0x3d, 0x00, 0xc0, 0x6f, 0x72, 0x69, 0x67, 0x69, 470x69, 0x67, 0x68, 0x74, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x3d,
480x6e, 0x00, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x13, 0x00, 0x42, 480x00, 0xc0, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x00, 0x75,
490x79, 0x00, 0x68, 0x69, 0x82, 0x00, 0x21, 0x6f, 0x70, 0x96, 490x6e, 0x74, 0x69, 0x6c, 0x13, 0x00, 0x42, 0x79, 0x00, 0x68,
500x00, 0x6a, 0x65, 0x00, 0x73, 0x69, 0x64, 0x65, 0x8a, 0x00, 500x69, 0x82, 0x00, 0x21, 0x6f, 0x70, 0x96, 0x00, 0x6a, 0x65,
510xf2, 0x00, 0x28, 0x61, 0x74, 0x00, 0x77, 0x68, 0x69, 0x63, 510x00, 0x73, 0x69, 0x64, 0x65, 0x8a, 0x00, 0xf2, 0x00, 0x28,
520x68, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x38, 0x00, 0xf1, 520x61, 0x74, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x70,
530x04, 0x65, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x29, 0x2c, 0x00, 530x6f, 0x69, 0x6e, 0x74, 0x38, 0x00, 0xf1, 0x04, 0x65, 0x6d,
540x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x00, 0x61, 0x66, 0x66, 540x65, 0x72, 0x67, 0x65, 0x29, 0x2c, 0x00, 0x75, 0x6e, 0x6c,
550x8c, 0x00, 0x00, 0xee, 0x00, 0x02, 0xf7, 0x00, 0x55, 0x69, 550x65, 0x73, 0x73, 0x00, 0x61, 0x66, 0x66, 0x8c, 0x00, 0x00,
560x6e, 0x00, 0x6f, 0x6e, 0x4b, 0x00, 0x60, 0x66, 0x6f, 0x6c, 560xee, 0x00, 0x02, 0xf7, 0x00, 0x55, 0x69, 0x6e, 0x00, 0x6f,
570x6c, 0x6f, 0x77, 0xcb, 0x00, 0xb1, 0x77, 0x61, 0x79, 0x73, 570x6e, 0x4b, 0x00, 0x60, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77,
580x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x41, 0xcd, 0x00, 0x50, 580xcb, 0x00, 0xb1, 0x77, 0x61, 0x79, 0x73, 0x3a, 0x00, 0x00,
590x00, 0x74, 0x68, 0x61, 0x74, 0x88, 0x00, 0x31, 0x73, 0x00, 590x00, 0x2d, 0x00, 0x41, 0xcd, 0x00, 0x50, 0x00, 0x74, 0x68,
600x61, 0x3b, 0x00, 0xf1, 0x05, 0x00, 0x68, 0x65, 0x61, 0x64, 600x61, 0x74, 0x88, 0x00, 0x31, 0x73, 0x00, 0x61, 0x3b, 0x00,
610x2d, 0x6f, 0x6e, 0x00, 0x69, 0x73, 0x00, 0x61, 0x62, 0x73, 610xf1, 0x05, 0x00, 0x68, 0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e,
620x6f, 0x72, 0x62, 0x65, 0x64, 0x0f, 0x01, 0x01, 0xde, 0x00, 620x00, 0x69, 0x73, 0x00, 0x61, 0x62, 0x73, 0x6f, 0x72, 0x62,
630x93, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x00, 0x72, 0x65, 0x2d, 630x65, 0x64, 0x0f, 0x01, 0x01, 0xde, 0x00, 0x93, 0x6e, 0x65,
640x83, 0x00, 0x50, 0x2e, 0x00, 0x54, 0x68, 0x69, 0x6e, 0x00, 640x76, 0x65, 0x72, 0x00, 0x72, 0x65, 0x2d, 0x83, 0x00, 0x50,
650x63, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x20, 0x01, 0x01, 650x2e, 0x00, 0x54, 0x68, 0x69, 0x6e, 0x00, 0x63, 0x63, 0x6c,
660x54, 0x00, 0x44, 0x6d, 0x65, 0x65, 0x74, 0x54, 0x00, 0x22, 660x75, 0x64, 0x65, 0x73, 0x20, 0x01, 0x01, 0x54, 0x00, 0x44,
670x6f, 0x6e, 0x87, 0x00, 0x99, 0x69, 0x72, 0x73, 0x74, 0x00, 670x6d, 0x65, 0x65, 0x74, 0x54, 0x00, 0x22, 0x6f, 0x6e, 0x87,
680x72, 0x61, 0x6e, 0x6b, 0xe4, 0x00, 0x18, 0x2e, 0x90, 0x00, 680x00, 0x99, 0x69, 0x72, 0x73, 0x74, 0x00, 0x72, 0x61, 0x6e,
690x44, 0x77, 0x69, 0x74, 0x68, 0x37, 0x00, 0x30, 0x69, 0x6e, 690x6b, 0xe4, 0x00, 0x18, 0x2e, 0x90, 0x00, 0x44, 0x77, 0x69,
700x00, 0x99, 0x00, 0xe0, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x2d, 700x74, 0x68, 0x37, 0x00, 0x30, 0x69, 0x6e, 0x00, 0x99, 0x00,
710x6c, 0x65, 0x66, 0x74, 0x00, 0x73, 0x71, 0x75, 0x7d, 0x01, 710xe0, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x66,
720x00, 0x90, 0x00, 0x22, 0x6e, 0x6f, 0x25, 0x00, 0x10, 0x61, 720x74, 0x00, 0x73, 0x71, 0x75, 0x7d, 0x01, 0x00, 0x90, 0x00,
730xb1, 0x00, 0x00, 0x50, 0x00, 0x76, 0x69, 0x74, 0x00, 0x67, 730x22, 0x6e, 0x6f, 0x25, 0x00, 0x10, 0x61, 0xb1, 0x00, 0x00,
740x65, 0x74, 0x73, 0x9a, 0x01, 0xb0, 0x00, 0x39, 0x30, 0x00, 740x50, 0x00, 0x76, 0x69, 0x74, 0x00, 0x67, 0x65, 0x74, 0x73,
750x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x73, 0x2c, 0x02, 0x00, 750x9a, 0x01, 0xb0, 0x00, 0x39, 0x30, 0x00, 0x64, 0x65, 0x67,
760x70, 0x00, 0x10, 0x72, 0x94, 0x01, 0x0f, 0x70, 0x00, 0x13, 760x72, 0x65, 0x65, 0x73, 0x2c, 0x02, 0x00, 0x70, 0x00, 0x10,
770x01, 0x2b, 0x00, 0x0f, 0x71, 0x00, 0x12, 0x97, 0x73, 0x69, 770x72, 0x94, 0x01, 0x0f, 0x70, 0x00, 0x13, 0x01, 0x2b, 0x00,
780x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x7b, 0x00, 0x03, 780x0f, 0x71, 0x00, 0x12, 0x97, 0x73, 0x69, 0x6d, 0x69, 0x6c,
790x70, 0x00, 0x00, 0xb5, 0x00, 0x1d, 0x2e, 0x6f, 0x01, 0x50, 790x61, 0x72, 0x6c, 0x79, 0x7b, 0x00, 0x03, 0x70, 0x00, 0x00,
800x77, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x01, 0x02, 0x45, 0x01, 800xb5, 0x00, 0x1d, 0x2e, 0x6f, 0x01, 0x50, 0x77, 0x6f, 0x75,
810x02, 0x24, 0x02, 0x00, 0x7a, 0x00, 0xc2, 0x65, 0x6e, 0x74, 810x6c, 0x64, 0x46, 0x01, 0x02, 0x45, 0x01, 0x02, 0x24, 0x02,
820x72, 0x79, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x83, 820x00, 0x7a, 0x00, 0xc2, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x00,
830x01, 0x30, 0x63, 0x6f, 0x6e, 0x14, 0x02, 0x12, 0x72, 0x53, 830x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x83, 0x01, 0x30, 0x63,
840x00, 0x54, 0x62, 0x65, 0x00, 0x60, 0x72, 0x64, 0x00, 0x1a, 840x6f, 0x6e, 0x14, 0x02, 0x12, 0x72, 0x53, 0x00, 0x54, 0x62,
850x27, 0xc8, 0x00, 0x01, 0x24, 0x02, 0x02, 0x5a, 0x00, 0x37, 850x65, 0x00, 0x60, 0x72, 0x64, 0x00, 0x1a, 0x27, 0xc8, 0x00,
860x67, 0x65, 0x74, 0x8b, 0x00, 0x60, 0x62, 0x65, 0x66, 0x6f, 860x01, 0x24, 0x02, 0x02, 0x5a, 0x00, 0x37, 0x67, 0x65, 0x74,
870x72, 0x65, 0x5c, 0x00, 0x11, 0x65, 0x08, 0x03, 0x06, 0x5c, 870x8b, 0x00, 0x60, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5c,
880x02, 0x24, 0x62, 0x79, 0xfa, 0x00, 0x03, 0xaf, 0x00, 0x07, 880x00, 0x11, 0x65, 0x08, 0x03, 0x06, 0x5c, 0x02, 0x24, 0x62,
890x6a, 0x01, 0x29, 0x6f, 0x72, 0x08, 0x01, 0x27, 0x6f, 0x66, 890x79, 0xfa, 0x00, 0x03, 0xaf, 0x00, 0x07, 0x6a, 0x01, 0x29,
900xa1, 0x00, 0x02, 0x8a, 0x02, 0x00, 0x21, 0x02, 0x3f, 0x6c, 900x6f, 0x72, 0x08, 0x01, 0x27, 0x6f, 0x66, 0xa1, 0x00, 0x02,
910x73, 0x6f, 0xa3, 0x00, 0x0e, 0x16, 0x42, 0x14, 0x02, 0x00, 910x8a, 0x02, 0x00, 0x21, 0x02, 0x3f, 0x6c, 0x73, 0x6f, 0xa3,
920x4b, 0x01, 0x15, 0x72, 0x99, 0x00, 0x40, 0x61, 0x70, 0x70, 920x00, 0x0e, 0x16, 0x42, 0x14, 0x02, 0x00, 0x4b, 0x01, 0x15,
930x65, 0xd7, 0x03, 0x00, 0x7b, 0x02, 0x48, 0x60, 0x52, 0x27, 930x72, 0x99, 0x00, 0x40, 0x61, 0x70, 0x70, 0x65, 0xd7, 0x03,
940x3b, 0x3e, 0x02, 0x00, 0x1a, 0x03, 0x02, 0xca, 0x02, 0x01, 940x00, 0x7b, 0x02, 0x48, 0x60, 0x52, 0x27, 0x3b, 0x3e, 0x02,
950x90, 0x02, 0x00, 0x42, 0x02, 0x06, 0x2f, 0x00, 0xf4, 0x02, 950x00, 0x1a, 0x03, 0x02, 0xca, 0x02, 0x01, 0x90, 0x02, 0x00,
960x60, 0x48, 0x27, 0x2e, 0x00, 0x4f, 0x74, 0x68, 0x65, 0x72, 960x42, 0x02, 0x06, 0x2f, 0x00, 0xf4, 0x02, 0x60, 0x48, 0x27,
970x77, 0x69, 0x73, 0x65, 0x2c, 0x00, 0x61, 0x47, 0x04, 0x02, 970x2e, 0x00, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, 0x73,
980x23, 0x00, 0x14, 0x73, 0xd9, 0x03, 0x03, 0xf9, 0x03, 0x02, 980x65, 0x2c, 0x00, 0x61, 0x47, 0x04, 0x02, 0x23, 0x00, 0x14,
990xaf, 0x00, 0x00, 0xc3, 0x01, 0x01, 0x93, 0x01, 0x04, 0x5e, 990x73, 0xd9, 0x03, 0x03, 0xf9, 0x03, 0x02, 0xaf, 0x00, 0x00,
1000x01, 0x42, 0x77, 0x68, 0x65, 0x72, 0x3b, 0x04, 0x01, 0x41, 1000xc3, 0x01, 0x01, 0x93, 0x01, 0x04, 0x5e, 0x01, 0x42, 0x77,
1010x01, 0x02, 0x8c, 0x01, 0x40, 0x73, 0x00, 0x28, 0x74, 0xd2, 1010x68, 0x65, 0x72, 0x3b, 0x04, 0x01, 0x41, 0x01, 0x02, 0x8c,
1020x02, 0x03, 0x51, 0x00, 0x81, 0x69, 0x73, 0x00, 0x75, 0x6e, 1020x01, 0x40, 0x73, 0x00, 0x28, 0x74, 0xd2, 0x02, 0x03, 0x51,
1030x69, 0x71, 0x75, 0x6d, 0x04, 0x01, 0x99, 0x00, 0x50, 0x73, 1030x00, 0x81, 0x69, 0x73, 0x00, 0x75, 0x6e, 0x69, 0x71, 0x75,
1040x68, 0x6f, 0x74, 0x29, 0xd7, 0x00, 0x00, 0x87, 0x04, 0xf5, 1040x6d, 0x04, 0x01, 0x99, 0x00, 0x50, 0x73, 0x68, 0x6f, 0x74,
1050x04, 0x63, 0x61, 0x6e, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 1050x29, 0xd7, 0x00, 0x00, 0x87, 0x04, 0xf5, 0x04, 0x63, 0x61,
1060x00, 0x67, 0x75, 0x65, 0x73, 0x73, 0x65, 0x73, 0x00, 0x61, 1060x6e, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x00, 0x67, 0x75,
1070x6b, 0x02, 0x05, 0x68, 0x00, 0x08, 0x8f, 0x04, 0x74, 0x2c, 1070x65, 0x73, 0x73, 0x65, 0x73, 0x00, 0x61, 0x6b, 0x02, 0x05,
1080x00, 0x62, 0x61, 0x73, 0x65, 0x64, 0x11, 0x03, 0x02, 0x4f, 1080x68, 0x00, 0x08, 0x8f, 0x04, 0x74, 0x2c, 0x00, 0x62, 0x61,
1090x01, 0x00, 0x9a, 0x00, 0xb7, 0x65, 0x78, 0x69, 0x74, 0x00, 1090x73, 0x65, 0x64, 0x11, 0x03, 0x02, 0x4f, 0x01, 0x00, 0x9a,
1100x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0xc2, 0x04, 0x00, 0x10, 1100x00, 0xb7, 0x65, 0x78, 0x69, 0x74, 0x00, 0x70, 0x61, 0x74,
1110x01, 0x84, 0x3b, 0x00, 0x6f, 0x6e, 0x63, 0x65, 0x00, 0x79, 1110x74, 0x65, 0x72, 0xc2, 0x04, 0x00, 0x10, 0x01, 0x84, 0x3b,
1120xf6, 0x04, 0x01, 0x70, 0x00, 0x84, 0x64, 0x00, 0x65, 0x6e, 1120x00, 0x6f, 0x6e, 0x63, 0x65, 0x00, 0x79, 0xf6, 0x04, 0x01,
1130x6f, 0x75, 0x67, 0x68, 0x36, 0x05, 0x55, 0x00, 0x62, 0x75, 1130x70, 0x00, 0x84, 0x64, 0x00, 0x65, 0x6e, 0x6f, 0x75, 0x67,
1140x74, 0x74, 0x24, 0x01, 0x00, 0xa8, 0x01, 0x30, 0x61, 0x62, 1140x68, 0x36, 0x05, 0x55, 0x00, 0x62, 0x75, 0x74, 0x74, 0x24,
1150x6c, 0xfc, 0x00, 0x00, 0x37, 0x00, 0x22, 0x74, 0x6f, 0x3a, 1150x01, 0x00, 0xa8, 0x01, 0x30, 0x61, 0x62, 0x6c, 0xfc, 0x00,
1160x00, 0x45, 0x79, 0x6f, 0x75, 0x72, 0xa9, 0x00, 0x52, 0x63, 1160x00, 0x37, 0x00, 0x22, 0x74, 0x6f, 0x3a, 0x00, 0x45, 0x79,
1170x68, 0x65, 0x63, 0x6b, 0xbe, 0x04, 0x10, 0x48, 0x08, 0x01, 1170x6f, 0x75, 0x72, 0xa9, 0x00, 0x52, 0x63, 0x68, 0x65, 0x63,
1180x10, 0x69, 0x46, 0x00, 0x70, 0x64, 0x69, 0x61, 0x67, 0x72, 1180x6b, 0xbe, 0x04, 0x10, 0x48, 0x08, 0x01, 0x10, 0x69, 0x46,
1190x61, 0x6d, 0xe5, 0x00, 0x17, 0x77, 0xf9, 0x04, 0x0a, 0x5c, 1190x00, 0x70, 0x64, 0x69, 0x61, 0x67, 0x72, 0x61, 0x6d, 0xe5,
1200x05, 0x02, 0x73, 0x00, 0x00, 0xfb, 0x00, 0xb8, 0x63, 0x72, 1200x00, 0x17, 0x77, 0xf9, 0x04, 0x0a, 0x5c, 0x05, 0x02, 0x73,
1210x65, 0x61, 0x74, 0x65, 0x00, 0x65, 0x61, 0x63, 0x68, 0xb3, 1210x00, 0x00, 0xfb, 0x00, 0xb8, 0x63, 0x72, 0x65, 0x61, 0x74,
1220x00, 0xb1, 0x00, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 1220x65, 0x00, 0x65, 0x61, 0x63, 0x68, 0xb3, 0x00, 0xb1, 0x00,
1230x75, 0x72, 0x73, 0x4a, 0x00, 0x70, 0x6e, 0x00, 0x61, 0x62, 1230x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x75, 0x72, 0x73,
1240x6f, 0x76, 0x65, 0x77, 0x04, 0xf3, 0x09, 0x31, 0x52, 0x48, 1240x4a, 0x00, 0x70, 0x6e, 0x00, 0x61, 0x62, 0x6f, 0x76, 0x65,
1250x52, 0x2d, 0x2d, 0x2d, 0x2d, 0x00, 0x00, 0x7c, 0x2e, 0x2e, 1250x77, 0x04, 0x42, 0x31, 0x52, 0x48, 0x52, 0xfc, 0x05, 0xe3,
1260x4f, 0x2e, 0x4f, 0x2e, 0x2e, 0x2e, 0x7c, 0x00, 0x00, 0x32, 1260x7c, 0x2e, 0x2e, 0x4f, 0x2e, 0x4f, 0x2e, 0x2e, 0x2e, 0x7c,
1270x2e, 0x01, 0x00, 0x11, 0x33, 0x18, 0x00, 0x02, 0x01, 0x00, 1270x00, 0x00, 0x32, 0x2e, 0x01, 0x00, 0x11, 0x33, 0x18, 0x00,
1280x1a, 0x7c, 0x0c, 0x00, 0x1e, 0x33, 0x18, 0x00, 0x10, 0x4f, 1280x02, 0x01, 0x00, 0x1a, 0x7c, 0x0c, 0x00, 0x1e, 0x33, 0x18,
1290x0c, 0x00, 0x1d, 0x48, 0x18, 0x00, 0x11, 0x4f, 0x0c, 0x00, 1290x00, 0x10, 0x4f, 0x0c, 0x00, 0x1d, 0x48, 0x18, 0x00, 0x11,
1300x51, 0x31, 0x32, 0x2d, 0x52, 0x52, 0x6a, 0x00, 0x33, 0x00, 1300x4f, 0x0c, 0x00, 0x52, 0x31, 0x32, 0x2d, 0x52, 0x52, 0x66,
1310x00, 0x41, 0x88, 0x00, 0x10, 0x2c, 0xb3, 0x03, 0x11, 0x69, 1310x06, 0x23, 0x00, 0x41, 0x88, 0x00, 0x10, 0x2c, 0xb3, 0x03,
1320x03, 0x06, 0x40, 0x73, 0x69, 0x62, 0x6c, 0x18, 0x05, 0x33, 1320x11, 0x69, 0x03, 0x06, 0x40, 0x73, 0x69, 0x62, 0x6c, 0x18,
1330x72, 0x00, 0x61, 0x99, 0x03, 0x10, 0x6f, 0x6c, 0x06, 0xc5, 1330x05, 0x33, 0x72, 0x00, 0x61, 0x99, 0x03, 0x10, 0x6f, 0x6c,
1340x65, 0x69, 0x76, 0x65, 0x00, 0x6d, 0x75, 0x6c, 0x74, 0x69, 1340x06, 0xc5, 0x65, 0x69, 0x76, 0x65, 0x00, 0x6d, 0x75, 0x6c,
1350x70, 0x6c, 0xab, 0x02, 0x01, 0xf9, 0x00, 0x03, 0x46, 0x03, 1350x74, 0x69, 0x70, 0x6c, 0xab, 0x02, 0x01, 0xf9, 0x00, 0x03,
1360x04, 0xb5, 0x03, 0x00, 0x1e, 0x01, 0xf4, 0x00, 0x28, 0x73, 1360x46, 0x03, 0x04, 0xb5, 0x03, 0x00, 0x1e, 0x01, 0xf4, 0x00,
1370x65, 0x65, 0x00, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x33, 0x29, 1370x28, 0x73, 0x65, 0x65, 0x00, 0x74, 0x75, 0x72, 0x6e, 0x00,
1380x2e, 0x00, 0x53, 0x06, 0x04, 0x14, 0x2c, 0x53, 0x00, 0x58, 1380x33, 0x29, 0x2e, 0x00, 0x53, 0x06, 0x04, 0x14, 0x2c, 0x53,
1390x6d, 0x61, 0x79, 0x00, 0x62, 0xf1, 0x02, 0x13, 0x28, 0x79, 1390x00, 0x58, 0x6d, 0x61, 0x79, 0x00, 0x62, 0xf1, 0x02, 0x13,
1400x00, 0x41, 0x79, 0x00, 0x6d, 0x6f, 0x7b, 0x02, 0x21, 0x61, 1400x28, 0x79, 0x00, 0x41, 0x79, 0x00, 0x6d, 0x6f, 0x7b, 0x02,
1410x6e, 0xdd, 0x01, 0x16, 0x29, 0x5e, 0x00, 0x00, 0x82, 0x00, 1410x21, 0x61, 0x6e, 0xdd, 0x01, 0x16, 0x29, 0x5e, 0x00, 0x00,
1420x00, 0x5c, 0x00, 0x11, 0x61, 0x02, 0x03, 0x10, 0x28, 0x53, 1420x82, 0x00, 0x00, 0x5c, 0x00, 0x11, 0x61, 0x02, 0x03, 0x10,
1430x01, 0x34, 0x60, 0x48, 0x27, 0x30, 0x02, 0x02, 0x04, 0x05, 1430x28, 0x53, 0x01, 0x34, 0x60, 0x48, 0x27, 0x30, 0x02, 0x02,
1440x07, 0x24, 0x06, 0x71, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 1440x04, 0x05, 0x07, 0x24, 0x06, 0x71, 0x65, 0x78, 0x61, 0x6d,
1450x65, 0x91, 0x02, 0x43, 0x4e, 0x6f, 0x74, 0x65, 0x67, 0x03, 1450x70, 0x6c, 0x65, 0x91, 0x02, 0x43, 0x4e, 0x6f, 0x74, 0x65,
1460x92, 0x6e, 0x79, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 1460x67, 0x03, 0x92, 0x6e, 0x79, 0x00, 0x6c, 0x61, 0x79, 0x6f,
1470xdf, 0x04, 0x06, 0x6e, 0x00, 0x13, 0x34, 0xbe, 0x01, 0x00, 1470x75, 0x74, 0xdf, 0x04, 0x06, 0x6e, 0x00, 0x13, 0x34, 0xbe,
1480x9b, 0x00, 0x01, 0x14, 0x02, 0x63, 0x61, 0x00, 0x6e, 0x6f, 1480x01, 0x00, 0x9b, 0x00, 0x01, 0x14, 0x02, 0x63, 0x61, 0x00,
1490x6e, 0x2d, 0xe4, 0x02, 0x40, 0x73, 0x6f, 0x6c, 0x75, 0xee, 1490x6e, 0x6f, 0x6e, 0x2d, 0xe4, 0x02, 0x40, 0x73, 0x6f, 0x6c,
1500x00, 0x00, 0xd6, 0x05, 0x08, 0x35, 0x06, 0x04, 0x17, 0x02, 1500x75, 0xee, 0x00, 0x00, 0xd6, 0x05, 0x08, 0x35, 0x06, 0x04,
1510x40, 0x69, 0x6c, 0x6c, 0x75, 0xd9, 0x06, 0x20, 0x74, 0x65, 1510x17, 0x02, 0x40, 0x69, 0x6c, 0x6c, 0x75, 0xd9, 0x06, 0x20,
1520xa6, 0x03, 0x61, 0x69, 0x73, 0x3b, 0x00, 0x69, 0x66, 0x62, 1520x74, 0x65, 0xa6, 0x03, 0x61, 0x69, 0x73, 0x3b, 0x00, 0x69,
1530x02, 0x23, 0x6b, 0x6e, 0x29, 0x02, 0x50, 0x62, 0x6f, 0x61, 1530x66, 0x62, 0x02, 0x23, 0x6b, 0x6e, 0x29, 0x02, 0x50, 0x62,
1540x72, 0x64, 0x12, 0x04, 0x74, 0x74, 0x61, 0x69, 0x6e, 0x73, 1540x6f, 0x61, 0x72, 0x64, 0x12, 0x04, 0x74, 0x74, 0x61, 0x69,
1550x00, 0x35, 0xf6, 0x02, 0x02, 0x78, 0x01, 0x25, 0x69, 0x6d, 1550x6e, 0x73, 0x00, 0x35, 0xf6, 0x02, 0x02, 0x78, 0x01, 0x25,
1560x7a, 0x01, 0x01, 0xbc, 0x07, 0x77, 0x74, 0x65, 0x72, 0x6d, 1560x69, 0x6d, 0x7a, 0x01, 0x01, 0xbc, 0x07, 0x77, 0x74, 0x65,
1570x69, 0x6e, 0x65, 0x8a, 0x03, 0x53, 0x66, 0x69, 0x66, 0x74, 1570x72, 0x6d, 0x69, 0x6e, 0x65, 0x8a, 0x03, 0x53, 0x66, 0x69,
1580x68, 0x8d, 0x05, 0x15, 0x73, 0x30, 0x01, 0x08, 0x81, 0x02, 1580x66, 0x74, 0x68, 0x8d, 0x05, 0x15, 0x73, 0x30, 0x01, 0x08,
1590x63, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x64, 0xb7, 0x05, 0x41, 1590x81, 0x02, 0x63, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x64, 0xb7,
1600x6e, 0x00, 0x78, 0x29, 0xce, 0x06, 0x03, 0x01, 0x00, 0x0f, 1600x05, 0x41, 0x6e, 0x00, 0x78, 0x29, 0xce, 0x06, 0x05, 0x53,
1610x3f, 0x02, 0x07, 0x11, 0x7c, 0x18, 0x02, 0x15, 0x4f, 0x18, 1610x08, 0x0f, 0x3f, 0x02, 0x05, 0x11, 0x7c, 0x18, 0x02, 0x15,
1620x00, 0x3e, 0x78, 0x78, 0x2e, 0x0c, 0x00, 0x0b, 0x24, 0x00, 1620x4f, 0x18, 0x00, 0x3e, 0x78, 0x78, 0x2e, 0x0c, 0x00, 0x0b,
1630x0e, 0x48, 0x00, 0x08, 0x6a, 0x00, 0x61, 0x00, 0x00, 0x46, 1630x24, 0x00, 0x0f, 0x48, 0x00, 0x01, 0x07, 0xbd, 0x08, 0x51,
1640x6f, 0x72, 0x00, 0x2d, 0x04, 0x70, 0x72, 0x65, 0x61, 0x73, 1640x00, 0x46, 0x6f, 0x72, 0x00, 0x2d, 0x04, 0x70, 0x72, 0x65,
1650x6f, 0x6e, 0x2c, 0xc8, 0x00, 0x16, 0x6e, 0xa9, 0x03, 0x0f, 1650x61, 0x73, 0x6f, 0x6e, 0x2c, 0xc8, 0x00, 0x16, 0x6e, 0xa9,
1660x6f, 0x03, 0x01, 0x11, 0x2c, 0xe6, 0x00, 0x42, 0x67, 0x61, 1660x03, 0x0f, 0x6f, 0x03, 0x01, 0x11, 0x2c, 0xe6, 0x00, 0x42,
1670x6d, 0x65, 0x50, 0x07, 0x01, 0x17, 0x00, 0x02, 0xb5, 0x01, 1670x67, 0x61, 0x6d, 0x65, 0x50, 0x07, 0x01, 0x17, 0x00, 0x02,
1680x01, 0x2f, 0x00, 0x04, 0x82, 0x01, 0x40, 0x00, 0x70, 0x72, 1680xb5, 0x01, 0x01, 0x2f, 0x00, 0x04, 0x82, 0x01, 0x40, 0x00,
1690x6f, 0xda, 0x08, 0xf2, 0x02, 0x73, 0x20, 0x74, 0x68, 0x65, 1690x70, 0x72, 0x6f, 0xda, 0x08, 0xf2, 0x02, 0x73, 0x20, 0x74,
1700x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 1700x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x72, 0x65,
1710x6c, 0x74, 0x62, 0x04, 0xf0, 0x02, 0x68, 0x65, 0x00, 0x63, 1710x73, 0x75, 0x6c, 0x74, 0x62, 0x04, 0xf0, 0x02, 0x68, 0x65,
1720x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x27, 0x73, 0x2c, 1720x00, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x27,
1730x00, 0x72, 0x61, 0x0c, 0x05, 0x02, 0xe2, 0x01, 0x0e, 0x4c, 1730x73, 0x2c, 0x00, 0x72, 0x61, 0x0c, 0x05, 0x02, 0xe2, 0x01,
1740x00, 0x01, 0x7a, 0x01, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x69, 1740x0e, 0x4c, 0x00, 0x01, 0x7a, 0x01, 0x75, 0x64, 0x65, 0x6e,
1750x63, 0x61, 0xea, 0x05, 0x06, 0x3f, 0x00, 0x62, 0x2e, 0x00, 1750x74, 0x69, 0x63, 0x61, 0xea, 0x05, 0x06, 0x3f, 0x00, 0x62,
1760x53, 0x6f, 0x00, 0x69, 0x60, 0x02, 0x01, 0xa8, 0x03, 0x04, 1760x2e, 0x00, 0x53, 0x6f, 0x00, 0x69, 0x60, 0x02, 0x01, 0xa8,
1770x55, 0x02, 0x11, 0x2c, 0xcb, 0x00, 0x11, 0x63, 0x52, 0x06, 1770x03, 0x04, 0x55, 0x02, 0x11, 0x2c, 0xcb, 0x00, 0x11, 0x63,
1780x3c, 0x70, 0x75, 0x74, 0xa0, 0x01, 0x03, 0x68, 0x02, 0x29, 1780x52, 0x06, 0x3c, 0x70, 0x75, 0x74, 0xa0, 0x01, 0x03, 0x68,
1790x6f, 0x66, 0xee, 0x04, 0x0e, 0xa1, 0x01, 0x11, 0x2c, 0xe0, 1790x02, 0x29, 0x6f, 0x66, 0xee, 0x04, 0x0e, 0xa1, 0x01, 0x11,
1800x04, 0x00, 0x4b, 0x00, 0x02, 0x9d, 0x06, 0x20, 0x73, 0x74, 1800x2c, 0xe0, 0x04, 0x00, 0x4b, 0x00, 0x02, 0x9d, 0x06, 0x20,
1810xf9, 0x00, 0x31, 0x77, 0x69, 0x6e, 0x1c, 0x06, 0x00, 0x0e, 1810x73, 0x74, 0xf9, 0x00, 0x31, 0x77, 0x69, 0x6e, 0x1c, 0x06,
1820x0a, 0x81, 0x00, 0x42, 0x6f, 0x78, 0x00, 0x77, 0x61, 0x73, 1820x00, 0x24, 0x0a, 0x81, 0x00, 0x42, 0x6f, 0x78, 0x00, 0x77,
1830x38, 0x02, 0x45, 0x72, 0x69, 0x62, 0x75, 0x41, 0x07, 0x01, 1830x61, 0x73, 0x38, 0x02, 0x45, 0x72, 0x69, 0x62, 0x75, 0x41,
1840x01, 0x07, 0x13, 0x6c, 0x84, 0x03, 0x00, 0xaf, 0x06, 0xd0, 1840x07, 0x01, 0x01, 0x07, 0x13, 0x6c, 0x84, 0x03, 0x00, 0xaf,
1850x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76, 1850x06, 0xd0, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61,
1860x65, 0x79, 0x2e, 0x50, 0x04, 0x37, 0x39, 0x2e, 0x31, 0x53, 1860x72, 0x76, 0x65, 0x79, 0x2e, 0x50, 0x04, 0x37, 0x39, 0x2e,
1870x0a, 0x01, 0x41, 0x00, 0x30, 0x6f, 0x6c, 0x73, 0x5c, 0x0a, 1870x31, 0x69, 0x0a, 0x01, 0x41, 0x00, 0x90, 0x6f, 0x6c, 0x73,
1880x20, 0x54, 0x6f, 0x00, 0x06, 0x00, 0xe2, 0x02, 0x01, 0xf9, 1880x20, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x00, 0x06, 0x00, 0xe2,
1890x09, 0x01, 0x93, 0x03, 0x11, 0x2c, 0x42, 0x03, 0x62, 0x2d, 1890x02, 0x01, 0xf9, 0x09, 0x01, 0x93, 0x03, 0x11, 0x2c, 0x42,
1900x63, 0x6c, 0x69, 0x63, 0x6b, 0x60, 0x0a, 0x04, 0xe0, 0x07, 1900x03, 0x62, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x60, 0x0a,
1910x55, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0e, 0x0a, 0x0b, 0x9f, 1910x04, 0xe0, 0x07, 0x55, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0e,
1920x08, 0x00, 0x0e, 0x03, 0x04, 0x7f, 0x01, 0x01, 0xb7, 0x01, 1920x0a, 0x0b, 0x9f, 0x08, 0x00, 0x0e, 0x03, 0x04, 0x7f, 0x01,
1930xf0, 0x0c, 0x62, 0x65, 0x00, 0x64, 0x69, 0x73, 0x70, 0x6c, 1930x01, 0xb7, 0x01, 0xf0, 0x0c, 0x62, 0x65, 0x00, 0x64, 0x69,
1940x61, 0x79, 0x65, 0x64, 0x00, 0x69, 0x6d, 0x6d, 0x65, 0x64, 1940x73, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x00, 0x69, 0x6d,
1950x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e, 0x00, 0x43, 0x5b, 1950x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e,
1960x00, 0x00, 0x34, 0x03, 0x74, 0x6f, 0x72, 0x00, 0x68, 0x6f, 1960x00, 0x43, 0x5b, 0x00, 0x00, 0x34, 0x03, 0x74, 0x6f, 0x72,
1970x6c, 0x64, 0x71, 0x07, 0x01, 0xba, 0x03, 0x03, 0xa5, 0x05, 1970x00, 0x68, 0x6f, 0x6c, 0x64, 0x71, 0x07, 0x01, 0xba, 0x03,
1980x18, 0x6f, 0x9c, 0x09, 0x23, 0x73, 0x65, 0x84, 0x00, 0x03, 1980x03, 0xa5, 0x05, 0x18, 0x6f, 0x9c, 0x09, 0x23, 0x73, 0x65,
1990x5b, 0x00, 0x51, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x73, 0x07, 1990x84, 0x00, 0x03, 0x5b, 0x00, 0x51, 0x68, 0x69, 0x67, 0x68,
2000x01, 0xa7, 0x01, 0xb1, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 2000x6c, 0x73, 0x07, 0x01, 0xa7, 0x01, 0xb1, 0x75, 0x72, 0x72,
2010x00, 0x67, 0x6f, 0x00, 0x28, 0xac, 0x04, 0x30, 0x70, 0x72, 2010x65, 0x6e, 0x74, 0x00, 0x67, 0x6f, 0x00, 0x28, 0xac, 0x04,
2020x65, 0x5a, 0x05, 0x71, 0x73, 0x00, 0x67, 0x6f, 0x29, 0x00, 2020x30, 0x70, 0x72, 0x65, 0x5a, 0x05, 0x71, 0x73, 0x00, 0x67,
2030x74, 0x7d, 0x07, 0x31, 0x66, 0x69, 0x72, 0x62, 0x0a, 0x03, 2030x6f, 0x29, 0x00, 0x74, 0x7d, 0x07, 0x31, 0x66, 0x69, 0x72,
2040x44, 0x06, 0x01, 0xed, 0x06, 0x00, 0xd9, 0x04, 0x01, 0x0a, 2040x62, 0x0a, 0x03, 0x44, 0x06, 0x01, 0xed, 0x06, 0x00, 0xd9,
2050x02, 0x01, 0xfc, 0x00, 0x10, 0x2c, 0xa8, 0x03, 0xb2, 0x61, 2050x04, 0x01, 0x0a, 0x02, 0x01, 0xfc, 0x00, 0x10, 0x2c, 0xa8,
2060x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6c, 0x65, 0x2e, 2060x03, 0xb2, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62,
2070x1e, 0x01, 0x01, 0x9b, 0x02, 0x0d, 0xab, 0x06, 0x02, 0x11, 2070x6c, 0x65, 0x2e, 0x1e, 0x01, 0x01, 0x9b, 0x02, 0x0d, 0xab,
2080x08, 0x09, 0x29, 0x01, 0x00, 0xcb, 0x01, 0x2b, 0x69, 0x6e, 2080x06, 0x02, 0x11, 0x08, 0x09, 0x29, 0x01, 0x00, 0xcb, 0x01,
2090x1e, 0x0b, 0x31, 0x61, 0x00, 0x62, 0xbc, 0x01, 0x53, 0x63, 2090x2b, 0x69, 0x6e, 0x1e, 0x0b, 0x31, 0x61, 0x00, 0x62, 0xbc,
2100x69, 0x72, 0x63, 0x6c, 0xce, 0x02, 0x03, 0xa2, 0x07, 0x00, 2100x01, 0x53, 0x63, 0x69, 0x72, 0x63, 0x6c, 0xce, 0x02, 0x03,
2110x02, 0x02, 0x04, 0xf8, 0x00, 0x01, 0x65, 0x00, 0x22, 0x3b, 2110xa2, 0x07, 0x00, 0x02, 0x02, 0x04, 0xf8, 0x00, 0x01, 0x65,
2120x00, 0x49, 0x00, 0x52, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x6a, 2120x00, 0x22, 0x3b, 0x00, 0x49, 0x00, 0x52, 0x61, 0x67, 0x61,
2130x05, 0x10, 0x6d, 0x69, 0x02, 0x05, 0x21, 0x00, 0x21, 0x65, 2130x69, 0x6e, 0x6a, 0x05, 0x10, 0x6d, 0x69, 0x02, 0x05, 0x21,
2140x64, 0x76, 0x00, 0x00, 0x99, 0x00, 0x15, 0x4c, 0x49, 0x02, 2140x00, 0x21, 0x65, 0x64, 0x76, 0x00, 0x00, 0x99, 0x00, 0x15,
2150x09, 0x73, 0x00, 0x03, 0x4c, 0x05, 0x20, 0x6c, 0x6f, 0x46, 2150x4c, 0x49, 0x02, 0x09, 0x73, 0x00, 0x03, 0x4c, 0x05, 0x20,
2160x03, 0x02, 0x49, 0x00, 0xb4, 0x73, 0x74, 0x00, 0x6d, 0x6f, 2160x6c, 0x6f, 0x46, 0x03, 0x02, 0x49, 0x00, 0xb4, 0x73, 0x74,
2170x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x1f, 0x02, 0x01, 0xac, 2170x00, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x1f,
2180x08, 0x33, 0x2d, 0x00, 0x63, 0x85, 0x01, 0x50, 0x3b, 0x00, 2180x02, 0x01, 0xac, 0x08, 0x33, 0x2d, 0x00, 0x63, 0x85, 0x01,
2190x77, 0x68, 0x6f, 0xc8, 0x05, 0x31, 0x6f, 0x77, 0x73, 0xb5, 2190x50, 0x3b, 0x00, 0x77, 0x68, 0x6f, 0xc8, 0x05, 0x31, 0x6f,
2200x00, 0x74, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x4e, 2200x77, 0x73, 0xb5, 0x00, 0x74, 0x63, 0x6f, 0x6c, 0x75, 0x6d,
2210x00, 0x06, 0xb9, 0x09, 0x03, 0x58, 0x00, 0x0e, 0x43, 0x00, 2210x6e, 0x73, 0x4e, 0x00, 0x06, 0xb9, 0x09, 0x03, 0x58, 0x00,
2220x04, 0x86, 0x00, 0x02, 0x44, 0x02, 0x04, 0x2e, 0x02, 0x00, 2220x0e, 0x43, 0x00, 0x04, 0x86, 0x00, 0x02, 0x44, 0x02, 0x04,
2230x27, 0x03, 0x62, 0x2f, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x66, 2230x2e, 0x02, 0x00, 0x27, 0x03, 0x62, 0x2f, 0x62, 0x65, 0x6c,
2240x01, 0x02, 0x58, 0x00, 0x10, 0x2c, 0xf5, 0x01, 0x07, 0xf9, 2240x6f, 0x77, 0x66, 0x01, 0x02, 0x58, 0x00, 0x10, 0x2c, 0xf5,
2250x09, 0x15, 0x2f, 0x3b, 0x09, 0x01, 0x25, 0x00, 0x31, 0x72, 2250x01, 0x07, 0xf9, 0x09, 0x15, 0x2f, 0x3b, 0x09, 0x01, 0x25,
2260x6f, 0x77, 0x7a, 0x01, 0x02, 0xd7, 0x01, 0x72, 0x73, 0x6f, 2260x00, 0x31, 0x72, 0x6f, 0x77, 0x7a, 0x01, 0x02, 0xd7, 0x01,
2270x72, 0x00, 0x6b, 0x65, 0x79, 0x8c, 0x00, 0x01, 0x48, 0x09, 2270x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x8c, 0x00,
2280x52, 0x62, 0x65, 0x00, 0x75, 0x73, 0xfa, 0x02, 0x01, 0x1e, 2280x01, 0x48, 0x09, 0x52, 0x62, 0x65, 0x00, 0x75, 0x73, 0xfa,
2290x01, 0x07, 0x94, 0x02, 0xb4, 0x67, 0x72, 0x69, 0x64, 0x2e, 2290x02, 0x01, 0x1e, 0x01, 0x07, 0x94, 0x02, 0xb4, 0x67, 0x72,
2300x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x59, 0x01, 0x10, 0x45, 2300x69, 0x64, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x59,
2310xcf, 0x09, 0x00, 0x42, 0x00, 0x02, 0x7b, 0x01, 0x09, 0xeb, 2310x01, 0x10, 0x45, 0xcf, 0x09, 0x00, 0x42, 0x00, 0x02, 0x7b,
2320x02, 0x00, 0x2a, 0x02, 0x20, 0x64, 0x64, 0xdc, 0x05, 0x21, 2320x01, 0x09, 0xeb, 0x02, 0x00, 0x2a, 0x02, 0x20, 0x64, 0x64,
2330x65, 0x77, 0x59, 0x01, 0x15, 0x2d, 0xe2, 0x01, 0x01, 0x6f, 2330xdc, 0x05, 0x21, 0x65, 0x77, 0x59, 0x01, 0x15, 0x2d, 0xe2,
2340x01, 0x02, 0x93, 0x03, 0x14, 0x70, 0x4f, 0x00, 0x43, 0x53, 2340x01, 0x01, 0x6f, 0x01, 0x02, 0x93, 0x03, 0x14, 0x70, 0x4f,
2350x70, 0x61, 0x63, 0xc2, 0x01, 0x20, 0x6c, 0x6f, 0xa7, 0x01, 2350x00, 0x43, 0x53, 0x70, 0x61, 0x63, 0xc2, 0x01, 0x20, 0x6c,
2360x30, 0x00, 0x63, 0x65, 0x03, 0x02, 0x31, 0x72, 0x6f, 0x77, 2360x6f, 0xa7, 0x01, 0x30, 0x00, 0x63, 0x65, 0x03, 0x02, 0x31,
2370xd3, 0x00, 0x02, 0xde, 0x00, 0x00, 0xbc, 0x00, 0x10, 0x57, 2370x72, 0x6f, 0x77, 0xd3, 0x00, 0x02, 0xde, 0x00, 0x00, 0xbc,
2380xe1, 0x04, 0x11, 0x61, 0x67, 0x08, 0x40, 0x72, 0x6f, 0x70, 2380x00, 0x10, 0x57, 0xe1, 0x04, 0x11, 0x61, 0x67, 0x08, 0x40,
2390x72, 0xf5, 0x02, 0x0d, 0xc2, 0x0d, 0x01, 0xfc, 0x04, 0x44, 2390x72, 0x6f, 0x70, 0x72, 0xf5, 0x02, 0x0d, 0xc2, 0x0d, 0x01,
2400x62, 0x65, 0x65, 0x6e, 0xdb, 0x01, 0x16, 0x2c, 0x9f, 0x08, 2400xfc, 0x04, 0x44, 0x62, 0x65, 0x65, 0x6e, 0xdb, 0x01, 0x16,
2410x08, 0x26, 0x02, 0x03, 0xa4, 0x09, 0x32, 0x74, 0x6f, 0x70, 2410x2c, 0x9f, 0x08, 0x08, 0x26, 0x02, 0x03, 0xa4, 0x09, 0x32,
2420x71, 0x0a, 0x42, 0x63, 0x6f, 0x72, 0x6e, 0x48, 0x00, 0x04, 2420x74, 0x6f, 0x70, 0x71, 0x0a, 0x42, 0x63, 0x6f, 0x72, 0x6e,
2430xf2, 0x00, 0x16, 0x3b, 0x86, 0x01, 0x01, 0x41, 0x01, 0x13, 2430x48, 0x00, 0x04, 0xf2, 0x00, 0x16, 0x3b, 0x86, 0x01, 0x01,
2440x28, 0xcd, 0x06, 0x31, 0x75, 0x73, 0x65, 0x97, 0x00, 0x01, 2440x41, 0x01, 0x13, 0x28, 0xcd, 0x06, 0x31, 0x75, 0x73, 0x65,
2450x45, 0x01, 0x18, 0x29, 0x49, 0x05, 0x08, 0x73, 0x05, 0x00, 2450x97, 0x00, 0x01, 0x45, 0x01, 0x18, 0x29, 0x49, 0x05, 0x08,
2460xb0, 0x00, 0x12, 0x49, 0x9f, 0x06, 0x02, 0x7e, 0x02, 0x01, 2460x73, 0x05, 0x00, 0xb0, 0x00, 0x12, 0x49, 0x9f, 0x06, 0x02,
2470x4c, 0x07, 0x01, 0x28, 0x00, 0x15, 0x27, 0x2f, 0x09, 0x2a, 2470x7e, 0x02, 0x01, 0x4c, 0x07, 0x01, 0x28, 0x00, 0x15, 0x27,
2480x6e, 0x64, 0xa7, 0x05, 0x00, 0xde, 0x01, 0x21, 0x6e, 0x6f, 2480x2f, 0x09, 0x2a, 0x6e, 0x64, 0xa7, 0x05, 0x00, 0xde, 0x01,
2490x8d, 0x00, 0x00, 0x7a, 0x0e, 0x0c, 0xaf, 0x05, 0x00, 0x46, 2490x21, 0x6e, 0x6f, 0x8d, 0x00, 0x00, 0x7a, 0x0e, 0x0c, 0xaf,
2500x08, 0x02, 0x53, 0x09, 0xf2, 0x02, 0x68, 0x65, 0x00, 0x6d, 2500x05, 0x00, 0x46, 0x08, 0x02, 0x53, 0x09, 0xf2, 0x02, 0x68,
2510x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00, 0x69, 0x6e, 0x66, 2510x65, 0x00, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00,
2520x6f, 0x72, 0x6d, 0x5f, 0x01, 0x92, 0x6e, 0x65, 0x63, 0x65, 2520x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x01, 0x92, 0x6e,
2530x73, 0x73, 0x61, 0x72, 0x79, 0xe1, 0x06, 0x32, 0x6d, 0x6f, 2530x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0xe1, 0x06,
2540x6e, 0x30, 0x07, 0x02, 0xcc, 0x04, 0x20, 0x74, 0x6f, 0x3d, 2540x32, 0x6d, 0x6f, 0x6e, 0x30, 0x07, 0x02, 0xcc, 0x04, 0x20,
2550x00, 0x31, 0x2c, 0x00, 0x73, 0x08, 0x00, 0x01, 0x43, 0x09, 2550x74, 0x6f, 0x3d, 0x00, 0x31, 0x2c, 0x00, 0x73, 0x08, 0x00,
2560x01, 0x00, 0x0a, 0x00, 0xce, 0x02, 0x13, 0x2e, 0xad, 0x00, 2560x01, 0x43, 0x09, 0x01, 0x00, 0x0a, 0x00, 0xce, 0x02, 0x13,
2570x12, 0x72, 0x5e, 0x05, 0x06, 0xf1, 0x06, 0x06, 0x93, 0x00, 2570x2e, 0xad, 0x00, 0x12, 0x72, 0x5e, 0x05, 0x06, 0xf1, 0x06,
2580x73, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0xd5, 0x07, 2580x06, 0x93, 0x00, 0x73, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65,
2590x05, 0x6e, 0x09, 0x51, 0x70, 0x61, 0x74, 0x68, 0x73, 0x51, 2590x6e, 0xd5, 0x07, 0x05, 0x6e, 0x09, 0x51, 0x70, 0x61, 0x74,
2600x00, 0x72, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x8f, 2600x68, 0x73, 0x51, 0x00, 0x72, 0x61, 0x6c, 0x72, 0x65, 0x61,
2610x07, 0x61, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x2c, 0x6a, 0x04, 2610x64, 0x79, 0x8f, 0x07, 0x61, 0x61, 0x62, 0x6f, 0x75, 0x74,
2620x05, 0x27, 0x00, 0x05, 0xbe, 0x04, 0x02, 0xb6, 0x03, 0x01, 2620x2c, 0x6a, 0x04, 0x05, 0x27, 0x00, 0x05, 0xbe, 0x04, 0x02,
2630x6c, 0x02, 0x30, 0x69, 0x6e, 0x64, 0x42, 0x03, 0x03, 0x3b, 2630xb6, 0x03, 0x01, 0x6c, 0x02, 0x30, 0x69, 0x6e, 0x64, 0x42,
2640x08, 0x20, 0x69, 0x74, 0x7b, 0x06, 0x32, 0x76, 0x65, 0x73, 2640x03, 0x03, 0x3b, 0x08, 0x20, 0x69, 0x74, 0x7b, 0x06, 0x32,
2650xb2, 0x05, 0x46, 0x72, 0x6f, 0x6e, 0x67, 0x9a, 0x00, 0x08, 2650x76, 0x65, 0x73, 0xb2, 0x05, 0x46, 0x72, 0x6f, 0x6e, 0x67,
2660x86, 0x07, 0x41, 0x74, 0x63, 0x68, 0x00, 0x3a, 0x0c, 0x02, 2660x9a, 0x00, 0x08, 0x86, 0x07, 0x41, 0x74, 0x63, 0x68, 0x00,
2670x79, 0x04, 0x20, 0x73, 0x74, 0xa7, 0x01, 0x07, 0x90, 0x00, 2670x3a, 0x0c, 0x02, 0x79, 0x04, 0x20, 0x73, 0x74, 0xa7, 0x01,
2680x22, 0x62, 0x75, 0xef, 0x0b, 0x03, 0xef, 0x05, 0x00, 0x49, 2680x07, 0x90, 0x00, 0x22, 0x62, 0x75, 0xef, 0x0b, 0x03, 0xef,
2690x00, 0x02, 0x8d, 0x00, 0x01, 0x94, 0x02, 0x0d, 0x91, 0x00, 2690x05, 0x00, 0x49, 0x00, 0x02, 0x8d, 0x00, 0x01, 0x94, 0x02,
2700x60, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x24, 0x09, 0x52, 2700x0d, 0x91, 0x00, 0x60, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c,
2710x77, 0x72, 0x69, 0x74, 0x74, 0xff, 0x0f, 0x43, 0x72, 0x65, 2710x24, 0x09, 0x52, 0x77, 0x72, 0x69, 0x74, 0x74, 0xff, 0x0f,
2720x64, 0x29, 0xd9, 0x0c, 0x2f, 0x69, 0x73, 0x05, 0x01, 0x02, 2720x43, 0x72, 0x65, 0x64, 0x29, 0xd9, 0x0c, 0x2f, 0x69, 0x73,
2730x01, 0x97, 0x00, 0x05, 0x25, 0x05, 0x0d, 0xf9, 0x01, 0x51, 2730x05, 0x01, 0x02, 0x01, 0x97, 0x00, 0x05, 0x25, 0x05, 0x0d,
2740x64, 0x65, 0x63, 0x69, 0x64, 0x6d, 0x08, 0x10, 0x67, 0xd9, 2740xf9, 0x01, 0x51, 0x64, 0x65, 0x63, 0x69, 0x64, 0x6d, 0x08,
2750x09, 0x21, 0x75, 0x70, 0xf8, 0x06, 0x20, 0x6c, 0x65, 0xac, 2750x10, 0x67, 0xd9, 0x09, 0x21, 0x75, 0x70, 0xf8, 0x06, 0x20,
2760x05, 0x15, 0x2c, 0x84, 0x01, 0x20, 0x73, 0x65, 0x62, 0x06, 2760x6c, 0x65, 0xac, 0x05, 0x15, 0x2c, 0x84, 0x01, 0x20, 0x73,
2770x42, 0x00, 0x53, 0x6f, 0x6c, 0x55, 0x10, 0x02, 0x8c, 0x00, 2770x65, 0x62, 0x06, 0x42, 0x00, 0x53, 0x6f, 0x6c, 0x55, 0x10,
2780x02, 0x7d, 0x04, 0x5b, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x93, 2780x02, 0x8c, 0x00, 0x02, 0x7d, 0x04, 0x5b, 0x63, 0x74, 0x75,
2790x01, 0x42, 0x2e, 0x00, 0x41, 0x74, 0xd1, 0x01, 0x01, 0x6d, 2790x61, 0x6c, 0x93, 0x01, 0x42, 0x2e, 0x00, 0x41, 0x74, 0xd1,
2800x05, 0x14, 0x2c, 0x2e, 0x02, 0x33, 0x6c, 0x79, 0x2d, 0x9d, 2800x01, 0x01, 0x6d, 0x05, 0x14, 0x2c, 0x2e, 0x02, 0x33, 0x6c,
2810x0b, 0x02, 0x0a, 0x03, 0x0e, 0x2b, 0x06, 0x60, 0x61, 0x73, 2810x79, 0x2d, 0x9d, 0x0b, 0x02, 0x0a, 0x03, 0x0e, 0x2b, 0x06,
2820x00, 0x66, 0x69, 0x6c, 0xe7, 0x00, 0x08, 0x3d, 0x05, 0x00, 2820x60, 0x61, 0x73, 0x00, 0x66, 0x69, 0x6c, 0xe7, 0x00, 0x08,
2830x20, 0x09, 0x1f, 0x6e, 0x44, 0x00, 0x04, 0x0f, 0x32, 0x00, 2830x3d, 0x05, 0x00, 0x20, 0x09, 0x1f, 0x6e, 0x44, 0x00, 0x04,
2840x04, 0x02, 0xfe, 0x00, 0x00, 0x73, 0x0d, 0x30, 0x63, 0x72, 2840x0f, 0x32, 0x00, 0x04, 0x02, 0xfe, 0x00, 0x00, 0x73, 0x0d,
2850x6f, 0xf5, 0x00, 0x02, 0xd3, 0x03, 0x22, 0x6d, 0x69, 0xd2, 2850x30, 0x63, 0x72, 0x6f, 0xf5, 0x00, 0x02, 0xd3, 0x03, 0x22,
2860x03, 0x0c, 0x3c, 0x00, 0x01, 0x29, 0x00, 0x02, 0x3a, 0x00, 2860x6d, 0x69, 0xd2, 0x03, 0x0c, 0x3c, 0x00, 0x01, 0x29, 0x00,
2870x40, 0x2e, 0x00, 0x49, 0x6e, 0x1e, 0x04, 0x01, 0xcb, 0x00, 2870x02, 0x3a, 0x00, 0x40, 0x2e, 0x00, 0x49, 0x6e, 0x1e, 0x04,
2880x00, 0x9b, 0x03, 0x06, 0x1c, 0x00, 0x01, 0xb9, 0x05, 0x13, 2880x01, 0xcb, 0x00, 0x00, 0x9b, 0x03, 0x06, 0x1c, 0x00, 0x01,
2890x73, 0x3e, 0x0a, 0x00, 0x48, 0x04, 0x02, 0xcb, 0x08, 0x15, 2890xb9, 0x05, 0x13, 0x73, 0x3e, 0x0a, 0x00, 0x48, 0x04, 0x02,
2900x64, 0x58, 0x02, 0x00, 0x65, 0x04, 0x1f, 0x64, 0x91, 0x01, 2900xcb, 0x08, 0x15, 0x64, 0x58, 0x02, 0x00, 0x65, 0x04, 0x1f,
2910x10, 0x01, 0x2f, 0x01, 0x03, 0x81, 0x0a, 0x50, 0x28, 0x6a, 2910x64, 0x91, 0x01, 0x10, 0x01, 0x2f, 0x01, 0x03, 0x81, 0x0a,
2920x75, 0x73, 0x74, 0x8b, 0x00, 0x05, 0x1d, 0x09, 0x01, 0x78, 2920x50, 0x28, 0x6a, 0x75, 0x73, 0x74, 0x8b, 0x00, 0x05, 0x1d,
2930x04, 0x0f, 0x8e, 0x03, 0x00, 0x12, 0x29, 0xc4, 0x00, 0x01, 2930x09, 0x01, 0x78, 0x04, 0x0f, 0x8e, 0x03, 0x00, 0x12, 0x29,
2940x48, 0x0e, 0x3e, 0x65, 0x78, 0x74, 0x8e, 0x00, 0x05, 0x8e, 2940xc4, 0x00, 0x01, 0x48, 0x0e, 0x3e, 0x65, 0x78, 0x74, 0x8e,
2950x08, 0x01, 0x63, 0x04, 0x02, 0x8d, 0x00, 0x00, 0xe8, 0x10, 2950x00, 0x05, 0x8e, 0x08, 0x01, 0x63, 0x04, 0x02, 0x8d, 0x00,
2960x41, 0x72, 0x64, 0x65, 0x72, 0x70, 0x03, 0x02, 0x86, 0x02, 2960x00, 0xe8, 0x10, 0x41, 0x72, 0x64, 0x65, 0x72, 0x70, 0x03,
2970x3e, 0x75, 0x69, 0x73, 0x88, 0x00, 0x05, 0x88, 0x11, 0x04, 2970x02, 0x86, 0x02, 0x3e, 0x75, 0x69, 0x73, 0x88, 0x00, 0x05,
2980xad, 0x01, 0x00, 0x8c, 0x02, 0x00, 0x2a, 0x02, 0x35, 0x28, 2980x88, 0x11, 0x04, 0xad, 0x01, 0x00, 0x8c, 0x02, 0x00, 0x2a,
2990x41, 0x6c, 0xeb, 0x01, 0x01, 0xdb, 0x02, 0x72, 0x64, 0x65, 2990x02, 0x35, 0x28, 0x41, 0x6c, 0xeb, 0x01, 0x01, 0xdb, 0x02,
3000x73, 0x63, 0x72, 0x69, 0x62, 0x5c, 0x00, 0x13, 0x73, 0x7b, 3000x72, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x5c, 0x00,
3010x08, 0x31, 0x32, 0x2e, 0x31, 0xce, 0x02, 0x01, 0xa9, 0x05, 3010x13, 0x73, 0x7b, 0x08, 0x31, 0x32, 0x2e, 0x31, 0xce, 0x02,
3020x51, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x49, 0x07, 0x12, 0x29, 3020x01, 0xa9, 0x05, 0x51, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x49,
3030x83, 0x08, 0x17, 0x32, 0x83, 0x08, 0x50, 0x70, 0x61, 0x72, 3030x07, 0x12, 0x29, 0x83, 0x08, 0x17, 0x32, 0x83, 0x08, 0x50,
3040x61, 0x6d, 0xda, 0x0a, 0x02, 0x85, 0x08, 0x01, 0xe1, 0x07, 3040x70, 0x61, 0x72, 0x61, 0x6d, 0xda, 0x0a, 0x02, 0x85, 0x08,
3050x06, 0x14, 0x00, 0x02, 0x45, 0x00, 0x04, 0x40, 0x00, 0x06, 3050x01, 0xe1, 0x07, 0x06, 0x14, 0x00, 0x02, 0x45, 0x00, 0x04,
3060x95, 0x00, 0xe2, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 3060x40, 0x00, 0x06, 0x95, 0x00, 0xe2, 0x60, 0x43, 0x75, 0x73,
3070x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x8c, 0x07, 0x12, 3070x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70,
3080x6e, 0x1a, 0x00, 0xa1, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 3080x8c, 0x07, 0x12, 0x6e, 0x1a, 0x00, 0xa1, 0x54, 0x79, 0x70,
3090x6d, 0x65, 0x6e, 0x75, 0x82, 0x05, 0x81, 0x69, 0x64, 0x74, 3090x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x82, 0x05, 0x81,
3100x68, 0x2c, 0x00, 0x48, 0x65, 0x5b, 0x06, 0x51, 0x00, 0x00, 3100x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x5b, 0x06,
3110x53, 0x69, 0x7a, 0x4a, 0x08, 0x00, 0x30, 0x05, 0x01, 0xb1, 3110x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x4a, 0x08, 0x00, 0x30,
3120x00, 0x02, 0x4c, 0x08, 0x01, 0xb4, 0x08, 0x21, 0x72, 0x65, 3120x05, 0x01, 0xb1, 0x00, 0x02, 0x4c, 0x08, 0x01, 0xb4, 0x08,
3130x6f, 0x00, 0x32, 0x32, 0x00, 0x78, 0x37, 0x00, 0x24, 0x00, 3130x21, 0x72, 0x65, 0x6f, 0x00, 0x32, 0x32, 0x00, 0x78, 0x37,
3140x78, 0x38, 0x00, 0x04, 0x0b, 0x13, 0x21, 0x65, 0x72, 0x39, 3140x00, 0x24, 0x00, 0x78, 0x38, 0x00, 0x04, 0x0b, 0x13, 0x21,
3150x00, 0x51, 0x2c, 0x00, 0x74, 0x77, 0x6f, 0x0e, 0x00, 0x32, 3150x65, 0x72, 0x39, 0x00, 0x51, 0x2c, 0x00, 0x74, 0x77, 0x6f,
3160x72, 0x6f, 0x77, 0x29, 0x0f, 0x03, 0x10, 0x00, 0x06, 0xfb, 3160x0e, 0x00, 0x32, 0x72, 0x6f, 0x77, 0x29, 0x0f, 0x03, 0x10,
3170x05, 0x72, 0x4e, 0x6f, 0x2e, 0x20, 0x6f, 0x66, 0x20, 0x5a, 3170x00, 0x06, 0xfb, 0x05, 0x72, 0x4e, 0x6f, 0x2e, 0x20, 0x6f,
3180x02, 0x3b, 0x00, 0x00, 0x4e, 0xf6, 0x05, 0x23, 0x74, 0x6f, 3180x66, 0x20, 0x5a, 0x02, 0x3b, 0x00, 0x00, 0x4e, 0xf6, 0x05,
3190x09, 0x0f, 0x27, 0x69, 0x6e, 0xb2, 0x06, 0x01, 0x1d, 0x12, 3190x23, 0x74, 0x6f, 0x09, 0x0f, 0x27, 0x69, 0x6e, 0xb2, 0x06,
3200x00, 0x63, 0x03, 0x10, 0x62, 0x9d, 0x06, 0x00, 0x9b, 0x02, 3200x01, 0x1d, 0x12, 0x00, 0x63, 0x03, 0x10, 0x62, 0x9d, 0x06,
3210x14, 0x6c, 0x31, 0x06, 0x11, 0x2c, 0xa6, 0x06, 0x00, 0x0d, 3210x00, 0x9b, 0x02, 0x14, 0x6c, 0x31, 0x06, 0x11, 0x2c, 0xa6,
3220x12, 0x20, 0x67, 0x65, 0x4a, 0x0d, 0x00, 0x24, 0x01, 0x15, 3220x06, 0x00, 0x0d, 0x12, 0x20, 0x67, 0x65, 0x4a, 0x0d, 0x00,
3230x74, 0x38, 0x0a, 0xf3, 0x04, 0x00, 0x68, 0x79, 0x70, 0x68, 3230x24, 0x01, 0x15, 0x74, 0x38, 0x0a, 0xf3, 0x04, 0x00, 0x68,
3240x65, 0x6e, 0x2c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0x00, 0x60, 3240x79, 0x70, 0x68, 0x65, 0x6e, 0x2c, 0x00, 0x6c, 0x69, 0x6b,
3250x32, 0x2d, 0x36, 0x27, 0x1f, 0x02, 0x05, 0x36, 0x0c, 0x02, 3250x65, 0x00, 0x60, 0x32, 0x2d, 0x36, 0x27, 0x1f, 0x02, 0x05,
3260x43, 0x02, 0x1f, 0x6e, 0x88, 0x00, 0x05, 0x1a, 0x6f, 0x88, 3260x36, 0x0c, 0x02, 0x43, 0x02, 0x1f, 0x6e, 0x88, 0x00, 0x05,
3270x00, 0x01, 0x96, 0x10, 0x00, 0xd7, 0x03, 0x05, 0xfc, 0x05, 3270x1a, 0x6f, 0x88, 0x00, 0x01, 0x96, 0x10, 0x00, 0xd7, 0x03,
3280x72, 0x69, 0x73, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x2b, 0x0f, 3280x05, 0xfc, 0x05, 0x72, 0x69, 0x73, 0x00, 0x6f, 0x6e, 0x6c,
3290x00, 0xee, 0x01, 0x16, 0x66, 0xbb, 0x0b, 0x04, 0x95, 0x08, 3290x79, 0x2b, 0x0f, 0x00, 0xee, 0x01, 0x16, 0x66, 0xbb, 0x0b,
3300x0f, 0xeb, 0x06, 0x0b, 0x33, 0x3b, 0x00, 0x61, 0x45, 0x09, 3300x04, 0x95, 0x08, 0x0f, 0xeb, 0x06, 0x0b, 0x33, 0x3b, 0x00,
3310x11, 0x75, 0x76, 0x03, 0x00, 0x40, 0x0f, 0x31, 0x66, 0x66, 3310x61, 0x45, 0x09, 0x11, 0x75, 0x76, 0x03, 0x00, 0x40, 0x0f,
3320x65, 0x8c, 0x04, 0x03, 0x2b, 0x00, 0x03, 0x0e, 0x08, 0x02, 3320x31, 0x66, 0x66, 0x65, 0x8c, 0x04, 0x03, 0x2b, 0x00, 0x03,
3330xfa, 0x13, 0x29, 0x61, 0x6c, 0x94, 0x0b, 0x02, 0x1a, 0x05, 3330x0e, 0x08, 0x02, 0xfa, 0x13, 0x29, 0x61, 0x6c, 0x94, 0x0b,
3340x60, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x83, 0x00, 0x02, 3340x02, 0x1a, 0x05, 0x60, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
3350xa6, 0x09, 0x26, 0x6c, 0x6c, 0xda, 0x05, 0x62, 0x69, 0x6e, 3350x83, 0x00, 0x02, 0xa6, 0x09, 0x26, 0x6c, 0x6c, 0xda, 0x05,
3360x70, 0x75, 0x74, 0x73, 0x8d, 0x14, 0x21, 0x75, 0x74, 0x0c, 3360x62, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x8d, 0x14, 0x21,
3370x00, 0x70, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x00, 3370x75, 0x74, 0x0c, 0x00, 0x70, 0x6d, 0x61, 0x74, 0x63, 0x68,
3380x2e, 0x00,
338}; 339};
339 340
340const unsigned short help_text_len = 5458; 341const unsigned short help_text_len = 5480;
341const unsigned short help_text_words = 1015; 342const unsigned short help_text_words = 1016;
343const bool help_valid = true;
342const char quick_help_text[] = "Find the hidden balls in the box by bouncing laser beams off them."; 344const char quick_help_text[] = "Find the hidden balls in the box by bouncing laser beams off them.";
diff --git a/apps/plugins/puzzles/help/bridges.c b/apps/plugins/puzzles/help/bridges.c
index be4469b5ac..4d42314e4b 100644
--- a/apps/plugins/puzzles/help/bridges.c
+++ b/apps/plugins/puzzles/help/bridges.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,336 +6,357 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 180, TEXT_CENTER | C_RED }, 9 { 178, TEXT_CENTER | C_RED },
10 { 312, TEXT_UNDERLINE }, 10 { 310, TEXT_UNDERLINE },
11 { 737, TEXT_CENTER | C_RED }, 11 { 763, TEXT_CENTER | C_RED },
12 { 754, TEXT_UNDERLINE }, 12 { 780, TEXT_UNDERLINE },
13 { 755, TEXT_UNDERLINE }, 13 { 781, TEXT_UNDERLINE },
14 { 765, TEXT_UNDERLINE }, 14 { 791, TEXT_UNDERLINE },
15 { 774, TEXT_UNDERLINE }, 15 { 800, TEXT_UNDERLINE },
16 { 813, TEXT_UNDERLINE }, 16 { 839, TEXT_UNDERLINE },
17 { 845, TEXT_UNDERLINE }, 17 { 871, TEXT_UNDERLINE },
18 { 880, TEXT_UNDERLINE }, 18 { 906, TEXT_UNDERLINE },
19 { 991, TEXT_CENTER | C_RED },
19 LAST_STYLE_ITEM 20 LAST_STYLE_ITEM
20}; 21};
21 22
22/* orig 5230 comp 3126 ratio 0.597706 level 10 saved 2104 */ 23/* orig 5613 comp 3312 ratio 0.590059 level 10 saved 2301 */
23const char help_text[] = { 24const char help_text[] = {
240xf3, 0x4b, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 250xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
250x32, 0x36, 0x3a, 0x20, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 260x32, 0x36, 0x3a, 0x20, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65,
260x73, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 270x73, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf3, 0x37, 0x00, 0x00,
270x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x73, 0x65, 0x74, 0x00, 280x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00,
280x6f, 0x66, 0x00, 0x69, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x73, 290x61, 0x00, 0x73, 0x65, 0x74, 0x00, 0x6f, 0x66, 0x00, 0x69,
290x00, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 300x73, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x00, 0x64, 0x69, 0x73,
300x65, 0x64, 0x00, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x00, 310x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x00, 0x61,
310x74, 0x68, 0x65, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 320x63, 0x72, 0x6f, 0x73, 0x73, 0x00, 0x74, 0x68, 0x65, 0x00,
320x67, 0x00, 0x61, 0x72, 0x65, 0x61, 0x2e, 0x00, 0x45, 0x61, 330x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x72,
330x63, 0x68, 0x32, 0x00, 0xf0, 0x04, 0x00, 0x63, 0x6f, 0x6e, 340x65, 0x61, 0x2e, 0x00, 0x45, 0x61, 0x63, 0x68, 0x32, 0x00,
340x74, 0x61, 0x69, 0x6e, 0x73, 0x00, 0x61, 0x00, 0x6e, 0x75, 350xf0, 0x04, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
350x6d, 0x62, 0x65, 0x72, 0x2e, 0x5e, 0x00, 0xb0, 0x72, 0x00, 360x73, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
360x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x22, 370x2e, 0x5e, 0x00, 0xb0, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00,
370x00, 0x41, 0x6e, 0x65, 0x63, 0x74, 0x48, 0x00, 0x04, 0x67, 380x69, 0x73, 0x00, 0x74, 0x6f, 0x22, 0x00, 0x41, 0x6e, 0x65,
380x00, 0xf2, 0x00, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 390x63, 0x74, 0x48, 0x00, 0x04, 0x67, 0x00, 0xf2, 0x00, 0x74,
390x72, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x62, 0x9a, 0x00, 400x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x00, 0x77, 0x69,
400xf3, 0x0b, 0x2c, 0x00, 0x69, 0x6e, 0x00, 0x73, 0x75, 0x63, 410x74, 0x68, 0x00, 0x62, 0xae, 0x00, 0xf3, 0x0b, 0x2c, 0x00,
410x68, 0x00, 0x61, 0x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 420x69, 0x6e, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x61, 0x00,
420x61, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xbb, 0x00, 430x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74, 0x3a, 0x00,
430xa0, 0x00, 0x72, 0x75, 0x6e, 0x00, 0x68, 0x6f, 0x72, 0x69, 440x00, 0x00, 0x2d, 0x00, 0xcf, 0x00, 0xa0, 0x00, 0x72, 0x75,
440x7a, 0x75, 0x00, 0xd0, 0x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 450x6e, 0x00, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x75, 0x00, 0xd0,
450x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00, 0x11, 460x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 0x00, 0x76, 0x65, 0x72,
460x2e, 0x2c, 0x00, 0x33, 0x54, 0x68, 0x65, 0x8a, 0x00, 0x00, 470x74, 0x69, 0x63, 0x0e, 0x00, 0x11, 0x2e, 0x2c, 0x00, 0x33,
470xd8, 0x00, 0x13, 0x62, 0x3a, 0x00, 0x81, 0x74, 0x65, 0x72, 480x54, 0x68, 0x65, 0x8a, 0x00, 0x00, 0xd8, 0x00, 0x13, 0x62,
480x6d, 0x69, 0x6e, 0x61, 0x74, 0xc5, 0x00, 0x54, 0x74, 0x00, 490x3a, 0x00, 0x81, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61,
490x61, 0x6e, 0x79, 0xc1, 0x00, 0x80, 0x69, 0x73, 0x00, 0x65, 500x74, 0xc5, 0x00, 0x54, 0x74, 0x00, 0x61, 0x6e, 0x79, 0xc1,
500x71, 0x75, 0x61, 0x6c, 0xab, 0x00, 0x16, 0x74, 0x3c, 0x00, 510x00, 0x80, 0x69, 0x73, 0x00, 0x65, 0x71, 0x75, 0x61, 0x6c,
510x70, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x93, 0x00, 520xab, 0x00, 0x16, 0x74, 0x3c, 0x00, 0x70, 0x77, 0x72, 0x69,
520x00, 0x88, 0x00, 0x03, 0x2e, 0x00, 0x03, 0x63, 0x00, 0x25, 530x74, 0x74, 0x65, 0x6e, 0x93, 0x00, 0x00, 0x88, 0x00, 0x03,
530x77, 0x6f, 0x59, 0x00, 0x31, 0x6d, 0x61, 0x79, 0x97, 0x00, 540x2e, 0x00, 0x03, 0x63, 0x00, 0x25, 0x77, 0x6f, 0x59, 0x00,
540xf1, 0x04, 0x69, 0x6e, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6c, 550x31, 0x6d, 0x61, 0x79, 0x97, 0x00, 0xf1, 0x04, 0x69, 0x6e,
550x6c, 0x65, 0x6c, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 560x00, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x00,
560x6e, 0x4f, 0x00, 0x84, 0x73, 0x61, 0x6d, 0x65, 0x00, 0x74, 570x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x4f, 0x00, 0x84,
570x77, 0x6f, 0xfb, 0x00, 0xd0, 0x2c, 0x00, 0x62, 0x75, 0x74, 580x73, 0x61, 0x6d, 0x65, 0x00, 0x74, 0x77, 0x6f, 0xfb, 0x00,
580x00, 0x6e, 0x6f, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x5b, 0x00, 590xd0, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x6e, 0x6f, 0x00,
590x11, 0x6e, 0x1e, 0x00, 0x00, 0x47, 0x00, 0x52, 0x64, 0x6f, 600x6d, 0x6f, 0x72, 0x65, 0x5b, 0x00, 0x11, 0x6e, 0x1e, 0x00,
600x00, 0x73, 0x6f, 0x62, 0x00, 0x14, 0x4e, 0x61, 0x00, 0x11, 610x00, 0x47, 0x00, 0x52, 0x64, 0x6f, 0x00, 0x73, 0x6f, 0x62,
610x00, 0x84, 0x01, 0x61, 0x65, 0x73, 0x00, 0x61, 0x6e, 0x6f, 620x00, 0x14, 0x4e, 0x61, 0x00, 0x11, 0x00, 0x84, 0x01, 0x61,
620x31, 0x01, 0x02, 0x17, 0x00, 0x02, 0x26, 0x00, 0x39, 0x41, 630x65, 0x73, 0x00, 0x61, 0x6e, 0x6f, 0x31, 0x01, 0x02, 0x17,
630x6c, 0x6c, 0x56, 0x01, 0x34, 0x61, 0x72, 0x65, 0x6e, 0x01, 640x00, 0x02, 0x26, 0x00, 0x39, 0x41, 0x6c, 0x6c, 0x56, 0x01,
640x25, 0x65, 0x64, 0x64, 0x01, 0x00, 0x2c, 0x00, 0x51, 0x54, 650x34, 0x61, 0x72, 0x65, 0x6e, 0x01, 0x25, 0x65, 0x64, 0x64,
650x68, 0x65, 0x72, 0x65, 0x20, 0x00, 0x31, 0x73, 0x6f, 0x6d, 660x01, 0x00, 0x2c, 0x00, 0x51, 0x54, 0x68, 0x65, 0x72, 0x65,
660x25, 0x00, 0xf0, 0x00, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 670x20, 0x00, 0x31, 0x73, 0x6f, 0x6d, 0x25, 0x00, 0xf0, 0x00,
670x62, 0x6c, 0x65, 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x1b, 680x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x00,
680x01, 0x60, 0x76, 0x65, 0x00, 0x6d, 0x6f, 0x64, 0x88, 0x01, 690x61, 0x6c, 0x74, 0x65, 0x72, 0x1b, 0x01, 0x60, 0x76, 0x65,
690x30, 0x77, 0x68, 0x69, 0xe2, 0x01, 0xc0, 0x6e, 0x76, 0x6f, 700x00, 0x6d, 0x6f, 0x64, 0x88, 0x01, 0x30, 0x77, 0x68, 0x69,
700x6c, 0x76, 0x65, 0x00, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x39, 710xe2, 0x01, 0xc0, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x00,
710x01, 0x01, 0x0a, 0x02, 0x03, 0xe5, 0x00, 0x13, 0x2d, 0xa0, 720x63, 0x68, 0x61, 0x6e, 0x67, 0x39, 0x01, 0x01, 0x0a, 0x02,
720x00, 0x50, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x3c, 0x01, 0x00, 730x03, 0xe5, 0x00, 0x13, 0x2d, 0xa0, 0x00, 0x50, 0x6c, 0x69,
730x59, 0x00, 0x20, 0x74, 0x68, 0x27, 0x00, 0x02, 0xa9, 0x00, 740x6d, 0x69, 0x74, 0x3c, 0x01, 0x00, 0x59, 0x00, 0x20, 0x74,
740x01, 0xdb, 0x00, 0x31, 0x32, 0x2c, 0x00, 0x64, 0x01, 0x74, 750x68, 0x27, 0x00, 0x02, 0xa9, 0x00, 0x01, 0xdb, 0x00, 0x31,
750x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x45, 0x00, 0xa0, 760x32, 0x2c, 0x00, 0x64, 0x01, 0x74, 0x6e, 0x74, 0x72, 0x6f,
760x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 770x64, 0x75, 0x63, 0x45, 0x00, 0xa0, 0x61, 0x64, 0x64, 0x69,
770x8b, 0x00, 0x72, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 780x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x8b, 0x00, 0x72, 0x73,
780x68, 0x01, 0xb8, 0x6e, 0x6f, 0x00, 0x73, 0x65, 0x71, 0x75, 790x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x68, 0x01, 0xb8, 0x6e,
790x65, 0x6e, 0x63, 0x65, 0xc0, 0x01, 0x00, 0x20, 0x01, 0xf4, 800x6f, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
800x05, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x61, 0x00, 0x6c, 0x6f, 810xc0, 0x01, 0x00, 0x20, 0x01, 0xf4, 0x05, 0x66, 0x6f, 0x72,
810x6f, 0x70, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x6f, 0x6e, 820x6d, 0x00, 0x61, 0x00, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x66,
820x65, 0xc6, 0x01, 0x44, 0x62, 0x61, 0x63, 0x6b, 0xc2, 0x01, 830x72, 0x6f, 0x6d, 0x00, 0x6f, 0x6e, 0x65, 0xc6, 0x01, 0x44,
830x01, 0x73, 0x01, 0x04, 0xb0, 0x01, 0x00, 0x0f, 0x02, 0x91, 840x62, 0x61, 0x63, 0x6b, 0xc2, 0x01, 0x01, 0x73, 0x01, 0x04,
840x72, 0x75, 0x6c, 0x65, 0x73, 0x00, 0x73, 0x74, 0x61, 0xd6, 850xb0, 0x01, 0x00, 0x0f, 0x02, 0x91, 0x72, 0x75, 0x6c, 0x65,
850x02, 0x32, 0x62, 0x6f, 0x76, 0x0b, 0x01, 0x00, 0x2c, 0x00, 860x73, 0x00, 0x73, 0x74, 0x61, 0xd6, 0x02, 0x32, 0x62, 0x6f,
860x70, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x00, 870x76, 0x0b, 0x01, 0x00, 0x2c, 0x00, 0x70, 0x64, 0x65, 0x66,
870x10, 0x73, 0x29, 0x01, 0x60, 0x43, 0x72, 0x65, 0x64, 0x69, 880x61, 0x75, 0x6c, 0x74, 0x4b, 0x00, 0x10, 0x73, 0x29, 0x01,
880x74, 0x6b, 0x00, 0xf1, 0x01, 0x00, 0x74, 0x68, 0x69, 0x73, 890x60, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x6b, 0x00, 0xf1,
890x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 900x01, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a,
900x65, 0xcc, 0x02, 0xb0, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 910x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0xcc, 0x02, 0xb0,
910x00, 0x5b, 0x31, 0x32, 0x5d, 0x2e, 0x00, 0x04, 0x98, 0x02, 920x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x31, 0x32,
920x31, 0x77, 0x61, 0x73, 0x09, 0x03, 0x04, 0x3a, 0x03, 0x01, 930x5d, 0x2e, 0x00, 0x04, 0x98, 0x02, 0x31, 0x77, 0x61, 0x73,
930x89, 0x00, 0x10, 0x69, 0x14, 0x00, 0x40, 0x6c, 0x6c, 0x65, 940x09, 0x03, 0x04, 0x3a, 0x03, 0x01, 0x89, 0x00, 0x10, 0x69,
940x63, 0xea, 0x00, 0xf1, 0x00, 0x00, 0x62, 0x79, 0x00, 0x4a, 950x14, 0x00, 0x40, 0x6c, 0x6c, 0x65, 0x63, 0xea, 0x00, 0xf1,
950x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 960x00, 0x00, 0x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73,
960xac, 0x02, 0x00, 0x46, 0x00, 0xd1, 0x00, 0x68, 0x74, 0x74, 970x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0xac, 0x02, 0x00, 0x46,
970x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x5d, 980x00, 0xe1, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
980x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 990x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x5e, 0x00, 0xa2, 0x2e,
990x6e, 0x2f, 0x7c, 0x00, 0xf1, 0x0c, 0x73, 0x2f, 0x68, 0x61, 1000x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0x7d,
1000x73, 0x68, 0x69, 0x77, 0x6f, 0x6b, 0x61, 0x6b, 0x65, 0x72, 1010x00, 0xf5, 0x08, 0x73, 0x2f, 0x48, 0x61, 0x73, 0x68, 0x69,
1010x6f, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x28, 0x62, 0x65, 1020x77, 0x6f, 0x6b, 0x61, 0x6b, 0x65, 0x72, 0x6f, 0x2f, 0x00,
1020x77, 0x61, 0x72, 0x22, 0x01, 0xd5, 0x46, 0x6c, 0x61, 0x73, 1030x00, 0x00, 0x32, 0x36, 0x2e, 0x31, 0xe4, 0x03, 0x01, 0x79,
1030x68, 0x29, 0x00, 0x00, 0x00, 0x32, 0x36, 0x2e, 0x31, 0xe5, 1040x00, 0x40, 0x6f, 0x6c, 0x73, 0x20, 0xe8, 0x01, 0x10, 0x6f,
1040x03, 0x01, 0x8e, 0x00, 0x21, 0x6f, 0x6c, 0xee, 0x03, 0x20, 1050xab, 0x03, 0x10, 0x63, 0xd9, 0x03, 0x03, 0xa0, 0x01, 0x05,
1050x54, 0x6f, 0xc0, 0x03, 0x10, 0x63, 0xee, 0x03, 0x03, 0xb5, 1060x8c, 0x02, 0x08, 0x83, 0x02, 0x30, 0x63, 0x6c, 0x69, 0x2f,
1060x01, 0x05, 0xa1, 0x02, 0x08, 0x98, 0x02, 0x30, 0x63, 0x6c, 1070x01, 0x10, 0x68, 0xf1, 0x01, 0xb8, 0x75, 0x73, 0x65, 0x00,
1070x69, 0x44, 0x01, 0x10, 0x68, 0x06, 0x02, 0xb8, 0x75, 0x73, 1080x64, 0x6f, 0x77, 0x6e, 0x00, 0x6f, 0x6e, 0x51, 0x01, 0x00,
1080x65, 0x00, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x6f, 0x6e, 0x66, 1090x04, 0x00, 0x51, 0x64, 0x72, 0x61, 0x67, 0x00, 0xde, 0x01,
1090x01, 0x00, 0x04, 0x00, 0x51, 0x64, 0x72, 0x61, 0x67, 0x00, 1100x42, 0x77, 0x61, 0x72, 0x64, 0x0a, 0x04, 0x01, 0xdd, 0x01,
1100xf3, 0x01, 0x42, 0x77, 0x61, 0x72, 0x64, 0x1f, 0x04, 0x01, 1110x01, 0xe4, 0x03, 0x00, 0xb0, 0x02, 0x62, 0x6e, 0x6f, 0x74,
1110xf2, 0x01, 0x01, 0xf9, 0x03, 0x00, 0xc5, 0x02, 0x62, 0x6e, 1120x00, 0x6e, 0x65, 0xf2, 0x00, 0x01, 0x2e, 0x00, 0x13, 0x61,
1120x6f, 0x74, 0x00, 0x6e, 0x65, 0x07, 0x01, 0x01, 0x2e, 0x00, 1130x93, 0x02, 0x01, 0xc0, 0x03, 0x16, 0x6f, 0x32, 0x00, 0x03,
1130x13, 0x61, 0xa8, 0x02, 0x01, 0xd5, 0x03, 0x16, 0x6f, 0x32, 1140x57, 0x00, 0xa5, 0x3b, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6f,
1140x00, 0x03, 0x57, 0x00, 0xa5, 0x3b, 0x00, 0x79, 0x6f, 0x75, 1150x6e, 0x6c, 0x79, 0x37, 0x00, 0x10, 0x6d, 0x8f, 0x01, 0x06,
1150x00, 0x6f, 0x6e, 0x6c, 0x79, 0x37, 0x00, 0x10, 0x6d, 0xa4, 1160x8b, 0x00, 0xa3, 0x66, 0x61, 0x72, 0x00, 0x65, 0x6e, 0x6f,
1160x01, 0x06, 0x8b, 0x00, 0xa3, 0x66, 0x61, 0x72, 0x00, 0x65, 1170x75, 0x67, 0x68, 0x85, 0x01, 0x10, 0x65, 0x3e, 0x02, 0x54,
1170x6e, 0x6f, 0x75, 0x67, 0x68, 0x9a, 0x01, 0x10, 0x65, 0x53, 1180x65, 0x6e, 0x64, 0x65, 0x64, 0xd3, 0x00, 0x33, 0x64, 0x69,
1180x02, 0x54, 0x65, 0x6e, 0x64, 0x65, 0x64, 0xd3, 0x00, 0x33, 1190x72, 0x55, 0x01, 0xf1, 0x07, 0x74, 0x6f, 0x00, 0x62, 0x65,
1190x64, 0x69, 0x72, 0x6a, 0x01, 0xf1, 0x07, 0x74, 0x6f, 0x00, 1200x00, 0x75, 0x6e, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f,
1200x62, 0x65, 0x00, 0x75, 0x6e, 0x61, 0x6d, 0x62, 0x69, 0x67, 1210x75, 0x73, 0x2e, 0x00, 0x28, 0x53, 0x6f, 0x64, 0x00, 0x87,
1210x75, 0x6f, 0x75, 0x73, 0x2e, 0x00, 0x28, 0x53, 0x6f, 0x64, 1220x63, 0x61, 0x6e, 0x00, 0x6b, 0x65, 0x65, 0x70, 0x5b, 0x00,
1220x00, 0x87, 0x63, 0x61, 0x6e, 0x00, 0x6b, 0x65, 0x65, 0x70, 1230x42, 0x6e, 0x65, 0x61, 0x72, 0x21, 0x02, 0x31, 0x74, 0x61,
1230x5b, 0x00, 0x42, 0x6e, 0x65, 0x61, 0x72, 0x36, 0x02, 0x31, 1240x72, 0x0a, 0x04, 0x07, 0xec, 0x00, 0xb0, 0x63, 0x6f, 0x6e,
1240x74, 0x61, 0x72, 0x1f, 0x04, 0x07, 0xec, 0x00, 0xb0, 0x63, 1250x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x74, 0x6c, 0x7a, 0x04,
1250x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x74, 0x6c, 1260x35, 0x72, 0x6f, 0x77, 0x7c, 0x02, 0x32, 0x6f, 0x75, 0x74,
1260x8f, 0x04, 0x35, 0x72, 0x6f, 0x77, 0x91, 0x02, 0x32, 0x6f, 1270x70, 0x02, 0x20, 0x69, 0x74, 0xe7, 0x03, 0x10, 0x6d, 0x3d,
1270x75, 0x74, 0x85, 0x02, 0x20, 0x69, 0x74, 0xfc, 0x03, 0x10, 1280x04, 0x05, 0x84, 0x00, 0x82, 0x73, 0x2e, 0x29, 0x00, 0x00,
1280x6d, 0x52, 0x04, 0x05, 0x84, 0x00, 0x20, 0x73, 0x2e, 0x91, 1290x00, 0x44, 0x6f, 0xdb, 0x02, 0xd6, 0x69, 0x73, 0x00, 0x61,
1290x01, 0x22, 0x44, 0x6f, 0xf0, 0x02, 0xd6, 0x69, 0x73, 0x00, 1300x67, 0x61, 0x69, 0x6e, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x85,
1300x61, 0x67, 0x61, 0x69, 0x6e, 0x00, 0x77, 0x68, 0x65, 0x6e, 1310x01, 0x00, 0x17, 0x00, 0xf0, 0x04, 0x6c, 0x72, 0x65, 0x61,
1310x85, 0x01, 0x00, 0x17, 0x00, 0xf0, 0x04, 0x6c, 0x72, 0x65, 1320x64, 0x79, 0x00, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
1320x61, 0x64, 0x79, 0x00, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 1330x00, 0x77, 0x69, 0x6c, 0x6c, 0x08, 0x03, 0x05, 0xd9, 0x03,
1330x74, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x1d, 0x03, 0x05, 0xee, 1340x06, 0x3e, 0x04, 0x03, 0xe2, 0x03, 0x45, 0x49, 0x66, 0x00,
1340x03, 0x06, 0x53, 0x04, 0x03, 0xf7, 0x03, 0x45, 0x49, 0x66, 1350x74, 0xb7, 0x03, 0x04, 0x3f, 0x00, 0x22, 0x61, 0x73, 0x80,
1350x00, 0x74, 0xcc, 0x03, 0x04, 0x3f, 0x00, 0x22, 0x61, 0x73, 1360x00, 0x04, 0x9c, 0x00, 0x08, 0x6b, 0x04, 0x07, 0xe3, 0x01,
1360x80, 0x00, 0x04, 0x9c, 0x00, 0x08, 0x80, 0x04, 0x07, 0xe3, 1370x00, 0x28, 0x00, 0x10, 0x70, 0xf3, 0x04, 0x20, 0x74, 0x74,
1370x01, 0x00, 0x28, 0x00, 0x10, 0x70, 0x08, 0x05, 0x20, 0x74, 1380x38, 0x01, 0x11, 0x79, 0x20, 0x00, 0x40, 0x63, 0x75, 0x72,
1380x74, 0x38, 0x01, 0x11, 0x79, 0x20, 0x00, 0x40, 0x63, 0x75, 1390x72, 0x7b, 0x00, 0x34, 0x67, 0x61, 0x6d, 0x14, 0x03, 0x51,
1390x72, 0x72, 0x7b, 0x00, 0x34, 0x67, 0x61, 0x6d, 0x29, 0x03, 1400x28, 0x69, 0x2e, 0x65, 0x2e, 0x39, 0x00, 0x24, 0x62, 0x79,
1400x51, 0x28, 0x69, 0x2e, 0x65, 0x2e, 0x39, 0x00, 0x24, 0x62, 1410x0c, 0x03, 0x26, 0x29, 0x2c, 0x46, 0x03, 0x00, 0xd4, 0x01,
1410x79, 0x21, 0x03, 0x26, 0x29, 0x2c, 0x5b, 0x03, 0x00, 0xd4, 1420x11, 0x67, 0x34, 0x05, 0x02, 0x71, 0x01, 0x01, 0xb6, 0x00,
1420x01, 0x11, 0x67, 0x49, 0x05, 0x02, 0x71, 0x01, 0x01, 0xb6, 1430x21, 0x72, 0x65, 0xb4, 0x01, 0x00, 0xeb, 0x01, 0x11, 0x6f,
1430x00, 0x21, 0x72, 0x65, 0xb4, 0x01, 0x00, 0xeb, 0x01, 0x11, 1440xa4, 0x00, 0x10, 0x6d, 0xce, 0x02, 0x21, 0x49, 0x66, 0x78,
1440x6f, 0xa4, 0x00, 0x10, 0x6d, 0xe3, 0x02, 0x21, 0x49, 0x66, 1450x01, 0x31, 0x77, 0x61, 0x6e, 0x0f, 0x04, 0x60, 0x72, 0x65,
1450x78, 0x01, 0x31, 0x77, 0x61, 0x6e, 0x24, 0x04, 0x60, 0x72, 1460x6d, 0x69, 0x6e, 0x64, 0x13, 0x00, 0x52, 0x72, 0x73, 0x65,
1460x65, 0x6d, 0x69, 0x6e, 0x64, 0x13, 0x00, 0x52, 0x72, 0x73, 1470x6c, 0x66, 0xdd, 0x03, 0x08, 0xa2, 0x00, 0x90, 0x64, 0x65,
1470x65, 0x6c, 0x66, 0xf2, 0x03, 0x08, 0xa2, 0x00, 0x90, 0x64, 1480x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x6c, 0xf9, 0x04, 0x10,
1480x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x6c, 0x0e, 0x05, 1490x20, 0x49, 0x02, 0x03, 0x92, 0x06, 0x0c, 0xb9, 0x02, 0x45,
1490x10, 0x20, 0x49, 0x02, 0x03, 0xa7, 0x06, 0x0c, 0xb9, 0x02, 1500x68, 0x65, 0x6d, 0x2c, 0xd3, 0x01, 0x61, 0x72, 0x69, 0x67,
1500x45, 0x68, 0x65, 0x6d, 0x2c, 0xd3, 0x01, 0x61, 0x72, 0x69, 1510x68, 0x74, 0x2d, 0x6b, 0x02, 0x08, 0x21, 0x00, 0x27, 0x00,
1510x67, 0x68, 0x74, 0x2d, 0x6b, 0x02, 0x08, 0x21, 0x00, 0x27, 1520x69, 0x6e, 0x05, 0x03, 0x7c, 0x02, 0xb3, 0x64, 0x72, 0x61,
1520x00, 0x69, 0x83, 0x05, 0x03, 0x7c, 0x02, 0xb3, 0x64, 0x72, 1530x77, 0x00, 0x61, 0x00, 0x60, 0x6e, 0x6f, 0x6e, 0xad, 0x04,
1530x61, 0x77, 0x00, 0x61, 0x00, 0x60, 0x6e, 0x6f, 0x6e, 0xc2, 1540x87, 0x27, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0xaf,
1540x04, 0x87, 0x27, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 1550x00, 0x00, 0xb2, 0x04, 0x34, 0x6b, 0x00, 0x79, 0x10, 0x07,
1550xaf, 0x00, 0x00, 0xc7, 0x04, 0x34, 0x6b, 0x00, 0x79, 0x25, 1560x00, 0x93, 0x00, 0x42, 0x73, 0x68, 0x65, 0x64, 0x98, 0x06,
1560x07, 0x00, 0x93, 0x00, 0x42, 0x73, 0x68, 0x65, 0x64, 0xad, 1570x24, 0x61, 0x6e, 0x22, 0x02, 0x02, 0x28, 0x01, 0x05, 0x27,
1570x06, 0x24, 0x61, 0x6e, 0x22, 0x02, 0x02, 0x28, 0x01, 0x05, 1580x00, 0x01, 0x64, 0x03, 0x11, 0x64, 0x02, 0x01, 0x35, 0x69,
1580x27, 0x00, 0x01, 0x64, 0x03, 0x11, 0x64, 0x02, 0x01, 0x35, 1590x74, 0x73, 0x8d, 0x01, 0x01, 0x4c, 0x02, 0x13, 0x72, 0x5f,
1590x69, 0x74, 0x73, 0x8d, 0x01, 0x01, 0x4c, 0x02, 0x13, 0x72, 1600x05, 0x24, 0x64, 0x65, 0xd3, 0x04, 0x41, 0x74, 0x68, 0x65,
1600x74, 0x05, 0x24, 0x64, 0x65, 0xe8, 0x04, 0x41, 0x74, 0x68, 1610x79, 0x18, 0x00, 0x03, 0xa5, 0x00, 0x01, 0xc4, 0x00, 0x02,
1610x65, 0x79, 0x18, 0x00, 0x03, 0xa5, 0x00, 0x01, 0xc4, 0x00, 1620x44, 0x00, 0x26, 0x73, 0x29, 0xdb, 0x00, 0x00, 0x9c, 0x00,
1620x02, 0x44, 0x00, 0x26, 0x73, 0x29, 0xdb, 0x00, 0x00, 0x9c, 1630x07, 0xcf, 0x05, 0x00, 0xc3, 0x01, 0x05, 0x8f, 0x00, 0x81,
1630x00, 0x07, 0xe4, 0x05, 0x00, 0xc3, 0x01, 0x05, 0x8f, 0x00, 1640x62, 0x79, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0xb9, 0x03,
1640x81, 0x62, 0x79, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0xb9, 1650x01, 0x64, 0x05, 0x40, 0x6e, 0x00, 0x69, 0x74, 0xde, 0x04,
1650x03, 0x01, 0x79, 0x05, 0x40, 0x6e, 0x00, 0x69, 0x74, 0xf3, 1660x22, 0x69, 0x73, 0x95, 0x01, 0x51, 0x68, 0x69, 0x67, 0x68,
1660x04, 0x22, 0x69, 0x73, 0x95, 0x01, 0x51, 0x68, 0x69, 0x67, 1670x6c, 0x59, 0x00, 0x22, 0x69, 0x74, 0x89, 0x00, 0x03, 0x8a,
1670x68, 0x6c, 0x59, 0x00, 0x22, 0x69, 0x74, 0x89, 0x00, 0x03, 1680x03, 0x04, 0x9d, 0x00, 0x08, 0x19, 0x06, 0x32, 0x00, 0x69,
1680x8a, 0x03, 0x04, 0x9d, 0x00, 0x08, 0x2e, 0x06, 0x32, 0x00, 1690x74, 0x99, 0x05, 0x01, 0xb2, 0x01, 0x20, 0x69, 0x6c, 0xb3,
1690x69, 0x74, 0xae, 0x05, 0x01, 0xb2, 0x01, 0x20, 0x69, 0x6c, 1700x06, 0x00, 0x9a, 0x02, 0x30, 0x76, 0x65, 0x6e, 0x21, 0x00,
1700xc8, 0x06, 0x00, 0x9a, 0x02, 0x30, 0x76, 0x65, 0x6e, 0x21, 1710x01, 0xed, 0x02, 0x31, 0x61, 0x63, 0x63, 0xc4, 0x00, 0x01,
1710x00, 0x01, 0xed, 0x02, 0x31, 0x61, 0x63, 0x63, 0xc4, 0x00, 1720x68, 0x07, 0x52, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x02, 0x08,
1720x01, 0x7d, 0x07, 0x52, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x17, 1730x22, 0x6e, 0x79, 0xf6, 0x01, 0x26, 0x6f, 0x73, 0x5c, 0x00,
1730x08, 0x22, 0x6e, 0x79, 0xf6, 0x01, 0x26, 0x6f, 0x73, 0x5c, 1740xc9, 0x69, 0x6e, 0x00, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65,
1740x00, 0xc9, 0x69, 0x6e, 0x00, 0x66, 0x75, 0x74, 0x75, 0x72, 1750x2e, 0x00, 0x4c, 0xa7, 0x00, 0x02, 0x0d, 0x03, 0x10, 0x6f,
1750x65, 0x2e, 0x00, 0x4c, 0xa7, 0x00, 0x02, 0x0d, 0x03, 0x10, 1760x0b, 0x03, 0x05, 0xa1, 0x00, 0x24, 0x65, 0x64, 0xe2, 0x00,
1760x6f, 0x0b, 0x03, 0x05, 0xa1, 0x00, 0x24, 0x65, 0x64, 0xe2, 1770x01, 0x7a, 0x00, 0x21, 0x75, 0x6e, 0xf9, 0x00, 0x03, 0xb6,
1770x00, 0x01, 0x7a, 0x00, 0x21, 0x75, 0x6e, 0xf9, 0x00, 0x03, 1780x00, 0x40, 0x72, 0x65, 0x73, 0x74, 0x19, 0x07, 0x11, 0x79,
1780xb6, 0x00, 0x40, 0x72, 0x65, 0x73, 0x74, 0x2e, 0x07, 0x11, 1790x44, 0x08, 0x51, 0x62, 0x69, 0x6c, 0x69, 0x74, 0xcd, 0x01,
1790x79, 0x59, 0x08, 0x51, 0x62, 0x69, 0x6c, 0x69, 0x74, 0xcd, 1800x02, 0x81, 0x00, 0x01, 0xf4, 0x00, 0x02, 0xbf, 0x08, 0x00,
1800x01, 0x02, 0x81, 0x00, 0x01, 0xf4, 0x00, 0x02, 0xd4, 0x08, 1810x32, 0x01, 0x50, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0xda, 0x03,
1810x00, 0x32, 0x01, 0x50, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0xda, 1820x03, 0xde, 0x02, 0x85, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65,
1820x03, 0x03, 0xde, 0x02, 0x85, 0x73, 0x6f, 0x72, 0x00, 0x6b, 1830x79, 0x73, 0x57, 0x04, 0x61, 0x61, 0x72, 0x6f, 0x75, 0x6e,
1830x65, 0x79, 0x73, 0x57, 0x04, 0x61, 0x61, 0x72, 0x6f, 0x75, 1840x64, 0x1f, 0x00, 0xf9, 0x01, 0x67, 0x72, 0x69, 0x64, 0x3a,
1840x6e, 0x64, 0x1f, 0x00, 0xf9, 0x01, 0x67, 0x72, 0x69, 0x64, 1850x00, 0x69, 0x66, 0x00, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62,
1850x3a, 0x00, 0x69, 0x66, 0x00, 0x70, 0x6f, 0x73, 0x73, 0x69, 1860x6c, 0x35, 0x00, 0x02, 0x8d, 0x03, 0x52, 0x6c, 0x77, 0x61,
1860x62, 0x6c, 0x35, 0x00, 0x02, 0x8d, 0x03, 0x52, 0x6c, 0x77, 1870x79, 0x73, 0x39, 0x00, 0x80, 0x6f, 0x72, 0x74, 0x68, 0x6f,
1870x61, 0x79, 0x73, 0x39, 0x00, 0x80, 0x6f, 0x72, 0x74, 0x68, 1880x67, 0x6f, 0x6e, 0xfd, 0x00, 0x12, 0x2c, 0xc2, 0x04, 0x63,
1880x6f, 0x67, 0x6f, 0x6e, 0xfd, 0x00, 0x12, 0x2c, 0xc2, 0x04, 1890x77, 0x69, 0x73, 0x65, 0x00, 0x69, 0xb9, 0x03, 0x02, 0xb5,
1890x63, 0x77, 0x69, 0x73, 0x65, 0x00, 0x69, 0xb9, 0x03, 0x02, 1900x04, 0x07, 0x17, 0x05, 0x00, 0x5c, 0x04, 0x24, 0x65, 0x73,
1900xb5, 0x04, 0x07, 0x17, 0x05, 0x00, 0x5c, 0x04, 0x24, 0x65, 1910x27, 0x08, 0x04, 0xfb, 0x04, 0x51, 0x69, 0x6e, 0x64, 0x69,
1910x73, 0x3c, 0x08, 0x04, 0xfb, 0x04, 0x51, 0x69, 0x6e, 0x64, 1920x63, 0x76, 0x06, 0x05, 0x30, 0x04, 0x60, 0x2e, 0x00, 0x48,
1920x69, 0x63, 0x8b, 0x06, 0x05, 0x30, 0x04, 0x60, 0x2e, 0x00, 1930x6f, 0x6c, 0x64, 0x1c, 0x01, 0x12, 0x43, 0xb8, 0x05, 0x01,
1930x48, 0x6f, 0x6c, 0x64, 0x1c, 0x01, 0x12, 0x43, 0xb8, 0x05, 1940xf7, 0x00, 0x00, 0x16, 0x04, 0x11, 0x73, 0x31, 0x01, 0x07,
1940x01, 0xf7, 0x00, 0x00, 0x16, 0x04, 0x11, 0x73, 0x31, 0x01, 1950xc8, 0x00, 0x02, 0x6b, 0x00, 0x37, 0x6c, 0x61, 0x79, 0x49,
1950x07, 0xc8, 0x00, 0x02, 0x6b, 0x00, 0x37, 0x6c, 0x61, 0x79, 1960x04, 0x03, 0x8e, 0x08, 0x06, 0x03, 0x05, 0x90, 0x28, 0x69,
1960x49, 0x04, 0x03, 0xa3, 0x08, 0x06, 0x03, 0x05, 0x90, 0x28, 1970x66, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0xcc, 0x07, 0x73,
1970x69, 0x66, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0xe1, 0x07, 1980x29, 0x3b, 0x00, 0x53, 0x68, 0x69, 0x66, 0x01, 0x02, 0x0f,
1980x73, 0x29, 0x3b, 0x00, 0x53, 0x68, 0x69, 0x66, 0x01, 0x02, 1990x4b, 0x00, 0x04, 0x01, 0x11, 0x03, 0x1c, 0x00, 0x12, 0x03,
1990x0f, 0x4b, 0x00, 0x04, 0x01, 0x11, 0x03, 0x1c, 0x00, 0x12, 2000x14, 0x50, 0x82, 0x00, 0x01, 0xa2, 0x02, 0x40, 0x65, 0x74,
2000x03, 0x14, 0x50, 0x82, 0x00, 0x01, 0xa2, 0x02, 0x40, 0x65, 2010x75, 0x72, 0x40, 0x05, 0x00, 0x75, 0x07, 0x42, 0x6c, 0x6c,
2010x74, 0x75, 0x72, 0x40, 0x05, 0x00, 0x8a, 0x07, 0x42, 0x6c, 2020x6f, 0x77, 0x83, 0x02, 0x0e, 0x52, 0x00, 0x01, 0x82, 0x01,
2020x6c, 0x6f, 0x77, 0x83, 0x02, 0x0e, 0x52, 0x00, 0x01, 0x82, 2030x0f, 0xa2, 0x00, 0x0b, 0x08, 0xb1, 0x01, 0x01, 0xea, 0x01,
2030x01, 0x0f, 0xa2, 0x00, 0x0b, 0x08, 0xb1, 0x01, 0x01, 0xea, 2040x2f, 0x61, 0x6e, 0xe2, 0x02, 0x04, 0x18, 0x70, 0x83, 0x00,
2040x01, 0x2f, 0x61, 0x6e, 0xe2, 0x02, 0x04, 0x18, 0x70, 0x83, 2050x20, 0x73, 0x70, 0xc7, 0x06, 0x30, 0x62, 0x61, 0x72, 0xe4,
2050x00, 0x20, 0x73, 0x70, 0xc7, 0x06, 0x30, 0x62, 0x61, 0x72, 2060x09, 0x0c, 0x1d, 0x00, 0x07, 0xa0, 0x00, 0x51, 0x74, 0x77,
2060xf9, 0x09, 0x0c, 0x1d, 0x00, 0x07, 0xa0, 0x00, 0x51, 0x74, 2070x69, 0x63, 0x65, 0x8c, 0x07, 0x07, 0x24, 0x00, 0x04, 0x8c,
2070x77, 0x69, 0x63, 0x65, 0xa1, 0x07, 0x07, 0x24, 0x00, 0x04, 2080x0a, 0x00, 0x22, 0x00, 0x06, 0x61, 0x03, 0x45, 0x6a, 0x75,
2080xa1, 0x0a, 0x00, 0x22, 0x00, 0x06, 0x61, 0x03, 0x45, 0x6a, 2090x6d, 0x70, 0xe6, 0x09, 0x0a, 0xb0, 0x01, 0x01, 0xef, 0x03,
2090x75, 0x6d, 0x70, 0xfb, 0x09, 0x0a, 0xb0, 0x01, 0x01, 0xef, 2100x02, 0x80, 0x08, 0x03, 0xc5, 0x0a, 0xf0, 0x0c, 0x4c, 0x65,
2100x03, 0x02, 0x95, 0x08, 0x03, 0xda, 0x0a, 0xf0, 0x0c, 0x4c, 2110x74, 0x74, 0x65, 0x72, 0x73, 0x00, 0x60, 0x61, 0x27, 0x2c,
2110x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x00, 0x60, 0x61, 0x27, 2120x00, 0x2e, 0x2e, 0x2e, 0x2c, 0x00, 0x60, 0x66, 0x27, 0x00,
2120x2c, 0x00, 0x2e, 0x2e, 0x2e, 0x2c, 0x00, 0x60, 0x66, 0x27, 2130x63, 0x6f, 0x75, 0x6e, 0x74, 0xb8, 0x00, 0x23, 0x31, 0x30,
2130x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0xb8, 0x00, 0x23, 0x31, 2140x16, 0x00, 0x21, 0x31, 0x35, 0x6d, 0x01, 0x31, 0x60, 0x30,
2140x30, 0x16, 0x00, 0x21, 0x31, 0x35, 0x6d, 0x01, 0x31, 0x60, 2150x27, 0x17, 0x00, 0x13, 0x36, 0x6e, 0x09, 0x46, 0x00, 0x60,
2150x30, 0x27, 0x17, 0x00, 0x10, 0x36, 0x8b, 0x00, 0x51, 0x56, 2160x47, 0x27, 0x27, 0x01, 0x03, 0x8b, 0x04, 0x86, 0x67, 0x72,
2160x69, 0x6f, 0x6c, 0x61, 0x1d, 0x06, 0x03, 0x21, 0x05, 0x04, 2170x65, 0x79, 0x00, 0x6c, 0x69, 0x6e, 0xdd, 0x04, 0x10, 0x65,
2170x5c, 0x08, 0x02, 0x75, 0x05, 0x04, 0x71, 0x03, 0x01, 0x7b, 2180x4a, 0x0b, 0x48, 0x70, 0x61, 0x69, 0x72, 0x84, 0x0b, 0x01,
2180x01, 0x10, 0x64, 0x32, 0x01, 0x42, 0x72, 0x65, 0x64, 0x3a, 2190x88, 0x00, 0x78, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x62,
2190xe7, 0x09, 0x1a, 0x6e, 0x92, 0x00, 0x2a, 0x6f, 0x6f, 0xef, 2200xd1, 0x09, 0x02, 0x94, 0x04, 0x04, 0x69, 0x01, 0x36, 0x6f,
2200x05, 0x04, 0x3b, 0x00, 0x09, 0x4d, 0x03, 0x01, 0x40, 0x00, 2210x72, 0x00, 0xda, 0x04, 0x01, 0x55, 0x0a, 0x25, 0x61, 0x72,
2210x1f, 0x2e, 0x40, 0x00, 0x05, 0x3f, 0x66, 0x65, 0x77, 0x3f, 2220xe4, 0x05, 0x00, 0x60, 0x07, 0x11, 0x6f, 0x2c, 0x03, 0x51,
2220x00, 0x10, 0x00, 0x1a, 0x03, 0x00, 0xdf, 0x06, 0x09, 0x98, 2230x56, 0x69, 0x6f, 0x6c, 0x61, 0xa9, 0x06, 0x03, 0xad, 0x05,
2230x05, 0xf3, 0x03, 0x61, 0x6e, 0x00, 0x65, 0x72, 0x72, 0x6f, 2240x04, 0xd3, 0x08, 0x02, 0x01, 0x06, 0x04, 0xfd, 0x03, 0x01,
2240x72, 0x00, 0x28, 0x61, 0x73, 0x00, 0x6f, 0x70, 0x70, 0x6f, 2250x07, 0x02, 0x10, 0x64, 0xbe, 0x01, 0x42, 0x72, 0x65, 0x64,
2250x73, 0xb3, 0x07, 0x20, 0x65, 0x72, 0x1f, 0x00, 0x00, 0xb4, 2260x3a, 0x5e, 0x0a, 0x1a, 0x6e, 0x1e, 0x01, 0x2a, 0x6f, 0x6f,
2260x05, 0x20, 0x62, 0x65, 0x70, 0x01, 0x05, 0xc6, 0x01, 0x41, 2270x7b, 0x06, 0x04, 0x3b, 0x00, 0x09, 0xd9, 0x03, 0x01, 0x40,
2270x79, 0x65, 0x74, 0x29, 0x66, 0x03, 0x21, 0x61, 0x64, 0xe4, 2280x00, 0x1f, 0x2e, 0x40, 0x00, 0x05, 0x3f, 0x66, 0x65, 0x77,
2280x02, 0x03, 0xca, 0x07, 0x05, 0x80, 0x00, 0x45, 0x6f, 0x75, 2290x3f, 0x00, 0x10, 0x00, 0xa6, 0x03, 0x00, 0x6b, 0x07, 0x09,
2290x6c, 0x64, 0x6d, 0x0a, 0x31, 0x68, 0x61, 0x76, 0xd1, 0x01, 2300x24, 0x06, 0xf3, 0x03, 0x61, 0x6e, 0x00, 0x65, 0x72, 0x72,
2300x12, 0x6f, 0xfa, 0x0a, 0x0b, 0xf8, 0x0a, 0x00, 0xf8, 0x01, 2310x6f, 0x72, 0x00, 0x28, 0x61, 0x73, 0x00, 0x6f, 0x70, 0x70,
2310x04, 0x6a, 0x06, 0x16, 0x00, 0xc4, 0x05, 0x03, 0xb1, 0x02, 2320x6f, 0x73, 0x3f, 0x08, 0x32, 0x65, 0x72, 0x65, 0xee, 0x00,
2320x10, 0x2c, 0x1f, 0x00, 0x28, 0x69, 0x66, 0x30, 0x05, 0x20, 2330x30, 0x00, 0x62, 0x65, 0xfc, 0x01, 0x05, 0x52, 0x02, 0x41,
2330x68, 0x61, 0x0f, 0x07, 0x29, 0x65, 0x6e, 0xde, 0x00, 0x01, 2340x79, 0x65, 0x74, 0x29, 0xf2, 0x03, 0x21, 0x61, 0x64, 0x70,
2340xae, 0x09, 0x54, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x49, 0x0b, 2350x03, 0x03, 0x56, 0x08, 0x05, 0x80, 0x00, 0x01, 0x61, 0x01,
2350x68, 0x00, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x09, 0x0d, 0x01, 2360x04, 0xe4, 0x0a, 0x31, 0x68, 0x61, 0x76, 0x5d, 0x02, 0x12,
2360x55, 0x0a, 0x07, 0x7d, 0x0a, 0x0f, 0x21, 0x01, 0x0c, 0x41, 2370x6f, 0x71, 0x0b, 0x0b, 0x6f, 0x0b, 0x00, 0x6d, 0x01, 0x04,
2370x61, 0x00, 0x63, 0x6c, 0x0a, 0x01, 0x33, 0x73, 0x75, 0x62, 2380xf6, 0x06, 0x08, 0x76, 0x01, 0x02, 0x3d, 0x03, 0x10, 0x2c,
2380x53, 0x0d, 0x07, 0xe3, 0x01, 0x01, 0x78, 0x01, 0x24, 0x6e, 2390x1f, 0x00, 0x28, 0x69, 0x66, 0xbc, 0x05, 0x20, 0x68, 0x61,
2390x6f, 0x85, 0x06, 0x04, 0x12, 0x0d, 0x02, 0x39, 0x0b, 0x02, 2400x9b, 0x07, 0x29, 0x65, 0x6e, 0xde, 0x00, 0x01, 0x25, 0x0a,
2400xbc, 0x02, 0x15, 0x73, 0x34, 0x00, 0x03, 0x82, 0x00, 0x90, 2410x54, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0xc0, 0x0b, 0x68, 0x00,
2410x2e, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0xa9, 2420x67, 0x72, 0x6f, 0x75, 0x70, 0xfc, 0x01, 0x01, 0xcc, 0x0a,
2420x00, 0x32, 0x2c, 0x00, 0x69, 0x9b, 0x06, 0x02, 0x42, 0x03, 2430x07, 0xf4, 0x0a, 0x0f, 0x21, 0x01, 0x0c, 0x41, 0x61, 0x00,
2430x26, 0x6c, 0x79, 0x56, 0x0d, 0x4c, 0x77, 0x6f, 0x00, 0x31, 2440x63, 0x6c, 0x0a, 0x01, 0x33, 0x73, 0x75, 0x62, 0xca, 0x0d,
2440x51, 0x0d, 0x05, 0x79, 0x03, 0x01, 0x77, 0x04, 0x04, 0x6a, 2450x07, 0xe3, 0x01, 0x01, 0x78, 0x01, 0x24, 0x6e, 0x6f, 0x11,
2450x06, 0x22, 0x6e, 0x6f, 0x85, 0x0d, 0x01, 0x6a, 0x09, 0x08, 2460x07, 0x04, 0x89, 0x0d, 0x02, 0xb0, 0x0b, 0x02, 0x48, 0x03,
2460x76, 0x07, 0x25, 0x6f, 0x6e, 0x0e, 0x05, 0x01, 0xe7, 0x07, 2470x15, 0x73, 0x34, 0x00, 0x03, 0x82, 0x00, 0x90, 0x2e, 0x00,
2470x04, 0x1a, 0x04, 0x01, 0x3a, 0x06, 0x43, 0x75, 0x70, 0x00, 2480x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0xa9, 0x00, 0x32,
2480x72, 0x20, 0x06, 0x03, 0xb9, 0x04, 0x02, 0x14, 0x03, 0x03, 2490x2c, 0x00, 0x69, 0x27, 0x07, 0x02, 0xce, 0x03, 0x26, 0x6c,
2490xa9, 0x0d, 0x02, 0x39, 0x01, 0x32, 0x63, 0x61, 0x6e, 0xf9, 2500x79, 0xcd, 0x0d, 0x4c, 0x77, 0x6f, 0x00, 0x31, 0xc8, 0x0d,
2500x01, 0x04, 0x10, 0x0e, 0x02, 0x23, 0x01, 0x00, 0xa6, 0x02, 2510x05, 0x9c, 0x02, 0x01, 0x03, 0x05, 0x04, 0xf6, 0x06, 0x22,
2510xa4, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x00, 0x73, 0x6f, 0x6c, 2520x6e, 0x6f, 0xfc, 0x0d, 0x01, 0xf6, 0x09, 0x08, 0x02, 0x08,
2520x75, 0xf9, 0x03, 0x14, 0x2d, 0x68, 0x07, 0x01, 0x37, 0x07, 2530x25, 0x6f, 0x6e, 0x9a, 0x05, 0x01, 0x73, 0x08, 0x04, 0xa6,
2530x33, 0x73, 0x65, 0x6c, 0x82, 0x06, 0x40, 0x68, 0x65, 0x00, 2540x04, 0x01, 0xc6, 0x06, 0x43, 0x75, 0x70, 0x00, 0x72, 0xac,
2540x28, 0xd4, 0x01, 0x04, 0x73, 0x08, 0x34, 0x00, 0x6f, 0x70, 2550x06, 0x03, 0x45, 0x05, 0x02, 0x18, 0x03, 0x03, 0x20, 0x0e,
2550xd1, 0x09, 0x40, 0x64, 0x69, 0x73, 0x61, 0x73, 0x04, 0x01, 2560x02, 0x39, 0x01, 0x32, 0x63, 0x61, 0x6e, 0xf9, 0x01, 0x04,
2560xfd, 0x0b, 0x15, 0x73, 0xd9, 0x07, 0x03, 0x55, 0x00, 0x15, 2570x87, 0x0e, 0x02, 0x23, 0x01, 0x00, 0xa6, 0x02, 0xa4, 0x76,
2570x2c, 0x88, 0x00, 0x07, 0x32, 0x0c, 0x02, 0xc4, 0x0c, 0x00, 2580x61, 0x6c, 0x69, 0x64, 0x00, 0x73, 0x6f, 0x6c, 0x75, 0x85,
2580x34, 0x0c, 0x14, 0x73, 0x35, 0x0c, 0x0e, 0xea, 0x02, 0x01, 2590x04, 0x14, 0x2d, 0xf4, 0x07, 0x01, 0xc3, 0x07, 0x33, 0x73,
2590x22, 0x03, 0x14, 0x28, 0x48, 0x0d, 0x02, 0xd0, 0x08, 0x00, 2600x65, 0x6c, 0x0e, 0x07, 0x40, 0x68, 0x65, 0x00, 0x28, 0xd4,
2600xee, 0x02, 0x52, 0x73, 0x63, 0x72, 0x69, 0x62, 0xc4, 0x00, 2610x01, 0x04, 0xff, 0x08, 0x34, 0x00, 0x6f, 0x70, 0x5d, 0x0a,
2610x13, 0x73, 0x54, 0x05, 0x33, 0x32, 0x2e, 0x31, 0x75, 0x09, 2620x40, 0x64, 0x69, 0x73, 0x61, 0xff, 0x04, 0x01, 0x74, 0x0c,
2620x26, 0x73, 0x6f, 0x5d, 0x05, 0x13, 0x2e, 0x7a, 0x0b, 0x15, 2630x15, 0x73, 0x65, 0x08, 0x03, 0x55, 0x00, 0x15, 0x2c, 0x88,
2630x32, 0x7a, 0x0b, 0x00, 0xb3, 0x09, 0x20, 0x6d, 0x65, 0x27, 2640x00, 0x07, 0xa9, 0x0c, 0x02, 0x3b, 0x0d, 0x00, 0xab, 0x0c,
2640x04, 0x12, 0x20, 0x79, 0x0d, 0x36, 0x73, 0x65, 0x00, 0x14, 2650x14, 0x73, 0xac, 0x0c, 0x0e, 0xea, 0x02, 0x01, 0x22, 0x03,
2650x00, 0x02, 0x43, 0x00, 0x04, 0x3e, 0x00, 0x02, 0x55, 0x07, 2660x14, 0x28, 0xbf, 0x0d, 0x02, 0x5c, 0x09, 0x00, 0xee, 0x02,
2660x00, 0x7b, 0x00, 0xb4, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 2670x52, 0x73, 0x63, 0x72, 0x69, 0x62, 0xc4, 0x00, 0x13, 0x73,
2670x6d, 0x2e, 0x2e, 0x2e, 0x27, 0xf7, 0x00, 0x03, 0x96, 0x01, 2680xe0, 0x05, 0x33, 0x32, 0x2e, 0x31, 0x01, 0x0a, 0x26, 0x73,
2680x40, 0x60, 0x54, 0x79, 0x70, 0x93, 0x05, 0x30, 0x65, 0x6e, 2690x6f, 0xe9, 0x05, 0x22, 0x2e, 0x29, 0x06, 0x0c, 0x15, 0x32,
2690x75, 0xad, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 2700x06, 0x0c, 0x00, 0x3f, 0x0a, 0x20, 0x6d, 0x65, 0xb3, 0x04,
2700x00, 0x48, 0x65, 0x9d, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69, 2710x12, 0x20, 0x82, 0x04, 0x36, 0x73, 0x65, 0x00, 0x14, 0x00,
2710x7a, 0x07, 0x0c, 0x00, 0xbd, 0x01, 0x01, 0xaf, 0x00, 0x42, 2720x02, 0x43, 0x00, 0x04, 0x3e, 0x00, 0x02, 0xe1, 0x07, 0x10,
2720x71, 0x75, 0x61, 0x72, 0xcc, 0x0c, 0xaa, 0x44, 0x69, 0x66, 2730x74, 0xa6, 0x04, 0xa4, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
2730x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x0d, 0x00, 0x41, 2740x2e, 0x2e, 0x2e, 0x27, 0xf7, 0x00, 0x03, 0x96, 0x01, 0x40,
2740x6c, 0x65, 0x76, 0x65, 0xa6, 0x09, 0x02, 0x9e, 0x02, 0x00, 2750x60, 0x54, 0x79, 0x70, 0x1f, 0x06, 0x30, 0x65, 0x6e, 0x75,
2750x2b, 0x00, 0x10, 0x41, 0x5d, 0x01, 0x12, 0x20, 0x5d, 0x01, 2760xad, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00,
2760x12, 0x00, 0x41, 0x08, 0x21, 0x69, 0x73, 0x1c, 0x10, 0x06, 2770x48, 0x65, 0x9d, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a,
2770x03, 0x0a, 0x01, 0x7d, 0x0a, 0x20, 0x63, 0x6c, 0x24, 0x05, 2780xa0, 0x0d, 0x00, 0xbd, 0x01, 0x01, 0xaf, 0x00, 0x42, 0x71,
2780x23, 0x64, 0x2c, 0x3c, 0x00, 0x06, 0x3e, 0x04, 0x51, 0x67, 2790x75, 0x61, 0x72, 0x43, 0x0d, 0xaa, 0x44, 0x69, 0x66, 0x66,
2790x65, 0x6e, 0x65, 0x72, 0xd6, 0x06, 0x0e, 0xc5, 0x0f, 0x06, 2800x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x0d, 0x00, 0x41, 0x6c,
2800x83, 0x02, 0x03, 0x4f, 0x07, 0x00, 0xb9, 0x01, 0x13, 0x62, 2810x65, 0x76, 0x65, 0x32, 0x0a, 0x02, 0x9e, 0x02, 0x00, 0x2b,
2810x18, 0x03, 0x00, 0x49, 0x0b, 0x33, 0x63, 0x72, 0x65, 0x9b, 2820x00, 0x10, 0x41, 0x5d, 0x01, 0x12, 0x20, 0x5d, 0x01, 0x12,
2820x0f, 0x01, 0xa9, 0x01, 0x02, 0x87, 0x08, 0x04, 0xde, 0x01, 2830x00, 0xcd, 0x08, 0x21, 0x69, 0x73, 0x93, 0x10, 0x06, 0x8f,
2830x04, 0xcb, 0x01, 0x25, 0x64, 0x6f, 0x25, 0x04, 0x0b, 0xd0, 2840x0a, 0x01, 0x09, 0x0b, 0x20, 0x63, 0x6c, 0xb0, 0x05, 0x23,
2840x01, 0x04, 0x1e, 0x02, 0x02, 0xcf, 0x01, 0x64, 0x4d, 0x61, 2850x64, 0x2c, 0x3c, 0x00, 0x06, 0x3e, 0x04, 0x51, 0x67, 0x65,
2850x78, 0x2e, 0x20, 0x62, 0x8e, 0x01, 0x36, 0x65, 0x72, 0x20, 2860x6e, 0x65, 0x72, 0x62, 0x07, 0x0e, 0x3c, 0x10, 0x06, 0x83,
2860x18, 0x07, 0x01, 0x1d, 0x00, 0x4e, 0x69, 0x6d, 0x75, 0x6d, 2870x02, 0x03, 0xdb, 0x07, 0x00, 0xb9, 0x01, 0x13, 0x62, 0x18,
2870x22, 0x10, 0x04, 0xaf, 0x02, 0x20, 0x70, 0x61, 0x4f, 0x10, 2880x03, 0x00, 0xd5, 0x0b, 0x33, 0x63, 0x72, 0x65, 0x12, 0x10,
2880x48, 0x75, 0x6c, 0x61, 0x72, 0xae, 0x06, 0x17, 0x54, 0x2b, 2890x01, 0xa9, 0x01, 0x02, 0x13, 0x09, 0x04, 0xde, 0x01, 0x04,
2890x0e, 0x42, 0x69, 0x73, 0x00, 0x32, 0xc6, 0x0f, 0x04, 0x44, 2900xcb, 0x01, 0x25, 0x64, 0x6f, 0x25, 0x04, 0x0b, 0xd0, 0x01,
2900x06, 0x01, 0x21, 0x0f, 0x13, 0x65, 0xcf, 0x03, 0x40, 0x31, 2910x04, 0x1e, 0x02, 0x02, 0xcf, 0x01, 0x64, 0x4d, 0x61, 0x78,
2910x2c, 0x00, 0x33, 0x88, 0x04, 0x53, 0x34, 0x2e, 0x00, 0x49, 2920x2e, 0x20, 0x62, 0x8e, 0x01, 0x36, 0x65, 0x72, 0x20, 0xa4,
2920x6e, 0x17, 0x01, 0x20, 0x6c, 0x2c, 0x72, 0x05, 0x21, 0x65, 2930x07, 0x01, 0x1d, 0x00, 0x4e, 0x69, 0x6d, 0x75, 0x6d, 0x99,
2930x72, 0x6d, 0x10, 0x32, 0x61, 0x73, 0x69, 0x6e, 0x0a, 0x82, 2940x10, 0x04, 0xaf, 0x02, 0x20, 0x70, 0x61, 0xc6, 0x10, 0x48,
2940x25, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x87, 0x03, 2950x75, 0x6c, 0x61, 0x72, 0x3a, 0x07, 0x17, 0x54, 0xa2, 0x0e,
2950x13, 0x20, 0xba, 0x01, 0x70, 0x00, 0x00, 0x00, 0x47, 0x69, 2960x42, 0x69, 0x73, 0x00, 0x32, 0x3d, 0x10, 0x04, 0xd0, 0x06,
2960x76, 0x65, 0xb5, 0x02, 0x11, 0x72, 0x28, 0x05, 0x40, 0x70, 2970x01, 0x98, 0x0f, 0x13, 0x65, 0xcf, 0x03, 0x40, 0x31, 0x2c,
2970x65, 0x72, 0x63, 0x73, 0x09, 0x28, 0x67, 0x65, 0xa0, 0x04, 2980x00, 0x33, 0x88, 0x04, 0x53, 0x34, 0x2e, 0x00, 0x49, 0x6e,
2980x25, 0x74, 0x68, 0x74, 0x01, 0x04, 0xac, 0x08, 0x31, 0x74, 2990x17, 0x01, 0x20, 0x6c, 0x2c, 0x72, 0x05, 0x21, 0x65, 0x72,
2990x72, 0x79, 0x39, 0x01, 0x11, 0x6c, 0xaf, 0x04, 0x34, 0x66, 3000xe4, 0x10, 0x32, 0x61, 0x73, 0x69, 0xfa, 0x0a, 0x82, 0x25,
3000x6f, 0x72, 0xbb, 0x0a, 0x05, 0xbc, 0x0f, 0x03, 0xf0, 0x01, 3010x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x87, 0x03, 0x13,
3010x30, 0x43, 0x65, 0x72, 0xa4, 0x03, 0x00, 0x29, 0x00, 0x33, 3020x20, 0xba, 0x01, 0x70, 0x00, 0x00, 0x00, 0x47, 0x69, 0x76,
3020x6f, 0x75, 0x74, 0xc4, 0x01, 0x00, 0xc0, 0x03, 0x30, 0x6d, 3030x65, 0xb5, 0x02, 0x11, 0x72, 0x28, 0x05, 0x40, 0x70, 0x65,
3030x61, 0x6e, 0x67, 0x00, 0x12, 0x74, 0xbf, 0x07, 0x03, 0xa8, 3040x72, 0x63, 0xff, 0x09, 0x2a, 0x67, 0x65, 0x9c, 0x06, 0x05,
3040x05, 0x03, 0x72, 0x00, 0x12, 0x3b, 0xca, 0x0c, 0x10, 0x69, 3050x74, 0x01, 0x04, 0x38, 0x09, 0x31, 0x74, 0x72, 0x79, 0x39,
3050x1b, 0x05, 0x40, 0x00, 0x75, 0x70, 0x70, 0x94, 0x05, 0x22, 3060x01, 0x11, 0x6c, 0xaf, 0x04, 0x34, 0x66, 0x6f, 0x72, 0x47,
3060x6f, 0x75, 0x13, 0x11, 0xf0, 0x03, 0x45, 0x78, 0x70, 0x61, 3070x0b, 0x05, 0x33, 0x10, 0x03, 0xf0, 0x01, 0x30, 0x43, 0x65,
3070x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x61, 0x63, 0x74, 3080x72, 0xa4, 0x03, 0x00, 0x29, 0x00, 0x33, 0x6f, 0x75, 0x74,
3080x6f, 0x72, 0x20, 0x28, 0xdc, 0x00, 0x12, 0x29, 0x00, 0x03, 3090xc4, 0x01, 0x00, 0xc0, 0x03, 0x30, 0x6d, 0x61, 0x6e, 0x67,
3090x02, 0x9b, 0x02, 0x07, 0xac, 0x00, 0x41, 0x6f, 0x72, 0x6b, 3100x00, 0x12, 0x74, 0x4b, 0x08, 0x03, 0xa8, 0x05, 0x03, 0x72,
3100x73, 0xbe, 0x07, 0x04, 0x10, 0x0a, 0x69, 0x6e, 0x00, 0x65, 3110x00, 0x12, 0x3b, 0x56, 0x0d, 0x10, 0x69, 0x1b, 0x05, 0x40,
3110x78, 0x69, 0x73, 0x7d, 0x0d, 0xf8, 0x05, 0x74, 0x00, 0x72, 3120x00, 0x75, 0x70, 0x70, 0x94, 0x05, 0x22, 0x6f, 0x75, 0x8a,
3120x61, 0x6e, 0x64, 0x6f, 0x6d, 0x00, 0x28, 0x61, 0x66, 0x74, 3130x11, 0xf0, 0x03, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69,
3130x65, 0x72, 0x00, 0x66, 0x69, 0x72, 0x73, 0x28, 0x02, 0x20, 3140x6f, 0x6e, 0x20, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x20,
3140x6e, 0x00, 0x90, 0x06, 0x34, 0x69, 0x61, 0x6c, 0x32, 0x00, 3150x28, 0xdc, 0x00, 0x13, 0x29, 0x82, 0x07, 0x01, 0x9b, 0x02,
3150x00, 0x7f, 0x10, 0x10, 0x77, 0x1a, 0x0d, 0x41, 0x29, 0x2e, 3160x07, 0xac, 0x00, 0x41, 0x6f, 0x72, 0x6b, 0x73, 0x4a, 0x08,
3160x00, 0x49, 0x72, 0x02, 0x40, 0x6e, 0x00, 0x64, 0x65, 0xae, 3170x04, 0x9c, 0x0a, 0x69, 0x6e, 0x00, 0x65, 0x78, 0x69, 0x73,
3170x0a, 0x12, 0x73, 0x6b, 0x0a, 0x07, 0x1e, 0x09, 0x05, 0x5e, 3180x09, 0x0e, 0xf8, 0x05, 0x74, 0x00, 0x72, 0x61, 0x6e, 0x64,
3180x00, 0x24, 0x29, 0x2c, 0x22, 0x05, 0x13, 0x6e, 0x97, 0x00, 3190x6f, 0x6d, 0x00, 0x28, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,
3190x00, 0x8e, 0x02, 0x31, 0x68, 0x6f, 0x77, 0x72, 0x0e, 0x41, 3200x66, 0x69, 0x72, 0x73, 0x28, 0x02, 0x20, 0x6e, 0x00, 0x90,
3200x69, 0x74, 0x00, 0x63, 0x9c, 0x06, 0x20, 0x65, 0x78, 0x6c, 3210x06, 0x34, 0x69, 0x61, 0x6c, 0x32, 0x00, 0x00, 0xf6, 0x10,
3210x0e, 0x04, 0x54, 0x01, 0x07, 0x85, 0x00, 0x08, 0xc6, 0x0e, 3220x10, 0x77, 0xa6, 0x0d, 0x41, 0x29, 0x2e, 0x00, 0x49, 0x72,
3220x03, 0x78, 0x0b, 0x05, 0xeb, 0x03, 0x32, 0x00, 0x64, 0x65, 3230x02, 0x40, 0x6e, 0x00, 0x64, 0x65, 0x3a, 0x0b, 0x12, 0x73,
3230x75, 0x12, 0x21, 0x65, 0x73, 0x52, 0x00, 0x50, 0x6c, 0x69, 3240xf7, 0x0a, 0x07, 0xaa, 0x09, 0x05, 0x5e, 0x00, 0x24, 0x29,
3240x6b, 0x65, 0x6c, 0xad, 0x0a, 0x03, 0x17, 0x13, 0x03, 0x55, 3250x2c, 0x22, 0x05, 0x13, 0x6e, 0x97, 0x00, 0x00, 0x8e, 0x02,
3250x00, 0x21, 0x61, 0x73, 0x6c, 0x00, 0x21, 0x61, 0x73, 0x6f, 3260x31, 0x68, 0x6f, 0x77, 0xfe, 0x0e, 0x14, 0x69, 0xfd, 0x07,
3260x00, 0x66, 0x61, 0x6e, 0x2c, 0x00, 0x72, 0x61, 0x40, 0x11, 3270x20, 0x65, 0x78, 0xf8, 0x0e, 0x04, 0x54, 0x01, 0x07, 0x85,
3270x41, 0x63, 0x68, 0x6f, 0x6f, 0xbd, 0x08, 0x05, 0xdf, 0x00, 3280x00, 0x08, 0x52, 0x0f, 0x03, 0x04, 0x0c, 0x05, 0xeb, 0x03,
3280x02, 0x68, 0x06, 0x01, 0x40, 0x02, 0x64, 0x48, 0x69, 0x67, 3290x32, 0x00, 0x64, 0x65, 0xec, 0x12, 0x21, 0x65, 0x73, 0x52,
3290x68, 0x00, 0x65, 0x7b, 0x01, 0x12, 0x00, 0x7b, 0x01, 0x52, 3300x00, 0x50, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x39, 0x0b, 0x03,
3300x73, 0x00, 0x75, 0x73, 0x75, 0x9f, 0x0b, 0x33, 0x65, 0x61, 3310x8e, 0x13, 0x03, 0x55, 0x00, 0x21, 0x61, 0x73, 0x6c, 0x00,
3310x6e, 0x6e, 0x02, 0x07, 0xae, 0x03, 0x23, 0x74, 0x68, 0x8b, 3320x21, 0x61, 0x73, 0x6f, 0x00, 0x66, 0x61, 0x6e, 0x2c, 0x00,
3320x02, 0x05, 0xf3, 0x0a, 0x05, 0xe0, 0x01, 0x00, 0x60, 0x05, 3330x72, 0x61, 0xb7, 0x11, 0x41, 0x63, 0x68, 0x6f, 0x6f, 0x49,
3330x0e, 0x4f, 0x00, 0x01, 0xe1, 0x02, 0x20, 0x72, 0x65, 0xf8, 3340x09, 0x05, 0xdf, 0x00, 0x02, 0x68, 0x06, 0x01, 0x40, 0x02,
3340x05, 0x32, 0x6c, 0x6f, 0x74, 0xbd, 0x08, 0x00, 0x7c, 0x04, 3350x64, 0x48, 0x69, 0x67, 0x68, 0x00, 0x65, 0x7b, 0x01, 0x12,
3350x73, 0x6c, 0x79, 0x2d, 0x70, 0x61, 0x63, 0x6b, 0xb9, 0x0b, 3360x00, 0x7b, 0x01, 0x52, 0x73, 0x00, 0x75, 0x73, 0x75, 0x2b,
3360x50, 0x6e, 0x64, 0x73, 0x2e, 0x00, 3370x0c, 0x33, 0x65, 0x61, 0x6e, 0x6e, 0x02, 0x07, 0xae, 0x03,
3380x23, 0x74, 0x68, 0x8b, 0x02, 0x05, 0x7f, 0x0b, 0x05, 0xe0,
3390x01, 0x00, 0x60, 0x05, 0x0e, 0x4f, 0x00, 0x01, 0xe1, 0x02,
3400x20, 0x72, 0x65, 0xf8, 0x05, 0x32, 0x6c, 0x6f, 0x74, 0xbd,
3410x08, 0x00, 0x7c, 0x04, 0x61, 0x6c, 0x79, 0x2d, 0x70, 0x61,
3420x63, 0xac, 0x08, 0x04, 0xb1, 0x06, 0x01, 0x0a, 0x05, 0x15,
3430x33, 0x0a, 0x05, 0xb0, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70,
3440x72, 0x65, 0x66, 0x65, 0x72, 0x46, 0x12, 0x01, 0x10, 0x05,
3450x20, 0x4f, 0x6e, 0x70, 0x0d, 0x12, 0x74, 0x97, 0x05, 0x03,
3460x48, 0x06, 0x60, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e,
3470x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0xfe, 0x04, 0x16,
3480x50, 0x12, 0x00, 0x0d, 0x1a, 0x05, 0x33, 0x47, 0x61, 0x6d,
3490x1a, 0x05, 0x03, 0xb1, 0x06, 0x13, 0x65, 0x94, 0x03, 0x03,
3500x56, 0x13, 0x10, 0x65, 0xfe, 0x0f, 0x28, 0x74, 0x68, 0xed,
3510x00, 0x04, 0x2d, 0x13, 0x23, 0x6f, 0x63, 0x8c, 0x09, 0x01,
3520x8b, 0x13, 0x80, 0x68, 0x6f, 0x77, 0x6e, 0x2e, 0x00, 0x55,
3530x6e, 0xa4, 0x01, 0x26, 0x00, 0x74, 0x35, 0x0a, 0x00, 0x84,
3540x00, 0x04, 0xd8, 0x0d, 0x75, 0x70, 0x65, 0x72, 0x73, 0x69,
3550x73, 0x74, 0x32, 0x0a, 0x70, 0x67, 0x61, 0x6d, 0x65, 0x73,
3560x2e, 0x00,
337}; 357};
338 358
339const unsigned short help_text_len = 5230; 359const unsigned short help_text_len = 5613;
340const unsigned short help_text_words = 963; 360const unsigned short help_text_words = 1026;
361const bool help_valid = true;
341const char quick_help_text[] = "Connect all the islands with a network of bridges."; 362const char quick_help_text[] = "Connect all the islands with a network of bridges.";
diff --git a/apps/plugins/puzzles/help/cube.c b/apps/plugins/puzzles/help/cube.c
index 6857cbf733..0e2fc94081 100644
--- a/apps/plugins/puzzles/help/cube.c
+++ b/apps/plugins/puzzles/help/cube.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,163 +6,165 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 207, TEXT_CENTER | C_RED }, 9 { 208, TEXT_CENTER | C_RED },
10 { 319, TEXT_CENTER | C_RED }, 10 { 320, TEXT_CENTER | C_RED },
11 { 336, TEXT_UNDERLINE }, 11 { 337, TEXT_UNDERLINE },
12 { 358, TEXT_UNDERLINE },
13 { 359, TEXT_UNDERLINE }, 12 { 359, TEXT_UNDERLINE },
13 { 360, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 2055 comp 1444 ratio 0.702676 level 10 saved 611 */ 17/* orig 2071 comp 1451 ratio 0.700628 level 10 saved 620 */
18const char help_text[] = { 18const char help_text[] = {
190xf2, 0x37, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xfa, 0x03, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x34, 0x3a, 0x20, 0x43, 0x75, 0x62, 0x65, 0x20, 0x00, 0x00, 200x34, 0x3a, 0x20, 0x43, 0x75, 0x62, 0x65, 0x20, 0x00, 0x2d,
210x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x69, 0x73, 0x00, 0x61, 210x01, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69,
220x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 0x6f, 0x6e, 0x65, 220x73, 0x00, 0x69, 0x73, 0x00, 0x61, 0x6e, 0x6f, 0x74, 0x68,
230x00, 0x49, 0x00, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 230x65, 0x72, 0x00, 0x6f, 0x6e, 0x65, 0x00, 0x49, 0x00, 0x6f,
240x6c, 0x6c, 0x79, 0x00, 0x73, 0x61, 0x77, 0x00, 0x61, 0x73, 240x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x00,
250x00, 0x61, 0x00, 0x77, 0x65, 0x62, 0x00, 0x67, 0x61, 0x6d, 250x73, 0x61, 0x77, 0x00, 0x61, 0x73, 0x00, 0x61, 0x00, 0x77,
260x65, 0x2e, 0x34, 0x00, 0x00, 0x29, 0x00, 0x11, 0x77, 0x19, 260x65, 0x62, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x34, 0x00,
270x00, 0x41, 0x4a, 0x61, 0x76, 0x61, 0x1a, 0x00, 0xf0, 0x28, 270x00, 0x29, 0x00, 0x11, 0x77, 0x19, 0x00, 0x41, 0x4a, 0x61,
280x00, 0x5b, 0x32, 0x5d, 0x2c, 0x00, 0x62, 0x79, 0x00, 0x50, 280x76, 0x61, 0x1a, 0x00, 0xf0, 0x28, 0x00, 0x5b, 0x32, 0x5d,
290x61, 0x75, 0x6c, 0x00, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x2e, 290x2c, 0x00, 0x62, 0x79, 0x00, 0x50, 0x61, 0x75, 0x6c, 0x00,
300x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 300x53, 0x63, 0x6f, 0x74, 0x74, 0x2e, 0x00, 0x59, 0x6f, 0x75,
310x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 310x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72,
320x31, 0x36, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 320x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x31, 0x36, 0x00, 0x73,
330x2c, 0x00, 0x73, 0x69, 0x78, 0x13, 0x00, 0xf1, 0x03, 0x77, 330x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x69,
340x68, 0x69, 0x63, 0x68, 0x00, 0x61, 0x72, 0x65, 0x00, 0x62, 340x78, 0x13, 0x00, 0xf1, 0x03, 0x77, 0x68, 0x69, 0x63, 0x68,
350x6c, 0x75, 0x65, 0x3b, 0x00, 0x6f, 0x6e, 0x61, 0x00, 0x02, 350x00, 0x61, 0x72, 0x65, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x3b,
360x27, 0x00, 0x50, 0x00, 0x72, 0x65, 0x73, 0x74, 0x6a, 0x00, 360x00, 0x6f, 0x6e, 0x61, 0x00, 0x02, 0x27, 0x00, 0x50, 0x00,
370x41, 0x63, 0x75, 0x62, 0x65, 0x52, 0x00, 0x60, 0x72, 0x00, 370x72, 0x65, 0x73, 0x74, 0x6a, 0x00, 0x41, 0x63, 0x75, 0x62,
380x6d, 0x6f, 0x76, 0x65, 0xb8, 0x00, 0xf1, 0x05, 0x74, 0x6f, 380x65, 0x52, 0x00, 0x60, 0x72, 0x00, 0x6d, 0x6f, 0x76, 0x65,
390x00, 0x75, 0x73, 0x65, 0x00, 0x74, 0x68, 0x65, 0x00, 0x61, 390xb8, 0x00, 0xf1, 0x05, 0x74, 0x6f, 0x00, 0x75, 0x73, 0x65,
400x72, 0x72, 0x6f, 0x77, 0x00, 0x6b, 0x65, 0x79, 0x16, 0x00, 400x00, 0x74, 0x68, 0x65, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77,
410x41, 0x72, 0x6f, 0x6c, 0x6c, 0x17, 0x00, 0x00, 0x35, 0x00, 410x00, 0x6b, 0x65, 0x79, 0x16, 0x00, 0x41, 0x72, 0x6f, 0x6c,
420xf1, 0x0f, 0x00, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 420x6c, 0x17, 0x00, 0x00, 0x35, 0x00, 0xf1, 0x0f, 0x00, 0x74,
430x00, 0x39, 0x30, 0x00, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 430x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x39, 0x30, 0x00,
440x73, 0x00, 0x73, 0x6f, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 440x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x73, 0x00, 0x73, 0x6f,
450x69, 0x74, 0x4d, 0x00, 0x01, 0x35, 0x00, 0xb3, 0x61, 0x6e, 450x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x69, 0x74, 0x4d, 0x00,
460x00, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x7c, 460x01, 0x35, 0x00, 0xb3, 0x61, 0x6e, 0x00, 0x61, 0x64, 0x6a,
470x00, 0x8b, 0x2e, 0x00, 0x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 470x61, 0x63, 0x65, 0x6e, 0x74, 0x7c, 0x00, 0x8b, 0x2e, 0x00,
480x50, 0x00, 0x21, 0x6f, 0x6e, 0x2f, 0x00, 0x01, 0xae, 0x00, 480x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x50, 0x00, 0x21, 0x6f,
490x03, 0x2a, 0x00, 0x11, 0x2c, 0x1e, 0x00, 0x07, 0x11, 0x00, 490x6e, 0x2f, 0x00, 0x01, 0xae, 0x00, 0x03, 0x2a, 0x00, 0x11,
500x00, 0x9f, 0x00, 0x94, 0x70, 0x69, 0x63, 0x6b, 0x65, 0x64, 500x2c, 0x1e, 0x00, 0x07, 0x11, 0x00, 0x00, 0x9f, 0x00, 0x94,
510x00, 0x75, 0x70, 0xd2, 0x00, 0x40, 0x66, 0x61, 0x63, 0x65, 510x70, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x00, 0x75, 0x70, 0xd2,
520xf1, 0x00, 0x04, 0x4a, 0x00, 0x37, 0x3b, 0x00, 0x69, 0x60, 520x00, 0x40, 0x66, 0x61, 0x63, 0x65, 0xf1, 0x00, 0x04, 0x4a,
530x00, 0x03, 0x51, 0x00, 0x0c, 0x25, 0x00, 0x05, 0x6f, 0x00, 530x00, 0x37, 0x3b, 0x00, 0x69, 0x60, 0x00, 0x03, 0x51, 0x00,
540x4f, 0x6e, 0x6f, 0x6e, 0x2d, 0x73, 0x00, 0x02, 0x41, 0x6e, 540x0c, 0x25, 0x00, 0x05, 0x6f, 0x00, 0x4f, 0x6e, 0x6f, 0x6e,
550x65, 0x73, 0x73, 0x70, 0x00, 0xfe, 0x15, 0x75, 0x74, 0x00, 550x2d, 0x73, 0x00, 0x02, 0x41, 0x6e, 0x65, 0x73, 0x73, 0x70,
560x64, 0x6f, 0x77, 0x6e, 0x00, 0x61, 0x67, 0x61, 0x69, 0x6e, 560x00, 0xfe, 0x15, 0x75, 0x74, 0x00, 0x64, 0x6f, 0x77, 0x6e,
570x2e, 0x00, 0x28, 0x49, 0x6e, 0x00, 0x67, 0x65, 0x6e, 0x65, 570x00, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x00, 0x28, 0x49,
580x72, 0x61, 0x6c, 0x2c, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x65, 580x6e, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x2c,
590x76, 0x65, 0x72, 0xd0, 0x00, 0x02, 0x49, 0x00, 0x31, 0x74, 590x00, 0x77, 0x68, 0x65, 0x6e, 0x65, 0x76, 0x65, 0x72, 0xd0,
600x77, 0x6f, 0x7b, 0x00, 0x12, 0x73, 0x19, 0x01, 0xf2, 0x11, 600x00, 0x02, 0x49, 0x00, 0x31, 0x74, 0x77, 0x6f, 0x7b, 0x00,
610x63, 0x6f, 0x6d, 0x65, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 610x12, 0x73, 0x19, 0x01, 0xf2, 0x11, 0x63, 0x6f, 0x6d, 0x65,
620x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x00, 0x73, 0x77, 620x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 0x63, 0x6f, 0x6e, 0x74,
630x61, 0x70, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x73, 630x61, 0x63, 0x74, 0x00, 0x73, 0x77, 0x61, 0x70, 0x00, 0x63,
640x2e, 0x29, 0x89, 0x01, 0x33, 0x6a, 0x6f, 0x62, 0x88, 0x01, 640x6f, 0x6c, 0x6f, 0x75, 0x72, 0x73, 0x2e, 0x29, 0x89, 0x01,
650x71, 0x67, 0x65, 0x74, 0x00, 0x61, 0x6c, 0x6c, 0xd3, 0x01, 650x33, 0x6a, 0x6f, 0x62, 0x88, 0x01, 0x71, 0x67, 0x65, 0x74,
660x07, 0xa5, 0x00, 0x13, 0x73, 0xbe, 0x00, 0x00, 0x62, 0x00, 660x00, 0x61, 0x6c, 0x6c, 0xd3, 0x01, 0x07, 0xa5, 0x00, 0x13,
670x00, 0x1b, 0x00, 0x02, 0x62, 0x00, 0x08, 0xde, 0x00, 0x22, 670x73, 0xbe, 0x00, 0x00, 0x62, 0x00, 0x00, 0x1b, 0x00, 0x02,
680x61, 0x74, 0x1d, 0x00, 0x00, 0x43, 0x02, 0x20, 0x74, 0x69, 680x62, 0x00, 0x08, 0xde, 0x00, 0x22, 0x61, 0x74, 0x1d, 0x00,
690x62, 0x02, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x79, 690x00, 0x43, 0x02, 0x20, 0x74, 0x69, 0x62, 0x02, 0x74, 0x43,
700xe9, 0x01, 0x00, 0x9f, 0x02, 0x50, 0x64, 0x00, 0x74, 0x72, 700x6f, 0x75, 0x6e, 0x74, 0x00, 0x79, 0xe9, 0x01, 0x00, 0x9f,
710x79, 0x48, 0x00, 0x20, 0x64, 0x6f, 0xb3, 0x01, 0x20, 0x69, 710x02, 0x50, 0x64, 0x00, 0x74, 0x72, 0x79, 0x48, 0x00, 0x20,
720x6e, 0x96, 0x02, 0x21, 0x66, 0x65, 0x9d, 0x02, 0xf2, 0x02, 720x64, 0x6f, 0xb3, 0x01, 0x20, 0x69, 0x6e, 0x96, 0x02, 0x21,
730x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x2e, 0x00, 730x66, 0x65, 0x9d, 0x02, 0xf2, 0x02, 0x70, 0x6f, 0x73, 0x73,
740x00, 0x00, 0x55, 0x6e, 0x6c, 0x69, 0x6b, 0x11, 0x02, 0x04, 740x69, 0x62, 0x6c, 0x65, 0x2e, 0x00, 0x00, 0x00, 0x55, 0x6e,
750xc6, 0x02, 0x06, 0xa2, 0x02, 0xf2, 0x01, 0x2c, 0x00, 0x6d, 750x6c, 0x69, 0x6b, 0x11, 0x02, 0x04, 0xc6, 0x02, 0x06, 0xa2,
760x79, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 760x02, 0xf2, 0x01, 0x2c, 0x00, 0x6d, 0x79, 0x00, 0x76, 0x65,
770x68, 0x61, 0x73, 0xf1, 0x01, 0x50, 0x64, 0x69, 0x74, 0x69, 770x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x68, 0x61, 0x73, 0xf1,
780x6f, 0x28, 0x00, 0xd0, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 780x01, 0x50, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x28, 0x00, 0xd0,
790x65, 0x3a, 0x00, 0x6f, 0x6e, 0x63, 0x65, 0x7f, 0x00, 0xc1, 790x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a, 0x00, 0x6f,
800x27, 0x76, 0x65, 0x00, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 800x6e, 0x63, 0x65, 0x7f, 0x00, 0xc1, 0x27, 0x76, 0x65, 0x00,
810x65, 0x64, 0x53, 0x00, 0x01, 0xe7, 0x02, 0x43, 0x77, 0x69, 810x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x53, 0x00,
820x74, 0x68, 0x8e, 0x02, 0x01, 0x47, 0x01, 0x30, 0x69, 0x6e, 820x01, 0xe7, 0x02, 0x43, 0x77, 0x69, 0x74, 0x68, 0x8e, 0x02,
830x67, 0xe4, 0x00, 0x14, 0x61, 0xf8, 0x01, 0x00, 0xe8, 0x02, 830x01, 0x47, 0x01, 0x30, 0x69, 0x6e, 0x67, 0xe4, 0x00, 0x14,
840x11, 0x2c, 0x65, 0x01, 0xa2, 0x63, 0x61, 0x6e, 0x00, 0x63, 840x61, 0xf8, 0x01, 0x00, 0xe8, 0x02, 0x11, 0x2c, 0x65, 0x01,
850x68, 0x61, 0x6e, 0x67, 0x65, 0xc0, 0x01, 0xa2, 0x74, 0x72, 850xa2, 0x63, 0x61, 0x6e, 0x00, 0x63, 0x68, 0x61, 0x6e, 0x67,
860x69, 0x61, 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x0d, 0x03, 860x65, 0xc0, 0x01, 0xa2, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67,
870x00, 0xd7, 0x00, 0x02, 0xf9, 0x01, 0x20, 0x6e, 0x79, 0x13, 870x75, 0x6c, 0x61, 0x72, 0x0d, 0x03, 0x00, 0xd7, 0x00, 0x02,
880x01, 0xe0, 0x61, 0x00, 0x74, 0x65, 0x74, 0x72, 0x61, 0x68, 880xf9, 0x01, 0x20, 0x6e, 0x79, 0x13, 0x01, 0xe0, 0x61, 0x00,
890x65, 0x64, 0x72, 0x6f, 0x6e, 0x2c, 0x9e, 0x00, 0x33, 0x6f, 890x74, 0x65, 0x74, 0x72, 0x61, 0x68, 0x65, 0x64, 0x72, 0x6f,
900x63, 0x74, 0x0f, 0x00, 0x30, 0x00, 0x6f, 0x72, 0x11, 0x00, 900x6e, 0x2c, 0x9e, 0x00, 0x33, 0x6f, 0x63, 0x74, 0x0f, 0x00,
910x43, 0x69, 0x63, 0x6f, 0x73, 0x12, 0x00, 0x00, 0xef, 0x00, 910x30, 0x00, 0x6f, 0x72, 0x11, 0x00, 0x43, 0x69, 0x63, 0x6f,
920xf0, 0x13, 0x5b, 0x32, 0x5d, 0x00, 0x68, 0x74, 0x74, 0x70, 920x73, 0x12, 0x00, 0x00, 0xef, 0x00, 0xf0, 0x13, 0x5b, 0x32,
930x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x33, 0x2e, 0x73, 0x79, 930x5d, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
940x6d, 0x70, 0x61, 0x74, 0x69, 0x63, 0x6f, 0x2e, 0x63, 0x61, 940x77, 0x77, 0x33, 0x2e, 0x73, 0x79, 0x6d, 0x70, 0x61, 0x74,
950x2f, 0x70, 0x61, 0x75, 0x6c, 0x73, 0x87, 0x03, 0x10, 0x2f, 950x69, 0x63, 0x6f, 0x2e, 0x63, 0x61, 0x2f, 0x70, 0x61, 0x75,
960xac, 0x00, 0x11, 0x2f, 0x3f, 0x03, 0x92, 0x68, 0x74, 0x6d, 960x6c, 0x73, 0x87, 0x03, 0x10, 0x2f, 0xac, 0x00, 0x11, 0x2f,
970x00, 0x00, 0x00, 0x34, 0x2e, 0x31, 0x03, 0x04, 0x00, 0xdb, 970x3f, 0x03, 0x92, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x34,
980x01, 0x45, 0x72, 0x6f, 0x6c, 0x73, 0x0c, 0x04, 0x01, 0xe2, 980x2e, 0x31, 0x13, 0x04, 0x00, 0xdb, 0x01, 0x54, 0x72, 0x6f,
990x00, 0x00, 0xb8, 0x00, 0x92, 0x62, 0x65, 0x00, 0x70, 0x6c, 990x6c, 0x73, 0x20, 0x0c, 0x04, 0x01, 0xe2, 0x00, 0x00, 0xb8,
1000x61, 0x79, 0x65, 0x64, 0xf0, 0x00, 0x21, 0x65, 0x69, 0x20, 1000x00, 0x92, 0x62, 0x65, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x65,
1010x04, 0x00, 0x05, 0x01, 0xa2, 0x6b, 0x65, 0x79, 0x62, 0x6f, 1010x64, 0xf0, 0x00, 0x21, 0x65, 0x69, 0x20, 0x04, 0x00, 0x05,
1020x61, 0x72, 0x64, 0x00, 0x6f, 0x10, 0x00, 0x41, 0x6d, 0x6f, 1020x01, 0xa2, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64,
1030x75, 0x73, 0x7c, 0x01, 0xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 1030x00, 0x6f, 0x10, 0x00, 0x41, 0x6d, 0x6f, 0x75, 0x73, 0x7c,
1040x63, 0x6c, 0x69, 0x63, 0x6b, 0x13, 0x01, 0x72, 0x61, 0x6e, 1040x01, 0xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69,
1050x79, 0x77, 0x68, 0x65, 0x72, 0xbe, 0x02, 0x10, 0x68, 0x37, 1050x63, 0x6b, 0x13, 0x01, 0x72, 0x61, 0x6e, 0x79, 0x77, 0x68,
1060x01, 0x92, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x77, 0x69, 0x6c, 1060x65, 0x72, 0xbe, 0x02, 0x10, 0x68, 0x37, 0x01, 0x92, 0x6e,
1070x6c, 0xbf, 0x03, 0x05, 0x01, 0x02, 0x42, 0x28, 0x6f, 0x72, 1070x64, 0x6f, 0x77, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0xbf, 0x03,
1080x00, 0x7f, 0x04, 0xe6, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x29, 1080x05, 0x01, 0x02, 0x42, 0x28, 0x6f, 0x72, 0x00, 0x7f, 0x04,
1090x00, 0x74, 0x6f, 0x77, 0x61, 0x72, 0x64, 0x73, 0x5e, 0x00, 1090xe6, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x29, 0x00, 0x74, 0x6f,
1100x91, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2e, 1100x77, 0x61, 0x72, 0x64, 0x73, 0x5e, 0x00, 0x91, 0x00, 0x70,
1110xa7, 0x00, 0x09, 0xec, 0x03, 0x10, 0x63, 0xc4, 0x01, 0x21, 1110x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0xa7, 0x00, 0x09,
1120x6c, 0x73, 0x08, 0x04, 0x1e, 0x64, 0xfa, 0x03, 0x68, 0x6f, 1120xec, 0x03, 0x10, 0x63, 0xc4, 0x01, 0x21, 0x6c, 0x73, 0x08,
1130x6e, 0x00, 0x69, 0x74, 0x73, 0x9b, 0x01, 0x22, 0x00, 0x69, 1130x04, 0x1e, 0x64, 0xfa, 0x03, 0x68, 0x6f, 0x6e, 0x00, 0x69,
1140x90, 0x00, 0x10, 0x66, 0x5f, 0x02, 0x41, 0x63, 0x61, 0x72, 1140x74, 0x73, 0x9b, 0x01, 0x22, 0x00, 0x69, 0x90, 0x00, 0x10,
1150x64, 0x24, 0x02, 0x50, 0x64, 0x69, 0x72, 0x65, 0x63, 0x08, 1150x66, 0x5f, 0x02, 0x41, 0x63, 0x61, 0x72, 0x64, 0x24, 0x02,
1160x02, 0x42, 0x73, 0x2e, 0x00, 0x4f, 0x21, 0x00, 0x0b, 0xae, 1160x50, 0x64, 0x69, 0x72, 0x65, 0x63, 0x08, 0x02, 0x42, 0x73,
1170x01, 0x22, 0x73, 0x2c, 0x90, 0x00, 0x31, 0x61, 0x70, 0x70, 1170x2e, 0x00, 0x4f, 0x21, 0x00, 0x0b, 0xae, 0x01, 0x22, 0x73,
1180xf2, 0x01, 0x1b, 0x66, 0x72, 0x04, 0x06, 0x42, 0x00, 0x00, 1180x2c, 0x90, 0x00, 0x31, 0x61, 0x70, 0x70, 0xf2, 0x01, 0x1b,
1190x11, 0x03, 0xf2, 0x0a, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x61, 1190x66, 0x72, 0x04, 0x06, 0x42, 0x00, 0x00, 0x11, 0x03, 0xf2,
1200x70, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 1200x0a, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x61, 0x70, 0x70, 0x72,
1210x2e, 0x00, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0xfd, 1210x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x2e, 0x00, 0x56,
1220x00, 0x10, 0x6d, 0x63, 0x04, 0xb0, 0x69, 0x73, 0x00, 0x64, 1220x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0xfd, 0x00, 0x10, 0x6d,
1230x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x67, 0x01, 0x01, 1230x63, 0x04, 0xb0, 0x69, 0x73, 0x00, 0x64, 0x69, 0x73, 0x61,
1240x2d, 0x01, 0x10, 0x69, 0xc1, 0x03, 0xe0, 0x65, 0x73, 0x6e, 1240x6c, 0x6c, 0x6f, 0x77, 0x67, 0x01, 0x01, 0x2d, 0x01, 0x10,
1250x27, 0x74, 0x00, 0x6d, 0x61, 0x6b, 0x65, 0x00, 0x73, 0x65, 1250x69, 0xc1, 0x03, 0xe0, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x00,
1260x6e, 0x5d, 0x01, 0x14, 0x54, 0xb1, 0x00, 0x01, 0x6e, 0x00, 1260x6d, 0x61, 0x6b, 0x65, 0x00, 0x73, 0x65, 0x6e, 0x5d, 0x01,
1270x80, 0x73, 0x75, 0x72, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x88, 1270x14, 0x54, 0xb1, 0x00, 0x01, 0x6e, 0x00, 0x80, 0x73, 0x75,
1280x00, 0x0b, 0xfb, 0x04, 0x03, 0x6d, 0x01, 0x70, 0x6e, 0x75, 1280x72, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x88, 0x00, 0x0b, 0xfb,
1290x6d, 0x65, 0x72, 0x69, 0x63, 0x14, 0x00, 0xc0, 0x70, 0x61, 1290x04, 0x03, 0x6d, 0x01, 0x70, 0x6e, 0x75, 0x6d, 0x65, 0x72,
1300x64, 0x00, 0x28, 0x60, 0x37, 0x27, 0x2c, 0x00, 0x60, 0x39, 1300x69, 0x63, 0x14, 0x00, 0xc0, 0x70, 0x61, 0x64, 0x00, 0x28,
1310x05, 0x00, 0x10, 0x31, 0x05, 0x00, 0x34, 0x33, 0x27, 0x29, 1310x60, 0x37, 0x27, 0x2c, 0x00, 0x60, 0x39, 0x05, 0x00, 0x10,
1320xe6, 0x01, 0x01, 0x38, 0x01, 0xa7, 0x66, 0x6f, 0x72, 0x00, 1320x31, 0x05, 0x00, 0x34, 0x33, 0x27, 0x29, 0xe6, 0x01, 0x01,
1330x64, 0x69, 0x61, 0x67, 0x6f, 0x6e, 0xa1, 0x00, 0x00, 0x6e, 1330x38, 0x01, 0xa7, 0x66, 0x6f, 0x72, 0x00, 0x64, 0x69, 0x61,
1340x01, 0x23, 0x28, 0x41, 0x4e, 0x01, 0x13, 0x61, 0xdc, 0x00, 1340x67, 0x6f, 0x6e, 0xa1, 0x00, 0x00, 0x6e, 0x01, 0x23, 0x28,
1350x81, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 1350x41, 0x4e, 0x01, 0x13, 0x61, 0xdc, 0x00, 0x81, 0x64, 0x65,
1360x01, 0x12, 0x73, 0xf2, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 1360x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x01, 0x12, 0x73,
1370xbe, 0x05, 0x01, 0x89, 0x01, 0x60, 0x61, 0x76, 0x61, 0x69, 1370xf2, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 0xbe, 0x05, 0x01,
1380x6c, 0x61, 0x90, 0x03, 0x11, 0x29, 0x6b, 0x02, 0x12, 0x32, 1380x89, 0x01, 0x60, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x90,
1390x6b, 0x02, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 1390x03, 0x11, 0x29, 0x6b, 0x02, 0x12, 0x32, 0x6b, 0x02, 0x93,
1400x65, 0x72, 0x6d, 0x02, 0x10, 0x65, 0xd7, 0x01, 0x05, 0x14, 1400x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x6d,
1410x00, 0x02, 0x3f, 0x00, 0x04, 0x3a, 0x00, 0x51, 0x00, 0x66, 1410x02, 0x10, 0x65, 0xd7, 0x01, 0x05, 0x14, 0x00, 0x02, 0x3f,
1420x72, 0x6f, 0x6d, 0x77, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 1420x00, 0x04, 0x3a, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d,
1430x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 1430x77, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
1440x6d, 0x00, 0x03, 0xe3, 0x00, 0xb1, 0x60, 0x54, 0x79, 0x70, 1440x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6d, 0x00, 0x03,
1450x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x17, 0x02, 0x71, 1450xe3, 0x00, 0xb1, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1460x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x43, 0x02, 0x92, 1460x6d, 0x65, 0x6e, 0x75, 0x17, 0x02, 0x71, 0x79, 0x70, 0x65,
1470x00, 0x00, 0x00, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x44, 1470x20, 0x6f, 0x66, 0x20, 0x43, 0x02, 0x92, 0x00, 0x00, 0x00,
1480x02, 0x02, 0x14, 0x00, 0x04, 0x1c, 0x02, 0x10, 0x28, 0x77, 1480x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x44, 0x02, 0x02, 0x14,
1490x03, 0x20, 0x68, 0x65, 0xe4, 0x03, 0x01, 0x1d, 0x00, 0x35, 1490x00, 0x04, 0x1c, 0x02, 0x10, 0x28, 0x77, 0x03, 0x20, 0x68,
1500x68, 0x61, 0x70, 0x6f, 0x05, 0x00, 0xe9, 0x01, 0x2a, 0x29, 1500x65, 0xe4, 0x03, 0x01, 0x1d, 0x00, 0x35, 0x68, 0x61, 0x70,
1510x3a, 0x87, 0x03, 0x02, 0x22, 0x05, 0x06, 0x8a, 0x03, 0x10, 1510x6f, 0x05, 0x00, 0xe9, 0x01, 0x2a, 0x29, 0x3a, 0x87, 0x03,
1520x2c, 0xf9, 0x02, 0x0b, 0x88, 0x03, 0xf0, 0x0c, 0x57, 0x69, 1520x02, 0x22, 0x05, 0x06, 0x8a, 0x03, 0x10, 0x2c, 0xf9, 0x02,
1530x64, 0x74, 0x68, 0x20, 0x2f, 0x20, 0x74, 0x6f, 0x70, 0x2c, 1530x0b, 0x88, 0x03, 0xf0, 0x0c, 0x57, 0x69, 0x64, 0x74, 0x68,
1540x00, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x2f, 0x20, 1540x20, 0x2f, 0x20, 0x74, 0x6f, 0x70, 0x2c, 0x00, 0x48, 0x65,
1550x62, 0x6f, 0x74, 0x74, 0x6f, 0x70, 0x03, 0x1d, 0x4f, 0x1f, 1550x69, 0x67, 0x68, 0x74, 0x20, 0x2f, 0x20, 0x62, 0x6f, 0x74,
1560x04, 0x50, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x58, 0x05, 0x11, 1560x74, 0x6f, 0x70, 0x03, 0x1d, 0x4f, 0x1f, 0x04, 0x50, 0x68,
1570x6c, 0x06, 0x04, 0x14, 0x76, 0x1d, 0x02, 0x65, 0x64, 0x69, 1570x6f, 0x72, 0x69, 0x7a, 0x58, 0x05, 0x11, 0x6c, 0x06, 0x04,
1580x6d, 0x65, 0x6e, 0x73, 0x88, 0x02, 0x0d, 0x34, 0x04, 0x14, 1580x14, 0x76, 0x1d, 0x02, 0x65, 0x64, 0x69, 0x6d, 0x65, 0x6e,
1590x2c, 0xdf, 0x01, 0x10, 0x62, 0xae, 0x07, 0x13, 0x66, 0x1f, 1590x73, 0x88, 0x02, 0x0d, 0x34, 0x04, 0x14, 0x2c, 0xdf, 0x01,
1600x00, 0x25, 0x6c, 0x65, 0xfa, 0x01, 0x31, 0x74, 0x6f, 0x70, 1600x10, 0x62, 0xae, 0x07, 0x13, 0x66, 0x1f, 0x00, 0x25, 0x6c,
1610x52, 0x00, 0x03, 0x7c, 0x00, 0x40, 0x72, 0x6f, 0x77, 0x73, 1610x65, 0xfa, 0x01, 0x31, 0x74, 0x6f, 0x70, 0x52, 0x00, 0x03,
1620x41, 0x07, 0xb0, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 1620x7c, 0x00, 0x40, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x07, 0xb0,
1630x6c, 0x79, 0x2e, 0x00, 1630x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x2e,
1640x00,
164}; 165};
165 166
166const unsigned short help_text_len = 2055; 167const unsigned short help_text_len = 2071;
167const unsigned short help_text_words = 385; 168const unsigned short help_text_words = 386;
169const bool help_valid = true;
168const char quick_help_text[] = "Pick up all the blue squares by rolling the cube over them."; 170const char quick_help_text[] = "Pick up all the blue squares by rolling the cube over them.";
diff --git a/apps/plugins/puzzles/help/dominosa.c b/apps/plugins/puzzles/help/dominosa.c
index 388abb16d4..94d876794b 100644
--- a/apps/plugins/puzzles/help/dominosa.c
+++ b/apps/plugins/puzzles/help/dominosa.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,174 +6,175 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 88, TEXT_CENTER | C_RED }, 9 { 89, TEXT_CENTER | C_RED },
10 { 151, TEXT_UNDERLINE }, 10 { 152, TEXT_UNDERLINE },
11 { 267, TEXT_CENTER | C_RED }, 11 { 268, TEXT_CENTER | C_RED },
12 { 284, TEXT_UNDERLINE }, 12 { 285, TEXT_UNDERLINE },
13 { 336, TEXT_UNDERLINE }, 13 { 337, TEXT_UNDERLINE },
14 { 377, TEXT_UNDERLINE }, 14 { 378, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 2278 comp 1541 ratio 0.676471 level 10 saved 737 */ 18/* orig 2299 comp 1550 ratio 0.674206 level 10 saved 749 */
19const char help_text[] = { 19const char help_text[] = {
200xf1, 0x1a, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x31, 0x37, 0x3a, 0x20, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x6f, 210x31, 0x37, 0x3a, 0x20, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x6f,
220x73, 0x61, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x6e, 0x6f, 220x73, 0x61, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf1, 0x05,
230x72, 0x6d, 0x61, 0x6c, 0x00, 0x73, 0x65, 0x74, 0x00, 0x6f, 230x00, 0x00, 0x00, 0x41, 0x00, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
240x66, 0x00, 0x64, 0x1c, 0x00, 0xf0, 0x0b, 0x65, 0x73, 0x00, 240x6c, 0x00, 0x73, 0x65, 0x74, 0x00, 0x6f, 0x66, 0x00, 0x64,
250x2d, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x69, 0x73, 0x2c, 250x31, 0x00, 0xf0, 0x0b, 0x65, 0x73, 0x00, 0x2d, 0x00, 0x74,
260x00, 0x6f, 0x6e, 0x65, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 260x68, 0x61, 0x74, 0x00, 0x69, 0x73, 0x2c, 0x00, 0x6f, 0x6e,
270x6e, 0x63, 0x65, 0x24, 0x00, 0xf0, 0x07, 0x65, 0x76, 0x65, 270x65, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
280x72, 0x79, 0x00, 0x28, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 280x24, 0x00, 0xf0, 0x07, 0x65, 0x76, 0x65, 0x72, 0x79, 0x00,
290x72, 0x65, 0x64, 0x29, 0x00, 0x70, 0x61, 0x69, 0x72, 0x1a, 290x28, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64,
300x00, 0xf0, 0x3b, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 300x29, 0x00, 0x70, 0x61, 0x69, 0x72, 0x1a, 0x00, 0xf0, 0x3b,
310x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x30, 0x00, 0x74, 0x6f, 310x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x00, 0x66, 0x72,
320x00, 0x36, 0x00, 0x2d, 0x00, 0x68, 0x61, 0x73, 0x00, 0x62, 320x6f, 0x6d, 0x00, 0x30, 0x00, 0x74, 0x6f, 0x00, 0x36, 0x00,
330x65, 0x65, 0x6e, 0x00, 0x61, 0x72, 0x72, 0x61, 0x6e, 0x67, 330x2d, 0x00, 0x68, 0x61, 0x73, 0x00, 0x62, 0x65, 0x65, 0x6e,
340x65, 0x64, 0x00, 0x69, 0x72, 0x72, 0x65, 0x67, 0x75, 0x6c, 340x00, 0x61, 0x72, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00,
350x61, 0x72, 0x6c, 0x79, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 350x69, 0x72, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x6c,
360x61, 0x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c, 360x79, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 0x61, 0x00, 0x72,
370x65, 0x3b, 0x00, 0x74, 0x68, 0x65, 0x6e, 0x05, 0x00, 0x03, 370x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x3b, 0x00,
380x4f, 0x00, 0xf6, 0x00, 0x00, 0x69, 0x6e, 0x00, 0x65, 0x61, 380x74, 0x68, 0x65, 0x6e, 0x05, 0x00, 0x03, 0x4f, 0x00, 0xf6,
390x63, 0x68, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x4f, 390x00, 0x00, 0x69, 0x6e, 0x00, 0x65, 0x61, 0x63, 0x68, 0x00,
400x00, 0xf1, 0x01, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 400x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x4f, 0x00, 0xf1, 0x01,
410x00, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x61, 0x6e, 0x64, 0x34, 410x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x64, 0x6f,
420x00, 0x05, 0xc1, 0x00, 0xf0, 0x11, 0x74, 0x68, 0x65, 0x6d, 420x77, 0x6e, 0x00, 0x61, 0x6e, 0x64, 0x34, 0x00, 0x05, 0xc1,
430x73, 0x65, 0x6c, 0x76, 0x65, 0x73, 0x00, 0x72, 0x65, 0x6d, 430x00, 0xf0, 0x11, 0x74, 0x68, 0x65, 0x6d, 0x73, 0x65, 0x6c,
440x6f, 0x76, 0x65, 0x64, 0x2e, 0x00, 0x59, 0x6f, 0x75, 0x72, 440x76, 0x65, 0x73, 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65,
450x00, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x69, 0x73, 0x9e, 0x00, 450x64, 0x2e, 0x00, 0x59, 0x6f, 0x75, 0x72, 0x00, 0x74, 0x61,
460xb1, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 460x73, 0x6b, 0x00, 0x69, 0x73, 0x9e, 0x00, 0xb1, 0x72, 0x65,
470x63, 0x74, 0x3d, 0x00, 0xa3, 0x70, 0x61, 0x74, 0x74, 0x65, 470x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3d,
480x72, 0x6e, 0x00, 0x62, 0x79, 0xac, 0x00, 0x31, 0x69, 0x6e, 480x00, 0xa3, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x00,
490x67, 0x19, 0x00, 0x0c, 0x1e, 0x01, 0x82, 0x74, 0x6f, 0x00, 490x62, 0x79, 0xac, 0x00, 0x31, 0x69, 0x6e, 0x67, 0x19, 0x00,
500x6d, 0x61, 0x74, 0x63, 0x68, 0x36, 0x00, 0x71, 0x72, 0x6f, 500x0c, 0x1e, 0x01, 0x82, 0x74, 0x6f, 0x00, 0x6d, 0x61, 0x74,
510x76, 0x69, 0x64, 0x65, 0x64, 0x34, 0x00, 0x17, 0x79, 0x08, 510x63, 0x68, 0x36, 0x00, 0x71, 0x72, 0x6f, 0x76, 0x69, 0x64,
520x01, 0xf0, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 520x65, 0x64, 0x34, 0x00, 0x17, 0x79, 0x08, 0x01, 0xf0, 0x00,
530x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x75, 0x00, 530x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x70,
540xf0, 0x00, 0x77, 0x69, 0x64, 0x65, 0x6c, 0x79, 0x00, 0x63, 540x75, 0x7a, 0x7a, 0x6c, 0x65, 0x75, 0x00, 0xf0, 0x00, 0x77,
550x72, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64, 0x49, 0x00, 0xc2, 550x69, 0x64, 0x65, 0x6c, 0x79, 0x00, 0x63, 0x72, 0x65, 0x64,
560x4f, 0x2e, 0x00, 0x53, 0x2e, 0x00, 0x41, 0x64, 0x6c, 0x65, 560x69, 0x74, 0x65, 0x64, 0x49, 0x00, 0xc2, 0x4f, 0x2e, 0x00,
570x72, 0x2c, 0xc7, 0x00, 0x81, 0x61, 0x6b, 0x65, 0x73, 0x00, 570x53, 0x2e, 0x00, 0x41, 0x64, 0x6c, 0x65, 0x72, 0x2c, 0xc7,
580x70, 0x61, 0x72, 0x74, 0x00, 0x82, 0x69, 0x74, 0x73, 0x00, 580x00, 0x81, 0x61, 0x6b, 0x65, 0x73, 0x00, 0x70, 0x61, 0x72,
590x6e, 0x61, 0x6d, 0x65, 0x55, 0x01, 0x40, 0x74, 0x68, 0x6f, 590x74, 0x00, 0x82, 0x69, 0x74, 0x73, 0x00, 0x6e, 0x61, 0x6d,
600x73, 0x8e, 0x01, 0x51, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x61, 600x65, 0x55, 0x01, 0x40, 0x74, 0x68, 0x6f, 0x73, 0x8e, 0x01,
610x00, 0x46, 0x31, 0x37, 0x2e, 0x31, 0xd3, 0x01, 0x80, 0x63, 610x51, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x61, 0x00, 0x46, 0x31,
620x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0xdc, 0x01, 0xa0, 620x37, 0x2e, 0x31, 0xe8, 0x01, 0xf0, 0x07, 0x63, 0x6f, 0x6e,
630x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 630x74, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x4c,
640xc7, 0x00, 0x41, 0x62, 0x65, 0x74, 0x77, 0x85, 0x01, 0xf5, 640x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xc7,
650x00, 0x6e, 0x79, 0x00, 0x74, 0x77, 0x6f, 0x00, 0x61, 0x64, 650x00, 0x41, 0x62, 0x65, 0x74, 0x77, 0x85, 0x01, 0xf5, 0x00,
660x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0xb5, 0x01, 0x83, 0x70, 660x6e, 0x79, 0x00, 0x74, 0x77, 0x6f, 0x00, 0x61, 0x64, 0x6a,
670x6c, 0x61, 0x63, 0x65, 0x73, 0x00, 0x61, 0xe6, 0x00, 0x63, 670x61, 0x63, 0x65, 0x6e, 0x74, 0xb5, 0x01, 0x83, 0x70, 0x6c,
680x00, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x01, 0x01, 0x53, 0x6d, 680x61, 0x63, 0x65, 0x73, 0x00, 0x61, 0xe6, 0x00, 0x63, 0x00,
690x2c, 0x00, 0x6f, 0x72, 0x48, 0x01, 0x12, 0x73, 0x11, 0x02, 690x63, 0x6f, 0x76, 0x65, 0x72, 0x01, 0x01, 0x53, 0x6d, 0x2c,
700x00, 0x9a, 0x00, 0x00, 0xd2, 0x00, 0xf1, 0x05, 0x61, 0x6c, 700x00, 0x6f, 0x72, 0x48, 0x01, 0x12, 0x73, 0x11, 0x02, 0x00,
710x72, 0x65, 0x61, 0x64, 0x79, 0x00, 0x70, 0x72, 0x65, 0x73, 710x9a, 0x00, 0x00, 0xd2, 0x00, 0xf1, 0x05, 0x61, 0x6c, 0x72,
720x65, 0x6e, 0x74, 0x2e, 0x00, 0x54, 0x72, 0x79, 0x36, 0x00, 720x65, 0x61, 0x64, 0x79, 0x00, 0x70, 0x72, 0x65, 0x73, 0x65,
730x12, 0x6f, 0x52, 0x00, 0x06, 0x51, 0x00, 0x60, 0x77, 0x68, 730x6e, 0x74, 0x2e, 0x00, 0x54, 0x72, 0x79, 0x36, 0x00, 0x12,
740x69, 0x63, 0x68, 0x00, 0x56, 0x00, 0xa0, 0x6c, 0x61, 0x70, 740x6f, 0x52, 0x00, 0x06, 0x51, 0x00, 0x60, 0x77, 0x68, 0x69,
750x73, 0x00, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2a, 0x00, 0x05, 750x63, 0x68, 0x00, 0x56, 0x00, 0xa0, 0x6c, 0x61, 0x70, 0x73,
760x56, 0x01, 0x43, 0x77, 0x69, 0x6c, 0x6c, 0x65, 0x00, 0x01, 760x00, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2a, 0x00, 0x05, 0x56,
770x59, 0x01, 0x40, 0x6f, 0x6e, 0x65, 0x73, 0x66, 0x00, 0x04, 770x01, 0x43, 0x77, 0x69, 0x6c, 0x6c, 0x65, 0x00, 0x01, 0x59,
780x33, 0x00, 0x00, 0xef, 0x00, 0x4e, 0x52, 0x69, 0x67, 0x68, 780x01, 0x40, 0x6f, 0x6e, 0x65, 0x73, 0x66, 0x00, 0x04, 0x33,
790xd6, 0x00, 0x0f, 0xd2, 0x00, 0x03, 0x40, 0x64, 0x72, 0x61, 790x00, 0x00, 0xef, 0x00, 0x4e, 0x52, 0x69, 0x67, 0x68, 0xd6,
800x77, 0xd1, 0x00, 0x46, 0x6c, 0x69, 0x6e, 0x65, 0x2a, 0x00, 800x00, 0x0f, 0xd2, 0x00, 0x03, 0x40, 0x64, 0x72, 0x61, 0x77,
810x01, 0xce, 0x00, 0x02, 0x8c, 0x00, 0xb2, 0x79, 0x6f, 0x75, 810xd1, 0x00, 0x46, 0x6c, 0x69, 0x6e, 0x65, 0x2a, 0x00, 0x01,
820x00, 0x63, 0x61, 0x6e, 0x00, 0x75, 0x73, 0x65, 0x0f, 0x02, 820xce, 0x00, 0x02, 0x8c, 0x00, 0xb2, 0x79, 0x6f, 0x75, 0x00,
830x40, 0x6d, 0x69, 0x6e, 0x64, 0x16, 0x00, 0x52, 0x72, 0x73, 830x63, 0x61, 0x6e, 0x00, 0x75, 0x73, 0x65, 0x0f, 0x02, 0x40,
840x65, 0x6c, 0x66, 0x02, 0x03, 0x00, 0x24, 0x00, 0x43, 0x6b, 840x6d, 0x69, 0x6e, 0x64, 0x16, 0x00, 0x52, 0x72, 0x73, 0x65,
850x6e, 0x6f, 0x77, 0x7b, 0x01, 0x00, 0x69, 0x00, 0x04, 0x60, 850x6c, 0x66, 0x02, 0x03, 0x00, 0x24, 0x00, 0x43, 0x6b, 0x6e,
860x00, 0x00, 0x8e, 0x02, 0x32, 0x6e, 0x6f, 0x74, 0x2a, 0x01, 860x6f, 0x77, 0x7b, 0x01, 0x00, 0x69, 0x00, 0x04, 0x60, 0x00,
870x21, 0x65, 0x64, 0x37, 0x02, 0x30, 0x00, 0x73, 0x69, 0xc6, 870x00, 0x8e, 0x02, 0x32, 0x6e, 0x6f, 0x74, 0x2a, 0x01, 0x21,
880x02, 0x03, 0xd5, 0x00, 0x1c, 0x2e, 0xb0, 0x00, 0x55, 0x61, 880x65, 0x64, 0x37, 0x02, 0x30, 0x00, 0x73, 0x69, 0xc6, 0x02,
890x67, 0x61, 0x69, 0x6e, 0x49, 0x01, 0x00, 0xe5, 0x00, 0x00, 890x03, 0xd5, 0x00, 0x1c, 0x2e, 0xb0, 0x00, 0x55, 0x61, 0x67,
900x9d, 0x00, 0x00, 0xd9, 0x00, 0x13, 0x59, 0x8c, 0x00, 0x42, 900x61, 0x69, 0x6e, 0x49, 0x01, 0x00, 0xe5, 0x00, 0x00, 0x9d,
910x61, 0x6c, 0x73, 0x6f, 0x91, 0x00, 0xd2, 0x68, 0x65, 0x00, 910x00, 0x00, 0xd9, 0x00, 0x13, 0x59, 0x8c, 0x00, 0x42, 0x61,
920x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 920x6c, 0x73, 0x6f, 0x91, 0x00, 0xd2, 0x68, 0x65, 0x00, 0x63,
930x74, 0x02, 0x00, 0x1a, 0x01, 0x14, 0x61, 0x16, 0x00, 0x43, 930x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x74,
940x61, 0x72, 0x6f, 0x75, 0xf6, 0x02, 0x74, 0x67, 0x72, 0x69, 940x02, 0x00, 0x1a, 0x01, 0x14, 0x61, 0x16, 0x00, 0x43, 0x61,
950x64, 0x2e, 0x00, 0x57, 0x39, 0x03, 0x03, 0x21, 0x00, 0xbf, 950x72, 0x6f, 0x75, 0xf6, 0x02, 0x74, 0x67, 0x72, 0x69, 0x64,
960x69, 0x73, 0x00, 0x68, 0x61, 0x6c, 0x66, 0x00, 0x77, 0x61, 960x2e, 0x00, 0x57, 0x39, 0x03, 0x03, 0x21, 0x00, 0xbf, 0x69,
970x79, 0x29, 0x01, 0x0a, 0x11, 0x2c, 0xbd, 0x01, 0x14, 0x73, 970x73, 0x00, 0x68, 0x61, 0x6c, 0x66, 0x00, 0x77, 0x61, 0x79,
980xed, 0x02, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x75, 980x29, 0x01, 0x0a, 0x11, 0x2c, 0xbd, 0x01, 0x14, 0x73, 0xed,
990x00, 0x02, 0x92, 0x01, 0x0b, 0xc7, 0x01, 0x07, 0x18, 0x02, 990x02, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x75, 0x00,
1000x00, 0x11, 0x01, 0x05, 0x44, 0x00, 0x2a, 0x6f, 0x72, 0x47, 1000x02, 0x92, 0x01, 0x0b, 0xc7, 0x01, 0x07, 0x18, 0x02, 0x00,
1010x00, 0x20, 0x73, 0x70, 0x37, 0x00, 0x32, 0x62, 0x61, 0x72, 1010x11, 0x01, 0x05, 0x44, 0x00, 0x2a, 0x6f, 0x72, 0x47, 0x00,
1020x46, 0x00, 0x3e, 0x6c, 0x61, 0x79, 0x8b, 0x01, 0x02, 0x50, 1020x20, 0x73, 0x70, 0x37, 0x00, 0x32, 0x62, 0x61, 0x72, 0x46,
1030x01, 0x02, 0xd9, 0x03, 0x81, 0x73, 0x2e, 0x00, 0x52, 0x65, 1030x00, 0x3e, 0x6c, 0x61, 0x79, 0x8b, 0x01, 0x02, 0x50, 0x01,
1040x70, 0x65, 0x61, 0x14, 0x02, 0xca, 0x65, 0x69, 0x74, 0x68, 1040x02, 0xd9, 0x03, 0x81, 0x73, 0x2e, 0x00, 0x52, 0x65, 0x70,
1050x65, 0x72, 0x00, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x30, 0x01, 1050x65, 0x61, 0x14, 0x02, 0xca, 0x65, 0x69, 0x74, 0x68, 0x65,
1060x03, 0x86, 0x00, 0x25, 0x6f, 0x72, 0x3a, 0x01, 0x14, 0x50, 1060x72, 0x00, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x30, 0x01, 0x03,
1070x76, 0x00, 0x14, 0x61, 0x32, 0x04, 0x05, 0xbb, 0x00, 0x50, 1070x86, 0x00, 0x25, 0x6f, 0x72, 0x3a, 0x01, 0x14, 0x50, 0x76,
1080x68, 0x69, 0x67, 0x68, 0x6c, 0x82, 0x01, 0xe0, 0x00, 0x61, 1080x00, 0x14, 0x61, 0x32, 0x04, 0x05, 0xbb, 0x00, 0x50, 0x68,
1090x6c, 0x6c, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 1090x69, 0x67, 0x68, 0x6c, 0x82, 0x01, 0xe0, 0x00, 0x61, 0x6c,
1100x6e, 0x63, 0xc4, 0x02, 0x03, 0xdf, 0x01, 0x02, 0x32, 0x00, 1100x6c, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
1110x16, 0x2e, 0x45, 0x00, 0x07, 0x16, 0x00, 0x03, 0xae, 0x01, 1110x63, 0xc4, 0x02, 0x03, 0xdf, 0x01, 0x02, 0x32, 0x00, 0x16,
1120x01, 0x4a, 0x00, 0x51, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x81, 1120x2e, 0x45, 0x00, 0x07, 0x16, 0x00, 0x03, 0xae, 0x01, 0x01,
1130x00, 0x05, 0x54, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x00, 1130x4a, 0x00, 0x51, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x81, 0x00,
1140x55, 0x70, 0x99, 0x01, 0x00, 0xc6, 0x00, 0x68, 0x64, 0x69, 1140x05, 0x54, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x00, 0x55,
1150x66, 0x66, 0x65, 0x72, 0x80, 0x02, 0x00, 0xcf, 0x01, 0x17, 1150x70, 0x99, 0x01, 0x00, 0xc6, 0x00, 0x68, 0x64, 0x69, 0x66,
1160x62, 0x31, 0x00, 0x00, 0x23, 0x04, 0x11, 0x74, 0x81, 0x03, 1160x66, 0x65, 0x72, 0x80, 0x02, 0x00, 0xcf, 0x01, 0x17, 0x62,
1170x30, 0x67, 0x69, 0x76, 0x00, 0x01, 0x21, 0x69, 0x6d, 0xc1, 1170x31, 0x00, 0x00, 0x23, 0x04, 0x11, 0x74, 0x81, 0x03, 0x30,
1180x00, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x5b, 0x00, 0x02, 0xef, 1180x67, 0x69, 0x76, 0x00, 0x01, 0x21, 0x69, 0x6d, 0xc1, 0x00,
1190x00, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 1190x41, 0x28, 0x41, 0x6c, 0x6c, 0x5b, 0x00, 0x02, 0xef, 0x00,
1200x62, 0x65, 0x64, 0xfc, 0x04, 0x22, 0x73, 0x65, 0x05, 0x01, 1200xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62,
1210x31, 0x32, 0x2e, 0x31, 0x72, 0x02, 0x01, 0x21, 0x02, 0xb2, 1210x65, 0x64, 0xfc, 0x04, 0x22, 0x73, 0x65, 0x05, 0x01, 0x31,
1220x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 1220x32, 0x2e, 0x31, 0x72, 0x02, 0x01, 0x21, 0x02, 0xb2, 0x61,
1230x29, 0x04, 0x04, 0x16, 0x32, 0x04, 0x04, 0xb1, 0x70, 0x61, 1230x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29,
1240x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x81, 1240x04, 0x04, 0x16, 0x32, 0x04, 0x04, 0xb1, 0x70, 0x61, 0x72,
1250x04, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x44, 1250x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x81, 0x04,
1260x00, 0x04, 0x3f, 0x00, 0x04, 0x56, 0x04, 0xf1, 0x01, 0x65, 1260x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x44, 0x00,
1270x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 1270x04, 0x3f, 0x00, 0x04, 0x56, 0x04, 0xf1, 0x01, 0x65, 0x00,
1280x2e, 0x27, 0x00, 0x6f, 0x70, 0x72, 0x00, 0x22, 0x6f, 0x6e, 1280x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e,
1290x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 1290x27, 0x00, 0x6f, 0x70, 0x72, 0x00, 0x22, 0x6f, 0x6e, 0x1a,
1300x65, 0x6e, 0x75, 0xae, 0x00, 0x82, 0x4d, 0x61, 0x78, 0x69, 1300x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65,
1310x6d, 0x75, 0x6d, 0x20, 0xe6, 0x00, 0x45, 0x20, 0x6f, 0x6e, 1310x6e, 0x75, 0xae, 0x00, 0x82, 0x4d, 0x61, 0x78, 0x69, 0x6d,
1320x20, 0xc1, 0x03, 0x33, 0x00, 0x00, 0x43, 0x80, 0x04, 0x02, 1320x75, 0x6d, 0x20, 0xe6, 0x00, 0x45, 0x20, 0x6f, 0x6e, 0x20,
1330x02, 0x02, 0x32, 0x69, 0x7a, 0x65, 0x69, 0x01, 0x13, 0x65, 1330xc1, 0x03, 0x33, 0x00, 0x00, 0x43, 0x80, 0x04, 0x02, 0x02,
1340x03, 0x05, 0x10, 0x2c, 0x24, 0x03, 0x03, 0xa4, 0x04, 0x15, 1340x02, 0x32, 0x69, 0x7a, 0x65, 0x69, 0x01, 0x13, 0x65, 0x03,
1350x6c, 0x29, 0x02, 0x07, 0x27, 0x00, 0x0c, 0x69, 0x05, 0x42, 1350x05, 0x10, 0x2c, 0x24, 0x03, 0x03, 0xa4, 0x04, 0x15, 0x6c,
1360x75, 0x73, 0x65, 0x64, 0x6e, 0x05, 0x86, 0x6b, 0x65, 0x00, 1360x29, 0x02, 0x07, 0x27, 0x00, 0x0c, 0x69, 0x05, 0x42, 0x75,
1370x69, 0x74, 0x2e, 0x00, 0x44, 0x2d, 0x04, 0x25, 0x74, 0x68, 1370x73, 0x65, 0x64, 0x6e, 0x05, 0x86, 0x6b, 0x65, 0x00, 0x69,
1380x6a, 0x01, 0x20, 0x67, 0x6f, 0x4d, 0x00, 0x11, 0x75, 0x8c, 1380x74, 0x2e, 0x00, 0x44, 0x2d, 0x04, 0x25, 0x74, 0x68, 0x6a,
1390x01, 0x12, 0x4e, 0xb1, 0x01, 0x00, 0x63, 0x01, 0x32, 0x00, 1390x01, 0x20, 0x67, 0x6f, 0x4d, 0x00, 0x11, 0x75, 0x8c, 0x01,
1400x72, 0x69, 0xda, 0x03, 0xa0, 0x61, 0x6e, 0x00, 0x28, 0x4e, 1400x12, 0x4e, 0xb1, 0x01, 0x00, 0x63, 0x01, 0x32, 0x00, 0x72,
1410x2b, 0x32, 0x29, 0x00, 0x78, 0x08, 0x00, 0x28, 0x31, 0x29, 1410x69, 0xda, 0x03, 0xa0, 0x61, 0x6e, 0x00, 0x28, 0x4e, 0x2b,
1420x6f, 0x06, 0x30, 0x73, 0x6f, 0x2c, 0x67, 0x01, 0x00, 0x70, 1420x32, 0x29, 0x00, 0x78, 0x08, 0x00, 0x28, 0x31, 0x29, 0x6f,
1430x05, 0x20, 0x69, 0x63, 0x95, 0x06, 0x12, 0x2c, 0x68, 0x02, 1430x06, 0x30, 0x73, 0x6f, 0x2c, 0x67, 0x01, 0x00, 0x70, 0x05,
1440xb1, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x76, 0x61, 1440x20, 0x69, 0x63, 0x95, 0x06, 0x12, 0x2c, 0x68, 0x02, 0xb1,
1450x6c, 0x75, 0x9f, 0x00, 0x11, 0x36, 0x53, 0x00, 0x10, 0x73, 1450x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x76, 0x61, 0x6c,
1460x4c, 0x00, 0x33, 0x38, 0x78, 0x37, 0x73, 0x03, 0xf1, 0x05, 1460x75, 0x9f, 0x00, 0x11, 0x36, 0x53, 0x00, 0x10, 0x73, 0x4c,
1470x00, 0x00, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x75, 1470x00, 0x33, 0x38, 0x78, 0x37, 0x73, 0x03, 0xf1, 0x05, 0x00,
1480x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x73, 0x6f, 0x6c, 0x75, 1480x00, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x75, 0x6e,
1490x3c, 0x01, 0x31, 0x00, 0x00, 0x4e, 0x59, 0x07, 0x44, 0x6c, 1490x69, 0x71, 0x75, 0x65, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x3c,
1500x79, 0x2c, 0x00, 0x9a, 0x01, 0x02, 0x99, 0x00, 0x01, 0xcb, 1500x01, 0x31, 0x00, 0x00, 0x4e, 0x59, 0x07, 0x44, 0x6c, 0x79,
1510x00, 0x00, 0x34, 0x00, 0x02, 0x6b, 0x02, 0x06, 0x1d, 0x01, 1510x2c, 0x00, 0x9a, 0x01, 0x02, 0x99, 0x00, 0x01, 0xcb, 0x00,
1520x01, 0xec, 0x04, 0x03, 0x47, 0x05, 0x00, 0xba, 0x03, 0x10, 1520x00, 0x34, 0x00, 0x02, 0x6b, 0x02, 0x06, 0x1d, 0x01, 0x01,
1530x76, 0x02, 0x05, 0x21, 0x6c, 0x79, 0x6f, 0x05, 0x04, 0x58, 1530xec, 0x04, 0x03, 0x47, 0x05, 0x00, 0xba, 0x03, 0x10, 0x76,
1540x00, 0x33, 0x2e, 0x00, 0x50, 0x2c, 0x00, 0x01, 0x00, 0x01, 1540x02, 0x05, 0x21, 0x6c, 0x79, 0x6f, 0x05, 0x04, 0x58, 0x00,
1550x94, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 1550x33, 0x2e, 0x00, 0x50, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x94,
1560x26, 0x02, 0x05, 0x75, 0x02, 0x41, 0x6d, 0x6f, 0x72, 0x65, 1560x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x26,
1570x93, 0x02, 0x00, 0xd4, 0x00, 0x11, 0x74, 0x58, 0x06, 0x40, 1570x02, 0x05, 0x75, 0x02, 0x41, 0x6d, 0x6f, 0x72, 0x65, 0x93,
1580x73, 0x6f, 0x6d, 0x65, 0x73, 0x02, 0x12, 0x73, 0x1d, 0x00, 1580x02, 0x00, 0xd4, 0x00, 0x11, 0x74, 0x58, 0x06, 0x40, 0x73,
1590x40, 0x73, 0x75, 0x62, 0x74, 0x92, 0x01, 0x20, 0x73, 0x6f, 1590x6f, 0x6d, 0x65, 0x73, 0x02, 0x12, 0x73, 0x1d, 0x00, 0x40,
1600xcd, 0x05, 0x00, 0xe7, 0x04, 0x45, 0x6c, 0x69, 0x6b, 0x65, 1600x73, 0x75, 0x62, 0x74, 0x92, 0x01, 0x20, 0x73, 0x6f, 0xcd,
1610x14, 0x05, 0x01, 0x04, 0x04, 0x20, 0x6f, 0x66, 0x97, 0x01, 1610x05, 0x00, 0xe7, 0x04, 0x45, 0x6c, 0x69, 0x6b, 0x65, 0x14,
1620xe0, 0x69, 0x73, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 1620x05, 0x01, 0x04, 0x04, 0x20, 0x6f, 0x66, 0x97, 0x01, 0xe0,
1630x65, 0x2e, 0x00, 0x41, 0x6c, 0x30, 0x01, 0x41, 0x66, 0x69, 1630x69, 0x73, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,
1640x6e, 0x64, 0x72, 0x03, 0x03, 0xb9, 0x02, 0x76, 0x70, 0x6f, 1640x2e, 0x00, 0x41, 0x6c, 0x30, 0x01, 0x41, 0x66, 0x69, 0x6e,
1650x73, 0x73, 0x69, 0x62, 0x6c, 0xa8, 0x00, 0x05, 0x88, 0x00, 1650x64, 0x72, 0x03, 0x03, 0xb9, 0x02, 0x76, 0x70, 0x6f, 0x73,
1660x00, 0xd2, 0x04, 0x30, 0x64, 0x64, 0x69, 0x14, 0x00, 0xf2, 1660x73, 0x69, 0x62, 0x6c, 0xa8, 0x00, 0x05, 0x88, 0x00, 0x00,
1670x01, 0x61, 0x6c, 0x00, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 1670xd2, 0x04, 0x30, 0x64, 0x64, 0x69, 0x14, 0x00, 0xf2, 0x01,
1680x6e, 0x67, 0x65, 0x00, 0x66, 0x6f, 0x72, 0x1c, 0x00, 0x10, 1680x61, 0x6c, 0x00, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e,
1690x76, 0x57, 0x08, 0x10, 0x64, 0x65, 0x04, 0x10, 0x79, 0x88, 1690x67, 0x65, 0x00, 0x66, 0x6f, 0x72, 0x1c, 0x00, 0x10, 0x76,
1700x03, 0x40, 0x54, 0x75, 0x72, 0x6e, 0x5a, 0x00, 0x05, 0x7a, 1700x57, 0x08, 0x10, 0x64, 0x65, 0x04, 0x10, 0x79, 0x88, 0x03,
1710x00, 0x03, 0x8c, 0x02, 0x05, 0x1b, 0x05, 0x50, 0x73, 0x70, 1710x40, 0x54, 0x75, 0x72, 0x6e, 0x5a, 0x00, 0x05, 0x7a, 0x00,
1720x65, 0x65, 0x64, 0xeb, 0x01, 0x03, 0x55, 0x07, 0xc0, 0x67, 1720x03, 0x8c, 0x02, 0x05, 0x1b, 0x05, 0x50, 0x73, 0x70, 0x65,
1730x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 1730x65, 0x64, 0xeb, 0x01, 0x03, 0x55, 0x07, 0xc0, 0x67, 0x65,
1740x00, 1740x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x00,
175}; 175};
176 176
177const unsigned short help_text_len = 2278; 177const unsigned short help_text_len = 2299;
178const unsigned short help_text_words = 400; 178const unsigned short help_text_words = 401;
179const bool help_valid = true;
179const char quick_help_text[] = "Tile the rectangle with a full set of dominoes."; 180const char quick_help_text[] = "Tile the rectangle with a full set of dominoes.";
diff --git a/apps/plugins/puzzles/help/fifteen.c b/apps/plugins/puzzles/help/fifteen.c
index 467118b7d5..32c8cba399 100644
--- a/apps/plugins/puzzles/help/fifteen.c
+++ b/apps/plugins/puzzles/help/fifteen.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,112 +6,151 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 83, TEXT_UNDERLINE }, 9 { 89, TEXT_UNDERLINE },
10 { 86, TEXT_CENTER | C_RED }, 10 { 92, TEXT_CENTER | C_RED },
11 { 153, TEXT_UNDERLINE }, 11 { 161, TEXT_UNDERLINE },
12 { 195, TEXT_CENTER | C_RED }, 12 { 203, TEXT_CENTER | C_RED },
13 { 211, TEXT_UNDERLINE }, 13 { 219, TEXT_UNDERLINE },
14 { 213, TEXT_UNDERLINE }, 14 { 221, TEXT_UNDERLINE },
15 { 239, TEXT_CENTER | C_RED },
16 { 343, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
16}; 18};
17 19
18/* orig 1245 comp 925 ratio 0.742972 level 5 saved 320 */ 20/* orig 1927 comp 1285 ratio 0.66684 level 10 saved 642 */
19const char help_text[] = { 21const char help_text[] = {
200xf1, 0x29, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x35, 0x3a, 0x20, 0x46, 0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 230x35, 0x3a, 0x20, 0x46, 0x69, 0x66, 0x74, 0x65, 0x65, 0x6e,
220x20, 0x00, 0x00, 0x00, 0x54, 0x68, 0x65, 0x00, 0x6f, 0x6c, 240x20, 0x00, 0x2d, 0x01, 0x00, 0xf1, 0x16, 0x00, 0x00, 0x00,
230x64, 0x00, 0x6f, 0x6e, 0x65, 0x73, 0x00, 0x61, 0x72, 0x65, 250x54, 0x68, 0x65, 0x00, 0x6f, 0x6c, 0x64, 0x00, 0x6f, 0x6e,
240x00, 0x74, 0x68, 0x65, 0x00, 0x62, 0x65, 0x73, 0x74, 0x3a, 260x65, 0x73, 0x00, 0x61, 0x72, 0x65, 0x00, 0x74, 0x68, 0x65,
250x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x69, 0x73, 0x12, 0x00, 270x00, 0x62, 0x65, 0x73, 0x74, 0x3a, 0x00, 0x74, 0x68, 0x69,
260x41, 0x67, 0x6f, 0x6f, 0x64, 0x28, 0x00, 0xf0, 0x23, 0x60, 280x73, 0x00, 0x69, 0x73, 0x12, 0x00, 0x41, 0x67, 0x6f, 0x6f,
270x31, 0x35, 0x2d, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x27, 290x64, 0x28, 0x00, 0xf1, 0x21, 0x60, 0x31, 0x35, 0x2d, 0x70,
280x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x73, 0x6c, 0x69, 0x64, 300x75, 0x7a, 0x7a, 0x6c, 0x65, 0x27, 0x00, 0x77, 0x69, 0x74,
290x69, 0x6e, 0x67, 0x00, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2e, 310x68, 0x00, 0x73, 0x6c, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x00,
300x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 320x74, 0x69, 0x6c, 0x65, 0x73, 0x2c, 0x00, 0x77, 0x68, 0x69,
310x61, 0x00, 0x34, 0x78, 0x34, 0x00, 0x73, 0x71, 0x75, 0x55, 330x63, 0x68, 0x00, 0x64, 0x61, 0x74, 0x65, 0x73, 0x00, 0x66,
320x00, 0x83, 0x67, 0x72, 0x69, 0x64, 0x3b, 0x00, 0x31, 0x35, 340x72, 0x6f, 0x6d, 0x3e, 0x00, 0xf0, 0x0a, 0x31, 0x38, 0x37,
330x10, 0x00, 0xf2, 0x03, 0x73, 0x00, 0x63, 0x6f, 0x6e, 0x74, 350x30, 0x73, 0x2e, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61,
340x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 360x76, 0x65, 0x00, 0x61, 0x00, 0x34, 0x78, 0x34, 0x00, 0x73,
350x65, 0x64, 0x3f, 0x00, 0x51, 0x2c, 0x00, 0x61, 0x6e, 0x64, 370x71, 0x75, 0x71, 0x00, 0x83, 0x67, 0x72, 0x69, 0x64, 0x3b,
360x70, 0x00, 0x30, 0x73, 0x69, 0x78, 0xa2, 0x00, 0x20, 0x74, 380x00, 0x31, 0x35, 0x10, 0x00, 0xf4, 0x03, 0x73, 0x00, 0x63,
370x68, 0x81, 0x00, 0x51, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5b, 390x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d,
380x00, 0x61, 0x72, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0x95, 0x00, 400x62, 0x65, 0x72, 0x65, 0x64, 0x5b, 0x00, 0x31, 0x61, 0x6e,
390x70, 0x6f, 0x00, 0x63, 0x68, 0x6f, 0x6f, 0x73, 0x69, 0x00, 410x64, 0x4e, 0x00, 0x30, 0x73, 0x69, 0x78, 0xd1, 0x00, 0x20,
400x00, 0x3c, 0x00, 0x50, 0x00, 0x6e, 0x65, 0x78, 0x74, 0x16, 420x74, 0x68, 0x9d, 0x00, 0x51, 0x65, 0x6d, 0x70, 0x74, 0x79,
410x00, 0x00, 0x3e, 0x00, 0x01, 0x31, 0x00, 0x62, 0x00, 0x73, 430x5b, 0x00, 0x61, 0x72, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0xb1,
420x70, 0x61, 0x63, 0x65, 0x53, 0x00, 0x00, 0xa5, 0x00, 0x73, 440x00, 0x70, 0x6f, 0x00, 0x63, 0x68, 0x6f, 0x6f, 0x73, 0x69,
430x65, 0x00, 0x69, 0x74, 0x00, 0x69, 0x6e, 0x23, 0x00, 0x01, 450x00, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x6e, 0x65, 0x78, 0x74,
440x1d, 0x00, 0x11, 0x2e, 0xff, 0x00, 0x33, 0x61, 0x69, 0x6d, 460x16, 0x00, 0x00, 0x3e, 0x00, 0x01, 0x31, 0x00, 0x62, 0x00,
450x52, 0x00, 0x62, 0x65, 0x6e, 0x64, 0x00, 0x75, 0x70, 0xd8, 470x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x00, 0x00, 0xc1, 0x00,
460x00, 0x00, 0x25, 0x00, 0x01, 0x95, 0x00, 0x12, 0x00, 0xa7, 480x73, 0x65, 0x00, 0x69, 0x74, 0x00, 0x69, 0x6e, 0x23, 0x00,
470x00, 0xd6, 0x65, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x00, 0x6f, 490x01, 0x1d, 0x00, 0x11, 0x2e, 0x1b, 0x01, 0x33, 0x61, 0x69,
480x72, 0x64, 0x65, 0x72, 0x2c, 0x23, 0x00, 0x01, 0x48, 0x00, 500x6d, 0x52, 0x00, 0x62, 0x65, 0x6e, 0x64, 0x00, 0x75, 0x70,
490x00, 0x23, 0x00, 0x01, 0x38, 0x01, 0xe0, 0x6f, 0x74, 0x74, 510xf4, 0x00, 0x00, 0x25, 0x00, 0x01, 0x95, 0x00, 0x12, 0x00,
500x6f, 0x6d, 0x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00, 0x28, 520xa7, 0x00, 0xd6, 0x65, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x00,
510x73, 0x6a, 0x00, 0x22, 0x61, 0x74, 0x4a, 0x00, 0xf5, 0x05, 530x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x23, 0x00, 0x01, 0x48,
520x6f, 0x70, 0x00, 0x72, 0x6f, 0x77, 0x00, 0x72, 0x65, 0x61, 540x00, 0x00, 0x23, 0x00, 0x01, 0x54, 0x01, 0xe0, 0x6f, 0x74,
530x64, 0x73, 0x00, 0x31, 0x2c, 0x32, 0x2c, 0x33, 0x2c, 0x34, 550x74, 0x6f, 0x6d, 0x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00,
540xee, 0x00, 0x04, 0x38, 0x00, 0x06, 0x21, 0x00, 0x81, 0x33, 560x28, 0x73, 0x6a, 0x00, 0x22, 0x61, 0x74, 0x4a, 0x00, 0xf5,
550x2c, 0x31, 0x34, 0x2c, 0x31, 0x35, 0x2c, 0x5f, 0x00, 0x85, 570x05, 0x6f, 0x70, 0x00, 0x72, 0x6f, 0x77, 0x00, 0x72, 0x65,
560x29, 0x2e, 0x00, 0x00, 0x00, 0x35, 0x2e, 0x31, 0xb8, 0x01, 580x61, 0x64, 0x73, 0x00, 0x31, 0x2c, 0x32, 0x2c, 0x33, 0x2c,
570x00, 0x3e, 0x01, 0x42, 0x72, 0x6f, 0x6c, 0x73, 0xc1, 0x01, 590x34, 0xee, 0x00, 0x04, 0x38, 0x00, 0x06, 0x21, 0x00, 0x81,
580xf3, 0x00, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 600x33, 0x2c, 0x31, 0x34, 0x2c, 0x31, 0x35, 0x2c, 0x5f, 0x00,
590x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x1d, 0x00, 0x36, 610x85, 0x29, 0x2e, 0x00, 0x00, 0x00, 0x35, 0x2e, 0x31, 0xe7,
600x6c, 0x65, 0x64, 0xa6, 0x00, 0x81, 0x6d, 0x6f, 0x75, 0x73, 620x01, 0x00, 0x3e, 0x01, 0x51, 0x72, 0x6f, 0x6c, 0x73, 0x20,
610x65, 0x00, 0x6f, 0x72, 0x0d, 0x00, 0x80, 0x6b, 0x65, 0x79, 630xdd, 0x01, 0xf3, 0x00, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d,
620x62, 0x6f, 0x61, 0x72, 0x64, 0x56, 0x00, 0xcc, 0x41, 0x00, 640x65, 0x00, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x1d,
630x6c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 650x00, 0x36, 0x6c, 0x65, 0x64, 0xa6, 0x00, 0x81, 0x6d, 0x6f,
640x2f, 0x00, 0x03, 0xd5, 0x00, 0x00, 0x96, 0x00, 0x94, 0x6f, 660x75, 0x73, 0x65, 0x00, 0x6f, 0x72, 0x0d, 0x00, 0x80, 0x6b,
650x72, 0x00, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0xb9, 0x01, 670x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x56, 0x00, 0xcc,
660x01, 0xf2, 0x01, 0x0a, 0x6a, 0x01, 0x52, 0x00, 0x77, 0x69, 680x41, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69,
670x6c, 0x6c, 0xa0, 0x01, 0x73, 0x61, 0x73, 0x00, 0x6d, 0x61, 690x63, 0x6b, 0x2f, 0x00, 0x03, 0xd5, 0x00, 0x00, 0x96, 0x00,
680x6e, 0x79, 0x40, 0x01, 0xc0, 0x61, 0x73, 0x00, 0x6e, 0x65, 700x94, 0x6f, 0x72, 0x00, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e,
690x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x66, 0x01, 0x01, 710xb9, 0x01, 0x01, 0x0e, 0x02, 0x0a, 0x6a, 0x01, 0x52, 0x00,
700x23, 0x00, 0x06, 0x3c, 0x01, 0x27, 0x74, 0x6f, 0x74, 0x00, 720x77, 0x69, 0x6c, 0x6c, 0xa0, 0x01, 0x73, 0x61, 0x73, 0x00,
710x83, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x9a, 730x6d, 0x61, 0x6e, 0x79, 0x40, 0x01, 0xc0, 0x61, 0x73, 0x00,
720x02, 0x20, 0x61, 0x72, 0x7e, 0x00, 0x48, 0x6b, 0x65, 0x79, 740x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x66,
730x73, 0x5e, 0x00, 0x02, 0xf1, 0x01, 0x75, 0x61, 0x64, 0x6a, 750x01, 0x01, 0x23, 0x00, 0x06, 0x3c, 0x01, 0x27, 0x74, 0x6f,
740x61, 0x63, 0x65, 0x6e, 0xf5, 0x01, 0x09, 0x8a, 0x01, 0xf4, 760x74, 0x00, 0x70, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72,
750x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 770x9b, 0x00, 0xb1, 0x42, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61,
760x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 780x75, 0x6c, 0x74, 0x2c, 0x21, 0x00, 0x20, 0x61, 0x72, 0x8a,
770x00, 0x28, 0x6d, 0x6f, 0x76, 0xb8, 0x00, 0x09, 0x2d, 0x00, 790x00, 0x48, 0x6b, 0x65, 0x79, 0x73, 0x6a, 0x00, 0x02, 0xfd,
780x77, 0x6f, 0x70, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x36, 0x00, 800x01, 0x75, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x01,
790x01, 0x72, 0x01, 0x50, 0x50, 0x72, 0x65, 0x73, 0x73, 0x31, 810x02, 0x09, 0x96, 0x01, 0xf4, 0x09, 0x64, 0x69, 0x72, 0x65,
800x00, 0x33, 0x60, 0x68, 0x27, 0x7f, 0x00, 0x20, 0x61, 0x6b, 820x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x69, 0x6e, 0x64, 0x69,
810x7f, 0x00, 0x60, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, 0x53, 830x63, 0x61, 0x74, 0x65, 0x64, 0x00, 0x28, 0x6d, 0x6f, 0x76,
820x00, 0x00, 0x90, 0x00, 0x1a, 0x2e, 0x29, 0x00, 0xb3, 0x65, 840xc4, 0x00, 0x09, 0x2d, 0x00, 0x77, 0x6f, 0x70, 0x70, 0x6f,
830x6e, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x74, 0x69, 0x6d, 0x65, 850x73, 0x69, 0x74, 0x36, 0x00, 0x01, 0x7e, 0x01, 0x50, 0x50,
840xb5, 0x00, 0x33, 0x73, 0x6f, 0x6c, 0xf1, 0x00, 0x00, 0xa7, 860x72, 0x65, 0x73, 0x73, 0x31, 0x00, 0x33, 0x60, 0x68, 0x27,
850x01, 0x50, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x8b, 0x02, 0xe0, 870x7f, 0x00, 0x20, 0x61, 0x6b, 0x7f, 0x00, 0x60, 0x73, 0x75,
860x6d, 0x61, 0x79, 0x00, 0x73, 0x63, 0x72, 0x61, 0x6d, 0x62, 880x67, 0x67, 0x65, 0x73, 0x53, 0x00, 0x00, 0x90, 0x00, 0x1a,
870x6c, 0x65, 0x00, 0x79, 0xdc, 0x02, 0x40, 0x70, 0x72, 0x6f, 890x2e, 0x29, 0x00, 0xb3, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68,
880x67, 0x4b, 0x00, 0x30, 0x00, 0x77, 0x68, 0xe1, 0x00, 0x20, 900x00, 0x74, 0x69, 0x6d, 0x65, 0xb5, 0x00, 0x33, 0x73, 0x6f,
890x64, 0x6f, 0x54, 0x00, 0x20, 0x73, 0x6f, 0x8c, 0x00, 0x41, 910x6c, 0xfd, 0x00, 0x00, 0xb3, 0x01, 0x50, 0x2c, 0x00, 0x62,
900x28, 0x41, 0x6c, 0x6c, 0x43, 0x00, 0x11, 0x61, 0xa0, 0x00, 920x75, 0x74, 0x97, 0x02, 0xe0, 0x6d, 0x61, 0x79, 0x00, 0x73,
910xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 930x63, 0x72, 0x61, 0x6d, 0x62, 0x6c, 0x65, 0x00, 0x79, 0xe8,
920x65, 0x64, 0xc5, 0x00, 0x13, 0x73, 0xec, 0x00, 0x31, 0x32, 940x02, 0x40, 0x70, 0x72, 0x6f, 0x67, 0x4b, 0x00, 0x00, 0x6d,
930x2e, 0x31, 0xc4, 0x03, 0xf1, 0x01, 0x61, 0x6c, 0x73, 0x6f, 950x03, 0x50, 0x6c, 0x65, 0x00, 0x64, 0x6f, 0x54, 0x00, 0x20,
940x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 960x73, 0x6f, 0x8c, 0x00, 0x42, 0x28, 0x41, 0x6c, 0x6c, 0x12,
950x2e, 0x29, 0x3f, 0x02, 0x15, 0x32, 0x3f, 0x02, 0xa5, 0x70, 970x01, 0x01, 0xa0, 0x00, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73,
960x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x02, 980x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0xc5, 0x00, 0x13, 0x73,
970x04, 0x62, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x70, 0x5b, 0x00, 990xec, 0x00, 0x31, 0x32, 0x2e, 0x31, 0xec, 0x03, 0xf1, 0x01,
980x05, 0x39, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x76, 1000x61, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c,
990x00, 0xb3, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 1010x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x4b, 0x02, 0x15, 0x32,
1000x2e, 0x2e, 0x27, 0x27, 0x00, 0x22, 0x00, 0x6f, 0x3c, 0x01, 1020x4b, 0x02, 0xb4, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1010xb1, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 1030x65, 0x72, 0x73, 0x20, 0x2a, 0x04, 0x62, 0x6e, 0x6c, 0x79,
1040x00, 0x6f, 0x70, 0x5b, 0x00, 0x05, 0x39, 0x00, 0x06, 0xe9,
1050x03, 0xb3, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e,
1060x2e, 0x2e, 0x27, 0x27, 0x00, 0x32, 0x00, 0x6f, 0x6e, 0x1a,
1070x00, 0xa1, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65,
1020x6e, 0x75, 0x7b, 0x00, 0x51, 0x57, 0x69, 0x64, 0x74, 0x68, 1080x6e, 0x75, 0x7b, 0x00, 0x51, 0x57, 0x69, 0x64, 0x74, 0x68,
1030xd9, 0x02, 0x20, 0x48, 0x65, 0x07, 0x03, 0x10, 0x2c, 0xcd, 1090xe5, 0x02, 0x20, 0x48, 0x65, 0x13, 0x03, 0x04, 0x3a, 0x04,
1040x00, 0x21, 0x63, 0x68, 0x1c, 0x00, 0xf1, 0x07, 0x73, 0x65, 1100x00, 0x1c, 0x00, 0xf1, 0x07, 0x73, 0x65, 0x6c, 0x66, 0x2d,
1050x6c, 0x66, 0x2d, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61, 1110x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61, 0x74, 0x6f, 0x72,
1060x74, 0x6f, 0x72, 0x79, 0x2e, 0x00, 0x28, 0x4f, 0x6e, 0x63, 1120x79, 0x2e, 0x00, 0x28, 0x4f, 0x6e, 0x63, 0xfd, 0x00, 0xa1,
1070xfd, 0x00, 0xa1, 0x27, 0x76, 0x65, 0x00, 0x63, 0x68, 0x61, 1130x27, 0x76, 0x65, 0x00, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
1080x6e, 0x67, 0x65, 0x12, 0x03, 0xe9, 0x73, 0x65, 0x2c, 0x00, 1140x1e, 0x03, 0xe9, 0x73, 0x65, 0x2c, 0x00, 0x69, 0x74, 0x27,
1090x69, 0x74, 0x27, 0x73, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x61, 1150x73, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x61, 0x9d, 0x04, 0x00,
1100x75, 0x04, 0x00, 0x58, 0x02, 0xf0, 0x03, 0x6d, 0x6f, 0x72, 1160x64, 0x02, 0xf2, 0x01, 0x6d, 0x6f, 0x72, 0x65, 0x2c, 0x00,
1110x65, 0x2c, 0x00, 0x6f, 0x66, 0x00, 0x63, 0x6f, 0x75, 0x72, 1170x6f, 0x66, 0x00, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x21,
1120x73, 0x65, 0x21, 0x29, 0x00, 1180xe1, 0x00, 0x15, 0x33, 0xe1, 0x00, 0xf1, 0x00, 0x75, 0x73,
1190x65, 0x72, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
1200x6e, 0x63, 0x65, 0xe7, 0x00, 0xc2, 0x4f, 0x6e, 0x00, 0x70,
1210x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0xa4, 0x03,
1220x80, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e,
1230x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0xd9, 0x00, 0x16,
1240x50, 0x12, 0x00, 0x0d, 0xf5, 0x00, 0x34, 0x47, 0x61, 0x6d,
1250xf5, 0x00, 0x01, 0xe3, 0x01, 0x30, 0x6c, 0x65, 0x74, 0xc6,
1260x00, 0x00, 0x2a, 0x03, 0x53, 0x66, 0x69, 0x67, 0x75, 0x72,
1270xec, 0x02, 0x20, 0x65, 0x6e, 0x82, 0x03, 0x1b, 0x66, 0xcb,
1280x02, 0x34, 0x2e, 0x00, 0x57, 0x75, 0x03, 0x03, 0xed, 0x02,
1290xc4, 0x00, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2c,
1300x00, 0x60, 0x4d, 0x29, 0x03, 0x00, 0xe4, 0x02, 0x1b, 0x27,
1310x06, 0x03, 0x01, 0x64, 0x00, 0x11, 0x70, 0x23, 0x02, 0x04,
1320xd7, 0x02, 0x1b, 0x73, 0xef, 0x02, 0x01, 0xda, 0x00, 0x00,
1330x27, 0x00, 0x44, 0x77, 0x61, 0x6e, 0x74, 0x2b, 0x03, 0x03,
1340x81, 0x03, 0x24, 0x2c, 0x00, 0x9f, 0x04, 0xf6, 0x01, 0x28,
1350x66, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
1360x65, 0x29, 0x00, 0x69, 0x66, 0x32, 0x00, 0x08, 0xac, 0x03,
1370x01, 0x3c, 0x00, 0x00, 0x2c, 0x04, 0x04, 0xc4, 0x00, 0x32,
1380x67, 0x61, 0x70, 0xe9, 0x04, 0x56, 0x77, 0x61, 0x72, 0x64,
1390x73, 0x59, 0x05, 0x41, 0x67, 0x61, 0x70, 0x2c, 0xc6, 0x01,
1400x13, 0x64, 0x9e, 0x00, 0x10, 0x52, 0x25, 0x00, 0x07, 0xe8,
1410x00, 0x05, 0x62, 0x03, 0x0e, 0xe9, 0x00, 0x00, 0x3d, 0x00,
1420x23, 0x27, 0x2c, 0x9f, 0x06, 0x40, 0x68, 0x61, 0x76, 0x69,
1430x0a, 0x03, 0x0d, 0x34, 0x01, 0x00, 0xad, 0x05, 0x82, 0x72,
1440x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, 0xdf, 0x05, 0x01,
1450xb2, 0x00, 0x34, 0x6f, 0x75, 0x6c, 0x73, 0x00, 0x3f, 0x4c,
1460x65, 0x66, 0xbe, 0x00, 0x10, 0x0a, 0xb3, 0x00, 0x08, 0xae,
1470x05, 0x00, 0x1e, 0x00, 0x40, 0x65, 0x6e, 0x64, 0x73, 0x0d,
1480x06, 0x34, 0x6f, 0x6e, 0x65, 0xc6, 0x06, 0x04, 0x41, 0x00,
1490xe0, 0x77, 0x68, 0x65, 0x72, 0x65, 0x00, 0x69, 0x74, 0x00,
1500x77, 0x61, 0x73, 0x2e, 0x00,
113}; 151};
114 152
115const unsigned short help_text_len = 1245; 153const unsigned short help_text_len = 1927;
116const unsigned short help_text_words = 229; 154const unsigned short help_text_words = 353;
155const bool help_valid = true;
117const char quick_help_text[] = "Slide the tiles around to arrange them into order."; 156const char quick_help_text[] = "Slide the tiles around to arrange them into order.";
diff --git a/apps/plugins/puzzles/help/filling.c b/apps/plugins/puzzles/help/filling.c
index 19ffb97dee..785cfce815 100644
--- a/apps/plugins/puzzles/help/filling.c
+++ b/apps/plugins/puzzles/help/filling.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,140 +6,141 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 141, TEXT_CENTER | C_RED }, 9 { 142, TEXT_CENTER | C_RED },
10 { 306, TEXT_CENTER | C_RED }, 10 { 307, TEXT_CENTER | C_RED },
11 LAST_STYLE_ITEM 11 LAST_STYLE_ITEM
12}; 12};
13 13
14/* orig 1804 comp 1242 ratio 0.68847 level 10 saved 562 */ 14/* orig 1821 comp 1245 ratio 0.68369 level 10 saved 576 */
15const char help_text[] = { 15const char help_text[] = {
160xf0, 0x28, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 160xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
170x32, 0x39, 0x3a, 0x20, 0x46, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 170x32, 0x39, 0x3a, 0x20, 0x46, 0x69, 0x6c, 0x6c, 0x69, 0x6e,
180x67, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 180x67, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x14, 0x00, 0x00,
190x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 190x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00,
200x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 200x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00,
210x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x11, 0x00, 0xf6, 210x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73,
220x13, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 220x6f, 0x6d, 0x65, 0x11, 0x00, 0xf6, 0x13, 0x77, 0x68, 0x69,
230x74, 0x61, 0x69, 0x6e, 0x00, 0x64, 0x69, 0x67, 0x69, 0x74, 230x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
240x73, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x74, 0x68, 0x65, 240x00, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x2c, 0x00, 0x61,
250x00, 0x72, 0x65, 0x73, 0x74, 0x26, 0x00, 0xa0, 0x61, 0x72, 250x6e, 0x64, 0x00, 0x74, 0x68, 0x65, 0x00, 0x72, 0x65, 0x73,
260x65, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x5b, 0x00, 260x74, 0x26, 0x00, 0xa0, 0x61, 0x72, 0x65, 0x00, 0x65, 0x6d,
270xf5, 0x02, 0x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 0x73, 270x70, 0x74, 0x79, 0x2e, 0x5b, 0x00, 0xf5, 0x02, 0x72, 0x00,
280x00, 0x74, 0x6f, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x00, 0x40, 280x6a, 0x6f, 0x62, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00,
290x00, 0x00, 0x0a, 0x00, 0x23, 0x74, 0x68, 0x2c, 0x00, 0x06, 290x66, 0x69, 0x6c, 0x6c, 0x00, 0x40, 0x00, 0x00, 0x0a, 0x00,
300x73, 0x00, 0x50, 0x69, 0x6e, 0x00, 0x73, 0x75, 0x47, 0x00, 300x23, 0x74, 0x68, 0x2c, 0x00, 0x06, 0x73, 0x00, 0x50, 0x69,
310xc2, 0x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74, 310x6e, 0x00, 0x73, 0x75, 0x47, 0x00, 0xc2, 0x00, 0x77, 0x61,
320x00, 0x65, 0x61, 0x7d, 0x00, 0xd7, 0x6e, 0x65, 0x63, 0x74, 320x79, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x65, 0x61, 0x7d,
330x65, 0x64, 0x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0xa8, 330x00, 0xd7, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x72,
340x00, 0x44, 0x00, 0x61, 0x6c, 0x6c, 0x9d, 0x00, 0x31, 0x69, 340x65, 0x67, 0x69, 0x6f, 0x6e, 0xa8, 0x00, 0x44, 0x00, 0x61,
350x6e, 0x67, 0x56, 0x00, 0x42, 0x73, 0x61, 0x6d, 0x65, 0x69, 350x6c, 0x6c, 0x9d, 0x00, 0x31, 0x69, 0x6e, 0x67, 0x56, 0x00,
360x00, 0x70, 0x00, 0x68, 0x61, 0x73, 0x00, 0x61, 0x6e, 0x98, 360x42, 0x73, 0x61, 0x6d, 0x65, 0x69, 0x00, 0x70, 0x00, 0x68,
370x00, 0x70, 0x61, 0x00, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x8c, 370x61, 0x73, 0x00, 0x61, 0x6e, 0x98, 0x00, 0x70, 0x61, 0x00,
380x00, 0x01, 0x59, 0x00, 0x01, 0x20, 0x00, 0x7b, 0x2e, 0x00, 380x65, 0x71, 0x75, 0x61, 0x6c, 0x8c, 0x00, 0x01, 0x59, 0x00,
390x00, 0x00, 0x28, 0x60, 0x43, 0x5f, 0x00, 0x61, 0x27, 0x2c, 390x01, 0x20, 0x00, 0x7b, 0x2e, 0x00, 0x00, 0x00, 0x28, 0x60,
400x00, 0x66, 0x6f, 0x72, 0x4b, 0x00, 0x80, 0x70, 0x75, 0x72, 400x43, 0x5f, 0x00, 0x61, 0x27, 0x2c, 0x00, 0x66, 0x6f, 0x72,
410x70, 0x6f, 0x73, 0x65, 0x73, 0x72, 0x00, 0xf0, 0x09, 0x74, 410x4b, 0x00, 0x80, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65,
420x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 420x73, 0x72, 0x00, 0xf0, 0x09, 0x74, 0x68, 0x69, 0x73, 0x00,
430x64, 0x6f, 0x65, 0x73, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x63, 430x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 0x64, 0x6f, 0x65, 0x73,
440x6f, 0x75, 0x6e, 0x4c, 0x00, 0xf0, 0x00, 0x61, 0x67, 0x6f, 440x00, 0x6e, 0x6f, 0x74, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x4c,
450x6e, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x65, 0x70, 0x61, 450x00, 0xf0, 0x00, 0x61, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x6c,
460x72, 0x61, 0x4c, 0x00, 0x05, 0xa1, 0x00, 0xf2, 0x17, 0x73, 460x79, 0x00, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x4c, 0x00,
470x00, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x2e, 470x05, 0xa1, 0x00, 0xf2, 0x17, 0x73, 0x00, 0x61, 0x64, 0x6a,
480x29, 0x00, 0x00, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 480x61, 0x63, 0x65, 0x6e, 0x74, 0x2e, 0x29, 0x00, 0x00, 0x00,
490x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x00, 0x69, 0x74, 0x00, 490x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
500x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x96, 0x00, 0x23, 500x65, 0x2c, 0x00, 0x69, 0x74, 0x00, 0x66, 0x6f, 0x6c, 0x6c,
510x6e, 0x6f, 0x38, 0x00, 0x45, 0x00, 0x63, 0x61, 0x6e, 0x75, 510x6f, 0x77, 0x73, 0x96, 0x00, 0x23, 0x6e, 0x6f, 0x38, 0x00,
520x01, 0x64, 0x61, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x75, 0x01, 520x45, 0x00, 0x63, 0x61, 0x6e, 0x75, 0x01, 0x64, 0x61, 0x00,
530x65, 0x61, 0x74, 0x00, 0x74, 0x77, 0x6f, 0x55, 0x00, 0x05, 530x7a, 0x65, 0x72, 0x6f, 0x75, 0x01, 0x65, 0x61, 0x74, 0x00,
540x69, 0x00, 0x00, 0x32, 0x00, 0x00, 0x94, 0x00, 0x47, 0x62, 540x74, 0x77, 0x6f, 0x55, 0x00, 0x05, 0x69, 0x00, 0x00, 0x32,
550x6f, 0x74, 0x68, 0x3b, 0x00, 0x74, 0x6f, 0x6e, 0x65, 0x2e, 550x00, 0x00, 0x94, 0x00, 0x47, 0x62, 0x6f, 0x74, 0x68, 0x3b,
560x00, 0x4e, 0x6f, 0x3b, 0x01, 0x08, 0x12, 0x01, 0x70, 0x67, 560x00, 0x74, 0x6f, 0x6e, 0x65, 0x2e, 0x00, 0x4e, 0x6f, 0x3b,
570x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x54, 0x00, 0xc0, 0x6e, 570x01, 0x08, 0x12, 0x01, 0x70, 0x67, 0x72, 0x65, 0x61, 0x74,
580x00, 0x39, 0x00, 0x28, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 580x65, 0x72, 0x54, 0x00, 0xc0, 0x6e, 0x00, 0x39, 0x00, 0x28,
590x65, 0xfa, 0x00, 0x20, 0x6e, 0x00, 0xa7, 0x01, 0x01, 0x26, 590x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0xfa, 0x00, 0x20,
600x00, 0x52, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x5a, 0x00, 0x00, 600x6e, 0x00, 0xa7, 0x01, 0x01, 0x26, 0x00, 0x52, 0x77, 0x6f,
610x29, 0x02, 0x53, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x60, 0x01, 610x75, 0x6c, 0x64, 0x5a, 0x00, 0x00, 0x29, 0x02, 0x53, 0x73,
620x10, 0x29, 0x41, 0x01, 0x41, 0x43, 0x72, 0x65, 0x64, 0xc8, 620x69, 0x6e, 0x67, 0x6c, 0x60, 0x01, 0x10, 0x29, 0x41, 0x01,
630x00, 0x12, 0x72, 0x23, 0x01, 0xa1, 0x70, 0x75, 0x7a, 0x7a, 630x41, 0x43, 0x72, 0x65, 0x64, 0xc8, 0x00, 0x12, 0x72, 0x23,
640x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0xfa, 0x01, 0xb0, 0x4e, 640x01, 0xa1, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67,
650x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x31, 0x34, 0x5d, 650x6f, 0x65, 0xfa, 0x01, 0xb0, 0x4e, 0x69, 0x6b, 0x6f, 0x6c,
660x2e, 0x00, 0x03, 0x7e, 0x02, 0x41, 0x00, 0x77, 0x61, 0x73, 660x69, 0x00, 0x5b, 0x31, 0x34, 0x5d, 0x2e, 0x00, 0x03, 0x92,
670xa5, 0x00, 0x40, 0x72, 0x69, 0x62, 0x75, 0x2f, 0x01, 0x01, 670x02, 0x41, 0x00, 0x77, 0x61, 0x73, 0xa5, 0x00, 0x40, 0x72,
680x98, 0x01, 0x10, 0x69, 0x14, 0x00, 0x50, 0x6c, 0x6c, 0x65, 680x69, 0x62, 0x75, 0x2f, 0x01, 0x01, 0x98, 0x01, 0x10, 0x69,
690x63, 0x74, 0xab, 0x00, 0xf0, 0x01, 0x62, 0x79, 0x00, 0x4a, 690x14, 0x00, 0x50, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0xab, 0x00,
700x6f, 0x6e, 0x61, 0x73, 0x00, 0x4b, 0x6f, 0x65, 0x6c, 0x6b, 700xf0, 0x01, 0x62, 0x79, 0x00, 0x4a, 0x6f, 0x6e, 0x61, 0x73,
710x65, 0x72, 0x3f, 0x00, 0x00, 0x47, 0x00, 0xd1, 0x00, 0x68, 710x00, 0x4b, 0x6f, 0x65, 0x6c, 0x6b, 0x65, 0x72, 0x3f, 0x00,
720x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 720x00, 0x47, 0x00, 0xe1, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73,
730x6e, 0x5e, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 730x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x5f, 0x00,
740x2f, 0x65, 0x6e, 0x2f, 0x7d, 0x00, 0x20, 0x73, 0x2f, 0x70, 740xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e,
750x02, 0xf5, 0x02, 0x6f, 0x6d, 0x69, 0x6e, 0x6f, 0x2e, 0x68, 750x2f, 0x7e, 0x00, 0x20, 0x73, 0x2f, 0x71, 0x02, 0xd5, 0x6f,
760x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x32, 0x39, 0x2e, 0x31, 760x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x00, 0x00, 0x00, 0x32, 0x39,
770xfb, 0x02, 0x01, 0x79, 0x00, 0x30, 0x6f, 0x6c, 0x73, 0x04, 770x2e, 0x31, 0x0c, 0x03, 0x01, 0x76, 0x00, 0xe4, 0x6f, 0x6c,
780x03, 0x74, 0x54, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x99, 780x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x00, 0x70, 0x6c,
790x00, 0xe1, 0x2c, 0x00, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x79, 790x61, 0x79, 0x96, 0x00, 0xe1, 0x2c, 0x00, 0x73, 0x69, 0x6d,
800x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x05, 0x02, 0x20, 0x6d, 800x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x02,
810x6f, 0x15, 0x01, 0x00, 0x4d, 0x01, 0x29, 0x6e, 0x79, 0xb3, 810x02, 0x20, 0x6d, 0x6f, 0x12, 0x01, 0x00, 0x4a, 0x01, 0x29,
820x02, 0x04, 0x06, 0x03, 0x50, 0x6e, 0x00, 0x74, 0x79, 0x70, 820x6e, 0x79, 0xb0, 0x02, 0x04, 0x03, 0x03, 0x50, 0x6e, 0x00,
830x1c, 0x01, 0x02, 0x75, 0x02, 0x12, 0x6f, 0xdd, 0x02, 0x85, 830x74, 0x79, 0x70, 0x19, 0x01, 0x02, 0x72, 0x02, 0x12, 0x6f,
840x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0xff, 0x02, 840xda, 0x02, 0x85, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72,
850x01, 0xbb, 0x01, 0x02, 0x3a, 0x00, 0xa4, 0x2e, 0x00, 0x42, 850x64, 0xfc, 0x02, 0x01, 0xb8, 0x01, 0x02, 0x3a, 0x00, 0xa4,
860x79, 0x00, 0x64, 0x72, 0x61, 0x67, 0x67, 0xb5, 0x02, 0x01, 860x2e, 0x00, 0x42, 0x79, 0x00, 0x64, 0x72, 0x61, 0x67, 0x67,
870x65, 0x00, 0x51, 0x2c, 0x00, 0x79, 0x6f, 0x75, 0xc9, 0x01, 870xb2, 0x02, 0x01, 0x65, 0x00, 0x51, 0x2c, 0x00, 0x79, 0x6f,
880x20, 0x73, 0x65, 0x06, 0x01, 0x95, 0x00, 0x6d, 0x75, 0x6c, 880x75, 0xc6, 0x01, 0x20, 0x73, 0x65, 0x03, 0x01, 0x95, 0x00,
890x74, 0x69, 0x70, 0x6c, 0x65, 0xe5, 0x01, 0x04, 0x4c, 0x00, 890x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0xe2, 0x01,
900x46, 0x77, 0x69, 0x74, 0x68, 0x8d, 0x01, 0xc1, 0x6b, 0x65, 900x04, 0x4c, 0x00, 0x46, 0x77, 0x69, 0x74, 0x68, 0x8a, 0x01,
910x79, 0x70, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x00, 0x49, 0x66, 910xc1, 0x6b, 0x65, 0x79, 0x70, 0x72, 0x65, 0x73, 0x73, 0x2e,
920x43, 0x00, 0x30, 0x6d, 0x61, 0x6b, 0x90, 0x00, 0x8e, 0x6d, 920x00, 0x49, 0x66, 0x43, 0x00, 0x30, 0x6d, 0x61, 0x6b, 0x90,
930x69, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0xcd, 0x00, 0x02, 930x00, 0x8e, 0x6d, 0x69, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2c,
940xa3, 0x00, 0x60, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x69, 940xcd, 0x00, 0x02, 0xa3, 0x00, 0x60, 0x69, 0x6e, 0x63, 0x6f,
950x00, 0x07, 0xd1, 0x00, 0x01, 0x4a, 0x00, 0xf0, 0x01, 0x00, 950x72, 0x72, 0x69, 0x00, 0x07, 0xd1, 0x00, 0x01, 0x4a, 0x00,
960x30, 0x2c, 0x00, 0x53, 0x70, 0x61, 0x63, 0x65, 0x2c, 0x00, 960xf0, 0x01, 0x00, 0x30, 0x2c, 0x00, 0x53, 0x70, 0x61, 0x63,
970x42, 0x61, 0x63, 0x6b, 0x73, 0x0b, 0x00, 0x61, 0x00, 0x6f, 970x65, 0x2c, 0x00, 0x42, 0x61, 0x63, 0x6b, 0x73, 0x0b, 0x00,
980x72, 0x00, 0x45, 0x6e, 0x31, 0x02, 0x70, 0x6f, 0x00, 0x63, 980x61, 0x00, 0x6f, 0x72, 0x00, 0x45, 0x6e, 0x2e, 0x02, 0x70,
990x6c, 0x65, 0x61, 0x72, 0xc0, 0x02, 0x20, 0x61, 0x67, 0x68, 990x6f, 0x00, 0x63, 0x6c, 0x65, 0x61, 0x72, 0xbd, 0x02, 0x20,
1000x02, 0x43, 0x28, 0x6f, 0x72, 0x00, 0x3b, 0x02, 0xd1, 0x00, 1000x61, 0x67, 0x65, 0x02, 0x43, 0x28, 0x6f, 0x72, 0x00, 0x38,
1010x55, 0x6e, 0x64, 0x6f, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75, 1010x02, 0xd1, 0x00, 0x55, 0x6e, 0x64, 0x6f, 0x00, 0x66, 0x65,
1020x72, 0x65, 0x22, 0x02, 0x13, 0x59, 0xd5, 0x00, 0x70, 0x61, 1020x61, 0x74, 0x75, 0x72, 0x65, 0x1f, 0x02, 0x13, 0x59, 0xd5,
1030x6c, 0x73, 0x6f, 0x00, 0x6d, 0x6f, 0x70, 0x04, 0x33, 0x72, 1030x00, 0x70, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x6d, 0x6f, 0x6d,
1040x6f, 0x75, 0x42, 0x04, 0x01, 0x79, 0x04, 0x01, 0xcf, 0x00, 1040x04, 0x33, 0x72, 0x6f, 0x75, 0x3f, 0x04, 0x01, 0x76, 0x04,
1050x00, 0x0e, 0x00, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 1050x01, 0xcf, 0x00, 0x00, 0x0e, 0x00, 0x60, 0x63, 0x75, 0x72,
1060xd1, 0x00, 0x20, 0x73, 0x3b, 0x56, 0x01, 0x00, 0x1f, 0x01, 1060x73, 0x6f, 0x72, 0xd1, 0x00, 0x20, 0x73, 0x3b, 0x56, 0x01,
1070x04, 0x58, 0x01, 0x10, 0x77, 0xf9, 0x00, 0x03, 0x4a, 0x01, 1070x00, 0x1f, 0x01, 0x04, 0x58, 0x01, 0x10, 0x77, 0xf9, 0x00,
1080x04, 0x12, 0x01, 0x0c, 0xfc, 0x03, 0x03, 0x40, 0x00, 0x03, 1080x03, 0x4a, 0x01, 0x04, 0x12, 0x01, 0x0c, 0xf9, 0x03, 0x03,
1090x50, 0x00, 0x00, 0x52, 0x03, 0x55, 0x75, 0x6d, 0x62, 0x65, 1090x40, 0x00, 0x03, 0x50, 0x00, 0x00, 0x4f, 0x03, 0x55, 0x75,
1100x72, 0x4c, 0x00, 0x12, 0x30, 0x46, 0x00, 0x04, 0xbe, 0x00, 1100x6d, 0x62, 0x65, 0x72, 0x4c, 0x00, 0x12, 0x30, 0x46, 0x00,
1110x1a, 0x2e, 0x9c, 0x00, 0x0f, 0x76, 0x01, 0x05, 0x00, 0xdc, 1110x04, 0xbe, 0x00, 0x1a, 0x2e, 0x9c, 0x00, 0x0f, 0x76, 0x01,
1120x02, 0x02, 0x49, 0x00, 0x00, 0x44, 0x00, 0x22, 0x6f, 0x72, 1120x05, 0x00, 0xd9, 0x02, 0x02, 0x49, 0x00, 0x00, 0x44, 0x00,
1130x40, 0x00, 0x01, 0xc7, 0x02, 0x04, 0xb9, 0x00, 0x61, 0x72, 1130x22, 0x6f, 0x72, 0x40, 0x00, 0x01, 0xc4, 0x02, 0x04, 0xb9,
1140x65, 0x74, 0x75, 0x72, 0x6e, 0x41, 0x01, 0x51, 0x61, 0x72, 1140x00, 0x61, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x41, 0x01,
1150x72, 0x6f, 0x77, 0xc3, 0x00, 0x8c, 0x2c, 0x00, 0x62, 0x65, 1150x51, 0x61, 0x72, 0x72, 0x6f, 0x77, 0xc3, 0x00, 0x8c, 0x2c,
1160x66, 0x6f, 0x72, 0x65, 0xca, 0x00, 0x04, 0xc6, 0x01, 0x04, 1160x00, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0xca, 0x00, 0x04,
1170x4a, 0x00, 0x01, 0x42, 0x00, 0x88, 0x68, 0x69, 0x67, 0x68, 1170xc6, 0x01, 0x04, 0x4a, 0x00, 0x01, 0x42, 0x00, 0x88, 0x68,
1180x6c, 0x69, 0x67, 0x68, 0x3d, 0x04, 0x10, 0x28, 0xae, 0x03, 1180x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x3a, 0x04, 0x10,
1190x30, 0x62, 0x6f, 0x76, 0x48, 0x01, 0x10, 0x54, 0xf5, 0x00, 1190x28, 0xab, 0x03, 0x30, 0x62, 0x6f, 0x76, 0x48, 0x01, 0x10,
1200x01, 0x88, 0x01, 0x70, 0x62, 0x61, 0x72, 0x00, 0x61, 0x64, 1200x54, 0xf5, 0x00, 0x01, 0x88, 0x01, 0x70, 0x62, 0x61, 0x72,
1210x64, 0xc9, 0x03, 0x00, 0xb4, 0x04, 0x00, 0x52, 0x01, 0x14, 1210x00, 0x61, 0x64, 0x64, 0xc6, 0x03, 0x00, 0xb1, 0x04, 0x00,
1220x73, 0x0b, 0x02, 0x07, 0x29, 0x02, 0x00, 0x1e, 0x00, 0x42, 1220x52, 0x01, 0x14, 0x73, 0x0b, 0x02, 0x07, 0x29, 0x02, 0x00,
1230x66, 0x72, 0x6f, 0x6d, 0x2f, 0x01, 0x13, 0x65, 0x57, 0x03, 1230x1e, 0x00, 0x42, 0x66, 0x72, 0x6f, 0x6d, 0x2f, 0x01, 0x13,
1240x17, 0x2e, 0xd1, 0x01, 0x00, 0x22, 0x00, 0x63, 0x65, 0x73, 1240x65, 0x54, 0x03, 0x17, 0x2e, 0xd1, 0x01, 0x00, 0x22, 0x00,
1250x63, 0x61, 0x70, 0x65, 0x47, 0x00, 0x01, 0x4f, 0x05, 0x05, 1250x63, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x47, 0x00, 0x01,
1260xf6, 0x00, 0x0e, 0x3c, 0x00, 0x00, 0x30, 0x05, 0x13, 0x41, 1260x4c, 0x05, 0x05, 0xf6, 0x00, 0x0e, 0x3c, 0x00, 0x00, 0x2d,
1270x81, 0x01, 0x11, 0x61, 0x13, 0x00, 0xb1, 0x73, 0x00, 0x64, 1270x05, 0x13, 0x41, 0x81, 0x01, 0x11, 0x61, 0x13, 0x00, 0xb1,
1280x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0xc0, 0x05, 1280x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
1290x03, 0xbc, 0x03, 0x31, 0x32, 0x2e, 0x31, 0x0e, 0x06, 0x01, 1290x64, 0xbd, 0x05, 0x03, 0xb9, 0x03, 0x31, 0x32, 0x2e, 0x31,
1300x57, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 1300x0b, 0x06, 0x01, 0x57, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69,
1310x6c, 0x65, 0x2e, 0x29, 0x8a, 0x03, 0x15, 0x32, 0x8a, 0x03, 1310x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x8a, 0x03, 0x15,
1320x00, 0x2e, 0x05, 0x77, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 1320x32, 0x8a, 0x03, 0x00, 0x2b, 0x05, 0x77, 0x6d, 0x65, 0x74,
1330x20, 0x1d, 0x04, 0x12, 0x61, 0x13, 0x05, 0x00, 0xca, 0x02, 1330x65, 0x72, 0x73, 0x20, 0x1a, 0x04, 0x12, 0x61, 0x10, 0x05,
1340x20, 0x74, 0x6f, 0xe3, 0x01, 0x52, 0x66, 0x69, 0x67, 0x75, 1340x00, 0xca, 0x02, 0x20, 0x74, 0x6f, 0xe3, 0x01, 0x52, 0x66,
1350x72, 0x5e, 0x02, 0x02, 0x88, 0x01, 0x00, 0x91, 0x05, 0x32, 1350x69, 0x67, 0x75, 0x72, 0x5e, 0x02, 0x02, 0x88, 0x01, 0x00,
1360x72, 0x6f, 0x77, 0x03, 0x01, 0x63, 0x63, 0x6f, 0x6c, 0x75, 1360x8e, 0x05, 0x32, 0x72, 0x6f, 0x77, 0x03, 0x01, 0x63, 0x63,
1370x6d, 0x6e, 0xa5, 0x05, 0x02, 0x52, 0x02, 0x82, 0x2c, 0x00, 1370x6f, 0x6c, 0x75, 0x6d, 0x6e, 0xa2, 0x05, 0x02, 0x52, 0x02,
1380x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x9d, 0x01, 0xd0, 0x60, 1380x82, 0x2c, 0x00, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x9d,
1390x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 1390x01, 0xd0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d,
1400x2e, 0x00, 1400x65, 0x6e, 0x75, 0x2e, 0x00,
141}; 141};
142 142
143const unsigned short help_text_len = 1804; 143const unsigned short help_text_len = 1821;
144const unsigned short help_text_words = 327; 144const unsigned short help_text_words = 328;
145const bool help_valid = true;
145const char quick_help_text[] = "Mark every square with the area of its containing region."; 146const char quick_help_text[] = "Mark every square with the area of its containing region.";
diff --git a/apps/plugins/puzzles/help/flip.c b/apps/plugins/puzzles/help/flip.c
index e5ec994fdb..4f847069cc 100644
--- a/apps/plugins/puzzles/help/flip.c
+++ b/apps/plugins/puzzles/help/flip.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,129 +6,130 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 76, TEXT_CENTER | C_RED }, 9 { 77, TEXT_CENTER | C_RED },
10 { 164, TEXT_UNDERLINE }, 10 { 165, TEXT_UNDERLINE },
11 { 203, TEXT_CENTER | C_RED }, 11 { 204, TEXT_CENTER | C_RED },
12 { 220, TEXT_UNDERLINE },
13 { 221, TEXT_UNDERLINE }, 12 { 221, TEXT_UNDERLINE },
14 { 231, TEXT_UNDERLINE }, 13 { 222, TEXT_UNDERLINE },
14 { 232, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 1522 comp 1092 ratio 0.717477 level 10 saved 430 */ 18/* orig 1539 comp 1099 ratio 0.7141 level 10 saved 440 */
19const char help_text[] = { 19const char help_text[] = {
200xf2, 0x2f, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfb, 0x04, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x31, 0x34, 0x3a, 0x20, 0x46, 0x6c, 0x69, 0x70, 0x20, 0x00, 210x31, 0x34, 0x3a, 0x20, 0x46, 0x6c, 0x69, 0x70, 0x20, 0x00,
220x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 220x2d, 0x01, 0x00, 0xf2, 0x1e, 0x00, 0x00, 0x00, 0x59, 0x6f,
230x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 230x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67,
240x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 240x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75,
250x73, 0x6f, 0x6d, 0x65, 0x00, 0x6c, 0x69, 0x67, 0x68, 0x74, 250x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65,
260x00, 0x61, 0x6e, 0x64, 0x0f, 0x00, 0x50, 0x64, 0x61, 0x72, 260x00, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x00, 0x61, 0x6e, 0x64,
270x6b, 0x2e, 0x36, 0x00, 0xb4, 0x72, 0x00, 0x61, 0x69, 0x6d, 270x0f, 0x00, 0x50, 0x64, 0x61, 0x72, 0x6b, 0x2e, 0x36, 0x00,
280x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x24, 0x00, 0x64, 0x6c, 280xb4, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00,
290x6c, 0x00, 0x74, 0x68, 0x65, 0x40, 0x00, 0x62, 0x00, 0x75, 290x74, 0x6f, 0x24, 0x00, 0x64, 0x6c, 0x6c, 0x00, 0x74, 0x68,
300x70, 0x00, 0x61, 0x74, 0x12, 0x00, 0x91, 0x61, 0x6d, 0x65, 300x65, 0x40, 0x00, 0x62, 0x00, 0x75, 0x70, 0x00, 0x61, 0x74,
310x00, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x70, 0x00, 0xe3, 0x63, 310x12, 0x00, 0x91, 0x61, 0x6d, 0x65, 0x00, 0x74, 0x69, 0x6d,
320x61, 0x6e, 0x00, 0x63, 0x68, 0x6f, 0x6f, 0x73, 0x65, 0x00, 320x65, 0x2e, 0x70, 0x00, 0xe3, 0x63, 0x61, 0x6e, 0x00, 0x63,
330x61, 0x6e, 0x79, 0x30, 0x00, 0x01, 0x63, 0x00, 0xf3, 0x04, 330x68, 0x6f, 0x6f, 0x73, 0x65, 0x00, 0x61, 0x6e, 0x79, 0x30,
340x66, 0x6c, 0x69, 0x70, 0x00, 0x69, 0x74, 0x73, 0x00, 0x73, 340x00, 0x01, 0x63, 0x00, 0xf3, 0x04, 0x66, 0x6c, 0x69, 0x70,
350x74, 0x61, 0x74, 0x65, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x5d, 350x00, 0x69, 0x74, 0x73, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65,
360x00, 0x21, 0x74, 0x6f, 0x7b, 0x00, 0x32, 0x00, 0x6f, 0x72, 360x00, 0x66, 0x72, 0x6f, 0x6d, 0x5d, 0x00, 0x21, 0x74, 0x6f,
370x08, 0x00, 0x04, 0x76, 0x00, 0xf5, 0x0c, 0x2c, 0x00, 0x62, 370x7b, 0x00, 0x32, 0x00, 0x6f, 0x72, 0x08, 0x00, 0x04, 0x76,
380x75, 0x74, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x00, 0x79, 0x6f, 380x00, 0xf5, 0x0c, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x77,
390x75, 0x00, 0x64, 0x6f, 0x00, 0x73, 0x6f, 0x2c, 0x00, 0x6f, 390x68, 0x65, 0x6e, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x64, 0x6f,
400x74, 0x68, 0x65, 0x72, 0x89, 0x00, 0xf3, 0x01, 0x61, 0x72, 400x00, 0x73, 0x6f, 0x2c, 0x00, 0x6f, 0x74, 0x68, 0x65, 0x72,
410x6f, 0x75, 0x6e, 0x64, 0x00, 0x69, 0x74, 0x00, 0x63, 0x68, 410x89, 0x00, 0xf3, 0x01, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64,
420x61, 0x6e, 0x67, 0x65, 0x5e, 0x00, 0xf4, 0x00, 0x61, 0x73, 420x00, 0x69, 0x74, 0x00, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
430x00, 0x77, 0x65, 0x6c, 0x6c, 0x2e, 0x00, 0x00, 0x00, 0x45, 430x5e, 0x00, 0xf4, 0x00, 0x61, 0x73, 0x00, 0x77, 0x65, 0x6c,
440x61, 0x63, 0x68, 0x88, 0x00, 0xd0, 0x63, 0x6f, 0x6e, 0x74, 440x6c, 0x2e, 0x00, 0x00, 0x00, 0x45, 0x61, 0x63, 0x68, 0x88,
450x61, 0x69, 0x6e, 0x73, 0x00, 0x61, 0x00, 0x73, 0x6d, 0xd4, 450x00, 0xd0, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73,
460x00, 0xfb, 0x06, 0x64, 0x69, 0x61, 0x67, 0x72, 0x61, 0x6d, 460x00, 0x61, 0x00, 0x73, 0x6d, 0xd4, 0x00, 0xfb, 0x06, 0x64,
470x00, 0x73, 0x68, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x00, 0x77, 470x69, 0x61, 0x67, 0x72, 0x61, 0x6d, 0x00, 0x73, 0x68, 0x6f,
480x68, 0x69, 0x63, 0x68, 0x63, 0x00, 0x03, 0x59, 0x00, 0x05, 480x77, 0x69, 0x6e, 0x67, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68,
490x88, 0x00, 0x03, 0xc9, 0x00, 0x00, 0x5c, 0x00, 0x42, 0x31, 490x63, 0x00, 0x03, 0x59, 0x00, 0x05, 0x88, 0x00, 0x03, 0xc9,
500x34, 0x2e, 0x31, 0x6f, 0x01, 0x00, 0x5a, 0x00, 0x40, 0x72, 500x00, 0x00, 0x5c, 0x00, 0x42, 0x31, 0x34, 0x2e, 0x31, 0x80,
510x6f, 0x6c, 0x73, 0x78, 0x01, 0x60, 0x54, 0x68, 0x69, 0x73, 510x01, 0x00, 0x5a, 0x00, 0xe0, 0x72, 0x6f, 0x6c, 0x73, 0x20,
520x00, 0x67, 0x18, 0x01, 0x00, 0x0e, 0x01, 0xf1, 0x02, 0x62, 520x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x67, 0x18,
530x65, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x00, 0x77, 530x01, 0x00, 0x0e, 0x01, 0xf1, 0x02, 0x62, 0x65, 0x00, 0x70,
540x69, 0x74, 0x68, 0x00, 0x65, 0x69, 0x5d, 0x00, 0x00, 0x3b, 540x6c, 0x61, 0x79, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68,
550x01, 0xa2, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 550x00, 0x65, 0x69, 0x5d, 0x00, 0x00, 0x3b, 0x01, 0xa2, 0x6b,
560x00, 0x6f, 0x10, 0x00, 0x50, 0x6d, 0x6f, 0x75, 0x73, 0x65, 560x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x6f, 0x10,
570x57, 0x00, 0xf4, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 570x00, 0x50, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x57, 0x00, 0xf4,
580x6c, 0x69, 0x63, 0x6b, 0x00, 0x69, 0x6e, 0x00, 0x61, 0xbe, 580x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63,
590x00, 0x24, 0x74, 0x6f, 0x7c, 0x00, 0x01, 0x51, 0x01, 0x00, 590x6b, 0x00, 0x69, 0x6e, 0x00, 0x61, 0xbe, 0x00, 0x24, 0x74,
600x4c, 0x01, 0xa6, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 600x6f, 0x7c, 0x00, 0x01, 0x51, 0x01, 0x00, 0x4c, 0x01, 0xa6,
610x74, 0x65, 0x64, 0xdb, 0x01, 0x61, 0x6f, 0x72, 0x00, 0x75, 610x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64,
620x73, 0x65, 0x52, 0x00, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f, 620xdb, 0x01, 0x61, 0x6f, 0x72, 0x00, 0x75, 0x73, 0x65, 0x52,
630x72, 0x69, 0x00, 0x01, 0xcc, 0x01, 0x04, 0x99, 0x01, 0x08, 630x00, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x69, 0x00,
640x97, 0x01, 0x01, 0xc4, 0x01, 0x80, 0x70, 0x61, 0x63, 0x65, 640x01, 0xcc, 0x01, 0x04, 0x99, 0x01, 0x08, 0x97, 0x01, 0x01,
650x00, 0x62, 0x61, 0x72, 0x3c, 0x00, 0x41, 0x45, 0x6e, 0x74, 650xc4, 0x01, 0x80, 0x70, 0x61, 0x63, 0x65, 0x00, 0x62, 0x61,
660x65, 0x33, 0x00, 0x04, 0x70, 0x00, 0x00, 0x92, 0x00, 0x21, 660x72, 0x3c, 0x00, 0x41, 0x45, 0x6e, 0x74, 0x65, 0x33, 0x00,
670x49, 0x66, 0xfb, 0x00, 0x04, 0x58, 0x00, 0xf4, 0x06, 0x60, 670x04, 0x70, 0x00, 0x00, 0x92, 0x00, 0x21, 0x49, 0x66, 0xfb,
680x53, 0x6f, 0x6c, 0x76, 0x65, 0x27, 0x00, 0x66, 0x75, 0x6e, 680x00, 0x04, 0x58, 0x00, 0xf4, 0x06, 0x60, 0x53, 0x6f, 0x6c,
690x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x6f, 0x6e, 0x00, 0x74, 690x76, 0x65, 0x27, 0x00, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
700xf6, 0x00, 0x10, 0x2c, 0xa1, 0x00, 0x60, 0x77, 0x69, 0x6c, 700x6f, 0x6e, 0x00, 0x6f, 0x6e, 0x00, 0x74, 0xf6, 0x00, 0x10,
710x6c, 0x00, 0x6d, 0xce, 0x01, 0x01, 0x5b, 0x02, 0x29, 0x6f, 710x2c, 0xa1, 0x00, 0x60, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x6d,
720x66, 0x3f, 0x02, 0x74, 0x69, 0x6e, 0x00, 0x72, 0x65, 0x64, 720xce, 0x01, 0x01, 0x5b, 0x02, 0x29, 0x6f, 0x66, 0x3f, 0x02,
730x2e, 0x57, 0x00, 0x02, 0xeb, 0x00, 0x40, 0x6f, 0x6e, 0x63, 730x74, 0x69, 0x6e, 0x00, 0x72, 0x65, 0x64, 0x2e, 0x57, 0x00,
740x65, 0x1a, 0x00, 0x45, 0x65, 0x76, 0x65, 0x72, 0x3a, 0x02, 740x02, 0xeb, 0x00, 0x40, 0x6f, 0x6e, 0x63, 0x65, 0x1a, 0x00,
750x01, 0x34, 0x01, 0x10, 0x61, 0x2e, 0x00, 0x01, 0x4e, 0x00, 750x45, 0x65, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x01, 0x34, 0x01,
760x11, 0x2c, 0x47, 0x00, 0x01, 0x5c, 0x01, 0x60, 0x73, 0x68, 760x10, 0x61, 0x2e, 0x00, 0x01, 0x4e, 0x00, 0x11, 0x2c, 0x47,
770x6f, 0x75, 0x6c, 0x64, 0x5f, 0x01, 0x10, 0x73, 0x8d, 0x00, 770x00, 0x01, 0x5c, 0x01, 0x60, 0x73, 0x68, 0x6f, 0x75, 0x6c,
780x49, 0x64, 0x2e, 0x00, 0x28, 0x4f, 0x00, 0x08, 0x3a, 0x01, 780x64, 0x5f, 0x01, 0x10, 0x73, 0x8d, 0x00, 0x49, 0x64, 0x2e,
790x00, 0x46, 0x00, 0x39, 0x6f, 0x75, 0x74, 0x49, 0x00, 0x06, 790x00, 0x28, 0x4f, 0x00, 0x08, 0x3a, 0x01, 0x00, 0x46, 0x00,
800x0c, 0x00, 0x02, 0xad, 0x00, 0x60, 0x61, 0x70, 0x70, 0x65, 800x39, 0x6f, 0x75, 0x74, 0x49, 0x00, 0x06, 0x0c, 0x00, 0x02,
810x61, 0x72, 0x37, 0x00, 0x11, 0x69, 0x93, 0x02, 0x70, 0x69, 810xad, 0x00, 0x60, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x37,
820x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0xf1, 0x00, 0x21, 0x61, 820x00, 0x11, 0x69, 0x93, 0x02, 0x70, 0x69, 0x6e, 0x64, 0x69,
830x74, 0x58, 0x00, 0x01, 0x27, 0x00, 0x40, 0x6e, 0x65, 0x65, 830x63, 0x61, 0x74, 0xf1, 0x00, 0x21, 0x61, 0x74, 0x58, 0x00,
840x64, 0x1f, 0x00, 0x10, 0x72, 0xa7, 0x00, 0x13, 0x73, 0x1e, 840x01, 0x27, 0x00, 0x40, 0x6e, 0x65, 0x65, 0x64, 0x1f, 0x00,
850x00, 0x51, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x09, 0x01, 0x01, 850x10, 0x72, 0xa7, 0x00, 0x13, 0x73, 0x1e, 0x00, 0x51, 0x6f,
860x1a, 0x00, 0x00, 0x72, 0x02, 0x21, 0x74, 0x68, 0x99, 0x00, 860x70, 0x65, 0x72, 0x61, 0x09, 0x01, 0x01, 0x1a, 0x00, 0x00,
870x10, 0x75, 0x16, 0x00, 0x73, 0x2e, 0x29, 0x00, 0x00, 0x00, 870x72, 0x02, 0x21, 0x74, 0x68, 0x99, 0x00, 0x10, 0x75, 0x16,
880x28, 0x41, 0x44, 0x03, 0x11, 0x61, 0x33, 0x01, 0xb0, 0x73, 880x00, 0x73, 0x2e, 0x29, 0x00, 0x00, 0x00, 0x28, 0x41, 0x44,
890x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 890x03, 0x11, 0x61, 0x33, 0x01, 0xb0, 0x73, 0x00, 0x64, 0x65,
900x74, 0x00, 0x22, 0x73, 0x65, 0x49, 0x01, 0x41, 0x32, 0x2e, 900x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x74, 0x00, 0x22,
910x31, 0x00, 0x9b, 0x01, 0xf2, 0x00, 0x6c, 0x73, 0x6f, 0x00, 910x73, 0x65, 0x49, 0x01, 0x41, 0x32, 0x2e, 0x31, 0x00, 0x9b,
920x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 920x01, 0xf2, 0x00, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61,
930x29, 0x6d, 0x02, 0x12, 0x32, 0x6d, 0x02, 0x93, 0x70, 0x61, 930x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x6d, 0x02,
940x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x6f, 0x02, 0x46, 940x12, 0x32, 0x6d, 0x02, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d,
950x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x40, 0x00, 0x04, 950x65, 0x74, 0x65, 0x72, 0x6f, 0x02, 0x46, 0x65, 0x73, 0x65,
960x3b, 0x00, 0x02, 0x69, 0x03, 0x01, 0xb5, 0x01, 0xd6, 0x43, 960x00, 0x14, 0x00, 0x02, 0x40, 0x00, 0x04, 0x3b, 0x00, 0x02,
970x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 970x69, 0x03, 0x01, 0xb5, 0x01, 0xd6, 0x43, 0x75, 0x73, 0x74,
980x6f, 0x70, 0xb7, 0x01, 0xd0, 0x65, 0x00, 0x60, 0x54, 0x79, 980x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0xb7,
990x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xed, 0x01, 990x01, 0xd0, 0x65, 0x00, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27,
1000x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 1000x00, 0x6d, 0x65, 0x6e, 0x75, 0xed, 0x01, 0x91, 0x57, 0x69,
1010x9e, 0x03, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0xc2, 0x01, 1010x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x9e, 0x03, 0x51,
1020x01, 0x45, 0x04, 0x24, 0x69, 0x6e, 0xc6, 0x01, 0x10, 0x2e, 1020x00, 0x00, 0x53, 0x69, 0x7a, 0xc2, 0x01, 0x01, 0x45, 0x04,
1030x1b, 0x00, 0x94, 0x68, 0x61, 0x70, 0x65, 0x20, 0x74, 0x79, 1030x24, 0x69, 0x6e, 0xc6, 0x01, 0x10, 0x2e, 0x1b, 0x00, 0x94,
1040x70, 0x65, 0xf8, 0x02, 0x03, 0x09, 0x03, 0x20, 0x00, 0x64, 1040x68, 0x61, 0x70, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0xf8,
1050x8c, 0x00, 0x52, 0x6d, 0x69, 0x6e, 0x65, 0x73, 0x0b, 0x01, 1050x02, 0x03, 0x09, 0x03, 0x20, 0x00, 0x64, 0x8c, 0x00, 0x52,
1060x00, 0x29, 0x00, 0x04, 0x07, 0x02, 0x30, 0x72, 0x65, 0x67, 1060x6d, 0x69, 0x6e, 0x65, 0x73, 0x0b, 0x01, 0x00, 0x29, 0x00,
1070x7e, 0x00, 0x02, 0x6d, 0x03, 0x21, 0x69, 0x73, 0x66, 0x02, 1070x04, 0x07, 0x02, 0x30, 0x72, 0x65, 0x67, 0x7e, 0x00, 0x02,
1080x62, 0x70, 0x65, 0x64, 0x00, 0x62, 0x79, 0xbc, 0x01, 0x00, 1080x6d, 0x03, 0x21, 0x69, 0x73, 0x66, 0x02, 0x62, 0x70, 0x65,
1090x8a, 0x03, 0x21, 0x69, 0x6e, 0x41, 0x04, 0x44, 0x67, 0x69, 1090x64, 0x00, 0x62, 0x79, 0xbc, 0x01, 0x00, 0x8a, 0x03, 0x21,
1100x76, 0x65, 0x72, 0x00, 0x10, 0x2e, 0xeb, 0x00, 0xfa, 0x14, 1100x69, 0x6e, 0x41, 0x04, 0x44, 0x67, 0x69, 0x76, 0x65, 0x72,
1110x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x73, 1110x00, 0x10, 0x2e, 0xeb, 0x00, 0xfa, 0x14, 0x00, 0x64, 0x65,
1120x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x00, 0x60, 0x43, 1120x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x73, 0x65, 0x74, 0x74,
1130x72, 0x6f, 0x73, 0x73, 0x65, 0x73, 0x27, 0x2c, 0x00, 0x63, 1130x69, 0x6e, 0x67, 0x2c, 0x00, 0x60, 0x43, 0x72, 0x6f, 0x73,
1140x61, 0x75, 0x73, 0x65, 0x73, 0x42, 0x02, 0x06, 0x36, 0x03, 1140x73, 0x65, 0x73, 0x27, 0x2c, 0x00, 0x63, 0x61, 0x75, 0x73,
1150x45, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x03, 0x10, 0x66, 0xe7, 1150x65, 0x73, 0x42, 0x02, 0x06, 0x36, 0x03, 0x45, 0x73, 0x65,
1160x04, 0x60, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0xe5, 0x01, 1160x6c, 0x66, 0x3a, 0x03, 0x10, 0x66, 0xe7, 0x04, 0x60, 0x69,
1170x10, 0x6e, 0xed, 0x00, 0x71, 0x62, 0x6f, 0x75, 0x72, 0x73, 1170x6d, 0x6d, 0x65, 0x64, 0x69, 0xe5, 0x01, 0x10, 0x6e, 0xed,
1180x00, 0x28, 0x8f, 0x03, 0x31, 0x72, 0x65, 0x65, 0x98, 0x03, 1180x00, 0x71, 0x62, 0x6f, 0x75, 0x72, 0x73, 0x00, 0x28, 0x8f,
1190xc0, 0x77, 0x6f, 0x00, 0x69, 0x66, 0x00, 0x69, 0x74, 0x27, 1190x03, 0x31, 0x72, 0x65, 0x65, 0x98, 0x03, 0xc0, 0x77, 0x6f,
1200x73, 0x00, 0x61, 0x78, 0x03, 0x41, 0x00, 0x65, 0x64, 0x67, 1200x00, 0x69, 0x66, 0x00, 0x69, 0x74, 0x27, 0x73, 0x00, 0x61,
1210x1a, 0x00, 0x72, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x29, 1210x78, 0x03, 0x41, 0x00, 0x65, 0x64, 0x67, 0x1a, 0x00, 0x72,
1220x95, 0x00, 0x03, 0x32, 0x04, 0x05, 0x93, 0x00, 0x66, 0x52, 1220x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x29, 0x95, 0x00, 0x03,
1230x61, 0x6e, 0x64, 0x6f, 0x6d, 0x92, 0x00, 0x31, 0x61, 0x00, 1230x32, 0x04, 0x05, 0x93, 0x00, 0x66, 0x52, 0x61, 0x6e, 0x64,
1240x72, 0x12, 0x00, 0x03, 0x08, 0x01, 0x20, 0x74, 0x6f, 0xb8, 1240x6f, 0x6d, 0x92, 0x00, 0x31, 0x61, 0x00, 0x72, 0x12, 0x00,
1250x02, 0xa9, 0x63, 0x68, 0x6f, 0x73, 0x65, 0x6e, 0x00, 0x66, 1250x03, 0x08, 0x01, 0x20, 0x74, 0x6f, 0xb8, 0x02, 0xa9, 0x63,
1260x6f, 0x72, 0xb2, 0x00, 0x00, 0x9d, 0x05, 0x06, 0xe7, 0x02, 1260x68, 0x6f, 0x73, 0x65, 0x6e, 0x00, 0x66, 0x6f, 0x72, 0xb2,
1270xc3, 0x69, 0x73, 0x00, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 1270x00, 0x00, 0x9d, 0x05, 0x06, 0xe7, 0x02, 0xc3, 0x69, 0x73,
1280x65, 0x6e, 0x74, 0x27, 0x00, 0x60, 0x74, 0x69, 0x6d, 0x65, 1280x00, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74,
1290x2e, 0x00, 1290x27, 0x00, 0x60, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x00,
130}; 130};
131 131
132const unsigned short help_text_len = 1522; 132const unsigned short help_text_len = 1539;
133const unsigned short help_text_words = 298; 133const unsigned short help_text_words = 299;
134const bool help_valid = true;
134const char quick_help_text[] = "Flip groups of squares to light them all up at once."; 135const char quick_help_text[] = "Flip groups of squares to light them all up at once.";
diff --git a/apps/plugins/puzzles/help/flood.c b/apps/plugins/puzzles/help/flood.c
index 80e4383903..ad25a5cf34 100644
--- a/apps/plugins/puzzles/help/flood.c
+++ b/apps/plugins/puzzles/help/flood.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,180 +6,181 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 155, TEXT_CENTER | C_RED }, 9 { 156, TEXT_CENTER | C_RED },
10 { 261, TEXT_CENTER | C_RED }, 10 { 262, TEXT_CENTER | C_RED },
11 { 278, TEXT_UNDERLINE },
12 { 279, TEXT_UNDERLINE }, 11 { 279, TEXT_UNDERLINE },
13 { 290, TEXT_UNDERLINE }, 12 { 280, TEXT_UNDERLINE },
14 { 332, TEXT_UNDERLINE }, 13 { 291, TEXT_UNDERLINE },
15 { 444, TEXT_UNDERLINE }, 14 { 333, TEXT_UNDERLINE },
15 { 445, TEXT_UNDERLINE },
16 LAST_STYLE_ITEM 16 LAST_STYLE_ITEM
17}; 17};
18 18
19/* orig 2377 comp 1592 ratio 0.669752 level 10 saved 785 */ 19/* orig 2395 comp 1600 ratio 0.668058 level 10 saved 795 */
20const char help_text[] = { 20const char help_text[] = {
210xf3, 0x45, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 210xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
220x33, 0x39, 0x3a, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 220x33, 0x39, 0x3a, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x64, 0x20,
230x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 230x00, 0x2d, 0x01, 0x00, 0xf3, 0x33, 0x00, 0x00, 0x00, 0x59,
240x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 240x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76,
250x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 250x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00,
260x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 260x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73,
270x75, 0x72, 0x65, 0x64, 0x00, 0x61, 0x74, 0x00, 0x72, 0x61, 270x2c, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x65, 0x64,
280x6e, 0x64, 0x6f, 0x6d, 0x00, 0x69, 0x6e, 0x00, 0x6d, 0x75, 280x00, 0x61, 0x74, 0x00, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
290x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x1f, 0x00, 0xf0, 0x0b, 290x00, 0x69, 0x6e, 0x00, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
300x73, 0x2e, 0x00, 0x49, 0x6e, 0x00, 0x65, 0x61, 0x63, 0x68, 300x6c, 0x65, 0x1f, 0x00, 0xf0, 0x0b, 0x73, 0x2e, 0x00, 0x49,
310x00, 0x6d, 0x6f, 0x76, 0x65, 0x2c, 0x00, 0x79, 0x6f, 0x75, 310x6e, 0x00, 0x65, 0x61, 0x63, 0x68, 0x00, 0x6d, 0x6f, 0x76,
320x00, 0x63, 0x61, 0x6e, 0x00, 0x66, 0x68, 0x00, 0xf3, 0x03, 320x65, 0x2c, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x63, 0x61, 0x6e,
330x2d, 0x66, 0x69, 0x6c, 0x6c, 0x00, 0x74, 0x68, 0x65, 0x00, 330x00, 0x66, 0x7a, 0x00, 0xf3, 0x03, 0x2d, 0x66, 0x69, 0x6c,
340x74, 0x6f, 0x70, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x5f, 0x00, 340x6c, 0x00, 0x74, 0x68, 0x65, 0x00, 0x74, 0x6f, 0x70, 0x00,
350x00, 0x4a, 0x00, 0x13, 0x61, 0x43, 0x00, 0x00, 0x75, 0x00, 350x6c, 0x65, 0x66, 0x74, 0x5f, 0x00, 0x00, 0x4a, 0x00, 0x13,
360x10, 0x79, 0x08, 0x00, 0xf4, 0x03, 0x63, 0x68, 0x6f, 0x69, 360x61, 0x43, 0x00, 0x00, 0x75, 0x00, 0x10, 0x79, 0x08, 0x00,
370x63, 0x65, 0x00, 0x28, 0x69, 0x2e, 0x65, 0x2e, 0x00, 0x65, 370xf4, 0x03, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x00, 0x28,
380x76, 0x65, 0x72, 0x79, 0x2e, 0x00, 0x10, 0x72, 0x61, 0x00, 380x69, 0x2e, 0x65, 0x2e, 0x00, 0x65, 0x76, 0x65, 0x72, 0x79,
390x91, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x66, 0x72, 0x6f, 0x6d, 390x2e, 0x00, 0x10, 0x72, 0x61, 0x00, 0x91, 0x61, 0x62, 0x6c,
400x51, 0x00, 0x84, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 400x65, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x51, 0x00, 0x84, 0x73,
410x67, 0x23, 0x00, 0xf7, 0x12, 0x62, 0x79, 0x00, 0x61, 0x6e, 410x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x23, 0x00, 0xf7,
420x00, 0x6f, 0x72, 0x74, 0x68, 0x6f, 0x67, 0x6f, 0x6e, 0x61, 420x12, 0x62, 0x79, 0x00, 0x61, 0x6e, 0x00, 0x6f, 0x72, 0x74,
430x6c, 0x6c, 0x79, 0x00, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 430x68, 0x6f, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x00,
440x74, 0x65, 0x64, 0x00, 0x70, 0x61, 0x74, 0x68, 0xdc, 0x00, 440x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00,
450x23, 0x00, 0x61, 0x96, 0x00, 0x44, 0x73, 0x61, 0x6d, 0x65, 450x70, 0x61, 0x74, 0x68, 0xdc, 0x00, 0x23, 0x00, 0x61, 0x96,
460x86, 0x00, 0x10, 0x77, 0xab, 0x00, 0x30, 0x62, 0x65, 0x00, 460x00, 0x44, 0x73, 0x61, 0x6d, 0x65, 0x86, 0x00, 0x10, 0x77,
470xb3, 0x00, 0x20, 0x65, 0x64, 0xa1, 0x00, 0x00, 0x22, 0x00, 470xab, 0x00, 0x30, 0x62, 0x65, 0x00, 0xb3, 0x00, 0x20, 0x65,
480x33, 0x6e, 0x65, 0x77, 0x21, 0x00, 0x51, 0x29, 0x2e, 0x00, 480x64, 0xa1, 0x00, 0x00, 0x22, 0x00, 0x33, 0x6e, 0x65, 0x77,
490x41, 0x73, 0xdf, 0x00, 0xf2, 0x02, 0x64, 0x6f, 0x00, 0x74, 490x21, 0x00, 0x51, 0x29, 0x2e, 0x00, 0x41, 0x73, 0xdf, 0x00,
500x68, 0x69, 0x73, 0x2c, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 500xf2, 0x02, 0x64, 0x6f, 0x00, 0x74, 0x68, 0x69, 0x73, 0x2c,
510x61, 0x6e, 0x64, 0x09, 0x00, 0x21, 0x6f, 0x66, 0x32, 0x00, 510x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x61, 0x6e, 0x64, 0x09,
520x01, 0x48, 0x01, 0x77, 0x62, 0x65, 0x63, 0x6f, 0x6d, 0x65, 520x00, 0x21, 0x6f, 0x66, 0x32, 0x00, 0x01, 0x48, 0x01, 0x77,
530x73, 0x83, 0x00, 0x2f, 0x74, 0x6f, 0xb7, 0x00, 0x01, 0x12, 530x62, 0x65, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x83, 0x00, 0x2f,
540x2e, 0x89, 0x01, 0x80, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 540x74, 0x6f, 0xb7, 0x00, 0x01, 0x12, 0x2e, 0x89, 0x01, 0x80,
550x69, 0x73, 0x26, 0x00, 0x41, 0x6d, 0x61, 0x6b, 0x65, 0x2b, 550x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x26, 0x00,
560x00, 0x43, 0x77, 0x68, 0x6f, 0x6c, 0x4f, 0x00, 0x0b, 0xac, 560x41, 0x6d, 0x61, 0x6b, 0x65, 0x2b, 0x00, 0x43, 0x77, 0x68,
570x00, 0x11, 0x2c, 0x3f, 0x01, 0x51, 0x73, 0x00, 0x66, 0x65, 570x6f, 0x6c, 0x4f, 0x00, 0x0b, 0xac, 0x00, 0x11, 0x2c, 0x3f,
580x77, 0x76, 0x01, 0x10, 0x73, 0x0d, 0x00, 0xb0, 0x70, 0x6f, 580x01, 0x51, 0x73, 0x00, 0x66, 0x65, 0x77, 0x76, 0x01, 0x10,
590x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x2e, 0x00, 0x54, 0x86, 590x73, 0x0d, 0x00, 0xb0, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62,
600x00, 0x00, 0x2e, 0x00, 0x01, 0xd3, 0x00, 0xd3, 0x73, 0x65, 600x6c, 0x65, 0x2e, 0x00, 0x54, 0x86, 0x00, 0x00, 0x2e, 0x00,
610x74, 0x00, 0x61, 0x00, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 610x01, 0xd3, 0x00, 0xd3, 0x73, 0x65, 0x74, 0x00, 0x61, 0x00,
620x6f, 0xd5, 0x00, 0x41, 0x75, 0x6d, 0x62, 0x65, 0x78, 0x01, 620x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x6f, 0xd5, 0x00, 0x41,
630x01, 0x3e, 0x00, 0x70, 0x2c, 0x00, 0x62, 0x61, 0x73, 0x65, 630x75, 0x6d, 0x62, 0x65, 0x78, 0x01, 0x01, 0x3e, 0x00, 0x70,
640x64, 0x1e, 0x00, 0x40, 0x72, 0x75, 0x6e, 0x6e, 0x9e, 0x00, 640x2c, 0x00, 0x62, 0x61, 0x73, 0x65, 0x64, 0x1e, 0x00, 0x40,
650xf1, 0x09, 0x69, 0x74, 0x73, 0x00, 0x6f, 0x77, 0x6e, 0x00, 650x72, 0x75, 0x6e, 0x6e, 0x9e, 0x00, 0xf1, 0x09, 0x69, 0x74,
660x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x00, 0x73, 660x73, 0x00, 0x6f, 0x77, 0x6e, 0x00, 0x69, 0x6e, 0x74, 0x65,
670x6f, 0x6c, 0x76, 0x65, 0x72, 0x2e, 0x36, 0x02, 0x65, 0x77, 670x72, 0x6e, 0x61, 0x6c, 0x00, 0x73, 0x6f, 0x6c, 0x76, 0x65,
680x69, 0x6e, 0x00, 0x69, 0x66, 0xea, 0x01, 0x0f, 0xb1, 0x00, 680x72, 0x2e, 0x36, 0x02, 0x65, 0x77, 0x69, 0x6e, 0x00, 0x69,
690x10, 0x02, 0x4e, 0x01, 0x73, 0x61, 0x74, 0x00, 0x6d, 0x61, 690x66, 0xea, 0x01, 0x0f, 0xb1, 0x00, 0x10, 0x02, 0x4e, 0x01,
700x6e, 0x79, 0xb3, 0x00, 0x20, 0x6f, 0x72, 0xc0, 0x00, 0x20, 700x73, 0x61, 0x74, 0x00, 0x6d, 0x61, 0x6e, 0x79, 0xb3, 0x00,
710x65, 0x72, 0x03, 0x01, 0x51, 0x49, 0x00, 0x73, 0x61, 0x77, 710x20, 0x6f, 0x72, 0xc0, 0x00, 0x20, 0x65, 0x72, 0x03, 0x01,
720x55, 0x01, 0x02, 0xb9, 0x00, 0xd2, 0x28, 0x77, 0x69, 0x74, 720x51, 0x49, 0x00, 0x73, 0x61, 0x77, 0x55, 0x01, 0x02, 0xb9,
730x68, 0x00, 0x61, 0x00, 0x66, 0x69, 0x78, 0x65, 0x64, 0x52, 730x00, 0xd2, 0x28, 0x77, 0x69, 0x74, 0x68, 0x00, 0x61, 0x00,
740x00, 0x53, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x11, 0x00, 0x06, 740x66, 0x69, 0x78, 0x65, 0x64, 0x52, 0x00, 0x53, 0x73, 0x69,
750xc0, 0x00, 0x03, 0x85, 0x02, 0x11, 0x2c, 0x86, 0x01, 0x02, 750x7a, 0x65, 0x2c, 0x11, 0x00, 0x06, 0xc0, 0x00, 0x03, 0x85,
760x1d, 0x00, 0x00, 0x5e, 0x00, 0x02, 0xef, 0x00, 0x10, 0x29, 760x02, 0x11, 0x2c, 0x86, 0x01, 0x02, 0x1d, 0x00, 0x00, 0x5e,
770xba, 0x02, 0x71, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 770x00, 0x02, 0xef, 0x00, 0x10, 0x29, 0xba, 0x02, 0x71, 0x68,
780x8f, 0x02, 0xf2, 0x0f, 0x69, 0x74, 0x2e, 0x61, 0x70, 0x70, 780x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x8f, 0x02, 0xf2, 0x0f,
790x73, 0x70, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x28, 790x69, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x73, 0x70, 0x6f, 0x74,
800x6e, 0x6f, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x00, 800x2e, 0x63, 0x6f, 0x6d, 0x00, 0x28, 0x6e, 0x6f, 0x00, 0x6c,
810x61, 0x63, 0x63, 0x65, 0x3f, 0x01, 0x10, 0x29, 0x90, 0x00, 810x6f, 0x6e, 0x67, 0x65, 0x72, 0x00, 0x61, 0x63, 0x63, 0x65,
820x43, 0x33, 0x39, 0x2e, 0x31, 0x2a, 0x03, 0x80, 0x63, 0x6f, 820x3f, 0x01, 0x10, 0x29, 0x90, 0x00, 0x43, 0x33, 0x39, 0x2e,
830x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x33, 0x03, 0x81, 0x54, 830x31, 0x3c, 0x03, 0xf1, 0x05, 0x63, 0x6f, 0x6e, 0x74, 0x72,
840x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x1a, 0x00, 0x71, 840x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x00,
850x2c, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xeb, 0x00, 0x43, 850x70, 0x6c, 0x61, 0x79, 0x00, 0x1a, 0x00, 0x71, 0x2c, 0x00,
860x6d, 0x6f, 0x75, 0x73, 0xd4, 0x02, 0x04, 0xd8, 0x01, 0x18, 860x63, 0x6c, 0x69, 0x63, 0x6b, 0xeb, 0x00, 0x43, 0x6d, 0x6f,
870x54, 0xf5, 0x02, 0x61, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 870x75, 0x73, 0xd4, 0x02, 0x04, 0xd8, 0x01, 0x18, 0x54, 0xf5,
880xa5, 0x00, 0x01, 0xd8, 0x02, 0x20, 0x74, 0x68, 0x65, 0x01, 880x02, 0x61, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0xa5, 0x00,
890x09, 0x1d, 0x02, 0x26, 0x69, 0x74, 0x82, 0x02, 0x05, 0x3b, 890x01, 0xd8, 0x02, 0x20, 0x74, 0x68, 0x65, 0x01, 0x09, 0x1d,
900x03, 0x31, 0x65, 0x64, 0x00, 0x0a, 0x01, 0x25, 0x74, 0x68, 900x02, 0x26, 0x69, 0x74, 0x82, 0x02, 0x05, 0x3b, 0x03, 0x31,
910x4a, 0x01, 0x03, 0x66, 0x02, 0x03, 0xf6, 0x02, 0x01, 0x84, 910x65, 0x64, 0x00, 0x0a, 0x01, 0x25, 0x74, 0x68, 0x4a, 0x01,
920x01, 0x00, 0x87, 0x00, 0x50, 0x65, 0x64, 0x2e, 0x00, 0x43, 920x03, 0x66, 0x02, 0x03, 0xf6, 0x02, 0x01, 0x84, 0x01, 0x00,
930x09, 0x00, 0x00, 0x59, 0x00, 0x04, 0x86, 0x00, 0x0d, 0x87, 930x87, 0x00, 0x50, 0x65, 0x64, 0x2e, 0x00, 0x43, 0x09, 0x00,
940x01, 0x4f, 0x61, 0x73, 0x00, 0x74, 0x98, 0x00, 0x00, 0xd2, 940x00, 0x59, 0x00, 0x04, 0x86, 0x00, 0x0d, 0x87, 0x01, 0x4f,
950x68, 0x61, 0x73, 0x00, 0x6e, 0x6f, 0x00, 0x65, 0x66, 0x66, 950x61, 0x73, 0x00, 0x74, 0x98, 0x00, 0x00, 0xd2, 0x68, 0x61,
960x65, 0x63, 0x74, 0x4c, 0x01, 0x60, 0x74, 0x68, 0x65, 0x72, 960x73, 0x00, 0x6e, 0x6f, 0x00, 0x65, 0x66, 0x66, 0x65, 0x63,
970x65, 0x66, 0xd7, 0x02, 0x30, 0x64, 0x6f, 0x65, 0x1e, 0x00, 970x74, 0x4c, 0x01, 0x60, 0x74, 0x68, 0x65, 0x72, 0x65, 0x66,
980x00, 0x2d, 0x00, 0x30, 0x75, 0x6e, 0x74, 0x43, 0x00, 0x11, 980xd7, 0x02, 0x30, 0x64, 0x6f, 0x65, 0x1e, 0x00, 0x00, 0x2d,
990x61, 0x64, 0x01, 0x03, 0xbb, 0x02, 0x01, 0x03, 0x02, 0x50, 990x00, 0x30, 0x75, 0x6e, 0x74, 0x43, 0x00, 0x11, 0x61, 0x64,
1000x61, 0x6c, 0x73, 0x6f, 0x00, 0x03, 0x01, 0x01, 0xa9, 0x00, 1000x01, 0x03, 0xbb, 0x02, 0x01, 0x03, 0x02, 0x50, 0x61, 0x6c,
1010x92, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 1010x73, 0x6f, 0x00, 0x03, 0x01, 0x01, 0xa9, 0x00, 0x92, 0x75,
1020xd0, 0x02, 0x00, 0x90, 0x01, 0x14, 0x61, 0x16, 0x00, 0xe3, 1020x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xd0, 0x02,
1030x28, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x62, 1030x00, 0x90, 0x01, 0x14, 0x61, 0x16, 0x00, 0xe3, 0x28, 0x6f,
1040x6c, 0x61, 0x63, 0x6b, 0xa8, 0x00, 0x85, 0x29, 0x00, 0x61, 1040x75, 0x74, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x62, 0x6c, 0x61,
1050x72, 0x6f, 0x75, 0x6e, 0x64, 0x40, 0x03, 0x40, 0x2e, 0x00, 1050x63, 0x6b, 0xa8, 0x00, 0x85, 0x29, 0x00, 0x61, 0x72, 0x6f,
1060x50, 0x72, 0x90, 0x01, 0x21, 0x6e, 0x67, 0x13, 0x00, 0x60, 1060x75, 0x6e, 0x64, 0x40, 0x03, 0x40, 0x2e, 0x00, 0x50, 0x72,
1070x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x52, 0x00, 0x02, 0x24, 1070x90, 0x01, 0x21, 0x6e, 0x67, 0x13, 0x00, 0x60, 0x72, 0x65,
1080x01, 0x0e, 0x56, 0x04, 0x03, 0xc9, 0x00, 0x2f, 0x69, 0x6e, 1080x74, 0x75, 0x72, 0x6e, 0x52, 0x00, 0x02, 0x24, 0x01, 0x0e,
1090x2b, 0x01, 0x07, 0x57, 0x75, 0x6e, 0x64, 0x65, 0x72, 0xa1, 1090x56, 0x04, 0x03, 0xc9, 0x00, 0x2f, 0x69, 0x6e, 0x2b, 0x01,
1100x00, 0x00, 0xc0, 0x00, 0x23, 0x28, 0x41, 0x49, 0x00, 0xf2, 1100x07, 0x57, 0x75, 0x6e, 0x64, 0x65, 0x72, 0xa1, 0x00, 0x00,
1110x00, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 1110xc0, 0x00, 0x23, 0x28, 0x41, 0x49, 0x00, 0xf2, 0x00, 0x61,
1120x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x00, 0x04, 0x21, 0x73, 1120x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x65, 0x73,
1130x65, 0x16, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 0x2a, 0x05, 1130x63, 0x72, 0x69, 0x62, 0x00, 0x04, 0x21, 0x73, 0x65, 0x16,
1140x01, 0xe6, 0x00, 0x60, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 1140x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 0x2a, 0x05, 0x01, 0xe6,
1150x68, 0x03, 0x12, 0x29, 0x29, 0x02, 0x13, 0x32, 0x29, 0x02, 1150x00, 0x60, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x68, 0x03,
1160x92, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 1160x12, 0x29, 0x29, 0x02, 0x13, 0x32, 0x29, 0x02, 0x92, 0x70,
1170x2b, 0x02, 0x56, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 1170x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2b, 0x02,
1180x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x06, 0xc7, 0x04, 0xe1, 1180x56, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x41,
1190x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 1190x00, 0x04, 0x3c, 0x00, 0x06, 0xc7, 0x04, 0xe1, 0x60, 0x43,
1200x27, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x03, 0xa5, 0x03, 0xb0, 1200x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00,
1210x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 1210x6f, 0x70, 0x6f, 0x00, 0x03, 0xa5, 0x03, 0xb0, 0x60, 0x54,
1220x75, 0xab, 0x00, 0xf9, 0x04, 0x57, 0x69, 0x64, 0x74, 0x68, 1220x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xab,
1230x2c, 0x00, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 1230x00, 0xf9, 0x04, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00,
1240x00, 0x53, 0x69, 0x7a, 0x73, 0x04, 0x01, 0x14, 0x04, 0x03, 1240x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x00, 0x53,
1250xe0, 0x04, 0x00, 0x30, 0x00, 0x12, 0x43, 0x1a, 0x03, 0x4c, 1250x69, 0x7a, 0x73, 0x04, 0x01, 0x14, 0x04, 0x03, 0xe0, 0x04,
1260x00, 0x00, 0x00, 0x4e, 0x2e, 0x03, 0x00, 0xaa, 0x01, 0x01, 1260x00, 0x30, 0x00, 0x12, 0x43, 0x1a, 0x03, 0x4c, 0x00, 0x00,
1270x74, 0x02, 0x05, 0x48, 0x01, 0x02, 0x74, 0x01, 0x40, 0x4d, 1270x00, 0x4e, 0x2e, 0x03, 0x00, 0xaa, 0x01, 0x01, 0x74, 0x02,
1280x75, 0x73, 0x74, 0x80, 0x02, 0xa3, 0x61, 0x74, 0x00, 0x6c, 1280x05, 0x48, 0x01, 0x02, 0x74, 0x01, 0x40, 0x4d, 0x75, 0x73,
1290x65, 0x61, 0x73, 0x74, 0x00, 0x33, 0x89, 0x03, 0x35, 0x74, 1290x74, 0x80, 0x02, 0xa3, 0x61, 0x74, 0x00, 0x6c, 0x65, 0x61,
1300x77, 0x6f, 0x3c, 0x00, 0x01, 0x19, 0x02, 0x50, 0x00, 0x77, 1300x73, 0x74, 0x00, 0x33, 0x89, 0x03, 0x35, 0x74, 0x77, 0x6f,
1310x6f, 0x75, 0x6c, 0x31, 0x04, 0x20, 0x6c, 0x79, 0x31, 0x00, 1310x3c, 0x00, 0x01, 0x19, 0x02, 0x50, 0x00, 0x77, 0x6f, 0x75,
1320x93, 0x6f, 0x6e, 0x65, 0x00, 0x6c, 0x65, 0x67, 0x61, 0x6c, 1320x6c, 0x31, 0x04, 0x20, 0x6c, 0x79, 0x31, 0x00, 0x93, 0x6f,
1330xed, 0x01, 0x20, 0x74, 0x00, 0xe7, 0x03, 0xc0, 0x73, 0x74, 1330x6e, 0x65, 0x00, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0xed, 0x01,
1340x61, 0x67, 0x65, 0x2c, 0x00, 0x68, 0x65, 0x6e, 0x63, 0x65, 1340x20, 0x74, 0x00, 0xe7, 0x03, 0xc0, 0x73, 0x74, 0x61, 0x67,
1350x5f, 0x02, 0x03, 0xdf, 0x05, 0x04, 0xe3, 0x04, 0x20, 0x61, 1350x65, 0x2c, 0x00, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x02,
1360x74, 0x87, 0x05, 0x12, 0x29, 0x6f, 0x02, 0x00, 0x1b, 0x04, 1360x03, 0xdf, 0x05, 0x04, 0xe3, 0x04, 0x20, 0x61, 0x74, 0x87,
1370x60, 0x6f, 0x73, 0x74, 0x00, 0x31, 0x30, 0xbc, 0x00, 0x61, 1370x05, 0x12, 0x29, 0x6f, 0x02, 0x00, 0x1b, 0x04, 0x60, 0x6f,
1380x45, 0x78, 0x74, 0x72, 0x61, 0x20, 0x27, 0x04, 0x70, 0x20, 1380x73, 0x74, 0x00, 0x31, 0x30, 0xbc, 0x00, 0x61, 0x45, 0x78,
1390x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x25, 0x03, 0x00, 0xd4, 1390x74, 0x72, 0x61, 0x20, 0x27, 0x04, 0x70, 0x20, 0x70, 0x65,
1400x00, 0x02, 0x8c, 0x03, 0x01, 0xb4, 0x00, 0xa4, 0x64, 0x69, 1400x72, 0x6d, 0x69, 0x74, 0x25, 0x03, 0x00, 0xd4, 0x00, 0x02,
1410x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x07, 0x01, 1410x8c, 0x03, 0x01, 0xb4, 0x00, 0xa4, 0x64, 0x69, 0x66, 0x66,
1420x70, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x0b, 0x06, 1420x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x07, 0x01, 0x70, 0x70,
1430x65, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x41, 0x02, 0x06, 1430x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x0b, 0x06, 0x65, 0x69,
1440x14, 0x04, 0x06, 0xaf, 0x06, 0x00, 0xda, 0x05, 0x02, 0x39, 1440x6e, 0x63, 0x72, 0x65, 0x61, 0x41, 0x02, 0x06, 0x14, 0x04,
1450x01, 0x01, 0xc0, 0x01, 0x02, 0x5a, 0x02, 0x5d, 0x72, 0x75, 1450x06, 0xaf, 0x06, 0x00, 0xda, 0x05, 0x02, 0x39, 0x01, 0x01,
1460x6e, 0x00, 0x61, 0xf7, 0x04, 0x00, 0xb4, 0x00, 0x85, 0x67, 1460xc0, 0x01, 0x02, 0x5a, 0x02, 0x5d, 0x72, 0x75, 0x6e, 0x00,
1470x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x1b, 0x05, 0x40, 1470x61, 0xf7, 0x04, 0x00, 0xb4, 0x00, 0x85, 0x67, 0x65, 0x6e,
1480x73, 0x6f, 0x6c, 0x75, 0xb0, 0x01, 0x05, 0x30, 0x03, 0x02, 1480x65, 0x72, 0x61, 0x74, 0x65, 0x1b, 0x05, 0x40, 0x73, 0x6f,
1490xb7, 0x01, 0x41, 0x76, 0x61, 0x6c, 0x75, 0x0b, 0x04, 0x01, 1490x6c, 0x75, 0xb0, 0x01, 0x05, 0x30, 0x03, 0x02, 0xb7, 0x01,
1500xce, 0x04, 0x55, 0x66, 0x69, 0x65, 0x6c, 0x64, 0xd9, 0x03, 1500x41, 0x76, 0x61, 0x6c, 0x75, 0x0b, 0x04, 0x01, 0xce, 0x04,
1510x36, 0x61, 0x64, 0x64, 0x0a, 0x06, 0x42, 0x6c, 0x65, 0x6e, 1510x55, 0x66, 0x69, 0x65, 0x6c, 0x64, 0xd9, 0x03, 0x36, 0x61,
1520x67, 0x96, 0x06, 0x01, 0x80, 0x00, 0x25, 0x27, 0x73, 0x52, 1520x64, 0x64, 0x0a, 0x06, 0x42, 0x6c, 0x65, 0x6e, 0x67, 0x96,
1530x00, 0x09, 0x6f, 0x00, 0x13, 0x74, 0xcd, 0x05, 0x29, 0x27, 1530x06, 0x01, 0x80, 0x00, 0x25, 0x27, 0x73, 0x52, 0x00, 0x09,
1540x73, 0xc6, 0x00, 0x43, 0x53, 0x6f, 0x00, 0x61, 0x6c, 0x00, 1540x6f, 0x00, 0x13, 0x74, 0xcd, 0x05, 0x29, 0x27, 0x73, 0xc6,
1550xc2, 0x6f, 0x66, 0x00, 0x30, 0x00, 0x72, 0x65, 0x71, 0x75, 1550x00, 0x43, 0x53, 0x6f, 0x00, 0x61, 0x6c, 0x00, 0xc2, 0x6f,
1560x69, 0x72, 0x65, 0xa1, 0x06, 0x20, 0x74, 0x6f, 0x6e, 0x00, 1560x66, 0x00, 0x30, 0x00, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
1570x31, 0x6a, 0x75, 0x73, 0xaf, 0x03, 0x10, 0x65, 0x28, 0x01, 1570x65, 0xa1, 0x06, 0x20, 0x74, 0x6f, 0x6e, 0x00, 0x31, 0x6a,
1580x22, 0x69, 0x65, 0xbc, 0x03, 0x04, 0x6c, 0x00, 0x60, 0x61, 1580x75, 0x73, 0xaf, 0x03, 0x10, 0x65, 0x28, 0x01, 0x22, 0x69,
1590x75, 0x74, 0x6f, 0x6d, 0x61, 0x57, 0x01, 0x02, 0xe3, 0x00, 1590x65, 0xbc, 0x03, 0x04, 0x6c, 0x00, 0x60, 0x61, 0x75, 0x74,
1600x03, 0x87, 0x01, 0x40, 0x00, 0x6c, 0x61, 0x72, 0x10, 0x05, 1600x6f, 0x6d, 0x61, 0x57, 0x01, 0x02, 0xe3, 0x00, 0x03, 0x87,
1610x02, 0x5a, 0x00, 0x00, 0xa8, 0x01, 0x10, 0x73, 0x9a, 0x04, 1610x01, 0x40, 0x00, 0x6c, 0x61, 0x72, 0x10, 0x05, 0x02, 0x5a,
1620x00, 0x4b, 0x01, 0x12, 0x65, 0x32, 0x03, 0x42, 0x4e, 0x6f, 1620x00, 0x00, 0xa8, 0x01, 0x10, 0x73, 0x9a, 0x04, 0x00, 0x4b,
1630x74, 0x65, 0xcc, 0x05, 0x04, 0x4b, 0x00, 0x0c, 0x2d, 0x01, 1630x01, 0x12, 0x65, 0x32, 0x03, 0x42, 0x4e, 0x6f, 0x74, 0x65,
1640x01, 0xee, 0x00, 0x00, 0x31, 0x04, 0x20, 0x6e, 0x65, 0x55, 1640xcc, 0x05, 0x04, 0x4b, 0x00, 0x0c, 0x2d, 0x01, 0x01, 0xee,
1650x05, 0x93, 0x61, 0x72, 0x69, 0x6c, 0x79, 0x00, 0x66, 0x69, 1650x00, 0x00, 0x31, 0x04, 0x20, 0x6e, 0x65, 0x55, 0x05, 0x93,
1660x6e, 0x16, 0x06, 0x75, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x73, 1660x61, 0x72, 0x69, 0x6c, 0x79, 0x00, 0x66, 0x69, 0x6e, 0x16,
1670x74, 0xb0, 0x06, 0x07, 0x49, 0x01, 0xf0, 0x00, 0x74, 0x68, 1670x06, 0x75, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x73, 0x74, 0xb0,
1680x6f, 0x75, 0x67, 0x68, 0x00, 0x49, 0x00, 0x62, 0x65, 0x6c, 1680x06, 0x07, 0x49, 0x01, 0xf0, 0x00, 0x74, 0x68, 0x6f, 0x75,
1690x69, 0x65, 0x76, 0x6c, 0x01, 0xf0, 0x07, 0x27, 0x73, 0x00, 1690x67, 0x68, 0x00, 0x49, 0x00, 0x62, 0x65, 0x6c, 0x69, 0x65,
1700x70, 0x72, 0x65, 0x74, 0x74, 0x79, 0x00, 0x63, 0x6c, 0x6f, 1700x76, 0x6c, 0x01, 0xf0, 0x07, 0x27, 0x73, 0x00, 0x70, 0x72,
1710x73, 0x65, 0x2e, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x61, 0x11, 1710x65, 0x74, 0x74, 0x79, 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65,
1720x08, 0x60, 0x6c, 0x00, 0x63, 0x68, 0x61, 0x6c, 0x46, 0x01, 1720x2e, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x61, 0x11, 0x08, 0x60,
1730x21, 0x65, 0x2c, 0xe5, 0x06, 0x01, 0x71, 0x01, 0x02, 0xb9, 1730x6c, 0x00, 0x63, 0x68, 0x61, 0x6c, 0x46, 0x01, 0x21, 0x65,
1740x00, 0x47, 0x74, 0x6f, 0x00, 0x30, 0x97, 0x01, 0x20, 0x72, 1740x2c, 0xe5, 0x06, 0x01, 0x71, 0x01, 0x02, 0xb9, 0x00, 0x47,
1750x79, 0x12, 0x00, 0x01, 0x9f, 0x00, 0x04, 0xe9, 0x08, 0x00, 1750x74, 0x6f, 0x00, 0x30, 0x97, 0x01, 0x20, 0x72, 0x79, 0x12,
1760x2d, 0x03, 0x81, 0x74, 0x72, 0x69, 0x63, 0x74, 0x6c, 0x79, 1760x00, 0x01, 0x9f, 0x00, 0x04, 0xe9, 0x08, 0x00, 0x2d, 0x03,
1770x20, 0x87, 0x06, 0x03, 0x96, 0x06, 0x32, 0x74, 0x68, 0x61, 1770x81, 0x74, 0x72, 0x69, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x87,
1780xc8, 0x01, 0x02, 0x31, 0x07, 0xf0, 0x00, 0x79, 0x6f, 0x75, 1780x06, 0x03, 0x96, 0x06, 0x32, 0x74, 0x68, 0x61, 0xc8, 0x01,
1790x27, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x21, 1790x02, 0x31, 0x07, 0xf0, 0x00, 0x79, 0x6f, 0x75, 0x27, 0x72,
1800x29, 0x00, 1800x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x21, 0x29, 0x00,
181}; 181};
182 182
183const unsigned short help_text_len = 2377; 183const unsigned short help_text_len = 2395;
184const unsigned short help_text_words = 451; 184const unsigned short help_text_words = 452;
185const bool help_valid = true;
185const char quick_help_text[] = "Turn the grid the same colour in as few flood fills as possible."; 186const char quick_help_text[] = "Turn the grid the same colour in as few flood fills as possible.";
diff --git a/apps/plugins/puzzles/help/galaxies.c b/apps/plugins/puzzles/help/galaxies.c
index 3942832c52..10bcda7b3b 100644
--- a/apps/plugins/puzzles/help/galaxies.c
+++ b/apps/plugins/puzzles/help/galaxies.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,206 +6,207 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 132, TEXT_CENTER | C_RED }, 9 { 133, TEXT_CENTER | C_RED },
10 { 444, TEXT_CENTER | C_RED }, 10 { 445, TEXT_CENTER | C_RED },
11 { 461, TEXT_UNDERLINE },
12 { 462, TEXT_UNDERLINE }, 11 { 462, TEXT_UNDERLINE },
13 { 472, TEXT_UNDERLINE }, 12 { 463, TEXT_UNDERLINE },
13 { 473, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 2754 comp 1877 ratio 0.681554 level 10 saved 877 */ 17/* orig 2766 comp 1876 ratio 0.678236 level 10 saved 890 */
18const char help_text[] = { 18const char help_text[] = {
190xf0, 0x41, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x32, 0x38, 0x3a, 0x20, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x69, 200x32, 0x38, 0x3a, 0x20, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x69,
210x65, 0x73, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 210x65, 0x73, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf0, 0x2c,
220x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x72, 0x65, 0x63, 220x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76,
230x74, 0x61, 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x00, 0x67, 230x65, 0x00, 0x61, 0x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e,
240x72, 0x69, 0x64, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 240x67, 0x75, 0x6c, 0x61, 0x72, 0x00, 0x67, 0x72, 0x69, 0x64,
250x6e, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 250x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e,
260x62, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x00, 0x64, 0x6f, 0x74, 260x67, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
270x73, 0x2e, 0x39, 0x00, 0xf4, 0x0a, 0x72, 0x00, 0x61, 0x69, 270x00, 0x6f, 0x66, 0x00, 0x64, 0x6f, 0x74, 0x73, 0x2e, 0x39,
280x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x70, 0x61, 280x00, 0xf4, 0x0a, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69,
290x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x74, 0x68, 290x73, 0x00, 0x74, 0x6f, 0x00, 0x70, 0x61, 0x72, 0x74, 0x69,
300x65, 0x4b, 0x00, 0x70, 0x6c, 0x65, 0x00, 0x69, 0x6e, 0x74, 300x74, 0x69, 0x6f, 0x6e, 0x00, 0x74, 0x68, 0x65, 0x4b, 0x00,
310x6f, 0x49, 0x00, 0xe0, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 310x70, 0x6c, 0x65, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x49, 0x00,
320x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x47, 0x00, 320xe0, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x72, 0x65,
330xf3, 0x12, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 330x67, 0x69, 0x6f, 0x6e, 0x73, 0x47, 0x00, 0xf3, 0x12, 0x73,
340x00, 0x69, 0x6e, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x61, 340x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x69, 0x6e,
350x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 350x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x61, 0x00, 0x77, 0x61,
360x65, 0x76, 0x65, 0x72, 0x79, 0x2d, 0x00, 0x00, 0x61, 0x00, 360x79, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x65, 0x76, 0x65,
370xf0, 0x00, 0x31, 0x38, 0x30, 0x2d, 0x64, 0x65, 0x67, 0x72, 370x72, 0x79, 0x2d, 0x00, 0x00, 0x61, 0x00, 0xf0, 0x00, 0x31,
380x65, 0x65, 0x00, 0x72, 0x6f, 0x74, 0x61, 0x68, 0x00, 0xf5, 380x38, 0x30, 0x2d, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x00,
390x03, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x79, 0x6d, 0x6d, 390x72, 0x6f, 0x74, 0x61, 0x68, 0x00, 0xf5, 0x03, 0x61, 0x6c,
400x65, 0x74, 0x72, 0x69, 0x63, 0x2c, 0x00, 0x61, 0x6e, 0xb1, 400x6c, 0x79, 0x00, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72,
410x00, 0xd0, 0x73, 0x00, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 410x69, 0x63, 0x2c, 0x00, 0x61, 0x6e, 0xb1, 0x00, 0xd0, 0x73,
420x79, 0x00, 0x6f, 0x6e, 0x65, 0xaf, 0x00, 0x60, 0x00, 0x77, 420x00, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x00, 0x6f,
430x68, 0x69, 0x63, 0x68, 0x49, 0x00, 0x40, 0x6c, 0x6f, 0x63, 430x6e, 0x65, 0xaf, 0x00, 0x60, 0x00, 0x77, 0x68, 0x69, 0x63,
440x61, 0x88, 0x00, 0xd1, 0x61, 0x74, 0x00, 0x69, 0x74, 0x73, 440x68, 0x49, 0x00, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x88, 0x00,
450x00, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x65, 0x8e, 0x00, 0x02, 450xd1, 0x61, 0x74, 0x00, 0x69, 0x74, 0x73, 0x00, 0x63, 0x65,
460x4a, 0x00, 0xf0, 0x00, 0x79, 0x2e, 0x00, 0x00, 0x00, 0x54, 460x6e, 0x74, 0x72, 0x65, 0x8e, 0x00, 0x02, 0x4a, 0x00, 0xf0,
470x6f, 0x00, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x00, 0x79, 0xe4, 470x00, 0x79, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x00, 0x65,
480x00, 0x40, 0x73, 0x6f, 0x6c, 0x75, 0x71, 0x00, 0x10, 0x2c, 480x6e, 0x74, 0x65, 0x72, 0x00, 0x79, 0xe4, 0x00, 0x40, 0x73,
490x0f, 0x00, 0xf1, 0x02, 0x00, 0x64, 0x72, 0x61, 0x77, 0x00, 490x6f, 0x6c, 0x75, 0x71, 0x00, 0x10, 0x2c, 0x0f, 0x00, 0xf1,
500x6c, 0x69, 0x6e, 0x65, 0x73, 0x00, 0x61, 0x6c, 0x6f, 0x6e, 500x02, 0x00, 0x64, 0x72, 0x61, 0x77, 0x00, 0x6c, 0x69, 0x6e,
510x67, 0xef, 0x00, 0x01, 0x2e, 0x01, 0x41, 0x65, 0x64, 0x67, 510x65, 0x73, 0x00, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0xef, 0x00,
520x65, 0x0b, 0x01, 0x41, 0x6d, 0x61, 0x72, 0x6b, 0x17, 0x00, 520x01, 0x2e, 0x01, 0x41, 0x65, 0x64, 0x67, 0x65, 0x0b, 0x01,
530x91, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x69, 0x65, 530x41, 0x6d, 0x61, 0x72, 0x6b, 0x17, 0x00, 0x91, 0x62, 0x6f,
540xf0, 0x00, 0x02, 0x18, 0x01, 0x01, 0xff, 0x00, 0xc0, 0x2e, 540x75, 0x6e, 0x64, 0x61, 0x72, 0x69, 0x65, 0xf0, 0x00, 0x02,
550x00, 0x54, 0x68, 0x65, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 550x18, 0x01, 0x01, 0xff, 0x00, 0xc0, 0x2e, 0x00, 0x54, 0x68,
560x65, 0x96, 0x00, 0xc2, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 560x65, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x96, 0x00,
570x74, 0x65, 0x00, 0x77, 0x68, 0x65, 0x41, 0x01, 0x00, 0x44, 570xc2, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x00,
580x00, 0x23, 0x65, 0x64, 0x69, 0x00, 0x26, 0x6f, 0x6e, 0x66, 580x77, 0x68, 0x65, 0x41, 0x01, 0x00, 0x44, 0x00, 0x23, 0x65,
590x00, 0xc0, 0x61, 0x72, 0x65, 0x00, 0x70, 0x72, 0x65, 0x63, 590x64, 0x69, 0x00, 0x26, 0x6f, 0x6e, 0x66, 0x00, 0xc0, 0x61,
600x69, 0x73, 0x65, 0x6c, 0x2d, 0x01, 0x32, 0x6f, 0x73, 0x65, 600x72, 0x65, 0x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65,
610x33, 0x01, 0xc4, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 610x6c, 0x2d, 0x01, 0x32, 0x6f, 0x73, 0x65, 0x33, 0x01, 0xc4,
620x65, 0x00, 0x74, 0x77, 0x6f, 0x5c, 0x01, 0x30, 0x00, 0x62, 620x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x00, 0x74,
630x65, 0xa4, 0x00, 0x10, 0x69, 0xa7, 0x00, 0xb6, 0x6f, 0x00, 630x77, 0x6f, 0x5c, 0x01, 0x30, 0x00, 0x62, 0x65, 0xa4, 0x00,
640x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x87, 640x10, 0x69, 0xa7, 0x00, 0xb6, 0x6f, 0x00, 0x64, 0x69, 0x66,
650x00, 0x64, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x8a, 0x00, 650x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x87, 0x00, 0x64, 0x00,
660x90, 0x77, 0x61, 0x73, 0x00, 0x69, 0x6e, 0x76, 0x65, 0x6e, 660x00, 0x54, 0x68, 0x69, 0x73, 0x8a, 0x00, 0x90, 0x77, 0x61,
670x22, 0x01, 0xf1, 0x06, 0x62, 0x79, 0x00, 0x4e, 0x69, 0x6b, 670x73, 0x00, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x22, 0x01, 0xf1,
680x6f, 0x6c, 0x69, 0x00, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x00, 680x06, 0x62, 0x79, 0x00, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69,
690x75, 0x6e, 0x64, 0x65, 0x72, 0x88, 0x00, 0x80, 0x6e, 0x61, 690x00, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x00, 0x75, 0x6e, 0x64,
700x6d, 0x65, 0x00, 0x60, 0x54, 0x65, 0x6c, 0x01, 0x71, 0x00, 700x65, 0x72, 0x88, 0x00, 0x80, 0x6e, 0x61, 0x6d, 0x65, 0x00,
710x53, 0x68, 0x6f, 0x77, 0x27, 0x3b, 0x4d, 0x01, 0x01, 0x18, 710x60, 0x54, 0x65, 0x6c, 0x01, 0x71, 0x00, 0x53, 0x68, 0x6f,
720x00, 0x02, 0xce, 0x00, 0x30, 0x6d, 0x6f, 0x6e, 0x9e, 0x00, 720x77, 0x27, 0x3b, 0x4d, 0x01, 0x01, 0x18, 0x00, 0x02, 0xce,
730x51, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x70, 0x01, 0x01, 0x07, 730x00, 0x30, 0x6d, 0x6f, 0x6e, 0x9e, 0x00, 0x51, 0x72, 0x61,
740x02, 0xf4, 0x04, 0x45, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 740x6e, 0x73, 0x6c, 0x70, 0x01, 0x01, 0x07, 0x02, 0xf4, 0x04,
750x00, 0x61, 0x73, 0x00, 0x60, 0x53, 0x70, 0x69, 0x72, 0x61, 750x45, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x00, 0x61, 0x73,
760x6c, 0x00, 0x8b, 0x02, 0x10, 0x27, 0x8c, 0x00, 0x04, 0x0d, 760x00, 0x60, 0x53, 0x70, 0x69, 0x72, 0x61, 0x6c, 0x00, 0xa0,
770x00, 0x01, 0x89, 0x00, 0x00, 0xcc, 0x01, 0x40, 0x72, 0x69, 770x02, 0x10, 0x27, 0x8c, 0x00, 0x04, 0x0d, 0x00, 0x01, 0x89,
780x62, 0x75, 0x3e, 0x00, 0x51, 0x74, 0x6f, 0x00, 0x74, 0x68, 780x00, 0x00, 0xcc, 0x01, 0x40, 0x72, 0x69, 0x62, 0x75, 0x3e,
790x5a, 0x00, 0x41, 0x6c, 0x6c, 0x65, 0x63, 0x66, 0x02, 0xe1, 790x00, 0x51, 0x74, 0x6f, 0x00, 0x74, 0x68, 0x5a, 0x00, 0x41,
800x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 800x6c, 0x6c, 0x65, 0x63, 0x66, 0x02, 0xe1, 0x62, 0x79, 0x00,
810x61, 0x72, 0x76, 0x65, 0xb7, 0x01, 0x00, 0xa8, 0x00, 0xd1, 810x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76,
820x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 820x65, 0xb7, 0x01, 0x00, 0xa8, 0x00, 0xe1, 0x00, 0x68, 0x74,
830x77, 0x2e, 0x6e, 0xbf, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 830x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
840x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0xe6, 0x00, 0xf6, 0x10, 840x6e, 0xc0, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70,
850x73, 0x2f, 0x61, 0x73, 0x74, 0x72, 0x6f, 0x6e, 0x6f, 0x6d, 850x2f, 0x65, 0x6e, 0x2f, 0xe7, 0x00, 0x31, 0x73, 0x2f, 0x74,
860x69, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x68, 0x6f, 0x77, 0x2e, 860xbb, 0x00, 0xd6, 0x5f, 0x73, 0x68, 0x6f, 0x77, 0x2f, 0x00,
870x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x32, 0x38, 0x2e, 870x00, 0x00, 0x32, 0x38, 0x2e, 0x31, 0x29, 0x03, 0x01, 0x78,
880x31, 0x1d, 0x03, 0x01, 0x81, 0x00, 0x21, 0x6f, 0x6c, 0x26, 880x00, 0xf0, 0x02, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00,
890x03, 0xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 890x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b,
900x63, 0x6b, 0x8d, 0x01, 0x32, 0x61, 0x6e, 0x79, 0x8d, 0x01, 900x84, 0x01, 0x32, 0x61, 0x6e, 0x79, 0x84, 0x01, 0x00, 0x96,
910x00, 0x9f, 0x01, 0x01, 0x5a, 0x01, 0x00, 0x15, 0x02, 0x21, 910x01, 0x01, 0x51, 0x01, 0x00, 0x0c, 0x02, 0x21, 0x61, 0x6e,
920x61, 0x6e, 0x03, 0x02, 0x21, 0x00, 0x69, 0xeb, 0x01, 0x10, 920xfa, 0x01, 0x21, 0x00, 0x69, 0xe2, 0x01, 0x10, 0x72, 0x02,
930x72, 0x0b, 0x01, 0x31, 0x6e, 0x27, 0x74, 0x83, 0x02, 0xb0, 930x01, 0x31, 0x6e, 0x27, 0x74, 0x7a, 0x02, 0xb0, 0x61, 0x6c,
940x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x2c, 0x00, 0x6f, 940x72, 0x65, 0x61, 0x64, 0x79, 0x2c, 0x00, 0x6f, 0x72, 0x2f,
950x72, 0x2f, 0x00, 0x61, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 950x00, 0x61, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x1a, 0x00,
960x1a, 0x00, 0x07, 0x2d, 0x00, 0x30, 0x2e, 0x00, 0x57, 0xfe, 960x07, 0x2d, 0x00, 0x30, 0x2e, 0x00, 0x57, 0xf5, 0x01, 0x00,
970x01, 0x00, 0x65, 0x02, 0x50, 0x63, 0x72, 0x65, 0x61, 0x74, 970x5c, 0x02, 0x50, 0x63, 0x72, 0x65, 0x61, 0x74, 0x8a, 0x03,
980x93, 0x03, 0x54, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x08, 0x03, 980x54, 0x76, 0x61, 0x6c, 0x69, 0x64, 0xff, 0x02, 0x10, 0x28,
990x10, 0x28, 0x31, 0x00, 0x05, 0xca, 0x02, 0x7f, 0x63, 0x6c, 990x31, 0x00, 0x05, 0xc1, 0x02, 0x7f, 0x63, 0x6c, 0x6f, 0x73,
1000x6f, 0x73, 0x65, 0x64, 0x2c, 0xf4, 0x02, 0x06, 0x1b, 0x2c, 1000x65, 0x64, 0x2c, 0xeb, 0x02, 0x06, 0x1b, 0x2c, 0x2f, 0x03,
1010x38, 0x03, 0x05, 0x2b, 0x03, 0x62, 0x00, 0x61, 0x62, 0x6f, 1010x05, 0x22, 0x03, 0x62, 0x00, 0x61, 0x62, 0x6f, 0x75, 0x74,
1020x75, 0x74, 0x35, 0x02, 0x01, 0x28, 0x00, 0x09, 0x3a, 0x03, 1020x2c, 0x02, 0x01, 0x28, 0x00, 0x09, 0x31, 0x03, 0x50, 0x6e,
1030x50, 0x6e, 0x6f, 0x00, 0x65, 0x78, 0xb9, 0x01, 0x43, 0x65, 1030x6f, 0x00, 0x65, 0x78, 0xb0, 0x01, 0x43, 0x65, 0x6f, 0x75,
1040x6f, 0x75, 0x73, 0xd4, 0x02, 0x71, 0x62, 0x65, 0x74, 0x77, 1040x73, 0xcb, 0x02, 0x71, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65,
1050x65, 0x65, 0x6e, 0x5a, 0x02, 0x21, 0x6f, 0x66, 0xec, 0x01, 1050x6e, 0x51, 0x02, 0x21, 0x6f, 0x66, 0xe3, 0x01, 0x34, 0x6f,
1060x34, 0x6f, 0x77, 0x6e, 0x65, 0x02, 0xf1, 0x07, 0x29, 0x2c, 1060x77, 0x6e, 0x5c, 0x02, 0xf1, 0x07, 0x29, 0x2c, 0x00, 0x69,
1070x00, 0x69, 0x74, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x62, 1070x74, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x62, 0x65, 0x00,
1080x65, 0x00, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 1080x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x5c, 0x03,
1090x65, 0x03, 0x80, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 1090x80, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0xa3,
1100x63, 0xac, 0x03, 0x6a, 0x3b, 0x00, 0x73, 0x6f, 0x00, 0x79, 1100x03, 0x6a, 0x3b, 0x00, 0x73, 0x6f, 0x00, 0x79, 0x2c, 0x04,
1110x35, 0x04, 0x01, 0x79, 0x04, 0x20, 0x74, 0x68, 0xd6, 0x00, 1110x01, 0x70, 0x04, 0x20, 0x74, 0x68, 0xd6, 0x00, 0x23, 0x6f,
1120x23, 0x6f, 0x6c, 0xe1, 0x02, 0x08, 0x41, 0x00, 0x22, 0x69, 1120x6c, 0xd8, 0x02, 0x08, 0x41, 0x00, 0x22, 0x69, 0x6e, 0xa7,
1130x6e, 0xa7, 0x00, 0x21, 0x77, 0x61, 0xe1, 0x01, 0x30, 0x44, 1130x00, 0x21, 0x77, 0x61, 0xd8, 0x01, 0x30, 0x44, 0x75, 0x72,
1140x75, 0x72, 0xcb, 0x02, 0x72, 0x73, 0x6f, 0x6c, 0x76, 0x69, 1140xc2, 0x02, 0x72, 0x73, 0x6f, 0x6c, 0x76, 0x69, 0x6e, 0x67,
1150x6e, 0x67, 0x90, 0x03, 0x10, 0x6d, 0x2b, 0x00, 0x52, 0x00, 1150x87, 0x03, 0x10, 0x6d, 0x2b, 0x00, 0x52, 0x00, 0x6b, 0x6e,
1160x6b, 0x6e, 0x6f, 0x77, 0x2b, 0x00, 0x12, 0x61, 0x8a, 0x04, 1160x6f, 0x77, 0x2b, 0x00, 0x12, 0x61, 0x81, 0x04, 0x16, 0x63,
1170x16, 0x63, 0xc6, 0x04, 0x02, 0xa7, 0x00, 0x03, 0x0b, 0x03, 1170xbd, 0x04, 0x02, 0xa7, 0x00, 0x03, 0x02, 0x03, 0x01, 0x77,
1180x01, 0x77, 0x00, 0xa2, 0x61, 0x00, 0x73, 0x70, 0x65, 0x63, 1180x00, 0xa2, 0x61, 0x00, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66,
1190x69, 0x66, 0x69, 0x63, 0x01, 0x01, 0x70, 0x62, 0x75, 0x74, 1190x69, 0x63, 0x01, 0x01, 0x70, 0x62, 0x75, 0x74, 0x00, 0x6e,
1200x00, 0x6e, 0x6f, 0x74, 0xbf, 0x00, 0x22, 0x73, 0x75, 0x0f, 1200x6f, 0x74, 0xbf, 0x00, 0x22, 0x73, 0x75, 0x06, 0x04, 0x11,
1210x04, 0x11, 0x77, 0x95, 0x01, 0x00, 0x9b, 0x00, 0x02, 0x03, 1210x77, 0x95, 0x01, 0x00, 0x9b, 0x00, 0x02, 0x03, 0x01, 0x21,
1220x01, 0x21, 0x67, 0x6f, 0x27, 0x01, 0x02, 0x7e, 0x01, 0x10, 1220x67, 0x6f, 0x27, 0x01, 0x02, 0x7e, 0x01, 0x10, 0x6f, 0xb3,
1230x6f, 0xb3, 0x01, 0x05, 0x64, 0x03, 0x00, 0x5d, 0x00, 0x06, 1230x01, 0x05, 0x5b, 0x03, 0x00, 0x5d, 0x00, 0x06, 0xd8, 0x04,
1240xe1, 0x04, 0x01, 0xab, 0x02, 0x01, 0x81, 0x01, 0x71, 0x2e, 1240x01, 0xa2, 0x02, 0x01, 0x81, 0x01, 0x71, 0x2e, 0x00, 0x49,
1250x00, 0x49, 0x6e, 0x00, 0x6f, 0x72, 0x36, 0x03, 0x05, 0x15, 1250x6e, 0x00, 0x6f, 0x72, 0x2d, 0x03, 0x05, 0x0c, 0x04, 0x23,
1260x04, 0x23, 0x69, 0x73, 0x07, 0x01, 0x30, 0x00, 0x64, 0x6f, 1260x69, 0x73, 0x07, 0x01, 0x30, 0x00, 0x64, 0x6f, 0x1e, 0x02,
1270x1e, 0x02, 0x72, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x2c, 1270x72, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x2c, 0xf6, 0x01,
1280xf6, 0x01, 0x40, 0x61, 0x6e, 0x00, 0x72, 0xcf, 0x00, 0x06, 1280x40, 0x61, 0x6e, 0x00, 0x72, 0xcf, 0x00, 0x06, 0x6a, 0x02,
1290x6a, 0x02, 0x03, 0x4b, 0x00, 0x01, 0x7c, 0x00, 0x53, 0x64, 1290x03, 0x4b, 0x00, 0x01, 0x7c, 0x00, 0x53, 0x64, 0x72, 0x61,
1300x72, 0x61, 0x67, 0x2c, 0x82, 0x00, 0x01, 0x6e, 0x01, 0x04, 1300x67, 0x2c, 0x82, 0x00, 0x01, 0x6e, 0x01, 0x04, 0x26, 0x02,
1310x26, 0x02, 0x72, 0x6e, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77, 1310x72, 0x6e, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x2b, 0x04,
1320x34, 0x04, 0x71, 0x72, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 1320x71, 0x72, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0xab, 0x05,
1330xb4, 0x05, 0x16, 0x74, 0x8b, 0x00, 0x42, 0x44, 0x72, 0x6f, 1330x16, 0x74, 0x8b, 0x00, 0x42, 0x44, 0x72, 0x6f, 0x70, 0x21,
1340x70, 0x21, 0x01, 0x44, 0x69, 0x6e, 0x00, 0x61, 0x14, 0x01, 1340x01, 0x44, 0x69, 0x6e, 0x00, 0x61, 0x14, 0x01, 0x22, 0x6f,
1350x22, 0x6f, 0x66, 0x92, 0x01, 0x61, 0x63, 0x68, 0x6f, 0x69, 1350x66, 0x92, 0x01, 0x61, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65,
1360x63, 0x65, 0x66, 0x00, 0x04, 0xcb, 0x01, 0x61, 0x72, 0x65, 1360x66, 0x00, 0x04, 0xcb, 0x01, 0x61, 0x72, 0x65, 0x6d, 0x69,
1370x6d, 0x69, 0x6e, 0x64, 0x98, 0x00, 0x02, 0x73, 0x00, 0x20, 1370x6e, 0x64, 0x98, 0x00, 0x02, 0x73, 0x00, 0x20, 0x64, 0x6f,
1380x64, 0x6f, 0x33, 0x05, 0x91, 0x27, 0x73, 0x00, 0x61, 0x73, 1380x2a, 0x05, 0x91, 0x27, 0x73, 0x00, 0x61, 0x73, 0x73, 0x6f,
1390x73, 0x6f, 0x63, 0x69, 0xd6, 0x03, 0x41, 0x77, 0x69, 0x74, 1390x63, 0x69, 0xcd, 0x03, 0x41, 0x77, 0x69, 0x74, 0x68, 0x01,
1400x68, 0x0a, 0x06, 0x01, 0xbc, 0x00, 0x4c, 0x61, 0x6c, 0x73, 1400x06, 0x01, 0xbc, 0x00, 0x4c, 0x61, 0x6c, 0x73, 0x6f, 0xc1,
1410x6f, 0xc1, 0x00, 0x42, 0x65, 0x78, 0x69, 0x73, 0x8d, 0x00, 1410x00, 0x42, 0x65, 0x78, 0x69, 0x73, 0x8d, 0x00, 0x00, 0xa3,
1420x00, 0xa3, 0x00, 0x02, 0x2a, 0x06, 0x21, 0x69, 0x63, 0x1f, 1420x00, 0x02, 0x21, 0x06, 0x21, 0x69, 0x63, 0x16, 0x05, 0x41,
1430x05, 0x41, 0x6d, 0x00, 0x75, 0x70, 0x73, 0x00, 0x01, 0x0d, 1430x6d, 0x00, 0x75, 0x70, 0x73, 0x00, 0x01, 0x0d, 0x03, 0x00,
1440x03, 0x00, 0x11, 0x00, 0x01, 0x20, 0x03, 0x20, 0x64, 0x65, 1440x11, 0x00, 0x01, 0x20, 0x03, 0x72, 0x64, 0x65, 0x73, 0x74,
1450x9f, 0x03, 0x12, 0x79, 0x22, 0x00, 0x81, 0x62, 0x79, 0x00, 1450x72, 0x6f, 0x79, 0x22, 0x00, 0x81, 0x62, 0x79, 0x00, 0x64,
1460x64, 0x72, 0x6f, 0x70, 0x70, 0xc2, 0x04, 0x00, 0x11, 0x00, 1460x72, 0x6f, 0x70, 0x70, 0xb9, 0x04, 0x00, 0x11, 0x00, 0x35,
1470x35, 0x6f, 0x66, 0x66, 0x96, 0x01, 0x04, 0x55, 0x05, 0x00, 1470x6f, 0x66, 0x66, 0x96, 0x01, 0x04, 0x4c, 0x05, 0x00, 0xe6,
1480xe6, 0x01, 0xa1, 0x2e, 0x00, 0x28, 0x41, 0x6c, 0x73, 0x6f, 1480x01, 0xa1, 0x2e, 0x00, 0x28, 0x41, 0x6c, 0x73, 0x6f, 0x2c,
1490x2c, 0x00, 0x69, 0xd3, 0x00, 0x31, 0x27, 0x72, 0x65, 0xd2, 1490x00, 0x69, 0xd3, 0x00, 0x31, 0x27, 0x72, 0x65, 0xd2, 0x01,
1500x01, 0x01, 0xcf, 0x01, 0x06, 0xc0, 0x00, 0x05, 0x2b, 0x01, 1500x01, 0xcf, 0x01, 0x06, 0xc0, 0x00, 0x05, 0x2b, 0x01, 0x28,
1510x28, 0x69, 0x73, 0x27, 0x01, 0x06, 0x7f, 0x01, 0x01, 0x9c, 1510x69, 0x73, 0x27, 0x01, 0x06, 0x7f, 0x01, 0x01, 0x9c, 0x00,
1520x00, 0x29, 0x69, 0x74, 0x9a, 0x00, 0x50, 0x69, 0x74, 0x00, 1520x29, 0x69, 0x74, 0x9a, 0x00, 0x50, 0x69, 0x74, 0x00, 0x61,
1530x61, 0x72, 0xca, 0x05, 0x02, 0xc7, 0x01, 0x11, 0x6b, 0x12, 1530x72, 0xc1, 0x05, 0x02, 0xc7, 0x01, 0x11, 0x6b, 0x12, 0x00,
1540x00, 0xa3, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x72, 0x2e, 1540xa3, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x72, 0x2e, 0x00,
1550x00, 0x49, 0x30, 0x01, 0x60, 0x73, 0x77, 0x69, 0x76, 0x65, 1550x49, 0x30, 0x01, 0x60, 0x73, 0x77, 0x69, 0x76, 0x65, 0x6c,
1560x6c, 0x09, 0x02, 0x40, 0x73, 0x74, 0x61, 0x6e, 0x88, 0x03, 1560x09, 0x02, 0x40, 0x73, 0x74, 0x61, 0x6e, 0x88, 0x03, 0x24,
1570x24, 0x61, 0x73, 0x31, 0x06, 0x50, 0x67, 0x00, 0x69, 0x74, 1570x61, 0x73, 0x28, 0x06, 0x50, 0x67, 0x00, 0x69, 0x74, 0x2c,
1580x2c, 0x3e, 0x00, 0x42, 0x73, 0x74, 0x61, 0x79, 0x79, 0x00, 1580x3e, 0x00, 0x42, 0x73, 0x74, 0x61, 0x79, 0x79, 0x00, 0x06,
1590x06, 0x83, 0x06, 0x21, 0x70, 0x61, 0x93, 0x05, 0x00, 0xa6, 1590x7a, 0x06, 0x21, 0x70, 0x61, 0x8a, 0x05, 0x00, 0xa6, 0x01,
1600x01, 0x3a, 0x29, 0x00, 0x00, 0x4d, 0x01, 0x22, 0x75, 0x73, 1600x3a, 0x29, 0x00, 0x00, 0x4d, 0x01, 0x22, 0x75, 0x73, 0x84,
1610x84, 0x02, 0xa2, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 1610x02, 0xa2, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b,
1620x6b, 0x65, 0x79, 0x61, 0x06, 0x00, 0x97, 0x00, 0x04, 0x94, 1620x65, 0x79, 0x58, 0x06, 0x00, 0x97, 0x00, 0x04, 0x94, 0x00,
1630x00, 0x04, 0x19, 0x06, 0x05, 0x8f, 0x02, 0x13, 0x6e, 0x37, 1630x04, 0x10, 0x06, 0x05, 0x8f, 0x02, 0x13, 0x6e, 0x2e, 0x06,
1640x06, 0x83, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x69, 1640x83, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x69, 0x9b,
1650xa4, 0x06, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 1650x06, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x00,
1660x00, 0x02, 0x66, 0x06, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x00, 1660x02, 0x5d, 0x06, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x00, 0x61,
1670x61, 0xc8, 0x04, 0x01, 0xc7, 0x00, 0x01, 0xca, 0x04, 0x22, 1670xc8, 0x04, 0x01, 0xc7, 0x00, 0x01, 0xca, 0x04, 0x22, 0x6f,
1680x6f, 0x72, 0xe0, 0x00, 0x01, 0xa0, 0x00, 0x00, 0x6a, 0x01, 1680x72, 0xe0, 0x00, 0x01, 0xa0, 0x00, 0x00, 0x6a, 0x01, 0x10,
1690x10, 0x2c, 0xcd, 0x00, 0x5f, 0x61, 0x62, 0x6f, 0x76, 0x65, 1690x2c, 0xcd, 0x00, 0x5f, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x55,
1700x55, 0x00, 0x13, 0x23, 0x64, 0x6f, 0x16, 0x01, 0x01, 0x50, 1700x00, 0x13, 0x23, 0x64, 0x6f, 0x16, 0x01, 0x01, 0x50, 0x01,
1710x01, 0x25, 0x75, 0x70, 0x79, 0x01, 0x01, 0x06, 0x01, 0x22, 1710x25, 0x75, 0x70, 0x79, 0x01, 0x01, 0x06, 0x01, 0x22, 0x62,
1720x62, 0x65, 0xd5, 0x01, 0x22, 0x65, 0x64, 0x4f, 0x06, 0x8c, 1720x65, 0xd5, 0x01, 0x22, 0x65, 0x64, 0x46, 0x06, 0x8c, 0x65,
1730x65, 0x78, 0x74, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x00, 1730x78, 0x74, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x00, 0x00,
1740x00, 0xa5, 0x01, 0x00, 0x6d, 0x00, 0x33, 0x65, 0x64, 0x3b, 1740xa5, 0x01, 0x00, 0x6d, 0x00, 0x33, 0x65, 0x64, 0x3b, 0xf6,
1750xff, 0x05, 0x04, 0x18, 0x01, 0x20, 0x62, 0x65, 0x1b, 0x01, 1750x05, 0x04, 0x18, 0x01, 0x20, 0x62, 0x65, 0x1b, 0x01, 0x15,
1760x15, 0x64, 0x0c, 0x01, 0x0c, 0x66, 0x02, 0x02, 0x1c, 0x01, 1760x64, 0x0c, 0x01, 0x0c, 0x66, 0x02, 0x02, 0x1c, 0x01, 0x12,
1770x12, 0x2c, 0x69, 0x05, 0x05, 0x3c, 0x02, 0x0e, 0x4d, 0x02, 1770x2c, 0x69, 0x05, 0x05, 0x3c, 0x02, 0x0e, 0x4d, 0x02, 0x13,
1780x13, 0x6e, 0xac, 0x00, 0x52, 0x6f, 0x72, 0x00, 0x61, 0x6e, 1780x6e, 0xac, 0x00, 0x52, 0x6f, 0x72, 0x00, 0x61, 0x6e, 0xd6,
1790xd6, 0x03, 0x01, 0x42, 0x00, 0x00, 0x6d, 0x04, 0x41, 0x28, 1790x03, 0x01, 0x42, 0x00, 0x00, 0x6d, 0x04, 0x41, 0x28, 0x41,
1800x41, 0x6c, 0x6c, 0x92, 0x00, 0x11, 0x61, 0x71, 0x06, 0x10, 1800x6c, 0x6c, 0x92, 0x00, 0x11, 0x61, 0x68, 0x06, 0x10, 0x73,
1810x73, 0x99, 0x02, 0x42, 0x63, 0x72, 0x69, 0x62, 0x97, 0x04, 1810x99, 0x02, 0x42, 0x63, 0x72, 0x69, 0x62, 0x97, 0x04, 0x13,
1820x13, 0x73, 0x87, 0x06, 0x31, 0x32, 0x2e, 0x31, 0x01, 0x04, 1820x73, 0x7e, 0x06, 0x31, 0x32, 0x2e, 0x31, 0x01, 0x04, 0x01,
1830x01, 0x97, 0x00, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 1830x97, 0x00, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
1840x62, 0x6c, 0x65, 0x2e, 0x29, 0x4e, 0x06, 0x16, 0x32, 0x4e, 1840x6c, 0x65, 0x2e, 0x29, 0x4e, 0x06, 0x16, 0x32, 0x4e, 0x06,
1850x06, 0x00, 0x9d, 0x07, 0x71, 0x6d, 0x65, 0x74, 0x65, 0x72, 1850x00, 0x94, 0x07, 0x71, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
1860x73, 0x20, 0x76, 0x07, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 1860x20, 0x6d, 0x07, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00,
1870x00, 0x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x51, 0x00, 0x66, 1870x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x51, 0x00, 0x66, 0x72,
1880x72, 0x6f, 0x6d, 0x7c, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 1880x6f, 0x6d, 0x7c, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74,
1890x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 1890x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x72,
1900x72, 0x00, 0x03, 0x16, 0x04, 0xb0, 0x60, 0x54, 0x79, 0x70, 1900x00, 0x03, 0x16, 0x04, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65,
1910x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xae, 0x00, 0x91, 1910x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xae, 0x00, 0x91, 0x57,
1920x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x0f, 1920x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x0f, 0x05,
1930x05, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x20, 0x03, 0x01, 1930x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x20, 0x03, 0x01, 0xdf,
1940xdf, 0x01, 0x15, 0x69, 0xac, 0x05, 0x01, 0x46, 0x05, 0x30, 1940x01, 0x15, 0x69, 0xac, 0x05, 0x01, 0x46, 0x05, 0x30, 0x69,
1950x69, 0x66, 0x66, 0x20, 0x05, 0x63, 0x74, 0x79, 0x00, 0x00, 1950x66, 0x66, 0x20, 0x05, 0x63, 0x74, 0x79, 0x00, 0x00, 0x00,
1960x00, 0x43, 0xe5, 0x06, 0x02, 0x2a, 0x04, 0x06, 0x1a, 0x00, 1960x43, 0xe5, 0x06, 0x02, 0x2a, 0x04, 0x06, 0x1a, 0x00, 0x04,
1970x04, 0x5b, 0x03, 0x41, 0x65, 0x6e, 0x65, 0x72, 0xeb, 0x03, 1970x5b, 0x03, 0x41, 0x65, 0x6e, 0x65, 0x72, 0xeb, 0x03, 0x02,
1980x02, 0x3d, 0x07, 0x57, 0x2e, 0x00, 0x4d, 0x6f, 0x72, 0x29, 1980x33, 0x07, 0x57, 0x2e, 0x00, 0x4d, 0x6f, 0x72, 0x29, 0x00,
1990x00, 0x13, 0x00, 0x54, 0x07, 0xa0, 0x00, 0x72, 0x65, 0x71, 1990x13, 0x00, 0x4a, 0x07, 0xa0, 0x00, 0x72, 0x65, 0x71, 0x75,
2000x75, 0x69, 0x72, 0x65, 0x00, 0x6d, 0x1f, 0x00, 0x02, 0xcf, 2000x69, 0x72, 0x65, 0x00, 0x6d, 0x1f, 0x00, 0x02, 0xc6, 0x08,
2010x08, 0x62, 0x78, 0x00, 0x64, 0x65, 0x64, 0x75, 0x3d, 0x01, 2010x62, 0x78, 0x00, 0x64, 0x65, 0x64, 0x75, 0x3d, 0x01, 0x02,
2020x02, 0x65, 0x06, 0x01, 0xb8, 0x00, 0x80, 0x55, 0x6e, 0x72, 2020x65, 0x06, 0x01, 0xb8, 0x00, 0x80, 0x55, 0x6e, 0x72, 0x65,
2030x65, 0x61, 0x73, 0x6f, 0x6e, 0xe9, 0x00, 0x18, 0x27, 0x73, 2030x61, 0x73, 0x6f, 0x6e, 0xe9, 0x00, 0x18, 0x27, 0x73, 0x00,
2040x00, 0x20, 0x6c, 0x65, 0x41, 0x03, 0x35, 0x6d, 0x61, 0x79, 2040x20, 0x6c, 0x65, 0x41, 0x03, 0x35, 0x6d, 0x61, 0x79, 0x4d,
2050x4d, 0x00, 0xe0, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 2050x00, 0xe0, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63,
2060x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x00, 2060x6b, 0x69, 0x6e, 0x67, 0x2e, 0x00,
207}; 207};
208 208
209const unsigned short help_text_len = 2754; 209const unsigned short help_text_len = 2766;
210const unsigned short help_text_words = 497; 210const unsigned short help_text_words = 498;
211const bool help_valid = true;
211const char quick_help_text[] = "Divide the grid into rotationally symmetric regions each centred on a dot."; 212const char quick_help_text[] = "Divide the grid into rotationally symmetric regions each centred on a dot.";
diff --git a/apps/plugins/puzzles/help/guess.c b/apps/plugins/puzzles/help/guess.c
index cdfc24e6b3..3c77344424 100644
--- a/apps/plugins/puzzles/help/guess.c
+++ b/apps/plugins/puzzles/help/guess.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,209 +6,237 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 123, TEXT_CENTER | C_RED }, 9 { 124, TEXT_CENTER | C_RED },
10 { 404, TEXT_CENTER | C_RED }, 10 { 444, TEXT_CENTER | C_RED },
11 { 432, TEXT_UNDERLINE }, 11 { 472, TEXT_UNDERLINE },
12 { 452, TEXT_UNDERLINE }, 12 { 492, TEXT_UNDERLINE },
13 { 465, TEXT_UNDERLINE }, 13 { 505, TEXT_UNDERLINE },
14 { 483, TEXT_UNDERLINE }, 14 { 523, TEXT_UNDERLINE },
15 { 543, TEXT_UNDERLINE }, 15 { 583, TEXT_UNDERLINE },
16 { 614, TEXT_CENTER | C_RED },
16 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
17}; 18};
18 19
19/* orig 3042 comp 1883 ratio 0.619001 level 10 saved 1159 */ 20/* orig 3506 comp 2143 ratio 0.611238 level 10 saved 1363 */
20const char help_text[] = { 21const char help_text[] = {
210xf2, 0x2a, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
220x31, 0x35, 0x3a, 0x20, 0x47, 0x75, 0x65, 0x73, 0x73, 0x20, 230x31, 0x35, 0x3a, 0x20, 0x47, 0x75, 0x65, 0x73, 0x73, 0x20,
230x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 240x00, 0x2d, 0x01, 0x00, 0xf2, 0x18, 0x00, 0x00, 0x00, 0x59,
240x65, 0x00, 0x61, 0x00, 0x73, 0x65, 0x74, 0x00, 0x6f, 0x66, 250x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
250x00, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x65, 0x64, 0x00, 260x73, 0x65, 0x74, 0x00, 0x6f, 0x66, 0x00, 0x63, 0x6f, 0x6c,
260x70, 0x65, 0x67, 0x73, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x21, 270x6f, 0x75, 0x72, 0x65, 0x64, 0x00, 0x70, 0x65, 0x67, 0x73,
270x00, 0xb0, 0x74, 0x6f, 0x00, 0x72, 0x65, 0x70, 0x72, 0x6f, 280x2c, 0x00, 0x61, 0x6e, 0x64, 0x21, 0x00, 0xb0, 0x74, 0x6f,
280x64, 0x75, 0x63, 0x2e, 0x00, 0xf0, 0x07, 0x70, 0x72, 0x65, 290x00, 0x72, 0x65, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x2e,
290x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 300x00, 0xf0, 0x07, 0x70, 0x72, 0x65, 0x64, 0x65, 0x74, 0x65,
300x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x41, 310x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x00, 0x73, 0x65, 0x71,
310x00, 0xf0, 0x00, 0x74, 0x68, 0x65, 0x6d, 0x00, 0x28, 0x63, 320x75, 0x65, 0x6e, 0x63, 0x65, 0x41, 0x00, 0xf0, 0x00, 0x74,
320x68, 0x6f, 0x73, 0x65, 0x6e, 0x00, 0x62, 0x79, 0x10, 0x00, 330x68, 0x65, 0x6d, 0x00, 0x28, 0x63, 0x68, 0x6f, 0x73, 0x65,
330xf0, 0x13, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 340x6e, 0x00, 0x62, 0x79, 0x10, 0x00, 0xf0, 0x13, 0x00, 0x63,
340x72, 0x29, 0x00, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x00, 350x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x29, 0x00, 0x77,
350x61, 0x00, 0x63, 0x65, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x00, 360x69, 0x74, 0x68, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x63, 0x65,
360x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x39, 0x00, 0x10, 0x67, 370x72, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62,
370x95, 0x00, 0xa2, 0x65, 0x73, 0x2e, 0x00, 0x00, 0x00, 0x45, 380x65, 0x72, 0x39, 0x00, 0x10, 0x67, 0xa7, 0x00, 0xa2, 0x65,
380x61, 0x63, 0x68, 0x10, 0x00, 0xc1, 0x00, 0x67, 0x65, 0x74, 390x73, 0x2e, 0x00, 0x00, 0x00, 0x45, 0x61, 0x63, 0x68, 0x10,
390x73, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x64, 0x3d, 0x00, 400x00, 0xc1, 0x00, 0x67, 0x65, 0x74, 0x73, 0x00, 0x6d, 0x61,
400x01, 0x50, 0x00, 0x06, 0x35, 0x00, 0xa9, 0x63, 0x6f, 0x72, 410x72, 0x6b, 0x65, 0x64, 0x3d, 0x00, 0x01, 0x50, 0x00, 0x06,
410x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x2d, 0xb9, 0x00, 0x33, 420x35, 0x00, 0xa9, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74,
420x00, 0x69, 0x6e, 0x79, 0x00, 0x01, 0x1f, 0x00, 0xf2, 0x03, 430x6c, 0x79, 0x2d, 0xb9, 0x00, 0x33, 0x00, 0x69, 0x6e, 0x79,
430x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x00, 0x28, 0x69, 440x00, 0x01, 0x1f, 0x00, 0xf2, 0x03, 0x00, 0x70, 0x6c, 0x61,
440x6e, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x29, 0xda, 0x00, 450x63, 0x65, 0x73, 0x00, 0x28, 0x69, 0x6e, 0x00, 0x62, 0x6c,
450x4f, 0x61, 0x6c, 0x73, 0x6f, 0x51, 0x00, 0x06, 0x1f, 0x00, 460x61, 0x63, 0x6b, 0x29, 0xda, 0x00, 0x4f, 0x61, 0x6c, 0x73,
460x52, 0x00, 0x02, 0x58, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x50, 470x6f, 0x51, 0x00, 0x06, 0x1f, 0x00, 0x52, 0x00, 0x02, 0x58,
470x00, 0x60, 0x77, 0x68, 0x69, 0x74, 0x65, 0x29, 0xb6, 0x00, 480x77, 0x72, 0x6f, 0x6e, 0x67, 0x50, 0x00, 0x60, 0x77, 0x68,
480xc2, 0x54, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 490x69, 0x74, 0x65, 0x29, 0xb6, 0x00, 0xc2, 0x54, 0x68, 0x69,
490x00, 0x69, 0x73, 0x5b, 0x00, 0x70, 0x6b, 0x6e, 0x6f, 0x77, 500x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x69, 0x73, 0x5b,
500x6e, 0x00, 0x28, 0x6b, 0x00, 0x01, 0xc3, 0x00, 0x40, 0x74, 510x00, 0x70, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x00, 0x28, 0x6b,
510x65, 0x64, 0x2c, 0x14, 0x01, 0xf2, 0x0b, 0x48, 0x61, 0x73, 520x00, 0x01, 0xc3, 0x00, 0x40, 0x74, 0x65, 0x64, 0x2c, 0x14,
520x62, 0x72, 0x6f, 0x2c, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x6c, 530x01, 0xf2, 0x0b, 0x48, 0x61, 0x73, 0x62, 0x72, 0x6f, 0x2c,
530x79, 0x29, 0x00, 0x61, 0x73, 0x00, 0x61, 0x00, 0x62, 0x6f, 540x00, 0x6d, 0x61, 0x69, 0x6e, 0x6c, 0x79, 0x29, 0x00, 0x61,
540x61, 0x72, 0x64, 0x40, 0x00, 0x42, 0x60, 0x4d, 0x61, 0x73, 550x73, 0x00, 0x61, 0x00, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x40,
550x5d, 0x01, 0x32, 0x64, 0x27, 0x2c, 0xf7, 0x00, 0x13, 0x36, 560x00, 0x42, 0x60, 0x4d, 0x61, 0x73, 0x5d, 0x01, 0x32, 0x64,
560x8f, 0x00, 0x42, 0x73, 0x2c, 0x00, 0x34, 0x91, 0x00, 0x72, 570x27, 0x2c, 0xf7, 0x00, 0x13, 0x36, 0x8f, 0x00, 0x42, 0x73,
570x70, 0x65, 0x72, 0x00, 0x72, 0x6f, 0x77, 0xca, 0x00, 0x26, 580x2c, 0x00, 0x34, 0x91, 0x00, 0x72, 0x70, 0x65, 0x72, 0x00,
580x31, 0x30, 0x40, 0x01, 0xa0, 0x48, 0x6f, 0x77, 0x65, 0x76, 590x72, 0x6f, 0x77, 0xca, 0x00, 0x26, 0x31, 0x30, 0x40, 0x01,
590x65, 0x72, 0x2c, 0x00, 0x74, 0x91, 0x00, 0xf0, 0x06, 0x76, 600xa0, 0x48, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, 0x2c, 0x00,
600x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x61, 0x6c, 0x6c, 610x74, 0x91, 0x00, 0xf0, 0x06, 0x76, 0x65, 0x72, 0x73, 0x69,
610x6f, 0x77, 0x73, 0x00, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 620x6f, 0x6e, 0x00, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x00,
620xee, 0x01, 0x50, 0x74, 0x69, 0x6e, 0x67, 0x73, 0xf3, 0x00, 630x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0xee, 0x01, 0x50, 0x74,
630x08, 0xfd, 0x00, 0x01, 0x63, 0x00, 0x40, 0x00, 0x28, 0x75, 640x69, 0x6e, 0x67, 0x73, 0xf3, 0x00, 0x08, 0xfd, 0x00, 0x01,
640x70, 0xf1, 0x01, 0x47, 0x31, 0x30, 0x29, 0x2c, 0x1e, 0x00, 650x63, 0x00, 0x40, 0x00, 0x28, 0x75, 0x70, 0xf1, 0x01, 0x47,
650x0e, 0x76, 0x00, 0x0f, 0xbd, 0x01, 0x02, 0x11, 0x47, 0xb8, 660x31, 0x30, 0x29, 0x2c, 0x1e, 0x00, 0x0e, 0x76, 0x00, 0x0f,
660x01, 0xf0, 0x00, 0x77, 0x61, 0x73, 0x00, 0x63, 0x6f, 0x6e, 670xbd, 0x01, 0x02, 0x11, 0x47, 0xb8, 0x01, 0xf0, 0x00, 0x77,
670x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x4f, 0x00, 680x61, 0x73, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62,
680x01, 0x8f, 0x00, 0x91, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 690x75, 0x74, 0x65, 0x64, 0x4f, 0x00, 0x01, 0x8f, 0x00, 0x91,
690x74, 0x69, 0x6f, 0x1d, 0x02, 0xc0, 0x4a, 0x61, 0x6d, 0x65, 700x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x1d,
700x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x3c, 0x00, 710x02, 0xc0, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61,
710x43, 0x31, 0x35, 0x2e, 0x31, 0x9e, 0x02, 0x01, 0x3d, 0x00, 720x72, 0x76, 0x65, 0x79, 0x3c, 0x00, 0x43, 0x31, 0x35, 0x2e,
720x49, 0x6f, 0x6c, 0x73, 0x20, 0x5a, 0x01, 0x60, 0x63, 0x61, 730x31, 0xb0, 0x02, 0x01, 0x3d, 0x00, 0x49, 0x6f, 0x6c, 0x73,
730x6e, 0x00, 0x62, 0x65, 0x80, 0x01, 0x14, 0x79, 0x11, 0x02, 740x20, 0x5a, 0x01, 0x60, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x65,
740x61, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x9d, 0x01, 0x32, 750x80, 0x01, 0x14, 0x79, 0x11, 0x02, 0x61, 0x65, 0x69, 0x74,
750x6b, 0x65, 0x79, 0x46, 0x01, 0x12, 0x6f, 0x10, 0x00, 0x50, 760x68, 0x65, 0x72, 0x9d, 0x01, 0x32, 0x6b, 0x65, 0x79, 0x46,
760x6d, 0x6f, 0x75, 0x73, 0x65, 0x58, 0x00, 0x14, 0x57, 0x3a, 770x01, 0x12, 0x6f, 0x10, 0x00, 0x50, 0x6d, 0x6f, 0x75, 0x73,
770x02, 0x01, 0x12, 0x00, 0x89, 0x2c, 0x00, 0x64, 0x72, 0x61, 780x65, 0x58, 0x00, 0x14, 0x57, 0x3a, 0x02, 0x01, 0x12, 0x00,
780x67, 0x00, 0x61, 0xe2, 0x01, 0x51, 0x00, 0x66, 0x72, 0x6f, 790x89, 0x2c, 0x00, 0x64, 0x72, 0x61, 0x67, 0x00, 0x61, 0xe2,
790x6d, 0x24, 0x00, 0x62, 0x74, 0x72, 0x61, 0x79, 0x00, 0x6f, 800x01, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x24, 0x00, 0x62,
800xef, 0x01, 0x60, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x68, 0xec, 810x74, 0x72, 0x61, 0x79, 0x00, 0x6f, 0xef, 0x01, 0x60, 0x6c,
810x00, 0x31, 0x73, 0x69, 0x64, 0x06, 0x03, 0x91, 0x69, 0x74, 820x65, 0x66, 0x74, 0x2d, 0x68, 0xec, 0x00, 0x31, 0x73, 0x69,
820x73, 0x00, 0x72, 0x65, 0x71, 0x75, 0x69, 0x38, 0x00, 0x31, 830x64, 0x06, 0x03, 0x91, 0x69, 0x74, 0x73, 0x00, 0x72, 0x65,
830x6f, 0x73, 0x69, 0xcc, 0x00, 0x04, 0x70, 0x02, 0x62, 0x75, 840x71, 0x75, 0x69, 0x38, 0x00, 0x31, 0x6f, 0x73, 0x69, 0xcc,
840x72, 0x72, 0x65, 0x6e, 0x74, 0x0f, 0x01, 0x12, 0x3b, 0x32, 850x00, 0x04, 0x70, 0x02, 0x62, 0x75, 0x72, 0x72, 0x65, 0x6e,
850x01, 0x32, 0x6d, 0x61, 0x79, 0x0e, 0x02, 0x21, 0x62, 0x65, 860x74, 0x0f, 0x01, 0x12, 0x3b, 0x32, 0x01, 0x32, 0x6d, 0x61,
860x78, 0x00, 0x32, 0x67, 0x65, 0x64, 0x6c, 0x00, 0x04, 0x2d, 870x79, 0x0e, 0x02, 0x21, 0x62, 0x65, 0x78, 0x00, 0x32, 0x67,
870x00, 0x00, 0x5e, 0x00, 0x44, 0x70, 0x61, 0x73, 0x74, 0x45, 880x65, 0x64, 0x6c, 0x00, 0x04, 0x2d, 0x00, 0x00, 0x5e, 0x00,
880x01, 0x00, 0x66, 0x00, 0x42, 0x63, 0x6f, 0x70, 0x79, 0x4b, 890x44, 0x70, 0x61, 0x73, 0x74, 0x45, 0x01, 0x00, 0x66, 0x00,
890x03, 0xc0, 0x65, 0x6c, 0x73, 0x65, 0x77, 0x68, 0x65, 0x72, 900x42, 0x63, 0x6f, 0x70, 0x79, 0x4b, 0x03, 0xc0, 0x65, 0x6c,
900x65, 0x2e, 0x00, 0x54, 0x84, 0x03, 0x31, 0x6d, 0x6f, 0x76, 910x73, 0x65, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x00, 0x54,
910x81, 0x03, 0x23, 0x65, 0x67, 0xc7, 0x00, 0x10, 0x69, 0xb8, 920x84, 0x03, 0x31, 0x6d, 0x6f, 0x76, 0x81, 0x03, 0x23, 0x65,
920x03, 0x11, 0x66, 0x98, 0x00, 0x04, 0x52, 0x00, 0x05, 0x97, 930x67, 0xc7, 0x00, 0x10, 0x69, 0xb8, 0x03, 0x11, 0x66, 0x98,
930x00, 0x62, 0x74, 0x6f, 0x00, 0x73, 0x6f, 0x6d, 0x40, 0x00, 940x00, 0x04, 0x52, 0x00, 0x05, 0x97, 0x00, 0x62, 0x74, 0x6f,
940x80, 0x00, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x10, 950x00, 0x73, 0x6f, 0x6d, 0x40, 0x00, 0x80, 0x00, 0x69, 0x6e,
950x01, 0xef, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x63, 0x6c, 960x76, 0x61, 0x6c, 0x69, 0x64, 0x10, 0x01, 0xef, 0x52, 0x69,
960x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0xbe, 0x00, 0x02, 0x40, 970x67, 0x68, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x69,
970x00, 0x61, 0x64, 0x64, 0x97, 0x02, 0x62, 0x60, 0x68, 0x6f, 980x6e, 0x67, 0xbe, 0x00, 0x02, 0x40, 0x00, 0x61, 0x64, 0x64,
980x6c, 0x64, 0x27, 0xc0, 0x02, 0x13, 0x72, 0xd3, 0x00, 0x42, 990x97, 0x02, 0x62, 0x60, 0x68, 0x6f, 0x6c, 0x64, 0x27, 0xc0,
990x74, 0x68, 0x61, 0x74, 0x17, 0x04, 0x00, 0x1d, 0x00, 0x03, 1000x02, 0x13, 0x72, 0xd3, 0x00, 0x42, 0x74, 0x68, 0x61, 0x74,
1000x1c, 0x00, 0x60, 0x73, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0xe6, 1010x17, 0x04, 0x00, 0x1d, 0x00, 0x03, 0x1c, 0x00, 0x60, 0x73,
1010x00, 0xd0, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 1020x00, 0x77, 0x69, 0x6c, 0x6c, 0xe6, 0x00, 0xd0, 0x61, 0x75,
1020x63, 0x61, 0x6c, 0x6c, 0x79, 0x48, 0x00, 0x04, 0x00, 0x02, 1030x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c,
1030x55, 0x65, 0x00, 0x6e, 0x65, 0x78, 0x60, 0x00, 0x41, 0x66, 1040x79, 0x48, 0x00, 0x04, 0x00, 0x02, 0x55, 0x65, 0x00, 0x6e,
1040x74, 0x65, 0x72, 0x3c, 0x00, 0x30, 0x69, 0x6e, 0x67, 0x95, 1050x65, 0x78, 0x60, 0x00, 0x41, 0x66, 0x74, 0x65, 0x72, 0x3c,
1050x00, 0xd3, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 1060x00, 0x30, 0x69, 0x6e, 0x67, 0x95, 0x00, 0xd3, 0x41, 0x6c,
1060x69, 0x76, 0x65, 0x6c, 0x79, 0xf7, 0x02, 0x08, 0xd6, 0x01, 1070x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x6c,
1070x11, 0x2c, 0x0e, 0x00, 0x21, 0x75, 0x70, 0x31, 0x01, 0x10, 1080x79, 0xf7, 0x02, 0x08, 0xd6, 0x01, 0x11, 0x2c, 0x0e, 0x00,
1080x64, 0x5a, 0x03, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 1090x21, 0x75, 0x70, 0x31, 0x01, 0x10, 0x64, 0x5a, 0x03, 0x60,
1090x21, 0x00, 0x14, 0x73, 0x1a, 0x02, 0x22, 0x75, 0x73, 0x6d, 1100x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x21, 0x00, 0x14, 0x73,
1100x00, 0x20, 0x73, 0x65, 0x67, 0x02, 0x02, 0x29, 0x01, 0x03, 1110x1a, 0x02, 0x22, 0x75, 0x73, 0x6d, 0x00, 0x20, 0x73, 0x65,
1110xe8, 0x01, 0x15, 0x2c, 0xd2, 0x01, 0x01, 0x42, 0x00, 0x10, 1120x67, 0x02, 0x02, 0x29, 0x01, 0x03, 0xe8, 0x01, 0x15, 0x2c,
1120x72, 0x04, 0x01, 0x02, 0x3c, 0x00, 0x0c, 0x30, 0x00, 0x04, 1130xd2, 0x01, 0x01, 0x42, 0x00, 0x10, 0x72, 0x04, 0x01, 0x02,
1130x40, 0x01, 0x02, 0xe6, 0x02, 0x00, 0x36, 0x00, 0x90, 0x73, 1140x3c, 0x00, 0x0c, 0x30, 0x00, 0x04, 0x40, 0x01, 0x02, 0xe6,
1140x70, 0x61, 0x63, 0x65, 0x00, 0x62, 0x61, 0x72, 0x5b, 0x02, 1150x02, 0x00, 0x36, 0x00, 0x41, 0x45, 0x6e, 0x74, 0x65, 0x69,
1150x41, 0x45, 0x6e, 0x74, 0x65, 0x76, 0x00, 0x00, 0x39, 0x00, 1160x00, 0x00, 0x2c, 0x00, 0x01, 0xfe, 0x03, 0x03, 0x2b, 0x00,
1160x01, 0x0b, 0x04, 0x03, 0x38, 0x00, 0x02, 0xff, 0x04, 0x03, 1170x02, 0xf2, 0x04, 0x03, 0x3f, 0x00, 0x23, 0x65, 0x64, 0x6b,
1170x4c, 0x00, 0x23, 0x65, 0x64, 0x78, 0x00, 0x05, 0x5f, 0x01, 1180x00, 0x05, 0x52, 0x01, 0x02, 0x07, 0x05, 0x04, 0x50, 0x00,
1180x02, 0x14, 0x05, 0x04, 0x5d, 0x00, 0x50, 0x2e, 0x00, 0x60, 1190x50, 0x2e, 0x00, 0x60, 0x44, 0x27, 0x9d, 0x02, 0x60, 0x42,
1190x44, 0x27, 0x4f, 0x00, 0x42, 0x42, 0x61, 0x63, 0x6b, 0x60, 1200x61, 0x63, 0x6b, 0x73, 0x70, 0x46, 0x00, 0x02, 0xd1, 0x01,
1200x00, 0x02, 0xde, 0x01, 0x14, 0x73, 0xdf, 0x01, 0x00, 0x7d, 1210x14, 0x73, 0xd2, 0x01, 0x00, 0x70, 0x00, 0x11, 0x53, 0x19,
1210x00, 0x11, 0x53, 0x19, 0x00, 0x03, 0x8c, 0x01, 0x07, 0x6e, 1220x00, 0x03, 0x7f, 0x01, 0x07, 0x61, 0x01, 0x02, 0x64, 0x04,
1220x01, 0x00, 0x31, 0x01, 0x50, 0x50, 0x72, 0x65, 0x73, 0x73, 1230x05, 0xad, 0x04, 0x05, 0xf6, 0x00, 0x04, 0x5d, 0x02, 0x04,
1230xc0, 0x01, 0x21, 0x60, 0x68, 0x49, 0x00, 0x32, 0x60, 0x3f, 1240xfb, 0x00, 0x61, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0xaa,
1240x27, 0x84, 0x01, 0x10, 0x66, 0x05, 0x00, 0x0e, 0xd2, 0x01, 1250x01, 0x53, 0x3a, 0x00, 0x60, 0x31, 0x27, 0x11, 0x00, 0x12,
1250x01, 0x52, 0x01, 0x80, 0x61, 0x00, 0x73, 0x75, 0x67, 0x67, 1260x73, 0xdd, 0x02, 0x75, 0x6f, 0x70, 0x2d, 0x6d, 0x6f, 0x73,
1260x65, 0x73, 0xa0, 0x00, 0x01, 0x17, 0x00, 0x31, 0x2e, 0x00, 1270x74, 0x14, 0x01, 0x33, 0x60, 0x32, 0x27, 0xc2, 0x00, 0x82,
1270x55, 0x45, 0x00, 0x01, 0x9f, 0x03, 0xf0, 0x01, 0x69, 0x73, 1280x63, 0x6f, 0x6e, 0x64, 0x00, 0x6f, 0x6e, 0x65, 0x86, 0x00,
1290x90, 0x73, 0x6f, 0x00, 0x66, 0x6f, 0x72, 0x74, 0x68, 0x2e,
1300x74, 0x00, 0x22, 0x73, 0x65, 0x66, 0x00, 0x01, 0x7d, 0x02,
1310x00, 0x2e, 0x00, 0x01, 0x56, 0x01, 0x02, 0x7e, 0x01, 0x03,
1320xda, 0x01, 0x01, 0x4f, 0x01, 0x70, 0x2e, 0x00, 0x50, 0x72,
1330x65, 0x73, 0x73, 0x54, 0x02, 0x32, 0x60, 0x4c, 0x27, 0x11,
1340x02, 0x54, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x35, 0x00, 0x15,
1350x73, 0xd7, 0x01, 0x23, 0x69, 0x72, 0xc9, 0x00, 0x01, 0x35,
1360x04, 0x06, 0x37, 0x00, 0x11, 0x68, 0x21, 0x01, 0x23, 0x60,
1370x3f, 0x3e, 0x00, 0x10, 0x66, 0x05, 0x00, 0x0e, 0x9d, 0x02,
1380x01, 0x46, 0x00, 0x80, 0x61, 0x00, 0x73, 0x75, 0x67, 0x67,
1390x65, 0x73, 0x78, 0x01, 0x01, 0x17, 0x00, 0x31, 0x2e, 0x00,
1400x55, 0x45, 0x00, 0x01, 0x6a, 0x04, 0xf0, 0x01, 0x69, 0x73,
1280x00, 0x6e, 0x6f, 0x74, 0x00, 0x72, 0x65, 0x63, 0x6f, 0x6d, 1410x00, 0x6e, 0x6f, 0x74, 0x00, 0x72, 0x65, 0x63, 0x6f, 0x6d,
1290x6d, 0x65, 0x6e, 0x64, 0xac, 0x02, 0x20, 0x6f, 0x72, 0x5f, 1420x6d, 0x65, 0x6e, 0x64, 0x77, 0x03, 0x20, 0x6f, 0x72, 0x2a,
1300x04, 0x72, 0x6f, 0x72, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x01, 1430x05, 0x63, 0x6f, 0x72, 0x00, 0x6d, 0x6f, 0x72, 0x90, 0x00,
1310x02, 0x20, 0x61, 0x73, 0x77, 0x02, 0x71, 0x69, 0x73, 0x00, 1440x20, 0x61, 0x73, 0x42, 0x03, 0x71, 0x69, 0x73, 0x00, 0x73,
1320x73, 0x6c, 0x6f, 0x77, 0x61, 0x03, 0x22, 0x68, 0x65, 0xe5, 1450x6c, 0x6f, 0x77, 0x2c, 0x04, 0x22, 0x68, 0x65, 0xbd, 0x01,
1330x00, 0x02, 0x6a, 0x00, 0x21, 0x69, 0x73, 0xf4, 0x05, 0x52, 1460x02, 0x6a, 0x00, 0x21, 0x69, 0x73, 0xbf, 0x06, 0x52, 0x6c,
1340x6c, 0x65, 0x74, 0x65, 0x2c, 0x13, 0x01, 0xf2, 0x00, 0x6d, 1470x65, 0x74, 0x65, 0x2c, 0x29, 0x01, 0xf4, 0x00, 0x6d, 0x61,
1350x61, 0x6c, 0x6c, 0x65, 0x72, 0x00, 0x66, 0x65, 0x65, 0x64, 1480x6c, 0x6c, 0x65, 0x72, 0x00, 0x66, 0x65, 0x65, 0x64, 0x62,
1360x62, 0x61, 0x63, 0x6b, 0x47, 0x00, 0x04, 0x31, 0x02, 0x50, 1490x61, 0x63, 0x6b, 0xd7, 0x00, 0x02, 0xfc, 0x02, 0x50, 0x68,
1370x68, 0x69, 0x67, 0x68, 0x6c, 0x8f, 0x01, 0x45, 0x65, 0x64, 1500x69, 0x67, 0x68, 0x6c, 0x0b, 0x01, 0x45, 0x65, 0x64, 0x3b,
1380x3b, 0x00, 0x96, 0x02, 0x02, 0x83, 0x03, 0x41, 0x73, 0x65, 1510x00, 0x61, 0x03, 0x02, 0x4e, 0x04, 0x41, 0x73, 0x65, 0x00,
1390x00, 0x28, 0x7c, 0x00, 0x12, 0x76, 0xa5, 0x00, 0x01, 0x82, 1520x28, 0x7c, 0x00, 0x12, 0x76, 0xa5, 0x00, 0x0f, 0x47, 0x01,
1400x00, 0x04, 0xfa, 0x01, 0x02, 0x56, 0x02, 0x16, 0x6d, 0x2c, 1530x00, 0x16, 0x6d, 0xf7, 0x02, 0x52, 0x61, 0x72, 0x72, 0x6f,
1410x02, 0x52, 0x61, 0x72, 0x72, 0x6f, 0x77, 0xd5, 0x01, 0x01, 1540x77, 0xe6, 0x01, 0x01, 0x22, 0x04, 0x04, 0x22, 0x01, 0x01,
1420x57, 0x03, 0x04, 0x22, 0x01, 0x0f, 0xc4, 0x01, 0x07, 0x12, 1550x84, 0x00, 0x01, 0x23, 0x02, 0x30, 0x62, 0x61, 0x72, 0xcc,
1430x29, 0x86, 0x00, 0x00, 0x56, 0x01, 0x0e, 0x33, 0x01, 0x12, 1560x00, 0x05, 0x9c, 0x02, 0x12, 0x29, 0x86, 0x00, 0x00, 0x2e,
1440x2c, 0x89, 0x03, 0x73, 0x61, 0x6e, 0x79, 0x00, 0x68, 0x65, 1570x02, 0x0e, 0x33, 0x01, 0x12, 0x2c, 0x54, 0x04, 0x73, 0x61,
1450x6c, 0x0a, 0x06, 0x0d, 0xcb, 0x02, 0x02, 0xaa, 0x01, 0x01, 1580x6e, 0x79, 0x00, 0x68, 0x65, 0x6c, 0xd5, 0x06, 0x0d, 0x96,
1460x99, 0x03, 0x00, 0x19, 0x00, 0x19, 0x60, 0x43, 0x00, 0x17, 1590x03, 0x02, 0xfc, 0x01, 0x05, 0xe7, 0x01, 0x19, 0x60, 0x43,
1470x27, 0xb1, 0x01, 0x66, 0x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 1600x00, 0x17, 0x27, 0x89, 0x02, 0x66, 0x49, 0x66, 0x00, 0x79,
1480x64, 0x06, 0x06, 0xa9, 0x03, 0x13, 0x61, 0xb1, 0x01, 0x03, 1610x6f, 0x75, 0x2f, 0x07, 0x06, 0x74, 0x04, 0x27, 0x61, 0x6c,
1490x5f, 0x03, 0x61, 0x65, 0x00, 0x73, 0x6f, 0x6c, 0x75, 0x1a, 1620xee, 0x01, 0x01, 0xad, 0x00, 0x31, 0x6f, 0x6c, 0x75, 0x1a,
1500x00, 0x04, 0x24, 0x01, 0x33, 0x64, 0x69, 0x73, 0xf6, 0x04, 1630x00, 0x04, 0x24, 0x01, 0x33, 0x64, 0x69, 0x73, 0xc1, 0x05,
1510x82, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x3b, 0x00, 0x69, 0x4d, 1640x82, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x3b, 0x00, 0x69, 0x4d,
1520x00, 0x77, 0x72, 0x75, 0x6e, 0x00, 0x6f, 0x75, 0x74, 0x85, 1650x00, 0x77, 0x72, 0x75, 0x6e, 0x00, 0x6f, 0x75, 0x74, 0x50,
1530x05, 0x01, 0x30, 0x01, 0x03, 0xd0, 0x02, 0xbe, 0x60, 0x53, 1660x06, 0x01, 0x30, 0x01, 0x03, 0x9b, 0x03, 0xbe, 0x60, 0x53,
1540x6f, 0x6c, 0x76, 0x65, 0x2e, 0x2e, 0x2e, 0x27, 0x29, 0x57, 1670x6f, 0x6c, 0x76, 0x65, 0x2e, 0x2e, 0x2e, 0x27, 0x29, 0x57,
1550x00, 0x05, 0x97, 0x04, 0x71, 0x72, 0x65, 0x76, 0x65, 0x61, 1680x00, 0x05, 0x05, 0x03, 0x71, 0x72, 0x65, 0x76, 0x65, 0x61,
1560x6c, 0x65, 0x1b, 0x04, 0x23, 0x28, 0x41, 0x8b, 0x00, 0x11, 1690x6c, 0x65, 0xe6, 0x04, 0x23, 0x28, 0x41, 0x8b, 0x00, 0x11,
1570x61, 0xa6, 0x05, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 1700x61, 0x71, 0x06, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63,
1580x72, 0x69, 0x62, 0x65, 0x64, 0xc8, 0x02, 0x13, 0x73, 0xbc, 1710x72, 0x69, 0x62, 0x65, 0x64, 0xa0, 0x03, 0x13, 0x73, 0x87,
1590x05, 0x72, 0x32, 0x2e, 0x31, 0x00, 0x61, 0x72, 0x65, 0x42, 1720x06, 0x63, 0x32, 0x2e, 0x31, 0x00, 0x61, 0x72, 0xe1, 0x02,
1600x00, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 1730xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65,
1610x65, 0x2e, 0x29, 0xc4, 0x05, 0x13, 0x32, 0xc4, 0x05, 0x50, 1740x2e, 0x29, 0x8f, 0x06, 0x13, 0x32, 0x8f, 0x06, 0x50, 0x70,
1620x70, 0x61, 0x72, 0x61, 0x6d, 0x27, 0x08, 0x03, 0xc6, 0x05, 1750x61, 0x72, 0x61, 0x6d, 0xf2, 0x08, 0x03, 0x91, 0x06, 0x00,
1630x00, 0xcd, 0x01, 0x06, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04, 1760xcd, 0x01, 0x06, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c,
1640x3c, 0x00, 0x06, 0x80, 0x05, 0x21, 0x60, 0x43, 0xa4, 0x06, 1770x00, 0x06, 0x4b, 0x06, 0x21, 0x60, 0x43, 0x6f, 0x07, 0x00,
1650x00, 0xb5, 0x00, 0x31, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x03, 1780xb5, 0x00, 0x31, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x03, 0x59,
1660x8e, 0x05, 0xc0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 1790x06, 0xc1, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d,
1670x6d, 0x65, 0x6e, 0x75, 0x2e, 0x4f, 0x00, 0x82, 0x00, 0x64, 1800x65, 0x6e, 0x75, 0x2e, 0xd6, 0x03, 0x72, 0x64, 0x65, 0x66,
1680x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x1c, 0x06, 0x62, 0x6d, 1810x61, 0x75, 0x6c, 0x74, 0xe7, 0x06, 0x62, 0x6d, 0x61, 0x74,
1690x61, 0x74, 0x63, 0x68, 0x65, 0x3f, 0x01, 0x07, 0x66, 0x00, 1820x63, 0x68, 0x65, 0x3f, 0x01, 0x07, 0x66, 0x00, 0x13, 0x66,
1700x13, 0x66, 0x0d, 0x06, 0x0f, 0x60, 0x07, 0x04, 0x00, 0xf4, 1830xd8, 0x06, 0x0f, 0x2b, 0x08, 0x04, 0x00, 0xf4, 0x00, 0x13,
1710x00, 0x13, 0x43, 0xf8, 0x06, 0x3d, 0x00, 0x00, 0x4e, 0x0c, 1840x43, 0xc3, 0x07, 0x3d, 0x00, 0x00, 0x4e, 0xd7, 0x07, 0x09,
1720x07, 0x09, 0x36, 0x01, 0x24, 0x69, 0x73, 0xc6, 0x03, 0x00, 1850x36, 0x01, 0x24, 0x69, 0x73, 0x9e, 0x04, 0x00, 0xae, 0x00,
1730xae, 0x00, 0x12, 0x3b, 0xb4, 0x00, 0x12, 0x32, 0x2c, 0x07, 1860x12, 0x3b, 0xb4, 0x00, 0x12, 0x32, 0xf7, 0x07, 0x21, 0x00,
1740x21, 0x00, 0x28, 0x20, 0x03, 0xa1, 0x69, 0x73, 0x00, 0x68, 1870x28, 0x20, 0x03, 0xa1, 0x69, 0x73, 0x00, 0x68, 0x61, 0x72,
1750x61, 0x72, 0x64, 0x65, 0x72, 0x29, 0xa3, 0x03, 0x82, 0x65, 1880x64, 0x65, 0x72, 0x29, 0xa3, 0x03, 0x82, 0x65, 0x67, 0x73,
1760x67, 0x73, 0x20, 0x70, 0x65, 0x72, 0x20, 0x18, 0x03, 0x08, 1890x20, 0x70, 0x65, 0x72, 0x20, 0x18, 0x03, 0x08, 0x61, 0x00,
1770x61, 0x00, 0x05, 0x4f, 0x07, 0x02, 0x1b, 0x00, 0x0e, 0x3e, 1900x05, 0x1a, 0x08, 0x02, 0x1b, 0x00, 0x0e, 0x3e, 0x00, 0x03,
1780x00, 0x03, 0x4b, 0x07, 0x1a, 0x65, 0x37, 0x00, 0x04, 0xe5, 1910x16, 0x08, 0x1a, 0x65, 0x37, 0x00, 0x04, 0xe5, 0x01, 0x00,
1790x01, 0x00, 0xfc, 0x01, 0x04, 0x9a, 0x09, 0x4b, 0x66, 0x69, 1920xfc, 0x01, 0x04, 0x65, 0x0a, 0x4b, 0x66, 0x69, 0x6e, 0x64,
1800x6e, 0x64, 0xa9, 0x00, 0x8b, 0x6e, 0x00, 0x28, 0x66, 0x65, 1930xa9, 0x00, 0x8b, 0x6e, 0x00, 0x28, 0x66, 0x65, 0x77, 0x65,
1810x77, 0x65, 0x72, 0x52, 0x00, 0x10, 0x41, 0x06, 0x08, 0x74, 1940x72, 0x52, 0x00, 0x10, 0x41, 0xd1, 0x08, 0x74, 0x20, 0x62,
1820x20, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x73, 0x0f, 0x00, 0x21, 1950x6c, 0x61, 0x6e, 0x6b, 0x73, 0x0f, 0x00, 0x21, 0x73, 0x00,
1830x73, 0x00, 0x10, 0x00, 0x05, 0xdd, 0x02, 0x80, 0x62, 0x65, 1960x10, 0x00, 0x05, 0xdd, 0x02, 0x80, 0x62, 0x65, 0x00, 0x67,
1840x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0xe6, 0x03, 0x31, 0x70, 1970x69, 0x76, 0x65, 0x6e, 0xe6, 0x03, 0x31, 0x70, 0x61, 0x72,
1850x61, 0x72, 0x5b, 0x02, 0x15, 0x61, 0xa6, 0x00, 0x31, 0x61, 1980x5b, 0x02, 0x15, 0x61, 0xa6, 0x00, 0x31, 0x61, 0x6b, 0x65,
1860x6b, 0x65, 0xfd, 0x03, 0x40, 0x65, 0x61, 0x73, 0x69, 0x65, 1990xfd, 0x03, 0x40, 0x65, 0x61, 0x73, 0x69, 0x30, 0x09, 0x71,
1870x08, 0x71, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x90, 2000x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x90, 0x00, 0x00,
1880x00, 0x00, 0xf0, 0x08, 0x02, 0x1d, 0x06, 0x10, 0x74, 0x26, 2010xbb, 0x09, 0x02, 0xe8, 0x06, 0x10, 0x74, 0x26, 0x01, 0x02,
1890x01, 0x02, 0x65, 0x02, 0x10, 0x6e, 0x8d, 0x08, 0x20, 0x00, 2020x65, 0x02, 0x10, 0x6e, 0x58, 0x09, 0x20, 0x00, 0x62, 0x58,
1900x62, 0x8d, 0x09, 0x20, 0x75, 0x6e, 0x76, 0x04, 0x07, 0x57, 2030x0a, 0x20, 0x75, 0x6e, 0x76, 0x04, 0x07, 0x57, 0x00, 0x08,
1910x00, 0x08, 0xb4, 0x00, 0x10, 0x29, 0xd3, 0x01, 0x02, 0x83, 2040xb4, 0x00, 0x10, 0x29, 0xd3, 0x01, 0x02, 0x83, 0x04, 0x30,
1920x04, 0x30, 0x74, 0x75, 0x72, 0x57, 0x0a, 0x00, 0xd7, 0x06, 2050x74, 0x75, 0x72, 0x22, 0x0b, 0x00, 0xa2, 0x07, 0x24, 0x62,
1930x24, 0x62, 0x79, 0xe5, 0x01, 0x10, 0x2e, 0x07, 0x01, 0x34, 2060x79, 0xe5, 0x01, 0x10, 0x2e, 0x07, 0x01, 0x34, 0x6f, 0x74,
1940x6f, 0x74, 0x65, 0x61, 0x00, 0xa2, 0x69, 0x73, 0x00, 0x64, 2070x65, 0x61, 0x00, 0xa2, 0x69, 0x73, 0x00, 0x64, 0x6f, 0x65,
1950x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0xdc, 0x08, 0x08, 0xc6, 2080x73, 0x6e, 0x27, 0x74, 0xa7, 0x09, 0x08, 0xc6, 0x00, 0x29,
1960x00, 0x29, 0x69, 0x6e, 0x5b, 0x00, 0x06, 0x2d, 0x03, 0x11, 2090x69, 0x6e, 0x5b, 0x00, 0x06, 0x2d, 0x03, 0x11, 0x65, 0x5e,
1970x65, 0x93, 0x06, 0x21, 0x77, 0x61, 0x89, 0x00, 0x00, 0x49, 2100x07, 0x21, 0x77, 0x61, 0x89, 0x00, 0x00, 0x49, 0x00, 0x10,
1980x00, 0x20, 0x2c, 0x00, 0xbd, 0x00, 0x84, 0x6f, 0x6e, 0x65, 2110x2c, 0x02, 0x06, 0x00, 0xc1, 0x05, 0x54, 0x00, 0x65, 0x78,
1990x00, 0x65, 0x78, 0x74, 0x72, 0x13, 0x08, 0x06, 0x2b, 0x01, 2120x74, 0x72, 0xde, 0x08, 0x06, 0x2b, 0x01, 0x97, 0x64, 0x75,
2000x97, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 2130x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2f, 0x01, 0x09,
2010x2f, 0x01, 0x09, 0x6a, 0x01, 0x14, 0x28, 0x23, 0x06, 0x03, 2140x6a, 0x01, 0x14, 0x28, 0xee, 0x06, 0x03, 0x99, 0x01, 0x12,
2020x99, 0x01, 0x12, 0x29, 0xbf, 0x07, 0x11, 0x6e, 0xde, 0x0a, 2150x29, 0x8a, 0x08, 0x11, 0x6e, 0xa9, 0x0b, 0x04, 0x45, 0x02,
2030x04, 0x45, 0x02, 0x01, 0xdc, 0x01, 0x30, 0x74, 0x68, 0x61, 2160x01, 0xdc, 0x01, 0x30, 0x74, 0x68, 0x61, 0xca, 0x02, 0x33,
2040xca, 0x02, 0x33, 0x63, 0x65, 0x3b, 0x6c, 0x05, 0x51, 0x6e, 2170x63, 0x65, 0x3b, 0x6c, 0x05, 0x51, 0x6e, 0x63, 0x72, 0x65,
2050x63, 0x72, 0x65, 0x61, 0xf1, 0x07, 0x01, 0x35, 0x06, 0x43, 2180x61, 0xbc, 0x08, 0x01, 0x4b, 0x06, 0x43, 0x61, 0x72, 0x63,
2060x61, 0x72, 0x63, 0x68, 0xa5, 0x04, 0x00, 0x67, 0x01, 0x03, 2190x68, 0xa5, 0x04, 0x00, 0x67, 0x01, 0x03, 0x94, 0x05, 0x25,
2070x94, 0x05, 0x25, 0x6e, 0x67, 0xc1, 0x01, 0x02, 0x68, 0x04, 2200x6e, 0x67, 0xc1, 0x01, 0x02, 0x68, 0x04, 0x07, 0x20, 0x01,
2080x07, 0x20, 0x01, 0x14, 0x6e, 0x1f, 0x01, 0x50, 0x75, 0x6c, 2210x1b, 0x6e, 0x1f, 0x01, 0x43, 0x31, 0x35, 0x2e, 0x33, 0x7b,
2090x74, 0x2e, 0x00, 2220x03, 0xb0, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x72, 0x65,
2230x66, 0x65, 0x72, 0x65, 0x0c, 0x01, 0x81, 0x03, 0x20, 0x4f,
2240x6e, 0x86, 0x07, 0x53, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x8f,
2250x08, 0x80, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x00,
2260x2e, 0x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0x6f, 0x03,
2270x16, 0x50, 0x12, 0x00, 0x0d, 0x8b, 0x03, 0x33, 0x47, 0x61,
2280x6d, 0x8b, 0x03, 0x03, 0xb2, 0x06, 0x23, 0x65, 0x74, 0xdd,
2290x04, 0xb1, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x00,
2300x77, 0x68, 0x65, 0x65, 0x0a, 0x02, 0x32, 0x06, 0x22, 0x72,
2310x65, 0xd5, 0x06, 0x3f, 0x6c, 0x65, 0x64, 0xcf, 0x06, 0x02,
2320x53, 0x55, 0x6e, 0x6c, 0x69, 0x6b, 0x41, 0x05, 0x20, 0x4c,
2330x27, 0x9a, 0x05, 0x03, 0x8c, 0x0b, 0x01, 0x5e, 0x00, 0x10,
2340x70, 0x91, 0x0b, 0xf0, 0x03, 0x73, 0x74, 0x00, 0x62, 0x65,
2350x74, 0x77, 0x65, 0x65, 0x6e, 0x00, 0x67, 0x61, 0x6d, 0x65,
2360x73, 0x2e, 0x00,
210}; 237};
211 238
212const unsigned short help_text_len = 3042; 239const unsigned short help_text_len = 3506;
213const unsigned short help_text_words = 572; 240const unsigned short help_text_words = 650;
241const bool help_valid = true;
214const char quick_help_text[] = "Guess the hidden combination of colours."; 242const char quick_help_text[] = "Guess the hidden combination of colours.";
diff --git a/apps/plugins/puzzles/help/inertia.c b/apps/plugins/puzzles/help/inertia.c
index 956d133d44..a0f33a9dc3 100644
--- a/apps/plugins/puzzles/help/inertia.c
+++ b/apps/plugins/puzzles/help/inertia.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,176 +6,178 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 39, TEXT_UNDERLINE }, 9 { 40, TEXT_UNDERLINE },
10 { 110, TEXT_UNDERLINE }, 10 { 111, TEXT_UNDERLINE },
11 { 196, TEXT_CENTER | C_RED }, 11 { 197, TEXT_CENTER | C_RED },
12 { 404, TEXT_CENTER | C_RED }, 12 { 405, TEXT_CENTER | C_RED },
13 { 421, TEXT_UNDERLINE },
14 { 422, TEXT_UNDERLINE }, 13 { 422, TEXT_UNDERLINE },
14 { 423, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 2266 comp 1565 ratio 0.690644 level 10 saved 701 */ 18/* orig 2286 comp 1571 ratio 0.687227 level 10 saved 715 */
19const char help_text[] = { 19const char help_text[] = {
200xf0, 0x1f, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x32, 0x34, 0x3a, 0x20, 0x49, 0x6e, 0x65, 0x72, 0x74, 0x69, 210x32, 0x34, 0x3a, 0x20, 0x49, 0x6e, 0x65, 0x72, 0x74, 0x69,
220x61, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 220x61, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x0b, 0x00, 0x00,
230x72, 0x65, 0x00, 0x61, 0x00, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 230x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x61,
240x00, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x00, 0x62, 0x0b, 0x00, 240x00, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x00, 0x67, 0x72, 0x65,
250xf0, 0x15, 0x73, 0x69, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x00, 250x65, 0x6e, 0x00, 0x62, 0x0b, 0x00, 0xf0, 0x15, 0x73, 0x69,
260x69, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 260x74, 0x74, 0x69, 0x6e, 0x67, 0x00, 0x69, 0x6e, 0x00, 0x61,
270x66, 0x75, 0x6c, 0x6c, 0x00, 0x6f, 0x66, 0x00, 0x6f, 0x62, 270x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x66, 0x75, 0x6c, 0x6c,
280x73, 0x74, 0x61, 0x63, 0x6c, 0x65, 0x73, 0x2e, 0x40, 0x00, 280x00, 0x6f, 0x66, 0x00, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x63,
290xf0, 0x05, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 290x6c, 0x65, 0x73, 0x2e, 0x40, 0x00, 0xf0, 0x05, 0x72, 0x00,
300x00, 0x74, 0x6f, 0x00, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 300x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00,
310x74, 0x00, 0x40, 0x00, 0xf2, 0x06, 0x74, 0x68, 0x65, 0x00, 310x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x40, 0x00,
320x67, 0x65, 0x6d, 0x73, 0x00, 0x77, 0x69, 0x74, 0x68, 0x6f, 320xf2, 0x06, 0x74, 0x68, 0x65, 0x00, 0x67, 0x65, 0x6d, 0x73,
330x75, 0x74, 0x00, 0x72, 0x75, 0x6e, 0x6e, 0x51, 0x00, 0xa0, 330x00, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x00, 0x72,
340x74, 0x6f, 0x00, 0x61, 0x6e, 0x79, 0x00, 0x6d, 0x69, 0x6e, 340x75, 0x6e, 0x6e, 0x51, 0x00, 0xa0, 0x74, 0x6f, 0x00, 0x61,
350x44, 0x00, 0x02, 0x86, 0x00, 0x81, 0x63, 0x61, 0x6e, 0x00, 350x6e, 0x79, 0x00, 0x6d, 0x69, 0x6e, 0x44, 0x00, 0x02, 0x86,
360x6d, 0x6f, 0x76, 0x65, 0x38, 0x00, 0x01, 0x81, 0x00, 0x21, 360x00, 0x81, 0x63, 0x61, 0x6e, 0x00, 0x6d, 0x6f, 0x76, 0x65,
370x69, 0x6e, 0x26, 0x00, 0xf2, 0x02, 0x6f, 0x72, 0x74, 0x68, 370x38, 0x00, 0x01, 0x81, 0x00, 0x21, 0x69, 0x6e, 0x26, 0x00,
380x6f, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x00, 0x6f, 0x72, 0x20, 380xf2, 0x02, 0x6f, 0x72, 0x74, 0x68, 0x6f, 0x67, 0x6f, 0x6e,
390x64, 0x69, 0x61, 0x0c, 0x00, 0xe7, 0x64, 0x69, 0x72, 0x65, 390x61, 0x6c, 0x00, 0x6f, 0x72, 0x20, 0x64, 0x69, 0x61, 0x0c,
400x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x00, 0x4f, 0x6e, 0x63, 400x00, 0xe7, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
410x37, 0x00, 0x60, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x4c, 410x6e, 0x2e, 0x00, 0x4f, 0x6e, 0x63, 0x37, 0x00, 0x60, 0x73,
420x00, 0xf0, 0x13, 0x69, 0x6e, 0x67, 0x2c, 0x00, 0x69, 0x74, 420x74, 0x61, 0x72, 0x74, 0x73, 0x4c, 0x00, 0xf0, 0x13, 0x69,
430x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x63, 0x6f, 0x6e, 0x74, 430x6e, 0x67, 0x2c, 0x00, 0x69, 0x74, 0x00, 0x77, 0x69, 0x6c,
440x69, 0x6e, 0x75, 0x65, 0x00, 0x75, 0x6e, 0x74, 0x69, 0x6c, 440x6c, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65,
450x00, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x8f, 0x00, 0xf4, 450x00, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x00, 0x73, 0x6f, 0x6d,
460x00, 0x73, 0x74, 0x6f, 0x70, 0x73, 0x00, 0x69, 0x74, 0x2e, 460x65, 0x74, 0x68, 0x8f, 0x00, 0xf4, 0x00, 0x73, 0x74, 0x6f,
470x00, 0x41, 0x00, 0x77, 0x61, 0x6c, 0x5a, 0x00, 0x20, 0x6c, 470x70, 0x73, 0x00, 0x69, 0x74, 0x2e, 0x00, 0x41, 0x00, 0x77,
480x79, 0x81, 0x00, 0x82, 0x69, 0x74, 0x73, 0x00, 0x70, 0x61, 480x61, 0x6c, 0x5a, 0x00, 0x20, 0x6c, 0x79, 0x81, 0x00, 0x82,
490x74, 0x68, 0x44, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x51, 0x00, 490x69, 0x74, 0x73, 0x00, 0x70, 0x61, 0x74, 0x68, 0x44, 0x00,
500x70, 0x28, 0x62, 0x75, 0x74, 0x00, 0x69, 0x66, 0x0b, 0x00, 500x00, 0x2b, 0x00, 0x00, 0x51, 0x00, 0x70, 0x28, 0x62, 0x75,
510x14, 0x69, 0x6a, 0x00, 0x14, 0x00, 0x9a, 0x00, 0x26, 0x6c, 510x74, 0x00, 0x69, 0x66, 0x0b, 0x00, 0x14, 0x69, 0x6a, 0x00,
520x79, 0x75, 0x00, 0x03, 0xd1, 0x00, 0x85, 0x72, 0x6f, 0x75, 520x14, 0x00, 0x9a, 0x00, 0x26, 0x6c, 0x79, 0x75, 0x00, 0x03,
530x67, 0x68, 0x00, 0x61, 0x00, 0xbd, 0x00, 0x80, 0x67, 0x61, 530xd1, 0x00, 0x85, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x61,
540x70, 0x00, 0x62, 0x65, 0x74, 0x77, 0x6d, 0x01, 0x91, 0x74, 540x00, 0xbd, 0x00, 0x80, 0x67, 0x61, 0x70, 0x00, 0x62, 0x65,
550x77, 0x6f, 0x00, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x7e, 0x00, 550x74, 0x77, 0x6d, 0x01, 0x91, 0x74, 0x77, 0x6f, 0x00, 0x6f,
560x06, 0x2f, 0x01, 0x00, 0x6d, 0x00, 0xc1, 0x70, 0x69, 0x6e, 560x74, 0x68, 0x65, 0x72, 0x7e, 0x00, 0x06, 0x2f, 0x01, 0x00,
570x67, 0x29, 0x2e, 0x00, 0x41, 0x6c, 0x73, 0x6f, 0x2c, 0xb3, 570x6d, 0x00, 0xc1, 0x70, 0x69, 0x6e, 0x67, 0x29, 0x2e, 0x00,
580x00, 0x00, 0x7f, 0x01, 0x00, 0xea, 0x00, 0x71, 0x73, 0x71, 580x41, 0x6c, 0x73, 0x6f, 0x2c, 0xb3, 0x00, 0x00, 0x7f, 0x01,
590x75, 0x61, 0x72, 0x65, 0x73, 0xbc, 0x01, 0x11, 0x60, 0xc2, 590x00, 0xea, 0x00, 0x71, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65,
600x00, 0x76, 0x27, 0x3b, 0x00, 0x77, 0x68, 0x65, 0x6e, 0x08, 600x73, 0xbc, 0x01, 0x11, 0x60, 0xc2, 0x00, 0x76, 0x27, 0x3b,
610x01, 0x00, 0x7c, 0x00, 0x50, 0x73, 0x00, 0x6f, 0x6e, 0x00, 610x00, 0x77, 0x68, 0x65, 0x6e, 0x08, 0x01, 0x00, 0x7c, 0x00,
620x6e, 0x01, 0x01, 0x4e, 0x00, 0x06, 0x98, 0x00, 0x01, 0xc9, 620x50, 0x73, 0x00, 0x6f, 0x6e, 0x00, 0x6e, 0x01, 0x01, 0x4e,
630x00, 0x03, 0xb8, 0x00, 0x70, 0x6e, 0x6f, 0x00, 0x6d, 0x61, 630x00, 0x06, 0x98, 0x00, 0x01, 0xc9, 0x00, 0x03, 0xb8, 0x00,
640x74, 0x74, 0x80, 0x00, 0x36, 0x68, 0x61, 0x74, 0x58, 0x01, 640x70, 0x6e, 0x6f, 0x00, 0x6d, 0x61, 0x74, 0x74, 0x80, 0x00,
650x01, 0x2d, 0x00, 0x52, 0x61, 0x73, 0x00, 0x67, 0x6f, 0xb5, 650x36, 0x68, 0x61, 0x74, 0x58, 0x01, 0x01, 0x2d, 0x00, 0x52,
660x01, 0x30, 0x2e, 0x00, 0x47, 0xce, 0x01, 0x52, 0x64, 0x6f, 660x61, 0x73, 0x00, 0x67, 0x6f, 0xb5, 0x01, 0x30, 0x2e, 0x00,
670x00, 0x6e, 0x6f, 0x9e, 0x00, 0x05, 0x6c, 0x00, 0x10, 0x3b, 670x47, 0xce, 0x01, 0x52, 0x64, 0x6f, 0x00, 0x6e, 0x6f, 0x9e,
680x2c, 0x00, 0x50, 0x70, 0x69, 0x63, 0x6b, 0x73, 0x13, 0x00, 680x00, 0x05, 0x6c, 0x00, 0x10, 0x3b, 0x2c, 0x00, 0x50, 0x70,
690xd1, 0x6d, 0x00, 0x75, 0x70, 0x00, 0x61, 0x6e, 0x64, 0x00, 690x69, 0x63, 0x6b, 0x73, 0x13, 0x00, 0xd1, 0x6d, 0x00, 0x75,
700x6b, 0x65, 0x65, 0x70, 0x82, 0x00, 0x01, 0x43, 0x00, 0x00, 700x70, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x6b, 0x65, 0x65, 0x70,
710xe9, 0x01, 0x19, 0x52, 0x03, 0x02, 0x01, 0x01, 0x02, 0x00, 710x82, 0x00, 0x01, 0x43, 0x00, 0x00, 0xe9, 0x01, 0x19, 0x52,
720x42, 0x01, 0xb0, 0x66, 0x61, 0x74, 0x61, 0x6c, 0x2e, 0x00, 720x03, 0x02, 0x01, 0x01, 0x02, 0x00, 0x42, 0x01, 0xb0, 0x66,
730x45, 0x76, 0x65, 0x6e, 0x57, 0x01, 0x31, 0x79, 0x6f, 0x75, 730x61, 0x74, 0x61, 0x6c, 0x2e, 0x00, 0x45, 0x76, 0x65, 0x6e,
740x4e, 0x00, 0x42, 0x65, 0x64, 0x00, 0x75, 0x65, 0x00, 0x40, 740x57, 0x01, 0x31, 0x79, 0x6f, 0x75, 0x4e, 0x00, 0x42, 0x65,
750x6c, 0x61, 0x73, 0x74, 0x4d, 0x02, 0x00, 0x93, 0x01, 0x01, 750x64, 0x00, 0x75, 0x65, 0x00, 0x40, 0x6c, 0x61, 0x73, 0x74,
760xff, 0x00, 0x32, 0x61, 0x6d, 0x65, 0x5d, 0x01, 0x70, 0x77, 760x4d, 0x02, 0x00, 0x93, 0x01, 0x01, 0xff, 0x00, 0x32, 0x61,
770x68, 0x69, 0x63, 0x68, 0x00, 0x74, 0xfa, 0x00, 0x33, 0x68, 770x6d, 0x65, 0x5d, 0x01, 0x70, 0x77, 0x68, 0x69, 0x63, 0x68,
780x69, 0x74, 0x54, 0x00, 0x12, 0x2c, 0x7d, 0x02, 0x00, 0x25, 780x00, 0x74, 0xfa, 0x00, 0x33, 0x68, 0x69, 0x74, 0x54, 0x00,
790x00, 0x03, 0xfc, 0x01, 0x31, 0x75, 0x6e, 0x74, 0x57, 0x00, 790x12, 0x2c, 0x7d, 0x02, 0x00, 0x25, 0x00, 0x03, 0xfc, 0x01,
800xa1, 0x61, 0x73, 0x00, 0x64, 0x65, 0x61, 0x64, 0x00, 0x72, 800x31, 0x75, 0x6e, 0x74, 0x57, 0x00, 0xa1, 0x61, 0x73, 0x00,
810x61, 0x72, 0x01, 0xe1, 0x74, 0x68, 0x61, 0x6e, 0x00, 0x76, 810x64, 0x65, 0x61, 0x64, 0x00, 0x72, 0x61, 0x72, 0x01, 0xe1,
820x69, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x6f, 0x75, 0x8c, 0x02, 820x74, 0x68, 0x61, 0x6e, 0x00, 0x76, 0x69, 0x63, 0x74, 0x6f,
830x43, 0x54, 0x68, 0x69, 0x73, 0x3b, 0x00, 0x81, 0x61, 0x73, 830x72, 0x69, 0x6f, 0x75, 0x8c, 0x02, 0x43, 0x54, 0x68, 0x69,
840x00, 0x6f, 0x72, 0x69, 0x67, 0x69, 0xd5, 0x01, 0xf0, 0x16, 840x73, 0x3b, 0x00, 0x81, 0x61, 0x73, 0x00, 0x6f, 0x72, 0x69,
850x00, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 850x67, 0x69, 0xd5, 0x01, 0xf0, 0x16, 0x00, 0x69, 0x6d, 0x70,
860x65, 0x64, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x57, 0x69, 0x6e, 860x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x00, 0x66,
870x64, 0x6f, 0x77, 0x73, 0x00, 0x62, 0x79, 0x00, 0x42, 0x65, 870x6f, 0x72, 0x00, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73,
880x6e, 0x00, 0x4f, 0x6c, 0x6d, 0x73, 0x74, 0x5b, 0x00, 0x91, 880x00, 0x62, 0x79, 0x00, 0x42, 0x65, 0x6e, 0x00, 0x4f, 0x6c,
890x5b, 0x31, 0x31, 0x5d, 0x2c, 0x00, 0x77, 0x68, 0x6f, 0x41, 890x6d, 0x73, 0x74, 0x5b, 0x00, 0x91, 0x5b, 0x31, 0x31, 0x5d,
900x00, 0x71, 0x6b, 0x69, 0x6e, 0x64, 0x00, 0x65, 0x6e, 0x01, 900x2c, 0x00, 0x77, 0x68, 0x6f, 0x41, 0x00, 0x71, 0x6b, 0x69,
910x02, 0xb0, 0x74, 0x6f, 0x00, 0x72, 0x65, 0x6c, 0x65, 0x61, 910x6e, 0x64, 0x00, 0x65, 0x6e, 0x01, 0x02, 0xb0, 0x74, 0x6f,
920x73, 0x65, 0x00, 0x65, 0x00, 0xb0, 0x73, 0x6f, 0x75, 0x72, 920x00, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x00, 0x65,
930x63, 0x65, 0x00, 0x63, 0x6f, 0x64, 0x65, 0x25, 0x01, 0xa0, 930x00, 0xb0, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, 0x63,
940x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00, 0x73, 0x6f, 940x6f, 0x64, 0x65, 0x25, 0x01, 0xa0, 0x72, 0x65, 0x71, 0x75,
950x97, 0x00, 0x10, 0x74, 0x53, 0x01, 0xcc, 0x63, 0x6f, 0x75, 950x65, 0x73, 0x74, 0x00, 0x73, 0x6f, 0x97, 0x00, 0x10, 0x74,
960x6c, 0x64, 0x00, 0x62, 0x65, 0x00, 0x72, 0x65, 0x2d, 0x7f, 960x53, 0x01, 0xcc, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x62,
970x00, 0x10, 0x74, 0x43, 0x00, 0x03, 0x70, 0x03, 0x01, 0x06, 970x65, 0x00, 0x72, 0x65, 0x2d, 0x7f, 0x00, 0x10, 0x74, 0x43,
980x03, 0x11, 0x00, 0x7a, 0x00, 0xf5, 0x09, 0x00, 0x68, 0x74, 980x00, 0x03, 0x70, 0x03, 0x01, 0x06, 0x03, 0x11, 0x00, 0x7a,
990x74, 0x70, 0x3a, 0x2f, 0x2f, 0x78, 0x6e, 0x31, 0x33, 0x2e, 990x00, 0xf5, 0x09, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
1000x63, 0x6f, 0x6d, 0x2f, 0x00, 0x00, 0x00, 0x32, 0x34, 0x2e, 1000x2f, 0x78, 0x6e, 0x31, 0x33, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
1010x31, 0xf5, 0x03, 0x00, 0x08, 0x03, 0x5f, 0x72, 0x6f, 0x6c, 1010x00, 0x00, 0x00, 0x32, 0x34, 0x2e, 0x31, 0x09, 0x04, 0x00,
1020x73, 0x20, 0x78, 0x03, 0x0e, 0x02, 0x6a, 0x02, 0x47, 0x65, 1020x08, 0x03, 0x5f, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x78, 0x03,
1030x69, 0x67, 0x68, 0x16, 0x02, 0x40, 0x73, 0x00, 0x75, 0x73, 1030x0e, 0x02, 0x6a, 0x02, 0x47, 0x65, 0x69, 0x67, 0x68, 0x16,
1040xc2, 0x01, 0x00, 0x1b, 0x00, 0xe0, 0x6e, 0x75, 0x6d, 0x65, 1040x02, 0x40, 0x73, 0x00, 0x75, 0x73, 0xc2, 0x01, 0x00, 0x1b,
1050x72, 0x69, 0x63, 0x00, 0x6b, 0x65, 0x79, 0x70, 0x61, 0x64, 1050x00, 0xe0, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x00,
1060xa7, 0x02, 0x91, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 1060x6b, 0x65, 0x79, 0x70, 0x61, 0x64, 0xa7, 0x02, 0x91, 0x74,
1070x76, 0x65, 0x05, 0x03, 0x02, 0xca, 0x01, 0x52, 0x63, 0x6c, 1070x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x05, 0x03,
1080x69, 0x63, 0x6b, 0xc6, 0x01, 0xf2, 0x03, 0x65, 0x66, 0x74, 1080x02, 0xca, 0x01, 0x52, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xc6,
1090x00, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 0x62, 0x75, 0x74, 1090x01, 0xf2, 0x03, 0x65, 0x66, 0x74, 0x00, 0x6d, 0x6f, 0x75,
1100x74, 0x6f, 0x6e, 0x00, 0x6f, 0xcf, 0x01, 0x00, 0x5e, 0x04, 1100x73, 0x65, 0x00, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x00,
1110x16, 0x2c, 0x81, 0x00, 0x01, 0xb4, 0x01, 0x31, 0x62, 0x65, 1110x6f, 0xcf, 0x01, 0x00, 0x5e, 0x04, 0x16, 0x2c, 0x81, 0x00,
1120x67, 0x7a, 0x04, 0x01, 0x9c, 0x00, 0x23, 0x69, 0x6e, 0x4f, 1120x01, 0xb4, 0x01, 0x31, 0x62, 0x65, 0x67, 0x7a, 0x04, 0x01,
1130x04, 0x38, 0x6e, 0x65, 0x72, 0xfc, 0x03, 0x00, 0xa5, 0x00, 1130x9c, 0x00, 0x23, 0x69, 0x6e, 0x4f, 0x04, 0x38, 0x6e, 0x65,
1140x56, 0x77, 0x68, 0x65, 0x72, 0x65, 0x6a, 0x00, 0x20, 0x65, 1140x72, 0xfc, 0x03, 0x00, 0xa5, 0x00, 0x56, 0x77, 0x68, 0x65,
1150x64, 0x0b, 0x01, 0x12, 0x49, 0x7c, 0x00, 0x22, 0x75, 0x73, 1150x72, 0x65, 0x6a, 0x00, 0x20, 0x65, 0x64, 0x0b, 0x01, 0x12,
1160xd8, 0x00, 0xb3, 0x60, 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x27, 1160x49, 0x7c, 0x00, 0x22, 0x75, 0x73, 0xd8, 0x00, 0xb3, 0x60,
1170x00, 0x66, 0x75, 0x6e, 0x38, 0x00, 0x00, 0x51, 0x00, 0x03, 1170x53, 0x6f, 0x6c, 0x76, 0x65, 0x27, 0x00, 0x66, 0x75, 0x6e,
1180xe9, 0x01, 0x02, 0x7a, 0x00, 0x74, 0x70, 0x72, 0x6f, 0x67, 1180x38, 0x00, 0x00, 0x51, 0x00, 0x03, 0xe9, 0x01, 0x02, 0x7a,
1190x72, 0x61, 0x6d, 0x31, 0x02, 0x40, 0x6d, 0x70, 0x75, 0x74, 1190x00, 0x74, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x31,
1200x17, 0x05, 0x01, 0xfd, 0x03, 0x04, 0xc2, 0x03, 0x04, 0xac, 1200x02, 0x40, 0x6d, 0x70, 0x75, 0x74, 0x17, 0x05, 0x01, 0xfd,
1210x00, 0x03, 0x76, 0x02, 0x03, 0x7e, 0x01, 0x15, 0x73, 0xef, 1210x03, 0x04, 0xc2, 0x03, 0x04, 0xac, 0x00, 0x03, 0x76, 0x02,
1220x04, 0x51, 0x72, 0x65, 0x6d, 0x61, 0x69, 0xe1, 0x02, 0x01, 1220x03, 0x7e, 0x01, 0x15, 0x73, 0xef, 0x04, 0x51, 0x72, 0x65,
1230xf9, 0x04, 0x00, 0x04, 0x03, 0x61, 0x72, 0x65, 0x74, 0x75, 1230x6d, 0x61, 0x69, 0xe1, 0x02, 0x01, 0xf9, 0x04, 0x00, 0x04,
1240x72, 0x6e, 0x1d, 0x05, 0x00, 0x22, 0x00, 0xc2, 0x63, 0x75, 1240x03, 0x61, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x1d, 0x05,
1250x72, 0x72, 0x65, 0x6e, 0x74, 0x00, 0x70, 0x6f, 0x73, 0x69, 1250x00, 0x22, 0x00, 0xc2, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
1260xb7, 0x01, 0xc2, 0x41, 0x00, 0x68, 0x69, 0x6e, 0x74, 0x00, 1260x74, 0x00, 0x70, 0x6f, 0x73, 0x69, 0xb7, 0x01, 0xc2, 0x41,
1270x61, 0x72, 0x72, 0x6f, 0x77, 0x7d, 0x00, 0x64, 0x61, 0x70, 1270x00, 0x68, 0x69, 0x6e, 0x74, 0x00, 0x61, 0x72, 0x72, 0x6f,
1280x70, 0x65, 0x61, 0x72, 0x1c, 0x01, 0x03, 0x93, 0x01, 0x54, 1280x77, 0x7d, 0x00, 0x64, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72,
1290x64, 0x69, 0x63, 0x61, 0x74, 0x79, 0x01, 0x07, 0xa4, 0x03, 1290x1c, 0x01, 0x03, 0x93, 0x01, 0x54, 0x64, 0x69, 0x63, 0x61,
1300x13, 0x6e, 0x8c, 0x00, 0x00, 0xee, 0x00, 0x21, 0x73, 0x68, 1300x74, 0x79, 0x01, 0x07, 0xa4, 0x03, 0x13, 0x6e, 0x8c, 0x00,
1310x30, 0x02, 0x02, 0xce, 0x01, 0x13, 0x6f, 0x42, 0x01, 0x04, 1310x00, 0xee, 0x00, 0x21, 0x73, 0x68, 0x30, 0x02, 0x02, 0xce,
1320xea, 0x00, 0x00, 0xc9, 0x00, 0x14, 0x2e, 0x18, 0x01, 0x01, 1320x01, 0x13, 0x6f, 0x42, 0x01, 0x04, 0xea, 0x00, 0x00, 0xc9,
1330x30, 0x03, 0x06, 0x5a, 0x01, 0x08, 0xf7, 0x03, 0x02, 0x0e, 1330x00, 0x14, 0x2e, 0x18, 0x01, 0x01, 0x30, 0x03, 0x06, 0x5a,
1340x01, 0x07, 0x8f, 0x00, 0x51, 0x75, 0x70, 0x64, 0x61, 0x74, 1340x01, 0x08, 0xf7, 0x03, 0x02, 0x0e, 0x01, 0x07, 0x8f, 0x00,
1350x51, 0x00, 0x03, 0x86, 0x00, 0x12, 0x65, 0xfd, 0x01, 0x28, 1350x51, 0x75, 0x70, 0x64, 0x61, 0x74, 0x51, 0x00, 0x03, 0x86,
1360x65, 0x78, 0x2d, 0x04, 0x03, 0xae, 0x00, 0x02, 0x66, 0x00, 1360x00, 0x12, 0x65, 0xfd, 0x01, 0x28, 0x65, 0x78, 0x2d, 0x04,
1370x04, 0x58, 0x02, 0xf1, 0x01, 0x61, 0x6c, 0x73, 0x6f, 0x00, 1370x03, 0xae, 0x00, 0x02, 0x66, 0x00, 0x04, 0x58, 0x02, 0xf1,
1380x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x61, 0x63, 1380x01, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x70, 0x72, 0x65, 0x73,
1390x65, 0x8b, 0x04, 0x81, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 1390x73, 0x00, 0x53, 0x70, 0x61, 0x63, 0x65, 0x8b, 0x04, 0x81,
1400x69, 0x63, 0x55, 0x03, 0x08, 0xde, 0x01, 0x09, 0xd6, 0x01, 1400x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x55, 0x03,
1410x00, 0x11, 0x00, 0x06, 0x19, 0x01, 0x05, 0xb9, 0x00, 0x04, 1410x08, 0xde, 0x01, 0x09, 0xd6, 0x01, 0x00, 0x11, 0x00, 0x06,
1420x30, 0x00, 0x00, 0x4c, 0x05, 0x31, 0x66, 0x66, 0x65, 0x4c, 1420x19, 0x01, 0x05, 0xb9, 0x00, 0x04, 0x30, 0x00, 0x00, 0x4c,
1430x01, 0x06, 0x38, 0x00, 0x41, 0x66, 0x72, 0x6f, 0x6d, 0x3a, 1430x05, 0x31, 0x66, 0x66, 0x65, 0x4c, 0x01, 0x06, 0x38, 0x00,
1440x00, 0x30, 0x6f, 0x6e, 0x65, 0x0e, 0x01, 0x20, 0x77, 0x6e, 1440x41, 0x66, 0x72, 0x6f, 0x6d, 0x3a, 0x00, 0x30, 0x6f, 0x6e,
1450x9e, 0x03, 0x05, 0xd0, 0x00, 0x12, 0x2c, 0x07, 0x00, 0x14, 1450x65, 0x0e, 0x01, 0x20, 0x77, 0x6e, 0x9e, 0x03, 0x05, 0xd0,
1460x73, 0x61, 0x02, 0x03, 0x23, 0x00, 0x20, 0x6f, 0x6e, 0xde, 1460x00, 0x12, 0x2c, 0x07, 0x00, 0x14, 0x73, 0x61, 0x02, 0x03,
1470x03, 0x12, 0x66, 0xc0, 0x00, 0x41, 0x75, 0x7a, 0x7a, 0x6c, 1470x23, 0x00, 0x20, 0x6f, 0x6e, 0xde, 0x03, 0x12, 0x66, 0xc0,
1480x94, 0x04, 0x21, 0x73, 0x74, 0x29, 0x05, 0x70, 0x6f, 0x6c, 1480x00, 0x41, 0x75, 0x7a, 0x7a, 0x6c, 0x94, 0x04, 0x21, 0x73,
1490x76, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x02, 0x13, 0x41, 0xe6, 1490x74, 0x29, 0x05, 0x70, 0x6f, 0x6c, 0x76, 0x61, 0x62, 0x6c,
1500x01, 0x13, 0x61, 0x09, 0x03, 0x90, 0x64, 0x65, 0x73, 0x63, 1500x65, 0x54, 0x02, 0x13, 0x41, 0xe6, 0x01, 0x13, 0x61, 0x09,
1510x72, 0x69, 0x62, 0x65, 0x64, 0x91, 0x00, 0x13, 0x73, 0x83, 1510x03, 0x90, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
1520x00, 0x32, 0x32, 0x2e, 0x31, 0x51, 0x07, 0x00, 0xfb, 0x00, 1520x64, 0x91, 0x00, 0x13, 0x73, 0x83, 0x00, 0x32, 0x32, 0x2e,
1530x52, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x3f, 0x00, 0xd5, 0x49, 1530x31, 0x51, 0x07, 0x00, 0xfb, 0x00, 0x52, 0x61, 0x76, 0x61,
1540x6e, 0x00, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 1540x69, 0x6c, 0x3f, 0x00, 0xd5, 0x49, 0x6e, 0x00, 0x70, 0x61,
1550x61, 0x72, 0x1c, 0x03, 0x20, 0x64, 0x6f, 0x16, 0x07, 0x09, 1550x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x1c, 0x03,
1560x0f, 0x05, 0x00, 0x35, 0x02, 0x63, 0x64, 0x69, 0x65, 0x2c, 1560x20, 0x64, 0x6f, 0x16, 0x07, 0x09, 0x0f, 0x05, 0x00, 0x35,
1570x00, 0x79, 0x45, 0x01, 0x04, 0xc4, 0x02, 0x46, 0x55, 0x6e, 1570x02, 0x63, 0x64, 0x69, 0x65, 0x2c, 0x00, 0x79, 0x45, 0x01,
1580x64, 0x6f, 0xc1, 0x02, 0x02, 0x5c, 0x02, 0x90, 0x73, 0x75, 1580x04, 0xc4, 0x02, 0x46, 0x55, 0x6e, 0x64, 0x6f, 0xc1, 0x02,
1590x6d, 0x65, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x1a, 0x02, 0x01, 1590x02, 0x5c, 0x02, 0x90, 0x73, 0x75, 0x6d, 0x65, 0x00, 0x70,
1600xfe, 0x00, 0x52, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x31, 0x00, 1600x6c, 0x61, 0x79, 0x1a, 0x02, 0x01, 0xfe, 0x00, 0x52, 0x62,
1610x01, 0x56, 0x05, 0x01, 0x32, 0x01, 0x39, 0x2e, 0x00, 0x54, 1610x65, 0x66, 0x6f, 0x72, 0x31, 0x00, 0x01, 0x56, 0x05, 0x01,
1620x10, 0x05, 0x00, 0x99, 0x05, 0x64, 0x00, 0x74, 0x72, 0x61, 1620x32, 0x01, 0x39, 0x2e, 0x00, 0x54, 0x10, 0x05, 0x00, 0x99,
1630x63, 0x6b, 0x6b, 0x01, 0x61, 0x6e, 0x75, 0x6d, 0x62, 0x65, 1630x05, 0x64, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x6b, 0x01,
1640x72, 0x0e, 0x00, 0x41, 0x69, 0x6d, 0x65, 0x73, 0x7d, 0x00, 1640x61, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x0e, 0x00, 0x41,
1650x60, 0x68, 0x61, 0x76, 0x65, 0x00, 0x64, 0x4b, 0x01, 0x00, 1650x69, 0x6d, 0x65, 0x73, 0x7d, 0x00, 0x60, 0x68, 0x61, 0x76,
1660x41, 0x02, 0x12, 0x2e, 0x49, 0x04, 0x15, 0x32, 0x49, 0x04, 1660x65, 0x00, 0x64, 0x4b, 0x01, 0x00, 0x41, 0x02, 0x12, 0x2e,
1670xb1, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 1670x49, 0x04, 0x15, 0x32, 0x49, 0x04, 0xb1, 0x70, 0x61, 0x72,
1680x73, 0x20, 0x37, 0x05, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 1680x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x37, 0x05,
1690x00, 0x02, 0x05, 0x01, 0x04, 0x00, 0x01, 0x06, 0x9a, 0x01, 1690x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x05, 0x01,
1700xe8, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 1700x04, 0x00, 0x01, 0x06, 0x9a, 0x01, 0xe8, 0x60, 0x43, 0x75,
1710x2e, 0x27, 0x00, 0x6f, 0x70, 0x3b, 0x02, 0xb0, 0x60, 0x54, 1710x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f,
1720x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x6c, 1720x70, 0x3b, 0x02, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27,
1730x00, 0x82, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 1730x00, 0x6d, 0x65, 0x6e, 0x75, 0x6c, 0x00, 0x82, 0x57, 0x69,
1740x80, 0x04, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0xfe, 0x06, 1740x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x80, 0x04, 0x51, 0x00,
1750x01, 0x80, 0x03, 0x00, 0x71, 0x01, 0x80, 0x71, 0x75, 0x61, 1750x00, 0x53, 0x69, 0x7a, 0xfe, 0x06, 0x01, 0x80, 0x03, 0x00,
1760x72, 0x65, 0x73, 0x2e, 0x00, 1760x71, 0x01, 0x80, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2e,
1770x00,
177}; 178};
178 179
179const unsigned short help_text_len = 2266; 180const unsigned short help_text_len = 2286;
180const unsigned short help_text_words = 430; 181const unsigned short help_text_words = 431;
182const bool help_valid = true;
181const char quick_help_text[] = "Collect all the gems without running into any of the mines."; 183const char quick_help_text[] = "Collect all the gems without running into any of the mines.";
diff --git a/apps/plugins/puzzles/help/keen.c b/apps/plugins/puzzles/help/keen.c
index 58a5f0b2af..0c69aea0d6 100644
--- a/apps/plugins/puzzles/help/keen.c
+++ b/apps/plugins/puzzles/help/keen.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,257 +6,259 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 324, TEXT_CENTER | C_RED }, 9 { 325, TEXT_CENTER | C_RED },
10 { 391, TEXT_UNDERLINE }, 10 { 392, TEXT_UNDERLINE },
11 { 649, TEXT_CENTER | C_RED }, 11 { 650, TEXT_CENTER | C_RED },
12 { 666, TEXT_UNDERLINE }, 12 { 667, TEXT_UNDERLINE },
13 { 698, TEXT_UNDERLINE }, 13 { 699, TEXT_UNDERLINE },
14 { 737, TEXT_UNDERLINE }, 14 { 738, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 3952 comp 2376 ratio 0.601215 level 10 saved 1576 */ 18/* orig 3969 comp 2383 ratio 0.600403 level 10 saved 1586 */
19const char help_text[] = { 19const char help_text[] = {
200xf4, 0x21, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfb, 0x04, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x33, 0x30, 0x3a, 0x20, 0x4b, 0x65, 0x65, 0x6e, 0x20, 0x00, 210x33, 0x30, 0x3a, 0x20, 0x4b, 0x65, 0x65, 0x6e, 0x20, 0x00,
220x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 220x2d, 0x01, 0x00, 0xf4, 0x10, 0x00, 0x00, 0x00, 0x59, 0x6f,
230x00, 0x61, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 230x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x73,
240x67, 0x72, 0x69, 0x64, 0x3b, 0x00, 0x65, 0x61, 0x63, 0x68, 240x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x67, 0x72, 0x69, 0x64,
250x12, 0x00, 0xf1, 0x1a, 0x6d, 0x61, 0x79, 0x00, 0x63, 0x6f, 250x3b, 0x00, 0x65, 0x61, 0x63, 0x68, 0x12, 0x00, 0xf1, 0x1a,
260x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x64, 0x69, 260x6d, 0x61, 0x79, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
270x67, 0x69, 0x74, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x31, 270x6e, 0x00, 0x61, 0x00, 0x64, 0x69, 0x67, 0x69, 0x74, 0x00,
280x00, 0x74, 0x6f, 0x00, 0x74, 0x68, 0x65, 0x00, 0x73, 0x69, 280x66, 0x72, 0x6f, 0x6d, 0x00, 0x31, 0x00, 0x74, 0x6f, 0x00,
290x7a, 0x65, 0x00, 0x6f, 0x66, 0x0c, 0x00, 0x00, 0x40, 0x00, 290x74, 0x68, 0x65, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6f,
300x33, 0x2e, 0x00, 0x54, 0x0a, 0x00, 0xf0, 0x08, 0x00, 0x69, 300x66, 0x0c, 0x00, 0x00, 0x40, 0x00, 0x33, 0x2e, 0x00, 0x54,
310x73, 0x00, 0x64, 0x69, 0x76, 0x69, 0x64, 0x65, 0x64, 0x00, 310x0a, 0x00, 0xf0, 0x08, 0x00, 0x69, 0x73, 0x00, 0x64, 0x69,
320x69, 0x6e, 0x74, 0x6f, 0x00, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 320x76, 0x69, 0x64, 0x65, 0x64, 0x00, 0x69, 0x6e, 0x74, 0x6f,
330x73, 0x2d, 0x00, 0xf1, 0x02, 0x76, 0x61, 0x72, 0x79, 0x69, 330x00, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2d, 0x00, 0xf1,
340x6e, 0x67, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x00, 0x61, 340x02, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x73,
350x6e, 0x64, 0x47, 0x00, 0xf0, 0x13, 0x2c, 0x00, 0x77, 0x69, 350x68, 0x61, 0x70, 0x65, 0x00, 0x61, 0x6e, 0x64, 0x47, 0x00,
360x74, 0x68, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x65, 360xf0, 0x13, 0x2c, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x61,
370x74, 0x69, 0x63, 0x00, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x00, 370x72, 0x69, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x00,
380x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x69, 0x6e, 380x63, 0x6c, 0x75, 0x65, 0x73, 0x00, 0x77, 0x72, 0x69, 0x74,
390x66, 0x00, 0x20, 0x6d, 0x2e, 0xba, 0x00, 0x50, 0x72, 0x00, 390x74, 0x65, 0x6e, 0x00, 0x69, 0x6e, 0x66, 0x00, 0x20, 0x6d,
400x61, 0x69, 0x6d, 0x62, 0x00, 0xf5, 0x02, 0x74, 0x6f, 0x00, 400x2e, 0xba, 0x00, 0x50, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x62,
410x66, 0x75, 0x6c, 0x6c, 0x79, 0x00, 0x70, 0x6f, 0x70, 0x75, 410x00, 0xf5, 0x02, 0x74, 0x6f, 0x00, 0x66, 0x75, 0x6c, 0x6c,
420x6c, 0x61, 0x74, 0x65, 0x8a, 0x00, 0x02, 0x4e, 0x00, 0x01, 420x79, 0x00, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65,
430xb4, 0x00, 0xf0, 0x03, 0x73, 0x00, 0x73, 0x75, 0x63, 0x68, 430x8a, 0x00, 0x02, 0x4e, 0x00, 0x01, 0xb4, 0x00, 0xf0, 0x03,
440x00, 0x74, 0x68, 0x61, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 440x73, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x74, 0x68, 0x61,
450x00, 0x45, 0xe4, 0x00, 0x34, 0x72, 0x6f, 0x77, 0xdd, 0x00, 450x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x45, 0xe4, 0x00,
460xf1, 0x05, 0x73, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 460x34, 0x72, 0x6f, 0x77, 0xdd, 0x00, 0xf1, 0x05, 0x73, 0x00,
470x6e, 0x65, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 470x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 0x65, 0x00, 0x6f,
480x6e, 0x63, 0xd7, 0x00, 0x01, 0x0d, 0x01, 0x02, 0xf8, 0x00, 480x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0xd7, 0x00,
490x05, 0x38, 0x00, 0x6f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 490x01, 0x0d, 0x01, 0x02, 0xf8, 0x00, 0x05, 0x38, 0x00, 0x6f,
500x3b, 0x00, 0x1d, 0x00, 0x17, 0x01, 0x03, 0x8d, 0x00, 0x22, 500x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x3b, 0x00, 0x1d, 0x00,
510x69, 0x6e, 0x1d, 0x00, 0x01, 0x11, 0x01, 0xf1, 0x01, 0x00, 510x17, 0x01, 0x03, 0x8d, 0x00, 0x22, 0x69, 0x6e, 0x1d, 0x00,
520x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x63, 0x6f, 0x6d, 520x01, 0x11, 0x01, 0xf1, 0x01, 0x00, 0x63, 0x61, 0x6e, 0x00,
530x62, 0x69, 0x6e, 0x65, 0x64, 0xd2, 0x00, 0x31, 0x6f, 0x72, 530x62, 0x65, 0x00, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65,
540x6d, 0xc8, 0x00, 0xd3, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 540x64, 0xd2, 0x00, 0x31, 0x6f, 0x72, 0x6d, 0xc8, 0x00, 0xd3,
550x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x01, 0x01, 0x01, 550x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x73, 0x74, 0x61,
560x16, 0x01, 0x40, 0x2c, 0x00, 0x75, 0x73, 0x42, 0x01, 0x00, 560x74, 0x65, 0x64, 0x01, 0x01, 0x01, 0x16, 0x01, 0x40, 0x2c,
570x10, 0x00, 0x07, 0x31, 0x01, 0xf8, 0x00, 0x6f, 0x70, 0x65, 570x00, 0x75, 0x73, 0x42, 0x01, 0x00, 0x10, 0x00, 0x07, 0x31,
580x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x67, 0x69, 0x76, 580x01, 0xf8, 0x00, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
590x65, 0x6e, 0x32, 0x00, 0x00, 0x99, 0x01, 0x52, 0x61, 0x74, 590x6f, 0x6e, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x32, 0x00,
600x00, 0x69, 0x73, 0x02, 0x01, 0x71, 0x41, 0x6e, 0x00, 0x61, 600x00, 0x99, 0x01, 0x52, 0x61, 0x74, 0x00, 0x69, 0x73, 0x02,
610x64, 0x64, 0x69, 0x2c, 0x00, 0x00, 0x1f, 0x00, 0x61, 0x00, 610x01, 0x71, 0x41, 0x6e, 0x00, 0x61, 0x64, 0x64, 0x69, 0x2c,
620x6d, 0x65, 0x61, 0x6e, 0x73, 0x23, 0x01, 0x02, 0xd8, 0x01, 620x00, 0x00, 0x1f, 0x00, 0x61, 0x00, 0x6d, 0x65, 0x61, 0x6e,
630x24, 0x75, 0x6d, 0xd7, 0x01, 0x06, 0xb6, 0x00, 0x00, 0x0e, 630x73, 0x23, 0x01, 0x02, 0xd8, 0x01, 0x24, 0x75, 0x6d, 0xd7,
640x00, 0x02, 0xb5, 0x00, 0x63, 0x6d, 0x75, 0x73, 0x74, 0x00, 640x01, 0x06, 0xb6, 0x00, 0x00, 0x0e, 0x00, 0x02, 0xb5, 0x00,
650x62, 0x6d, 0x01, 0x01, 0x6b, 0x00, 0x02, 0xab, 0x00, 0xf5, 650x63, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x62, 0x6d, 0x01, 0x01,
660x05, 0x2e, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 660x6b, 0x00, 0x02, 0xab, 0x00, 0xf5, 0x05, 0x2e, 0x00, 0x46,
670x6d, 0x70, 0x6c, 0x65, 0x2c, 0x00, 0x60, 0x31, 0x35, 0x2b, 670x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
680x27, 0x5b, 0x00, 0x11, 0x65, 0x34, 0x01, 0x31, 0x65, 0x6e, 680x2c, 0x00, 0x60, 0x31, 0x35, 0x2b, 0x27, 0x5b, 0x00, 0x11,
690x74, 0x05, 0x02, 0x06, 0x4d, 0x00, 0x71, 0x61, 0x64, 0x64, 690x65, 0x34, 0x01, 0x31, 0x65, 0x6e, 0x74, 0x05, 0x02, 0x06,
700x73, 0x00, 0x75, 0x70, 0xfa, 0x00, 0x72, 0x69, 0x66, 0x74, 700x4d, 0x00, 0x71, 0x61, 0x64, 0x64, 0x73, 0x00, 0x75, 0x70,
710x65, 0x65, 0x6e, 0x2e, 0xa4, 0x00, 0xb6, 0x00, 0x6d, 0x75, 710xfa, 0x00, 0x72, 0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x2e,
720x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x63, 0x61, 0xa9, 0x00, 720xa4, 0x00, 0xb6, 0x00, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
730xfc, 0x09, 0x28, 0x65, 0x2e, 0x67, 0x2e, 0x00, 0x60, 0x36, 730x6c, 0x69, 0x63, 0x61, 0xa9, 0x00, 0xfc, 0x09, 0x28, 0x65,
740x30, 0x2a, 0x27, 0x29, 0x2c, 0x00, 0x73, 0x69, 0x6d, 0x69, 740x2e, 0x67, 0x2e, 0x00, 0x60, 0x36, 0x30, 0x2a, 0x27, 0x29,
750x6c, 0x61, 0x72, 0x6c, 0x79, 0x2c, 0xc2, 0x00, 0x7f, 0x70, 750x2c, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c,
760x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0xc6, 0x00, 0x23, 0x02, 760x79, 0x2c, 0xc2, 0x00, 0x7f, 0x70, 0x72, 0x6f, 0x64, 0x75,
770x7f, 0x00, 0x76, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 770x63, 0x74, 0xc6, 0x00, 0x23, 0x02, 0x7f, 0x00, 0x76, 0x73,
780x7c, 0x00, 0xb0, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x61, 0x6c, 780x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x7c, 0x00, 0xb0, 0x77,
790x77, 0x61, 0x79, 0x73, 0x38, 0x00, 0x07, 0x9a, 0x02, 0x13, 790x69, 0x6c, 0x6c, 0x00, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73,
800x61, 0x53, 0x00, 0x22, 0x6f, 0x66, 0x13, 0x03, 0x41, 0x74, 800x38, 0x00, 0x07, 0x9a, 0x02, 0x13, 0x61, 0x53, 0x00, 0x22,
810x77, 0x6f, 0x2c, 0xda, 0x02, 0x28, 0x69, 0x74, 0x99, 0x00, 810x6f, 0x66, 0x13, 0x03, 0x41, 0x74, 0x77, 0x6f, 0x2c, 0xda,
820x01, 0x27, 0x02, 0x0f, 0x91, 0x00, 0x07, 0xa0, 0x69, 0x73, 820x02, 0x28, 0x69, 0x74, 0x99, 0x00, 0x01, 0x27, 0x02, 0x0f,
830x00, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x2f, 0x00, 830x91, 0x00, 0x07, 0xa0, 0x69, 0x73, 0x00, 0x67, 0x72, 0x65,
840x02, 0x1a, 0x00, 0x87, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 840x61, 0x74, 0x65, 0x72, 0x2f, 0x00, 0x02, 0x1a, 0x00, 0x87,
850x62, 0x79, 0xa6, 0x00, 0x6c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 850x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 0x62, 0x79, 0xa6, 0x00,
860x74, 0x6c, 0x01, 0x3f, 0x32, 0x2d, 0x27, 0x6b, 0x00, 0x1b, 860x6c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x6c, 0x01, 0x3f,
870x6b, 0x32, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x6a, 0x00, 0x20, 870x32, 0x2d, 0x27, 0x6b, 0x00, 0x1b, 0x6b, 0x32, 0x00, 0x6d,
880x2c, 0x00, 0x55, 0x00, 0xb6, 0x71, 0x75, 0x69, 0x76, 0x61, 880x6f, 0x72, 0x65, 0x6a, 0x00, 0x20, 0x2c, 0x00, 0x55, 0x00,
890x6c, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x4e, 0x00, 0x02, 0xcd, 890xb6, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74,
900x02, 0x57, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x9a, 0x00, 0x00, 900x6c, 0x79, 0x4e, 0x00, 0x02, 0xcd, 0x02, 0x57, 0x6d, 0x69,
910x1a, 0x00, 0x00, 0x4d, 0x00, 0x02, 0xfa, 0x03, 0x34, 0x74, 910x6e, 0x75, 0x73, 0x9a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x4d,
920x77, 0x6f, 0x6f, 0x00, 0x50, 0x63, 0x6f, 0x75, 0x6c, 0x64, 920x00, 0x02, 0xfa, 0x03, 0x34, 0x74, 0x77, 0x6f, 0x6f, 0x00,
930x27, 0x01, 0x21, 0x65, 0x69, 0x29, 0x00, 0xf4, 0x02, 0x77, 930x50, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x27, 0x01, 0x21, 0x65,
940x61, 0x79, 0x00, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x00, 940x69, 0x29, 0x00, 0xf4, 0x02, 0x77, 0x61, 0x79, 0x00, 0x72,
950x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x67, 0x01, 0x00, 0x26, 950x6f, 0x75, 0x6e, 0x64, 0x2c, 0x00, 0x74, 0x68, 0x6f, 0x75,
960x04, 0x1c, 0x73, 0xe0, 0x01, 0x2b, 0x33, 0x2f, 0xdf, 0x01, 960x67, 0x68, 0x67, 0x01, 0x00, 0x26, 0x04, 0x1c, 0x73, 0xe0,
970x24, 0x69, 0x73, 0x7a, 0x01, 0x0f, 0x6f, 0x01, 0x03, 0x01, 970x01, 0x2b, 0x33, 0x2f, 0xdf, 0x01, 0x24, 0x69, 0x73, 0x7a,
980x6e, 0x01, 0x0b, 0x00, 0x01, 0x02, 0xb2, 0x00, 0x04, 0x86, 980x01, 0x0f, 0x6f, 0x01, 0x03, 0x01, 0x6e, 0x01, 0x0b, 0x00,
990x04, 0x27, 0x62, 0x79, 0xb7, 0x00, 0x20, 0x69, 0x73, 0xe6, 990x01, 0x02, 0xb2, 0x00, 0x04, 0x86, 0x04, 0x27, 0x62, 0x79,
1000x00, 0x24, 0x61, 0x6c, 0xc9, 0x04, 0x0a, 0x5a, 0x01, 0x62, 1000xb7, 0x00, 0x20, 0x69, 0x73, 0xe6, 0x00, 0x24, 0x61, 0x6c,
1010x00, 0x00, 0x4e, 0x6f, 0x74, 0x65, 0x49, 0x00, 0x04, 0x6c, 1010xc9, 0x04, 0x0a, 0x5a, 0x01, 0x62, 0x00, 0x00, 0x4e, 0x6f,
1020x00, 0x08, 0x0d, 0x05, 0x01, 0x23, 0x03, 0x25, 0x61, 0x6d, 1020x74, 0x65, 0x49, 0x00, 0x04, 0x6c, 0x00, 0x08, 0x0d, 0x05,
1030x14, 0x01, 0x05, 0x48, 0x01, 0x10, 0x6f, 0xfe, 0x03, 0x42, 1030x01, 0x23, 0x03, 0x25, 0x61, 0x6d, 0x14, 0x01, 0x05, 0x48,
1040x28, 0x70, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x28, 0x00, 0x90, 1040x01, 0x10, 0x6f, 0xfe, 0x03, 0x42, 0x28, 0x70, 0x72, 0x6f,
1050x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x93, 1050x73, 0x00, 0x00, 0x28, 0x00, 0x90, 0x69, 0x64, 0x65, 0x6e,
1060x00, 0x20, 0x73, 0x00, 0x58, 0x05, 0x48, 0x6e, 0x6f, 0x74, 1060x74, 0x69, 0x63, 0x61, 0x6c, 0x93, 0x00, 0x20, 0x73, 0x00,
1070x00, 0x46, 0x00, 0x00, 0x8b, 0x04, 0x00, 0xbf, 0x00, 0x02, 1070x58, 0x05, 0x48, 0x6e, 0x6f, 0x74, 0x00, 0x46, 0x00, 0x00,
1080x5b, 0x04, 0x10, 0x29, 0x4b, 0x01, 0x61, 0x69, 0x73, 0x00, 1080x8b, 0x04, 0x00, 0xbf, 0x00, 0x02, 0x5b, 0x04, 0x10, 0x29,
1090x72, 0x75, 0x6c, 0x5b, 0x01, 0x83, 0x70, 0x72, 0x65, 0x63, 1090x4b, 0x01, 0x61, 0x69, 0x73, 0x00, 0x72, 0x75, 0x6c, 0x5b,
1100x69, 0x73, 0x65, 0x6c, 0xbf, 0x00, 0x65, 0x70, 0x70, 0x6f, 1100x01, 0x83, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x6c,
1110x73, 0x69, 0x74, 0xdd, 0x01, 0x02, 0x26, 0x00, 0xf2, 0x0e, 1110xbf, 0x00, 0x65, 0x70, 0x70, 0x6f, 0x73, 0x69, 0x74, 0xdd,
1120x6e, 0x00, 0x53, 0x6f, 0x6c, 0x6f, 0x27, 0x73, 0x00, 0x60, 1120x01, 0x02, 0x26, 0x00, 0xf2, 0x0e, 0x6e, 0x00, 0x53, 0x6f,
1130x4b, 0x69, 0x6c, 0x6c, 0x65, 0x72, 0x27, 0x00, 0x6d, 0x6f, 1130x6c, 0x6f, 0x27, 0x73, 0x00, 0x60, 0x4b, 0x69, 0x6c, 0x6c,
1140x64, 0x65, 0x00, 0x28, 0x73, 0x65, 0x65, 0x00, 0x63, 0xfe, 1140x65, 0x72, 0x27, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x28,
1150x05, 0x40, 0x00, 0x31, 0x31, 0x29, 0xda, 0x00, 0x01, 0x5c, 1150x73, 0x65, 0x65, 0x00, 0x63, 0x0f, 0x06, 0x40, 0x00, 0x31,
1160x00, 0xd5, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x61, 1160x31, 0x29, 0xda, 0x00, 0x01, 0x5c, 0x00, 0xd5, 0x70, 0x75,
1170x70, 0x70, 0x65, 0x61, 0x72, 0x1b, 0x02, 0xb2, 0x54, 0x69, 1170x7a, 0x7a, 0x6c, 0x65, 0x00, 0x61, 0x70, 0x70, 0x65, 0x61,
1180x6d, 0x65, 0x73, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x80, 1180x72, 0x1b, 0x02, 0xb2, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x00,
1190x04, 0x00, 0x9d, 0x00, 0x80, 0x60, 0x4b, 0x65, 0x6e, 0x4b, 1190x75, 0x6e, 0x64, 0x65, 0x72, 0x80, 0x04, 0x00, 0x9d, 0x00,
1200x65, 0x6e, 0x27, 0x3c, 0x00, 0x42, 0x33, 0x30, 0x2e, 0x31, 1200x80, 0x60, 0x4b, 0x65, 0x6e, 0x4b, 0x65, 0x6e, 0x27, 0x3c,
1210x42, 0x06, 0x00, 0x0a, 0x01, 0x40, 0x72, 0x6f, 0x6c, 0x73, 1210x00, 0x42, 0x33, 0x30, 0x2e, 0x31, 0x53, 0x06, 0x00, 0x0a,
1220x4b, 0x06, 0x00, 0x11, 0x00, 0x00, 0xcd, 0x05, 0x50, 0x72, 1220x01, 0x80, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00,
1230x65, 0x73, 0x00, 0x6d, 0x6a, 0x05, 0x32, 0x6f, 0x66, 0x00, 1230x11, 0x00, 0x00, 0xcd, 0x05, 0x50, 0x72, 0x65, 0x73, 0x00,
1240x02, 0x02, 0x01, 0x24, 0x00, 0x72, 0x00, 0x73, 0x79, 0x73, 1240x6d, 0x6a, 0x05, 0x32, 0x6f, 0x66, 0x00, 0x02, 0x02, 0x01,
1250x74, 0x65, 0x6d, 0x91, 0x05, 0x00, 0xa7, 0x00, 0x20, 0x00, 1250x24, 0x00, 0x72, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
1260x28, 0xed, 0x00, 0x21, 0x55, 0x6e, 0x81, 0x01, 0x02, 0x94, 1260x91, 0x05, 0x00, 0xa7, 0x00, 0x20, 0x00, 0x28, 0xed, 0x00,
1270x00, 0x61, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x4a, 0x00, 1270x21, 0x55, 0x6e, 0x81, 0x01, 0x02, 0x94, 0x00, 0x61, 0x6f,
1280x01, 0xf6, 0x01, 0x91, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 1280x00, 0x70, 0x6c, 0x61, 0x79, 0x4a, 0x00, 0x01, 0xf6, 0x01,
1290x69, 0x63, 0x6b, 0x88, 0x00, 0x51, 0x6d, 0x6f, 0x75, 0x73, 1290x91, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b,
1300x65, 0xf8, 0x01, 0x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 0x70, 1300x88, 0x00, 0x51, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0xf8, 0x01,
1310x74, 0x79, 0xa2, 0x06, 0x21, 0x61, 0x6e, 0x65, 0x01, 0x65, 1310x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0xa2,
1320x6e, 0x00, 0x74, 0x79, 0x70, 0x65, 0xa4, 0x06, 0x12, 0x6f, 1320x06, 0x21, 0x61, 0x6e, 0x65, 0x01, 0x65, 0x6e, 0x00, 0x74,
1330xcf, 0x00, 0x72, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 1330x79, 0x70, 0x65, 0xa4, 0x06, 0x12, 0x6f, 0xcf, 0x00, 0x72,
1340x54, 0x05, 0x00, 0xbb, 0x03, 0x01, 0xd0, 0x01, 0x02, 0x3a, 1340x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x54, 0x05, 0x00,
1350x00, 0xc0, 0x2e, 0x00, 0x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 1350xbb, 0x03, 0x01, 0xd0, 0x01, 0x02, 0x3a, 0x00, 0xc0, 0x2e,
1360x00, 0x6d, 0x61, 0x6b, 0x39, 0x00, 0x8f, 0x6d, 0x69, 0x73, 1360x00, 0x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6d, 0x61,
1370x74, 0x61, 0x6b, 0x65, 0x2c, 0x76, 0x00, 0x01, 0x01, 0xc6, 1370x6b, 0x39, 0x00, 0x8f, 0x6d, 0x69, 0x73, 0x74, 0x61, 0x6b,
1380x01, 0x88, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 1380x65, 0x2c, 0x76, 0x00, 0x01, 0x01, 0xc6, 0x01, 0x88, 0x6e,
1390x7a, 0x00, 0xb0, 0x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 1390x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x7a, 0x00, 0xb0,
1400x70, 0x61, 0x63, 0x65, 0x64, 0x00, 0x50, 0x63, 0x6c, 0x65, 1400x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x61, 0x63,
1410x61, 0x72, 0xf2, 0x03, 0x20, 0x61, 0x67, 0x25, 0x02, 0x62, 1410x65, 0x64, 0x00, 0x50, 0x63, 0x6c, 0x65, 0x61, 0x72, 0xf2,
1420x28, 0x6f, 0x72, 0x00, 0x75, 0x73, 0x5f, 0x04, 0xc1, 0x55, 1420x03, 0x20, 0x61, 0x67, 0x25, 0x02, 0x62, 0x28, 0x6f, 0x72,
1430x6e, 0x64, 0x6f, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 1430x00, 0x75, 0x73, 0x5f, 0x04, 0xc1, 0x55, 0x6e, 0x64, 0x6f,
1440x65, 0xf2, 0x00, 0x03, 0x7e, 0x00, 0x62, 0x72, 0x69, 0x67, 1440x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0xf2, 0x00,
1450x68, 0x74, 0x2d, 0x74, 0x00, 0x01, 0xd8, 0x02, 0x0f, 0xd8, 1450x03, 0x7e, 0x00, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d,
1460x00, 0x04, 0x02, 0x99, 0x04, 0x12, 0x2c, 0xc2, 0x00, 0x03, 1460x74, 0x00, 0x01, 0xd8, 0x02, 0x0f, 0xd8, 0x00, 0x04, 0x02,
1470x17, 0x06, 0x01, 0x8e, 0x04, 0x00, 0x60, 0x03, 0x46, 0x6e, 1470x99, 0x04, 0x12, 0x2c, 0xc2, 0x00, 0x03, 0x17, 0x06, 0x01,
1480x74, 0x65, 0x72, 0x20, 0x06, 0x04, 0x42, 0x00, 0xf1, 0x02, 1480x8e, 0x04, 0x00, 0x60, 0x03, 0x46, 0x6e, 0x74, 0x65, 0x72,
1490x73, 0x00, 0x61, 0x00, 0x60, 0x70, 0x65, 0x6e, 0x63, 0x69, 1490x20, 0x06, 0x04, 0x42, 0x00, 0xf1, 0x02, 0x73, 0x00, 0x61,
1500x6c, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x27, 0x3a, 0x07, 0x01, 1500x00, 0x60, 0x70, 0x65, 0x6e, 0x63, 0x69, 0x6c, 0x00, 0x6d,
1510x70, 0x06, 0x01, 0xf8, 0x07, 0x07, 0x1b, 0x00, 0x10, 0x73, 1510x61, 0x72, 0x6b, 0x27, 0x3a, 0x07, 0x01, 0x70, 0x06, 0x01,
1520x73, 0x06, 0x04, 0x6e, 0x05, 0x04, 0x77, 0x06, 0x19, 0x73, 1520xf8, 0x07, 0x07, 0x1b, 0x00, 0x10, 0x73, 0x73, 0x06, 0x04,
1530xa3, 0x02, 0x04, 0x36, 0x01, 0x11, 0x53, 0x08, 0x00, 0x14, 1530x6e, 0x05, 0x04, 0x77, 0x06, 0x19, 0x73, 0xa3, 0x02, 0x04,
1540x73, 0x0a, 0x03, 0x00, 0x85, 0x06, 0x10, 0x66, 0x72, 0x02, 1540x36, 0x01, 0x11, 0x53, 0x08, 0x00, 0x14, 0x73, 0x0a, 0x03,
1550x45, 0x64, 0x2d, 0x69, 0x6e, 0x39, 0x00, 0x30, 0x63, 0x61, 1550x00, 0x85, 0x06, 0x10, 0x66, 0x72, 0x02, 0x45, 0x64, 0x2d,
1560x6e, 0xe3, 0x02, 0x45, 0x61, 0x6c, 0x73, 0x6f, 0x33, 0x03, 1560x69, 0x6e, 0x39, 0x00, 0x30, 0x63, 0x61, 0x6e, 0xe3, 0x02,
1570x08, 0x6f, 0x00, 0x02, 0x87, 0x02, 0x30, 0x65, 0x00, 0x67, 1570x45, 0x61, 0x6c, 0x73, 0x6f, 0x33, 0x03, 0x08, 0x6f, 0x00,
1580x5a, 0x00, 0x10, 0x70, 0xd4, 0x03, 0x40, 0x6e, 0x6f, 0x00, 1580x02, 0x87, 0x02, 0x30, 0x65, 0x00, 0x67, 0x5a, 0x00, 0x10,
1590x61, 0x4c, 0x05, 0x01, 0x6c, 0x05, 0x29, 0x74, 0x6f, 0x2e, 1590x70, 0xd4, 0x03, 0x40, 0x6e, 0x6f, 0x00, 0x61, 0x4c, 0x05,
1600x00, 0x40, 0x2c, 0x00, 0x73, 0x6f, 0xe8, 0x04, 0x10, 0x63, 1600x01, 0x6c, 0x05, 0x29, 0x74, 0x6f, 0x2e, 0x00, 0x40, 0x2c,
1610x8e, 0x04, 0x10, 0x77, 0x01, 0x01, 0x00, 0x3a, 0x01, 0x03, 1610x00, 0x73, 0x6f, 0xe8, 0x04, 0x10, 0x63, 0x8e, 0x04, 0x10,
1620x5a, 0x01, 0x11, 0x6d, 0xbb, 0x00, 0x14, 0x69, 0x41, 0x06, 1620x77, 0x01, 0x01, 0x00, 0x3a, 0x01, 0x03, 0x5a, 0x01, 0x11,
1630x63, 0x79, 0x6f, 0x75, 0x3a, 0x00, 0x79, 0xe7, 0x00, 0x05, 1630x6d, 0xbb, 0x00, 0x14, 0x69, 0x41, 0x06, 0x63, 0x79, 0x6f,
1640x23, 0x00, 0x70, 0x61, 0x73, 0x00, 0x72, 0x65, 0x6d, 0x69, 1640x75, 0x3a, 0x00, 0x79, 0xe7, 0x00, 0x05, 0x23, 0x00, 0x70,
1650xd8, 0x02, 0x14, 0x73, 0xd5, 0x03, 0xa4, 0x70, 0x61, 0x72, 1650x61, 0x73, 0x00, 0x72, 0x65, 0x6d, 0x69, 0xd8, 0x02, 0x14,
1660x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x32, 0x01, 0x41, 1660x73, 0xd5, 0x03, 0xa4, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
1670x6e, 0x65, 0x65, 0x64, 0x52, 0x08, 0x60, 0x62, 0x65, 0x00, 1670x75, 0x6c, 0x61, 0x72, 0x32, 0x01, 0x41, 0x6e, 0x65, 0x65,
1680x72, 0x65, 0x2d, 0x5a, 0x05, 0x01, 0x92, 0x07, 0x01, 0xd4, 1680x64, 0x52, 0x08, 0x60, 0x62, 0x65, 0x00, 0x72, 0x65, 0x2d,
1690x03, 0x00, 0x54, 0x00, 0x42, 0x6b, 0x6e, 0x6f, 0x77, 0xec, 1690x5a, 0x05, 0x01, 0x92, 0x07, 0x01, 0xd4, 0x03, 0x00, 0x54,
1700x03, 0x4b, 0x61, 0x62, 0x6f, 0x75, 0x45, 0x00, 0x04, 0xa2, 1700x00, 0x42, 0x6b, 0x6e, 0x6f, 0x77, 0xec, 0x03, 0x4b, 0x61,
1710x01, 0x2f, 0x6f, 0x72, 0x80, 0x00, 0x02, 0x36, 0x6c, 0x69, 1710x62, 0x6f, 0x75, 0x45, 0x00, 0x04, 0xa2, 0x01, 0x2f, 0x6f,
1720x73, 0xf8, 0x06, 0x6a, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 1720x72, 0x80, 0x00, 0x02, 0x36, 0x6c, 0x69, 0x73, 0xf8, 0x06,
1730x6a, 0x01, 0x13, 0x61, 0x83, 0x04, 0x02, 0x8d, 0x00, 0x01, 1730x6a, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6a, 0x01, 0x13,
1740x48, 0x00, 0x50, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x62, 0x01, 1740x61, 0x83, 0x04, 0x02, 0x8d, 0x00, 0x01, 0x48, 0x00, 0x50,
1750x32, 0x65, 0x6c, 0x73, 0x82, 0x00, 0x93, 0x66, 0x65, 0x65, 1750x61, 0x6e, 0x79, 0x74, 0x68, 0x62, 0x01, 0x32, 0x65, 0x6c,
1760x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0x35, 0x03, 0x41, 0x65, 1760x73, 0x82, 0x00, 0x93, 0x66, 0x65, 0x65, 0x6c, 0x00, 0x6c,
1770x72, 0x61, 0x73, 0xc2, 0x09, 0x49, 0x69, 0x6e, 0x67, 0x6c, 1770x69, 0x6b, 0x65, 0x35, 0x03, 0x41, 0x65, 0x72, 0x61, 0x73,
1780xd3, 0x01, 0x1c, 0x2c, 0x5b, 0x02, 0x08, 0x1b, 0x02, 0x22, 1780xc2, 0x09, 0x49, 0x69, 0x6e, 0x67, 0x6c, 0xd3, 0x01, 0x1c,
1790x6e, 0x64, 0x58, 0x02, 0x05, 0xde, 0x01, 0x03, 0x52, 0x02, 1790x2c, 0x5b, 0x02, 0x08, 0x1b, 0x02, 0x22, 0x6e, 0x64, 0x58,
1800x01, 0xb8, 0x02, 0x00, 0x5b, 0x00, 0x3a, 0x41, 0x6c, 0x6c, 1800x02, 0x05, 0xde, 0x01, 0x03, 0x52, 0x02, 0x01, 0xb8, 0x02,
1810x20, 0x02, 0x09, 0x9c, 0x02, 0x22, 0x72, 0x65, 0x79, 0x00, 1810x00, 0x5b, 0x00, 0x3a, 0x41, 0x6c, 0x6c, 0x20, 0x02, 0x09,
1820x30, 0x64, 0x00, 0x77, 0xa3, 0x02, 0x00, 0x99, 0x00, 0x34, 1820x9c, 0x02, 0x22, 0x72, 0x65, 0x79, 0x00, 0x30, 0x64, 0x00,
1830x6c, 0x65, 0x66, 0x6c, 0x00, 0x05, 0x5e, 0x00, 0x18, 0x61, 1830x77, 0xa3, 0x02, 0x00, 0x99, 0x00, 0x34, 0x6c, 0x65, 0x66,
1840x14, 0x01, 0x0f, 0x2a, 0x00, 0x05, 0x02, 0x43, 0x03, 0x10, 1840x6c, 0x00, 0x05, 0x5e, 0x00, 0x18, 0x61, 0x14, 0x01, 0x0f,
1850x73, 0x43, 0x03, 0x36, 0x2e, 0x00, 0x52, 0xb3, 0x00, 0x00, 1850x2a, 0x00, 0x05, 0x02, 0x43, 0x03, 0x10, 0x73, 0x43, 0x03,
1860xf7, 0x00, 0x05, 0x20, 0x00, 0x01, 0x2d, 0x0a, 0x01, 0x66, 1860x36, 0x2e, 0x00, 0x52, 0xb3, 0x00, 0x00, 0xf7, 0x00, 0x05,
1870x03, 0x03, 0x82, 0x07, 0x14, 0x73, 0xfb, 0x00, 0x0c, 0x56, 1870x20, 0x00, 0x01, 0x2d, 0x0a, 0x01, 0x66, 0x03, 0x03, 0x82,
1880x02, 0x12, 0x41, 0xcb, 0x02, 0x00, 0x66, 0x04, 0x12, 0x2c, 1880x07, 0x14, 0x73, 0xfb, 0x00, 0x0c, 0x56, 0x02, 0x12, 0x41,
1890x73, 0x08, 0x50, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x0b, 0x04, 1890xcb, 0x02, 0x00, 0x66, 0x04, 0x12, 0x2c, 0x73, 0x08, 0x50,
1900x14, 0x73, 0x6b, 0x09, 0x22, 0x75, 0x73, 0x2d, 0x03, 0x62, 1900x75, 0x72, 0x73, 0x6f, 0x72, 0x0b, 0x04, 0x14, 0x73, 0x6b,
1910x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, 0xdf, 0x07, 0x01, 0x9c, 1910x09, 0x22, 0x75, 0x73, 0x2d, 0x03, 0x62, 0x63, 0x6f, 0x6e,
1920x04, 0x05, 0x2c, 0x07, 0x02, 0x2f, 0x00, 0x65, 0x74, 0x6f, 1920x6a, 0x75, 0x6e, 0xdf, 0x07, 0x01, 0x9c, 0x04, 0x05, 0x2c,
1930x00, 0x73, 0x65, 0x74, 0xa4, 0x01, 0x2b, 0x6f, 0x72, 0x6e, 1930x07, 0x02, 0x2f, 0x00, 0x65, 0x74, 0x6f, 0x00, 0x73, 0x65,
1940x00, 0x3d, 0x55, 0x73, 0x65, 0x63, 0x00, 0x51, 0x74, 0x6f, 1940x74, 0xa4, 0x01, 0x2b, 0x6f, 0x72, 0x6e, 0x00, 0x3d, 0x55,
1950x00, 0x6d, 0x6f, 0x55, 0x0b, 0x50, 0x68, 0x69, 0x67, 0x68, 1950x73, 0x65, 0x63, 0x00, 0x51, 0x74, 0x6f, 0x00, 0x6d, 0x6f,
1960x6c, 0xd0, 0x00, 0x21, 0x00, 0x61, 0xf6, 0x06, 0x05, 0x99, 1960x55, 0x0b, 0x50, 0x68, 0x69, 0x67, 0x68, 0x6c, 0xd0, 0x00,
1970x0a, 0x02, 0x18, 0x08, 0x09, 0xb0, 0x04, 0x22, 0x74, 0x6f, 1970x21, 0x00, 0x61, 0xf6, 0x06, 0x05, 0x99, 0x0a, 0x02, 0x18,
1980xc5, 0x03, 0x25, 0x00, 0x69, 0x18, 0x06, 0x05, 0x3f, 0x00, 1980x08, 0x09, 0xb0, 0x04, 0x22, 0x74, 0x6f, 0xc5, 0x03, 0x25,
1990x25, 0x65, 0x64, 0x7c, 0x03, 0x14, 0x50, 0x0c, 0x01, 0x50, 1990x00, 0x69, 0x18, 0x06, 0x05, 0x3f, 0x00, 0x25, 0x65, 0x64,
2000x72, 0x65, 0x74, 0x75, 0x72, 0x28, 0x03, 0x5a, 0x67, 0x67, 2000x7c, 0x03, 0x14, 0x50, 0x0c, 0x01, 0x50, 0x72, 0x65, 0x74,
2010x6c, 0x65, 0x73, 0x30, 0x00, 0x02, 0x6d, 0x0b, 0x12, 0x61, 2010x75, 0x72, 0x28, 0x03, 0x5a, 0x67, 0x67, 0x6c, 0x65, 0x73,
2020x00, 0x06, 0x85, 0x69, 0x6e, 0x00, 0x77, 0x68, 0x69, 0x63, 2020x30, 0x00, 0x02, 0x6d, 0x0b, 0x12, 0x61, 0x00, 0x06, 0x85,
2030x68, 0x93, 0x02, 0x02, 0x67, 0x00, 0x20, 0x6f, 0x72, 0x10, 2030x69, 0x6e, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x93, 0x02,
2040x03, 0x2e, 0x6f, 0x76, 0x40, 0x01, 0x05, 0x63, 0x00, 0x12, 2040x02, 0x67, 0x00, 0x20, 0x6f, 0x72, 0x10, 0x03, 0x2e, 0x6f,
2050x4d, 0x6b, 0x01, 0x01, 0x37, 0x05, 0x01, 0xfd, 0x01, 0x00, 2050x76, 0x40, 0x01, 0x05, 0x63, 0x00, 0x12, 0x4d, 0x6b, 0x01,
2060x67, 0x0b, 0x01, 0x10, 0x01, 0x2d, 0x6f, 0x66, 0x1b, 0x02, 2060x01, 0x37, 0x05, 0x01, 0xfd, 0x01, 0x00, 0x67, 0x0b, 0x01,
2070x45, 0x65, 0x76, 0x65, 0x72, 0x93, 0x05, 0x01, 0x60, 0x03, 2070x10, 0x01, 0x2d, 0x6f, 0x66, 0x1b, 0x02, 0x45, 0x65, 0x76,
2080x41, 0x64, 0x6f, 0x65, 0x73, 0xe7, 0x06, 0x03, 0x63, 0x0c, 2080x65, 0x72, 0x93, 0x05, 0x01, 0x60, 0x03, 0x41, 0x64, 0x6f,
2090x10, 0x6d, 0x03, 0x04, 0x02, 0xed, 0x00, 0x41, 0x69, 0x6e, 2090x65, 0x73, 0xe7, 0x06, 0x03, 0x63, 0x0c, 0x10, 0x6d, 0x03,
2100x00, 0x69, 0x63, 0x07, 0x10, 0x28, 0x66, 0x02, 0x01, 0xc2, 2100x04, 0x02, 0xed, 0x00, 0x41, 0x69, 0x6e, 0x00, 0x69, 0x63,
2110x0a, 0x01, 0x84, 0x01, 0x92, 0x73, 0x00, 0x64, 0x65, 0x73, 2110x07, 0x10, 0x28, 0x66, 0x02, 0x01, 0xc2, 0x0a, 0x01, 0x84,
2120x63, 0x72, 0x69, 0x62, 0x9e, 0x01, 0x22, 0x73, 0x65, 0x9a, 2120x01, 0x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
2130x01, 0x31, 0x32, 0x2e, 0x31, 0x72, 0x02, 0x01, 0xf5, 0x01, 2130x62, 0x9e, 0x01, 0x22, 0x73, 0x65, 0x9a, 0x01, 0x31, 0x32,
2140xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 2140x2e, 0x31, 0x72, 0x02, 0x01, 0xf5, 0x01, 0xb2, 0x61, 0x76,
2150x2e, 0x29, 0x8e, 0x06, 0x12, 0x32, 0x8e, 0x06, 0xb2, 0x70, 2150x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x8e,
2160x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 2160x06, 0x12, 0x32, 0x8e, 0x06, 0xb2, 0x70, 0x61, 0x72, 0x61,
2170x5b, 0x04, 0x00, 0x1b, 0x02, 0x05, 0x14, 0x00, 0x02, 0x40, 2170x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x5b, 0x04, 0x00,
2180x00, 0x04, 0x3b, 0x00, 0x02, 0xc2, 0x0c, 0x00, 0x78, 0x00, 2180x1b, 0x02, 0x05, 0x14, 0x00, 0x02, 0x40, 0x00, 0x04, 0x3b,
2190xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 2190x00, 0x02, 0xc2, 0x0c, 0x00, 0x78, 0x00, 0xe1, 0x60, 0x43,
2200x2e, 0x27, 0x00, 0x6f, 0x70, 0x6e, 0x00, 0x03, 0x3a, 0x06, 2200x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00,
2210x50, 0x60, 0x54, 0x79, 0x70, 0x65, 0x4f, 0x09, 0x20, 0x6e, 2210x6f, 0x70, 0x6e, 0x00, 0x03, 0x3a, 0x06, 0x50, 0x60, 0x54,
2220x75, 0xaa, 0x00, 0x51, 0x47, 0x72, 0x69, 0x64, 0x20, 0x69, 2220x79, 0x70, 0x65, 0x4f, 0x09, 0x20, 0x6e, 0x75, 0xaa, 0x00,
2230x08, 0xbf, 0x00, 0x00, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 2230x51, 0x47, 0x72, 0x69, 0x64, 0x20, 0x69, 0x08, 0xbf, 0x00,
2240x69, 0x65, 0x73, 0x00, 0x0d, 0x04, 0xb0, 0x4c, 0x6f, 0x77, 2240x00, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73,
2250x65, 0x72, 0x00, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x97, 0x04, 2250x00, 0x0d, 0x04, 0xb0, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x00,
2260x68, 0x33, 0x3b, 0x00, 0x75, 0x70, 0x70, 0x12, 0x00, 0x74, 2260x6c, 0x69, 0x6d, 0x69, 0x74, 0x97, 0x04, 0x68, 0x33, 0x3b,
2270x39, 0x00, 0x28, 0x62, 0x65, 0x63, 0x61, 0x1a, 0x06, 0x20, 2270x00, 0x75, 0x70, 0x70, 0x12, 0x00, 0x74, 0x39, 0x00, 0x28,
2280x75, 0x73, 0xf8, 0x01, 0x00, 0x97, 0x01, 0x11, 0x66, 0xe1, 2280x62, 0x65, 0x63, 0x61, 0x1a, 0x06, 0x20, 0x75, 0x73, 0xf8,
2290x02, 0x03, 0x36, 0x09, 0x42, 0x63, 0x6f, 0x6d, 0x65, 0x6c, 2290x01, 0x00, 0x97, 0x01, 0x11, 0x66, 0xe1, 0x02, 0x03, 0x36,
2300x04, 0x40, 0x64, 0x69, 0x66, 0x66, 0x64, 0x04, 0x12, 0x74, 2300x09, 0x42, 0x63, 0x6f, 0x6d, 0x65, 0x6c, 0x04, 0x40, 0x64,
2310xa5, 0x02, 0x12, 0x60, 0x5f, 0x09, 0x64, 0x27, 0x00, 0x62, 2310x69, 0x66, 0x66, 0x64, 0x04, 0x12, 0x74, 0xa5, 0x02, 0x12,
2320x69, 0x67, 0x67, 0x29, 0x0a, 0x21, 0x39, 0x21, 0x55, 0x06, 2320x60, 0x5f, 0x09, 0x64, 0x27, 0x00, 0x62, 0x69, 0x67, 0x67,
2330x14, 0x44, 0x2b, 0x00, 0x53, 0x79, 0x00, 0x00, 0x00, 0x43, 2330x29, 0x0a, 0x21, 0x39, 0x21, 0x55, 0x06, 0x14, 0x44, 0x2b,
2340xa2, 0x07, 0x03, 0xd7, 0x02, 0x05, 0x1a, 0x00, 0x04, 0xae, 2340x00, 0x53, 0x79, 0x00, 0x00, 0x00, 0x43, 0xa2, 0x07, 0x03,
2350x00, 0x41, 0x65, 0x6e, 0x65, 0x72, 0x58, 0x0c, 0x02, 0x0c, 2350xd7, 0x02, 0x05, 0x1a, 0x00, 0x04, 0xae, 0x00, 0x41, 0x65,
2360x08, 0xd1, 0x2e, 0x00, 0x41, 0x74, 0x00, 0x55, 0x6e, 0x72, 2360x6e, 0x65, 0x72, 0x58, 0x0c, 0x02, 0x0c, 0x08, 0xd1, 0x2e,
2370x65, 0x61, 0x73, 0x6f, 0x6e, 0x28, 0x01, 0x50, 0x6c, 0x65, 2370x00, 0x41, 0x74, 0x00, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x73,
2380x76, 0x65, 0x6c, 0x7d, 0x05, 0x70, 0x6d, 0x65, 0x00, 0x62, 2380x6f, 0x6e, 0x28, 0x01, 0x50, 0x6c, 0x65, 0x76, 0x65, 0x6c,
2390x61, 0x63, 0x6b, 0x0c, 0x0b, 0x01, 0x98, 0x03, 0x04, 0x79, 2390x7d, 0x05, 0x70, 0x6d, 0x65, 0x00, 0x62, 0x61, 0x63, 0x6b,
2400x06, 0x10, 0x72, 0x28, 0x0a, 0x73, 0x72, 0x65, 0x64, 0x2c, 2400x0c, 0x0b, 0x01, 0x98, 0x03, 0x04, 0x79, 0x06, 0x10, 0x72,
2410x00, 0x62, 0x75, 0x37, 0x0c, 0x31, 0x6f, 0x6c, 0x75, 0x48, 2410x28, 0x0a, 0x73, 0x72, 0x65, 0x64, 0x2c, 0x00, 0x62, 0x75,
2420x01, 0x21, 0x73, 0x68, 0xc8, 0x00, 0x23, 0x73, 0x74, 0x2b, 2420x37, 0x0c, 0x31, 0x6f, 0x6c, 0x75, 0x48, 0x01, 0x21, 0x73,
2430x00, 0x42, 0x75, 0x6e, 0x69, 0x71, 0x85, 0x0c, 0x10, 0x65, 2430x68, 0xc8, 0x00, 0x23, 0x73, 0x74, 0x2b, 0x00, 0x42, 0x75,
2440x7b, 0x02, 0x03, 0x3a, 0x06, 0x01, 0x62, 0x00, 0x14, 0x73, 2440x6e, 0x69, 0x71, 0x85, 0x0c, 0x10, 0x65, 0x7b, 0x02, 0x03,
2450x48, 0x00, 0x00, 0x64, 0x07, 0x00, 0x7f, 0x00, 0x00, 0xcc, 2450x3a, 0x06, 0x01, 0x62, 0x00, 0x14, 0x73, 0x48, 0x00, 0x00,
2460x04, 0x00, 0x6b, 0x09, 0x00, 0xd8, 0x0a, 0x22, 0x78, 0x00, 2460x64, 0x07, 0x00, 0x7f, 0x00, 0x00, 0xcc, 0x04, 0x00, 0x6b,
2470x91, 0x00, 0x01, 0xed, 0x0c, 0x00, 0x00, 0x02, 0x30, 0x6f, 2470x09, 0x00, 0xd8, 0x0a, 0x22, 0x78, 0x00, 0x91, 0x00, 0x01,
2480x69, 0x64, 0x54, 0x02, 0x03, 0x10, 0x00, 0x05, 0x97, 0x00, 2480xed, 0x0c, 0x00, 0x00, 0x02, 0x30, 0x6f, 0x69, 0x64, 0x54,
2490x00, 0xfe, 0x00, 0x19, 0x4d, 0x2e, 0x0c, 0x11, 0x20, 0x98, 2490x02, 0x03, 0x10, 0x00, 0x05, 0x97, 0x00, 0x00, 0xfe, 0x00,
2500x0d, 0x01, 0x69, 0x07, 0x10, 0x74, 0xf2, 0x08, 0x00, 0xf1, 2500x19, 0x4d, 0x2e, 0x0c, 0x11, 0x20, 0x98, 0x0d, 0x01, 0x69,
2510x09, 0x01, 0xd8, 0x00, 0x00, 0x81, 0x03, 0x00, 0x95, 0x00, 2510x07, 0x10, 0x74, 0xf2, 0x08, 0x00, 0xf1, 0x09, 0x01, 0xd8,
2520x45, 0x6f, 0x78, 0x65, 0x73, 0xcb, 0x00, 0x0b, 0x6a, 0x0c, 2520x00, 0x00, 0x81, 0x03, 0x00, 0x95, 0x00, 0x45, 0x6f, 0x78,
2530x01, 0x1d, 0x00, 0x32, 0x2e, 0x00, 0x57, 0x16, 0x04, 0x03, 2530x65, 0x73, 0xcb, 0x00, 0x0b, 0x6a, 0x0c, 0x01, 0x1d, 0x00,
2540x8c, 0x09, 0x02, 0x51, 0x04, 0x03, 0x3a, 0x09, 0x21, 0x69, 2540x32, 0x2e, 0x00, 0x57, 0x16, 0x04, 0x03, 0x8c, 0x09, 0x02,
2550x73, 0x0f, 0x06, 0x10, 0x6e, 0xdc, 0x05, 0xf0, 0x02, 0x60, 2550x51, 0x04, 0x03, 0x3a, 0x09, 0x21, 0x69, 0x73, 0x0f, 0x06,
2560x49, 0x6e, 0x73, 0x68, 0x69, 0x00, 0x4e, 0x6f, 0x00, 0x48, 2560x10, 0x6e, 0xdc, 0x05, 0xf0, 0x02, 0x60, 0x49, 0x6e, 0x73,
2570x65, 0x79, 0x61, 0x27, 0x2e, 0x00, 2570x68, 0x69, 0x00, 0x4e, 0x6f, 0x00, 0x48, 0x65, 0x79, 0x61,
2580x27, 0x2e, 0x00,
258}; 259};
259 260
260const unsigned short help_text_len = 3952; 261const unsigned short help_text_len = 3969;
261const unsigned short help_text_words = 761; 262const unsigned short help_text_words = 762;
263const bool help_valid = true;
262const char quick_help_text[] = "Complete the latin square in accordance with the arithmetic clues."; 264const char quick_help_text[] = "Complete the latin square in accordance with the arithmetic clues.";
diff --git a/apps/plugins/puzzles/help/lightup.c b/apps/plugins/puzzles/help/lightup.c
index 5562b954b7..c109773bf6 100644
--- a/apps/plugins/puzzles/help/lightup.c
+++ b/apps/plugins/puzzles/help/lightup.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,176 +6,190 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 169, TEXT_CENTER | C_RED }, 9 { 167, TEXT_CENTER | C_RED },
10 { 304, TEXT_CENTER | C_RED }, 10 { 302, TEXT_CENTER | C_RED },
11 { 321, TEXT_UNDERLINE }, 11 { 319, TEXT_UNDERLINE },
12 { 322, TEXT_UNDERLINE }, 12 { 320, TEXT_UNDERLINE },
13 { 332, TEXT_UNDERLINE }, 13 { 330, TEXT_UNDERLINE },
14 { 380, TEXT_UNDERLINE }, 14 { 378, TEXT_UNDERLINE },
15 { 409, TEXT_UNDERLINE }, 15 { 407, TEXT_UNDERLINE },
16 { 433, TEXT_CENTER | C_RED },
16 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
17}; 18};
18 19
19/* orig 2327 comp 1558 ratio 0.669532 level 10 saved 769 */ 20/* orig 2549 comp 1677 ratio 0.657905 level 10 saved 872 */
20const char help_text[] = { 21const char help_text[] = {
210xf0, 0x40, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
220x32, 0x31, 0x3a, 0x20, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x20, 230x32, 0x31, 0x3a, 0x20, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x20,
230x55, 0x70, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 240x55, 0x70, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf0, 0x2b,
240x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 250x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76,
250x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 260x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f,
260x65, 0x73, 0x2e, 0x00, 0x53, 0x6f, 0x6d, 0x65, 0x00, 0x61, 270x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2e,
270x72, 0x65, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 280x00, 0x53, 0x6f, 0x6d, 0x65, 0x00, 0x61, 0x72, 0x65, 0x00,
280x69, 0x6e, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x3b, 0x00, 290x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x69, 0x6e, 0x00,
290x73, 0x1a, 0x00, 0x62, 0x6f, 0x66, 0x00, 0x74, 0x68, 0x65, 300x62, 0x6c, 0x61, 0x63, 0x6b, 0x3b, 0x00, 0x73, 0x1a, 0x00,
300x13, 0x00, 0x04, 0x35, 0x00, 0x01, 0x2f, 0x00, 0x90, 0x6e, 310x62, 0x6f, 0x66, 0x00, 0x74, 0x68, 0x65, 0x13, 0x00, 0x04,
310x75, 0x6d, 0x62, 0x65, 0x72, 0x65, 0x64, 0x2e, 0x5e, 0x00, 320x35, 0x00, 0x01, 0x2f, 0x00, 0x90, 0x6e, 0x75, 0x6d, 0x62,
320xe0, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 330x65, 0x72, 0x65, 0x64, 0x2e, 0x5e, 0x00, 0xe0, 0x72, 0x00,
330x74, 0x6f, 0x00, 0x60, 0x6c, 0x7a, 0x00, 0x81, 0x00, 0x75, 340x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00,
340x70, 0x27, 0x00, 0x61, 0x6c, 0x6c, 0x3e, 0x00, 0x55, 0x65, 350x60, 0x6c, 0x8f, 0x00, 0x81, 0x00, 0x75, 0x70, 0x27, 0x00,
350x6d, 0x70, 0x74, 0x79, 0x3e, 0x00, 0xb2, 0x62, 0x79, 0x00, 360x61, 0x6c, 0x6c, 0x3e, 0x00, 0x55, 0x65, 0x6d, 0x70, 0x74,
360x70, 0x6c, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x00, 0x2b, 0x00, 370x79, 0x3e, 0x00, 0xb2, 0x62, 0x79, 0x00, 0x70, 0x6c, 0x61,
370x50, 0x62, 0x75, 0x6c, 0x62, 0x73, 0x79, 0x00, 0x07, 0x72, 380x63, 0x69, 0x6e, 0x67, 0x00, 0x2b, 0x00, 0x50, 0x62, 0x75,
380x00, 0x97, 0x6d, 0x2e, 0x00, 0x00, 0x00, 0x45, 0x61, 0x63, 390x6c, 0x62, 0x73, 0x79, 0x00, 0x07, 0x72, 0x00, 0x97, 0x6d,
390x68, 0x24, 0x00, 0xc1, 0x00, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 400x2e, 0x00, 0x00, 0x00, 0x45, 0x61, 0x63, 0x68, 0x24, 0x00,
400x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x58, 0x00, 0x02, 0x52, 410xc1, 0x00, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x69, 0x6e, 0x61,
410x00, 0x30, 0x00, 0x69, 0x74, 0x7b, 0x00, 0x81, 0x6f, 0x6e, 420x74, 0x65, 0x73, 0x58, 0x00, 0x02, 0x52, 0x00, 0x30, 0x00,
420x2c, 0x00, 0x70, 0x6c, 0x75, 0x73, 0x76, 0x00, 0x04, 0x6c, 430x69, 0x74, 0x7b, 0x00, 0x81, 0x6f, 0x6e, 0x2c, 0x00, 0x70,
430x00, 0xc0, 0x69, 0x6e, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x00, 440x6c, 0x75, 0x73, 0x76, 0x00, 0x04, 0x6c, 0x00, 0xc0, 0x69,
440x77, 0x69, 0x74, 0x68, 0x28, 0x00, 0xf1, 0x07, 0x68, 0x6f, 450x6e, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x77, 0x69, 0x74,
450x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x6c, 0x79, 460x68, 0x28, 0x00, 0xf1, 0x07, 0x68, 0x6f, 0x72, 0x69, 0x7a,
460x00, 0x6f, 0x72, 0x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 470x6f, 0x6e, 0x74, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72,
470x0e, 0x00, 0x89, 0x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x00, 480x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00, 0x89,
480x61, 0xec, 0x00, 0x00, 0x59, 0x00, 0x50, 0x62, 0x6c, 0x6f, 490x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x00, 0x61, 0xec, 0x00,
490x63, 0x6b, 0xae, 0x00, 0x00, 0x73, 0x00, 0x30, 0x77, 0x61, 500x00, 0x59, 0x00, 0x50, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0xae,
500x79, 0x9a, 0x00, 0x61, 0x54, 0x6f, 0x00, 0x77, 0x69, 0x6e, 510x00, 0x00, 0x73, 0x00, 0x30, 0x77, 0x61, 0x79, 0x9a, 0x00,
510x12, 0x00, 0xf1, 0x07, 0x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 520x61, 0x54, 0x6f, 0x00, 0x77, 0x69, 0x6e, 0x12, 0x00, 0xf1,
520x79, 0x6f, 0x75, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x73, 530x07, 0x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 0x79, 0x6f, 0x75,
530x61, 0x74, 0x69, 0x73, 0x66, 0x79, 0x1b, 0x00, 0x60, 0x66, 540x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x73, 0x61, 0x74, 0x69,
540x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x3b, 0x00, 0xfe, 0x09, 0x63, 550x73, 0x66, 0x79, 0x1b, 0x00, 0x60, 0x66, 0x6f, 0x6c, 0x6c,
550x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 560x6f, 0x77, 0x3b, 0x00, 0xfe, 0x09, 0x63, 0x6f, 0x6e, 0x64,
560x00, 0x00, 0x00, 0x2d, 0x00, 0x41, 0x6c, 0x6c, 0x00, 0x6e, 570x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x00, 0x00, 0x00,
570x6f, 0x6e, 0x2d, 0x58, 0x01, 0x41, 0x6c, 0x69, 0x74, 0x2e, 580x2d, 0x00, 0x41, 0x6c, 0x6c, 0x00, 0x6e, 0x6f, 0x6e, 0x2d,
580x23, 0x00, 0x23, 0x4e, 0x6f, 0xfb, 0x00, 0x20, 0x69, 0x73, 590x58, 0x01, 0x41, 0x6c, 0x69, 0x74, 0x2e, 0x23, 0x00, 0x23,
590x15, 0x00, 0x00, 0x37, 0x01, 0x72, 0x61, 0x6e, 0x6f, 0x74, 600x4e, 0x6f, 0xfb, 0x00, 0x20, 0x69, 0x73, 0x15, 0x00, 0x00,
600x68, 0x65, 0x72, 0x18, 0x00, 0x16, 0x2e, 0x49, 0x00, 0x03, 610x37, 0x01, 0x72, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72,
610x8b, 0x01, 0x0b, 0xa6, 0x01, 0x01, 0xf2, 0x01, 0x60, 0x65, 620x18, 0x00, 0x16, 0x2e, 0x49, 0x00, 0x03, 0x8b, 0x01, 0x0b,
620x78, 0x61, 0x63, 0x74, 0x6c, 0x8f, 0x00, 0x23, 0x61, 0x74, 630xa6, 0x01, 0x01, 0xf2, 0x01, 0x60, 0x65, 0x78, 0x61, 0x63,
630x29, 0x00, 0x00, 0x62, 0x01, 0x01, 0x42, 0x00, 0xa0, 0x73, 640x74, 0x6c, 0x8f, 0x00, 0x23, 0x61, 0x74, 0x29, 0x00, 0x00,
640x00, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0xb8, 650x62, 0x01, 0x01, 0x42, 0x00, 0xa0, 0x73, 0x00, 0x61, 0x64,
650x01, 0x00, 0x75, 0x01, 0x23, 0x00, 0x28, 0xd5, 0x00, 0x10, 660x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0xb8, 0x01, 0x00, 0x75,
660x66, 0xd4, 0x01, 0x05, 0x97, 0x00, 0xf3, 0x01, 0x62, 0x6f, 670x01, 0x23, 0x00, 0x28, 0xd5, 0x00, 0x10, 0x66, 0xd4, 0x01,
670x76, 0x65, 0x2c, 0x00, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x2c, 680x05, 0x97, 0x00, 0xf3, 0x01, 0x62, 0x6f, 0x76, 0x65, 0x2c,
680x00, 0x61, 0x6e, 0x64, 0x2f, 0x00, 0x60, 0x00, 0x73, 0x69, 690x00, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x2c, 0x00, 0x61, 0x6e,
690x64, 0x65, 0x29, 0x88, 0x00, 0x4f, 0x4e, 0x6f, 0x6e, 0x2d, 700x64, 0x2f, 0x00, 0x60, 0x00, 0x73, 0x69, 0x64, 0x65, 0x29,
700x86, 0x00, 0x04, 0x33, 0x6d, 0x61, 0x79, 0x7c, 0x02, 0x2f, 710x88, 0x00, 0x4f, 0x4e, 0x6f, 0x6e, 0x2d, 0x86, 0x00, 0x04,
710x6e, 0x79, 0x81, 0x00, 0x0f, 0x00, 0x4d, 0x00, 0xf1, 0x0b, 720x33, 0x6d, 0x61, 0x79, 0x7c, 0x02, 0x2f, 0x6e, 0x79, 0x81,
720x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 0x72, 730x00, 0x0f, 0x00, 0x4d, 0x00, 0xf1, 0x0b, 0x43, 0x72, 0x65,
730x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 740x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x74, 0x68,
740x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0x60, 0x02, 0xa0, 0x4e, 750x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00,
750x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x39, 0x5d, 0x2d, 760x67, 0x6f, 0x65, 0x60, 0x02, 0xa0, 0x4e, 0x69, 0x6b, 0x6f,
760x00, 0x11, 0x4c, 0x23, 0x01, 0x60, 0x55, 0x70, 0x00, 0x77, 770x6c, 0x69, 0x00, 0x5b, 0x39, 0x5d, 0x2d, 0x00, 0x11, 0x4c,
770x61, 0x73, 0x66, 0x01, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 780x23, 0x01, 0x60, 0x55, 0x70, 0x00, 0x77, 0x61, 0x73, 0x66,
780x74, 0x65, 0xa3, 0x00, 0x10, 0x69, 0x14, 0x00, 0x40, 0x6c, 790x01, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0xa3,
790x6c, 0x65, 0x63, 0x7b, 0x01, 0x00, 0x42, 0x01, 0xb1, 0x4a, 800x00, 0x10, 0x69, 0x14, 0x00, 0x40, 0x6c, 0x6c, 0x65, 0x63,
800x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 810x7b, 0x01, 0x00, 0x42, 0x01, 0xb1, 0x4a, 0x61, 0x6d, 0x65,
810xc8, 0x01, 0xf1, 0x01, 0x5b, 0x39, 0x5d, 0x00, 0x68, 0x74, 820x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0xc8, 0x01, 0xf1,
820x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 830x02, 0x5b, 0x39, 0x5d, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73,
830x5c, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 840x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x5d, 0x00,
840x65, 0x6e, 0x2f, 0x7b, 0x00, 0xf1, 0x04, 0x73, 0x2f, 0x61, 850xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e,
850x6b, 0x61, 0x72, 0x69, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 860x2f, 0x7c, 0x00, 0xf6, 0x00, 0x73, 0x2f, 0x61, 0x6b, 0x61,
860x28, 0x62, 0x65, 0x77, 0x61, 0x72, 0xa7, 0x02, 0xd6, 0x46, 870x72, 0x69, 0x2f, 0x00, 0x00, 0x00, 0x32, 0x31, 0x2e, 0x31,
870x6c, 0x61, 0x73, 0x68, 0x29, 0x00, 0x00, 0x00, 0x32, 0x31, 880x71, 0x03, 0x01, 0x71, 0x00, 0x40, 0x6f, 0x6c, 0x73, 0x20,
880x2e, 0x31, 0x71, 0x03, 0x01, 0x86, 0x00, 0x40, 0x6f, 0x6c, 890x8a, 0x00, 0x72, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69,
890x73, 0x20, 0x9f, 0x00, 0x72, 0x65, 0x66, 0x74, 0x2d, 0x63, 900x2c, 0x02, 0x4d, 0x69, 0x6e, 0x00, 0x61, 0xe2, 0x01, 0x30,
900x6c, 0x69, 0x41, 0x02, 0x4d, 0x69, 0x6e, 0x00, 0x61, 0xf7, 910x00, 0x77, 0x69, 0x12, 0x03, 0x51, 0x6f, 0x67, 0x67, 0x6c,
910x01, 0x30, 0x00, 0x77, 0x69, 0x27, 0x03, 0x51, 0x6f, 0x67, 920x65, 0x41, 0x01, 0x71, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
920x67, 0x6c, 0x65, 0x56, 0x01, 0x71, 0x70, 0x72, 0x65, 0x73, 930x63, 0xf5, 0x02, 0x14, 0x61, 0xef, 0x01, 0x12, 0x6e, 0xad,
930x65, 0x6e, 0x63, 0x63, 0x00, 0x14, 0x61, 0x04, 0x02, 0x12, 940x01, 0x02, 0x33, 0x00, 0x30, 0x2e, 0x00, 0x52, 0x16, 0x00,
940x6e, 0xc2, 0x01, 0x02, 0x33, 0x00, 0x30, 0x2e, 0x00, 0x52, 950x0f, 0x59, 0x00, 0x0d, 0x02, 0x54, 0x00, 0x00, 0xbe, 0x02,
950x16, 0x00, 0x0f, 0x59, 0x00, 0x0d, 0x02, 0x54, 0x00, 0x00, 960x40, 0x6d, 0x61, 0x72, 0x6b, 0x5c, 0x00, 0x01, 0x15, 0x00,
960xd3, 0x02, 0x40, 0x6d, 0x61, 0x72, 0x6b, 0x5c, 0x00, 0x01, 970xd0, 0x00, 0x61, 0x69, 0x64, 0x00, 0x73, 0x6f, 0x6c, 0x76,
970x15, 0x00, 0xd0, 0x00, 0x61, 0x69, 0x64, 0x00, 0x73, 0x6f, 980x69, 0x6e, 0x67, 0x3b, 0x00, 0x03, 0x92, 0x63, 0x61, 0x6e,
980x6c, 0x76, 0x69, 0x6e, 0x67, 0x3b, 0x15, 0x03, 0x92, 0x63, 990x00, 0x62, 0x65, 0x00, 0x75, 0x73, 0x22, 0x01, 0x42, 0x68,
990x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x75, 0x73, 0x37, 0x01, 1000x69, 0x67, 0x68, 0x76, 0x00, 0x04, 0xaf, 0x01, 0x01, 0x7b,
1000x42, 0x68, 0x69, 0x67, 0x68, 0x76, 0x00, 0x04, 0xc4, 0x01, 1010x00, 0x81, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x00, 0x62,
1010x01, 0x7b, 0x00, 0x81, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 1020x8e, 0x02, 0x11, 0x2c, 0x8a, 0x01, 0x83, 0x65, 0x78, 0x61,
1020x00, 0x62, 0xa3, 0x02, 0x11, 0x2c, 0x9f, 0x01, 0x83, 0x65, 1030x6d, 0x70, 0x6c, 0x65, 0x2e, 0x4e, 0x04, 0x00, 0xd6, 0x01,
1030x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x04, 0x00, 1040x00, 0x23, 0x00, 0x00, 0xc9, 0x03, 0x18, 0x65, 0xbb, 0x00,
1040xeb, 0x01, 0x00, 0x23, 0x00, 0x00, 0xde, 0x03, 0x18, 0x65, 1050x02, 0x81, 0x00, 0x23, 0x65, 0x64, 0x51, 0x00, 0x55, 0x2c,
1050xbb, 0x00, 0x02, 0x81, 0x00, 0x23, 0x65, 0x64, 0x51, 0x00, 1060x00, 0x6e, 0x6f, 0x72, 0x26, 0x00, 0x01, 0x9c, 0x00, 0x21,
1060x55, 0x2c, 0x00, 0x6e, 0x6f, 0x72, 0x26, 0x00, 0x01, 0x9c, 1070x69, 0x6e, 0x30, 0x00, 0x06, 0xe1, 0x00, 0x33, 0x00, 0x00,
1070x00, 0x21, 0x69, 0x6e, 0x30, 0x00, 0x06, 0xe1, 0x00, 0x33, 1080x54, 0x47, 0x03, 0x02, 0x20, 0x01, 0x06, 0x95, 0x00, 0xd1,
1080x00, 0x00, 0x54, 0x5c, 0x03, 0x02, 0x20, 0x01, 0x06, 0x95, 1090x6f, 0x62, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x65, 0x72,
1090x00, 0xd1, 0x6f, 0x62, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x00, 1100x72, 0x6f, 0x72, 0xcd, 0x03, 0x01, 0x71, 0x04, 0x01, 0x7f,
1100x65, 0x72, 0x72, 0x6f, 0x72, 0xe2, 0x03, 0x01, 0x86, 0x04, 1110x01, 0x05, 0x15, 0x03, 0x07, 0x13, 0x03, 0x02, 0x42, 0x03,
1110x01, 0x7f, 0x01, 0x05, 0x2a, 0x03, 0x07, 0x28, 0x03, 0x02, 1120x05, 0x40, 0x00, 0x02, 0xce, 0x04, 0x01, 0x06, 0x02, 0x6a,
1120x57, 0x03, 0x05, 0x40, 0x00, 0x02, 0xe3, 0x04, 0x01, 0x1b, 1130x77, 0x61, 0x79, 0x2c, 0x00, 0x61, 0xba, 0x04, 0x05, 0xf4,
1130x02, 0x6a, 0x77, 0x61, 0x79, 0x2c, 0x00, 0x61, 0xcf, 0x04, 1140x00, 0x81, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x64, 0x6f,
1140x05, 0xf4, 0x00, 0x81, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 1150xd2, 0x00, 0x33, 0x28, 0x6f, 0x72, 0x00, 0x01, 0x12, 0x29,
1150x64, 0x6f, 0xd2, 0x00, 0x33, 0x28, 0x6f, 0x72, 0x00, 0x01, 1160xb8, 0x02, 0x00, 0xa8, 0x01, 0x11, 0x72, 0x8f, 0x00, 0x0d,
1160x12, 0x29, 0xcd, 0x02, 0x00, 0xa8, 0x01, 0x11, 0x72, 0x8f, 1170xbe, 0x02, 0x39, 0x6e, 0x65, 0x78, 0xba, 0x02, 0x52, 0x54,
1170x00, 0x0d, 0xd3, 0x02, 0x39, 0x6e, 0x65, 0x78, 0xcf, 0x02, 1180x68, 0x75, 0x73, 0x2c, 0x15, 0x04, 0x00, 0x67, 0x05, 0x21,
1180x52, 0x54, 0x68, 0x75, 0x73, 0x2c, 0x2a, 0x04, 0x00, 0x7c, 1190x69, 0x73, 0x7c, 0x01, 0x9e, 0x65, 0x64, 0x00, 0x77, 0x68,
1190x05, 0x21, 0x69, 0x73, 0x7c, 0x01, 0x9e, 0x65, 0x64, 0x00, 1200x65, 0x6e, 0x00, 0x61, 0xf4, 0x03, 0x04, 0xa6, 0x03, 0x20,
1200x77, 0x68, 0x65, 0x6e, 0x00, 0x61, 0x09, 0x04, 0x04, 0xbb, 1210x79, 0x65, 0x29, 0x04, 0x06, 0xba, 0x00, 0x12, 0x73, 0x66,
1210x03, 0x20, 0x79, 0x65, 0x3e, 0x04, 0x06, 0xba, 0x00, 0x12, 1220x03, 0x01, 0xc6, 0x01, 0x01, 0xb3, 0x00, 0x10, 0x6f, 0xfa,
1220x73, 0x7b, 0x03, 0x01, 0xc6, 0x01, 0x01, 0xb3, 0x00, 0x10, 1230x00, 0x03, 0x77, 0x00, 0x00, 0x6a, 0x00, 0x23, 0x28, 0x41,
1230x6f, 0xfa, 0x00, 0x03, 0x77, 0x00, 0x00, 0x6a, 0x00, 0x23, 1240x5b, 0x05, 0x21, 0x61, 0x63, 0x56, 0x04, 0x82, 0x00, 0x64,
1240x28, 0x41, 0x70, 0x05, 0x21, 0x61, 0x63, 0x6b, 0x04, 0x82, 1250x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0xf3, 0x00, 0x13, 0x73,
1250x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0xf3, 0x00, 1260xf1, 0x02, 0x31, 0x32, 0x2e, 0x31, 0x3f, 0x00, 0xf2, 0x01,
1260x13, 0x73, 0x06, 0x03, 0x31, 0x32, 0x2e, 0x31, 0x3f, 0x00, 1270x61, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c,
1270xf3, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 1280x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0xc8, 0x02, 0x16, 0x32,
1280x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0xc8, 0x02, 0x16, 1290xc8, 0x02, 0xb2, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1290x32, 0xc8, 0x02, 0xb2, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 1300x65, 0x72, 0x73, 0x20, 0x8f, 0x01, 0x36, 0x73, 0x65, 0x00,
1300x74, 0x65, 0x72, 0x73, 0x20, 0x8f, 0x01, 0x36, 0x73, 0x65, 1310x14, 0x00, 0x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x51, 0x00,
1310x00, 0x14, 0x00, 0x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x51, 1320x66, 0x72, 0x6f, 0x6d, 0x7c, 0x00, 0xe1, 0x60, 0x43, 0x75,
1320x00, 0x66, 0x72, 0x6f, 0x6d, 0x7c, 0x00, 0xe1, 0x60, 0x43, 1330x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f,
1330x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 1340x70, 0x72, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0, 0x54,
1340x6f, 0x70, 0x72, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0, 1350x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xae,
1350x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 1360x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48,
1360xae, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 1370x65, 0x47, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0xf3,
1370x48, 0x65, 0x47, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 1380x02, 0x02, 0x26, 0x01, 0x16, 0x6e, 0x8d, 0x06, 0xa1, 0x00,
1380xf3, 0x02, 0x02, 0x26, 0x01, 0x16, 0x6e, 0xa2, 0x06, 0xa1, 1390x00, 0x25, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x25,
1390x00, 0x00, 0x25, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 1400x01, 0x14, 0x20, 0x25, 0x01, 0xb0, 0x00, 0x00, 0x52, 0x6f,
1400x25, 0x01, 0x14, 0x20, 0x25, 0x01, 0xb0, 0x00, 0x00, 0x52, 1410x75, 0x67, 0x68, 0x00, 0x70, 0x65, 0x72, 0x2e, 0x04, 0x21,
1410x6f, 0x75, 0x67, 0x68, 0x00, 0x70, 0x65, 0x72, 0x43, 0x04, 1420x61, 0x67, 0x3f, 0x00, 0x0a, 0x49, 0x01, 0x25, 0x69, 0x6e,
1420x21, 0x61, 0x67, 0x3f, 0x00, 0x0a, 0x49, 0x01, 0x25, 0x69, 1430x7a, 0x01, 0x02, 0x8c, 0x01, 0x40, 0x69, 0x73, 0x00, 0x69,
1430x6e, 0x7a, 0x01, 0x02, 0x8c, 0x01, 0x40, 0x69, 0x73, 0x00, 1440x12, 0x03, 0x71, 0x68, 0x69, 0x6e, 0x74, 0x00, 0x72, 0x61,
1440x69, 0x12, 0x03, 0x71, 0x68, 0x69, 0x6e, 0x74, 0x00, 0x72, 1450x27, 0x02, 0xe1, 0x74, 0x68, 0x61, 0x6e, 0x00, 0x61, 0x6e,
1450x61, 0x27, 0x02, 0xe1, 0x74, 0x68, 0x61, 0x6e, 0x00, 0x61, 1460x00, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x26, 0x01, 0x46,
1460x6e, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x26, 0x01, 1470x2e, 0x00, 0x49, 0x66, 0xb4, 0x01, 0x90, 0x67, 0x65, 0x6e,
1470x46, 0x2e, 0x00, 0x49, 0x66, 0xb4, 0x01, 0x90, 0x67, 0x65, 1480x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x00, 0x21, 0x75,
1480x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x00, 0x21, 1490x6e, 0xf1, 0x00, 0x24, 0x74, 0x6f, 0x17, 0x00, 0x00, 0xc5,
1490x75, 0x6e, 0xf1, 0x00, 0x24, 0x74, 0x6f, 0x17, 0x00, 0x00, 1500x02, 0x03, 0x8f, 0x04, 0x04, 0x60, 0x04, 0xf0, 0x02, 0x70,
1500xc5, 0x02, 0x03, 0xa4, 0x04, 0x04, 0x75, 0x04, 0xf0, 0x02, 1510x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x00, 0x73, 0x70, 0x65,
1510x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x00, 0x73, 0x70, 1520x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x54, 0x00, 0x10, 0x2c,
1520x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x54, 0x00, 0x10, 1530x6c, 0x03, 0x01, 0xcd, 0x02, 0x74, 0x69, 0x6e, 0x63, 0x72,
1530x2c, 0x6c, 0x03, 0x01, 0xcd, 0x02, 0x74, 0x69, 0x6e, 0x63, 1540x65, 0x61, 0x73, 0xef, 0x03, 0x42, 0x6f, 0x70, 0x6f, 0x72,
1540x72, 0x65, 0x61, 0x73, 0xef, 0x03, 0x42, 0x6f, 0x70, 0x6f, 1550x29, 0x01, 0x0c, 0xbf, 0x00, 0x53, 0x75, 0x6e, 0x74, 0x69,
1550x72, 0x29, 0x01, 0x0c, 0xbf, 0x00, 0x53, 0x75, 0x6e, 0x74, 1560x6c, 0xa3, 0x03, 0x10, 0x2e, 0x24, 0x01, 0xb0, 0x79, 0x6d,
1560x69, 0x6c, 0xa3, 0x03, 0x10, 0x2e, 0x24, 0x01, 0xb0, 0x79, 1570x6d, 0x65, 0x74, 0x72, 0x79, 0x00, 0x00, 0x00, 0x41, 0x1d,
1570x6d, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x00, 0x00, 0x00, 0x41, 1580x02, 0x11, 0x73, 0x63, 0x06, 0x23, 0x74, 0x6f, 0x69, 0x00,
1580x1d, 0x02, 0x11, 0x73, 0x78, 0x06, 0x23, 0x74, 0x6f, 0x69, 1590x12, 0x79, 0x98, 0x02, 0x41, 0x65, 0x71, 0x75, 0x69, 0xcb,
1590x00, 0x12, 0x79, 0x98, 0x02, 0x41, 0x65, 0x71, 0x75, 0x69, 1600x02, 0x04, 0x2e, 0x00, 0x0e, 0xb0, 0x07, 0x0c, 0x1b, 0x01,
1600xcb, 0x02, 0x04, 0x2e, 0x00, 0x0e, 0xc5, 0x07, 0x0c, 0x1b, 1610x11, 0x28, 0x1a, 0x01, 0x10, 0x64, 0x4e, 0x05, 0x00, 0xf0,
1610x01, 0x11, 0x28, 0x1a, 0x01, 0x10, 0x64, 0x63, 0x05, 0x00, 1620x02, 0x61, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x20, 0x00,
1620xf0, 0x02, 0x61, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x20, 1630x95, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
1630x00, 0x95, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 1640x47, 0x00, 0x03, 0xff, 0x04, 0x00, 0x29, 0x00, 0x71, 0x69,
1640x74, 0x47, 0x00, 0x03, 0x15, 0x05, 0x00, 0x29, 0x00, 0x71, 1650x63, 0x65, 0x61, 0x62, 0x6c, 0x79, 0x3d, 0x02, 0x16, 0x44,
1650x69, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x79, 0x3d, 0x02, 0x16, 1660x29, 0x00, 0x70, 0x00, 0x00, 0x60, 0x45, 0x61, 0x73, 0x79,
1660x44, 0x29, 0x00, 0x70, 0x00, 0x00, 0x60, 0x45, 0x61, 0x73, 1670xec, 0x01, 0x23, 0x61, 0x6e, 0x3f, 0x04, 0x08, 0x3a, 0x00,
1670x79, 0xec, 0x01, 0x23, 0x61, 0x6e, 0x3f, 0x04, 0x08, 0x3a, 1680x60, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x4b, 0x04, 0x62,
1680x00, 0x60, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x4b, 0x04, 1690x73, 0x6f, 0x6c, 0x75, 0x62, 0x6c, 0x8b, 0x07, 0xb2, 0x6f,
1690x62, 0x73, 0x6f, 0x6c, 0x75, 0x62, 0x6c, 0xa0, 0x07, 0xb2, 1700x75, 0x74, 0x00, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61,
1700x6f, 0x75, 0x74, 0x00, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 1710xd6, 0x04, 0xf9, 0x03, 0x6f, 0x72, 0x00, 0x67, 0x75, 0x65,
1710x61, 0xd6, 0x04, 0xf9, 0x03, 0x6f, 0x72, 0x00, 0x67, 0x75, 1720x73, 0x73, 0x69, 0x6e, 0x67, 0x2c, 0x00, 0x60, 0x48, 0x61,
1720x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x2c, 0x00, 0x60, 0x48, 1730x72, 0x64, 0x52, 0x00, 0x01, 0x19, 0x08, 0x01, 0x21, 0x00,
1730x61, 0x72, 0x64, 0x52, 0x00, 0x01, 0x2e, 0x08, 0x01, 0x21, 1740x22, 0x65, 0x73, 0x58, 0x01, 0x40, 0x70, 0x72, 0x6f, 0x62,
1740x00, 0x22, 0x65, 0x73, 0x58, 0x01, 0x40, 0x70, 0x72, 0x6f, 1750x90, 0x00, 0x00, 0x5a, 0x00, 0xa2, 0x6e, 0x65, 0x63, 0x65,
1750x62, 0x90, 0x00, 0x00, 0x5a, 0x00, 0xb0, 0x6e, 0x65, 0x63, 1760x73, 0x73, 0x61, 0x72, 0x79, 0x2e, 0xd9, 0x02, 0x16, 0x33,
1760x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x2e, 0x00, 1770xd9, 0x02, 0xb0, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x72,
1780x65, 0x66, 0x65, 0x72, 0x68, 0x05, 0x01, 0xdf, 0x02, 0x20,
1790x4f, 0x6e, 0x91, 0x04, 0x54, 0x74, 0x66, 0x6f, 0x72, 0x6d,
1800x5f, 0x00, 0x20, 0x75, 0x70, 0x98, 0x01, 0x10, 0x00, 0x2e,
1810x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0xcd, 0x02, 0x16,
1820x50, 0x12, 0x00, 0x0d, 0xe9, 0x02, 0x33, 0x47, 0x61, 0x6d,
1830xe9, 0x02, 0x02, 0x98, 0x00, 0x31, 0x6c, 0x65, 0x74, 0xa9,
1840x01, 0x90, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
1850x65, 0xfc, 0x03, 0x01, 0x7b, 0x02, 0x23, 0x60, 0x74, 0x92,
1860x02, 0x01, 0x76, 0x01, 0x02, 0xcc, 0x03, 0x11, 0x27, 0x13,
1870x05, 0x02, 0x61, 0x03, 0x52, 0x73, 0x68, 0x6f, 0x77, 0x6e,
1880x2a, 0x04, 0x08, 0xdc, 0x08, 0x00, 0xd0, 0x08, 0x80, 0x73,
1890x6f, 0x00, 0x6c, 0x69, 0x74, 0x2e, 0x00,
177}; 190};
178 191
179const unsigned short help_text_len = 2327; 192const unsigned short help_text_len = 2549;
180const unsigned short help_text_words = 433; 193const unsigned short help_text_words = 468;
194const bool help_valid = true;
181const char quick_help_text[] = "Place bulbs to light up all the squares."; 195const char quick_help_text[] = "Place bulbs to light up all the squares.";
diff --git a/apps/plugins/puzzles/help/loopy.c b/apps/plugins/puzzles/help/loopy.c
index 425b157107..76c441511c 100644
--- a/apps/plugins/puzzles/help/loopy.c
+++ b/apps/plugins/puzzles/help/loopy.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,184 +6,263 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 169, TEXT_CENTER | C_RED }, 9 { 167, TEXT_CENTER | C_RED },
10 { 220, TEXT_UNDERLINE }, 10 { 218, TEXT_UNDERLINE },
11 { 262, TEXT_CENTER | C_RED }, 11 { 260, TEXT_CENTER | C_RED },
12 { 279, TEXT_UNDERLINE }, 12 { 277, TEXT_UNDERLINE },
13 { 280, TEXT_UNDERLINE }, 13 { 278, TEXT_UNDERLINE },
14 { 324, TEXT_UNDERLINE }, 14 { 322, TEXT_UNDERLINE },
15 { 353, TEXT_UNDERLINE },
15 { 355, TEXT_UNDERLINE }, 16 { 355, TEXT_UNDERLINE },
16 { 357, TEXT_UNDERLINE }, 17 { 373, TEXT_UNDERLINE },
17 { 375, TEXT_UNDERLINE }, 18 { 388, TEXT_UNDERLINE },
18 { 390, TEXT_UNDERLINE }, 19 { 402, TEXT_UNDERLINE },
19 { 404, TEXT_UNDERLINE }, 20 { 414, TEXT_CENTER | C_RED },
21 { 618, TEXT_UNDERLINE },
20 LAST_STYLE_ITEM 22 LAST_STYLE_ITEM
21}; 23};
22 24
23/* orig 2260 comp 1600 ratio 0.707965 level 10 saved 660 */ 25/* orig 3584 comp 2352 ratio 0.65625 level 10 saved 1232 */
24const char help_text[] = { 26const char help_text[] = {
250xf1, 0x4e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 270xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
260x32, 0x33, 0x3a, 0x20, 0x4c, 0x6f, 0x6f, 0x70, 0x79, 0x20, 280x32, 0x33, 0x3a, 0x20, 0x4c, 0x6f, 0x6f, 0x70, 0x79, 0x20,
270x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 290x00, 0x2d, 0x01, 0x00, 0xf1, 0x3c, 0x00, 0x00, 0x00, 0x59,
280x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 300x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76,
290x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x64, 0x6f, 0x74, 310x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00,
300x73, 0x2c, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x64, 0x00, 320x6f, 0x66, 0x00, 0x64, 0x6f, 0x74, 0x73, 0x2c, 0x00, 0x6d,
310x77, 0x69, 0x74, 0x68, 0x00, 0x79, 0x65, 0x6c, 0x6c, 0x6f, 330x61, 0x72, 0x6b, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68,
320x77, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x00, 0x74, 0x6f, 340x00, 0x79, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x00, 0x6c, 0x69,
330x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 350x6e, 0x65, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x69, 0x6e, 0x64,
340x77, 0x68, 0x69, 0x63, 0x68, 0x31, 0x00, 0x23, 0x00, 0x79, 360x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x77, 0x68, 0x69, 0x63,
350x4e, 0x00, 0x10, 0x61, 0x2b, 0x00, 0x20, 0x65, 0x64, 0x27, 370x68, 0x31, 0x00, 0x23, 0x00, 0x79, 0x4e, 0x00, 0x10, 0x61,
360x00, 0xf0, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 380x2b, 0x00, 0x20, 0x65, 0x64, 0x27, 0x00, 0xf0, 0x0b, 0x63,
370x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x00, 390x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x64, 0x69, 0x72,
380x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x7c, 400x65, 0x63, 0x74, 0x6c, 0x79, 0x00, 0x74, 0x6f, 0x67, 0x65,
390x00, 0x71, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x51, 410x74, 0x68, 0x65, 0x72, 0x2e, 0x7c, 0x00, 0x71, 0x72, 0x00,
400x00, 0xf0, 0x00, 0x75, 0x73, 0x65, 0x00, 0x73, 0x6f, 0x6d, 420x61, 0x69, 0x6d, 0x00, 0x69, 0x51, 0x00, 0xf0, 0x00, 0x75,
410x65, 0x00, 0x73, 0x75, 0x62, 0x73, 0x65, 0x74, 0x86, 0x00, 430x73, 0x65, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x00, 0x73, 0x75,
420x5d, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x7a, 0x00, 0xf0, 0x11, 440x62, 0x73, 0x65, 0x74, 0x86, 0x00, 0x5d, 0x74, 0x68, 0x6f,
430x64, 0x72, 0x61, 0x77, 0x00, 0x61, 0x00, 0x73, 0x69, 0x6e, 450x73, 0x65, 0x7a, 0x00, 0xf0, 0x11, 0x64, 0x72, 0x61, 0x77,
440x67, 0x6c, 0x65, 0x00, 0x75, 0x6e, 0x62, 0x72, 0x6f, 0x6b, 460x00, 0x61, 0x00, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x00,
450x65, 0x6e, 0x00, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x66, 0x72, 470x75, 0x6e, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x00, 0x6c,
460x6f, 0x6d, 0x8c, 0x00, 0x01, 0x28, 0x00, 0x21, 0x6f, 0x74, 480x6f, 0x6f, 0x70, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x8c, 0x00,
470xbb, 0x00, 0x61, 0x69, 0x6e, 0x00, 0x74, 0x68, 0x65, 0xdb, 490x01, 0x28, 0x00, 0x21, 0x6f, 0x74, 0xbb, 0x00, 0x61, 0x69,
480x00, 0x50, 0x2e, 0x00, 0x00, 0x00, 0x53, 0x64, 0x00, 0x21, 500x6e, 0x00, 0x74, 0x68, 0x65, 0xdb, 0x00, 0x50, 0x2e, 0x00,
490x6f, 0x66, 0x14, 0x00, 0xd2, 0x73, 0x70, 0x61, 0x63, 0x65, 510x00, 0x00, 0x53, 0x64, 0x00, 0x21, 0x6f, 0x66, 0x14, 0x00,
500x73, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x27, 0x00, 520xd2, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x00, 0x62, 0x65,
510x02, 0x67, 0x00, 0xf4, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 530x74, 0x77, 0x65, 0x65, 0x27, 0x00, 0x02, 0x67, 0x00, 0xf4,
520x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 540x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e,
530x2e, 0x00, 0x54, 0x68, 0x65, 0x73, 0x65, 0x0f, 0x00, 0x06, 550x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x00, 0x54, 0x68,
540xfd, 0x00, 0x84, 0x68, 0x6f, 0x77, 0x00, 0x6d, 0x61, 0x6e, 560x65, 0x73, 0x65, 0x0f, 0x00, 0x06, 0xfd, 0x00, 0x84, 0x68,
550x79, 0x51, 0x00, 0x02, 0x3e, 0x00, 0xb2, 0x61, 0x72, 0x6f, 570x6f, 0x77, 0x00, 0x6d, 0x61, 0x6e, 0x79, 0x51, 0x00, 0x02,
560x75, 0x6e, 0x64, 0x00, 0x74, 0x68, 0x61, 0x74, 0x63, 0x00, 580x3e, 0x00, 0xb2, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x00,
570xa5, 0x00, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x70, 0x61, 0x72, 590x74, 0x68, 0x61, 0x74, 0x63, 0x00, 0xa5, 0x00, 0x66, 0x6f,
580x74, 0x29, 0x00, 0x31, 0x6f, 0x6f, 0x70, 0x56, 0x00, 0x02, 600x72, 0x6d, 0x00, 0x70, 0x61, 0x72, 0x74, 0x29, 0x00, 0x31,
590xb8, 0x00, 0x00, 0x3a, 0x01, 0x01, 0xd8, 0x00, 0x83, 0x6d, 610x6f, 0x6f, 0x70, 0x56, 0x00, 0x02, 0xb8, 0x00, 0x00, 0x3a,
600x75, 0x73, 0x74, 0x00, 0x63, 0x6f, 0x72, 0x2e, 0x01, 0x70, 620x01, 0x01, 0xd8, 0x00, 0x83, 0x6d, 0x75, 0x73, 0x74, 0x00,
610x73, 0x61, 0x74, 0x69, 0x73, 0x66, 0x79, 0x52, 0x01, 0x03, 630x63, 0x6f, 0x72, 0x2e, 0x01, 0x70, 0x73, 0x61, 0x74, 0x69,
620x3a, 0x00, 0x62, 0x73, 0x65, 0x00, 0x63, 0x6c, 0x75, 0x0a, 640x73, 0x66, 0x79, 0x52, 0x01, 0x03, 0x3a, 0x00, 0x62, 0x73,
630x01, 0x20, 0x62, 0x65, 0xa9, 0x00, 0x94, 0x73, 0x69, 0x64, 650x65, 0x00, 0x63, 0x6c, 0x75, 0x0a, 0x01, 0x20, 0x62, 0x65,
640x65, 0x72, 0x65, 0x64, 0x00, 0x61, 0x38, 0x00, 0x90, 0x00, 660xa9, 0x00, 0x94, 0x73, 0x69, 0x64, 0x65, 0x72, 0x65, 0x64,
650x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0xef, 0x00, 670x00, 0x61, 0x38, 0x00, 0x90, 0x00, 0x73, 0x6f, 0x6c, 0x75,
660x12, 0x49, 0xd7, 0x00, 0xd2, 0x64, 0x65, 0x66, 0x61, 0x75, 680x74, 0x69, 0x6f, 0x6e, 0xef, 0x00, 0x12, 0x49, 0xd7, 0x00,
670x6c, 0x74, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x2c, 0x12, 0x00, 690xd2, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x6d,
680x00, 0xb2, 0x01, 0x01, 0xae, 0x01, 0x98, 0x72, 0x72, 0x61, 700x6f, 0x64, 0x65, 0x2c, 0x12, 0x00, 0x00, 0xb2, 0x01, 0x01,
690x6e, 0x67, 0x65, 0x64, 0x00, 0x69, 0x02, 0x02, 0x80, 0x73, 710xae, 0x01, 0x98, 0x72, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x64,
700x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x3b, 0xe3, 0x00, 0x51, 720x00, 0x69, 0x02, 0x02, 0x80, 0x73, 0x71, 0x75, 0x61, 0x72,
710x65, 0x76, 0x65, 0x72, 0x2c, 0xa4, 0x00, 0xf1, 0x19, 0x63, 730x65, 0x73, 0x3b, 0xe3, 0x00, 0x51, 0x65, 0x76, 0x65, 0x72,
720x61, 0x6e, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x70, 0x6c, 740x2c, 0xa4, 0x00, 0xf1, 0x19, 0x63, 0x61, 0x6e, 0x00, 0x61,
730x61, 0x79, 0x00, 0x6f, 0x6e, 0x00, 0x74, 0x72, 0x69, 0x61, 750x6c, 0x73, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x6f,
740x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x00, 0x6f, 0x72, 0x00, 760x6e, 0x00, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x75, 0x6c,
750x68, 0x65, 0x78, 0x61, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x47, 770x61, 0x72, 0x00, 0x6f, 0x72, 0x00, 0x68, 0x65, 0x78, 0x61,
760x00, 0x20, 0x73, 0x2c, 0x14, 0x00, 0x10, 0x65, 0x5a, 0x02, 780x67, 0x6f, 0x6e, 0x61, 0x6c, 0x47, 0x00, 0x20, 0x73, 0x2c,
770xf0, 0x01, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x65, 0x78, 0x6f, 790x14, 0x00, 0x10, 0x65, 0x5a, 0x02, 0xf0, 0x01, 0x6d, 0x6f,
780x74, 0x69, 0x63, 0x00, 0x6f, 0x6e, 0x65, 0x73, 0x9a, 0x00, 800x72, 0x65, 0x00, 0x65, 0x78, 0x6f, 0x74, 0x69, 0x63, 0x00,
790x60, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x1b, 0x01, 0x01, 810x6f, 0x6e, 0x65, 0x73, 0x9a, 0x00, 0x60, 0x43, 0x72, 0x65,
800x90, 0x00, 0xf2, 0x05, 0x62, 0x61, 0x73, 0x69, 0x63, 0x00, 820x64, 0x69, 0x74, 0x1b, 0x01, 0x01, 0x90, 0x00, 0xf2, 0x05,
810x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x69, 0x64, 0x65, 830x62, 0x61, 0x73, 0x69, 0x63, 0x00, 0x70, 0x75, 0x7a, 0x7a,
820x61, 0x00, 0x67, 0x6f, 0xe7, 0x00, 0xb0, 0x4e, 0x69, 0x6b, 840x6c, 0x65, 0x00, 0x69, 0x64, 0x65, 0x61, 0x00, 0x67, 0x6f,
830x6f, 0x6c, 0x69, 0x00, 0x5b, 0x31, 0x30, 0x5d, 0x38, 0x00, 850xe7, 0x00, 0xb0, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00,
840x01, 0xbd, 0x02, 0xf1, 0x00, 0x00, 0x77, 0x61, 0x73, 0x00, 860x5b, 0x31, 0x30, 0x5d, 0x38, 0x00, 0x01, 0xcf, 0x02, 0xf1,
850x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 870x00, 0x00, 0x77, 0x61, 0x73, 0x00, 0x6f, 0x72, 0x69, 0x67,
860xb1, 0x01, 0x52, 0x72, 0x69, 0x62, 0x75, 0x74, 0x77, 0x02, 880x69, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0xb1, 0x01, 0x52, 0x72,
870x30, 0x74, 0x68, 0x69, 0xc5, 0x01, 0x40, 0x6c, 0x6c, 0x65, 890x69, 0x62, 0x75, 0x74, 0x77, 0x02, 0x30, 0x74, 0x68, 0x69,
880x63, 0x09, 0x01, 0xf2, 0x04, 0x00, 0x62, 0x79, 0x00, 0x4d, 900xc5, 0x01, 0x40, 0x6c, 0x6c, 0x65, 0x63, 0x09, 0x01, 0xf2,
890x69, 0x6b, 0x65, 0x00, 0x50, 0x69, 0x6e, 0x6e, 0x61, 0x2c, 910x04, 0x00, 0x62, 0x79, 0x00, 0x4d, 0x69, 0x6b, 0x65, 0x00,
900x00, 0x61, 0x6e, 0x64, 0x67, 0x02, 0x40, 0x71, 0x75, 0x65, 920x50, 0x69, 0x6e, 0x6e, 0x61, 0x2c, 0x00, 0x61, 0x6e, 0x64,
910x6e, 0x68, 0x01, 0x62, 0x65, 0x6e, 0x68, 0x61, 0x6e, 0x63, 930x67, 0x02, 0x40, 0x71, 0x75, 0x65, 0x6e, 0x68, 0x01, 0x62,
920x3c, 0x00, 0xf0, 0x05, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 940x65, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x3c, 0x00, 0xf0, 0x05,
930x00, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x74, 950x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x76, 0x61, 0x72,
940x79, 0x70, 0x65, 0x73, 0x12, 0x01, 0x42, 0x6e, 0x6f, 0x6e, 960x69, 0x6f, 0x75, 0x73, 0x00, 0x74, 0x79, 0x70, 0x65, 0x73,
950x2d, 0x16, 0x01, 0x02, 0x25, 0x01, 0xa3, 0x62, 0x79, 0x00, 970x12, 0x01, 0x42, 0x6e, 0x6f, 0x6e, 0x2d, 0x16, 0x01, 0x02,
960x4c, 0x61, 0x6d, 0x62, 0x72, 0x6f, 0x73, 0x08, 0x00, 0x10, 980x25, 0x01, 0xa3, 0x62, 0x79, 0x00, 0x4c, 0x61, 0x6d, 0x62,
970x75, 0x9e, 0x00, 0x00, 0xa6, 0x00, 0xd1, 0x00, 0x68, 0x74, 990x72, 0x6f, 0x73, 0x08, 0x00, 0x10, 0x75, 0x9e, 0x00, 0x00,
980x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 1000xa6, 0x00, 0xe1, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
990xbd, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 1010x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0xbe, 0x00, 0xa2,
1000x65, 0x6e, 0x2f, 0xe1, 0x00, 0x50, 0x73, 0x2f, 0x73, 0x6c, 1020x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f,
1010x69, 0x07, 0x03, 0xf1, 0x01, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 1030xe2, 0x00, 0x50, 0x73, 0x2f, 0x73, 0x6c, 0x69, 0x08, 0x03,
1020x68, 0x74, 0x6d, 0x6c, 0x00, 0x28, 0x62, 0x65, 0x77, 0x61, 1040xc3, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x00, 0x00, 0x00, 0x32,
1030x72, 0x9b, 0x02, 0xd3, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x29, 1050x33, 0x2e, 0x31, 0xaa, 0x03, 0x01, 0xcc, 0x00, 0x40, 0x6f,
1040x00, 0x00, 0x00, 0x32, 0x33, 0x2e, 0x31, 0xad, 0x03, 0x01, 1060x6c, 0x73, 0x20, 0x25, 0x01, 0x42, 0x6c, 0x69, 0x63, 0x6b,
1050xe1, 0x00, 0x40, 0x6f, 0x6c, 0x73, 0x20, 0x3a, 0x01, 0x42, 1070x32, 0x02, 0x20, 0x65, 0x66, 0xbf, 0x01, 0x00, 0x27, 0x03,
1060x6c, 0x69, 0x63, 0x6b, 0x47, 0x02, 0x20, 0x65, 0x66, 0xd4, 1080x60, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x7f, 0x01, 0x18,
1070x01, 0x00, 0x3c, 0x03, 0x60, 0x62, 0x75, 0x74, 0x74, 0x6f, 1090x61, 0x1e, 0x03, 0x01, 0xf9, 0x00, 0xd4, 0x75, 0x72, 0x6e,
1080x6e, 0x94, 0x01, 0x18, 0x61, 0x33, 0x03, 0x01, 0x0e, 0x01, 1100x00, 0x69, 0x74, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x2c,
1090xd4, 0x75, 0x72, 0x6e, 0x00, 0x69, 0x74, 0x00, 0x62, 0x6c, 1110xa9, 0x02, 0x32, 0x69, 0x6e, 0x67, 0x8e, 0x02, 0x00, 0xc4,
1100x61, 0x63, 0x6b, 0x2c, 0xbe, 0x02, 0x32, 0x69, 0x6e, 0x67, 1120x01, 0x00, 0x12, 0x03, 0x10, 0x6b, 0x24, 0x00, 0x2e, 0x69,
1110xa3, 0x02, 0x00, 0xd9, 0x01, 0x00, 0x27, 0x03, 0x10, 0x6b, 1130x73, 0x93, 0x02, 0x03, 0x71, 0x00, 0x20, 0x61, 0x67, 0xfe,
1120x24, 0x00, 0x2e, 0x69, 0x73, 0xa8, 0x02, 0x03, 0x71, 0x00, 1140x02, 0x04, 0x50, 0x00, 0x04, 0xda, 0x02, 0x04, 0x6d, 0x00,
1130x20, 0x61, 0x67, 0x13, 0x03, 0x04, 0x50, 0x00, 0x04, 0xef, 1150x02, 0x1e, 0x00, 0x50, 0x28, 0x6d, 0x65, 0x61, 0x6e, 0x5a,
1140x02, 0x04, 0x6d, 0x00, 0x02, 0x1e, 0x00, 0x50, 0x28, 0x6d, 1160x00, 0x03, 0xf7, 0x03, 0x70, 0x6e, 0x27, 0x74, 0x00, 0x73,
1150x65, 0x61, 0x6e, 0x5a, 0x00, 0x03, 0x0c, 0x04, 0x70, 0x6e, 1170x75, 0x72, 0x26, 0x00, 0x21, 0x74, 0x29, 0x7b, 0x02, 0x15,
1160x27, 0x74, 0x00, 0x73, 0x75, 0x72, 0x26, 0x00, 0x21, 0x74, 1180x66, 0x12, 0x04, 0x01, 0x18, 0x00, 0x01, 0x82, 0x00, 0x11,
1170x29, 0x90, 0x02, 0x15, 0x66, 0x27, 0x04, 0x01, 0x18, 0x00, 1190x61, 0x74, 0x00, 0x21, 0x69, 0x63, 0x33, 0x02, 0x01, 0x55,
1180x01, 0x82, 0x00, 0x11, 0x61, 0x74, 0x00, 0x21, 0x69, 0x63, 1200x00, 0x61, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x8f, 0x00,
1190x48, 0x02, 0x01, 0x55, 0x00, 0x61, 0x73, 0x65, 0x67, 0x6d, 1210x3d, 0x6e, 0x6f, 0x74, 0x93, 0x00, 0x06, 0x79, 0x02, 0x15,
1200x65, 0x6e, 0x8f, 0x00, 0x3d, 0x6e, 0x6f, 0x74, 0x93, 0x00, 1220x63, 0x0c, 0x01, 0x4b, 0x72, 0x69, 0x67, 0x68, 0x0d, 0x01,
1210x06, 0x8e, 0x02, 0x15, 0x63, 0x0c, 0x01, 0x4b, 0x72, 0x69, 1230xb0, 0x74, 0x6f, 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65,
1220x67, 0x68, 0x0d, 0x01, 0xb0, 0x74, 0x6f, 0x00, 0x72, 0x65, 1240x00, 0x69, 0x41, 0x03, 0xb0, 0x6d, 0x70, 0x6c, 0x65, 0x74,
1230x6d, 0x6f, 0x76, 0x65, 0x00, 0x69, 0x56, 0x03, 0xb0, 0x6d, 1250x65, 0x6c, 0x79, 0x2e, 0x00, 0x41, 0xad, 0x00, 0x12, 0x2c,
1240x70, 0x6c, 0x65, 0x74, 0x65, 0x6c, 0x79, 0x2e, 0x00, 0x41, 1260x3d, 0x00, 0x00, 0xae, 0x00, 0x60, 0x61, 0x00, 0x73, 0x65,
1250xad, 0x00, 0x12, 0x2c, 0x3d, 0x00, 0x00, 0xae, 0x00, 0x60, 1270x63, 0x6f, 0x9f, 0x03, 0x8b, 0x69, 0x6d, 0x65, 0x00, 0x77,
1260x61, 0x00, 0x73, 0x65, 0x63, 0x6f, 0xb4, 0x03, 0x8b, 0x69, 1280x69, 0x6c, 0x6c, 0xe5, 0x00, 0x20, 0x62, 0x61, 0x66, 0x00,
1270x6d, 0x65, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0xe5, 0x00, 0x20, 1290x13, 0x6f, 0xed, 0x00, 0x00, 0xc9, 0x00, 0x41, 0x28, 0x41,
1280x62, 0x61, 0x66, 0x00, 0x13, 0x6f, 0xed, 0x00, 0x00, 0xc9, 1300x6c, 0x6c, 0x20, 0x00, 0x11, 0x61, 0x4e, 0x02, 0x92, 0x73,
1290x00, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x20, 0x00, 0x11, 0x61, 1310x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x34, 0x03,
1300x63, 0x02, 0x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 1320x13, 0x73, 0x64, 0x02, 0x33, 0x32, 0x2e, 0x31, 0xfe, 0x04,
1310x69, 0x62, 0x49, 0x03, 0x13, 0x73, 0x79, 0x02, 0x33, 0x32, 1330xe2, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,
1320x2e, 0x31, 0x13, 0x05, 0xd3, 0x73, 0x6f, 0x00, 0x61, 0x76, 1340x62, 0x6c, 0x65, 0x2e, 0x29, 0xdd, 0x01, 0x13, 0x32, 0xdd,
1330x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0xdd, 0x01, 1350x01, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
1340x13, 0x32, 0xdd, 0x01, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 1360x72, 0xdf, 0x01, 0x02, 0x57, 0x04, 0x06, 0x14, 0x00, 0x02,
1350x65, 0x74, 0x65, 0x72, 0xdf, 0x01, 0x02, 0x6c, 0x04, 0x06, 1370x41, 0x00, 0x04, 0x3c, 0x00, 0x02, 0xcf, 0x04, 0x00, 0x79,
1360x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x02, 0xe4, 1380x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e,
1370x04, 0x00, 0x79, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 1390x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x22, 0x6f,
1380x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6f, 1400x6e, 0x1a, 0x00, 0x91, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1390x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0x91, 0x54, 0x79, 0x70, 1410x6d, 0x65, 0x6e, 0x7f, 0x02, 0x91, 0x57, 0x69, 0x64, 0x74,
1400x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x94, 0x02, 0x91, 0x57, 1420x68, 0x2c, 0x00, 0x48, 0x65, 0x22, 0x01, 0x51, 0x00, 0x00,
1410x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x22, 0x01, 1430x53, 0x69, 0x7a, 0xee, 0x04, 0x00, 0xb2, 0x02, 0x70, 0x2c,
1420x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x68, 0x02, 0x00, 0xc7, 1440x00, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x2c, 0x04, 0x05, 0xdb,
1430x02, 0x70, 0x2c, 0x00, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x41, 1450x04, 0x00, 0x1c, 0x00, 0x31, 0x72, 0x65, 0x67, 0xd6, 0x00,
1440x04, 0x05, 0xf0, 0x04, 0x00, 0x1c, 0x00, 0x31, 0x72, 0x65, 1460x61, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x1d, 0x03, 0x93,
1450x67, 0xd6, 0x00, 0x61, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 1470x64, 0x6f, 0x77, 0x6e, 0x2e, 0x00, 0x46, 0x6f, 0x72, 0x08,
1460x32, 0x03, 0x93, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x00, 0x46, 1480x04, 0x04, 0xd0, 0x03, 0xa1, 0x69, 0x74, 0x27, 0x73, 0x00,
1470x6f, 0x72, 0x1d, 0x04, 0x04, 0xe5, 0x03, 0xa1, 0x69, 0x74, 1490x63, 0x6c, 0x65, 0x61, 0x72, 0xfb, 0x04, 0x01, 0x67, 0x03,
1480x27, 0x73, 0x00, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x10, 0x05, 1500x01, 0x6a, 0x03, 0x61, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x3b,
1490x01, 0x7c, 0x03, 0x01, 0x7f, 0x03, 0x61, 0x75, 0x6e, 0x74, 1510xcd, 0x03, 0x10, 0x6f, 0xd7, 0x02, 0x06, 0x37, 0x03, 0x01,
1500x65, 0x64, 0x3b, 0xe2, 0x03, 0x10, 0x6f, 0xed, 0x02, 0x06, 1520x2c, 0x03, 0x00, 0xc3, 0x01, 0x72, 0x6d, 0x61, 0x79, 0x00,
1510x4c, 0x03, 0x01, 0x41, 0x03, 0x00, 0xc3, 0x01, 0x72, 0x6d, 1530x68, 0x61, 0x76, 0xa7, 0x02, 0x01, 0x84, 0x02, 0x41, 0x61,
1520x61, 0x79, 0x00, 0x68, 0x61, 0x76, 0xa7, 0x02, 0x01, 0x84, 1540x00, 0x62, 0x69, 0xab, 0x05, 0x22, 0x73, 0x65, 0x4b, 0x05,
1530x02, 0x41, 0x61, 0x00, 0x62, 0x69, 0xc0, 0x05, 0x22, 0x73, 1550x01, 0x98, 0x04, 0x52, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x91,
1540x65, 0x60, 0x05, 0x01, 0xad, 0x04, 0x52, 0x69, 0x6d, 0x65, 1560x00, 0x25, 0x72, 0x65, 0xb3, 0x00, 0x00, 0xdd, 0x00, 0x50,
1550x6e, 0x73, 0x91, 0x00, 0x25, 0x72, 0x65, 0xb3, 0x00, 0x00, 1570x47, 0x72, 0x69, 0x64, 0x20, 0x59, 0x00, 0x40, 0x00, 0x00,
1560xdd, 0x00, 0x50, 0x47, 0x72, 0x69, 0x64, 0x20, 0x59, 0x00, 1580x00, 0x41, 0x9d, 0x01, 0x02, 0x73, 0x06, 0x00, 0x67, 0x06,
1570x40, 0x00, 0x00, 0x00, 0x41, 0x9d, 0x01, 0x02, 0x88, 0x06, 1590x20, 0x68, 0x6f, 0x2b, 0x06, 0x04, 0xc9, 0x05, 0x00, 0xe4,
1580x00, 0x7c, 0x06, 0x20, 0x68, 0x6f, 0x40, 0x06, 0x04, 0xde, 1600x01, 0x04, 0xfb, 0x03, 0x26, 0x6f, 0x66, 0x8c, 0x00, 0x72,
1590x05, 0x00, 0xe4, 0x01, 0x04, 0x10, 0x04, 0x26, 0x6f, 0x66, 1610x74, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x04, 0x06, 0x01,
1600x8c, 0x00, 0x72, 0x74, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 1620x8c, 0x00, 0x13, 0x61, 0xda, 0x01, 0x11, 0x66, 0x09, 0x06,
1610x19, 0x06, 0x01, 0x8c, 0x00, 0x13, 0x61, 0xda, 0x01, 0x11, 1630x01, 0x14, 0x06, 0x21, 0x61, 0x6d, 0x55, 0x02, 0x06, 0xb0,
1620x66, 0x1e, 0x06, 0x01, 0x29, 0x06, 0x21, 0x61, 0x6d, 0x55, 1640x00, 0x70, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x9b,
1630x02, 0x06, 0xb0, 0x00, 0x70, 0x6d, 0x75, 0x6c, 0x74, 0x69, 1650x00, 0x40, 0x66, 0x66, 0x65, 0x72, 0xac, 0x02, 0x05, 0x52,
1640x70, 0x6c, 0x9b, 0x00, 0x40, 0x66, 0x66, 0x65, 0x72, 0xac, 1660x00, 0xc1, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x28,
1650x02, 0x05, 0x52, 0x00, 0xc1, 0x76, 0x65, 0x72, 0x74, 0x65, 1670x65, 0x2e, 0x67, 0x2e, 0x3f, 0x00, 0x50, 0x43, 0x61, 0x69,
1660x78, 0x00, 0x28, 0x65, 0x2e, 0x67, 0x2e, 0x3f, 0x00, 0x50, 1680x72, 0x6f, 0xf8, 0x04, 0x51, 0x4b, 0x69, 0x74, 0x65, 0x73,
1670x43, 0x61, 0x69, 0x72, 0x6f, 0x0d, 0x05, 0x51, 0x4b, 0x69, 1690x70, 0x05, 0x22, 0x29, 0x3b, 0x14, 0x01, 0x1a, 0x73, 0x73,
1680x74, 0x65, 0x73, 0x85, 0x05, 0x22, 0x29, 0x3b, 0x14, 0x01, 1700x00, 0x00, 0x3b, 0x00, 0x1f, 0x69, 0x76, 0x00, 0x07, 0x0e,
1690x1a, 0x73, 0x73, 0x00, 0x00, 0x3b, 0x00, 0x1f, 0x69, 0x76, 1710x6d, 0x00, 0x01, 0xa5, 0x00, 0x07, 0x6b, 0x00, 0x74, 0x47,
1700x00, 0x07, 0x0e, 0x6d, 0x00, 0x01, 0xa5, 0x00, 0x07, 0x6b, 1720x72, 0x65, 0x61, 0x74, 0x20, 0x48, 0x74, 0x05, 0x12, 0x29,
1710x00, 0x74, 0x47, 0x72, 0x65, 0x61, 0x74, 0x20, 0x48, 0x89, 1730x50, 0x06, 0x02, 0xb1, 0x01, 0x18, 0x2c, 0x9a, 0x05, 0x00,
1720x05, 0x12, 0x29, 0x65, 0x06, 0x02, 0xb1, 0x01, 0x18, 0x2c, 1740xd2, 0x01, 0x92, 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x63, 0x6f,
1730xaf, 0x05, 0x00, 0xd2, 0x01, 0x92, 0x68, 0x6f, 0x6e, 0x65, 1750x6d, 0x62, 0xcb, 0x01, 0x01, 0x5c, 0x01, 0x20, 0x66, 0x75,
1740x79, 0x63, 0x6f, 0x6d, 0x62, 0xcb, 0x01, 0x01, 0x5c, 0x01, 1760x3b, 0x05, 0x21, 0x72, 0x65, 0x26, 0x00, 0x02, 0x16, 0x05,
1750x20, 0x66, 0x75, 0x50, 0x05, 0x21, 0x72, 0x65, 0x26, 0x00, 1770x08, 0xa2, 0x00, 0x26, 0x69, 0x72, 0xa4, 0x00, 0x00, 0x1c,
1760x02, 0x2b, 0x05, 0x08, 0xa2, 0x00, 0x26, 0x69, 0x72, 0xa4, 1780x00, 0x0a, 0x24, 0x01, 0x12, 0x3b, 0xfe, 0x01, 0x32, 0x6d,
1770x00, 0x00, 0x1c, 0x00, 0x0a, 0x24, 0x01, 0x12, 0x3b, 0xfe, 1790x61, 0x6b, 0x15, 0x00, 0x13, 0x6d, 0xa6, 0x04, 0x11, 0x61,
1780x01, 0x32, 0x6d, 0x61, 0x6b, 0x15, 0x00, 0x13, 0x6d, 0xa6, 1800xc1, 0x06, 0x41, 0x6e, 0x66, 0x75, 0x73, 0x76, 0x04, 0x02,
1790x04, 0x11, 0x61, 0xd6, 0x06, 0x41, 0x6e, 0x66, 0x75, 0x73, 1810x2b, 0x06, 0x00, 0xbe, 0x01, 0x40, 0x44, 0x69, 0x66, 0x66,
1800x76, 0x04, 0x02, 0x40, 0x06, 0x00, 0xbe, 0x01, 0x40, 0x44, 1820xf8, 0x03, 0x20, 0x74, 0x79, 0xd8, 0x04, 0x03, 0xe4, 0x04,
1810x69, 0x66, 0x66, 0xf8, 0x03, 0x20, 0x74, 0x79, 0xd8, 0x04, 1830x03, 0xf3, 0x01, 0x05, 0x1a, 0x00, 0x03, 0xf9, 0x03, 0x60,
1820x03, 0xe4, 0x04, 0x03, 0xf3, 0x01, 0x05, 0x1a, 0x00, 0x03, 1840x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0xcd, 0x05, 0x02, 0x2f,
1830xf9, 0x03, 0x60, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0xe2, 1850x05, 0x12, 0x2e, 0x45, 0x03, 0x13, 0x33, 0x45, 0x03, 0x81,
1840x05, 0x80, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2e, 0x00, 1860x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x72, 0x65, 0x21, 0x01,
1870x21, 0x63, 0x65, 0x4b, 0x03, 0x20, 0x4f, 0x6e, 0x6a, 0x00,
1880x10, 0x74, 0x6a, 0x07, 0x13, 0x73, 0x7b, 0x07, 0x70, 0x75,
1890x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x17, 0x00,
1900x2e, 0x00, 0x12, 0x2c, 0x39, 0x03, 0x16, 0x50, 0x12, 0x00,
1910x0d, 0x55, 0x03, 0x33, 0x47, 0x61, 0x6d, 0x55, 0x03, 0x02,
1920x22, 0x04, 0x22, 0x6c, 0x65, 0x3c, 0x05, 0x00, 0xd9, 0x00,
1930x22, 0x69, 0x67, 0xcd, 0x04, 0x00, 0xd6, 0x07, 0x00, 0x85,
1940x02, 0x02, 0x5d, 0x05, 0xa0, 0x69, 0x6e, 0x67, 0x73, 0x3a,
1950x00, 0x00, 0x00, 0x60, 0x44, 0xc7, 0x07, 0x82, 0x65, 0x78,
1960x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0xfe, 0x02, 0x02, 0x16,
1970x08, 0x30, 0x66, 0x61, 0x69, 0x6c, 0x06, 0x10, 0x27, 0xa7,
1980x01, 0x02, 0x37, 0x03, 0x02, 0x99, 0x06, 0x03, 0x9b, 0x07,
1990x41, 0x3a, 0x00, 0x77, 0x68, 0xbb, 0x02, 0x01, 0x85, 0x04,
2000x04, 0x08, 0x01, 0x00, 0x3f, 0x00, 0x20, 0x68, 0x61, 0xa3,
2010x08, 0xa1, 0x65, 0x6e, 0x00, 0x65, 0x78, 0x70, 0x6c, 0x69,
2020x63, 0x69, 0xb1, 0x06, 0x04, 0x61, 0x00, 0x05, 0x23, 0x04,
2030x04, 0xf5, 0x07, 0x00, 0x53, 0x00, 0x01, 0x20, 0x05, 0x15,
2040x2d, 0xf3, 0x04, 0x36, 0x69, 0x74, 0x2c, 0xdf, 0x04, 0x50,
2050x69, 0x73, 0x00, 0x73, 0x74, 0xd4, 0x00, 0x00, 0x70, 0x08,
2060x40, 0x6e, 0x2c, 0x00, 0x6a, 0x72, 0x08, 0x01, 0xfb, 0x07,
2070x01, 0xa1, 0x00, 0x50, 0x00, 0x67, 0x72, 0x65, 0x79, 0x3b,
2080x07, 0x44, 0x6f, 0x75, 0x72, 0x2e, 0xc3, 0x05, 0x03, 0x21,
2090x05, 0x25, 0x69, 0x73, 0x28, 0x01, 0x36, 0x66, 0x66, 0x2c,
2100x84, 0x00, 0x04, 0xf6, 0x08, 0x11, 0x65, 0xc1, 0x05, 0x01,
2110x58, 0x00, 0x30, 0x00, 0x61, 0x74, 0x47, 0x02, 0x10, 0x2e,
2120x12, 0x01, 0x52, 0x41, 0x75, 0x74, 0x6f, 0x2d, 0x2c, 0x01,
2130x60, 0x00, 0x75, 0x6e, 0x69, 0x71, 0x75, 0xf2, 0x04, 0x21,
2140x74, 0x68, 0xd3, 0x02, 0x58, 0x65, 0x64, 0x67, 0x65, 0x73,
2150x13, 0x01, 0x27, 0x66, 0x66, 0x14, 0x01, 0x30, 0x2e, 0x00,
2160x57, 0x14, 0x01, 0x01, 0x76, 0x04, 0x18, 0x6f, 0xc5, 0x05,
2170x01, 0xf3, 0x03, 0x00, 0xb6, 0x08, 0x02, 0xfb, 0x00, 0x41,
2180x74, 0x61, 0x74, 0x75, 0x4f, 0x00, 0x05, 0x17, 0x0a, 0x05,
2190x78, 0x01, 0x02, 0xc0, 0x01, 0x71, 0x70, 0x6f, 0x74, 0x65,
2200x6e, 0x74, 0x69, 0x2b, 0x08, 0x82, 0x70, 0x72, 0x6f, 0x70,
2210x61, 0x67, 0x61, 0x74, 0x3c, 0x00, 0x03, 0x47, 0x00, 0x56,
2220x61, 0x6c, 0x6f, 0x6e, 0x67, 0xea, 0x03, 0x01, 0xd2, 0x00,
2230x40, 0x2c, 0x00, 0x69, 0x66, 0xae, 0x08, 0x00, 0xce, 0x03,
2240x98, 0x62, 0x6f, 0x74, 0x68, 0x00, 0x65, 0x6e, 0x64, 0x73,
2250xea, 0x09, 0x02, 0x13, 0x02, 0x00, 0x94, 0x00, 0x25, 0x65,
2260x64, 0xea, 0x0a, 0x72, 0x74, 0x6f, 0x00, 0x6f, 0x6e, 0x6c,
2270x79, 0x39, 0x00, 0x01, 0x0b, 0x05, 0x00, 0x2b, 0x00, 0x53,
2280x2e, 0x00, 0x28, 0x54, 0x68, 0xd8, 0x08, 0x13, 0x69, 0xa0,
2290x02, 0x63, 0x69, 0x66, 0x00, 0x74, 0x77, 0x6f, 0x3e, 0x01,
2300x41, 0x6d, 0x65, 0x65, 0x74, 0x35, 0x01, 0x04, 0x53, 0x04,
2310x00, 0x72, 0x03, 0x27, 0x6e, 0x6f, 0x43, 0x00, 0x43, 0x73,
2320x00, 0x64, 0x6f, 0x57, 0x01, 0x01, 0xcf, 0x01, 0x13, 0x6e,
2330x20, 0x0b, 0x06, 0x7e, 0x01, 0x11, 0x65, 0x52, 0x08, 0x02,
2340xac, 0x00, 0x0c, 0x47, 0x07, 0x00, 0xc5, 0x00, 0x12, 0x6e,
2350x21, 0x00, 0x50, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x09, 0x00,
2360x30, 0x65, 0x27, 0x73, 0x63, 0x00, 0x61, 0x72, 0x65, 0x61,
2370x73, 0x6f, 0x6e, 0xcd, 0x00, 0x65, 0x73, 0x68, 0x6f, 0x75,
2380x6c, 0x64, 0xa7, 0x05, 0x02, 0x72, 0x07, 0x40, 0x73, 0x65,
2390x70, 0x61, 0x8d, 0x03, 0x01, 0xbe, 0x0b, 0x00, 0xc1, 0x0b,
2400x00, 0x61, 0x01, 0x40, 0x65, 0x61, 0x63, 0x68, 0xe4, 0x00,
2410x26, 0x2e, 0x29, 0x65, 0x0a, 0x00, 0xed, 0x04, 0x70, 0x00,
2420x60, 0x42, 0x61, 0x73, 0x65, 0x64, 0xdc, 0x02, 0x02, 0x4b,
2430x0a, 0x42, 0x6e, 0x6c, 0x79, 0x27, 0x7f, 0x02, 0x63, 0x65,
2440x66, 0x66, 0x65, 0x63, 0x74, 0xa8, 0x01, 0x02, 0x5b, 0x00,
2450x01, 0x9d, 0x01, 0x29, 0x6f, 0x6e, 0x96, 0x01, 0x03, 0x87,
2460x06, 0x07, 0x1f, 0x05, 0x23, 0x61, 0x74, 0x13, 0x05, 0x83,
2470x65, 0x67, 0x72, 0x65, 0x65, 0x00, 0x32, 0x00, 0xd3, 0x0b,
2480x70, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x11, 0x02,
2490x02, 0xde, 0x0b, 0x10, 0x46, 0x70, 0x0a, 0x20, 0x78, 0x61,
2500xfd, 0x07, 0x14, 0x2c, 0x25, 0x00, 0x07, 0xc1, 0x06, 0x08,
2510x87, 0x00, 0x07, 0x7b, 0x00, 0x30, 0x6f, 0x63, 0x63, 0x8a,
2520x0c, 0x13, 0x74, 0xd4, 0x03, 0x22, 0x75, 0x72, 0xba, 0x00,
2530x41, 0x63, 0x6f, 0x72, 0x6e, 0xfc, 0x0b, 0x0f, 0xe0, 0x00,
2540x0a, 0x00, 0xa7, 0x01, 0x32, 0x67, 0x61, 0x6d, 0x85, 0x02,
2550x13, 0x65, 0xea, 0x00, 0x04, 0xcd, 0x00, 0x00, 0x2b, 0x03,
2560x01, 0x68, 0x00, 0x01, 0x14, 0x08, 0x30, 0x74, 0x61, 0x6b,
2570xde, 0x00, 0x01, 0x2d, 0x07, 0x05, 0xfc, 0x02, 0x00, 0x77,
2580x01, 0x11, 0x27, 0x74, 0x05, 0x4f, 0x72, 0x65, 0x61, 0x64,
2590xd9, 0x03, 0x09, 0x03, 0xb7, 0x01, 0x00, 0x39, 0x02, 0x03,
2600xbe, 0x00, 0x27, 0x64, 0x6f, 0x5e, 0x0b, 0xe0, 0x77, 0x6f,
2610x72, 0x6b, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x79, 0x6f, 0x75,
2620x2e, 0x00,
185}; 263};
186 264
187const unsigned short help_text_len = 2260; 265const unsigned short help_text_len = 3584;
188const unsigned short help_text_words = 414; 266const unsigned short help_text_words = 660;
267const bool help_valid = true;
189const char quick_help_text[] = "Draw a single closed loop, given clues about number of adjacent edges."; 268const char quick_help_text[] = "Draw a single closed loop, given clues about number of adjacent edges.";
diff --git a/apps/plugins/puzzles/help/magnets.c b/apps/plugins/puzzles/help/magnets.c
index 8c53b2f127..aaaa3bcdad 100644
--- a/apps/plugins/puzzles/help/magnets.c
+++ b/apps/plugins/puzzles/help/magnets.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,187 +6,189 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 143, TEXT_CENTER | C_RED }, 9 { 144, TEXT_CENTER | C_RED },
10 { 323, TEXT_CENTER | C_RED }, 10 { 324, TEXT_CENTER | C_RED },
11 { 340, TEXT_UNDERLINE },
12 { 341, TEXT_UNDERLINE }, 11 { 341, TEXT_UNDERLINE },
13 { 353, TEXT_UNDERLINE }, 12 { 342, TEXT_UNDERLINE },
14 { 355, TEXT_UNDERLINE }, 13 { 354, TEXT_UNDERLINE },
15 { 388, TEXT_UNDERLINE }, 14 { 356, TEXT_UNDERLINE },
16 { 416, TEXT_UNDERLINE }, 15 { 389, TEXT_UNDERLINE },
16 { 417, TEXT_UNDERLINE },
17 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
18}; 18};
19 19
20/* orig 2502 comp 1653 ratio 0.660671 level 10 saved 849 */ 20/* orig 2522 comp 1662 ratio 0.659001 level 10 saved 860 */
21const char help_text[] = { 21const char help_text[] = {
220xf2, 0x3e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
230x33, 0x33, 0x3a, 0x20, 0x4d, 0x61, 0x67, 0x6e, 0x65, 0x74, 230x33, 0x33, 0x3a, 0x20, 0x4d, 0x61, 0x67, 0x6e, 0x65, 0x74,
240x73, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x72, 0x65, 0x63, 240x73, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf2, 0x2a, 0x00, 0x00,
250x74, 0x61, 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x00, 0x67, 250x00, 0x41, 0x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67,
260x72, 0x69, 0x64, 0x00, 0x68, 0x61, 0x73, 0x00, 0x62, 0x65, 260x75, 0x6c, 0x61, 0x72, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00,
270x65, 0x6e, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 270x68, 0x61, 0x73, 0x00, 0x62, 0x65, 0x65, 0x6e, 0x00, 0x66,
280x77, 0x69, 0x74, 0x68, 0x00, 0x61, 0x00, 0x6d, 0x69, 0x78, 280x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68,
290x74, 0x75, 0x72, 0x65, 0x00, 0x6f, 0x66, 0x00, 0x6d, 0x40, 290x00, 0x61, 0x00, 0x6d, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65,
300x00, 0xf2, 0x04, 0x00, 0x28, 0x74, 0x68, 0x61, 0x74, 0x00, 300x00, 0x6f, 0x66, 0x00, 0x6d, 0x54, 0x00, 0xf2, 0x04, 0x00,
310x69, 0x73, 0x2c, 0x00, 0x64, 0x6f, 0x6d, 0x69, 0x6e, 0x6f, 310x28, 0x74, 0x68, 0x61, 0x74, 0x00, 0x69, 0x73, 0x2c, 0x00,
320x65, 0x73, 0x2d, 0x00, 0xf1, 0x05, 0x6f, 0x6e, 0x65, 0x00, 320x64, 0x6f, 0x6d, 0x69, 0x6e, 0x6f, 0x65, 0x73, 0x2d, 0x00,
330x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x00, 0x65, 330xf1, 0x05, 0x6f, 0x6e, 0x65, 0x00, 0x70, 0x6f, 0x73, 0x69,
340x6e, 0x64, 0x00, 0x61, 0x6e, 0x64, 0x15, 0x00, 0x44, 0x6e, 340x74, 0x69, 0x76, 0x65, 0x00, 0x65, 0x6e, 0x64, 0x00, 0x61,
350x65, 0x67, 0x61, 0x15, 0x00, 0x11, 0x29, 0x16, 0x00, 0x56, 350x6e, 0x64, 0x15, 0x00, 0x44, 0x6e, 0x65, 0x67, 0x61, 0x15,
360x62, 0x6c, 0x61, 0x6e, 0x6b, 0x3f, 0x00, 0x0f, 0x52, 0x00, 360x00, 0x11, 0x29, 0x16, 0x00, 0x56, 0x62, 0x6c, 0x61, 0x6e,
370x05, 0xf6, 0x0a, 0x74, 0x77, 0x6f, 0x00, 0x6e, 0x65, 0x75, 370x6b, 0x3f, 0x00, 0x0f, 0x52, 0x00, 0x05, 0xf6, 0x0a, 0x74,
380x74, 0x72, 0x61, 0x6c, 0x00, 0x70, 0x6f, 0x6c, 0x65, 0x73, 380x77, 0x6f, 0x00, 0x6e, 0x65, 0x75, 0x74, 0x72, 0x61, 0x6c,
390x29, 0x2e, 0x00, 0x54, 0x68, 0x65, 0x73, 0x65, 0x28, 0x00, 390x00, 0x70, 0x6f, 0x6c, 0x65, 0x73, 0x29, 0x2e, 0x00, 0x54,
400xf0, 0x05, 0x61, 0x72, 0x65, 0x00, 0x69, 0x6e, 0x69, 0x74, 400x68, 0x65, 0x73, 0x65, 0x28, 0x00, 0xf0, 0x05, 0x61, 0x72,
410x69, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 410x65, 0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x6c,
420x00, 0x73, 0xc6, 0x00, 0xf2, 0x0a, 0x69, 0x6e, 0x00, 0x73, 420x79, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x73, 0xc6, 0x00,
430x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x2e, 430xf2, 0x0a, 0x69, 0x6e, 0x00, 0x73, 0x69, 0x6c, 0x68, 0x6f,
440x00, 0x41, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x00, 0x74, 0x68, 440x75, 0x65, 0x74, 0x74, 0x65, 0x2e, 0x00, 0x41, 0x72, 0x6f,
450x65, 0xee, 0x00, 0x00, 0x37, 0x00, 0xf0, 0x00, 0x70, 0x6c, 450x75, 0x6e, 0x64, 0x00, 0x74, 0x68, 0x65, 0xee, 0x00, 0x00,
460x61, 0x63, 0x65, 0x64, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 460x37, 0x00, 0xf0, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64,
470x62, 0x65, 0x72, 0xe3, 0x00, 0xf1, 0x01, 0x63, 0x6c, 0x75, 470x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xe3,
480x65, 0x73, 0x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 480x00, 0xf1, 0x01, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x00, 0x69,
490x69, 0x6e, 0x67, 0x31, 0x00, 0x06, 0x1f, 0x00, 0x05, 0xde, 490x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x31,
500x00, 0x00, 0xc4, 0x00, 0x05, 0xd6, 0x00, 0x01, 0x9a, 0x00, 500x00, 0x06, 0x1f, 0x00, 0x05, 0xde, 0x00, 0x00, 0xc4, 0x00,
510xa0, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 510x05, 0xd6, 0x00, 0x01, 0x9a, 0x00, 0xa0, 0x00, 0x63, 0x6f,
520x64, 0x7b, 0x00, 0x50, 0x63, 0x65, 0x72, 0x74, 0x61, 0x08, 520x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x7b, 0x00, 0x50,
530x00, 0x61, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x30, 0x00, 530x63, 0x65, 0x72, 0x74, 0x61, 0x08, 0x00, 0x61, 0x6f, 0x6c,
540xf0, 0x0b, 0x72, 0x6f, 0x77, 0x73, 0x2e, 0x00, 0x00, 0x00, 540x75, 0x6d, 0x6e, 0x73, 0x30, 0x00, 0xf0, 0x0b, 0x72, 0x6f,
550x59, 0x6f, 0x75, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 550x77, 0x73, 0x2e, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x72,
560x73, 0x00, 0x74, 0x6f, 0x00, 0x63, 0x6f, 0x72, 0x8c, 0x01, 560x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f,
570x22, 0x6c, 0x79, 0x90, 0x00, 0x01, 0x72, 0x00, 0x04, 0x6a, 570x00, 0x63, 0x6f, 0x72, 0x8c, 0x01, 0x22, 0x6c, 0x79, 0x90,
580x01, 0x0f, 0x2b, 0x01, 0x00, 0x51, 0x73, 0x75, 0x63, 0x68, 580x00, 0x01, 0x72, 0x00, 0x04, 0x6a, 0x01, 0x0f, 0x2b, 0x01,
590x00, 0x2f, 0x01, 0x31, 0x61, 0x6c, 0x6c, 0x2d, 0x00, 0x02, 590x00, 0x51, 0x73, 0x75, 0x63, 0x68, 0x00, 0x2f, 0x01, 0x31,
600xb4, 0x00, 0x00, 0xd1, 0x00, 0xa3, 0x73, 0x61, 0x74, 0x69, 600x61, 0x6c, 0x6c, 0x2d, 0x00, 0x02, 0xb4, 0x00, 0x00, 0xd1,
610x73, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x3f, 0x01, 0xd0, 0x68, 610x00, 0xa3, 0x73, 0x61, 0x74, 0x69, 0x73, 0x66, 0x69, 0x65,
620x65, 0x00, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 620x64, 0x2c, 0x3f, 0x01, 0xd0, 0x68, 0x65, 0x00, 0x61, 0x64,
630x61, 0x6c, 0xa2, 0x00, 0x72, 0x73, 0x74, 0x72, 0x61, 0x69, 630x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0xa2, 0x00,
640x6e, 0x74, 0x41, 0x00, 0x21, 0x6e, 0x6f, 0x61, 0x01, 0x40, 640x72, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x41, 0x00,
650x73, 0x69, 0x6d, 0x69, 0x06, 0x02, 0x02, 0x75, 0x00, 0x23, 650x21, 0x6e, 0x6f, 0x61, 0x01, 0x40, 0x73, 0x69, 0x6d, 0x69,
660x69, 0x63, 0xd0, 0x00, 0xf1, 0x00, 0x6d, 0x61, 0x79, 0x00, 660x06, 0x02, 0x02, 0x75, 0x00, 0x23, 0x69, 0x63, 0xd0, 0x00,
670x62, 0x65, 0x00, 0x6f, 0x72, 0x74, 0x68, 0x6f, 0x67, 0x6f, 670xf1, 0x00, 0x6d, 0x61, 0x79, 0x00, 0x62, 0x65, 0x00, 0x6f,
680x6e, 0x5f, 0x01, 0xd2, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 680x72, 0x74, 0x68, 0x6f, 0x67, 0x6f, 0x6e, 0x5f, 0x01, 0xd2,
690x6e, 0x74, 0x00, 0x28, 0x73, 0x69, 0x6e, 0xac, 0x00, 0xb8, 690x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x00, 0x28,
700x79, 0x00, 0x72, 0x65, 0x70, 0x65, 0x6c, 0x29, 0x2e, 0x00, 700x73, 0x69, 0x6e, 0xac, 0x00, 0xb8, 0x79, 0x00, 0x72, 0x65,
710x4e, 0xa9, 0x01, 0x72, 0x00, 0x64, 0x6f, 0x00, 0x6e, 0x6f, 710x70, 0x65, 0x6c, 0x29, 0x2e, 0x00, 0x4e, 0xa9, 0x01, 0x72,
720x74, 0x1d, 0x00, 0x11, 0x2c, 0xc9, 0x00, 0x30, 0x63, 0x61, 720x00, 0x64, 0x6f, 0x00, 0x6e, 0x6f, 0x74, 0x1d, 0x00, 0x11,
730x6e, 0x51, 0x00, 0x05, 0x44, 0x00, 0xc1, 0x74, 0x6f, 0x00, 730x2c, 0xc9, 0x00, 0x30, 0x63, 0x61, 0x6e, 0x51, 0x00, 0x05,
740x61, 0x6e, 0x79, 0x00, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x35, 740x44, 0x00, 0xc1, 0x74, 0x6f, 0x00, 0x61, 0x6e, 0x79, 0x00,
750x00, 0x00, 0x1d, 0x01, 0xf1, 0x0b, 0x43, 0x72, 0x65, 0x64, 750x6f, 0x74, 0x68, 0x65, 0x72, 0x35, 0x00, 0x00, 0x1d, 0x01,
760x69, 0x74, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x74, 0x68, 0x69, 760xf1, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x66,
770x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 770x6f, 0x72, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75,
780x6f, 0x65, 0x2d, 0x01, 0xa0, 0x4a, 0x61, 0x6e, 0x6b, 0x6f, 780x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0x2d, 0x01,
790x00, 0x5b, 0x31, 0x36, 0x5d, 0x2d, 0x00, 0x13, 0x4d, 0x27, 790xa0, 0x4a, 0x61, 0x6e, 0x6b, 0x6f, 0x00, 0x5b, 0x31, 0x36,
800x01, 0x22, 0x77, 0x61, 0x7f, 0x01, 0x70, 0x72, 0x69, 0x62, 800x5d, 0x2d, 0x00, 0x13, 0x4d, 0x27, 0x01, 0x22, 0x77, 0x61,
810x75, 0x74, 0x65, 0x64, 0x29, 0x00, 0x01, 0x3d, 0x00, 0x60, 810x7f, 0x01, 0x70, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
820x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0xfe, 0x00, 0xf0, 0x01, 820x29, 0x00, 0x01, 0x3d, 0x00, 0x60, 0x63, 0x6f, 0x6c, 0x6c,
830x00, 0x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 830x65, 0x63, 0xfe, 0x00, 0xf0, 0x01, 0x00, 0x62, 0x79, 0x00,
840x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x3e, 0x00, 0x00, 0x46, 840x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76,
850x00, 0xd0, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 850x65, 0x79, 0x3e, 0x00, 0x00, 0x46, 0x00, 0xd0, 0x00, 0x68,
860x77, 0x77, 0x77, 0x2e, 0x6a, 0x5c, 0x00, 0xc2, 0x2e, 0x61, 860x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
870x74, 0x2f, 0x52, 0x61, 0x65, 0x74, 0x73, 0x65, 0x6c, 0x2f, 870x6a, 0x5c, 0x00, 0xc2, 0x2e, 0x61, 0x74, 0x2f, 0x52, 0x61,
880x5f, 0x00, 0xf5, 0x03, 0x65, 0x2f, 0x69, 0x6e, 0x64, 0x65, 880x65, 0x74, 0x73, 0x65, 0x6c, 0x2f, 0x5f, 0x00, 0xf5, 0x03,
890x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x33, 0x33, 890x65, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74,
900x2e, 0x31, 0x49, 0x03, 0x01, 0x74, 0x00, 0x21, 0x6f, 0x6c, 900x6d, 0x00, 0x00, 0x00, 0x33, 0x33, 0x2e, 0x31, 0x5d, 0x03,
910x52, 0x03, 0xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 910x01, 0x74, 0x00, 0xf0, 0x02, 0x6f, 0x6c, 0x73, 0x20, 0x00,
920x69, 0x63, 0x6b, 0x37, 0x02, 0xf5, 0x00, 0x6f, 0x6e, 0x00, 920x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69,
930x61, 0x6e, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00, 0x73, 930x63, 0x6b, 0x37, 0x02, 0xf5, 0x00, 0x6f, 0x6e, 0x00, 0x61,
940x71, 0x75, 0x6e, 0x02, 0x33, 0x73, 0x00, 0x61, 0x68, 0x01, 940x6e, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00, 0x73, 0x71,
950x23, 0x00, 0x61, 0x86, 0x01, 0x02, 0x54, 0x02, 0x26, 0x6f, 950x75, 0x6e, 0x02, 0x33, 0x73, 0x00, 0x61, 0x68, 0x01, 0x23,
960x6e, 0xb3, 0x01, 0x05, 0x66, 0x02, 0x00, 0x15, 0x01, 0x00, 960x00, 0x61, 0x86, 0x01, 0x02, 0x54, 0x02, 0x26, 0x6f, 0x6e,
970x4b, 0x00, 0x00, 0x15, 0x00, 0x03, 0x46, 0x00, 0x13, 0x61, 970xb3, 0x01, 0x05, 0x66, 0x02, 0x00, 0x15, 0x01, 0x00, 0x4b,
980xc5, 0x02, 0x09, 0x7d, 0x02, 0x04, 0x24, 0x00, 0x02, 0x4b, 980x00, 0x00, 0x15, 0x00, 0x03, 0x46, 0x00, 0x13, 0x61, 0xc5,
990x01, 0x40, 0x68, 0x61, 0x6c, 0x66, 0xad, 0x02, 0x06, 0x49, 990x02, 0x09, 0x7d, 0x02, 0x04, 0x24, 0x00, 0x02, 0x4b, 0x01,
1000x02, 0x39, 0x3b, 0x00, 0x6c, 0x9e, 0x00, 0x20, 0x61, 0x67, 1000x40, 0x68, 0x61, 0x6c, 0x66, 0xad, 0x02, 0x06, 0x49, 0x02,
1010x9c, 0x02, 0x83, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 1010x39, 0x3b, 0x00, 0x6c, 0x9e, 0x00, 0x20, 0x61, 0x67, 0x9c,
1020x73, 0x74, 0x00, 0x62, 0x6c, 0x61, 0x72, 0x69, 0x74, 0x79, 1020x02, 0x83, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x73,
1030xab, 0x01, 0x10, 0x61, 0x40, 0x01, 0x31, 0x72, 0x64, 0x00, 1030x74, 0x00, 0x62, 0x6c, 0x61, 0x72, 0x69, 0x74, 0x79, 0xab,
1040x32, 0x00, 0x87, 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 1040x01, 0x10, 0x61, 0x40, 0x01, 0x31, 0x72, 0x64, 0x00, 0x32,
1050x73, 0x51, 0x00, 0x00, 0x3f, 0x01, 0x4f, 0x52, 0x69, 0x67, 1050x00, 0x87, 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73,
1060x68, 0xf2, 0x00, 0x14, 0x08, 0xc3, 0x02, 0x10, 0x00, 0xa4, 1060x51, 0x00, 0x00, 0x3f, 0x01, 0x4f, 0x52, 0x69, 0x67, 0x68,
1070x00, 0x2c, 0x65, 0x2e, 0x3f, 0x00, 0x02, 0x93, 0x00, 0x03, 1070xf2, 0x00, 0x14, 0x08, 0xc3, 0x02, 0x10, 0x00, 0xa4, 0x00,
1080x32, 0x00, 0x00, 0x96, 0x02, 0x41, 0x71, 0x75, 0x65, 0x73, 1080x2c, 0x65, 0x2e, 0x3f, 0x00, 0x02, 0x93, 0x00, 0x03, 0x32,
1090x17, 0x01, 0x54, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0xe2, 0x00, 1090x00, 0x00, 0x96, 0x02, 0x41, 0x71, 0x75, 0x65, 0x73, 0x17,
1100x02, 0x44, 0x00, 0x90, 0x2c, 0x00, 0x73, 0x69, 0x67, 0x6e, 1100x01, 0x54, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0xe2, 0x00, 0x02,
1110x69, 0x66, 0x79, 0x3a, 0x00, 0x12, 0x60, 0xeb, 0x01, 0x20, 1110x44, 0x00, 0x90, 0x2c, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x69,
1120x61, 0x6e, 0x6f, 0x02, 0x22, 0x62, 0x65, 0x6d, 0x00, 0x84, 1120x66, 0x79, 0x3a, 0x00, 0x12, 0x60, 0xeb, 0x01, 0x20, 0x61,
1130x27, 0x00, 0x28, 0x77, 0x68, 0x69, 0x63, 0x68, 0x75, 0x02, 1130x6e, 0x6f, 0x02, 0x22, 0x62, 0x65, 0x6d, 0x00, 0x84, 0x27,
1140x81, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x00, 0x74, 0x95, 1140x00, 0x28, 0x77, 0x68, 0x69, 0x63, 0x68, 0x75, 0x02, 0x81,
1150x02, 0x61, 0x65, 0x00, 0x64, 0x65, 0x64, 0x75, 0x18, 0x02, 1150x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x00, 0x74, 0x95, 0x02,
1160xf2, 0x02, 0x73, 0x00, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x00, 1160x61, 0x65, 0x00, 0x64, 0x65, 0x64, 0x75, 0x18, 0x02, 0xf2,
1170x73, 0x6f, 0x6c, 0x76, 0x69, 0x6e, 0x67, 0x29, 0x2c, 0xaa, 1170x02, 0x73, 0x00, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x00, 0x73,
1180x03, 0x0e, 0x9c, 0x00, 0x02, 0xdb, 0x00, 0x37, 0x69, 0x65, 1180x6f, 0x6c, 0x76, 0x69, 0x6e, 0x67, 0x29, 0x2c, 0xaa, 0x03,
1190x73, 0x87, 0x00, 0x1d, 0x2e, 0xf7, 0x01, 0x11, 0x61, 0x8d, 1190x0e, 0x9c, 0x00, 0x02, 0xdb, 0x00, 0x37, 0x69, 0x65, 0x73,
1200x03, 0x51, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0xb8, 0x00, 0x50, 1200x87, 0x00, 0x1d, 0x2e, 0xf7, 0x01, 0x11, 0x61, 0x8d, 0x03,
1210x00, 0x69, 0x74, 0x00, 0x61, 0x0c, 0x03, 0x80, 0x6e, 0x65, 1210x51, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0xb8, 0x00, 0x50, 0x00,
1220x00, 0x28, 0x67, 0x72, 0x65, 0x79, 0x11, 0x00, 0xb4, 0x6f, 1220x69, 0x74, 0x00, 0x61, 0x0c, 0x03, 0x80, 0x6e, 0x65, 0x00,
1230x75, 0x74, 0x29, 0x2c, 0x00, 0x6f, 0x72, 0x00, 0x75, 0x6e, 1230x28, 0x67, 0x72, 0x65, 0x79, 0x11, 0x00, 0xb4, 0x6f, 0x75,
1240x24, 0x00, 0x21, 0x69, 0x66, 0x06, 0x00, 0x91, 0x73, 0x00, 1240x74, 0x29, 0x2c, 0x00, 0x6f, 0x72, 0x00, 0x75, 0x6e, 0x24,
1250x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x3d, 0x00, 0x23, 1250x00, 0x21, 0x69, 0x66, 0x06, 0x00, 0x91, 0x73, 0x00, 0x61,
1260x65, 0x64, 0x2e, 0x04, 0x01, 0xc5, 0x00, 0x40, 0x61, 0x6c, 1260x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x3d, 0x00, 0x23, 0x65,
1270x73, 0x6f, 0xc7, 0x00, 0x02, 0xf3, 0x03, 0x91, 0x75, 0x72, 1270x64, 0x2e, 0x04, 0x01, 0xc5, 0x00, 0x40, 0x61, 0x6c, 0x73,
1280x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x16, 0x03, 0x20, 1280x6f, 0xc7, 0x00, 0x02, 0xf3, 0x03, 0x91, 0x75, 0x72, 0x73,
1290x6d, 0x6f, 0x93, 0x04, 0x04, 0x16, 0x00, 0x1a, 0x61, 0xeb, 1290x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x16, 0x03, 0x20, 0x6d,
1300x04, 0x74, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0xcd, 1300x6f, 0x93, 0x04, 0x04, 0x16, 0x00, 0x1a, 0x61, 0xeb, 0x04,
1310x04, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x00, 1310x74, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0xcd, 0x04,
1320x02, 0xa7, 0x00, 0x54, 0x6c, 0x61, 0x79, 0x00, 0x61, 0x9c, 1320x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x00, 0x02,
1330x01, 0x03, 0xf4, 0x05, 0x0a, 0x81, 0x02, 0x0c, 0xa9, 0x02, 1330xa7, 0x00, 0x54, 0x6c, 0x61, 0x79, 0x00, 0x61, 0x9c, 0x01,
1340x34, 0x3b, 0x00, 0x70, 0x51, 0x00, 0x0f, 0x49, 0x02, 0x08, 1340x03, 0xf4, 0x05, 0x0a, 0x81, 0x02, 0x0c, 0xa9, 0x02, 0x34,
1350x04, 0xaa, 0x02, 0x19, 0x6e, 0x3f, 0x02, 0x04, 0xc0, 0x01, 1350x3b, 0x00, 0x70, 0x51, 0x00, 0x0f, 0x49, 0x02, 0x08, 0x04,
1360x13, 0x61, 0xe1, 0x05, 0x09, 0x98, 0x02, 0x35, 0x2e, 0x00, 1360xaa, 0x02, 0x19, 0x6e, 0x3f, 0x02, 0x04, 0xc0, 0x01, 0x13,
1370x55, 0xa7, 0x00, 0x20, 0x73, 0x70, 0x0c, 0x05, 0x30, 0x62, 1370x61, 0xe1, 0x05, 0x09, 0x98, 0x02, 0x35, 0x2e, 0x00, 0x55,
1380x61, 0x72, 0xe7, 0x04, 0x32, 0x6f, 0x77, 0x73, 0x1b, 0x02, 1380xa7, 0x00, 0x20, 0x73, 0x70, 0x0c, 0x05, 0x30, 0x62, 0x61,
1390x10, 0x6d, 0x38, 0x04, 0x2c, 0x6f, 0x66, 0x14, 0x05, 0x03, 1390x72, 0xe7, 0x04, 0x32, 0x6f, 0x77, 0x73, 0x1b, 0x02, 0x10,
1400x5e, 0x04, 0x72, 0x6e, 0x6f, 0x74, 0x2d, 0x62, 0x65, 0x2d, 1400x6d, 0x38, 0x04, 0x2c, 0x6f, 0x66, 0x14, 0x05, 0x03, 0x5e,
1410x1d, 0x00, 0x51, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x69, 0x00, 1410x04, 0x72, 0x6e, 0x6f, 0x74, 0x2d, 0x62, 0x65, 0x2d, 0x1d,
1420x00, 0x4c, 0x04, 0x02, 0xd2, 0x01, 0x16, 0x00, 0x6a, 0x00, 1420x00, 0x51, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x69, 0x00, 0x00,
1430x43, 0x00, 0x00, 0x28, 0x41, 0x40, 0x05, 0x13, 0x61, 0x0a, 1430x4c, 0x04, 0x02, 0xd2, 0x01, 0x16, 0x00, 0x6a, 0x00, 0x43,
1440x02, 0x72, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0xc1, 1440x00, 0x00, 0x28, 0x41, 0x40, 0x05, 0x13, 0x61, 0x0a, 0x02,
1450x05, 0x13, 0x73, 0x38, 0x04, 0x31, 0x32, 0x2e, 0x31, 0x5b, 1450x72, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0xc1, 0x05,
1460x05, 0x01, 0x7f, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 1460x13, 0x73, 0x38, 0x04, 0x31, 0x32, 0x2e, 0x31, 0x5b, 0x05,
1470x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x0b, 0x04, 0x15, 0x32, 1470x01, 0x7f, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,
1480x0b, 0x04, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 1480x62, 0x6c, 0x65, 0x2e, 0x29, 0x0b, 0x04, 0x15, 0x32, 0x0b,
1490x65, 0x72, 0x0d, 0x04, 0x02, 0xa4, 0x06, 0x06, 0x14, 0x00, 1490x04, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
1500x02, 0x43, 0x00, 0x04, 0x3e, 0x00, 0x51, 0x00, 0x66, 0x72, 1500x72, 0x0d, 0x04, 0x02, 0xa4, 0x06, 0x06, 0x14, 0x00, 0x02,
1510x6f, 0x6d, 0x7b, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 1510x43, 0x00, 0x04, 0x3e, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f,
1520x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 1520x6d, 0x7b, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f,
1530x00, 0x03, 0xe9, 0x02, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 1530x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 0x00,
1540x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xad, 0x00, 0x90, 0x57, 1540x03, 0xe9, 0x02, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27,
1550x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xc8, 0x00, 1550x00, 0x6d, 0x65, 0x6e, 0x75, 0xad, 0x00, 0x90, 0x57, 0x69,
1560x61, 0x00, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x93, 0x07, 0x01, 1560x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xc8, 0x00, 0x61,
1570xcc, 0x06, 0x23, 0x69, 0x6e, 0x72, 0x03, 0x11, 0x73, 0x1e, 1570x00, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x93, 0x07, 0x01, 0xcc,
1580x07, 0x13, 0x72, 0x81, 0x02, 0x22, 0x62, 0x65, 0x0b, 0x04, 1580x06, 0x23, 0x69, 0x6e, 0x72, 0x03, 0x11, 0x73, 0x1e, 0x07,
1590x01, 0x3c, 0x00, 0x24, 0x00, 0x78, 0x3d, 0x00, 0x05, 0x36, 1590x13, 0x72, 0x81, 0x02, 0x22, 0x62, 0x65, 0x0b, 0x04, 0x01,
1600x01, 0x25, 0x69, 0x6e, 0x24, 0x02, 0x10, 0x3a, 0x82, 0x02, 1600x3c, 0x00, 0x24, 0x00, 0x78, 0x3d, 0x00, 0x05, 0x36, 0x01,
1610x01, 0x48, 0x03, 0x03, 0xec, 0x06, 0x53, 0x69, 0x73, 0x00, 1610x25, 0x69, 0x6e, 0x24, 0x02, 0x10, 0x3a, 0x82, 0x02, 0x01,
1620x6f, 0x64, 0xc0, 0x01, 0x25, 0x6f, 0x6e, 0x7e, 0x04, 0x04, 1620x48, 0x03, 0x03, 0xec, 0x06, 0x53, 0x69, 0x73, 0x00, 0x6f,
1630x58, 0x00, 0x01, 0x63, 0x01, 0x01, 0x45, 0x01, 0x53, 0x47, 1630x64, 0xc0, 0x01, 0x25, 0x6f, 0x6e, 0x7e, 0x04, 0x04, 0x58,
1640x72, 0x69, 0x64, 0x73, 0x38, 0x02, 0x71, 0x74, 0x00, 0x6c, 1640x00, 0x01, 0x63, 0x01, 0x01, 0x45, 0x01, 0x53, 0x47, 0x72,
1650x65, 0x61, 0x73, 0x74, 0x31, 0x00, 0x00, 0x3e, 0x00, 0x70, 1650x69, 0x64, 0x73, 0x38, 0x02, 0x71, 0x74, 0x00, 0x6c, 0x65,
1660x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0xd2, 0x00, 0x21, 1660x61, 0x73, 0x74, 0x31, 0x00, 0x00, 0x3e, 0x00, 0x70, 0x64,
1670x65, 0x6e, 0x96, 0x05, 0x90, 0x62, 0x65, 0x00, 0x65, 0x61, 1670x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0xd2, 0x00, 0x21, 0x65,
1680x73, 0x69, 0x65, 0x72, 0x0d, 0x00, 0x00, 0x74, 0x03, 0x02, 1680x6e, 0x96, 0x05, 0x90, 0x62, 0x65, 0x00, 0x65, 0x61, 0x73,
1690x4a, 0x01, 0xa0, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 1690x69, 0x65, 0x72, 0x0d, 0x00, 0x00, 0x74, 0x03, 0x02, 0x4a,
1700x6c, 0x74, 0x79, 0x02, 0x06, 0x03, 0x55, 0x05, 0x02, 0x30, 1700x01, 0xa0, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c,
1710x02, 0x06, 0x1a, 0x00, 0x03, 0xd2, 0x04, 0x60, 0x67, 0x65, 1710x74, 0x79, 0x02, 0x06, 0x03, 0x55, 0x05, 0x02, 0x30, 0x02,
1720x6e, 0x65, 0x72, 0x61, 0xe6, 0x05, 0x02, 0x1b, 0x06, 0xf0, 1720x06, 0x1a, 0x00, 0x03, 0xd2, 0x04, 0x60, 0x67, 0x65, 0x6e,
1730x00, 0x2e, 0x00, 0x41, 0x74, 0x00, 0x54, 0x72, 0x69, 0x63, 1730x65, 0x72, 0x61, 0xe6, 0x05, 0x02, 0x1b, 0x06, 0xf0, 0x00,
1740x6b, 0x79, 0x00, 0x6c, 0x65, 0x76, 0x6d, 0x06, 0x31, 0x79, 1740x2e, 0x00, 0x41, 0x74, 0x00, 0x54, 0x72, 0x69, 0x63, 0x6b,
1750x6f, 0x75, 0x71, 0x01, 0x62, 0x72, 0x65, 0x71, 0x75, 0x69, 1750x79, 0x00, 0x6c, 0x65, 0x76, 0x6d, 0x06, 0x31, 0x79, 0x6f,
1760x72, 0x10, 0x06, 0x89, 0x6d, 0x61, 0x6b, 0x65, 0x00, 0x6d, 1760x75, 0x71, 0x01, 0x62, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
1770x6f, 0x72, 0xfc, 0x03, 0x53, 0x61, 0x62, 0x6f, 0x75, 0x74, 1770x10, 0x06, 0x89, 0x6d, 0x61, 0x6b, 0x65, 0x00, 0x6d, 0x6f,
1780xb4, 0x04, 0x09, 0x4d, 0x02, 0x42, 0x72, 0x6f, 0x77, 0x2f, 1780x72, 0xfc, 0x03, 0x53, 0x61, 0x62, 0x6f, 0x75, 0x74, 0xb4,
1790xbb, 0x07, 0x61, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0xb8, 1790x04, 0x09, 0x4d, 0x02, 0x42, 0x72, 0x6f, 0x77, 0x2f, 0xbb,
1800x07, 0x62, 0x53, 0x74, 0x72, 0x69, 0x70, 0x20, 0x6e, 0x07, 1800x07, 0x61, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0xb8, 0x07,
1810xe1, 0x00, 0x00, 0x49, 0x66, 0x00, 0x74, 0x72, 0x75, 0x65, 1810x62, 0x53, 0x74, 0x72, 0x69, 0x70, 0x20, 0x6e, 0x07, 0xe1,
1820x2c, 0x00, 0x73, 0x6f, 0x6d, 0x8b, 0x01, 0x08, 0x8b, 0x07, 1820x00, 0x00, 0x49, 0x66, 0x00, 0x74, 0x72, 0x75, 0x65, 0x2c,
1830x0e, 0x6c, 0x08, 0x02, 0x05, 0x03, 0x10, 0x64, 0x25, 0x01, 1830x00, 0x73, 0x6f, 0x6d, 0x8b, 0x01, 0x08, 0x8b, 0x07, 0x0e,
1840x03, 0xc2, 0x00, 0x01, 0x18, 0x01, 0x40, 0x69, 0x6d, 0x65, 1840x6c, 0x08, 0x02, 0x05, 0x03, 0x10, 0x64, 0x25, 0x01, 0x03,
1850x2c, 0x9c, 0x00, 0x04, 0xf7, 0x02, 0x03, 0xef, 0x06, 0x02, 1850xc2, 0x00, 0x01, 0x18, 0x01, 0x40, 0x69, 0x6d, 0x65, 0x2c,
1860xa9, 0x00, 0xa0, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 1860x9c, 0x00, 0x04, 0xf7, 0x02, 0x03, 0xef, 0x06, 0x02, 0xa9,
1870x74, 0x2e, 0x00, 1870x00, 0xa0, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
1880x2e, 0x00,
188}; 189};
189 190
190const unsigned short help_text_len = 2502; 191const unsigned short help_text_len = 2522;
191const unsigned short help_text_words = 438; 192const unsigned short help_text_words = 439;
193const bool help_valid = true;
192const char quick_help_text[] = "Place magnets to satisfy the clues and avoid like poles touching."; 194const char quick_help_text[] = "Place magnets to satisfy the clues and avoid like poles touching.";
diff --git a/apps/plugins/puzzles/help/map.c b/apps/plugins/puzzles/help/map.c
index 2f577ae6b2..05474e3181 100644
--- a/apps/plugins/puzzles/help/map.c
+++ b/apps/plugins/puzzles/help/map.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,251 +6,270 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 81, TEXT_UNDERLINE }, 9 { 87, TEXT_UNDERLINE },
10 { 157, TEXT_CENTER | C_RED }, 10 { 163, TEXT_CENTER | C_RED },
11 { 244, TEXT_UNDERLINE }, 11 { 250, TEXT_UNDERLINE },
12 { 268, TEXT_UNDERLINE }, 12 { 274, TEXT_UNDERLINE },
13 { 507, TEXT_CENTER | C_RED }, 13 { 513, TEXT_CENTER | C_RED },
14 { 524, TEXT_UNDERLINE }, 14 { 530, TEXT_UNDERLINE },
15 { 525, TEXT_UNDERLINE }, 15 { 531, TEXT_UNDERLINE },
16 { 535, TEXT_UNDERLINE }, 16 { 541, TEXT_UNDERLINE },
17 { 547, TEXT_UNDERLINE }, 17 { 553, TEXT_UNDERLINE },
18 { 650, TEXT_CENTER | C_RED },
18 LAST_STYLE_ITEM 19 LAST_STYLE_ITEM
19}; 20};
20 21
21/* orig 3466 comp 2283 ratio 0.658684 level 10 saved 1183 */ 22/* orig 3752 comp 2460 ratio 0.65565 level 10 saved 1292 */
22const char help_text[] = { 23const char help_text[] = {
230xf0, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 240xfa, 0x03, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
240x32, 0x32, 0x3a, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x00, 0x00, 250x32, 0x32, 0x3a, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x00, 0x2d,
250x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 260x01, 0x00, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75,
260x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x61, 0x70, 270x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e,
270x00, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x69, 0x6e, 280x00, 0x61, 0x00, 0x6d, 0x61, 0x70, 0x00, 0x63, 0x6f, 0x6e,
280x67, 0x00, 0x6f, 0x66, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 290x73, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x00, 0x6f, 0x66,
290x62, 0x65, 0x72, 0x0c, 0x00, 0x80, 0x72, 0x65, 0x67, 0x69, 300x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x0c,
300x6f, 0x6e, 0x73, 0x2e, 0x37, 0x00, 0xf0, 0x01, 0x72, 0x00, 310x00, 0x80, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
310x74, 0x61, 0x73, 0x6b, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 320x37, 0x00, 0xf0, 0x01, 0x72, 0x00, 0x74, 0x61, 0x73, 0x6b,
320x00, 0x63, 0x6f, 0x6c, 0x12, 0x00, 0x43, 0x65, 0x61, 0x63, 330x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x63, 0x6f, 0x6c,
330x68, 0x25, 0x00, 0x90, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 340x12, 0x00, 0x43, 0x65, 0x61, 0x63, 0x68, 0x25, 0x00, 0x90,
340x6f, 0x6e, 0x65, 0x38, 0x00, 0x10, 0x66, 0x1d, 0x00, 0x02, 350x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x6f, 0x6e, 0x65, 0x38,
350x24, 0x00, 0xf4, 0x0d, 0x73, 0x2c, 0x00, 0x69, 0x6e, 0x00, 360x00, 0x10, 0x66, 0x1d, 0x00, 0x02, 0x24, 0x00, 0xf4, 0x0d,
360x73, 0x75, 0x63, 0x68, 0x00, 0x61, 0x00, 0x77, 0x61, 0x79, 370x73, 0x2c, 0x00, 0x69, 0x6e, 0x00, 0x73, 0x75, 0x63, 0x68,
370x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x6e, 0x6f, 0x00, 0x74, 380x00, 0x61, 0x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61,
380x77, 0x6f, 0x60, 0x00, 0x50, 0x00, 0x73, 0x68, 0x61, 0x72, 390x74, 0x00, 0x6e, 0x6f, 0x00, 0x74, 0x77, 0x6f, 0x60, 0x00,
390x7f, 0x00, 0xf3, 0x09, 0x61, 0x00, 0x62, 0x6f, 0x75, 0x6e, 400x50, 0x00, 0x73, 0x68, 0x61, 0x72, 0x7f, 0x00, 0xf3, 0x09,
400x64, 0x61, 0x72, 0x79, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 410x61, 0x00, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79,
410x74, 0x68, 0x65, 0x00, 0x73, 0x61, 0x6d, 0x65, 0x4c, 0x00, 420x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x74, 0x68, 0x65, 0x00,
420x15, 0x2e, 0xbf, 0x00, 0x82, 0x70, 0x72, 0x6f, 0x76, 0x69, 430x73, 0x61, 0x6d, 0x65, 0x4c, 0x00, 0x15, 0x2e, 0xbf, 0x00,
430x64, 0x65, 0x64, 0x76, 0x00, 0x45, 0x73, 0x6f, 0x6d, 0x65, 440x82, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x76,
440x4c, 0x00, 0x73, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 450x00, 0x45, 0x73, 0x6f, 0x6d, 0x65, 0x4c, 0x00, 0x73, 0x61,
450x33, 0x00, 0xe0, 0x65, 0x64, 0x2c, 0x00, 0x73, 0x75, 0x66, 460x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x33, 0x00, 0xe0, 0x65,
460x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0xbb, 0x00, 0x32, 470x64, 0x2c, 0x00, 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69,
470x6d, 0x61, 0x6b, 0x59, 0x00, 0x72, 0x72, 0x65, 0x6d, 0x61, 480x65, 0x6e, 0x74, 0xbb, 0x00, 0x32, 0x6d, 0x61, 0x6b, 0x59,
480x69, 0x6e, 0x64, 0xea, 0x00, 0x01, 0x6a, 0x00, 0x40, 0x6f, 490x00, 0x72, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0xea,
490x6c, 0x75, 0x74, 0xcb, 0x00, 0xe5, 0x75, 0x6e, 0x69, 0x71, 500x00, 0x01, 0x6a, 0x00, 0x40, 0x6f, 0x6c, 0x75, 0x74, 0xcb,
500x75, 0x65, 0x2e, 0x00, 0x00, 0x00, 0x4f, 0x6e, 0x6c, 0x79, 510x00, 0xb0, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x2c, 0x00,
510x5a, 0x00, 0x51, 0x77, 0x68, 0x69, 0x63, 0x68, 0xac, 0x00, 520x61, 0x6e, 0x64, 0x19, 0x00, 0xf5, 0x0d, 0x73, 0x65, 0x00,
520xa0, 0x65, 0x00, 0x61, 0x00, 0x6c, 0x65, 0x6e, 0x67, 0x74, 530x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x00, 0x62, 0x65, 0x00,
530x68, 0x3c, 0x00, 0x30, 0x62, 0x6f, 0x72, 0x46, 0x00, 0x20, 540x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x00, 0x00,
540x61, 0x72, 0x54, 0x00, 0x60, 0x71, 0x75, 0x69, 0x72, 0x65, 550x00, 0x4f, 0x6e, 0x6c, 0x79, 0x77, 0x00, 0x51, 0x77, 0x68,
550x64, 0x69, 0x00, 0x90, 0x62, 0x65, 0x00, 0x64, 0x69, 0x66, 560x69, 0x63, 0x68, 0xc9, 0x00, 0xa0, 0x65, 0x00, 0x61, 0x00,
560x66, 0x65, 0x72, 0x79, 0x00, 0x03, 0x0d, 0x01, 0x5b, 0x2e, 570x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x59, 0x00, 0x30, 0x62,
570x00, 0x54, 0x77, 0x6f, 0x51, 0x00, 0x90, 0x6d, 0x65, 0x65, 580x6f, 0x72, 0x63, 0x00, 0x20, 0x61, 0x72, 0x71, 0x00, 0x60,
580x74, 0x00, 0x61, 0x74, 0x00, 0x6f, 0x6c, 0x00, 0x00, 0x41, 590x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x86, 0x00, 0x90, 0x62,
590x01, 0xb1, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x28, 0x69, 600x65, 0x00, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x96, 0x00,
600x2e, 0x65, 0x2e, 0x55, 0x00, 0xff, 0x0c, 0x64, 0x69, 0x61, 610x03, 0x2a, 0x01, 0x5b, 0x2e, 0x00, 0x54, 0x77, 0x6f, 0x51,
610x67, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x65, 620x00, 0x90, 0x6d, 0x65, 0x65, 0x74, 0x00, 0x61, 0x74, 0x00,
620x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x64, 0x29, 0x00, 0x6d, 630x6f, 0x6c, 0x00, 0x00, 0x5e, 0x01, 0xb1, 0x70, 0x6f, 0x69,
630x61, 0x79, 0x00, 0x62, 0x23, 0x01, 0x00, 0x91, 0x00, 0x00, 640x6e, 0x74, 0x00, 0x28, 0x69, 0x2e, 0x65, 0x2e, 0x55, 0x00,
640x49, 0x00, 0x62, 0x65, 0x6c, 0x69, 0x65, 0x40, 0x01, 0x90, 650xff, 0x0c, 0x64, 0x69, 0x61, 0x67, 0x6f, 0x6e, 0x61, 0x6c,
650x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0xb9, 660x6c, 0x79, 0x00, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74,
660x01, 0xf0, 0x08, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 670x65, 0x64, 0x29, 0x00, 0x6d, 0x61, 0x79, 0x00, 0x62, 0x40,
670x6c, 0x3b, 0x00, 0x49, 0x27, 0x76, 0x65, 0x00, 0x6e, 0x65, 680x01, 0x00, 0x91, 0x00, 0x00, 0x49, 0x00, 0x62, 0x65, 0x6c,
680x76, 0x65, 0x72, 0x00, 0x73, 0x65, 0x09, 0x02, 0xc1, 0x6e, 690x69, 0x65, 0x5d, 0x01, 0x90, 0x69, 0x73, 0x00, 0x70, 0x75,
690x00, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 700x7a, 0x7a, 0x6c, 0x65, 0xd6, 0x01, 0xf0, 0x08, 0x6f, 0x72,
700x61, 0x04, 0x01, 0xf0, 0x09, 0x6f, 0x66, 0x00, 0x69, 0x74, 710x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x3b, 0x00, 0x49, 0x27,
710x00, 0x61, 0x6e, 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x00, 720x76, 0x65, 0x00, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x00, 0x73,
720x65, 0x6c, 0x73, 0x65, 0x2e, 0x00, 0x54, 0x68, 0x65, 0x2e, 730x65, 0x26, 0x02, 0xc1, 0x6e, 0x00, 0x69, 0x6d, 0x70, 0x6c,
730x02, 0x42, 0x63, 0x65, 0x70, 0x74, 0x2b, 0x02, 0x00, 0xe9, 740x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x21, 0x01, 0xf0, 0x09,
740x01, 0x12, 0x2d, 0x7a, 0x00, 0x00, 0xc0, 0x01, 0x03, 0x6b, 750x6f, 0x66, 0x00, 0x69, 0x74, 0x00, 0x61, 0x6e, 0x79, 0x77,
750x00, 0xf0, 0x20, 0x77, 0x61, 0x73, 0x00, 0x73, 0x75, 0x67, 760x68, 0x65, 0x72, 0x65, 0x00, 0x65, 0x6c, 0x73, 0x65, 0x2e,
760x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x00, 0x62, 0x79, 0x00, 770x00, 0x54, 0x68, 0x65, 0x4b, 0x02, 0x42, 0x63, 0x65, 0x70,
770x4f, 0x77, 0x65, 0x6e, 0x00, 0x44, 0x75, 0x6e, 0x6e, 0x3b, 780x74, 0x48, 0x02, 0x00, 0x06, 0x02, 0x12, 0x2d, 0x7a, 0x00,
780x00, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x6d, 0x75, 790x00, 0xdd, 0x01, 0x03, 0x6b, 0x00, 0xf0, 0x26, 0x77, 0x61,
790x73, 0x74, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x67, 0x6f, 800x73, 0x00, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x65,
800x2d, 0x01, 0x91, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 810x64, 0x00, 0x62, 0x79, 0x00, 0x41, 0x6c, 0x65, 0x78, 0x61,
810x61, 0x6e, 0x3b, 0x01, 0xf1, 0x07, 0x56, 0x65, 0x72, 0x69, 820x6e, 0x64, 0x72, 0x61, 0x00, 0x4c, 0x61, 0x6e, 0x65, 0x73,
820x74, 0x79, 0x00, 0x41, 0x6c, 0x6c, 0x61, 0x6e, 0x00, 0x66, 830x3b, 0x00, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x6d,
830x6f, 0x72, 0x00, 0x69, 0x6e, 0x73, 0x70, 0x69, 0x63, 0x00, 840x75, 0x73, 0x74, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x67,
840x00, 0xf0, 0x00, 0x52, 0x74, 0x72, 0x61, 0x69, 0x6e, 0xb6, 850x6f, 0x33, 0x01, 0x62, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69,
850x01, 0x52, 0x6f, 0x75, 0x67, 0x68, 0x74, 0x54, 0x02, 0x12, 860x95, 0x01, 0x70, 0x6f, 0x00, 0x56, 0x65, 0x72, 0x69, 0x74,
860x6c, 0x77, 0x01, 0x01, 0x0b, 0x02, 0x40, 0x61, 0x6c, 0x69, 870x3d, 0x00, 0xd1, 0x6c, 0x61, 0x6e, 0x00, 0x66, 0x6f, 0x72,
870x73, 0x2e, 0x00, 0x00, 0x79, 0x00, 0x15, 0x27, 0x8d, 0x00, 880x00, 0x69, 0x6e, 0x73, 0x70, 0x69, 0x69, 0x00, 0x00, 0xf6,
880x01, 0xa7, 0x02, 0xb3, 0x61, 0x73, 0x00, 0x61, 0x00, 0x76, 890x00, 0x52, 0x74, 0x72, 0x61, 0x69, 0x6e, 0xd9, 0x01, 0x52,
890x69, 0x61, 0x62, 0x6c, 0x65, 0xb0, 0x00, 0x00, 0xd8, 0x00, 900x6f, 0x75, 0x67, 0x68, 0x74, 0x77, 0x02, 0x12, 0x6c, 0x7d,
900x42, 0x61, 0x6e, 0x6b, 0x73, 0x90, 0x00, 0xf1, 0x01, 0x74, 910x01, 0x01, 0x2e, 0x02, 0x40, 0x61, 0x6c, 0x69, 0x73, 0x2e,
910x6f, 0x00, 0x47, 0x61, 0x72, 0x65, 0x74, 0x68, 0x00, 0x54, 920x00, 0x00, 0x7f, 0x00, 0x15, 0x27, 0x93, 0x00, 0x01, 0xca,
920x61, 0x79, 0x6c, 0x6f, 0x72, 0x80, 0x00, 0xa0, 0x6d, 0x61, 930x02, 0xb3, 0x61, 0x73, 0x00, 0x61, 0x00, 0x76, 0x69, 0x61,
930x6e, 0x79, 0x00, 0x64, 0x65, 0x74, 0x61, 0x69, 0x66, 0x00, 940x62, 0x6c, 0x65, 0xb6, 0x00, 0x00, 0xde, 0x00, 0x42, 0x61,
940x06, 0x4f, 0x00, 0x10, 0x73, 0x70, 0x01, 0x41, 0x32, 0x32, 950x6e, 0x6b, 0x73, 0x90, 0x00, 0xf1, 0x01, 0x74, 0x6f, 0x00,
950x2e, 0x31, 0x60, 0x03, 0x80, 0x63, 0x6f, 0x6e, 0x74, 0x72, 960x47, 0x61, 0x72, 0x65, 0x74, 0x68, 0x00, 0x54, 0x61, 0x79,
960x6f, 0x6c, 0x73, 0x69, 0x03, 0x15, 0x54, 0x25, 0x03, 0x13, 970x6c, 0x6f, 0x72, 0x80, 0x00, 0xa0, 0x6d, 0x61, 0x6e, 0x79,
970x61, 0xf0, 0x01, 0x71, 0x2c, 0x00, 0x63, 0x6c, 0x69, 0x63, 980x00, 0x64, 0x65, 0x74, 0x61, 0x69, 0x66, 0x00, 0x06, 0x4f,
980x6b, 0xc2, 0x00, 0xf0, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x00, 990x00, 0x10, 0x73, 0x76, 0x01, 0x41, 0x32, 0x32, 0x2e, 0x31,
990x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 0x62, 0x75, 0x74, 0x74, 1000x93, 0x03, 0xd5, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
1000x70, 0x01, 0x01, 0x85, 0x01, 0x23, 0x65, 0x78, 0x89, 0x03, 1010x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x48, 0x03, 0x13, 0x61,
1010x03, 0x55, 0x03, 0x03, 0x9a, 0x02, 0x31, 0x64, 0x65, 0x73, 1020xf6, 0x01, 0x71, 0x2c, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b,
1020x56, 0x02, 0x04, 0x52, 0x00, 0x72, 0x6e, 0x64, 0x00, 0x64, 1030xc2, 0x00, 0xf0, 0x00, 0x6c, 0x65, 0x66, 0x74, 0x00, 0x6d,
1030x72, 0x61, 0x67, 0xf8, 0x00, 0x03, 0x15, 0x00, 0x41, 0x69, 1040x6f, 0x75, 0x73, 0x65, 0x00, 0x62, 0x75, 0x74, 0x74, 0x76,
1040x6e, 0x74, 0x6f, 0x2d, 0x00, 0x33, 0x6e, 0x65, 0x77, 0x3f, 1050x01, 0x01, 0x8b, 0x01, 0x23, 0x65, 0x78, 0xac, 0x03, 0x03,
1050x00, 0x00, 0x9d, 0x00, 0x31, 0x28, 0x54, 0x68, 0x2f, 0x03, 1060x78, 0x03, 0x03, 0xbd, 0x02, 0x31, 0x64, 0x65, 0x73, 0x5c,
1060xf3, 0x07, 0x67, 0x72, 0x61, 0x6d, 0x00, 0x77, 0x69, 0x6c, 1070x02, 0x04, 0x52, 0x00, 0x72, 0x6e, 0x64, 0x00, 0x64, 0x72,
1070x6c, 0x00, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x00, 0x65, 1080x61, 0x67, 0xf8, 0x00, 0x03, 0x15, 0x00, 0x41, 0x69, 0x6e,
1080x6e, 0x73, 0x75, 0x72, 0x40, 0x02, 0x47, 0x74, 0x61, 0x72, 1090x74, 0x6f, 0x2d, 0x00, 0x33, 0x6e, 0x65, 0x77, 0x3f, 0x00,
1090x74, 0xc0, 0x01, 0x10, 0x68, 0x24, 0x01, 0x00, 0x51, 0x01, 1100x00, 0x9d, 0x00, 0x31, 0x28, 0x54, 0x68, 0x52, 0x03, 0xf3,
1100x31, 0x61, 0x73, 0x74, 0x92, 0x02, 0x06, 0x8e, 0x00, 0x01, 1110x07, 0x67, 0x72, 0x61, 0x6d, 0x00, 0x77, 0x69, 0x6c, 0x6c,
1110xf2, 0x03, 0x02, 0x72, 0x00, 0x42, 0x2c, 0x00, 0x73, 0x6f, 1120x00, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x00, 0x65, 0x6e,
1120x82, 0x00, 0x01, 0x67, 0x02, 0x24, 0x69, 0x73, 0x5a, 0x00, 1130x73, 0x75, 0x72, 0x46, 0x02, 0x47, 0x74, 0x61, 0x72, 0x74,
1130xa0, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x21, 1140xc6, 0x01, 0x10, 0x68, 0x24, 0x01, 0x00, 0x51, 0x01, 0x31,
1140x29, 0x8d, 0x02, 0x82, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 1150x61, 0x73, 0x74, 0x98, 0x02, 0x06, 0x8e, 0x00, 0x01, 0x15,
1150x6e, 0x65, 0x9e, 0x01, 0x48, 0x63, 0x6c, 0x65, 0x61, 0x13, 1160x04, 0x02, 0x72, 0x00, 0x42, 0x2c, 0x00, 0x73, 0x6f, 0x82,
1160x01, 0x00, 0x1c, 0x00, 0x32, 0x63, 0x61, 0x6e, 0xcf, 0x00, 1170x00, 0x01, 0x6d, 0x02, 0x24, 0x69, 0x73, 0x5a, 0x00, 0xa0,
1170x41, 0x66, 0x72, 0x6f, 0x6d, 0x06, 0x01, 0x45, 0x6d, 0x70, 1180x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x21, 0x29,
1180x74, 0x79, 0x23, 0x00, 0x22, 0x6f, 0x72, 0x19, 0x00, 0x24, 1190x93, 0x02, 0x82, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6e,
1190x74, 0x68, 0xb1, 0x01, 0x06, 0x26, 0x04, 0x11, 0x69, 0x20, 1200x65, 0x9e, 0x01, 0x48, 0x63, 0x6c, 0x65, 0x61, 0x13, 0x01,
1200x01, 0x21, 0x72, 0x65, 0x28, 0x03, 0x29, 0x6e, 0x6f, 0x3a, 1210x00, 0x1c, 0x00, 0x32, 0x63, 0x61, 0x6e, 0xcf, 0x00, 0x41,
1210x00, 0x11, 0x73, 0x66, 0x01, 0x00, 0x04, 0x01, 0x52, 0x44, 1220x66, 0x72, 0x6f, 0x6d, 0x06, 0x01, 0x45, 0x6d, 0x70, 0x74,
1220x72, 0x61, 0x67, 0x67, 0x60, 0x04, 0x03, 0x2d, 0x01, 0x24, 1230x79, 0x23, 0x00, 0x22, 0x6f, 0x72, 0x19, 0x00, 0x24, 0x74,
1230x75, 0x73, 0x4c, 0x02, 0x4b, 0x72, 0x69, 0x67, 0x68, 0x8b, 1240x68, 0xb1, 0x01, 0x06, 0x49, 0x04, 0x11, 0x69, 0x20, 0x01,
1240x01, 0x01, 0x26, 0x01, 0x64, 0x73, 0x74, 0x69, 0x70, 0x70, 1250x21, 0x72, 0x65, 0x2e, 0x03, 0x29, 0x6e, 0x6f, 0x3a, 0x00,
1250x6c, 0x2a, 0x04, 0x01, 0xff, 0x00, 0x28, 0x69, 0x6e, 0x71, 1260x11, 0x73, 0x66, 0x01, 0x00, 0x04, 0x01, 0x52, 0x44, 0x72,
1260x01, 0x13, 0x2c, 0xbf, 0x03, 0x04, 0xc2, 0x00, 0x00, 0x41, 1270x61, 0x67, 0x67, 0x83, 0x04, 0x03, 0x2d, 0x01, 0x24, 0x75,
1270x00, 0x01, 0x5c, 0x02, 0x40, 0x6e, 0x6f, 0x74, 0x65, 0xeb, 1280x73, 0x4c, 0x02, 0x4b, 0x72, 0x69, 0x67, 0x68, 0x8b, 0x01,
1280x00, 0x10, 0x79, 0xf1, 0x03, 0x32, 0x65, 0x6c, 0x66, 0x35, 1290x01, 0x26, 0x01, 0x64, 0x73, 0x74, 0x69, 0x70, 0x70, 0x6c,
1290x00, 0x00, 0x27, 0x00, 0x58, 0x74, 0x68, 0x69, 0x6e, 0x6b, 1300x4d, 0x04, 0x01, 0xff, 0x00, 0x28, 0x69, 0x6e, 0x71, 0x01,
1300x52, 0x00, 0x11, 0x6d, 0x7d, 0x00, 0x28, 0x62, 0x65, 0x58, 1310x13, 0x2c, 0xc5, 0x03, 0x04, 0xc2, 0x00, 0x00, 0x41, 0x00,
1310x00, 0x34, 0x2e, 0x00, 0x41, 0x1f, 0x00, 0x00, 0x57, 0x00, 1320x01, 0x5c, 0x02, 0x40, 0x6e, 0x6f, 0x74, 0x65, 0xeb, 0x00,
1320x00, 0x54, 0x02, 0x00, 0xee, 0x02, 0x03, 0x90, 0x00, 0x10, 1330x10, 0x79, 0xf7, 0x03, 0x32, 0x65, 0x6c, 0x66, 0x35, 0x00,
1330x73, 0x86, 0x00, 0x74, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 1340x00, 0x27, 0x00, 0x58, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x52,
1340x6c, 0xf4, 0x03, 0x01, 0xad, 0x01, 0x00, 0x96, 0x03, 0x10, 1350x00, 0x11, 0x6d, 0x7d, 0x00, 0x28, 0x62, 0x65, 0x58, 0x00,
1350x2e, 0xee, 0x01, 0x02, 0x8b, 0x01, 0x41, 0x6f, 0x66, 0x74, 1360x34, 0x2e, 0x00, 0x41, 0x1f, 0x00, 0x00, 0x57, 0x00, 0x00,
1360x65, 0x94, 0x00, 0x41, 0x66, 0x75, 0x6c, 0x00, 0xa3, 0x01, 1370x54, 0x02, 0x00, 0xee, 0x02, 0x03, 0x90, 0x00, 0x10, 0x73,
1370x00, 0xd7, 0x01, 0x01, 0xa8, 0x04, 0x20, 0x64, 0x69, 0x0f, 1380x86, 0x00, 0x74, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
1380x05, 0xd3, 0x75, 0x6c, 0x74, 0x79, 0x00, 0x6c, 0x65, 0x76, 1390xfa, 0x03, 0x01, 0xad, 0x01, 0x00, 0x9c, 0x03, 0x10, 0x2e,
1390x65, 0x6c, 0x73, 0x2e, 0x29, 0x19, 0x06, 0x00, 0x70, 0x00, 1400xee, 0x01, 0x02, 0x8b, 0x01, 0x41, 0x6f, 0x66, 0x74, 0x65,
1400x01, 0x04, 0x03, 0x22, 0x75, 0x73, 0xf9, 0x00, 0xb1, 0x63, 1410x94, 0x00, 0x41, 0x66, 0x75, 0x6c, 0x00, 0xa3, 0x01, 0x00,
1410x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 1420xd7, 0x01, 0x01, 0xae, 0x04, 0x20, 0x64, 0x69, 0x32, 0x05,
1420x5b, 0x03, 0x20, 0x6f, 0x76, 0x6a, 0x01, 0x00, 0x7d, 0x01, 1430xd3, 0x75, 0x6c, 0x74, 0x79, 0x00, 0x6c, 0x65, 0x76, 0x65,
1430x01, 0x1f, 0x00, 0x42, 0x6d, 0x61, 0x70, 0x3a, 0x28, 0x00, 1440x6c, 0x73, 0x2e, 0x29, 0x3c, 0x06, 0x00, 0x70, 0x00, 0x01,
1440x02, 0x56, 0x01, 0x28, 0x6f, 0x66, 0x36, 0x00, 0x92, 0x69, 1450x04, 0x03, 0x22, 0x75, 0x73, 0xf9, 0x00, 0xb1, 0x63, 0x75,
1450x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0xba, 0x01, 1460x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x5b,
1460x34, 0x6f, 0x73, 0x69, 0x59, 0x04, 0x07, 0x33, 0x00, 0x00, 1470x03, 0x20, 0x6f, 0x76, 0x6a, 0x01, 0x22, 0x6f, 0x75, 0x3b,
1470x17, 0x01, 0x43, 0x77, 0x6f, 0x75, 0x6c, 0xd1, 0x02, 0x12, 1480x05, 0x00, 0x60, 0x06, 0x12, 0x3a, 0x28, 0x00, 0x02, 0x56,
1480x28, 0x54, 0x01, 0x20, 0x69, 0x73, 0x46, 0x01, 0xa1, 0x00, 1490x01, 0x28, 0x6f, 0x66, 0x36, 0x00, 0x92, 0x69, 0x6e, 0x64,
1490x6f, 0x62, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x69, 0x44, 1500x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0xba, 0x01, 0x34, 0x6f,
1500x02, 0x31, 0x27, 0x72, 0x65, 0x26, 0x03, 0x03, 0x1e, 0x01, 1510x73, 0x69, 0x5f, 0x04, 0x07, 0x33, 0x00, 0x00, 0x17, 0x01,
1510x25, 0x27, 0x73, 0x07, 0x02, 0x70, 0x2c, 0x00, 0x73, 0x69, 1520x43, 0x77, 0x6f, 0x75, 0x6c, 0xd1, 0x02, 0x12, 0x28, 0x54,
1520x6e, 0x63, 0x65, 0xb1, 0x04, 0x70, 0x64, 0x65, 0x70, 0x65, 1530x01, 0x30, 0x69, 0x73, 0x00, 0x92, 0x05, 0x91, 0x6f, 0x62,
1530x6e, 0x64, 0x73, 0x29, 0x00, 0x01, 0x39, 0x03, 0x41, 0x69, 1540x76, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x69, 0x44, 0x02, 0x31,
1540x72, 0x65, 0x63, 0x77, 0x00, 0x01, 0x43, 0x02, 0x06, 0xb2, 1550x27, 0x72, 0x65, 0x26, 0x03, 0x03, 0x1e, 0x01, 0x25, 0x27,
1550x01, 0x92, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x61, 0x63, 0x68, 1560x73, 0x07, 0x02, 0x70, 0x2c, 0x00, 0x73, 0x69, 0x6e, 0x63,
1560x65, 0xca, 0x00, 0x04, 0x4a, 0x00, 0x76, 0x29, 0x2e, 0x00, 1570x65, 0xb7, 0x04, 0x70, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64,
1570x50, 0x72, 0x65, 0x73, 0x1e, 0x02, 0x50, 0x65, 0x74, 0x75, 1580x73, 0x29, 0x00, 0x01, 0x39, 0x03, 0x41, 0x69, 0x72, 0x65,
1580x72, 0x6e, 0x01, 0x01, 0x02, 0x25, 0x03, 0x00, 0xe0, 0x01, 1590x63, 0x77, 0x00, 0x01, 0x43, 0x02, 0x06, 0xb2, 0x01, 0x92,
1590x01, 0xa9, 0x00, 0x2a, 0x6f, 0x66, 0x0c, 0x02, 0x00, 0xfa, 1600x61, 0x70, 0x70, 0x72, 0x6f, 0x61, 0x63, 0x68, 0x65, 0xca,
1600x01, 0x49, 0x62, 0x6f, 0x76, 0x65, 0x16, 0x02, 0x02, 0x0f, 1610x00, 0x04, 0x4a, 0x00, 0x76, 0x29, 0x2e, 0x00, 0x50, 0x72,
1610x04, 0x02, 0xa4, 0x06, 0x0b, 0x4f, 0x01, 0x3f, 0x3b, 0x00, 1620x65, 0x73, 0x1e, 0x02, 0x50, 0x65, 0x74, 0x75, 0x72, 0x6e,
1620x70, 0x68, 0x00, 0x04, 0x20, 0x61, 0x67, 0xef, 0x01, 0x63, 1630x01, 0x01, 0x02, 0x25, 0x03, 0x00, 0xe0, 0x01, 0x01, 0xa9,
1630x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x3c, 0x01, 0x00, 0x72, 1640x00, 0x2a, 0x6f, 0x66, 0x0c, 0x02, 0x00, 0xfa, 0x01, 0x49,
1640x00, 0x02, 0x7d, 0x05, 0x91, 0x73, 0x70, 0x61, 0x63, 0x65, 1650x62, 0x6f, 0x76, 0x65, 0x16, 0x02, 0x02, 0x0f, 0x04, 0x02,
1650x00, 0x62, 0x61, 0x72, 0xac, 0x01, 0x20, 0x62, 0x65, 0xaa, 1660xc7, 0x06, 0x0b, 0x4f, 0x01, 0x3f, 0x3b, 0x00, 0x70, 0x68,
1660x01, 0xb1, 0x64, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 1670x00, 0x04, 0x20, 0x61, 0x67, 0xef, 0x01, 0x63, 0x66, 0x69,
1670x72, 0x6c, 0x79, 0x62, 0x03, 0x40, 0x72, 0x65, 0x61, 0x74, 1680x6e, 0x69, 0x73, 0x68, 0x3c, 0x01, 0x00, 0x72, 0x00, 0x02,
1680xa1, 0x06, 0x03, 0x32, 0x02, 0x15, 0x64, 0x0d, 0x04, 0x7f, 1690x83, 0x05, 0x91, 0x73, 0x70, 0x61, 0x63, 0x65, 0x00, 0x62,
1690x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2d, 0x79, 0x00, 0x05, 1700x61, 0x72, 0xac, 0x01, 0x20, 0x62, 0x65, 0xaa, 0x01, 0xb1,
1700x30, 0x28, 0x77, 0x69, 0x50, 0x05, 0x10, 0x74, 0xe7, 0x01, 1710x64, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c,
1710x04, 0x1f, 0x00, 0x02, 0xb2, 0x00, 0x12, 0x29, 0x13, 0x03, 1720x79, 0x62, 0x03, 0x40, 0x72, 0x65, 0x61, 0x74, 0xa7, 0x06,
1720x02, 0xbc, 0x03, 0x06, 0xbf, 0x02, 0x02, 0xf6, 0x00, 0x0f, 1730x03, 0x32, 0x02, 0x15, 0x64, 0x0d, 0x04, 0x7f, 0x44, 0x6f,
1730xbb, 0x03, 0x07, 0x65, 0x00, 0x64, 0x6f, 0x65, 0x73, 0x3a, 1740x75, 0x62, 0x6c, 0x65, 0x2d, 0x79, 0x00, 0x05, 0x30, 0x28,
1740x1e, 0x04, 0x03, 0x8d, 0x02, 0x0c, 0x0e, 0x01, 0x43, 0x6d, 1750x77, 0x69, 0x50, 0x05, 0x10, 0x74, 0xe7, 0x01, 0x04, 0x1f,
1750x6f, 0x64, 0x65, 0xe2, 0x01, 0x02, 0xf5, 0x07, 0x43, 0x66, 1760x00, 0x02, 0xb2, 0x00, 0x12, 0x29, 0x13, 0x03, 0x02, 0xbc,
1760x69, 0x6c, 0x6c, 0x97, 0x01, 0x51, 0x65, 0x6e, 0x74, 0x69, 1770x03, 0x06, 0xbf, 0x02, 0x02, 0xf6, 0x00, 0x0f, 0xbb, 0x03,
1770x72, 0x68, 0x02, 0x00, 0xf7, 0x02, 0x36, 0x62, 0x75, 0x74, 1780x07, 0x65, 0x00, 0x64, 0x6f, 0x65, 0x73, 0x3a, 0x1e, 0x04,
1780x48, 0x04, 0x20, 0x6f, 0x72, 0xda, 0x01, 0x02, 0x4d, 0x05, 1790x03, 0x8d, 0x02, 0x0c, 0x0e, 0x01, 0x43, 0x6d, 0x6f, 0x64,
1790x66, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x2e, 0x6d, 0x04, 0x01, 1800x65, 0xe2, 0x01, 0x02, 0x18, 0x08, 0x43, 0x66, 0x69, 0x6c,
1800xe2, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x64, 0x96, 0x06, 0x41, 1810x6c, 0x97, 0x01, 0x51, 0x65, 0x6e, 0x74, 0x69, 0x72, 0x68,
1810x6c, 0x61, 0x79, 0x2c, 0x2a, 0x00, 0x10, 0x67, 0x29, 0x07, 1820x02, 0x00, 0xf7, 0x02, 0x36, 0x62, 0x75, 0x74, 0x48, 0x04,
1820x01, 0xcc, 0x00, 0x50, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x20, 1830x20, 0x6f, 0x72, 0xda, 0x01, 0x02, 0x4d, 0x05, 0x66, 0x61,
1830x02, 0x10, 0x73, 0x1e, 0x00, 0x09, 0xf8, 0x08, 0x29, 0x69, 1840x79, 0x6f, 0x75, 0x74, 0x2e, 0x6d, 0x04, 0x01, 0xe2, 0x00,
1840x6e, 0xd8, 0x08, 0x24, 0x6f, 0x66, 0xec, 0x02, 0x00, 0x83, 1850x43, 0x00, 0x4c, 0x00, 0x64, 0x9c, 0x06, 0x41, 0x6c, 0x61,
1850x01, 0x09, 0xcb, 0x00, 0x03, 0xb6, 0x00, 0x22, 0x77, 0x61, 1860x79, 0x2c, 0x2a, 0x00, 0x10, 0x67, 0x2f, 0x07, 0x01, 0xcc,
1860x5a, 0x08, 0x60, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x13, 1870x00, 0x50, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x20, 0x02, 0x10,
1870x01, 0x10, 0x70, 0x50, 0x05, 0x55, 0x63, 0x75, 0x6c, 0x61, 1880x73, 0x1e, 0x00, 0x09, 0x1b, 0x09, 0x29, 0x69, 0x6e, 0xfb,
1880x72, 0x7e, 0x07, 0x40, 0x6e, 0x73, 0x74, 0x61, 0xa4, 0x02, 1890x08, 0x24, 0x6f, 0x66, 0xec, 0x02, 0x00, 0x83, 0x01, 0x09,
1890x01, 0xff, 0x00, 0xa0, 0x61, 0x00, 0x66, 0x72, 0x69, 0x65, 1900xcb, 0x00, 0x03, 0xb6, 0x00, 0x22, 0x77, 0x61, 0x7d, 0x08,
1900x6e, 0x64, 0x00, 0x2d, 0xf3, 0x00, 0x01, 0x9a, 0x04, 0x00, 1910x60, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x13, 0x01, 0x10,
1910x76, 0x08, 0x60, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0xf2, 1920x70, 0x50, 0x05, 0x55, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x84,
1920x02, 0x10, 0x6e, 0xa9, 0x00, 0x2a, 0x66, 0x6f, 0x62, 0x09, 1930x07, 0x40, 0x6e, 0x73, 0x74, 0x61, 0xa4, 0x02, 0x01, 0xff,
1930x40, 0x69, 0x73, 0x00, 0x6d, 0x48, 0x09, 0x60, 0x65, 0x61, 1940x00, 0xa0, 0x61, 0x00, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64,
1940x73, 0x69, 0x65, 0x72, 0x80, 0x02, 0x51, 0x6e, 0x00, 0x74, 1950x00, 0x2d, 0xf3, 0x00, 0x01, 0x9a, 0x04, 0x00, 0x99, 0x08,
1950x72, 0x79, 0xac, 0x01, 0x00, 0x55, 0x08, 0x10, 0x66, 0x15, 1960x60, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0xf2, 0x02, 0x10,
1960x00, 0x01, 0x04, 0x06, 0x50, 0x6d, 0x00, 0x61, 0x6c, 0x6c, 1970x6e, 0xa9, 0x00, 0x2a, 0x66, 0x6f, 0x85, 0x09, 0x40, 0x69,
1970x79, 0x07, 0x00, 0x48, 0x00, 0x13, 0x73, 0x7e, 0x09, 0x30, 1980x73, 0x00, 0x6d, 0x6b, 0x09, 0x60, 0x65, 0x61, 0x73, 0x69,
1980x73, 0x00, 0x60, 0xc9, 0x00, 0x00, 0xd0, 0x05, 0x41, 0x64, 1990x65, 0x72, 0x80, 0x02, 0x51, 0x6e, 0x00, 0x74, 0x72, 0x79,
1990x6f, 0x77, 0x6e, 0x46, 0x06, 0x02, 0xfe, 0x04, 0x03, 0xe3, 2000xac, 0x01, 0x00, 0x5b, 0x08, 0x10, 0x66, 0x15, 0x00, 0x01,
2000x00, 0x20, 0x62, 0x72, 0x17, 0x00, 0x01, 0xc3, 0x09, 0x12, 2010x04, 0x06, 0x50, 0x6d, 0x00, 0x61, 0x6c, 0x6c, 0x7f, 0x07,
2010x6e, 0x68, 0x07, 0x23, 0x6f, 0x70, 0xdd, 0x08, 0x11, 0x27, 2020x00, 0x48, 0x00, 0x13, 0x73, 0xa1, 0x09, 0x30, 0x73, 0x00,
2020x4b, 0x06, 0x31, 0x41, 0x6c, 0x6c, 0x18, 0x00, 0x21, 0x61, 2030x60, 0xc9, 0x00, 0x00, 0xd0, 0x05, 0x41, 0x64, 0x6f, 0x77,
2030x63, 0xfc, 0x06, 0x00, 0x97, 0x06, 0x61, 0x63, 0x72, 0x69, 2040x6e, 0x46, 0x06, 0x02, 0xfe, 0x04, 0x03, 0xe3, 0x00, 0x20,
2040x62, 0x65, 0x64, 0xe3, 0x09, 0x03, 0x69, 0x03, 0x31, 0x32, 2050x62, 0x72, 0x17, 0x00, 0x01, 0xe6, 0x09, 0x12, 0x6e, 0x68,
2050x2e, 0x31, 0x8e, 0x05, 0x01, 0x49, 0x04, 0x50, 0x61, 0x76, 2060x07, 0x23, 0x6f, 0x70, 0xe3, 0x08, 0x11, 0x27, 0x4b, 0x06,
2060x61, 0x69, 0x6c, 0x6b, 0x07, 0x22, 0x2e, 0x29, 0x29, 0x07, 2070x31, 0x41, 0x6c, 0x6c, 0x18, 0x00, 0x21, 0x61, 0x63, 0xfc,
2070x11, 0x32, 0x29, 0x07, 0x00, 0xc5, 0x08, 0x52, 0x6d, 0x65, 2080x06, 0x00, 0x97, 0x06, 0x61, 0x63, 0x72, 0x69, 0x62, 0x65,
2080x74, 0x65, 0x72, 0x2b, 0x07, 0x56, 0x68, 0x65, 0x73, 0x65, 2090x64, 0x06, 0x0a, 0x03, 0x69, 0x03, 0x31, 0x32, 0x2e, 0x31,
2090x00, 0x14, 0x00, 0x02, 0x3f, 0x00, 0x04, 0x3a, 0x00, 0x06, 2100x8e, 0x05, 0x01, 0x49, 0x04, 0x50, 0x61, 0x76, 0x61, 0x69,
2100xfd, 0x05, 0xe2, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 2110x6c, 0x6b, 0x07, 0x22, 0x2e, 0x29, 0x29, 0x07, 0x11, 0x32,
2110x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x4d, 0x04, 0x12, 2120x29, 0x07, 0x00, 0xcb, 0x08, 0x52, 0x6d, 0x65, 0x74, 0x65,
2120x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 2130x72, 0x2b, 0x07, 0x01, 0x86, 0x09, 0x06, 0x14, 0x00, 0x02,
2130x6d, 0x65, 0x6e, 0x75, 0xa9, 0x00, 0x91, 0x57, 0x69, 0x64, 2140x3f, 0x00, 0x04, 0x3a, 0x00, 0x06, 0xfd, 0x05, 0xe2, 0x60,
2140x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xde, 0x00, 0x51, 0x00, 2150x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
2150x00, 0x53, 0x69, 0x7a, 0x97, 0x0a, 0x32, 0x67, 0x72, 0x69, 2160x00, 0x6f, 0x70, 0x4d, 0x04, 0x12, 0x6e, 0x1a, 0x00, 0xa0,
2160xab, 0x00, 0x51, 0x71, 0x75, 0x61, 0x72, 0x65, 0xbc, 0x07, 2170x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75,
2170x13, 0x52, 0x2b, 0x06, 0x3c, 0x00, 0x00, 0x4e, 0xf6, 0x0a, 2180xa9, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00,
2180x22, 0x00, 0x69, 0x5d, 0x00, 0x41, 0x67, 0x65, 0x6e, 0x65, 2190x48, 0x65, 0xde, 0x00, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a,
2190x74, 0x09, 0x02, 0x04, 0x02, 0x36, 0x00, 0x00, 0x44, 0x41, 2200xba, 0x0a, 0x32, 0x67, 0x72, 0x69, 0xab, 0x00, 0x51, 0x71,
2200x05, 0xb1, 0x00, 0x00, 0x49, 0x6e, 0x00, 0x60, 0x45, 0x61, 2210x75, 0x61, 0x72, 0x65, 0xbc, 0x07, 0x13, 0x52, 0x2b, 0x06,
2210x73, 0x79, 0x27, 0xc9, 0x02, 0x13, 0x2c, 0x8f, 0x06, 0x21, 2220x3c, 0x00, 0x00, 0x4e, 0x19, 0x0b, 0x22, 0x00, 0x69, 0x5d,
2220x73, 0x68, 0xd4, 0x04, 0x03, 0x11, 0x07, 0x2f, 0x62, 0x65, 2230x00, 0x41, 0x67, 0x65, 0x6e, 0x65, 0x7a, 0x09, 0x02, 0x04,
2230x4f, 0x07, 0x02, 0x45, 0x77, 0x68, 0x6f, 0x73, 0x09, 0x05, 2240x02, 0x36, 0x00, 0x00, 0x44, 0x41, 0x05, 0xb1, 0x00, 0x00,
2240x03, 0xd0, 0x03, 0x10, 0x64, 0x02, 0x01, 0x30, 0x6d, 0x69, 2250x49, 0x6e, 0x00, 0x60, 0x45, 0x61, 0x73, 0x79, 0x27, 0xc9,
2250x6e, 0xe6, 0x02, 0x40, 0x72, 0x69, 0x76, 0x69, 0xf6, 0x09, 2260x02, 0x13, 0x2c, 0x8f, 0x06, 0x21, 0x73, 0x68, 0xd4, 0x04,
2260x11, 0x2e, 0x65, 0x00, 0x71, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 2270x03, 0x11, 0x07, 0x2f, 0x62, 0x65, 0x4f, 0x07, 0x02, 0x45,
2270x6c, 0x27, 0xb7, 0x01, 0x52, 0x60, 0x48, 0x61, 0x72, 0x64, 2280x77, 0x68, 0x6f, 0x73, 0x09, 0x05, 0x03, 0xd0, 0x03, 0x10,
2280x72, 0x00, 0x12, 0x73, 0x44, 0x07, 0x01, 0xd5, 0x02, 0x02, 2290x64, 0x02, 0x01, 0x30, 0x6d, 0x69, 0x6e, 0xe6, 0x02, 0x40,
2290x34, 0x0b, 0x02, 0xbf, 0x05, 0x20, 0x69, 0x6e, 0x09, 0x04, 2300x72, 0x69, 0x76, 0x69, 0xfc, 0x09, 0x11, 0x2e, 0x65, 0x00,
2300x00, 0xe8, 0x03, 0x10, 0x6c, 0x0c, 0x0b, 0x00, 0xdd, 0x09, 2310x71, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x27, 0xb7, 0x01,
2310x71, 0x78, 0x00, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0xab, 0x02, 2320x52, 0x60, 0x48, 0x61, 0x72, 0x64, 0x72, 0x00, 0x12, 0x73,
2320x5b, 0x65, 0x64, 0x75, 0x63, 0x65, 0xbc, 0x05, 0x08, 0x47, 2330x44, 0x07, 0x01, 0xd5, 0x02, 0x02, 0x57, 0x0b, 0x02, 0xbf,
2330x0b, 0x50, 0x2e, 0x00, 0x48, 0x6f, 0x77, 0x20, 0x0a, 0x10, 2340x05, 0x20, 0x69, 0x6e, 0x09, 0x04, 0x00, 0xe8, 0x03, 0x10,
2340x2c, 0x56, 0x05, 0x08, 0x38, 0x08, 0x25, 0x62, 0x65, 0xe1, 2350x6c, 0x2f, 0x0b, 0x00, 0xe3, 0x09, 0x71, 0x78, 0x00, 0x6c,
2350x07, 0x14, 0x00, 0x32, 0x04, 0x03, 0xc5, 0x02, 0x70, 0x74, 2360x6f, 0x67, 0x69, 0x63, 0xab, 0x02, 0x5b, 0x65, 0x64, 0x75,
2360x6f, 0x00, 0x67, 0x75, 0x65, 0x73, 0x69, 0x0a, 0xb3, 0x00, 2370x63, 0x65, 0xbc, 0x05, 0x08, 0x6a, 0x0b, 0x50, 0x2e, 0x00,
2370x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x2e, 2380x48, 0x6f, 0x77, 0x26, 0x0a, 0x10, 0x2c, 0x56, 0x05, 0x08,
2380x25, 0x01, 0x20, 0x55, 0x6e, 0x90, 0x00, 0x20, 0x6f, 0x6e, 2390x38, 0x08, 0x25, 0x62, 0x65, 0xe1, 0x07, 0x14, 0x00, 0x32,
2390xd4, 0x01, 0x07, 0x2d, 0x01, 0x0a, 0x9c, 0x08, 0x81, 0x66, 2400x04, 0x03, 0xc5, 0x02, 0x70, 0x74, 0x6f, 0x00, 0x67, 0x75,
2400x65, 0x65, 0x6c, 0x00, 0x66, 0x72, 0x65, 0xc7, 0x00, 0x04, 2410x65, 0x73, 0x6f, 0x0a, 0xb3, 0x00, 0x62, 0x61, 0x63, 0x6b,
2410x79, 0x01, 0x03, 0x44, 0x03, 0x04, 0x30, 0x0b, 0x01, 0x1e, 2420x74, 0x72, 0x61, 0x63, 0x6b, 0x2e, 0x25, 0x01, 0x20, 0x55,
2420x02, 0x11, 0x73, 0xce, 0x06, 0x00, 0x08, 0x00, 0x21, 0x69, 2430x6e, 0x90, 0x00, 0x20, 0x6f, 0x6e, 0xd4, 0x01, 0x07, 0x2d,
2430x74, 0x3c, 0x01, 0x03, 0x94, 0x00, 0x15, 0x79, 0xe0, 0x0b, 2440x01, 0x0a, 0x9c, 0x08, 0x81, 0x66, 0x65, 0x65, 0x6c, 0x00,
2440x12, 0x6d, 0x9b, 0x06, 0x01, 0x56, 0x0b, 0x00, 0xe2, 0x0c, 2450x66, 0x72, 0x65, 0xc7, 0x00, 0x04, 0x79, 0x01, 0x03, 0x44,
2450x01, 0x35, 0x0a, 0x11, 0x74, 0xc0, 0x0c, 0x12, 0x68, 0x11, 2460x03, 0x04, 0x36, 0x0b, 0x01, 0x1e, 0x02, 0x11, 0x73, 0xce,
2460x07, 0x14, 0x79, 0xa7, 0x01, 0x25, 0x73, 0x74, 0x3e, 0x01, 2470x06, 0x00, 0x08, 0x00, 0x21, 0x69, 0x74, 0x3c, 0x01, 0x03,
2470x13, 0x61, 0xfd, 0x0b, 0x05, 0x0d, 0x0c, 0x51, 0x2e, 0x00, 2480x94, 0x00, 0x15, 0x79, 0x03, 0x0c, 0x12, 0x6d, 0x9b, 0x06,
2480x53, 0x6f, 0x6c, 0xde, 0x00, 0x08, 0xc1, 0x00, 0x05, 0x93, 2490x01, 0x5c, 0x0b, 0x00, 0x05, 0x0d, 0x01, 0x35, 0x0a, 0x11,
2490x00, 0x00, 0x86, 0x0b, 0x03, 0xf5, 0x0b, 0x02, 0xfc, 0x00, 2500x74, 0xe3, 0x0c, 0x12, 0x68, 0x11, 0x07, 0x14, 0x79, 0xa7,
2500x02, 0xcd, 0x03, 0x16, 0x64, 0x00, 0x01, 0x50, 0x69, 0x6e, 2510x01, 0x25, 0x73, 0x74, 0x3e, 0x01, 0x13, 0x61, 0x20, 0x0c,
2510x67, 0x2e, 0x00, 2520x05, 0x30, 0x0c, 0x51, 0x2e, 0x00, 0x53, 0x6f, 0x6c, 0xde,
2530x00, 0x08, 0xc1, 0x00, 0x05, 0x93, 0x00, 0x00, 0x8c, 0x0b,
2540x03, 0xfb, 0x0b, 0x02, 0xfc, 0x00, 0x02, 0xcd, 0x03, 0x16,
2550x64, 0x00, 0x01, 0x33, 0x69, 0x6e, 0x67, 0x25, 0x0a, 0x11,
2560x33, 0xfc, 0x02, 0x81, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70,
2570x72, 0x65, 0x1f, 0x0c, 0x41, 0x63, 0x65, 0x73, 0x20, 0x6d,
2580x0c, 0x00, 0xa5, 0x04, 0x53, 0x74, 0x66, 0x6f, 0x72, 0x6d,
2590x9f, 0x00, 0x80, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
2600x00, 0x2e, 0x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0xf0,
2610x02, 0x16, 0x50, 0x12, 0x00, 0x0d, 0x0c, 0x03, 0x33, 0x47,
2620x61, 0x6d, 0x0c, 0x03, 0x02, 0x54, 0x01, 0x34, 0x6c, 0x65,
2630x74, 0x8f, 0x06, 0x36, 0x66, 0x69, 0x67, 0xf4, 0x09, 0x34,
2640x79, 0x6c, 0x65, 0xfa, 0x03, 0xd1, 0x76, 0x69, 0x63, 0x74,
2650x6f, 0x72, 0x79, 0x00, 0x66, 0x6c, 0x61, 0x73, 0x68, 0xbf,
2660x00, 0x01, 0xbe, 0x03, 0x30, 0x77, 0x68, 0x65, 0xcf, 0x02,
2670x07, 0xf3, 0x05, 0x12, 0x73, 0x07, 0x07, 0x10, 0x00, 0xf5,
2680x01, 0x64, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x6c, 0xa7, 0x0d,
2690x90, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x00,
252}; 270};
253 271
254const unsigned short help_text_len = 3466; 272const unsigned short help_text_len = 3752;
255const unsigned short help_text_words = 642; 273const unsigned short help_text_words = 686;
274const bool help_valid = true;
256const char quick_help_text[] = "Colour the map so that adjacent regions are never the same colour."; 275const char quick_help_text[] = "Colour the map so that adjacent regions are never the same colour.";
diff --git a/apps/plugins/puzzles/help/mines.c b/apps/plugins/puzzles/help/mines.c
index c2f73d4b54..80cd81ed3f 100644
--- a/apps/plugins/puzzles/help/mines.c
+++ b/apps/plugins/puzzles/help/mines.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,258 +6,259 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 29, TEXT_UNDERLINE }, 9 { 30, TEXT_UNDERLINE },
10 { 119, TEXT_UNDERLINE }, 10 { 120, TEXT_UNDERLINE },
11 { 171, TEXT_CENTER | C_RED }, 11 { 172, TEXT_CENTER | C_RED },
12 { 248, TEXT_UNDERLINE }, 12 { 249, TEXT_UNDERLINE },
13 { 283, TEXT_UNDERLINE }, 13 { 284, TEXT_UNDERLINE },
14 { 334, TEXT_UNDERLINE }, 14 { 335, TEXT_UNDERLINE },
15 { 583, TEXT_CENTER | C_RED }, 15 { 584, TEXT_CENTER | C_RED },
16 { 600, TEXT_UNDERLINE },
17 { 601, TEXT_UNDERLINE }, 16 { 601, TEXT_UNDERLINE },
18 { 611, TEXT_UNDERLINE }, 17 { 602, TEXT_UNDERLINE },
19 { 685, TEXT_UNDERLINE }, 18 { 612, TEXT_UNDERLINE },
19 { 686, TEXT_UNDERLINE },
20 LAST_STYLE_ITEM 20 LAST_STYLE_ITEM
21}; 21};
22 22
23/* orig 3796 comp 2333 ratio 0.614594 level 10 saved 1463 */ 23/* orig 3814 comp 2339 ratio 0.613267 level 10 saved 1475 */
24const char help_text[] = { 24const char help_text[] = {
250xf0, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 250xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
260x31, 0x32, 0x3a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x73, 0x20, 260x31, 0x32, 0x3a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x73, 0x20,
270x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 270x00, 0x2d, 0x01, 0x00, 0xf0, 0x1c, 0x00, 0x00, 0x00, 0x59,
280x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 280x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
290x66, 0x00, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, 0x00, 290x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x63, 0x6f,
300x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 300x76, 0x65, 0x72, 0x65, 0x64, 0x00, 0x73, 0x71, 0x75, 0x61,
310x6f, 0x6d, 0x65, 0x19, 0x00, 0xf0, 0x02, 0x77, 0x68, 0x69, 310x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x19,
320x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 320x00, 0xf0, 0x02, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x63,
330x00, 0x6d, 0x69, 0x6e, 0x1d, 0x00, 0xf2, 0x03, 0x62, 0x75, 330x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6d, 0x69, 0x6e,
340x74, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x64, 0x6f, 0x6e, 0x27, 340x1d, 0x00, 0xf2, 0x03, 0x62, 0x75, 0x74, 0x00, 0x79, 0x6f,
350x74, 0x00, 0x6b, 0x6e, 0x6f, 0x77, 0x28, 0x00, 0x10, 0x2e, 350x75, 0x00, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x00, 0x6b, 0x6e,
360x5b, 0x00, 0xe1, 0x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 360x6f, 0x77, 0x28, 0x00, 0x10, 0x2e, 0x5b, 0x00, 0xe1, 0x72,
370x73, 0x00, 0x74, 0x6f, 0x00, 0x75, 0x6e, 0x59, 0x00, 0x63, 370x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f,
380x00, 0x65, 0x76, 0x65, 0x72, 0x79, 0x5d, 0x00, 0x03, 0x53, 380x00, 0x75, 0x6e, 0x59, 0x00, 0x63, 0x00, 0x65, 0x76, 0x65,
390x00, 0x85, 0x64, 0x6f, 0x65, 0x73, 0x00, 0x6e, 0x6f, 0x74, 390x72, 0x79, 0x5d, 0x00, 0x03, 0x53, 0x00, 0x85, 0x64, 0x6f,
400x5c, 0x00, 0x11, 0x61, 0x5e, 0x00, 0x41, 0x2e, 0x00, 0x49, 400x65, 0x73, 0x00, 0x6e, 0x6f, 0x74, 0x5c, 0x00, 0x11, 0x61,
410x66, 0x5c, 0x00, 0x04, 0x3b, 0x00, 0x14, 0x61, 0x37, 0x00, 410x5e, 0x00, 0x41, 0x2e, 0x00, 0x49, 0x66, 0x5c, 0x00, 0x04,
420x03, 0x28, 0x00, 0x33, 0x69, 0x6e, 0x67, 0x2b, 0x00, 0x11, 420x3b, 0x00, 0x14, 0x61, 0x37, 0x00, 0x03, 0x28, 0x00, 0x33,
430x2c, 0x28, 0x00, 0x3f, 0x6c, 0x6f, 0x73, 0x35, 0x00, 0x08, 430x69, 0x6e, 0x67, 0x2b, 0x00, 0x11, 0x2c, 0x28, 0x00, 0x3f,
440x0f, 0x6c, 0x00, 0x0a, 0x02, 0x41, 0x00, 0x00, 0x27, 0x00, 440x6c, 0x6f, 0x73, 0x35, 0x00, 0x08, 0x0f, 0x6c, 0x00, 0x0a,
450xd2, 0x74, 0x6f, 0x6c, 0x64, 0x00, 0x68, 0x6f, 0x77, 0x00, 450x02, 0x41, 0x00, 0x00, 0x27, 0x00, 0xd2, 0x74, 0x6f, 0x6c,
460x6d, 0x61, 0x6e, 0x79, 0xe6, 0x00, 0x17, 0x00, 0x74, 0x00, 460x64, 0x00, 0x68, 0x6f, 0x77, 0x00, 0x6d, 0x61, 0x6e, 0x79,
470xf0, 0x0d, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68, 0x69, 470xe6, 0x00, 0x17, 0x00, 0x74, 0x00, 0xf0, 0x0d, 0x65, 0x64,
480x6e, 0x00, 0x74, 0x68, 0x65, 0x00, 0x65, 0x69, 0x67, 0x68, 480x00, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x00, 0x74, 0x68,
490x74, 0x00, 0x73, 0x75, 0x72, 0x72, 0x6f, 0x75, 0x6e, 0x64, 490x65, 0x00, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x73, 0x75,
500x90, 0x00, 0x03, 0x36, 0x01, 0xf0, 0x03, 0x2e, 0x00, 0x00, 500x72, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x90, 0x00, 0x03, 0x36,
510x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 510x01, 0xf0, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69,
520x00, 0x6e, 0x65, 0x65, 0x64, 0x76, 0x00, 0xf0, 0x1a, 0x00, 520x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x6e, 0x65, 0x65,
530x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 530x64, 0x76, 0x00, 0xf0, 0x1a, 0x00, 0x69, 0x6e, 0x74, 0x72,
540x6f, 0x6e, 0x3b, 0x00, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 540x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00,
550x72, 0x69, 0x73, 0x65, 0x64, 0x00, 0x62, 0x79, 0x00, 0x57, 550x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x69, 0x73, 0x65,
560x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2c, 0x00, 0x69, 0x74, 560x64, 0x00, 0x62, 0x79, 0x00, 0x57, 0x69, 0x6e, 0x64, 0x6f,
570x34, 0x01, 0x71, 0x70, 0x65, 0x72, 0x68, 0x61, 0x70, 0x73, 570x77, 0x73, 0x2c, 0x00, 0x69, 0x74, 0x34, 0x01, 0x71, 0x70,
580x68, 0x00, 0xa2, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x00, 580x65, 0x72, 0x68, 0x61, 0x70, 0x73, 0x68, 0x00, 0xa2, 0x73,
590x62, 0x65, 0x73, 0x64, 0x01, 0xf2, 0x01, 0x6e, 0x00, 0x64, 590x69, 0x6e, 0x67, 0x6c, 0x65, 0x00, 0x62, 0x65, 0x73, 0x64,
600x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x00, 0x70, 0x75, 0x7a, 600x01, 0xf2, 0x01, 0x6e, 0x00, 0x64, 0x65, 0x73, 0x6b, 0x74,
610x7a, 0x6c, 0x65, 0x67, 0x00, 0xc5, 0x69, 0x6e, 0x00, 0x65, 610x6f, 0x70, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x67,
620x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x81, 0x00, 620x00, 0xc5, 0x69, 0x6e, 0x00, 0x65, 0x78, 0x69, 0x73, 0x74,
630x70, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0xc1, 0x01, 630x65, 0x6e, 0x63, 0x65, 0x81, 0x00, 0x70, 0x76, 0x65, 0x72,
640xf1, 0x17, 0x69, 0x74, 0x00, 0x68, 0x61, 0x73, 0x00, 0x61, 640x73, 0x69, 0x6f, 0x6e, 0xc1, 0x01, 0xf1, 0x17, 0x69, 0x74,
650x6e, 0x00, 0x75, 0x6e, 0x75, 0x73, 0x75, 0x61, 0x6c, 0x00, 650x00, 0x68, 0x61, 0x73, 0x00, 0x61, 0x6e, 0x00, 0x75, 0x6e,
660x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x00, 660x75, 0x73, 0x75, 0x61, 0x6c, 0x00, 0x70, 0x72, 0x6f, 0x70,
670x42, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 670x65, 0x72, 0x74, 0x79, 0x2e, 0x00, 0x42, 0x79, 0x00, 0x64,
680x80, 0x00, 0xf1, 0x02, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x67, 680x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x80, 0x00, 0xf1, 0x02,
690x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x00, 0x69, 0x74, 690x77, 0x69, 0x6c, 0x6c, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72,
700x73, 0x0a, 0x01, 0x50, 0x00, 0x70, 0x6f, 0x73, 0x69, 0xbc, 700x61, 0x74, 0x65, 0x00, 0x69, 0x74, 0x73, 0x0a, 0x01, 0x50,
710x00, 0x10, 0x73, 0x6c, 0x00, 0xc1, 0x73, 0x75, 0x63, 0x68, 710x00, 0x70, 0x6f, 0x73, 0x69, 0xbc, 0x00, 0x10, 0x73, 0x6c,
720x00, 0x61, 0x00, 0x77, 0x61, 0x79, 0x00, 0x61, 0xe3, 0x01, 720x00, 0xc1, 0x73, 0x75, 0x63, 0x68, 0x00, 0x61, 0x00, 0x77,
730x40, 0x65, 0x6e, 0x73, 0x75, 0x42, 0x01, 0x22, 0x68, 0x61, 730x61, 0x79, 0x00, 0x61, 0xe3, 0x01, 0x40, 0x65, 0x6e, 0x73,
740x14, 0x02, 0x10, 0x6e, 0xec, 0x01, 0x01, 0xfd, 0x00, 0x00, 740x75, 0x42, 0x01, 0x22, 0x68, 0x61, 0x14, 0x02, 0x10, 0x6e,
750x1e, 0x00, 0xb3, 0x67, 0x75, 0x65, 0x73, 0x73, 0x00, 0x77, 750xec, 0x01, 0x01, 0xfd, 0x00, 0x00, 0x1e, 0x00, 0xb3, 0x67,
760x68, 0x65, 0x72, 0x65, 0x75, 0x01, 0x41, 0x00, 0x69, 0x73, 760x75, 0x65, 0x73, 0x73, 0x00, 0x77, 0x68, 0x65, 0x72, 0x65,
770x3a, 0x29, 0x00, 0x01, 0x6e, 0x00, 0xe0, 0x61, 0x6c, 0x77, 770x75, 0x01, 0x41, 0x00, 0x69, 0x73, 0x3a, 0x29, 0x00, 0x01,
780x61, 0x79, 0x73, 0x00, 0x62, 0x65, 0x00, 0x61, 0x62, 0x6c, 780x6e, 0x00, 0xe0, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x00,
790x65, 0x32, 0x00, 0x50, 0x64, 0x65, 0x64, 0x75, 0x63, 0x7e, 790x62, 0x65, 0x00, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x00, 0x50,
800x00, 0x01, 0x83, 0x02, 0x76, 0x68, 0x6f, 0x77, 0x2e, 0x00, 800x64, 0x65, 0x64, 0x75, 0x63, 0x7e, 0x00, 0x01, 0x83, 0x02,
810x53, 0x6f, 0x31, 0x00, 0x01, 0x5f, 0x00, 0x10, 0x2c, 0x7c, 810x76, 0x68, 0x6f, 0x77, 0x2e, 0x00, 0x53, 0x6f, 0x31, 0x00,
820x00, 0xa0, 0x63, 0x61, 0x6e, 0x00, 0x68, 0x61, 0x70, 0x70, 820x01, 0x5f, 0x00, 0x10, 0x2c, 0x7c, 0x00, 0xa0, 0x63, 0x61,
830x65, 0x6e, 0x98, 0x00, 0x54, 0x6f, 0x74, 0x68, 0x65, 0x72, 830x6e, 0x00, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x98, 0x00,
840xf8, 0x00, 0x60, 0x73, 0x2c, 0x00, 0x67, 0x65, 0x74, 0x4e, 840x54, 0x6f, 0x74, 0x68, 0x65, 0x72, 0xf8, 0x00, 0x60, 0x73,
850x00, 0x00, 0x48, 0x01, 0x60, 0x6c, 0x61, 0x73, 0x74, 0x00, 850x2c, 0x00, 0x67, 0x65, 0x74, 0x4e, 0x00, 0x00, 0x48, 0x01,
860x66, 0x99, 0x02, 0x03, 0xa8, 0x01, 0x82, 0x00, 0x61, 0x6e, 860x60, 0x6c, 0x61, 0x73, 0x74, 0x00, 0x66, 0x99, 0x02, 0x03,
870x64, 0x00, 0x64, 0x69, 0x73, 0x2c, 0x02, 0x01, 0xba, 0x00, 870xa8, 0x01, 0x82, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x64, 0x69,
880x12, 0x74, 0xa2, 0x00, 0x00, 0xc9, 0x00, 0x23, 0x77, 0x6f, 880x73, 0x2c, 0x02, 0x01, 0xba, 0x00, 0x12, 0x74, 0xa2, 0x00,
890x01, 0x02, 0x45, 0x6c, 0x65, 0x66, 0x74, 0xeb, 0x02, 0x01, 890x00, 0xc9, 0x00, 0x23, 0x77, 0x6f, 0x01, 0x02, 0x45, 0x6c,
900x30, 0x03, 0x21, 0x6e, 0x6f, 0xf9, 0x00, 0x21, 0x6f, 0x66, 900x65, 0x66, 0x74, 0xeb, 0x02, 0x01, 0x30, 0x03, 0x21, 0x6e,
910x90, 0x01, 0x00, 0xfa, 0x01, 0x41, 0x66, 0x6f, 0x72, 0x00, 910x6f, 0xf9, 0x00, 0x21, 0x6f, 0x66, 0x90, 0x01, 0x00, 0xfa,
920x00, 0x01, 0x02, 0xe3, 0x00, 0x40, 0x74, 0x68, 0x65, 0x79, 920x01, 0x41, 0x66, 0x6f, 0x72, 0x00, 0x00, 0x01, 0x02, 0xe3,
930x46, 0x00, 0x00, 0x89, 0x01, 0x43, 0x31, 0x32, 0x2e, 0x31, 930x00, 0x40, 0x74, 0x68, 0x65, 0x79, 0x46, 0x00, 0x00, 0x89,
940x74, 0x03, 0x20, 0x63, 0x6f, 0x03, 0x02, 0x39, 0x6c, 0x73, 940x01, 0x43, 0x31, 0x32, 0x2e, 0x31, 0x86, 0x03, 0x20, 0x63,
950x20, 0x21, 0x02, 0x00, 0xef, 0x01, 0x33, 0x6c, 0x61, 0x79, 950x6f, 0x03, 0x02, 0x39, 0x6c, 0x73, 0x20, 0x21, 0x02, 0x00,
960x5d, 0x02, 0x01, 0xab, 0x00, 0x41, 0x6d, 0x6f, 0x75, 0x73, 960xef, 0x01, 0x33, 0x6c, 0x61, 0x79, 0x5d, 0x02, 0x01, 0xab,
970x3d, 0x00, 0x03, 0xd0, 0x02, 0x00, 0x83, 0x00, 0x71, 0x2d, 970x00, 0x41, 0x6d, 0x6f, 0x75, 0x73, 0x3d, 0x00, 0x03, 0xd0,
980x63, 0x6c, 0x69, 0x63, 0x6b, 0x00, 0xb6, 0x02, 0x0a, 0xa7, 980x02, 0x00, 0x83, 0x00, 0x71, 0x2d, 0x63, 0x6c, 0x69, 0x63,
990x03, 0x06, 0xb5, 0x01, 0x24, 0x62, 0x65, 0xfb, 0x02, 0x27, 990x6b, 0x00, 0xb6, 0x02, 0x0a, 0xa7, 0x03, 0x06, 0xb5, 0x01,
1000x65, 0x64, 0x3f, 0x00, 0x10, 0x72, 0xaa, 0x02, 0x0f, 0x40, 1000x24, 0x62, 0x65, 0xfb, 0x02, 0x27, 0x65, 0x64, 0x3f, 0x00,
1010x00, 0x11, 0x40, 0x70, 0x6c, 0x61, 0x63, 0xa1, 0x01, 0x43, 1010x10, 0x72, 0xaa, 0x02, 0x0f, 0x40, 0x00, 0x11, 0x40, 0x70,
1020x66, 0x6c, 0x61, 0x67, 0x34, 0x03, 0x95, 0x69, 0x6e, 0x64, 1020x6c, 0x61, 0x63, 0xa1, 0x01, 0x43, 0x66, 0x6c, 0x61, 0x67,
1030x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x21, 0x01, 0x04, 0x54, 1030x34, 0x03, 0x95, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74,
1040x03, 0x10, 0x69, 0xb0, 0x01, 0x42, 0x6c, 0x69, 0x65, 0x76, 1040x65, 0x73, 0x21, 0x01, 0x04, 0x54, 0x03, 0x10, 0x69, 0xb0,
1050xe3, 0x01, 0x25, 0x62, 0x65, 0xbb, 0x03, 0x15, 0x4c, 0xaf, 1050x01, 0x42, 0x6c, 0x69, 0x65, 0x76, 0xe3, 0x01, 0x25, 0x62,
1060x00, 0x00, 0x1c, 0x01, 0x02, 0x68, 0x03, 0x35, 0x61, 0x72, 1060x65, 0xbb, 0x03, 0x15, 0x4c, 0xaf, 0x00, 0x00, 0x1c, 0x01,
1070x6b, 0x71, 0x00, 0x03, 0xc3, 0x01, 0x25, 0x6f, 0x74, 0xa9, 1070x02, 0x68, 0x03, 0x35, 0x61, 0x72, 0x6b, 0x71, 0x00, 0x03,
1080x03, 0x32, 0x69, 0x74, 0x2c, 0x44, 0x01, 0x51, 0x61, 0x66, 1080xc3, 0x01, 0x25, 0x6f, 0x74, 0xa9, 0x03, 0x32, 0x69, 0x74,
1090x65, 0x74, 0x79, 0x40, 0x04, 0x01, 0xd9, 0x01, 0x08, 0xba, 1090x2c, 0x44, 0x01, 0x51, 0x61, 0x66, 0x65, 0x74, 0x79, 0x40,
1100x00, 0x30, 0x61, 0x67, 0x61, 0x7a, 0x03, 0x83, 0x6f, 0x00, 1100x04, 0x01, 0xd9, 0x01, 0x08, 0xba, 0x00, 0x30, 0x61, 0x67,
1110x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x00, 0x02, 0xb4, 1110x61, 0x7a, 0x03, 0x83, 0x6f, 0x00, 0x72, 0x65, 0x6d, 0x6f,
1120x00, 0x11, 0x64, 0x00, 0x03, 0x4f, 0x72, 0x72, 0x6f, 0x72, 1120x76, 0x65, 0x55, 0x00, 0x02, 0xb4, 0x00, 0x11, 0x64, 0x00,
1130x36, 0x01, 0x07, 0x00, 0xfa, 0x02, 0x0f, 0xf9, 0x00, 0x05, 1130x03, 0x4f, 0x72, 0x72, 0x6f, 0x72, 0x36, 0x01, 0x07, 0x00,
1140x81, 0x60, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x00, 0x61, 0xcb, 1140xfa, 0x02, 0x0f, 0xf9, 0x00, 0x05, 0x81, 0x60, 0x63, 0x6c,
1150x03, 0x17, 0x27, 0xe6, 0x00, 0x12, 0x2e, 0xa9, 0x01, 0x98, 1150x65, 0x61, 0x72, 0x00, 0x61, 0xcb, 0x03, 0x17, 0x27, 0xe6,
1160x6d, 0x65, 0x61, 0x6e, 0x73, 0x3a, 0x00, 0x69, 0x66, 0x01, 1160x00, 0x12, 0x2e, 0xa9, 0x01, 0x98, 0x6d, 0x65, 0x61, 0x6e,
1170x01, 0x00, 0x50, 0x03, 0x61, 0x65, 0x78, 0x61, 0x63, 0x74, 1170x73, 0x3a, 0x00, 0x69, 0x66, 0x01, 0x01, 0x00, 0x50, 0x03,
1180x6c, 0x05, 0x03, 0x01, 0x34, 0x04, 0x00, 0x3a, 0x01, 0x19, 1180x61, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x05, 0x03, 0x01,
1190x73, 0x15, 0x04, 0x20, 0x69, 0x74, 0x1d, 0x00, 0x00, 0xc8, 1190x34, 0x04, 0x00, 0x3a, 0x01, 0x19, 0x73, 0x15, 0x04, 0x20,
1200x02, 0x52, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x42, 0x02, 0x03, 1200x69, 0x74, 0x1d, 0x00, 0x00, 0xc8, 0x02, 0x52, 0x68, 0x6f,
1210x41, 0x05, 0x81, 0x74, 0x68, 0x65, 0x6e, 0x00, 0x61, 0x6c, 1210x75, 0x6c, 0x64, 0x42, 0x02, 0x03, 0x41, 0x05, 0x81, 0x74,
1220x6c, 0x56, 0x00, 0x0b, 0x7c, 0x05, 0x41, 0x00, 0x6e, 0x65, 1220x68, 0x65, 0x6e, 0x00, 0x61, 0x6c, 0x6c, 0x56, 0x00, 0x0b,
1230x78, 0xbc, 0x02, 0x23, 0x69, 0x74, 0x8b, 0x01, 0x00, 0x74, 1230x7c, 0x05, 0x41, 0x00, 0x6e, 0x65, 0x78, 0xbc, 0x02, 0x23,
1240x00, 0x00, 0x35, 0x01, 0x00, 0x64, 0x00, 0x3e, 0x67, 0x65, 1240x69, 0x74, 0x8b, 0x01, 0x00, 0x74, 0x00, 0x00, 0x35, 0x01,
1250x64, 0xf3, 0x01, 0x01, 0x23, 0x03, 0x41, 0x6f, 0x6e, 0x63, 1250x00, 0x64, 0x00, 0x3e, 0x67, 0x65, 0x64, 0xf3, 0x01, 0x01,
1260x65, 0xff, 0x00, 0x00, 0xa9, 0x04, 0x11, 0x6b, 0x0a, 0x00, 1260x23, 0x03, 0x41, 0x6f, 0x6e, 0x63, 0x65, 0xff, 0x00, 0x00,
1270x01, 0x9a, 0x05, 0x01, 0x03, 0x03, 0x43, 0x6f, 0x63, 0x61, 1270xa9, 0x04, 0x11, 0x6b, 0x0a, 0x00, 0x01, 0x9a, 0x05, 0x01,
1280x74, 0x11, 0x04, 0x04, 0x77, 0x00, 0x04, 0xe6, 0x04, 0x00, 1280x03, 0x03, 0x43, 0x6f, 0x63, 0x61, 0x74, 0x11, 0x04, 0x04,
1290xb0, 0x00, 0x05, 0x31, 0x05, 0x02, 0x13, 0x05, 0x10, 0x63, 1290x77, 0x00, 0x04, 0xe6, 0x04, 0x00, 0xb0, 0x00, 0x05, 0x31,
1300x34, 0x01, 0x20, 0x73, 0x65, 0x4a, 0x00, 0x61, 0x73, 0x00, 1300x05, 0x02, 0x13, 0x05, 0x10, 0x63, 0x34, 0x01, 0x20, 0x73,
1310x66, 0x75, 0x6e, 0x63, 0x3c, 0x00, 0x00, 0x46, 0x04, 0x00, 1310x65, 0x4a, 0x00, 0x61, 0x73, 0x00, 0x66, 0x75, 0x6e, 0x63,
1320xcd, 0x00, 0x41, 0x72, 0x74, 0x63, 0x75, 0xa1, 0x00, 0x41, 1320x3c, 0x00, 0x00, 0x46, 0x04, 0x00, 0xcd, 0x00, 0x41, 0x72,
1330x61, 0x76, 0x6f, 0x69, 0xd8, 0x00, 0x00, 0xef, 0x00, 0x32, 1330x74, 0x63, 0x75, 0xa1, 0x00, 0x41, 0x61, 0x76, 0x6f, 0x69,
1340x74, 0x6f, 0x00, 0x73, 0x01, 0x92, 0x6f, 0x6e, 0x00, 0x65, 1340xd8, 0x00, 0x00, 0xef, 0x00, 0x32, 0x74, 0x6f, 0x00, 0x73,
1350x61, 0x63, 0x68, 0x00, 0x6f, 0x34, 0x01, 0x33, 0x72, 0x65, 1350x01, 0x92, 0x6f, 0x6e, 0x00, 0x65, 0x61, 0x63, 0x68, 0x00,
1360x6d, 0xb6, 0x05, 0x04, 0xe0, 0x00, 0x30, 0x6f, 0x6e, 0x65, 1360x6f, 0x34, 0x01, 0x33, 0x72, 0x65, 0x6d, 0xb6, 0x05, 0x04,
1370xfa, 0x04, 0x28, 0x6f, 0x6e, 0xea, 0x02, 0x0f, 0xba, 0x05, 1370xe0, 0x00, 0x30, 0x6f, 0x6e, 0x65, 0xfa, 0x04, 0x28, 0x6f,
1380x04, 0x00, 0x6b, 0x01, 0x14, 0x6e, 0x91, 0x03, 0x03, 0x80, 1380x6e, 0xea, 0x02, 0x0f, 0xba, 0x05, 0x04, 0x00, 0x6b, 0x01,
1390x05, 0x08, 0x65, 0x01, 0x03, 0x8c, 0x05, 0x04, 0xb6, 0x06, 1390x14, 0x6e, 0x91, 0x03, 0x03, 0x80, 0x05, 0x08, 0x65, 0x01,
1400x01, 0x58, 0x01, 0x02, 0x4a, 0x05, 0x91, 0x6f, 0x62, 0x76, 1400x03, 0x8c, 0x05, 0x04, 0xb6, 0x06, 0x01, 0x58, 0x01, 0x02,
1410x69, 0x6f, 0x75, 0x73, 0x6c, 0x79, 0x5c, 0x02, 0x08, 0x8d, 1410x4a, 0x05, 0x91, 0x6f, 0x62, 0x76, 0x69, 0x6f, 0x75, 0x73,
1420x06, 0x44, 0x74, 0x68, 0x6f, 0x73, 0xcc, 0x01, 0x02, 0x55, 1420x6c, 0x79, 0x5c, 0x02, 0x08, 0x8d, 0x06, 0x44, 0x74, 0x68,
1430x00, 0x41, 0x75, 0x72, 0x6e, 0x2c, 0x15, 0x04, 0x10, 0x73, 1430x6f, 0x73, 0xcc, 0x01, 0x02, 0x55, 0x00, 0x41, 0x75, 0x72,
1440x47, 0x01, 0x00, 0xee, 0x01, 0x00, 0xd3, 0x01, 0x02, 0xc1, 1440x6e, 0x2c, 0x15, 0x04, 0x10, 0x73, 0x47, 0x01, 0x00, 0xee,
1450x00, 0x64, 0x6d, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x89, 0x00, 1450x01, 0x00, 0xd3, 0x01, 0x02, 0xc1, 0x00, 0x64, 0x6d, 0x00,
1460x08, 0x7c, 0x00, 0x01, 0x95, 0x00, 0x03, 0x28, 0x02, 0x04, 1460x61, 0x6c, 0x73, 0x6f, 0x89, 0x00, 0x08, 0x7c, 0x00, 0x01,
1470x93, 0x01, 0x10, 0x64, 0xdd, 0x00, 0x00, 0xd1, 0x02, 0x01, 1470x95, 0x00, 0x03, 0x28, 0x02, 0x04, 0x93, 0x01, 0x10, 0x64,
1480x5a, 0x06, 0xd0, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 1480xdd, 0x00, 0x00, 0xd1, 0x02, 0x01, 0x5a, 0x06, 0xd0, 0x75,
1490x63, 0x61, 0x6c, 0x6c, 0x79, 0x3b, 0x5d, 0x00, 0x00, 0xd3, 1490x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c,
1500x04, 0x41, 0x74, 0x69, 0x6d, 0x65, 0x13, 0x05, 0x1f, 0x6e, 1500x79, 0x3b, 0x5d, 0x00, 0x00, 0xd3, 0x04, 0x41, 0x74, 0x69,
1510xf9, 0x00, 0x02, 0x10, 0x2c, 0x5e, 0x05, 0x30, 0x68, 0x6f, 1510x6d, 0x65, 0x13, 0x05, 0x1f, 0x6e, 0xf9, 0x00, 0x02, 0x10,
1520x6c, 0x41, 0x06, 0x10, 0x77, 0xfb, 0x01, 0x12, 0x61, 0x5d, 1520x2c, 0x5e, 0x05, 0x30, 0x68, 0x6f, 0x6c, 0x41, 0x06, 0x10,
1530x00, 0x10, 0x6f, 0xe9, 0x04, 0x23, 0x75, 0x70, 0x72, 0x03, 1530x77, 0xfb, 0x01, 0x12, 0x61, 0x5d, 0x00, 0x10, 0x6f, 0xe9,
1540x53, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0xed, 0x03, 0x04, 0x34, 1540x04, 0x23, 0x75, 0x70, 0x72, 0x03, 0x53, 0x65, 0x78, 0x70,
1550x03, 0x01, 0xa8, 0x00, 0x02, 0xb7, 0x01, 0xc1, 0x65, 0x00, 1550x6c, 0x6f, 0xed, 0x03, 0x04, 0x34, 0x03, 0x01, 0xa8, 0x00,
1560x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 1560x02, 0xb7, 0x01, 0xc1, 0x65, 0x00, 0x63, 0x75, 0x72, 0x73,
1570xa6, 0x05, 0x02, 0x39, 0x03, 0x02, 0xec, 0x01, 0x04, 0xfd, 1570x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xa6, 0x05, 0x02, 0x39,
1580x01, 0xb0, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x00, 0x50, 1580x03, 0x02, 0xec, 0x01, 0x04, 0xfd, 0x01, 0xb0, 0x66, 0x69,
1590x72, 0x65, 0x73, 0x6b, 0x06, 0x03, 0xae, 0x01, 0x00, 0x0d, 1590x65, 0x6c, 0x64, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x6b,
1600x01, 0x00, 0x37, 0x00, 0x0f, 0x31, 0x04, 0x01, 0x04, 0xad, 1600x06, 0x03, 0xae, 0x01, 0x00, 0x0d, 0x01, 0x00, 0x37, 0x00,
1610x00, 0x00, 0xd7, 0x02, 0x02, 0x31, 0x01, 0x0e, 0x60, 0x03, 1610x0f, 0x31, 0x04, 0x01, 0x04, 0xad, 0x00, 0x00, 0xd7, 0x02,
1620x06, 0xe8, 0x03, 0x08, 0x5b, 0x03, 0x00, 0x95, 0x01, 0x31, 1620x02, 0x31, 0x01, 0x0e, 0x60, 0x03, 0x06, 0xe8, 0x03, 0x08,
1630x28, 0x73, 0x6f, 0x15, 0x03, 0x52, 0x63, 0x74, 0x73, 0x00, 1630x5b, 0x03, 0x00, 0x95, 0x01, 0x31, 0x28, 0x73, 0x6f, 0x15,
1640x61, 0xe3, 0x06, 0x04, 0x63, 0x05, 0x78, 0x74, 0x6f, 0x6e, 1640x03, 0x52, 0x63, 0x74, 0x73, 0x00, 0x61, 0xe3, 0x06, 0x04,
1650x29, 0x2c, 0x00, 0x70, 0x8a, 0x00, 0x20, 0x73, 0x70, 0x93, 1650x63, 0x05, 0x78, 0x74, 0x6f, 0x6e, 0x29, 0x2c, 0x00, 0x70,
1660x04, 0x3f, 0x62, 0x61, 0x72, 0x89, 0x00, 0x02, 0x0e, 0xb6, 1660x8a, 0x00, 0x20, 0x73, 0x70, 0x93, 0x04, 0x3f, 0x62, 0x61,
1670x04, 0xbc, 0x28, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 1670x72, 0x89, 0x00, 0x02, 0x0e, 0xb6, 0x04, 0xbc, 0x28, 0x73,
1680x6c, 0x79, 0x2c, 0x66, 0x00, 0x01, 0x4d, 0x04, 0x04, 0x67, 1680x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x2c, 0x66,
1690x00, 0x00, 0x32, 0x01, 0x13, 0x41, 0x03, 0x03, 0x22, 0x61, 1690x00, 0x01, 0x4d, 0x04, 0x04, 0x67, 0x00, 0x00, 0x32, 0x01,
1700x63, 0xd8, 0x06, 0x72, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 1700x13, 0x41, 0x03, 0x03, 0x22, 0x61, 0x63, 0xd8, 0x06, 0x72,
1710x62, 0x4e, 0x04, 0x22, 0x73, 0x65, 0xef, 0x02, 0x31, 0x32, 1710x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x4e, 0x04, 0x22,
1720x2e, 0x31, 0x7a, 0x03, 0x01, 0x57, 0x01, 0x50, 0x61, 0x76, 1720x73, 0x65, 0xef, 0x02, 0x31, 0x32, 0x2e, 0x31, 0x7a, 0x03,
1730x61, 0x69, 0x6c, 0xa3, 0x06, 0x00, 0x3f, 0x00, 0x90, 0x45, 1730x01, 0x57, 0x01, 0x50, 0x61, 0x76, 0x61, 0x69, 0x6c, 0xa3,
1740x76, 0x65, 0x6e, 0x00, 0x55, 0x6e, 0x64, 0x6f, 0x68, 0x02, 1740x06, 0x00, 0x3f, 0x00, 0x90, 0x45, 0x76, 0x65, 0x6e, 0x00,
1750x05, 0x1a, 0x00, 0xa1, 0x2c, 0x00, 0x61, 0x6c, 0x74, 0x68, 1750x55, 0x6e, 0x64, 0x6f, 0x68, 0x02, 0x05, 0x1a, 0x00, 0xa1,
1760x6f, 0x75, 0x67, 0x68, 0xd8, 0x01, 0x11, 0x6d, 0x75, 0x00, 1760x2c, 0x00, 0x61, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68,
1770x61, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0xe9, 0x04, 0x63, 1770xd8, 0x01, 0x11, 0x6d, 0x75, 0x00, 0x61, 0x63, 0x6f, 0x6e,
1780x00, 0x63, 0x68, 0x65, 0x61, 0x74, 0x2b, 0x03, 0x20, 0x75, 1780x73, 0x69, 0x64, 0xe9, 0x04, 0x63, 0x00, 0x63, 0x68, 0x65,
1790x73, 0xe3, 0x06, 0x05, 0xb8, 0x08, 0x66, 0x73, 0x74, 0x65, 1790x61, 0x74, 0x2b, 0x03, 0x20, 0x75, 0x73, 0xe3, 0x06, 0x05,
1800x70, 0x00, 0x6f, 0x98, 0x08, 0x00, 0x9d, 0x00, 0x73, 0x70, 1800xb8, 0x08, 0x66, 0x73, 0x74, 0x65, 0x70, 0x00, 0x6f, 0x98,
1810x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0xfe, 0x01, 0x96, 0x6e, 1810x08, 0x00, 0x9d, 0x00, 0x73, 0x70, 0x72, 0x6f, 0x67, 0x72,
1820x6c, 0x79, 0x00, 0x72, 0x65, 0x76, 0x65, 0x61, 0xbd, 0x03, 1820x61, 0x6d, 0xfe, 0x01, 0x96, 0x6e, 0x6c, 0x79, 0x00, 0x72,
1830x00, 0xad, 0x00, 0x41, 0x71, 0x75, 0x65, 0x73, 0xae, 0x00, 1830x65, 0x76, 0x65, 0x61, 0xbd, 0x03, 0x00, 0xad, 0x00, 0x41,
1840x60, 0x28, 0x75, 0x6e, 0x6c, 0x69, 0x6b, 0x42, 0x06, 0x23, 1840x71, 0x75, 0x65, 0x73, 0xae, 0x00, 0x60, 0x28, 0x75, 0x6e,
1850x73, 0x74, 0x0d, 0x07, 0xb2, 0x69, 0x6d, 0x70, 0x6c, 0x65, 1850x6c, 0x69, 0x6b, 0x42, 0x06, 0x23, 0x73, 0x74, 0x0d, 0x07,
1860x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x14, 0x07, 0x02, 0x53, 1860xb2, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
1870x03, 0x03, 0x46, 0x00, 0x00, 0x07, 0x04, 0x03, 0xe2, 0x02, 1870x61, 0x74, 0x14, 0x07, 0x02, 0x53, 0x03, 0x03, 0x46, 0x00,
1880x16, 0x29, 0x78, 0x05, 0x24, 0x74, 0x68, 0xdb, 0x00, 0x10, 1880x00, 0x07, 0x04, 0x03, 0xe2, 0x02, 0x16, 0x29, 0x78, 0x05,
1890x79, 0x31, 0x07, 0x53, 0x66, 0x61, 0x74, 0x61, 0x6c, 0x3d, 1890x24, 0x74, 0x68, 0xdb, 0x00, 0x10, 0x79, 0x31, 0x07, 0x53,
1900x02, 0x21, 0x6e, 0x64, 0x0f, 0x09, 0x41, 0x69, 0x6e, 0x75, 1900x66, 0x61, 0x74, 0x61, 0x6c, 0x3d, 0x02, 0x21, 0x6e, 0x64,
1910x65, 0xb8, 0x06, 0x01, 0xe4, 0x04, 0x03, 0x71, 0x05, 0x21, 1910x0f, 0x09, 0x41, 0x69, 0x6e, 0x75, 0x65, 0xb8, 0x06, 0x01,
1920x69, 0x6b, 0x31, 0x05, 0x0b, 0xb5, 0x00, 0x51, 0x74, 0x72, 1920xe4, 0x04, 0x03, 0x71, 0x05, 0x21, 0x69, 0x6b, 0x31, 0x05,
1930x61, 0x63, 0x6b, 0xaf, 0x00, 0x61, 0x6e, 0x75, 0x6d, 0x62, 1930x0b, 0xb5, 0x00, 0x51, 0x74, 0x72, 0x61, 0x63, 0x6b, 0xaf,
1940x65, 0x72, 0x70, 0x00, 0x01, 0xfe, 0x02, 0x01, 0x3d, 0x0a, 1940x00, 0x61, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x70, 0x00,
1950x50, 0x69, 0x65, 0x64, 0x00, 0x28, 0x57, 0x00, 0x01, 0x70, 1950x01, 0xfe, 0x02, 0x01, 0x3d, 0x0a, 0x50, 0x69, 0x65, 0x64,
1960x00, 0x05, 0x1b, 0x06, 0x12, 0x72, 0x01, 0x08, 0x01, 0x76, 1960x00, 0x28, 0x57, 0x00, 0x01, 0x70, 0x00, 0x05, 0x1b, 0x06,
1970x06, 0x80, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x29, 1970x12, 0x72, 0x01, 0x08, 0x01, 0x76, 0x06, 0x80, 0x63, 0x6f,
1980x90, 0x0a, 0x06, 0x33, 0x03, 0x07, 0xdc, 0x07, 0x34, 0x65, 1980x75, 0x6e, 0x74, 0x65, 0x72, 0x29, 0x90, 0x0a, 0x06, 0x33,
1990x6e, 0x64, 0x69, 0x04, 0x01, 0x54, 0x07, 0x05, 0xf6, 0x04, 1990x03, 0x07, 0xdc, 0x07, 0x34, 0x65, 0x6e, 0x64, 0x69, 0x04,
2000x31, 0x77, 0x68, 0x65, 0x05, 0x01, 0x21, 0x6f, 0x72, 0x55, 2000x01, 0x54, 0x07, 0x05, 0xf6, 0x04, 0x31, 0x77, 0x68, 0x65,
2010x00, 0x02, 0x71, 0x00, 0x12, 0x64, 0xf8, 0x05, 0x00, 0xa6, 2010x05, 0x01, 0x21, 0x6f, 0x72, 0x55, 0x00, 0x02, 0x71, 0x00,
2020x01, 0x41, 0x74, 0x00, 0x6d, 0x61, 0xa6, 0x06, 0x00, 0xea, 2020x12, 0x64, 0xf8, 0x05, 0x00, 0xa6, 0x01, 0x41, 0x74, 0x00,
2030x03, 0x01, 0x41, 0x06, 0x01, 0xbf, 0x09, 0x14, 0x28, 0x3a, 2030x6d, 0x61, 0xa6, 0x06, 0x00, 0xea, 0x03, 0x01, 0x41, 0x06,
2040x07, 0x10, 0x65, 0xb4, 0x03, 0x41, 0x00, 0x77, 0x61, 0x6e, 2040x01, 0xbf, 0x09, 0x14, 0x28, 0x3a, 0x07, 0x10, 0x65, 0xb4,
2050x6b, 0x00, 0x05, 0x49, 0x05, 0x80, 0x66, 0x75, 0x6c, 0x6c, 2050x03, 0x41, 0x00, 0x77, 0x61, 0x6e, 0x6b, 0x00, 0x05, 0x49,
2060x00, 0x6c, 0x61, 0x79, 0x41, 0x00, 0x04, 0x78, 0x00, 0x34, 2060x05, 0x80, 0x66, 0x75, 0x6c, 0x6c, 0x00, 0x6c, 0x61, 0x79,
2070x72, 0x69, 0x64, 0x5c, 0x01, 0x0f, 0x79, 0x01, 0x02, 0x02, 2070x41, 0x00, 0x04, 0x78, 0x00, 0x34, 0x72, 0x69, 0x64, 0x5c,
2080xdb, 0x00, 0x10, 0x73, 0x5f, 0x0a, 0x01, 0x16, 0x04, 0x44, 2080x01, 0x0f, 0x79, 0x01, 0x02, 0x02, 0xdb, 0x00, 0x10, 0x73,
2090x66, 0x74, 0x65, 0x72, 0x02, 0x01, 0x06, 0x70, 0x05, 0x03, 2090x5f, 0x0a, 0x01, 0x16, 0x04, 0x44, 0x66, 0x74, 0x65, 0x72,
2100x0a, 0x09, 0x04, 0xc0, 0x03, 0x30, 0x53, 0x6f, 0x6c, 0x2f, 2100x02, 0x01, 0x06, 0x70, 0x05, 0x03, 0x0a, 0x09, 0x04, 0xc0,
2110x06, 0x60, 0x65, 0x6e, 0x75, 0x00, 0x6f, 0x70, 0x47, 0x00, 2110x03, 0x30, 0x53, 0x6f, 0x6c, 0x2f, 0x06, 0x60, 0x65, 0x6e,
2120x22, 0x2e, 0x29, 0x54, 0x08, 0x13, 0x32, 0x54, 0x08, 0x93, 2120x75, 0x00, 0x6f, 0x70, 0x47, 0x00, 0x22, 0x2e, 0x29, 0x54,
2130x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 2130x08, 0x13, 0x32, 0x54, 0x08, 0x93, 0x70, 0x61, 0x72, 0x61,
2140x08, 0x13, 0x65, 0x28, 0x00, 0x07, 0x92, 0x02, 0x51, 0x00, 2140x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x08, 0x13, 0x65, 0x28,
2150x66, 0x72, 0x6f, 0x6d, 0x4e, 0x00, 0xb3, 0x60, 0x43, 0x75, 2150x00, 0x07, 0x92, 0x02, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d,
2160x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x27, 0x00, 2160x4e, 0x00, 0xb3, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
2170x00, 0x6e, 0x02, 0x01, 0x1a, 0x00, 0x52, 0x54, 0x79, 0x70, 2170x2e, 0x2e, 0x2e, 0x27, 0x27, 0x00, 0x00, 0x6e, 0x02, 0x01,
2180x65, 0x27, 0x69, 0x00, 0xf2, 0x00, 0x61, 0x72, 0x65, 0x3a, 2180x1a, 0x00, 0x52, 0x54, 0x79, 0x70, 0x65, 0x27, 0x69, 0x00,
2190x00, 0x00, 0x00, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 2190xf2, 0x00, 0x61, 0x72, 0x65, 0x3a, 0x00, 0x00, 0x00, 0x57,
2200x48, 0x5c, 0x05, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x0b, 2200x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x5c, 0x05, 0x51,
2210x0c, 0x01, 0x2c, 0x0c, 0x28, 0x69, 0x6e, 0xee, 0x0a, 0x11, 2210x00, 0x00, 0x53, 0x69, 0x7a, 0x0b, 0x0c, 0x01, 0x2c, 0x0c,
2220x4d, 0x98, 0x05, 0x35, 0x00, 0x00, 0x4e, 0xe0, 0x01, 0x09, 2220x28, 0x69, 0x6e, 0xee, 0x0a, 0x11, 0x4d, 0x98, 0x05, 0x35,
2230xaa, 0x05, 0x00, 0x32, 0x00, 0x06, 0x5c, 0x02, 0x10, 0x65, 2230x00, 0x00, 0x4e, 0xe0, 0x01, 0x09, 0xaa, 0x05, 0x00, 0x32,
2240xcb, 0x01, 0x02, 0x54, 0x06, 0x02, 0x91, 0x0a, 0x73, 0x61, 2240x00, 0x06, 0x5c, 0x02, 0x10, 0x65, 0xcb, 0x01, 0x02, 0x54,
2250x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0xce, 0x02, 0x01, 0xec, 2250x06, 0x02, 0x91, 0x0a, 0x73, 0x61, 0x62, 0x73, 0x6f, 0x6c,
2260x01, 0x10, 0x2c, 0xb1, 0x01, 0xd5, 0x61, 0x6c, 0x74, 0x65, 2260x75, 0x74, 0xce, 0x02, 0x01, 0xec, 0x01, 0x10, 0x2c, 0xb1,
2270x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x21, 2270x01, 0xd5, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74,
2280x01, 0xb5, 0x70, 0x75, 0x74, 0x00, 0x61, 0x00, 0x25, 0x00, 2280x69, 0x76, 0x65, 0x6c, 0x79, 0x21, 0x01, 0xb5, 0x70, 0x75,
2290x73, 0x69, 0x67, 0xbe, 0x00, 0x12, 0x65, 0x83, 0x04, 0x03, 2290x74, 0x00, 0x61, 0x00, 0x25, 0x00, 0x73, 0x69, 0x67, 0xbe,
2300xa5, 0x0c, 0x36, 0x61, 0x73, 0x65, 0x0a, 0x02, 0x02, 0x5f, 2300x00, 0x12, 0x65, 0x83, 0x04, 0x03, 0xa5, 0x0c, 0x36, 0x61,
2310x0a, 0x52, 0x72, 0x72, 0x61, 0x6e, 0x67, 0x92, 0x05, 0x01, 2310x73, 0x65, 0x0a, 0x02, 0x02, 0x5f, 0x0a, 0x52, 0x72, 0x72,
2320x51, 0x02, 0x00, 0xfc, 0x0a, 0x24, 0x6f, 0x72, 0x0b, 0x07, 2320x61, 0x6e, 0x67, 0x92, 0x05, 0x01, 0x51, 0x02, 0x00, 0xfc,
2330x06, 0xd4, 0x07, 0x09, 0xb3, 0x00, 0x03, 0x64, 0x05, 0x03, 2330x0a, 0x24, 0x6f, 0x72, 0x0b, 0x07, 0x06, 0xd4, 0x07, 0x09,
2340xe0, 0x05, 0x71, 0x00, 0x00, 0x42, 0x65, 0x77, 0x61, 0x72, 2340xb3, 0x00, 0x03, 0x64, 0x05, 0x03, 0xe0, 0x05, 0x71, 0x00,
2350x03, 0x01, 0x44, 0x73, 0x65, 0x74, 0x74, 0xa1, 0x04, 0x06, 2350x00, 0x42, 0x65, 0x77, 0x61, 0x72, 0x03, 0x01, 0x44, 0x73,
2360xb5, 0x00, 0xe1, 0x00, 0x74, 0x6f, 0x6f, 0x00, 0x68, 0x69, 2360x65, 0x74, 0x74, 0xa1, 0x04, 0x06, 0xb5, 0x00, 0xe1, 0x00,
2370x67, 0x68, 0x2e, 0x00, 0x41, 0x74, 0x00, 0xeb, 0x0c, 0x00, 2370x74, 0x6f, 0x6f, 0x00, 0x68, 0x69, 0x67, 0x68, 0x2e, 0x00,
2380x0e, 0x00, 0x40, 0x00, 0x64, 0x65, 0x6e, 0x3a, 0x0b, 0x2a, 2380x41, 0x74, 0x00, 0xeb, 0x0c, 0x00, 0x0e, 0x00, 0x40, 0x00,
2390x65, 0x73, 0xd1, 0x03, 0x60, 0x6d, 0x61, 0x79, 0x00, 0x73, 2390x64, 0x65, 0x6e, 0x3a, 0x0b, 0x2a, 0x65, 0x73, 0xd1, 0x03,
2400x70, 0xbb, 0x00, 0x31, 0x66, 0x6f, 0x72, 0x2e, 0x0b, 0x64, 2400x60, 0x6d, 0x61, 0x79, 0x00, 0x73, 0x70, 0xbb, 0x00, 0x31,
2410x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x0a, 0x61, 0x61, 2410x66, 0x6f, 0x72, 0x2e, 0x0b, 0x64, 0x73, 0x65, 0x61, 0x72,
2420x00, 0x73, 0x6f, 0x6c, 0x76, 0xc1, 0x01, 0x02, 0x41, 0x01, 2420x63, 0x68, 0x50, 0x0a, 0x61, 0x61, 0x00, 0x73, 0x6f, 0x6c,
2430x31, 0x00, 0x00, 0x45, 0x65, 0x0b, 0x10, 0x20, 0x2f, 0x01, 2430x76, 0xc1, 0x01, 0x02, 0x41, 0x01, 0x31, 0x00, 0x00, 0x45,
2440x60, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0xa7, 0x01, 0x00, 2440x65, 0x0b, 0x10, 0x20, 0x2f, 0x01, 0x60, 0x62, 0x69, 0x6c,
2450x0f, 0x03, 0x01, 0x4e, 0x01, 0x03, 0xd2, 0x01, 0x50, 0x69, 2450x69, 0x74, 0x79, 0xa7, 0x01, 0x00, 0x0f, 0x03, 0x01, 0x4e,
2460x73, 0x00, 0x65, 0x6e, 0x37, 0x00, 0x00, 0x57, 0x03, 0x13, 2460x01, 0x03, 0xd2, 0x01, 0x50, 0x69, 0x73, 0x00, 0x65, 0x6e,
2470x73, 0x07, 0x07, 0x15, 0x62, 0xe3, 0x0b, 0x23, 0x29, 0x2c, 2470x37, 0x00, 0x00, 0x57, 0x03, 0x13, 0x73, 0x07, 0x07, 0x15,
2480xaf, 0x01, 0x01, 0x1a, 0x01, 0x08, 0xb7, 0x0b, 0x02, 0x4a, 2480x62, 0xe3, 0x0b, 0x23, 0x29, 0x2c, 0xaf, 0x01, 0x01, 0x1a,
2490x01, 0x33, 0x74, 0x69, 0x72, 0xff, 0x00, 0x00, 0x6e, 0x01, 2490x01, 0x08, 0xb7, 0x0b, 0x02, 0x4a, 0x01, 0x33, 0x74, 0x69,
2500x12, 0x62, 0xec, 0x02, 0x13, 0x79, 0x90, 0x0b, 0x61, 0x64, 2500x72, 0xff, 0x00, 0x00, 0x6e, 0x01, 0x12, 0x62, 0xec, 0x02,
2510x00, 0x73, 0x74, 0x61, 0x72, 0xfc, 0x00, 0x05, 0x55, 0x02, 2510x13, 0x79, 0x90, 0x0b, 0x61, 0x64, 0x00, 0x73, 0x74, 0x61,
2520x63, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x9a, 0x06, 0x01, 2520x72, 0xfc, 0x00, 0x05, 0x55, 0x02, 0x63, 0x69, 0x6e, 0x69,
2530xaf, 0x05, 0x05, 0xd0, 0x04, 0x63, 0x70, 0x72, 0x65, 0x66, 2530x74, 0x69, 0x61, 0x9a, 0x06, 0x01, 0xaf, 0x05, 0x05, 0xd0,
2540x65, 0x72, 0x7d, 0x05, 0x51, 0x73, 0x6b, 0x69, 0x65, 0x72, 2540x04, 0x63, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x7d, 0x05,
2550x5a, 0x00, 0x15, 0x73, 0x5e, 0x0c, 0x01, 0xfc, 0x0c, 0x0f, 2550x51, 0x73, 0x6b, 0x69, 0x65, 0x72, 0x5a, 0x00, 0x15, 0x73,
2560xa7, 0x04, 0x04, 0x04, 0xf1, 0x01, 0x41, 0x73, 0x77, 0x69, 2560x5e, 0x0c, 0x01, 0xfc, 0x0c, 0x0f, 0xa7, 0x04, 0x04, 0x04,
2570x74, 0x48, 0x08, 0x15, 0x66, 0xe7, 0x00, 0x50, 0x69, 0x6f, 2570xf1, 0x01, 0x41, 0x73, 0x77, 0x69, 0x74, 0x48, 0x08, 0x15,
2580x6e, 0x2e, 0x00, 2580x66, 0xe7, 0x00, 0x50, 0x69, 0x6f, 0x6e, 0x2e, 0x00,
259}; 259};
260 260
261const unsigned short help_text_len = 3796; 261const unsigned short help_text_len = 3814;
262const unsigned short help_text_words = 731; 262const unsigned short help_text_words = 732;
263const bool help_valid = true;
263const char quick_help_text[] = "Find all the mines without treading on any of them."; 264const char quick_help_text[] = "Find all the mines without treading on any of them.";
diff --git a/apps/plugins/puzzles/help/mosaic.c b/apps/plugins/puzzles/help/mosaic.c
new file mode 100644
index 0000000000..eb0c52c0bf
--- /dev/null
+++ b/apps/plugins/puzzles/help/mosaic.c
@@ -0,0 +1,151 @@
1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */
4
5#include "lib/display_text.h"
6
7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED },
9 { 43, TEXT_UNDERLINE },
10 { 105, TEXT_CENTER | C_RED },
11 { 223, TEXT_CENTER | C_RED },
12 { 240, TEXT_UNDERLINE },
13 { 241, TEXT_UNDERLINE },
14 { 251, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM
16};
17
18/* orig 1673 comp 1259 ratio 0.75254 level 10 saved 414 */
19const char help_text[] = {
200xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x34, 0x32, 0x3a, 0x20, 0x4d, 0x6f, 0x73, 0x61, 0x69, 0x63,
220x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x3a, 0x00, 0x00, 0x00,
230x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69,
240x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64,
250x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65,
260x73, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x79,
270x6f, 0x75, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x63, 0x6f,
280x6c, 0x6f, 0x75, 0x72, 0x00, 0x65, 0x69, 0x74, 0x68, 0x65,
290x72, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x00, 0x6f, 0x72,
300x26, 0x00, 0xa4, 0x74, 0x65, 0x2e, 0x00, 0x00, 0x00, 0x53,
310x6f, 0x6d, 0x65, 0x3d, 0x00, 0xf2, 0x0c, 0x00, 0x63, 0x6f,
320x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x63, 0x6c, 0x75, 0x65,
330x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x00,
340x45, 0x61, 0x63, 0x68, 0x13, 0x00, 0x51, 0x74, 0x65, 0x6c,
350x6c, 0x73, 0x5c, 0x00, 0x24, 0x74, 0x68, 0x21, 0x00, 0x00,
360x7d, 0x00, 0x02, 0x57, 0x00, 0x04, 0x46, 0x00, 0x21, 0x69,
370x6e, 0x1f, 0x00, 0xf1, 0x07, 0x33, 0x2a, 0x33, 0x00, 0x72,
380x65, 0x67, 0x69, 0x6f, 0x6e, 0x00, 0x73, 0x75, 0x72, 0x72,
390x6f, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x1b, 0x00, 0x01,
400x4d, 0x00, 0x7a, 0x2d, 0x00, 0x69, 0x6e, 0x63, 0x6c, 0x75,
410x15, 0x00, 0x02, 0x44, 0x00, 0x70, 0x00, 0x69, 0x74, 0x73,
420x65, 0x6c, 0x66, 0xa0, 0x00, 0xf0, 0x0d, 0x54, 0x68, 0x69,
430x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x69, 0x73, 0x00,
440x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x6c, 0x79, 0x00,
450x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x6a, 0x00, 0x11, 0x6f, 0xd8,
460x00, 0xf2, 0x02, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
470x6e, 0x73, 0x00, 0x61, 0x73, 0x3a, 0x00, 0x41, 0x72, 0x74,
480x44, 0x01, 0xf0, 0x0e, 0x6f, 0x2c, 0x00, 0x43, 0x6f, 0x75,
490x6e, 0x74, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x44, 0x61, 0x72,
500x6b, 0x65, 0x6e, 0x2c, 0x00, 0x43, 0x75, 0x65, 0x6e, 0x74,
510x61, 0x00, 0x59, 0xfb, 0x00, 0xf3, 0x01, 0x62, 0x72, 0x65,
520x61, 0x2c, 0x00, 0x46, 0x69, 0x6c, 0x6c, 0x2d, 0x61, 0x2d,
530x50, 0x69, 0x78, 0x0c, 0x00, 0xf0, 0x0a, 0x49, 0x6e, 0x2c,
540x00, 0x4b, 0x6f, 0x6d, 0x73, 0x75, 0x00, 0x4b, 0x61, 0x72,
550x61, 0x6c, 0x61, 0x2c, 0x00, 0x4d, 0x61, 0x67, 0x69, 0x70,
560x69, 0x63, 0x09, 0x00, 0x85, 0x6a, 0x69, 0x70, 0x69, 0x6b,
570x75, 0x2c, 0x00, 0x63, 0x00, 0x01, 0x09, 0x00, 0x10, 0x6b,
580x08, 0x00, 0xf4, 0x0d, 0x7a, 0x61, 0x69, 0x65, 0x6b, 0x2c,
590x00, 0x4e, 0x61, 0x6d, 0x70, 0x72, 0x65, 0x00, 0x50, 0x75,
600x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x00, 0x4e, 0x75, 0x72, 0x69,
610x65, 0x2d, 0x0e, 0x00, 0x62, 0x4f, 0x65, 0x6b, 0x61, 0x6b,
620x69, 0x6d, 0x00, 0x81, 0x56, 0x6f, 0x69, 0x73, 0x69, 0x6d,
630x61, 0x67, 0x8a, 0x01, 0x02, 0x50, 0x00, 0x32, 0x00, 0x77,
640x61, 0x88, 0x01, 0xe0, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
650x64, 0x00, 0x74, 0x6f, 0x00, 0x74, 0x68, 0x69, 0x14, 0x00,
660x50, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x4f, 0x01, 0xf2, 0x01,
670x62, 0x79, 0x00, 0x44, 0x69, 0x64, 0x69, 0x00, 0x4b, 0x6f,
680x68, 0x65, 0x6e, 0x2e, 0x00, 0x43, 0xe3, 0x01, 0x51, 0x64,
690x65, 0x73, 0x69, 0x67, 0x1d, 0x00, 0xf0, 0x0a, 0x4d, 0x69,
700x63, 0x68, 0x61, 0x6c, 0x00, 0x53, 0x68, 0x6f, 0x6d, 0x65,
710x72, 0x2e, 0x00, 0x54, 0x68, 0x65, 0x00, 0x69, 0x6d, 0x70,
720x6c, 0x65, 0x6d, 0xf9, 0x00, 0x01, 0x42, 0x00, 0xf2, 0x1b,
730x69, 0x73, 0x00, 0x6c, 0x6f, 0x6f, 0x73, 0x65, 0x6c, 0x79,
740x00, 0x62, 0x61, 0x73, 0x65, 0x64, 0x00, 0x6f, 0x6e, 0x00,
750x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
760x2f, 0x6d, 0x6f, 0x72, 0x64, 0x65, 0x63, 0x68, 0x61, 0x69,
770x6d, 0x2f, 0x96, 0x00, 0x00, 0xa0, 0x00, 0x44, 0x34, 0x32,
780x2e, 0x31, 0x9c, 0x02, 0x01, 0xa1, 0x00, 0x40, 0x6f, 0x6c,
790x73, 0x20, 0xa2, 0x01, 0x63, 0x6f, 0x00, 0x70, 0x6c, 0x61,
800x79, 0xc0, 0x00, 0x71, 0x2c, 0x00, 0x63, 0x6c, 0x69, 0x63,
810x6b, 0xd2, 0x01, 0x50, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0xa5,
820x01, 0x14, 0x61, 0xd8, 0x01, 0x81, 0x74, 0x6f, 0x00, 0x63,
830x68, 0x61, 0x6e, 0x67, 0xe2, 0x01, 0x03, 0x9c, 0x02, 0x71,
840x2e, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x37, 0x00, 0x00,
850x0c, 0x02, 0x84, 0x61, 0x6e, 0x00, 0x65, 0x6d, 0x70, 0x74,
860x79, 0x34, 0x00, 0xc2, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x74,
870x75, 0x72, 0x6e, 0x00, 0x69, 0x74, 0x6a, 0x02, 0x11, 0x2c,
880xd0, 0x01, 0x47, 0x72, 0x69, 0x67, 0x68, 0x37, 0x00, 0x09,
890x27, 0x00, 0x03, 0xdf, 0x02, 0x42, 0x4b, 0x65, 0x65, 0x70,
900x90, 0x00, 0x04, 0x65, 0x02, 0x10, 0x73, 0x4a, 0x02, 0x61,
910x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x8d, 0x00, 0xc1, 0x79,
920x63, 0x6c, 0x65, 0x00, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67,
930x68, 0x21, 0x00, 0xf2, 0x0a, 0x74, 0x68, 0x72, 0x65, 0x65,
940x00, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x00,
950x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x00, 0x66, 0x6f, 0x72,
960x3f, 0x00, 0x01, 0x93, 0x00, 0x00, 0x0e, 0x01, 0x21, 0x49,
970x66, 0x08, 0x03, 0x60, 0x68, 0x6f, 0x6c, 0x64, 0x00, 0x64,
980x8c, 0x02, 0x06, 0xf1, 0x00, 0x03, 0x5f, 0x00, 0x00, 0xa4,
990x00, 0x51, 0x64, 0x72, 0x61, 0x67, 0x2c, 0x29, 0x00, 0x34,
1000x63, 0x61, 0x6e, 0x8c, 0x03, 0xa1, 0x6d, 0x75, 0x6c, 0x74,
1010x69, 0x70, 0x6c, 0x65, 0x00, 0x63, 0x4f, 0x03, 0x02, 0x20,
1020x01, 0x71, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x00, 0x61, 0xe1,
1030x01, 0x13, 0x2e, 0xe8, 0x03, 0x00, 0x34, 0x00, 0x50, 0x61,
1040x6c, 0x73, 0x6f, 0x00, 0x56, 0x00, 0x01, 0x23, 0x03, 0xa0,
1050x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73,
1060xbe, 0x00, 0x61, 0x6d, 0x6f, 0x76, 0x65, 0x00, 0x61, 0x5a,
1070x03, 0x01, 0x1f, 0x00, 0x00, 0x0c, 0x04, 0x74, 0x2e, 0x00,
1080x50, 0x72, 0x65, 0x73, 0x73, 0xf0, 0x00, 0x21, 0x72, 0x65,
1090x13, 0x01, 0x82, 0x6f, 0x72, 0x00, 0x73, 0x70, 0x61, 0x63,
1100x65, 0x3b, 0x00, 0x06, 0x2b, 0x01, 0x0c, 0x6c, 0x01, 0x0a,
1110x20, 0x04, 0x50, 0x00, 0x72, 0x65, 0x73, 0x70, 0x74, 0x02,
1120x10, 0x76, 0x29, 0x02, 0x22, 0x28, 0x61, 0x67, 0x00, 0x15,
1130x6e, 0x36, 0x01, 0x13, 0x65, 0xd2, 0x00, 0x05, 0xf3, 0x03,
1140x01, 0x5e, 0x01, 0x6d, 0x77, 0x61, 0x79, 0x00, 0x61, 0x73,
1150x10, 0x01, 0x22, 0x73, 0x29, 0xb7, 0x01, 0x14, 0x70, 0x9f,
1160x00, 0x42, 0x42, 0x61, 0x63, 0x6b, 0x95, 0x00, 0x01, 0x90,
1170x00, 0x59, 0x72, 0x65, 0x73, 0x65, 0x74, 0x2a, 0x02, 0x01,
1180x9a, 0x00, 0x03, 0x7a, 0x02, 0x14, 0x32, 0x7a, 0x02, 0x92,
1190x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x7c,
1200x02, 0x56, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x01,
1210x1b, 0x05, 0x60, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0xbe,
1220x01, 0x41, 0x66, 0x72, 0x6f, 0x6d, 0x89, 0x00, 0xe1, 0x60,
1230x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
1240x00, 0x6f, 0x70, 0x03, 0x03, 0x22, 0x6f, 0x6e, 0x1a, 0x00,
1250xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e,
1260x75, 0x6b, 0x00, 0x90, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c,
1270x00, 0x48, 0x65, 0x5d, 0x02, 0x00, 0x1f, 0x05, 0x30, 0x69,
1280x7a, 0x65, 0xe2, 0x04, 0x01, 0x67, 0x05, 0x24, 0x69, 0x6e,
1290xe4, 0x04, 0x00, 0x2b, 0x00, 0x31, 0x41, 0x67, 0x67, 0xcb,
1300x00, 0x82, 0x76, 0x65, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72,
1310x5a, 0x03, 0x00, 0x43, 0x00, 0x22, 0x74, 0x68, 0xb3, 0x03,
1320x03, 0x6a, 0x00, 0x42, 0x73, 0x65, 0x74, 0x2c, 0xa0, 0x01,
1330x00, 0x1f, 0x01, 0x03, 0x2c, 0x00, 0x23, 0x6f, 0x72, 0x87,
1340x01, 0x91, 0x72, 0x79, 0x00, 0x68, 0x61, 0x72, 0x64, 0x65,
1350x72, 0xf3, 0x00, 0xf1, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x6e,
1360x61, 0x74, 0x65, 0x00, 0x75, 0x6e, 0x6e, 0x65, 0x63, 0x65,
1370x73, 0x73, 0x61, 0x72, 0x79, 0x1a, 0x05, 0x14, 0x73, 0xb1,
1380x00, 0x62, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x2e, 0x18, 0x05,
1390x52, 0x73, 0x6c, 0x6f, 0x77, 0x73, 0x7f, 0x02, 0x06, 0x83,
1400x00, 0xf1, 0x0a, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x69, 0x74,
1410x27, 0x73, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x72, 0x65, 0x63,
1420x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0xc6, 0x02,
1430x01, 0x3f, 0x00, 0x70, 0x73, 0x00, 0x6c, 0x61, 0x72, 0x67,
1440x65, 0xd4, 0x02, 0x10, 0x61, 0x30, 0x00, 0xb0, 0x61, 0x79,
1450x2c, 0x00, 0x33, 0x30, 0x2a, 0x33, 0x30, 0x2e, 0x00,
146};
147
148const unsigned short help_text_len = 1673;
149const unsigned short help_text_words = 285;
150const bool help_valid = true;
151const char quick_help_text[] = "Fill in the grid given clues about number of nearby black squares.";
diff --git a/apps/plugins/puzzles/help/net.c b/apps/plugins/puzzles/help/net.c
index b0224838cf..de528b53bc 100644
--- a/apps/plugins/puzzles/help/net.c
+++ b/apps/plugins/puzzles/help/net.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,268 +6,296 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 3, TEXT_UNDERLINE }, 9 { 4, TEXT_UNDERLINE },
10 { 120, TEXT_CENTER | C_RED }, 10 { 121, TEXT_CENTER | C_RED },
11 { 140, TEXT_UNDERLINE }, 11 { 141, TEXT_UNDERLINE },
12 { 147, TEXT_UNDERLINE }, 12 { 148, TEXT_UNDERLINE },
13 { 155, TEXT_UNDERLINE }, 13 { 156, TEXT_UNDERLINE },
14 { 163, TEXT_UNDERLINE }, 14 { 164, TEXT_UNDERLINE },
15 { 168, TEXT_UNDERLINE }, 15 { 169, TEXT_UNDERLINE },
16 { 221, TEXT_UNDERLINE }, 16 { 222, TEXT_UNDERLINE },
17 { 257, TEXT_UNDERLINE }, 17 { 258, TEXT_UNDERLINE },
18 { 313, TEXT_UNDERLINE }, 18 { 314, TEXT_UNDERLINE },
19 { 344, TEXT_CENTER | C_RED }, 19 { 345, TEXT_CENTER | C_RED },
20 { 361, TEXT_UNDERLINE },
21 { 362, TEXT_UNDERLINE }, 20 { 362, TEXT_UNDERLINE },
22 { 372, TEXT_UNDERLINE }, 21 { 363, TEXT_UNDERLINE },
23 { 398, TEXT_UNDERLINE }, 22 { 373, TEXT_UNDERLINE },
24 { 485, TEXT_UNDERLINE }, 23 { 399, TEXT_UNDERLINE },
25 { 553, TEXT_UNDERLINE }, 24 { 486, TEXT_UNDERLINE },
26 { 598, TEXT_UNDERLINE }, 25 { 554, TEXT_UNDERLINE },
26 { 599, TEXT_UNDERLINE },
27 { 614, TEXT_CENTER | C_RED },
27 LAST_STYLE_ITEM 28 LAST_STYLE_ITEM
28}; 29};
29 30
30/* orig 3439 comp 2361 ratio 0.686537 level 10 saved 1078 */ 31/* orig 3919 comp 2622 ratio 0.669048 level 10 saved 1297 */
31const char help_text[] = { 32const char help_text[] = {
320xf4, 0x57, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 330xf9, 0x02, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
330x33, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x20, 0x00, 0x00, 0x00, 340x33, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x20, 0x00, 0x2d, 0x01,
340x28, 0x4e, 0x6f, 0x74, 0x65, 0x3a, 0x00, 0x74, 0x68, 0x65, 350x00, 0xf4, 0x48, 0x00, 0x00, 0x00, 0x28, 0x4e, 0x6f, 0x74,
350x00, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x00, 0x76, 360x65, 0x3a, 0x00, 0x74, 0x68, 0x65, 0x00, 0x57, 0x69, 0x6e,
360x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x6f, 0x66, 0x00, 370x64, 0x6f, 0x77, 0x73, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69,
370x74, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 380x6f, 0x6e, 0x00, 0x6f, 0x66, 0x00, 0x74, 0x68, 0x69, 0x73,
380x69, 0x73, 0x00, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, 390x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x69, 0x73, 0x00, 0x63,
390x4e, 0x45, 0x54, 0x47, 0x41, 0x4d, 0x45, 0x2e, 0x45, 0x58, 400x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x4e, 0x45, 0x54, 0x47,
400x45, 0x00, 0x74, 0x6f, 0x00, 0x61, 0x76, 0x6f, 0x69, 0x64, 410x41, 0x4d, 0x45, 0x2e, 0x45, 0x58, 0x45, 0x00, 0x74, 0x6f,
410x00, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x00, 420x00, 0x61, 0x76, 0x6f, 0x69, 0x64, 0x00, 0x63, 0x6c, 0x61,
420x77, 0x69, 0x74, 0x68, 0x4a, 0x00, 0x60, 0x27, 0x73, 0x00, 430x73, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x77, 0x69, 0x74, 0x68,
430x6f, 0x77, 0x6e, 0x31, 0x00, 0x00, 0x2d, 0x00, 0xf2, 0x06, 440x4a, 0x00, 0x60, 0x27, 0x73, 0x00, 0x6f, 0x77, 0x6e, 0x31,
440x2e, 0x29, 0x00, 0x00, 0x00, 0x49, 0x00, 0x6f, 0x72, 0x69, 450x00, 0x00, 0x2d, 0x00, 0xf2, 0x06, 0x2e, 0x29, 0x00, 0x00,
450x67, 0x69, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x61, 460x00, 0x49, 0x00, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
460x77, 0x62, 0x00, 0x21, 0x69, 0x6e, 0x81, 0x00, 0x40, 0x66, 470x6c, 0x6c, 0x79, 0x00, 0x73, 0x61, 0x77, 0x62, 0x00, 0x21,
470x6f, 0x72, 0x6d, 0x76, 0x00, 0x30, 0x61, 0x00, 0x46, 0x4f, 480x69, 0x6e, 0x81, 0x00, 0x40, 0x66, 0x6f, 0x72, 0x6d, 0x76,
480x00, 0x02, 0x79, 0x00, 0x03, 0x76, 0x00, 0xf0, 0x18, 0x46, 490x00, 0x30, 0x61, 0x00, 0x46, 0x4f, 0x00, 0x02, 0x79, 0x00,
490x72, 0x65, 0x65, 0x4e, 0x65, 0x74, 0x00, 0x5b, 0x31, 0x5d, 500x03, 0x76, 0x00, 0xf0, 0x18, 0x46, 0x72, 0x65, 0x65, 0x4e,
500x2c, 0x00, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 510x65, 0x74, 0x00, 0x5b, 0x31, 0x5d, 0x2c, 0x00, 0x77, 0x72,
510x62, 0x79, 0x00, 0x50, 0x61, 0x76, 0x69, 0x6c, 0x73, 0x00, 520x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x50,
520x4a, 0x75, 0x72, 0x6a, 0x61, 0x6e, 0x73, 0x3b, 0x48, 0x00, 530x61, 0x76, 0x69, 0x6c, 0x73, 0x00, 0x4a, 0x75, 0x72, 0x6a,
530xf0, 0x01, 0x72, 0x65, 0x00, 0x61, 0x72, 0x65, 0x00, 0x73, 540x61, 0x6e, 0x73, 0x3b, 0x48, 0x00, 0xf0, 0x01, 0x72, 0x65,
540x65, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x00, 0x6f, 0x13, 0x00, 550x00, 0x61, 0x72, 0x65, 0x00, 0x73, 0x65, 0x76, 0x65, 0x72,
550xf1, 0x07, 0x00, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 560x61, 0x6c, 0x00, 0x6f, 0x13, 0x00, 0xf1, 0x07, 0x00, 0x69,
560x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x75, 570x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74,
570x6e, 0x64, 0x65, 0x72, 0x76, 0x00, 0x10, 0x6e, 0x66, 0x00, 580x69, 0x6f, 0x6e, 0x73, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72,
580xf0, 0x19, 0x4e, 0x65, 0x74, 0x57, 0x61, 0x6c, 0x6b, 0x2e, 590x76, 0x00, 0x10, 0x6e, 0x66, 0x00, 0xf0, 0x19, 0x4e, 0x65,
590x00, 0x54, 0x68, 0x65, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x75, 600x74, 0x57, 0x61, 0x6c, 0x6b, 0x2e, 0x00, 0x54, 0x68, 0x65,
600x74, 0x65, 0x72, 0x00, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 610x00, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x00,
610x65, 0x73, 0x00, 0x61, 0x00, 0x6e, 0x65, 0x74, 0x77, 0x6f, 620x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x73, 0x00, 0x61,
620x72, 0x6b, 0x73, 0x00, 0x70, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 630x00, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x00,
630x63, 0x74, 0xee, 0x00, 0x21, 0x75, 0x70, 0x43, 0x00, 0x40, 640x70, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xee, 0x00,
640x63, 0x65, 0x6e, 0x74, 0x27, 0x00, 0x61, 0x6f, 0x66, 0x00, 650x21, 0x75, 0x70, 0x43, 0x00, 0x40, 0x63, 0x65, 0x6e, 0x74,
650x73, 0x71, 0x75, 0x32, 0x00, 0xe0, 0x69, 0x6e, 0x00, 0x61, 660x27, 0x00, 0x61, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x32,
660x00, 0x67, 0x72, 0x69, 0x64, 0x2c, 0x00, 0x61, 0x6e, 0x64, 670x00, 0xe0, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69,
670x26, 0x00, 0xa2, 0x6e, 0x00, 0x73, 0x68, 0x75, 0x66, 0x66, 680x64, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x26, 0x00, 0xa2, 0x6e,
680x6c, 0x65, 0x73, 0x77, 0x00, 0x06, 0x51, 0x00, 0x41, 0x72, 690x00, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x77,
690x6f, 0x74, 0x61, 0x4f, 0x00, 0x00, 0xb2, 0x00, 0xf0, 0x0a, 700x00, 0x06, 0x51, 0x00, 0x41, 0x72, 0x6f, 0x74, 0x61, 0x4f,
700x79, 0x00, 0x74, 0x69, 0x6c, 0x65, 0x00, 0x72, 0x61, 0x6e, 710x00, 0x00, 0xb2, 0x00, 0xf0, 0x0a, 0x79, 0x00, 0x74, 0x69,
710x64, 0x6f, 0x6d, 0x6c, 0x79, 0x2e, 0x00, 0x59, 0x6f, 0x75, 720x6c, 0x65, 0x00, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c,
720x72, 0x00, 0x6a, 0x6f, 0x62, 0x83, 0x01, 0x22, 0x74, 0x6f, 730x79, 0x2e, 0x00, 0x59, 0x6f, 0x75, 0x72, 0x00, 0x6a, 0x6f,
730x2d, 0x00, 0xf2, 0x09, 0x65, 0x00, 0x69, 0x74, 0x00, 0x61, 740x62, 0x83, 0x01, 0x22, 0x74, 0x6f, 0x2d, 0x00, 0xf2, 0x09,
740x6c, 0x6c, 0x00, 0x62, 0x61, 0x63, 0x6b, 0x00, 0x69, 0x6e, 750x65, 0x00, 0x69, 0x74, 0x00, 0x61, 0x6c, 0x6c, 0x00, 0x62,
750x74, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0xc0, 0x00, 760x61, 0x63, 0x6b, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 0x70,
760xf0, 0x01, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 770x6c, 0x61, 0x63, 0x65, 0xc0, 0x00, 0xf0, 0x01, 0x73, 0x75,
770x75, 0x6c, 0x00, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0xcd, 0x01, 780x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x00, 0x73,
780x20, 0x77, 0x69, 0x2e, 0x00, 0xc5, 0x65, 0x00, 0x61, 0x6e, 790x6f, 0x6c, 0x75, 0x74, 0xcd, 0x01, 0x20, 0x77, 0x69, 0x2e,
790x00, 0x65, 0x6e, 0x74, 0x69, 0x72, 0x65, 0x6c, 0xc9, 0x00, 800x00, 0xc5, 0x65, 0x00, 0x61, 0x6e, 0x00, 0x65, 0x6e, 0x74,
800x24, 0x65, 0x64, 0x8d, 0x00, 0x12, 0x2c, 0xbf, 0x01, 0xf0, 810x69, 0x72, 0x65, 0x6c, 0xc9, 0x00, 0x24, 0x65, 0x64, 0x8d,
810x03, 0x6e, 0x6f, 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 820x00, 0x12, 0x2c, 0xbf, 0x01, 0xf0, 0x03, 0x6e, 0x6f, 0x00,
820x00, 0x6c, 0x6f, 0x6f, 0x70, 0x73, 0x2e, 0x00, 0x41, 0x02, 830x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x00, 0x6c, 0x6f, 0x6f,
830x01, 0x81, 0x76, 0x69, 0x73, 0x75, 0x61, 0x6c, 0x00, 0x61, 840x70, 0x73, 0x2e, 0x00, 0x41, 0x02, 0x01, 0x81, 0x76, 0x69,
840xd3, 0x00, 0x21, 0x6c, 0x6c, 0xa7, 0x00, 0x71, 0x73, 0x00, 850x73, 0x75, 0x61, 0x6c, 0x00, 0x61, 0xd3, 0x00, 0x21, 0x6c,
850x77, 0x68, 0x69, 0x63, 0x68, 0x70, 0x01, 0x06, 0x4e, 0x00, 860x6c, 0xa7, 0x00, 0x71, 0x73, 0x00, 0x77, 0x68, 0x69, 0x63,
860x21, 0x74, 0x6f, 0xe2, 0x00, 0x34, 0x6f, 0x6e, 0x65, 0xda, 870x68, 0x70, 0x01, 0x06, 0x4e, 0x00, 0x21, 0x74, 0x6f, 0xe2,
870x01, 0x52, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x97, 0x01, 0xe0, 880x00, 0x34, 0x6f, 0x6e, 0x65, 0xda, 0x01, 0x52, 0x6d, 0x69,
880x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 890x64, 0x64, 0x6c, 0x97, 0x01, 0xe0, 0x68, 0x69, 0x67, 0x68,
890x64, 0x2e, 0x00, 0x00, 0xd0, 0x01, 0xd2, 0x00, 0x68, 0x74, 900x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x00,
900x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6a, 910xd0, 0x01, 0xd2, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
910xc8, 0x01, 0xe3, 0x2e, 0x6c, 0x76, 0x2f, 0x73, 0x74, 0x75, 920x2f, 0x77, 0x77, 0x77, 0x2e, 0x6a, 0xc8, 0x01, 0xe3, 0x2e,
920x66, 0x66, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0xfc, 0x01, 0xa1, 930x6c, 0x76, 0x2f, 0x73, 0x74, 0x75, 0x66, 0x66, 0x2f, 0x6e,
930x2e, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x33, 0x2e, 0x31, 940x65, 0x74, 0x2f, 0xfc, 0x01, 0xa1, 0x2e, 0x68, 0x74, 0x6d,
940xbd, 0x02, 0x80, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 950x00, 0x00, 0x00, 0x33, 0x2e, 0x31, 0xcc, 0x02, 0xd5, 0x63,
950x73, 0xc6, 0x02, 0x15, 0x54, 0xa8, 0x02, 0x30, 0x63, 0x61, 960x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00,
960x6e, 0xe7, 0x00, 0x62, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 970x00, 0x54, 0xa8, 0x02, 0x30, 0x63, 0x61, 0x6e, 0xe7, 0x00,
970xcf, 0x00, 0x43, 0x65, 0x69, 0x74, 0x68, 0xec, 0x01, 0xb2, 980x62, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 0xcf, 0x00, 0x43,
980x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x6f, 990x65, 0x69, 0x74, 0x68, 0xec, 0x01, 0xb2, 0x6b, 0x65, 0x79,
990x72, 0x98, 0x00, 0x33, 0x6f, 0x75, 0x73, 0x35, 0x01, 0x04, 1000x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x6f, 0x72, 0x98, 0x00,
1000x4f, 0x00, 0x00, 0xa5, 0x00, 0xb0, 0x3a, 0x00, 0x00, 0x00, 1010x33, 0x6f, 0x75, 0x73, 0x35, 0x01, 0x04, 0x4f, 0x00, 0x00,
1010x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0xe6, 0x00, 0x12, 1020xa5, 0x00, 0xb0, 0x3a, 0x00, 0x00, 0x00, 0x53, 0x65, 0x6c,
1020x3a, 0x28, 0x00, 0xf0, 0x00, 0x00, 0x70, 0x6f, 0x69, 0x6e, 1030x65, 0x63, 0x74, 0x20, 0xe6, 0x00, 0x12, 0x3a, 0x28, 0x00,
1030x74, 0x65, 0x72, 0x2c, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77, 1040xf0, 0x00, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72,
1040x4d, 0x00, 0x51, 0x73, 0x00, 0x00, 0x00, 0x52, 0x91, 0x01, 1050x2c, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x4d, 0x00, 0x51,
1050x01, 0x29, 0x00, 0xf3, 0x05, 0x20, 0x61, 0x6e, 0x74, 0x69, 1060x73, 0x00, 0x00, 0x00, 0x52, 0x91, 0x01, 0x01, 0x29, 0x00,
1060x63, 0x6c, 0x6f, 0x63, 0x6b, 0x77, 0x69, 0x73, 0x65, 0x3a, 1070xf3, 0x05, 0x20, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x6c, 0x6f,
1070x00, 0x6c, 0x65, 0x66, 0x74, 0x3c, 0x00, 0xb0, 0x62, 0x75, 1080x63, 0x6b, 0x77, 0x69, 0x73, 0x65, 0x3a, 0x00, 0x6c, 0x65,
1080x74, 0x74, 0x6f, 0x6e, 0x2c, 0x00, 0x60, 0x41, 0x27, 0x39, 1090x66, 0x74, 0x3c, 0x00, 0xb0, 0x62, 0x75, 0x74, 0x74, 0x6f,
1090x00, 0x0b, 0x38, 0x00, 0x07, 0x34, 0x00, 0x10, 0x72, 0x1c, 1100x6e, 0x2c, 0x00, 0x60, 0x41, 0x27, 0x39, 0x00, 0x0b, 0x38,
1100x01, 0x0c, 0x35, 0x00, 0x1f, 0x44, 0x35, 0x00, 0x01, 0xf4, 1110x00, 0x07, 0x34, 0x00, 0x10, 0x72, 0x1c, 0x01, 0x0c, 0x35,
1110x03, 0x62, 0x79, 0x20, 0x31, 0x38, 0x30, 0x20, 0x64, 0x65, 1120x00, 0x1f, 0x44, 0x35, 0x00, 0x01, 0xf4, 0x03, 0x62, 0x79,
1120x67, 0x72, 0x65, 0x65, 0x73, 0x3a, 0x00, 0x60, 0x46, 0x26, 1130x20, 0x31, 0x38, 0x30, 0x20, 0x64, 0x65, 0x67, 0x72, 0x65,
1130x00, 0xb0, 0x4c, 0x6f, 0x63, 0x6b, 0x20, 0x28, 0x6f, 0x72, 1140x65, 0x73, 0x3a, 0x00, 0x60, 0x46, 0x26, 0x00, 0xb0, 0x4c,
1140x20, 0x75, 0x6e, 0x59, 0x00, 0x14, 0x29, 0xc6, 0x00, 0x02, 1150x6f, 0x63, 0x6b, 0x20, 0x28, 0x6f, 0x72, 0x20, 0x75, 0x6e,
1150x86, 0x01, 0x0a, 0x5c, 0x00, 0xf4, 0x00, 0x73, 0x68, 0x69, 1160x59, 0x00, 0x14, 0x29, 0xc6, 0x00, 0x02, 0x86, 0x01, 0x0a,
1160x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x2c, 0x00, 1170x5c, 0x00, 0xf4, 0x00, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2d,
1170x60, 0x53, 0x43, 0x00, 0x31, 0x59, 0x6f, 0x75, 0x4c, 0x01, 1180x63, 0x6c, 0x69, 0x63, 0x6b, 0x2c, 0x00, 0x60, 0x53, 0x43,
1180x00, 0x40, 0x00, 0x22, 0x00, 0x61, 0x94, 0x02, 0xa0, 0x6f, 1190x00, 0x31, 0x59, 0x6f, 0x75, 0x4c, 0x01, 0x00, 0x40, 0x00,
1190x6e, 0x63, 0x65, 0x00, 0x79, 0x6f, 0x75, 0x27, 0x72, 0x64, 1200x22, 0x00, 0x61, 0x94, 0x02, 0xa0, 0x6f, 0x6e, 0x63, 0x65,
1200x02, 0x20, 0x72, 0x65, 0xf0, 0x02, 0x30, 0x69, 0x74, 0x73, 1210x00, 0x79, 0x6f, 0x75, 0x27, 0x72, 0x64, 0x02, 0x20, 0x72,
1210xd3, 0x03, 0x04, 0x59, 0x03, 0x15, 0x2e, 0x39, 0x00, 0x52, 1220x65, 0xf0, 0x02, 0x30, 0x69, 0x74, 0x73, 0xd3, 0x03, 0x04,
1220x61, 0x6c, 0x73, 0x6f, 0x00, 0x80, 0x00, 0x01, 0xad, 0x02, 1230x59, 0x03, 0x15, 0x2e, 0x39, 0x00, 0x52, 0x61, 0x6c, 0x73,
1230x50, 0x67, 0x61, 0x69, 0x6e, 0x2c, 0x76, 0x00, 0x00, 0x33, 1240x6f, 0x00, 0x80, 0x00, 0x01, 0xad, 0x02, 0x50, 0x67, 0x61,
1240x02, 0x10, 0x6c, 0xc1, 0x02, 0x21, 0x27, 0x73, 0x5e, 0x00, 1250x69, 0x6e, 0x2c, 0x76, 0x00, 0x00, 0x33, 0x02, 0x10, 0x6c,
1250x42, 0x65, 0x64, 0x00, 0x79, 0x34, 0x00, 0x80, 0x27, 0x74, 1260xc1, 0x02, 0x21, 0x27, 0x73, 0x5e, 0x00, 0x42, 0x65, 0x64,
1260x00, 0x61, 0x63, 0x63, 0x69, 0x64, 0x4d, 0x00, 0x00, 0x20, 1270x00, 0x79, 0x34, 0x00, 0x80, 0x27, 0x74, 0x00, 0x61, 0x63,
1270x04, 0x81, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x69, 0x74, 0x2e, 1280x63, 0x69, 0x64, 0x4d, 0x00, 0x00, 0x20, 0x04, 0x81, 0x74,
1280xe1, 0x01, 0x00, 0x1f, 0x04, 0x40, 0x6c, 0x6c, 0x6f, 0x77, 1290x75, 0x72, 0x6e, 0x00, 0x69, 0x74, 0x2e, 0xe1, 0x01, 0x00,
1290x28, 0x03, 0x08, 0xac, 0x01, 0x70, 0x00, 0x6e, 0x6f, 0x74, 1300x1f, 0x04, 0x40, 0x6c, 0x6c, 0x6f, 0x77, 0x28, 0x03, 0x08,
1300x00, 0x6e, 0x65, 0xf1, 0x02, 0x10, 0x61, 0x3d, 0x03, 0x11, 1310xac, 0x01, 0x70, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x6e, 0x65,
1310x6f, 0xbf, 0x03, 0x41, 0x6c, 0x65, 0x74, 0x65, 0xe2, 0x01, 1320xf1, 0x02, 0x10, 0x61, 0x3d, 0x03, 0x11, 0x6f, 0xbf, 0x03,
1320x00, 0x15, 0x02, 0x02, 0x77, 0x00, 0x30, 0x6d, 0x61, 0x79, 1330x41, 0x6c, 0x65, 0x74, 0x65, 0xe2, 0x01, 0x00, 0x15, 0x02,
1330x1a, 0x02, 0x61, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0xe2, 1340x02, 0x77, 0x00, 0x30, 0x6d, 0x61, 0x79, 0x1a, 0x02, 0x61,
1340x01, 0x00, 0xfa, 0x00, 0x10, 0x20, 0xa4, 0x03, 0x12, 0x3a, 1350x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0xe2, 0x01, 0x00, 0xfa,
1350x0c, 0x00, 0x2a, 0x00, 0x2b, 0xda, 0x01, 0x21, 0x4f, 0x6e, 1360x00, 0x10, 0x20, 0xa4, 0x03, 0x12, 0x3a, 0x0c, 0x00, 0x2a,
1360xc2, 0x03, 0x00, 0xb0, 0x03, 0x84, 0x61, 0x74, 0x00, 0x77, 1370x00, 0x2b, 0xda, 0x01, 0x21, 0x4f, 0x6e, 0xc2, 0x03, 0x00,
1370x72, 0x61, 0x70, 0x2c, 0xab, 0x00, 0x42, 0x00, 0x6d, 0x6f, 1380xb0, 0x03, 0x84, 0x61, 0x74, 0x00, 0x77, 0x72, 0x61, 0x70,
1380x76, 0x61, 0x00, 0x02, 0xd0, 0x04, 0x02, 0x2d, 0x05, 0x13, 1390x2c, 0xab, 0x00, 0x42, 0x00, 0x6d, 0x6f, 0x76, 0x61, 0x00,
1390x65, 0xf2, 0x03, 0x22, 0x73, 0x6f, 0x33, 0x00, 0x02, 0x23, 1400x02, 0xd0, 0x04, 0x02, 0x2d, 0x05, 0x13, 0x65, 0xf2, 0x03,
1400x03, 0x02, 0x3e, 0x00, 0x00, 0x97, 0x04, 0x00, 0x55, 0x05, 1410x22, 0x73, 0x6f, 0x33, 0x00, 0x02, 0x23, 0x03, 0x02, 0x3e,
1410xd8, 0x70, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x00, 0x73, 1420x00, 0x00, 0x97, 0x04, 0x00, 0x55, 0x05, 0xd8, 0x70, 0x70,
1420x69, 0x64, 0x65, 0x73, 0x37, 0x00, 0x04, 0xbb, 0x02, 0x90, 1430x6f, 0x73, 0x69, 0x74, 0x65, 0x00, 0x73, 0x69, 0x64, 0x65,
1430x73, 0x65, 0x65, 0x6e, 0x00, 0x74, 0x6f, 0x67, 0x65, 0xb6, 1440x73, 0x37, 0x00, 0x04, 0xbb, 0x02, 0x90, 0x73, 0x65, 0x65,
1440x02, 0x00, 0xfc, 0x00, 0x52, 0x4d, 0x6f, 0x76, 0x65, 0x20, 1450x6e, 0x00, 0x74, 0x6f, 0x67, 0x65, 0xb6, 0x02, 0x00, 0xfc,
1450x63, 0x04, 0x6c, 0x3a, 0x00, 0x43, 0x74, 0x72, 0x6c, 0xa8, 1460x00, 0x52, 0x4d, 0x6f, 0x76, 0x65, 0x20, 0x63, 0x04, 0x6c,
1460x00, 0x04, 0x73, 0x01, 0x63, 0x63, 0x68, 0x61, 0x6e, 0x67, 1470x3a, 0x00, 0x43, 0x74, 0x72, 0x6c, 0xa8, 0x00, 0x04, 0x73,
1470x65, 0x93, 0x03, 0x11, 0x74, 0x65, 0x01, 0x30, 0x73, 0x00, 1480x01, 0x63, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x93, 0x03,
1480x75, 0xc8, 0x03, 0x12, 0x61, 0x74, 0x04, 0x51, 0x73, 0x6f, 1490x11, 0x74, 0x65, 0x01, 0x30, 0x73, 0x00, 0x75, 0xc8, 0x03,
1490x75, 0x72, 0x63, 0xb7, 0x01, 0x05, 0x86, 0x03, 0xd0, 0x69, 1500x12, 0x61, 0x74, 0x04, 0x51, 0x73, 0x6f, 0x75, 0x72, 0x63,
1500x6e, 0x67, 0x2e, 0x00, 0x28, 0x49, 0x74, 0x00, 0x64, 0x6f, 1510xb7, 0x01, 0x05, 0x86, 0x03, 0xd0, 0x69, 0x6e, 0x67, 0x2e,
1510x65, 0x73, 0x82, 0x01, 0x70, 0x75, 0x6c, 0x74, 0x69, 0x6d, 1520x00, 0x28, 0x49, 0x74, 0x00, 0x64, 0x6f, 0x65, 0x73, 0x82,
1520x61, 0x74, 0x20, 0x04, 0x30, 0x6d, 0x61, 0x74, 0x06, 0x05, 1530x01, 0x70, 0x75, 0x6c, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x20,
1530x07, 0x50, 0x00, 0x02, 0xae, 0x05, 0x20, 0x73, 0x2c, 0x51, 1540x04, 0x30, 0x6d, 0x61, 0x74, 0x06, 0x05, 0x07, 0x50, 0x00,
1540x00, 0x07, 0xad, 0x04, 0x04, 0x5d, 0x04, 0x09, 0x03, 0x04, 1550x02, 0xae, 0x05, 0x20, 0x73, 0x2c, 0x51, 0x00, 0x07, 0xad,
1550x02, 0x20, 0x00, 0x02, 0x7e, 0x05, 0x02, 0x8d, 0x00, 0x00, 1560x04, 0x04, 0x5d, 0x04, 0x09, 0x03, 0x04, 0x02, 0x20, 0x00,
1560x13, 0x05, 0x75, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 1570x02, 0x7e, 0x05, 0x02, 0x8d, 0x00, 0x00, 0x13, 0x05, 0x75,
1570x99, 0x04, 0x02, 0x9c, 0x01, 0x15, 0x69, 0x9f, 0x01, 0x40, 1580x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x99, 0x04, 0x02,
1580x68, 0x65, 0x6c, 0x70, 0xb9, 0x04, 0x03, 0x39, 0x04, 0x01, 1590x9c, 0x01, 0x15, 0x69, 0x9f, 0x01, 0x40, 0x68, 0x65, 0x6c,
1590x71, 0x03, 0x50, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x33, 0x01, 1600x70, 0xb9, 0x04, 0x03, 0x39, 0x04, 0x01, 0x71, 0x03, 0x50,
1600x33, 0x74, 0x61, 0x67, 0x66, 0x05, 0x30, 0x6f, 0x6c, 0x76, 1610x6d, 0x65, 0x64, 0x69, 0x61, 0x33, 0x01, 0x33, 0x74, 0x61,
1610x09, 0x02, 0x00, 0x23, 0x00, 0x61, 0x70, 0x75, 0x7a, 0x7a, 1620x67, 0x66, 0x05, 0x30, 0x6f, 0x6c, 0x76, 0x09, 0x02, 0x00,
1620x6c, 0x65, 0x5e, 0x06, 0x52, 0x4a, 0x75, 0x6d, 0x62, 0x6c, 1630x23, 0x00, 0x61, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x5e,
1630x1e, 0x03, 0x00, 0x10, 0x03, 0x14, 0x4a, 0xcd, 0x02, 0x01, 1640x06, 0x52, 0x4a, 0x75, 0x6d, 0x62, 0x6c, 0x1e, 0x03, 0x00,
1640x1f, 0x04, 0x22, 0x6b, 0x65, 0x52, 0x02, 0x17, 0x73, 0xbe, 1650x10, 0x03, 0x14, 0x4a, 0xcd, 0x02, 0x01, 0x1f, 0x04, 0x22,
1650x04, 0x01, 0x9b, 0x01, 0x04, 0x45, 0x02, 0x03, 0x8d, 0x02, 1660x6b, 0x65, 0x52, 0x02, 0x17, 0x73, 0xbe, 0x04, 0x01, 0x9b,
1660x23, 0x74, 0x6f, 0x7d, 0x05, 0x08, 0xd8, 0x02, 0x20, 0x73, 1670x01, 0x04, 0x45, 0x02, 0x03, 0x8d, 0x02, 0x23, 0x74, 0x6f,
1670x2e, 0x2c, 0x07, 0x31, 0x41, 0x6c, 0x6c, 0x73, 0x00, 0x22, 1680x7d, 0x05, 0x08, 0xd8, 0x02, 0x20, 0x73, 0x2e, 0x2c, 0x07,
1680x61, 0x63, 0x45, 0x06, 0x90, 0x64, 0x65, 0x73, 0x63, 0x72, 1690x31, 0x41, 0x6c, 0x6c, 0x73, 0x00, 0x22, 0x61, 0x63, 0x45,
1690x69, 0x62, 0x65, 0x64, 0xaf, 0x00, 0x31, 0x73, 0x65, 0x63, 1700x06, 0x90, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
1700x6a, 0x05, 0x31, 0x32, 0x2e, 0x31, 0x53, 0x00, 0x01, 0x01, 1710x64, 0xaf, 0x00, 0x31, 0x73, 0x65, 0x63, 0x6a, 0x05, 0x31,
1710x03, 0x73, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0xa0, 1720x32, 0x2e, 0x31, 0x53, 0x00, 0x01, 0x01, 0x03, 0x73, 0x61,
1720x00, 0x31, 0x33, 0x2e, 0x32, 0xbb, 0x04, 0x93, 0x70, 0x61, 1730x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0xa0, 0x00, 0x31, 0x33,
1730x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0xbd, 0x04, 0x10, 1740x2e, 0x32, 0xbb, 0x04, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d,
1740x65, 0x5d, 0x04, 0x05, 0x14, 0x00, 0x02, 0x3e, 0x00, 0x04, 1750x65, 0x74, 0x65, 0x72, 0xbd, 0x04, 0x10, 0x65, 0x5d, 0x04,
1750x39, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x76, 0x00, 1760x05, 0x14, 0x00, 0x02, 0x3e, 0x00, 0x04, 0x39, 0x00, 0x51,
1760xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 1770x00, 0x66, 0x72, 0x6f, 0x6d, 0x76, 0x00, 0xe1, 0x60, 0x43,
1770x2e, 0x27, 0x00, 0x6f, 0x70, 0x6c, 0x00, 0x22, 0x6f, 0x6e, 1780x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00,
1780x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 1790x6f, 0x70, 0x6c, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0,
1790x65, 0x6e, 0x75, 0xa8, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 1800x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75,
1800x68, 0x2c, 0x00, 0x48, 0x65, 0x4b, 0x04, 0x51, 0x00, 0x00, 1810xa8, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00,
1810x53, 0x69, 0x7a, 0xf5, 0x01, 0x01, 0x66, 0x02, 0x22, 0x69, 1820x48, 0x65, 0x4b, 0x04, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a,
1820x6e, 0xfc, 0x00, 0x01, 0x29, 0x00, 0x50, 0x61, 0x6c, 0x6c, 1830xf5, 0x01, 0x01, 0x66, 0x02, 0x22, 0x69, 0x6e, 0xfc, 0x00,
1830x73, 0x20, 0xd9, 0x02, 0x70, 0x20, 0x61, 0x72, 0x6f, 0x75, 1840x01, 0x29, 0x00, 0x50, 0x61, 0x6c, 0x6c, 0x73, 0x20, 0xd9,
1840x6e, 0x64, 0xa2, 0x07, 0x50, 0x66, 0x00, 0x63, 0x68, 0x65, 1850x02, 0x70, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0xa2,
1850x0a, 0x01, 0x61, 0x2c, 0x00, 0x66, 0x6c, 0x6f, 0x77, 0x5a, 1860x07, 0x50, 0x66, 0x00, 0x63, 0x68, 0x65, 0x0a, 0x01, 0x61,
1860x02, 0x46, 0x70, 0x61, 0x73, 0x73, 0x89, 0x00, 0x01, 0xd7, 1870x2c, 0x00, 0x66, 0x6c, 0x6f, 0x77, 0x5a, 0x02, 0x46, 0x70,
1870x04, 0x44, 0x65, 0x64, 0x67, 0x65, 0xea, 0x05, 0x02, 0xb4, 1880x61, 0x73, 0x73, 0x89, 0x00, 0x01, 0xd7, 0x04, 0x44, 0x65,
1880x04, 0x00, 0x12, 0x00, 0x02, 0xee, 0x06, 0x02, 0x2a, 0x00, 1890x64, 0x67, 0x65, 0xea, 0x05, 0x02, 0xb4, 0x04, 0x00, 0x12,
1890x20, 0x6f, 0x70, 0x20, 0x00, 0x62, 0x62, 0x6f, 0x74, 0x74, 1900x00, 0x02, 0xee, 0x06, 0x02, 0x2a, 0x00, 0x20, 0x6f, 0x70,
1900x6f, 0x6d, 0x18, 0x00, 0x41, 0x76, 0x69, 0x63, 0x65, 0x5f, 1910x20, 0x00, 0x62, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x18,
1910x08, 0x10, 0x61, 0x7e, 0x00, 0xf0, 0x03, 0x42, 0x61, 0x72, 1920x00, 0x41, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x08, 0x10, 0x61,
1920x72, 0x69, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x61, 1930x7e, 0x00, 0xf0, 0x03, 0x42, 0x61, 0x72, 0x72, 0x69, 0x65,
1930x62, 0x69, 0x6c, 0x69, 0x74, 0xac, 0x01, 0xd0, 0x41, 0x00, 1940x72, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c,
1940x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x62, 0x65, 0x74, 1950x69, 0x74, 0xac, 0x01, 0xd0, 0x41, 0x00, 0x6e, 0x75, 0x6d,
1950x77, 0x0b, 0x03, 0x31, 0x30, 0x2e, 0x30, 0x3d, 0x00, 0x34, 1960x62, 0x65, 0x72, 0x00, 0x62, 0x65, 0x74, 0x77, 0x0b, 0x03,
1960x31, 0x2e, 0x30, 0xf9, 0x03, 0x11, 0x6c, 0x6a, 0x08, 0x11, 1970x31, 0x30, 0x2e, 0x30, 0x3d, 0x00, 0x34, 0x31, 0x2e, 0x30,
1970x68, 0x22, 0x03, 0x00, 0xd2, 0x06, 0x51, 0x69, 0x6d, 0x6d, 1980xf9, 0x03, 0x11, 0x6c, 0x6a, 0x08, 0x11, 0x68, 0x22, 0x03,
1980x6f, 0x76, 0x2d, 0x01, 0x12, 0x62, 0x54, 0x00, 0x00, 0xf9, 1990x00, 0xd2, 0x06, 0x51, 0x69, 0x6d, 0x6d, 0x6f, 0x76, 0x2d,
1990x02, 0x01, 0x11, 0x07, 0x15, 0x64, 0x47, 0x00, 0x34, 0x74, 2000x01, 0x12, 0x62, 0x54, 0x00, 0x00, 0xf9, 0x02, 0x01, 0x11,
2000x77, 0x6f, 0xf5, 0x01, 0x10, 0x6f, 0xd9, 0x07, 0x42, 0x76, 2010x07, 0x15, 0x64, 0x47, 0x00, 0x34, 0x74, 0x77, 0x6f, 0xf5,
2010x65, 0x6e, 0x74, 0xe1, 0x00, 0x05, 0x22, 0x00, 0x61, 0x68, 2020x01, 0x10, 0x6f, 0xd9, 0x07, 0x42, 0x76, 0x65, 0x6e, 0x74,
2020x65, 0x6d, 0x00, 0x28, 0x61, 0x1c, 0x03, 0x24, 0x65, 0x72, 2030xe1, 0x00, 0x05, 0x22, 0x00, 0x61, 0x68, 0x65, 0x6d, 0x00,
2030x87, 0x00, 0x95, 0x67, 0x69, 0x76, 0x65, 0x73, 0x00, 0x6d, 2040x28, 0x61, 0x1c, 0x03, 0x24, 0x65, 0x72, 0x87, 0x00, 0x95,
2040x6f, 0x72, 0x5d, 0x00, 0x86, 0x73, 0x29, 0x2e, 0x00, 0x53, 2050x67, 0x69, 0x76, 0x65, 0x73, 0x00, 0x6d, 0x6f, 0x72, 0x5d,
2050x69, 0x6e, 0x63, 0x11, 0x00, 0x01, 0xb2, 0x01, 0x05, 0x85, 2060x00, 0x86, 0x73, 0x29, 0x2e, 0x00, 0x53, 0x69, 0x6e, 0x63,
2060x00, 0x10, 0x2c, 0x4a, 0x00, 0x10, 0x79, 0x25, 0x02, 0x00, 2070x11, 0x00, 0x01, 0xb2, 0x01, 0x05, 0x85, 0x00, 0x10, 0x2c,
2070x27, 0x03, 0xb4, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 2080x4a, 0x00, 0x10, 0x79, 0x25, 0x02, 0x00, 0x27, 0x03, 0xb4,
2080x69, 0x6e, 0x74, 0x73, 0xaf, 0x01, 0x05, 0x95, 0x07, 0x80, 2090x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74,
2090x28, 0x69, 0x2e, 0x65, 0x2e, 0x2c, 0x00, 0x68, 0x1d, 0x00, 2100x73, 0xaf, 0x01, 0x05, 0x95, 0x07, 0x80, 0x28, 0x69, 0x2e,
2100x14, 0x29, 0xe7, 0x04, 0x01, 0xa6, 0x01, 0x51, 0x67, 0x65, 2110x65, 0x2e, 0x2c, 0x00, 0x68, 0x1d, 0x00, 0x14, 0x29, 0xe7,
2110x6e, 0x65, 0x72, 0x7a, 0x02, 0x00, 0xb1, 0x01, 0x00, 0xf9, 2120x04, 0x01, 0xa6, 0x01, 0x51, 0x67, 0x65, 0x6e, 0x65, 0x72,
2120x08, 0x50, 0x68, 0x61, 0x73, 0x00, 0x62, 0xa7, 0x00, 0x30, 2130x7a, 0x02, 0x00, 0xb1, 0x01, 0x00, 0xf9, 0x08, 0x50, 0x68,
2130x63, 0x61, 0x72, 0xc2, 0x04, 0x20, 0x6c, 0x79, 0x05, 0x04, 2140x61, 0x73, 0x00, 0x62, 0xa7, 0x00, 0x30, 0x63, 0x61, 0x72,
2140x00, 0xf1, 0x03, 0x16, 0x64, 0x70, 0x04, 0x1c, 0x68, 0x94, 2150xc2, 0x04, 0x20, 0x6c, 0x79, 0x05, 0x04, 0x00, 0xf1, 0x03,
2150x00, 0x60, 0x6e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x52, 0x05, 2160x16, 0x64, 0x70, 0x04, 0x1c, 0x68, 0x94, 0x00, 0x60, 0x6e,
2160x04, 0x6b, 0x04, 0x48, 0x72, 0x65, 0x73, 0x74, 0x77, 0x04, 2170x64, 0x65, 0x70, 0x65, 0x6e, 0x52, 0x05, 0x04, 0x6b, 0x04,
2170x12, 0x2e, 0x1e, 0x03, 0x43, 0x6d, 0x65, 0x61, 0x6e, 0x10, 2180x48, 0x72, 0x65, 0x73, 0x74, 0x77, 0x04, 0x12, 0x2e, 0x1e,
2180x03, 0x21, 0x69, 0x66, 0xe6, 0x04, 0x82, 0x6e, 0x6f, 0x74, 2190x03, 0x43, 0x6d, 0x65, 0x61, 0x6e, 0x10, 0x03, 0x21, 0x69,
2190x65, 0x00, 0x64, 0x6f, 0x77, 0xb4, 0x00, 0x03, 0x13, 0x03, 2200x66, 0xe6, 0x04, 0x82, 0x6e, 0x6f, 0x74, 0x65, 0x00, 0x64,
2200x42, 0x73, 0x65, 0x65, 0x64, 0x53, 0x04, 0x24, 0x74, 0x6f, 2210x6f, 0x77, 0xb4, 0x00, 0x03, 0x13, 0x03, 0x42, 0x73, 0x65,
2210xa5, 0x00, 0x12, 0x65, 0x04, 0x09, 0x30, 0x75, 0x72, 0x72, 2220x65, 0x64, 0x53, 0x04, 0x24, 0x74, 0x6f, 0xa5, 0x00, 0x12,
2220x67, 0x00, 0x02, 0x8f, 0x03, 0x57, 0x00, 0x28, 0x73, 0x65, 2230x65, 0x04, 0x09, 0x30, 0x75, 0x72, 0x72, 0x67, 0x00, 0x02,
2230x65, 0x13, 0x03, 0x34, 0x32, 0x29, 0x2c, 0x9f, 0x04, 0x00, 2240x8f, 0x03, 0x57, 0x00, 0x28, 0x73, 0x65, 0x65, 0x13, 0x03,
2240x2d, 0x00, 0x0f, 0xfb, 0x01, 0x01, 0x05, 0x01, 0x03, 0x07, 2250x34, 0x32, 0x29, 0x2c, 0x9f, 0x04, 0x00, 0x2d, 0x00, 0x0f,
2250x32, 0x09, 0x40, 0x72, 0x65, 0x2d, 0x65, 0x02, 0x04, 0x02, 2260xfb, 0x01, 0x01, 0x05, 0x01, 0x03, 0x07, 0x32, 0x09, 0x40,
2260x37, 0x01, 0x29, 0x61, 0x6d, 0x88, 0x00, 0x02, 0x8d, 0x05, 2270x72, 0x65, 0x2d, 0x65, 0x02, 0x04, 0x02, 0x37, 0x01, 0x29,
2270x70, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x6f, 0x00, 2280x61, 0x6d, 0x88, 0x00, 0x02, 0x8d, 0x05, 0x70, 0x73, 0x68,
2280x76, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x2d, 0x00, 2290x6f, 0x75, 0x6c, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x65, 0x78,
2290x41, 0x73, 0x74, 0x61, 0x72, 0x59, 0x09, 0x02, 0x97, 0x05, 2300x61, 0x63, 0x74, 0x6c, 0x79, 0x2d, 0x00, 0x41, 0x73, 0x74,
2300x01, 0x0e, 0x08, 0x02, 0x9a, 0x08, 0x24, 0x6c, 0x79, 0x8f, 2310x61, 0x72, 0x59, 0x09, 0x02, 0x97, 0x05, 0x01, 0x0e, 0x08,
2310x00, 0x24, 0x62, 0x65, 0x49, 0x04, 0x03, 0xf1, 0x01, 0x25, 2320x02, 0x9a, 0x08, 0x24, 0x6c, 0x79, 0x8f, 0x00, 0x24, 0x62,
2320x6f, 0x66, 0x44, 0x01, 0x43, 0x2e, 0x00, 0x53, 0x6f, 0x0f, 2330x65, 0x49, 0x04, 0x03, 0xf1, 0x01, 0x25, 0x6f, 0x66, 0x44,
2330x01, 0x01, 0xf4, 0x06, 0x60, 0x74, 0x75, 0x63, 0x6b, 0x00, 2340x01, 0x43, 0x2e, 0x00, 0x53, 0x6f, 0x0f, 0x01, 0x01, 0xf4,
2340x6f, 0xc7, 0x04, 0x10, 0x70, 0x59, 0x00, 0x52, 0x63, 0x75, 2350x06, 0x60, 0x74, 0x75, 0x63, 0x6b, 0x00, 0x6f, 0xc7, 0x04,
2350x6c, 0x61, 0x72, 0xb0, 0x01, 0x20, 0x61, 0x6e, 0x45, 0x09, 2360x10, 0x70, 0x59, 0x00, 0x52, 0x63, 0x75, 0x6c, 0x61, 0x72,
2360x00, 0x6a, 0x05, 0x01, 0xce, 0x01, 0x03, 0x28, 0x06, 0x02, 2370xb0, 0x01, 0x20, 0x61, 0x6e, 0x45, 0x09, 0x00, 0x6a, 0x05,
2370x9a, 0x00, 0x00, 0x85, 0x00, 0x00, 0x2a, 0x0a, 0x23, 0x61, 2380x01, 0xce, 0x01, 0x03, 0x28, 0x06, 0x02, 0x9a, 0x00, 0x00,
2380x6e, 0x8b, 0x0a, 0x52, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x88, 2390x85, 0x00, 0x00, 0x2a, 0x0a, 0x23, 0x61, 0x6e, 0x8b, 0x0a,
2390x05, 0x71, 0x4e, 0x65, 0x74, 0x2c, 0x00, 0x73, 0x65, 0x20, 2400x52, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x88, 0x05, 0x71, 0x4e,
2400x00, 0x05, 0xb7, 0x00, 0x07, 0x0b, 0x04, 0x00, 0x1d, 0x05, 2410x65, 0x74, 0x2c, 0x00, 0x73, 0x65, 0x20, 0x00, 0x05, 0xb7,
2410x05, 0x92, 0x02, 0x04, 0xdd, 0x02, 0x07, 0x36, 0x01, 0x02, 2420x00, 0x07, 0x0b, 0x04, 0x00, 0x1d, 0x05, 0x05, 0x92, 0x02,
2420x2c, 0x01, 0x06, 0x24, 0x01, 0x01, 0x04, 0x09, 0x01, 0xa5, 2430x04, 0xdd, 0x02, 0x07, 0x36, 0x01, 0x02, 0x2c, 0x01, 0x06,
2430x01, 0x05, 0xae, 0x03, 0x04, 0x71, 0x0b, 0x01, 0x46, 0x02, 2440x24, 0x01, 0x01, 0x04, 0x09, 0x01, 0xa5, 0x01, 0x05, 0xae,
2440x11, 0x77, 0x9a, 0x0b, 0x00, 0x6b, 0x02, 0x20, 0x45, 0x6e, 2450x03, 0x04, 0x71, 0x0b, 0x01, 0x46, 0x02, 0x11, 0x77, 0x9a,
2450xc3, 0x07, 0x85, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 2460x0b, 0x00, 0x6b, 0x02, 0x20, 0x45, 0x6e, 0xc3, 0x07, 0x85,
2460x20, 0x93, 0x02, 0x60, 0x00, 0x00, 0x4e, 0x6f, 0x72, 0x6d, 2470x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x93, 0x02,
2470x7f, 0x07, 0x13, 0x2c, 0x31, 0x00, 0x63, 0x6c, 0x6c, 0x00, 2480x60, 0x00, 0x00, 0x4e, 0x6f, 0x72, 0x6d, 0x7f, 0x07, 0x13,
2480x6d, 0x61, 0x6b, 0xf2, 0x07, 0x05, 0x67, 0x02, 0x02, 0xe7, 2490x2c, 0x31, 0x00, 0x63, 0x6c, 0x6c, 0x00, 0x6d, 0x61, 0x6b,
2490x01, 0x10, 0x73, 0xba, 0x05, 0x50, 0x70, 0x72, 0x65, 0x73, 2500xf2, 0x07, 0x05, 0x67, 0x02, 0x02, 0xe7, 0x01, 0x10, 0x73,
2500x65, 0xdf, 0x02, 0x33, 0x68, 0x61, 0x76, 0x5c, 0x01, 0x26, 2510xba, 0x05, 0x50, 0x70, 0x72, 0x65, 0x73, 0x65, 0xdf, 0x02,
2510x6f, 0x6e, 0xe6, 0x02, 0x33, 0x2e, 0x00, 0x50, 0x2c, 0x00, 2520x33, 0x68, 0x61, 0x76, 0x5c, 0x01, 0x26, 0x6f, 0x6e, 0xe6,
2520x01, 0x80, 0x01, 0x94, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 2530x02, 0x33, 0x2e, 0x00, 0x50, 0x2c, 0x00, 0x01, 0x80, 0x01,
2530x6f, 0x75, 0x73, 0x1e, 0x02, 0x14, 0x73, 0xf9, 0x06, 0x01, 2540x94, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73,
2540x64, 0x03, 0x40, 0x64, 0x69, 0x66, 0x66, 0x59, 0x01, 0x11, 2550x1e, 0x02, 0x14, 0x73, 0xf9, 0x06, 0x01, 0x64, 0x03, 0x40,
2550x74, 0xd9, 0x00, 0x21, 0x6d, 0x6f, 0x6b, 0x08, 0x41, 0x62, 2560x64, 0x69, 0x66, 0x66, 0x59, 0x01, 0x11, 0x74, 0xd9, 0x00,
2560x74, 0x6c, 0x65, 0x56, 0x07, 0x03, 0x9b, 0x02, 0x45, 0x6c, 2570x21, 0x6d, 0x6f, 0x6b, 0x08, 0x41, 0x62, 0x74, 0x6c, 0x65,
2570x69, 0x6b, 0x65, 0x8a, 0x07, 0x01, 0x26, 0x08, 0x23, 0x6f, 2580x56, 0x07, 0x03, 0x9b, 0x02, 0x45, 0x6c, 0x69, 0x6b, 0x65,
2580x66, 0xad, 0x0c, 0x40, 0x66, 0x65, 0x61, 0x74, 0xa9, 0x00, 2590x8a, 0x07, 0x01, 0x26, 0x08, 0x23, 0x6f, 0x66, 0xad, 0x0c,
2590x00, 0x3e, 0x00, 0x40, 0x72, 0x69, 0x73, 0x6b, 0x95, 0x00, 2600x40, 0x66, 0x65, 0x61, 0x74, 0xa9, 0x00, 0x00, 0x3e, 0x00,
2600x00, 0xe1, 0x01, 0x06, 0x77, 0x00, 0x03, 0xba, 0x00, 0x10, 2610x40, 0x72, 0x69, 0x73, 0x6b, 0x95, 0x00, 0x00, 0xe1, 0x01,
2610x2e, 0xcf, 0x05, 0x81, 0x73, 0x6f, 0x2c, 0x00, 0x66, 0x69, 2620x06, 0x77, 0x00, 0x03, 0xba, 0x00, 0x10, 0x2e, 0xcf, 0x05,
2620x6e, 0x64, 0x22, 0x00, 0x03, 0xdd, 0x05, 0x76, 0x70, 0x6f, 2630x81, 0x73, 0x6f, 0x2c, 0x00, 0x66, 0x69, 0x6e, 0x64, 0x22,
2630x73, 0x73, 0x69, 0x62, 0x6c, 0xc1, 0x00, 0x05, 0xa1, 0x00, 2640x00, 0x03, 0xdd, 0x05, 0x76, 0x70, 0x6f, 0x73, 0x73, 0x69,
2640x00, 0xd6, 0x08, 0x30, 0x64, 0x64, 0x69, 0x14, 0x00, 0x20, 2650x62, 0x6c, 0xc1, 0x00, 0x05, 0xa1, 0x00, 0x00, 0xd6, 0x08,
2650x61, 0x6c, 0x40, 0x02, 0x51, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 2660x30, 0x64, 0x64, 0x69, 0x14, 0x00, 0x20, 0x61, 0x6c, 0x40,
2660xb8, 0x0c, 0x02, 0x1c, 0x00, 0x30, 0x76, 0x61, 0x6e, 0x73, 2670x02, 0x51, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0xb8, 0x0c, 0x02,
2670x04, 0x90, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x29, 2680x1c, 0x00, 0x10, 0x76, 0xd7, 0x01, 0x12, 0x64, 0x7d, 0x0a,
2680x00, 2690x13, 0x72, 0xf2, 0x05, 0x11, 0x33, 0xf2, 0x05, 0x21, 0x75,
2700x73, 0xee, 0x02, 0xa2, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
2710x63, 0x65, 0x73, 0x20, 0x5f, 0x08, 0x40, 0x70, 0x6c, 0x61,
2720x74, 0xf6, 0x0c, 0x03, 0x7a, 0x03, 0x50, 0x73, 0x75, 0x70,
2730x70, 0x6f, 0x2d, 0x02, 0x12, 0x73, 0x7d, 0x0c, 0x04, 0x2e,
2740x00, 0x12, 0x2c, 0xe6, 0x05, 0x16, 0x50, 0x12, 0x00, 0x0d,
2750x02, 0x06, 0x33, 0x47, 0x61, 0x6d, 0x02, 0x06, 0x02, 0xb3,
2760x01, 0x33, 0x6c, 0x65, 0x74, 0x84, 0x02, 0x40, 0x6e, 0x66,
2770x69, 0x67, 0x12, 0x01, 0x10, 0x77, 0x51, 0x03, 0x01, 0xd1,
2780x0b, 0x0c, 0x88, 0x0b, 0x01, 0xce, 0x07, 0x30, 0x72, 0x72,
2790x6f, 0xf1, 0x02, 0xa2, 0x42, 0x79, 0x00, 0x64, 0x65, 0x66,
2800x61, 0x75, 0x6c, 0x74, 0xc7, 0x04, 0x11, 0x27, 0xca, 0x06,
2810x48, 0x77, 0x61, 0x79, 0x73, 0x32, 0x00, 0x33, 0x3b, 0x00,
2820x62, 0x41, 0x03, 0x02, 0x3d, 0x03, 0x23, 0x69, 0x73, 0x90,
2830x00, 0x06, 0x21, 0x09, 0x32, 0x61, 0x73, 0x6b, 0x19, 0x01,
2840x01, 0x74, 0x00, 0x01, 0x0a, 0x06, 0x0a, 0x75, 0x00, 0x01,
2850x29, 0x02, 0x28, 0x69, 0x66, 0x48, 0x08, 0x00, 0x0b, 0x01,
2860x00, 0x51, 0x00, 0x00, 0x5e, 0x03, 0x04, 0xa4, 0x04, 0x01,
2870x3e, 0x00, 0x14, 0x69, 0x2b, 0x0a, 0x03, 0xae, 0x04, 0x01,
2880x75, 0x0e, 0x16, 0x73, 0x20, 0x00, 0x08, 0xcf, 0x08, 0x01,
2890x9e, 0x07, 0x00, 0x07, 0x00, 0x00, 0xb5, 0x0c, 0x72, 0x73,
2900x70, 0x6f, 0x69, 0x6c, 0x65, 0x72, 0x8a, 0x00, 0x05, 0x53,
2910x07, 0x43, 0x64, 0x65, 0x64, 0x75, 0x7c, 0x02, 0x55, 0x61,
2920x62, 0x6f, 0x75, 0x74, 0xb6, 0x0d, 0x00, 0xbd, 0x00, 0x00,
2930xc6, 0x02, 0x00, 0x09, 0x09, 0x22, 0x65, 0x76, 0x30, 0x01,
2940x00, 0x14, 0x08, 0x80, 0x61, 0x74, 0x00, 0x79, 0x65, 0x74,
2950x2e, 0x00,
269}; 296};
270 297
271const unsigned short help_text_len = 3439; 298const unsigned short help_text_len = 3919;
272const unsigned short help_text_words = 611; 299const unsigned short help_text_words = 689;
300const bool help_valid = true;
273const char quick_help_text[] = "Rotate each tile to reassemble the network."; 301const char quick_help_text[] = "Rotate each tile to reassemble the network.";
diff --git a/apps/plugins/puzzles/help/netslide.c b/apps/plugins/puzzles/help/netslide.c
index 0035c6e3e1..17ec9ec440 100644
--- a/apps/plugins/puzzles/help/netslide.c
+++ b/apps/plugins/puzzles/help/netslide.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -9,52 +9,54 @@ struct style_text help_text_style[] = {
9 LAST_STYLE_ITEM 9 LAST_STYLE_ITEM
10}; 10};
11 11
12/* orig 526 comp 419 ratio 0.796578 level 3 saved 107 */ 12/* orig 546 comp 425 ratio 0.778388 level 3 saved 121 */
13const char help_text[] = { 13const char help_text[] = {
140xf2, 0x3c, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 140xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
150x39, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x73, 0x6c, 0x69, 0x64, 150x39, 0x3a, 0x20, 0x4e, 0x65, 0x74, 0x73, 0x6c, 0x69, 0x64,
160x65, 0x20, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00, 160x65, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf2, 0x28, 0x00, 0x00,
170x67, 0x61, 0x6d, 0x65, 0x00, 0x63, 0x6f, 0x6d, 0x62, 0x69, 170x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65,
180x6e, 0x65, 0x73, 0x00, 0x74, 0x68, 0x65, 0x00, 0x67, 0x72, 180x00, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x73, 0x00,
190x69, 0x64, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 190x74, 0x68, 0x65, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x67,
200x69, 0x6f, 0x6e, 0x00, 0x6f, 0x66, 0x00, 0x4e, 0x65, 0x74, 200x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00,
210x00, 0x28, 0x73, 0x65, 0x65, 0x00, 0x63, 0x4a, 0x00, 0x81, 210x6f, 0x66, 0x00, 0x4e, 0x65, 0x74, 0x00, 0x28, 0x73, 0x65,
220x00, 0x33, 0x29, 0x00, 0x77, 0x69, 0x74, 0x68, 0x30, 0x00, 220x65, 0x00, 0x63, 0x5e, 0x00, 0x81, 0x00, 0x33, 0x29, 0x00,
230x80, 0x6d, 0x6f, 0x76, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x29, 230x77, 0x69, 0x74, 0x68, 0x30, 0x00, 0x80, 0x6d, 0x6f, 0x76,
240x00, 0x7a, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e, 0x2d, 240x65, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x00, 0x7a, 0x53, 0x69,
250x00, 0xe1, 0x36, 0x29, 0x3a, 0x00, 0x79, 0x6f, 0x75, 0x00, 250x78, 0x74, 0x65, 0x65, 0x6e, 0x2d, 0x00, 0xe1, 0x36, 0x29,
260x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x4d, 0x00, 0x00, 0x64, 260x3a, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65,
270x00, 0xd0, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x69, 0x6e, 270x00, 0x61, 0x4d, 0x00, 0x00, 0x64, 0x00, 0xd0, 0x2c, 0x00,
280x73, 0x74, 0x65, 0x61, 0x64, 0x3d, 0x00, 0xf6, 0x0f, 0x72, 280x62, 0x75, 0x74, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61,
290x6f, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x69, 290x64, 0x3d, 0x00, 0xf6, 0x0f, 0x72, 0x6f, 0x74, 0x61, 0x74,
300x6c, 0x65, 0x73, 0x00, 0x62, 0x61, 0x63, 0x6b, 0x00, 0x69, 300x69, 0x6e, 0x67, 0x00, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x00,
310x6e, 0x74, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x43, 310x62, 0x61, 0x63, 0x6b, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00,
320x00, 0x31, 0x74, 0x6f, 0x00, 0xc4, 0x00, 0x00, 0x7e, 0x00, 320x70, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x00, 0x31, 0x74, 0x6f,
330x18, 0x6d, 0x22, 0x00, 0x20, 0x62, 0x79, 0x8d, 0x00, 0x00, 330x00, 0xd8, 0x00, 0x00, 0x7e, 0x00, 0x18, 0x6d, 0x22, 0x00,
340x42, 0x00, 0xf4, 0x0f, 0x61, 0x00, 0x77, 0x68, 0x6f, 0x6c, 340x20, 0x62, 0x79, 0x8d, 0x00, 0x00, 0x42, 0x00, 0xf4, 0x0f,
350x65, 0x00, 0x72, 0x6f, 0x77, 0x00, 0x61, 0x74, 0x00, 0x61, 350x61, 0x00, 0x77, 0x68, 0x6f, 0x6c, 0x65, 0x00, 0x72, 0x6f,
360x00, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x00, 0x00, 0x00, 0x41, 360x77, 0x00, 0x61, 0x74, 0x00, 0x61, 0x00, 0x74, 0x69, 0x6d,
370x73, 0x00, 0x69, 0x6e, 0xa7, 0x00, 0xc8, 0x2c, 0x00, 0x63, 370x65, 0x2e, 0x00, 0x00, 0x00, 0x41, 0x73, 0x00, 0x69, 0x6e,
380x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x00, 0x69, 0x73, 0xd0, 380xa7, 0x00, 0xc8, 0x2c, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x72,
390x00, 0xf1, 0x0c, 0x75, 0x73, 0x65, 0x00, 0x6f, 0x72, 0x00, 390x6f, 0x6c, 0x00, 0x69, 0x73, 0xd0, 0x00, 0xf1, 0x0c, 0x75,
400x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 400x73, 0x65, 0x00, 0x6f, 0x72, 0x00, 0x63, 0x75, 0x72, 0x73,
410x73, 0x2e, 0x00, 0x53, 0x65, 0x65, 0x00, 0x73, 0x65, 0x63, 410x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x00, 0x53,
420x12, 0x01, 0x41, 0x36, 0x2e, 0x31, 0x2e, 0x40, 0x01, 0xb2, 420x65, 0x65, 0x00, 0x73, 0x65, 0x63, 0x12, 0x01, 0x41, 0x36,
430x65, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 430x2e, 0x31, 0x2e, 0x40, 0x01, 0xb2, 0x65, 0x00, 0x61, 0x76,
440x65, 0x49, 0x01, 0xa2, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 440x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x01, 0xa2,
450x74, 0x65, 0x72, 0x73, 0xab, 0x00, 0xf0, 0x01, 0x73, 0x69, 450x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
460x6d, 0x69, 0x6c, 0x61, 0x72, 0x00, 0x6d, 0x65, 0x61, 0x6e, 460xab, 0x00, 0xf0, 0x01, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61,
470x69, 0x6e, 0x67, 0x73, 0xbc, 0x00, 0x50, 0x74, 0x68, 0x6f, 470x72, 0x00, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x73,
480x73, 0x65, 0x86, 0x00, 0x05, 0x56, 0x01, 0x04, 0x58, 0x00, 480xbc, 0x00, 0x50, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x86, 0x00,
490x8a, 0x33, 0x2e, 0x32, 0x29, 0x00, 0x61, 0x6e, 0x64, 0x47, 490x05, 0x56, 0x01, 0x04, 0x58, 0x00, 0x8a, 0x33, 0x2e, 0x32,
500x01, 0x06, 0x76, 0x00, 0x20, 0x32, 0x29, 0x77, 0x00, 0x04, 500x29, 0x00, 0x61, 0x6e, 0x64, 0x47, 0x01, 0x06, 0x76, 0x00,
510xc3, 0x01, 0x42, 0x00, 0x77, 0x61, 0x73, 0xc1, 0x00, 0x62, 510x20, 0x32, 0x29, 0x77, 0x00, 0x04, 0xd7, 0x01, 0x42, 0x00,
520x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x5c, 0x00, 0x10, 0x69, 520x77, 0x61, 0x73, 0xc1, 0x00, 0x62, 0x69, 0x62, 0x75, 0x74,
530x14, 0x00, 0x23, 0x6c, 0x6c, 0x34, 0x00, 0xf0, 0x05, 0x62, 530x65, 0x64, 0x5c, 0x00, 0x10, 0x69, 0x14, 0x00, 0x23, 0x6c,
540x79, 0x00, 0x52, 0x69, 0x63, 0x68, 0x61, 0x72, 0x64, 0x00, 540x6c, 0x34, 0x00, 0xf0, 0x05, 0x62, 0x79, 0x00, 0x52, 0x69,
550x42, 0x6f, 0x75, 0x6c, 0x74, 0x6f, 0x6e, 0x2e, 0x00, 550x63, 0x68, 0x61, 0x72, 0x64, 0x00, 0x42, 0x6f, 0x75, 0x6c,
560x74, 0x6f, 0x6e, 0x2e, 0x00,
56}; 57};
57 58
58const unsigned short help_text_len = 526; 59const unsigned short help_text_len = 546;
59const unsigned short help_text_words = 98; 60const unsigned short help_text_words = 99;
61const bool help_valid = true;
60const char quick_help_text[] = "Slide a row at a time to reassemble the network."; 62const char quick_help_text[] = "Slide a row at a time to reassemble the network.";
diff --git a/apps/plugins/puzzles/help/nullgame.c b/apps/plugins/puzzles/help/nullgame.c
new file mode 100644
index 0000000000..219a68c7e8
--- /dev/null
+++ b/apps/plugins/puzzles/help/nullgame.c
@@ -0,0 +1 @@
const char quick_help_text[] = "";
diff --git a/apps/plugins/puzzles/help/palisade.c b/apps/plugins/puzzles/help/palisade.c
index 1094502ea7..36d3c65248 100644
--- a/apps/plugins/puzzles/help/palisade.c
+++ b/apps/plugins/puzzles/help/palisade.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,108 +6,139 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 87, TEXT_CENTER | C_RED }, 9 { 88, TEXT_CENTER | C_RED },
10 { 149, TEXT_CENTER | C_RED }, 10 { 199, TEXT_CENTER | C_RED },
11 { 166, TEXT_UNDERLINE }, 11 { 216, TEXT_UNDERLINE },
12 { 167, TEXT_UNDERLINE }, 12 { 217, TEXT_UNDERLINE },
13 { 177, TEXT_UNDERLINE }, 13 { 227, TEXT_UNDERLINE },
14 { 244, TEXT_CENTER | C_RED },
14 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
15}; 16};
16 17
17/* orig 1113 comp 892 ratio 0.801438 level 3 saved 221 */ 18/* orig 1672 comp 1186 ratio 0.70933 level 4 saved 486 */
18const char help_text[] = { 19const char help_text[] = {
190xf0, 0x2d, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x34, 0x31, 0x3a, 0x20, 0x50, 0x61, 0x6c, 0x69, 0x73, 0x61, 210x34, 0x31, 0x3a, 0x20, 0x50, 0x61, 0x6c, 0x69, 0x73, 0x61,
210x64, 0x65, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x27, 220x64, 0x65, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf0, 0x18,
220x72, 0x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 230x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x00,
230x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 240x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72,
240x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 250x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61,
250x6d, 0x65, 0x11, 0x00, 0xf0, 0x07, 0x77, 0x68, 0x69, 0x63, 260x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x11,
260x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 270x00, 0xf0, 0x07, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x63,
270x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x3f, 0x00, 280x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d,
280xf2, 0x0b, 0x72, 0x00, 0x67, 0x6f, 0x61, 0x6c, 0x00, 0x69, 290x62, 0x65, 0x72, 0x73, 0x2e, 0x3f, 0x00, 0xf2, 0x0b, 0x72,
290x73, 0x00, 0x74, 0x6f, 0x00, 0x73, 0x75, 0x62, 0x64, 0x69, 300x00, 0x67, 0x6f, 0x61, 0x6c, 0x00, 0x69, 0x73, 0x00, 0x74,
300x76, 0x69, 0x64, 0x65, 0x00, 0x74, 0x68, 0x65, 0x4e, 0x00, 310x6f, 0x00, 0x73, 0x75, 0x62, 0x64, 0x69, 0x76, 0x69, 0x64,
310x41, 0x69, 0x6e, 0x74, 0x6f, 0x39, 0x00, 0xf0, 0x04, 0x69, 320x65, 0x00, 0x74, 0x68, 0x65, 0x4e, 0x00, 0x41, 0x69, 0x6e,
320x67, 0x75, 0x6f, 0x75, 0x73, 0x00, 0x72, 0x65, 0x67, 0x69, 330x74, 0x6f, 0x39, 0x00, 0xf0, 0x04, 0x69, 0x67, 0x75, 0x6f,
330x6f, 0x6e, 0x73, 0x2c, 0x00, 0x61, 0x6c, 0x6c, 0x5a, 0x00, 340x75, 0x73, 0x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73,
340x00, 0x29, 0x00, 0x61, 0x73, 0x61, 0x6d, 0x65, 0x00, 0x28, 350x2c, 0x00, 0x61, 0x6c, 0x6c, 0x5a, 0x00, 0x00, 0x29, 0x00,
350x85, 0x00, 0xf3, 0x07, 0x29, 0x00, 0x73, 0x69, 0x7a, 0x65, 360x61, 0x73, 0x61, 0x6d, 0x65, 0x00, 0x28, 0x85, 0x00, 0xf3,
360x2c, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x74, 0x68, 0x61, 370x07, 0x29, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x00, 0x73,
370x74, 0x00, 0x65, 0x61, 0x63, 0x68, 0x91, 0x00, 0x04, 0x81, 380x75, 0x63, 0x68, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x65,
380x00, 0x53, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x86, 0x00, 0x00, 390x61, 0x63, 0x68, 0x91, 0x00, 0x04, 0x81, 0x00, 0x53, 0x69,
390x7a, 0x00, 0x80, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 400x6e, 0x67, 0x00, 0x61, 0x86, 0x00, 0x00, 0x7a, 0x00, 0x80,
400x74, 0x83, 0x00, 0x72, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 410x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x83, 0x00,
410x79, 0x3c, 0x00, 0xf0, 0x03, 0x6d, 0x61, 0x6e, 0x79, 0x00, 420x72, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x3c, 0x00,
420x65, 0x64, 0x67, 0x65, 0x73, 0x00, 0x28, 0x69, 0x6e, 0x63, 430xf0, 0x03, 0x6d, 0x61, 0x6e, 0x79, 0x00, 0x65, 0x64, 0x67,
430x6c, 0x75, 0x64, 0x3b, 0x00, 0xd1, 0x74, 0x68, 0x6f, 0x73, 440x65, 0x73, 0x00, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
440x65, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x81, 450x3b, 0x00, 0xd1, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x00, 0x62,
450x00, 0x30, 0x69, 0x6e, 0x73, 0xb5, 0x00, 0x31, 0x61, 0x6e, 460x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x81, 0x00, 0x30, 0x69,
460x64, 0x0f, 0x00, 0x31, 0x6f, 0x75, 0x74, 0x10, 0x00, 0x25, 470x6e, 0x73, 0xb5, 0x00, 0x31, 0x61, 0x6e, 0x64, 0x0f, 0x00,
470x6f, 0x66, 0xc8, 0x00, 0xf1, 0x10, 0x29, 0x2e, 0x00, 0x00, 480x31, 0x6f, 0x75, 0x74, 0x10, 0x00, 0x25, 0x6f, 0x66, 0xc8,
480x00, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 490x00, 0xf1, 0x10, 0x29, 0x2e, 0x00, 0x00, 0x00, 0x43, 0x72,
490x72, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 500x65, 0x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x74,
500x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0xfe, 0x00, 0xd0, 510x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65,
510x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x2c, 0x00, 0x77, 0x68, 520x00, 0x67, 0x6f, 0x65, 0xfe, 0x00, 0xd0, 0x4e, 0x69, 0x6b,
520x6f, 0x00, 0x63, 0xdf, 0x00, 0xf0, 0x06, 0x69, 0x74, 0x00, 530x6f, 0x6c, 0x69, 0x2c, 0x00, 0x77, 0x68, 0x6f, 0x00, 0x63,
530x60, 0x46, 0x69, 0x76, 0x65, 0x00, 0x43, 0x65, 0x6c, 0x6c, 540xdf, 0x00, 0xf0, 0x06, 0x69, 0x74, 0x00, 0x60, 0x46, 0x69,
540x73, 0x27, 0x2e, 0x00, 0x5b, 0x32, 0x32, 0x5d, 0x49, 0x00, 550x76, 0x65, 0x00, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x27, 0x2e,
550x04, 0x83, 0x01, 0x41, 0x00, 0x77, 0x61, 0x73, 0xd5, 0x00, 560x00, 0x5b, 0x32, 0x32, 0x5d, 0x49, 0x00, 0x04, 0x98, 0x01,
560x70, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x46, 0x00, 570x41, 0x00, 0x77, 0x61, 0x73, 0xd5, 0x00, 0x70, 0x72, 0x69,
570x01, 0x5a, 0x00, 0xf0, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 580x62, 0x75, 0x74, 0x65, 0x64, 0x46, 0x00, 0x01, 0x5a, 0x00,
580x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x4a, 590xf0, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
590x6f, 0x6e, 0x61, 0x73, 0x00, 0x4b, 0x6f, 0x65, 0x6c, 0x6b, 600x6f, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x4a, 0x6f, 0x6e, 0x61,
600x65, 0x72, 0x40, 0x00, 0x00, 0x48, 0x00, 0x91, 0x00, 0x68, 610x73, 0x00, 0x4b, 0x6f, 0x65, 0x6c, 0x6b, 0x65, 0x72, 0x40,
610x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x76, 0x00, 0xa2, 620x00, 0x00, 0x48, 0x00, 0xe1, 0x00, 0x68, 0x74, 0x74, 0x70,
620x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 630x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x7b,
630x95, 0x00, 0x80, 0x73, 0x2f, 0x66, 0x69, 0x76, 0x65, 0x5f, 640x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65,
640x63, 0x79, 0x00, 0xc6, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 650x6e, 0x2f, 0x9a, 0x00, 0x80, 0x73, 0x2f, 0x66, 0x69, 0x76,
650x00, 0x00, 0x34, 0x31, 0x2e, 0x31, 0xfe, 0x01, 0x01, 0x77, 660x65, 0x5f, 0x63, 0x7e, 0x00, 0x86, 0x2f, 0x00, 0x00, 0x00,
660x00, 0x30, 0x6f, 0x6c, 0x73, 0x07, 0x02, 0xa0, 0x4c, 0x65, 670x34, 0x31, 0x2e, 0x31, 0x14, 0x02, 0x01, 0x78, 0x00, 0xf0,
670x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x82, 0x00, 680x02, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x4c, 0x65,
680x40, 0x70, 0x6c, 0x61, 0x63, 0x0d, 0x01, 0x01, 0x3a, 0x01, 690x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x83, 0x00,
700x40, 0x70, 0x6c, 0x61, 0x63, 0x0e, 0x01, 0x01, 0x3b, 0x01,
690x67, 0x2e, 0x00, 0x52, 0x69, 0x67, 0x68, 0x1e, 0x00, 0xc1, 710x67, 0x2e, 0x00, 0x52, 0x69, 0x67, 0x68, 0x1e, 0x00, 0xc1,
700x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x60, 720x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x60,
710x6e, 0x6f, 0x22, 0x00, 0xf1, 0x02, 0x27, 0x2e, 0x00, 0x41, 730x6e, 0x6f, 0x22, 0x00, 0x10, 0x27, 0x93, 0x00, 0xe1, 0x41,
720x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 740x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65,
730x6c, 0x79, 0x2c, 0x35, 0x01, 0xf0, 0x07, 0x61, 0x72, 0x72, 750x6c, 0x79, 0x2c, 0x38, 0x01, 0xf0, 0x07, 0x61, 0x72, 0x72,
740x6f, 0x77, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x00, 0x77, 0x69, 760x6f, 0x77, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x00, 0x77, 0x69,
750x6c, 0x6c, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0x00, 0x61, 0x11, 770x6c, 0x6c, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0x00, 0x61, 0x11,
760x00, 0xf1, 0x02, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x63, 780x00, 0xf1, 0x04, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x63,
770x75, 0x72, 0x73, 0x6f, 0x72, 0x2e, 0x00, 0x48, 0x6f, 0x6c, 790x75, 0x72, 0x73, 0x6f, 0x72, 0x2e, 0x00, 0x44, 0x65, 0x70,
780x95, 0x01, 0x12, 0x43, 0x8f, 0x00, 0x00, 0x6a, 0x02, 0x81, 800x65, 0x6e, 0x9a, 0x01, 0x12, 0x6f, 0x8f, 0x01, 0x21, 0x60,
790x6c, 0x65, 0x00, 0x70, 0x72, 0x65, 0x73, 0x73, 0xe7, 0x01, 810x43, 0x1a, 0x00, 0xf3, 0x09, 0x00, 0x6d, 0x6f, 0x64, 0x65,
800x16, 0x6e, 0x4a, 0x00, 0x02, 0x49, 0x00, 0x0b, 0x9e, 0x00, 820x27, 0x00, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
810x10, 0x50, 0x2a, 0x00, 0x72, 0x00, 0x53, 0x68, 0x69, 0x66, 830x63, 0x65, 0x00, 0x28, 0x73, 0x65, 0x65, 0x00, 0x73, 0x19,
820x74, 0x2d, 0x2b, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x8c, 0x02, 840x01, 0x52, 0x34, 0x31, 0x2e, 0x33, 0x29, 0x6a, 0x00, 0x02,
830x97, 0x77, 0x69, 0x74, 0x63, 0x68, 0x00, 0x6f, 0x66, 0x66, 850x4a, 0x00, 0x02, 0x66, 0x00, 0xc0, 0x65, 0x69, 0x74, 0x68,
840xcb, 0x00, 0x51, 0x65, 0x70, 0x65, 0x61, 0x74, 0x55, 0x00, 860x65, 0x72, 0x00, 0x6e, 0x61, 0x76, 0x69, 0x67, 0xa6, 0x00,
850x02, 0x5d, 0x01, 0x00, 0xee, 0x00, 0xf0, 0x03, 0x65, 0x72, 870x56, 0x61, 0x6d, 0x6f, 0x6e, 0x67, 0x90, 0x02, 0x05, 0xdb,
860x66, 0x6f, 0x72, 0x6d, 0x00, 0x69, 0x74, 0x73, 0x00, 0x69, 880x02, 0x53, 0x6f, 0x72, 0x00, 0x61, 0x6c, 0x1b, 0x00, 0x71,
870x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63, 0x01, 0x42, 0x28, 890x69, 0x72, 0x00, 0x62, 0x6f, 0x72, 0x64, 0xd4, 0x02, 0x90,
880x41, 0x6c, 0x6c, 0xc9, 0x00, 0x01, 0x2a, 0x00, 0xa0, 0x73, 900x49, 0x6e, 0x00, 0x60, 0x46, 0x75, 0x6c, 0x6c, 0x2d, 0x2f,
890x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xd3, 910x00, 0x11, 0x27, 0x84, 0x00, 0x82, 0x2c, 0x00, 0x68, 0x6f,
900x02, 0x23, 0x00, 0x73, 0x9d, 0x01, 0x40, 0x32, 0x2e, 0x31, 920x6c, 0x64, 0x00, 0x43, 0x31, 0x01, 0x00, 0x0d, 0x03, 0x20,
910x00, 0x99, 0x02, 0xf2, 0x01, 0x61, 0x6c, 0x73, 0x6f, 0x00, 930x6c, 0x65, 0x97, 0x00, 0x21, 0x73, 0x73, 0x8a, 0x02, 0x16,
920x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 940x6e, 0xea, 0x00, 0x0d, 0x3e, 0x01, 0x11, 0x2c, 0x55, 0x02,
930x29, 0x6e, 0x01, 0x16, 0x32, 0x6e, 0x01, 0x91, 0x70, 0x61, 950x01, 0x2c, 0x00, 0x71, 0x00, 0x53, 0x68, 0x69, 0x66, 0x74,
940x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x70, 0x01, 0x66, 960x2d, 0x2c, 0x00, 0x03, 0x2b, 0x00, 0xa6, 0x73, 0x77, 0x69,
950x54, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x44, 970x74, 0x63, 0x68, 0x00, 0x6f, 0x66, 0x66, 0x6e, 0x01, 0x00,
960x00, 0x04, 0x3f, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 980x80, 0x00, 0x68, 0x48, 0x61, 0x6c, 0x66, 0x2d, 0x00, 0x81,
970x7c, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 990x00, 0x02, 0x42, 0x00, 0x5f, 0x45, 0x6e, 0x74, 0x65, 0x72,
980x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x72, 0x00, 0x22, 1000x64, 0x00, 0x04, 0x20, 0x53, 0x70, 0x13, 0x00, 0x0f, 0x55,
990x6f, 0x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 1010x00, 0x07, 0x03, 0x18, 0x01, 0x02, 0x4f, 0x00, 0xe1, 0x79,
1000x00, 0x6d, 0x65, 0x6e, 0x75, 0xae, 0x00, 0x90, 0x57, 0x69, 1020x6f, 0x75, 0x00, 0x63, 0x61, 0x6e, 0x00, 0x72, 0x65, 0x70,
1010x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xac, 0x01, 0x61, 1030x65, 0x61, 0x74, 0xc3, 0x00, 0x02, 0x55, 0x01, 0x00, 0x5c,
1020x00, 0x00, 0x00, 0x53, 0x69, 0x7a, 0xbf, 0x02, 0x03, 0x83, 1040x00, 0xf0, 0x03, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x00,
1030x03, 0x04, 0xd1, 0x03, 0x00, 0x2b, 0x00, 0x11, 0x52, 0x81, 1050x69, 0x74, 0x73, 0x00, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73,
1040x03, 0x10, 0x20, 0x67, 0x03, 0x02, 0x8a, 0x00, 0x01, 0x72, 1060x65, 0xe1, 0x01, 0x42, 0x28, 0x41, 0x6c, 0x6c, 0xd7, 0x01,
1050x03, 0x04, 0xec, 0x02, 0x03, 0x9f, 0x03, 0x02, 0xb7, 0x03, 1070x01, 0x2a, 0x00, 0xa0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63,
1060x02, 0xf6, 0x03, 0x05, 0xcb, 0x03, 0x74, 0x6d, 0x75, 0x73, 1080x72, 0x69, 0x62, 0x65, 0xe4, 0x03, 0x05, 0x95, 0x01, 0x40,
1070x74, 0x00, 0x62, 0x65, 0xe6, 0x03, 0x50, 0x64, 0x65, 0x64, 1090x32, 0x2e, 0x31, 0x00, 0xaa, 0x03, 0xf2, 0x01, 0x61, 0x6c,
1080x2e, 0x00, 1100x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
1110x6c, 0x65, 0x2e, 0x29, 0x7e, 0x02, 0x16, 0x32, 0x7e, 0x02,
1120x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
1130x80, 0x02, 0x66, 0x54, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14,
1140x00, 0x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x54, 0x00, 0x66,
1150x72, 0x6f, 0x6d, 0x1a, 0x02, 0xb1, 0x73, 0x74, 0x6f, 0x6d,
1160x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x72, 0x00, 0x04,
1170x34, 0x02, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d,
1180x65, 0x6e, 0x75, 0xae, 0x00, 0x90, 0x57, 0x69, 0x64, 0x74,
1190x68, 0x2c, 0x00, 0x48, 0x65, 0xbc, 0x02, 0x61, 0x00, 0x00,
1200x00, 0x53, 0x69, 0x7a, 0xd0, 0x03, 0x03, 0x94, 0x04, 0x04,
1210x07, 0x02, 0x00, 0x2b, 0x00, 0x11, 0x52, 0x92, 0x04, 0x10,
1220x20, 0x78, 0x04, 0x02, 0x8a, 0x00, 0x01, 0x83, 0x04, 0x04,
1230xfd, 0x03, 0x03, 0xb0, 0x04, 0x02, 0xc8, 0x04, 0x02, 0x07,
1240x05, 0x05, 0x4c, 0x02, 0x76, 0x6d, 0x75, 0x73, 0x74, 0x00,
1250x62, 0x65, 0xf7, 0x04, 0x22, 0x64, 0x2e, 0xe8, 0x00, 0x16,
1260x33, 0xe8, 0x00, 0x56, 0x75, 0x73, 0x65, 0x72, 0x20, 0xc3,
1270x02, 0x01, 0xee, 0x00, 0x20, 0x4f, 0x6e, 0xc1, 0x01, 0x10,
1280x74, 0x66, 0x01, 0x12, 0x73, 0xa9, 0x04, 0x80, 0x73, 0x75,
1290x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x07, 0xf1,
1300x02, 0x12, 0x73, 0xdf, 0x02, 0x26, 0x60, 0x50, 0x12, 0x00,
1310x0d, 0xf8, 0x00, 0x33, 0x47, 0x61, 0x6d, 0xf8, 0x00, 0x02,
1320x00, 0x03, 0x32, 0x6c, 0x65, 0x74, 0xdf, 0x01, 0x72, 0x6f,
1330x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x8c, 0x05, 0x84, 0x62,
1340x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0xd7, 0x00, 0x03,
1350x32, 0x03, 0x01, 0x9d, 0x03, 0x2f, 0x74, 0x6f, 0x35, 0x03,
1360x04, 0x4f, 0x66, 0x75, 0x6c, 0x6c, 0x36, 0x03, 0x08, 0x04,
1370x34, 0x03, 0x08, 0x28, 0x05, 0xa0, 0x00, 0x73, 0x71, 0x75,
1380x61, 0x72, 0x65, 0x73, 0x2e, 0x00,
109}; 139};
110 140
111const unsigned short help_text_len = 1113; 141const unsigned short help_text_len = 1672;
112const unsigned short help_text_words = 192; 142const unsigned short help_text_words = 285;
143const bool help_valid = true;
113const char quick_help_text[] = "Divide the grid into equal-sized areas in accordance with the clues."; 144const char quick_help_text[] = "Divide the grid into equal-sized areas in accordance with the clues.";
diff --git a/apps/plugins/puzzles/help/pattern.c b/apps/plugins/puzzles/help/pattern.c
index 2b43a878bd..fae35c0f64 100644
--- a/apps/plugins/puzzles/help/pattern.c
+++ b/apps/plugins/puzzles/help/pattern.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,92 +6,94 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 137, TEXT_UNDERLINE }, 9 { 142, TEXT_UNDERLINE },
10 { 173, TEXT_CENTER | C_RED }, 10 { 178, TEXT_CENTER | C_RED },
11 { 362, TEXT_CENTER | C_RED }, 11 { 367, TEXT_CENTER | C_RED },
12 { 378, TEXT_UNDERLINE }, 12 { 383, TEXT_UNDERLINE },
13 { 380, TEXT_UNDERLINE }, 13 { 385, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 2125 comp 1459 ratio 0.686588 level 4 saved 666 */ 17/* orig 2167 comp 1479 ratio 0.68251 level 4 saved 688 */
18const char help_text[] = { 18const char help_text[] = {
190xf0, 0x4f, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x31, 0x30, 0x3a, 0x20, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 200x31, 0x30, 0x3a, 0x20, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72,
210x6e, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 210x6e, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x3b, 0x00, 0x00,
220x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 220x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00,
230x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 230x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00,
240x73, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x6d, 240x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x77,
250x75, 0x73, 0x74, 0x00, 0x61, 0x6c, 0x6c, 0x00, 0x62, 0x65, 250x68, 0x69, 0x63, 0x68, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00,
260x00, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x69, 0x6e, 260x61, 0x6c, 0x6c, 0x00, 0x62, 0x65, 0x00, 0x66, 0x69, 0x6c,
270x00, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x00, 0x62, 0x6c, 270x6c, 0x65, 0x64, 0x00, 0x69, 0x6e, 0x00, 0x65, 0x69, 0x74,
280x61, 0x63, 0x6b, 0x00, 0x6f, 0x72, 0x2c, 0x00, 0xf0, 0x04, 280x68, 0x65, 0x72, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x00,
290x74, 0x65, 0x2e, 0x00, 0x42, 0x65, 0x73, 0x69, 0x64, 0x65, 290x6f, 0x72, 0x2c, 0x00, 0xf0, 0x04, 0x74, 0x65, 0x2e, 0x00,
300x00, 0x65, 0x61, 0x63, 0x68, 0x00, 0x72, 0x6f, 0x77, 0x4f, 300x42, 0x65, 0x73, 0x69, 0x64, 0x65, 0x00, 0x65, 0x61, 0x63,
310x00, 0x32, 0x74, 0x68, 0x65, 0x5b, 0x00, 0xa1, 0x61, 0x72, 310x68, 0x00, 0x72, 0x6f, 0x77, 0x4f, 0x00, 0x32, 0x74, 0x68,
320x65, 0x00, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x14, 0x00, 320x65, 0x5b, 0x00, 0xb0, 0x61, 0x72, 0x65, 0x00, 0x6c, 0x69,
330x74, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x73, 0x23, 0x00, 330x73, 0x74, 0x65, 0x64, 0x2c, 0x42, 0x00, 0x61, 0x6f, 0x72,
340x31, 0x72, 0x75, 0x6e, 0x0c, 0x00, 0x02, 0x52, 0x00, 0x03, 340x64, 0x65, 0x72, 0x2c, 0x1f, 0x00, 0x74, 0x6c, 0x65, 0x6e,
350x84, 0x00, 0x80, 0x00, 0x6f, 0x6e, 0x00, 0x74, 0x68, 0x61, 350x67, 0x74, 0x68, 0x73, 0x2e, 0x00, 0x31, 0x72, 0x75, 0x6e,
360x74, 0x4c, 0x00, 0x63, 0x3b, 0x00, 0x61, 0x62, 0x6f, 0x76, 360x0c, 0x00, 0x02, 0x5d, 0x00, 0x03, 0x8f, 0x00, 0x80, 0x00,
370x5c, 0x00, 0x6f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x53, 370x6f, 0x6e, 0x00, 0x74, 0x68, 0x61, 0x74, 0x57, 0x00, 0x63,
380x00, 0x22, 0x13, 0x69, 0x53, 0x00, 0x02, 0x43, 0x00, 0x10, 380x3b, 0x00, 0x61, 0x62, 0x6f, 0x76, 0x67, 0x00, 0x6f, 0x63,
390x2e, 0x02, 0x01, 0xb1, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 390x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5e, 0x00, 0x2d, 0x13, 0x69,
400x69, 0x73, 0x00, 0x74, 0x6f, 0xe3, 0x00, 0x02, 0x24, 0x00, 400x5e, 0x00, 0x02, 0x4e, 0x00, 0x10, 0x2e, 0x18, 0x01, 0xb1,
410x73, 0x65, 0x00, 0x65, 0x6e, 0x74, 0x69, 0x72, 0xbe, 0x00, 410x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74,
420x0c, 0xea, 0x00, 0xf1, 0x22, 0x00, 0x00, 0x49, 0x00, 0x66, 420x6f, 0xf9, 0x00, 0x02, 0x24, 0x00, 0x73, 0x65, 0x00, 0x65,
430x69, 0x72, 0x73, 0x74, 0x00, 0x73, 0x61, 0x77, 0x00, 0x74, 430x6e, 0x74, 0x69, 0x72, 0xd4, 0x00, 0x0c, 0x00, 0x01, 0xf1,
440x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 440x22, 0x00, 0x00, 0x49, 0x00, 0x66, 0x69, 0x72, 0x73, 0x74,
450x00, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x61, 0x72, 0x6f, 0x75, 450x00, 0x73, 0x61, 0x77, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00,
460x6e, 0x64, 0x00, 0x31, 0x39, 0x39, 0x35, 0x2c, 0x00, 0x75, 460x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x66, 0x6f, 0x72,
470x6e, 0x64, 0x65, 0x72, 0x52, 0x00, 0xf0, 0x0f, 0x6e, 0x61, 470x6d, 0x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x00, 0x31,
480x6d, 0x65, 0x00, 0x60, 0x6e, 0x6f, 0x6e, 0x6f, 0x67, 0x72, 480x39, 0x39, 0x35, 0x2c, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72,
490x61, 0x6d, 0x73, 0x27, 0x2e, 0x00, 0x49, 0x27, 0x76, 0x65, 490x52, 0x00, 0xf0, 0x0f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x60,
500x00, 0x73, 0x65, 0x65, 0x6e, 0x00, 0x69, 0x74, 0x78, 0x00, 500x6e, 0x6f, 0x6e, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x73, 0x27,
510xf0, 0x05, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 510x2e, 0x00, 0x49, 0x27, 0x76, 0x65, 0x00, 0x73, 0x65, 0x65,
520x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x00, 0x73, 0x69, 0x6e, 520x6e, 0x00, 0x69, 0x74, 0x78, 0x00, 0xf0, 0x05, 0x76, 0x61,
530x63, 0x65, 0x3b, 0x00, 0x14, 0x6e, 0x47, 0x00, 0x91, 0x64, 530x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x70, 0x6c, 0x61, 0x63,
540x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x00, 540x65, 0x73, 0x00, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x3b, 0x00,
550x10, 0x73, 0x8a, 0x00, 0x93, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 550x14, 0x6e, 0x47, 0x00, 0x91, 0x64, 0x69, 0x66, 0x66, 0x65,
560x6c, 0x6c, 0x79, 0x2c, 0x83, 0x00, 0x03, 0x02, 0x01, 0x00, 560x72, 0x65, 0x6e, 0x74, 0x4d, 0x00, 0x10, 0x73, 0x8a, 0x00,
570xd1, 0x00, 0xc0, 0x79, 0x70, 0x65, 0x00, 0x74, 0x75, 0x72, 570x93, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x6c, 0x79, 0x2c,
580x6e, 0x00, 0x6f, 0x75, 0x74, 0xdf, 0x00, 0x10, 0x62, 0xea, 580x83, 0x00, 0x03, 0x02, 0x01, 0x00, 0xd1, 0x00, 0xc0, 0x79,
590x01, 0xf1, 0x03, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 590x70, 0x65, 0x00, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x6f, 0x75,
600x66, 0x75, 0x6c, 0x00, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 600x74, 0xdf, 0x00, 0x10, 0x62, 0x00, 0x02, 0xf1, 0x03, 0x6d,
610x65, 0xf8, 0x01, 0xa0, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 610x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x66, 0x75, 0x6c, 0x00,
620x6e, 0x67, 0x00, 0x6f, 0x74, 0x00, 0x31, 0x79, 0x6f, 0x75, 620x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0e, 0x02, 0xa0,
630x9b, 0x00, 0x32, 0x6f, 0x6c, 0x76, 0x5f, 0x01, 0xb5, 0x6d, 630x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x6f,
640x2e, 0x00, 0x48, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, 0x2c, 640x74, 0x00, 0x31, 0x79, 0x6f, 0x75, 0x9b, 0x00, 0x50, 0x6f,
650x97, 0x00, 0xf1, 0x05, 0x69, 0x73, 0x00, 0x76, 0x65, 0x72, 650x6c, 0x76, 0x65, 0x64, 0x82, 0x00, 0x80, 0x6d, 0x2e, 0x00,
660x73, 0x69, 0x6f, 0x6e, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 660x48, 0x6f, 0x77, 0x65, 0x76, 0x6e, 0x01, 0x04, 0x97, 0x00,
670x61, 0x74, 0x65, 0x73, 0xe9, 0x00, 0x04, 0x89, 0x00, 0x92, 670xf1, 0x05, 0x69, 0x73, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69,
680x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0xa0, 680x6f, 0x6e, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
690x00, 0x60, 0x74, 0x68, 0x65, 0x79, 0x00, 0x77, 0x63, 0x01, 690x65, 0x73, 0xe9, 0x00, 0x04, 0x89, 0x00, 0x92, 0x61, 0x75,
700x10, 0x6a, 0x57, 0x02, 0xf7, 0x0b, 0x6c, 0x6f, 0x6f, 0x6b, 700x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0xa0, 0x00, 0x60,
710x00, 0x6c, 0x69, 0x6b, 0x65, 0x00, 0x72, 0x61, 0x6e, 0x64, 710x74, 0x68, 0x65, 0x79, 0x00, 0x77, 0x63, 0x01, 0x10, 0x6a,
720x6f, 0x6d, 0x00, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 720x6d, 0x02, 0xf7, 0x0b, 0x6c, 0x6f, 0x6f, 0x6b, 0x00, 0x6c,
730x67, 0x73, 0x89, 0x02, 0xf3, 0x06, 0x2e, 0x00, 0x28, 0x4f, 730x69, 0x6b, 0x65, 0x00, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
740x6e, 0x65, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x68, 0x61, 740x00, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x73,
750x73, 0x00, 0x73, 0x75, 0x67, 0x67, 0x65, 0xf4, 0x01, 0x22, 750x9f, 0x02, 0xf0, 0x06, 0x2e, 0x00, 0x28, 0x4f, 0x6e, 0x65,
760x61, 0x74, 0x85, 0x00, 0x70, 0x69, 0x73, 0x00, 0x61, 0x63, 760x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x68, 0x61, 0x73, 0x00,
770x74, 0x75, 0x65, 0x00, 0x00, 0xca, 0x02, 0x41, 0x6f, 0x6f, 770x73, 0x75, 0x67, 0x67, 0x65, 0xff, 0x01, 0x02, 0xc8, 0x01,
780x64, 0x00, 0xcb, 0x00, 0x04, 0xaa, 0x00, 0xb0, 0x69, 0x74, 780x01, 0x85, 0x00, 0x70, 0x69, 0x73, 0x00, 0x61, 0x63, 0x74,
790x00, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0xd9, 790x75, 0x65, 0x00, 0x00, 0xe0, 0x02, 0x41, 0x6f, 0x6f, 0x64,
800x00, 0x30, 0x00, 0x66, 0x72, 0x6d, 0x00, 0x40, 0x75, 0x65, 800x00, 0xcb, 0x00, 0x04, 0xaa, 0x00, 0xb0, 0x69, 0x74, 0x00,
810x73, 0x73, 0xf0, 0x00, 0x00, 0xb1, 0x00, 0x30, 0x63, 0x6f, 810x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0xd9, 0x00,
820x6c, 0x09, 0x02, 0x06, 0x77, 0x00, 0x62, 0x00, 0x62, 0x61, 820x30, 0x00, 0x66, 0x72, 0x6d, 0x00, 0x40, 0x75, 0x65, 0x73,
830x73, 0x65, 0x64, 0x82, 0x02, 0x14, 0x65, 0x28, 0x01, 0x50, 830x73, 0xf0, 0x00, 0x00, 0xb1, 0x00, 0x30, 0x63, 0x6f, 0x6c,
840x2c, 0x00, 0x61, 0x6e, 0x64, 0xe2, 0x01, 0x22, 0x63, 0x65, 840x09, 0x02, 0x06, 0x77, 0x00, 0x62, 0x00, 0x62, 0x61, 0x73,
850x49, 0x00, 0x20, 0x74, 0x6f, 0x99, 0x00, 0xf0, 0x0f, 0x00, 850x65, 0x64, 0x8d, 0x02, 0x14, 0x65, 0x28, 0x01, 0x50, 0x2c,
860x6c, 0x6f, 0x67, 0x69, 0x63, 0x00, 0x69, 0x6e, 0x73, 0x74, 860x00, 0x61, 0x6e, 0x64, 0xe2, 0x01, 0x22, 0x63, 0x65, 0x49,
870x65, 0x61, 0x64, 0x2e, 0x29, 0x00, 0x54, 0x68, 0x65, 0x00, 870x00, 0x20, 0x74, 0x6f, 0x99, 0x00, 0xf0, 0x0f, 0x00, 0x6c,
880x61, 0x64, 0x76, 0x61, 0x6e, 0x74, 0x61, 0x67, 0x65, 0xf7, 880x6f, 0x67, 0x69, 0x63, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x65,
890x00, 0x51, 0x6f, 0x75, 0x67, 0x68, 0x2c, 0x63, 0x02, 0x00, 890x61, 0x64, 0x2e, 0x29, 0x00, 0x54, 0x68, 0x65, 0x00, 0x61,
900xb4, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x6e, 0x4a, 0x01, 0x00, 900x64, 0x76, 0x61, 0x6e, 0x74, 0x61, 0x67, 0x65, 0xf7, 0x00,
910xa4, 0x02, 0x01, 0x9b, 0x01, 0x23, 0x6f, 0x66, 0x63, 0x01, 910x51, 0x6f, 0x75, 0x67, 0x68, 0x2c, 0x63, 0x02, 0x00, 0xb4,
920x65, 0x00, 0x00, 0x31, 0x30, 0x2e, 0x31, 0xa3, 0x03, 0x80, 920x00, 0x00, 0x3a, 0x00, 0x10, 0x6e, 0x4a, 0x01, 0x00, 0xa4,
930x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0xac, 0x03, 930x02, 0x01, 0x9b, 0x01, 0x23, 0x6f, 0x66, 0x63, 0x01, 0x65,
940x10, 0x54, 0xea, 0x00, 0x10, 0x67, 0x42, 0x02, 0x11, 0x69, 940x00, 0x00, 0x31, 0x30, 0x2e, 0x31, 0xcd, 0x03, 0xd0, 0x63,
950x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00,
960x00, 0x54, 0xea, 0x00, 0x10, 0x67, 0x42, 0x02, 0x11, 0x69,
950x20, 0x02, 0x81, 0x79, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 970x20, 0x02, 0x81, 0x79, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74,
960x68, 0xa1, 0x00, 0x41, 0x6d, 0x6f, 0x75, 0x73, 0x97, 0x02, 980x68, 0xa1, 0x00, 0x41, 0x6d, 0x6f, 0x75, 0x73, 0x97, 0x02,
970xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 990xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63,
@@ -111,7 +113,7 @@ const char help_text[] = {
1110x74, 0x00, 0x67, 0x72, 0x65, 0x79, 0x00, 0x28, 0xb2, 0x02, 1130x74, 0x00, 0x67, 0x72, 0x65, 0x79, 0x00, 0x28, 0xb2, 0x02,
1120x20, 0x00, 0x60, 0x04, 0x03, 0xe3, 0x63, 0x69, 0x64, 0x65, 1140x20, 0x00, 0x60, 0x04, 0x03, 0xe3, 0x63, 0x69, 0x64, 0x65,
1130x64, 0x27, 0x29, 0x00, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 1150x64, 0x27, 0x29, 0x00, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e,
1140xc5, 0x04, 0x00, 0x91, 0x00, 0x02, 0xca, 0x00, 0x00, 0xa2, 1160xdb, 0x04, 0x00, 0x91, 0x00, 0x02, 0xca, 0x00, 0x00, 0xa2,
1150x01, 0x46, 0x64, 0x72, 0x61, 0x67, 0x1c, 0x01, 0x40, 0x6c, 1170x01, 0x46, 0x64, 0x72, 0x61, 0x67, 0x1c, 0x01, 0x40, 0x6c,
1160x65, 0x66, 0x74, 0xa0, 0x00, 0x10, 0x72, 0xf0, 0x00, 0x02, 1180x65, 0x66, 0x74, 0xa0, 0x00, 0x10, 0x72, 0xf0, 0x00, 0x02,
1170x2a, 0x01, 0x03, 0x84, 0x00, 0x07, 0x83, 0x00, 0x10, 0x61, 1190x2a, 0x01, 0x03, 0x84, 0x00, 0x07, 0x83, 0x00, 0x10, 0x61,
@@ -126,8 +128,8 @@ const char help_text[] = {
1260x6f, 0x01, 0x05, 0xa8, 0x00, 0xe9, 0x77, 0x68, 0x6f, 0x6c, 1280x6f, 0x01, 0x05, 0xa8, 0x00, 0xe9, 0x77, 0x68, 0x6f, 0x6c,
1270x65, 0x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c, 1290x65, 0x00, 0x72, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c,
1280x9c, 0x00, 0x00, 0x2e, 0x01, 0x08, 0x12, 0x01, 0x60, 0x61, 1300x9c, 0x00, 0x00, 0x2e, 0x01, 0x08, 0x12, 0x01, 0x60, 0x61,
1290x6c, 0x73, 0x6f, 0x00, 0x6d, 0x37, 0x05, 0x03, 0x91, 0x04, 1310x6c, 0x73, 0x6f, 0x00, 0x6d, 0x42, 0x05, 0x03, 0x91, 0x04,
1300x05, 0x8e, 0x05, 0x05, 0x39, 0x02, 0xf7, 0x00, 0x63, 0x75, 1320x05, 0xa4, 0x05, 0x05, 0x39, 0x02, 0xf7, 0x00, 0x63, 0x75,
1310x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x2e, 1330x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x2e,
1320x00, 0x50, 0x72, 0x13, 0x03, 0x21, 0x72, 0x65, 0x3a, 0x04, 1340x00, 0x50, 0x72, 0x13, 0x03, 0x21, 0x72, 0x65, 0x3a, 0x04,
1330x14, 0x6b, 0xb3, 0x03, 0x54, 0x63, 0x79, 0x63, 0x6c, 0x65, 1350x14, 0x6b, 0xb3, 0x03, 0x54, 0x63, 0x79, 0x63, 0x6c, 0x65,
@@ -148,8 +150,8 @@ const char help_text[] = {
1480x6f, 0x74, 0x68, 0x3b, 0x00, 0x15, 0x6d, 0x87, 0x01, 0x21, 1500x6f, 0x74, 0x68, 0x3b, 0x00, 0x15, 0x6d, 0x87, 0x01, 0x21,
1490x28, 0x41, 0x20, 0x01, 0x10, 0x65, 0x9d, 0x04, 0xc2, 0x69, 1510x28, 0x41, 0x20, 0x01, 0x10, 0x65, 0x9d, 0x04, 0xc2, 0x69,
1500x6f, 0x6e, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 1520x6f, 0x6e, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
1510x62, 0x44, 0x07, 0x21, 0x73, 0x65, 0x16, 0x00, 0x41, 0x00, 1530x62, 0x5a, 0x07, 0x21, 0x73, 0x65, 0x16, 0x00, 0x41, 0x00,
1520x32, 0x2e, 0x31, 0xca, 0x06, 0x01, 0xad, 0x01, 0xb2, 0x61, 1540x32, 0x2e, 0x31, 0xd5, 0x06, 0x01, 0xad, 0x01, 0xb2, 0x61,
1530x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 1550x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29,
1540x0c, 0x04, 0x15, 0x32, 0x0c, 0x04, 0x93, 0x70, 0x61, 0x72, 1560x0c, 0x04, 0x15, 0x32, 0x0c, 0x04, 0x93, 0x70, 0x61, 0x72,
1550x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0e, 0x04, 0x92, 0x65, 1570x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0e, 0x04, 0x92, 0x65,
@@ -159,11 +161,12 @@ const char help_text[] = {
1590x27, 0x27, 0x00, 0x04, 0xd0, 0x04, 0xb1, 0x60, 0x54, 0x79, 1610x27, 0x27, 0x00, 0x04, 0xd0, 0x04, 0xb1, 0x60, 0x54, 0x79,
1600x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x7c, 0x00, 1620x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x7c, 0x00,
1610x31, 0x57, 0x69, 0x64, 0x9d, 0x03, 0x40, 0x64, 0x00, 0x48, 1630x31, 0x57, 0x69, 0x64, 0x9d, 0x03, 0x40, 0x64, 0x00, 0x48,
1620x65, 0x26, 0x03, 0x04, 0xfe, 0x07, 0x00, 0x1c, 0x00, 0xf0, 1640x65, 0x26, 0x03, 0x04, 0x14, 0x08, 0x00, 0x1c, 0x00, 0xf0,
1630x03, 0x73, 0x65, 0x6c, 0x66, 0x2d, 0x65, 0x78, 0x70, 0x6c, 1650x03, 0x73, 0x65, 0x6c, 0x66, 0x2d, 0x65, 0x78, 0x70, 0x6c,
1640x61, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x00, 1660x61, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x00,
165}; 167};
166 168
167const unsigned short help_text_len = 2125; 169const unsigned short help_text_len = 2167;
168const unsigned short help_text_words = 384; 170const unsigned short help_text_words = 389;
171const bool help_valid = true;
169const char quick_help_text[] = "Fill in the pattern in the grid, given only the lengths of runs of black squares."; 172const char quick_help_text[] = "Fill in the pattern in the grid, given only the lengths of runs of black squares.";
diff --git a/apps/plugins/puzzles/help/pearl.c b/apps/plugins/puzzles/help/pearl.c
index 353c541d47..033aca17fa 100644
--- a/apps/plugins/puzzles/help/pearl.c
+++ b/apps/plugins/puzzles/help/pearl.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,180 +6,248 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 138, TEXT_UNDERLINE }, 9 { 139, TEXT_UNDERLINE },
10 { 164, TEXT_UNDERLINE }, 10 { 165, TEXT_UNDERLINE },
11 { 185, TEXT_UNDERLINE }, 11 { 186, TEXT_UNDERLINE },
12 { 223, TEXT_CENTER | C_RED }, 12 { 221, TEXT_CENTER | C_RED },
13 { 369, TEXT_UNDERLINE }, 13 { 367, TEXT_UNDERLINE },
14 { 460, TEXT_CENTER | C_RED }, 14 { 458, TEXT_CENTER | C_RED },
15 { 475, TEXT_UNDERLINE },
16 { 476, TEXT_UNDERLINE },
17 { 486, TEXT_UNDERLINE },
18 { 498, TEXT_UNDERLINE },
19 { 586, TEXT_CENTER | C_RED },
20 { 652, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 21 LAST_STYLE_ITEM
16}; 22};
17 23
18/* orig 2570 comp 1610 ratio 0.626459 level 10 saved 960 */ 24/* orig 3598 comp 2217 ratio 0.616176 level 10 saved 1381 */
19const char help_text[] = { 25const char help_text[] = {
200xf0, 0x21, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 260xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x33, 0x36, 0x3a, 0x20, 0x50, 0x65, 0x61, 0x72, 0x6c, 0x20, 270x33, 0x36, 0x3a, 0x20, 0x50, 0x65, 0x61, 0x72, 0x6c, 0x20,
220x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 280x00, 0x2d, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x59,
230x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 290x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
240x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2e, 300x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71,
250x1c, 0x00, 0xf0, 0x1b, 0x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 310x75, 0x61, 0x72, 0x65, 0x73, 0x2e, 0x1c, 0x00, 0xf0, 0x1b,
260x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x64, 0x72, 0x61, 0x77, 320x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 0x73, 0x00, 0x74,
270x00, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x00, 0x62, 0x65, 0x74, 330x6f, 0x00, 0x64, 0x72, 0x61, 0x77, 0x00, 0x6c, 0x69, 0x6e,
280x77, 0x65, 0x65, 0x6e, 0x00, 0x74, 0x68, 0x65, 0x00, 0x63, 340x65, 0x73, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e,
290x65, 0x6e, 0x74, 0x72, 0x65, 0x73, 0x3a, 0x00, 0xf1, 0x07, 350x00, 0x74, 0x68, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x74, 0x72,
300x68, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 360x65, 0x73, 0x3a, 0x00, 0xf1, 0x07, 0x68, 0x6f, 0x72, 0x69,
310x6c, 0x79, 0x00, 0x6f, 0x72, 0x00, 0x76, 0x65, 0x72, 0x74, 370x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x6f,
320x69, 0x63, 0x0e, 0x00, 0x40, 0x61, 0x64, 0x6a, 0x61, 0x2a, 380x72, 0x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00,
330x00, 0x04, 0x5e, 0x00, 0x91, 0x2c, 0x00, 0x73, 0x6f, 0x00, 390x40, 0x61, 0x64, 0x6a, 0x61, 0x2a, 0x00, 0x04, 0x5e, 0x00,
340x74, 0x68, 0x61, 0x74, 0x44, 0x00, 0x02, 0x56, 0x00, 0xf2, 400x91, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x74, 0x68, 0x61, 0x74,
350x0d, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x69, 410x44, 0x00, 0x02, 0x56, 0x00, 0xf2, 0x0d, 0x66, 0x6f, 0x72,
360x6e, 0x67, 0x6c, 0x65, 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 420x6d, 0x00, 0x61, 0x00, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
370x64, 0x00, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x00, 0x49, 0x6c, 430x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x00, 0x6c, 0x6f,
380x00, 0x91, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 440x6f, 0x70, 0x2e, 0x00, 0x49, 0x6c, 0x00, 0x91, 0x72, 0x65,
390x67, 0xad, 0x00, 0x00, 0x44, 0x00, 0x20, 0x6d, 0x65, 0x79, 450x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0xad, 0x00, 0x00,
400x00, 0x00, 0x1c, 0x00, 0x03, 0x59, 0x00, 0x07, 0x55, 0x00, 460x44, 0x00, 0x20, 0x6d, 0x65, 0x79, 0x00, 0x00, 0x1c, 0x00,
410x81, 0x6f, 0x6f, 0x70, 0x00, 0x70, 0x61, 0x73, 0x73, 0x15, 470x03, 0x59, 0x00, 0x07, 0x55, 0x00, 0x81, 0x6f, 0x6f, 0x70,
420x00, 0xc0, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x77, 0x69, 480x00, 0x70, 0x61, 0x73, 0x73, 0x15, 0x00, 0xc0, 0x72, 0x6f,
430x6c, 0x6c, 0x00, 0x63, 0xa3, 0x00, 0xf2, 0x00, 0x69, 0x6e, 490x75, 0x67, 0x68, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x63,
440x00, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x73, 0x2c, 0x00, 500xa3, 0x00, 0xf2, 0x00, 0x69, 0x6e, 0x00, 0x63, 0x6f, 0x72,
450x61, 0x6e, 0x64, 0x4b, 0x00, 0x01, 0x1f, 0x00, 0xb7, 0x62, 510x6e, 0x65, 0x72, 0x73, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x4b,
460x65, 0x00, 0x73, 0x74, 0x72, 0x61, 0x69, 0x67, 0x68, 0x74, 520x00, 0x01, 0x1f, 0x00, 0xb7, 0x62, 0x65, 0x00, 0x73, 0x74,
470xd2, 0x00, 0x08, 0xd0, 0x00, 0x02, 0xb0, 0x00, 0x44, 0x2e, 530x72, 0x61, 0x69, 0x67, 0x68, 0x74, 0xd2, 0x00, 0x08, 0xd0,
480x00, 0x28, 0x41, 0x39, 0x00, 0x04, 0x7d, 0x00, 0x30, 0x63, 540x00, 0x02, 0xb0, 0x00, 0x44, 0x2e, 0x00, 0x28, 0x41, 0x39,
490x61, 0x6e, 0x40, 0x00, 0xf6, 0x03, 0x63, 0x6f, 0x6d, 0x70, 550x00, 0x04, 0x7d, 0x00, 0x30, 0x63, 0x61, 0x6e, 0x40, 0x00,
500x6c, 0x65, 0x74, 0x65, 0x6c, 0x79, 0x00, 0x65, 0x6d, 0x70, 560xf6, 0x03, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
510x74, 0x79, 0x00, 0x2d, 0x92, 0x00, 0x72, 0x64, 0x6f, 0x65, 570x6c, 0x79, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00, 0x2d,
520x73, 0x6e, 0x27, 0x74, 0x76, 0x01, 0x21, 0x74, 0x6f, 0xa2, 580x92, 0x00, 0x72, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74,
530x00, 0x05, 0xa0, 0x00, 0x53, 0x65, 0x76, 0x65, 0x72, 0x79, 590x76, 0x01, 0x21, 0x74, 0x6f, 0xa2, 0x00, 0x05, 0xa0, 0x00,
540x4e, 0x00, 0x6e, 0x2e, 0x29, 0x00, 0x00, 0x00, 0x53, 0xe2, 600x53, 0x65, 0x76, 0x65, 0x72, 0x79, 0x4e, 0x00, 0x6e, 0x2e,
550x00, 0x05, 0xc0, 0x00, 0x51, 0x62, 0x6c, 0x61, 0x63, 0x6b, 610x29, 0x00, 0x00, 0x00, 0x53, 0xe2, 0x00, 0x05, 0xc0, 0x00,
560xbd, 0x00, 0xb0, 0x77, 0x68, 0x69, 0x74, 0x65, 0x00, 0x63, 620x51, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0xbd, 0x00, 0xb0, 0x77,
570x69, 0x72, 0x63, 0x6c, 0x5b, 0x01, 0x80, 0x77, 0x68, 0x69, 630x68, 0x69, 0x74, 0x65, 0x00, 0x63, 0x69, 0x72, 0x63, 0x6c,
580x63, 0x68, 0x00, 0x61, 0x72, 0x45, 0x01, 0x1d, 0x75, 0x13, 640x5b, 0x01, 0x80, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x61,
590x01, 0xf3, 0x02, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x73, 0x61, 650x72, 0x45, 0x01, 0x1d, 0x75, 0x13, 0x01, 0xf3, 0x02, 0x6d,
600x74, 0x69, 0x73, 0x66, 0x79, 0x2e, 0x00, 0x00, 0x00, 0x41, 660x75, 0x73, 0x74, 0x00, 0x73, 0x61, 0x74, 0x69, 0x73, 0x66,
610x49, 0x00, 0x02, 0x3f, 0x00, 0x53, 0x00, 0x69, 0x6e, 0x00, 670x79, 0x2e, 0x00, 0x00, 0x00, 0x41, 0x49, 0x00, 0x02, 0x3f,
620x61, 0x6b, 0x00, 0x86, 0x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 680x00, 0x53, 0x00, 0x69, 0x6e, 0x00, 0x61, 0x6b, 0x00, 0x86,
630x61, 0x74, 0x43, 0x00, 0x14, 0x61, 0xc1, 0x01, 0x00, 0x14, 690x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x43, 0x00,
640x02, 0x13, 0x61, 0x42, 0x01, 0xdc, 0x2c, 0x00, 0x62, 0x75, 700x14, 0x61, 0xc1, 0x01, 0x00, 0x14, 0x02, 0x13, 0x61, 0x42,
650x74, 0x00, 0x6e, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0xad, 710x01, 0xdc, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x6e, 0x65,
660x00, 0x05, 0xf9, 0x01, 0x50, 0x74, 0x6f, 0x00, 0x69, 0x74, 720x69, 0x74, 0x68, 0x65, 0x72, 0xad, 0x00, 0x05, 0xf9, 0x01,
670x5e, 0x00, 0x05, 0x89, 0x00, 0x00, 0x43, 0x00, 0x35, 0x6c, 730x50, 0x74, 0x6f, 0x00, 0x69, 0x74, 0x5e, 0x00, 0x05, 0x89,
680x73, 0x6f, 0x48, 0x00, 0x02, 0x8d, 0x00, 0x08, 0xcc, 0x00, 740x00, 0x00, 0x43, 0x00, 0x35, 0x6c, 0x73, 0x6f, 0x48, 0x00,
690x0e, 0x81, 0x00, 0x19, 0x65, 0x80, 0x00, 0x05, 0xa8, 0x01, 750x02, 0x8d, 0x00, 0x08, 0xcc, 0x00, 0x0e, 0x81, 0x00, 0x19,
700x42, 0x65, 0x64, 0x67, 0x65, 0x87, 0x00, 0xcf, 0x61, 0x74, 760x65, 0x80, 0x00, 0x05, 0xa8, 0x01, 0x42, 0x65, 0x64, 0x67,
710x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 770x65, 0x87, 0x00, 0xcf, 0x61, 0x74, 0x20, 0x6c, 0x65, 0x61,
720x8c, 0x00, 0x1c, 0x07, 0x87, 0x00, 0xc0, 0x28, 0x49, 0x6e, 780x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x8c, 0x00, 0x1c, 0x07,
730x00, 0x62, 0x6f, 0x74, 0x68, 0x00, 0x63, 0x61, 0x73, 0x52, 790x87, 0x00, 0xc0, 0x28, 0x49, 0x6e, 0x00, 0x62, 0x6f, 0x74,
740x01, 0x22, 0x74, 0x68, 0x4c, 0x01, 0x50, 0x00, 0x6f, 0x6e, 800x68, 0x00, 0x63, 0x61, 0x73, 0x52, 0x01, 0x22, 0x74, 0x68,
750x6c, 0x79, 0x81, 0x01, 0x01, 0x7a, 0x00, 0x21, 0x6e, 0x73, 810x4c, 0x01, 0x50, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x81, 0x01,
760x19, 0x00, 0x3e, 0x74, 0x77, 0x6f, 0x63, 0x00, 0x70, 0x69, 820x01, 0x7a, 0x00, 0x21, 0x6e, 0x73, 0x19, 0x00, 0x3e, 0x74,
770x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5d, 0x00, 0x12, 0x2c, 830x77, 0x6f, 0x63, 0x00, 0x70, 0x69, 0x6e, 0x20, 0x74, 0x68,
780xbd, 0x00, 0x3f, 0x69, 0x73, 0x2c, 0xa9, 0x02, 0x0f, 0xf0, 840x65, 0x20, 0x5d, 0x00, 0x12, 0x2c, 0xbd, 0x00, 0x3f, 0x69,
790x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 0x61, 0x66, 0x74, 0x65, 850x73, 0x2c, 0xa9, 0x02, 0x0f, 0xf0, 0x00, 0x69, 0x6e, 0x74,
800x72, 0x00, 0x6c, 0x65, 0x61, 0x76, 0xeb, 0x02, 0x05, 0x7c, 860x6f, 0x00, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, 0x6c, 0x65,
810x00, 0x03, 0x17, 0x02, 0x2c, 0x00, 0x54, 0x45, 0x00, 0x23, 870x61, 0x76, 0xeb, 0x02, 0x05, 0x7c, 0x00, 0x03, 0x17, 0x02,
820x61, 0x72, 0x99, 0x00, 0x0c, 0x7e, 0x00, 0x01, 0xd3, 0x03, 880x2c, 0x00, 0x54, 0x45, 0x00, 0x23, 0x61, 0x72, 0x99, 0x00,
830x00, 0x1e, 0x00, 0x36, 0x6e, 0x6f, 0x74, 0xb6, 0x00, 0x21, 890x0c, 0x7e, 0x00, 0x01, 0xd3, 0x03, 0x00, 0x1e, 0x00, 0x36,
840x65, 0x64, 0x5b, 0x02, 0x60, 0x43, 0x72, 0x65, 0x64, 0x69, 900x6e, 0x6f, 0x74, 0xb6, 0x00, 0x21, 0x65, 0x64, 0x5b, 0x02,
850x74, 0x76, 0x03, 0xf1, 0x01, 0x00, 0x74, 0x68, 0x69, 0x73, 910x60, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x76, 0x03, 0xf1,
860x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 920x01, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a,
870x65, 0xef, 0x03, 0x60, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 930x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0xef, 0x03, 0x60,
880x4d, 0x02, 0x20, 0x6f, 0x00, 0xc4, 0x03, 0x00, 0x32, 0x01, 940x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x4d, 0x02, 0x20, 0x6f,
890xf1, 0x06, 0x60, 0x4d, 0x61, 0x73, 0x79, 0x75, 0x27, 0x2e, 950x00, 0xc4, 0x03, 0x00, 0x32, 0x01, 0xf1, 0x06, 0x60, 0x4d,
900x00, 0x5b, 0x31, 0x39, 0x5d, 0x00, 0x00, 0x00, 0x54, 0x68, 960x61, 0x73, 0x79, 0x75, 0x27, 0x2e, 0x00, 0x5b, 0x31, 0x39,
910x61, 0x6e, 0x6b, 0x2e, 0x00, 0xc1, 0x4a, 0x61, 0x6d, 0x65, 970x5d, 0x00, 0x00, 0x00, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x2e,
920x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x53, 0x00, 980x00, 0xc1, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61,
930x90, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 990x72, 0x76, 0x65, 0x79, 0x53, 0x00, 0x90, 0x61, 0x73, 0x73,
940x56, 0x03, 0x21, 0x74, 0x68, 0xc6, 0x00, 0x10, 0x69, 0x16, 1000x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x56, 0x03, 0x21, 0x74,
950x03, 0x90, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 1010x68, 0xc6, 0x00, 0x10, 0x69, 0x16, 0x03, 0x90, 0x6d, 0x65,
960x6e, 0x68, 0x01, 0x01, 0x48, 0x00, 0xc1, 0x68, 0x74, 0x74, 1020x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x68, 0x01, 0x01,
970x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x75, 1030x48, 0x00, 0xd1, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
980x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 1040x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x76, 0x00, 0xa2, 0x2e,
990x6e, 0x2f, 0x94, 0x00, 0x30, 0x73, 0x2f, 0x6d, 0x78, 0x00, 1050x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0x95,
1000xc1, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x28, 0x62, 0x65, 1060x00, 0x30, 0x73, 0x2f, 0x6d, 0x79, 0x00, 0x83, 0x2f, 0x00,
1010x77, 0x61, 0x72, 0xdc, 0x01, 0x50, 0x46, 0x6c, 0x61, 0x73, 1070x00, 0x00, 0x33, 0x36, 0x2e, 0x31, 0xd1, 0x04, 0x00, 0x08,
1020x68, 0xcb, 0x00, 0x43, 0x33, 0x36, 0x2e, 0x31, 0xd4, 0x04, 1080x03, 0x50, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0xcd, 0x00, 0x46,
1030x00, 0x1d, 0x03, 0x50, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0xe2, 1090x6c, 0x69, 0x63, 0x6b, 0x6a, 0x00, 0x40, 0x6c, 0x65, 0x66,
1040x00, 0x46, 0x6c, 0x69, 0x63, 0x6b, 0x7f, 0x00, 0x40, 0x6c, 1100x74, 0x0f, 0x02, 0x64, 0x74, 0x6f, 0x6e, 0x00, 0x6f, 0x6e,
1050x65, 0x66, 0x74, 0x24, 0x02, 0x64, 0x74, 0x6f, 0x6e, 0x00, 1110xdd, 0x04, 0x00, 0x26, 0x02, 0x05, 0xca, 0x04, 0x50, 0x61,
1060x6f, 0x6e, 0xf2, 0x04, 0x00, 0x3b, 0x02, 0x05, 0xdf, 0x04, 1120x00, 0x73, 0x65, 0x67, 0x8d, 0x00, 0x04, 0x26, 0x02, 0x01,
1070x50, 0x61, 0x00, 0x73, 0x65, 0x67, 0xa2, 0x00, 0x04, 0x3b, 1130x87, 0x01, 0x04, 0x89, 0x03, 0x01, 0x58, 0x01, 0x02, 0x56,
1080x02, 0x01, 0x9c, 0x01, 0x04, 0x9e, 0x03, 0x01, 0x6d, 0x01, 1140x02, 0x20, 0x6f, 0x72, 0x34, 0x00, 0x68, 0x72, 0x65, 0x6d,
1090x02, 0x6b, 0x02, 0x20, 0x6f, 0x72, 0x34, 0x00, 0x68, 0x72, 1150x6f, 0x76, 0x65, 0x36, 0x00, 0x00, 0xdb, 0x00, 0x10, 0x69,
1100x65, 0x6d, 0x6f, 0x76, 0x65, 0x36, 0x00, 0x00, 0xf0, 0x00, 1160xd7, 0x01, 0x01, 0x50, 0x00, 0x01, 0xcf, 0x00, 0x4f, 0x44,
1110x10, 0x69, 0xec, 0x01, 0x01, 0x50, 0x00, 0x01, 0xe4, 0x00, 1170x72, 0x61, 0x67, 0x85, 0x00, 0x03, 0x04, 0x58, 0x00, 0x00,
1120x4f, 0x44, 0x72, 0x61, 0x67, 0x85, 0x00, 0x03, 0x04, 0x58, 1180x40, 0x00, 0x47, 0x72, 0x69, 0x65, 0x73, 0x69, 0x05, 0x05,
1130x00, 0x00, 0x40, 0x00, 0x47, 0x72, 0x69, 0x65, 0x73, 0x7e, 1190x92, 0x00, 0x40, 0x6d, 0x6f, 0x72, 0x65, 0x79, 0x00, 0x00,
1140x05, 0x05, 0x92, 0x00, 0x40, 0x6d, 0x6f, 0x72, 0x65, 0x79, 1200xb3, 0x00, 0x1f, 0x65, 0x9e, 0x00, 0x02, 0x12, 0x69, 0x1b,
1150x00, 0x00, 0xb3, 0x00, 0x1f, 0x65, 0x9e, 0x00, 0x02, 0x12, 1210x00, 0xf0, 0x05, 0x67, 0x6f, 0x2e, 0x00, 0x41, 0x6c, 0x74,
1160x69, 0x1b, 0x00, 0xf0, 0x05, 0x67, 0x6f, 0x2e, 0x00, 0x41, 1220x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79,
1170x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 1230x2c, 0x00, 0x64, 0x7a, 0x00, 0xc1, 0x6f, 0x76, 0x65, 0x72,
1180x6c, 0x79, 0x2c, 0x00, 0x64, 0x7a, 0x00, 0xc1, 0x6f, 0x76, 1240x00, 0x61, 0x6e, 0x00, 0x65, 0x78, 0x69, 0x73, 0x26, 0x05,
1190x65, 0x72, 0x00, 0x61, 0x6e, 0x00, 0x65, 0x78, 0x69, 0x73, 1250x3b, 0x70, 0x61, 0x72, 0xdf, 0x00, 0x41, 0x6f, 0x00, 0x75,
1200x3b, 0x05, 0x3b, 0x70, 0x61, 0x72, 0xdf, 0x00, 0x41, 0x6f, 1260x6e, 0x6d, 0x00, 0x24, 0x69, 0x74, 0xda, 0x00, 0x03, 0x11,
1210x00, 0x75, 0x6e, 0x6d, 0x00, 0x24, 0x69, 0x74, 0xda, 0x00, 1270x00, 0x04, 0x2c, 0x00, 0x21, 0x69, 0x74, 0x53, 0x04, 0x72,
1220x03, 0x11, 0x00, 0x04, 0x2c, 0x00, 0x21, 0x69, 0x74, 0x68, 1280x74, 0x68, 0x65, 0x6e, 0x00, 0x67, 0x6f, 0x0f, 0x04, 0x60,
1230x04, 0x72, 0x74, 0x68, 0x65, 0x6e, 0x00, 0x67, 0x6f, 0x24, 1290x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x8e, 0x00, 0x54, 0x64,
1240x04, 0x60, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x8e, 0x00, 1300x69, 0x72, 0x65, 0x63, 0xbe, 0x01, 0x0b, 0x75, 0x01, 0x11,
1250x54, 0x64, 0x69, 0x72, 0x65, 0x63, 0xd3, 0x01, 0x0b, 0x75, 1310x72, 0x8b, 0x03, 0x0f, 0x76, 0x01, 0x06, 0x40, 0x6d, 0x61,
1260x01, 0x11, 0x72, 0xa0, 0x03, 0x0f, 0x76, 0x01, 0x06, 0x40, 1320x72, 0x6b, 0x5e, 0x00, 0x01, 0x30, 0x00, 0x84, 0x61, 0x00,
1270x6d, 0x61, 0x72, 0x6b, 0x5e, 0x00, 0x01, 0x30, 0x00, 0x84, 1330x63, 0x72, 0x6f, 0x73, 0x73, 0x2c, 0xe2, 0x03, 0x02, 0xe9,
1280x61, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x2c, 0xf7, 0x03, 1340x02, 0x61, 0x61, 0x74, 0x00, 0x79, 0x6f, 0x75, 0xb2, 0x02,
1290x02, 0xfe, 0x02, 0x61, 0x61, 0x74, 0x00, 0x79, 0x6f, 0x75, 1350x4a, 0x73, 0x75, 0x72, 0x65, 0x35, 0x05, 0x01, 0xc5, 0x02,
1300xc7, 0x02, 0x4a, 0x73, 0x75, 0x72, 0x65, 0x4a, 0x05, 0x01, 1360x2e, 0x67, 0x6f, 0xa3, 0x01, 0x92, 0x2e, 0x00, 0x28, 0x46,
1310xda, 0x02, 0x2e, 0x67, 0x6f, 0xa3, 0x01, 0x92, 0x2e, 0x00, 1370x6f, 0x72, 0x00, 0x69, 0x6e, 0x70, 0x02, 0x64, 0x2c, 0x00,
1320x28, 0x46, 0x6f, 0x72, 0x00, 0x69, 0x6e, 0x85, 0x02, 0x64, 1380x69, 0x66, 0x00, 0x79, 0xd8, 0x06, 0x73, 0x64, 0x65, 0x63,
1330x2c, 0x00, 0x69, 0x66, 0x00, 0x79, 0xed, 0x06, 0x73, 0x64, 1390x69, 0x64, 0x65, 0x64, 0x14, 0x05, 0x0f, 0x11, 0x04, 0x08,
1340x65, 0x63, 0x69, 0x64, 0x65, 0x64, 0x29, 0x05, 0x0f, 0x26, 1400x14, 0x61, 0x7a, 0x04, 0x00, 0x6b, 0x03, 0x21, 0x68, 0x61,
1350x04, 0x08, 0x14, 0x61, 0x8f, 0x04, 0x00, 0x80, 0x03, 0x21, 1410x97, 0x01, 0x2b, 0x62, 0x65, 0xe5, 0x04, 0x20, 0x64, 0x6f,
1360x68, 0x61, 0x97, 0x01, 0x2b, 0x62, 0x65, 0xfa, 0x04, 0x20, 1420xbb, 0x05, 0x83, 0x79, 0x65, 0x74, 0x00, 0x6b, 0x6e, 0x6f,
1370x64, 0x6f, 0xd0, 0x05, 0x83, 0x79, 0x65, 0x74, 0x00, 0x6b, 1430x77, 0x55, 0x00, 0x32, 0x77, 0x61, 0x79, 0xa5, 0x03, 0x01,
1380x6e, 0x6f, 0x77, 0x55, 0x00, 0x32, 0x77, 0x61, 0x79, 0xba, 1440x29, 0x00, 0x71, 0x00, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x2c,
1390x03, 0x01, 0x29, 0x00, 0x71, 0x00, 0x74, 0x75, 0x72, 0x6e, 1450x82, 0x00, 0x11, 0x6d, 0x12, 0x01, 0x01, 0xf9, 0x00, 0x00,
1400x73, 0x2c, 0x82, 0x00, 0x11, 0x6d, 0x12, 0x01, 0x01, 0xf9, 1460x21, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x2d, 0x00, 0x20, 0x69,
1410x00, 0x00, 0x21, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x2d, 0x00, 1470x74, 0x29, 0x06, 0x11, 0x27, 0xce, 0x00, 0x08, 0x0e, 0x01,
1420x20, 0x69, 0x74, 0x3e, 0x06, 0x11, 0x27, 0xce, 0x00, 0x08, 1480x01, 0x97, 0x03, 0x0b, 0xd9, 0x01, 0x32, 0x75, 0x73, 0x65,
1430x0e, 0x01, 0x01, 0xac, 0x03, 0x0b, 0xd9, 0x01, 0x32, 0x75, 1490x5d, 0x00, 0x91, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b,
1440x73, 0x65, 0x5d, 0x00, 0x91, 0x75, 0x72, 0x73, 0x6f, 0x72, 1500x65, 0x79, 0x9a, 0x00, 0x01, 0x8d, 0x02, 0x06, 0x18, 0x00,
1450x00, 0x6b, 0x65, 0x79, 0x9a, 0x00, 0x01, 0x8d, 0x02, 0x06, 1510x33, 0x2e, 0x00, 0x55, 0x28, 0x00, 0x41, 0x45, 0x6e, 0x74,
1460x18, 0x00, 0x33, 0x2e, 0x00, 0x55, 0x28, 0x00, 0x41, 0x45, 1520x65, 0x27, 0x00, 0x02, 0xc0, 0x00, 0x31, 0x67, 0x69, 0x6e,
1470x6e, 0x74, 0x65, 0x27, 0x00, 0x02, 0xc0, 0x00, 0x31, 0x67, 1530xcf, 0x01, 0x30, 0x65, 0x6e, 0x64, 0x15, 0x00, 0x70, 0x62,
1480x69, 0x6e, 0xcf, 0x01, 0x30, 0x65, 0x6e, 0x64, 0x15, 0x00, 1540x6f, 0x61, 0x72, 0x64, 0x00, 0x60, 0x2e, 0x02, 0x61, 0x27,
1490x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x60, 0x2e, 0x02, 1550x00, 0x6f, 0x70, 0x65, 0x72, 0x8a, 0x03, 0x16, 0x73, 0x3f,
1500x61, 0x27, 0x00, 0x6f, 0x70, 0x65, 0x72, 0x9f, 0x03, 0x16, 1560x00, 0x30, 0x53, 0x70, 0x61, 0x42, 0x01, 0x60, 0x45, 0x73,
1510x73, 0x3f, 0x00, 0x30, 0x53, 0x70, 0x61, 0x42, 0x01, 0x60, 1570x63, 0x61, 0x70, 0x65, 0x21, 0x02, 0x50, 0x42, 0x61, 0x63,
1520x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x21, 0x02, 0x50, 0x42, 1580x6b, 0x73, 0x15, 0x00, 0x05, 0x7b, 0x00, 0x10, 0x63, 0x65,
1530x61, 0x63, 0x6b, 0x73, 0x15, 0x00, 0x05, 0x7b, 0x00, 0x10, 1590x01, 0x11, 0x6c, 0x2e, 0x00, 0x00, 0x48, 0x00, 0xc2, 0x2e,
1540x63, 0x65, 0x01, 0x11, 0x6c, 0x2e, 0x00, 0x00, 0x48, 0x00, 1600x00, 0x4f, 0x72, 0x2c, 0x00, 0x68, 0x6f, 0x6c, 0x64, 0x00,
1550xc2, 0x2e, 0x00, 0x4f, 0x72, 0x2c, 0x00, 0x68, 0x6f, 0x6c, 1610x43, 0x91, 0x03, 0x00, 0x17, 0x01, 0x12, 0x6c, 0x1d, 0x00,
1560x64, 0x00, 0x43, 0x91, 0x03, 0x00, 0x17, 0x01, 0x12, 0x6c, 1620x37, 0x67, 0x69, 0x6e, 0x11, 0x03, 0x0b, 0xc4, 0x00, 0x55,
1570x1d, 0x00, 0x37, 0x67, 0x69, 0x6e, 0x11, 0x03, 0x0b, 0xc4, 1630x74, 0x6f, 0x67, 0x67, 0x6c, 0xe9, 0x02, 0x42, 0x73, 0x00,
1580x00, 0x55, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0xe9, 0x02, 0x42, 1640x61, 0x73, 0x35, 0x01, 0x00, 0xdb, 0x00, 0x04, 0x62, 0x08,
1590x73, 0x00, 0x61, 0x73, 0x35, 0x01, 0x00, 0xdb, 0x00, 0x04, 1650x05, 0x8d, 0x08, 0x60, 0x00, 0x00, 0x50, 0x72, 0x65, 0x73,
1600x77, 0x08, 0x05, 0xa2, 0x08, 0x60, 0x00, 0x00, 0x50, 0x72, 1660x1c, 0x08, 0x04, 0x67, 0x00, 0xe1, 0x2d, 0x53, 0x68, 0x69,
1610x65, 0x73, 0x31, 0x08, 0x04, 0x67, 0x00, 0xe1, 0x2d, 0x53, 1670x66, 0x74, 0x2d, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x6b, 0x65,
1620x68, 0x69, 0x66, 0x74, 0x2d, 0x61, 0x72, 0x72, 0x6f, 0x77, 1680x71, 0x08, 0x0b, 0x12, 0x00, 0x51, 0x73, 0x69, 0x6d, 0x75,
1630x6b, 0x65, 0x86, 0x08, 0x0b, 0x12, 0x00, 0x51, 0x73, 0x69, 1690x6c, 0x3e, 0x06, 0x12, 0x61, 0x8d, 0x03, 0x23, 0x6f, 0x72,
1640x6d, 0x75, 0x6c, 0x53, 0x06, 0x12, 0x61, 0x8d, 0x03, 0x23, 1700xa5, 0x02, 0x10, 0x63, 0xba, 0x02, 0x10, 0x2c, 0x50, 0x08,
1650x6f, 0x72, 0xa5, 0x02, 0x10, 0x63, 0xba, 0x02, 0x10, 0x2c, 1710x34, 0x70, 0x65, 0x63, 0x6b, 0x01, 0x12, 0x6f, 0x10, 0x06,
1660x65, 0x08, 0x34, 0x70, 0x65, 0x63, 0x6b, 0x01, 0x12, 0x6f, 1720x01, 0xb0, 0x02, 0x03, 0x1c, 0x06, 0x05, 0xef, 0x02, 0x04,
1670x25, 0x06, 0x01, 0xb0, 0x02, 0x03, 0x31, 0x06, 0x05, 0xef, 1730x3a, 0x02, 0x31, 0x6b, 0x65, 0x79, 0x20, 0x06, 0x22, 0x41,
1680x02, 0x04, 0x3a, 0x02, 0x31, 0x6b, 0x65, 0x79, 0x35, 0x06, 1740x6c, 0x02, 0x01, 0x21, 0x61, 0x63, 0x41, 0x01, 0xa0, 0x00,
1690x22, 0x41, 0x6c, 0x02, 0x01, 0x21, 0x61, 0x63, 0x41, 0x01, 1750x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x3a,
1700xa0, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 1760x00, 0x13, 0x73, 0x34, 0x00, 0x31, 0x32, 0x2e, 0x31, 0xcc,
1710x64, 0x3a, 0x00, 0x13, 0x73, 0x34, 0x00, 0x31, 0x32, 0x2e, 1770x02, 0x02, 0xe6, 0x06, 0x72, 0x76, 0x61, 0x69, 0x6c, 0x61,
1720x31, 0xcc, 0x02, 0x02, 0xfb, 0x06, 0x93, 0x76, 0x61, 0x69, 1780x62, 0x6c, 0xd8, 0x07, 0x43, 0x33, 0x36, 0x2e, 0x32, 0xc7,
1730x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0xc7, 0x04, 0x13, 0x32, 1790x04, 0xb1, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
1740xc7, 0x04, 0xb1, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 1800x72, 0x73, 0x20, 0x53, 0x05, 0x46, 0x65, 0x73, 0x65, 0x00,
1750x65, 0x72, 0x73, 0x20, 0x68, 0x05, 0x46, 0x65, 0x73, 0x65, 1810x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x51, 0x00,
1760x00, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x51, 1820x66, 0x72, 0x6f, 0x6d, 0x79, 0x00, 0xa0, 0x60, 0x43, 0x75,
1770x00, 0x66, 0x72, 0x6f, 0x6d, 0x79, 0x00, 0xa0, 0x60, 0x43, 1830x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0xc9, 0x01, 0x02,
1780x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0xc9, 0x01, 1840xa3, 0x00, 0x12, 0x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70,
1790x02, 0xa3, 0x00, 0x12, 0x6e, 0x1a, 0x00, 0xc0, 0x54, 0x79, 1850x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xab, 0x00, 0x91,
1800x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x2e, 0x00, 1860x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xf9,
1870x00, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x19, 0x07, 0x01,
1880x9c, 0x03, 0x19, 0x69, 0x62, 0x01, 0xa0, 0x44, 0x69, 0x66,
1890x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0xdd, 0x03, 0x03,
1900x5e, 0x05, 0x03, 0x08, 0x01, 0x05, 0x1a, 0x00, 0x03, 0x09,
1910x01, 0x30, 0x67, 0x65, 0x6e, 0x3e, 0x02, 0x23, 0x65, 0x64,
1920x38, 0x06, 0x11, 0x2e, 0xbb, 0x02, 0xa0, 0x6c, 0x6f, 0x77,
1930x20, 0x75, 0x6e, 0x73, 0x6f, 0x6c, 0x75, 0xad, 0x00, 0x42,
1940x00, 0x00, 0x49, 0x66, 0x5c, 0x06, 0x72, 0x69, 0x73, 0x00,
1950x73, 0x65, 0x74, 0x2c, 0x56, 0x04, 0x01, 0x3f, 0x00, 0x17,
1960x61, 0x6f, 0x09, 0x06, 0x4c, 0x00, 0x03, 0x77, 0x01, 0x11,
1970x73, 0x2a, 0x06, 0x20, 0x73, 0x74, 0x2e, 0x03, 0x13, 0x3a,
1980x15, 0x09, 0x07, 0x03, 0x07, 0x02, 0xfb, 0x03, 0x00, 0x6c,
1990x09, 0x80, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79,
2000x46, 0x00, 0x42, 0x70, 0x72, 0x6f, 0x76, 0xeb, 0x03, 0x04,
2010xc6, 0x09, 0x42, 0x68, 0x6f, 0x77, 0x6e, 0xde, 0x09, 0x07,
2020xb2, 0x00, 0x22, 0x6f, 0x72, 0x21, 0x00, 0x01, 0x13, 0x07,
2030x20, 0x68, 0x65, 0xc1, 0x04, 0x21, 0x68, 0x65, 0xba, 0x08,
2040x00, 0x25, 0x00, 0x03, 0x05, 0x07, 0x03, 0xc8, 0x09, 0x50,
2050x75, 0x6e, 0x69, 0x71, 0x75, 0xc6, 0x09, 0x71, 0x73, 0x6f,
2060x6c, 0x76, 0x65, 0x64, 0x2e, 0xa0, 0x01, 0x00, 0xcc, 0x00,
2070x82, 0x70, 0x65, 0x65, 0x64, 0x73, 0x00, 0x75, 0x70, 0xc8,
2080x00, 0x03, 0x5a, 0x00, 0x23, 0x69, 0x6f, 0x6e, 0x00, 0x10,
2090x61, 0x08, 0x01, 0xd1, 0x73, 0x00, 0x6d, 0x75, 0x63, 0x68,
2100x00, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x6e, 0x01, 0x04,
2110x53, 0x04, 0x40, 0x70, 0x6c, 0x61, 0x79, 0x4d, 0x00, 0x31,
2120x41, 0x74, 0x00, 0xab, 0x08, 0x01, 0x18, 0x04, 0x03, 0xca,
2130x00, 0x20, 0x65, 0x00, 0x3d, 0x01, 0x01, 0xcf, 0x01, 0x01,
2140xa6, 0x00, 0x20, 0x73, 0x74, 0x06, 0x00, 0x62, 0x61, 0x6c,
2150x77, 0x61, 0x79, 0x73, 0xd8, 0x05, 0x02, 0x89, 0x04, 0x00,
2160xb1, 0x00, 0x20, 0x65, 0x27, 0x14, 0x05, 0xa3, 0x00, 0x67,
2170x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x67, 0x08,
2180x15, 0x74, 0x01, 0x01, 0x02, 0xbf, 0x00, 0x02, 0xe8, 0x05,
2190x0b, 0x1b, 0x00, 0x05, 0x6f, 0x00, 0x00, 0x7d, 0x06, 0x32,
2200x65, 0x64, 0x75, 0xcc, 0x06, 0x71, 0x73, 0x74, 0x65, 0x70,
2210x00, 0x62, 0x79, 0x08, 0x00, 0x12, 0x2e, 0xa6, 0x02, 0x13,
2220x33, 0xa6, 0x02, 0x81, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70,
2230x72, 0x65, 0x08, 0x06, 0x21, 0x63, 0x65, 0xac, 0x02, 0x20,
2240x4f, 0x6e, 0xcc, 0x00, 0x10, 0x74, 0xb8, 0x0b, 0x13, 0x73,
2250x1d, 0x0a, 0x70, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x00,
2260x2e, 0x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0x9a, 0x02,
2270x16, 0x50, 0x12, 0x00, 0x0d, 0xb6, 0x02, 0x33, 0x47, 0x61,
2280x6d, 0xb6, 0x02, 0x02, 0xa5, 0x00, 0x22, 0x6c, 0x65, 0x0d,
2290x06, 0x64, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0e, 0x06,
2300x45, 0x73, 0x74, 0x79, 0x6c, 0xd8, 0x09, 0x01, 0x7d, 0x01,
2310x30, 0x64, 0x69, 0x73, 0x51, 0x01, 0x80, 0x2e, 0x00, 0x60,
2320x54, 0x72, 0x61, 0x64, 0x69, 0x54, 0x00, 0x53, 0x61, 0x6c,
2330x27, 0x00, 0x69, 0xb9, 0x02, 0xb0, 0x65, 0x66, 0x61, 0x75,
2340x6c, 0x74, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x6d, 0x06, 0x03,
2350xb3, 0x05, 0x05, 0x59, 0x06, 0x36, 0x72, 0x75, 0x6e, 0xcf,
2360x0c, 0x07, 0xcb, 0x0c, 0x01, 0x1e, 0x03, 0x05, 0xac, 0x0c,
2370x01, 0x42, 0x05, 0x33, 0x61, 0x63, 0x68, 0x04, 0x0a, 0x51,
2380x63, 0x63, 0x75, 0x70, 0x69, 0x56, 0x04, 0x04, 0x93, 0x09,
2390x71, 0x60, 0x4c, 0x6f, 0x6f, 0x70, 0x79, 0x2d, 0x9e, 0x00,
2400x11, 0x27, 0x4d, 0x0a, 0x36, 0x6e, 0x00, 0x61, 0xc9, 0x05,
2410x01, 0x7f, 0x00, 0x02, 0x22, 0x01, 0x52, 0x6c, 0x6f, 0x6f,
2420x6b, 0x73, 0xeb, 0x07, 0x51, 0x6c, 0x69, 0x6b, 0x65, 0x00,
2430x39, 0x00, 0x32, 0x00, 0x28, 0x63, 0xaf, 0x0d, 0x4f, 0x00,
2440x32, 0x33, 0x29, 0xa7, 0x00, 0x0e, 0x01, 0x9c, 0x00, 0x02,
2450x8c, 0x0c, 0x04, 0x9d, 0x00, 0x04, 0x24, 0x0a, 0x03, 0x52,
2460x0b, 0x01, 0xa2, 0x00, 0xc0, 0x79, 0x00, 0x76, 0x65, 0x72,
2470x74, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x00,
181}; 248};
182 249
183const unsigned short help_text_len = 2570; 250const unsigned short help_text_len = 3598;
184const unsigned short help_text_words = 475; 251const unsigned short help_text_words = 659;
252const bool help_valid = true;
185const char quick_help_text[] = "Draw a single closed loop, given clues about corner and straight squares."; 253const char quick_help_text[] = "Draw a single closed loop, given clues about corner and straight squares.";
diff --git a/apps/plugins/puzzles/help/pegs.c b/apps/plugins/puzzles/help/pegs.c
index 14cbbf7f94..32552a87fd 100644
--- a/apps/plugins/puzzles/help/pegs.c
+++ b/apps/plugins/puzzles/help/pegs.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,143 +6,147 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 72, TEXT_CENTER | C_RED }, 9 { 73, TEXT_CENTER | C_RED },
10 { 228, TEXT_CENTER | C_RED }, 10 { 229, TEXT_CENTER | C_RED },
11 { 245, TEXT_UNDERLINE },
12 { 246, TEXT_UNDERLINE }, 11 { 246, TEXT_UNDERLINE },
13 { 256, TEXT_UNDERLINE }, 12 { 247, TEXT_UNDERLINE },
13 { 257, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 1689 comp 1250 ratio 0.740083 level 3 saved 439 */ 17/* orig 1734 comp 1272 ratio 0.733564 level 4 saved 462 */
18const char help_text[] = { 18const char help_text[] = {
190xf0, 0x46, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xfb, 0x04, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x31, 0x36, 0x3a, 0x20, 0x50, 0x65, 0x67, 0x73, 0x20, 0x00, 200x31, 0x36, 0x3a, 0x20, 0x50, 0x65, 0x67, 0x73, 0x20, 0x00,
210x00, 0x00, 0x41, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 210x2d, 0x01, 0x00, 0xf0, 0x35, 0x00, 0x00, 0x00, 0x41, 0x00,
220x00, 0x6f, 0x66, 0x00, 0x70, 0x65, 0x67, 0x73, 0x00, 0x61, 220x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x00,
230x72, 0x65, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x00, 230x70, 0x65, 0x67, 0x73, 0x00, 0x61, 0x72, 0x65, 0x00, 0x70,
240x69, 0x6e, 0x00, 0x68, 0x6f, 0x6c, 0x65, 0x73, 0x00, 0x6f, 240x6c, 0x61, 0x63, 0x65, 0x64, 0x00, 0x69, 0x6e, 0x00, 0x68,
250x6e, 0x00, 0x61, 0x00, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x2e, 250x6f, 0x6c, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x00, 0x61, 0x00,
260x00, 0x59, 0x6f, 0x75, 0x00, 0x63, 0x61, 0x6e, 0x00, 0x72, 260x62, 0x6f, 0x61, 0x72, 0x64, 0x2e, 0x00, 0x59, 0x6f, 0x75,
270x65, 0x6d, 0x6f, 0x76, 0x65, 0x00, 0x61, 0x36, 0x00, 0xf1, 270x00, 0x63, 0x61, 0x6e, 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x76,
280x08, 0x00, 0x62, 0x79, 0x00, 0x6a, 0x75, 0x6d, 0x70, 0x69, 280x65, 0x00, 0x61, 0x36, 0x00, 0xf1, 0x08, 0x00, 0x62, 0x79,
290x6e, 0x67, 0x00, 0x61, 0x6e, 0x00, 0x61, 0x64, 0x6a, 0x61, 290x00, 0x6a, 0x75, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x61,
300x63, 0x65, 0x6e, 0x74, 0x1b, 0x00, 0xf0, 0x10, 0x6f, 0x76, 300x6e, 0x00, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74,
310x65, 0x72, 0x00, 0x69, 0x74, 0x00, 0x28, 0x68, 0x6f, 0x72, 310x1b, 0x00, 0xf0, 0x10, 0x6f, 0x76, 0x65, 0x72, 0x00, 0x69,
320x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x6c, 0x79, 0x00, 320x74, 0x00, 0x28, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e,
330x6f, 0x72, 0x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 330x74, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 0x00, 0x76,
340x00, 0xd1, 0x29, 0x00, 0x74, 0x6f, 0x00, 0x61, 0x00, 0x76, 340x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00, 0xd1, 0x29, 0x00,
350x61, 0x63, 0x61, 0x6e, 0x74, 0x73, 0x00, 0x00, 0x72, 0x00, 350x74, 0x6f, 0x00, 0x61, 0x00, 0x76, 0x61, 0x63, 0x61, 0x6e,
360xe1, 0x74, 0x68, 0x65, 0x00, 0x6f, 0x74, 0x68, 0x65, 0x72, 360x74, 0x73, 0x00, 0x00, 0x72, 0x00, 0xe1, 0x74, 0x68, 0x65,
370x00, 0x73, 0x69, 0x64, 0x65, 0x79, 0x00, 0x80, 0x72, 0x00, 370x00, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 0x73, 0x69, 0x64,
380x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x30, 0x00, 0x04, 0x80, 380x65, 0x79, 0x00, 0x80, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00,
390x00, 0xa0, 0x6c, 0x6c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x6f, 390x69, 0x73, 0x30, 0x00, 0x04, 0x80, 0x00, 0xa0, 0x6c, 0x6c,
400x6e, 0x65, 0xc3, 0x00, 0x00, 0x35, 0x00, 0x01, 0xc7, 0x00, 400x00, 0x62, 0x75, 0x74, 0x00, 0x6f, 0x6e, 0x65, 0xc3, 0x00,
410x51, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6b, 0x00, 0xf0, 0x25, 410x00, 0x35, 0x00, 0x01, 0xc7, 0x00, 0x51, 0x69, 0x6e, 0x69,
420x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2e, 0x00, 0x00, 420x74, 0x69, 0x6b, 0x00, 0xf0, 0x25, 0x70, 0x72, 0x65, 0x73,
430x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 430x65, 0x6e, 0x74, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69,
440x2c, 0x00, 0x62, 0x65, 0x73, 0x74, 0x00, 0x6b, 0x6e, 0x6f, 440x73, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 0x62, 0x65,
450x77, 0x6e, 0x00, 0x61, 0x73, 0x00, 0x60, 0x50, 0x65, 0x67, 450x73, 0x74, 0x00, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x00, 0x61,
460x00, 0x53, 0x6f, 0x6c, 0x69, 0x74, 0x61, 0x69, 0x72, 0x65, 460x73, 0x00, 0x60, 0x50, 0x65, 0x67, 0x00, 0x53, 0x6f, 0x6c,
470x27, 0x2c, 0x64, 0x00, 0x88, 0x70, 0x6f, 0x73, 0x73, 0x69, 470x69, 0x74, 0x61, 0x69, 0x72, 0x65, 0x27, 0x2c, 0x64, 0x00,
480x62, 0x6c, 0x79, 0x5b, 0x00, 0x30, 0x6f, 0x6c, 0x64, 0x38, 480x88, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79, 0x5b,
490x00, 0x61, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x4a, 0x00, 490x00, 0x30, 0x6f, 0x6c, 0x64, 0x38, 0x00, 0x61, 0x70, 0x75,
500xf2, 0x01, 0x73, 0x00, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x00, 500x7a, 0x7a, 0x6c, 0x65, 0x4a, 0x00, 0xf2, 0x01, 0x73, 0x00,
510x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6c, 0x79, 0x54, 0x00, 510x73, 0x74, 0x69, 0x6c, 0x6c, 0x00, 0x63, 0x6f, 0x6d, 0x6d,
520x00, 0x6d, 0x00, 0x42, 0x31, 0x36, 0x2e, 0x31, 0x67, 0x01, 520x6f, 0x6e, 0x6c, 0x79, 0x54, 0x00, 0x00, 0x6d, 0x00, 0x42,
530x71, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x01, 530x31, 0x36, 0x2e, 0x31, 0x78, 0x01, 0x90, 0x63, 0x6f, 0x6e,
540x36, 0x54, 0x6f, 0x00, 0x38, 0x01, 0x60, 0x2c, 0x00, 0x64, 540x74, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x83, 0x00, 0x26, 0x6f,
550x72, 0x61, 0x67, 0x1e, 0x01, 0x41, 0x77, 0x69, 0x74, 0x68, 550x00, 0x38, 0x01, 0x60, 0x2c, 0x00, 0x64, 0x72, 0x61, 0x67,
560x62, 0x00, 0xf1, 0x04, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 560x1e, 0x01, 0x41, 0x77, 0x69, 0x74, 0x68, 0x62, 0x00, 0xf1,
570x66, 0x72, 0x6f, 0x6d, 0x00, 0x69, 0x74, 0x73, 0x00, 0x63, 570x04, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 0x66, 0x72, 0x6f,
580x75, 0x72, 0x72, 0x4a, 0x01, 0x50, 0x6f, 0x73, 0x69, 0x74, 580x6d, 0x00, 0x69, 0x74, 0x73, 0x00, 0x63, 0x75, 0x72, 0x72,
590x69, 0x16, 0x01, 0x11, 0x6f, 0x18, 0x00, 0x55, 0x66, 0x69, 590x4a, 0x01, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x16, 0x01,
600x6e, 0x61, 0x6c, 0x16, 0x00, 0x32, 0x2e, 0x00, 0x49, 0xa0, 600x11, 0x6f, 0x18, 0x00, 0x55, 0x66, 0x69, 0x6e, 0x61, 0x6c,
610x00, 0x0a, 0x17, 0x00, 0x00, 0xc6, 0x00, 0xb3, 0x65, 0x78, 610x16, 0x00, 0x32, 0x2e, 0x00, 0x49, 0xa0, 0x00, 0x0a, 0x17,
620x61, 0x63, 0x74, 0x6c, 0x79, 0x00, 0x74, 0x77, 0x6f, 0xcd, 620x00, 0x00, 0xc6, 0x00, 0xb3, 0x65, 0x78, 0x61, 0x63, 0x74,
630x01, 0x42, 0x61, 0x77, 0x61, 0x79, 0x61, 0x00, 0x00, 0x32, 630x6c, 0x79, 0x00, 0x74, 0x77, 0x6f, 0xcd, 0x01, 0x42, 0x61,
640x00, 0x03, 0x28, 0x01, 0x05, 0x34, 0x00, 0x01, 0xfb, 0x00, 640x77, 0x61, 0x79, 0x61, 0x00, 0x00, 0x32, 0x00, 0x03, 0x28,
650x03, 0x76, 0x00, 0xd0, 0x6c, 0x79, 0x00, 0x75, 0x6e, 0x6f, 650x01, 0x05, 0x34, 0x00, 0x01, 0xfb, 0x00, 0x03, 0x76, 0x00,
660x63, 0x63, 0x75, 0x70, 0x69, 0x65, 0x64, 0xe4, 0x01, 0x03, 660xd0, 0x6c, 0x79, 0x00, 0x75, 0x6e, 0x6f, 0x63, 0x63, 0x75,
670xb5, 0x00, 0x30, 0x61, 0x6e, 0x64, 0x3c, 0x00, 0x20, 0x72, 670x70, 0x69, 0x65, 0x64, 0xe4, 0x01, 0x03, 0xb5, 0x00, 0x30,
680x65, 0x2c, 0x00, 0x02, 0x01, 0x02, 0x23, 0x69, 0x6e, 0x4e, 680x61, 0x6e, 0x64, 0x3c, 0x00, 0x20, 0x72, 0x65, 0x2c, 0x00,
690x00, 0x60, 0x74, 0x65, 0x72, 0x76, 0x65, 0x6e, 0x09, 0x02, 690x02, 0x01, 0x02, 0x23, 0x69, 0x6e, 0x4e, 0x00, 0x60, 0x74,
700x73, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x2c, 0xd6, 0x00, 700x65, 0x72, 0x76, 0x65, 0x6e, 0x09, 0x02, 0x73, 0x73, 0x71,
710x40, 0x76, 0x65, 0x00, 0x77, 0x28, 0x01, 0x10, 0x62, 0xa0, 710x75, 0x61, 0x72, 0x65, 0x2c, 0xd6, 0x00, 0x40, 0x76, 0x65,
720x01, 0x74, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x49, 720x00, 0x77, 0x28, 0x01, 0x10, 0x62, 0xa0, 0x01, 0x74, 0x72,
730x00, 0x09, 0x37, 0x00, 0x00, 0x4e, 0x00, 0x04, 0x2a, 0x00, 730x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x49, 0x00, 0x09, 0x37,
740x02, 0xe4, 0x01, 0x10, 0x64, 0x4e, 0x01, 0x12, 0x56, 0x1d, 740x00, 0x00, 0x4e, 0x00, 0x04, 0x2a, 0x00, 0x02, 0xe4, 0x01,
750x02, 0xe3, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x00, 0x77, 750x10, 0x64, 0x4e, 0x01, 0x12, 0x56, 0x1d, 0x02, 0xe3, 0x73,
760x68, 0x69, 0x63, 0x68, 0x00, 0x79, 0x8b, 0x02, 0x06, 0x51, 760x70, 0x61, 0x63, 0x65, 0x73, 0x00, 0x77, 0x68, 0x69, 0x63,
770x01, 0x00, 0x4a, 0x00, 0x11, 0x6f, 0xc3, 0x02, 0x62, 0x6d, 770x68, 0x00, 0x79, 0x8b, 0x02, 0x07, 0x89, 0x02, 0x41, 0x69,
780x61, 0x72, 0x6b, 0x65, 0x64, 0x58, 0x01, 0x01, 0xf8, 0x00, 780x6e, 0x74, 0x6f, 0xc3, 0x02, 0x62, 0x6d, 0x61, 0x72, 0x6b,
790x32, 0x2e, 0x00, 0x41, 0x3e, 0x00, 0x02, 0x14, 0x00, 0x21, 790x65, 0x64, 0x58, 0x01, 0x01, 0xf8, 0x00, 0x32, 0x2e, 0x00,
800x6e, 0x6f, 0x30, 0x00, 0x00, 0x82, 0x00, 0x12, 0x6e, 0x1a, 800x41, 0x3e, 0x00, 0x02, 0x14, 0x00, 0x21, 0x6e, 0x6f, 0x30,
810x01, 0x00, 0xcd, 0x00, 0xf0, 0x02, 0x6e, 0x6f, 0x74, 0x00, 810x00, 0x00, 0x82, 0x00, 0x12, 0x6e, 0x1a, 0x01, 0x00, 0xcd,
820x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 820x00, 0xf0, 0x02, 0x6e, 0x6f, 0x74, 0x00, 0x61, 0x76, 0x61,
830x66, 0x6f, 0x72, 0x5c, 0x00, 0x01, 0xd6, 0x02, 0x10, 0x74, 830x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x66, 0x6f, 0x72,
840x6a, 0x02, 0x10, 0x3a, 0xab, 0x01, 0x00, 0xf4, 0x00, 0xa7, 840x5c, 0x00, 0x01, 0xd6, 0x02, 0x10, 0x74, 0x6a, 0x02, 0x10,
850x6e, 0x00, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x63, 0x6c, 0x65, 850x3a, 0xab, 0x01, 0x00, 0xf4, 0x00, 0xa7, 0x6e, 0x00, 0x6f,
860x8b, 0x00, 0xf1, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x77, 860x62, 0x73, 0x74, 0x61, 0x63, 0x6c, 0x65, 0x8b, 0x00, 0xf1,
870x6f, 0x72, 0x6b, 0x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0xb7, 870x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x77, 0x6f, 0x72, 0x6b,
880x00, 0x04, 0x2e, 0x03, 0x50, 0x61, 0x6c, 0x73, 0x6f, 0x00, 880x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0xb7, 0x00, 0x04, 0x2e,
890xda, 0x01, 0x00, 0xef, 0x00, 0xa1, 0x63, 0x75, 0x72, 0x73, 890x03, 0x50, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0xda, 0x01, 0x00,
900x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xca, 0x02, 0x04, 0x10, 900xef, 0x00, 0xa1, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00,
910x02, 0x05, 0xb6, 0x01, 0x83, 0x6e, 0x64, 0x69, 0x63, 0x61, 910x6b, 0x65, 0x79, 0xca, 0x02, 0x04, 0x10, 0x02, 0x05, 0xb6,
920x74, 0x6f, 0x72, 0x48, 0x00, 0x01, 0x34, 0x00, 0x03, 0x7e, 920x01, 0x83, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
930x03, 0x50, 0x50, 0x72, 0x65, 0x73, 0x73, 0x91, 0x00, 0x00, 930x48, 0x00, 0x01, 0x34, 0x00, 0x03, 0x7e, 0x03, 0x50, 0x50,
940x14, 0x00, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x48, 940x72, 0x65, 0x73, 0x73, 0x91, 0x00, 0x00, 0x14, 0x00, 0x60,
950x00, 0x00, 0x86, 0x00, 0x22, 0x6c, 0x65, 0x6c, 0x03, 0x03, 950x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x48, 0x00, 0x00, 0x86,
960xa5, 0x01, 0x64, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0xb8, 960x00, 0x22, 0x6c, 0x65, 0x6c, 0x03, 0x03, 0xa5, 0x01, 0x64,
970x01, 0x06, 0x73, 0x00, 0x12, 0x2c, 0x5e, 0x01, 0x00, 0xaf, 970x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0xb8, 0x01, 0x06, 0x73,
980x03, 0x04, 0x2e, 0x03, 0x02, 0xbe, 0x01, 0x81, 0x61, 0x74, 980x00, 0x12, 0x2c, 0x5e, 0x01, 0x00, 0xaf, 0x03, 0x04, 0x2e,
990x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x84, 0x00, 0x32, 0x28, 990x03, 0x02, 0xbe, 0x01, 0x81, 0x61, 0x74, 0x00, 0x64, 0x69,
1000x69, 0x66, 0x13, 0x00, 0x01, 0xe2, 0x01, 0x51, 0x6c, 0x65, 1000x72, 0x65, 0x63, 0x84, 0x00, 0x32, 0x28, 0x69, 0x66, 0x13,
1010x67, 0x61, 0x6c, 0xa8, 0x00, 0x10, 0x29, 0xd5, 0x00, 0x41, 1010x00, 0x01, 0xe2, 0x01, 0x51, 0x6c, 0x65, 0x67, 0x61, 0x6c,
1020x28, 0x41, 0x6c, 0x6c, 0x3c, 0x00, 0x11, 0x61, 0x2d, 0x00, 1020xa8, 0x00, 0x10, 0x29, 0xd5, 0x00, 0x41, 0x28, 0x41, 0x6c,
1030x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 1030x6c, 0x3c, 0x00, 0x11, 0x61, 0x2d, 0x00, 0x92, 0x73, 0x00,
1040x33, 0x04, 0x13, 0x73, 0x43, 0x00, 0x31, 0x32, 0x2e, 0x31, 1040x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x33, 0x04, 0x13,
1050x8a, 0x01, 0x01, 0xfb, 0x00, 0x05, 0x55, 0x01, 0x22, 0x2e, 1050x73, 0x43, 0x00, 0x31, 0x32, 0x2e, 0x31, 0x8a, 0x01, 0x01,
1060x29, 0x1b, 0x03, 0x12, 0x32, 0x1b, 0x03, 0x92, 0x70, 0x61, 1060xfb, 0x00, 0x05, 0x55, 0x01, 0x22, 0x2e, 0x29, 0x1b, 0x03,
1070x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1d, 0x03, 0x56, 1070x12, 0x32, 0x1b, 0x03, 0x92, 0x70, 0x61, 0x72, 0x61, 0x6d,
1080x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x40, 0x00, 1080x65, 0x74, 0x65, 0x72, 0x1d, 0x03, 0x56, 0x68, 0x65, 0x73,
1090x06, 0x90, 0x01, 0x04, 0xb5, 0x02, 0xe1, 0x60, 0x43, 0x75, 1090x65, 0x00, 0x14, 0x00, 0x02, 0x40, 0x00, 0x06, 0x90, 0x01,
1100x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 1100x04, 0xb5, 0x02, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f,
1110x70, 0x6e, 0x00, 0x03, 0x31, 0x04, 0xb0, 0x60, 0x54, 0x79, 1110x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6e, 0x00,
1120x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xaa, 0x00, 1120x03, 0x31, 0x04, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27,
1130xf1, 0x04, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 1130x00, 0x6d, 0x65, 0x6e, 0x75, 0xaa, 0x00, 0xf1, 0x04, 0x57,
1140x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x00, 0x53, 0x69, 1140x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x69, 0x67,
1150x7a, 0xcc, 0x03, 0x36, 0x67, 0x72, 0x69, 0xdf, 0x04, 0x00, 1150x68, 0x74, 0x00, 0x00, 0x00, 0x53, 0x69, 0x7a, 0xcc, 0x03,
1160x29, 0x00, 0x10, 0x42, 0x5f, 0x01, 0x93, 0x20, 0x74, 0x79, 1160x36, 0x67, 0x72, 0x69, 0xdf, 0x04, 0x00, 0x29, 0x00, 0x10,
1170x70, 0x65, 0x00, 0x00, 0x00, 0x43, 0xb0, 0x03, 0x41, 0x00, 1170x42, 0x5f, 0x01, 0x93, 0x20, 0x74, 0x79, 0x70, 0x65, 0x00,
1180x77, 0x68, 0x65, 0x81, 0x04, 0x00, 0xde, 0x01, 0x00, 0x8b, 1180x00, 0x00, 0x43, 0xb0, 0x03, 0x41, 0x00, 0x77, 0x68, 0x65,
1190x00, 0x45, 0x67, 0x69, 0x76, 0x65, 0x0b, 0x05, 0x00, 0x48, 1190x81, 0x04, 0x00, 0xde, 0x01, 0x00, 0x8b, 0x00, 0x45, 0x67,
1200x00, 0x70, 0x61, 0x00, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x0e, 1200x69, 0x76, 0x65, 0x0b, 0x05, 0x00, 0x48, 0x00, 0x70, 0x61,
1210x00, 0x50, 0x73, 0x68, 0x61, 0x70, 0x65, 0xd8, 0x04, 0xf3, 1210x00, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x0e, 0x00, 0x50, 0x73,
1220x04, 0x61, 0x00, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c, 1220x68, 0x61, 0x70, 0x65, 0xd8, 0x04, 0xf3, 0x04, 0x61, 0x00,
1230x79, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 1230x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c, 0x79, 0x00, 0x67,
1240x1e, 0x00, 0x10, 0x2e, 0xe1, 0x00, 0x01, 0x8a, 0x03, 0x0a, 1240x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x1e, 0x00, 0x10,
1250x36, 0x00, 0x08, 0x71, 0x03, 0x61, 0x73, 0x75, 0x70, 0x70, 1250x2e, 0xe1, 0x00, 0x01, 0x8a, 0x03, 0x0a, 0x36, 0x00, 0x08,
1260x6f, 0x72, 0x1d, 0x03, 0x10, 0x72, 0xe9, 0x00, 0x51, 0x72, 1260x71, 0x03, 0x61, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x1d,
1270x6f, 0x73, 0x73, 0x27, 0xa7, 0x02, 0xb1, 0x60, 0x4f, 0x63, 1270x03, 0x10, 0x72, 0xe9, 0x00, 0xf1, 0x09, 0x72, 0x6f, 0x73,
1280x74, 0x61, 0x67, 0x6f, 0x6e, 0x27, 0x00, 0x28, 0x53, 0x01, 1280x73, 0x27, 0x00, 0x28, 0x69, 0x6e, 0x00, 0x76, 0x61, 0x72,
1290x0a, 0x72, 0x04, 0x00, 0xc6, 0x04, 0x00, 0x01, 0x01, 0x71, 1290x69, 0x6f, 0x75, 0x73, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x73,
1300x45, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x32, 0x00, 0xd1, 1300x29, 0xba, 0x02, 0x92, 0x60, 0x4f, 0x63, 0x74, 0x61, 0x67,
1310x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x61, 0x6e, 0x00, 0x74, 1310x6f, 0x6e, 0x27, 0x5a, 0x00, 0x41, 0x37, 0x78, 0x37, 0x00,
1320x72, 0x61, 0x64, 0x5c, 0x02, 0x23, 0x61, 0x6c, 0xbc, 0x00, 1320x31, 0x00, 0x01, 0x23, 0x05, 0x00, 0x6b, 0x00, 0x31, 0x72,
1330xc0, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x00, 0x72, 1330x61, 0x64, 0x54, 0x02, 0xa3, 0x61, 0x6c, 0x00, 0x45, 0x6e,
1340x65, 0x73, 0x70, 0xb0, 0x01, 0xa0, 0x76, 0x65, 0x6c, 0x79, 1340x67, 0x6c, 0x69, 0x73, 0x68, 0xbc, 0x00, 0x62, 0x6c, 0x61,
1350x29, 0x2e, 0x00, 0x53, 0x65, 0x6c, 0x0e, 0x00, 0x51, 0x6e, 1350x79, 0x6f, 0x75, 0x74, 0x37, 0x00, 0x03, 0x45, 0x00, 0x0f,
1360x67, 0x00, 0x60, 0x52, 0xc5, 0x00, 0x12, 0x27, 0x2e, 0x02, 1360x35, 0x00, 0x01, 0x60, 0x46, 0x72, 0x65, 0x6e, 0x63, 0x68,
1370x00, 0xf9, 0x00, 0x02, 0x06, 0x01, 0x61, 0x00, 0x64, 0x69, 1370x02, 0x05, 0x50, 0x2e, 0x00, 0x53, 0x65, 0x6c, 0xda, 0x01,
1380x66, 0x66, 0x65, 0x94, 0x04, 0x26, 0x62, 0x6f, 0xf8, 0x00, 1380x51, 0x6e, 0x67, 0x00, 0x60, 0x52, 0xe1, 0x00, 0x12, 0x27,
1390xc0, 0x65, 0x76, 0x65, 0x72, 0x79, 0x00, 0x74, 0x69, 0x6d, 1390x4a, 0x02, 0x00, 0x15, 0x01, 0x02, 0x22, 0x01, 0x61, 0x00,
1400x65, 0x00, 0x28, 0x8f, 0x05, 0x61, 0x61, 0x6c, 0x77, 0x61, 1400x64, 0x69, 0x66, 0x66, 0x65, 0xb0, 0x04, 0x26, 0x62, 0x6f,
1410x79, 0x73, 0x3b, 0x05, 0x04, 0x47, 0x02, 0x02, 0xa7, 0x00, 1410x14, 0x01, 0xc0, 0x65, 0x76, 0x65, 0x72, 0x79, 0x00, 0x74,
1420x51, 0x74, 0x6f, 0x00, 0x68, 0x61, 0xf0, 0x02, 0xb0, 0x73, 1420x69, 0x6d, 0x65, 0x00, 0x28, 0xab, 0x05, 0x61, 0x61, 0x6c,
1430x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x00, 1430x77, 0x61, 0x79, 0x73, 0x57, 0x05, 0x04, 0x63, 0x02, 0x02,
1440x89, 0x05, 0x51, 0x74, 0x6f, 0x00, 0x68, 0x61, 0x0c, 0x03,
1450xb0, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29,
1460x2e, 0x00,
144}; 147};
145 148
146const unsigned short help_text_len = 1689; 149const unsigned short help_text_len = 1734;
147const unsigned short help_text_words = 318; 150const unsigned short help_text_words = 326;
151const bool help_valid = true;
148const char quick_help_text[] = "Jump pegs over each other to remove all but one."; 152const char quick_help_text[] = "Jump pegs over each other to remove all but one.";
diff --git a/apps/plugins/puzzles/help/range.c b/apps/plugins/puzzles/help/range.c
index 1ce92c05d1..65495dc3c1 100644
--- a/apps/plugins/puzzles/help/range.c
+++ b/apps/plugins/puzzles/help/range.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,157 +6,169 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 212, TEXT_CENTER | C_RED }, 9 { 213, TEXT_CENTER | C_RED },
10 { 245, TEXT_UNDERLINE }, 10 { 246, TEXT_UNDERLINE },
11 { 339, TEXT_CENTER | C_RED }, 11 { 340, TEXT_CENTER | C_RED },
12 { 356, TEXT_UNDERLINE },
13 { 357, TEXT_UNDERLINE }, 12 { 357, TEXT_UNDERLINE },
13 { 358, TEXT_UNDERLINE },
14 { 368, TEXT_CENTER | C_RED },
14 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
15}; 16};
16 17
17/* orig 2036 comp 1387 ratio 0.681238 level 4 saved 649 */ 18/* orig 2223 comp 1486 ratio 0.668466 level 10 saved 737 */
18const char help_text[] = { 19const char help_text[] = {
190xf4, 0x26, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x33, 0x35, 0x3a, 0x20, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x20, 210x33, 0x35, 0x3a, 0x20, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x20,
210x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 220x00, 0x2d, 0x01, 0x00, 0xf4, 0x14, 0x00, 0x00, 0x00, 0x59,
220x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 230x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
230x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x3b, 240x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71,
240x00, 0x73, 0x6f, 0x6d, 0x65, 0x0e, 0x00, 0xf0, 0x02, 0x00, 250x75, 0x61, 0x72, 0x65, 0x73, 0x3b, 0x00, 0x73, 0x6f, 0x6d,
250x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 260x65, 0x0e, 0x00, 0xf0, 0x02, 0x00, 0x63, 0x6f, 0x6e, 0x74,
260x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x3a, 0x00, 0xf0, 0x00, 270x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
270x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 0x73, 0x00, 0x74, 280x73, 0x2e, 0x3a, 0x00, 0xf0, 0x00, 0x72, 0x00, 0x6a, 0x6f,
280x6f, 0x00, 0x63, 0x6f, 0x6c, 0x11, 0x00, 0x01, 0x34, 0x00, 290x62, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x63, 0x6f,
290x56, 0x6f, 0x66, 0x00, 0x74, 0x68, 0x3b, 0x00, 0xf3, 0x28, 300x6c, 0x11, 0x00, 0x01, 0x34, 0x00, 0x56, 0x6f, 0x66, 0x00,
300x62, 0x6c, 0x61, 0x63, 0x6b, 0x2c, 0x00, 0x73, 0x75, 0x63, 310x74, 0x68, 0x3b, 0x00, 0xf3, 0x28, 0x62, 0x6c, 0x61, 0x63,
310x68, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x73, 0x65, 0x76, 320x6b, 0x2c, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00, 0x74, 0x68,
320x65, 0x72, 0x61, 0x6c, 0x00, 0x63, 0x72, 0x69, 0x74, 0x65, 330x61, 0x74, 0x00, 0x73, 0x65, 0x76, 0x65, 0x72, 0x61, 0x6c,
330x72, 0x69, 0x61, 0x00, 0x61, 0x72, 0x65, 0x00, 0x73, 0x61, 340x00, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x00,
340x74, 0x69, 0x73, 0x66, 0x69, 0x65, 0x64, 0x3a, 0x00, 0x00, 350x61, 0x72, 0x65, 0x00, 0x73, 0x61, 0x74, 0x69, 0x73, 0x66,
350x00, 0x2d, 0x00, 0x6e, 0x6f, 0x40, 0x00, 0x73, 0x00, 0x77, 360x69, 0x65, 0x64, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x6e,
360x69, 0x74, 0x68, 0x00, 0x61, 0x79, 0x00, 0x00, 0x6e, 0x00, 370x6f, 0x40, 0x00, 0x73, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00,
370x02, 0x6b, 0x00, 0x22, 0x65, 0x64, 0x59, 0x00, 0x14, 0x2e, 380x61, 0x79, 0x00, 0x00, 0x6e, 0x00, 0x02, 0x6b, 0x00, 0x22,
380x2f, 0x00, 0x32, 0x74, 0x77, 0x6f, 0x12, 0x00, 0x05, 0x79, 390x65, 0x64, 0x59, 0x00, 0x14, 0x2e, 0x2f, 0x00, 0x32, 0x74,
390x00, 0x00, 0x3e, 0x00, 0xf0, 0x00, 0x61, 0x64, 0x6a, 0x61, 400x77, 0x6f, 0x12, 0x00, 0x05, 0x79, 0x00, 0x00, 0x3e, 0x00,
400x63, 0x65, 0x6e, 0x74, 0x00, 0x28, 0x68, 0x6f, 0x72, 0x69, 410xf0, 0x00, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74,
410x7a, 0xc6, 0x00, 0xd0, 0x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 420x00, 0x28, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0xc6, 0x00, 0xd0,
420x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00, 0x12, 430x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 0x00, 0x76, 0x65, 0x72,
430x29, 0x44, 0x00, 0x71, 0x66, 0x6f, 0x72, 0x00, 0x61, 0x6e, 440x74, 0x69, 0x63, 0x0e, 0x00, 0x12, 0x29, 0x44, 0x00, 0x71,
440x79, 0x49, 0x00, 0x45, 0x77, 0x68, 0x69, 0x74, 0xc2, 0x00, 450x66, 0x6f, 0x72, 0x00, 0x61, 0x6e, 0x79, 0x49, 0x00, 0x45,
450x10, 0x2c, 0xcf, 0x00, 0x20, 0x72, 0x65, 0x7c, 0x00, 0xe0, 460x77, 0x68, 0x69, 0x74, 0xc2, 0x00, 0x10, 0x2c, 0xcf, 0x00,
460x61, 0x00, 0x70, 0x61, 0x74, 0x68, 0x00, 0x62, 0x65, 0x74, 470x20, 0x72, 0x65, 0x7c, 0x00, 0xe0, 0x61, 0x00, 0x70, 0x61,
470x77, 0x65, 0x65, 0x6e, 0x18, 0x00, 0xca, 0x6d, 0x00, 0x75, 480x74, 0x68, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e,
480x73, 0x69, 0x6e, 0x67, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x37, 490x18, 0x00, 0xca, 0x6d, 0x00, 0x75, 0x73, 0x69, 0x6e, 0x67,
490x00, 0x06, 0x56, 0x00, 0x4f, 0x65, 0x61, 0x63, 0x68, 0xcf, 500x00, 0x6f, 0x6e, 0x6c, 0x79, 0x37, 0x00, 0x06, 0x56, 0x00,
500x00, 0x02, 0x12, 0x2c, 0x11, 0x01, 0x03, 0xdc, 0x00, 0x71, 510x4f, 0x65, 0x61, 0x63, 0x68, 0xcf, 0x00, 0x02, 0x12, 0x2c,
510x64, 0x65, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x3d, 0x01, 0x54, 520x11, 0x01, 0x03, 0xdc, 0x00, 0x71, 0x64, 0x65, 0x6e, 0x6f,
520x74, 0x6f, 0x74, 0x61, 0x6c, 0x19, 0x00, 0x2a, 0x6f, 0x66, 530x74, 0x65, 0x73, 0x3d, 0x01, 0x54, 0x74, 0x6f, 0x74, 0x61,
530x5a, 0x00, 0x20, 0x00, 0x72, 0x52, 0x00, 0x93, 0x61, 0x62, 540x6c, 0x19, 0x00, 0x2a, 0x6f, 0x66, 0x5a, 0x00, 0x20, 0x00,
540x6c, 0x65, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x56, 0x01, 0x02, 550x72, 0x52, 0x00, 0x93, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x66,
550x60, 0x00, 0x20, 0x67, 0x6f, 0x8e, 0x00, 0xf1, 0x03, 0x69, 560x72, 0x6f, 0x6d, 0x56, 0x01, 0x02, 0x60, 0x00, 0x20, 0x67,
560x6e, 0x00, 0x61, 0x00, 0x73, 0x74, 0x72, 0x61, 0x69, 0x67, 570x6f, 0x8e, 0x00, 0xf1, 0x03, 0x69, 0x6e, 0x00, 0x61, 0x00,
570x68, 0x74, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x13, 0x00, 0x36, 580x73, 0x74, 0x72, 0x61, 0x69, 0x67, 0x68, 0x74, 0x00, 0x6c,
580x6e, 0x79, 0x00, 0x07, 0x01, 0x08, 0x05, 0x01, 0xf0, 0x06, 590x69, 0x6e, 0x65, 0x13, 0x00, 0x36, 0x6e, 0x79, 0x00, 0x07,
590x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 600x01, 0x08, 0x05, 0x01, 0xf0, 0x06, 0x00, 0x64, 0x69, 0x72,
600x00, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x00, 0x68, 0x69, 0x74, 610x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x75, 0x6e, 0x74,
610x74, 0x49, 0x00, 0x51, 0x61, 0x00, 0x77, 0x61, 0x6c, 0x2b, 620x69, 0x6c, 0x00, 0x68, 0x69, 0x74, 0x74, 0x49, 0x00, 0x51,
620x00, 0x19, 0x61, 0x5e, 0x01, 0x17, 0x3b, 0xe3, 0x01, 0x02, 630x61, 0x00, 0x77, 0x61, 0x6c, 0x2b, 0x00, 0x19, 0x61, 0x5e,
630xd4, 0x00, 0x00, 0x10, 0x00, 0x06, 0xa5, 0x01, 0x80, 0x69, 640x01, 0x17, 0x3b, 0xe3, 0x01, 0x02, 0xd4, 0x00, 0x00, 0x10,
640x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x77, 0x00, 0x06, 650x00, 0x06, 0xa5, 0x01, 0x80, 0x69, 0x6e, 0x63, 0x6c, 0x75,
650xd0, 0x00, 0x51, 0x28, 0x6f, 0x6e, 0x63, 0x65, 0x6f, 0x01, 660x64, 0x65, 0x64, 0x77, 0x00, 0x06, 0xd0, 0x00, 0x51, 0x28,
660xf4, 0x00, 0x46, 0x6f, 0x72, 0x00, 0x69, 0x6e, 0x73, 0x74, 670x6f, 0x6e, 0x63, 0x65, 0x6f, 0x01, 0xf4, 0x00, 0x46, 0x6f,
670x61, 0x6e, 0x63, 0x65, 0x2c, 0x00, 0x61, 0x4a, 0x00, 0x03, 680x72, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
680x67, 0x02, 0x00, 0x7a, 0x00, 0x07, 0x50, 0x00, 0x82, 0x6f, 690x2c, 0x00, 0x61, 0x4a, 0x00, 0x03, 0x67, 0x02, 0x00, 0x7a,
690x6e, 0x65, 0x00, 0x6d, 0x75, 0x73, 0x74, 0xab, 0x02, 0x10, 700x00, 0x07, 0x50, 0x00, 0x82, 0x6f, 0x6e, 0x65, 0x00, 0x6d,
700x66, 0x69, 0x02, 0x0b, 0xea, 0x01, 0xf1, 0x04, 0x73, 0x00, 710x75, 0x73, 0x74, 0xab, 0x02, 0x10, 0x66, 0x69, 0x02, 0x0b,
710x69, 0x74, 0x73, 0x00, 0x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 720xea, 0x01, 0xf1, 0x04, 0x73, 0x00, 0x69, 0x74, 0x73, 0x00,
720x6f, 0x75, 0x72, 0x73, 0x00, 0x62, 0x79, 0x41, 0x00, 0x44, 730x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x75, 0x72, 0x73,
730x6c, 0x61, 0x73, 0x74, 0x68, 0x02, 0x70, 0x6f, 0x6e, 0x3b, 740x00, 0x62, 0x79, 0x41, 0x00, 0x44, 0x6c, 0x61, 0x73, 0x74,
740x00, 0x62, 0x75, 0x74, 0x18, 0x00, 0xe1, 0x6e, 0x00, 0x69, 750x68, 0x02, 0x70, 0x6f, 0x6e, 0x3b, 0x00, 0x62, 0x75, 0x74,
750x74, 0x27, 0x73, 0x00, 0x69, 0x6d, 0x70, 0x6f, 0x73, 0x73, 760x18, 0x00, 0xe1, 0x6e, 0x00, 0x69, 0x74, 0x27, 0x73, 0x00,
760x69, 0x48, 0x01, 0x00, 0x90, 0x00, 0x10, 0x74, 0xca, 0x02, 770x69, 0x6d, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x48, 0x01, 0x00,
770x11, 0x62, 0x86, 0x00, 0x60, 0x6e, 0x65, 0x63, 0x74, 0x65, 780x90, 0x00, 0x10, 0x74, 0xca, 0x02, 0x11, 0x62, 0x86, 0x00,
780x64, 0x10, 0x00, 0x00, 0x35, 0x01, 0x79, 0x6f, 0x75, 0x74, 790x60, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x00, 0x00,
790x73, 0x69, 0x64, 0x65, 0x86, 0x01, 0x10, 0x2c, 0x0e, 0x00, 800x35, 0x01, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x69, 0x64, 0x65,
800x84, 0x63, 0x68, 0x00, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0xb7, 810x86, 0x01, 0x10, 0x2c, 0x0e, 0x00, 0x84, 0x63, 0x68, 0x00,
810x01, 0x51, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x37, 0x00, 0x0a, 820x76, 0x69, 0x6f, 0x6c, 0x61, 0xb7, 0x01, 0x51, 0x73, 0x65,
820x7a, 0x00, 0x49, 0x2e, 0x00, 0x53, 0x6f, 0xd4, 0x02, 0x25, 830x63, 0x6f, 0x6e, 0x37, 0x00, 0x0a, 0x7a, 0x00, 0x49, 0x2e,
830x6c, 0x6c, 0x53, 0x03, 0x0a, 0xe9, 0x00, 0x00, 0x1d, 0x01, 840x00, 0x53, 0x6f, 0xd4, 0x02, 0x25, 0x6c, 0x6c, 0x53, 0x03,
840x61, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x94, 0x00, 0xa0, 850x0a, 0xe9, 0x00, 0x00, 0x1d, 0x01, 0x61, 0x43, 0x72, 0x65,
850x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 860x64, 0x69, 0x74, 0x94, 0x00, 0xa0, 0x74, 0x68, 0x69, 0x73,
860xdb, 0x01, 0x11, 0x65, 0x6c, 0x03, 0x60, 0x4e, 0x69, 0x6b, 870x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0xdb, 0x01, 0x11, 0x65,
870x6f, 0x6c, 0x69, 0x80, 0x00, 0x12, 0x6f, 0x12, 0x01, 0xa0, 880x6c, 0x03, 0x60, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x80,
880x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x6c, 0x79, 0x00, 890x00, 0x12, 0x6f, 0x12, 0x01, 0xa0, 0x76, 0x61, 0x72, 0x69,
890xd0, 0x02, 0x00, 0x75, 0x01, 0xd2, 0x74, 0x00, 0x60, 0x4b, 900x6f, 0x75, 0x73, 0x6c, 0x79, 0x00, 0xd0, 0x02, 0x20, 0x65,
900x75, 0x72, 0x6f, 0x64, 0x6f, 0x6b, 0x6f, 0x27, 0x2c, 0x0c, 910x64, 0xca, 0x00, 0xb2, 0x60, 0x4b, 0x75, 0x72, 0x6f, 0x64,
910x00, 0x50, 0x6d, 0x61, 0x73, 0x75, 0x27, 0xc9, 0x01, 0x24, 920x6f, 0x6b, 0x6f, 0x27, 0x2c, 0x0c, 0x00, 0x50, 0x6d, 0x61,
920x60, 0x57, 0xce, 0x02, 0x11, 0x42, 0x45, 0x01, 0xc0, 0x43, 930x73, 0x75, 0x27, 0xc9, 0x01, 0x24, 0x60, 0x57, 0xce, 0x02,
930x65, 0x6c, 0x6c, 0x73, 0x27, 0x2e, 0x00, 0x5b, 0x31, 0x38, 940x11, 0x42, 0x45, 0x01, 0xc0, 0x43, 0x65, 0x6c, 0x6c, 0x73,
940x5d, 0x7e, 0x00, 0x01, 0x1d, 0x04, 0x32, 0x00, 0x77, 0x61, 950x27, 0x2e, 0x00, 0x5b, 0x31, 0x38, 0x5d, 0x7e, 0x00, 0x01,
950xf5, 0x03, 0x43, 0x72, 0x69, 0x62, 0x75, 0x0a, 0x01, 0x22, 960x2f, 0x04, 0x32, 0x00, 0x77, 0x61, 0xf5, 0x03, 0x43, 0x72,
960x74, 0x68, 0x7e, 0x03, 0x13, 0x6c, 0x2a, 0x02, 0xf0, 0x01, 970x69, 0x62, 0x75, 0x0a, 0x01, 0x22, 0x74, 0x68, 0x7e, 0x03,
970x62, 0x79, 0x00, 0x4a, 0x6f, 0x6e, 0x61, 0x73, 0x00, 0x4b, 980x13, 0x6c, 0x2a, 0x02, 0xf0, 0x01, 0x62, 0x79, 0x00, 0x4a,
980x6f, 0x65, 0x6c, 0x6b, 0x65, 0x72, 0x3d, 0x00, 0x00, 0x45, 990x6f, 0x6e, 0x61, 0x73, 0x00, 0x4b, 0x6f, 0x65, 0x6c, 0x6b,
990x00, 0xd1, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 1000x65, 0x72, 0x3d, 0x00, 0x00, 0x45, 0x00, 0xe1, 0x00, 0x68,
1000x77, 0x77, 0x77, 0x2e, 0x6e, 0xac, 0x00, 0xa2, 0x2e, 0x63, 1010x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
1010x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0xcb, 0x00, 1020x2e, 0x6e, 0xad, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a,
1020x30, 0x73, 0x2f, 0x77, 0x84, 0x00, 0x41, 0x5f, 0x69, 0x73, 1030x70, 0x2f, 0x65, 0x6e, 0x2f, 0xcc, 0x00, 0x33, 0x73, 0x2f,
1030x5f, 0xc9, 0x01, 0x20, 0x5f, 0x63, 0x84, 0x00, 0xc3, 0x2e, 1040x6b, 0x9f, 0x00, 0x83, 0x2f, 0x00, 0x00, 0x00, 0x33, 0x35,
1040x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x33, 0x35, 0x2e, 1050x2e, 0x31, 0xa6, 0x04, 0x01, 0x73, 0x00, 0x40, 0x6f, 0x6c,
1050x31, 0xa3, 0x04, 0x01, 0x82, 0x00, 0x30, 0x6f, 0x6c, 0x73, 1060x73, 0x20, 0x07, 0x01, 0x46, 0x6c, 0x69, 0x63, 0x6b, 0x5d,
1060xac, 0x04, 0x56, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x6c, 0x02, 1070x02, 0x40, 0x6c, 0x65, 0x66, 0x74, 0xc1, 0x01, 0x30, 0x74,
1070x40, 0x6c, 0x65, 0x66, 0x74, 0xd0, 0x01, 0x30, 0x74, 0x6f, 1080x6f, 0x6e, 0x8e, 0x00, 0x56, 0x70, 0x61, 0x69, 0x6e, 0x74,
1080x6e, 0x9d, 0x00, 0x56, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x49, 1090x3a, 0x02, 0x03, 0x66, 0x04, 0x26, 0x6f, 0x72, 0x31, 0x00,
1090x02, 0x03, 0x75, 0x04, 0x26, 0x6f, 0x72, 0x31, 0x00, 0x11, 1100x11, 0x72, 0xfd, 0x02, 0x06, 0x32, 0x00, 0x6b, 0x6d, 0x61,
1100x72, 0x0c, 0x03, 0x06, 0x32, 0x00, 0x6b, 0x6d, 0x61, 0x72, 1110x72, 0x6b, 0x00, 0x61, 0x89, 0x03, 0x21, 0x64, 0x6f, 0xf6,
1110x6b, 0x00, 0x61, 0x98, 0x03, 0x21, 0x64, 0x6f, 0x05, 0x02, 1120x01, 0x82, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
1120x82, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5b, 1130x4c, 0x03, 0x32, 0x79, 0x6f, 0x75, 0x95, 0x04, 0x30, 0x75,
1130x03, 0x32, 0x79, 0x6f, 0x75, 0xa4, 0x04, 0x10, 0x75, 0x38, 1140x72, 0x65, 0x4d, 0x01, 0xa0, 0x73, 0x68, 0x6f, 0x75, 0x6c,
1140x01, 0xc0, 0x74, 0x00, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 1150x64, 0x00, 0x6e, 0x6f, 0x74, 0x1f, 0x02, 0x01, 0x7a, 0x00,
1150x00, 0x6e, 0x6f, 0x74, 0x2e, 0x02, 0x01, 0x7a, 0x00, 0x06, 1160x06, 0x80, 0x04, 0x50, 0x52, 0x65, 0x70, 0x65, 0x61, 0x10,
1160x8f, 0x04, 0x50, 0x52, 0x65, 0x70, 0x65, 0x61, 0x10, 0x00, 1170x00, 0x10, 0x63, 0xb0, 0x00, 0x00, 0xbb, 0x02, 0x01, 0x5b,
1170x10, 0x63, 0xb0, 0x00, 0x00, 0xca, 0x02, 0x01, 0x5b, 0x00, 1180x00, 0x20, 0x65, 0x69, 0x36, 0x04, 0x04, 0x7f, 0x00, 0x02,
1180x20, 0x65, 0x69, 0x45, 0x04, 0x04, 0x7f, 0x00, 0x02, 0x01, 1190xf2, 0x01, 0x48, 0x79, 0x63, 0x6c, 0x65, 0x39, 0x03, 0x72,
1190x02, 0x48, 0x79, 0x63, 0x6c, 0x65, 0x48, 0x03, 0x62, 0x74, 1200x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x22, 0x03, 0x55,
1200x68, 0x72, 0x6f, 0x75, 0x67, 0xae, 0x00, 0x65, 0x74, 0x68, 1210x68, 0x72, 0x65, 0x65, 0x00, 0x91, 0x02, 0x21, 0x73, 0x74,
1210x72, 0x65, 0x65, 0x00, 0xa0, 0x02, 0x21, 0x73, 0x74, 0x64, 1220x55, 0x02, 0x30, 0x28, 0x66, 0x69, 0xd2, 0x01, 0x10, 0x2c,
1220x02, 0x30, 0x28, 0x66, 0x69, 0xe1, 0x01, 0x10, 0x2c, 0xa8, 1230xa8, 0x00, 0x00, 0x64, 0x00, 0x00, 0x4e, 0x04, 0x50, 0x6d,
1230x00, 0x00, 0x64, 0x00, 0x00, 0x5d, 0x04, 0x50, 0x6d, 0x70, 1240x70, 0x74, 0x79, 0x29, 0x59, 0x03, 0x50, 0x6f, 0x70, 0x70,
1240x74, 0x79, 0x29, 0x68, 0x03, 0x50, 0x6f, 0x70, 0x70, 0x6f, 1250x6f, 0x73, 0x92, 0x02, 0x05, 0xbe, 0x03, 0x23, 0x73, 0x2e,
1250x73, 0xa1, 0x02, 0x05, 0xcd, 0x03, 0x23, 0x73, 0x2e, 0xe4, 1260xd5, 0x05, 0xb2, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c, 0x73,
1260x05, 0xb2, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c, 0x73, 0x6f, 1270x6f, 0x00, 0x75, 0x73, 0x72, 0x00, 0xb1, 0x63, 0x75, 0x72,
1270x00, 0x75, 0x73, 0x72, 0x00, 0xa1, 0x63, 0x75, 0x72, 0x73, 1280x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x0c, 0x01,
1280x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x53, 0x02, 0x20, 0x6d, 1290x10, 0x6f, 0xf5, 0x05, 0x51, 0x72, 0x6f, 0x75, 0x6e, 0x64,
1290x6f, 0x04, 0x06, 0x51, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x1f, 1300x1f, 0x00, 0x01, 0xfe, 0x05, 0x05, 0xb9, 0x04, 0x41, 0x50,
1300x00, 0x01, 0x0d, 0x06, 0x05, 0xc8, 0x04, 0x41, 0x50, 0x72, 1310x72, 0x65, 0x73, 0xd6, 0x04, 0x94, 0x52, 0x65, 0x74, 0x75,
1310x65, 0x73, 0xe5, 0x04, 0x94, 0x52, 0x65, 0x74, 0x75, 0x72, 1320x72, 0x6e, 0x00, 0x64, 0x6f, 0xe0, 0x02, 0x30, 0x61, 0x6d,
1320x6e, 0x00, 0x64, 0x6f, 0xef, 0x02, 0x30, 0x61, 0x6d, 0x65, 1330x65, 0x6e, 0x03, 0x0a, 0xeb, 0x00, 0x0b, 0x9e, 0x01, 0x01,
1330x7d, 0x03, 0x0a, 0xeb, 0x00, 0x0b, 0x9e, 0x01, 0x01, 0x29, 1340x1a, 0x03, 0x44, 0x6c, 0x65, 0x00, 0x70, 0x46, 0x00, 0x5e,
1340x03, 0x44, 0x6c, 0x65, 0x00, 0x70, 0x46, 0x00, 0x5e, 0x53, 1350x53, 0x70, 0x61, 0x63, 0x65, 0x45, 0x00, 0x1a, 0x61, 0xa2,
1350x70, 0x61, 0x63, 0x65, 0x45, 0x00, 0x1a, 0x61, 0xa2, 0x01, 1360x01, 0x01, 0x54, 0x00, 0x59, 0x2e, 0x00, 0x4d, 0x6f, 0x76,
1360x01, 0x54, 0x00, 0x59, 0x2e, 0x00, 0x4d, 0x6f, 0x76, 0x59, 1370x59, 0x00, 0x08, 0xb9, 0x00, 0x02, 0x58, 0x00, 0x41, 0x68,
1370x00, 0x08, 0xb9, 0x00, 0x02, 0x58, 0x00, 0x41, 0x68, 0x6f, 1380x6f, 0x6c, 0x64, 0x57, 0x00, 0x42, 0x68, 0x69, 0x66, 0x74,
1380x6c, 0x64, 0x57, 0x00, 0x42, 0x68, 0x69, 0x66, 0x74, 0x5a, 1390x5a, 0x01, 0x22, 0x70, 0x6c, 0x62, 0x00, 0x21, 0x74, 0x73,
1390x01, 0x22, 0x70, 0x6c, 0x62, 0x00, 0x21, 0x74, 0x73, 0xf7, 1400xe8, 0x04, 0x25, 0x6c, 0x6c, 0x1b, 0x04, 0x01, 0xd0, 0x01,
1400x04, 0x25, 0x6c, 0x6c, 0x2a, 0x04, 0x01, 0xd0, 0x01, 0x00, 1410x00, 0x6c, 0x01, 0x00, 0xf2, 0x00, 0x14, 0x64, 0x72, 0x01,
1410x6c, 0x01, 0x00, 0xf2, 0x00, 0x14, 0x64, 0x72, 0x01, 0x00, 1420x00, 0x27, 0x01, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x62, 0x00,
1420x27, 0x01, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x62, 0x00, 0x12, 1430x12, 0x61, 0x3b, 0x01, 0x82, 0x00, 0x64, 0x65, 0x73, 0x63,
1430x61, 0x3b, 0x01, 0x82, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 1440x72, 0x69, 0x62, 0xb5, 0x04, 0x13, 0x73, 0xe5, 0x02, 0x32,
1440x69, 0x62, 0xc4, 0x04, 0x13, 0x73, 0xf4, 0x02, 0x32, 0x32, 1450x32, 0x2e, 0x31, 0x49, 0x06, 0x00, 0x4d, 0x01, 0x50, 0x61,
1450x2e, 0x31, 0x58, 0x06, 0x00, 0x4d, 0x01, 0x50, 0x61, 0x76, 1460x76, 0x61, 0x69, 0x6c, 0x78, 0x05, 0x22, 0x2e, 0x29, 0xb7,
1460x61, 0x69, 0x6c, 0x87, 0x05, 0x22, 0x2e, 0x29, 0xb7, 0x02, 1470x02, 0x13, 0x32, 0xb7, 0x02, 0x91, 0x70, 0x61, 0x72, 0x61,
1470x13, 0x32, 0xb7, 0x02, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 1480x6d, 0x65, 0x74, 0x65, 0x72, 0xb9, 0x02, 0x66, 0x54, 0x68,
1480x65, 0x74, 0x65, 0x72, 0xb9, 0x02, 0x66, 0x54, 0x68, 0x65, 1490x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04,
1490x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 1500x3c, 0x00, 0x04, 0xb4, 0x05, 0xf1, 0x01, 0x65, 0x00, 0x60,
1500x00, 0x04, 0xc3, 0x05, 0xf1, 0x01, 0x65, 0x00, 0x60, 0x43, 1510x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
1510x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 1520x00, 0x6f, 0x70, 0x6f, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00,
1520x6f, 0x70, 0x6f, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0, 1530xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e,
1530x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 1540x75, 0xab, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c,
1540xab, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 1550x00, 0x48, 0x65, 0x30, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69,
1550x48, 0x65, 0x30, 0x01, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 1560x7a, 0x67, 0x07, 0x01, 0xb6, 0x01, 0x28, 0x69, 0x6e, 0x72,
1560x76, 0x07, 0x01, 0xb6, 0x01, 0x00, 0xad, 0x00, 0x80, 0x71, 1570x06, 0x43, 0x33, 0x35, 0x2e, 0x33, 0x95, 0x00, 0xf1, 0x00,
1570x75, 0x61, 0x72, 0x65, 0x73, 0x2e, 0x00, 1580x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65,
1590x72, 0x65, 0x6e, 0x63, 0x65, 0x9b, 0x00, 0x20, 0x4f, 0x6e,
1600x2c, 0x01, 0x63, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x38,
1610x06, 0x70, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e,
1620x00, 0x17, 0x00, 0x2e, 0x00, 0x12, 0x2c, 0x89, 0x00, 0x16,
1630x50, 0x12, 0x00, 0x0d, 0xa5, 0x00, 0x33, 0x47, 0x61, 0x6d,
1640xa5, 0x00, 0x02, 0x86, 0x01, 0x22, 0x6c, 0x65, 0x3b, 0x03,
1650x60, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3c, 0x03, 0x02,
1660x23, 0x05, 0x46, 0x77, 0x61, 0x79, 0x00, 0x6b, 0x02, 0x20,
1670x6d, 0x6f, 0x94, 0x02, 0x02, 0xf5, 0x01, 0x80, 0x73, 0x00,
1680x77, 0x6f, 0x72, 0x6b, 0x2e, 0x00,
158}; 169};
159 170
160const unsigned short help_text_len = 2036; 171const unsigned short help_text_len = 2223;
161const unsigned short help_text_words = 365; 172const unsigned short help_text_words = 395;
173const bool help_valid = true;
162const char quick_help_text[] = "Place black squares to limit the visible distance from each numbered cell."; 174const char quick_help_text[] = "Place black squares to limit the visible distance from each numbered cell.";
diff --git a/apps/plugins/puzzles/help/rect.c b/apps/plugins/puzzles/help/rect.c
index e86a77091b..dfd597eb0f 100644
--- a/apps/plugins/puzzles/help/rect.c
+++ b/apps/plugins/puzzles/help/rect.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,257 +6,257 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 144, TEXT_CENTER | C_RED }, 9 { 142, TEXT_CENTER | C_RED },
10 { 311, TEXT_CENTER | C_RED }, 10 { 309, TEXT_CENTER | C_RED },
11 { 328, TEXT_UNDERLINE }, 11 { 326, TEXT_UNDERLINE },
12 { 329, TEXT_UNDERLINE }, 12 { 327, TEXT_UNDERLINE },
13 { 339, TEXT_UNDERLINE }, 13 { 337, TEXT_UNDERLINE },
14 { 381, TEXT_UNDERLINE }, 14 { 379, TEXT_UNDERLINE },
15 { 512, TEXT_UNDERLINE }, 15 { 510, TEXT_UNDERLINE },
16 { 537, TEXT_UNDERLINE }, 16 { 535, TEXT_UNDERLINE },
17 { 582, TEXT_UNDERLINE }, 17 { 580, TEXT_UNDERLINE },
18 LAST_STYLE_ITEM 18 LAST_STYLE_ITEM
19}; 19};
20 20
21/* orig 3535 comp 2341 ratio 0.662235 level 10 saved 1194 */ 21/* orig 3536 comp 2332 ratio 0.659502 level 10 saved 1204 */
22const char help_text[] = { 22const char help_text[] = {
230xf0, 0x50, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 230xff, 0x09, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
240x38, 0x3a, 0x20, 0x52, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 240x38, 0x3a, 0x20, 0x52, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67,
250x6c, 0x65, 0x73, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 250x6c, 0x65, 0x73, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x01, 0xf0,
260x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 260x3a, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61,
270x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 270x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00,
280x72, 0x65, 0x73, 0x2c, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 280x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73,
290x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x00, 0x77, 0x72, 290x2c, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x6e, 0x75, 0x6d,
300x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x69, 0x6e, 0x00, 0x73, 300x62, 0x65, 0x72, 0x73, 0x00, 0x77, 0x72, 0x69, 0x74, 0x74,
310x6f, 0x6d, 0x65, 0x00, 0x28, 0x62, 0x75, 0x74, 0x00, 0x6e, 310x65, 0x6e, 0x00, 0x69, 0x6e, 0x00, 0x73, 0x6f, 0x6d, 0x65,
320x6f, 0x74, 0x00, 0x61, 0x6c, 0x6c, 0x29, 0x37, 0x00, 0x34, 320x00, 0x28, 0x62, 0x75, 0x74, 0x00, 0x6e, 0x6f, 0x74, 0x00,
330x74, 0x68, 0x65, 0x3b, 0x00, 0x10, 0x2e, 0x57, 0x00, 0xf1, 330x61, 0x6c, 0x6c, 0x29, 0x37, 0x00, 0x34, 0x74, 0x68, 0x65,
340x07, 0x72, 0x00, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x69, 0x73, 340x3b, 0x00, 0x10, 0x2e, 0x57, 0x00, 0xf1, 0x07, 0x72, 0x00,
350x00, 0x74, 0x6f, 0x00, 0x73, 0x75, 0x62, 0x64, 0x69, 0x76, 350x74, 0x61, 0x73, 0x6b, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f,
360x69, 0x64, 0x65, 0x27, 0x00, 0x01, 0x6a, 0x00, 0x65, 0x69, 360x00, 0x73, 0x75, 0x62, 0x64, 0x69, 0x76, 0x69, 0x64, 0x65,
370x6e, 0x74, 0x6f, 0x00, 0x72, 0x8d, 0x00, 0x00, 0x43, 0x00, 370x27, 0x00, 0x01, 0x6a, 0x00, 0x65, 0x69, 0x6e, 0x74, 0x6f,
380xb0, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x73, 380x00, 0x72, 0xa3, 0x00, 0x00, 0x43, 0x00, 0xb0, 0x76, 0x61,
390x69, 0x7a, 0x80, 0x00, 0xf6, 0x04, 0x73, 0x75, 0x63, 0x68, 390x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x73, 0x69, 0x7a, 0x80,
400x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x28, 0x61, 0x29, 0x00, 400x00, 0xf6, 0x04, 0x73, 0x75, 0x63, 0x68, 0x00, 0x74, 0x68,
410x65, 0x76, 0x65, 0x72, 0x79, 0x31, 0x00, 0xf3, 0x06, 0x00, 410x61, 0x74, 0x00, 0x28, 0x61, 0x29, 0x00, 0x65, 0x76, 0x65,
420x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x00, 0x65, 420x72, 0x79, 0x31, 0x00, 0xf3, 0x06, 0x00, 0x63, 0x6f, 0x6e,
430x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 0x65, 430x74, 0x61, 0x69, 0x6e, 0x73, 0x00, 0x65, 0x78, 0x61, 0x63,
440xae, 0x00, 0x23, 0x65, 0x64, 0x8a, 0x00, 0x91, 0x2c, 0x00, 440x74, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 0x65, 0xae, 0x00, 0x23,
450x61, 0x6e, 0x64, 0x00, 0x28, 0x62, 0x29, 0x77, 0x00, 0x40, 450x65, 0x64, 0x8a, 0x00, 0x91, 0x2c, 0x00, 0x61, 0x6e, 0x64,
460x61, 0x72, 0x65, 0x61, 0x67, 0x00, 0x47, 0x65, 0x61, 0x63, 460x00, 0x28, 0x62, 0x29, 0x77, 0x00, 0x40, 0x61, 0x72, 0x65,
470x68, 0x49, 0x00, 0x80, 0x69, 0x73, 0x00, 0x65, 0x71, 0x75, 470x61, 0x67, 0x00, 0x47, 0x65, 0x61, 0x63, 0x68, 0x49, 0x00,
480x61, 0x6c, 0xa8, 0x00, 0x24, 0x74, 0x68, 0x44, 0x00, 0x08, 480x80, 0x69, 0x73, 0x00, 0x65, 0x71, 0x75, 0x61, 0x6c, 0xa8,
490xf1, 0x00, 0x3c, 0x69, 0x74, 0x73, 0x5a, 0x00, 0xf4, 0x0e, 490x00, 0x24, 0x74, 0x68, 0x44, 0x00, 0x08, 0xf1, 0x00, 0x3c,
500x2e, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 500x69, 0x74, 0x73, 0x5a, 0x00, 0xf4, 0x0e, 0x2e, 0x00, 0x00,
510x00, 0x66, 0x6f, 0x72, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 510x00, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x66, 0x6f,
520x67, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6f, 0x65, 0x73, 0x4a, 520x72, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d,
530x00, 0xf0, 0x18, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x65, 0x73, 530x65, 0x00, 0x67, 0x6f, 0x65, 0x73, 0x4a, 0x00, 0xf0, 0x18,
540x65, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x6d, 540x4a, 0x61, 0x70, 0x61, 0x6e, 0x65, 0x73, 0x65, 0x00, 0x70,
550x61, 0x67, 0x61, 0x7a, 0x69, 0x6e, 0x65, 0x00, 0x4e, 0x69, 550x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x6d, 0x61, 0x67, 0x61,
560x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x33, 0x5d, 0x3b, 0x00, 560x7a, 0x69, 0x6e, 0x65, 0x00, 0x4e, 0x69, 0x6b, 0x6f, 0x6c,
570x49, 0x27, 0x7e, 0x01, 0xf2, 0x14, 0x6c, 0x73, 0x6f, 0x00, 570x69, 0x00, 0x5b, 0x33, 0x5d, 0x3b, 0x00, 0x49, 0x27, 0x7e,
580x73, 0x65, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x50, 0x61, 0x6c, 580x01, 0xf2, 0x14, 0x6c, 0x73, 0x6f, 0x00, 0x73, 0x65, 0x65,
590x6d, 0x00, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 590x6e, 0x00, 0x61, 0x00, 0x50, 0x61, 0x6c, 0x6d, 0x00, 0x69,
600x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x61, 0x74, 0x00, 600x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74,
610x50, 0x44, 0x00, 0xfa, 0x03, 0x50, 0x61, 0x6c, 0x61, 0x63, 610x69, 0x6f, 0x6e, 0x00, 0x61, 0x74, 0x00, 0x50, 0x44, 0x00,
620x65, 0x00, 0x5b, 0x34, 0x5d, 0x2e, 0x00, 0x55, 0x6e, 0x6c, 620xfa, 0x03, 0x50, 0x61, 0x6c, 0x61, 0x63, 0x65, 0x00, 0x5b,
630x69, 0x6b, 0x65, 0x1a, 0x00, 0x2b, 0x27, 0x73, 0x3c, 0x00, 630x34, 0x5d, 0x2e, 0x00, 0x55, 0x6e, 0x6c, 0x69, 0x6b, 0x65,
640x91, 0x2c, 0x00, 0x6d, 0x79, 0x00, 0x76, 0x65, 0x72, 0x73, 640x1a, 0x00, 0x2b, 0x27, 0x73, 0x3c, 0x00, 0x91, 0x2c, 0x00,
650x48, 0x00, 0xf1, 0x0e, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 650x6d, 0x79, 0x00, 0x76, 0x65, 0x72, 0x73, 0x48, 0x00, 0xf1,
660x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x67, 0x65, 0x6e, 660x0e, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61,
670x65, 0x72, 0x61, 0x74, 0x65, 0x73, 0x00, 0x72, 0x61, 0x6e, 670x6c, 0x6c, 0x79, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
680x64, 0x6f, 0x6d, 0x99, 0x01, 0x01, 0x8a, 0x01, 0x31, 0x61, 680x74, 0x65, 0x73, 0x00, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
690x6e, 0x79, 0x86, 0x01, 0x50, 0x00, 0x79, 0x6f, 0x75, 0x00, 690x99, 0x01, 0x01, 0x8a, 0x01, 0x31, 0x61, 0x6e, 0x79, 0x86,
700x65, 0x00, 0x60, 0x2e, 0x00, 0x54, 0x68, 0x65, 0x00, 0x27, 700x01, 0x50, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x65, 0x00, 0x60,
710x01, 0x30, 0x69, 0x74, 0x79, 0x22, 0x00, 0x03, 0xd3, 0x00, 710x2e, 0x00, 0x54, 0x68, 0x65, 0x00, 0x27, 0x01, 0x30, 0x69,
720x61, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0xe6, 0x01, 0x81, 720x74, 0x79, 0x22, 0x00, 0x03, 0xd3, 0x00, 0x61, 0x64, 0x65,
730x68, 0x65, 0x72, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x16, 0x02, 730x73, 0x69, 0x67, 0x6e, 0xe6, 0x01, 0x81, 0x68, 0x65, 0x72,
740xd0, 0x71, 0x75, 0x69, 0x74, 0x65, 0x00, 0x61, 0x73, 0x00, 740x65, 0x66, 0x6f, 0x72, 0x65, 0x16, 0x02, 0xd0, 0x71, 0x75,
750x67, 0x6f, 0x6f, 0x64, 0x08, 0x00, 0xc3, 0x68, 0x61, 0x6e, 750x69, 0x74, 0x65, 0x00, 0x61, 0x73, 0x00, 0x67, 0x6f, 0x6f,
760x64, 0x2d, 0x63, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x3d, 760x64, 0x08, 0x00, 0xc3, 0x68, 0x61, 0x6e, 0x64, 0x2d, 0x63,
770x00, 0xc0, 0x73, 0x00, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x00, 770x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x3d, 0x00, 0xc0, 0x73,
780x62, 0x65, 0x2c, 0x00, 0x4e, 0x02, 0x21, 0x6f, 0x6e, 0x36, 780x00, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x62, 0x65, 0x2c,
790x01, 0x21, 0x70, 0x6c, 0x03, 0x02, 0x12, 0x64, 0x7d, 0x00, 790x00, 0x4e, 0x02, 0x21, 0x6f, 0x6e, 0x36, 0x01, 0x21, 0x70,
800x50, 0x67, 0x65, 0x74, 0x00, 0x61, 0x86, 0x01, 0xf7, 0x02, 800x6c, 0x03, 0x02, 0x12, 0x64, 0x7d, 0x00, 0x50, 0x67, 0x65,
810x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x69, 0x62, 0x6c, 810x74, 0x00, 0x61, 0x86, 0x01, 0xf7, 0x02, 0x65, 0x78, 0x68,
820x65, 0x00, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x87, 0x00, 0x70, 820x61, 0x75, 0x73, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x00, 0x73,
830x73, 0x00, 0x74, 0x61, 0x69, 0x6c, 0x6f, 0x9f, 0x01, 0x40, 830x75, 0x70, 0x70, 0x6c, 0x87, 0x00, 0x70, 0x73, 0x00, 0x74,
840x74, 0x6f, 0x00, 0x79, 0x7d, 0x02, 0xc1, 0x6f, 0x77, 0x6e, 840x61, 0x69, 0x6c, 0x6f, 0x9f, 0x01, 0x40, 0x74, 0x6f, 0x00,
850x00, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x08, 850x79, 0x7d, 0x02, 0xc1, 0x6f, 0x77, 0x6e, 0x00, 0x73, 0x70,
860x01, 0x00, 0xb2, 0x01, 0xf1, 0x01, 0x5b, 0x33, 0x5d, 0x00, 860x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x08, 0x01, 0x00, 0xb2,
870x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 870x01, 0xf1, 0x02, 0x5b, 0x33, 0x5d, 0x00, 0x68, 0x74, 0x74,
880x2e, 0x6e, 0x87, 0x01, 0xa3, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 880x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e,
890x70, 0x2f, 0x65, 0x6e, 0x2f, 0x4d, 0x00, 0xf0, 0x06, 0x2f, 890x88, 0x01, 0xa3, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f,
900x73, 0x68, 0x69, 0x6b, 0x61, 0x6b, 0x75, 0x2e, 0x68, 0x74, 900x65, 0x6e, 0x2f, 0x4e, 0x00, 0x90, 0x2f, 0x73, 0x68, 0x69,
910x6d, 0x6c, 0x00, 0x28, 0x62, 0x65, 0x77, 0x61, 0x72, 0x65, 910x6b, 0x61, 0x6b, 0x75, 0x2f, 0x33, 0x00, 0x17, 0x34, 0x33,
920x6d, 0x00, 0x60, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x29, 0x48, 920x00, 0xe0, 0x65, 0x62, 0x2e, 0x61, 0x72, 0x63, 0x68, 0x69,
930x00, 0x12, 0x34, 0x48, 0x00, 0x10, 0x73, 0x49, 0x00, 0xe0, 930x76, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x10, 0x00, 0xf0, 0x01,
940x65, 0x62, 0x2e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 940x2f, 0x32, 0x30, 0x30, 0x34, 0x31, 0x30, 0x32, 0x34, 0x30,
950x2e, 0x6f, 0x72, 0x67, 0x10, 0x00, 0xf7, 0x01, 0x2f, 0x32, 950x30, 0x31, 0x34, 0x35, 0x39, 0x2f, 0x2b, 0x00, 0x03, 0x5d,
960x30, 0x30, 0x34, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30, 0x31, 960x00, 0x02, 0x4d, 0x00, 0x30, 0x2e, 0x67, 0x72, 0x5d, 0x00,
970x34, 0x35, 0x39, 0x2f, 0x73, 0x00, 0x02, 0x63, 0x00, 0x30, 970x02, 0x0d, 0x00, 0x22, 0x2f, 0x73, 0x58, 0x00, 0xf8, 0x09,
980x2e, 0x67, 0x72, 0x73, 0x00, 0x02, 0x0d, 0x00, 0x21, 0x2f, 980x70, 0x61, 0x6c, 0x6d, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78,
990x73, 0x6e, 0x00, 0xb1, 0x2f, 0x70, 0x61, 0x6c, 0x6d, 0x2f, 990x2e, 0x68, 0x74, 0x6d, 0x6c, 0x2e, 0x65, 0x6e, 0x00, 0x00,
1000x69, 0x6e, 0x64, 0x65, 0x78, 0x79, 0x00, 0x98, 0x2e, 0x65, 1000x00, 0x38, 0x2e, 0x31, 0xb3, 0x03, 0x00, 0xe0, 0x02, 0x95,
1010x6e, 0x00, 0x00, 0x00, 0x38, 0x2e, 0x31, 0xb2, 0x03, 0x00, 1010x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x5f,
1020xf5, 0x02, 0x31, 0x72, 0x6f, 0x6c, 0xbb, 0x03, 0x15, 0x54, 1020x02, 0x92, 0x69, 0x73, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x65,
1030x74, 0x02, 0x92, 0x69, 0x73, 0x00, 0x70, 0x6c, 0x61, 0x79, 1030x64, 0x9e, 0x03, 0x00, 0x30, 0x01, 0xf0, 0x05, 0x6d, 0x6f,
1040x65, 0x64, 0xb3, 0x03, 0x00, 0x45, 0x01, 0xf0, 0x05, 0x6d, 1040x75, 0x73, 0x65, 0x00, 0x6f, 0x72, 0x00, 0x63, 0x75, 0x72,
1050x6f, 0x75, 0x73, 0x65, 0x00, 0x6f, 0x72, 0x00, 0x63, 0x75, 1050x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0xed, 0x00,
1060x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x02, 1060xa1, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63,
1070x01, 0xa1, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 1070x6b, 0xcf, 0x01, 0x41, 0x65, 0x64, 0x67, 0x65, 0x99, 0x02,
1080x63, 0x6b, 0xe4, 0x01, 0x41, 0x65, 0x64, 0x67, 0x65, 0xae, 1080x21, 0x6f, 0x67, 0xf6, 0x02, 0x01, 0x70, 0x01, 0x70, 0x6f,
1090x02, 0x21, 0x6f, 0x67, 0x0b, 0x03, 0x01, 0x85, 0x01, 0x70, 1090x72, 0x00, 0x6f, 0x66, 0x66, 0x2c, 0x08, 0x00, 0x18, 0x6c,
1100x6f, 0x72, 0x00, 0x6f, 0x66, 0x66, 0x2c, 0x08, 0x00, 0x18, 1100x2f, 0x00, 0x60, 0x64, 0x00, 0x64, 0x72, 0x61, 0x67, 0x2f,
1110x6c, 0x2f, 0x00, 0x60, 0x64, 0x00, 0x64, 0x72, 0x61, 0x67, 1110x00, 0x40, 0x64, 0x72, 0x61, 0x77, 0x81, 0x01, 0x67, 0x65,
1120x2f, 0x00, 0x40, 0x64, 0x72, 0x61, 0x77, 0x96, 0x01, 0x67, 1120x6e, 0x74, 0x69, 0x72, 0x65, 0x37, 0x03, 0x10, 0x28, 0x34,
1130x65, 0x6e, 0x74, 0x69, 0x72, 0x65, 0x4c, 0x03, 0x10, 0x28, 1130x00, 0x44, 0x69, 0x6e, 0x65, 0x29, 0xb8, 0x01, 0x03, 0xd6,
1140x34, 0x00, 0x44, 0x69, 0x6e, 0x65, 0x29, 0xcd, 0x01, 0x03, 1140x03, 0x01, 0x88, 0x03, 0xc2, 0x67, 0x6f, 0x00, 0x28, 0x72,
1150xeb, 0x03, 0x01, 0x9d, 0x03, 0xc2, 0x67, 0x6f, 0x00, 0x28, 1150x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x7e, 0x00, 0x40,
1160x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x7e, 0x00, 1160x78, 0x69, 0x73, 0x74, 0x0d, 0x00, 0x00, 0x87, 0x00, 0x11,
1170x40, 0x78, 0x69, 0x73, 0x74, 0x0d, 0x00, 0x00, 0x87, 0x00, 1170x73, 0xbd, 0x00, 0x22, 0x69, 0x6e, 0xdd, 0x03, 0x05, 0x53,
1180x11, 0x73, 0xbd, 0x00, 0x22, 0x69, 0x6e, 0xf2, 0x03, 0x05, 1180x00, 0x73, 0x29, 0x2e, 0x00, 0x52, 0x69, 0x67, 0x68, 0x86,
1190x53, 0x00, 0x73, 0x29, 0x2e, 0x00, 0x52, 0x69, 0x67, 0x68, 1190x00, 0x02, 0x3a, 0x00, 0x02, 0x89, 0x00, 0x10, 0x67, 0x0d,
1200x86, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x89, 0x00, 0x10, 0x67, 1200x00, 0x40, 0x77, 0x69, 0x6c, 0x6c, 0x6e, 0x04, 0x21, 0x6f,
1210x0d, 0x00, 0x40, 0x77, 0x69, 0x6c, 0x6c, 0x83, 0x04, 0x21, 1210x77, 0x19, 0x02, 0x72, 0x74, 0x6f, 0x00, 0x65, 0x72, 0x61,
1220x6f, 0x77, 0x2e, 0x02, 0x72, 0x74, 0x6f, 0x00, 0x65, 0x72, 1220x73, 0x52, 0x04, 0x00, 0x2d, 0x01, 0x32, 0x65, 0x6e, 0x74,
1230x61, 0x73, 0x67, 0x04, 0x00, 0x2d, 0x01, 0x32, 0x65, 0x6e, 1230xbc, 0x02, 0x07, 0xa5, 0x00, 0x00, 0x68, 0x00, 0x91, 0x6f,
1240x74, 0xd1, 0x02, 0x07, 0xa5, 0x00, 0x00, 0x68, 0x00, 0x91, 1240x75, 0x74, 0x00, 0x61, 0x66, 0x66, 0x65, 0x63, 0x80, 0x00,
1250x6f, 0x75, 0x74, 0x00, 0x61, 0x66, 0x66, 0x65, 0x63, 0x80, 1250x00, 0xcc, 0x03, 0x01, 0x84, 0x00, 0x00, 0x23, 0x01, 0xf4,
1260x00, 0x00, 0xe1, 0x03, 0x01, 0x84, 0x00, 0x00, 0x23, 0x01, 1260x01, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69,
1270xf4, 0x01, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 1270x76, 0x65, 0x6c, 0x79, 0x2c, 0x00, 0x75, 0x4e, 0x00, 0x06,
1280x69, 0x76, 0x65, 0x6c, 0x79, 0x2c, 0x00, 0x75, 0x4e, 0x00, 1280x49, 0x01, 0x00, 0x67, 0x00, 0x42, 0x6d, 0x6f, 0x76, 0x65,
1290x06, 0x49, 0x01, 0x00, 0x67, 0x00, 0x42, 0x6d, 0x6f, 0x76, 1290x9a, 0x02, 0x31, 0x6f, 0x73, 0x69, 0x8f, 0x03, 0x30, 0x69,
1300x65, 0xaf, 0x02, 0x31, 0x6f, 0x73, 0x69, 0xa4, 0x03, 0x30, 1300x6e, 0x64, 0x56, 0x02, 0x91, 0x6f, 0x72, 0x00, 0x61, 0x72,
1310x69, 0x6e, 0x64, 0x6b, 0x02, 0x91, 0x6f, 0x72, 0x00, 0x61, 1310x6f, 0x75, 0x6e, 0x64, 0x1e, 0x00, 0xc0, 0x62, 0x6f, 0x61,
1320x72, 0x6f, 0x75, 0x6e, 0x64, 0x1e, 0x00, 0xc0, 0x62, 0x6f, 1320x72, 0x64, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x6a,
1330x61, 0x72, 0x64, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 1330x00, 0x20, 0x74, 0x68, 0x2f, 0x01, 0x40, 0x74, 0x75, 0x72,
1340x6a, 0x00, 0x20, 0x74, 0x68, 0x2f, 0x01, 0x40, 0x74, 0x75, 1340x6e, 0x4a, 0x00, 0x00, 0x0f, 0x00, 0x12, 0x6e, 0xbf, 0x00,
1350x72, 0x6e, 0x4a, 0x00, 0x00, 0x0f, 0x00, 0x12, 0x6e, 0xbf, 1350x14, 0x73, 0xc0, 0x00, 0x0f, 0x70, 0x00, 0x04, 0x01, 0x7b,
1360x00, 0x14, 0x73, 0xc0, 0x00, 0x0f, 0x70, 0x00, 0x04, 0x01, 1360x01, 0x08, 0xc6, 0x00, 0x00, 0xc2, 0x00, 0x42, 0x66, 0x72,
1370x7b, 0x01, 0x08, 0xc6, 0x00, 0x00, 0xc2, 0x00, 0x42, 0x66, 1370x6f, 0x6d, 0x30, 0x01, 0x04, 0x86, 0x00, 0x02, 0xdd, 0x04,
1380x72, 0x6f, 0x6d, 0x30, 0x01, 0x04, 0x86, 0x00, 0x02, 0xf2, 1380x1f, 0x70, 0x6f, 0x00, 0x04, 0x80, 0x61, 0x67, 0x61, 0x69,
1390x04, 0x1f, 0x70, 0x6f, 0x00, 0x04, 0x80, 0x61, 0x67, 0x61, 1390x6e, 0x00, 0x63, 0x6f, 0x0c, 0x04, 0x21, 0x74, 0x65, 0x9f,
1400x69, 0x6e, 0x00, 0x63, 0x6f, 0x21, 0x04, 0x21, 0x74, 0x65, 1400x03, 0x06, 0x52, 0x00, 0x35, 0x2e, 0x00, 0x55, 0x34, 0x00,
1410xb4, 0x03, 0x06, 0x52, 0x00, 0x35, 0x2e, 0x00, 0x55, 0x34, 1410x20, 0x73, 0x70, 0x50, 0x04, 0xa1, 0x62, 0x61, 0x72, 0x00,
1420x00, 0x20, 0x73, 0x70, 0x65, 0x04, 0xa1, 0x62, 0x61, 0x72, 1420x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x04, 0x06, 0x0c, 0x4d,
1430x00, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x19, 0x06, 0x0c, 1430x00, 0x09, 0xb7, 0x00, 0x0f, 0x77, 0x01, 0x2a, 0x10, 0x2c,
1440x4d, 0x00, 0x09, 0xb7, 0x00, 0x0f, 0x77, 0x01, 0x2a, 0x10, 1440x0d, 0x04, 0x57, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x2b, 0x01,
1450x2c, 0x22, 0x04, 0x57, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x2b, 1450xf1, 0x01, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x00, 0x63,
1460x01, 0xf1, 0x01, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x00, 1460x61, 0x6e, 0x63, 0x65, 0x6c, 0x73, 0x00, 0x61, 0x03, 0x01,
1470x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x73, 0x00, 0x61, 0x03, 1470x00, 0xa1, 0x01, 0x11, 0x57, 0x35, 0x01, 0x08, 0x0b, 0x01,
1480x01, 0x00, 0xa1, 0x01, 0x11, 0x57, 0x35, 0x01, 0x08, 0x0b, 1480x13, 0x66, 0x79, 0x00, 0x10, 0x72, 0x14, 0x00, 0x02, 0xa4,
1490x01, 0x13, 0x66, 0x79, 0x00, 0x10, 0x72, 0x14, 0x00, 0x02, 1490x04, 0x25, 0x69, 0x73, 0xe8, 0x00, 0x20, 0x64, 0x2c, 0xd9,
1500xb9, 0x04, 0x25, 0x69, 0x73, 0xe8, 0x00, 0x20, 0x64, 0x2c, 1500x02, 0x01, 0x2a, 0x02, 0x90, 0x62, 0x65, 0x00, 0x73, 0x68,
1510xd9, 0x02, 0x01, 0x2a, 0x02, 0x90, 0x62, 0x65, 0x00, 0x73, 1510x61, 0x64, 0x65, 0x64, 0x48, 0x00, 0x42, 0x28, 0x41, 0x6c,
1520x68, 0x61, 0x64, 0x65, 0x64, 0x48, 0x00, 0x42, 0x28, 0x41, 1520x6c, 0x04, 0x06, 0x10, 0x63, 0x3b, 0x01, 0x10, 0x73, 0xb8,
1530x6c, 0x6c, 0x19, 0x06, 0x10, 0x63, 0x3b, 0x01, 0x10, 0x73, 1530x04, 0x61, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0xd1, 0x06,
1540xcd, 0x04, 0x61, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0xe6, 1540x21, 0x65, 0x63, 0xd7, 0x01, 0x30, 0x32, 0x2e, 0x31, 0x25,
1550x06, 0x21, 0x65, 0x63, 0xd7, 0x01, 0x30, 0x32, 0x2e, 0x31, 1550x06, 0x02, 0x8e, 0x05, 0xb1, 0x61, 0x76, 0x61, 0x69, 0x6c,
1560x3a, 0x06, 0x02, 0xa3, 0x05, 0xb1, 0x61, 0x76, 0x61, 0x69, 1560x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x9d, 0x03, 0x18, 0x32,
1570x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x9d, 0x03, 0x18, 1570x9d, 0x03, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1580x32, 0x9d, 0x03, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 1580x65, 0x72, 0x9f, 0x03, 0x01, 0xe5, 0x05, 0x05, 0x14, 0x00,
1590x74, 0x65, 0x72, 0x9f, 0x03, 0x01, 0xfa, 0x05, 0x05, 0x14, 1590x02, 0x45, 0x00, 0x04, 0x40, 0x00, 0x04, 0xbb, 0x01, 0xf1,
1600x00, 0x02, 0x45, 0x00, 0x04, 0x40, 0x00, 0x04, 0xbb, 0x01, 1600x01, 0x65, 0x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
1610xf1, 0x01, 0x65, 0x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 1610x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x73, 0x00, 0x03,
1620x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x73, 0x00, 1620x3c, 0x03, 0xb1, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1630x03, 0x3c, 0x03, 0xb1, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 1630x6d, 0x65, 0x6e, 0x75, 0xf7, 0x00, 0x80, 0x69, 0x64, 0x74,
1640x00, 0x6d, 0x65, 0x6e, 0x75, 0xf7, 0x00, 0x80, 0x69, 0x64, 1640x68, 0x2c, 0x00, 0x48, 0x65, 0x0f, 0x03, 0x61, 0x00, 0x00,
1650x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x0f, 0x03, 0x61, 0x00, 1650x00, 0x53, 0x69, 0x7a, 0xfb, 0x00, 0x00, 0x63, 0x03, 0x11,
1660x00, 0x00, 0x53, 0x69, 0x7a, 0xfb, 0x00, 0x00, 0x63, 0x03, 1660x2c, 0xb2, 0x00, 0x04, 0x69, 0x07, 0x70, 0x00, 0x00, 0x45,
1670x11, 0x2c, 0xb2, 0x00, 0x04, 0x7e, 0x07, 0x70, 0x00, 0x00, 1670x78, 0x70, 0x61, 0x6e, 0xdd, 0x05, 0x40, 0x20, 0x66, 0x61,
1680x45, 0x78, 0x70, 0x61, 0x6e, 0xf2, 0x05, 0x40, 0x20, 0x66, 1680x63, 0x93, 0x02, 0x03, 0x2f, 0x04, 0x10, 0x69, 0x48, 0x01,
1690x61, 0x63, 0x93, 0x02, 0x03, 0x2f, 0x04, 0x10, 0x69, 0x48, 1690x91, 0x6d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d,
1700x01, 0x91, 0x6d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 1700xa6, 0x06, 0x00, 0x0c, 0x00, 0x14, 0x67, 0xf8, 0x01, 0x35,
1710x6d, 0xbb, 0x06, 0x00, 0x0c, 0x00, 0x14, 0x67, 0xf8, 0x01, 1710x74, 0x79, 0x70, 0x54, 0x00, 0x15, 0x73, 0x0b, 0x06, 0x42,
1720x35, 0x74, 0x79, 0x70, 0x54, 0x00, 0x15, 0x73, 0x20, 0x06, 1720x64, 0x00, 0x62, 0x79, 0xec, 0x02, 0x90, 0x72, 0x6f, 0x67,
1730x42, 0x64, 0x00, 0x62, 0x79, 0xec, 0x02, 0x90, 0x72, 0x6f, 1730x72, 0x61, 0x6d, 0x2e, 0x00, 0x53, 0xee, 0x07, 0x60, 0x70,
1740x67, 0x72, 0x61, 0x6d, 0x2e, 0x00, 0x53, 0x03, 0x08, 0x60, 1740x65, 0x6f, 0x70, 0x6c, 0x65, 0x6d, 0x02, 0x34, 0x66, 0x65,
1750x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x6d, 0x02, 0x34, 0x66, 1750x72, 0x2c, 0x08, 0x03, 0x82, 0x07, 0x01, 0xa9, 0x03, 0x97,
1760x65, 0x72, 0x41, 0x08, 0x03, 0x97, 0x07, 0x01, 0xa9, 0x03, 1760x00, 0x66, 0x65, 0x77, 0x00, 0x6c, 0x61, 0x72, 0x67, 0x66,
1770x97, 0x00, 0x66, 0x65, 0x77, 0x00, 0x6c, 0x61, 0x72, 0x67, 1770x02, 0x01, 0xcd, 0x02, 0x00, 0x0e, 0x04, 0x07, 0x29, 0x00,
1780x66, 0x02, 0x01, 0xcd, 0x02, 0x00, 0x0e, 0x04, 0x07, 0x29, 1780x11, 0x6d, 0x5a, 0x06, 0x40, 0x6d, 0x61, 0x6c, 0x6c, 0x1a,
1790x00, 0x11, 0x6d, 0x6f, 0x06, 0x40, 0x6d, 0x61, 0x6c, 0x6c, 1790x00, 0x10, 0x73, 0x5f, 0x00, 0x01, 0x57, 0x02, 0x40, 0x63,
1800x1a, 0x00, 0x10, 0x73, 0x5f, 0x00, 0x01, 0x57, 0x02, 0x40, 1800x61, 0x6e, 0x00, 0x2f, 0x08, 0x19, 0x52, 0x3d, 0x00, 0x30,
1810x63, 0x61, 0x6e, 0x00, 0x44, 0x08, 0x19, 0x52, 0x3d, 0x00, 1810x65, 0x73, 0x73, 0x78, 0x04, 0x09, 0xad, 0x06, 0x22, 0x00,
1820x30, 0x65, 0x73, 0x73, 0x78, 0x04, 0x09, 0xc2, 0x06, 0x22, 1820x61, 0x40, 0x00, 0x22, 0x65, 0x72, 0x86, 0x00, 0x32, 0x74,
1830x00, 0x61, 0x40, 0x00, 0x22, 0x65, 0x72, 0x86, 0x00, 0x32, 1830x68, 0x61, 0x44, 0x01, 0x05, 0xb0, 0x06, 0x03, 0xf7, 0x05,
1840x74, 0x68, 0x61, 0x44, 0x01, 0x05, 0xc5, 0x06, 0x03, 0x0c, 1840x22, 0x65, 0x64, 0x22, 0x03, 0x01, 0x79, 0x03, 0x00, 0x4d,
1850x06, 0x22, 0x65, 0x64, 0x22, 0x03, 0x01, 0x79, 0x03, 0x00, 1850x00, 0x00, 0x29, 0x01, 0x10, 0x64, 0x20, 0x02, 0x60, 0x62,
1860x4d, 0x00, 0x00, 0x29, 0x01, 0x10, 0x64, 0x20, 0x02, 0x60, 1860x79, 0x00, 0x61, 0x64, 0x64, 0x8f, 0x00, 0x10, 0x72, 0xd7,
1870x62, 0x79, 0x00, 0x61, 0x64, 0x64, 0x8f, 0x00, 0x10, 0x72, 1870x02, 0x20, 0x61, 0x6e, 0xcc, 0x00, 0x41, 0x6c, 0x75, 0x6d,
1880xd7, 0x02, 0x20, 0x61, 0x6e, 0xcc, 0x00, 0x41, 0x6c, 0x75, 1880x6e, 0x51, 0x01, 0x00, 0xe9, 0x06, 0x72, 0x64, 0x65, 0x66,
1890x6d, 0x6e, 0x51, 0x01, 0x00, 0xfe, 0x06, 0x72, 0x64, 0x65, 1890x61, 0x75, 0x6c, 0x74, 0x34, 0x00, 0x01, 0x3a, 0x07, 0x03,
1900x66, 0x61, 0x75, 0x6c, 0x74, 0x34, 0x00, 0x01, 0x4f, 0x07, 1900x5d, 0x01, 0xd2, 0x6f, 0x66, 0x00, 0x7a, 0x65, 0x72, 0x6f,
1910x03, 0x5d, 0x01, 0xd2, 0x6f, 0x66, 0x00, 0x7a, 0x65, 0x72, 1910x00, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x90, 0x03, 0x07, 0xb3,
1920x6f, 0x00, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x90, 0x03, 0x07, 1920x00, 0x01, 0x79, 0x02, 0x10, 0x73, 0x80, 0x07, 0x09, 0xb0,
1930xb3, 0x00, 0x01, 0x79, 0x02, 0x10, 0x73, 0x95, 0x07, 0x09, 1930x00, 0x04, 0x5a, 0x09, 0x09, 0xa6, 0x00, 0x00, 0xee, 0x00,
1940xb0, 0x00, 0x04, 0x6f, 0x09, 0x09, 0xa6, 0x00, 0x00, 0xee, 1940x42, 0x66, 0x6f, 0x72, 0x2c, 0xe5, 0x04, 0x10, 0x6f, 0x36,
1950x00, 0x42, 0x66, 0x6f, 0x72, 0x2c, 0xe5, 0x04, 0x10, 0x6f, 1950x07, 0x10, 0x68, 0x93, 0x00, 0x30, 0x66, 0x75, 0x72, 0x4b,
1960x4b, 0x07, 0x10, 0x68, 0x93, 0x00, 0x30, 0x66, 0x75, 0x72, 1960x07, 0x42, 0x2e, 0x00, 0x49, 0x66, 0xce, 0x00, 0x02, 0x01,
1970x60, 0x07, 0x42, 0x2e, 0x00, 0x49, 0x66, 0xce, 0x00, 0x02, 1970x07, 0x0f, 0x8a, 0x00, 0x01, 0x91, 0x28, 0x73, 0x61, 0x79,
1980x16, 0x07, 0x0f, 0x8a, 0x00, 0x01, 0x91, 0x28, 0x73, 0x61, 1980x29, 0x00, 0x30, 0x2e, 0x35, 0xf6, 0x02, 0x07, 0x93, 0x00,
1990x79, 0x29, 0x00, 0x30, 0x2e, 0x35, 0xf6, 0x02, 0x07, 0x93, 1990x01, 0xe2, 0x08, 0x42, 0x64, 0x69, 0x6d, 0x65, 0x32, 0x00,
2000x00, 0x01, 0xf7, 0x08, 0x42, 0x64, 0x69, 0x6d, 0x65, 0x32, 2000x26, 0x6f, 0x66, 0xa1, 0x05, 0x04, 0x1c, 0x03, 0x02, 0x0e,
2010x00, 0x26, 0x6f, 0x66, 0xa1, 0x05, 0x04, 0x1c, 0x03, 0x02, 2010x01, 0x02, 0x31, 0x07, 0x43, 0x68, 0x61, 0x6c, 0x66, 0x31,
2020x0e, 0x01, 0x02, 0x46, 0x07, 0x43, 0x68, 0x61, 0x6c, 0x66, 2020x04, 0x70, 0x61, 0x73, 0x00, 0x62, 0x69, 0x67, 0x00, 0xa7,
2030x31, 0x04, 0x70, 0x61, 0x73, 0x00, 0x62, 0x69, 0x67, 0x00, 2030x07, 0x14, 0x72, 0xc3, 0x00, 0x01, 0x3d, 0x07, 0x60, 0x49,
2040xbc, 0x07, 0x14, 0x72, 0xc3, 0x00, 0x01, 0x52, 0x07, 0x60, 2040x6e, 0x00, 0x6f, 0x74, 0x68, 0x18, 0x09, 0x51, 0x6f, 0x72,
2050x49, 0x6e, 0x00, 0x6f, 0x74, 0x68, 0x2d, 0x09, 0x51, 0x6f, 2050x64, 0x73, 0x2c, 0x51, 0x00, 0x30, 0x69, 0x6e, 0x69, 0x9d,
2060x72, 0x64, 0x73, 0x2c, 0x51, 0x00, 0x30, 0x69, 0x6e, 0x69, 2060x01, 0x0a, 0x59, 0x00, 0x36, 0x32, 0x2f, 0x33, 0xe9, 0x00,
2070x9d, 0x01, 0x0a, 0x59, 0x00, 0x36, 0x32, 0x2f, 0x33, 0xe9, 2070x2b, 0x69, 0x6e, 0x8c, 0x00, 0x02, 0xef, 0x00, 0x0f, 0x85,
2080x00, 0x2b, 0x69, 0x6e, 0x8c, 0x00, 0x02, 0xef, 0x00, 0x0f, 2080x00, 0x01, 0x00, 0x22, 0x04, 0x21, 0x66, 0x75, 0x40, 0x01,
2090x85, 0x00, 0x01, 0x00, 0x22, 0x04, 0x21, 0x66, 0x75, 0x40, 2090x17, 0x7a, 0x42, 0x04, 0x24, 0x64, 0x64, 0x43, 0x06, 0x28,
2100x01, 0x17, 0x7a, 0x42, 0x04, 0x24, 0x64, 0x64, 0x43, 0x06, 2100x6d, 0x6f, 0x80, 0x06, 0x01, 0xab, 0x01, 0x31, 0x53, 0x65,
2110x28, 0x6d, 0x6f, 0x80, 0x06, 0x01, 0xab, 0x01, 0x31, 0x53, 2110x74, 0x5e, 0x04, 0x0f, 0x20, 0x01, 0x04, 0x03, 0x9d, 0x05,
2120x65, 0x74, 0x5e, 0x04, 0x0f, 0x20, 0x01, 0x04, 0x03, 0x9d, 2120x82, 0x30, 0x2e, 0x35, 0x00, 0x74, 0x65, 0x6e, 0x64, 0xcd,
2130x05, 0x82, 0x30, 0x2e, 0x35, 0x00, 0x74, 0x65, 0x6e, 0x64, 2130x05, 0x23, 0x61, 0x6b, 0x85, 0x0a, 0x00, 0x4f, 0x07, 0x01,
2140xcd, 0x05, 0x23, 0x61, 0x6b, 0x9a, 0x0a, 0x00, 0x4f, 0x07, 2140x54, 0x00, 0x92, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75,
2150x01, 0x54, 0x00, 0x92, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 2150x6c, 0x74, 0x9d, 0x00, 0x01, 0xfe, 0x03, 0x50, 0x28, 0x69,
2160x75, 0x6c, 0x74, 0x9d, 0x00, 0x01, 0xfe, 0x03, 0x50, 0x28, 2160x6e, 0x00, 0x6d, 0xb7, 0x06, 0xf0, 0x01, 0x70, 0x65, 0x72,
2170x69, 0x6e, 0x00, 0x6d, 0xb7, 0x06, 0xb0, 0x70, 0x65, 0x72, 2170x69, 0x65, 0x6e, 0x63, 0x65, 0x29, 0x00, 0x72, 0x65, 0x77,
2180x69, 0x65, 0x6e, 0x63, 0x65, 0x29, 0x00, 0x72, 0x1d, 0x08, 2180x61, 0x72, 0x64, 0x56, 0x03, 0x30, 0x6c, 0x65, 0x73, 0x42,
2190x10, 0x64, 0x56, 0x03, 0x30, 0x6c, 0x65, 0x73, 0x42, 0x04, 2190x04, 0x30, 0x64, 0x75, 0x63, 0x3a, 0x06, 0x01, 0x35, 0x00,
2200x30, 0x64, 0x75, 0x63, 0x3a, 0x06, 0x01, 0x35, 0x00, 0x01, 2200x01, 0x49, 0x00, 0x51, 0x69, 0x6e, 0x74, 0x75, 0x69, 0x13,
2210x49, 0x00, 0x51, 0x69, 0x6e, 0x74, 0x75, 0x69, 0x13, 0x00, 2210x00, 0x00, 0xa4, 0x07, 0x00, 0x99, 0x00, 0x59, 0x73, 0x74,
2220x00, 0xa4, 0x07, 0x00, 0x99, 0x00, 0x59, 0x73, 0x74, 0x79, 2220x79, 0x6c, 0x65, 0xcb, 0x01, 0xd0, 0x69, 0x74, 0x00, 0x74,
2230x6c, 0x65, 0xcb, 0x01, 0xd0, 0x69, 0x74, 0x00, 0x74, 0x6f, 2230x6f, 0x6f, 0x00, 0x68, 0x69, 0x67, 0x68, 0x2c, 0x00, 0xe4,
2240x6f, 0x00, 0x68, 0x69, 0x67, 0x68, 0x2c, 0x00, 0xe4, 0x00, 2240x00, 0x02, 0x08, 0x00, 0x03, 0x90, 0x00, 0x03, 0x3c, 0x02,
2250x02, 0x08, 0x00, 0x03, 0x90, 0x00, 0x03, 0x3c, 0x02, 0x30, 2250x30, 0x63, 0x61, 0x6e, 0x45, 0x09, 0x05, 0x43, 0x02, 0x01,
2260x63, 0x61, 0x6e, 0x5a, 0x09, 0x05, 0x43, 0x02, 0x01, 0x5e, 2260x5e, 0x00, 0x01, 0xe9, 0x02, 0x02, 0x69, 0x03, 0x0a, 0x63,
2270x00, 0x01, 0xe9, 0x02, 0x02, 0x69, 0x03, 0x0a, 0x63, 0x03, 2270x03, 0x51, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x44, 0x00, 0x03,
2280x51, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x44, 0x00, 0x03, 0xa5, 2280xa5, 0x07, 0x02, 0x27, 0x04, 0x03, 0xfc, 0x02, 0x02, 0x59,
2290x07, 0x02, 0x27, 0x04, 0x03, 0xfc, 0x02, 0x02, 0x59, 0x00, 2290x00, 0x50, 0x62, 0x65, 0x63, 0x6f, 0x6d, 0x2f, 0x00, 0x61,
2300x50, 0x62, 0x65, 0x63, 0x6f, 0x6d, 0x2f, 0x00, 0x61, 0x72, 2300x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x39, 0x04, 0xf1, 0x02,
2310x69, 0x76, 0x69, 0x61, 0x6c, 0x39, 0x04, 0xf1, 0x02, 0x6e, 2310x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x71,
2320x73, 0x75, 0x72, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 2320x75, 0x65, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x92, 0x04, 0x60,
2330x65, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x92, 0x04, 0x60, 0x00, 2330x00, 0x00, 0x4e, 0x6f, 0x72, 0x6d, 0x73, 0x03, 0x1d, 0x2c,
2340x00, 0x4e, 0x6f, 0x72, 0x6d, 0x73, 0x03, 0x1d, 0x2c, 0xdb, 2340xdb, 0x02, 0x01, 0x3d, 0x01, 0x00, 0x36, 0x00, 0x02, 0x67,
2350x02, 0x01, 0x3d, 0x01, 0x00, 0x36, 0x00, 0x02, 0x67, 0x02, 2350x02, 0x01, 0x28, 0x04, 0x03, 0x81, 0x09, 0x21, 0x69, 0x74,
2360x01, 0x28, 0x04, 0x03, 0x96, 0x09, 0x21, 0x69, 0x74, 0x8b, 2360x8b, 0x06, 0x01, 0x0e, 0x06, 0x01, 0x51, 0x0c, 0x23, 0x6f,
2370x06, 0x01, 0x0e, 0x06, 0x01, 0x66, 0x0c, 0x23, 0x6f, 0x6e, 2370x6e, 0x94, 0x0b, 0x04, 0x5a, 0x00, 0x13, 0x2e, 0xa9, 0x0a,
2380xa9, 0x0b, 0x04, 0x5a, 0x00, 0x13, 0x2e, 0xbe, 0x0a, 0x12, 2380x12, 0x73, 0xbb, 0x08, 0x61, 0x61, 0x6d, 0x62, 0x69, 0x67,
2390x73, 0xbb, 0x08, 0x61, 0x61, 0x6d, 0x62, 0x69, 0x67, 0x75, 2390x75, 0xef, 0x0b, 0x13, 0x65, 0x95, 0x05, 0x00, 0x05, 0x04,
2400x04, 0x0c, 0x13, 0x65, 0x95, 0x05, 0x00, 0x05, 0x04, 0x1c, 2400x1c, 0x62, 0x95, 0x01, 0x06, 0x5f, 0x01, 0xd5, 0x73, 0x75,
2410x62, 0x95, 0x01, 0x06, 0x5f, 0x01, 0xd5, 0x73, 0x75, 0x62, 2410x62, 0x74, 0x6c, 0x65, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x69,
2420x74, 0x6c, 0x65, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x69, 0x66, 2420x66, 0x9a, 0x0a, 0x05, 0x3f, 0x04, 0x01, 0xae, 0x06, 0x32,
2430xaf, 0x0a, 0x05, 0x3f, 0x04, 0x01, 0xae, 0x06, 0x32, 0x6f, 2430x6f, 0x66, 0x66, 0x8d, 0x0b, 0x62, 0x66, 0x65, 0x61, 0x74,
2440x66, 0x66, 0xa2, 0x0b, 0x62, 0x66, 0x65, 0x61, 0x74, 0x75, 2440x75, 0x72, 0x9d, 0x01, 0x40, 0x72, 0x69, 0x73, 0x6b, 0x95,
2450x72, 0x9d, 0x01, 0x40, 0x72, 0x69, 0x73, 0x6b, 0x95, 0x00, 2450x00, 0x01, 0x2b, 0x02, 0x05, 0x77, 0x00, 0x03, 0xba, 0x00,
2460x01, 0x2b, 0x02, 0x05, 0x77, 0x00, 0x03, 0xba, 0x00, 0xb2, 2460xb2, 0x2e, 0x00, 0x41, 0x6c, 0x73, 0x6f, 0x2c, 0x00, 0x66,
2470x2e, 0x00, 0x41, 0x6c, 0x73, 0x6f, 0x2c, 0x00, 0x66, 0x69, 2470x69, 0x6e, 0x6b, 0x02, 0x24, 0x6c, 0x6c, 0xed, 0x07, 0x12,
2480x6e, 0x6b, 0x02, 0x24, 0x6c, 0x6c, 0xed, 0x07, 0x12, 0x73, 2480x73, 0x6d, 0x0a, 0x03, 0xc0, 0x00, 0x05, 0xa0, 0x00, 0x21,
2490x82, 0x0a, 0x03, 0xc0, 0x00, 0x05, 0xa0, 0x00, 0x21, 0x61, 2490x61, 0x6e, 0x97, 0x02, 0x00, 0x14, 0x00, 0x20, 0x61, 0x6c,
2500x6e, 0x97, 0x02, 0x00, 0x14, 0x00, 0x20, 0x61, 0x6c, 0x55, 2500x55, 0x05, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x63,
2510x05, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x63, 0x05, 2510x05, 0x01, 0x1c, 0x00, 0x10, 0x76, 0xc8, 0x06, 0x12, 0x64,
2520x01, 0x1c, 0x00, 0x10, 0x76, 0xc8, 0x06, 0x12, 0x64, 0xa9, 2520xa9, 0x09, 0x61, 0x72, 0x2e, 0x00, 0x54, 0x75, 0x72, 0xfc,
2530x09, 0x61, 0x72, 0x2e, 0x00, 0x54, 0x75, 0x72, 0xfc, 0x04, 2530x04, 0x05, 0x9c, 0x00, 0x03, 0xfc, 0x05, 0x01, 0xf4, 0x04,
2540x05, 0x9c, 0x00, 0x03, 0xfc, 0x05, 0x01, 0xf4, 0x04, 0x01, 2540x01, 0xf9, 0x0b, 0x74, 0x70, 0x65, 0x65, 0x64, 0x00, 0x75,
2550x0e, 0x0c, 0x74, 0x70, 0x65, 0x65, 0x64, 0x00, 0x75, 0x70, 2550x70, 0x55, 0x0b, 0x03, 0xf8, 0x01, 0x50, 0x69, 0x6f, 0x6e,
2560x6a, 0x0b, 0x03, 0xf8, 0x01, 0x50, 0x69, 0x6f, 0x6e, 0x2e, 2560x2e, 0x00,
2570x00,
258}; 257};
259 258
260const unsigned short help_text_len = 3535; 259const unsigned short help_text_len = 3536;
261const unsigned short help_text_words = 605; 260const unsigned short help_text_words = 603;
261const bool help_valid = true;
262const char quick_help_text[] = "Divide the grid into rectangles with areas equal to the numbers."; 262const char quick_help_text[] = "Divide the grid into rectangles with areas equal to the numbers.";
diff --git a/apps/plugins/puzzles/help/samegame.c b/apps/plugins/puzzles/help/samegame.c
index 5efe0ff4c3..62589b4f11 100644
--- a/apps/plugins/puzzles/help/samegame.c
+++ b/apps/plugins/puzzles/help/samegame.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,185 +6,187 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 116, TEXT_CENTER | C_RED }, 9 { 117, TEXT_CENTER | C_RED },
10 { 229, TEXT_CENTER | C_RED }, 10 { 230, TEXT_CENTER | C_RED },
11 { 246, TEXT_UNDERLINE },
12 { 247, TEXT_UNDERLINE }, 11 { 247, TEXT_UNDERLINE },
13 { 257, TEXT_UNDERLINE }, 12 { 248, TEXT_UNDERLINE },
14 { 292, TEXT_UNDERLINE }, 13 { 258, TEXT_UNDERLINE },
15 { 342, TEXT_UNDERLINE }, 14 { 293, TEXT_UNDERLINE },
16 { 414, TEXT_UNDERLINE }, 15 { 343, TEXT_UNDERLINE },
16 { 415, TEXT_UNDERLINE },
17 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
18}; 18};
19 19
20/* orig 2470 comp 1634 ratio 0.661538 level 10 saved 836 */ 20/* orig 2492 comp 1641 ratio 0.658507 level 10 saved 851 */
21const char help_text[] = { 21const char help_text[] = {
220xf0, 0x03, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xf0, 0x03, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
230x31, 0x33, 0x3a, 0x20, 0x53, 0x61, 0x6d, 0x65, 0x20, 0x47, 230x31, 0x33, 0x3a, 0x20, 0x53, 0x61, 0x6d, 0x65, 0x20, 0x47,
240x05, 0x00, 0xf4, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 240x05, 0x00, 0x2f, 0x00, 0x2d, 0x01, 0x00, 0x01, 0xf4, 0x20,
250x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 250x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76,
260x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 260x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f,
270x75, 0x72, 0x65, 0x64, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 270x66, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x65, 0x64,
280x65, 0x73, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 280x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00,
290x79, 0x2b, 0x00, 0xf0, 0x1c, 0x74, 0x6f, 0x00, 0x63, 0x6c, 290x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x79, 0x2b, 0x00, 0xf0,
300x65, 0x61, 0x72, 0x00, 0x62, 0x79, 0x00, 0x68, 0x69, 0x67, 300x1c, 0x74, 0x6f, 0x00, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x00,
310x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x00, 310x62, 0x79, 0x00, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67,
320x63, 0x6f, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 320x68, 0x74, 0x69, 0x6e, 0x67, 0x00, 0x63, 0x6f, 0x6e, 0x74,
330x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x00, 330x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x00, 0x72, 0x65, 0x67,
340xdc, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x74, 0x68, 0x61, 0x6e, 340x69, 0x6f, 0x6e, 0x73, 0x50, 0x00, 0xdc, 0x6d, 0x6f, 0x72,
350x00, 0x6f, 0x6e, 0x65, 0x5e, 0x00, 0xc1, 0x3b, 0x00, 0x74, 350x65, 0x00, 0x74, 0x68, 0x61, 0x6e, 0x00, 0x6f, 0x6e, 0x65,
360x68, 0x65, 0x00, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x0b, 360x5e, 0x00, 0xc1, 0x3b, 0x00, 0x74, 0x68, 0x65, 0x00, 0x6c,
370x00, 0x02, 0x39, 0x00, 0x02, 0x6d, 0x00, 0x04, 0x5c, 0x00, 370x61, 0x72, 0x67, 0x65, 0x72, 0x0b, 0x00, 0x02, 0x39, 0x00,
380x11, 0x2c, 0x1a, 0x00, 0x01, 0x48, 0x00, 0x61, 0x70, 0x6f, 380x02, 0x6d, 0x00, 0x04, 0x5c, 0x00, 0x11, 0x2c, 0x1a, 0x00,
390x69, 0x6e, 0x74, 0x73, 0x1f, 0x00, 0x81, 0x67, 0x65, 0x74, 390x01, 0x48, 0x00, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73,
400x00, 0x28, 0x61, 0x6e, 0x64, 0x1d, 0x00, 0x61, 0x66, 0x61, 400x1f, 0x00, 0x81, 0x67, 0x65, 0x74, 0x00, 0x28, 0x61, 0x6e,
410x73, 0x74, 0x65, 0x72, 0x18, 0x00, 0x02, 0x9c, 0x00, 0x00, 410x64, 0x1d, 0x00, 0x61, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72,
420x15, 0x00, 0xcb, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x29, 0x2e, 420x18, 0x00, 0x02, 0x9c, 0x00, 0x00, 0x15, 0x00, 0xcb, 0x61,
430x00, 0x00, 0x00, 0x49, 0x66, 0x1b, 0x00, 0x01, 0xed, 0x00, 430x72, 0x65, 0x6e, 0x61, 0x29, 0x2e, 0x00, 0x00, 0x00, 0x49,
440x00, 0x13, 0x00, 0x44, 0x77, 0x69, 0x6e, 0x2e, 0x1f, 0x00, 440x66, 0x1b, 0x00, 0x01, 0xed, 0x00, 0x00, 0x13, 0x00, 0x44,
450xf0, 0x01, 0x65, 0x6e, 0x64, 0x00, 0x75, 0x70, 0x00, 0x77, 450x77, 0x69, 0x6e, 0x2e, 0x1f, 0x00, 0xf0, 0x01, 0x65, 0x6e,
460x69, 0x74, 0x68, 0x00, 0x6e, 0x6f, 0x74, 0x68, 0xd4, 0x00, 460x64, 0x00, 0x75, 0x70, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00,
470xa4, 0x62, 0x75, 0x74, 0x00, 0x73, 0x69, 0x6e, 0x67, 0x6c, 470x6e, 0x6f, 0x74, 0x68, 0xd4, 0x00, 0xa4, 0x62, 0x75, 0x74,
480x65, 0x10, 0x01, 0x61, 0x00, 0x28, 0x69, 0x2e, 0x65, 0x2e, 480x00, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x10, 0x01, 0x61,
490x94, 0x00, 0x11, 0x72, 0x64, 0x00, 0x32, 0x00, 0x6e, 0x6f, 490x00, 0x28, 0x69, 0x2e, 0x65, 0x2e, 0x94, 0x00, 0x11, 0x72,
500x9d, 0x00, 0x95, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x61, 0x62, 500x64, 0x00, 0x32, 0x00, 0x6e, 0x6f, 0x9d, 0x00, 0x95, 0x63,
510x6c, 0x65, 0xff, 0x00, 0x51, 0x6c, 0x65, 0x66, 0x74, 0x29, 510x6c, 0x69, 0x63, 0x6b, 0x61, 0x62, 0x6c, 0x65, 0xff, 0x00,
520x5c, 0x00, 0x40, 0x6c, 0x6f, 0x73, 0x65, 0x8a, 0x00, 0x50, 520x51, 0x6c, 0x65, 0x66, 0x74, 0x29, 0x5c, 0x00, 0x40, 0x6c,
530x52, 0x65, 0x6d, 0x6f, 0x76, 0x59, 0x00, 0x14, 0x61, 0xeb, 530x6f, 0x73, 0x65, 0x8a, 0x00, 0x50, 0x52, 0x65, 0x6d, 0x6f,
540x00, 0x63, 0x63, 0x61, 0x75, 0x73, 0x65, 0x73, 0xfd, 0x00, 540x76, 0x59, 0x00, 0x14, 0x61, 0xeb, 0x00, 0x63, 0x63, 0x61,
550x20, 0x73, 0x74, 0x33, 0x01, 0x05, 0xa2, 0x00, 0xf0, 0x06, 550x75, 0x73, 0x65, 0x73, 0xfd, 0x00, 0x20, 0x73, 0x74, 0x33,
560x74, 0x6f, 0x00, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 560x01, 0x05, 0xa2, 0x00, 0xf0, 0x06, 0x74, 0x6f, 0x00, 0x73,
570x00, 0x75, 0x70, 0x3a, 0x00, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 570x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x00, 0x75, 0x70, 0x3a,
580x73, 0x4d, 0x01, 0x11, 0x74, 0x79, 0x00, 0xf3, 0x13, 0x73, 580x00, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x01, 0x11,
590x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x00, 0x77, 590x74, 0x79, 0x00, 0xf3, 0x13, 0x73, 0x75, 0x73, 0x70, 0x65,
600x69, 0x6c, 0x6c, 0x00, 0x66, 0x61, 0x6c, 0x6c, 0x00, 0x64, 600x6e, 0x64, 0x65, 0x64, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00,
610x6f, 0x77, 0x6e, 0x00, 0x28, 0x66, 0x69, 0x72, 0x73, 0x74, 610x66, 0x61, 0x6c, 0x6c, 0x00, 0x64, 0x6f, 0x77, 0x6e, 0x00,
620x29, 0x2c, 0x00, 0x20, 0x01, 0x70, 0x6e, 0x00, 0x65, 0x6d, 620x28, 0x66, 0x69, 0x72, 0x73, 0x74, 0x29, 0x2c, 0x00, 0x20,
630x70, 0x74, 0x79, 0x7e, 0x01, 0x41, 0x75, 0x6d, 0x6e, 0x73, 630x01, 0x70, 0x6e, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x7e,
640x3d, 0x00, 0xb2, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 640x01, 0x41, 0x75, 0x6d, 0x6e, 0x73, 0x3d, 0x00, 0xb2, 0x66,
650x66, 0x72, 0x6f, 0x6d, 0x7d, 0x00, 0x00, 0x6b, 0x01, 0x00, 650x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x66, 0x72, 0x6f, 0x6d,
660xa3, 0x00, 0x00, 0x21, 0x02, 0x20, 0x00, 0x47, 0x05, 0x00, 660x7d, 0x00, 0x00, 0x6b, 0x01, 0x00, 0xa3, 0x00, 0x00, 0x37,
670x31, 0x77, 0x61, 0x73, 0xd5, 0x01, 0x61, 0x72, 0x69, 0x62, 670x02, 0x20, 0x00, 0x47, 0x05, 0x00, 0x31, 0x77, 0x61, 0x73,
680x75, 0x74, 0x65, 0x8f, 0x00, 0x40, 0x74, 0x68, 0x69, 0x73, 680xd5, 0x01, 0x61, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x8f,
690x47, 0x00, 0x40, 0x6c, 0x65, 0x63, 0x74, 0xbe, 0x00, 0xf0, 690x00, 0x40, 0x74, 0x68, 0x69, 0x73, 0x47, 0x00, 0x40, 0x6c,
700x00, 0x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 700x65, 0x63, 0x74, 0xbe, 0x00, 0xf0, 0x00, 0x62, 0x79, 0x00,
710x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x40, 0x00, 0x47, 0x31, 710x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76,
720x33, 0x2e, 0x31, 0x66, 0x02, 0x01, 0x41, 0x00, 0x30, 0x6f, 720x65, 0x79, 0x40, 0x00, 0x47, 0x31, 0x33, 0x2e, 0x31, 0x7c,
730x6c, 0x73, 0x6f, 0x02, 0x10, 0x54, 0x3e, 0x00, 0x10, 0x67, 730x02, 0x01, 0x41, 0x00, 0x80, 0x6f, 0x6c, 0x73, 0x20, 0x00,
740x5b, 0x00, 0xd2, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 740x00, 0x00, 0x54, 0x3e, 0x00, 0x10, 0x67, 0x5b, 0x00, 0xd2,
750x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x73, 0x01, 0x43, 0x65, 750x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x70, 0x6c, 0x61,
760x69, 0x74, 0x68, 0x06, 0x02, 0xb3, 0x6b, 0x65, 0x79, 0x62, 760x79, 0x65, 0x64, 0x73, 0x01, 0x43, 0x65, 0x69, 0x74, 0x68,
770x6f, 0x61, 0x72, 0x64, 0x00, 0x6f, 0x72, 0xfc, 0x01, 0x37, 770x06, 0x02, 0xb3, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72,
780x75, 0x73, 0x65, 0xc9, 0x01, 0x00, 0x58, 0x01, 0x11, 0x2d, 780x64, 0x00, 0x6f, 0x72, 0xfc, 0x01, 0x37, 0x75, 0x73, 0x65,
790x6f, 0x01, 0x80, 0x00, 0x61, 0x6e, 0x00, 0x75, 0x6e, 0x73, 790xc9, 0x01, 0x00, 0x58, 0x01, 0x11, 0x2d, 0x6f, 0x01, 0x80,
800x65, 0x90, 0x00, 0x23, 0x65, 0x64, 0x54, 0x01, 0x90, 0x2c, 800x00, 0x61, 0x6e, 0x00, 0x75, 0x6e, 0x73, 0x65, 0x90, 0x00,
810x00, 0x69, 0x74, 0x00, 0x62, 0x65, 0x63, 0x6f, 0x99, 0x00, 810x23, 0x65, 0x64, 0x54, 0x01, 0x90, 0x2c, 0x00, 0x69, 0x74,
820x05, 0x1c, 0x00, 0x92, 0x28, 0x70, 0x6f, 0x73, 0x73, 0x69, 820x00, 0x62, 0x65, 0x63, 0x6f, 0x99, 0x00, 0x05, 0x1c, 0x00,
830x62, 0x6c, 0x79, 0x08, 0x02, 0x00, 0x85, 0x01, 0x00, 0x5c, 830x92, 0x28, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79,
840x00, 0x73, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x28, 840x08, 0x02, 0x00, 0x85, 0x01, 0x00, 0x5c, 0x00, 0x73, 0x63,
850x00, 0x4f, 0x69, 0x6f, 0x6e, 0x29, 0x69, 0x00, 0x03, 0x00, 850x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x28, 0x00, 0x4f, 0x69,
860x2c, 0x00, 0x0f, 0x68, 0x00, 0x01, 0x01, 0x7b, 0x01, 0x10, 860x6f, 0x6e, 0x29, 0x69, 0x00, 0x03, 0x00, 0x2c, 0x00, 0x0f,
870x62, 0xbd, 0x01, 0x56, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x94, 870x68, 0x00, 0x01, 0x01, 0x7b, 0x01, 0x10, 0x62, 0xbd, 0x01,
880x02, 0x0d, 0xce, 0x01, 0x03, 0xcb, 0x01, 0xd8, 0x64, 0x00, 880x56, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x94, 0x02, 0x0d, 0xce,
890x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 890x01, 0x03, 0xcb, 0x01, 0xd8, 0x64, 0x00, 0x69, 0x6d, 0x6d,
900x79, 0x6d, 0x00, 0x01, 0x82, 0x01, 0x0f, 0x6e, 0x00, 0x14, 900x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x6d, 0x00,
910x06, 0xf4, 0x00, 0x11, 0x2e, 0x58, 0x01, 0x01, 0xc8, 0x00, 910x01, 0x82, 0x01, 0x0f, 0x6e, 0x00, 0x14, 0x06, 0xf4, 0x00,
920x30, 0x73, 0x6f, 0x72, 0x3b, 0x01, 0x41, 0x73, 0x00, 0x6d, 920x11, 0x2e, 0x58, 0x01, 0x01, 0xc8, 0x00, 0x30, 0x73, 0x6f,
930x6f, 0xd3, 0x03, 0x03, 0x13, 0x00, 0x65, 0x61, 0x72, 0x6f, 930x72, 0x3b, 0x01, 0x41, 0x73, 0x00, 0x6d, 0x6f, 0xd3, 0x03,
940x75, 0x6e, 0x64, 0x88, 0x00, 0x74, 0x2e, 0x00, 0x50, 0x72, 940x03, 0x13, 0x00, 0x65, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64,
950x65, 0x73, 0x73, 0x00, 0x01, 0x50, 0x53, 0x70, 0x61, 0x63, 950x88, 0x00, 0x74, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73,
960x65, 0x69, 0x01, 0x43, 0x45, 0x6e, 0x74, 0x65, 0x40, 0x00, 960x00, 0x01, 0x50, 0x53, 0x70, 0x61, 0x63, 0x65, 0x69, 0x01,
970x40, 0x77, 0x68, 0x69, 0x6c, 0xb5, 0x03, 0x05, 0x56, 0x00, 970x43, 0x45, 0x6e, 0x74, 0x65, 0x40, 0x00, 0x40, 0x77, 0x68,
980x5f, 0x69, 0x73, 0x00, 0x69, 0x6e, 0x6c, 0x01, 0x02, 0x03, 980x69, 0x6c, 0xb5, 0x03, 0x05, 0x56, 0x00, 0x5f, 0x69, 0x73,
990xa6, 0x00, 0x74, 0x73, 0x00, 0x69, 0x74, 0x3b, 0x00, 0x70, 990x00, 0x69, 0x6e, 0x6c, 0x01, 0x02, 0x03, 0xa6, 0x00, 0x74,
1000x59, 0x00, 0x0b, 0x55, 0x00, 0x53, 0x61, 0x67, 0x61, 0x69, 1000x73, 0x00, 0x69, 0x74, 0x3b, 0x00, 0x70, 0x59, 0x00, 0x0b,
1010x6e, 0x22, 0x01, 0x10, 0x73, 0xc7, 0x00, 0x71, 0x61, 0x73, 1010x55, 0x00, 0x53, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x22, 0x01,
1020x00, 0x61, 0x62, 0x6f, 0x76, 0xd4, 0x01, 0x42, 0x28, 0x41, 1020x10, 0x73, 0xc7, 0x00, 0x71, 0x61, 0x73, 0x00, 0x61, 0x62,
1030x6c, 0x6c, 0xb0, 0x03, 0x21, 0x63, 0x74, 0x36, 0x03, 0x70, 1030x6f, 0x76, 0xd4, 0x01, 0x42, 0x28, 0x41, 0x6c, 0x6c, 0xb0,
1040x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x29, 0x01, 0x00, 1040x03, 0x21, 0x63, 0x74, 0x36, 0x03, 0x70, 0x64, 0x65, 0x73,
1050x5f, 0x00, 0x02, 0x69, 0x02, 0x31, 0x32, 0x2e, 0x31, 0xb7, 1050x63, 0x72, 0x69, 0x62, 0x29, 0x01, 0x00, 0x5f, 0x00, 0x02,
1060x02, 0xa0, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 1060x69, 0x02, 0x31, 0x32, 0x2e, 0x31, 0xb7, 0x02, 0xa0, 0x61,
1070x69, 0x6c, 0x6a, 0x03, 0x22, 0x2e, 0x29, 0x71, 0x02, 0x17, 1070x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x6a,
1080x32, 0x71, 0x02, 0x93, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 1080x03, 0x22, 0x2e, 0x29, 0x71, 0x02, 0x17, 0x32, 0x71, 0x02,
1090x74, 0x65, 0x72, 0x73, 0x02, 0x46, 0x65, 0x73, 0x65, 0x00, 1090x93, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
1100x14, 0x00, 0x02, 0x45, 0x00, 0x04, 0x40, 0x00, 0x06, 0xff, 1100x73, 0x02, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02,
1110x02, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 1110x45, 0x00, 0x04, 0x40, 0x00, 0x06, 0xff, 0x02, 0xe1, 0x60,
1120x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x73, 0x00, 0x22, 0x6f, 1120x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
1130x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 1130x00, 0x6f, 0x70, 0x73, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00,
1140x6d, 0x65, 0x6e, 0x75, 0xaf, 0x00, 0x90, 0x57, 0x69, 0x64, 1140xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e,
1150x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xae, 0x01, 0x00, 0x2f, 1150x75, 0xaf, 0x00, 0x90, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c,
1160x03, 0x30, 0x69, 0x7a, 0x65, 0xe7, 0x01, 0x01, 0xe3, 0x01, 1160x00, 0x48, 0x65, 0xae, 0x01, 0x00, 0x2f, 0x03, 0x30, 0x69,
1170x24, 0x69, 0x6e, 0x27, 0x04, 0x00, 0x2b, 0x00, 0x72, 0x4e, 1170x7a, 0x65, 0xe7, 0x01, 0x01, 0xe3, 0x01, 0x24, 0x69, 0x6e,
1180x6f, 0x2e, 0x20, 0x6f, 0x66, 0x20, 0xf4, 0x04, 0x10, 0x73, 1180x27, 0x04, 0x00, 0x2b, 0x00, 0x72, 0x4e, 0x6f, 0x2e, 0x20,
1190x11, 0x00, 0x50, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x00, 1190x6f, 0x66, 0x20, 0xf4, 0x04, 0x10, 0x73, 0x11, 0x00, 0x50,
1200x51, 0x64, 0x69, 0x66, 0x66, 0x65, 0x78, 0x02, 0x04, 0x1e, 1200x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x00, 0x51, 0x64, 0x69,
1210x00, 0x22, 0x75, 0x73, 0x62, 0x03, 0x00, 0x98, 0x03, 0x05, 1210x66, 0x66, 0x65, 0x78, 0x02, 0x04, 0x1e, 0x00, 0x22, 0x75,
1220xac, 0x01, 0x16, 0x3b, 0xfb, 0x04, 0x03, 0x28, 0x00, 0x12, 1220x73, 0x62, 0x03, 0x00, 0x98, 0x03, 0x05, 0xac, 0x01, 0x16,
1230x2c, 0xf0, 0x04, 0x42, 0x65, 0x77, 0x65, 0x72, 0x38, 0x05, 1230x3b, 0xfb, 0x04, 0x03, 0x28, 0x00, 0x12, 0x2c, 0xf0, 0x04,
1240x08, 0x6c, 0x05, 0x02, 0x24, 0x00, 0x03, 0xf6, 0x03, 0x26, 1240x42, 0x65, 0x77, 0x65, 0x72, 0x38, 0x05, 0x08, 0x6c, 0x05,
1250x75, 0x73, 0x3d, 0x00, 0x00, 0x6f, 0x00, 0x50, 0x69, 0x63, 1250x02, 0x24, 0x00, 0x03, 0xf6, 0x03, 0x26, 0x75, 0x73, 0x3d,
1260x75, 0x6c, 0x74, 0x7d, 0x01, 0x21, 0x69, 0x73, 0x59, 0x04, 1260x00, 0x00, 0x6f, 0x00, 0x50, 0x69, 0x63, 0x75, 0x6c, 0x74,
1270xbb, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 1270x7d, 0x01, 0x21, 0x69, 0x73, 0x59, 0x04, 0xbb, 0x75, 0x63,
1280x6c, 0x79, 0x1a, 0x05, 0x01, 0x06, 0x04, 0x20, 0x63, 0x6f, 1280x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x1a,
1290x23, 0x03, 0xb3, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 1290x05, 0x01, 0x06, 0x04, 0x20, 0x63, 0x6f, 0x23, 0x03, 0xb3,
1300x00, 0x00, 0x00, 0x43, 0xc8, 0x03, 0x01, 0x26, 0x00, 0xf2, 1300x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x00, 0x00,
1310x02, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x00, 0x6d, 1310x43, 0xc8, 0x03, 0x01, 0x26, 0x00, 0xf2, 0x02, 0x70, 0x72,
1320x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0xbf, 0x00, 1320x65, 0x63, 0x69, 0x73, 0x65, 0x00, 0x6d, 0x65, 0x63, 0x68,
1330x52, 0x66, 0x6f, 0x72, 0x00, 0x73, 0x39, 0x00, 0x30, 0x2e, 1330x61, 0x6e, 0x69, 0x73, 0x6d, 0xbf, 0x00, 0x52, 0x66, 0x6f,
1340x00, 0x57, 0xd5, 0x03, 0x00, 0x2d, 0x00, 0x40, 0x64, 0x65, 1340x72, 0x00, 0x73, 0x39, 0x00, 0x30, 0x2e, 0x00, 0x57, 0xd5,
1350x66, 0x61, 0x7b, 0x00, 0x02, 0x4b, 0x00, 0xf8, 0x02, 0x2c, 1350x03, 0x00, 0x2d, 0x00, 0x40, 0x64, 0x65, 0x66, 0x61, 0x7b,
1360x00, 0x60, 0x28, 0x6e, 0x2d, 0x32, 0x29, 0x5e, 0x32, 0x27, 1360x00, 0x02, 0x4b, 0x00, 0xf8, 0x02, 0x2c, 0x00, 0x60, 0x28,
1370x2c, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0xc1, 0x00, 0x46, 0x74, 1370x6e, 0x2d, 0x32, 0x29, 0x5e, 0x32, 0x27, 0x2c, 0x00, 0x6f,
1380x68, 0x72, 0x65, 0x6a, 0x05, 0x22, 0x6f, 0x72, 0xbe, 0x00, 1380x6e, 0x6c, 0x79, 0xc1, 0x00, 0x46, 0x74, 0x68, 0x72, 0x65,
1390x01, 0xf5, 0x02, 0x20, 0x73, 0x63, 0x0b, 0x00, 0x34, 0x61, 1390x6a, 0x05, 0x22, 0x6f, 0x72, 0xbe, 0x00, 0x01, 0xf5, 0x02,
1400x6e, 0x79, 0x05, 0x06, 0x00, 0x05, 0x05, 0x27, 0x6c, 0x6c, 1400x20, 0x73, 0x63, 0x0b, 0x00, 0x34, 0x61, 0x6e, 0x79, 0x05,
1410x68, 0x00, 0xb1, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 1410x06, 0x00, 0x05, 0x05, 0x27, 0x6c, 0x6c, 0x68, 0x00, 0xb1,
1420x74, 0x69, 0x76, 0x65, 0x64, 0x00, 0x10, 0x31, 0x64, 0x00, 1420x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76,
1430x05, 0x76, 0x00, 0x08, 0x66, 0x00, 0x25, 0x77, 0x6f, 0x64, 1430x65, 0x64, 0x00, 0x10, 0x31, 0x64, 0x00, 0x05, 0x76, 0x00,
1440x00, 0x03, 0x57, 0x00, 0x02, 0x55, 0x00, 0x52, 0x00, 0x65, 1440x08, 0x66, 0x00, 0x25, 0x77, 0x6f, 0x64, 0x00, 0x03, 0x57,
1450x61, 0x63, 0x68, 0x36, 0x05, 0x03, 0x94, 0x06, 0x04, 0x36, 1450x00, 0x02, 0x55, 0x00, 0x52, 0x00, 0x65, 0x61, 0x63, 0x68,
1460x00, 0x02, 0x27, 0x00, 0x31, 0x72, 0x65, 0x6c, 0x5f, 0x00, 1460x36, 0x05, 0x03, 0x94, 0x06, 0x04, 0x36, 0x00, 0x02, 0x27,
1470x28, 0x6c, 0x79, 0x8f, 0x06, 0x00, 0x2f, 0x01, 0xf2, 0x02, 1470x00, 0x31, 0x72, 0x65, 0x6c, 0x5f, 0x00, 0x28, 0x6c, 0x79,
1480x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x73, 0x6f, 0x6c, 1480x8f, 0x06, 0x00, 0x2f, 0x01, 0xf2, 0x02, 0x45, 0x6e, 0x73,
1490x75, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0xd7, 0x03, 0x01, 1490x75, 0x72, 0x65, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x62, 0x69,
1500x2f, 0x05, 0x03, 0x4f, 0x02, 0x00, 0x77, 0x01, 0x30, 0x69, 1500x6c, 0x69, 0x74, 0x79, 0xd7, 0x03, 0x01, 0x2f, 0x05, 0x03,
1510x63, 0x6b, 0x23, 0x04, 0x09, 0x16, 0x01, 0xd0, 0x74, 0x61, 1510x4f, 0x02, 0x00, 0x77, 0x01, 0x30, 0x69, 0x63, 0x6b, 0x23,
1520x74, 0x65, 0x29, 0x2c, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 1520x04, 0x09, 0x16, 0x01, 0xd0, 0x74, 0x61, 0x74, 0x65, 0x29,
1530x61, 0x59, 0x03, 0x00, 0x83, 0x01, 0x15, 0x73, 0xea, 0x03, 1530x2c, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x59, 0x03,
1540x82, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x1e, 1540x00, 0x83, 0x01, 0x15, 0x73, 0xea, 0x03, 0x82, 0x67, 0x75,
1550x02, 0x02, 0xad, 0x07, 0x71, 0x74, 0x00, 0x6c, 0x65, 0x61, 1550x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x1e, 0x02, 0x02, 0xad,
1560x73, 0x74, 0x52, 0x07, 0x00, 0x73, 0x00, 0x00, 0x60, 0x00, 1560x07, 0x71, 0x74, 0x00, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x52,
1570x07, 0x49, 0x04, 0x60, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x69, 1570x07, 0x00, 0x73, 0x00, 0x00, 0x60, 0x00, 0x07, 0x49, 0x04,
1580x7d, 0x04, 0x12, 0x66, 0x2f, 0x02, 0x01, 0x73, 0x05, 0x03, 1580x60, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x69, 0x7d, 0x04, 0x12,
1590x61, 0x00, 0x22, 0x6f, 0x72, 0x5b, 0x00, 0x70, 0x6e, 0x6f, 1590x66, 0x2f, 0x02, 0x01, 0x73, 0x05, 0x03, 0x61, 0x00, 0x22,
1600x74, 0x00, 0x74, 0x72, 0x79, 0x55, 0x00, 0x05, 0x63, 0x00, 1600x6f, 0x72, 0x5b, 0x00, 0x70, 0x6e, 0x6f, 0x74, 0x00, 0x74,
1610x01, 0x4d, 0x00, 0x00, 0x17, 0x03, 0x01, 0x83, 0x00, 0x14, 1610x72, 0x79, 0x55, 0x00, 0x05, 0x63, 0x00, 0x01, 0x4d, 0x00,
1620x3b, 0x71, 0x04, 0xd0, 0x2c, 0x00, 0x68, 0x6f, 0x77, 0x65, 1620x00, 0x17, 0x03, 0x01, 0x83, 0x00, 0x14, 0x3b, 0x71, 0x04,
1630x76, 0x65, 0x72, 0x2c, 0x00, 0x73, 0x74, 0x3c, 0x00, 0x11, 1630xd0, 0x2c, 0x00, 0x68, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72,
1640x65, 0xee, 0x00, 0x02, 0x89, 0x06, 0x06, 0x08, 0x07, 0x05, 1640x2c, 0x00, 0x73, 0x74, 0x3c, 0x00, 0x11, 0x65, 0xee, 0x00,
1650x97, 0x00, 0x16, 0x32, 0xc2, 0x01, 0x11, 0x66, 0x53, 0x01, 1650x02, 0x89, 0x06, 0x06, 0x08, 0x07, 0x05, 0x97, 0x00, 0x16,
1660x04, 0x99, 0x02, 0x26, 0x6f, 0x6e, 0x0e, 0x05, 0x02, 0x3b, 1660x32, 0xc2, 0x01, 0x11, 0x66, 0x53, 0x01, 0x04, 0x99, 0x02,
1670x00, 0x00, 0xff, 0x00, 0x85, 0x72, 0x74, 0x00, 0x28, 0x73, 1670x26, 0x6f, 0x6e, 0x0e, 0x05, 0x02, 0x3b, 0x00, 0x00, 0xff,
1680x69, 0x6e, 0x63, 0x86, 0x08, 0x02, 0x0f, 0x06, 0x62, 0x78, 1680x00, 0x85, 0x72, 0x74, 0x00, 0x28, 0x73, 0x69, 0x6e, 0x63,
1690x61, 0x63, 0x74, 0x6c, 0x79, 0xe4, 0x00, 0x01, 0x4f, 0x00, 1690x86, 0x08, 0x02, 0x0f, 0x06, 0x62, 0x78, 0x61, 0x63, 0x74,
1700x00, 0x4e, 0x00, 0x74, 0x61, 0x00, 0x67, 0x69, 0x76, 0x65, 1700x6c, 0x79, 0xe4, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x4e, 0x00,
1710x6e, 0x51, 0x00, 0x20, 0x69, 0x73, 0x48, 0x01, 0x40, 0x69, 1710x74, 0x61, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x51, 0x00,
1720x6e, 0x69, 0x74, 0x94, 0x01, 0x23, 0x69, 0x6e, 0xc0, 0x00, 1720x20, 0x69, 0x73, 0x48, 0x01, 0x40, 0x69, 0x6e, 0x69, 0x74,
1730x41, 0x29, 0x2e, 0x00, 0x47, 0x45, 0x01, 0x06, 0x55, 0x01, 1730x94, 0x01, 0x23, 0x69, 0x6e, 0xc0, 0x00, 0x41, 0x29, 0x2e,
1740x01, 0x54, 0x00, 0x08, 0x8f, 0x01, 0x50, 0x64, 0x69, 0x73, 1740x00, 0x47, 0x45, 0x01, 0x06, 0x55, 0x01, 0x01, 0x54, 0x00,
1750x61, 0x62, 0x8e, 0x05, 0x31, 0x6d, 0x61, 0x79, 0xe6, 0x06, 1750x08, 0x8f, 0x01, 0x50, 0x64, 0x69, 0x73, 0x61, 0x62, 0x8e,
1760x00, 0xa3, 0x04, 0x01, 0xd6, 0x01, 0x02, 0x57, 0x03, 0x43, 1760x05, 0x31, 0x6d, 0x61, 0x79, 0xe6, 0x06, 0x00, 0xa3, 0x04,
1770x61, 0x72, 0x65, 0x61, 0x55, 0x03, 0x05, 0xd7, 0x08, 0x02, 1770x01, 0xd6, 0x01, 0x02, 0x57, 0x03, 0x43, 0x61, 0x72, 0x65,
1780x76, 0x00, 0x10, 0x2c, 0xe7, 0x00, 0x11, 0x64, 0x40, 0x05, 1780x61, 0x55, 0x03, 0x05, 0xd7, 0x08, 0x02, 0x76, 0x00, 0x10,
1790xf1, 0x00, 0x6f, 0x00, 0x6f, 0x70, 0x70, 0x6f, 0x72, 0x74, 1790x2c, 0xe7, 0x00, 0x11, 0x64, 0x40, 0x05, 0xf1, 0x00, 0x6f,
1800x75, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x01, 0x03, 0x00, 1800x00, 0x6f, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x75, 0x6e, 0x69,
1810xb8, 0x08, 0x22, 0x65, 0x72, 0x32, 0x02, 0x11, 0x73, 0xc1, 1810x74, 0x69, 0x65, 0x73, 0x01, 0x03, 0x00, 0xb8, 0x08, 0x22,
1820x03, 0x11, 0x79, 0xef, 0x06, 0x01, 0xba, 0x04, 0x80, 0x74, 1820x65, 0x72, 0x32, 0x02, 0x11, 0x73, 0xc1, 0x03, 0x11, 0x79,
1830x61, 0x6b, 0x65, 0x00, 0x6c, 0x65, 0x73, 0x0c, 0x02, 0x21, 1830xef, 0x06, 0x01, 0xba, 0x04, 0x80, 0x74, 0x61, 0x6b, 0x65,
1840x6d, 0x65, 0x7d, 0x01, 0x90, 0x65, 0x6e, 0x65, 0x72, 0x61, 1840x00, 0x6c, 0x65, 0x73, 0x0c, 0x02, 0x21, 0x6d, 0x65, 0x7d,
1850x74, 0x65, 0x2e, 0x00, 1850x01, 0x90, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x2e,
1860x00,
186}; 187};
187 188
188const unsigned short help_text_len = 2470; 189const unsigned short help_text_len = 2492;
189const unsigned short help_text_words = 444; 190const unsigned short help_text_words = 445;
191const bool help_valid = true;
190const char quick_help_text[] = "Clear the grid by removing touching groups of the same colour squares."; 192const char quick_help_text[] = "Clear the grid by removing touching groups of the same colour squares.";
diff --git a/apps/plugins/puzzles/help/signpost.c b/apps/plugins/puzzles/help/signpost.c
index 6f89cbdd62..34e043da57 100644
--- a/apps/plugins/puzzles/help/signpost.c
+++ b/apps/plugins/puzzles/help/signpost.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,208 +6,221 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 158, TEXT_CENTER | C_RED }, 9 { 159, TEXT_CENTER | C_RED },
10 { 495, TEXT_CENTER | C_RED }, 10 { 496, TEXT_CENTER | C_RED },
11 { 512, TEXT_UNDERLINE },
12 { 513, TEXT_UNDERLINE }, 11 { 513, TEXT_UNDERLINE },
13 { 523, TEXT_UNDERLINE }, 12 { 514, TEXT_UNDERLINE },
13 { 524, TEXT_UNDERLINE },
14 { 569, TEXT_CENTER | C_RED },
14 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
15}; 16};
16 17
17/* orig 3054 comp 1895 ratio 0.620498 level 10 saved 1159 */ 18/* orig 3255 comp 2009 ratio 0.617204 level 10 saved 1246 */
18const char help_text[] = { 19const char help_text[] = {
190xf3, 0x29, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x33, 0x34, 0x3a, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x70, 0x6f, 210x33, 0x34, 0x3a, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x70, 0x6f,
210x73, 0x74, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 220x73, 0x74, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf3, 0x14,
220x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 230x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76,
230x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 240x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f,
240x65, 0x73, 0x3b, 0x00, 0x65, 0x61, 0x63, 0x68, 0x0e, 0x00, 250x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x3b,
250xf4, 0x23, 0x00, 0x28, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 260x00, 0x65, 0x61, 0x63, 0x68, 0x0e, 0x00, 0xf4, 0x23, 0x00,
260x00, 0x74, 0x68, 0x65, 0x00, 0x6c, 0x61, 0x73, 0x74, 0x00, 270x28, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x00, 0x74, 0x68,
270x6f, 0x6e, 0x65, 0x29, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 280x65, 0x00, 0x6c, 0x61, 0x73, 0x74, 0x00, 0x6f, 0x6e, 0x65,
280x69, 0x6e, 0x73, 0x00, 0x61, 0x6e, 0x00, 0x61, 0x72, 0x72, 290x29, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73,
290x6f, 0x77, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x73, 0x6f, 300x00, 0x61, 0x6e, 0x00, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x2c,
300x6d, 0x65, 0x47, 0x00, 0x54, 0x00, 0x61, 0x6c, 0x73, 0x6f, 310x00, 0x61, 0x6e, 0x64, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x47,
310x29, 0x00, 0x90, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 320x00, 0x54, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x29, 0x00, 0x90,
320x73, 0x2e, 0x78, 0x00, 0xa1, 0x72, 0x00, 0x6a, 0x6f, 0x62, 330x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x78,
330x00, 0x69, 0x73, 0x00, 0x74, 0x20, 0x00, 0x32, 0x6e, 0x65, 340x00, 0xa1, 0x72, 0x00, 0x6a, 0x6f, 0x62, 0x00, 0x69, 0x73,
340x63, 0x5f, 0x00, 0x04, 0x39, 0x00, 0x91, 0x74, 0x6f, 0x00, 350x00, 0x74, 0x20, 0x00, 0x32, 0x6e, 0x65, 0x63, 0x5f, 0x00,
350x66, 0x6f, 0x72, 0x6d, 0x00, 0x61, 0x3e, 0x00, 0x90, 0x69, 360x04, 0x39, 0x00, 0x91, 0x74, 0x6f, 0x00, 0x66, 0x6f, 0x72,
360x6e, 0x75, 0x6f, 0x75, 0x73, 0x00, 0x6c, 0x69, 0x7c, 0x00, 370x6d, 0x00, 0x61, 0x3e, 0x00, 0x90, 0x69, 0x6e, 0x75, 0x6f,
370x14, 0x66, 0x49, 0x00, 0xe1, 0x00, 0x73, 0x74, 0x61, 0x72, 380x75, 0x73, 0x00, 0x6c, 0x69, 0x7c, 0x00, 0x14, 0x66, 0x49,
380x74, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x74, 0x00, 0x31, 0x7d, 390x00, 0xe1, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e,
390x00, 0x91, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x00, 0x69, 400x67, 0x00, 0x61, 0x74, 0x00, 0x31, 0x7d, 0x00, 0x91, 0x6c,
400x6e, 0x4d, 0x00, 0x90, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 410x69, 0x6e, 0x6b, 0x65, 0x64, 0x00, 0x69, 0x6e, 0x4d, 0x00,
410x69, 0x6f, 0x6e, 0x35, 0x00, 0x00, 0x11, 0x00, 0x01, 0xa7, 420x90, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
420x00, 0x66, 0x73, 0x00, 0x2d, 0x00, 0x73, 0x6f, 0x10, 0x00, 430x35, 0x00, 0x00, 0x11, 0x00, 0x01, 0xa7, 0x00, 0x66, 0x73,
430x77, 0x00, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x7f, 0x00, 440x00, 0x2d, 0x00, 0x73, 0x6f, 0x10, 0x00, 0x77, 0x00, 0x69,
440x51, 0x00, 0x77, 0x69, 0x74, 0x68, 0x10, 0x00, 0x02, 0x6a, 450x6e, 0x73, 0x69, 0x64, 0x65, 0x7f, 0x00, 0x51, 0x00, 0x77,
450x00, 0xd0, 0x00, 0x31, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 460x69, 0x74, 0x68, 0x10, 0x00, 0x02, 0x6a, 0x00, 0xd0, 0x00,
460x70, 0x6f, 0x69, 0x6e, 0x74, 0x9b, 0x00, 0x07, 0x2b, 0x00, 470x31, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x70, 0x6f, 0x69,
470x03, 0xdd, 0x00, 0x00, 0x86, 0x00, 0x07, 0x31, 0x00, 0x8f, 480x6e, 0x74, 0x9b, 0x00, 0x07, 0x2b, 0x00, 0x03, 0xdd, 0x00,
480x32, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x38, 0x00, 490x00, 0x86, 0x00, 0x07, 0x31, 0x00, 0x8f, 0x32, 0x2c, 0x00,
490x1d, 0x97, 0x33, 0x2c, 0x00, 0x65, 0x74, 0x63, 0x2e, 0x00, 500x77, 0x68, 0x69, 0x63, 0x68, 0x38, 0x00, 0x1d, 0x97, 0x33,
500x45, 0x7e, 0x01, 0xf1, 0x0e, 0x63, 0x61, 0x6e, 0x00, 0x62, 510x2c, 0x00, 0x65, 0x74, 0x63, 0x2e, 0x00, 0x45, 0x7e, 0x01,
510x65, 0x00, 0x61, 0x6e, 0x79, 0x00, 0x64, 0x69, 0x73, 0x74, 520xf1, 0x0e, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x61,
520x61, 0x6e, 0x63, 0x65, 0x00, 0x61, 0x77, 0x61, 0x79, 0x00, 530x6e, 0x79, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63,
530x66, 0x72, 0x6f, 0x6d, 0x3d, 0x00, 0x50, 0x70, 0x72, 0x65, 540x65, 0x00, 0x61, 0x77, 0x61, 0x79, 0x00, 0x66, 0x72, 0x6f,
540x76, 0x69, 0x21, 0x01, 0xc0, 0x6f, 0x6e, 0x65, 0x2c, 0x00, 550x6d, 0x3d, 0x00, 0x50, 0x70, 0x72, 0x65, 0x76, 0x69, 0x21,
550x61, 0x73, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x08, 0x00, 0x20, 560x01, 0xc0, 0x6f, 0x6e, 0x65, 0x2c, 0x00, 0x61, 0x73, 0x00,
560x69, 0x74, 0x63, 0x01, 0x00, 0x92, 0x01, 0x5f, 0x77, 0x68, 570x6c, 0x6f, 0x6e, 0x67, 0x08, 0x00, 0x20, 0x69, 0x74, 0x63,
570x65, 0x72, 0x65, 0x18, 0x01, 0x0b, 0x60, 0x2e, 0x00, 0x00, 580x01, 0x00, 0x92, 0x01, 0x5f, 0x77, 0x68, 0x65, 0x72, 0x65,
580x00, 0x42, 0x79, 0x99, 0x00, 0x31, 0x76, 0x65, 0x6e, 0x1e, 590x18, 0x01, 0x0b, 0x60, 0x2e, 0x00, 0x00, 0x00, 0x42, 0x79,
590x00, 0x00, 0x1b, 0x00, 0x52, 0x66, 0x69, 0x72, 0x73, 0x74, 600x99, 0x00, 0x31, 0x76, 0x65, 0x6e, 0x1e, 0x00, 0x00, 0x1b,
600x5c, 0x01, 0x00, 0xfa, 0x01, 0x04, 0x7b, 0x01, 0x00, 0x99, 610x00, 0x52, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5c, 0x01, 0x00,
610x00, 0x60, 0x73, 0x68, 0x6f, 0x77, 0x6e, 0x3b, 0x75, 0x00, 620xfa, 0x01, 0x04, 0x7b, 0x01, 0x00, 0x99, 0x00, 0x60, 0x73,
620x61, 0x00, 0x6f, 0x72, 0x00, 0x6d, 0x6f, 0x61, 0x00, 0x55, 630x68, 0x6f, 0x77, 0x6e, 0x3b, 0x75, 0x00, 0x61, 0x00, 0x6f,
630x74, 0x65, 0x72, 0x69, 0x6d, 0x27, 0x00, 0x32, 0x6d, 0x61, 640x72, 0x00, 0x6d, 0x6f, 0x61, 0x00, 0x55, 0x74, 0x65, 0x72,
640x79, 0x04, 0x02, 0x82, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 650x69, 0x6d, 0x27, 0x00, 0x32, 0x6d, 0x61, 0x79, 0x04, 0x02,
650x00, 0x61, 0xe6, 0x01, 0x50, 0x62, 0x65, 0x67, 0x69, 0x6e, 660x82, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x00, 0x61, 0xe6,
660xfc, 0x00, 0x00, 0x74, 0x00, 0x60, 0x43, 0x72, 0x65, 0x64, 670x01, 0x50, 0x62, 0x65, 0x67, 0x69, 0x6e, 0xfc, 0x00, 0x00,
670x69, 0x74, 0xef, 0x01, 0xf2, 0x00, 0x00, 0x74, 0x68, 0x69, 680x74, 0x00, 0x60, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0xef,
680x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 690x01, 0xf2, 0x00, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70,
690x6f, 0x07, 0x02, 0xa0, 0x4a, 0x61, 0x6e, 0x6b, 0x6f, 0x00, 700x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x07, 0x02,
700x5b, 0x31, 0x37, 0x5d, 0x54, 0x01, 0x60, 0x6f, 0x00, 0x63, 710xa0, 0x4a, 0x61, 0x6e, 0x6b, 0x6f, 0x00, 0x5b, 0x31, 0x37,
710x61, 0x6c, 0x6c, 0xd9, 0x00, 0xe2, 0x60, 0x50, 0x66, 0x65, 720x5d, 0x54, 0x01, 0x60, 0x6f, 0x00, 0x63, 0x61, 0x6c, 0x6c,
720x69, 0x6c, 0x70, 0x66, 0x61, 0x64, 0x27, 0x00, 0x28, 0x60, 730xd9, 0x00, 0xe2, 0x60, 0x50, 0x66, 0x65, 0x69, 0x6c, 0x70,
730xca, 0x01, 0x60, 0x70, 0x61, 0x74, 0x68, 0x27, 0x29, 0x55, 740x66, 0x61, 0x64, 0x27, 0x00, 0x28, 0x60, 0xca, 0x01, 0x60,
740x00, 0x04, 0xe7, 0x02, 0x41, 0x00, 0x77, 0x61, 0x73, 0x6c, 750x70, 0x61, 0x74, 0x68, 0x27, 0x29, 0x55, 0x00, 0x04, 0xfc,
750x01, 0x72, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x86, 760x02, 0x41, 0x00, 0x77, 0x61, 0x73, 0x6c, 0x01, 0x72, 0x72,
760x01, 0x10, 0x69, 0x14, 0x00, 0x23, 0x6c, 0x6c, 0x05, 0x01, 770x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x86, 0x01, 0x10, 0x69,
770xf0, 0x00, 0x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 780x14, 0x00, 0x23, 0x6c, 0x6c, 0x05, 0x01, 0xf0, 0x00, 0x62,
780x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x3f, 0x00, 0x00, 790x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61,
790x6f, 0x00, 0x90, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 800x72, 0x76, 0x65, 0x79, 0x3f, 0x00, 0x00, 0x6f, 0x00, 0x90,
800x2f, 0x6a, 0x81, 0x00, 0xc5, 0x2e, 0x61, 0x74, 0x2f, 0x52, 810x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6a, 0x81,
810x61, 0x65, 0x74, 0x73, 0x65, 0x6c, 0x2f, 0x79, 0x00, 0xf6, 820x00, 0xc5, 0x2e, 0x61, 0x74, 0x2f, 0x52, 0x61, 0x65, 0x74,
820x02, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 830x73, 0x65, 0x6c, 0x2f, 0x79, 0x00, 0xf6, 0x02, 0x2f, 0x69,
830x6d, 0x00, 0x00, 0x00, 0x33, 0x34, 0x2e, 0x31, 0x5e, 0x03, 840x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0x00,
840x01, 0x73, 0x00, 0x30, 0x6f, 0x6c, 0x73, 0x67, 0x03, 0x75, 850x00, 0x33, 0x34, 0x2e, 0x31, 0x73, 0x03, 0x01, 0x73, 0x00,
850x54, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x94, 0x00, 0x55, 860xe5, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x6f,
860x2c, 0x00, 0x79, 0x6f, 0x75, 0xf6, 0x02, 0x06, 0xf2, 0x02, 870x00, 0x70, 0x6c, 0x61, 0x79, 0x94, 0x00, 0x55, 0x2c, 0x00,
870x60, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x8f, 0x00, 0x50, 880x79, 0x6f, 0x75, 0xf6, 0x02, 0x06, 0xf2, 0x02, 0x60, 0x67,
880x64, 0x72, 0x61, 0x67, 0x67, 0x1b, 0x02, 0x01, 0xe3, 0x01, 890x65, 0x74, 0x68, 0x65, 0x72, 0x8f, 0x00, 0x50, 0x64, 0x72,
890x25, 0x6f, 0x6e, 0x36, 0x02, 0x60, 0x74, 0x6f, 0x00, 0x61, 900x61, 0x67, 0x67, 0x1b, 0x02, 0x01, 0xe3, 0x01, 0x25, 0x6f,
900x6e, 0x6f, 0x27, 0x00, 0x92, 0x2c, 0x00, 0x69, 0x6e, 0x64, 910x6e, 0x36, 0x02, 0x60, 0x74, 0x6f, 0x00, 0x61, 0x6e, 0x6f,
910x69, 0x63, 0x61, 0x74, 0x42, 0x02, 0x02, 0x59, 0x01, 0x11, 920x27, 0x00, 0x92, 0x2c, 0x00, 0x69, 0x6e, 0x64, 0x69, 0x63,
920x79, 0x98, 0x01, 0x84, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 930x61, 0x74, 0x42, 0x02, 0x02, 0x59, 0x01, 0x11, 0x79, 0x98,
930x6e, 0x74, 0xef, 0x01, 0xe6, 0x73, 0x65, 0x71, 0x75, 0x65, 940x01, 0x84, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74,
940x6e, 0x63, 0x65, 0x2e, 0x00, 0x44, 0x72, 0x61, 0x67, 0xdd, 950xef, 0x01, 0xe6, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63,
950x02, 0xb2, 0x6c, 0x65, 0x66, 0x74, 0x00, 0x62, 0x75, 0x74, 960x65, 0x2e, 0x00, 0x44, 0x72, 0x61, 0x67, 0xdd, 0x02, 0xb2,
960x74, 0x6f, 0x6e, 0x69, 0x00, 0x17, 0x61, 0x67, 0x00, 0xe0, 970x6c, 0x65, 0x66, 0x74, 0x00, 0x62, 0x75, 0x74, 0x74, 0x6f,
970x69, 0x74, 0x73, 0x00, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 980x6e, 0x69, 0x00, 0x17, 0x61, 0x67, 0x00, 0xe0, 0x69, 0x74,
980x73, 0x6f, 0x72, 0x2c, 0xe1, 0x01, 0x05, 0x38, 0x00, 0x4f, 990x73, 0x00, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f,
990x72, 0x69, 0x67, 0x68, 0x39, 0x00, 0x0b, 0x52, 0x70, 0x72, 1000x72, 0x2c, 0xe1, 0x01, 0x05, 0x38, 0x00, 0x4f, 0x72, 0x69,
1000x65, 0x64, 0x65, 0x3b, 0x00, 0x00, 0x4e, 0x01, 0x29, 0x49, 1010x67, 0x68, 0x39, 0x00, 0x0b, 0x52, 0x70, 0x72, 0x65, 0x64,
1010x66, 0xf2, 0x00, 0x05, 0xea, 0x00, 0x35, 0x74, 0x77, 0x6f, 1020x65, 0x3b, 0x00, 0x00, 0x4e, 0x01, 0x29, 0x49, 0x66, 0xf2,
1020xff, 0x00, 0x22, 0x69, 0x6e, 0x98, 0x01, 0x00, 0xd9, 0x02, 1030x00, 0x05, 0xea, 0x00, 0x35, 0x74, 0x77, 0x6f, 0xff, 0x00,
1030x00, 0x6e, 0x02, 0x01, 0x56, 0x02, 0x01, 0x9e, 0x02, 0x74, 1040x22, 0x69, 0x6e, 0x98, 0x01, 0x00, 0xd9, 0x02, 0x00, 0x6e,
1040x6d, 0x00, 0x68, 0x61, 0x73, 0x00, 0x61, 0x23, 0x03, 0x62, 1050x02, 0x01, 0x56, 0x02, 0x01, 0x9e, 0x02, 0x74, 0x6d, 0x00,
1050x69, 0x6e, 0x00, 0x69, 0x74, 0x2c, 0xb7, 0x02, 0x95, 0x70, 1060x68, 0x61, 0x73, 0x00, 0x61, 0x23, 0x03, 0x62, 0x69, 0x6e,
1060x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x41, 0x03, 1070x00, 0x69, 0x74, 0x2c, 0xb7, 0x02, 0x95, 0x70, 0x70, 0x72,
1070x01, 0x70, 0x03, 0x03, 0x6d, 0x02, 0x03, 0xff, 0x00, 0x01, 1080x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x41, 0x03, 0x01, 0x70,
1080x2f, 0x01, 0x03, 0x68, 0x00, 0x1d, 0x2e, 0x8c, 0x00, 0x72, 1090x03, 0x03, 0x6d, 0x02, 0x03, 0xff, 0x00, 0x01, 0x2f, 0x01,
1090x77, 0x6f, 0x00, 0x6e, 0x6f, 0x6e, 0x2d, 0x3f, 0x00, 0x24, 1100x03, 0x68, 0x00, 0x1d, 0x2e, 0x8c, 0x00, 0x72, 0x77, 0x6f,
1100x65, 0x64, 0x90, 0x00, 0x12, 0x2c, 0x4f, 0x01, 0x01, 0x4f, 1110x00, 0x6e, 0x6f, 0x6e, 0x2d, 0x3f, 0x00, 0x24, 0x65, 0x64,
1110x00, 0x00, 0x7d, 0x03, 0x50, 0x73, 0x73, 0x69, 0x67, 0x6e, 1120x90, 0x00, 0x12, 0x2c, 0x4f, 0x01, 0x01, 0x4f, 0x00, 0x00,
1120x3f, 0x02, 0x70, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 1130x7d, 0x03, 0x50, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x3f, 0x02,
1130xd7, 0x02, 0xf9, 0x01, 0x67, 0x65, 0x62, 0x72, 0x61, 0x69, 1140x70, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0xd7, 0x02,
1140x63, 0x00, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x3a, 0x00, 1150xf9, 0x01, 0x67, 0x65, 0x62, 0x72, 0x61, 0x69, 0x63, 0x00,
1150x32, 0x03, 0x8b, 0x6f, 0x63, 0x63, 0x61, 0x73, 0x69, 0x6f, 1160x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x3a, 0x00, 0x32, 0x03,
1160x6e, 0x49, 0x00, 0x01, 0x2c, 0x00, 0x71, 0x6c, 0x65, 0x64, 1170x8b, 0x6f, 0x63, 0x63, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x49,
1170x00, 0x60, 0x61, 0x27, 0xe8, 0x00, 0x52, 0x60, 0x61, 0x2b, 1180x00, 0x01, 0x2c, 0x00, 0x71, 0x6c, 0x65, 0x64, 0x00, 0x60,
1180x31, 0x27, 0x3a, 0x05, 0x73, 0x74, 0x68, 0x65, 0x6e, 0x00, 1190x61, 0x27, 0xe8, 0x00, 0x52, 0x60, 0x61, 0x2b, 0x31, 0x27,
1190x60, 0x62, 0x18, 0x00, 0x15, 0x62, 0x18, 0x00, 0x82, 0x73, 1200x3a, 0x05, 0x73, 0x74, 0x68, 0x65, 0x6e, 0x00, 0x60, 0x62,
1200x6f, 0x00, 0x6f, 0x6e, 0x2e, 0x00, 0x43, 0xb3, 0x00, 0x00, 1210x18, 0x00, 0x15, 0x62, 0x18, 0x00, 0x82, 0x73, 0x6f, 0x00,
1210xf0, 0x01, 0x01, 0x6c, 0x03, 0x04, 0x3a, 0x01, 0x24, 0x6f, 1220x6f, 0x6e, 0x2e, 0x00, 0x43, 0xb3, 0x00, 0x00, 0xf0, 0x01,
1220x6e, 0x5b, 0x04, 0x41, 0x65, 0x6e, 0x64, 0x73, 0xc5, 0x05, 1230x01, 0x6c, 0x03, 0x04, 0x3a, 0x01, 0x24, 0x6f, 0x6e, 0x5b,
1230x30, 0x75, 0x63, 0x68, 0x3a, 0x05, 0x10, 0x68, 0x76, 0x05, 1240x04, 0x41, 0x65, 0x6e, 0x64, 0x73, 0xc5, 0x05, 0x30, 0x75,
1240x01, 0x7a, 0x00, 0x52, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 1250x63, 0x68, 0x3a, 0x05, 0x10, 0x68, 0x76, 0x05, 0x01, 0x7a,
1250x01, 0x00, 0x3f, 0x03, 0x29, 0x74, 0x6f, 0x8c, 0x00, 0x05, 1260x00, 0x52, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x01, 0x00,
1260xda, 0x01, 0x30, 0x73, 0x61, 0x6d, 0x17, 0x02, 0x31, 0x74, 1270x3f, 0x03, 0x29, 0x74, 0x6f, 0x8c, 0x00, 0x05, 0xda, 0x01,
1270x74, 0x65, 0xb8, 0x01, 0x10, 0x57, 0x91, 0x00, 0x00, 0x2e, 1280x30, 0x73, 0x61, 0x6d, 0x17, 0x02, 0x31, 0x74, 0x74, 0x65,
1280x01, 0x00, 0x2a, 0x02, 0x60, 0x2d, 0x63, 0x6c, 0x69, 0x63, 1290xb8, 0x01, 0x10, 0x57, 0x91, 0x00, 0x00, 0x2e, 0x01, 0x00,
1290x6b, 0x09, 0x02, 0x01, 0x00, 0x02, 0x03, 0x0f, 0x00, 0x25, 1300x2a, 0x02, 0x60, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x09,
1300x69, 0x6e, 0xfd, 0x01, 0x13, 0x2c, 0x55, 0x02, 0x38, 0x67, 1310x02, 0x01, 0x00, 0x02, 0x03, 0x0f, 0x00, 0x25, 0x69, 0x6e,
1310x61, 0x6c, 0xc7, 0x05, 0x04, 0x6a, 0x01, 0x11, 0x69, 0x02, 1320xfd, 0x01, 0x13, 0x2c, 0x55, 0x02, 0x38, 0x67, 0x61, 0x6c,
1320x05, 0x04, 0x08, 0x01, 0x01, 0x3c, 0x04, 0x10, 0x2e, 0x1d, 1330xc7, 0x05, 0x04, 0x6a, 0x01, 0x11, 0x69, 0x02, 0x05, 0x04,
1330x03, 0x07, 0x87, 0x05, 0x09, 0x75, 0x06, 0x01, 0xdd, 0x05, 1340x08, 0x01, 0x01, 0x3c, 0x04, 0x10, 0x2e, 0x1d, 0x03, 0x07,
1340x00, 0xcf, 0x00, 0x72, 0x66, 0x00, 0x62, 0x6c, 0x61, 0x63, 1350x87, 0x05, 0x09, 0x75, 0x06, 0x01, 0xdd, 0x05, 0x00, 0xcf,
1350x6b, 0x0c, 0x01, 0x01, 0x16, 0x04, 0x60, 0x67, 0x72, 0x65, 1360x00, 0x72, 0x66, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x0c,
1360x79, 0x00, 0x6f, 0x08, 0x05, 0x09, 0xcb, 0x01, 0x2e, 0x68, 1370x01, 0x01, 0x16, 0x04, 0x60, 0x67, 0x72, 0x65, 0x79, 0x00,
1370x65, 0xc0, 0x02, 0x04, 0x85, 0x02, 0x59, 0x41, 0x6c, 0x73, 1380x6f, 0x08, 0x05, 0x09, 0xcb, 0x01, 0x2e, 0x68, 0x65, 0xc0,
1380x6f, 0x2c, 0x60, 0x00, 0x02, 0xa0, 0x05, 0x40, 0x6e, 0x65, 1390x02, 0x04, 0x85, 0x02, 0x59, 0x41, 0x6c, 0x73, 0x6f, 0x2c,
1390x65, 0x64, 0x5d, 0x02, 0x07, 0xb2, 0x02, 0x03, 0x6f, 0x02, 1400x60, 0x00, 0x02, 0xa0, 0x05, 0x40, 0x6e, 0x65, 0x65, 0x64,
1400x20, 0x73, 0x6d, 0x2a, 0x01, 0x25, 0x64, 0x6f, 0x47, 0x03, 1410x5d, 0x02, 0x07, 0xb2, 0x02, 0x03, 0x6f, 0x02, 0x20, 0x73,
1410x62, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x36, 0x03, 0x64, 1420x6d, 0x2a, 0x01, 0x25, 0x64, 0x6f, 0x47, 0x03, 0x62, 0x62,
1420x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0xe5, 0x05, 0x86, 0x76, 1430x6f, 0x74, 0x74, 0x6f, 0x6d, 0x36, 0x03, 0x64, 0x63, 0x6f,
1430x61, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x73, 0x94, 0x00, 0x00, 1440x72, 0x6e, 0x65, 0x72, 0xe5, 0x05, 0x86, 0x76, 0x61, 0x6e,
1440x88, 0x06, 0x0b, 0x16, 0x03, 0x60, 0x2e, 0x00, 0x53, 0x6f, 1450x69, 0x73, 0x68, 0x65, 0x73, 0x94, 0x00, 0x00, 0x88, 0x06,
1450x00, 0x79, 0xfa, 0x06, 0x50, 0x61, 0x69, 0x6d, 0x00, 0x69, 1460x0b, 0x16, 0x03, 0x60, 0x2e, 0x00, 0x53, 0x6f, 0x00, 0x79,
1460x1c, 0x07, 0x39, 0x77, 0x61, 0x79, 0x23, 0x01, 0x19, 0x61, 1470xfa, 0x06, 0x50, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x1c, 0x07,
1470x80, 0x06, 0x12, 0x61, 0xf8, 0x00, 0x03, 0x1e, 0x01, 0x2d, 1480x39, 0x77, 0x61, 0x79, 0x23, 0x01, 0x19, 0x61, 0x80, 0x06,
1480x74, 0x6f, 0x1f, 0x00, 0x42, 0x64, 0x6f, 0x74, 0x2e, 0x5f, 1490x12, 0x61, 0xf8, 0x00, 0x03, 0x1e, 0x01, 0x2d, 0x74, 0x6f,
1490x04, 0x52, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x23, 0x06, 0x00, 1500x1f, 0x00, 0x42, 0x64, 0x6f, 0x74, 0x2e, 0x5f, 0x04, 0x52,
1500x7a, 0x00, 0x11, 0x73, 0x4d, 0x05, 0x30, 0x61, 0x00, 0x70, 1510x72, 0x65, 0x6d, 0x6f, 0x76, 0x23, 0x06, 0x00, 0x7a, 0x00,
1510x1f, 0x07, 0x55, 0x63, 0x75, 0x6c, 0x61, 0x72, 0xca, 0x07, 1520x11, 0x73, 0x4d, 0x05, 0x30, 0x61, 0x00, 0x70, 0x1f, 0x07,
1520xa1, 0x62, 0x6f, 0x74, 0x68, 0x00, 0x69, 0x6e, 0x63, 0x6f, 1530x55, 0x63, 0x75, 0x6c, 0x61, 0x72, 0xca, 0x07, 0xa1, 0x62,
1530x6d, 0x38, 0x07, 0x00, 0x69, 0x03, 0x92, 0x75, 0x74, 0x67, 1540x6f, 0x74, 0x68, 0x00, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x38,
1540x6f, 0x69, 0x6e, 0x67, 0x29, 0x2c, 0xe7, 0x01, 0x10, 0x64, 1550x07, 0x00, 0x69, 0x03, 0x92, 0x75, 0x74, 0x67, 0x6f, 0x69,
1550x24, 0x04, 0x10, 0x69, 0x68, 0x07, 0x02, 0x1c, 0x06, 0x00, 1560x6e, 0x67, 0x29, 0x2c, 0xe7, 0x01, 0x10, 0x64, 0x24, 0x04,
1560x1a, 0x08, 0x18, 0x2e, 0x65, 0x00, 0x00, 0x86, 0x05, 0x22, 1570x10, 0x69, 0x68, 0x07, 0x02, 0x1c, 0x06, 0x00, 0x1a, 0x08,
1570x6c, 0x65, 0x5e, 0x02, 0x13, 0x2c, 0x0d, 0x02, 0x01, 0x35, 1580x18, 0x2e, 0x65, 0x00, 0x00, 0x86, 0x05, 0x22, 0x6c, 0x65,
1580x00, 0x00, 0x7f, 0x00, 0x03, 0x68, 0x00, 0x03, 0x33, 0x01, 1590x5e, 0x02, 0x13, 0x2c, 0x0d, 0x02, 0x01, 0x35, 0x00, 0x00,
1590x02, 0x82, 0x02, 0x0a, 0x4a, 0x00, 0x02, 0x77, 0x08, 0x00, 1600x7f, 0x00, 0x03, 0x68, 0x00, 0x03, 0x33, 0x01, 0x02, 0x82,
1600xd5, 0x06, 0x01, 0x19, 0x06, 0x03, 0x98, 0x02, 0x40, 0x00, 1610x02, 0x0a, 0x4a, 0x00, 0x02, 0x77, 0x08, 0x00, 0xd5, 0x06,
1610x63, 0x75, 0x72, 0x7c, 0x01, 0x22, 0x6b, 0x65, 0x13, 0x01, 1620x01, 0x19, 0x06, 0x03, 0x98, 0x02, 0x40, 0x00, 0x63, 0x75,
1620x02, 0x6b, 0x00, 0x55, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3c, 1630x72, 0x7c, 0x01, 0x22, 0x6b, 0x65, 0x13, 0x01, 0x02, 0x6b,
1630x00, 0x06, 0x56, 0x08, 0x02, 0xea, 0x07, 0x94, 0x65, 0x73, 1640x00, 0x55, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3c, 0x00, 0x06,
1640x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0x45, 0x07, 0x80, 1650x56, 0x08, 0x02, 0xea, 0x07, 0x94, 0x65, 0x73, 0x2e, 0x00,
1650x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x6b, 0x70, 0x03, 1660x50, 0x72, 0x65, 0x73, 0x73, 0x45, 0x07, 0x80, 0x72, 0x65,
1660x00, 0xbb, 0x02, 0x30, 0x6f, 0x76, 0x65, 0x07, 0x01, 0x0a, 1670x74, 0x75, 0x72, 0x6e, 0x00, 0x6b, 0x70, 0x03, 0x00, 0xbb,
1670x51, 0x02, 0x12, 0x61, 0x9d, 0x01, 0x50, 0x6f, 0x70, 0x65, 1680x02, 0x30, 0x6f, 0x76, 0x65, 0x07, 0x01, 0x0a, 0x51, 0x02,
1680x72, 0x61, 0xff, 0x05, 0x02, 0x58, 0x02, 0x1f, 0x70, 0x48, 1690x12, 0x61, 0x9d, 0x01, 0x50, 0x6f, 0x70, 0x65, 0x72, 0x61,
1690x00, 0x04, 0x4c, 0x61, 0x67, 0x61, 0x69, 0x49, 0x00, 0x01, 1700xff, 0x05, 0x02, 0x58, 0x02, 0x1f, 0x70, 0x48, 0x00, 0x04,
1700xc4, 0x02, 0x20, 0x66, 0x69, 0xf4, 0x01, 0x02, 0xf3, 0x02, 1710x4c, 0x61, 0x67, 0x61, 0x69, 0x49, 0x00, 0x01, 0xc4, 0x02,
1710x70, 0x69, 0x6e, 0x6b, 0x2c, 0x00, 0x69, 0x66, 0x5b, 0x03, 1720x20, 0x66, 0x69, 0xf4, 0x01, 0x02, 0xf3, 0x02, 0x70, 0x69,
1720x6b, 0x6f, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x98, 0x00, 0x9f, 1730x6e, 0x6b, 0x2c, 0x00, 0x69, 0x66, 0x5b, 0x03, 0x6b, 0x6f,
1730x73, 0x70, 0x61, 0x63, 0x65, 0x00, 0x62, 0x61, 0x72, 0x49, 1740x77, 0x61, 0x62, 0x6c, 0x65, 0x98, 0x00, 0x9f, 0x73, 0x70,
1740x00, 0x01, 0x00, 0x0a, 0x03, 0x0d, 0xa8, 0x04, 0x12, 0x73, 1750x61, 0x63, 0x65, 0x00, 0x62, 0x61, 0x72, 0x49, 0x00, 0x01,
1750x34, 0x08, 0x01, 0x41, 0x00, 0x00, 0x3d, 0x02, 0x02, 0xa5, 1760x00, 0x0a, 0x03, 0x0d, 0xa8, 0x04, 0x12, 0x73, 0x34, 0x08,
1760x00, 0x01, 0x60, 0x00, 0x01, 0x60, 0x02, 0x06, 0x1f, 0x09, 1770x01, 0x41, 0x00, 0x00, 0x3d, 0x02, 0x02, 0xa5, 0x00, 0x01,
1770x72, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, 0xfb, 0x08, 1780x60, 0x00, 0x01, 0x60, 0x02, 0x06, 0x1f, 0x09, 0x72, 0x62,
1780x0e, 0xcc, 0x00, 0x07, 0x7c, 0x00, 0x02, 0xcb, 0x00, 0x10, 1790x61, 0x63, 0x6b, 0x77, 0x61, 0x72, 0xfb, 0x08, 0x0e, 0xcc,
1790x63, 0x40, 0x08, 0x21, 0x6c, 0x73, 0x6d, 0x05, 0x00, 0x8d, 1800x00, 0x07, 0x7c, 0x00, 0x02, 0xcb, 0x00, 0x10, 0x63, 0x40,
1800x01, 0x42, 0x28, 0x41, 0x6c, 0x6c, 0x4d, 0x05, 0x01, 0x0d, 1810x08, 0x21, 0x6c, 0x73, 0x6d, 0x05, 0x00, 0x8d, 0x01, 0x42,
1810x07, 0x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 1820x28, 0x41, 0x6c, 0x6c, 0x4d, 0x05, 0x01, 0x0d, 0x07, 0x92,
1820x62, 0x46, 0x09, 0x13, 0x73, 0x23, 0x07, 0x32, 0x32, 0x2e, 1830x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x46,
1830x31, 0x5b, 0x06, 0x01, 0xcc, 0x07, 0x41, 0x76, 0x61, 0x69, 1840x09, 0x13, 0x73, 0x23, 0x07, 0x32, 0x32, 0x2e, 0x31, 0x5b,
1840x6c, 0xe7, 0x00, 0x12, 0x29, 0xf8, 0x06, 0x16, 0x32, 0xf8, 1850x06, 0x01, 0xcc, 0x07, 0x41, 0x76, 0x61, 0x69, 0x6c, 0xe7,
1850x06, 0xb2, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 1860x00, 0x12, 0x29, 0xf8, 0x06, 0x16, 0x32, 0xf8, 0x06, 0xb2,
1860x72, 0x73, 0x20, 0xdd, 0x03, 0x36, 0x73, 0x65, 0x00, 0x14, 1870x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
1870x00, 0x02, 0x44, 0x00, 0x04, 0x3f, 0x00, 0x06, 0xc1, 0x08, 1880x20, 0xdd, 0x03, 0x36, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02,
1880xe2, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 1890x44, 0x00, 0x04, 0x3f, 0x00, 0x06, 0xc1, 0x08, 0xe2, 0x60,
1890x2e, 0x27, 0x00, 0x6f, 0x70, 0x9a, 0x08, 0x12, 0x6e, 0x1a, 1900x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
1900x00, 0xa1, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 1910x00, 0x6f, 0x70, 0x9a, 0x08, 0x12, 0x6e, 0x1a, 0x00, 0xa1,
1910x6e, 0x75, 0x92, 0x04, 0x81, 0x69, 0x64, 0x74, 0x68, 0x2c, 1920x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75,
1920x00, 0x48, 0x65, 0x83, 0x06, 0x00, 0xe7, 0x07, 0x11, 0x7a, 1930x92, 0x04, 0x81, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48,
1930x27, 0x06, 0x01, 0x1f, 0x02, 0x24, 0x69, 0x6e, 0x3e, 0x01, 1940x65, 0x83, 0x06, 0x00, 0xe7, 0x07, 0x11, 0x7a, 0x27, 0x06,
1940x00, 0x2b, 0x00, 0x61, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x20, 1950x01, 0x1f, 0x02, 0x24, 0x69, 0x6e, 0x3e, 0x01, 0x00, 0x2b,
1950xf5, 0x01, 0x82, 0x2f, 0x65, 0x6e, 0x64, 0x20, 0x74, 0x6f, 1960x00, 0x61, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x20, 0xf5, 0x01,
1960x20, 0xb6, 0x03, 0x12, 0x73, 0x92, 0x06, 0x33, 0x74, 0x72, 1970x82, 0x2f, 0x65, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0xb6,
1970x75, 0xb3, 0x04, 0x01, 0x24, 0x00, 0x01, 0x3a, 0x01, 0x27, 1980x03, 0x12, 0x73, 0x92, 0x06, 0x33, 0x74, 0x72, 0x75, 0xb3,
1980x65, 0x6e, 0x65, 0x02, 0x24, 0x72, 0x65, 0xa3, 0x03, 0x42, 1990x04, 0x01, 0x24, 0x00, 0x01, 0x3a, 0x01, 0x27, 0x65, 0x6e,
1990x70, 0x6c, 0x61, 0x63, 0x10, 0x01, 0x94, 0x6f, 0x70, 0x70, 2000x65, 0x02, 0x24, 0x72, 0x65, 0xa3, 0x03, 0x42, 0x70, 0x6c,
2000x6f, 0x73, 0x69, 0x74, 0x65, 0x00, 0x4b, 0x00, 0x17, 0x28, 2010x61, 0x63, 0x10, 0x01, 0x94, 0x6f, 0x70, 0x70, 0x6f, 0x73,
2010x41, 0x00, 0x02, 0x83, 0x04, 0x31, 0x74, 0x6f, 0x70, 0x49, 2020x69, 0x74, 0x65, 0x00, 0x4b, 0x00, 0x17, 0x28, 0x41, 0x00,
2020x03, 0x05, 0xd4, 0x05, 0x01, 0x56, 0x00, 0x28, 0x61, 0x74, 2030x02, 0x83, 0x04, 0x31, 0x74, 0x6f, 0x70, 0x49, 0x03, 0x05,
2030x44, 0x04, 0x01, 0x35, 0x03, 0x11, 0x29, 0x8d, 0x06, 0x5f, 2040xd4, 0x05, 0x01, 0x56, 0x00, 0x28, 0x61, 0x74, 0x44, 0x04,
2040x66, 0x61, 0x6c, 0x73, 0x65, 0x87, 0x00, 0x0c, 0x03, 0x80, 2050x01, 0x35, 0x03, 0x11, 0x29, 0x8d, 0x06, 0x5f, 0x66, 0x61,
2050x00, 0xf4, 0x03, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c, 2060x6c, 0x73, 0x65, 0x87, 0x00, 0x0c, 0x03, 0x80, 0x00, 0xf4,
2060x79, 0x00, 0x28, 0x61, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67, 2070x03, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c, 0x79, 0x00,
2070x68, 0xa1, 0x00, 0x01, 0xd6, 0x03, 0x80, 0x73, 0x68, 0x6f, 2080x28, 0x61, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0xa1,
2080x77, 0x6e, 0x29, 0x2e, 0x00, 2090x00, 0x01, 0xd6, 0x03, 0x01, 0x53, 0x05, 0x22, 0x29, 0x2e,
2100x93, 0x01, 0x16, 0x33, 0x93, 0x01, 0xb0, 0x75, 0x73, 0x65,
2110x72, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x06, 0x08,
2120x01, 0x99, 0x01, 0x20, 0x4f, 0x6e, 0x55, 0x00, 0x10, 0x74,
2130x44, 0x02, 0x12, 0x73, 0x3e, 0x08, 0x80, 0x73, 0x75, 0x70,
2140x70, 0x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x17, 0x00, 0x2e,
2150x00, 0x12, 0x2c, 0x87, 0x01, 0x16, 0x50, 0x12, 0x00, 0x0d,
2160xa3, 0x01, 0x33, 0x47, 0x61, 0x6d, 0xa3, 0x01, 0x02, 0xd2,
2170x02, 0x34, 0x6c, 0x65, 0x74, 0x99, 0x05, 0x54, 0x66, 0x69,
2180x67, 0x75, 0x72, 0xdf, 0x00, 0x34, 0x79, 0x6c, 0x65, 0x71,
2190x0a, 0xf0, 0x01, 0x76, 0x69, 0x63, 0x74, 0x6f, 0x72, 0x79,
2200x00, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x2e, 0x00,
209}; 221};
210 222
211const unsigned short help_text_len = 3054; 223const unsigned short help_text_len = 3255;
212const unsigned short help_text_words = 566; 224const unsigned short help_text_words = 595;
225const bool help_valid = true;
213const char quick_help_text[] = "Connect the squares into a path following the arrows."; 226const char quick_help_text[] = "Connect the squares into a path following the arrows.";
diff --git a/apps/plugins/puzzles/help/singles.c b/apps/plugins/puzzles/help/singles.c
index 3f4ecd157b..9a1771e3c7 100644
--- a/apps/plugins/puzzles/help/singles.c
+++ b/apps/plugins/puzzles/help/singles.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,124 +6,148 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 122, TEXT_CENTER | C_RED }, 9 { 120, TEXT_CENTER | C_RED },
10 { 212, TEXT_CENTER | C_RED }, 10 { 232, TEXT_CENTER | C_RED },
11 { 229, TEXT_UNDERLINE }, 11 { 249, TEXT_UNDERLINE },
12 { 230, TEXT_UNDERLINE }, 12 { 250, TEXT_UNDERLINE },
13 { 240, TEXT_UNDERLINE }, 13 { 260, TEXT_UNDERLINE },
14 { 272, TEXT_CENTER | C_RED },
14 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
15}; 16};
16 17
17/* orig 1392 comp 1052 ratio 0.755747 level 10 saved 340 */ 18/* orig 1780 comp 1279 ratio 0.718539 level 10 saved 501 */
18const char help_text[] = { 19const char help_text[] = {
190xf3, 0x2d, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x33, 0x32, 0x3a, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 210x33, 0x32, 0x3a, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65,
210x73, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 220x73, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf3, 0x19, 0x00, 0x00,
220x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 230x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00,
230x00, 0x6f, 0x66, 0x00, 0x77, 0x68, 0x69, 0x74, 0x65, 0x00, 240x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00,
240x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x61, 250x77, 0x68, 0x69, 0x74, 0x65, 0x00, 0x73, 0x71, 0x75, 0x61,
250x6c, 0x6c, 0x16, 0x00, 0xf0, 0x04, 0x63, 0x68, 0x00, 0x63, 260x72, 0x65, 0x73, 0x2c, 0x00, 0x61, 0x6c, 0x6c, 0x16, 0x00,
260x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 270xf0, 0x04, 0x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61,
270x62, 0x65, 0x72, 0x73, 0x2e, 0x40, 0x00, 0xf0, 0x01, 0x72, 280x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73,
280x00, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x69, 0x73, 0x00, 0x74, 290x2e, 0x40, 0x00, 0xf0, 0x01, 0x72, 0x00, 0x74, 0x61, 0x73,
290x6f, 0x00, 0x63, 0x6f, 0x6c, 0x12, 0x00, 0x40, 0x73, 0x6f, 300x6b, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x63, 0x6f,
300x6d, 0x65, 0x36, 0x00, 0x25, 0x74, 0x68, 0x4a, 0x00, 0xf1, 310x6c, 0x12, 0x00, 0x40, 0x73, 0x6f, 0x6d, 0x65, 0x36, 0x00,
310x01, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x00, 0x28, 0x72, 320x25, 0x74, 0x68, 0x4a, 0x00, 0xf1, 0x01, 0x00, 0x62, 0x6c,
320x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x1c, 0x00, 0x02, 330x61, 0x63, 0x6b, 0x00, 0x28, 0x72, 0x65, 0x6d, 0x6f, 0x76,
330x48, 0x00, 0x61, 0x29, 0x00, 0x73, 0x6f, 0x00, 0x61, 0x40, 340x69, 0x6e, 0x67, 0x1c, 0x00, 0x02, 0x48, 0x00, 0x61, 0x29,
340x00, 0x74, 0x73, 0x61, 0x74, 0x69, 0x73, 0x66, 0x79, 0x76, 350x00, 0x73, 0x6f, 0x00, 0x61, 0x40, 0x00, 0x74, 0x73, 0x61,
350x00, 0x00, 0x24, 0x00, 0x60, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 360x74, 0x69, 0x73, 0x66, 0x79, 0x76, 0x00, 0x00, 0x24, 0x00,
360x77, 0x32, 0x00, 0xf3, 0x03, 0x63, 0x6f, 0x6e, 0x64, 0x69, 370x60, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x32, 0x00, 0xf3,
370x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x00, 0x00, 0x00, 0x2d, 380x03, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
380x00, 0x4e, 0x6f, 0x41, 0x00, 0xf0, 0x15, 0x00, 0x6f, 0x63, 390x73, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x4e, 0x6f, 0x41,
390x63, 0x75, 0x72, 0x73, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 400x00, 0xf0, 0x15, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x73,
400x74, 0x68, 0x61, 0x6e, 0x00, 0x6f, 0x6e, 0x63, 0x65, 0x00, 410x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x74, 0x68, 0x61, 0x6e,
410x69, 0x6e, 0x00, 0x61, 0x6e, 0x79, 0x00, 0x72, 0x6f, 0x77, 420x00, 0x6f, 0x6e, 0x63, 0x65, 0x00, 0x69, 0x6e, 0x00, 0x61,
420x00, 0x6f, 0x72, 0x9b, 0x00, 0x44, 0x75, 0x6d, 0x6e, 0x2e, 430x6e, 0x79, 0x00, 0x72, 0x6f, 0x77, 0x00, 0x6f, 0x72, 0x9b,
430x3a, 0x00, 0x02, 0x8f, 0x00, 0x02, 0x9d, 0x00, 0x00, 0xbd, 440x00, 0x44, 0x75, 0x6d, 0x6e, 0x2e, 0x3a, 0x00, 0x02, 0x8f,
440x00, 0x50, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0xdf, 0x00, 0x30, 450x00, 0x02, 0x9d, 0x00, 0x00, 0xbd, 0x00, 0x50, 0x68, 0x6f,
450x6c, 0x6c, 0x79, 0x2f, 0x00, 0x61, 0x76, 0x65, 0x72, 0x74, 460x72, 0x69, 0x7a, 0xdf, 0x00, 0x30, 0x6c, 0x6c, 0x79, 0x2f,
460x69, 0x63, 0x0e, 0x00, 0x80, 0x61, 0x64, 0x6a, 0x61, 0x63, 470x00, 0x61, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e, 0x00,
470x65, 0x6e, 0x74, 0xa1, 0x00, 0x00, 0x51, 0x00, 0x59, 0x6f, 480x80, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0xa1,
480x74, 0x68, 0x65, 0x72, 0x41, 0x00, 0x02, 0x56, 0x00, 0xa0, 490x00, 0x00, 0x51, 0x00, 0x59, 0x6f, 0x74, 0x68, 0x65, 0x72,
490x54, 0x68, 0x65, 0x00, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 500x41, 0x00, 0x02, 0x56, 0x00, 0xa0, 0x54, 0x68, 0x65, 0x00,
500xae, 0x00, 0x09, 0x48, 0x01, 0x51, 0x00, 0x6d, 0x75, 0x73, 510x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0xae, 0x00, 0x09, 0x48,
510x74, 0xd6, 0x00, 0x81, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x6f, 520x01, 0x51, 0x00, 0x6d, 0x75, 0x73, 0x74, 0xd6, 0x00, 0x81,
520x6e, 0x65, 0x4c, 0x01, 0xf0, 0x10, 0x69, 0x67, 0x75, 0x6f, 530x66, 0x6f, 0x72, 0x6d, 0x00, 0x6f, 0x6e, 0x65, 0x4c, 0x01,
530x75, 0x73, 0x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x00, 540xf0, 0x10, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x00, 0x72,
540x28, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 550x65, 0x67, 0x69, 0x6f, 0x6e, 0x00, 0x28, 0x63, 0x6f, 0x6e,
550x00, 0x62, 0x79, 0x00, 0x65, 0x64, 0x67, 0x80, 0x01, 0x70, 560x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x62, 0x79, 0x00,
560x6e, 0x6f, 0x74, 0x00, 0x6a, 0x75, 0x73, 0x7d, 0x00, 0x30, 570x65, 0x64, 0x67, 0x80, 0x01, 0x70, 0x6e, 0x6f, 0x74, 0x00,
570x75, 0x63, 0x68, 0x59, 0x00, 0xb0, 0x61, 0x74, 0x00, 0x63, 580x6a, 0x75, 0x73, 0x7d, 0x00, 0x30, 0x75, 0x63, 0x68, 0x59,
580x6f, 0x72, 0x6e, 0x65, 0x72, 0x73, 0x29, 0x78, 0x00, 0x60, 590x00, 0xb0, 0x61, 0x74, 0x00, 0x63, 0x6f, 0x72, 0x6e, 0x65,
590x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x58, 0x00, 0xf1, 0x01, 600x72, 0x73, 0x29, 0x78, 0x00, 0x60, 0x43, 0x72, 0x65, 0x64,
600x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 610x69, 0x74, 0x58, 0x00, 0xf1, 0x01, 0x00, 0x74, 0x68, 0x69,
610x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0x52, 0x01, 0xf0, 0x02, 620x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67,
620x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x31, 0x35, 630x6f, 0x65, 0x52, 0x01, 0xf0, 0x02, 0x4e, 0x69, 0x6b, 0x6f,
630x5d, 0x00, 0x77, 0x68, 0x6f, 0x00, 0x63, 0x85, 0x00, 0x90, 640x6c, 0x69, 0x00, 0x5b, 0x31, 0x35, 0x5d, 0x00, 0x77, 0x68,
640x69, 0x74, 0x00, 0x48, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x41, 650x6f, 0x00, 0x63, 0x85, 0x00, 0x90, 0x69, 0x74, 0x00, 0x48,
650x00, 0x03, 0x0f, 0x02, 0x41, 0x00, 0x77, 0x61, 0x73, 0x95, 660x69, 0x74, 0x6f, 0x72, 0x69, 0x41, 0x00, 0x03, 0x23, 0x02,
660x00, 0x40, 0x72, 0x69, 0x62, 0x75, 0x84, 0x00, 0x22, 0x74, 670x41, 0x00, 0x77, 0x61, 0x73, 0x95, 0x00, 0x40, 0x72, 0x69,
670x6f, 0x51, 0x00, 0x60, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 680x62, 0x75, 0x84, 0x00, 0x22, 0x74, 0x6f, 0x51, 0x00, 0x60,
680x78, 0x01, 0x00, 0x97, 0x00, 0xc0, 0x4a, 0x61, 0x6d, 0x65, 690x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x78, 0x01, 0x00, 0x97,
690x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0x79, 0x3e, 0x00, 700x00, 0xc0, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61,
700x01, 0x59, 0x00, 0xc1, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 710x72, 0x76, 0x65, 0x79, 0x3e, 0x00, 0x01, 0x59, 0x00, 0xd1,
710x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x70, 0x00, 0x82, 0x2e, 720x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
720x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x2f, 0x8d, 0x00, 0x32, 730x77, 0x2e, 0x6e, 0x71, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e,
730x73, 0x2f, 0x68, 0x6e, 0x00, 0xb1, 0x68, 0x74, 0x6d, 0x6c, 740x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0x90, 0x00, 0x31, 0x73,
740x00, 0x28, 0x62, 0x65, 0x77, 0x61, 0x72, 0x20, 0x02, 0xd5, 750x2f, 0x68, 0x71, 0x00, 0x85, 0x2f, 0x00, 0x00, 0x00, 0x33,
750x46, 0x6c, 0x61, 0x73, 0x68, 0x29, 0x00, 0x00, 0x00, 0x33, 760x32, 0x2e, 0x31, 0x99, 0x02, 0x01, 0x72, 0x00, 0xf0, 0x02,
760x32, 0x2e, 0x31, 0x98, 0x02, 0x01, 0x85, 0x00, 0x21, 0x6f, 770x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66,
770x6c, 0xa1, 0x02, 0xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 780x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xe8, 0x00, 0x10,
780x6c, 0x69, 0x63, 0x6b, 0xfb, 0x00, 0x10, 0x6f, 0xd2, 0x01, 790x6f, 0xbf, 0x01, 0x64, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79,
790x64, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0xbb, 0x01, 0x44, 800xa8, 0x01, 0x44, 0x77, 0x69, 0x6c, 0x6c, 0x64, 0x02, 0x22,
800x77, 0x69, 0x6c, 0x6c, 0x77, 0x02, 0x22, 0x69, 0x74, 0x96, 810x69, 0x74, 0x83, 0x01, 0x30, 0x3b, 0x00, 0x6c, 0x37, 0x00,
810x01, 0x39, 0x3b, 0x00, 0x6c, 0x37, 0x00, 0x20, 0x61, 0x67, 820x15, 0x00, 0x38, 0x00, 0x20, 0x61, 0x67, 0xa3, 0x02, 0x01,
820xb5, 0x02, 0x01, 0x2a, 0x00, 0x42, 0x72, 0x65, 0x73, 0x74, 830x2b, 0x00, 0x42, 0x72, 0x65, 0x73, 0x74, 0x14, 0x02, 0x04,
830x26, 0x02, 0x04, 0x7e, 0x02, 0x67, 0x2e, 0x00, 0x52, 0x69, 840x6c, 0x02, 0x67, 0x2e, 0x00, 0x52, 0x69, 0x67, 0x68, 0x66,
840x67, 0x68, 0x2e, 0x00, 0x01, 0x28, 0x00, 0xf1, 0x05, 0x61, 850x00, 0x01, 0x28, 0x00, 0xf1, 0x05, 0x61, 0x64, 0x64, 0x00,
850x64, 0x64, 0x00, 0x61, 0x00, 0x63, 0x69, 0x72, 0x63, 0x6c, 860x61, 0x00, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x28,
860x65, 0x00, 0x28, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x64, 870x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x52, 0x01, 0x72, 0x69,
870x01, 0x72, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0xc2, 880x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0xb0, 0x02, 0x20, 0x61,
880x02, 0x20, 0x61, 0x74, 0x25, 0x00, 0x30, 0x65, 0x6c, 0x6c, 890x74, 0x25, 0x00, 0x30, 0x65, 0x6c, 0x6c, 0x30, 0x02, 0xa1,
890x42, 0x02, 0xa1, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 900x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x6c, 0x79,
900x65, 0x6c, 0x79, 0xb5, 0x01, 0x01, 0x8a, 0x00, 0x23, 0x29, 910xa3, 0x01, 0x01, 0x8b, 0x00, 0x45, 0x29, 0x2e, 0x00, 0x43,
910x2e, 0x65, 0x03, 0xb2, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c, 920xbe, 0x00, 0x52, 0x75, 0x74, 0x73, 0x69, 0x64, 0x7b, 0x00,
920x73, 0x6f, 0x00, 0x75, 0x73, 0x7d, 0x00, 0x00, 0xb1, 0x02, 930x01, 0x5b, 0x03, 0x01, 0x69, 0x00, 0xad, 0x74, 0x6f, 0x67,
930x61, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xa7, 0x01, 0x20, 940x67, 0x6c, 0x65, 0x00, 0x77, 0x68, 0x65, 0x44, 0x02, 0x00,
940x6d, 0x6f, 0x85, 0x03, 0x51, 0x72, 0x6f, 0x75, 0x6e, 0x64, 950x69, 0x01, 0x41, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x00, 0x14,
950x1f, 0x00, 0x00, 0x8e, 0x03, 0x74, 0x2e, 0x00, 0x50, 0x72, 960x68, 0x3b, 0x00, 0x03, 0x6a, 0x03, 0x00, 0x0d, 0x01, 0x50,
960x65, 0x73, 0x73, 0x2d, 0x03, 0x60, 0x72, 0x65, 0x74, 0x75, 970x74, 0x68, 0x65, 0x6d, 0x2c, 0x9b, 0x02, 0x71, 0x64, 0x69,
970x72, 0x6e, 0x9c, 0x02, 0x52, 0x73, 0x70, 0x61, 0x63, 0x65, 980x73, 0x70, 0x6c, 0x61, 0x79, 0x11, 0x00, 0x00, 0xe5, 0x02,
980x3b, 0x00, 0x01, 0xad, 0x00, 0x01, 0x18, 0x00, 0x14, 0x61, 990x72, 0x64, 0x61, 0x72, 0x6b, 0x00, 0x67, 0x72, 0x90, 0x01,
990x12, 0x01, 0x02, 0x99, 0x02, 0x2a, 0x6f, 0x72, 0xc4, 0x00, 1000x00, 0xd2, 0x03, 0xb2, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c,
1000x40, 0x72, 0x65, 0x73, 0x70, 0xc6, 0x01, 0xb8, 0x76, 0x65, 1010x73, 0x6f, 0x00, 0x75, 0x73, 0x46, 0x00, 0x00, 0x1e, 0x03,
1010x6c, 0x79, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x70, 0x5d, 1020x61, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x14, 0x02, 0x20,
1020x00, 0x3f, 0x6b, 0x65, 0x79, 0x27, 0x01, 0x0b, 0x00, 0x51, 1030x6d, 0x6f, 0xf2, 0x03, 0x55, 0x72, 0x6f, 0x75, 0x6e, 0x64,
1030x00, 0x01, 0xbc, 0x03, 0x03, 0xbf, 0x00, 0x01, 0x56, 0x00, 1040xa0, 0x00, 0x74, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73,
1040x00, 0xde, 0x00, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x13, 0x00, 1050x9a, 0x03, 0x60, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x6e,
1050x11, 0x61, 0x24, 0x02, 0x00, 0x08, 0x01, 0x70, 0x73, 0x63, 1060x00, 0x52, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3b, 0x00, 0x02,
1060x72, 0x69, 0x62, 0x65, 0x64, 0x81, 0x03, 0x13, 0x73, 0x3a, 1070xc3, 0x00, 0x00, 0x18, 0x00, 0x14, 0x61, 0x92, 0x01, 0x02,
1070x02, 0x40, 0x32, 0x2e, 0x31, 0x00, 0x9d, 0x00, 0x01, 0x04, 1080xc2, 0x00, 0x2a, 0x6f, 0x72, 0x43, 0x01, 0x40, 0x72, 0x65,
1080x01, 0xa3, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 1090x73, 0x70, 0x33, 0x02, 0xb8, 0x76, 0x65, 0x6c, 0x79, 0x2c,
1090x65, 0x2e, 0xfc, 0x01, 0x15, 0x32, 0xfc, 0x01, 0x91, 0x70, 1100x00, 0x61, 0x6e, 0x64, 0x00, 0x70, 0x5d, 0x00, 0x3f, 0x6b,
1100x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0xfe, 0x01, 1110x65, 0x79, 0xa6, 0x01, 0x0b, 0x00, 0x51, 0x00, 0x01, 0x29,
1110x66, 0x54, 0x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 1120x04, 0x03, 0xbf, 0x00, 0x01, 0x56, 0x00, 0x00, 0xde, 0x00,
1120x43, 0x00, 0x04, 0x3e, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 1130x41, 0x28, 0x41, 0x6c, 0x6c, 0x13, 0x00, 0x11, 0x61, 0x91,
1130x6d, 0x7b, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 1140x02, 0x00, 0x87, 0x01, 0x70, 0x73, 0x63, 0x72, 0x69, 0x62,
1140x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 0x00, 1150x65, 0x64, 0x09, 0x01, 0x13, 0x73, 0xa7, 0x02, 0x40, 0x32,
1150x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 1160x2e, 0x31, 0x00, 0x9d, 0x00, 0x01, 0x04, 0x01, 0xb2, 0x61,
1160x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xad, 0x00, 0x90, 0x57, 1170x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29,
1170x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0xf3, 0x01, 1180x7c, 0x02, 0x15, 0x32, 0x7c, 0x02, 0x91, 0x70, 0x61, 0x72,
1180x01, 0xfc, 0x02, 0x11, 0x7a, 0x89, 0x02, 0x01, 0xfd, 0x04, 1190x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x7e, 0x02, 0x66, 0x54,
1190x24, 0x69, 0x6e, 0xaf, 0x03, 0x00, 0x2b, 0x00, 0xa0, 0x44, 1200x68, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x43, 0x00,
1200x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x65, 1210x04, 0x3e, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x7b,
1210x03, 0x03, 0x93, 0x02, 0x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 1220x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e,
1220x00, 0x03, 0xa4, 0x04, 0x60, 0x67, 0x65, 0x6e, 0x65, 0x72, 1230x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 0x00, 0x22, 0x6f,
1230x61, 0x35, 0x03, 0x80, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 1240x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1240x2e, 0x00, 1250x6d, 0x65, 0x6e, 0x75, 0xad, 0x00, 0x90, 0x57, 0x69, 0x64,
1260x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x72, 0x02, 0x01, 0x69,
1270x03, 0x11, 0x7a, 0x16, 0x05, 0x01, 0x0f, 0x02, 0x24, 0x69,
1280x6e, 0xf8, 0x01, 0x00, 0x2b, 0x00, 0xa0, 0x44, 0x69, 0x66,
1290x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0xd2, 0x03, 0x03,
1300x13, 0x03, 0x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x03,
1310x11, 0x05, 0x60, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0xa2,
1320x03, 0x02, 0x5b, 0x03, 0x12, 0x2e, 0xd7, 0x00, 0x15, 0x33,
1330xd7, 0x00, 0xf1, 0x00, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70,
1340x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0xdd,
1350x00, 0x70, 0x4f, 0x6e, 0x00, 0x70, 0x6c, 0x61, 0x74, 0x7e,
1360x04, 0x12, 0x73, 0xcb, 0x02, 0x80, 0x73, 0x75, 0x70, 0x70,
1370x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x17, 0x00, 0x2e, 0x00,
1380x12, 0x2c, 0xcb, 0x00, 0x16, 0x50, 0x12, 0x00, 0x0d, 0xe7,
1390x00, 0x33, 0x47, 0x61, 0x6d, 0xe7, 0x00, 0x02, 0xc1, 0x01,
1400x70, 0x6c, 0x65, 0x74, 0x00, 0x79, 0x6f, 0x75, 0x3e, 0x04,
1410x56, 0x66, 0x69, 0x67, 0x75, 0x72, 0xe1, 0x02, 0x07, 0xbf,
1420x02, 0x0a, 0xec, 0x02, 0x00, 0x5c, 0x01, 0x50, 0x76, 0x69,
1430x73, 0x69, 0x62, 0xc0, 0x00, 0x8f, 0x55, 0x6e, 0x6c, 0x69,
1440x6b, 0x65, 0x00, 0x63, 0x3c, 0x03, 0x05, 0x12, 0x2c, 0x89,
1450x04, 0x01, 0x6c, 0x00, 0xf0, 0x08, 0x70, 0x65, 0x72, 0x73,
1460x69, 0x73, 0x74, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65,
1470x6e, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x00,
125}; 148};
126 149
127const unsigned short help_text_len = 1392; 150const unsigned short help_text_len = 1780;
128const unsigned short help_text_words = 250; 151const unsigned short help_text_words = 309;
152const bool help_valid = true;
129const char quick_help_text[] = "Black out the right set of duplicate numbers."; 153const char quick_help_text[] = "Black out the right set of duplicate numbers.";
diff --git a/apps/plugins/puzzles/help/sixteen.c b/apps/plugins/puzzles/help/sixteen.c
index c3d52b6220..1ba92291f3 100644
--- a/apps/plugins/puzzles/help/sixteen.c
+++ b/apps/plugins/puzzles/help/sixteen.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,194 +6,196 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 113, TEXT_UNDERLINE }, 9 { 114, TEXT_UNDERLINE },
10 { 187, TEXT_CENTER | C_RED }, 10 { 188, TEXT_CENTER | C_RED },
11 { 321, TEXT_CENTER | C_RED }, 11 { 322, TEXT_CENTER | C_RED },
12 { 339, TEXT_UNDERLINE }, 12 { 340, TEXT_UNDERLINE },
13 { 341, TEXT_UNDERLINE }, 13 { 342, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 2534 comp 1755 ratio 0.692581 level 6 saved 779 */ 17/* orig 2553 comp 1763 ratio 0.69056 level 6 saved 790 */
18const char help_text[] = { 18const char help_text[] = {
190xf0, 0x3b, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x36, 0x3a, 0x20, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e, 200x36, 0x3a, 0x20, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e,
210x20, 0x00, 0x00, 0x00, 0x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 210x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x28, 0x00, 0x00, 0x00,
220x72, 0x00, 0x73, 0x6c, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x00, 220x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 0x73, 0x6c,
230x74, 0x69, 0x6c, 0x65, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 230x69, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x69, 0x6c, 0x65,
240x65, 0x2c, 0x00, 0x76, 0x69, 0x73, 0x75, 0x61, 0x6c, 0x6c, 240x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x00, 0x76,
250x79, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x00, 250x69, 0x73, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x69,
260x74, 0x6f, 0x00, 0x46, 0x69, 0x66, 0x3c, 0x00, 0x72, 0x00, 260x6d, 0x69, 0x6c, 0x61, 0x72, 0x00, 0x74, 0x6f, 0x00, 0x46,
270x28, 0x73, 0x65, 0x65, 0x00, 0x63, 0x54, 0x00, 0xf0, 0x23, 270x69, 0x66, 0x4f, 0x00, 0x72, 0x00, 0x28, 0x73, 0x65, 0x65,
280x00, 0x35, 0x29, 0x00, 0x62, 0x75, 0x74, 0x00, 0x77, 0x69, 280x00, 0x63, 0x67, 0x00, 0xf0, 0x23, 0x00, 0x35, 0x29, 0x00,
290x74, 0x68, 0x00, 0x61, 0x00, 0x64, 0x69, 0x66, 0x66, 0x65, 290x62, 0x75, 0x74, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x61,
300x72, 0x65, 0x6e, 0x74, 0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 300x00, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74,
310x6f, 0x66, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0x2e, 0x00, 0x54, 310x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x6f, 0x66, 0x00, 0x6d,
320x68, 0x69, 0x73, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x2c, 0x00, 320x6f, 0x76, 0x65, 0x2e, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00,
330x74, 0x00, 0xf0, 0x10, 0x65, 0x00, 0x69, 0x73, 0x00, 0x6e, 330x74, 0x69, 0x6d, 0x65, 0x2c, 0x00, 0x74, 0x00, 0xf0, 0x10,
340x6f, 0x00, 0x68, 0x6f, 0x6c, 0x65, 0x3a, 0x00, 0x61, 0x6c, 340x65, 0x00, 0x69, 0x73, 0x00, 0x6e, 0x6f, 0x00, 0x68, 0x6f,
350x6c, 0x00, 0x31, 0x36, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 350x6c, 0x65, 0x3a, 0x00, 0x61, 0x6c, 0x6c, 0x00, 0x31, 0x36,
360x65, 0x73, 0x00, 0x6f, 0x6e, 0x24, 0x00, 0xf4, 0x07, 0x00, 360x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x00, 0x6f,
370x67, 0x72, 0x69, 0x64, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 370x6e, 0x24, 0x00, 0xf4, 0x07, 0x00, 0x67, 0x72, 0x69, 0x64,
380x69, 0x6e, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x65, 380x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x6e,
390x64, 0x25, 0x00, 0x61, 0x2e, 0x00, 0x59, 0x6f, 0x75, 0x72, 390x75, 0x6d, 0x62, 0x65, 0x72, 0x65, 0x64, 0x25, 0x00, 0x61,
400x5d, 0x00, 0x00, 0x4b, 0x00, 0xf0, 0x16, 0x74, 0x6f, 0x00, 400x2e, 0x00, 0x59, 0x6f, 0x75, 0x72, 0x5d, 0x00, 0x00, 0x4b,
410x73, 0x68, 0x69, 0x66, 0x74, 0x00, 0x61, 0x6e, 0x00, 0x65, 410x00, 0xf0, 0x16, 0x74, 0x6f, 0x00, 0x73, 0x68, 0x69, 0x66,
420x6e, 0x74, 0x69, 0x72, 0x65, 0x00, 0x72, 0x6f, 0x77, 0x00, 420x74, 0x00, 0x61, 0x6e, 0x00, 0x65, 0x6e, 0x74, 0x69, 0x72,
430x6c, 0x65, 0x66, 0x74, 0x00, 0x6f, 0x72, 0x00, 0x72, 0x69, 430x65, 0x00, 0x72, 0x6f, 0x77, 0x00, 0x6c, 0x65, 0x66, 0x74,
440x67, 0x68, 0x74, 0x2c, 0x0a, 0x00, 0x0c, 0x26, 0x00, 0x90, 440x00, 0x6f, 0x72, 0x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2c,
450x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x00, 0x75, 0x70, 0x1d, 450x0a, 0x00, 0x0c, 0x26, 0x00, 0x90, 0x63, 0x6f, 0x6c, 0x75,
460x00, 0xb1, 0x64, 0x6f, 0x77, 0x6e, 0x3b, 0x00, 0x65, 0x76, 460x6d, 0x6e, 0x00, 0x75, 0x70, 0x1d, 0x00, 0xb1, 0x64, 0x6f,
470x65, 0x72, 0x79, 0xac, 0x00, 0xc1, 0x00, 0x79, 0x6f, 0x75, 470x77, 0x6e, 0x3b, 0x00, 0x65, 0x76, 0x65, 0x72, 0x79, 0xac,
480x00, 0x64, 0x6f, 0x00, 0x74, 0x68, 0x61, 0x74, 0xb8, 0x00, 480x00, 0xc1, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x64, 0x6f, 0x00,
490x02, 0x23, 0x01, 0x00, 0x16, 0x00, 0x02, 0x48, 0x00, 0x36, 490x74, 0x68, 0x61, 0x74, 0xb8, 0x00, 0x02, 0x23, 0x01, 0x00,
500x6f, 0x66, 0x66, 0xab, 0x00, 0xd1, 0x72, 0x65, 0x2d, 0x61, 500x16, 0x00, 0x02, 0x48, 0x00, 0x36, 0x6f, 0x66, 0x66, 0xab,
510x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x00, 0x61, 0x74, 0x17, 510x00, 0xd1, 0x72, 0x65, 0x2d, 0x61, 0x70, 0x70, 0x65, 0x61,
520x00, 0x02, 0x5f, 0x01, 0x52, 0x65, 0x6e, 0x64, 0x00, 0x6f, 520x72, 0x73, 0x00, 0x61, 0x74, 0x17, 0x00, 0x02, 0x5f, 0x01,
530x28, 0x00, 0x31, 0x73, 0x61, 0x6d, 0x99, 0x00, 0x32, 0x2c, 530x52, 0x65, 0x6e, 0x64, 0x00, 0x6f, 0x28, 0x00, 0x31, 0x73,
540x00, 0x69, 0xe4, 0x00, 0x42, 0x73, 0x70, 0x61, 0x63, 0x51, 540x61, 0x6d, 0x99, 0x00, 0x32, 0x2c, 0x00, 0x69, 0xe4, 0x00,
550x00, 0xf5, 0x0e, 0x6a, 0x75, 0x73, 0x74, 0x00, 0x76, 0x61, 550x42, 0x73, 0x70, 0x61, 0x63, 0x51, 0x00, 0xf5, 0x0e, 0x6a,
560x63, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x54, 0x6f, 0x00, 560x75, 0x73, 0x74, 0x00, 0x76, 0x61, 0x63, 0x61, 0x74, 0x65,
570x77, 0x69, 0x6e, 0x2c, 0x00, 0x61, 0x72, 0x72, 0x61, 0x6e, 570x64, 0x2e, 0x00, 0x54, 0x6f, 0x00, 0x77, 0x69, 0x6e, 0x2c,
580x67, 0x65, 0x7c, 0x00, 0x60, 0x73, 0x00, 0x69, 0x6e, 0x74, 580x00, 0x61, 0x72, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x7c, 0x00,
590x6f, 0x0e, 0x01, 0xf4, 0x06, 0x65, 0x72, 0x69, 0x63, 0x61, 590x60, 0x73, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x0e, 0x01, 0xf4,
600x6c, 0x00, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x00, 0x28, 0x31, 600x06, 0x65, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x00, 0x6f, 0x72,
610x2c, 0x32, 0x2c, 0x33, 0x2c, 0x34, 0x3b, 0x01, 0x32, 0x74, 610x64, 0x65, 0x72, 0x00, 0x28, 0x31, 0x2c, 0x32, 0x2c, 0x33,
620x6f, 0x70, 0x67, 0x00, 0xb4, 0x31, 0x33, 0x2c, 0x31, 0x34, 620x2c, 0x34, 0x3b, 0x01, 0x32, 0x74, 0x6f, 0x70, 0x67, 0x00,
630x2c, 0x31, 0x35, 0x2c, 0x31, 0x36, 0x1c, 0x00, 0xd0, 0x62, 630xb4, 0x31, 0x33, 0x2c, 0x31, 0x34, 0x2c, 0x31, 0x35, 0x2c,
640x6f, 0x74, 0x74, 0x6f, 0x6d, 0x29, 0x2e, 0x00, 0x57, 0x68, 640x31, 0x36, 0x1c, 0x00, 0xd0, 0x62, 0x6f, 0x74, 0x74, 0x6f,
650x65, 0x6e, 0x7b, 0x00, 0x84, 0x27, 0x76, 0x65, 0x00, 0x64, 650x6d, 0x29, 0x2e, 0x00, 0x57, 0x68, 0x65, 0x6e, 0x7b, 0x00,
660x6f, 0x6e, 0x65, 0xe7, 0x00, 0x70, 0x72, 0x79, 0x00, 0x70, 660x84, 0x27, 0x76, 0x65, 0x00, 0x64, 0x6f, 0x6e, 0x65, 0xe7,
670x6c, 0x61, 0x79, 0x12, 0x02, 0x27, 0x6f, 0x6e, 0xd1, 0x01, 670x00, 0x70, 0x72, 0x79, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x12,
680x30, 0x73, 0x69, 0x7a, 0x9d, 0x01, 0x11, 0x66, 0xee, 0x00, 680x02, 0x27, 0x6f, 0x6e, 0xd1, 0x01, 0x30, 0x73, 0x69, 0x7a,
690x70, 0x2e, 0x00, 0x00, 0x00, 0x49, 0x00, 0x6d, 0x58, 0x01, 690x9d, 0x01, 0x11, 0x66, 0xee, 0x00, 0x70, 0x2e, 0x00, 0x00,
700x30, 0x00, 0x68, 0x61, 0x85, 0x01, 0x90, 0x6e, 0x76, 0x65, 700x00, 0x49, 0x00, 0x6d, 0x58, 0x01, 0x30, 0x00, 0x68, 0x61,
710x6e, 0x74, 0x65, 0x64, 0x00, 0x74, 0xea, 0x01, 0x10, 0x67, 710x85, 0x01, 0x90, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x65, 0x64,
720xe9, 0x00, 0x60, 0x6d, 0x79, 0x73, 0x65, 0x6c, 0x66, 0x39, 720x00, 0x74, 0xea, 0x01, 0x10, 0x67, 0xe9, 0x00, 0x60, 0x6d,
730x01, 0xf0, 0x03, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x6f, 0x6e, 730x79, 0x73, 0x65, 0x6c, 0x66, 0x39, 0x01, 0xf0, 0x03, 0x6f,
740x6c, 0x79, 0x00, 0x62, 0x79, 0x00, 0x61, 0x63, 0x63, 0x69, 740x75, 0x67, 0x68, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x62,
750x64, 0x51, 0x00, 0xf4, 0x03, 0x69, 0x66, 0x00, 0x73, 0x6f, 750x79, 0x00, 0x61, 0x63, 0x63, 0x69, 0x64, 0x51, 0x00, 0xf4,
760x00, 0x28, 0x61, 0x6e, 0x64, 0x00, 0x49, 0x27, 0x6d, 0x00, 760x03, 0x69, 0x66, 0x00, 0x73, 0x6f, 0x00, 0x28, 0x61, 0x6e,
770x73, 0x75, 0x72, 0x33, 0x01, 0x64, 0x70, 0x65, 0x6f, 0x70, 770x64, 0x00, 0x49, 0x27, 0x6d, 0x00, 0x73, 0x75, 0x72, 0x33,
780x6c, 0x65, 0x59, 0x00, 0x50, 0x64, 0x65, 0x70, 0x65, 0x6e, 780x01, 0x64, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x59, 0x00,
790x32, 0x00, 0x26, 0x6c, 0x79, 0x67, 0x00, 0x63, 0x69, 0x74, 790x50, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x32, 0x00, 0x26, 0x6c,
800x29, 0x2e, 0x00, 0x49, 0x5c, 0x00, 0xe0, 0x74, 0x00, 0x49, 800x79, 0x67, 0x00, 0x63, 0x69, 0x74, 0x29, 0x2e, 0x00, 0x49,
810x00, 0x77, 0x61, 0x73, 0x00, 0x69, 0x6d, 0x69, 0x74, 0x61, 810x5c, 0x00, 0xe0, 0x74, 0x00, 0x49, 0x00, 0x77, 0x61, 0x73,
820x74, 0xba, 0x00, 0xf1, 0x08, 0x61, 0x00, 0x73, 0x63, 0x72, 820x00, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0xba, 0x00, 0xf1,
830x65, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x72, 0x00, 0x49, 830x08, 0x61, 0x00, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73,
840x27, 0x64, 0x00, 0x73, 0x65, 0x65, 0x6e, 0x2c, 0xab, 0x02, 840x61, 0x76, 0x65, 0x72, 0x00, 0x49, 0x27, 0x64, 0x00, 0x73,
850x12, 0x49, 0x59, 0x00, 0x61, 0x61, 0x00, 0x66, 0x65, 0x65, 850x65, 0x65, 0x6e, 0x2c, 0xab, 0x02, 0x12, 0x49, 0x59, 0x00,
860x6c, 0xf9, 0x02, 0x13, 0x68, 0xb1, 0x01, 0x08, 0x34, 0x00, 860x61, 0x61, 0x00, 0x66, 0x65, 0x65, 0x6c, 0xf9, 0x02, 0x13,
870x02, 0xdc, 0x00, 0x32, 0x61, 0x63, 0x74, 0x07, 0x03, 0x01, 870x68, 0xb1, 0x01, 0x08, 0x34, 0x00, 0x02, 0xdc, 0x00, 0x32,
880x33, 0x00, 0x10, 0x62, 0xfe, 0x02, 0x14, 0x61, 0x08, 0x03, 880x61, 0x63, 0x74, 0x07, 0x03, 0x01, 0x33, 0x00, 0x10, 0x62,
890x11, 0x2d, 0xe3, 0x02, 0x02, 0x31, 0x03, 0x31, 0x00, 0x72, 890xfe, 0x02, 0x14, 0x61, 0x08, 0x03, 0x11, 0x2d, 0xe3, 0x02,
900x61, 0xba, 0x00, 0x42, 0x74, 0x68, 0x61, 0x6e, 0x03, 0x01, 900x02, 0x31, 0x03, 0x31, 0x00, 0x72, 0x61, 0xba, 0x00, 0x42,
910x20, 0x73, 0x6c, 0x41, 0x00, 0x27, 0x6c, 0x79, 0x42, 0x01, 910x74, 0x68, 0x61, 0x6e, 0x03, 0x01, 0x20, 0x73, 0x6c, 0x41,
920x82, 0x6b, 0x69, 0x6e, 0x64, 0x2e, 0x00, 0x53, 0x6f, 0x21, 920x00, 0x27, 0x6c, 0x79, 0x42, 0x01, 0x82, 0x6b, 0x69, 0x6e,
930x00, 0x02, 0x61, 0x00, 0x12, 0x62, 0xdd, 0x01, 0x02, 0x80, 930x64, 0x2e, 0x00, 0x53, 0x6f, 0x21, 0x00, 0x02, 0x61, 0x00,
940x01, 0x00, 0x8d, 0x00, 0x54, 0x69, 0x6e, 0x00, 0x6d, 0x79, 940x12, 0x62, 0xdd, 0x01, 0x02, 0x80, 0x01, 0x00, 0x8d, 0x00,
950x56, 0x00, 0xf2, 0x19, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 950x54, 0x69, 0x6e, 0x00, 0x6d, 0x79, 0x56, 0x00, 0xf2, 0x19,
960x74, 0x69, 0x6f, 0x6e, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 960x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
970x00, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 970x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x72, 0x65, 0x70,
980x73, 0x00, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x76, 0x69, 980x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x63, 0x72,
990x74, 0x79, 0x00, 0x6f, 0x34, 0x00, 0x39, 0x61, 0x72, 0x74, 990x65, 0x61, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x00, 0x6f,
1000x88, 0x00, 0x01, 0x54, 0x02, 0xb0, 0x65, 0x6e, 0x67, 0x69, 1000x34, 0x00, 0x39, 0x61, 0x72, 0x74, 0x88, 0x00, 0x01, 0x54,
1010x6e, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0xb5, 0x01, 0x35, 1010x02, 0xb0, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x65, 0x72,
1020x36, 0x2e, 0x31, 0x04, 0x04, 0x00, 0x5d, 0x03, 0x40, 0x72, 1020x69, 0x6e, 0x67, 0xb5, 0x01, 0x35, 0x36, 0x2e, 0x31, 0x17,
1030x6f, 0x6c, 0x73, 0x0d, 0x04, 0xa3, 0x4c, 0x65, 0x66, 0x74, 1030x04, 0x00, 0x5d, 0x03, 0xf3, 0x03, 0x72, 0x6f, 0x6c, 0x73,
1040x2d, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xf9, 0x01, 0x20, 0x61, 1040x20, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63,
1050x6e, 0x7e, 0x02, 0x72, 0x6f, 0x77, 0x00, 0x77, 0x69, 0x6c, 1050x6c, 0x69, 0x63, 0x6b, 0xf9, 0x01, 0x20, 0x61, 0x6e, 0x7e,
1060x6c, 0x69, 0x03, 0x00, 0xa9, 0x00, 0xa2, 0x61, 0x70, 0x70, 1060x02, 0x72, 0x6f, 0x77, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x69,
1070x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x63, 0x03, 0x24, 1070x03, 0x00, 0xa9, 0x00, 0xa2, 0x61, 0x70, 0x70, 0x72, 0x6f,
1080x6f, 0x72, 0x44, 0x03, 0x03, 0xd3, 0x02, 0x33, 0x64, 0x69, 1080x70, 0x72, 0x69, 0x61, 0x74, 0x63, 0x03, 0x24, 0x6f, 0x72,
1090x72, 0xb2, 0x00, 0x43, 0x69, 0x6e, 0x64, 0x69, 0xd0, 0x02, 1090x44, 0x03, 0x03, 0xd3, 0x02, 0x33, 0x64, 0x69, 0x72, 0xb2,
1100x10, 0x52, 0xec, 0x00, 0x06, 0x5f, 0x00, 0x06, 0x53, 0x00, 1100x00, 0x43, 0x69, 0x6e, 0x64, 0x69, 0xd0, 0x02, 0x10, 0x52,
1110x24, 0x69, 0x74, 0x38, 0x00, 0x77, 0x6f, 0x70, 0x70, 0x6f, 1110xec, 0x00, 0x06, 0x5f, 0x00, 0x06, 0x53, 0x00, 0x24, 0x69,
1120x73, 0x69, 0x74, 0x41, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x41, 1120x74, 0x38, 0x00, 0x77, 0x6f, 0x70, 0x70, 0x6f, 0x73, 0x69,
1130x6c, 0x74, 0x65, 0x72, 0x6e, 0xe8, 0x00, 0x72, 0x65, 0x6c, 1130x74, 0x41, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x41, 0x6c, 0x74,
1140x79, 0x2c, 0x00, 0x75, 0x73, 0x86, 0x00, 0xa1, 0x63, 0x75, 1140x65, 0x72, 0x6e, 0xe8, 0x00, 0x72, 0x65, 0x6c, 0x79, 0x2c,
1150x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xfc, 0x03, 1150x00, 0x75, 0x73, 0x86, 0x00, 0xa1, 0x63, 0x75, 0x72, 0x73,
1160x05, 0x9e, 0x00, 0x01, 0x43, 0x00, 0x07, 0x7c, 0x00, 0x91, 1160x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0xfc, 0x03, 0x05, 0x9e,
1170x6f, 0x72, 0x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x1e, 1170x00, 0x01, 0x43, 0x00, 0x07, 0x7c, 0x00, 0x91, 0x6f, 0x72,
1180x00, 0x31, 0x65, 0x64, 0x67, 0x93, 0x04, 0x04, 0xb3, 0x03, 1180x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x1e, 0x00, 0x31,
1190x20, 0x2c, 0x00, 0x7c, 0x02, 0x04, 0x54, 0x00, 0x60, 0x72, 1190x65, 0x64, 0x67, 0x93, 0x04, 0x04, 0xb3, 0x03, 0x20, 0x2c,
1200x65, 0x74, 0x75, 0x72, 0x6e, 0x54, 0x00, 0x09, 0x53, 0x00, 1200x00, 0x7c, 0x02, 0x04, 0x54, 0x00, 0x60, 0x72, 0x65, 0x74,
1210x4f, 0x72, 0x6f, 0x77, 0x2f, 0xe2, 0x00, 0x10, 0x10, 0x00, 1210x75, 0x72, 0x6e, 0x54, 0x00, 0x09, 0x53, 0x00, 0x4f, 0x72,
1220x91, 0x04, 0x20, 0x00, 0x63, 0x36, 0x01, 0x27, 0x6c, 0x73, 1220x6f, 0x77, 0x2f, 0xe2, 0x00, 0x10, 0x10, 0x00, 0x91, 0x04,
1230x3f, 0x00, 0x02, 0xb6, 0x03, 0x02, 0x33, 0x00, 0x79, 0x6c, 1230x20, 0x00, 0x63, 0x36, 0x01, 0x27, 0x6c, 0x73, 0x3f, 0x00,
1240x79, 0x2e, 0x00, 0x4d, 0x6f, 0x76, 0xc3, 0x00, 0x10, 0x6f, 1240x02, 0xb6, 0x03, 0x02, 0x33, 0x00, 0x79, 0x6c, 0x79, 0x2e,
1250xd0, 0x03, 0x11, 0x61, 0x27, 0x00, 0x10, 0x2c, 0x0f, 0x05, 1250x00, 0x4d, 0x6f, 0x76, 0xc3, 0x00, 0x10, 0x6f, 0xd0, 0x03,
1260x32, 0x64, 0x00, 0x43, 0x96, 0x01, 0x01, 0x9c, 0x00, 0x00, 1260x11, 0x61, 0x27, 0x00, 0x10, 0x2c, 0x0f, 0x05, 0x32, 0x64,
1270xed, 0x01, 0x16, 0x73, 0x8b, 0x01, 0x0c, 0x9c, 0x00, 0x01, 1270x00, 0x43, 0x96, 0x01, 0x01, 0x9c, 0x00, 0x00, 0xed, 0x01,
1280x8f, 0x04, 0x20, 0x75, 0x6e, 0x03, 0x04, 0x07, 0x53, 0x00, 1280x16, 0x73, 0x8b, 0x01, 0x0c, 0x9c, 0x00, 0x01, 0x8f, 0x04,
1290x00, 0x39, 0x00, 0x1b, 0x6d, 0x67, 0x00, 0x52, 0x61, 0x6c, 1290x20, 0x75, 0x6e, 0x03, 0x04, 0x07, 0x53, 0x00, 0x00, 0x39,
1300x6f, 0x6e, 0x67, 0xa5, 0x05, 0x04, 0x39, 0x00, 0x43, 0x2e, 1300x00, 0x1b, 0x6d, 0x67, 0x00, 0x52, 0x61, 0x6c, 0x6f, 0x6e,
1310x00, 0x4f, 0x72, 0x73, 0x00, 0x11, 0x53, 0xce, 0x04, 0x04, 1310x67, 0xa5, 0x05, 0x04, 0x39, 0x00, 0x43, 0x2e, 0x00, 0x4f,
1320x5a, 0x00, 0x01, 0xa9, 0x03, 0x06, 0x26, 0x00, 0x10, 0x50, 1320x72, 0x73, 0x00, 0x11, 0x53, 0xce, 0x04, 0x04, 0x5a, 0x00,
1330x84, 0x00, 0x00, 0xb6, 0x01, 0x20, 0x45, 0x6e, 0xf0, 0x05, 1330x01, 0xa9, 0x03, 0x06, 0x26, 0x00, 0x10, 0x50, 0x84, 0x00,
1340x91, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 1340x00, 0xb6, 0x01, 0x20, 0x45, 0x6e, 0xf0, 0x05, 0x91, 0x73,
1350x3b, 0x00, 0x00, 0x18, 0x00, 0x00, 0x37, 0x05, 0x05, 0xb6, 1350x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x3b, 0x00,
1360x00, 0x12, 0x28, 0xb3, 0x00, 0x02, 0x2c, 0x00, 0x20, 0x61, 1360x00, 0x18, 0x00, 0x00, 0x37, 0x05, 0x05, 0xb6, 0x00, 0x12,
1370x67, 0xbc, 0x05, 0xc0, 0x74, 0x6f, 0x00, 0x72, 0x65, 0x6c, 1370x28, 0xb3, 0x00, 0x02, 0x2c, 0x00, 0x20, 0x61, 0x67, 0xbc,
1380x65, 0x61, 0x73, 0x65, 0x29, 0x2c, 0xc7, 0x02, 0x00, 0x66, 1380x05, 0xc0, 0x74, 0x6f, 0x00, 0x72, 0x65, 0x6c, 0x65, 0x61,
1390x06, 0x04, 0x54, 0x00, 0x11, 0x53, 0x01, 0x05, 0x0f, 0x54, 1390x73, 0x65, 0x29, 0x2c, 0xc7, 0x02, 0x00, 0x66, 0x06, 0x04,
1400x00, 0x04, 0x01, 0x65, 0x05, 0x00, 0x5b, 0x01, 0x42, 0x28, 1400x54, 0x00, 0x11, 0x53, 0x01, 0x05, 0x0f, 0x54, 0x00, 0x04,
1410x41, 0x6c, 0x6c, 0x7e, 0x02, 0x01, 0x78, 0x01, 0x90, 0x73, 1410x01, 0x65, 0x05, 0x00, 0x5b, 0x01, 0x42, 0x28, 0x41, 0x6c,
1420x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x08, 0x04, 1420x6c, 0x7e, 0x02, 0x01, 0x78, 0x01, 0x90, 0x73, 0x00, 0x64,
1430x33, 0x6e, 0x00, 0x73, 0x8e, 0x01, 0x72, 0x32, 0x2e, 0x31, 1430x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x08, 0x04, 0x33, 0x6e,
1440x00, 0x61, 0x72, 0x65, 0x81, 0x01, 0xb1, 0x61, 0x76, 0x61, 1440x00, 0x73, 0x8e, 0x01, 0x72, 0x32, 0x2e, 0x31, 0x00, 0x61,
1450x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0xf6, 0x02, 1450x72, 0x65, 0x81, 0x01, 0xb1, 0x61, 0x76, 0x61, 0x69, 0x6c,
1460x15, 0x32, 0xf6, 0x02, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 1460x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0xf6, 0x02, 0x15, 0x32,
1470x65, 0x74, 0x65, 0x72, 0xf8, 0x02, 0x10, 0x54, 0x36, 0x02, 1470xf6, 0x02, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1480x05, 0x12, 0x00, 0x06, 0x37, 0x00, 0x51, 0x00, 0x66, 0x72, 1480x65, 0x72, 0xf8, 0x02, 0x10, 0x54, 0x36, 0x02, 0x05, 0x12,
1490x6f, 0x6d, 0x74, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 1490x00, 0x06, 0x37, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d,
1500x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6a, 1500x74, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
1510x00, 0x03, 0x4b, 0x05, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 1510x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6a, 0x00, 0x03,
1520x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x79, 0x00, 0xb1, 0x3a, 1520x4b, 0x05, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1530x00, 0x00, 0x00, 0x2d, 0x00, 0x57, 0x69, 0x64, 0x74, 0x68, 1530x6d, 0x65, 0x6e, 0x75, 0x79, 0x00, 0xb1, 0x3a, 0x00, 0x00,
1540x84, 0x01, 0x22, 0x48, 0x65, 0x77, 0x06, 0x02, 0xba, 0x03, 1540x00, 0x2d, 0x00, 0x57, 0x69, 0x64, 0x74, 0x68, 0x84, 0x01,
1550x00, 0x9a, 0x00, 0x00, 0x10, 0x05, 0x70, 0x2d, 0x65, 0x78, 1550x22, 0x48, 0x65, 0x77, 0x06, 0x02, 0xba, 0x03, 0x00, 0x9a,
1560x70, 0x6c, 0x61, 0x6e, 0x9c, 0x02, 0x21, 0x79, 0x2e, 0x32, 1560x00, 0x00, 0x10, 0x05, 0x70, 0x2d, 0x65, 0x78, 0x70, 0x6c,
1570x00, 0x05, 0x39, 0x02, 0x40, 0x73, 0x6b, 0x00, 0x66, 0xac, 1570x61, 0x6e, 0x9c, 0x02, 0x21, 0x79, 0x2e, 0x32, 0x00, 0x05,
1580x01, 0x20, 0x00, 0x6c, 0xc9, 0x04, 0x00, 0xed, 0x06, 0x41, 1580x39, 0x02, 0x40, 0x73, 0x6b, 0x00, 0x66, 0xac, 0x01, 0x20,
1590x68, 0x75, 0x66, 0x66, 0xa3, 0x04, 0x51, 0x6f, 0x70, 0x65, 1590x00, 0x6c, 0xc9, 0x04, 0x00, 0xed, 0x06, 0x41, 0x68, 0x75,
1600x72, 0x61, 0x7c, 0x00, 0x20, 0x74, 0x6f, 0x34, 0x04, 0x98, 1600x66, 0x66, 0xa3, 0x04, 0x51, 0x6f, 0x70, 0x65, 0x72, 0x61,
1610x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x2e, 1610x7c, 0x00, 0x20, 0x74, 0x6f, 0x34, 0x04, 0x98, 0x70, 0x65,
1620x07, 0xe3, 0x2e, 0x00, 0x42, 0x79, 0x00, 0x64, 0x65, 0x66, 1620x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x2e, 0x07, 0xe3,
1630x61, 0x75, 0x6c, 0x74, 0x2c, 0x00, 0xf0, 0x00, 0x02, 0x6d, 1630x2e, 0x00, 0x42, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75,
1640x03, 0x02, 0x4a, 0x00, 0x16, 0x65, 0xae, 0x06, 0x00, 0x38, 1640x6c, 0x74, 0x2c, 0x00, 0xf0, 0x00, 0x02, 0x6d, 0x03, 0x02,
1650x01, 0x10, 0x75, 0x97, 0x00, 0x42, 0x00, 0x77, 0x61, 0x79, 1650x4a, 0x00, 0x16, 0x65, 0xae, 0x06, 0x00, 0x38, 0x01, 0x10,
1660x02, 0x05, 0x34, 0x61, 0x6e, 0x79, 0x71, 0x06, 0x11, 0x6d, 1660x75, 0x97, 0x00, 0x42, 0x00, 0x77, 0x61, 0x79, 0x02, 0x05,
1670xa0, 0x05, 0xf1, 0x00, 0x73, 0x00, 0x61, 0x62, 0x6f, 0x75, 1670x34, 0x61, 0x6e, 0x79, 0x71, 0x06, 0x11, 0x6d, 0xa0, 0x05,
1680x74, 0x00, 0x61, 0x73, 0x00, 0x70, 0x72, 0x6f, 0x62, 0x17, 1680xf1, 0x00, 0x73, 0x00, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x00,
1690x01, 0x21, 0x61, 0x73, 0x28, 0x00, 0x01, 0xa8, 0x05, 0x15, 1690x61, 0x73, 0x00, 0x70, 0x72, 0x6f, 0x62, 0x17, 0x01, 0x21,
1700x2e, 0xbb, 0x00, 0x82, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 1700x61, 0x73, 0x28, 0x00, 0x01, 0xa8, 0x05, 0x15, 0x2e, 0xbb,
1710x64, 0x65, 0xda, 0x04, 0x93, 0x62, 0x79, 0x00, 0x72, 0x65, 1710x00, 0x82, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
1720x71, 0x75, 0x65, 0x73, 0x8b, 0x05, 0x73, 0x70, 0x72, 0x65, 1720xda, 0x04, 0x93, 0x62, 0x79, 0x00, 0x72, 0x65, 0x71, 0x75,
1730x63, 0x69, 0x73, 0x65, 0xc5, 0x07, 0x00, 0x82, 0x03, 0x06, 1730x65, 0x73, 0x8b, 0x05, 0x73, 0x70, 0x72, 0x65, 0x63, 0x69,
1740xd9, 0x00, 0x00, 0x72, 0x02, 0x1c, 0x73, 0xd5, 0x00, 0x71, 1740x73, 0x65, 0xc5, 0x07, 0x00, 0x82, 0x03, 0x06, 0xd9, 0x00,
1750x2e, 0x00, 0x54, 0x79, 0x70, 0x69, 0x63, 0x79, 0x05, 0x10, 1750x00, 0x72, 0x02, 0x1c, 0x73, 0xd5, 0x00, 0x71, 0x2e, 0x00,
1760x79, 0xe8, 0x07, 0x31, 0x61, 0x69, 0x6d, 0xe7, 0x07, 0x21, 1760x54, 0x79, 0x70, 0x69, 0x63, 0x79, 0x05, 0x10, 0x79, 0xe8,
1770x68, 0x65, 0x01, 0x01, 0x10, 0x64, 0xad, 0x01, 0x33, 0x6d, 1770x07, 0x31, 0x61, 0x69, 0x6d, 0xe7, 0x07, 0x21, 0x68, 0x65,
1780x69, 0x6e, 0xf5, 0x03, 0x03, 0x5f, 0x00, 0x3f, 0x73, 0x65, 1780x01, 0x01, 0x10, 0x64, 0xad, 0x01, 0x33, 0x6d, 0x69, 0x6e,
1790x74, 0x5c, 0x00, 0x01, 0x00, 0x8b, 0x01, 0x00, 0x37, 0x06, 1790xf5, 0x03, 0x03, 0x5f, 0x00, 0x3f, 0x73, 0x65, 0x74, 0x5c,
1800x11, 0x72, 0xe4, 0x05, 0x50, 0x6d, 0x00, 0x65, 0x78, 0x61, 1800x00, 0x01, 0x00, 0x8b, 0x01, 0x00, 0x37, 0x06, 0x11, 0x72,
1810x8b, 0x03, 0x32, 0x2c, 0x00, 0x73, 0xda, 0x07, 0x01, 0x7f, 1810xe4, 0x05, 0x50, 0x6d, 0x00, 0x65, 0x78, 0x61, 0x8b, 0x03,
1820x07, 0x40, 0x61, 0x6e, 0x73, 0x77, 0x49, 0x07, 0x40, 0x73, 1820x32, 0x2c, 0x00, 0x73, 0xda, 0x07, 0x01, 0x7f, 0x07, 0x40,
1830x61, 0x79, 0x29, 0x1a, 0x06, 0x41, 0x6f, 0x75, 0x72, 0x2d, 1830x61, 0x6e, 0x73, 0x77, 0x49, 0x07, 0x40, 0x73, 0x61, 0x79,
1840x0d, 0x03, 0x04, 0x39, 0x01, 0x03, 0xe0, 0x08, 0x07, 0x19, 1840x29, 0x1a, 0x06, 0x41, 0x6f, 0x75, 0x72, 0x2d, 0x0d, 0x03,
1850x00, 0x32, 0x6f, 0x6c, 0x75, 0xa6, 0x04, 0x46, 0x4e, 0x6f, 1850x04, 0x39, 0x01, 0x03, 0xe0, 0x08, 0x07, 0x19, 0x00, 0x32,
1860x74, 0x65, 0x44, 0x06, 0x43, 0x6d, 0x6f, 0x72, 0x65, 0x7a, 1860x6f, 0x6c, 0x75, 0xa6, 0x04, 0x46, 0x4e, 0x6f, 0x74, 0x65,
1870x00, 0x01, 0x59, 0x00, 0x02, 0xd5, 0x01, 0x16, 0x2c, 0x1c, 1870x44, 0x06, 0x43, 0x6d, 0x6f, 0x72, 0x65, 0x7a, 0x00, 0x01,
1880x00, 0x40, 0x6c, 0x69, 0x6b, 0x65, 0xca, 0x06, 0x12, 0x74, 1880x59, 0x00, 0x02, 0xd5, 0x01, 0x16, 0x2c, 0x1c, 0x00, 0x40,
1890xd2, 0x00, 0x25, 0x61, 0x74, 0x4b, 0x00, 0x76, 0x73, 0x00, 1890x6c, 0x69, 0x6b, 0x65, 0xca, 0x06, 0x12, 0x74, 0xd2, 0x00,
1900x73, 0x68, 0x6f, 0x72, 0x74, 0x47, 0x06, 0xf2, 0x00, 0x65, 1900x25, 0x61, 0x74, 0x4b, 0x00, 0x76, 0x73, 0x00, 0x73, 0x68,
1910x00, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x6c, 0x65, 1910x6f, 0x72, 0x74, 0x47, 0x06, 0xf2, 0x00, 0x65, 0x00, 0x74,
1920x6e, 0x67, 0x74, 0x68, 0xcb, 0x01, 0x01, 0xa6, 0x04, 0x00, 1920x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x6c, 0x65, 0x6e, 0x67,
1930x97, 0x01, 0x03, 0x35, 0x01, 0x90, 0x6f, 0x73, 0x73, 0x69, 1930x74, 0x68, 0xcb, 0x01, 0x01, 0xa6, 0x04, 0x00, 0x97, 0x01,
1940x62, 0x6c, 0x65, 0x2e, 0x00, 1940x03, 0x35, 0x01, 0x90, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c,
1950x65, 0x2e, 0x00,
195}; 196};
196 197
197const unsigned short help_text_len = 2534; 198const unsigned short help_text_len = 2553;
198const unsigned short help_text_words = 453; 199const unsigned short help_text_words = 454;
200const bool help_valid = true;
199const char quick_help_text[] = "Slide a row at a time to arrange the tiles into order."; 201const char quick_help_text[] = "Slide a row at a time to arrange the tiles into order.";
diff --git a/apps/plugins/puzzles/help/slant.c b/apps/plugins/puzzles/help/slant.c
index ad4363825e..7c24a1bac3 100644
--- a/apps/plugins/puzzles/help/slant.c
+++ b/apps/plugins/puzzles/help/slant.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,184 +6,198 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 125, TEXT_CENTER | C_RED }, 9 { 126, TEXT_CENTER | C_RED },
10 { 328, TEXT_CENTER | C_RED }, 10 { 329, TEXT_CENTER | C_RED },
11 { 345, TEXT_UNDERLINE },
12 { 346, TEXT_UNDERLINE }, 11 { 346, TEXT_UNDERLINE },
13 { 356, TEXT_UNDERLINE }, 12 { 347, TEXT_UNDERLINE },
14 { 379, TEXT_UNDERLINE }, 13 { 357, TEXT_UNDERLINE },
14 { 380, TEXT_UNDERLINE },
15 { 447, TEXT_CENTER | C_RED },
15 LAST_STYLE_ITEM 16 LAST_STYLE_ITEM
16}; 17};
17 18
18/* orig 2378 comp 1650 ratio 0.69386 level 10 saved 728 */ 19/* orig 2582 comp 1767 ratio 0.684353 level 10 saved 815 */
19const char help_text[] = { 20const char help_text[] = {
200xf0, 0x21, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 210xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x32, 0x30, 0x3a, 0x20, 0x53, 0x6c, 0x61, 0x6e, 0x74, 0x20, 220x32, 0x30, 0x3a, 0x20, 0x53, 0x6c, 0x61, 0x6e, 0x74, 0x20,
220x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 230x00, 0x2d, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x59,
230x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 240x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
240x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2e, 250x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71,
250x1c, 0x00, 0xf3, 0x1e, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 260x75, 0x61, 0x72, 0x65, 0x73, 0x2e, 0x1c, 0x00, 0xf3, 0x1e,
260x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x64, 0x72, 0x61, 0x77, 270x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74,
270x00, 0x61, 0x00, 0x64, 0x69, 0x61, 0x67, 0x6f, 0x6e, 0x61, 280x6f, 0x00, 0x64, 0x72, 0x61, 0x77, 0x00, 0x61, 0x00, 0x64,
280x6c, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x74, 0x68, 0x72, 290x69, 0x61, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x00, 0x6c, 0x69,
290x6f, 0x75, 0x67, 0x68, 0x00, 0x65, 0x61, 0x63, 0x68, 0x3a, 300x6e, 0x65, 0x00, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68,
300x00, 0xf2, 0x07, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x63, 310x00, 0x65, 0x61, 0x63, 0x68, 0x3a, 0x00, 0xf2, 0x07, 0x2c,
310x68, 0x6f, 0x6f, 0x73, 0x65, 0x00, 0x77, 0x68, 0x69, 0x63, 320x00, 0x61, 0x6e, 0x64, 0x00, 0x63, 0x68, 0x6f, 0x6f, 0x73,
320x68, 0x00, 0x77, 0x61, 0x79, 0x22, 0x00, 0x01, 0x34, 0x00, 330x65, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x77, 0x61,
330x10, 0x73, 0x7d, 0x00, 0xfa, 0x24, 0x73, 0x00, 0x73, 0x6f, 340x79, 0x22, 0x00, 0x01, 0x34, 0x00, 0x10, 0x73, 0x8f, 0x00,
340x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x74, 0x68, 0x65, 0x00, 350xfa, 0x24, 0x73, 0x00, 0x73, 0x6f, 0x00, 0x74, 0x68, 0x61,
350x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x00, 360x74, 0x00, 0x74, 0x68, 0x65, 0x00, 0x66, 0x6f, 0x6c, 0x6c,
360x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 370x6f, 0x77, 0x69, 0x6e, 0x67, 0x00, 0x63, 0x6f, 0x6e, 0x64,
370x00, 0x61, 0x72, 0x65, 0x00, 0x6d, 0x65, 0x74, 0x3a, 0x00, 380x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x61, 0x72, 0x65,
380x00, 0x00, 0x2d, 0x00, 0x54, 0x68, 0x65, 0x7b, 0x00, 0xf1, 390x00, 0x6d, 0x65, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00,
390x05, 0x73, 0x00, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x00, 0x66, 400x54, 0x68, 0x65, 0x7b, 0x00, 0xf1, 0x05, 0x73, 0x00, 0x6e,
400x6f, 0x72, 0x6d, 0x00, 0x61, 0x00, 0x6c, 0x6f, 0x6f, 0x70, 410x65, 0x76, 0x65, 0x72, 0x00, 0x66, 0x6f, 0x72, 0x6d, 0x00,
410x2e, 0x2a, 0x00, 0xf2, 0x1e, 0x41, 0x6e, 0x79, 0x00, 0x70, 420x61, 0x00, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x2a, 0x00, 0xf2,
420x6f, 0x69, 0x6e, 0x74, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 430x1e, 0x41, 0x6e, 0x79, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74,
430x61, 0x00, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x64, 0x00, 440x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x61, 0x00, 0x63, 0x69,
440x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x68, 0x61, 0x73, 450x72, 0x63, 0x6c, 0x65, 0x64, 0x00, 0x6e, 0x75, 0x6d, 0x62,
450x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x6c, 0x79, 460x65, 0x72, 0x00, 0x68, 0x61, 0x73, 0x00, 0x70, 0x72, 0x65,
460x83, 0x00, 0x43, 0x6d, 0x61, 0x6e, 0x79, 0x55, 0x00, 0x40, 470x63, 0x69, 0x73, 0x65, 0x6c, 0x79, 0x83, 0x00, 0x43, 0x6d,
470x6d, 0x65, 0x65, 0x74, 0x88, 0x00, 0xf1, 0x02, 0x61, 0x74, 480x61, 0x6e, 0x79, 0x55, 0x00, 0x40, 0x6d, 0x65, 0x65, 0x74,
480x00, 0x69, 0x74, 0x2e, 0x00, 0x28, 0x54, 0x68, 0x75, 0x73, 490x88, 0x00, 0xf1, 0x02, 0x61, 0x74, 0x00, 0x69, 0x74, 0x2e,
490x2c, 0x00, 0x61, 0x00, 0x34, 0x06, 0x01, 0x90, 0x68, 0x65, 500x00, 0x28, 0x54, 0x68, 0x75, 0x73, 0x2c, 0x00, 0x61, 0x00,
500x00, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x65, 0x29, 0x01, 0xfe, 510x34, 0x06, 0x01, 0x90, 0x68, 0x65, 0x00, 0x63, 0x65, 0x6e,
510x0e, 0x61, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x00, 0x73, 520x74, 0x72, 0x65, 0x29, 0x01, 0xfe, 0x0e, 0x61, 0x00, 0x63,
520x68, 0x61, 0x70, 0x65, 0x2c, 0x00, 0x77, 0x68, 0x65, 0x72, 530x72, 0x6f, 0x73, 0x73, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65,
530x65, 0x61, 0x73, 0x00, 0x61, 0x00, 0x7a, 0x65, 0x72, 0x6f, 540x2c, 0x00, 0x77, 0x68, 0x65, 0x72, 0x65, 0x61, 0x73, 0x00,
540x2f, 0x00, 0x01, 0x3b, 0x01, 0x42, 0x6d, 0x6f, 0x6e, 0x64, 550x61, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x00, 0x01, 0x3b,
550x31, 0x00, 0xc0, 0x00, 0x2d, 0x00, 0x6f, 0x72, 0x00, 0x72, 560x01, 0x42, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x00, 0xc0, 0x00,
560x61, 0x74, 0x68, 0x65, 0x72, 0x61, 0x00, 0x7a, 0x70, 0x61, 570x2d, 0x00, 0x6f, 0x72, 0x00, 0x72, 0x61, 0x74, 0x68, 0x65,
570x72, 0x74, 0x69, 0x61, 0x6c, 0x25, 0x00, 0x94, 0x2c, 0x00, 580x72, 0x61, 0x00, 0x7a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61,
580x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x56, 0x00, 0x33, 590x6c, 0x25, 0x00, 0x94, 0x2c, 0x00, 0x62, 0x65, 0x63, 0x61,
590x63, 0x61, 0x6e, 0xf8, 0x00, 0x91, 0x61, 0x70, 0x70, 0x65, 600x75, 0x73, 0x65, 0x56, 0x00, 0x33, 0x63, 0x61, 0x6e, 0xf8,
600x61, 0x72, 0x00, 0x69, 0x6e, 0x67, 0x00, 0x51, 0x6d, 0x69, 610x00, 0x91, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x00, 0x69,
610x64, 0x64, 0x6c, 0x67, 0x00, 0x00, 0x0e, 0x00, 0x01, 0xcb, 620x6e, 0x67, 0x00, 0x51, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x67,
620x01, 0x04, 0x3a, 0x00, 0x01, 0xe2, 0x00, 0xe0, 0x77, 0x6f, 630x00, 0x00, 0x0e, 0x00, 0x01, 0xcb, 0x01, 0x04, 0x3a, 0x00,
630x75, 0x6c, 0x64, 0x00, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 640x01, 0xe2, 0x00, 0xe0, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x00,
640x61, 0x74, 0xf9, 0x00, 0x04, 0x57, 0x00, 0x01, 0x39, 0x01, 650x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0xf9, 0x00,
650xa0, 0x29, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x64, 0x69, 660x04, 0x57, 0x00, 0x01, 0x39, 0x01, 0xa0, 0x29, 0x00, 0x00,
660x74, 0x50, 0x01, 0xf1, 0x01, 0x00, 0x74, 0x68, 0x69, 0x73, 670x00, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x50, 0x01, 0xf1,
670x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 680x01, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a,
680x65, 0xff, 0x01, 0xa0, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 690x7a, 0x6c, 0x65, 0x00, 0x67, 0x6f, 0x65, 0xff, 0x01, 0xa0,
690x00, 0x5b, 0x38, 0x5d, 0x67, 0x01, 0xf1, 0x01, 0x5b, 0x38, 700x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b, 0x38, 0x5d,
700x5d, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 710x67, 0x01, 0xf1, 0x02, 0x5b, 0x38, 0x5d, 0x00, 0x68, 0x74,
710x77, 0x77, 0x2e, 0x6e, 0x1d, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 720x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
720x2e, 0x6a, 0x70, 0x2f, 0x6a, 0x61, 0x2f, 0x3c, 0x00, 0xf0, 730x6e, 0x1e, 0x00, 0xa2, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70,
730x0e, 0x73, 0x2f, 0x67, 0x6f, 0x6b, 0x69, 0x67, 0x65, 0x6e, 740x2f, 0x6a, 0x61, 0x2f, 0x3d, 0x00, 0xf0, 0x0f, 0x73, 0x2f,
740x5f, 0x6e, 0x61, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x28, 0x69, 750x67, 0x6f, 0x6b, 0x69, 0x67, 0x65, 0x6e, 0x5f, 0x6e, 0x61,
750x6e, 0x00, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x65, 0x73, 0x65, 760x6e, 0x61, 0x6d, 0x65, 0x2f, 0x00, 0x28, 0x69, 0x6e, 0x00,
760x73, 0x00, 0x43, 0x32, 0x30, 0x2e, 0x31, 0x8c, 0x02, 0x80, 770x4a, 0x61, 0x70, 0x61, 0x6e, 0x65, 0x73, 0x65, 0x75, 0x00,
770x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x95, 0x02, 780x43, 0x32, 0x30, 0x2e, 0x31, 0xa0, 0x02, 0xf0, 0x07, 0x63,
780xa0, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63, 790x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00,
790x6b, 0x8a, 0x01, 0xa3, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x62, 800x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63,
800x6c, 0x61, 0x6e, 0x6b, 0x61, 0x02, 0xa0, 0x00, 0x77, 0x69, 810x6b, 0x8c, 0x01, 0xa3, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x62,
810x6c, 0x6c, 0x00, 0x70, 0x6c, 0x61, 0x63, 0xc0, 0x00, 0x10, 820x6c, 0x61, 0x6e, 0x6b, 0x63, 0x02, 0xa0, 0x00, 0x77, 0x69,
820x5c, 0x21, 0x00, 0x52, 0x69, 0x74, 0x00, 0x28, 0x61, 0x5e, 830x6c, 0x6c, 0x00, 0x70, 0x6c, 0x61, 0x63, 0xc2, 0x00, 0x10,
840x5c, 0x21, 0x00, 0x52, 0x69, 0x74, 0x00, 0x28, 0x61, 0x60,
830x02, 0x40, 0x6c, 0x65, 0x61, 0x6e, 0x37, 0x00, 0x10, 0x74, 850x02, 0x40, 0x6c, 0x65, 0x61, 0x6e, 0x37, 0x00, 0x10, 0x74,
840x5f, 0x02, 0x00, 0x0f, 0x00, 0xc1, 0x66, 0x74, 0x2c, 0x00, 860x61, 0x02, 0x00, 0x0f, 0x00, 0xc1, 0x66, 0x74, 0x2c, 0x00,
850x69, 0x2e, 0x65, 0x2e, 0x00, 0x72, 0x75, 0x6e, 0x1a, 0x00, 870x69, 0x2e, 0x65, 0x2e, 0x00, 0x72, 0x75, 0x6e, 0x1a, 0x00,
860x41, 0x66, 0x72, 0x6f, 0x6d, 0x1c, 0x00, 0x31, 0x74, 0x6f, 880x41, 0x66, 0x72, 0x6f, 0x6d, 0x1c, 0x00, 0x31, 0x74, 0x6f,
870x70, 0x20, 0x00, 0x04, 0x37, 0x01, 0x03, 0x5f, 0x00, 0x03, 890x70, 0x20, 0x00, 0x04, 0x39, 0x01, 0x03, 0x5f, 0x00, 0x03,
880x3a, 0x00, 0xf0, 0x01, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 900x3a, 0x00, 0xf0, 0x01, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d,
890x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x29, 0x2e, 0x00, 0x52, 910x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x29, 0x2e, 0x00, 0x52,
900x08, 0x00, 0x0f, 0x96, 0x00, 0x16, 0x14, 0x2f, 0x96, 0x00, 920x08, 0x00, 0x0f, 0x96, 0x00, 0x16, 0x14, 0x2f, 0x96, 0x00,
910x0b, 0x8f, 0x00, 0x01, 0x4e, 0x00, 0x1b, 0x2c, 0x8b, 0x00, 930x0b, 0x8f, 0x00, 0x01, 0x4e, 0x00, 0x1b, 0x2c, 0x8b, 0x00,
920x22, 0x6f, 0x70, 0x18, 0x00, 0x00, 0x25, 0x00, 0x03, 0x76, 940x22, 0x6f, 0x70, 0x18, 0x00, 0x00, 0x25, 0x00, 0x03, 0x76,
930x00, 0x00, 0x97, 0x00, 0x20, 0x29, 0x2e, 0x96, 0x01, 0x63, 950x00, 0x00, 0x97, 0x00, 0x20, 0x29, 0x2e, 0x98, 0x01, 0x63,
940x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x43, 0x00, 0x01, 0x7f, 960x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x43, 0x00, 0x01, 0x7f,
950x00, 0x30, 0x00, 0x65, 0x69, 0x35, 0x02, 0x72, 0x00, 0x62, 970x00, 0x30, 0x00, 0x65, 0x69, 0x37, 0x02, 0x72, 0x00, 0x62,
960x75, 0x74, 0x74, 0x6f, 0x6e, 0x78, 0x00, 0xd2, 0x63, 0x79, 980x75, 0x74, 0x74, 0x6f, 0x6e, 0x78, 0x00, 0xd2, 0x63, 0x79,
970x63, 0x6c, 0x65, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 990x63, 0x6c, 0x65, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65,
980x6e, 0xdd, 0x00, 0xc5, 0x68, 0x72, 0x65, 0x65, 0x00, 0x70, 1000x6e, 0xdd, 0x00, 0xc5, 0x68, 0x72, 0x65, 0x65, 0x00, 0x70,
990x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0xdc, 0x00, 0x00, 0x67, 1010x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0xdc, 0x00, 0x00, 0x67,
1000x01, 0x62, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x00, 0xd9, 0x02, 1020x01, 0x62, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x00, 0xdb, 0x02,
1010x85, 0x69, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6c, 0x72, 1030x85, 0x69, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6c, 0x72,
1020x01, 0xbe, 0x00, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 1040x01, 0xbe, 0x00, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
1030x64, 0x6c, 0x79, 0xe4, 0x00, 0x00, 0xd2, 0x00, 0x02, 0x6f, 1050x64, 0x6c, 0x79, 0xe4, 0x00, 0x00, 0xd2, 0x00, 0x02, 0x6f,
1040x00, 0x52, 0x68, 0x61, 0x6e, 0x67, 0x65, 0xbf, 0x00, 0x02, 1060x00, 0x52, 0x68, 0x61, 0x6e, 0x67, 0x65, 0xbf, 0x00, 0x02,
1050x21, 0x00, 0x40, 0x74, 0x6f, 0x00, 0x5c, 0x05, 0x00, 0x51, 1070x21, 0x00, 0x40, 0x74, 0x6f, 0x00, 0x5c, 0x05, 0x00, 0x51,
1060x2f, 0x00, 0x62, 0x61, 0x63, 0x0f, 0x00, 0x01, 0x18, 0x00, 1080x2f, 0x00, 0x62, 0x61, 0x63, 0x0f, 0x00, 0x01, 0x18, 0x00,
1070x02, 0x0d, 0x04, 0x03, 0x66, 0x00, 0x01, 0xe5, 0x00, 0x0e, 1090x02, 0x0f, 0x04, 0x03, 0x66, 0x00, 0x01, 0xe5, 0x00, 0x0e,
1080x67, 0x00, 0x07, 0x7b, 0x01, 0x0f, 0x5d, 0x00, 0x07, 0x12, 1100x67, 0x00, 0x07, 0x7b, 0x01, 0x0f, 0x5d, 0x00, 0x07, 0x12,
1090x2f, 0x62, 0x00, 0x09, 0x5d, 0x00, 0x01, 0x9f, 0x03, 0x50, 1110x2f, 0x62, 0x00, 0x09, 0x5d, 0x00, 0x01, 0xa1, 0x03, 0x50,
1100x65, 0x72, 0x65, 0x66, 0x6f, 0x76, 0x04, 0x00, 0x62, 0x00, 1120x65, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x04, 0x00, 0x62, 0x00,
1110x00, 0x1f, 0x03, 0x42, 0x70, 0x6c, 0x61, 0x79, 0x06, 0x03, 1130x00, 0x21, 0x03, 0x42, 0x70, 0x6c, 0x61, 0x79, 0x08, 0x03,
1120x00, 0x73, 0x02, 0x50, 0x65, 0x6e, 0x74, 0x69, 0x72, 0xf0, 1140x90, 0x61, 0x6d, 0x65, 0x00, 0x65, 0x6e, 0x74, 0x69, 0x72,
1130x02, 0x01, 0x0d, 0x04, 0x34, 0x6f, 0x6e, 0x65, 0x37, 0x01, 1150xf2, 0x02, 0x01, 0x0f, 0x04, 0x34, 0x6f, 0x6e, 0x65, 0x37,
1140x03, 0x94, 0x00, 0x71, 0x6e, 0x65, 0x65, 0x64, 0x00, 0x74, 1160x01, 0x03, 0x94, 0x00, 0x71, 0x6e, 0x65, 0x65, 0x64, 0x00,
1150x6f, 0x02, 0x03, 0x13, 0x59, 0x42, 0x00, 0x52, 0x61, 0x6c, 1170x74, 0x6f, 0x04, 0x03, 0x13, 0x59, 0x42, 0x00, 0x52, 0x61,
1160x73, 0x6f, 0x00, 0x3b, 0x03, 0xc1, 0x65, 0x00, 0x63, 0x75, 1180x6c, 0x73, 0x6f, 0x00, 0x3d, 0x03, 0xc1, 0x65, 0x00, 0x63,
1170x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x07, 0x03, 1190x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x09,
1180x20, 0x6d, 0x6f, 0x2d, 0x05, 0x55, 0x72, 0x6f, 0x75, 0x6e, 1200x03, 0x20, 0x6d, 0x6f, 0x2f, 0x05, 0x55, 0x72, 0x6f, 0x75,
1190x64, 0x6b, 0x03, 0x71, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 1210x6e, 0x64, 0x6d, 0x03, 0x71, 0x2e, 0x00, 0x50, 0x72, 0x65,
1200x73, 0xa4, 0x01, 0x00, 0xe4, 0x01, 0x50, 0x65, 0x74, 0x75, 1220x73, 0x73, 0xa4, 0x01, 0x00, 0xe4, 0x01, 0x50, 0x65, 0x74,
1210x72, 0x6e, 0xde, 0x03, 0x20, 0x73, 0x70, 0x0e, 0x02, 0x01, 1230x75, 0x72, 0x6e, 0xe0, 0x03, 0x20, 0x73, 0x70, 0x0e, 0x02,
1220x3b, 0x00, 0x0b, 0xb4, 0x02, 0x20, 0x6f, 0x72, 0x25, 0x02, 1240x01, 0x3b, 0x00, 0x0b, 0xb4, 0x02, 0x20, 0x6f, 0x72, 0x25,
1230xe2, 0x2c, 0x00, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, 0x74, 1250x02, 0xe2, 0x2c, 0x00, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
1240x69, 0x76, 0x65, 0x6c, 0x79, 0x2c, 0x01, 0x01, 0x29, 0x00, 1260x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x2c, 0x01, 0x01, 0x29,
1250x43, 0x74, 0x68, 0x65, 0x6e, 0xd4, 0x01, 0x50, 0x74, 0x68, 1270x00, 0x43, 0x74, 0x68, 0x65, 0x6e, 0xd4, 0x01, 0x50, 0x74,
1260x65, 0x6d, 0x00, 0x57, 0x04, 0x5a, 0x62, 0x6f, 0x76, 0x65, 1280x68, 0x65, 0x6d, 0x00, 0x59, 0x04, 0x5a, 0x62, 0x6f, 0x76,
1270x2e, 0xa4, 0x00, 0x10, 0x70, 0x77, 0x00, 0x20, 0x00, 0x2f, 1290x65, 0x2e, 0xa4, 0x00, 0x10, 0x70, 0x77, 0x00, 0x20, 0x00,
1280x4e, 0x00, 0x01, 0x7d, 0x01, 0x06, 0x7e, 0x02, 0x00, 0x12, 1300x2f, 0x4e, 0x00, 0x01, 0x7d, 0x01, 0x06, 0x7e, 0x02, 0x00,
1290x00, 0x0c, 0x5e, 0x00, 0xa1, 0x69, 0x6e, 0x64, 0x65, 0x70, 1310x12, 0x00, 0x0c, 0x5e, 0x00, 0xa1, 0x69, 0x6e, 0x64, 0x65,
1300x65, 0x6e, 0x64, 0x65, 0x6e, 0xf2, 0x02, 0x20, 0x77, 0x68, 1320x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0xf2, 0x02, 0x20, 0x77,
1310xe4, 0x04, 0x81, 0x73, 0x00, 0x61, 0x6c, 0x72, 0x65, 0x61, 1330x68, 0xe6, 0x04, 0x81, 0x73, 0x00, 0x61, 0x6c, 0x72, 0x65,
1320x64, 0xee, 0x01, 0x07, 0xf0, 0x00, 0x02, 0x91, 0x01, 0x62, 1340x61, 0x64, 0xee, 0x01, 0x07, 0xf0, 0x00, 0x02, 0x91, 0x01,
1330x2e, 0x00, 0x42, 0x61, 0x63, 0x6b, 0xc7, 0x00, 0x20, 0x72, 1350x62, 0x2e, 0x00, 0x42, 0x61, 0x63, 0x6b, 0xc7, 0x00, 0x20,
1340x65, 0xfc, 0x00, 0x24, 0x73, 0x00, 0x2c, 0x05, 0x06, 0x48, 1360x72, 0x65, 0xfc, 0x00, 0x24, 0x73, 0x00, 0x2e, 0x05, 0x06,
1350x03, 0x0b, 0x33, 0x00, 0x42, 0x00, 0x00, 0x28, 0x41, 0xc4, 1370x48, 0x03, 0x0b, 0x33, 0x00, 0x42, 0x00, 0x00, 0x28, 0x41,
1360x00, 0x32, 0x00, 0x61, 0x63, 0xcb, 0x05, 0x90, 0x64, 0x65, 1380xc4, 0x00, 0x32, 0x00, 0x61, 0x63, 0xcd, 0x05, 0x90, 0x64,
1370x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x66, 0x00, 0x21, 1390x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x66, 0x00,
1380x73, 0x65, 0x16, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 0xe4, 1400x21, 0x73, 0x65, 0x16, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31,
1390x05, 0x01, 0xcb, 0x00, 0xa3, 0x61, 0x76, 0x61, 0x69, 0x6c, 1410xe6, 0x05, 0x01, 0xcb, 0x00, 0xa3, 0x61, 0x76, 0x61, 0x69,
1400x61, 0x62, 0x6c, 0x65, 0x2e, 0x19, 0x04, 0x13, 0x32, 0x19, 1420x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x19, 0x04, 0x13, 0x32,
1410x04, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 1430x19, 0x04, 0x91, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1420x72, 0x1b, 0x04, 0x66, 0x54, 0x68, 0x65, 0x73, 0x65, 0x00, 1440x65, 0x72, 0x1b, 0x04, 0x66, 0x54, 0x68, 0x65, 0x73, 0x65,
1430x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x06, 0x93, 1450x00, 0x14, 0x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x06,
1440x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 1460x93, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
1450x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x22, 0x6f, 1470x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6f, 0x00, 0x22,
1460x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 1480x6f, 0x6e, 0x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27,
1470x6d, 0x65, 0x6e, 0x75, 0xab, 0x00, 0x91, 0x57, 0x69, 0x64, 1490x00, 0x6d, 0x65, 0x6e, 0x75, 0xab, 0x00, 0x91, 0x57, 0x69,
1480x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x81, 0x03, 0x51, 0x00, 1500x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x81, 0x03, 0x51,
1490x00, 0x53, 0x69, 0x7a, 0x47, 0x05, 0x01, 0x43, 0x05, 0x26, 1510x00, 0x00, 0x53, 0x69, 0x7a, 0x49, 0x05, 0x01, 0x45, 0x05,
1500x69, 0x6e, 0x0e, 0x07, 0xc3, 0x00, 0x00, 0x44, 0x69, 0x66, 1520x26, 0x69, 0x6e, 0x10, 0x07, 0xc3, 0x00, 0x00, 0x44, 0x69,
1510x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x98, 0x03, 0x00, 1530x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x98, 0x03,
1520xb0, 0x04, 0x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x04, 1540x00, 0xb0, 0x04, 0x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 0x00,
1530x82, 0x05, 0x40, 0x65, 0x6e, 0x65, 0x72, 0xe0, 0x02, 0x03, 1550x04, 0x84, 0x05, 0x40, 0x65, 0x6e, 0x65, 0x72, 0xe0, 0x02,
1540x47, 0x05, 0xf2, 0x00, 0x2e, 0x00, 0x41, 0x74, 0x00, 0x48, 1560x03, 0x49, 0x05, 0xf2, 0x00, 0x2e, 0x00, 0x41, 0x74, 0x00,
1550x61, 0x72, 0x64, 0x00, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0xae, 1570x48, 0x61, 0x72, 0x64, 0x00, 0x6c, 0x65, 0x76, 0x65, 0x6c,
1560x02, 0x20, 0x61, 0x72, 0x56, 0x01, 0x61, 0x71, 0x75, 0x69, 1580xae, 0x02, 0x20, 0x61, 0x72, 0x56, 0x01, 0x61, 0x71, 0x75,
1570x72, 0x65, 0x64, 0x62, 0x07, 0x63, 0x6f, 0x00, 0x64, 0x65, 1590x69, 0x72, 0x65, 0x64, 0x64, 0x07, 0x63, 0x6f, 0x00, 0x64,
1580x64, 0x75, 0x34, 0x01, 0x50, 0x62, 0x61, 0x73, 0x65, 0x64, 1600x65, 0x64, 0x75, 0x34, 0x01, 0x50, 0x62, 0x61, 0x73, 0x65,
1590xb6, 0x00, 0x81, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 1610x64, 0xb6, 0x00, 0x81, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65,
1600x67, 0x98, 0x00, 0x41, 0x72, 0x65, 0x6c, 0x61, 0x20, 0x00, 1620x64, 0x67, 0x98, 0x00, 0x41, 0x72, 0x65, 0x6c, 0x61, 0x20,
1610x45, 0x68, 0x69, 0x70, 0x73, 0xf1, 0x03, 0x03, 0xa6, 0x00, 1630x00, 0x45, 0x68, 0x69, 0x70, 0x73, 0xf1, 0x03, 0x03, 0xa6,
1620x03, 0x4f, 0x06, 0x00, 0xf3, 0x05, 0x00, 0x22, 0x02, 0x30, 1640x00, 0x03, 0x51, 0x06, 0x00, 0xf5, 0x05, 0x00, 0x22, 0x02,
1630x77, 0x61, 0x79, 0x23, 0x00, 0x01, 0xcf, 0x06, 0x10, 0x62, 1650x30, 0x77, 0x61, 0x79, 0x23, 0x00, 0x01, 0xd1, 0x06, 0x10,
1640x4b, 0x02, 0x03, 0x60, 0x00, 0x02, 0xd9, 0x02, 0x55, 0x65, 1660x62, 0x4b, 0x02, 0x03, 0x60, 0x00, 0x02, 0xd9, 0x02, 0x55,
1650x78, 0x61, 0x63, 0x74, 0x11, 0x04, 0x00, 0x5c, 0x00, 0x25, 1670x65, 0x78, 0x61, 0x63, 0x74, 0x11, 0x04, 0x00, 0x5c, 0x00,
1660x6f, 0x6e, 0x89, 0x03, 0xf0, 0x00, 0x61, 0x74, 0x00, 0x61, 1680x25, 0x6f, 0x6e, 0x89, 0x03, 0xf0, 0x00, 0x61, 0x74, 0x00,
1670x00, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x00, 0x28, 0x46, 0x6f, 1690x61, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x00, 0x28, 0x46,
1680x72, 0x2d, 0x00, 0x33, 0x6d, 0x70, 0x6c, 0x66, 0x03, 0x11, 1700x6f, 0x72, 0x2d, 0x00, 0x33, 0x6d, 0x70, 0x6c, 0x66, 0x03,
1690x6d, 0x2c, 0x01, 0x00, 0x97, 0x00, 0x03, 0xc2, 0x07, 0x25, 1710x11, 0x6d, 0x2c, 0x01, 0x00, 0x97, 0x00, 0x03, 0xc4, 0x07,
1700x77, 0x6f, 0x82, 0x00, 0x01, 0xdd, 0x07, 0x04, 0x4c, 0x02, 1720x25, 0x77, 0x6f, 0x82, 0x00, 0x01, 0xdf, 0x07, 0x04, 0x4c,
1710x30, 0x73, 0x61, 0x6d, 0x1d, 0x01, 0x12, 0x72, 0xf1, 0x01, 1730x02, 0x30, 0x73, 0x61, 0x6d, 0x1d, 0x01, 0x12, 0x72, 0xf1,
1720x55, 0x2c, 0x00, 0x65, 0x76, 0x65, 0x79, 0x03, 0x83, 0x64, 1740x01, 0x55, 0x2c, 0x00, 0x65, 0x76, 0x65, 0x79, 0x03, 0x83,
1730x6f, 0x6e, 0x27, 0x74, 0x00, 0x79, 0x65, 0x49, 0x00, 0x01, 1750x64, 0x6f, 0x6e, 0x27, 0x74, 0x00, 0x79, 0x65, 0x49, 0x00,
1740x8e, 0x02, 0x01, 0x4e, 0x00, 0x05, 0x30, 0x00, 0x32, 0x00, 1760x01, 0x8e, 0x02, 0x01, 0x4e, 0x00, 0x05, 0x30, 0x00, 0x32,
1750x69, 0x73, 0x13, 0x03, 0x01, 0x8f, 0x06, 0x02, 0x75, 0x00, 1770x00, 0x69, 0x73, 0x13, 0x03, 0x01, 0x91, 0x06, 0x02, 0x75,
1760x21, 0x65, 0x6e, 0xca, 0x00, 0x00, 0x41, 0x00, 0x06, 0xce, 1780x00, 0x21, 0x65, 0x6e, 0xca, 0x00, 0x00, 0x41, 0x00, 0x06,
1770x00, 0x62, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0xe7, 0x00, 1790xce, 0x00, 0x62, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0xe7,
1780x60, 0x6f, 0x75, 0x74, 0x00, 0x73, 0x74, 0x44, 0x03, 0x11, 1800x00, 0x60, 0x6f, 0x75, 0x74, 0x00, 0x73, 0x74, 0x44, 0x03,
1790x6f, 0x0b, 0x01, 0x04, 0xc0, 0x01, 0x30, 0x29, 0x00, 0x45, 1810x11, 0x6f, 0x0b, 0x01, 0x04, 0xc0, 0x01, 0x30, 0x29, 0x00,
1800x7d, 0x00, 0x1a, 0x61, 0x86, 0x01, 0x91, 0x67, 0x75, 0x65, 1820x45, 0x7d, 0x00, 0x1a, 0x61, 0x86, 0x01, 0x91, 0x67, 0x75,
1810x73, 0x73, 0x77, 0x6f, 0x72, 0x6b, 0x68, 0x00, 0x00, 0x5d, 1830x65, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x6b, 0x68, 0x00, 0x00,
1820x04, 0x32, 0x74, 0x72, 0x61, 0xe1, 0x05, 0x21, 0x73, 0x68, 1840x5d, 0x04, 0x32, 0x74, 0x72, 0x61, 0xe1, 0x05, 0x21, 0x73,
1830x33, 0x07, 0x02, 0x6d, 0x07, 0xe0, 0x62, 0x65, 0x00, 0x6e, 1850x68, 0x35, 0x07, 0x02, 0x6f, 0x07, 0xd2, 0x62, 0x65, 0x00,
1840x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x2e, 0x00, 1860x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x2e,
1870xa0, 0x02, 0x13, 0x33, 0xa0, 0x02, 0xf1, 0x00, 0x75, 0x73,
1880x65, 0x72, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
1890x6e, 0x63, 0x65, 0xa6, 0x02, 0x11, 0x4f, 0x86, 0x04, 0x10,
1900x74, 0x9d, 0x08, 0x12, 0x73, 0xd5, 0x00, 0x80, 0x73, 0x75,
1910x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x17, 0x00,
1920x2e, 0x00, 0x12, 0x2c, 0x94, 0x02, 0x16, 0x50, 0x12, 0x00,
1930x0d, 0xb0, 0x02, 0x33, 0x47, 0x61, 0x6d, 0xb0, 0x02, 0x02,
1940x1c, 0x04, 0x32, 0x6c, 0x65, 0x74, 0xec, 0x04, 0x78, 0x6f,
1950x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x67, 0x09, 0x06, 0x98,
1960x04, 0x44, 0x6d, 0x6f, 0x75, 0x73, 0xec, 0x04, 0x80, 0x73,
1970x00, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x00,
185}; 198};
186 199
187const unsigned short help_text_len = 2378; 200const unsigned short help_text_len = 2582;
188const unsigned short help_text_words = 444; 201const unsigned short help_text_words = 474;
202const bool help_valid = true;
189const char quick_help_text[] = "Draw a maze of slanting lines that matches the clues."; 203const char quick_help_text[] = "Draw a maze of slanting lines that matches the clues.";
diff --git a/apps/plugins/puzzles/help/solo.c b/apps/plugins/puzzles/help/solo.c
index 6ebeec86c0..68927490e6 100644
--- a/apps/plugins/puzzles/help/solo.c
+++ b/apps/plugins/puzzles/help/solo.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,382 +6,382 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 360, TEXT_UNDERLINE }, 9 { 361, TEXT_UNDERLINE },
10 { 390, TEXT_CENTER | C_RED }, 10 { 388, TEXT_CENTER | C_RED },
11 { 446, TEXT_UNDERLINE }, 11 { 444, TEXT_UNDERLINE },
12 { 674, TEXT_CENTER | C_RED }, 12 { 672, TEXT_CENTER | C_RED },
13 { 1059, TEXT_UNDERLINE }, 13 { 1057, TEXT_UNDERLINE },
14 LAST_STYLE_ITEM 14 LAST_STYLE_ITEM
15}; 15};
16 16
17/* orig 6263 comp 3635 ratio 0.580393 level 10 saved 2628 */ 17/* orig 6259 comp 3626 ratio 0.579326 level 10 saved 2633 */
18const char help_text[] = { 18const char help_text[] = {
190xf0, 0x53, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 190xfb, 0x04, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x31, 0x31, 0x3a, 0x20, 0x53, 0x6f, 0x6c, 0x6f, 0x20, 0x00, 200x31, 0x31, 0x3a, 0x20, 0x53, 0x6f, 0x6c, 0x6f, 0x20, 0x00,
210x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 210x2d, 0x01, 0x00, 0xf0, 0x42, 0x00, 0x00, 0x00, 0x59, 0x6f,
220x00, 0x61, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 220x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x73,
230x67, 0x72, 0x69, 0x64, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 230x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x67, 0x72, 0x69, 0x64,
240x68, 0x00, 0x69, 0x73, 0x00, 0x64, 0x69, 0x76, 0x69, 0x64, 240x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x69, 0x73,
250x65, 0x64, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x00, 0x61, 0x73, 250x00, 0x64, 0x69, 0x76, 0x69, 0x64, 0x65, 0x64, 0x00, 0x69,
260x00, 0x6d, 0x61, 0x6e, 0x79, 0x00, 0x65, 0x71, 0x75, 0x61, 260x6e, 0x74, 0x6f, 0x00, 0x61, 0x73, 0x00, 0x6d, 0x61, 0x6e,
270x6c, 0x6c, 0x79, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x64, 0x00, 270x79, 0x00, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x00,
280x73, 0x75, 0x62, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 280x73, 0x69, 0x7a, 0x65, 0x64, 0x00, 0x73, 0x75, 0x62, 0x2d,
290x21, 0x00, 0x22, 0x74, 0x68, 0x44, 0x00, 0xf4, 0x00, 0x00, 290x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x21, 0x00, 0x22, 0x74,
300x68, 0x61, 0x73, 0x00, 0x72, 0x6f, 0x77, 0x73, 0x2e, 0x00, 300x68, 0x44, 0x00, 0xf4, 0x00, 0x00, 0x68, 0x61, 0x73, 0x00,
310x45, 0x61, 0x63, 0x68, 0x5f, 0x00, 0xc1, 0x6d, 0x75, 0x73, 310x72, 0x6f, 0x77, 0x73, 0x2e, 0x00, 0x45, 0x61, 0x63, 0x68,
320x74, 0x00, 0x62, 0x65, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x57, 320x5f, 0x00, 0xc1, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x62, 0x65,
330x00, 0xf1, 0x08, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x61, 330x00, 0x66, 0x69, 0x6c, 0x6c, 0x57, 0x00, 0xf1, 0x08, 0x00,
340x00, 0x64, 0x69, 0x67, 0x69, 0x74, 0x00, 0x66, 0x72, 0x6f, 340x77, 0x69, 0x74, 0x68, 0x00, 0x61, 0x00, 0x64, 0x69, 0x67,
350x6d, 0x00, 0x31, 0x00, 0x74, 0x6f, 0x48, 0x00, 0x00, 0x60, 350x69, 0x74, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x31, 0x00,
360x00, 0x35, 0x00, 0x6f, 0x66, 0x54, 0x00, 0x10, 0x2c, 0x30, 360x74, 0x6f, 0x48, 0x00, 0x00, 0x60, 0x00, 0x35, 0x00, 0x6f,
370x00, 0x30, 0x73, 0x75, 0x63, 0x30, 0x00, 0xf0, 0x03, 0x77, 370x66, 0x54, 0x00, 0x10, 0x2c, 0x30, 0x00, 0x30, 0x73, 0x75,
380x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x00, 0x00, 380x63, 0x30, 0x00, 0xf0, 0x03, 0x77, 0x61, 0x79, 0x00, 0x74,
390x2d, 0x00, 0x65, 0x76, 0x65, 0x72, 0x79, 0x6e, 0x00, 0xf1, 390x68, 0x61, 0x74, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x65, 0x76,
400x0d, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 400x65, 0x72, 0x79, 0x6e, 0x00, 0xf1, 0x0d, 0x00, 0x63, 0x6f,
410x00, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 0x65, 0x00, 410x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x00, 0x6f, 0x6e, 0x6c,
420x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x4b, 420x79, 0x00, 0x6f, 0x6e, 0x65, 0x00, 0x6f, 0x63, 0x63, 0x75,
430x00, 0x10, 0x65, 0x8c, 0x00, 0x02, 0x6c, 0x00, 0x06, 0x39, 430x72, 0x72, 0x65, 0x6e, 0x63, 0x4b, 0x00, 0x10, 0x65, 0x8c,
440x00, 0x6f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x3c, 0x00, 440x00, 0x02, 0x6c, 0x00, 0x06, 0x39, 0x00, 0x6f, 0x63, 0x6f,
450x23, 0x01, 0xfa, 0x00, 0x0f, 0x3b, 0x00, 0x18, 0x11, 0x2e, 450x6c, 0x75, 0x6d, 0x6e, 0x3c, 0x00, 0x23, 0x01, 0xfa, 0x00,
460x3c, 0x00, 0x70, 0x28, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 460x0f, 0x3b, 0x00, 0x18, 0x11, 0x2e, 0x3c, 0x00, 0x70, 0x28,
470x46, 0x01, 0xf2, 0x02, 0x2c, 0x00, 0x62, 0x79, 0x00, 0x64, 470x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x01, 0xf2, 0x02,
480x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x6f, 0x66, 0x66, 480x2c, 0x00, 0x62, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75,
490x29, 0x2d, 0x00, 0x03, 0xf7, 0x00, 0x02, 0x37, 0x01, 0xf0, 490x6c, 0x74, 0x00, 0x6f, 0x66, 0x66, 0x29, 0x2d, 0x00, 0x03,
500x01, 0x27, 0x73, 0x00, 0x74, 0x77, 0x6f, 0x00, 0x6d, 0x61, 500xf7, 0x00, 0x02, 0x37, 0x01, 0xf0, 0x01, 0x27, 0x73, 0x00,
510x69, 0x6e, 0x00, 0x64, 0x69, 0x61, 0x67, 0x3a, 0x00, 0x1f, 510x74, 0x77, 0x6f, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x64,
520x73, 0x75, 0x00, 0x1c, 0x00, 0xeb, 0x01, 0x01, 0xe1, 0x01, 520x69, 0x61, 0x67, 0x3a, 0x00, 0x1f, 0x73, 0x75, 0x00, 0x1c,
530x85, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x73, 0x6f, 0x6d, 0x5b, 530x00, 0xeb, 0x01, 0x01, 0xe1, 0x01, 0x85, 0x69, 0x76, 0x65,
540x01, 0x61, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xbe, 0x01, 540x6e, 0x00, 0x73, 0x6f, 0x6d, 0x5b, 0x01, 0x61, 0x6e, 0x75,
550xf0, 0x00, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x00, 0x79, 550x6d, 0x62, 0x65, 0x72, 0xbe, 0x01, 0xf0, 0x00, 0x63, 0x6c,
560x6f, 0x75, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x02, 0x02, 0x81, 560x75, 0x65, 0x73, 0x3b, 0x00, 0x79, 0x6f, 0x75, 0x72, 0x00,
570x74, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2b, 0x00, 570x61, 0x69, 0x6d, 0x02, 0x02, 0x81, 0x74, 0x6f, 0x00, 0x70,
580x4c, 0x72, 0x65, 0x73, 0x74, 0x37, 0x00, 0x90, 0x63, 0x6f, 580x6c, 0x61, 0x63, 0x65, 0x2b, 0x00, 0x4c, 0x72, 0x65, 0x73,
590x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x66, 0x00, 0x51, 590x74, 0x37, 0x00, 0x90, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63,
600x55, 0x6e, 0x64, 0x65, 0x72, 0x1f, 0x00, 0x04, 0xd3, 0x00, 600x74, 0x6c, 0x79, 0x66, 0x00, 0x51, 0x55, 0x6e, 0x64, 0x65,
610x92, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 610x72, 0x1f, 0x00, 0x04, 0xd3, 0x00, 0x92, 0x73, 0x65, 0x74,
620xd0, 0x00, 0x07, 0x2d, 0x02, 0x24, 0x72, 0x65, 0x16, 0x02, 620x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0xd0, 0x00, 0x07, 0x2d,
630x30, 0x6f, 0x72, 0x00, 0x43, 0x00, 0xa7, 0x61, 0x6e, 0x67, 630x02, 0x24, 0x72, 0x65, 0x16, 0x02, 0x30, 0x6f, 0x72, 0x00,
640x75, 0x6c, 0x61, 0x72, 0x2e, 0x00, 0x54, 0x40, 0x00, 0x53, 640x43, 0x00, 0xa7, 0x61, 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72,
650x70, 0x75, 0x7a, 0x7a, 0x6c, 0x0c, 0x02, 0xf3, 0x05, 0x69, 650x2e, 0x00, 0x54, 0x40, 0x00, 0x53, 0x70, 0x75, 0x7a, 0x7a,
660x73, 0x00, 0x33, 0x78, 0x33, 0x00, 0x28, 0x61, 0x00, 0x39, 660x6c, 0x0c, 0x02, 0xf3, 0x05, 0x69, 0x73, 0x00, 0x33, 0x78,
670x78, 0x39, 0x00, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x1a, 670x33, 0x00, 0x28, 0x61, 0x00, 0x39, 0x78, 0x39, 0x00, 0x61,
680x02, 0x09, 0xa9, 0x02, 0x41, 0x6e, 0x69, 0x6e, 0x65, 0x2a, 680x63, 0x74, 0x75, 0x61, 0x6c, 0x1a, 0x02, 0x09, 0xa9, 0x02,
690x00, 0x02, 0x6b, 0x00, 0x21, 0x29, 0x2e, 0xfe, 0x00, 0xf1, 690x41, 0x6e, 0x69, 0x6e, 0x65, 0x2a, 0x00, 0x02, 0x6b, 0x00,
700x00, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 700x21, 0x29, 0x2e, 0xfe, 0x00, 0xf1, 0x00, 0x63, 0x61, 0x6e,
710x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x00, 0x12, 0x73, 710x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x73, 0x65, 0x6c, 0x65,
720x80, 0x02, 0x07, 0x7e, 0x00, 0x13, 0x00, 0x9f, 0x00, 0x70, 720x63, 0x74, 0x53, 0x00, 0x12, 0x73, 0x80, 0x02, 0x07, 0x7e,
730x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0xee, 0x00, 0x04, 730x00, 0x13, 0x00, 0x9f, 0x00, 0x70, 0x69, 0x6e, 0x73, 0x74,
740xa6, 0x00, 0x43, 0x6e, 0x65, 0x73, 0x2c, 0x80, 0x02, 0x32, 740x65, 0x61, 0x64, 0xee, 0x00, 0x04, 0xa6, 0x00, 0x43, 0x6e,
750x73, 0x00, 0x32, 0x89, 0x00, 0x32, 0x36, 0x78, 0x36, 0xf0, 750x65, 0x73, 0x2c, 0x80, 0x02, 0x32, 0x73, 0x00, 0x32, 0x89,
760x02, 0x09, 0x81, 0x00, 0x76, 0x73, 0x69, 0x78, 0x00, 0x33, 760x00, 0x32, 0x36, 0x78, 0x36, 0xf0, 0x02, 0x09, 0x81, 0x00,
770x78, 0x32, 0x80, 0x00, 0xb0, 0x41, 0x6c, 0x74, 0x65, 0x72, 770x76, 0x73, 0x69, 0x78, 0x00, 0x33, 0x78, 0x32, 0x80, 0x00,
780x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0xf3, 0x01, 0x13, 0x79, 780xb0, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69,
790x8f, 0x00, 0x03, 0x8a, 0x00, 0xd1, 0x60, 0x6a, 0x69, 0x67, 790x76, 0x65, 0xf3, 0x01, 0x13, 0x79, 0x8f, 0x00, 0x03, 0x8a,
800x73, 0x61, 0x77, 0x27, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0xe6, 800x00, 0xd1, 0x60, 0x6a, 0x69, 0x67, 0x73, 0x61, 0x77, 0x27,
810x02, 0x02, 0x81, 0x03, 0x0f, 0x32, 0x01, 0x00, 0xe0, 0x61, 810x00, 0x6d, 0x6f, 0x64, 0x65, 0xe6, 0x02, 0x02, 0x81, 0x03,
820x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x00, 0x73, 820x0f, 0x32, 0x01, 0x00, 0xe0, 0x61, 0x72, 0x62, 0x69, 0x74,
830x68, 0x61, 0x70, 0xc0, 0x00, 0x21, 0x68, 0x69, 0xe7, 0x01, 830x72, 0x61, 0x72, 0x79, 0x00, 0x73, 0x68, 0x61, 0x70, 0xc0,
840xf1, 0x00, 0x66, 0x66, 0x65, 0x72, 0x00, 0x62, 0x65, 0x74, 840x00, 0x21, 0x68, 0x69, 0xe7, 0x01, 0xf1, 0x00, 0x66, 0x66,
850x77, 0x65, 0x65, 0x6e, 0x00, 0x69, 0x6e, 0x8f, 0x00, 0x00, 850x65, 0x72, 0x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e,
860x1f, 0x01, 0x02, 0x40, 0x01, 0x10, 0x73, 0x9d, 0x01, 0xf1, 860x00, 0x69, 0x6e, 0x8f, 0x00, 0x00, 0x1f, 0x01, 0x02, 0x40,
870x02, 0x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, 0x61, 870x01, 0x10, 0x73, 0x9d, 0x01, 0xf1, 0x02, 0x41, 0x6e, 0x6f,
880x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x70, 0x00, 880x74, 0x68, 0x65, 0x72, 0x00, 0x61, 0x76, 0x61, 0x69, 0x6c,
890x00, 0x56, 0x01, 0x20, 0x60, 0x6b, 0x90, 0x03, 0xb3, 0x72, 890x61, 0x62, 0x6c, 0x65, 0x70, 0x00, 0x00, 0x56, 0x01, 0x20,
900x27, 0x2e, 0x00, 0x49, 0x6e, 0x00, 0x74, 0x68, 0x69, 0x73, 900x60, 0x6b, 0x90, 0x03, 0xb3, 0x72, 0x27, 0x2e, 0x00, 0x49,
910x8a, 0x00, 0x01, 0x10, 0x02, 0x01, 0x78, 0x00, 0x33, 0x6e, 910x6e, 0x00, 0x74, 0x68, 0x69, 0x73, 0x8a, 0x00, 0x01, 0x10,
920x6f, 0x74, 0x3b, 0x02, 0x21, 0x69, 0x6e, 0x98, 0x00, 0x40, 920x02, 0x01, 0x78, 0x00, 0x33, 0x6e, 0x6f, 0x74, 0x3b, 0x02,
930x66, 0x6f, 0x72, 0x6d, 0x1d, 0x01, 0x02, 0xca, 0x03, 0x33, 930x21, 0x69, 0x6e, 0x98, 0x00, 0x40, 0x66, 0x6f, 0x72, 0x6d,
940x2d, 0x69, 0x6e, 0x27, 0x01, 0x24, 0x73, 0x3b, 0x3b, 0x01, 940x1d, 0x01, 0x02, 0xca, 0x03, 0x33, 0x2d, 0x69, 0x6e, 0x27,
950x16, 0x2c, 0x0d, 0x04, 0x0c, 0x4a, 0x04, 0x91, 0x60, 0x63, 950x01, 0x24, 0x73, 0x3b, 0x3b, 0x01, 0x16, 0x2c, 0x0d, 0x04,
960x61, 0x67, 0x65, 0x73, 0x27, 0x00, 0x62, 0x7d, 0x03, 0x81, 960x0c, 0x4a, 0x04, 0x91, 0x60, 0x63, 0x61, 0x67, 0x65, 0x73,
970x6f, 0x75, 0x72, 0x65, 0x64, 0x00, 0x6c, 0x69, 0x60, 0x01, 970x27, 0x00, 0x62, 0x7d, 0x03, 0x81, 0x6f, 0x75, 0x72, 0x65,
980x30, 0x61, 0x6e, 0x64, 0x5c, 0x00, 0x02, 0xbe, 0x02, 0x00, 980x64, 0x00, 0x6c, 0x69, 0x60, 0x01, 0x30, 0x61, 0x6e, 0x64,
990x27, 0x00, 0x02, 0x46, 0x00, 0x91, 0x61, 0x6d, 0x65, 0x00, 990x5c, 0x00, 0x02, 0xbe, 0x02, 0x00, 0x27, 0x00, 0x02, 0x46,
1000x74, 0x65, 0x6c, 0x6c, 0x73, 0x3c, 0x01, 0x10, 0x77, 0xfa, 1000x00, 0x91, 0x61, 0x6d, 0x65, 0x00, 0x74, 0x65, 0x6c, 0x6c,
1010x03, 0x02, 0x1e, 0x01, 0x01, 0x85, 0x00, 0x32, 0x61, 0x6c, 1010x73, 0x3c, 0x01, 0x10, 0x77, 0xfa, 0x03, 0x02, 0x1e, 0x01,
1020x6c, 0x75, 0x02, 0x00, 0xee, 0x02, 0x00, 0xbc, 0x01, 0x02, 1020x01, 0x85, 0x00, 0x32, 0x61, 0x6c, 0x6c, 0x75, 0x02, 0x00,
1030x1c, 0x04, 0x01, 0x3f, 0x00, 0x90, 0x73, 0x68, 0x6f, 0x75, 1030xee, 0x02, 0x00, 0xbc, 0x01, 0x02, 0x1c, 0x04, 0x01, 0x3f,
1040x6c, 0x64, 0x00, 0x62, 0x65, 0x86, 0x01, 0x63, 0x73, 0x6f, 1040x00, 0x90, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x62,
1050x2c, 0x00, 0x6e, 0x6f, 0xc6, 0x03, 0xe0, 0x6d, 0x61, 0x79, 1050x65, 0x86, 0x01, 0x63, 0x73, 0x6f, 0x2c, 0x00, 0x6e, 0x6f,
1060x00, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x00, 0x6d, 0x6f, 1060xc6, 0x03, 0xe0, 0x6d, 0x61, 0x79, 0x00, 0x61, 0x70, 0x70,
1070x72, 0x69, 0x00, 0x40, 0x61, 0x6e, 0x00, 0x6f, 0x3e, 0x03, 1070x65, 0x61, 0x72, 0x00, 0x6d, 0x6f, 0x72, 0x69, 0x00, 0x40,
1080x00, 0x15, 0x02, 0x41, 0x69, 0x6e, 0x00, 0x61, 0x42, 0x00, 1080x61, 0x6e, 0x00, 0x6f, 0x3e, 0x03, 0x00, 0x15, 0x02, 0x41,
1090x31, 0x2c, 0x00, 0x65, 0xf8, 0x00, 0x02, 0xf7, 0x02, 0x01, 1090x69, 0x6e, 0x00, 0x61, 0x42, 0x00, 0x31, 0x2c, 0x00, 0x65,
1100x54, 0x00, 0x62, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x65, 0xee, 1100xf8, 0x00, 0x02, 0xf7, 0x02, 0x01, 0x54, 0x00, 0x62, 0x63,
1110x04, 0xa1, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x69, 1110x72, 0x6f, 0x73, 0x73, 0x65, 0xee, 0x04, 0xa1, 0x62, 0x6f,
1120x65, 0x73, 0x75, 0x03, 0x30, 0x78, 0x69, 0x73, 0xf0, 0x02, 1120x75, 0x6e, 0x64, 0x61, 0x72, 0x69, 0x65, 0x73, 0x75, 0x03,
1130x71, 0x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x78, 0x01, 1130x30, 0x78, 0x69, 0x73, 0xf0, 0x02, 0x71, 0x00, 0x72, 0x65,
1140x21, 0x49, 0x66, 0xb5, 0x00, 0x03, 0xed, 0x01, 0x19, 0x61, 1140x67, 0x69, 0x6f, 0x6e, 0x78, 0x01, 0x21, 0x49, 0x66, 0xb5,
1150xd3, 0x02, 0x02, 0xbf, 0x01, 0x87, 0x72, 0x65, 0x71, 0x75, 1150x00, 0x03, 0xed, 0x01, 0x19, 0x61, 0xd3, 0x02, 0x02, 0xbf,
1160x69, 0x72, 0x65, 0x73, 0x88, 0x00, 0x13, 0x39, 0xcd, 0x00, 1160x01, 0x87, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73,
1170x02, 0x46, 0x01, 0x42, 0x61, 0x64, 0x64, 0x69, 0x32, 0x04, 1170x88, 0x00, 0x13, 0x39, 0xcd, 0x00, 0x02, 0x46, 0x01, 0x42,
1180x04, 0xe4, 0x00, 0x40, 0x77, 0x69, 0x6c, 0x6c, 0x4a, 0x05, 1180x61, 0x64, 0x64, 0x69, 0x32, 0x04, 0x04, 0xe4, 0x00, 0x40,
1190x40, 0x6c, 0x65, 0x74, 0x74, 0x87, 0x03, 0x03, 0x96, 0x03, 1190x77, 0x69, 0x6c, 0x6c, 0x4a, 0x05, 0x40, 0x6c, 0x65, 0x74,
1200xb0, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x65, 0x74, 0x2e, 1200x74, 0x87, 0x03, 0x03, 0x96, 0x03, 0xb0, 0x61, 0x6c, 0x70,
1210x00, 0x46, 0x45, 0x01, 0x50, 0x78, 0x61, 0x6d, 0x70, 0x6c, 1210x68, 0x61, 0x62, 0x65, 0x74, 0x2e, 0x00, 0x46, 0x45, 0x01,
1220x53, 0x02, 0x09, 0x83, 0x00, 0x30, 0x33, 0x78, 0x34, 0x2d, 1220x50, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x53, 0x02, 0x09, 0x83,
1230x00, 0x18, 0x6e, 0x34, 0x01, 0x02, 0x89, 0x00, 0x20, 0x67, 1230x00, 0x30, 0x33, 0x78, 0x34, 0x2d, 0x00, 0x18, 0x6e, 0x34,
1240x6f, 0x3d, 0x01, 0x01, 0x00, 0x04, 0x01, 0xb9, 0x01, 0x04, 1240x01, 0x02, 0x89, 0x00, 0x20, 0x67, 0x6f, 0x3d, 0x01, 0x01,
1250x66, 0x00, 0x01, 0x94, 0x05, 0xf1, 0x01, 0x39, 0x2c, 0x00, 1250x00, 0x04, 0x01, 0xb9, 0x01, 0x04, 0x66, 0x00, 0x01, 0x94,
1260x70, 0x6c, 0x75, 0x73, 0x00, 0x60, 0x61, 0x27, 0x2c, 0x00, 1260x05, 0xf1, 0x01, 0x39, 0x2c, 0x00, 0x70, 0x6c, 0x75, 0x73,
1270x60, 0x62, 0x27, 0xac, 0x01, 0x30, 0x60, 0x63, 0x27, 0xb1, 1270x00, 0x60, 0x61, 0x27, 0x2c, 0x00, 0x60, 0x62, 0x27, 0xac,
1280x03, 0x20, 0x69, 0x73, 0xd1, 0x02, 0x21, 0x6e, 0x6f, 0xde, 1280x01, 0x30, 0x60, 0x63, 0x27, 0xb1, 0x03, 0x20, 0x69, 0x73,
1290x05, 0x02, 0x67, 0x00, 0x12, 0x65, 0xc9, 0x01, 0x02, 0x5b, 1290xd1, 0x02, 0x21, 0x6e, 0x6f, 0xde, 0x05, 0x02, 0x67, 0x00,
1300x02, 0x08, 0x88, 0x02, 0xb2, 0x49, 0x00, 0x66, 0x69, 0x72, 1300x12, 0x65, 0xc9, 0x01, 0x02, 0x5b, 0x02, 0x08, 0x88, 0x02,
1310x73, 0x74, 0x00, 0x73, 0x61, 0x77, 0x6d, 0x02, 0x03, 0x11, 1310xb2, 0x49, 0x00, 0x66, 0x69, 0x72, 0x73, 0x74, 0x00, 0x73,
1320x01, 0xf2, 0x0d, 0x69, 0x6e, 0x00, 0x4e, 0x69, 0x6b, 0x6f, 1320x61, 0x77, 0x6d, 0x02, 0x03, 0x11, 0x01, 0xf2, 0x0d, 0x69,
1330x6c, 0x69, 0x00, 0x5b, 0x35, 0x5d, 0x2c, 0x00, 0x61, 0x6c, 1330x6e, 0x00, 0x4e, 0x69, 0x6b, 0x6f, 0x6c, 0x69, 0x00, 0x5b,
1340x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x69, 0x74, 0x27, 1340x35, 0x5d, 0x2c, 0x00, 0x61, 0x6c, 0x74, 0x68, 0x6f, 0x75,
1350x73, 0xba, 0x03, 0x10, 0x62, 0xdd, 0x02, 0x30, 0x70, 0x6f, 1350x67, 0x68, 0x00, 0x69, 0x74, 0x27, 0x73, 0xba, 0x03, 0x10,
1360x70, 0xa9, 0x03, 0x40, 0x69, 0x73, 0x65, 0x64, 0x41, 0x02, 1360x62, 0xdd, 0x02, 0x30, 0x70, 0x6f, 0x70, 0xa9, 0x03, 0x40,
1370xf0, 0x00, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 1370x69, 0x73, 0x65, 0x64, 0x41, 0x02, 0xf0, 0x00, 0x76, 0x61,
1380x6e, 0x65, 0x77, 0x73, 0x70, 0x61, 0x70, 0x0c, 0x01, 0x15, 1380x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x6e, 0x65, 0x77, 0x73,
1390x75, 0x86, 0x04, 0x10, 0x6e, 0x38, 0x02, 0x80, 0x60, 0x53, 1390x70, 0x61, 0x70, 0x0c, 0x01, 0x15, 0x75, 0x86, 0x04, 0x10,
1400x75, 0x64, 0x6f, 0x6b, 0x75, 0x27, 0x68, 0x04, 0x50, 0x60, 1400x6e, 0x38, 0x02, 0x80, 0x60, 0x53, 0x75, 0x64, 0x6f, 0x6b,
1410x53, 0x75, 0x00, 0x44, 0x0d, 0x00, 0xf1, 0x01, 0x2e, 0x00, 1410x75, 0x27, 0x68, 0x04, 0x50, 0x60, 0x53, 0x75, 0x00, 0x44,
1420x48, 0x6f, 0x77, 0x61, 0x72, 0x64, 0x00, 0x47, 0x61, 0x72, 1420x0d, 0x00, 0xf1, 0x01, 0x2e, 0x00, 0x48, 0x6f, 0x77, 0x61,
1430x6e, 0x73, 0x00, 0x69, 0x50, 0x05, 0x40, 0x73, 0x69, 0x64, 1430x72, 0x64, 0x00, 0x47, 0x61, 0x72, 0x6e, 0x73, 0x00, 0x69,
1440x65, 0x8c, 0x02, 0x20, 0x74, 0x68, 0x8e, 0x00, 0x64, 0x76, 1440x50, 0x05, 0x40, 0x73, 0x69, 0x64, 0x65, 0x8c, 0x02, 0x20,
1450x65, 0x6e, 0x74, 0x6f, 0x72, 0x5a, 0x01, 0x00, 0x12, 0x03, 1450x74, 0x68, 0x8e, 0x00, 0x64, 0x76, 0x65, 0x6e, 0x74, 0x6f,
1460x25, 0x72, 0x6e, 0xf8, 0x02, 0x00, 0x13, 0x00, 0x02, 0xb8, 1460x72, 0x5a, 0x01, 0x00, 0x12, 0x03, 0x25, 0x72, 0x6e, 0xf8,
1470x00, 0x02, 0xb4, 0x02, 0x63, 0x69, 0x74, 0x00, 0x77, 0x61, 1470x02, 0x00, 0x13, 0x00, 0x02, 0xb8, 0x00, 0x02, 0xb4, 0x02,
1480x73, 0xda, 0x00, 0x72, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 1480x63, 0x69, 0x74, 0x00, 0x77, 0x61, 0x73, 0xda, 0x00, 0x72,
1490x68, 0xe2, 0x06, 0xd2, 0x44, 0x65, 0x6c, 0x6c, 0x20, 0x50, 1490x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0xe2, 0x06, 0xd2,
1500x65, 0x6e, 0x63, 0x69, 0x6c, 0x20, 0x50, 0x06, 0x01, 0xd0, 1500x44, 0x65, 0x6c, 0x6c, 0x20, 0x50, 0x65, 0x6e, 0x63, 0x69,
1510x20, 0x61, 0x6e, 0x64, 0x20, 0x57, 0x6f, 0x72, 0x64, 0x20, 1510x6c, 0x20, 0x50, 0x06, 0x01, 0xd0, 0x20, 0x61, 0x6e, 0x64,
1520x47, 0x61, 0x6d, 0x15, 0x01, 0x12, 0x41, 0xfa, 0x01, 0xf5, 1520x20, 0x57, 0x6f, 0x72, 0x64, 0x20, 0x47, 0x61, 0x6d, 0x15,
1530x03, 0x65, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x65, 1530x01, 0x12, 0x41, 0xfa, 0x01, 0xf5, 0x03, 0x65, 0x6c, 0x61,
1540x00, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x6f, 1540x62, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x00, 0x74, 0x72, 0x65,
1550x05, 0x7a, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x7b, 1550x61, 0x74, 0x6d, 0x65, 0x6e, 0x6f, 0x05, 0x7a, 0x68, 0x69,
1560x00, 0x01, 0x45, 0x04, 0x00, 0x4f, 0x07, 0x00, 0x84, 0x02, 1560x73, 0x74, 0x6f, 0x72, 0x79, 0x7b, 0x00, 0x01, 0x45, 0x04,
1570xf0, 0x02, 0x00, 0x6f, 0x6e, 0x00, 0x57, 0x69, 0x6b, 0x69, 1570x00, 0x4f, 0x07, 0x00, 0x84, 0x02, 0xf0, 0x02, 0x00, 0x6f,
1580x70, 0x65, 0x64, 0x69, 0x61, 0x00, 0x5b, 0x36, 0x5d, 0x6c, 1580x6e, 0x00, 0x57, 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69,
1590x01, 0xf1, 0x01, 0x5b, 0x35, 0x5d, 0x00, 0x68, 0x74, 0x74, 1590x61, 0x00, 0x5b, 0x36, 0x5d, 0x6c, 0x01, 0xf1, 0x02, 0x5b,
1600x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x60, 1600x35, 0x5d, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
1610x01, 0xa3, 0x2e, 0x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 1610x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x61, 0x01, 0xa3, 0x2e,
1620x6e, 0x2f, 0x96, 0x01, 0x21, 0x2f, 0x73, 0x22, 0x01, 0xc1, 1620x63, 0x6f, 0x2e, 0x6a, 0x70, 0x2f, 0x65, 0x6e, 0x2f, 0x97,
1630x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x28, 0x62, 0x65, 0x77, 1630x01, 0x21, 0x2f, 0x73, 0x23, 0x01, 0x10, 0x2f, 0x32, 0x00,
1640x61, 0x72, 0x1f, 0x06, 0x60, 0x46, 0x6c, 0x61, 0x73, 0x68, 1640x12, 0x36, 0x32, 0x00, 0x74, 0x3a, 0x2f, 0x2f, 0x65, 0x6e,
1650x29, 0x47, 0x00, 0x15, 0x36, 0x47, 0x00, 0x44, 0x65, 0x6e, 1650x2e, 0x77, 0x51, 0x00, 0x50, 0x2e, 0x6f, 0x72, 0x67, 0x2f,
1660x2e, 0x77, 0x66, 0x00, 0x50, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 1660x0e, 0x00, 0x12, 0x2f, 0x4e, 0x01, 0x72, 0x00, 0x00, 0x00,
1670x0e, 0x00, 0x12, 0x2f, 0x63, 0x01, 0x72, 0x00, 0x00, 0x00, 1670x31, 0x31, 0x2e, 0x31, 0x5c, 0x08, 0x00, 0x8b, 0x06, 0x91,
1680x31, 0x31, 0x2e, 0x31, 0x60, 0x08, 0x00, 0xa0, 0x06, 0x40, 1680x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x31,
1690x72, 0x6f, 0x6c, 0x73, 0x69, 0x08, 0x11, 0x54, 0x46, 0x06, 1690x06, 0x20, 0x79, 0x00, 0x19, 0x00, 0xe3, 0x2c, 0x00, 0x73,
1700x20, 0x79, 0x00, 0x19, 0x00, 0xe3, 0x2c, 0x00, 0x73, 0x69, 1700x69, 0x6d, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63,
1710x6d, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 1710x6b, 0x47, 0x01, 0x21, 0x75, 0x73, 0xeb, 0x01, 0x01, 0x4a,
1720x5c, 0x01, 0x21, 0x75, 0x73, 0x00, 0x02, 0x01, 0x5f, 0x08, 1720x08, 0x44, 0x6d, 0x70, 0x74, 0x79, 0x60, 0x05, 0x00, 0x46,
1730x44, 0x6d, 0x70, 0x74, 0x79, 0x75, 0x05, 0x00, 0x5b, 0x01, 1730x01, 0x02, 0x96, 0x02, 0x35, 0x79, 0x70, 0x65, 0x13, 0x08,
1740x02, 0xab, 0x02, 0x35, 0x79, 0x70, 0x65, 0x28, 0x08, 0x23, 1740x23, 0x6f, 0x72, 0xe3, 0x02, 0x22, 0x00, 0x6f, 0xb0, 0x02,
1750x6f, 0x72, 0xf8, 0x02, 0x22, 0x00, 0x6f, 0xc5, 0x02, 0x50, 1750x50, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0xb9, 0x01, 0x21, 0x74,
1760x6b, 0x65, 0x79, 0x62, 0x6f, 0xce, 0x01, 0x21, 0x74, 0x6f, 1760x6f, 0x7d, 0x04, 0x02, 0xeb, 0x03, 0x02, 0x44, 0x00, 0x14,
1770x92, 0x04, 0x02, 0x00, 0x04, 0x02, 0x44, 0x00, 0x14, 0x2e, 1770x2e, 0x6c, 0x03, 0x30, 0x6d, 0x61, 0x6b, 0x43, 0x00, 0x8f,
1780x81, 0x03, 0x30, 0x6d, 0x61, 0x6b, 0x43, 0x00, 0x8f, 0x6d, 1780x6d, 0x69, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0x80, 0x00,
1790x69, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0x80, 0x00, 0x01, 1790x01, 0x02, 0xe4, 0x01, 0x03, 0xbe, 0x06, 0x08, 0x84, 0x00,
1800x02, 0xf9, 0x01, 0x03, 0xd3, 0x06, 0x08, 0x84, 0x00, 0x81, 1800x81, 0x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0xf5,
1810x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x0a, 0x07, 1810x06, 0x40, 0x6f, 0x00, 0x63, 0x6c, 0x21, 0x04, 0x50, 0x69,
1820x40, 0x6f, 0x00, 0x63, 0x6c, 0x36, 0x04, 0x50, 0x69, 0x74, 1820x74, 0x00, 0x61, 0x67, 0x80, 0x07, 0x62, 0x28, 0x6f, 0x72,
1830x00, 0x61, 0x67, 0x95, 0x07, 0x62, 0x28, 0x6f, 0x72, 0x00, 1830x00, 0x75, 0x73, 0x96, 0x04, 0xd7, 0x55, 0x6e, 0x64, 0x6f,
1840x75, 0x73, 0xab, 0x04, 0xd7, 0x55, 0x6e, 0x64, 0x6f, 0x00, 1840x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x29, 0xea,
1850x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x29, 0xff, 0x03, 1850x03, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x00,
1860x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x00, 0x01, 1860x01, 0x47, 0x04, 0x0f, 0xe2, 0x00, 0x04, 0x02, 0x43, 0x07,
1870x5c, 0x04, 0x0f, 0xe2, 0x00, 0x04, 0x02, 0x58, 0x07, 0x12, 1870x12, 0x2c, 0xc2, 0x00, 0x02, 0x0d, 0x00, 0x05, 0x72, 0x03,
1880x2c, 0xc2, 0x00, 0x02, 0x0d, 0x00, 0x05, 0x87, 0x03, 0x52, 1880x52, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x41, 0x02, 0x06, 0x09,
1890x65, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x02, 0x06, 0x1e, 0x08, 1890x08, 0x00, 0x79, 0x06, 0x41, 0x61, 0x00, 0x60, 0x70, 0x4d,
1900x00, 0x8e, 0x06, 0x41, 0x61, 0x00, 0x60, 0x70, 0x62, 0x02, 1900x02, 0x66, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x27, 0xdc, 0x06,
1910x66, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x27, 0xf1, 0x06, 0x01, 1910x01, 0xc9, 0x09, 0x07, 0x1b, 0x00, 0x11, 0x73, 0x7f, 0x03,
1920xde, 0x09, 0x07, 0x1b, 0x00, 0x11, 0x73, 0x94, 0x03, 0x76, 1920x76, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0xb0, 0x07,
1930x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0xc5, 0x07, 0x04, 1930x04, 0x51, 0x00, 0x00, 0x1a, 0x03, 0x04, 0x36, 0x01, 0x12,
1940x51, 0x00, 0x00, 0x2f, 0x03, 0x04, 0x36, 0x01, 0x12, 0x53, 1940x53, 0xbb, 0x05, 0x04, 0x53, 0x08, 0x00, 0xc0, 0x04, 0x06,
1950xd0, 0x05, 0x04, 0x68, 0x08, 0x00, 0xd5, 0x04, 0x06, 0xed, 1950xd8, 0x05, 0x05, 0xe9, 0x07, 0x02, 0xe0, 0x03, 0x01, 0x89,
1960x05, 0x05, 0xfe, 0x07, 0x02, 0xf5, 0x03, 0x01, 0x9e, 0x03, 1960x03, 0x03, 0x29, 0x00, 0x09, 0x6f, 0x00, 0x10, 0x2e, 0xfd,
1970x03, 0x29, 0x00, 0x09, 0x6f, 0x00, 0x10, 0x2e, 0xfd, 0x01, 1970x01, 0x04, 0xac, 0x05, 0x40, 0x70, 0x61, 0x79, 0x73, 0x6a,
1980x04, 0xc1, 0x05, 0x40, 0x70, 0x61, 0x79, 0x73, 0x7f, 0x05, 1980x05, 0x50, 0x61, 0x74, 0x74, 0x65, 0x6e, 0xba, 0x04, 0x01,
1990x50, 0x61, 0x74, 0x74, 0x65, 0x6e, 0xcf, 0x04, 0x01, 0x5e, 1990x49, 0x08, 0x07, 0x2e, 0x00, 0x40, 0x2c, 0x00, 0x73, 0x6f,
2000x08, 0x07, 0x2e, 0x00, 0x40, 0x2c, 0x00, 0x73, 0x6f, 0xb5, 2000xa0, 0x04, 0x00, 0x3a, 0x08, 0x02, 0xd0, 0x05, 0x00, 0x3a,
2010x04, 0x00, 0x4f, 0x08, 0x02, 0xe5, 0x05, 0x00, 0x3a, 0x01, 2010x01, 0x03, 0x5a, 0x01, 0x11, 0x6d, 0xbb, 0x00, 0x50, 0x69,
2020x03, 0x5a, 0x01, 0x11, 0x6d, 0xbb, 0x00, 0x50, 0x69, 0x73, 2020x73, 0x00, 0x75, 0x70, 0x38, 0x00, 0x45, 0x79, 0x6f, 0x75,
2030x00, 0x75, 0x70, 0x38, 0x00, 0x45, 0x79, 0x6f, 0x75, 0x3a, 2030x3a, 0x34, 0x07, 0x05, 0x23, 0x00, 0x00, 0x61, 0x0a, 0x30,
2040x49, 0x07, 0x05, 0x23, 0x00, 0x00, 0x76, 0x0a, 0x30, 0x65, 2040x65, 0x6d, 0x69, 0xec, 0x03, 0x12, 0x73, 0x43, 0x01, 0x81,
2050x6d, 0x69, 0x01, 0x04, 0x12, 0x73, 0x43, 0x01, 0x81, 0x61, 2050x61, 0x00, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0xc8, 0x07,
2060x00, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0xdd, 0x07, 0x03, 2060x03, 0x32, 0x01, 0x41, 0x6e, 0x65, 0x65, 0x64, 0xc6, 0x08,
2070x32, 0x01, 0x41, 0x6e, 0x65, 0x65, 0x64, 0xdb, 0x08, 0x10, 2070x10, 0x62, 0xbf, 0x08, 0x10, 0x2d, 0x12, 0x05, 0x42, 0x69,
2080x62, 0xd4, 0x08, 0x10, 0x2d, 0x27, 0x05, 0x42, 0x69, 0x6e, 2080x6e, 0x65, 0x64, 0xe8, 0x05, 0x00, 0x54, 0x00, 0x42, 0x6b,
2090x65, 0x64, 0xfd, 0x05, 0x00, 0x54, 0x00, 0x42, 0x6b, 0x6e, 2090x6e, 0x6f, 0x77, 0x7e, 0x03, 0x4b, 0x61, 0x62, 0x6f, 0x75,
2100x6f, 0x77, 0x93, 0x03, 0x4b, 0x61, 0x62, 0x6f, 0x75, 0x45, 2100x45, 0x00, 0x04, 0xa2, 0x01, 0x2f, 0x6f, 0x72, 0x80, 0x00,
2110x00, 0x04, 0xa2, 0x01, 0x2f, 0x6f, 0x72, 0x80, 0x00, 0x02, 2110x02, 0x55, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x93, 0x03, 0x5a,
2120x55, 0x6c, 0x69, 0x73, 0x74, 0x73, 0xa8, 0x03, 0x5a, 0x6f, 2120x6f, 0x73, 0x73, 0x69, 0x62, 0x6a, 0x01, 0x14, 0x61, 0x70,
2130x73, 0x73, 0x69, 0x62, 0x6a, 0x01, 0x14, 0x61, 0x85, 0x09, 2130x09, 0x01, 0x8d, 0x00, 0x01, 0x48, 0x00, 0x50, 0x61, 0x6e,
2140x01, 0x8d, 0x00, 0x01, 0x48, 0x00, 0x50, 0x61, 0x6e, 0x79, 2140x79, 0x74, 0x68, 0x62, 0x01, 0x32, 0x65, 0x6c, 0x73, 0x82,
2150x74, 0x68, 0x62, 0x01, 0x32, 0x65, 0x6c, 0x73, 0x82, 0x00, 2150x00, 0xa2, 0x66, 0x65, 0x65, 0x6c, 0x00, 0x6c, 0x69, 0x6b,
2160xa2, 0x66, 0x65, 0x65, 0x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 2160x65, 0x2e, 0x3f, 0x03, 0x41, 0x65, 0x72, 0x61, 0x73, 0x93,
2170x2e, 0x3f, 0x03, 0x41, 0x65, 0x72, 0x61, 0x73, 0xa8, 0x0b, 2170x0b, 0x49, 0x69, 0x6e, 0x67, 0x6c, 0xd3, 0x01, 0x1c, 0x2c,
2180x49, 0x69, 0x6e, 0x67, 0x6c, 0xd3, 0x01, 0x1c, 0x2c, 0x5b, 2180x5b, 0x02, 0x08, 0x1b, 0x02, 0x22, 0x6e, 0x64, 0x58, 0x02,
2190x02, 0x08, 0x1b, 0x02, 0x22, 0x6e, 0x64, 0x58, 0x02, 0x05, 2190x05, 0xde, 0x01, 0x03, 0x52, 0x02, 0x01, 0xb8, 0x02, 0x01,
2200xde, 0x01, 0x03, 0x52, 0x02, 0x01, 0xb8, 0x02, 0x01, 0x15, 2200x00, 0x08, 0x2a, 0x6c, 0x6c, 0x20, 0x02, 0x09, 0x9c, 0x02,
2210x08, 0x2a, 0x6c, 0x6c, 0x20, 0x02, 0x09, 0x9c, 0x02, 0x22, 2210x22, 0x72, 0x65, 0x79, 0x00, 0x30, 0x64, 0x00, 0x77, 0xa3,
2220x72, 0x65, 0x79, 0x00, 0x30, 0x64, 0x00, 0x77, 0xa3, 0x02, 2220x02, 0x00, 0x99, 0x00, 0x34, 0x6c, 0x65, 0x66, 0x6c, 0x00,
2230x00, 0x99, 0x00, 0x34, 0x6c, 0x65, 0x66, 0x6c, 0x00, 0x05, 2230x05, 0x5e, 0x00, 0x18, 0x61, 0x14, 0x01, 0x0f, 0x2a, 0x00,
2240x5e, 0x00, 0x18, 0x61, 0x14, 0x01, 0x0f, 0x2a, 0x00, 0x05, 2240x05, 0x02, 0x43, 0x03, 0x10, 0x73, 0x43, 0x03, 0x36, 0x2e,
2250x02, 0x43, 0x03, 0x10, 0x73, 0x43, 0x03, 0x36, 0x2e, 0x00, 2250x00, 0x52, 0xb3, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x20, 0x00,
2260x52, 0xb3, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x20, 0x00, 0x00, 2260x00, 0x0d, 0x00, 0x01, 0x23, 0x00, 0x02, 0xf4, 0x02, 0x01,
2270x0d, 0x00, 0x01, 0x23, 0x00, 0x02, 0xf4, 0x02, 0x01, 0x58, 2270x58, 0x02, 0x02, 0xfb, 0x00, 0x0c, 0x56, 0x02, 0x0b, 0x3e,
2280x02, 0x02, 0xfb, 0x00, 0x0c, 0x56, 0x02, 0x0b, 0x53, 0x09, 2280x09, 0x04, 0x7f, 0x03, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f,
2290x04, 0x7f, 0x03, 0x60, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 2290x72, 0x11, 0x04, 0x01, 0xe1, 0x01, 0x32, 0x6d, 0x6f, 0x76,
2300x11, 0x04, 0x01, 0xe1, 0x01, 0x32, 0x6d, 0x6f, 0x76, 0x18, 2300x18, 0x00, 0x00, 0x38, 0x00, 0x31, 0x00, 0x61, 0x72, 0x14,
2310x00, 0x00, 0x38, 0x00, 0x31, 0x00, 0x61, 0x72, 0x29, 0x05, 2310x05, 0x04, 0x83, 0x08, 0x34, 0x2e, 0x00, 0x50, 0x74, 0x00,
2320x04, 0x98, 0x08, 0x34, 0x2e, 0x00, 0x50, 0x74, 0x00, 0x02, 2320x02, 0xc9, 0x0a, 0x40, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x00,
2330xde, 0x0a, 0x40, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x00, 0x86, 2330x86, 0x00, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x73, 0x3a,
2340x00, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x73, 0x3a, 0x00, 2340x00, 0x11, 0x28, 0x86, 0x0c, 0x82, 0x61, 0x00, 0x6e, 0x6f,
2350x11, 0x28, 0x9b, 0x0c, 0x82, 0x61, 0x00, 0x6e, 0x6f, 0x72, 2350x72, 0x6d, 0x61, 0x6c, 0x14, 0x00, 0x00, 0xfe, 0x0c, 0x08,
2360x6d, 0x61, 0x6c, 0x14, 0x00, 0x00, 0x13, 0x0d, 0x08, 0x97, 2360x97, 0x00, 0x24, 0x29, 0x2c, 0x12, 0x01, 0x01, 0xd3, 0x00,
2370x00, 0x24, 0x29, 0x2c, 0x12, 0x01, 0x01, 0xd3, 0x00, 0x04, 2370x04, 0x6b, 0x01, 0x20, 0x69, 0x6e, 0xb5, 0x02, 0x0f, 0xbb,
2380x6b, 0x01, 0x20, 0x69, 0x6e, 0xb5, 0x02, 0x0f, 0xbb, 0x03, 2380x03, 0x03, 0x03, 0x0e, 0x00, 0x80, 0x61, 0x70, 0x70, 0x72,
2390x03, 0x03, 0x0e, 0x00, 0x80, 0x61, 0x70, 0x70, 0x72, 0x6f, 2390x6f, 0x70, 0x72, 0x69, 0xe5, 0x05, 0x44, 0x77, 0x61, 0x79,
2400x70, 0x72, 0x69, 0xfa, 0x05, 0x44, 0x77, 0x61, 0x79, 0x3b, 2400x3b, 0x44, 0x00, 0x01, 0x8c, 0x01, 0x21, 0x30, 0x00, 0x5e,
2410x44, 0x00, 0x01, 0x8c, 0x01, 0x21, 0x30, 0x00, 0x5e, 0x04, 2410x04, 0x04, 0xa6, 0x00, 0x02, 0x1e, 0x01, 0x23, 0x62, 0x61,
2420x04, 0xa6, 0x00, 0x02, 0x1e, 0x01, 0x23, 0x62, 0x61, 0x16, 2420x16, 0x04, 0x02, 0x8a, 0x04, 0x14, 0x61, 0x3d, 0x0d, 0x04,
2430x04, 0x02, 0x8a, 0x04, 0x14, 0x61, 0x52, 0x0d, 0x04, 0xbd, 2430xbd, 0x03, 0x43, 0x00, 0x00, 0x28, 0x41, 0x00, 0x09, 0x30,
2440x03, 0x43, 0x00, 0x00, 0x28, 0x41, 0x15, 0x09, 0x30, 0x61, 2440x61, 0x63, 0x74, 0x75, 0x08, 0x82, 0x00, 0x64, 0x65, 0x73,
2450x63, 0x74, 0x8a, 0x08, 0x82, 0x00, 0x64, 0x65, 0x73, 0x63, 2450x63, 0x72, 0x69, 0x62, 0x84, 0x00, 0x31, 0x73, 0x65, 0x63,
2460x72, 0x69, 0x62, 0x84, 0x00, 0x31, 0x73, 0x65, 0x63, 0x81, 2460x81, 0x03, 0x32, 0x32, 0x2e, 0x31, 0x43, 0x0a, 0x00, 0x69,
2470x03, 0x32, 0x32, 0x2e, 0x31, 0x58, 0x0a, 0x00, 0x69, 0x01, 2470x01, 0x05, 0x04, 0x0a, 0x22, 0x2e, 0x29, 0xca, 0x05, 0x12,
2480x05, 0x19, 0x0a, 0x22, 0x2e, 0x29, 0xca, 0x05, 0x12, 0x32, 2480x32, 0xca, 0x05, 0x60, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
2490xca, 0x05, 0x60, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x67, 2490x52, 0x08, 0x00, 0xcc, 0x05, 0x00, 0x13, 0x00, 0x00, 0x5d,
2500x08, 0x00, 0xcc, 0x05, 0x00, 0x13, 0x00, 0x00, 0x72, 0x09, 2500x09, 0x22, 0x6f, 0x77, 0x78, 0x09, 0x11, 0x74, 0xfa, 0x03,
2510x22, 0x6f, 0x77, 0x8d, 0x09, 0x11, 0x74, 0xfa, 0x03, 0x40, 2510x40, 0x66, 0x69, 0x67, 0x75, 0xa7, 0x08, 0x10, 0x77, 0x4b,
2520x66, 0x69, 0x67, 0x75, 0xbc, 0x08, 0x10, 0x77, 0x60, 0x0b, 2520x0b, 0x00, 0x31, 0x00, 0x10, 0x74, 0x46, 0x08, 0x41, 0x6d,
2530x00, 0x31, 0x00, 0x10, 0x74, 0x5b, 0x08, 0x41, 0x6d, 0x65, 2530x65, 0x6e, 0x73, 0x7d, 0x00, 0x0a, 0xa2, 0x06, 0x01, 0x47,
2540x6e, 0x73, 0x7d, 0x00, 0x0a, 0xb7, 0x06, 0x01, 0x5c, 0x08, 2540x08, 0x03, 0xbb, 0x05, 0xc7, 0x60, 0x54, 0x79, 0x70, 0x65,
2550x03, 0xbb, 0x05, 0xc7, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 2550x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x3a, 0x44, 0x0c, 0x00,
2560x00, 0x6d, 0x65, 0x6e, 0x75, 0x3a, 0x59, 0x0c, 0x00, 0x32, 2560x32, 0x00, 0x02, 0x84, 0x0d, 0x03, 0x00, 0x0a, 0x0a, 0x1b,
2570x00, 0x02, 0x99, 0x0d, 0x03, 0x15, 0x0a, 0x0a, 0x1b, 0x00, 2570x00, 0x00, 0x46, 0x0e, 0x12, 0x2c, 0x38, 0x0a, 0x06, 0x17,
2580x00, 0x5b, 0x0e, 0x12, 0x2c, 0x4d, 0x0a, 0x06, 0x2c, 0x0b, 2580x0b, 0x01, 0x0c, 0x0d, 0x0b, 0x5c, 0x0a, 0x47, 0x2e, 0x00,
2590x01, 0x21, 0x0d, 0x0b, 0x71, 0x0a, 0x47, 0x2e, 0x00, 0x28, 2590x28, 0x54, 0x37, 0x0e, 0x13, 0x61, 0x9d, 0x0d, 0x25, 0x69,
2600x54, 0x4c, 0x0e, 0x13, 0x61, 0xb2, 0x0d, 0x25, 0x69, 0x73, 2600x73, 0xd0, 0x07, 0x23, 0x72, 0x73, 0xf6, 0x0c, 0x5f, 0x69,
2610xe5, 0x07, 0x23, 0x72, 0x73, 0x0b, 0x0d, 0x5f, 0x69, 0x73, 2610x73, 0x3a, 0x00, 0x66, 0x21, 0x09, 0x07, 0x14, 0x32, 0x90,
2620x3a, 0x00, 0x66, 0x36, 0x09, 0x07, 0x14, 0x32, 0x90, 0x00, 2620x00, 0x01, 0x8f, 0x00, 0x13, 0x33, 0x83, 0x00, 0x01, 0x93,
2630x01, 0x8f, 0x00, 0x13, 0x33, 0x83, 0x00, 0x01, 0xa8, 0x0a, 2630x0a, 0x03, 0x76, 0x0c, 0x02, 0x5b, 0x00, 0x01, 0x97, 0x01,
2640x03, 0x8b, 0x0c, 0x02, 0x5b, 0x00, 0x01, 0x97, 0x01, 0x01, 2640x01, 0x78, 0x05, 0x19, 0x33, 0x32, 0x00, 0x12, 0x32, 0xfb,
2650x78, 0x05, 0x19, 0x33, 0x32, 0x00, 0x12, 0x32, 0x10, 0x0f, 2650x0e, 0x16, 0x29, 0x13, 0x06, 0x14, 0x74, 0x80, 0x06, 0xd2,
2660x16, 0x29, 0x13, 0x06, 0x14, 0x74, 0x80, 0x06, 0xd2, 0x60, 2660x60, 0x58, 0x27, 0x00, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62,
2670x58, 0x27, 0x00, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 2670x6f, 0x78, 0x2c, 0x61, 0x01, 0x02, 0xff, 0x02, 0x10, 0x70,
2680x78, 0x2c, 0x61, 0x01, 0x02, 0xff, 0x02, 0x10, 0x70, 0x28, 2680x28, 0x07, 0x00, 0x22, 0x00, 0x04, 0x1c, 0x0e, 0x61, 0x00,
2690x07, 0x00, 0x22, 0x00, 0x04, 0x31, 0x0e, 0x61, 0x00, 0x65, 2690x65, 0x78, 0x74, 0x72, 0x61, 0x97, 0x08, 0x50, 0x74, 0x72,
2700x78, 0x74, 0x72, 0x61, 0xac, 0x08, 0x50, 0x74, 0x72, 0x61, 2700x61, 0x69, 0x6e, 0xf2, 0x0a, 0x03, 0xf7, 0x0a, 0x0e, 0x0e,
2710x69, 0x6e, 0x07, 0x0b, 0x03, 0x0c, 0x0b, 0x0e, 0x23, 0x0e, 2710x0e, 0x08, 0x28, 0x0f, 0x0a, 0xa3, 0x05, 0x01, 0x19, 0x0e,
2720x08, 0x3d, 0x0f, 0x0a, 0xa3, 0x05, 0x01, 0x2e, 0x0e, 0x13, 2720x13, 0x66, 0xaf, 0x0e, 0x03, 0x0f, 0x0e, 0x11, 0x28, 0xb1,
2730x66, 0xc4, 0x0e, 0x03, 0x24, 0x0e, 0x11, 0x28, 0xc6, 0x09, 2730x09, 0x21, 0x69, 0x73, 0x08, 0x0e, 0x51, 0x74, 0x69, 0x6d,
2740x21, 0x69, 0x73, 0x1d, 0x0e, 0x51, 0x74, 0x69, 0x6d, 0x65, 2740x65, 0x73, 0x07, 0x05, 0x10, 0x6e, 0xd4, 0x04, 0x03, 0x2e,
2750x73, 0x07, 0x05, 0x10, 0x6e, 0xd4, 0x04, 0x03, 0x43, 0x09, 2750x09, 0x30, 0x2d, 0x58, 0x27, 0x3d, 0x02, 0x06, 0x56, 0x09,
2760x30, 0x2d, 0x58, 0x27, 0x3d, 0x02, 0x06, 0x6b, 0x09, 0x2b, 2760x2b, 0x2e, 0x29, 0x1d, 0x0c, 0x06, 0xdc, 0x02, 0x00, 0x83,
2770x2e, 0x29, 0x32, 0x0c, 0x06, 0xdc, 0x02, 0x00, 0x98, 0x0e, 2770x0e, 0x0f, 0x98, 0x00, 0x05, 0x04, 0xd0, 0x06, 0x40, 0x73,
2780x0f, 0x98, 0x00, 0x05, 0x04, 0xd0, 0x06, 0x40, 0x73, 0x68, 2780x68, 0x61, 0x64, 0xb5, 0x02, 0x10, 0x6c, 0x0e, 0x04, 0x00,
2790x61, 0x64, 0xb5, 0x02, 0x10, 0x6c, 0x0e, 0x04, 0x00, 0x52, 2790x3d, 0x10, 0x12, 0x6f, 0xcf, 0x00, 0x05, 0x83, 0x05, 0x01,
2800x10, 0x12, 0x6f, 0xcf, 0x00, 0x05, 0x83, 0x05, 0x01, 0xfb, 2800xe6, 0x09, 0x20, 0x65, 0x6e, 0x94, 0x02, 0x2f, 0x64, 0x2e,
2810x09, 0x20, 0x65, 0x6e, 0x94, 0x02, 0x2f, 0x64, 0x2e, 0x39, 2810x39, 0x01, 0x01, 0x13, 0x4a, 0x25, 0x0d, 0x0f, 0x3e, 0x01,
2820x01, 0x01, 0x13, 0x4a, 0x3a, 0x0d, 0x0f, 0x3e, 0x01, 0x01, 2820x01, 0x41, 0x67, 0x65, 0x6e, 0x65, 0x88, 0x02, 0x73, 0x72,
2830x41, 0x67, 0x65, 0x6e, 0x65, 0x88, 0x02, 0x73, 0x72, 0x61, 2830x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6c, 0x1f, 0x0d, 0x08, 0x9e,
2840x6e, 0x64, 0x6f, 0x6d, 0x6c, 0x34, 0x0d, 0x08, 0xb3, 0x10, 2840x10, 0x1e, 0x2e, 0xbf, 0x00, 0x08, 0x43, 0x0e, 0x03, 0x8f,
2850x1e, 0x2e, 0xbf, 0x00, 0x08, 0x58, 0x0e, 0x03, 0xa4, 0x0b, 2850x0b, 0x03, 0xae, 0x00, 0x00, 0x36, 0x08, 0x13, 0x6e, 0x36,
2860x03, 0xae, 0x00, 0x00, 0x36, 0x08, 0x13, 0x6e, 0x36, 0x06, 2860x06, 0x01, 0xcd, 0x02, 0x5d, 0x72, 0x6f, 0x64, 0x75, 0x63,
2870x01, 0xcd, 0x02, 0x5d, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x11, 2870xfc, 0x0e, 0x0b, 0xea, 0x03, 0x22, 0x60, 0x43, 0x04, 0x02,
2880x0f, 0x0b, 0xea, 0x03, 0x22, 0x60, 0x43, 0x04, 0x02, 0x03, 2880x03, 0x1a, 0x0b, 0x30, 0x52, 0x6f, 0x77, 0xe4, 0x0c, 0x41,
2890x2f, 0x0b, 0x30, 0x52, 0x6f, 0x77, 0xf9, 0x0c, 0x41, 0x6f, 2890x6f, 0x78, 0x65, 0x73, 0xd4, 0x0e, 0x00, 0x05, 0x04, 0x01,
2900x78, 0x65, 0x73, 0xe9, 0x0e, 0x00, 0x05, 0x04, 0x01, 0x15, 2900x15, 0x07, 0x50, 0x72, 0x65, 0x61, 0x73, 0x6f, 0xe5, 0x0d,
2910x07, 0x50, 0x72, 0x65, 0x61, 0x73, 0x6f, 0xfa, 0x0d, 0x34, 2910x34, 0x79, 0x00, 0x79, 0x82, 0x11, 0x22, 0x74, 0x6f, 0x4e,
2920x79, 0x00, 0x79, 0x97, 0x11, 0x22, 0x74, 0x6f, 0x4e, 0x00, 2920x00, 0x06, 0x4d, 0x04, 0x10, 0x67, 0x03, 0x0a, 0x22, 0x65,
2930x06, 0x4d, 0x04, 0x10, 0x67, 0x18, 0x0a, 0x22, 0x65, 0x72, 2930x72, 0x0f, 0x0c, 0x10, 0x31, 0x64, 0x00, 0x42, 0x62, 0x6f,
2940x24, 0x0c, 0x10, 0x31, 0x64, 0x00, 0x42, 0x62, 0x6f, 0x74, 2940x74, 0x68, 0x50, 0x00, 0x22, 0x3b, 0x00, 0x0e, 0x01, 0x02,
2950x68, 0x50, 0x00, 0x22, 0x3b, 0x00, 0x0e, 0x01, 0x02, 0xd7, 2950xc2, 0x0d, 0x21, 0x68, 0x61, 0x57, 0x00, 0x07, 0x2a, 0x02,
2960x0d, 0x21, 0x68, 0x61, 0x57, 0x00, 0x07, 0x2a, 0x02, 0x03, 2960x03, 0x90, 0x01, 0x05, 0xd9, 0x00, 0x05, 0xa4, 0x0a, 0x00,
2970x90, 0x01, 0x05, 0xd9, 0x00, 0x05, 0xb9, 0x0a, 0x00, 0xc2, 2970xc2, 0x06, 0x01, 0xcf, 0x0c, 0x20, 0x62, 0x65, 0xd2, 0x04,
2980x06, 0x01, 0xe4, 0x0c, 0x20, 0x62, 0x65, 0xd2, 0x04, 0x26, 2980x26, 0x72, 0x69, 0x28, 0x06, 0x2a, 0x69, 0x66, 0x8e, 0x06,
2990x72, 0x69, 0x28, 0x06, 0x2a, 0x69, 0x66, 0x8e, 0x06, 0x3f, 2990x3f, 0x00, 0x69, 0x74, 0x88, 0x01, 0x02, 0x12, 0x4b, 0x2c,
3000x00, 0x69, 0x74, 0x88, 0x01, 0x02, 0x12, 0x4b, 0x41, 0x0e, 3000x0e, 0x0f, 0x88, 0x01, 0x0b, 0x10, 0x61, 0xf9, 0x0f, 0x00,
3010x0f, 0x88, 0x01, 0x0b, 0x10, 0x61, 0x0e, 0x10, 0x00, 0x2f, 3010x2f, 0x01, 0x00, 0xdc, 0x03, 0x00, 0xed, 0x0d, 0x04, 0x55,
3020x01, 0x00, 0xdc, 0x03, 0x00, 0x02, 0x0e, 0x04, 0x6a, 0x12, 3020x12, 0x2e, 0x61, 0x72, 0xa5, 0x01, 0x00, 0x9c, 0x00, 0x41,
3030x2e, 0x61, 0x72, 0xa5, 0x01, 0x00, 0x9c, 0x00, 0x41, 0x64, 3030x64, 0x72, 0x61, 0x77, 0x5d, 0x0e, 0x00, 0x84, 0x0d, 0x20,
3040x72, 0x61, 0x77, 0x72, 0x0e, 0x00, 0x99, 0x0d, 0x20, 0x75, 3040x75, 0x74, 0x0e, 0x0e, 0x02, 0xc5, 0x03, 0x02, 0xdb, 0x0e,
3050x74, 0x23, 0x0e, 0x02, 0xc5, 0x03, 0x02, 0xf0, 0x0e, 0x00, 3050x00, 0x0a, 0x0b, 0x02, 0x2b, 0x0e, 0x03, 0x51, 0x12, 0x02,
3060x1f, 0x0b, 0x02, 0x40, 0x0e, 0x03, 0x66, 0x12, 0x02, 0x8a, 3060x8a, 0x01, 0x24, 0x73, 0x65, 0x69, 0x0d, 0x06, 0x0f, 0x11,
3070x01, 0x24, 0x73, 0x65, 0x7e, 0x0d, 0x06, 0x24, 0x11, 0x51, 3070x51, 0x61, 0x00, 0x73, 0x6d, 0x61, 0x74, 0x0c, 0x00, 0xbf,
3080x61, 0x00, 0x73, 0x6d, 0x61, 0x89, 0x0c, 0x00, 0xd4, 0x0e, 3080x0e, 0x03, 0x77, 0x00, 0x20, 0x73, 0x68, 0xc4, 0x04, 0x05,
3090x03, 0x77, 0x00, 0x20, 0x73, 0x68, 0xc4, 0x04, 0x05, 0x05, 3090xf0, 0x0c, 0x0c, 0x39, 0x0e, 0x04, 0xc8, 0x02, 0x13, 0x69,
3100x0d, 0x0c, 0x4e, 0x0e, 0x04, 0xc8, 0x02, 0x13, 0x69, 0x23, 3100x23, 0x02, 0x02, 0x55, 0x00, 0x04, 0x38, 0x11, 0x05, 0x3a,
3110x02, 0x02, 0x55, 0x00, 0x04, 0x4d, 0x11, 0x05, 0x4f, 0x10, 3110x10, 0x07, 0xfd, 0x04, 0x22, 0x68, 0x65, 0x10, 0x07, 0x00,
3120x07, 0xfd, 0x04, 0x22, 0x68, 0x65, 0x10, 0x07, 0x00, 0x35, 3120x20, 0x10, 0x71, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x79,
3130x10, 0x71, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x65, 3130x65, 0x00, 0x05, 0x21, 0x0f, 0x04, 0x0b, 0x01, 0x16, 0x64,
3140x00, 0x05, 0x36, 0x0f, 0x04, 0x0b, 0x01, 0x16, 0x64, 0x10, 3140xfb, 0x0c, 0x10, 0x4d, 0x66, 0x08, 0x05, 0x2e, 0x00, 0x00,
3150x0d, 0x10, 0x4d, 0x66, 0x08, 0x05, 0x2e, 0x00, 0x00, 0xa4, 3150xa4, 0x0a, 0x17, 0x73, 0x2b, 0x05, 0x60, 0x73, 0x00, 0x6c,
3160x0a, 0x17, 0x73, 0x2b, 0x05, 0x60, 0x73, 0x00, 0x6c, 0x6f, 3160x6f, 0x6f, 0x6b, 0x1c, 0x07, 0x30, 0x74, 0x74, 0x69, 0xcd,
3170x6f, 0x6b, 0x1c, 0x07, 0x30, 0x74, 0x74, 0x69, 0xe2, 0x0f, 3170x0f, 0x13, 0x75, 0xa3, 0x0e, 0x00, 0x7a, 0x00, 0x01, 0xd1,
3180x13, 0x75, 0xb8, 0x0e, 0x00, 0x7a, 0x00, 0x01, 0xd1, 0x0a, 3180x0a, 0x01, 0x77, 0x08, 0x60, 0x65, 0x61, 0x73, 0x69, 0x65,
3190x01, 0x77, 0x08, 0x60, 0x65, 0x61, 0x73, 0x69, 0x65, 0x72, 3190x72, 0x5a, 0x0b, 0x13, 0x6e, 0x96, 0x11, 0x05, 0x52, 0x00,
3200x5a, 0x0b, 0x13, 0x6e, 0xab, 0x11, 0x05, 0x52, 0x00, 0x06, 3200x06, 0x01, 0x02, 0x01, 0xae, 0x09, 0x00, 0xe2, 0x04, 0x22,
3210x01, 0x02, 0x01, 0xae, 0x09, 0x00, 0xe2, 0x04, 0x22, 0x63, 3210x63, 0x65, 0xdc, 0x08, 0x02, 0xca, 0x0f, 0x01, 0x4b, 0x02,
3220x65, 0xdc, 0x08, 0x02, 0xdf, 0x0f, 0x01, 0x4b, 0x02, 0x60, 3220x60, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x43, 0x10, 0x02,
3230x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x58, 0x10, 0x02, 0xe0, 3230xe0, 0x02, 0x00, 0x8d, 0x07, 0x70, 0x65, 0x6e, 0x74, 0x2e,
3240x02, 0x00, 0x8d, 0x07, 0x70, 0x65, 0x6e, 0x74, 0x2e, 0x00, 3240x00, 0x43, 0x6f, 0x0c, 0x05, 0x63, 0x74, 0x65, 0x6c, 0x79,
3250x43, 0x6f, 0x0c, 0x05, 0x63, 0x74, 0x65, 0x6c, 0x79, 0x00, 3250x00, 0x61, 0x54, 0x00, 0x25, 0x69, 0x63, 0x9d, 0x00, 0x02,
3260x61, 0x54, 0x00, 0x25, 0x69, 0x63, 0x9d, 0x00, 0x02, 0xa6, 3260xa6, 0x02, 0x00, 0xfa, 0x0f, 0x86, 0x72, 0x65, 0x65, 0x64,
3270x02, 0x00, 0x0f, 0x10, 0x86, 0x72, 0x65, 0x65, 0x64, 0x6f, 3270x6f, 0x6d, 0x00, 0x74, 0x64, 0x04, 0x00, 0xf9, 0x0c, 0x24,
3280x6d, 0x00, 0x74, 0x64, 0x04, 0x00, 0x0e, 0x0d, 0x24, 0x65, 3280x65, 0x77, 0x2f, 0x10, 0x15, 0x73, 0x0b, 0x09, 0x00, 0x41,
3290x77, 0x44, 0x10, 0x15, 0x73, 0x0b, 0x09, 0x00, 0x41, 0x01, 3290x01, 0x23, 0x46, 0x69, 0xe8, 0x12, 0x04, 0x41, 0x09, 0x0a,
3300x23, 0x46, 0x69, 0xfd, 0x12, 0x04, 0x41, 0x09, 0x0a, 0x45, 3300x45, 0x01, 0x00, 0xdf, 0x01, 0x00, 0x6d, 0x09, 0x15, 0x74,
3310x01, 0x00, 0xdf, 0x01, 0x00, 0x6d, 0x09, 0x15, 0x74, 0xf0, 3310xdb, 0x0c, 0x0f, 0x39, 0x01, 0x00, 0x16, 0x44, 0x25, 0x00,
3320x0c, 0x0f, 0x39, 0x01, 0x00, 0x16, 0x44, 0x25, 0x00, 0x52, 3320x52, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x94, 0x10, 0x42, 0x6a,
3330x6c, 0x65, 0x76, 0x65, 0x6c, 0xa9, 0x10, 0x42, 0x6a, 0x75, 3330x75, 0x64, 0x67, 0x04, 0x0e, 0x01, 0x0e, 0x08, 0x01, 0xbe,
3340x64, 0x67, 0x19, 0x0e, 0x01, 0x0e, 0x08, 0x01, 0xbe, 0x00, 3340x00, 0x26, 0x78, 0x69, 0x49, 0x00, 0x82, 0x74, 0x65, 0x63,
3350x26, 0x78, 0x69, 0x49, 0x00, 0x82, 0x74, 0x65, 0x63, 0x68, 3350x68, 0x6e, 0x69, 0x71, 0x75, 0x9a, 0x0f, 0x42, 0x64, 0x65,
3360x6e, 0x69, 0x71, 0x75, 0xaf, 0x0f, 0x42, 0x64, 0x65, 0x64, 3360x64, 0x75, 0x09, 0x07, 0x03, 0x6e, 0x0f, 0x01, 0x42, 0x0c,
3370x75, 0x09, 0x07, 0x03, 0x83, 0x0f, 0x01, 0x42, 0x0c, 0x57, 3370x57, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x85, 0x01, 0x12, 0x3a,
3380x73, 0x6f, 0x6c, 0x76, 0x65, 0x85, 0x01, 0x12, 0x3a, 0xe6, 3380xe6, 0x05, 0x01, 0x66, 0x00, 0x06, 0x97, 0x0f, 0x12, 0x61,
3390x05, 0x01, 0x66, 0x00, 0x06, 0xac, 0x0f, 0x12, 0x61, 0x68, 3390x68, 0x03, 0x23, 0x6f, 0x66, 0xbb, 0x03, 0x00, 0xa1, 0x07,
3400x03, 0x23, 0x6f, 0x66, 0xbb, 0x03, 0x00, 0xa1, 0x07, 0x02, 3400x02, 0x58, 0x02, 0x11, 0x77, 0x7b, 0x03, 0x17, 0x74, 0x55,
3410x58, 0x02, 0x11, 0x77, 0x7b, 0x03, 0x17, 0x74, 0x55, 0x01, 3410x01, 0x23, 0x69, 0x6e, 0x32, 0x04, 0x21, 0x65, 0x76, 0x98,
3420x23, 0x69, 0x6e, 0x32, 0x04, 0x21, 0x65, 0x76, 0xad, 0x0e, 3420x0e, 0x31, 0x6f, 0x6e, 0x65, 0x7b, 0x04, 0x06, 0x56, 0x0a,
3430x31, 0x6f, 0x6e, 0x65, 0x7b, 0x04, 0x06, 0x56, 0x0a, 0x10, 3430x10, 0x2c, 0x9f, 0x03, 0x1d, 0x64, 0xd3, 0x00, 0x83, 0x60,
3440x2c, 0x9f, 0x03, 0x1d, 0x64, 0xd3, 0x00, 0x83, 0x60, 0x54, 3440x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x3e, 0x04, 0x70,
3450x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x3e, 0x04, 0x70, 0x42, 3450x42, 0x61, 0x73, 0x69, 0x63, 0x27, 0x00, 0xb2, 0x11, 0x06,
3460x61, 0x73, 0x69, 0x63, 0x27, 0x00, 0xc7, 0x11, 0x06, 0x98, 3460x98, 0x04, 0x05, 0xaf, 0x09, 0x04, 0x4d, 0x01, 0x01, 0x02,
3470x04, 0x05, 0xaf, 0x09, 0x04, 0x4d, 0x01, 0x01, 0x02, 0x0d, 3470x0d, 0x06, 0x47, 0x15, 0x03, 0x33, 0x0a, 0x04, 0xfa, 0x09,
3480x06, 0x5c, 0x15, 0x03, 0x33, 0x0a, 0x04, 0xfa, 0x09, 0x12, 3480x12, 0x74, 0xe1, 0x02, 0x22, 0x69, 0x6d, 0x80, 0x03, 0x10,
3490x74, 0xe1, 0x02, 0x22, 0x69, 0x6d, 0x80, 0x03, 0x10, 0x65, 3490x65, 0xbe, 0x00, 0x00, 0x16, 0x00, 0x20, 0x60, 0x49, 0x6b,
3500xbe, 0x00, 0x00, 0x16, 0x00, 0x20, 0x60, 0x49, 0x6b, 0x04, 3500x04, 0x30, 0x6d, 0x65, 0x64, 0x87, 0x08, 0x13, 0x27, 0xee,
3510x30, 0x6d, 0x65, 0x64, 0x87, 0x08, 0x13, 0x27, 0xee, 0x00, 3510x00, 0x20, 0x61, 0x6e, 0x26, 0x11, 0x42, 0x79, 0x6f, 0x6e,
3520x20, 0x61, 0x6e, 0x3b, 0x11, 0x42, 0x79, 0x6f, 0x6e, 0x64, 3520x64, 0x64, 0x11, 0x05, 0xdc, 0x06, 0x13, 0x74, 0x7f, 0x02,
3530x79, 0x11, 0x05, 0xdc, 0x06, 0x13, 0x74, 0x7f, 0x02, 0x01, 3530x01, 0xc2, 0x00, 0x26, 0x61, 0x6c, 0x4a, 0x01, 0x13, 0x73,
3540xc2, 0x00, 0x26, 0x61, 0x6c, 0x4a, 0x01, 0x13, 0x73, 0x33, 3540x33, 0x0b, 0x01, 0x85, 0x02, 0x02, 0xef, 0x03, 0x04, 0x49,
3550x0b, 0x01, 0x85, 0x02, 0x02, 0xef, 0x03, 0x04, 0x49, 0x03, 3550x03, 0x05, 0xc6, 0x04, 0x13, 0x63, 0x7e, 0x11, 0x13, 0x00,
3560x05, 0xc6, 0x04, 0x13, 0x63, 0x93, 0x11, 0x13, 0x00, 0x3c, 3560x3c, 0x0d, 0x07, 0x2c, 0x00, 0x04, 0x46, 0x05, 0x02, 0xac,
3570x0d, 0x07, 0x2c, 0x00, 0x04, 0x46, 0x05, 0x02, 0xc1, 0x11, 3570x11, 0x07, 0x28, 0x00, 0x04, 0xe6, 0x00, 0x00, 0x39, 0x13,
3580x07, 0x28, 0x00, 0x04, 0xe6, 0x00, 0x00, 0x4e, 0x13, 0x52, 3580x52, 0x74, 0x00, 0x60, 0x55, 0x6e, 0x76, 0x01, 0x00, 0x1b,
3590x74, 0x00, 0x60, 0x55, 0x6e, 0x76, 0x01, 0x00, 0x1b, 0x06, 3590x06, 0x03, 0xad, 0x00, 0x03, 0x94, 0x11, 0x13, 0x74, 0xc7,
3600x03, 0xad, 0x00, 0x03, 0xa9, 0x11, 0x13, 0x74, 0xc7, 0x06, 3600x06, 0x00, 0x82, 0x01, 0x20, 0x65, 0x6e, 0x2d, 0x10, 0x02,
3610x00, 0x82, 0x01, 0x20, 0x65, 0x6e, 0x42, 0x10, 0x02, 0xe9, 3610xe9, 0x04, 0x05, 0xc0, 0x00, 0x10, 0x65, 0xc4, 0x0f, 0x02,
3620x04, 0x05, 0xc0, 0x00, 0x10, 0x65, 0xd9, 0x0f, 0x02, 0xc3, 3620xae, 0x16, 0x09, 0xcb, 0x00, 0x86, 0x61, 0x00, 0x67, 0x75,
3630x16, 0x09, 0xcb, 0x00, 0x86, 0x61, 0x00, 0x67, 0x75, 0x65, 3630x65, 0x73, 0x73, 0x2c, 0x93, 0x0d, 0x70, 0x62, 0x61, 0x63,
3640x73, 0x73, 0x2c, 0x93, 0x0d, 0x70, 0x62, 0x61, 0x63, 0x6b, 3640x6b, 0x74, 0x72, 0x61, 0x30, 0x08, 0x10, 0x66, 0x29, 0x05,
3650x74, 0x72, 0x61, 0x30, 0x08, 0x10, 0x66, 0x29, 0x05, 0x00, 3650x00, 0x20, 0x0a, 0x10, 0x73, 0x8a, 0x04, 0x03, 0x26, 0x03,
3660x20, 0x0a, 0x10, 0x73, 0x8a, 0x04, 0x03, 0x26, 0x03, 0x50, 3660x50, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0xd0, 0x02, 0x12, 0x47,
3670x77, 0x72, 0x6f, 0x6e, 0x67, 0xd0, 0x02, 0x12, 0x47, 0x9f, 3670x9f, 0x02, 0x00, 0x03, 0x02, 0x05, 0xc4, 0x01, 0x05, 0x25,
3680x02, 0x00, 0x03, 0x02, 0x05, 0xc4, 0x01, 0x05, 0x25, 0x03, 3680x03, 0x00, 0x8f, 0x00, 0x56, 0x74, 0x73, 0x65, 0x6c, 0x66,
3690x00, 0x8f, 0x00, 0x56, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x1c, 3690x1c, 0x00, 0x1b, 0x3a, 0x5c, 0x08, 0x03, 0x90, 0x07, 0x02,
3700x00, 0x1b, 0x3a, 0x5c, 0x08, 0x03, 0x90, 0x07, 0x02, 0xe9, 3700xd4, 0x0f, 0x10, 0x67, 0x90, 0x13, 0x0d, 0x0b, 0x02, 0x03,
3710x0f, 0x10, 0x67, 0xa5, 0x13, 0x0d, 0x0b, 0x02, 0x03, 0x52, 3710x52, 0x05, 0x2b, 0x6d, 0x61, 0xb6, 0x00, 0x01, 0x7e, 0x17,
3720x05, 0x2b, 0x6d, 0x61, 0xb6, 0x00, 0x01, 0x93, 0x17, 0x00, 3720x00, 0x52, 0x0d, 0x31, 0x6d, 0x70, 0x74, 0xc8, 0x01, 0x16,
3730x52, 0x0d, 0x31, 0x6d, 0x70, 0x74, 0xc8, 0x01, 0x16, 0x67, 3730x67, 0x8c, 0x00, 0x05, 0x57, 0x12, 0x30, 0x62, 0x65, 0x66,
3740x8c, 0x00, 0x05, 0x6c, 0x12, 0x30, 0x62, 0x65, 0x66, 0xf1, 3740xf1, 0x03, 0x00, 0x4e, 0x17, 0x41, 0x69, 0x6e, 0x64, 0x73,
3750x03, 0x00, 0x63, 0x17, 0x41, 0x69, 0x6e, 0x64, 0x73, 0x70, 3750x70, 0x00, 0x10, 0x68, 0x30, 0x0f, 0x02, 0x21, 0x01, 0x22,
3760x00, 0x10, 0x68, 0x30, 0x0f, 0x02, 0x21, 0x01, 0x22, 0x00, 3760x00, 0x66, 0xcc, 0x0c, 0x31, 0x2e, 0x00, 0x42, 0xa5, 0x02,
3770x66, 0xcc, 0x0c, 0x31, 0x2e, 0x00, 0x42, 0xa5, 0x02, 0x23, 3770x23, 0x70, 0x61, 0x0a, 0x03, 0xc1, 0x77, 0x61, 0x69, 0x74,
3780x70, 0x61, 0x0a, 0x03, 0xc1, 0x77, 0x61, 0x69, 0x74, 0x2c, 3780x2c, 0x00, 0x65, 0x73, 0x70, 0x65, 0x63, 0x69, 0x31, 0x01,
3790x00, 0x65, 0x73, 0x70, 0x65, 0x63, 0x69, 0x31, 0x01, 0x26, 3790x26, 0x69, 0x66, 0x9e, 0x06, 0x0a, 0xfe, 0x04, 0x95, 0x64,
3800x69, 0x66, 0x9e, 0x06, 0x0a, 0xfe, 0x04, 0x95, 0x64, 0x00, 3800x00, 0x61, 0x00, 0x6c, 0x61, 0x72, 0x67, 0x65, 0xcb, 0x12,
3810x61, 0x00, 0x6c, 0x61, 0x72, 0x67, 0x65, 0xe0, 0x12, 0x50, 3810x50, 0x69, 0x7a, 0x65, 0x2e, 0x00,
3820x69, 0x7a, 0x65, 0x2e, 0x00,
383}; 382};
384 383
385const unsigned short help_text_len = 6263; 384const unsigned short help_text_len = 6259;
386const unsigned short help_text_words = 1155; 385const unsigned short help_text_words = 1153;
386const bool help_valid = true;
387const char quick_help_text[] = "Fill in the grid so that each row, column and square block contains one of every digit."; 387const char quick_help_text[] = "Fill in the grid so that each row, column and square block contains one of every digit.";
diff --git a/apps/plugins/puzzles/help/tents.c b/apps/plugins/puzzles/help/tents.c
index f0617fefd4..8b14490a51 100644
--- a/apps/plugins/puzzles/help/tents.c
+++ b/apps/plugins/puzzles/help/tents.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,160 +6,162 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 102, TEXT_UNDERLINE }, 9 { 103, TEXT_UNDERLINE },
10 { 160, TEXT_CENTER | C_RED }, 10 { 161, TEXT_CENTER | C_RED },
11 { 190, TEXT_UNDERLINE }, 11 { 191, TEXT_UNDERLINE },
12 { 207, TEXT_UNDERLINE }, 12 { 208, TEXT_UNDERLINE },
13 { 342, TEXT_CENTER | C_RED }, 13 { 343, TEXT_CENTER | C_RED },
14 { 359, TEXT_UNDERLINE },
15 { 360, TEXT_UNDERLINE }, 14 { 360, TEXT_UNDERLINE },
16 { 370, TEXT_UNDERLINE }, 15 { 361, TEXT_UNDERLINE },
16 { 371, TEXT_UNDERLINE },
17 LAST_STYLE_ITEM 17 LAST_STYLE_ITEM
18}; 18};
19 19
20/* orig 2140 comp 1385 ratio 0.647196 level 10 saved 755 */ 20/* orig 2158 comp 1394 ratio 0.645968 level 10 saved 764 */
21const char help_text[] = { 21const char help_text[] = {
220xf0, 0x26, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 220xfc, 0x05, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
230x32, 0x35, 0x3a, 0x20, 0x54, 0x65, 0x6e, 0x74, 0x73, 0x20, 230x32, 0x35, 0x3a, 0x20, 0x54, 0x65, 0x6e, 0x74, 0x73, 0x20,
240x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 240x00, 0x2d, 0x01, 0x00, 0xf0, 0x14, 0x00, 0x00, 0x00, 0x59,
250x65, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 250x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61, 0x00,
260x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 260x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71,
270x00, 0x73, 0x6f, 0x6d, 0x65, 0x11, 0x00, 0xf0, 0x05, 0x77, 270x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d,
280x68, 0x69, 0x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 280x65, 0x11, 0x00, 0xf0, 0x05, 0x77, 0x68, 0x69, 0x63, 0x68,
290x69, 0x6e, 0x00, 0x74, 0x72, 0x65, 0x65, 0x73, 0x2e, 0x39, 290x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x74,
300x00, 0xf0, 0x04, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 300x72, 0x65, 0x65, 0x73, 0x2e, 0x39, 0x00, 0xf0, 0x04, 0x72,
310x73, 0x00, 0x74, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 310x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73, 0x00, 0x74, 0x6f,
320x00, 0x74, 0x57, 0x00, 0x35, 0x00, 0x69, 0x6e, 0x3b, 0x00, 320x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x00, 0x74, 0x69, 0x00,
330xd6, 0x74, 0x68, 0x65, 0x00, 0x72, 0x65, 0x6d, 0x61, 0x69, 330x35, 0x00, 0x69, 0x6e, 0x3b, 0x00, 0xd6, 0x74, 0x68, 0x65,
340x6e, 0x69, 0x6e, 0x67, 0x5a, 0x00, 0x00, 0x22, 0x00, 0xe1, 340x00, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
350x75, 0x63, 0x68, 0x00, 0x61, 0x00, 0x77, 0x61, 0x79, 0x00, 350x5a, 0x00, 0x00, 0x22, 0x00, 0xe1, 0x75, 0x63, 0x68, 0x00,
360x74, 0x68, 0x61, 0x74, 0x2a, 0x00, 0x60, 0x66, 0x6f, 0x6c, 360x61, 0x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74,
370x6c, 0x6f, 0x77, 0x2a, 0x00, 0xf1, 0x0e, 0x63, 0x6f, 0x6e, 370x2a, 0x00, 0x60, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x2a,
380x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x61, 0x72, 380x00, 0xf1, 0x0e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69,
390x65, 0x00, 0x6d, 0x65, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 390x6f, 0x6e, 0x73, 0x00, 0x61, 0x72, 0x65, 0x00, 0x6d, 0x65,
400x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x13, 0x00, 0xf3, 0x00, 400x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x54, 0x68, 0x65,
410x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x00, 0x61, 0x73, 410x72, 0x65, 0x13, 0x00, 0xf3, 0x00, 0x65, 0x78, 0x61, 0x63,
420x00, 0x6d, 0x61, 0x6e, 0x79, 0x7b, 0x00, 0x24, 0x61, 0x73, 420x74, 0x6c, 0x79, 0x00, 0x61, 0x73, 0x00, 0x6d, 0x61, 0x6e,
430xa0, 0x00, 0x03, 0x2e, 0x00, 0x04, 0x18, 0x00, 0x22, 0x6e, 430x79, 0x7b, 0x00, 0x24, 0x61, 0x73, 0xa0, 0x00, 0x03, 0x2e,
440x64, 0x19, 0x00, 0xff, 0x03, 0x00, 0x63, 0x61, 0x6e, 0x00, 440x00, 0x04, 0x18, 0x00, 0x22, 0x6e, 0x64, 0x19, 0x00, 0xff,
450x62, 0x65, 0x00, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 450x03, 0x00, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x65, 0x00, 0x6d,
460x00, 0x75, 0x70, 0x8d, 0x00, 0x01, 0x41, 0x65, 0x61, 0x63, 460x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x00, 0x75, 0x70, 0x8d,
470x68, 0x3a, 0x00, 0x00, 0xde, 0x00, 0x42, 0x64, 0x69, 0x72, 470x00, 0x01, 0x41, 0x65, 0x61, 0x63, 0x68, 0x3a, 0x00, 0x00,
480x65, 0x6b, 0x00, 0x40, 0x64, 0x6a, 0x61, 0x63, 0x15, 0x00, 480xde, 0x00, 0x42, 0x64, 0x69, 0x72, 0x65, 0x6b, 0x00, 0x40,
490x60, 0x28, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x10, 0x01, 0xd0, 490x64, 0x6a, 0x61, 0x63, 0x15, 0x00, 0x60, 0x28, 0x68, 0x6f,
500x6c, 0x6c, 0x79, 0x00, 0x6f, 0x72, 0x00, 0x76, 0x65, 0x72, 500x72, 0x69, 0x7a, 0x10, 0x01, 0xd0, 0x6c, 0x6c, 0x79, 0x00,
510x74, 0x69, 0x63, 0x0e, 0x00, 0xf0, 0x01, 0x2c, 0x00, 0x62, 510x6f, 0x72, 0x00, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x0e,
520x75, 0x74, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x64, 0x69, 0x61, 520x00, 0xf0, 0x01, 0x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x6e,
530x67, 0x6f, 0x6e, 0x14, 0x00, 0x10, 0x29, 0x21, 0x01, 0x62, 530x6f, 0x74, 0x00, 0x64, 0x69, 0x61, 0x67, 0x6f, 0x6e, 0x14,
540x69, 0x74, 0x73, 0x00, 0x6f, 0x77, 0x3f, 0x01, 0xc2, 0x2e, 540x00, 0x10, 0x29, 0x21, 0x01, 0x62, 0x69, 0x74, 0x73, 0x00,
550x00, 0x48, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, 0x2c, 0x00, 550x6f, 0x77, 0x3f, 0x01, 0xc2, 0x2e, 0x00, 0x48, 0x6f, 0x77,
560x61, 0x67, 0x00, 0x30, 0x6d, 0x61, 0x79, 0x96, 0x00, 0x05, 560x65, 0x76, 0x65, 0x72, 0x2c, 0x00, 0x61, 0x67, 0x00, 0x30,
570x62, 0x00, 0x83, 0x74, 0x6f, 0x00, 0x6f, 0x74, 0x68, 0x65, 570x6d, 0x61, 0x79, 0x96, 0x00, 0x05, 0x62, 0x00, 0x83, 0x74,
580x72, 0xb5, 0x00, 0x70, 0x61, 0x73, 0x00, 0x77, 0x65, 0x6c, 580x6f, 0x00, 0x6f, 0x74, 0x68, 0x65, 0x72, 0xb5, 0x00, 0x70,
590x6c, 0x08, 0x00, 0x03, 0x48, 0x00, 0x02, 0xe1, 0x00, 0x64, 590x61, 0x73, 0x00, 0x77, 0x65, 0x6c, 0x6c, 0x08, 0x00, 0x03,
600x4e, 0x6f, 0x00, 0x74, 0x77, 0x6f, 0xe4, 0x00, 0x17, 0x72, 600x48, 0x00, 0x02, 0xe1, 0x00, 0x64, 0x4e, 0x6f, 0x00, 0x74,
610x41, 0x00, 0x08, 0xa2, 0x00, 0x17, 0x2c, 0xa0, 0x00, 0x46, 610x77, 0x6f, 0xe4, 0x00, 0x17, 0x72, 0x41, 0x00, 0x08, 0xa2,
620x00, 0x6f, 0x72, 0x20, 0x9a, 0x00, 0x06, 0x27, 0x01, 0x61, 620x00, 0x17, 0x2c, 0xa0, 0x00, 0x46, 0x00, 0x6f, 0x72, 0x20,
630x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xb3, 0x01, 0x04, 0xc4, 630x9a, 0x00, 0x06, 0x27, 0x01, 0x61, 0x6e, 0x75, 0x6d, 0x62,
640x01, 0x01, 0x05, 0x01, 0x41, 0x72, 0x6f, 0x77, 0x2c, 0x3e, 640x65, 0x72, 0xb3, 0x01, 0x04, 0xc4, 0x01, 0x01, 0x05, 0x01,
650x01, 0x04, 0x11, 0x00, 0x73, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 650x41, 0x72, 0x6f, 0x77, 0x2c, 0x3e, 0x01, 0x04, 0x11, 0x00,
660x6e, 0x2c, 0x41, 0x01, 0x35, 0x73, 0x00, 0x74, 0x3d, 0x00, 660x73, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x2c, 0x41, 0x01,
670xd1, 0x73, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x72, 670x35, 0x73, 0x00, 0x74, 0x3d, 0x00, 0xd1, 0x73, 0x00, 0x67,
680x6f, 0x75, 0x6e, 0x64, 0x18, 0x00, 0x54, 0x73, 0x69, 0x64, 680x69, 0x76, 0x65, 0x6e, 0x00, 0x72, 0x6f, 0x75, 0x6e, 0x64,
690x65, 0x73, 0x07, 0x02, 0x00, 0x5f, 0x02, 0x00, 0x70, 0x00, 690x18, 0x00, 0x54, 0x73, 0x69, 0x64, 0x65, 0x73, 0x07, 0x02,
700xb4, 0x54, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 700x00, 0x5f, 0x02, 0x00, 0x70, 0x00, 0xb4, 0x54, 0x68, 0x69,
710x6c, 0x65, 0x8d, 0x01, 0x11, 0x66, 0x32, 0x00, 0x00, 0x88, 710x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x8d, 0x01,
720x01, 0x00, 0x15, 0x01, 0x22, 0x61, 0x6c, 0x4e, 0x02, 0x41, 720x11, 0x66, 0x32, 0x00, 0x00, 0x88, 0x01, 0x00, 0x15, 0x01,
730x73, 0x00, 0x6f, 0x6e, 0x3a, 0x00, 0x82, 0x49, 0x6e, 0x74, 730x22, 0x61, 0x6c, 0x4e, 0x02, 0x41, 0x73, 0x00, 0x6f, 0x6e,
740x65, 0x72, 0x6e, 0x65, 0x74, 0x89, 0x00, 0xa1, 0x77, 0x61, 740x3a, 0x00, 0x82, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
750x73, 0x00, 0x62, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x27, 0x01, 750x74, 0x89, 0x00, 0xa1, 0x77, 0x61, 0x73, 0x00, 0x62, 0x72,
760x50, 0x6d, 0x79, 0x00, 0x61, 0x74, 0xb4, 0x00, 0xf0, 0x28, 760x6f, 0x75, 0x67, 0x68, 0x27, 0x01, 0x50, 0x6d, 0x79, 0x00,
770x69, 0x6f, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x65, 0x2d, 0x6d, 770x61, 0x74, 0xb4, 0x00, 0xf0, 0x28, 0x69, 0x6f, 0x6e, 0x00,
780x61, 0x69, 0x6c, 0x2e, 0x00, 0x49, 0x00, 0x64, 0x6f, 0x6e, 780x62, 0x79, 0x00, 0x65, 0x2d, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
790x27, 0x74, 0x00, 0x6b, 0x6e, 0x6f, 0x77, 0x00, 0x77, 0x68, 790x00, 0x49, 0x00, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x00, 0x6b,
800x6f, 0x00, 0x49, 0x00, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 800x6e, 0x6f, 0x77, 0x00, 0x77, 0x68, 0x6f, 0x00, 0x49, 0x00,
810x00, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 810x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x63, 0x72, 0x65,
820x72, 0x00, 0x69, 0x6e, 0x76, 0x3a, 0x00, 0x50, 0x6e, 0x67, 820x64, 0x69, 0x74, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x69, 0x6e,
830x00, 0x69, 0x74, 0x9c, 0x00, 0x43, 0x32, 0x35, 0x2e, 0x31, 830x76, 0x3a, 0x00, 0x50, 0x6e, 0x67, 0x00, 0x69, 0x74, 0x9c,
840x1c, 0x03, 0x00, 0xef, 0x02, 0x31, 0x72, 0x6f, 0x6c, 0x25, 840x00, 0x43, 0x32, 0x35, 0x2e, 0x31, 0x2e, 0x03, 0x00, 0xef,
850x03, 0xa1, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 850x02, 0xf1, 0x03, 0x72, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x00,
860x63, 0x6b, 0x2b, 0x00, 0x93, 0x6e, 0x00, 0x61, 0x00, 0x62, 860x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69, 0x63,
870x6c, 0x61, 0x6e, 0x6b, 0xd1, 0x02, 0x43, 0x00, 0x77, 0x69, 870x6b, 0x2b, 0x00, 0x93, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x6c,
880x6c, 0xb4, 0x00, 0x04, 0xd0, 0x01, 0x21, 0x69, 0x6e, 0x52, 880x61, 0x6e, 0x6b, 0xd1, 0x02, 0x43, 0x00, 0x77, 0x69, 0x6c,
890x00, 0x7f, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x00, 0x3b, 890xb4, 0x00, 0x04, 0xd0, 0x01, 0x21, 0x69, 0x6e, 0x52, 0x00,
900x00, 0x0d, 0x30, 0x63, 0x6f, 0x6c, 0x4e, 0x03, 0x80, 0x69, 900x7f, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x00, 0x3b, 0x00,
910x74, 0x00, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x03, 0x41, 910x0d, 0x30, 0x63, 0x6f, 0x6c, 0x4e, 0x03, 0x80, 0x69, 0x74,
920x64, 0x69, 0x63, 0x61, 0x99, 0x00, 0x01, 0x90, 0x02, 0x31, 920x00, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x03, 0x41, 0x64,
930x79, 0x6f, 0x75, 0xdf, 0x01, 0x60, 0x73, 0x75, 0x72, 0x65, 930x69, 0x63, 0x61, 0x99, 0x00, 0x01, 0x90, 0x02, 0x31, 0x79,
940x00, 0x69, 0x96, 0x02, 0x00, 0xdb, 0x00, 0x02, 0x6c, 0x00, 940x6f, 0x75, 0xdf, 0x01, 0x60, 0x73, 0x75, 0x72, 0x65, 0x00,
950x34, 0x2e, 0x00, 0x43, 0x5f, 0x00, 0x21, 0x65, 0x69, 0x34, 950x69, 0x96, 0x02, 0x00, 0xdb, 0x00, 0x02, 0x6c, 0x00, 0x34,
960x02, 0x61, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x6d, 0x00, 960x2e, 0x00, 0x43, 0x5f, 0x00, 0x21, 0x65, 0x69, 0x34, 0x02,
970xaa, 0x6e, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x70, 0x69, 0x65, 970x61, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x6d, 0x00, 0xaa,
980x64, 0x71, 0x00, 0x43, 0x6c, 0x65, 0x61, 0x72, 0xf4, 0x00, 980x6e, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x70, 0x69, 0x65, 0x64,
990x21, 0x49, 0x66, 0x5f, 0x00, 0x92, 0x64, 0x72, 0x61, 0x67, 990x71, 0x00, 0x43, 0x6c, 0x65, 0x61, 0x72, 0xf4, 0x00, 0x21,
1000x00, 0x77, 0x69, 0x74, 0x68, 0xb4, 0x03, 0x00, 0xb9, 0x00, 1000x49, 0x66, 0x5f, 0x00, 0x92, 0x64, 0x72, 0x61, 0x67, 0x00,
1010x04, 0x49, 0x00, 0x70, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 1010x77, 0x69, 0x74, 0x68, 0xb4, 0x03, 0x00, 0xb9, 0x00, 0x04,
1020x61, 0x0c, 0x02, 0x00, 0xed, 0x02, 0x04, 0x02, 0x02, 0x00, 1020x49, 0x00, 0x70, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x61,
1030xac, 0x01, 0x1a, 0x79, 0xce, 0x00, 0x23, 0x69, 0x6e, 0xf2, 1030x0c, 0x02, 0x00, 0xed, 0x02, 0x04, 0x02, 0x02, 0x00, 0xac,
1040x03, 0x10, 0x67, 0x8e, 0x01, 0x00, 0x57, 0x00, 0x52, 0x63, 1040x01, 0x1a, 0x79, 0xce, 0x00, 0x23, 0x69, 0x6e, 0xf2, 0x03,
1050x6f, 0x76, 0x65, 0x72, 0x75, 0x00, 0x94, 0x62, 0x65, 0x00, 1050x10, 0x67, 0x8e, 0x01, 0x00, 0x57, 0x00, 0x52, 0x63, 0x6f,
1060x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0xe6, 0x00, 0x00, 0xce, 1060x76, 0x65, 0x72, 0x75, 0x00, 0x94, 0x62, 0x65, 0x00, 0x74,
1070x01, 0x14, 0x6e, 0xe9, 0x02, 0x03, 0x1c, 0x04, 0x05, 0x2b, 1070x75, 0x72, 0x6e, 0x65, 0x64, 0xe6, 0x00, 0x00, 0xce, 0x01,
1080x00, 0xb1, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 1080x14, 0x6e, 0xe9, 0x02, 0x03, 0x1c, 0x04, 0x05, 0x2b, 0x00,
1090x2e, 0x00, 0x28, 0x32, 0x02, 0x91, 0x69, 0x73, 0x00, 0x75, 1090xb1, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2e,
1100x73, 0x65, 0x66, 0x75, 0x6c, 0xb9, 0x01, 0x01, 0xc1, 0x00, 1100x00, 0x28, 0x32, 0x02, 0x91, 0x69, 0x73, 0x00, 0x75, 0x73,
1110x02, 0x1f, 0x01, 0x04, 0x61, 0x04, 0x12, 0x64, 0xbf, 0x02, 1110x65, 0x66, 0x75, 0x6c, 0xb9, 0x01, 0x01, 0xc1, 0x00, 0x02,
1120x03, 0xa7, 0x00, 0x54, 0x6e, 0x63, 0x65, 0x00, 0x79, 0xdc, 1120x1f, 0x01, 0x04, 0x61, 0x04, 0x12, 0x64, 0xbf, 0x02, 0x03,
1130x04, 0x01, 0x9b, 0x01, 0x51, 0x64, 0x00, 0x61, 0x6c, 0x6c, 1130xa7, 0x00, 0x54, 0x6e, 0x63, 0x65, 0x00, 0x79, 0xdc, 0x04,
1140x3f, 0x03, 0x01, 0xe2, 0x02, 0x23, 0x2e, 0x29, 0xfe, 0x04, 1140x01, 0x9b, 0x01, 0x51, 0x64, 0x00, 0x61, 0x6c, 0x6c, 0x3f,
1150x20, 0x63, 0x61, 0xe2, 0x00, 0x20, 0x73, 0x6f, 0x5f, 0x00, 1150x03, 0x01, 0xe2, 0x02, 0x23, 0x2e, 0x29, 0xfe, 0x04, 0x20,
1160x01, 0x4f, 0x00, 0xb1, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 1160x63, 0x61, 0xe2, 0x00, 0x20, 0x73, 0x6f, 0x5f, 0x00, 0x01,
1170x00, 0x6b, 0x65, 0x79, 0x73, 0x61, 0x02, 0x10, 0x6f, 0x1e, 1170x4f, 0x00, 0xb1, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00,
1180x05, 0x06, 0xd5, 0x02, 0x02, 0xc8, 0x02, 0x56, 0x50, 0x72, 1180x6b, 0x65, 0x79, 0x73, 0x61, 0x02, 0x10, 0x6f, 0x1e, 0x05,
1190x65, 0x73, 0x73, 0x81, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x32, 1190x06, 0xd5, 0x02, 0x02, 0xc8, 0x02, 0x56, 0x50, 0x72, 0x65,
1200x00, 0x11, 0x00, 0xef, 0x00, 0x8f, 0x61, 0x6e, 0x00, 0x65, 1200x73, 0x73, 0x81, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x32, 0x00,
1210x6d, 0x70, 0x74, 0x79, 0x20, 0x02, 0x06, 0x02, 0xfc, 0x00, 1210x11, 0x00, 0xef, 0x00, 0x8f, 0x61, 0x6e, 0x00, 0x65, 0x6d,
1220x18, 0x70, 0x44, 0x00, 0x20, 0x73, 0x70, 0x1f, 0x00, 0x3e, 1220x70, 0x74, 0x79, 0x20, 0x02, 0x06, 0x02, 0xfc, 0x00, 0x18,
1230x62, 0x61, 0x72, 0x43, 0x00, 0x0f, 0x28, 0x02, 0x05, 0x14, 1230x70, 0x44, 0x00, 0x20, 0x73, 0x70, 0x1f, 0x00, 0x3e, 0x62,
1240x3b, 0xf1, 0x01, 0x00, 0x79, 0x00, 0x07, 0xd8, 0x01, 0x0e, 1240x61, 0x72, 0x43, 0x00, 0x0f, 0x28, 0x02, 0x05, 0x14, 0x3b,
1250xf6, 0x01, 0x00, 0x71, 0x04, 0x20, 0x6c, 0x64, 0x6b, 0x00, 1250xf1, 0x01, 0x00, 0x79, 0x00, 0x07, 0xd8, 0x01, 0x0e, 0xf6,
1260x5e, 0x53, 0x68, 0x69, 0x66, 0x74, 0x7e, 0x00, 0x08, 0xf4, 1260x01, 0x00, 0x71, 0x04, 0x20, 0x6c, 0x64, 0x6b, 0x00, 0x5e,
1270x00, 0x08, 0x6b, 0x00, 0x08, 0x84, 0x00, 0x12, 0x73, 0x76, 1270x53, 0x68, 0x69, 0x66, 0x74, 0x7e, 0x00, 0x08, 0xf4, 0x00,
1280x00, 0x06, 0x4c, 0x00, 0x12, 0x43, 0x23, 0x03, 0x0f, 0x4e, 1280x08, 0x6b, 0x00, 0x08, 0x84, 0x00, 0x12, 0x73, 0x76, 0x00,
1290x00, 0x17, 0x01, 0x40, 0x00, 0x5b, 0x00, 0x62, 0x6f, 0x74, 1290x06, 0x4c, 0x00, 0x12, 0x43, 0x23, 0x03, 0x0f, 0x4e, 0x00,
1300x68, 0x59, 0x00, 0x00, 0x42, 0x00, 0x06, 0x01, 0x02, 0x12, 1300x17, 0x01, 0x40, 0x00, 0x5b, 0x00, 0x62, 0x6f, 0x74, 0x68,
1310x74, 0x7c, 0x05, 0x01, 0xc5, 0x05, 0x41, 0x28, 0x41, 0x6c, 1310x59, 0x00, 0x00, 0x42, 0x00, 0x06, 0x01, 0x02, 0x12, 0x74,
1320x6c, 0x54, 0x00, 0x22, 0x61, 0x63, 0x0f, 0x06, 0x83, 0x64, 1320x7c, 0x05, 0x01, 0xc5, 0x05, 0x41, 0x28, 0x41, 0x6c, 0x6c,
1330x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x30, 0x04, 0x11, 1330x54, 0x00, 0x22, 0x61, 0x63, 0x0f, 0x06, 0x83, 0x64, 0x65,
1340x63, 0xf8, 0x03, 0x32, 0x32, 0x2e, 0x31, 0xff, 0x04, 0x00, 1340x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x30, 0x04, 0x11, 0x63,
1350xc8, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 1350xf8, 0x03, 0x32, 0x32, 0x2e, 0x31, 0xff, 0x04, 0x00, 0xc8,
1360x6c, 0x65, 0x2e, 0x29, 0xd3, 0x03, 0x13, 0x32, 0xd3, 0x03, 1360x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
1370xb1, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 1370x65, 0x2e, 0x29, 0xd3, 0x03, 0x13, 0x32, 0xd3, 0x03, 0xb1,
1380x73, 0x20, 0x88, 0x04, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 1380x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
1390x00, 0x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x51, 0x00, 0x66, 1390x20, 0x88, 0x04, 0x46, 0x65, 0x73, 0x65, 0x00, 0x14, 0x00,
1400x72, 0x6f, 0x6d, 0x79, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 1400x02, 0x41, 0x00, 0x04, 0x3c, 0x00, 0x51, 0x00, 0x66, 0x72,
1410x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 1410x6f, 0x6d, 0x79, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 0x74,
1420x6f, 0x00, 0x03, 0x98, 0x04, 0xb0, 0x60, 0x54, 0x79, 0x70, 1420x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x6f,
1430x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xab, 0x00, 0x91, 1430x00, 0x03, 0x98, 0x04, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65,
1440x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x3c, 1440x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xab, 0x00, 0x91, 0x57,
1450x03, 0x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x04, 0x07, 0x01, 1450x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x3c, 0x03,
1460x58, 0x07, 0x24, 0x69, 0x6e, 0xe1, 0x00, 0x00, 0x2b, 0x00, 1460x51, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x04, 0x07, 0x01, 0x58,
1470xc4, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 1470x07, 0x24, 0x69, 0x6e, 0xe1, 0x00, 0x00, 0x2b, 0x00, 0xc4,
1480x79, 0x00, 0x00, 0x47, 0x01, 0x02, 0x4b, 0x05, 0x16, 0x64, 1480x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79,
1490x1a, 0x00, 0x04, 0x38, 0x05, 0x83, 0x65, 0x6e, 0x65, 0x72, 1490x00, 0x00, 0x47, 0x01, 0x02, 0x4b, 0x05, 0x16, 0x64, 0x1a,
1500x61, 0x74, 0x65, 0x64, 0x35, 0x05, 0x57, 0x2e, 0x00, 0x4d, 1500x00, 0x04, 0x38, 0x05, 0x83, 0x65, 0x6e, 0x65, 0x72, 0x61,
1510x6f, 0x72, 0x29, 0x00, 0x03, 0x17, 0x00, 0x70, 0x73, 0x00, 1510x74, 0x65, 0x64, 0x35, 0x05, 0x57, 0x2e, 0x00, 0x4d, 0x6f,
1520x72, 0x65, 0x71, 0x75, 0x69, 0x2d, 0x07, 0x00, 0x1f, 0x00, 1520x72, 0x29, 0x00, 0x03, 0x17, 0x00, 0x70, 0x73, 0x00, 0x72,
1530xc2, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x00, 0x64, 1530x65, 0x71, 0x75, 0x69, 0x2d, 0x07, 0x00, 0x1f, 0x00, 0xc2,
1540x65, 0x64, 0x75, 0x3a, 0x01, 0x02, 0x9a, 0x06, 0x21, 0x61, 1540x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x00, 0x64, 0x65,
1550x74, 0xab, 0x01, 0x20, 0x65, 0x6e, 0xa5, 0x06, 0x15, 0x6e, 1550x64, 0x75, 0x3a, 0x01, 0x02, 0x9a, 0x06, 0x21, 0x61, 0x74,
1560xa4, 0x07, 0x06, 0xf8, 0x00, 0x07, 0x81, 0x00, 0x55, 0x6c, 1560xab, 0x01, 0x20, 0x65, 0x6e, 0xa5, 0x06, 0x15, 0x6e, 0xa4,
1570x65, 0x76, 0x65, 0x6c, 0x58, 0x00, 0xb0, 0x73, 0x00, 0x67, 1570x07, 0x06, 0xf8, 0x00, 0x07, 0x81, 0x00, 0x55, 0x6c, 0x65,
1580x75, 0x65, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x6b, 0x06, 0x04, 1580x76, 0x65, 0x6c, 0x58, 0x00, 0xb0, 0x73, 0x00, 0x67, 0x75,
1590xe0, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x6b, 1590x65, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x6b, 0x06, 0x04, 0xe0,
1600x69, 0x6e, 0x67, 0x2e, 0x00, 1600x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69,
1610x6e, 0x67, 0x2e, 0x00,
161}; 162};
162 163
163const unsigned short help_text_len = 2140; 164const unsigned short help_text_len = 2158;
164const unsigned short help_text_words = 400; 165const unsigned short help_text_words = 401;
166const bool help_valid = true;
165const char quick_help_text[] = "Place a tent next to each tree."; 167const char quick_help_text[] = "Place a tent next to each tree.";
diff --git a/apps/plugins/puzzles/help/towers.c b/apps/plugins/puzzles/help/towers.c
index 7b27064659..ff5a0a495a 100644
--- a/apps/plugins/puzzles/help/towers.c
+++ b/apps/plugins/puzzles/help/towers.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,239 +6,262 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 216, TEXT_CENTER | C_RED }, 9 { 217, TEXT_CENTER | C_RED },
10 { 291, TEXT_UNDERLINE }, 10 { 292, TEXT_UNDERLINE },
11 { 588, TEXT_CENTER | C_RED }, 11 { 589, TEXT_CENTER | C_RED },
12 { 605, TEXT_UNDERLINE }, 12 { 606, TEXT_UNDERLINE },
13 { 637, TEXT_UNDERLINE }, 13 { 638, TEXT_UNDERLINE },
14 { 677, TEXT_CENTER | C_RED },
14 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
15}; 16};
16 17
17/* orig 3541 comp 2203 ratio 0.622141 level 10 saved 1338 */ 18/* orig 3906 comp 2412 ratio 0.617512 level 10 saved 1494 */
18const char help_text[] = { 19const char help_text[] = {
190xf4, 0x26, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
200x33, 0x31, 0x3a, 0x20, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 210x33, 0x31, 0x3a, 0x20, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73,
210x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 220x20, 0x00, 0x2d, 0x01, 0x00, 0xf4, 0x13, 0x00, 0x00, 0x00,
220x76, 0x65, 0x00, 0x61, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 230x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61,
230x65, 0x00, 0x67, 0x72, 0x69, 0x64, 0x2e, 0x00, 0x4f, 0x6e, 240x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x67, 0x72,
240x00, 0x65, 0x61, 0x63, 0x68, 0x15, 0x00, 0x52, 0x6f, 0x66, 250x69, 0x64, 0x2e, 0x00, 0x4f, 0x6e, 0x00, 0x65, 0x61, 0x63,
250x00, 0x74, 0x68, 0x1c, 0x00, 0xf0, 0x03, 0x00, 0x79, 0x6f, 260x68, 0x15, 0x00, 0x52, 0x6f, 0x66, 0x00, 0x74, 0x68, 0x1c,
260x75, 0x00, 0x63, 0x61, 0x6e, 0x00, 0x62, 0x75, 0x69, 0x6c, 270x00, 0xf0, 0x03, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x63, 0x61,
270x64, 0x00, 0x61, 0x00, 0x74, 0x4d, 0x00, 0xf1, 0x14, 0x2c, 280x6e, 0x00, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x00, 0x61, 0x00,
280x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x69, 0x74, 0x73, 0x00, 290x74, 0x60, 0x00, 0xf1, 0x14, 0x2c, 0x00, 0x77, 0x69, 0x74,
290x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x72, 0x61, 0x6e, 300x68, 0x00, 0x69, 0x74, 0x73, 0x00, 0x68, 0x65, 0x69, 0x67,
300x67, 0x69, 0x6e, 0x67, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 310x68, 0x74, 0x00, 0x72, 0x61, 0x6e, 0x67, 0x69, 0x6e, 0x67,
310x31, 0x00, 0x74, 0x6f, 0x42, 0x00, 0x39, 0x73, 0x69, 0x7a, 320x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x31, 0x00, 0x74, 0x6f,
320x4e, 0x00, 0x81, 0x2e, 0x00, 0x41, 0x72, 0x6f, 0x75, 0x6e, 330x42, 0x00, 0x39, 0x73, 0x69, 0x7a, 0x4e, 0x00, 0x81, 0x2e,
330x64, 0x11, 0x00, 0x3a, 0x65, 0x64, 0x67, 0x6b, 0x00, 0x00, 340x00, 0x41, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x11, 0x00, 0x3a,
340x7b, 0x00, 0xf2, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x00, 0x6e, 350x65, 0x64, 0x67, 0x6b, 0x00, 0x00, 0x7b, 0x00, 0xf2, 0x04,
350x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x00, 0x63, 0x6c, 0x75, 360x73, 0x6f, 0x6d, 0x65, 0x00, 0x6e, 0x75, 0x6d, 0x65, 0x72,
360x65, 0x73, 0x2e, 0xb8, 0x00, 0x90, 0x72, 0x00, 0x74, 0x61, 370x69, 0x63, 0x00, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0xb8,
370x73, 0x6b, 0x00, 0x69, 0x73, 0x5c, 0x00, 0x09, 0x8d, 0x00, 380x00, 0x90, 0x72, 0x00, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x69,
380x93, 0x00, 0x6f, 0x6e, 0x00, 0x65, 0x76, 0x65, 0x72, 0x79, 390x73, 0x5c, 0x00, 0x09, 0x8d, 0x00, 0x93, 0x00, 0x6f, 0x6e,
390xbf, 0x00, 0xf0, 0x0c, 0x2c, 0x00, 0x69, 0x6e, 0x00, 0x73, 400x00, 0x65, 0x76, 0x65, 0x72, 0x79, 0xbf, 0x00, 0xf0, 0x0c,
400x75, 0x63, 0x68, 0x00, 0x61, 0x00, 0x77, 0x61, 0x79, 0x00, 410x2c, 0x00, 0x69, 0x6e, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00,
410x74, 0x68, 0x61, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 420x61, 0x00, 0x77, 0x61, 0x79, 0x00, 0x74, 0x68, 0x61, 0x74,
420x45, 0xe4, 0x00, 0xc3, 0x72, 0x6f, 0x77, 0x00, 0x63, 0x6f, 430x3a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x45, 0xe4, 0x00, 0xc3,
430x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x38, 0x00, 0x84, 0x70, 440x72, 0x6f, 0x77, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
440x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0xcd, 0x00, 0x25, 450x6e, 0x73, 0x38, 0x00, 0x84, 0x70, 0x6f, 0x73, 0x73, 0x69,
450x6f, 0x66, 0x5a, 0x00, 0x26, 0x63, 0x65, 0x3a, 0x00, 0x6f, 460x62, 0x6c, 0x65, 0xcd, 0x00, 0x25, 0x6f, 0x66, 0x5a, 0x00,
460x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x3d, 0x00, 0x24, 0x08, 470x26, 0x63, 0x65, 0x3a, 0x00, 0x6f, 0x63, 0x6f, 0x6c, 0x75,
470xd4, 0x00, 0xa1, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 480x6d, 0x6e, 0x3d, 0x00, 0x24, 0x08, 0xd4, 0x00, 0xa1, 0x00,
480x62, 0x65, 0x73, 0xfd, 0x00, 0x65, 0x6e, 0x75, 0x6d, 0x62, 490x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x73, 0xfd,
490x65, 0x72, 0x39, 0x00, 0x11, 0x73, 0xb2, 0x00, 0x02, 0x75, 500x00, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x39, 0x00,
500x01, 0x91, 0x65, 0x00, 0x73, 0x65, 0x65, 0x6e, 0x00, 0x69, 510x11, 0x73, 0xb2, 0x00, 0x02, 0x75, 0x01, 0x91, 0x65, 0x00,
510x66, 0x88, 0x01, 0x74, 0x6c, 0x6f, 0x6f, 0x6b, 0x00, 0x69, 520x73, 0x65, 0x65, 0x6e, 0x00, 0x69, 0x66, 0x88, 0x01, 0x74,
520x6e, 0x5d, 0x01, 0x02, 0xad, 0x01, 0x01, 0x72, 0x01, 0x01, 530x6c, 0x6f, 0x6f, 0x6b, 0x00, 0x69, 0x6e, 0x5d, 0x01, 0x02,
530x32, 0x00, 0xf0, 0x01, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 540xad, 0x01, 0x01, 0x72, 0x01, 0x01, 0x32, 0x00, 0xf0, 0x01,
540x69, 0x6f, 0x6e, 0x2c, 0x00, 0x61, 0x73, 0x73, 0x75, 0x6d, 550x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c,
550x90, 0x01, 0x01, 0x19, 0x00, 0x74, 0x73, 0x68, 0x6f, 0x72, 560x00, 0x61, 0x73, 0x73, 0x75, 0x6d, 0x90, 0x01, 0x01, 0x19,
560x74, 0x65, 0x72, 0x5f, 0x00, 0x00, 0x36, 0x00, 0x50, 0x68, 570x00, 0x74, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x5f,
570x69, 0x64, 0x64, 0x65, 0x61, 0x00, 0x20, 0x68, 0x69, 0x8f, 580x00, 0x00, 0x36, 0x00, 0x50, 0x68, 0x69, 0x64, 0x64, 0x65,
580x01, 0x31, 0x61, 0x6c, 0x6c, 0xb2, 0x00, 0x00, 0x6f, 0x01, 590x61, 0x00, 0x20, 0x68, 0x69, 0x8f, 0x01, 0x31, 0x61, 0x6c,
590xa2, 0x46, 0x6f, 0x72, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 600x6c, 0xb2, 0x00, 0x00, 0x6f, 0x01, 0xa2, 0x46, 0x6f, 0x72,
600x6c, 0x4b, 0x01, 0x51, 0x61, 0x00, 0x35, 0x78, 0x35, 0xa2, 610x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x4b, 0x01, 0x51,
610x01, 0x32, 0x2c, 0x00, 0x61, 0xc0, 0x00, 0xf3, 0x04, 0x6d, 620x61, 0x00, 0x35, 0x78, 0x35, 0xa2, 0x01, 0x32, 0x2c, 0x00,
620x61, 0x72, 0x6b, 0x65, 0x64, 0x00, 0x60, 0x35, 0x27, 0x00, 630x61, 0xc0, 0x00, 0xf3, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x65,
630x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0xb6, 0x00, 640x64, 0x00, 0x60, 0x35, 0x27, 0x00, 0x69, 0x6e, 0x64, 0x69,
640x00, 0x99, 0x00, 0x43, 0x66, 0x69, 0x76, 0x65, 0x04, 0x01, 650x63, 0x61, 0x74, 0x65, 0xb6, 0x00, 0x00, 0x99, 0x00, 0x43,
650x02, 0x14, 0x01, 0xd0, 0x73, 0x00, 0x6d, 0x75, 0x73, 0x74, 660x66, 0x69, 0x76, 0x65, 0x04, 0x01, 0x02, 0x14, 0x01, 0xd0,
660x00, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x53, 0x00, 0x70, 670x73, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x61, 0x70, 0x70,
670x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0xa1, 0x00, 0xf1, 680x65, 0x61, 0x72, 0x53, 0x00, 0x70, 0x69, 0x6e, 0x63, 0x72,
680x01, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x00, 0x28, 0x6f, 0x74, 690x65, 0x61, 0x73, 0xa1, 0x00, 0xf1, 0x01, 0x6f, 0x72, 0x64,
690x68, 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0xe9, 0x00, 0x90, 700x65, 0x72, 0x00, 0x28, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x77,
700x77, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x6e, 0x6f, 0x74, 0x02, 710x69, 0x73, 0x65, 0xe9, 0x00, 0x90, 0x77, 0x6f, 0x75, 0x6c,
710x01, 0x30, 0x61, 0x62, 0x6c, 0x4f, 0x00, 0x00, 0x0a, 0x01, 720x64, 0x00, 0x6e, 0x6f, 0x74, 0x02, 0x01, 0x30, 0x61, 0x62,
720x47, 0x00, 0x61, 0x6c, 0x6c, 0x5f, 0x00, 0x70, 0x73, 0x29, 730x6c, 0x4f, 0x00, 0x00, 0x0a, 0x01, 0x47, 0x00, 0x61, 0x6c,
730x2c, 0x00, 0x77, 0x68, 0x65, 0x4b, 0x00, 0x0c, 0x9a, 0x00, 740x6c, 0x5f, 0x00, 0x70, 0x73, 0x29, 0x2c, 0x00, 0x77, 0x68,
740x1f, 0x31, 0x9a, 0x00, 0x02, 0x01, 0xe8, 0x00, 0x23, 0x73, 750x65, 0x4b, 0x00, 0x0c, 0x9a, 0x00, 0x1f, 0x31, 0x9a, 0x00,
750x74, 0x9d, 0x00, 0x10, 0x28, 0x13, 0x00, 0x25, 0x6f, 0x6e, 760x02, 0x01, 0xe8, 0x00, 0x23, 0x73, 0x74, 0x9d, 0x00, 0x10,
760x35, 0x00, 0x22, 0x35, 0x29, 0xa8, 0x00, 0x30, 0x63, 0x6f, 770x28, 0x13, 0x00, 0x25, 0x6f, 0x6e, 0x35, 0x00, 0x22, 0x35,
770x6d, 0xc5, 0x00, 0x30, 0x72, 0x73, 0x74, 0x7c, 0x02, 0x51, 780x29, 0xa8, 0x00, 0x30, 0x63, 0x6f, 0x6d, 0xc5, 0x00, 0x30,
780x49, 0x6e, 0x00, 0x68, 0x61, 0xa5, 0x00, 0xf2, 0x03, 0x6f, 790x72, 0x73, 0x74, 0x7c, 0x02, 0x51, 0x49, 0x6e, 0x00, 0x68,
790x72, 0x00, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x00, 0x70, 800x61, 0xa5, 0x00, 0xf2, 0x03, 0x6f, 0x72, 0x00, 0x6c, 0x61,
800x75, 0x7a, 0x7a, 0x6c, 0x65, 0x73, 0x2c, 0xaf, 0x02, 0x03, 810x72, 0x67, 0x65, 0x72, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c,
810x57, 0x01, 0x41, 0x77, 0x69, 0x6c, 0x6c, 0xb2, 0x01, 0xc1, 820x65, 0x73, 0x2c, 0xaf, 0x02, 0x03, 0x57, 0x01, 0x41, 0x77,
820x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x00, 0x66, 830x69, 0x6c, 0x6c, 0xb2, 0x01, 0xc1, 0x70, 0x65, 0x63, 0x69,
830x6f, 0x72, 0xcf, 0x00, 0x92, 0x61, 0x73, 0x00, 0x77, 0x65, 840x66, 0x69, 0x65, 0x64, 0x00, 0x66, 0x6f, 0x72, 0xcf, 0x00,
840x6c, 0x6c, 0x00, 0x61, 0xf0, 0x01, 0x01, 0xd7, 0x02, 0x1a, 850x92, 0x61, 0x73, 0x00, 0x77, 0x65, 0x6c, 0x6c, 0x00, 0x61,
850x00, 0x09, 0x03, 0x52, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x4f, 860xf0, 0x01, 0x01, 0xd7, 0x02, 0x1a, 0x00, 0x09, 0x03, 0x52,
860x00, 0x01, 0x18, 0x03, 0x02, 0x24, 0x00, 0x30, 0x6d, 0x61, 870x2c, 0x00, 0x61, 0x6e, 0x64, 0x4f, 0x00, 0x01, 0x18, 0x03,
870x79, 0x52, 0x00, 0x30, 0x6d, 0x69, 0x73, 0x2c, 0x01, 0x00, 880x02, 0x24, 0x00, 0x30, 0x6d, 0x61, 0x79, 0x52, 0x00, 0x30,
880x8e, 0x00, 0x43, 0x54, 0x68, 0x69, 0x73, 0x7f, 0x00, 0x03, 890x6d, 0x69, 0x73, 0x2c, 0x01, 0x00, 0x8e, 0x00, 0x43, 0x54,
890x50, 0x01, 0x10, 0x73, 0x00, 0x03, 0x00, 0x47, 0x00, 0x60, 900x68, 0x69, 0x73, 0x7f, 0x00, 0x03, 0x50, 0x01, 0x10, 0x73,
900x77, 0x65, 0x62, 0x00, 0x75, 0x6e, 0xa9, 0x00, 0xb0, 0x76, 910x00, 0x03, 0x00, 0x47, 0x00, 0x60, 0x77, 0x65, 0x62, 0x00,
910x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x00, 0x6e, 0x61, 0x6d, 920x75, 0x6e, 0xa9, 0x00, 0xb0, 0x76, 0x61, 0x72, 0x69, 0x6f,
920xa5, 0x00, 0xf2, 0x21, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 930x75, 0x73, 0x00, 0x6e, 0x61, 0x6d, 0xa5, 0x00, 0xf2, 0x21,
930x75, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x00, 0x60, 0x53, 0x6b, 940x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72,
940x79, 0x73, 0x63, 0x72, 0x61, 0x70, 0x65, 0x72, 0x73, 0x27, 950x6c, 0x79, 0x00, 0x60, 0x53, 0x6b, 0x79, 0x73, 0x63, 0x72,
950x2c, 0x00, 0x62, 0x75, 0x74, 0x00, 0x49, 0x00, 0x64, 0x6f, 960x61, 0x70, 0x65, 0x72, 0x73, 0x27, 0x2c, 0x00, 0x62, 0x75,
960x6e, 0x27, 0x74, 0x00, 0x6b, 0x6e, 0x6f, 0x77, 0x00, 0x77, 970x74, 0x00, 0x49, 0x00, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x00,
970x68, 0x6f, 0xfc, 0x00, 0xb1, 0x00, 0x69, 0x6e, 0x76, 0x65, 980x6b, 0x6e, 0x6f, 0x77, 0x00, 0x77, 0x68, 0x6f, 0xfc, 0x00,
980x6e, 0x74, 0x65, 0x64, 0x00, 0x69, 0x08, 0x01, 0x44, 0x33, 990xb1, 0x00, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x65, 0x64,
990x31, 0x2e, 0x31, 0x4b, 0x04, 0x00, 0x03, 0x03, 0x31, 0x72, 1000x00, 0x69, 0x08, 0x01, 0x44, 0x33, 0x31, 0x2e, 0x31, 0x5e,
1000x6f, 0x6c, 0x54, 0x04, 0x12, 0x54, 0xfe, 0x00, 0x40, 0x73, 1010x04, 0x00, 0x03, 0x03, 0x50, 0x72, 0x6f, 0x6c, 0x73, 0x20,
1010x68, 0x61, 0x72, 0xb2, 0x00, 0x00, 0x78, 0x03, 0x21, 0x6f, 1020x92, 0x00, 0x02, 0xfe, 0x00, 0x40, 0x73, 0x68, 0x61, 0x72,
1020x66, 0x1b, 0x04, 0x03, 0x26, 0x00, 0x72, 0x00, 0x73, 0x79, 1030xb2, 0x00, 0x00, 0x78, 0x03, 0x21, 0x6f, 0x66, 0x1b, 0x04,
1030x73, 0x74, 0x65, 0x6d, 0x33, 0x04, 0xd1, 0x53, 0x6f, 0x6c, 1040x03, 0x26, 0x00, 0x72, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65,
1040x6f, 0x2c, 0x00, 0x55, 0x6e, 0x65, 0x71, 0x75, 0x61, 0x6c, 1050x6d, 0x33, 0x04, 0xd1, 0x53, 0x6f, 0x6c, 0x6f, 0x2c, 0x00,
1050xf4, 0x00, 0x51, 0x4b, 0x65, 0x65, 0x6e, 0x2e, 0x48, 0x00, 1060x55, 0x6e, 0x65, 0x71, 0x75, 0x61, 0x6c, 0xf4, 0x00, 0x51,
1060x53, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x50, 0x00, 0xe1, 0x2c, 1070x4b, 0x65, 0x65, 0x6e, 0x2e, 0x48, 0x00, 0x53, 0x00, 0x70,
1070x00, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 1080x6c, 0x61, 0x79, 0x50, 0x00, 0xe1, 0x2c, 0x00, 0x73, 0x69,
1080x69, 0x63, 0x6b, 0xe0, 0x00, 0x51, 0x6d, 0x6f, 0x75, 0x73, 1090x6d, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b,
1090x65, 0x91, 0x02, 0x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 0x70, 1100xe0, 0x00, 0x51, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x91, 0x02,
1100x74, 0x79, 0x03, 0x03, 0x12, 0x61, 0x49, 0x01, 0x50, 0x6e, 1110x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x03,
1110x00, 0x74, 0x79, 0x70, 0xdc, 0x04, 0x54, 0x64, 0x69, 0x67, 1120x03, 0x12, 0x61, 0x49, 0x01, 0x50, 0x6e, 0x00, 0x74, 0x79,
1120x69, 0x74, 0x17, 0x01, 0x80, 0x6b, 0x65, 0x79, 0x62, 0x6f, 1130x70, 0xdc, 0x04, 0x54, 0x64, 0x69, 0x67, 0x69, 0x74, 0x17,
1130x61, 0x72, 0x64, 0x43, 0x02, 0x10, 0x66, 0xa3, 0x01, 0x02, 1140x01, 0x80, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64,
1140x13, 0x03, 0x02, 0x3a, 0x00, 0x01, 0x94, 0x00, 0x05, 0x48, 1150x43, 0x02, 0x10, 0x66, 0xa3, 0x01, 0x02, 0x13, 0x03, 0x02,
1150x04, 0x03, 0x8c, 0x04, 0x43, 0x69, 0x76, 0x65, 0x6e, 0xba, 1160x3a, 0x00, 0x01, 0x94, 0x00, 0x05, 0x48, 0x04, 0x03, 0x8c,
1160x02, 0x32, 0x2e, 0x00, 0x49, 0x7b, 0x03, 0x30, 0x6d, 0x61, 1170x04, 0x43, 0x69, 0x76, 0x65, 0x6e, 0xba, 0x02, 0x32, 0x2e,
1170x6b, 0x5a, 0x00, 0x8e, 0x6d, 0x69, 0x73, 0x74, 0x61, 0x6b, 1180x00, 0x49, 0x7b, 0x03, 0x30, 0x6d, 0x61, 0x6b, 0x5a, 0x00,
1180x65, 0x2c, 0x97, 0x00, 0x02, 0x6d, 0x00, 0x50, 0x69, 0x6e, 1190x8e, 0x6d, 0x69, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0x97,
1190x63, 0x6f, 0x72, 0x86, 0x03, 0x08, 0x9b, 0x00, 0xa1, 0x70, 1200x00, 0x02, 0x6d, 0x00, 0x50, 0x69, 0x6e, 0x63, 0x6f, 0x72,
1200x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x61, 0x63, 0xc8, 1210x86, 0x03, 0x08, 0x9b, 0x00, 0xa1, 0x70, 0x72, 0x65, 0x73,
1210x02, 0x21, 0x63, 0x6c, 0x06, 0x03, 0xc0, 0x74, 0x00, 0x61, 1220x73, 0x00, 0x53, 0x70, 0x61, 0x63, 0xc8, 0x02, 0x21, 0x63,
1220x67, 0x61, 0x69, 0x6e, 0x00, 0x28, 0x6f, 0x72, 0x00, 0x42, 1230x6c, 0x06, 0x03, 0xc0, 0x74, 0x00, 0x61, 0x67, 0x61, 0x69,
1230x00, 0x00, 0x3f, 0x00, 0xd1, 0x55, 0x6e, 0x64, 0x6f, 0x00, 1240x6e, 0x00, 0x28, 0x6f, 0x72, 0x00, 0x42, 0x00, 0x00, 0x3f,
1240x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x29, 0x7d, 0x02, 1250x00, 0xd1, 0x55, 0x6e, 0x64, 0x6f, 0x00, 0x66, 0x65, 0x61,
1250x02, 0x7e, 0x00, 0x10, 0x72, 0x8c, 0x00, 0x12, 0x2d, 0x74, 1260x74, 0x75, 0x72, 0x65, 0x29, 0x7d, 0x02, 0x02, 0x7e, 0x00,
1260x00, 0x01, 0x92, 0x03, 0x0f, 0xf9, 0x00, 0x04, 0x02, 0x4a, 1270x10, 0x72, 0x8c, 0x00, 0x12, 0x2d, 0x74, 0x00, 0x01, 0x92,
1270x04, 0x12, 0x2c, 0xe3, 0x00, 0x03, 0x57, 0x04, 0x04, 0x97, 1280x03, 0x0f, 0xf9, 0x00, 0x04, 0x02, 0x4a, 0x04, 0x12, 0x2c,
1280x02, 0x00, 0xcc, 0x01, 0x10, 0x72, 0xce, 0x01, 0x18, 0x6e, 1290xe3, 0x00, 0x03, 0x57, 0x04, 0x04, 0x97, 0x02, 0x00, 0xcc,
1290x3e, 0x04, 0x01, 0x3a, 0x03, 0x71, 0x60, 0x70, 0x65, 0x6e, 1300x01, 0x10, 0x72, 0xce, 0x01, 0x18, 0x6e, 0x3e, 0x04, 0x01,
1300x63, 0x69, 0x6c, 0x08, 0x03, 0x43, 0x27, 0x2e, 0x00, 0x59, 1310x3a, 0x03, 0x71, 0x60, 0x70, 0x65, 0x6e, 0x63, 0x69, 0x6c,
1310xf3, 0x05, 0x01, 0x2a, 0x06, 0x07, 0x1b, 0x00, 0x11, 0x73, 1320x08, 0x03, 0x43, 0x27, 0x2e, 0x00, 0x59, 0xf3, 0x05, 0x01,
1320xd1, 0x02, 0x74, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 1330x2a, 0x06, 0x07, 0x1b, 0x00, 0x11, 0x73, 0xd1, 0x02, 0x74,
1330xb7, 0x04, 0x15, 0x73, 0x51, 0x00, 0x24, 0x61, 0x6d, 0x56, 1340x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0xb7, 0x04, 0x15,
1340x00, 0x34, 0x2e, 0x00, 0x41, 0x60, 0x00, 0x03, 0x2d, 0x05, 1350x73, 0x51, 0x00, 0x24, 0x61, 0x6d, 0x56, 0x00, 0x34, 0x2e,
1350x00, 0xea, 0x03, 0x04, 0x67, 0x01, 0x30, 0x63, 0x61, 0x6e, 1360x00, 0x41, 0x60, 0x00, 0x03, 0x2d, 0x05, 0x00, 0xea, 0x03,
1360xda, 0x03, 0x44, 0x61, 0x6c, 0x73, 0x6f, 0x1f, 0x00, 0x09, 1370x04, 0x67, 0x01, 0x30, 0x63, 0x61, 0x6e, 0xda, 0x03, 0x44,
1370x66, 0x00, 0x02, 0xe7, 0x02, 0x30, 0x65, 0x00, 0x67, 0x51, 1380x61, 0x6c, 0x73, 0x6f, 0x1f, 0x00, 0x09, 0x66, 0x00, 0x02,
1380x00, 0xd0, 0x70, 0x61, 0x79, 0x73, 0x00, 0x6e, 0x6f, 0x00, 1390xe7, 0x02, 0x30, 0x65, 0x00, 0x67, 0x51, 0x00, 0xd0, 0x70,
1390x61, 0x74, 0x74, 0x65, 0x6e, 0xdc, 0x04, 0x00, 0x42, 0x01, 1400x61, 0x79, 0x73, 0x00, 0x6e, 0x6f, 0x00, 0x61, 0x74, 0x74,
1400x08, 0x2e, 0x00, 0x00, 0x84, 0x03, 0x00, 0xaf, 0x04, 0x60, 1410x65, 0x6e, 0xdc, 0x04, 0x00, 0x42, 0x01, 0x08, 0x2e, 0x00,
1410x63, 0x74, 0x6c, 0x79, 0x00, 0x77, 0xf8, 0x00, 0x00, 0x31, 1420x00, 0x84, 0x03, 0x00, 0xaf, 0x04, 0x60, 0x63, 0x74, 0x6c,
1420x01, 0x03, 0x51, 0x01, 0x11, 0x6d, 0xb2, 0x00, 0x50, 0x69, 1430x79, 0x00, 0x77, 0xf8, 0x00, 0x00, 0x31, 0x01, 0x03, 0x51,
1430x73, 0x00, 0x75, 0x70, 0x38, 0x00, 0x45, 0x79, 0x6f, 0x75, 1440x01, 0x11, 0x6d, 0xb2, 0x00, 0x50, 0x69, 0x73, 0x00, 0x75,
1440x3a, 0xd1, 0x06, 0x05, 0x23, 0x00, 0x95, 0x61, 0x73, 0x00, 1450x70, 0x38, 0x00, 0x45, 0x79, 0x6f, 0x75, 0x3a, 0xd1, 0x06,
1450x72, 0x65, 0x6d, 0x69, 0x6e, 0x64, 0x7b, 0x05, 0x17, 0x61, 1460x05, 0x23, 0x00, 0x95, 0x61, 0x73, 0x00, 0x72, 0x65, 0x6d,
1460x33, 0x03, 0x04, 0xc9, 0x00, 0x42, 0x6e, 0x65, 0x65, 0x64, 1470x69, 0x6e, 0x64, 0x7b, 0x05, 0x17, 0x61, 0x33, 0x03, 0x04,
1470x7c, 0x06, 0x50, 0x65, 0x00, 0x72, 0x65, 0x2d, 0x21, 0x05, 1480xc9, 0x00, 0x42, 0x6e, 0x65, 0x65, 0x64, 0x7c, 0x06, 0x50,
1480x42, 0x69, 0x6e, 0x65, 0x64, 0xe6, 0x05, 0x00, 0x54, 0x00, 1490x65, 0x00, 0x72, 0x65, 0x2d, 0x21, 0x05, 0x42, 0x69, 0x6e,
1490x01, 0x3e, 0x03, 0x30, 0x6d, 0x6f, 0x72, 0xb1, 0x04, 0x2b, 1500x65, 0x64, 0xe6, 0x05, 0x00, 0x54, 0x00, 0x01, 0x3e, 0x03,
1500x6f, 0x75, 0x45, 0x00, 0x04, 0x99, 0x01, 0x2f, 0x6f, 0x72, 1510x30, 0x6d, 0x6f, 0x72, 0xb1, 0x04, 0x2b, 0x6f, 0x75, 0x45,
1510x80, 0x00, 0x02, 0x54, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x80, 1520x00, 0x04, 0x99, 0x01, 0x2f, 0x6f, 0x72, 0x80, 0x00, 0x02,
1520x02, 0x05, 0x51, 0x06, 0x07, 0x61, 0x01, 0x13, 0x61, 0x96, 1530x54, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x80, 0x02, 0x05, 0x51,
1530x02, 0x04, 0xe2, 0x06, 0x20, 0x6f, 0x72, 0x06, 0x03, 0x20, 1540x06, 0x07, 0x61, 0x01, 0x13, 0x61, 0x96, 0x02, 0x04, 0xe2,
1540x74, 0x68, 0x58, 0x01, 0x23, 0x65, 0x6c, 0x36, 0x05, 0x93, 1550x06, 0x20, 0x6f, 0x72, 0x06, 0x03, 0x20, 0x74, 0x68, 0x58,
1550x66, 0x65, 0x65, 0x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0x4f, 1560x01, 0x23, 0x65, 0x6c, 0x36, 0x05, 0x93, 0x66, 0x65, 0x65,
1560x03, 0x41, 0x65, 0x72, 0x61, 0x73, 0xeb, 0x07, 0x49, 0x69, 1570x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0x4f, 0x03, 0x41, 0x65,
1570x6e, 0x67, 0x6c, 0xca, 0x01, 0x1c, 0x2c, 0x52, 0x02, 0x08, 1580x72, 0x61, 0x73, 0xeb, 0x07, 0x49, 0x69, 0x6e, 0x67, 0x6c,
1580x12, 0x02, 0x22, 0x6e, 0x64, 0x4f, 0x02, 0x05, 0xd5, 0x01, 1590xca, 0x01, 0x1c, 0x2c, 0x52, 0x02, 0x08, 0x12, 0x02, 0x22,
1590x03, 0x49, 0x02, 0x01, 0xaf, 0x02, 0x00, 0x5b, 0x00, 0x3a, 1600x6e, 0x64, 0x4f, 0x02, 0x05, 0xd5, 0x01, 0x03, 0x49, 0x02,
1600x41, 0x6c, 0x6c, 0x17, 0x02, 0x09, 0x93, 0x02, 0x22, 0x72, 1610x01, 0xaf, 0x02, 0x00, 0x5b, 0x00, 0x3a, 0x41, 0x6c, 0x6c,
1610x65, 0x79, 0x00, 0x10, 0x64, 0x9b, 0x05, 0x12, 0x6e, 0xb8, 1620x17, 0x02, 0x09, 0x93, 0x02, 0x22, 0x72, 0x65, 0x79, 0x00,
1620x06, 0x24, 0x65, 0x66, 0x6c, 0x00, 0x05, 0x5e, 0x00, 0x18, 1630x10, 0x64, 0x9b, 0x05, 0x12, 0x6e, 0xb8, 0x06, 0x24, 0x65,
1630x61, 0x14, 0x01, 0x0f, 0x2a, 0x00, 0x05, 0x02, 0x3a, 0x03, 1640x66, 0x6c, 0x00, 0x05, 0x5e, 0x00, 0x18, 0x61, 0x14, 0x01,
1640x10, 0x73, 0x3a, 0x03, 0x36, 0x2e, 0x00, 0x52, 0xb3, 0x00, 1650x0f, 0x2a, 0x00, 0x05, 0x02, 0x3a, 0x03, 0x10, 0x73, 0x3a,
1650x01, 0x4f, 0x02, 0x04, 0x20, 0x00, 0x00, 0x0d, 0x00, 0x01, 1660x03, 0x36, 0x2e, 0x00, 0x52, 0xb3, 0x00, 0x01, 0x4f, 0x02,
1660x23, 0x00, 0x02, 0xeb, 0x02, 0x01, 0x58, 0x02, 0x02, 0xfb, 1670x04, 0x20, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x23, 0x00, 0x02,
1670x00, 0x0c, 0x56, 0x02, 0x12, 0x41, 0xc2, 0x02, 0x02, 0x84, 1680xeb, 0x02, 0x01, 0x58, 0x02, 0x02, 0xfb, 0x00, 0x0c, 0x56,
1680x04, 0x01, 0x8a, 0x05, 0x50, 0x75, 0x72, 0x73, 0x6f, 0x72, 1690x02, 0x12, 0x41, 0xc2, 0x02, 0x02, 0x84, 0x04, 0x01, 0x8a,
1690x23, 0x04, 0x14, 0x73, 0x70, 0x07, 0x22, 0x75, 0x73, 0x24, 1700x05, 0x50, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x23, 0x04, 0x14,
1700x03, 0x61, 0x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, 0x4f, 0x07, 1710x73, 0x70, 0x07, 0x22, 0x75, 0x73, 0x24, 0x03, 0x61, 0x63,
1710x02, 0x26, 0x04, 0x00, 0x30, 0x00, 0x02, 0x59, 0x04, 0x01, 1720x6f, 0x6e, 0x6a, 0x75, 0x6e, 0x4f, 0x07, 0x02, 0x26, 0x04,
1720x2f, 0x00, 0x01, 0x91, 0x06, 0x15, 0x74, 0xa4, 0x01, 0x2b, 1730x00, 0x30, 0x00, 0x02, 0x59, 0x04, 0x01, 0x2f, 0x00, 0x01,
1730x6f, 0x72, 0x6e, 0x00, 0x3d, 0x55, 0x73, 0x65, 0x63, 0x00, 1740x91, 0x06, 0x15, 0x74, 0xa4, 0x01, 0x2b, 0x6f, 0x72, 0x6e,
1740x51, 0x74, 0x6f, 0x00, 0x6d, 0x6f, 0x7e, 0x09, 0x51, 0x68, 1750x00, 0x3d, 0x55, 0x73, 0x65, 0x63, 0x00, 0x51, 0x74, 0x6f,
1750x69, 0x67, 0x68, 0x6c, 0x2f, 0x08, 0x16, 0x61, 0x08, 0x06, 1760x00, 0x6d, 0x6f, 0x7e, 0x09, 0x51, 0x68, 0x69, 0x67, 0x68,
1760x03, 0x63, 0x07, 0x2a, 0x6e, 0x64, 0xc8, 0x04, 0x22, 0x74, 1770x6c, 0x2f, 0x08, 0x16, 0x61, 0x08, 0x06, 0x03, 0x63, 0x07,
1770x6f, 0xbc, 0x03, 0x00, 0x33, 0x04, 0x03, 0xab, 0x01, 0x05, 1780x2a, 0x6e, 0x64, 0xc8, 0x04, 0x22, 0x74, 0x6f, 0xbc, 0x03,
1780x3f, 0x00, 0x25, 0x65, 0x64, 0x73, 0x03, 0x14, 0x50, 0x0c, 1790x00, 0x33, 0x04, 0x03, 0xab, 0x01, 0x05, 0x3f, 0x00, 0x25,
1790x01, 0x50, 0x72, 0x65, 0x74, 0x75, 0x72, 0x28, 0x03, 0x5a, 1800x65, 0x64, 0x73, 0x03, 0x14, 0x50, 0x0c, 0x01, 0x50, 0x72,
1800x67, 0x67, 0x6c, 0x65, 0x73, 0x30, 0x00, 0x02, 0x3e, 0x08, 1810x65, 0x74, 0x75, 0x72, 0x28, 0x03, 0x5a, 0x67, 0x67, 0x6c,
1810x51, 0x61, 0x00, 0x6d, 0x6f, 0x64, 0xb4, 0x04, 0x55, 0x77, 1820x65, 0x73, 0x30, 0x00, 0x02, 0x3e, 0x08, 0x51, 0x61, 0x00,
1820x68, 0x69, 0x63, 0x68, 0x93, 0x02, 0x02, 0x67, 0x00, 0x20, 1830x6d, 0x6f, 0x64, 0xb4, 0x04, 0x55, 0x77, 0x68, 0x69, 0x63,
1830x6f, 0x72, 0x10, 0x03, 0x2e, 0x6f, 0x76, 0x40, 0x01, 0x05, 1840x68, 0x93, 0x02, 0x02, 0x67, 0x00, 0x20, 0x6f, 0x72, 0x10,
1840x63, 0x00, 0x12, 0x4d, 0x6b, 0x01, 0x01, 0x4f, 0x05, 0x01, 1850x03, 0x2e, 0x6f, 0x76, 0x40, 0x01, 0x05, 0x63, 0x00, 0x12,
1850xfd, 0x01, 0x41, 0x66, 0x75, 0x6c, 0x6c, 0x10, 0x01, 0x2d, 1860x4d, 0x6b, 0x01, 0x01, 0x4f, 0x05, 0x01, 0xfd, 0x01, 0x41,
1860x6f, 0x66, 0x1b, 0x02, 0x08, 0x9c, 0x09, 0x03, 0xa9, 0x08, 1870x66, 0x75, 0x6c, 0x6c, 0x10, 0x01, 0x2d, 0x6f, 0x66, 0x1b,
1870x31, 0x6f, 0x65, 0x73, 0xde, 0x07, 0x03, 0x8c, 0x0a, 0x10, 1880x02, 0x08, 0x9c, 0x09, 0x03, 0xa9, 0x08, 0x31, 0x6f, 0x65,
1880x6d, 0x03, 0x04, 0x02, 0xed, 0x00, 0x23, 0x69, 0x6e, 0x6f, 1890x73, 0xde, 0x07, 0x03, 0x8c, 0x0a, 0x10, 0x6d, 0x03, 0x04,
1890x06, 0x38, 0x4c, 0x65, 0x66, 0xed, 0x01, 0x02, 0xde, 0x07, 1900x02, 0xed, 0x00, 0x23, 0x69, 0x6e, 0x6f, 0x06, 0x38, 0x4c,
1900x01, 0x76, 0x00, 0x00, 0x59, 0x00, 0x01, 0x3f, 0x05, 0x30, 1910x65, 0x66, 0xed, 0x01, 0x02, 0xde, 0x07, 0x01, 0x76, 0x00,
1910x73, 0x00, 0x64, 0xbe, 0x07, 0x60, 0x28, 0x67, 0x72, 0x65, 1920x00, 0x59, 0x00, 0x01, 0x3f, 0x05, 0x30, 0x73, 0x00, 0x64,
1920x79, 0x00, 0xf1, 0x05, 0x31, 0x75, 0x74, 0x29, 0x4f, 0x02, 1930xbe, 0x07, 0x60, 0x28, 0x67, 0x72, 0x65, 0x79, 0x00, 0xf1,
1930x24, 0x75, 0x6e, 0x24, 0x00, 0x10, 0x69, 0x8a, 0x06, 0x00, 1940x05, 0x31, 0x75, 0x74, 0x29, 0x4f, 0x02, 0x24, 0x75, 0x6e,
1940x01, 0x04, 0x73, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 1950x24, 0x00, 0x10, 0x69, 0x8a, 0x06, 0x00, 0x01, 0x04, 0x73,
1950xeb, 0x07, 0x60, 0x2e, 0x00, 0x48, 0x6f, 0x6c, 0x64, 0x59, 1960x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0xeb, 0x07, 0x60,
1960x00, 0x13, 0x43, 0xa4, 0x06, 0x00, 0x17, 0x02, 0x4a, 0x68, 1970x2e, 0x00, 0x48, 0x6f, 0x6c, 0x64, 0x59, 0x00, 0x13, 0x43,
1970x69, 0x66, 0x74, 0x57, 0x02, 0x50, 0x61, 0x6e, 0x00, 0x61, 1980xa4, 0x06, 0x00, 0x17, 0x02, 0x4a, 0x68, 0x69, 0x66, 0x74,
1980x72, 0x31, 0x0a, 0x31, 0x6b, 0x65, 0x79, 0x5a, 0x03, 0x01, 1990x57, 0x02, 0x50, 0x61, 0x6e, 0x00, 0x61, 0x72, 0x31, 0x0a,
1990xa2, 0x08, 0x02, 0xda, 0x00, 0x00, 0x86, 0x06, 0x01, 0x95, 2000x31, 0x6b, 0x65, 0x79, 0x5a, 0x03, 0x01, 0xa2, 0x08, 0x02,
2000x00, 0x27, 0x69, 0x6e, 0x37, 0x06, 0x05, 0x84, 0x09, 0x00, 2010xda, 0x00, 0x00, 0x86, 0x06, 0x01, 0x95, 0x00, 0x27, 0x69,
2010xc4, 0x00, 0x10, 0x28, 0x2a, 0x03, 0x00, 0x1c, 0x00, 0x11, 2020x6e, 0x37, 0x06, 0x05, 0x84, 0x09, 0x00, 0xc4, 0x00, 0x10,
2020x61, 0x13, 0x00, 0x15, 0x73, 0xf8, 0x09, 0x11, 0x64, 0xa3, 2030x28, 0x2a, 0x03, 0x00, 0x1c, 0x00, 0x11, 0x61, 0x13, 0x00,
2030x0a, 0x12, 0x65, 0x5e, 0x02, 0x31, 0x32, 0x2e, 0x31, 0x36, 2040x15, 0x73, 0xf8, 0x09, 0x11, 0x64, 0xa3, 0x0a, 0x12, 0x65,
2040x03, 0x01, 0xb9, 0x02, 0x50, 0x61, 0x76, 0x61, 0x69, 0x6c, 2050x5e, 0x02, 0x31, 0x32, 0x2e, 0x31, 0x36, 0x03, 0x01, 0xb9,
2050xf2, 0x08, 0x22, 0x2e, 0x29, 0x74, 0x07, 0x14, 0x32, 0x74, 2060x02, 0x50, 0x61, 0x76, 0x61, 0x69, 0x6c, 0xf2, 0x08, 0x22,
2060x07, 0x73, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0xca, 2070x2e, 0x29, 0x74, 0x07, 0x14, 0x32, 0x74, 0x07, 0x71, 0x70,
2070x0b, 0x30, 0x54, 0x68, 0x65, 0xe1, 0x02, 0x05, 0x14, 0x00, 2080x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0xdd, 0x0b, 0x01, 0x21,
2080x02, 0x42, 0x00, 0x04, 0x3d, 0x00, 0x04, 0x15, 0x0a, 0xf1, 2090x05, 0x00, 0xe1, 0x02, 0x05, 0x14, 0x00, 0x02, 0x42, 0x00,
2090x01, 0x65, 0x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 2100x04, 0x3d, 0x00, 0x04, 0x15, 0x0a, 0xf1, 0x01, 0x65, 0x00,
2100x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x70, 0x00, 0x03, 2110x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e,
2110x18, 0x07, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 2120x27, 0x00, 0x6f, 0x70, 0x70, 0x00, 0x03, 0x18, 0x07, 0xb0,
2120x6d, 0x65, 0x6e, 0x75, 0xac, 0x00, 0x51, 0x47, 0x72, 0x69, 2130x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e,
2130x64, 0x20, 0xb0, 0x0b, 0x33, 0x00, 0x00, 0x53, 0xc2, 0x08, 2140x75, 0xac, 0x00, 0x51, 0x47, 0x72, 0x69, 0x64, 0x20, 0xb0,
2140x1f, 0x73, 0xc5, 0x0b, 0x04, 0x11, 0x4c, 0xc8, 0x05, 0x32, 2150x0b, 0x33, 0x00, 0x00, 0x53, 0xc2, 0x08, 0x1f, 0x73, 0xc5,
2150x6c, 0x69, 0x6d, 0x5c, 0x01, 0x68, 0x33, 0x3b, 0x00, 0x75, 2160x0b, 0x04, 0x11, 0x4c, 0xc8, 0x05, 0x32, 0x6c, 0x69, 0x6d,
2160x70, 0x70, 0x12, 0x00, 0x74, 0x39, 0x00, 0x28, 0x62, 0x65, 2170x5c, 0x01, 0x68, 0x33, 0x3b, 0x00, 0x75, 0x70, 0x70, 0x12,
2170x63, 0x61, 0xd7, 0x06, 0x20, 0x75, 0x73, 0xbe, 0x02, 0x00, 2180x00, 0x74, 0x39, 0x00, 0x28, 0x62, 0x65, 0x63, 0x61, 0xd7,
2180x5d, 0x02, 0x11, 0x66, 0xa7, 0x03, 0x01, 0xde, 0x09, 0x21, 2190x06, 0x20, 0x75, 0x73, 0xbe, 0x02, 0x00, 0x5d, 0x02, 0x11,
2190x62, 0x65, 0x68, 0x09, 0x01, 0x32, 0x05, 0x40, 0x64, 0x69, 2200x66, 0xa7, 0x03, 0x01, 0xde, 0x09, 0x21, 0x62, 0x65, 0x68,
2200x66, 0x66, 0x2a, 0x05, 0x12, 0x74, 0x6b, 0x03, 0x11, 0x60, 2210x09, 0x01, 0x32, 0x05, 0x40, 0x64, 0x69, 0x66, 0x66, 0x2a,
2210x0c, 0x02, 0x70, 0x73, 0x27, 0x00, 0x62, 0x69, 0x67, 0x67, 2220x05, 0x12, 0x74, 0x6b, 0x03, 0x11, 0x60, 0x0c, 0x02, 0x70,
2220xb8, 0x0a, 0x61, 0x68, 0x61, 0x6e, 0x00, 0x39, 0x21, 0x12, 2230x73, 0x27, 0x00, 0x62, 0x69, 0x67, 0x67, 0xb8, 0x0a, 0x61,
2230x07, 0x14, 0x44, 0x2b, 0x00, 0x34, 0x79, 0x00, 0x00, 0xbe, 2240x68, 0x61, 0x6e, 0x00, 0x39, 0x21, 0x12, 0x07, 0x14, 0x44,
2240x01, 0x13, 0x73, 0x9d, 0x03, 0x05, 0x1a, 0x00, 0x04, 0xae, 2250x2b, 0x00, 0x34, 0x79, 0x00, 0x00, 0xbe, 0x01, 0x13, 0x73,
2250x00, 0x50, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x25, 0x03, 0x02, 2260x9d, 0x03, 0x05, 0x1a, 0x00, 0x04, 0xae, 0x00, 0x50, 0x65,
2260x32, 0x09, 0x70, 0x2e, 0x00, 0x41, 0x74, 0x00, 0x55, 0x6e, 2270x6e, 0x65, 0x72, 0x61, 0x25, 0x03, 0x02, 0x32, 0x09, 0x70,
2270x30, 0x0a, 0x21, 0x6f, 0x6e, 0x28, 0x01, 0x53, 0x6c, 0x65, 2280x2e, 0x00, 0x41, 0x74, 0x00, 0x55, 0x6e, 0x30, 0x0a, 0x21,
2280x76, 0x65, 0x6c, 0xc7, 0x09, 0x72, 0x62, 0x61, 0x63, 0x6b, 2290x6f, 0x6e, 0x28, 0x01, 0x53, 0x6c, 0x65, 0x76, 0x65, 0x6c,
2290x74, 0x72, 0x61, 0x71, 0x02, 0x04, 0x36, 0x07, 0x82, 0x72, 2300xc7, 0x09, 0x72, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61,
2300x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x2a, 0x09, 0x01, 2310x71, 0x02, 0x04, 0x36, 0x07, 0x82, 0x72, 0x65, 0x71, 0x75,
2310x0f, 0x01, 0x31, 0x6f, 0x6c, 0x75, 0x48, 0x01, 0x21, 0x73, 2320x69, 0x72, 0x65, 0x64, 0x2a, 0x09, 0x01, 0x0f, 0x01, 0x31,
2320x68, 0xc8, 0x00, 0x23, 0x73, 0x74, 0x2b, 0x00, 0x71, 0x75, 2330x6f, 0x6c, 0x75, 0x48, 0x01, 0x21, 0x73, 0x68, 0xc8, 0x00,
2330x6e, 0x69, 0x71, 0x75, 0x65, 0x2e, 0xbc, 0x06, 0x33, 0x72, 2340x23, 0x73, 0x74, 0x2b, 0x00, 0x71, 0x75, 0x6e, 0x69, 0x71,
2340x65, 0x6d, 0xf6, 0x06, 0x01, 0x62, 0x00, 0x14, 0x73, 0x48, 2350x75, 0x65, 0x2e, 0xbc, 0x06, 0x33, 0x72, 0x65, 0x6d, 0xf6,
2350x00, 0x07, 0xfa, 0x0a, 0x00, 0xdc, 0x08, 0x10, 0x6f, 0x65, 2360x06, 0x01, 0x62, 0x00, 0x14, 0x73, 0x48, 0x00, 0x07, 0xfa,
2360x0b, 0x22, 0x78, 0x00, 0x91, 0x00, 0x01, 0xaf, 0x0b, 0x00, 2370x0a, 0x00, 0xdc, 0x08, 0x10, 0x6f, 0x65, 0x0b, 0x22, 0x78,
2370x02, 0x02, 0x30, 0x6f, 0x69, 0x64, 0x1a, 0x03, 0x03, 0x10, 2380x00, 0x91, 0x00, 0x01, 0xaf, 0x0b, 0x00, 0x02, 0x02, 0x30,
2380x00, 0xb0, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 2390x6f, 0x69, 0x64, 0x1a, 0x03, 0x03, 0x10, 0x00, 0x05, 0x97,
2390x6b, 0x2e, 0x00, 2400x00, 0x03, 0x85, 0x09, 0x14, 0x33, 0x11, 0x02, 0x00, 0x56,
2410x01, 0xb1, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
2420x6e, 0x63, 0x65, 0x17, 0x02, 0x20, 0x4f, 0x6e, 0x45, 0x09,
2430x63, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0xe9, 0x08, 0x62,
2440x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x84, 0x01, 0x07, 0x2e,
2450x00, 0x12, 0x2c, 0x05, 0x02, 0x16, 0x50, 0x12, 0x00, 0x0d,
2460x21, 0x02, 0x33, 0x47, 0x61, 0x6d, 0x21, 0x02, 0x02, 0x0f,
2470x01, 0x32, 0x6c, 0x65, 0x74, 0x2b, 0x04, 0x73, 0x6f, 0x6e,
2480x66, 0x69, 0x67, 0x75, 0x72, 0x23, 0x06, 0x36, 0x74, 0x79,
2490x6c, 0x20, 0x02, 0x20, 0x61, 0x6d, 0x89, 0x01, 0x10, 0x73,
2500xca, 0x09, 0x05, 0x3c, 0x09, 0x02, 0x5e, 0x0a, 0x00, 0x3f,
2510x03, 0x02, 0x85, 0x0b, 0xf1, 0x01, 0x68, 0x72, 0x65, 0x65,
2520x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
2530x61, 0x6c, 0x8e, 0x04, 0x50, 0x2c, 0x00, 0x73, 0x65, 0x6c,
2540x12, 0x03, 0x72, 0x6e, 0x67, 0x00, 0x60, 0x32, 0x44, 0x27,
2550x73, 0x00, 0x61, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x17,
2560x01, 0x02, 0x11, 0x0a, 0x24, 0x65, 0x72, 0x5f, 0x00, 0x03,
2570x79, 0x00, 0x05, 0xc5, 0x04, 0x07, 0xdd, 0x0c, 0xa0, 0x73,
2580x68, 0x6f, 0x77, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x6a, 0xcf,
2590x0b, 0x42, 0x77, 0x72, 0x69, 0x74, 0x0b, 0x0d, 0x24, 0x65,
2600x69, 0x92, 0x0c, 0x07, 0xf4, 0x06, 0x50, 0x61, 0x72, 0x65,
2610x2e, 0x00,
240}; 262};
241 263
242const unsigned short help_text_len = 3541; 264const unsigned short help_text_len = 3906;
243const unsigned short help_text_words = 674; 265const unsigned short help_text_words = 732;
266const bool help_valid = true;
244const char quick_help_text[] = "Complete the latin square of towers in accordance with the clues."; 267const char quick_help_text[] = "Complete the latin square of towers in accordance with the clues.";
diff --git a/apps/plugins/puzzles/help/tracks.c b/apps/plugins/puzzles/help/tracks.c
index dc1bd011fa..f3677352b5 100644
--- a/apps/plugins/puzzles/help/tracks.c
+++ b/apps/plugins/puzzles/help/tracks.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,147 +6,149 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 86, TEXT_CENTER | C_RED }, 9 { 87, TEXT_CENTER | C_RED },
10 { 207, TEXT_CENTER | C_RED }, 10 { 208, TEXT_CENTER | C_RED },
11 { 224, TEXT_UNDERLINE },
12 { 225, TEXT_UNDERLINE }, 11 { 225, TEXT_UNDERLINE },
13 { 236, TEXT_UNDERLINE }, 12 { 226, TEXT_UNDERLINE },
14 { 268, TEXT_UNDERLINE }, 13 { 237, TEXT_UNDERLINE },
14 { 269, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 1862 comp 1274 ratio 0.684211 level 10 saved 588 */ 18/* orig 1881 comp 1282 ratio 0.681552 level 10 saved 599 */
19const char help_text[] = { 19const char help_text[] = {
200xf0, 0x2c, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x34, 0x30, 0x3a, 0x20, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x73, 210x34, 0x30, 0x3a, 0x20, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x73,
220x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 220x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x19, 0x00, 0x00, 0x00,
230x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 230x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69,
240x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 240x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64,
250x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 250x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65,
260x65, 0x11, 0x00, 0x51, 0x77, 0x68, 0x69, 0x63, 0x68, 0x2b, 260x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x11, 0x00, 0x51,
270x00, 0xf1, 0x04, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 270x77, 0x68, 0x69, 0x63, 0x68, 0x2b, 0x00, 0xf1, 0x04, 0x66,
280x77, 0x69, 0x74, 0x68, 0x00, 0x74, 0x72, 0x61, 0x69, 0x6e, 280x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68,
290x00, 0x74, 0x4f, 0x00, 0x11, 0x2e, 0x4d, 0x00, 0xf2, 0x05, 290x00, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x00, 0x74, 0x62, 0x00,
300x6e, 0x65, 0x65, 0x64, 0x00, 0x74, 0x6f, 0x00, 0x63, 0x6f, 300x11, 0x2e, 0x4d, 0x00, 0xf2, 0x05, 0x6e, 0x65, 0x65, 0x64,
310x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x74, 0x68, 0x65, 310x00, 0x74, 0x6f, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
320x21, 0x00, 0x70, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x41, 320x74, 0x65, 0x00, 0x74, 0x68, 0x65, 0x21, 0x00, 0x70, 0x00,
330x1d, 0x00, 0x91, 0x42, 0x00, 0x73, 0x6f, 0x00, 0x74, 0x68, 330x66, 0x72, 0x6f, 0x6d, 0x00, 0x41, 0x1d, 0x00, 0x91, 0x42,
340x61, 0x74, 0x1e, 0x00, 0xf1, 0x06, 0x72, 0x6f, 0x77, 0x73, 340x00, 0x73, 0x6f, 0x00, 0x74, 0x68, 0x61, 0x74, 0x1e, 0x00,
350x00, 0x61, 0x6e, 0x64, 0x00, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 350xf1, 0x06, 0x72, 0x6f, 0x77, 0x73, 0x00, 0x61, 0x6e, 0x64,
360x6e, 0x73, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x58, 0x00, 0xe0, 360x00, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x00, 0x63,
370x68, 0x65, 0x00, 0x73, 0x61, 0x6d, 0x65, 0x00, 0x6e, 0x75, 370x6f, 0x6e, 0x74, 0x58, 0x00, 0xe0, 0x68, 0x65, 0x00, 0x73,
380x6d, 0x62, 0x65, 0x72, 0x87, 0x00, 0x02, 0x4a, 0x00, 0xb1, 380x61, 0x6d, 0x65, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
390x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x61, 390x87, 0x00, 0x02, 0x4a, 0x00, 0xb1, 0x73, 0x65, 0x67, 0x6d,
400x73, 0x93, 0x00, 0xa3, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 400x65, 0x6e, 0x74, 0x73, 0x00, 0x61, 0x73, 0x93, 0x00, 0xa3,
410x74, 0x65, 0x64, 0x00, 0x36, 0x00, 0x50, 0x63, 0x6c, 0x75, 410x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x00,
420x65, 0x73, 0x6a, 0x00, 0x01, 0x7e, 0x00, 0x21, 0x6f, 0x70, 420x36, 0x00, 0x50, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x6a, 0x00,
430x5f, 0x00, 0x51, 0x72, 0x69, 0x67, 0x68, 0x74, 0x45, 0x00, 430x01, 0x7e, 0x00, 0x21, 0x6f, 0x70, 0x5f, 0x00, 0x51, 0x72,
440x21, 0x68, 0x65, 0xe9, 0x00, 0x91, 0x2e, 0x00, 0x00, 0x00, 440x69, 0x67, 0x68, 0x74, 0x45, 0x00, 0x21, 0x68, 0x65, 0xe9,
450x54, 0x68, 0x65, 0x72, 0x65, 0x45, 0x00, 0x60, 0x6f, 0x6e, 450x00, 0x91, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x65, 0x72,
460x6c, 0x79, 0x00, 0x73, 0xd2, 0x00, 0x00, 0x27, 0x00, 0x00, 460x65, 0x45, 0x00, 0x60, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x73,
470x31, 0x00, 0xf1, 0x08, 0x39, 0x30, 0x00, 0x64, 0x65, 0x67, 470xd2, 0x00, 0x00, 0x27, 0x00, 0x00, 0x31, 0x00, 0xf1, 0x08,
480x72, 0x65, 0x65, 0x00, 0x63, 0x75, 0x72, 0x76, 0x65, 0x64, 480x39, 0x30, 0x00, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x00,
490x00, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x2c, 0x1c, 0x00, 0x06, 490x63, 0x75, 0x72, 0x76, 0x65, 0x64, 0x00, 0x72, 0x61, 0x69,
500xd7, 0x00, 0xf1, 0x05, 0x6d, 0x61, 0x79, 0x00, 0x6e, 0x6f, 500x6c, 0x73, 0x2c, 0x1c, 0x00, 0x06, 0xd7, 0x00, 0xf1, 0x05,
510x74, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x00, 0x69, 0x74, 510x6d, 0x61, 0x79, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x63, 0x72,
520x73, 0x65, 0x6c, 0x66, 0x5a, 0x00, 0x01, 0x16, 0x01, 0x32, 520x6f, 0x73, 0x73, 0x00, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66,
530x00, 0x77, 0x61, 0xd1, 0x00, 0x40, 0x72, 0x69, 0x62, 0x75, 530x5a, 0x00, 0x01, 0x16, 0x01, 0x32, 0x00, 0x77, 0x61, 0xd1,
540xa2, 0x00, 0x01, 0x95, 0x00, 0x20, 0x69, 0x73, 0xed, 0x00, 540x00, 0x40, 0x72, 0x69, 0x62, 0x75, 0xa2, 0x00, 0x01, 0x95,
550xf0, 0x08, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 550x00, 0x20, 0x69, 0x73, 0xed, 0x00, 0xf0, 0x08, 0x6c, 0x65,
560x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 560x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x4a,
570x61, 0x72, 0x76, 0x65, 0x79, 0x3d, 0x00, 0x44, 0x34, 0x30, 570x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72, 0x76, 0x65,
580x2e, 0x31, 0xa7, 0x01, 0x01, 0x3e, 0x00, 0x21, 0x6f, 0x6c, 580x79, 0x3d, 0x00, 0x44, 0x34, 0x30, 0x2e, 0x31, 0xba, 0x01,
590xb0, 0x01, 0xf4, 0x15, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 590x01, 0x3e, 0x00, 0xf4, 0x1c, 0x6f, 0x6c, 0x73, 0x20, 0x00,
600x6c, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x00, 0x6f, 0x6e, 600x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x63, 0x6c, 0x69,
610x00, 0x61, 0x6e, 0x00, 0x65, 0x64, 0x67, 0x65, 0x00, 0x62, 610x63, 0x6b, 0x69, 0x6e, 0x67, 0x00, 0x6f, 0x6e, 0x00, 0x61,
620x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x00, 0x74, 0x77, 0x6f, 620x6e, 0x00, 0x65, 0x64, 0x67, 0x65, 0x00, 0x62, 0x65, 0x74,
630xbd, 0x01, 0x7a, 0x00, 0x61, 0x64, 0x64, 0x73, 0x00, 0x61, 630x77, 0x65, 0x65, 0x6e, 0x00, 0x74, 0x77, 0x6f, 0xbd, 0x01,
640x34, 0x01, 0x06, 0x29, 0x00, 0x28, 0x68, 0x65, 0x2d, 0x00, 640x7a, 0x00, 0x61, 0x64, 0x64, 0x73, 0x00, 0x61, 0x34, 0x01,
650x30, 0x2e, 0x00, 0x52, 0xf8, 0x00, 0x0f, 0x5c, 0x00, 0x02, 650x06, 0x29, 0x00, 0x28, 0x68, 0x65, 0x2d, 0x00, 0x30, 0x2e,
660x03, 0x48, 0x00, 0x02, 0xe1, 0x00, 0x12, 0x6f, 0x3b, 0x00, 660x00, 0x52, 0xf8, 0x00, 0x0f, 0x5c, 0x00, 0x02, 0x03, 0x48,
670x00, 0x19, 0x00, 0x14, 0x2c, 0x79, 0x01, 0x00, 0x30, 0x00, 670x00, 0x02, 0xe1, 0x00, 0x12, 0x6f, 0x3b, 0x00, 0x00, 0x19,
680x23, 0x6e, 0x6f, 0x69, 0x00, 0xa1, 0x69, 0x73, 0x00, 0x70, 680x00, 0x14, 0x2c, 0x79, 0x01, 0x00, 0x30, 0x00, 0x23, 0x6e,
690x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0xfd, 0x01, 0x3d, 0x72, 690x6f, 0x69, 0x00, 0xa1, 0x69, 0x73, 0x00, 0x70, 0x6f, 0x73,
700x65, 0x2e, 0xb8, 0x00, 0x10, 0x69, 0x6b, 0x02, 0x02, 0x79, 700x73, 0x69, 0x62, 0x6c, 0xfd, 0x01, 0x3d, 0x72, 0x65, 0x2e,
710x00, 0x05, 0x5d, 0x00, 0x54, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 710xb8, 0x00, 0x10, 0x69, 0x6b, 0x02, 0x02, 0x79, 0x00, 0x05,
720x51, 0x00, 0x70, 0x6f, 0x72, 0x00, 0x73, 0x68, 0x6f, 0x77, 720x5d, 0x00, 0x54, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x51, 0x00,
730x2c, 0x00, 0x01, 0x27, 0x02, 0x82, 0x79, 0x6f, 0x75, 0x00, 730x70, 0x6f, 0x72, 0x00, 0x73, 0x68, 0x6f, 0x77, 0x2c, 0x00,
740x6b, 0x6e, 0x6f, 0x77, 0x13, 0x02, 0x02, 0x39, 0x00, 0x45, 740x01, 0x27, 0x02, 0x82, 0x79, 0x6f, 0x75, 0x00, 0x6b, 0x6e,
750x6d, 0x75, 0x73, 0x74, 0x2b, 0x02, 0x03, 0xe6, 0x00, 0x30, 750x6f, 0x77, 0x13, 0x02, 0x02, 0x39, 0x00, 0x45, 0x6d, 0x75,
760x2c, 0x00, 0x65, 0xc8, 0x02, 0x21, 0x69, 0x66, 0x32, 0x00, 760x73, 0x74, 0x2b, 0x02, 0x03, 0xe6, 0x00, 0x30, 0x2c, 0x00,
770x52, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x38, 0x00, 0x02, 0xbf, 770x65, 0xc8, 0x02, 0x21, 0x69, 0x66, 0x32, 0x00, 0x52, 0x64,
780x02, 0x00, 0xb5, 0x00, 0x00, 0xa3, 0x01, 0x02, 0xcb, 0x00, 780x6f, 0x6e, 0x27, 0x74, 0x38, 0x00, 0x02, 0xbf, 0x02, 0x00,
790x6d, 0x65, 0x73, 0x00, 0x79, 0x65, 0x74, 0xf9, 0x00, 0x0e, 790xb5, 0x00, 0x00, 0xa3, 0x01, 0x02, 0xcb, 0x00, 0x6d, 0x65,
800x9d, 0x00, 0x04, 0xdb, 0x01, 0x06, 0xed, 0x00, 0x15, 0x69, 800x73, 0x00, 0x79, 0x65, 0x74, 0xf9, 0x00, 0x0e, 0x9d, 0x00,
810x7a, 0x00, 0x16, 0x73, 0xf9, 0x00, 0x03, 0x62, 0x01, 0x05, 810x04, 0xdb, 0x01, 0x06, 0xed, 0x00, 0x15, 0x69, 0x7a, 0x00,
820xef, 0x00, 0x32, 0x00, 0x6f, 0x72, 0x74, 0x02, 0x60, 0x2d, 820x16, 0x73, 0xf9, 0x00, 0x03, 0x62, 0x01, 0x05, 0xef, 0x00,
830x64, 0x72, 0x61, 0x67, 0x67, 0x38, 0x00, 0x04, 0x7d, 0x01, 830x32, 0x00, 0x6f, 0x72, 0x74, 0x02, 0x60, 0x2d, 0x64, 0x72,
840x05, 0xa2, 0x01, 0x20, 0x6c, 0x6c, 0xfd, 0x02, 0x00, 0xad, 840x61, 0x67, 0x67, 0x38, 0x00, 0x04, 0x7d, 0x01, 0x05, 0xa2,
850x00, 0x86, 0x74, 0x6f, 0x00, 0x6c, 0x61, 0x79, 0x00, 0x61, 850x01, 0x20, 0x6c, 0x6c, 0xfd, 0x02, 0x00, 0xad, 0x00, 0x86,
860x83, 0x02, 0x31, 0x6c, 0x69, 0x6e, 0x7b, 0x03, 0x32, 0x69, 860x74, 0x6f, 0x00, 0x6c, 0x61, 0x79, 0x00, 0x61, 0x83, 0x02,
870x73, 0x2d, 0x61, 0x00, 0x20, 0x6f, 0x72, 0x0c, 0x00, 0x33, 870x31, 0x6c, 0x69, 0x6e, 0x7b, 0x03, 0x32, 0x69, 0x73, 0x2d,
880x6e, 0x6f, 0x74, 0x10, 0x00, 0x05, 0x2d, 0x01, 0xd1, 0x73, 880x61, 0x00, 0x20, 0x6f, 0x72, 0x0c, 0x00, 0x33, 0x6e, 0x6f,
890x2c, 0x00, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x00, 0x66, 890x74, 0x10, 0x00, 0x05, 0x2d, 0x01, 0xd1, 0x73, 0x2c, 0x00,
900x6f, 0x72, 0xa1, 0x03, 0x03, 0xc9, 0x00, 0x01, 0x5b, 0x03, 900x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x00, 0x66, 0x6f, 0x72,
910x25, 0x6f, 0x72, 0x5a, 0x03, 0x85, 0x74, 0x6f, 0x00, 0x6d, 910xa1, 0x03, 0x03, 0xc9, 0x00, 0x01, 0x5b, 0x03, 0x25, 0x6f,
920x61, 0x74, 0x63, 0x68, 0x25, 0x03, 0x00, 0xad, 0x00, 0x41, 920x72, 0x5a, 0x03, 0x85, 0x74, 0x6f, 0x00, 0x6d, 0x61, 0x74,
930x28, 0x41, 0x6c, 0x6c, 0x11, 0x00, 0x11, 0x61, 0x8f, 0x02, 930x63, 0x68, 0x25, 0x03, 0x00, 0xad, 0x00, 0x41, 0x28, 0x41,
940x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 940x6c, 0x6c, 0x11, 0x00, 0x11, 0x61, 0x8f, 0x02, 0x92, 0x73,
950x4f, 0x03, 0x13, 0x73, 0xa5, 0x02, 0x41, 0x32, 0x2e, 0x31, 950x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x4f, 0x03,
960x00, 0x13, 0x01, 0xf2, 0x00, 0x6c, 0x73, 0x6f, 0x00, 0x61, 960x13, 0x73, 0xa5, 0x02, 0x41, 0x32, 0x2e, 0x31, 0x00, 0x13,
970x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 970x01, 0xf2, 0x00, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61,
980xad, 0x02, 0x14, 0x32, 0xad, 0x02, 0xb2, 0x70, 0x61, 0x72, 980x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0xad, 0x02,
990x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x5e, 0x03, 990x14, 0x32, 0xad, 0x02, 0xb2, 0x70, 0x61, 0x72, 0x61, 0x6d,
1000x36, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x42, 0x00, 0x04, 1000x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x5e, 0x03, 0x36, 0x73,
1010x3d, 0x00, 0x02, 0x12, 0x04, 0x00, 0x7a, 0x00, 0xe1, 0x60, 1010x65, 0x00, 0x14, 0x00, 0x02, 0x42, 0x00, 0x04, 0x3d, 0x00,
1020x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 1020x02, 0x12, 0x04, 0x00, 0x7a, 0x00, 0xe1, 0x60, 0x43, 0x75,
1030x00, 0x6f, 0x70, 0x70, 0x00, 0x03, 0x68, 0x02, 0xb0, 0x60, 1030x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f,
1040x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 1040x70, 0x70, 0x00, 0x03, 0x68, 0x02, 0xb0, 0x60, 0x54, 0x79,
1050xac, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 1050x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xac, 0x00,
1060x48, 0x65, 0x22, 0x01, 0x68, 0x00, 0x00, 0x53, 0x69, 0x7a, 1060x91, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48, 0x65,
1070x65, 0xd3, 0x03, 0x00, 0x93, 0x02, 0x06, 0xd3, 0x02, 0xf3, 1070x22, 0x01, 0x68, 0x00, 0x00, 0x53, 0x69, 0x7a, 0x65, 0xd3,
1080x01, 0x00, 0x00, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 1080x03, 0x00, 0x93, 0x02, 0x06, 0xd3, 0x02, 0xf3, 0x01, 0x00,
1090x6c, 0x74, 0x79, 0x00, 0x00, 0x00, 0x43, 0x49, 0x03, 0x01, 1090x00, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
1100x2e, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x04, 0x40, 0x00, 0x41, 1100x79, 0x00, 0x00, 0x00, 0x43, 0x49, 0x03, 0x01, 0x2e, 0x00,
1110x65, 0x6e, 0x65, 0x72, 0x46, 0x04, 0xf3, 0x0b, 0x70, 0x75, 1110x16, 0x64, 0x1a, 0x00, 0x04, 0x40, 0x00, 0x41, 0x65, 0x6e,
1120x7a, 0x7a, 0x6c, 0x65, 0x3a, 0x00, 0x61, 0x74, 0x00, 0x54, 1120x65, 0x72, 0x46, 0x04, 0xf3, 0x0b, 0x70, 0x75, 0x7a, 0x7a,
1130x72, 0x69, 0x63, 0x6b, 0x79, 0x00, 0x6c, 0x65, 0x76, 0x65, 1130x6c, 0x65, 0x3a, 0x00, 0x61, 0x74, 0x00, 0x54, 0x72, 0x69,
1140x6c, 0x2c, 0x00, 0x79, 0x2f, 0x05, 0x82, 0x72, 0x65, 0x71, 1140x63, 0x6b, 0x79, 0x00, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x2c,
1150x75, 0x69, 0x72, 0x65, 0x64, 0x51, 0x01, 0xc3, 0x6b, 0x65, 1150x00, 0x79, 0x2f, 0x05, 0x82, 0x72, 0x65, 0x71, 0x75, 0x69,
1160x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x64, 0x65, 0x64, 0x75, 1160x72, 0x65, 0x64, 0x51, 0x01, 0xc3, 0x6b, 0x65, 0x00, 0x6d,
1170x43, 0x01, 0x60, 0x72, 0x65, 0x67, 0x61, 0x72, 0x64, 0x86, 1170x6f, 0x72, 0x65, 0x00, 0x64, 0x65, 0x64, 0x75, 0x43, 0x01,
1180x01, 0x36, 0x64, 0x69, 0x73, 0x0d, 0x00, 0x52, 0x6d, 0x6f, 1180x60, 0x72, 0x65, 0x67, 0x61, 0x72, 0x64, 0x86, 0x01, 0x36,
1190x76, 0x65, 0x73, 0xd3, 0x02, 0x91, 0x77, 0x6f, 0x75, 0x6c, 1190x64, 0x69, 0x73, 0x0d, 0x00, 0x52, 0x6d, 0x6f, 0x76, 0x65,
1200x64, 0x00, 0x6c, 0x65, 0x61, 0x45, 0x00, 0x25, 0x69, 0x6d, 1200x73, 0xd3, 0x02, 0x91, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x00,
1210x34, 0x03, 0x01, 0x6d, 0x02, 0xa2, 0x69, 0x6e, 0x67, 0x73, 1210x6c, 0x65, 0x61, 0x45, 0x00, 0x25, 0x69, 0x6d, 0x34, 0x03,
1220x00, 0x6c, 0x61, 0x74, 0x65, 0x72, 0xc6, 0x00, 0x11, 0x73, 1220x01, 0x6d, 0x02, 0xa2, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x6c,
1230x2a, 0x02, 0x00, 0x0b, 0x04, 0xb2, 0x73, 0x65, 0x63, 0x75, 1230x61, 0x74, 0x65, 0x72, 0xc6, 0x00, 0x11, 0x73, 0x2a, 0x02,
1240x74, 0x69, 0x76, 0x65, 0x20, 0x31, 0x20, 0xe6, 0x04, 0x07, 1240x00, 0x0b, 0x04, 0xb2, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69,
1250xd8, 0x00, 0x30, 0x77, 0x68, 0x65, 0x72, 0x03, 0x01, 0xce, 1250x76, 0x65, 0x20, 0x31, 0x20, 0xe6, 0x04, 0x07, 0xd8, 0x00,
1260x00, 0x03, 0x7f, 0x04, 0x35, 0x67, 0x61, 0x6d, 0xda, 0x00, 1260x30, 0x77, 0x68, 0x65, 0x72, 0x03, 0x01, 0xce, 0x00, 0x03,
1270x00, 0x57, 0x01, 0x71, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 1270x7f, 0x04, 0x35, 0x67, 0x61, 0x6d, 0xda, 0x00, 0x00, 0x57,
1280x73, 0xfb, 0x03, 0x50, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x14, 1280x01, 0x71, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x73, 0xfb,
1290x04, 0x0f, 0x27, 0x02, 0x00, 0x81, 0x68, 0x61, 0x76, 0x65, 1290x03, 0x50, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x14, 0x04, 0x0f,
1300x00, 0x61, 0x00, 0x31, 0x26, 0x02, 0x10, 0x2c, 0x1d, 0x00, 1300x27, 0x02, 0x00, 0x81, 0x68, 0x61, 0x76, 0x65, 0x00, 0x61,
1310x05, 0x3a, 0x00, 0x02, 0xb3, 0x05, 0x06, 0x30, 0x00, 0x04, 1310x00, 0x31, 0x26, 0x02, 0x10, 0x2c, 0x1d, 0x00, 0x05, 0x3a,
1320x40, 0x01, 0x01, 0x95, 0x02, 0xbd, 0x27, 0x73, 0x00, 0x65, 1320x00, 0x02, 0xb3, 0x05, 0x06, 0x30, 0x00, 0x04, 0x40, 0x01,
1330x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x47, 0x00, 0xc2, 1330x01, 0x95, 0x02, 0xbd, 0x27, 0x73, 0x00, 0x65, 0x6e, 0x64,
1340x2e, 0x00, 0x42, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 1340x70, 0x6f, 0x69, 0x6e, 0x74, 0x47, 0x00, 0xc2, 0x2e, 0x00,
1350x6c, 0x74, 0x05, 0x05, 0x21, 0x69, 0x73, 0x3b, 0x05, 0x02, 1350x42, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
1360x5b, 0x00, 0x60, 0x74, 0x65, 0x64, 0x2c, 0x00, 0x74, 0x5d, 1360x05, 0x05, 0x21, 0x69, 0x73, 0x3b, 0x05, 0x02, 0x5b, 0x00,
1370x02, 0x86, 0x6f, 0x69, 0x64, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 1370x60, 0x74, 0x65, 0x64, 0x2c, 0x00, 0x74, 0x5d, 0x02, 0x86,
1380x0c, 0x03, 0x30, 0x62, 0x6f, 0x72, 0x40, 0x01, 0x05, 0xf9, 1380x6f, 0x69, 0x64, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x0c, 0x03,
1390x05, 0x05, 0x0b, 0x06, 0x00, 0x8c, 0x05, 0x01, 0x82, 0x01, 1390x30, 0x62, 0x6f, 0x72, 0x40, 0x01, 0x05, 0xf9, 0x05, 0x05,
1400x01, 0xc2, 0x01, 0x01, 0x46, 0x05, 0x01, 0x8c, 0x01, 0x71, 1400x0b, 0x06, 0x00, 0x8c, 0x05, 0x01, 0x82, 0x01, 0x01, 0xc2,
1410x74, 0x77, 0x69, 0x64, 0x64, 0x6c, 0x79, 0x20, 0x00, 0x70, 1410x01, 0x01, 0x46, 0x05, 0x01, 0x8c, 0x01, 0x71, 0x74, 0x77,
1420x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0xba, 0x03, 0x32, 1420x69, 0x64, 0x64, 0x6c, 0x79, 0x20, 0x00, 0x70, 0x69, 0x6e,
1430x2e, 0x00, 0x49, 0x1c, 0x04, 0x22, 0x77, 0x61, 0xa1, 0x00, 1430x74, 0x65, 0x72, 0x65, 0x73, 0xba, 0x03, 0x32, 0x2e, 0x00,
1440x00, 0x18, 0x00, 0x22, 0x6f, 0x72, 0x3f, 0x00, 0x02, 0x83, 1440x49, 0x1c, 0x04, 0x22, 0x77, 0x61, 0xa1, 0x00, 0x00, 0x18,
1450x01, 0xb2, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2c, 0x00, 0x74, 1450x00, 0x22, 0x6f, 0x72, 0x3f, 0x00, 0x02, 0x83, 0x01, 0xb2,
1460x75, 0x72, 0x6e, 0xa5, 0x00, 0x03, 0x91, 0x02, 0x50, 0x6f, 1460x69, 0x6c, 0x69, 0x74, 0x79, 0x2c, 0x00, 0x74, 0x75, 0x72,
1470x66, 0x66, 0x2e, 0x00, 1470x6e, 0xa5, 0x00, 0x03, 0x91, 0x02, 0x50, 0x6f, 0x66, 0x66,
1480x2e, 0x00,
148}; 149};
149 150
150const unsigned short help_text_len = 1862; 151const unsigned short help_text_len = 1881;
151const unsigned short help_text_words = 336; 152const unsigned short help_text_words = 337;
153const bool help_valid = true;
152const char quick_help_text[] = "Fill in the railway track according to the clues."; 154const char quick_help_text[] = "Fill in the railway track according to the clues.";
diff --git a/apps/plugins/puzzles/help/twiddle.c b/apps/plugins/puzzles/help/twiddle.c
index 48efbc10ad..a07ba931d4 100644
--- a/apps/plugins/puzzles/help/twiddle.c
+++ b/apps/plugins/puzzles/help/twiddle.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,202 +6,204 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 137, TEXT_CENTER | C_RED }, 9 { 138, TEXT_CENTER | C_RED },
10 { 297, TEXT_CENTER | C_RED }, 10 { 298, TEXT_CENTER | C_RED },
11 LAST_STYLE_ITEM 11 LAST_STYLE_ITEM
12}; 12};
13 13
14/* orig 2926 comp 1866 ratio 0.637731 level 10 saved 1060 */ 14/* orig 2945 comp 1873 ratio 0.635993 level 10 saved 1072 */
15const char help_text[] = { 15const char help_text[] = {
160xf3, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 160xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
170x37, 0x3a, 0x20, 0x54, 0x77, 0x69, 0x64, 0x64, 0x6c, 0x65, 170x37, 0x3a, 0x20, 0x54, 0x77, 0x69, 0x64, 0x64, 0x6c, 0x65,
180x20, 0x00, 0x00, 0x00, 0x0b, 0x00, 0xf2, 0x34, 0x00, 0x69, 180x20, 0x00, 0x2d, 0x01, 0x00, 0x33, 0x00, 0x00, 0x00, 0x1e,
190x73, 0x00, 0x61, 0x00, 0x74, 0x69, 0x6c, 0x65, 0x2d, 0x72, 190x00, 0xf2, 0x34, 0x00, 0x69, 0x73, 0x00, 0x61, 0x00, 0x74,
200x65, 0x61, 0x72, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6d, 0x65, 200x69, 0x6c, 0x65, 0x2d, 0x72, 0x65, 0x61, 0x72, 0x72, 0x61,
210x6e, 0x74, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c, 210x6e, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x70, 0x75,
220x00, 0x76, 0x69, 0x73, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x00, 220x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x00, 0x76, 0x69, 0x73, 0x75,
230x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x00, 0x74, 0x6f, 230x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c,
240x00, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e, 0x00, 0x28, 240x61, 0x72, 0x00, 0x74, 0x6f, 0x00, 0x53, 0x69, 0x78, 0x74,
250x73, 0x65, 0x65, 0x00, 0x63, 0x5f, 0x00, 0xf0, 0x11, 0x00, 250x65, 0x65, 0x6e, 0x00, 0x28, 0x73, 0x65, 0x65, 0x00, 0x63,
260x36, 0x29, 0x3a, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x61, 0x72, 260x72, 0x00, 0xf0, 0x11, 0x00, 0x36, 0x29, 0x3a, 0x00, 0x79,
270x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 270x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76,
280x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 280x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64, 0x00,
290x75, 0x17, 0x00, 0x00, 0x67, 0x00, 0xf0, 0x11, 0x73, 0x2c, 290x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x17, 0x00, 0x00, 0x67,
300x00, 0x65, 0x61, 0x63, 0x68, 0x00, 0x63, 0x6f, 0x6e, 0x74, 300x00, 0xf0, 0x11, 0x73, 0x2c, 0x00, 0x65, 0x61, 0x63, 0x68,
310x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x00, 0x6e, 310x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e,
320x75, 0x6d, 0x62, 0x65, 0x72, 0x2c, 0x00, 0x61, 0x6e, 0x64, 320x67, 0x00, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
330x44, 0x00, 0x50, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x9a, 0x00, 330x2c, 0x00, 0x61, 0x6e, 0x64, 0x44, 0x00, 0x50, 0x72, 0x00,
340x33, 0x74, 0x6f, 0x00, 0x94, 0x00, 0x43, 0x00, 0x74, 0x68, 340x61, 0x69, 0x6d, 0x9a, 0x00, 0x33, 0x74, 0x6f, 0x00, 0x94,
350x65, 0x27, 0x00, 0x40, 0x73, 0x00, 0x69, 0x6e, 0x19, 0x00, 350x00, 0x43, 0x00, 0x74, 0x68, 0x65, 0x27, 0x00, 0x40, 0x73,
360x50, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x40, 0x00, 0xf4, 0x02, 360x00, 0x69, 0x6e, 0x19, 0x00, 0x50, 0x73, 0x63, 0x65, 0x6e,
370x6f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x00, 0x49, 370x64, 0x40, 0x00, 0xf4, 0x02, 0x6f, 0x72, 0x64, 0x65, 0x72,
380x6e, 0x00, 0x62, 0x61, 0x73, 0x69, 0x63, 0xdd, 0x00, 0x12, 380x2e, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x00, 0x62, 0x61, 0x73,
390x2c, 0x4d, 0x00, 0x43, 0x6d, 0x6f, 0x76, 0x65, 0x4e, 0x00, 390x69, 0x63, 0xdd, 0x00, 0x12, 0x2c, 0x4d, 0x00, 0x43, 0x6d,
400x84, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x00, 0x61, 0x92, 400x6f, 0x76, 0x65, 0x4e, 0x00, 0x84, 0x72, 0x6f, 0x74, 0x61,
410x00, 0x50, 0x67, 0x72, 0x6f, 0x75, 0x70, 0xa2, 0x00, 0x10, 410x74, 0x65, 0x00, 0x61, 0x92, 0x00, 0x50, 0x67, 0x72, 0x6f,
420x66, 0x29, 0x00, 0x01, 0xa0, 0x00, 0x60, 0x00, 0x61, 0x62, 420x75, 0x70, 0xa2, 0x00, 0x10, 0x66, 0x29, 0x00, 0x01, 0xa0,
430x6f, 0x75, 0x74, 0x70, 0x00, 0xf0, 0x0f, 0x69, 0x72, 0x00, 430x00, 0x60, 0x00, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x70, 0x00,
440x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x00, 0x63, 0x65, 0x6e, 440xf0, 0x0f, 0x69, 0x72, 0x00, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
450x74, 0x72, 0x65, 0x2e, 0x00, 0x28, 0x4f, 0x72, 0x69, 0x65, 450x6e, 0x00, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x65, 0x2e, 0x00,
460x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x00, 0xf1, 460x28, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
470x03, 0x6e, 0x6f, 0x74, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x69, 470x6f, 0x6e, 0x52, 0x00, 0xf1, 0x03, 0x6e, 0x6f, 0x74, 0x00,
480x66, 0x69, 0x63, 0x61, 0x6e, 0x74, 0x00, 0x69, 0x6e, 0xa8, 480x73, 0x69, 0x67, 0x6e, 0x69, 0x66, 0x69, 0x63, 0x61, 0x6e,
490x00, 0x02, 0x85, 0x00, 0x04, 0x42, 0x01, 0x81, 0x61, 0x6c, 490x74, 0x00, 0x69, 0x6e, 0xa8, 0x00, 0x02, 0x85, 0x00, 0x04,
500x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x1e, 0x01, 0xf5, 0x1b, 500x42, 0x01, 0x81, 0x61, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67,
510x63, 0x61, 0x6e, 0x00, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 510x68, 0x1e, 0x01, 0xf5, 0x1b, 0x63, 0x61, 0x6e, 0x00, 0x73,
520x00, 0x69, 0x74, 0x2e, 0x29, 0x00, 0x4f, 0x6e, 0x00, 0x6d, 520x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x69, 0x74, 0x2e, 0x29,
530x6f, 0x72, 0x65, 0x00, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 530x00, 0x4f, 0x6e, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x00, 0x61,
540x65, 0x64, 0x00, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 540x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x00, 0x73, 0x65,
550x73, 0x2c, 0x2f, 0x00, 0x05, 0xb4, 0x00, 0x6d, 0x6c, 0x61, 550x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x2f, 0x00, 0x05,
560x72, 0x67, 0x65, 0x72, 0xbb, 0x00, 0x01, 0xb6, 0x00, 0x01, 560xb4, 0x00, 0x6d, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0xbb,
570xff, 0x00, 0xd0, 0x00, 0x66, 0x69, 0x72, 0x73, 0x74, 0x00, 570x00, 0x01, 0xb6, 0x00, 0x01, 0xff, 0x00, 0xd0, 0x00, 0x66,
580x73, 0x61, 0x77, 0x00, 0x74, 0x68, 0xf1, 0x00, 0x30, 0x79, 580x69, 0x72, 0x73, 0x74, 0x00, 0x73, 0x61, 0x77, 0x00, 0x74,
590x70, 0x65, 0x22, 0x00, 0x02, 0x8a, 0x00, 0x04, 0x9e, 0x00, 590x68, 0xf1, 0x00, 0x30, 0x79, 0x70, 0x65, 0x22, 0x00, 0x02,
600xf2, 0x13, 0x47, 0x61, 0x6d, 0x65, 0x43, 0x75, 0x62, 0x65, 600x8a, 0x00, 0x04, 0x9e, 0x00, 0xf2, 0x13, 0x47, 0x61, 0x6d,
610x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x60, 0x4d, 0x65, 0x74, 610x65, 0x43, 0x75, 0x62, 0x65, 0x00, 0x67, 0x61, 0x6d, 0x65,
620x72, 0x6f, 0x69, 0x64, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x65, 620x00, 0x60, 0x4d, 0x65, 0x74, 0x72, 0x6f, 0x69, 0x64, 0x00,
630x00, 0x32, 0x27, 0x2e, 0x00, 0x49, 0x28, 0x00, 0xd0, 0x4d, 630x50, 0x72, 0x69, 0x6d, 0x65, 0x00, 0x32, 0x27, 0x2e, 0x00,
640x61, 0x69, 0x6e, 0x00, 0x47, 0x79, 0x72, 0x6f, 0x00, 0x43, 640x49, 0x28, 0x00, 0xd0, 0x4d, 0x61, 0x69, 0x6e, 0x00, 0x47,
650x68, 0x61, 0x79, 0x01, 0x02, 0x41, 0x00, 0x21, 0x61, 0x74, 650x79, 0x72, 0x6f, 0x00, 0x43, 0x68, 0x61, 0x79, 0x01, 0x02,
660x39, 0x00, 0x10, 0x2c, 0x24, 0x00, 0x13, 0x72, 0x40, 0x02, 660x41, 0x00, 0x21, 0x61, 0x74, 0x39, 0x00, 0x10, 0x2c, 0x24,
670x03, 0x61, 0x00, 0x00, 0xb2, 0x00, 0x50, 0x73, 0x6f, 0x6c, 670x00, 0x13, 0x72, 0x40, 0x02, 0x03, 0x61, 0x00, 0x00, 0xb2,
680x76, 0x65, 0x6b, 0x01, 0xf2, 0x05, 0x75, 0x6e, 0x6c, 0x6f, 680x00, 0x50, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x6b, 0x01, 0xf2,
690x63, 0x6b, 0x00, 0x61, 0x00, 0x64, 0x6f, 0x6f, 0x72, 0x2c, 690x05, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x61, 0x00,
700x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x2e, 0x00, 0xb1, 0x73, 700x64, 0x6f, 0x6f, 0x72, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63,
710x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x00, 0x63, 0x61, 0x73, 710x68, 0x2e, 0x00, 0xb1, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61,
720x9f, 0x00, 0x03, 0xae, 0x01, 0xd2, 0x2e, 0x00, 0x49, 0x00, 720x6c, 0x00, 0x63, 0x61, 0x73, 0x9f, 0x00, 0x03, 0xae, 0x01,
730x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x64, 0xc1, 730xd2, 0x2e, 0x00, 0x49, 0x00, 0x64, 0x65, 0x76, 0x65, 0x6c,
740x00, 0x01, 0xa2, 0x00, 0x20, 0x61, 0x73, 0x4a, 0x02, 0x82, 740x6f, 0x70, 0x65, 0x64, 0xc1, 0x00, 0x01, 0xa2, 0x00, 0x20,
750x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x69, 0x73, 0x7c, 0x01, 750x61, 0x73, 0x4a, 0x02, 0x82, 0x65, 0x6e, 0x65, 0x72, 0x61,
760x22, 0x6f, 0x66, 0x8a, 0x00, 0x02, 0x79, 0x00, 0x00, 0xfd, 760x6c, 0x69, 0x73, 0x7c, 0x01, 0x22, 0x6f, 0x66, 0x8a, 0x00,
770x00, 0x35, 0x37, 0x2e, 0x31, 0xdf, 0x02, 0x00, 0x5c, 0x02, 770x02, 0x79, 0x00, 0x00, 0xfd, 0x00, 0x35, 0x37, 0x2e, 0x31,
780x41, 0x72, 0x6f, 0x6c, 0x73, 0xe8, 0x02, 0x66, 0x6f, 0x00, 780xf2, 0x02, 0x00, 0x5c, 0x02, 0x50, 0x72, 0x6f, 0x6c, 0x73,
790x70, 0x6c, 0x61, 0x79, 0x13, 0x02, 0x51, 0x63, 0x6c, 0x69, 790x20, 0xe8, 0x02, 0x66, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79,
800x63, 0x6b, 0xe1, 0x00, 0x45, 0x6d, 0x6f, 0x75, 0x73, 0x16, 800x13, 0x02, 0x51, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xe1, 0x00,
810x01, 0x02, 0xe3, 0x01, 0x02, 0x59, 0x00, 0x1a, 0x65, 0x64, 810x45, 0x6d, 0x6f, 0x75, 0x73, 0x16, 0x01, 0x02, 0xe3, 0x01,
820x01, 0x00, 0xd7, 0x00, 0x46, 0x77, 0x69, 0x73, 0x68, 0x41, 820x02, 0x59, 0x00, 0x1a, 0x65, 0x64, 0x01, 0x00, 0xd7, 0x00,
830x02, 0x05, 0x24, 0x01, 0x02, 0xea, 0x01, 0x32, 0x6d, 0x6f, 830x46, 0x77, 0x69, 0x73, 0x68, 0x41, 0x02, 0x05, 0x24, 0x01,
840x64, 0x6c, 0x02, 0x06, 0xac, 0x01, 0x33, 0x32, 0x78, 0x32, 840x02, 0xea, 0x01, 0x32, 0x6d, 0x6f, 0x64, 0x6c, 0x02, 0x06,
850x45, 0x00, 0x04, 0xfb, 0x00, 0x51, 0x6d, 0x65, 0x61, 0x6e, 850xac, 0x01, 0x33, 0x32, 0x78, 0x32, 0x45, 0x00, 0x04, 0xfb,
860x73, 0x25, 0x00, 0x22, 0x68, 0x61, 0x22, 0x01, 0x02, 0x8a, 860x00, 0x51, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x25, 0x00, 0x22,
870x00, 0xf1, 0x04, 0x61, 0x74, 0x00, 0x61, 0x00, 0x63, 0x6f, 870x68, 0x61, 0x22, 0x01, 0x02, 0x8a, 0x00, 0xf1, 0x04, 0x61,
880x72, 0x6e, 0x65, 0x72, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 880x74, 0x00, 0x61, 0x00, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72,
890x00, 0x77, 0x59, 0x01, 0x07, 0x92, 0x02, 0x43, 0x6d, 0x65, 890x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x77, 0x59, 0x01,
900x65, 0x74, 0xe0, 0x02, 0x0a, 0x2d, 0x02, 0x00, 0x79, 0x00, 900x07, 0x92, 0x02, 0x43, 0x6d, 0x65, 0x65, 0x74, 0xe0, 0x02,
910x02, 0x54, 0x00, 0x82, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x00, 910x0a, 0x2d, 0x02, 0x00, 0x79, 0x00, 0x02, 0x54, 0x00, 0x82,
920x62, 0x65, 0x82, 0x00, 0x00, 0x16, 0x03, 0xa4, 0x33, 0x78, 920x6d, 0x69, 0x67, 0x68, 0x74, 0x00, 0x62, 0x65, 0x82, 0x00,
930x33, 0x00, 0x6f, 0x72, 0x00, 0x65, 0x76, 0x65, 0x36, 0x00, 930x00, 0x16, 0x03, 0xa4, 0x33, 0x78, 0x33, 0x00, 0x6f, 0x72,
940x11, 0x74, 0xea, 0x03, 0x53, 0x6d, 0x65, 0x3b, 0x00, 0x69, 940x00, 0x65, 0x76, 0x65, 0x36, 0x00, 0x11, 0x74, 0xea, 0x03,
950xe6, 0x00, 0x2c, 0x69, 0x7a, 0xf2, 0x00, 0x60, 0x69, 0x73, 950x53, 0x6d, 0x65, 0x3b, 0x00, 0x69, 0xe6, 0x00, 0x2c, 0x69,
960x00, 0x6f, 0x64, 0x64, 0x12, 0x00, 0x12, 0x6e, 0xcf, 0x01, 960x7a, 0xf2, 0x00, 0x60, 0x69, 0x73, 0x00, 0x6f, 0x64, 0x64,
970x53, 0x69, 0x6d, 0x70, 0x6c, 0x79, 0xab, 0x00, 0x0a, 0x2b, 970x12, 0x00, 0x12, 0x6e, 0xcf, 0x01, 0x53, 0x69, 0x6d, 0x70,
980x01, 0x00, 0x9c, 0x00, 0x0b, 0x3e, 0x00, 0x01, 0x2a, 0x01, 980x6c, 0x79, 0xab, 0x00, 0x0a, 0x2b, 0x01, 0x00, 0x9c, 0x00,
990x00, 0x09, 0x03, 0x07, 0x2a, 0x01, 0x30, 0x00, 0x00, 0x43, 990x0b, 0x3e, 0x00, 0x01, 0x2a, 0x01, 0x00, 0x09, 0x03, 0x07,
1000x3d, 0x00, 0x00, 0x93, 0x00, 0x41, 0x77, 0x69, 0x74, 0x68, 1000x2a, 0x01, 0x30, 0x00, 0x00, 0x43, 0x3d, 0x00, 0x00, 0x93,
1010x2f, 0x00, 0x43, 0x6c, 0x65, 0x66, 0x74, 0x7f, 0x01, 0x54, 1010x00, 0x41, 0x77, 0x69, 0x74, 0x68, 0x2f, 0x00, 0x43, 0x6c,
1020x62, 0x75, 0x74, 0x74, 0x6f, 0xe5, 0x02, 0x11, 0x73, 0x1e, 1020x65, 0x66, 0x74, 0x7f, 0x01, 0x54, 0x62, 0x75, 0x74, 0x74,
1030x00, 0x02, 0x76, 0x01, 0x50, 0x61, 0x6e, 0x74, 0x69, 0x63, 1030x6f, 0xe5, 0x02, 0x11, 0x73, 0x1e, 0x00, 0x02, 0x76, 0x01,
1040x43, 0x02, 0x5f, 0x77, 0x69, 0x73, 0x65, 0x2e, 0x45, 0x00, 1040x50, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x43, 0x02, 0x5f, 0x77,
1050x00, 0x12, 0x72, 0xf3, 0x00, 0x0a, 0x40, 0x00, 0x37, 0x69, 1050x69, 0x73, 0x65, 0x2e, 0x45, 0x00, 0x00, 0x12, 0x72, 0xf3,
1060x74, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x59, 0x45, 0x03, 1060x00, 0x0a, 0x40, 0x00, 0x37, 0x69, 0x74, 0x00, 0x35, 0x00,
1070x42, 0x61, 0x6c, 0x73, 0x6f, 0x09, 0x04, 0x95, 0x61, 0x6e, 1070x33, 0x00, 0x00, 0x59, 0x45, 0x03, 0x42, 0x61, 0x6c, 0x73,
1080x00, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x6e, 0xb6, 0x00, 0x51, 1080x6f, 0x09, 0x04, 0x95, 0x61, 0x6e, 0x00, 0x6f, 0x75, 0x74,
1090x61, 0x72, 0x6f, 0x75, 0x6e, 0xf4, 0x00, 0x02, 0xb1, 0x04, 1090x6c, 0x69, 0x6e, 0xb6, 0x00, 0x51, 0x61, 0x72, 0x6f, 0x75,
1100x05, 0x62, 0x00, 0xcb, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 1100x6e, 0xf4, 0x00, 0x02, 0xb1, 0x04, 0x05, 0x62, 0x00, 0xcb,
1110x00, 0x6b, 0x65, 0x79, 0x73, 0x3b, 0x25, 0x01, 0x05, 0x3f, 1110x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b, 0x65, 0x79,
1120x01, 0x20, 0x61, 0x62, 0x55, 0x00, 0x10, 0x28, 0xf0, 0x01, 1120x73, 0x3b, 0x25, 0x01, 0x05, 0x3f, 0x01, 0x20, 0x61, 0x62,
1130xb0, 0x62, 0x79, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 1130x55, 0x00, 0x10, 0x28, 0xf0, 0x01, 0xb0, 0x62, 0x79, 0x00,
1140x74, 0x2c, 0x7a, 0x01, 0x02, 0xaf, 0x03, 0x80, 0x29, 0x2e, 1140x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x7a, 0x01,
1150x00, 0x50, 0x72, 0x65, 0x73, 0x73, 0xbb, 0x00, 0x01, 0xb6, 1150x02, 0xaf, 0x03, 0x80, 0x29, 0x2e, 0x00, 0x50, 0x72, 0x65,
1160x00, 0x50, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x54, 0x00, 0x00, 1160x73, 0x73, 0xbb, 0x00, 0x01, 0xb6, 0x00, 0x50, 0x65, 0x74,
1170x24, 0x00, 0x40, 0x73, 0x70, 0x61, 0x63, 0x46, 0x02, 0x64, 1170x75, 0x72, 0x6e, 0x54, 0x00, 0x00, 0x24, 0x00, 0x40, 0x73,
1180x72, 0x00, 0x77, 0x69, 0x6c, 0x6c, 0x3f, 0x02, 0x03, 0x7c, 1180x70, 0x61, 0x63, 0x46, 0x02, 0x64, 0x72, 0x00, 0x77, 0x69,
1190x00, 0x10, 0x72, 0x88, 0x05, 0x04, 0xa4, 0x00, 0x08, 0x0e, 1190x6c, 0x6c, 0x3f, 0x02, 0x03, 0x7c, 0x00, 0x10, 0x72, 0x88,
1200x01, 0x00, 0x3a, 0x00, 0x06, 0x0d, 0x00, 0x20, 0x72, 0x65, 1200x05, 0x04, 0xa4, 0x00, 0x08, 0x0e, 0x01, 0x00, 0x3a, 0x00,
1210x51, 0x03, 0x60, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0xf3, 1210x06, 0x0d, 0x00, 0x20, 0x72, 0x65, 0x51, 0x03, 0x60, 0x74,
1220x00, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x43, 0x00, 0x20, 0x61, 1220x69, 0x76, 0x65, 0x6c, 0x79, 0xf3, 0x00, 0x41, 0x28, 0x41,
1230x63, 0x2c, 0x03, 0xd0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 1230x6c, 0x6c, 0x43, 0x00, 0x20, 0x61, 0x63, 0x2c, 0x03, 0xd0,
1240x72, 0x69, 0x62, 0x65, 0x64, 0x00, 0x69, 0x7d, 0x04, 0x11, 1240x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
1250x63, 0x42, 0x03, 0x31, 0x32, 0x2e, 0x31, 0xab, 0x05, 0x01, 1250x64, 0x00, 0x69, 0x7d, 0x04, 0x11, 0x63, 0x42, 0x03, 0x31,
1260x19, 0x01, 0xb1, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 1260x32, 0x2e, 0x31, 0xab, 0x05, 0x01, 0x19, 0x01, 0xb1, 0x61,
1270x6c, 0x65, 0x2e, 0x29, 0x4b, 0x03, 0x15, 0x32, 0x4b, 0x03, 1270x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29,
1280xa8, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 1280x4b, 0x03, 0x15, 0x32, 0x4b, 0x03, 0xb7, 0x70, 0x61, 0x72,
1290x73, 0x35, 0x06, 0x50, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x7b, 1290x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x35, 0x06,
1300x02, 0x30, 0x73, 0x65, 0x76, 0x97, 0x03, 0x00, 0xce, 0x05, 1300x50, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x7b, 0x02, 0x30, 0x73,
1310x53, 0x66, 0x69, 0x67, 0x75, 0x72, 0x9e, 0x03, 0x12, 0x70, 1310x65, 0x76, 0x97, 0x03, 0x00, 0xce, 0x05, 0x53, 0x66, 0x69,
1320x79, 0x00, 0x31, 0x76, 0x69, 0x61, 0x89, 0x00, 0x83, 0x60, 1320x67, 0x75, 0x72, 0x9e, 0x03, 0x12, 0x70, 0x79, 0x00, 0x31,
1330x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x27, 0x19, 0x00, 0x32, 1330x76, 0x69, 0x61, 0x89, 0x00, 0x83, 0x60, 0x43, 0x75, 0x73,
1340x00, 0x6f, 0x6e, 0x17, 0x00, 0xf5, 0x00, 0x54, 0x79, 0x70, 1340x74, 0x6f, 0x6d, 0x27, 0x19, 0x00, 0x32, 0x00, 0x6f, 0x6e,
1350x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x3a, 0x00, 0x00, 1350x17, 0x00, 0xf5, 0x00, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00,
1360x00, 0x2d, 0xad, 0x01, 0x04, 0x4e, 0x00, 0x02, 0x0a, 0x01, 1360x6d, 0x65, 0x6e, 0x75, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0xad,
1370x51, 0x77, 0x69, 0x64, 0x74, 0x68, 0x1b, 0x06, 0x21, 0x68, 1370x01, 0x04, 0x4e, 0x00, 0x02, 0x0a, 0x01, 0x51, 0x77, 0x69,
1380x65, 0xf3, 0x01, 0x03, 0x74, 0x02, 0x03, 0x75, 0x04, 0x00, 1380x64, 0x74, 0x68, 0x1b, 0x06, 0x21, 0x68, 0x65, 0xf3, 0x01,
1390xb3, 0x01, 0x1f, 0x2e, 0x3f, 0x00, 0x08, 0x04, 0xe5, 0x02, 1390x03, 0x74, 0x02, 0x03, 0x75, 0x04, 0x00, 0xb3, 0x01, 0x1f,
1400x03, 0x49, 0x01, 0x11, 0x62, 0x96, 0x04, 0x01, 0x3d, 0x04, 1400x2e, 0x3f, 0x00, 0x08, 0x04, 0xe5, 0x02, 0x03, 0x49, 0x01,
1410x04, 0x34, 0x02, 0x05, 0x19, 0x03, 0x0a, 0x47, 0x00, 0x52, 1410x11, 0x62, 0x96, 0x04, 0x01, 0x3d, 0x04, 0x04, 0x34, 0x02,
1420x61, 0x73, 0x6b, 0x00, 0x66, 0x42, 0x03, 0x25, 0x72, 0x79, 1420x05, 0x19, 0x03, 0x0a, 0x47, 0x00, 0x52, 0x61, 0x73, 0x6b,
1430xfb, 0x01, 0x16, 0x6e, 0x28, 0x02, 0x20, 0x74, 0x6f, 0x71, 1430x00, 0x66, 0x42, 0x03, 0x25, 0x72, 0x79, 0xfb, 0x01, 0x16,
1440x03, 0x30, 0x64, 0x69, 0x73, 0x70, 0x03, 0x40, 0x75, 0x69, 1440x6e, 0x28, 0x02, 0x20, 0x74, 0x6f, 0x71, 0x03, 0x30, 0x64,
1450x73, 0x68, 0x42, 0x01, 0x20, 0x00, 0x28, 0x20, 0x00, 0x03, 1450x69, 0x73, 0x70, 0x03, 0x40, 0x75, 0x69, 0x73, 0x68, 0x42,
1460x08, 0x02, 0x11, 0x29, 0x09, 0x02, 0x1b, 0x79, 0x51, 0x00, 1460x01, 0x20, 0x00, 0x28, 0x20, 0x00, 0x03, 0x08, 0x02, 0x11,
1470x12, 0x61, 0x56, 0x03, 0x57, 0x69, 0x66, 0x69, 0x65, 0x64, 1470x29, 0x09, 0x02, 0x1b, 0x79, 0x51, 0x00, 0x12, 0x61, 0x56,
1480x9c, 0x05, 0x02, 0x27, 0x04, 0x02, 0x56, 0x05, 0x05, 0x7d, 1480x03, 0x57, 0x69, 0x66, 0x69, 0x65, 0x64, 0x9c, 0x05, 0x02,
1490x04, 0x10, 0x73, 0xbb, 0x00, 0x30, 0x69, 0x64, 0x65, 0xff, 1490x27, 0x04, 0x02, 0x56, 0x05, 0x05, 0x7d, 0x04, 0x10, 0x73,
1500x01, 0x24, 0x61, 0x6c, 0x02, 0x07, 0x05, 0x80, 0x04, 0x0e, 1500xbb, 0x00, 0x30, 0x69, 0x64, 0x65, 0xff, 0x01, 0x24, 0x61,
1510x49, 0x00, 0x08, 0x3f, 0x07, 0x48, 0x6a, 0x75, 0x73, 0x74, 1510x6c, 0x02, 0x07, 0x05, 0x80, 0x04, 0x0e, 0x49, 0x00, 0x08,
1520x44, 0x07, 0x13, 0x61, 0x19, 0x02, 0x13, 0x31, 0x43, 0x07, 1520x3f, 0x07, 0x48, 0x6a, 0x75, 0x73, 0x74, 0x44, 0x07, 0x13,
1530x00, 0x0c, 0x00, 0x02, 0x33, 0x06, 0x45, 0x72, 0x6f, 0x77, 1530x61, 0x19, 0x02, 0x13, 0x31, 0x43, 0x07, 0x00, 0x0c, 0x00,
1540x2c, 0x1f, 0x00, 0x17, 0x32, 0x1f, 0x00, 0x63, 0x73, 0x65, 1540x02, 0x33, 0x06, 0x45, 0x72, 0x6f, 0x77, 0x2c, 0x1f, 0x00,
1550x63, 0x6f, 0x6e, 0x64, 0x20, 0x00, 0x8f, 0x6e, 0x64, 0x00, 1550x17, 0x32, 0x1f, 0x00, 0x63, 0x73, 0x65, 0x63, 0x6f, 0x6e,
1560x73, 0x6f, 0x00, 0x6f, 0x6e, 0x6f, 0x01, 0x05, 0x30, 0x77, 1560x64, 0x20, 0x00, 0x8f, 0x6e, 0x64, 0x00, 0x73, 0x6f, 0x00,
1570x68, 0x65, 0xbf, 0x00, 0x01, 0x39, 0x00, 0x17, 0x6f, 0x2a, 1570x6f, 0x6e, 0x6f, 0x01, 0x05, 0x30, 0x77, 0x68, 0x65, 0xbf,
1580x07, 0x24, 0x6f, 0x66, 0xc9, 0x04, 0x32, 0x61, 0x74, 0x74, 1580x00, 0x01, 0x39, 0x00, 0x17, 0x6f, 0x2a, 0x07, 0x24, 0x6f,
1590xbf, 0x00, 0x12, 0x66, 0x2c, 0x08, 0x04, 0x12, 0x01, 0x14, 1590x66, 0xc9, 0x04, 0x32, 0x61, 0x74, 0x74, 0xbf, 0x00, 0x12,
1600x6e, 0x30, 0x00, 0x24, 0x62, 0x6c, 0xd9, 0x01, 0x03, 0x28, 1600x66, 0x2c, 0x08, 0x04, 0x12, 0x01, 0x14, 0x6e, 0x30, 0x00,
1610x08, 0x01, 0x66, 0x04, 0x01, 0x1d, 0x03, 0x01, 0x37, 0x05, 1610x24, 0x62, 0x6c, 0xd9, 0x01, 0x03, 0x28, 0x08, 0x01, 0x66,
1620xf0, 0x01, 0x61, 0x00, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 1620x04, 0x01, 0x1d, 0x03, 0x01, 0x37, 0x05, 0xf0, 0x01, 0x61,
1630x6c, 0x65, 0x00, 0x64, 0x72, 0x61, 0x77, 0x6e, 0x39, 0x01, 1630x00, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00,
1640x44, 0x69, 0x74, 0x2e, 0x00, 0xf4, 0x02, 0x04, 0x1e, 0x00, 1640x64, 0x72, 0x61, 0x77, 0x6e, 0x39, 0x01, 0x44, 0x69, 0x74,
1650x51, 0x73, 0x00, 0x6d, 0x75, 0x73, 0x11, 0x05, 0x01, 0x54, 1650x2e, 0x00, 0xf4, 0x02, 0x04, 0x1e, 0x00, 0x51, 0x73, 0x00,
1660x05, 0x00, 0x7e, 0x03, 0x71, 0x75, 0x70, 0x77, 0x61, 0x72, 1660x6d, 0x75, 0x73, 0x11, 0x05, 0x01, 0x54, 0x05, 0x00, 0x7e,
1670x64, 0x73, 0x7a, 0x05, 0x77, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 1670x03, 0x71, 0x75, 0x70, 0x77, 0x61, 0x72, 0x64, 0x73, 0x7a,
1680x74, 0x65, 0x48, 0x02, 0x0e, 0xfc, 0x01, 0x02, 0xab, 0x01, 1680x05, 0x77, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48,
1690x50, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x98, 0x07, 0x51, 0x68, 1690x02, 0x0e, 0xfc, 0x01, 0x02, 0xab, 0x01, 0x50, 0x6c, 0x69,
1700x75, 0x66, 0x66, 0x6c, 0x6f, 0x08, 0x23, 0x70, 0x65, 0xe8, 1700x6d, 0x69, 0x74, 0x98, 0x07, 0x51, 0x68, 0x75, 0x66, 0x66,
1710x02, 0x02, 0x01, 0x02, 0x94, 0x70, 0x65, 0x72, 0x66, 0x6f, 1710x6c, 0x6f, 0x08, 0x23, 0x70, 0x65, 0xe8, 0x02, 0x02, 0x01,
1720x72, 0x6d, 0x65, 0x64, 0xd8, 0x02, 0x02, 0x92, 0x02, 0x17, 1720x02, 0x94, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
1730x42, 0x0e, 0x04, 0x04, 0x38, 0x03, 0x01, 0xc8, 0x00, 0x02, 1730x64, 0xd8, 0x02, 0x02, 0x92, 0x02, 0x17, 0x42, 0x0e, 0x04,
1740x4a, 0x00, 0x16, 0x65, 0x48, 0x02, 0x00, 0x9a, 0x04, 0x11, 1740x04, 0x38, 0x03, 0x01, 0xc8, 0x00, 0x02, 0x4a, 0x00, 0x16,
1750x75, 0xfb, 0x01, 0x00, 0x8a, 0x02, 0x38, 0x6e, 0x79, 0x00, 1750x65, 0x48, 0x02, 0x00, 0x9a, 0x04, 0x11, 0x75, 0xfb, 0x01,
1760x88, 0x09, 0x14, 0x69, 0x91, 0x08, 0x20, 0x61, 0x73, 0x77, 1760x00, 0x8a, 0x02, 0x38, 0x6e, 0x79, 0x00, 0x88, 0x09, 0x14,
1770x03, 0x11, 0x62, 0x22, 0x01, 0x21, 0x61, 0x73, 0x28, 0x00, 1770x69, 0x91, 0x08, 0x20, 0x61, 0x73, 0x77, 0x03, 0x11, 0x62,
1780x10, 0x6f, 0x6e, 0x01, 0x15, 0x2e, 0xb5, 0x00, 0x82, 0x6f, 1780x22, 0x01, 0x21, 0x61, 0x73, 0x28, 0x00, 0x10, 0x6f, 0x6e,
1790x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x3c, 0x07, 0x81, 1790x01, 0x15, 0x2e, 0xb5, 0x00, 0x82, 0x6f, 0x76, 0x65, 0x72,
1800x62, 0x79, 0x00, 0x72, 0x65, 0x71, 0x75, 0x65, 0x9e, 0x02, 1800x72, 0x69, 0x64, 0x65, 0x3c, 0x07, 0x81, 0x62, 0x79, 0x00,
1810x00, 0xa4, 0x07, 0x54, 0x72, 0x65, 0x63, 0x69, 0x73, 0x45, 1810x72, 0x65, 0x71, 0x75, 0x65, 0x9e, 0x02, 0x00, 0xa4, 0x07,
1820x09, 0x01, 0x12, 0x03, 0x05, 0xd3, 0x00, 0x00, 0x19, 0x05, 1820x54, 0x72, 0x65, 0x63, 0x69, 0x73, 0x45, 0x09, 0x01, 0x12,
1830x1c, 0x73, 0xcf, 0x00, 0x71, 0x2e, 0x00, 0x54, 0x79, 0x70, 1830x03, 0x05, 0xd3, 0x00, 0x00, 0x19, 0x05, 0x1c, 0x73, 0xcf,
1840x69, 0x63, 0xfd, 0x09, 0x09, 0x95, 0x09, 0x21, 0x68, 0x65, 1840x00, 0x71, 0x2e, 0x00, 0x54, 0x79, 0x70, 0x69, 0x63, 0xfd,
1850xfb, 0x00, 0x10, 0x64, 0x17, 0x04, 0x33, 0x6d, 0x69, 0x6e, 1850x09, 0x09, 0x95, 0x09, 0x21, 0x68, 0x65, 0xfb, 0x00, 0x10,
1860x46, 0x01, 0x03, 0x5f, 0x00, 0x3e, 0x73, 0x65, 0x74, 0x5c, 1860x64, 0x17, 0x04, 0x33, 0x6d, 0x69, 0x6e, 0x46, 0x01, 0x03,
1870x00, 0x00, 0xaf, 0x00, 0x00, 0x7e, 0x04, 0x31, 0x76, 0x65, 1870x5f, 0x00, 0x3e, 0x73, 0x65, 0x74, 0x5c, 0x00, 0x00, 0xaf,
1880x72, 0x5a, 0x09, 0xa0, 0x6d, 0x00, 0x65, 0x78, 0x61, 0x63, 1880x00, 0x00, 0x7e, 0x04, 0x31, 0x76, 0x65, 0x72, 0x5a, 0x09,
1890x74, 0x6c, 0x79, 0x2c, 0xfd, 0x00, 0x01, 0xf8, 0x00, 0x01, 1890xa0, 0x6d, 0x00, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79,
1900x0f, 0x02, 0xd1, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x00, 0x28, 1900x2c, 0xfd, 0x00, 0x01, 0xf8, 0x00, 0x01, 0x0f, 0x02, 0xd1,
1910x73, 0x61, 0x79, 0x29, 0x00, 0x61, 0x02, 0x07, 0x11, 0x2d, 1910x6e, 0x73, 0x77, 0x65, 0x72, 0x00, 0x28, 0x73, 0x61, 0x79,
1920xb4, 0x05, 0x04, 0x33, 0x01, 0x01, 0x9a, 0x05, 0x09, 0x19, 1920x29, 0x00, 0x61, 0x02, 0x07, 0x11, 0x2d, 0xb4, 0x05, 0x04,
1930x00, 0x50, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x92, 0x02, 0x21, 1930x33, 0x01, 0x01, 0x9a, 0x05, 0x09, 0x19, 0x00, 0x50, 0x6f,
1940x4e, 0x6f, 0xcf, 0x01, 0x11, 0x61, 0x60, 0x00, 0x02, 0xf1, 1940x6c, 0x75, 0x74, 0x69, 0x92, 0x02, 0x21, 0x4e, 0x6f, 0xcf,
1950x06, 0x02, 0x7a, 0x00, 0x07, 0x68, 0x02, 0x16, 0x2c, 0x1c, 1950x01, 0x11, 0x61, 0x60, 0x00, 0x02, 0xf1, 0x06, 0x02, 0x7a,
1960x00, 0x81, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x00, 0x69, 1960x00, 0x07, 0x68, 0x02, 0x16, 0x2c, 0x1c, 0x00, 0x81, 0x6c,
1970x61, 0x01, 0x01, 0x37, 0x00, 0x04, 0x4b, 0x00, 0x71, 0x73, 1970x69, 0x6b, 0x65, 0x6c, 0x79, 0x00, 0x69, 0x61, 0x01, 0x01,
1980x00, 0x73, 0x68, 0x6f, 0x72, 0x74, 0xc7, 0x02, 0x22, 0x61, 1980x37, 0x00, 0x04, 0x4b, 0x00, 0x71, 0x73, 0x00, 0x73, 0x68,
1990x6e, 0x55, 0x02, 0x00, 0xca, 0x05, 0x82, 0x74, 0x00, 0x6c, 1990x6f, 0x72, 0x74, 0xc7, 0x02, 0x22, 0x61, 0x6e, 0x55, 0x02,
2000x65, 0x6e, 0x67, 0x74, 0x68, 0xc5, 0x01, 0x01, 0xc5, 0x05, 2000x00, 0xca, 0x05, 0x82, 0x74, 0x00, 0x6c, 0x65, 0x6e, 0x67,
2010x01, 0x28, 0x0a, 0x12, 0x6f, 0x65, 0x02, 0x80, 0x73, 0x73, 2010x74, 0x68, 0xc5, 0x01, 0x01, 0xc5, 0x05, 0x01, 0x28, 0x0a,
2020x69, 0x62, 0x6c, 0x65, 0x2e, 0x00, 2020x12, 0x6f, 0x65, 0x02, 0x80, 0x73, 0x73, 0x69, 0x62, 0x6c,
2030x65, 0x2e, 0x00,
203}; 204};
204 205
205const unsigned short help_text_len = 2926; 206const unsigned short help_text_len = 2945;
206const unsigned short help_text_words = 548; 207const unsigned short help_text_words = 549;
208const bool help_valid = true;
207const char quick_help_text[] = "Rotate the tiles around themselves to arrange them into order."; 209const char quick_help_text[] = "Rotate the tiles around themselves to arrange them into order.";
diff --git a/apps/plugins/puzzles/help/undead.c b/apps/plugins/puzzles/help/undead.c
index c08801003a..dc75e88743 100644
--- a/apps/plugins/puzzles/help/undead.c
+++ b/apps/plugins/puzzles/help/undead.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,230 +6,247 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 202, TEXT_CENTER | C_RED }, 9 { 203, TEXT_CENTER | C_RED },
10 { 282, TEXT_UNDERLINE }, 10 { 291, TEXT_UNDERLINE },
11 { 576, TEXT_CENTER | C_RED }, 11 { 591, TEXT_CENTER | C_RED },
12 { 593, TEXT_UNDERLINE }, 12 { 608, TEXT_UNDERLINE },
13 { 594, TEXT_UNDERLINE }, 13 { 609, TEXT_UNDERLINE },
14 { 604, TEXT_UNDERLINE }, 14 { 619, TEXT_UNDERLINE },
15 { 631, TEXT_CENTER | C_RED },
15 LAST_STYLE_ITEM 16 LAST_STYLE_ITEM
16}; 17};
17 18
18/* orig 3286 comp 2109 ratio 0.641814 level 10 saved 1177 */ 19/* orig 3574 comp 2258 ratio 0.631785 level 10 saved 1316 */
19const char help_text[] = { 20const char help_text[] = {
200xf0, 0x2c, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 210xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x33, 0x37, 0x3a, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x61, 0x64, 220x33, 0x37, 0x3a, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x61, 0x64,
220x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 230x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x19, 0x00, 0x00, 0x00,
230x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 240x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69,
240x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 250x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64,
250x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 260x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65,
260x65, 0x11, 0x00, 0xf3, 0x16, 0x77, 0x68, 0x69, 0x63, 0x68, 270x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x11, 0x00, 0xf3,
270x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x64, 280x16, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x63, 0x6f, 0x6e,
280x69, 0x61, 0x67, 0x6f, 0x6e, 0x61, 0x6c, 0x00, 0x6d, 0x69, 290x74, 0x61, 0x69, 0x6e, 0x00, 0x64, 0x69, 0x61, 0x67, 0x6f,
290x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x00, 0x45, 0x76, 0x65, 300x6e, 0x61, 0x6c, 0x00, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72,
300x72, 0x79, 0x37, 0x00, 0x03, 0x2d, 0x00, 0x83, 0x69, 0x73, 310x73, 0x2e, 0x00, 0x45, 0x76, 0x65, 0x72, 0x79, 0x37, 0x00,
310x00, 0x6e, 0x6f, 0x74, 0x00, 0x61, 0x25, 0x00, 0xf1, 0x08, 320x03, 0x2d, 0x00, 0x83, 0x69, 0x73, 0x00, 0x6e, 0x6f, 0x74,
320x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x62, 0x65, 0x00, 0x66, 330x00, 0x61, 0x25, 0x00, 0xf1, 0x08, 0x00, 0x6d, 0x75, 0x73,
330x69, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x77, 0x69, 0x74, 0x68, 340x74, 0x00, 0x62, 0x65, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x65,
340x00, 0x6f, 0x6e, 0x5e, 0x00, 0xb0, 0x74, 0x68, 0x72, 0x65, 350x64, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x6f, 0x6e, 0x5e,
350x65, 0x00, 0x74, 0x79, 0x70, 0x65, 0x73, 0x0f, 0x00, 0x11, 360x00, 0xb0, 0x74, 0x68, 0x72, 0x65, 0x65, 0x00, 0x74, 0x79,
360x75, 0xa0, 0x00, 0x90, 0x00, 0x6d, 0x6f, 0x6e, 0x73, 0x74, 370x70, 0x65, 0x73, 0x0f, 0x00, 0x11, 0x75, 0xb3, 0x00, 0x90,
370x65, 0x72, 0x3a, 0x98, 0x00, 0xf2, 0x12, 0x68, 0x6f, 0x73, 380x00, 0x6d, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x72, 0x3a, 0x98,
380x74, 0x2c, 0x00, 0x61, 0x00, 0x76, 0x61, 0x6d, 0x70, 0x69, 390x00, 0xf2, 0x12, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x00, 0x61,
390x72, 0x65, 0x2c, 0x00, 0x6f, 0x72, 0x00, 0x61, 0x00, 0x7a, 400x00, 0x76, 0x61, 0x6d, 0x70, 0x69, 0x72, 0x65, 0x2c, 0x00,
400x6f, 0x6d, 0x62, 0x69, 0x65, 0x2e, 0x00, 0x00, 0x00, 0x56, 410x6f, 0x72, 0x00, 0x61, 0x00, 0x7a, 0x6f, 0x6d, 0x62, 0x69,
410x18, 0x00, 0x50, 0x73, 0x00, 0x63, 0x61, 0x6e, 0x65, 0x00, 420x65, 0x2e, 0x00, 0x00, 0x00, 0x56, 0x18, 0x00, 0x50, 0x73,
420x30, 0x73, 0x65, 0x65, 0xa7, 0x00, 0xb1, 0x72, 0x65, 0x63, 430x00, 0x63, 0x61, 0x6e, 0x65, 0x00, 0x30, 0x73, 0x65, 0x65,
430x74, 0x6c, 0x79, 0x2c, 0x00, 0x62, 0x75, 0x74, 0xe8, 0x00, 440xa7, 0x00, 0xb1, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x2c,
440x80, 0x69, 0x6e, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0xa4, 450x00, 0x62, 0x75, 0x74, 0xe8, 0x00, 0x80, 0x69, 0x6e, 0x76,
450x00, 0xf6, 0x00, 0x65, 0x6e, 0x00, 0x72, 0x65, 0x66, 0x6c, 460x69, 0x73, 0x69, 0x62, 0x6c, 0xa4, 0x00, 0xf6, 0x00, 0x65,
460x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x69, 0x6e, 0xcc, 0x00, 470x6e, 0x00, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x65,
470x10, 0x47, 0x6d, 0x00, 0x11, 0x73, 0x30, 0x00, 0xf0, 0x08, 480x64, 0x00, 0x69, 0x6e, 0xcc, 0x00, 0x10, 0x47, 0x6d, 0x00,
480x74, 0x68, 0x65, 0x00, 0x6f, 0x70, 0x70, 0x6f, 0x73, 0x69, 490x11, 0x73, 0x30, 0x00, 0xf0, 0x08, 0x74, 0x68, 0x65, 0x00,
490x74, 0x65, 0x00, 0x77, 0x61, 0x79, 0x00, 0x72, 0x6f, 0x75, 500x6f, 0x70, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x00, 0x77,
500x6e, 0x64, 0x3a, 0x18, 0x00, 0x19, 0x79, 0x6b, 0x00, 0x06, 510x61, 0x79, 0x00, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x18,
510x40, 0x00, 0x0f, 0x6d, 0x00, 0x06, 0x95, 0x6c, 0x6f, 0x6f, 520x00, 0x19, 0x79, 0x6b, 0x00, 0x06, 0x40, 0x00, 0x0f, 0x6d,
520x6b, 0x65, 0x64, 0x00, 0x61, 0x74, 0x98, 0x00, 0x31, 0x2e, 530x00, 0x06, 0x95, 0x6c, 0x6f, 0x6f, 0x6b, 0x65, 0x64, 0x00,
530x00, 0x5a, 0xc1, 0x00, 0x02, 0x6c, 0x00, 0x04, 0x2d, 0x00, 540x61, 0x74, 0x98, 0x00, 0x31, 0x2e, 0x00, 0x5a, 0xc1, 0x00,
540xd7, 0x62, 0x79, 0x00, 0x61, 0x6e, 0x79, 0x00, 0x6d, 0x65, 550x02, 0x6c, 0x00, 0x04, 0x2d, 0x00, 0xd7, 0x62, 0x79, 0x00,
550x61, 0x6e, 0x73, 0x2e, 0xa4, 0x01, 0x91, 0x61, 0x6c, 0x73, 560x61, 0x6e, 0x79, 0x00, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x2e,
560x6f, 0x00, 0x74, 0x6f, 0x6c, 0x64, 0x96, 0x00, 0xc0, 0x74, 570xa4, 0x01, 0x91, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x74, 0x6f,
570x6f, 0x74, 0x61, 0x6c, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 580x6c, 0x64, 0x96, 0x00, 0xc0, 0x74, 0x6f, 0x74, 0x61, 0x6c,
580x72, 0x34, 0x01, 0x41, 0x65, 0x61, 0x63, 0x68, 0x42, 0x01, 590x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x34, 0x01, 0x41,
590x00, 0x0d, 0x00, 0x03, 0x3a, 0x01, 0x00, 0x96, 0x00, 0x00, 600x65, 0x61, 0x63, 0x68, 0x42, 0x01, 0x00, 0x0d, 0x00, 0x03,
600x2c, 0x00, 0x00, 0xd6, 0x01, 0x30, 0x2e, 0x00, 0x41, 0x40, 610x3a, 0x01, 0x00, 0x96, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xd6,
610x00, 0x11, 0x61, 0xc1, 0x00, 0x01, 0x16, 0x00, 0x33, 0x65, 620x01, 0x30, 0x2e, 0x00, 0x41, 0x40, 0x00, 0x11, 0x61, 0xc1,
620x64, 0x67, 0x7d, 0x01, 0x12, 0x65, 0xf8, 0x01, 0x01, 0xbb, 630x00, 0x01, 0x16, 0x00, 0x33, 0x65, 0x64, 0x67, 0x7d, 0x01,
630x01, 0x63, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x59, 0x00, 640x12, 0x65, 0xf8, 0x01, 0x01, 0xbb, 0x01, 0x63, 0x72, 0x69,
640x24, 0x73, 0x2c, 0xcc, 0x01, 0xd1, 0x6e, 0x64, 0x69, 0x63, 650x74, 0x74, 0x65, 0x6e, 0x59, 0x00, 0x24, 0x73, 0x2c, 0xcc,
650x61, 0x74, 0x65, 0x00, 0x68, 0x6f, 0x77, 0x00, 0x6d, 0x9f, 660x01, 0xd1, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00,
660x00, 0x02, 0x63, 0x00, 0x1a, 0x73, 0x06, 0x01, 0x51, 0x66, 670x68, 0x6f, 0x77, 0x00, 0x6d, 0x9f, 0x00, 0x02, 0x63, 0x00,
670x00, 0x79, 0x6f, 0x75, 0xea, 0x00, 0x57, 0x00, 0x69, 0x6e, 680x1a, 0x73, 0x06, 0x01, 0x51, 0x66, 0x00, 0x79, 0x6f, 0x75,
680x74, 0x6f, 0x5c, 0x00, 0xa0, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 690xea, 0x00, 0x57, 0x00, 0x69, 0x6e, 0x74, 0x6f, 0x5c, 0x00,
690x61, 0x00, 0x72, 0x6f, 0x77, 0xbb, 0x01, 0xf1, 0x0b, 0x63, 700xa0, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x61, 0x00, 0x72, 0x6f,
700x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x00, 0x73, 0x74, 0x61, 0x72, 710x77, 0xbb, 0x01, 0xf1, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d,
710x74, 0x69, 0x6e, 0x67, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 720x6e, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67,
720x74, 0x68, 0x61, 0x74, 0x00, 0x6c, 0x01, 0x9d, 0x69, 0x6f, 730x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x74, 0x68, 0x61, 0x74,
730x6e, 0x2e, 0x00, 0x28, 0x54, 0x68, 0x65, 0x6a, 0x02, 0x01, 740x00, 0x6c, 0x01, 0x9d, 0x69, 0x6f, 0x6e, 0x2e, 0x00, 0x28,
740xa5, 0x00, 0x03, 0xb7, 0x01, 0xf1, 0x05, 0x69, 0x76, 0x65, 750x54, 0x68, 0x65, 0x6a, 0x02, 0x01, 0xa5, 0x00, 0x03, 0xb7,
750x00, 0x6f, 0x6e, 0x00, 0x62, 0x6f, 0x74, 0x68, 0x00, 0x73, 760x01, 0xf1, 0x05, 0x69, 0x76, 0x65, 0x00, 0x6f, 0x6e, 0x00,
760x69, 0x64, 0x65, 0x73, 0x2e, 0x00, 0x49, 0x81, 0x00, 0x17, 770x62, 0x6f, 0x74, 0x68, 0x00, 0x73, 0x69, 0x64, 0x65, 0x73,
770x72, 0xd9, 0x01, 0x22, 0x6c, 0x69, 0x63, 0x02, 0xd1, 0x73, 780x2e, 0x00, 0x49, 0x81, 0x00, 0x17, 0x72, 0xd9, 0x01, 0x22,
780x69, 0x67, 0x68, 0x74, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 790x6c, 0x69, 0x63, 0x02, 0xd1, 0x73, 0x69, 0x67, 0x68, 0x74,
790x65, 0x73, 0x98, 0x00, 0x45, 0x73, 0x61, 0x6d, 0x65, 0x2a, 800x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x65, 0x73, 0x98, 0x00,
800x01, 0x21, 0x6d, 0x6f, 0xee, 0x01, 0x81, 0x61, 0x6e, 0x00, 810x45, 0x73, 0x61, 0x6d, 0x65, 0x2a, 0x01, 0x21, 0x6d, 0x6f,
810x6f, 0x6e, 0x63, 0x65, 0x2c, 0x21, 0x00, 0x03, 0x5d, 0x01, 820xee, 0x01, 0x81, 0x61, 0x6e, 0x00, 0x6f, 0x6e, 0x63, 0x65,
820xd3, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x63, 0x6f, 0x75, 0x6e, 830x2c, 0x21, 0x00, 0x03, 0x5d, 0x01, 0xd3, 0x77, 0x69, 0x6c,
830x74, 0x00, 0x69, 0x74, 0x68, 0x01, 0x30, 0x69, 0x6d, 0x65, 840x6c, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x69, 0x74,
840x0d, 0x00, 0x24, 0x69, 0x73, 0xb6, 0x01, 0x11, 0x2c, 0xec, 850x68, 0x01, 0x30, 0x69, 0x6d, 0x65, 0x0d, 0x00, 0x24, 0x69,
850x02, 0x10, 0x6a, 0xe3, 0x02, 0x00, 0x41, 0x00, 0xf2, 0x01, 860x73, 0xb6, 0x01, 0x11, 0x2c, 0xec, 0x02, 0x10, 0x6a, 0xe3,
860x2e, 0x29, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00, 870x02, 0x00, 0x41, 0x00, 0xf2, 0x01, 0x2e, 0x29, 0x00, 0x00,
870x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x9a, 0x01, 0x30, 0x77, 880x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a,
880x61, 0x73, 0x15, 0x02, 0x20, 0x65, 0x6e, 0x99, 0x00, 0xf1, 890x6c, 0x65, 0x9a, 0x01, 0x30, 0x77, 0x61, 0x73, 0x15, 0x02,
890x01, 0x62, 0x79, 0x00, 0x44, 0x61, 0x76, 0x69, 0x64, 0x00, 900x20, 0x65, 0x6e, 0x99, 0x00, 0xf1, 0x01, 0x62, 0x79, 0x00,
900x4d, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x2c, 0xf6, 0x02, 0x12, 910x44, 0x61, 0x76, 0x69, 0x64, 0x00, 0x4d, 0x69, 0x6c, 0x6c,
910x72, 0x79, 0x00, 0x00, 0x9a, 0x00, 0x41, 0x60, 0x48, 0x61, 920x61, 0x72, 0x2c, 0xf6, 0x02, 0x12, 0x72, 0x79, 0x00, 0x00,
920x75, 0x29, 0x00, 0x12, 0x4d, 0x3f, 0x03, 0xf3, 0x03, 0x4d, 930x9a, 0x00, 0x41, 0x60, 0x48, 0x61, 0x75, 0x29, 0x00, 0x12,
930x61, 0x7a, 0x65, 0x27, 0x2e, 0x00, 0x53, 0x65, 0x65, 0x00, 940x4d, 0x3f, 0x03, 0xf3, 0x03, 0x4d, 0x61, 0x7a, 0x65, 0x27,
940x5b, 0x32, 0x30, 0x5d, 0x00, 0x66, 0x6f, 0xb6, 0x00, 0x61, 950x2e, 0x00, 0x53, 0x65, 0x65, 0x00, 0x5b, 0x32, 0x30, 0x5d,
950x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x2b, 0x02, 0x02, 0xd9, 960x00, 0x66, 0x6f, 0xb6, 0x00, 0x61, 0x64, 0x65, 0x74, 0x61,
960x03, 0x01, 0x68, 0x00, 0x00, 0xab, 0x03, 0x40, 0x72, 0x69, 970x69, 0x6c, 0x2b, 0x02, 0x02, 0xec, 0x03, 0x01, 0x68, 0x00,
970x62, 0x75, 0x42, 0x00, 0x01, 0x89, 0x01, 0x20, 0x69, 0x73, 980x00, 0xab, 0x03, 0x40, 0x72, 0x69, 0x62, 0x75, 0x42, 0x00,
980x76, 0x01, 0x01, 0x38, 0x01, 0x20, 0x6f, 0x6e, 0x7e, 0x00, 990x01, 0x89, 0x01, 0x20, 0x69, 0x73, 0x76, 0x01, 0x01, 0x38,
990xd0, 0x53, 0x74, 0x65, 0x66, 0x66, 0x65, 0x6e, 0x00, 0x42, 1000x01, 0x20, 0x6f, 0x6e, 0x7e, 0x00, 0xd0, 0x53, 0x74, 0x65,
1000x61, 0x75, 0x65, 0x72, 0x3e, 0x00, 0x01, 0x57, 0x00, 0xf4, 1010x66, 0x66, 0x65, 0x6e, 0x00, 0x42, 0x61, 0x75, 0x65, 0x72,
1010x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 1020x3e, 0x00, 0x01, 0x57, 0x00, 0xf4, 0x29, 0x68, 0x74, 0x74,
1020x77, 0x2e, 0x6a, 0x61, 0x6e, 0x6b, 0x6f, 0x2e, 0x61, 0x74, 1030x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6a, 0x61,
1030x2f, 0x52, 0x61, 0x65, 0x74, 0x73, 0x65, 0x6c, 0x2f, 0x53, 1040x6e, 0x6b, 0x6f, 0x2e, 0x61, 0x74, 0x2f, 0x52, 0x61, 0x65,
1040x70, 0x75, 0x6b, 0x73, 0x63, 0x68, 0x6c, 0x6f, 0x73, 0x73, 1050x74, 0x73, 0x65, 0x6c, 0x2f, 0x53, 0x70, 0x75, 0x6b, 0x73,
1050x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 1060x63, 0x68, 0x6c, 0x6f, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x64,
1060x00, 0x00, 0x00, 0x33, 0x37, 0x2e, 0x31, 0x55, 0x04, 0x01, 1070x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x33,
1070x78, 0x00, 0x46, 0x6f, 0x6c, 0x73, 0x20, 0x8f, 0x00, 0xe3, 1080x37, 0x2e, 0x31, 0x68, 0x04, 0x01, 0x78, 0x00, 0x46, 0x6f,
1080x68, 0x61, 0x73, 0x00, 0x61, 0x00, 0x73, 0x69, 0x6d, 0x69, 1090x6c, 0x73, 0x20, 0x8f, 0x00, 0xe3, 0x68, 0x61, 0x73, 0x00,
1090x6c, 0x61, 0x72, 0x00, 0x21, 0x00, 0x70, 0x00, 0x73, 0x79, 1100x61, 0x00, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x00,
1100x73, 0x74, 0x65, 0x6d, 0x9c, 0x00, 0xf0, 0x08, 0x53, 0x6f, 1110x21, 0x00, 0x70, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
1110x6c, 0x6f, 0x2c, 0x00, 0x55, 0x6e, 0x65, 0x71, 0x75, 0x61, 1120x9c, 0x00, 0xf0, 0x08, 0x53, 0x6f, 0x6c, 0x6f, 0x2c, 0x00,
1120x6c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x4b, 0x65, 0x65, 0x6e, 1130x55, 0x6e, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x00, 0x61, 0x6e,
1130x2e, 0x42, 0x01, 0x63, 0x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 1140x64, 0x00, 0x4b, 0x65, 0x65, 0x6e, 0x2e, 0x42, 0x01, 0x63,
1140x49, 0x00, 0x71, 0x2c, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 1150x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x00, 0x71, 0x2c,
1150x23, 0x01, 0x40, 0x6d, 0x6f, 0x75, 0x73, 0x62, 0x03, 0x01, 1160x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x23, 0x01, 0x40, 0x6d,
1160x2b, 0x03, 0x45, 0x65, 0x6d, 0x70, 0x74, 0x7d, 0x04, 0x12, 1170x6f, 0x75, 0x73, 0x62, 0x03, 0x01, 0x2b, 0x03, 0x45, 0x65,
1170x61, 0xdf, 0x02, 0x12, 0x6e, 0x73, 0x01, 0x60, 0x61, 0x00, 1180x6d, 0x70, 0x74, 0x7d, 0x04, 0x12, 0x61, 0xdf, 0x02, 0x12,
1180x6c, 0x65, 0x74, 0x74, 0x23, 0x03, 0x02, 0x0b, 0x03, 0x84, 1190x6e, 0x73, 0x01, 0x60, 0x61, 0x00, 0x6c, 0x65, 0x74, 0x74,
1190x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0xd2, 0x02, 1200x23, 0x03, 0x15, 0x72, 0x2d, 0x03, 0x02, 0x15, 0x03, 0x84,
1200x00, 0x7d, 0x02, 0x01, 0x4f, 0x03, 0x0a, 0x3a, 0x03, 0x51, 1210x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0xdc, 0x02,
1210x3a, 0x00, 0x60, 0x47, 0x27, 0x63, 0x01, 0x05, 0x7c, 0x04, 1220x00, 0x87, 0x02, 0x01, 0x59, 0x03, 0x0a, 0x44, 0x03, 0x50,
1220x24, 0x60, 0x56, 0x11, 0x00, 0x08, 0x84, 0x04, 0x24, 0x60, 1230x3a, 0x00, 0x60, 0x47, 0x27, 0x3e, 0x00, 0x31, 0x60, 0x31,
1230x5a, 0x16, 0x00, 0x04, 0x8c, 0x04, 0x12, 0x49, 0x04, 0x03, 1240x27, 0x74, 0x01, 0x05, 0x8d, 0x04, 0x22, 0x60, 0x56, 0x18,
1240x41, 0x6d, 0x61, 0x6b, 0x65, 0xfc, 0x04, 0x5e, 0x73, 0x74, 1250x00, 0x14, 0x32, 0x18, 0x00, 0x08, 0x9c, 0x04, 0x22, 0x60,
1250x61, 0x6b, 0x65, 0xbb, 0x00, 0x03, 0x9b, 0x03, 0x50, 0x69, 1260x5a, 0x1d, 0x00, 0x14, 0x33, 0x1d, 0x00, 0x04, 0xab, 0x04,
1260x6e, 0x63, 0x6f, 0x72, 0x0e, 0x04, 0x08, 0xbf, 0x00, 0xb0, 1270x12, 0x49, 0x23, 0x03, 0x41, 0x6d, 0x61, 0x6b, 0x65, 0x1b,
1270x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x61, 0x63, 1280x05, 0x5e, 0x73, 0x74, 0x61, 0x6b, 0x65, 0xda, 0x00, 0x03,
1280x65, 0x20, 0x01, 0x50, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x6f, 1290xba, 0x03, 0x50, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x2d, 0x04,
1290x02, 0x20, 0x61, 0x67, 0x7d, 0x05, 0x62, 0x28, 0x6f, 0x72, 1300x08, 0xde, 0x00, 0xb0, 0x70, 0x72, 0x65, 0x73, 0x73, 0x00,
1300x00, 0x75, 0x73, 0x9c, 0x04, 0xd0, 0x55, 0x6e, 0x64, 0x6f, 1310x53, 0x70, 0x61, 0x63, 0x65, 0x3f, 0x01, 0x50, 0x63, 0x6c,
1310x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x29, 0x32, 1320x65, 0x61, 0x72, 0x8e, 0x02, 0x20, 0x61, 0x67, 0x9c, 0x05,
1320x01, 0x03, 0x7e, 0x00, 0x10, 0x72, 0xee, 0x02, 0x12, 0x2d, 1330x62, 0x28, 0x6f, 0x72, 0x00, 0x75, 0x73, 0xbb, 0x04, 0xd0,
1330x74, 0x00, 0x00, 0x25, 0x01, 0x0f, 0x1d, 0x01, 0x0b, 0x02, 1340x55, 0x6e, 0x64, 0x6f, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75,
1340xef, 0x02, 0x01, 0x89, 0x00, 0x50, 0x73, 0x70, 0x6f, 0x6e, 1350x72, 0x65, 0x29, 0x51, 0x01, 0x03, 0x7e, 0x00, 0x10, 0x72,
1350x64, 0x15, 0x01, 0x04, 0x19, 0x03, 0x01, 0xfe, 0x02, 0x00, 1360x0d, 0x03, 0x12, 0x2d, 0x74, 0x00, 0x00, 0x44, 0x01, 0x0f,
1360xe0, 0x03, 0x31, 0x68, 0x6f, 0x77, 0xe7, 0x04, 0xb3, 0x72, 1370x3c, 0x01, 0x15, 0x02, 0x18, 0x03, 0x01, 0x93, 0x00, 0x50,
1370x65, 0x64, 0x75, 0x63, 0x65, 0x64, 0x00, 0x73, 0x69, 0x7a, 1380x73, 0x70, 0x6f, 0x6e, 0x64, 0x34, 0x01, 0x04, 0x42, 0x03,
1380xc6, 0x00, 0x14, 0x61, 0xbd, 0x00, 0x21, 0x2c, 0x00, 0xea, 1390x01, 0x27, 0x03, 0x00, 0x09, 0x04, 0x31, 0x68, 0x6f, 0x77,
1390x01, 0xe1, 0x60, 0x70, 0x65, 0x6e, 0x63, 0x69, 0x6c, 0x00, 1400x10, 0x05, 0xb3, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x64,
1400x6d, 0x61, 0x72, 0x6b, 0x27, 0x2e, 0xc0, 0x04, 0x00, 0x25, 1410x00, 0x73, 0x69, 0x7a, 0xd0, 0x00, 0x14, 0x61, 0xc7, 0x00,
1410x04, 0x57, 0x68, 0x61, 0x76, 0x65, 0x00, 0x1b, 0x00, 0x12, 1420x21, 0x2c, 0x00, 0x13, 0x02, 0xe1, 0x60, 0x70, 0x65, 0x6e,
1420x73, 0xc3, 0x02, 0x65, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 1430x63, 0x69, 0x6c, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x27, 0x2e,
1430x8a, 0x03, 0x00, 0x22, 0x03, 0x06, 0x9f, 0x03, 0x02, 0x58, 1440xe9, 0x04, 0x00, 0x4e, 0x04, 0x57, 0x68, 0x61, 0x76, 0x65,
1440x00, 0x34, 0x2e, 0x00, 0x41, 0xc1, 0x00, 0x03, 0x82, 0x06, 1450x00, 0x1b, 0x00, 0x12, 0x73, 0xec, 0x02, 0x65, 0x75, 0x6c,
1450x11, 0x69, 0x4d, 0x04, 0x51, 0x66, 0x75, 0x6c, 0x6c, 0x2d, 1460x74, 0x69, 0x70, 0x6c, 0xb3, 0x03, 0x00, 0x4b, 0x03, 0x06,
1460x88, 0x00, 0x04, 0xae, 0x00, 0x31, 0x63, 0x61, 0x6e, 0x74, 1470xc8, 0x03, 0x02, 0x58, 0x00, 0x34, 0x2e, 0x00, 0x41, 0xcb,
1470x06, 0x00, 0xeb, 0x04, 0x04, 0xad, 0x06, 0x08, 0x73, 0x00, 1480x00, 0x03, 0xab, 0x06, 0x11, 0x69, 0x76, 0x04, 0x51, 0x66,
1480x01, 0x55, 0x02, 0x00, 0x94, 0x04, 0x00, 0x5d, 0x00, 0x30, 1490x75, 0x6c, 0x6c, 0x2d, 0x88, 0x00, 0x04, 0xae, 0x00, 0x31,
1490x70, 0x61, 0x79, 0xa3, 0x06, 0x20, 0x00, 0x61, 0xf2, 0x04, 1500x63, 0x61, 0x6e, 0x9d, 0x06, 0x00, 0x14, 0x05, 0x04, 0xd6,
1500x01, 0x16, 0x03, 0x29, 0x74, 0x6f, 0x2e, 0x00, 0x00, 0xff, 1510x06, 0x08, 0x73, 0x00, 0x01, 0x7e, 0x02, 0x00, 0xbd, 0x04,
1510x06, 0x40, 0x00, 0x65, 0x78, 0x61, 0xac, 0x05, 0x20, 0x00, 1520x00, 0x5d, 0x00, 0x30, 0x70, 0x61, 0x79, 0xcc, 0x06, 0x20,
1520x77, 0xe8, 0x00, 0x00, 0x5c, 0x01, 0x03, 0x7c, 0x01, 0x11, 1530x00, 0x61, 0x1b, 0x05, 0x01, 0x3f, 0x03, 0x29, 0x74, 0x6f,
1530x6d, 0xbf, 0x00, 0x50, 0x69, 0x73, 0x00, 0x75, 0x70, 0x38, 1540x2e, 0x00, 0x00, 0x28, 0x07, 0x40, 0x00, 0x65, 0x78, 0x61,
1540x00, 0x63, 0x79, 0x6f, 0x75, 0x3a, 0x00, 0x79, 0xeb, 0x00, 1550xd5, 0x05, 0x20, 0x00, 0x77, 0xe8, 0x00, 0x00, 0x66, 0x01,
1550x05, 0x23, 0x00, 0x70, 0x61, 0x73, 0x00, 0x72, 0x65, 0x6d, 1560x03, 0x86, 0x01, 0x11, 0x6d, 0xbf, 0x00, 0x50, 0x69, 0x73,
1560x69, 0xda, 0x03, 0x12, 0x73, 0x2a, 0x01, 0x30, 0x61, 0x00, 1570x00, 0x75, 0x70, 0x38, 0x00, 0x63, 0x79, 0x6f, 0x75, 0x3a,
1570x70, 0xf4, 0x04, 0x20, 0x63, 0x75, 0x0c, 0x03, 0x03, 0xd5, 1580x00, 0x79, 0xeb, 0x00, 0x05, 0x23, 0x00, 0x70, 0x61, 0x73,
1580x00, 0x50, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x45, 0x00, 0x10, 1590x00, 0x72, 0x65, 0x6d, 0x69, 0x03, 0x04, 0x12, 0x73, 0x2a,
1590x62, 0xdc, 0x04, 0x91, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x69, 1600x01, 0x30, 0x61, 0x00, 0x70, 0x1d, 0x05, 0x20, 0x63, 0x75,
1600x6e, 0x65, 0x64, 0x4a, 0x04, 0x01, 0x54, 0x00, 0x42, 0x6b, 1610x35, 0x03, 0x03, 0xd5, 0x00, 0x50, 0x6e, 0x65, 0x65, 0x64,
1610x6e, 0x6f, 0x77, 0xed, 0x03, 0x4b, 0x61, 0x62, 0x6f, 0x75, 1620x73, 0x45, 0x00, 0x10, 0x62, 0x05, 0x05, 0x91, 0x2d, 0x65,
1620x45, 0x00, 0x03, 0xfc, 0x00, 0x01, 0x8f, 0x02, 0x0f, 0x81, 1630x78, 0x61, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x73, 0x04, 0x01,
1630x00, 0x01, 0x20, 0x6c, 0x69, 0xce, 0x06, 0x03, 0xe9, 0x05, 1640x54, 0x00, 0x42, 0x6b, 0x6e, 0x6f, 0x77, 0x16, 0x04, 0x4b,
1640x32, 0x70, 0x6f, 0x73, 0x66, 0x06, 0x07, 0x23, 0x06, 0x13, 1650x61, 0x62, 0x6f, 0x75, 0x45, 0x00, 0x03, 0xfc, 0x00, 0x01,
1650x61, 0xff, 0x07, 0x04, 0xc5, 0x01, 0x20, 0x6f, 0x72, 0x56, 1660xa0, 0x02, 0x0f, 0x81, 0x00, 0x01, 0x20, 0x6c, 0x69, 0xf7,
1660x03, 0x20, 0x74, 0x68, 0x65, 0x01, 0x32, 0x65, 0x6c, 0x73, 1670x06, 0x03, 0x12, 0x06, 0x32, 0x70, 0x6f, 0x73, 0x8f, 0x06,
1670x83, 0x00, 0x93, 0x66, 0x65, 0x65, 0x6c, 0x00, 0x6c, 0x69, 1680x07, 0x4c, 0x06, 0x13, 0x61, 0x28, 0x08, 0x04, 0xc5, 0x01,
1680x6b, 0x65, 0x98, 0x03, 0x51, 0x65, 0x72, 0x61, 0x73, 0x65, 1690x20, 0x6f, 0x72, 0x7f, 0x03, 0x20, 0x74, 0x68, 0x65, 0x01,
1690xd7, 0x03, 0x39, 0x6e, 0x67, 0x6c, 0xd8, 0x01, 0x1c, 0x2c, 1700x32, 0x65, 0x6c, 0x73, 0x83, 0x00, 0x93, 0x66, 0x65, 0x65,
1700x7e, 0x02, 0x01, 0xce, 0x01, 0x07, 0x80, 0x02, 0x00, 0x7b, 1710x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0xc1, 0x03, 0x51, 0x65,
1710x02, 0x05, 0xe2, 0x01, 0x03, 0x9f, 0x03, 0x01, 0xdb, 0x02, 1720x72, 0x61, 0x73, 0x65, 0x00, 0x04, 0x39, 0x6e, 0x67, 0x6c,
1720x00, 0x5b, 0x00, 0x3a, 0x41, 0x6c, 0x6c, 0x25, 0x02, 0x09, 1730xd8, 0x01, 0x1c, 0x2c, 0x88, 0x02, 0x01, 0xce, 0x01, 0x07,
1730xbf, 0x02, 0x22, 0x72, 0x65, 0x79, 0x00, 0x12, 0x64, 0x53, 1740x8a, 0x02, 0x00, 0x85, 0x02, 0x05, 0xe2, 0x01, 0x0d, 0xc8,
1740x07, 0x01, 0x6d, 0x06, 0x24, 0x65, 0x66, 0x6c, 0x00, 0x05, 1750x03, 0x01, 0xef, 0x02, 0x00, 0x65, 0x00, 0x3a, 0x41, 0x6c,
1750x5e, 0x00, 0x15, 0x61, 0xdf, 0x00, 0x04, 0xe1, 0x02, 0x2f, 1760x6c, 0x2f, 0x02, 0x09, 0xd3, 0x02, 0x22, 0x72, 0x65, 0x83,
1760x6f, 0x72, 0x32, 0x00, 0x06, 0x07, 0x6e, 0x03, 0x31, 0x2e, 1770x00, 0x12, 0x64, 0x86, 0x07, 0x01, 0xa0, 0x06, 0x24, 0x65,
1770x00, 0x52, 0xbb, 0x00, 0x02, 0xae, 0x03, 0x01, 0x65, 0x02, 1780x66, 0x76, 0x00, 0x05, 0x68, 0x00, 0x15, 0x61, 0xe9, 0x00,
1780x04, 0x21, 0x00, 0x00, 0x0d, 0x00, 0x11, 0x73, 0x92, 0x03, 1790x02, 0x69, 0x00, 0x01, 0xde, 0x00, 0x0f, 0x32, 0x00, 0x05,
1790x01, 0x12, 0x03, 0x01, 0x62, 0x02, 0x02, 0x04, 0x01, 0x0c, 1800x07, 0x82, 0x03, 0x31, 0x2e, 0x00, 0x52, 0xc5, 0x00, 0x02,
1800x60, 0x02, 0x12, 0x41, 0xd9, 0x02, 0x02, 0xd6, 0x04, 0x01, 1810xc2, 0x03, 0x01, 0x6f, 0x02, 0x04, 0x21, 0x00, 0x00, 0x0d,
1810x59, 0x03, 0x50, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x7b, 0x04, 1820x00, 0x11, 0x73, 0xa6, 0x03, 0x01, 0x1c, 0x03, 0x01, 0x6c,
1820x05, 0x2e, 0x07, 0x22, 0x75, 0x73, 0x74, 0x08, 0x62, 0x63, 1830x02, 0x02, 0x0e, 0x01, 0x0c, 0x6a, 0x02, 0x12, 0x41, 0xe3,
1830x6f, 0x6e, 0x6a, 0x75, 0x6e, 0x93, 0x05, 0x01, 0x11, 0x09, 1840x02, 0x02, 0x09, 0x05, 0x01, 0x63, 0x03, 0x50, 0x75, 0x72,
1840x25, 0x74, 0x68, 0x13, 0x01, 0x01, 0x30, 0x00, 0x11, 0x74, 1850x73, 0x6f, 0x72, 0xa4, 0x04, 0x05, 0x61, 0x07, 0x22, 0x75,
1850x02, 0x05, 0x17, 0x63, 0x1f, 0x03, 0x2b, 0x6f, 0x72, 0x72, 1860x73, 0xa7, 0x08, 0x62, 0x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e,
1860x00, 0x3d, 0x55, 0x73, 0x65, 0x67, 0x00, 0x60, 0x74, 0x6f, 1870xc6, 0x05, 0x01, 0x44, 0x09, 0x25, 0x74, 0x68, 0x1d, 0x01,
1870x00, 0x6d, 0x6f, 0x76, 0x09, 0x01, 0x51, 0x68, 0x69, 0x67, 1880x01, 0x30, 0x00, 0x11, 0x74, 0x35, 0x05, 0x17, 0x63, 0x29,
1880x68, 0x6c, 0xfc, 0x06, 0x07, 0xfb, 0x07, 0x00, 0x93, 0x07, 1890x03, 0x2b, 0x6f, 0x72, 0x72, 0x00, 0x3d, 0x55, 0x73, 0x65,
1890x1f, 0x2c, 0x2f, 0x01, 0x07, 0x00, 0x3f, 0x00, 0x00, 0xa5, 1900x67, 0x00, 0x60, 0x74, 0x6f, 0x00, 0x6d, 0x6f, 0x76, 0x09,
1900x06, 0x01, 0x75, 0x04, 0x03, 0xc1, 0x01, 0x05, 0x48, 0x00, 1910x01, 0x51, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x2f, 0x07, 0x07,
1910x25, 0x65, 0x64, 0x96, 0x03, 0x14, 0x50, 0x19, 0x01, 0x50, 1920x2e, 0x08, 0x00, 0xc6, 0x07, 0x1f, 0x2c, 0x2f, 0x01, 0x07,
1920x72, 0x65, 0x74, 0x75, 0x72, 0x3f, 0x03, 0x5a, 0x67, 0x67, 1930x07, 0x98, 0x01, 0x30, 0x74, 0x6f, 0x00, 0xe2, 0x06, 0x01,
1930x6c, 0x65, 0x73, 0x30, 0x00, 0x02, 0x09, 0x08, 0x00, 0x63, 1940x93, 0x04, 0x03, 0xd5, 0x01, 0x05, 0x52, 0x00, 0x25, 0x65,
1940x00, 0x11, 0x64, 0x30, 0x04, 0x02, 0x51, 0x08, 0x04, 0xa9, 1950x64, 0xaa, 0x03, 0x14, 0x50, 0x23, 0x01, 0x50, 0x72, 0x65,
1950x02, 0x02, 0x67, 0x00, 0x20, 0x6f, 0x72, 0x27, 0x03, 0x1b, 1960x74, 0x75, 0x72, 0x53, 0x03, 0x5a, 0x67, 0x67, 0x6c, 0x65,
1960x6f, 0x20, 0x04, 0x07, 0xd0, 0x04, 0x60, 0x70, 0x72, 0x65, 1970x73, 0x30, 0x00, 0x02, 0x46, 0x08, 0x00, 0x6d, 0x00, 0x11,
1970x66, 0x65, 0x72, 0x0b, 0x01, 0x23, 0x69, 0x6e, 0xa5, 0x00, 1980x64, 0x44, 0x04, 0x02, 0x8e, 0x08, 0x04, 0xbd, 0x02, 0x02,
1980x05, 0xd3, 0x02, 0x81, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 1990x67, 0x00, 0x10, 0x6f, 0xef, 0x07, 0x2b, 0x6d, 0x6f, 0x34,
1990x65, 0x74, 0x2b, 0x05, 0x26, 0x75, 0x74, 0xdb, 0x02, 0x51, 2000x04, 0x07, 0xee, 0x04, 0x60, 0x70, 0x72, 0x65, 0x66, 0x65,
2000x70, 0x69, 0x63, 0x74, 0x75, 0xc6, 0x0a, 0x04, 0x6e, 0x00, 2010x72, 0x15, 0x01, 0x23, 0x69, 0x6e, 0xaf, 0x00, 0x05, 0xe7,
2010x02, 0xeb, 0x01, 0x31, 0x60, 0x41, 0x27, 0x13, 0x07, 0x01, 2020x02, 0x81, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x65, 0x74,
2020xae, 0x00, 0x50, 0x00, 0x62, 0x65, 0x74, 0x77, 0xbc, 0x08, 2030x49, 0x05, 0x26, 0x75, 0x74, 0xef, 0x02, 0x51, 0x70, 0x69,
2030x00, 0xe1, 0x04, 0x04, 0x0e, 0x06, 0x05, 0x6d, 0x01, 0x29, 2040x63, 0x74, 0x75, 0x03, 0x0b, 0x04, 0x6e, 0x00, 0x02, 0xf5,
2040x61, 0x73, 0x79, 0x01, 0x07, 0x24, 0x00, 0x02, 0x5b, 0x03, 2050x01, 0x31, 0x60, 0x41, 0x27, 0x50, 0x07, 0x01, 0xae, 0x00,
2050x02, 0x8a, 0x00, 0x00, 0xa9, 0x00, 0x15, 0x4c, 0x55, 0x02, 2060x50, 0x00, 0x62, 0x65, 0x74, 0x77, 0xf9, 0x08, 0x00, 0xf5,
2060x02, 0x9c, 0x04, 0x33, 0x63, 0x6c, 0x75, 0x2b, 0x02, 0x00, 2070x04, 0x04, 0x41, 0x06, 0x05, 0x77, 0x01, 0x29, 0x61, 0x73,
2070xcc, 0x00, 0x01, 0xbe, 0x05, 0x30, 0x73, 0x00, 0x64, 0xf0, 2080x83, 0x01, 0x07, 0x24, 0x00, 0x02, 0x6f, 0x03, 0x02, 0x8a,
2080x0a, 0x50, 0x28, 0x67, 0x72, 0x65, 0x79, 0x11, 0x00, 0x41, 2090x00, 0x00, 0xa9, 0x00, 0x15, 0x4c, 0x5f, 0x02, 0x02, 0xb0,
2090x6f, 0x75, 0x74, 0x29, 0x9a, 0x02, 0x24, 0x75, 0x6e, 0x24, 2100x04, 0x33, 0x63, 0x6c, 0x75, 0x35, 0x02, 0x00, 0xcc, 0x00,
2100x00, 0x23, 0x69, 0x66, 0x57, 0x08, 0x71, 0x61, 0x6c, 0x72, 2110x01, 0xdc, 0x05, 0x30, 0x73, 0x00, 0x64, 0x2d, 0x0b, 0x50,
2110x65, 0x61, 0x64, 0x79, 0x3d, 0x00, 0x20, 0x65, 0x64, 0x61, 2120x28, 0x67, 0x72, 0x65, 0x79, 0x11, 0x00, 0x41, 0x6f, 0x75,
2120x00, 0x10, 0x28, 0x1a, 0x03, 0x01, 0xf0, 0x00, 0x01, 0x2f, 2130x74, 0x29, 0xa4, 0x02, 0x24, 0x75, 0x6e, 0x24, 0x00, 0x23,
2130x02, 0x92, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 2140x69, 0x66, 0x94, 0x08, 0x71, 0x61, 0x6c, 0x72, 0x65, 0x61,
2140x62, 0x49, 0x02, 0x13, 0x73, 0xd8, 0x07, 0x36, 0x32, 0x2e, 2150x64, 0x79, 0x3d, 0x00, 0x20, 0x65, 0x64, 0x61, 0x00, 0x10,
2150x31, 0x2d, 0x0a, 0x82, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 2160x28, 0x24, 0x03, 0x01, 0xf0, 0x00, 0x01, 0x39, 0x02, 0x92,
2160x62, 0x6c, 0x8f, 0x08, 0x44, 0x33, 0x37, 0x2e, 0x32, 0xa6, 2170x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x53,
2170x07, 0x60, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0xbc, 0x00, 2180x02, 0x13, 0x73, 0x15, 0x08, 0x36, 0x32, 0x2e, 0x31, 0x6a,
2180x12, 0x20, 0x12, 0x05, 0x00, 0xc8, 0x02, 0x05, 0x14, 0x00, 2190x0a, 0x82, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
2190x02, 0x42, 0x00, 0x04, 0x3d, 0x00, 0x04, 0x9d, 0x09, 0xf1, 2200xcc, 0x08, 0x44, 0x33, 0x37, 0x2e, 0x32, 0xe3, 0x07, 0x60,
2200x01, 0x65, 0x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 2210x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0xbc, 0x00, 0x12, 0x20,
2210x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x70, 0x00, 0x03, 2220x26, 0x05, 0x00, 0xd2, 0x02, 0x05, 0x14, 0x00, 0x02, 0x42,
2220x57, 0x07, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 2230x00, 0x04, 0x3d, 0x00, 0x04, 0xda, 0x09, 0xf1, 0x01, 0x65,
2230x6d, 0x65, 0x6e, 0x75, 0xac, 0x00, 0x91, 0x57, 0x69, 0x64, 2240x00, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e,
2240x74, 0x68, 0x2c, 0x00, 0x48, 0x65, 0x01, 0x02, 0x51, 0x00, 2250x2e, 0x27, 0x00, 0x6f, 0x70, 0x70, 0x00, 0x03, 0x8a, 0x07,
2250x00, 0x53, 0x69, 0x7a, 0x5e, 0x07, 0x01, 0x0b, 0x0a, 0x14, 2260xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65,
2260x69, 0x6a, 0x04, 0x01, 0x38, 0x01, 0x40, 0x44, 0x69, 0x66, 2270x6e, 0x75, 0xac, 0x00, 0x91, 0x57, 0x69, 0x64, 0x74, 0x68,
2270x66, 0xc9, 0x04, 0x63, 0x74, 0x79, 0x00, 0x00, 0x00, 0x43, 2280x2c, 0x00, 0x48, 0x65, 0x01, 0x02, 0x51, 0x00, 0x00, 0x53,
2280x3d, 0x08, 0x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x04, 2290x69, 0x7a, 0x91, 0x07, 0x01, 0x48, 0x0a, 0x14, 0x69, 0x7e,
2290xa6, 0x0a, 0x50, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x82, 0x02, 2300x04, 0x01, 0x38, 0x01, 0x40, 0x44, 0x69, 0x66, 0x66, 0xdd,
2300x80, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2e, 0x00, 2310x04, 0x63, 0x74, 0x79, 0x00, 0x00, 0x00, 0x43, 0x7a, 0x08,
2320x01, 0x54, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x04, 0xe3, 0x0a,
2330x50, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x82, 0x02, 0x02, 0x93,
2340x09, 0x12, 0x2e, 0xd6, 0x00, 0x14, 0x33, 0xd6, 0x00, 0x52,
2350x75, 0x73, 0x65, 0x72, 0x20, 0x2b, 0x02, 0x41, 0x65, 0x6e,
2360x63, 0x65, 0xdc, 0x00, 0x20, 0x4f, 0x6e, 0x36, 0x02, 0x63,
2370x74, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0xc0, 0x06, 0x70, 0x75,
2380x70, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x2e, 0x00, 0x17, 0x00,
2390x2e, 0x00, 0x12, 0x2c, 0xca, 0x00, 0x16, 0x50, 0x12, 0x00,
2400x0d, 0xe6, 0x00, 0x33, 0x47, 0x61, 0x6d, 0xe6, 0x00, 0x02,
2410xdb, 0x01, 0x32, 0x6c, 0x65, 0x74, 0x5c, 0x02, 0x71, 0x6f,
2420x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x27, 0x0c, 0x44, 0x74,
2430x68, 0x65, 0x72, 0x38, 0x09, 0x10, 0x75, 0xaa, 0x0a, 0x05,
2440xb0, 0x02, 0x06, 0x93, 0x02, 0x00, 0x80, 0x02, 0x20, 0x72,
2450x65, 0x8f, 0x02, 0xe0, 0x65, 0x6e, 0x74, 0x00, 0x6d, 0x6f,
2460x6e, 0x73, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x00,
231}; 247};
232 248
233const unsigned short help_text_len = 3286; 249const unsigned short help_text_len = 3574;
234const unsigned short help_text_words = 614; 250const unsigned short help_text_words = 660;
251const bool help_valid = true;
235const char quick_help_text[] = "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors."; 252const char quick_help_text[] = "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.";
diff --git a/apps/plugins/puzzles/help/unequal.c b/apps/plugins/puzzles/help/unequal.c
index 8885d70c33..6bccf6bef3 100644
--- a/apps/plugins/puzzles/help/unequal.c
+++ b/apps/plugins/puzzles/help/unequal.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,255 +6,256 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 239, TEXT_CENTER | C_RED }, 9 { 240, TEXT_CENTER | C_RED },
10 { 306, TEXT_UNDERLINE }, 10 { 307, TEXT_UNDERLINE },
11 { 631, TEXT_CENTER | C_RED }, 11 { 632, TEXT_CENTER | C_RED },
12 { 648, TEXT_UNDERLINE }, 12 { 649, TEXT_UNDERLINE },
13 { 660, TEXT_UNDERLINE }, 13 { 661, TEXT_UNDERLINE },
14 { 668, TEXT_UNDERLINE }, 14 { 669, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 3934 comp 2352 ratio 0.597865 level 10 saved 1582 */ 18/* orig 3954 comp 2358 ratio 0.596358 level 10 saved 1596 */
19const char help_text[] = { 19const char help_text[] = {
200xf4, 0x24, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfe, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x32, 0x37, 0x3a, 0x20, 0x55, 0x6e, 0x65, 0x71, 0x75, 0x61, 210x32, 0x37, 0x3a, 0x20, 0x55, 0x6e, 0x65, 0x71, 0x75, 0x61,
220x6c, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 220x6c, 0x20, 0x00, 0x2d, 0x01, 0x00, 0xf4, 0x10, 0x00, 0x00,
230x61, 0x76, 0x65, 0x00, 0x61, 0x00, 0x73, 0x71, 0x75, 0x61, 230x00, 0x59, 0x6f, 0x75, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00,
240x72, 0x65, 0x00, 0x67, 0x72, 0x69, 0x64, 0x3b, 0x00, 0x65, 240x61, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x67,
250x61, 0x63, 0x68, 0x12, 0x00, 0xf1, 0x1a, 0x6d, 0x61, 0x79, 250x72, 0x69, 0x64, 0x3b, 0x00, 0x65, 0x61, 0x63, 0x68, 0x12,
260x00, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x61, 260x00, 0xf1, 0x1a, 0x6d, 0x61, 0x79, 0x00, 0x63, 0x6f, 0x6e,
270x00, 0x64, 0x69, 0x67, 0x69, 0x74, 0x00, 0x66, 0x72, 0x6f, 270x74, 0x61, 0x69, 0x6e, 0x00, 0x61, 0x00, 0x64, 0x69, 0x67,
280x6d, 0x00, 0x31, 0x00, 0x74, 0x6f, 0x00, 0x74, 0x68, 0x65, 280x69, 0x74, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x31, 0x00,
290x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6f, 0x66, 0x0c, 0x00, 290x74, 0x6f, 0x00, 0x74, 0x68, 0x65, 0x00, 0x73, 0x69, 0x7a,
300x00, 0x40, 0x00, 0xa3, 0x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 300x65, 0x00, 0x6f, 0x66, 0x0c, 0x00, 0x00, 0x40, 0x00, 0xa3,
310x73, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x12, 0x73, 0x65, 0x00, 310x2c, 0x00, 0x61, 0x6e, 0x64, 0x00, 0x73, 0x6f, 0x6d, 0x65,
320x30, 0x63, 0x6c, 0x75, 0x2d, 0x00, 0xb0, 0x67, 0x6e, 0x73, 320x44, 0x00, 0x12, 0x73, 0x65, 0x00, 0x30, 0x63, 0x6c, 0x75,
330x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x33, 0x00, 330x2d, 0x00, 0xb0, 0x67, 0x6e, 0x73, 0x00, 0x62, 0x65, 0x74,
340x20, 0x6d, 0x2e, 0x87, 0x00, 0x80, 0x72, 0x00, 0x61, 0x69, 340x77, 0x65, 0x65, 0x6e, 0x33, 0x00, 0x20, 0x6d, 0x2e, 0x87,
350x6d, 0x00, 0x69, 0x73, 0x54, 0x00, 0xe5, 0x66, 0x75, 0x6c, 350x00, 0x80, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73,
360x6c, 0x79, 0x00, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 360x54, 0x00, 0xe5, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x00, 0x70,
370x65, 0x57, 0x00, 0xf0, 0x0f, 0x00, 0x77, 0x69, 0x74, 0x68, 370x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x00, 0xf0,
380x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x00, 0x73, 380x0f, 0x00, 0x77, 0x69, 0x74, 0x68, 0x00, 0x6e, 0x75, 0x6d,
390x75, 0x63, 0x68, 0x00, 0x74, 0x68, 0x61, 0x74, 0x3a, 0x00, 390x62, 0x65, 0x72, 0x73, 0x00, 0x73, 0x75, 0x63, 0x68, 0x00,
400x00, 0x00, 0x2d, 0x00, 0x45, 0xb2, 0x00, 0x34, 0x72, 0x6f, 400x74, 0x68, 0x61, 0x74, 0x3a, 0x00, 0x00, 0x00, 0x2d, 0x00,
410x77, 0xab, 0x00, 0xf1, 0x05, 0x73, 0x00, 0x6f, 0x6e, 0x6c, 410x45, 0xb2, 0x00, 0x34, 0x72, 0x6f, 0x77, 0xab, 0x00, 0xf1,
420x79, 0x00, 0x6f, 0x6e, 0x65, 0x00, 0x6f, 0x63, 0x63, 0x75, 420x05, 0x73, 0x00, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x6e,
430x72, 0x72, 0x65, 0x6e, 0x63, 0xa5, 0x00, 0x01, 0xdb, 0x00, 430x65, 0x00, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
440x02, 0xc6, 0x00, 0x05, 0x38, 0x00, 0x6f, 0x63, 0x6f, 0x6c, 440x63, 0xa5, 0x00, 0x01, 0xdb, 0x00, 0x02, 0xc6, 0x00, 0x05,
450x75, 0x6d, 0x6e, 0x3b, 0x00, 0x1d, 0x31, 0x41, 0x6c, 0x6c, 450x38, 0x00, 0x6f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x3b,
460x9c, 0x00, 0x07, 0xd7, 0x00, 0x00, 0x30, 0x01, 0xf1, 0x03, 460x00, 0x1d, 0x31, 0x41, 0x6c, 0x6c, 0x9c, 0x00, 0x07, 0xd7,
470x73, 0x61, 0x74, 0x69, 0x73, 0x66, 0x69, 0x65, 0x64, 0x2e, 470x00, 0x00, 0x30, 0x01, 0xf1, 0x03, 0x73, 0x61, 0x74, 0x69,
480x00, 0x00, 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x17, 0x00, 480x73, 0x66, 0x69, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x00, 0x54,
490xf3, 0x0b, 0x74, 0x77, 0x6f, 0x00, 0x6d, 0x6f, 0x64, 0x65, 490x68, 0x65, 0x72, 0x65, 0x17, 0x00, 0xf3, 0x0b, 0x74, 0x77,
500x73, 0x00, 0x66, 0x6f, 0x72, 0x00, 0x74, 0x68, 0x69, 0x73, 500x6f, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x66, 0x6f,
510x00, 0x67, 0x61, 0x6d, 0x65, 0x2c, 0x00, 0x60, 0x90, 0x01, 510x72, 0x00, 0x74, 0x68, 0x69, 0x73, 0x00, 0x67, 0x61, 0x6d,
520x11, 0x27, 0x36, 0x01, 0xa0, 0x60, 0x41, 0x64, 0x6a, 0x61, 520x65, 0x2c, 0x00, 0x60, 0xa4, 0x01, 0x11, 0x27, 0x36, 0x01,
530x63, 0x65, 0x6e, 0x74, 0x27, 0x3f, 0x00, 0x27, 0x49, 0x6e, 530xa0, 0x60, 0x41, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74,
540x1f, 0x00, 0x00, 0x3e, 0x00, 0x1e, 0x2c, 0x72, 0x00, 0x00, 540x27, 0x3f, 0x00, 0x27, 0x49, 0x6e, 0x1f, 0x00, 0x00, 0x3e,
550x1d, 0x01, 0xf1, 0x0e, 0x65, 0x61, 0x74, 0x65, 0x72, 0x2d, 550x00, 0x1e, 0x2c, 0x72, 0x00, 0x00, 0x1d, 0x01, 0xf1, 0x0e,
560x74, 0x68, 0x61, 0x6e, 0x00, 0x73, 0x79, 0x6d, 0x62, 0x6f, 560x65, 0x61, 0x74, 0x65, 0x72, 0x2d, 0x74, 0x68, 0x61, 0x6e,
570x6c, 0x73, 0x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 570x00, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x00, 0x69,
580x69, 0x6e, 0x67, 0xca, 0x00, 0x02, 0x89, 0x01, 0x50, 0x27, 580x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0xca,
590x73, 0x00, 0x76, 0x61, 0x3d, 0x00, 0x24, 0x69, 0x73, 0x36, 590x00, 0x02, 0x89, 0x01, 0x50, 0x27, 0x73, 0x00, 0x76, 0x61,
600x00, 0x11, 0x00, 0x36, 0x00, 0xf0, 0x01, 0x69, 0x74, 0x73, 600x3d, 0x00, 0x24, 0x69, 0x73, 0x36, 0x00, 0x11, 0x00, 0x36,
610x00, 0x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x75, 0x72, 610x00, 0xf0, 0x01, 0x69, 0x74, 0x73, 0x00, 0x6e, 0x65, 0x69,
620x27, 0x73, 0x2e, 0x7a, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x75, 620x67, 0x68, 0x62, 0x6f, 0x75, 0x72, 0x27, 0x73, 0x2e, 0x7a,
630x00, 0x81, 0x00, 0x6e, 0x6f, 0x74, 0x00, 0x61, 0x6c, 0x6c, 630x00, 0x01, 0xa4, 0x00, 0x00, 0x75, 0x00, 0x81, 0x00, 0x6e,
640x78, 0x00, 0x11, 0x73, 0x11, 0x02, 0xf0, 0x18, 0x62, 0x65, 640x6f, 0x74, 0x00, 0x61, 0x6c, 0x6c, 0x78, 0x00, 0x11, 0x73,
650x00, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x2c, 0x00, 650x11, 0x02, 0xf0, 0x18, 0x62, 0x65, 0x00, 0x76, 0x69, 0x73,
660x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 660x69, 0x62, 0x6c, 0x65, 0x2c, 0x00, 0x70, 0x61, 0x72, 0x74,
670x6c, 0x79, 0x00, 0x61, 0x74, 0x00, 0x68, 0x69, 0x67, 0x68, 670x69, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x00, 0x61,
680x65, 0x72, 0x00, 0x64, 0x69, 0x66, 0x66, 0x17, 0x00, 0x94, 680x74, 0x00, 0x68, 0x69, 0x67, 0x68, 0x65, 0x72, 0x00, 0x64,
690x74, 0x79, 0x00, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0xd1, 690x69, 0x66, 0x66, 0x17, 0x00, 0x94, 0x74, 0x79, 0x00, 0x6c,
700x00, 0x05, 0xe2, 0x00, 0x0f, 0xd2, 0x00, 0x07, 0x3f, 0x62, 700x65, 0x76, 0x65, 0x6c, 0x73, 0xd1, 0x00, 0x05, 0xe2, 0x00,
710x61, 0x72, 0xc2, 0x00, 0x10, 0x90, 0x6e, 0x75, 0x6d, 0x65, 710x0f, 0xd2, 0x00, 0x07, 0x3f, 0x62, 0x61, 0x72, 0xc2, 0x00,
720x72, 0x69, 0x63, 0x61, 0x6c, 0x78, 0x00, 0x03, 0x55, 0x00, 720x10, 0x90, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
730x61, 0x00, 0x28, 0x69, 0x2e, 0x65, 0x2e, 0x31, 0x00, 0x03, 730x6c, 0x78, 0x00, 0x03, 0x55, 0x00, 0x61, 0x00, 0x28, 0x69,
740x88, 0x00, 0x21, 0x6f, 0x72, 0x0e, 0x00, 0x6f, 0x6c, 0x6f, 740x2e, 0x65, 0x2e, 0x31, 0x00, 0x03, 0x88, 0x00, 0x21, 0x6f,
750x77, 0x65, 0x72, 0x29, 0xee, 0x00, 0x00, 0x0b, 0xec, 0x00, 750x72, 0x0e, 0x00, 0x6f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x29,
760x06, 0xe8, 0x00, 0x20, 0x61, 0x72, 0x0e, 0x00, 0x44, 0x77, 760xee, 0x00, 0x00, 0x0b, 0xec, 0x00, 0x06, 0xe8, 0x00, 0x20,
770x61, 0x79, 0x73, 0xec, 0x00, 0x54, 0x3a, 0x00, 0x61, 0x62, 770x61, 0x72, 0x0e, 0x00, 0x44, 0x77, 0x61, 0x79, 0x73, 0xec,
780x73, 0x0e, 0x02, 0x10, 0x61, 0xa6, 0x00, 0xb1, 0x00, 0x74, 780x00, 0x54, 0x3a, 0x00, 0x61, 0x62, 0x73, 0x0e, 0x02, 0x10,
790x68, 0x75, 0x73, 0x00, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x8d, 790x61, 0xa6, 0x00, 0xb1, 0x00, 0x74, 0x68, 0x75, 0x73, 0x00,
800x02, 0x2f, 0x00, 0x61, 0xa8, 0x00, 0x00, 0xa1, 0x64, 0x65, 800x6d, 0x65, 0x61, 0x6e, 0x73, 0x8d, 0x02, 0x2f, 0x00, 0x61,
810x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x6c, 0x79, 0x4a, 0x01, 810xa8, 0x00, 0x00, 0xa1, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69,
820x0f, 0xb7, 0x00, 0x02, 0x22, 0x74, 0x6f, 0x40, 0x00, 0x09, 820x74, 0x65, 0x6c, 0x79, 0x4a, 0x01, 0x0f, 0xb7, 0x00, 0x02,
830x85, 0x01, 0x02, 0x30, 0x01, 0x50, 0x54, 0x72, 0x69, 0x76, 830x22, 0x74, 0x6f, 0x40, 0x00, 0x09, 0x85, 0x01, 0x02, 0x30,
840x69, 0x01, 0x02, 0x0c, 0x52, 0x01, 0xa0, 0x00, 0x28, 0x61, 840x01, 0x50, 0x54, 0x72, 0x69, 0x76, 0x69, 0x01, 0x02, 0x0c,
850x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x8e, 0x01, 0x11, 850x52, 0x01, 0xa0, 0x00, 0x28, 0x61, 0x76, 0x61, 0x69, 0x6c,
860x61, 0x49, 0x01, 0x81, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 860x61, 0x62, 0x6c, 0x8e, 0x01, 0x11, 0x61, 0x49, 0x01, 0x81,
870x6d, 0x27, 0x5d, 0x02, 0xf1, 0x00, 0x00, 0x74, 0x79, 0x70, 870x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x27, 0x5d, 0x02,
880x65, 0x00, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 880xf1, 0x00, 0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x73, 0x65,
890x29, 0x6b, 0x01, 0x03, 0x8f, 0x02, 0x2b, 0x6e, 0x6f, 0x37, 890x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x29, 0x6b, 0x01, 0x03,
900x02, 0x01, 0x7c, 0x01, 0x1c, 0x69, 0x70, 0x02, 0x11, 0x3b, 900x8f, 0x02, 0x2b, 0x6e, 0x6f, 0x37, 0x02, 0x01, 0x7c, 0x01,
910x55, 0x00, 0x63, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x9e, 910x1c, 0x69, 0x70, 0x02, 0x11, 0x3b, 0x55, 0x00, 0x63, 0x70,
920x03, 0x42, 0x73, 0x6f, 0x6c, 0x76, 0x95, 0x03, 0x10, 0x4c, 920x75, 0x7a, 0x7a, 0x6c, 0x65, 0x9e, 0x03, 0x42, 0x73, 0x6f,
930x9d, 0x01, 0x04, 0x27, 0x04, 0x00, 0x34, 0x03, 0x00, 0xb2, 930x6c, 0x76, 0x95, 0x03, 0x10, 0x4c, 0x9d, 0x01, 0x04, 0x27,
940x00, 0x21, 0x41, 0x74, 0x1c, 0x00, 0x31, 0x74, 0x69, 0x6d, 940x04, 0x00, 0x34, 0x03, 0x00, 0xb2, 0x00, 0x21, 0x41, 0x74,
950x26, 0x01, 0x30, 0x77, 0x72, 0x69, 0xc2, 0x01, 0x02, 0xe6, 950x1c, 0x00, 0x31, 0x74, 0x69, 0x6d, 0x26, 0x01, 0x30, 0x77,
960x01, 0x0a, 0x5c, 0x00, 0x02, 0x33, 0x04, 0x27, 0x69, 0x73, 960x72, 0x69, 0xc2, 0x01, 0x02, 0xe6, 0x01, 0x0a, 0x5c, 0x00,
970x5f, 0x00, 0x60, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0xf2, 970x02, 0x33, 0x04, 0x27, 0x69, 0x73, 0x5f, 0x00, 0x60, 0x61,
980x01, 0x21, 0x69, 0x6e, 0x32, 0x00, 0xf1, 0x06, 0x47, 0x75, 980x70, 0x70, 0x65, 0x61, 0x72, 0xf2, 0x01, 0x21, 0x69, 0x6e,
990x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x00, 0x77, 0x65, 0x65, 990x32, 0x00, 0xf1, 0x06, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69,
1000x6b, 0x6c, 0x79, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x1a, 1000x61, 0x6e, 0x00, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x00,
1010x00, 0x10, 0x6e, 0xe0, 0x00, 0xa1, 0x60, 0x46, 0x75, 0x74, 1010x75, 0x6e, 0x64, 0x65, 0x72, 0x1a, 0x00, 0x10, 0x6e, 0xe0,
1020x6f, 0x73, 0x68, 0x69, 0x6b, 0x69, 0x2f, 0x03, 0x03, 0x5f, 1020x00, 0xa1, 0x60, 0x46, 0x75, 0x74, 0x6f, 0x73, 0x68, 0x69,
1030x00, 0x41, 0x00, 0x77, 0x61, 0x73, 0xcd, 0x03, 0x72, 0x72, 1030x6b, 0x69, 0x2f, 0x03, 0x03, 0x5f, 0x00, 0x41, 0x00, 0x77,
1040x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x5d, 0x01, 0x20, 0x69, 1040x61, 0x73, 0xcd, 0x03, 0x72, 0x72, 0x69, 0x62, 0x75, 0x74,
1050x73, 0xe8, 0x03, 0x00, 0x0b, 0x01, 0xf1, 0x03, 0x69, 0x6f, 1050x65, 0x64, 0x5d, 0x01, 0x20, 0x69, 0x73, 0xe8, 0x03, 0x00,
1060x6e, 0x00, 0x62, 0x79, 0x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 1060x0b, 0x01, 0xf1, 0x03, 0x69, 0x6f, 0x6e, 0x00, 0x62, 0x79,
1070x00, 0x48, 0x61, 0x72, 0x76, 0x65, 0xba, 0x00, 0x45, 0x32, 1070x00, 0x4a, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x48, 0x61, 0x72,
1080x37, 0x2e, 0x31, 0x1d, 0x05, 0x01, 0x3f, 0x00, 0x47, 0x6f, 1080x76, 0x65, 0xba, 0x00, 0x45, 0x32, 0x37, 0x2e, 0x31, 0x31,
1090x6c, 0x73, 0x20, 0x57, 0x00, 0x21, 0x73, 0x68, 0xcc, 0x04, 1090x05, 0x01, 0x3f, 0x00, 0x47, 0x6f, 0x6c, 0x73, 0x20, 0x57,
1100x10, 0x6d, 0x7a, 0x04, 0x21, 0x6f, 0x66, 0x46, 0x02, 0x03, 1100x00, 0x21, 0x73, 0x68, 0xcc, 0x04, 0x10, 0x6d, 0x7a, 0x04,
1110x27, 0x00, 0x72, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 1110x21, 0x6f, 0x66, 0x46, 0x02, 0x03, 0x27, 0x00, 0x72, 0x00,
1120xa2, 0x04, 0x41, 0x53, 0x6f, 0x6c, 0x6f, 0xfc, 0x03, 0x64, 1120x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0xa2, 0x04, 0x41, 0x53,
1130x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x3f, 0x00, 0xe1, 0x2c, 1130x6f, 0x6c, 0x6f, 0xfc, 0x03, 0x64, 0x6f, 0x00, 0x70, 0x6c,
1140x00, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 1140x61, 0x79, 0x3f, 0x00, 0xe1, 0x2c, 0x00, 0x73, 0x69, 0x6d,
1150x69, 0x63, 0x6b, 0xc4, 0x00, 0x60, 0x6d, 0x6f, 0x75, 0x73, 1150x70, 0x6c, 0x79, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xc4,
1160x65, 0x00, 0x58, 0x05, 0x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 1160x00, 0x60, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 0x58, 0x05,
1170x70, 0x74, 0x79, 0x4e, 0x01, 0x00, 0x0b, 0x04, 0x42, 0x74, 1170x84, 0x6e, 0x79, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x4e,
1180x68, 0x65, 0x6e, 0xc2, 0x01, 0x04, 0x77, 0x05, 0x70, 0x6f, 1180x01, 0x00, 0x0b, 0x04, 0x42, 0x74, 0x68, 0x65, 0x6e, 0xc2,
1190x72, 0x00, 0x6c, 0x65, 0x74, 0x74, 0xd5, 0x02, 0x02, 0x1f, 1190x01, 0x04, 0x77, 0x05, 0x70, 0x6f, 0x72, 0x00, 0x6c, 0x65,
1200x01, 0x81, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 1200x74, 0x74, 0xd5, 0x02, 0x02, 0x1f, 0x01, 0x81, 0x6b, 0x65,
1210x36, 0x05, 0x32, 0x69, 0x6c, 0x6c, 0x44, 0x02, 0x02, 0x44, 1210x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x36, 0x05, 0x32, 0x69,
1220x00, 0xc0, 0x2e, 0x00, 0x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 1220x6c, 0x6c, 0x44, 0x02, 0x02, 0x44, 0x00, 0xc0, 0x2e, 0x00,
1230x00, 0x6d, 0x61, 0x6b, 0x43, 0x00, 0x8e, 0x6d, 0x69, 0x73, 1230x49, 0x66, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6d, 0x61, 0x6b,
1240x74, 0x61, 0x6b, 0x65, 0x2c, 0x80, 0x00, 0x02, 0x4c, 0x00, 1240x43, 0x00, 0x8e, 0x6d, 0x69, 0x73, 0x74, 0x61, 0x6b, 0x65,
1250x98, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 1250x2c, 0x80, 0x00, 0x02, 0x4c, 0x00, 0x98, 0x69, 0x6e, 0x63,
1260x84, 0x00, 0xb0, 0x70, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 1260x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x84, 0x00, 0xb0, 0x70,
1270x70, 0x61, 0x63, 0x65, 0x64, 0x00, 0xb0, 0x63, 0x6c, 0x65, 1270x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x70, 0x61, 0x63, 0x65,
1280x61, 0x72, 0x00, 0x69, 0x74, 0x00, 0x61, 0x67, 0x0f, 0x06, 1280x64, 0x00, 0xb0, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x00, 0x69,
1290x62, 0x28, 0x6f, 0x72, 0x00, 0x75, 0x73, 0x0d, 0x02, 0xd1, 1290x74, 0x00, 0x61, 0x67, 0x0f, 0x06, 0x62, 0x28, 0x6f, 0x72,
1300x55, 0x6e, 0x64, 0x6f, 0x00, 0x66, 0x65, 0x61, 0x74, 0x75, 1300x00, 0x75, 0x73, 0x0d, 0x02, 0xd1, 0x55, 0x6e, 0x64, 0x6f,
1310x72, 0x65, 0x29, 0xbb, 0x02, 0x02, 0x7e, 0x00, 0x62, 0x72, 1310x00, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x29, 0xbb,
1320x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x00, 0x01, 0x42, 0x06, 1320x02, 0x02, 0x7e, 0x00, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74,
1330x0f, 0xe2, 0x00, 0x04, 0x02, 0xd8, 0x05, 0x13, 0x2c, 0x06, 1330x2d, 0x74, 0x00, 0x01, 0x42, 0x06, 0x0f, 0xe2, 0x00, 0x04,
1340x03, 0x01, 0x0d, 0x00, 0x20, 0x00, 0x77, 0xd3, 0x00, 0xa4, 1340x02, 0xd8, 0x05, 0x13, 0x2c, 0x06, 0x03, 0x01, 0x0d, 0x00,
1350x62, 0x65, 0x00, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 1350x20, 0x00, 0x77, 0xd3, 0x00, 0xa4, 0x62, 0x65, 0x00, 0x65,
1360xaa, 0x00, 0x04, 0x42, 0x00, 0xf1, 0x03, 0x73, 0x00, 0x61, 1360x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0xaa, 0x00, 0x04, 0x42,
1370x00, 0x60, 0x70, 0x65, 0x6e, 0x63, 0x69, 0x6c, 0x00, 0x6d, 1370x00, 0xf1, 0x03, 0x73, 0x00, 0x61, 0x00, 0x60, 0x70, 0x65,
1380x61, 0x72, 0x6b, 0x27, 0x2e, 0xd1, 0x06, 0x32, 0x63, 0x61, 1380x6e, 0x63, 0x69, 0x6c, 0x00, 0x6d, 0x61, 0x72, 0x6b, 0x27,
1390x6e, 0x70, 0x06, 0x07, 0x1b, 0x00, 0x02, 0x76, 0x05, 0x85, 1390x2e, 0xd1, 0x06, 0x32, 0x63, 0x61, 0x6e, 0x70, 0x06, 0x07,
1400x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x45, 0x06, 1400x1b, 0x00, 0x02, 0x76, 0x05, 0x85, 0x6d, 0x75, 0x6c, 0x74,
1410x04, 0x51, 0x00, 0x00, 0x4c, 0x02, 0x04, 0x36, 0x01, 0x13, 1410x69, 0x70, 0x6c, 0x65, 0x45, 0x06, 0x04, 0x51, 0x00, 0x00,
1420x53, 0xb3, 0x06, 0x03, 0x0e, 0x06, 0x00, 0x8d, 0x02, 0x00, 1420x4c, 0x02, 0x04, 0x36, 0x01, 0x13, 0x53, 0xb3, 0x06, 0x03,
1430x5b, 0x01, 0x55, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x39, 0x00, 1430x0e, 0x06, 0x00, 0x8d, 0x02, 0x00, 0x5b, 0x01, 0x55, 0x65,
1440x32, 0x63, 0x61, 0x6e, 0x15, 0x05, 0x25, 0x73, 0x6f, 0x1d, 1440x64, 0x2d, 0x69, 0x6e, 0x39, 0x00, 0x32, 0x63, 0x61, 0x6e,
1450x07, 0x08, 0x6f, 0x00, 0x03, 0xfc, 0x05, 0x02, 0x86, 0x03, 1450x15, 0x05, 0x25, 0x73, 0x6f, 0x1d, 0x07, 0x08, 0x6f, 0x00,
1460x10, 0x70, 0x43, 0x04, 0x81, 0x6e, 0x6f, 0x00, 0x61, 0x74, 1460x03, 0xfc, 0x05, 0x02, 0x86, 0x03, 0x10, 0x70, 0x43, 0x04,
1470x74, 0x65, 0x6e, 0x7e, 0x02, 0x29, 0x74, 0x6f, 0x2e, 0x00, 1470x81, 0x6e, 0x6f, 0x00, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x7e,
1480xe0, 0x2c, 0x00, 0x73, 0x6f, 0x00, 0x65, 0x78, 0x61, 0x63, 1480x02, 0x29, 0x74, 0x6f, 0x2e, 0x00, 0xe0, 0x2c, 0x00, 0x73,
1490x74, 0x6c, 0x79, 0x00, 0x77, 0x01, 0x01, 0x00, 0x3a, 0x01, 1490x6f, 0x00, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x00,
1500x03, 0x5a, 0x01, 0x11, 0x6d, 0xbb, 0x00, 0x50, 0x69, 0x73, 1500x77, 0x01, 0x01, 0x00, 0x3a, 0x01, 0x03, 0x5a, 0x01, 0x11,
1510x00, 0x75, 0x70, 0x38, 0x00, 0x63, 0x79, 0x6f, 0x75, 0x3a, 1510x6d, 0xbb, 0x00, 0x50, 0x69, 0x73, 0x00, 0x75, 0x70, 0x38,
1520x00, 0x79, 0xe7, 0x00, 0x05, 0x23, 0x00, 0x70, 0x61, 0x73, 1520x00, 0x63, 0x79, 0x6f, 0x75, 0x3a, 0x00, 0x79, 0xe7, 0x00,
1530x00, 0x72, 0x65, 0x6d, 0x69, 0x1e, 0x03, 0x05, 0x89, 0x04, 1530x05, 0x23, 0x00, 0x70, 0x61, 0x73, 0x00, 0x72, 0x65, 0x6d,
1540x06, 0x98, 0x05, 0x04, 0x32, 0x01, 0x41, 0x6e, 0x65, 0x65, 1540x69, 0x1e, 0x03, 0x05, 0x89, 0x04, 0x06, 0x98, 0x05, 0x04,
1550x64, 0xc4, 0x03, 0xf0, 0x01, 0x62, 0x65, 0x00, 0x72, 0x65, 1550x32, 0x01, 0x41, 0x6e, 0x65, 0x65, 0x64, 0xc4, 0x03, 0xf0,
1560x2d, 0x65, 0x78, 0x61, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x00, 1560x01, 0x62, 0x65, 0x00, 0x72, 0x65, 0x2d, 0x65, 0x78, 0x61,
1570x6f, 0xd3, 0x04, 0x00, 0x54, 0x00, 0x70, 0x6b, 0x6e, 0x6f, 1570x6d, 0x69, 0x6e, 0x65, 0x64, 0x00, 0x6f, 0xd3, 0x04, 0x00,
1580x77, 0x00, 0x6d, 0x6f, 0x5d, 0x01, 0x3b, 0x62, 0x6f, 0x75, 1580x54, 0x00, 0x70, 0x6b, 0x6e, 0x6f, 0x77, 0x00, 0x6d, 0x6f,
1590x45, 0x00, 0x04, 0xa2, 0x01, 0x2f, 0x6f, 0x72, 0x80, 0x00, 1590x5d, 0x01, 0x3b, 0x62, 0x6f, 0x75, 0x45, 0x00, 0x04, 0xa2,
1600x02, 0x54, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x07, 0x08, 0x31, 1600x01, 0x2f, 0x6f, 0x72, 0x80, 0x00, 0x02, 0x54, 0x6c, 0x69,
1610x70, 0x6f, 0x73, 0x32, 0x05, 0x08, 0x6a, 0x01, 0x64, 0x61, 1610x73, 0x74, 0x73, 0x07, 0x08, 0x31, 0x70, 0x6f, 0x73, 0x32,
1620x00, 0x67, 0x69, 0x76, 0x65, 0x31, 0x04, 0x01, 0x48, 0x00, 1620x05, 0x08, 0x6a, 0x01, 0x64, 0x61, 0x00, 0x67, 0x69, 0x76,
1630x50, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x62, 0x01, 0x32, 0x65, 1630x65, 0x31, 0x04, 0x01, 0x48, 0x00, 0x50, 0x61, 0x6e, 0x79,
1640x6c, 0x73, 0x82, 0x00, 0x93, 0x66, 0x65, 0x65, 0x6c, 0x00, 1640x74, 0x68, 0x62, 0x01, 0x32, 0x65, 0x6c, 0x73, 0x82, 0x00,
1650x6c, 0x69, 0x6b, 0x65, 0x42, 0x03, 0x41, 0x65, 0x72, 0x61, 1650x93, 0x66, 0x65, 0x65, 0x6c, 0x00, 0x6c, 0x69, 0x6b, 0x65,
1660x73, 0x9f, 0x08, 0x49, 0x69, 0x6e, 0x67, 0x6c, 0xd3, 0x01, 1660x42, 0x03, 0x41, 0x65, 0x72, 0x61, 0x73, 0x9f, 0x08, 0x49,
1670x1c, 0x2c, 0x5b, 0x02, 0x08, 0x1b, 0x02, 0x22, 0x6e, 0x64, 1670x69, 0x6e, 0x67, 0x6c, 0xd3, 0x01, 0x1c, 0x2c, 0x5b, 0x02,
1680x58, 0x02, 0x05, 0xde, 0x01, 0x03, 0x52, 0x02, 0x01, 0xb8, 1680x08, 0x1b, 0x02, 0x22, 0x6e, 0x64, 0x58, 0x02, 0x05, 0xde,
1690x02, 0x01, 0xa7, 0x04, 0x2a, 0x6c, 0x6c, 0x20, 0x02, 0x09, 1690x01, 0x03, 0x52, 0x02, 0x01, 0xb8, 0x02, 0x01, 0xa7, 0x04,
1700x9c, 0x02, 0x22, 0x72, 0x65, 0x79, 0x00, 0x30, 0x64, 0x00, 1700x2a, 0x6c, 0x6c, 0x20, 0x02, 0x09, 0x9c, 0x02, 0x22, 0x72,
1710x77, 0xa3, 0x02, 0x00, 0x99, 0x00, 0x34, 0x6c, 0x65, 0x66, 1710x65, 0x79, 0x00, 0x30, 0x64, 0x00, 0x77, 0xa3, 0x02, 0x00,
1720x6c, 0x00, 0x05, 0x5e, 0x00, 0x18, 0x61, 0x14, 0x01, 0x0f, 1720x99, 0x00, 0x34, 0x6c, 0x65, 0x66, 0x6c, 0x00, 0x05, 0x5e,
1730x2a, 0x00, 0x05, 0x02, 0x43, 0x03, 0x10, 0x73, 0x43, 0x03, 1730x00, 0x18, 0x61, 0x14, 0x01, 0x0f, 0x2a, 0x00, 0x05, 0x02,
1740x36, 0x2e, 0x00, 0x52, 0xb3, 0x00, 0x00, 0xf7, 0x00, 0x05, 1740x43, 0x03, 0x10, 0x73, 0x43, 0x03, 0x36, 0x2e, 0x00, 0x52,
1750x20, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x23, 0x00, 0x02, 0xf4, 1750xb3, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x20, 0x00, 0x00, 0x0d,
1760x02, 0x01, 0x58, 0x02, 0x02, 0xfb, 0x00, 0x0c, 0x56, 0x02, 1760x00, 0x01, 0x23, 0x00, 0x02, 0xf4, 0x02, 0x01, 0x58, 0x02,
1770x12, 0x41, 0xcb, 0x02, 0x00, 0x65, 0x04, 0x03, 0x3b, 0x07, 1770x02, 0xfb, 0x00, 0x0c, 0x56, 0x02, 0x12, 0x41, 0xcb, 0x02,
1780x50, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x0b, 0x04, 0x01, 0x97, 1780x00, 0x65, 0x04, 0x03, 0x3b, 0x07, 0x50, 0x75, 0x72, 0x73,
1790x02, 0x00, 0xdc, 0x01, 0x22, 0x75, 0x73, 0x2d, 0x03, 0x62, 1790x6f, 0x72, 0x0b, 0x04, 0x01, 0x97, 0x02, 0x00, 0xdc, 0x01,
1800x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, 0xf1, 0x04, 0x01, 0x9b, 1800x22, 0x75, 0x73, 0x2d, 0x03, 0x62, 0x63, 0x6f, 0x6e, 0x6a,
1810x04, 0x00, 0x30, 0x00, 0x02, 0x4b, 0x04, 0x01, 0x2f, 0x00, 1810x75, 0x6e, 0xf1, 0x04, 0x01, 0x9b, 0x04, 0x00, 0x30, 0x00,
1820x00, 0xce, 0x05, 0x25, 0x65, 0x74, 0xa4, 0x01, 0x2b, 0x6f, 1820x02, 0x4b, 0x04, 0x01, 0x2f, 0x00, 0x00, 0xce, 0x05, 0x25,
1830x72, 0x6e, 0x00, 0x04, 0x4e, 0x03, 0x01, 0x8f, 0x00, 0x04, 1830x65, 0x74, 0xa4, 0x01, 0x2b, 0x6f, 0x72, 0x6e, 0x00, 0x04,
1840xe9, 0x03, 0x30, 0x60, 0x4d, 0x27, 0x3e, 0x00, 0x00, 0x3d, 1840x4e, 0x03, 0x01, 0x8f, 0x00, 0x04, 0xe9, 0x03, 0x30, 0x60,
1850x00, 0x51, 0x61, 0x75, 0x74, 0x6f, 0x2d, 0x78, 0x04, 0x54, 1850x4d, 0x27, 0x3e, 0x00, 0x00, 0x3d, 0x00, 0x51, 0x61, 0x75,
1860x65, 0x76, 0x65, 0x72, 0x79, 0xda, 0x06, 0xc1, 0x00, 0x68, 1860x74, 0x6f, 0x2d, 0x78, 0x04, 0x54, 0x65, 0x76, 0x65, 0x72,
1870x69, 0x6e, 0x74, 0x2c, 0x00, 0x72, 0x65, 0x61, 0x64, 0x79, 1870x79, 0xda, 0x06, 0xc1, 0x00, 0x68, 0x69, 0x6e, 0x74, 0x2c,
1880xad, 0x00, 0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x61, 0x6c, 1880x00, 0x72, 0x65, 0x61, 0x64, 0x79, 0xad, 0x00, 0x72, 0x72,
1890xa6, 0x02, 0x61, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x33, 1890x65, 0x6d, 0x6f, 0x76, 0x61, 0x6c, 0xa6, 0x02, 0x61, 0x71,
1900x01, 0x01, 0x4f, 0x00, 0x15, 0x48, 0x4f, 0x00, 0x26, 0x64, 1900x75, 0x69, 0x72, 0x65, 0x64, 0x33, 0x01, 0x01, 0x4f, 0x00,
1910x6f, 0xb0, 0x01, 0x23, 0x62, 0x75, 0x5e, 0x03, 0x22, 0x74, 1910x15, 0x48, 0x4f, 0x00, 0x26, 0x64, 0x6f, 0xb0, 0x01, 0x23,
1920x6f, 0x3f, 0x00, 0x02, 0x9a, 0x07, 0x71, 0x6f, 0x62, 0x76, 1920x62, 0x75, 0x5e, 0x03, 0x22, 0x74, 0x6f, 0x3f, 0x00, 0x02,
1930x69, 0x6f, 0x75, 0x73, 0x62, 0x00, 0x12, 0x73, 0xc8, 0x01, 1930x9a, 0x07, 0x71, 0x6f, 0x62, 0x76, 0x69, 0x6f, 0x75, 0x73,
1940xc5, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 1940x62, 0x00, 0x12, 0x73, 0xc8, 0x01, 0xc5, 0x74, 0x65, 0x72,
1950x6c, 0x79, 0x2c, 0xa5, 0x00, 0x08, 0x15, 0x01, 0x10, 0x74, 1950x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x2c, 0xa5,
1960x79, 0x09, 0x03, 0xb3, 0x06, 0x00, 0xd9, 0x00, 0x75, 0x00, 1960x00, 0x08, 0x15, 0x01, 0x10, 0x74, 0x79, 0x09, 0x03, 0xb3,
1970x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x58, 0x0a, 0x34, 0x2e, 1970x06, 0x00, 0xd9, 0x00, 0x75, 0x00, 0x61, 0x72, 0x6f, 0x75,
1980x00, 0x50, 0x83, 0x01, 0x20, 0x74, 0x68, 0x19, 0x03, 0x43, 1980x6e, 0x64, 0x58, 0x0a, 0x34, 0x2e, 0x00, 0x50, 0x83, 0x01,
1990x74, 0x75, 0x72, 0x6e, 0x94, 0x00, 0x56, 0x67, 0x67, 0x6c, 1990x20, 0x74, 0x68, 0x19, 0x03, 0x43, 0x74, 0x75, 0x72, 0x6e,
2000x65, 0x73, 0x3a, 0x00, 0x11, 0x28, 0xf9, 0x0a, 0x72, 0x61, 2000x94, 0x00, 0x56, 0x67, 0x67, 0x6c, 0x65, 0x73, 0x3a, 0x00,
2010x00, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x27, 0x01, 0x01, 0x08, 2010x11, 0x28, 0xf9, 0x0a, 0x72, 0x61, 0x00, 0x6e, 0x6f, 0x72,
2020x01, 0x08, 0x38, 0x01, 0x24, 0x29, 0x2c, 0x21, 0x02, 0x01, 2020x6d, 0x61, 0x27, 0x01, 0x01, 0x08, 0x01, 0x08, 0x38, 0x01,
2030xe2, 0x01, 0x04, 0x7a, 0x02, 0x20, 0x69, 0x6e, 0xc4, 0x03, 2030x24, 0x29, 0x2c, 0x21, 0x02, 0x01, 0xe2, 0x01, 0x04, 0x7a,
2040x0f, 0xca, 0x04, 0x03, 0x03, 0x0e, 0x00, 0x80, 0x61, 0x70, 2040x02, 0x20, 0x69, 0x6e, 0xc4, 0x03, 0x0f, 0xca, 0x04, 0x03,
2050x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0xf5, 0x0a, 0x44, 0x77, 2050x03, 0x0e, 0x00, 0x80, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70,
2060x61, 0x79, 0x3b, 0x44, 0x00, 0x01, 0x9b, 0x02, 0x21, 0x30, 2060x72, 0x69, 0xf5, 0x0a, 0x44, 0x77, 0x61, 0x79, 0x3b, 0x44,
2070x00, 0x6d, 0x05, 0x04, 0xa6, 0x00, 0x02, 0x2d, 0x02, 0x23, 2070x00, 0x01, 0x9b, 0x02, 0x21, 0x30, 0x00, 0x6d, 0x05, 0x04,
2080x62, 0x61, 0x25, 0x05, 0x02, 0x99, 0x05, 0x13, 0x61, 0xaa, 2080xa6, 0x00, 0x02, 0x2d, 0x02, 0x23, 0x62, 0x61, 0x25, 0x05,
2090x04, 0x05, 0xcc, 0x04, 0x58, 0x00, 0x00, 0x4c, 0x65, 0x66, 2090x02, 0x99, 0x05, 0x13, 0x61, 0xaa, 0x04, 0x05, 0xcc, 0x04,
2100x70, 0x02, 0x02, 0x69, 0x09, 0x01, 0x33, 0x00, 0x01, 0xbd, 2100x58, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x70, 0x02, 0x02, 0x69,
2110x00, 0x20, 0x69, 0x74, 0x95, 0x01, 0x10, 0x64, 0x21, 0x09, 2110x09, 0x01, 0x33, 0x00, 0x01, 0xbd, 0x00, 0x20, 0x69, 0x74,
2120x60, 0x28, 0x67, 0x72, 0x65, 0x79, 0x00, 0x66, 0x06, 0x41, 2120x95, 0x01, 0x10, 0x64, 0x21, 0x09, 0x60, 0x28, 0x67, 0x72,
2130x75, 0x74, 0x29, 0x2c, 0x6e, 0x00, 0x14, 0x6e, 0x24, 0x00, 2130x65, 0x79, 0x00, 0x66, 0x06, 0x41, 0x75, 0x74, 0x29, 0x2c,
2140x10, 0x69, 0xee, 0x06, 0x01, 0xa1, 0x07, 0x12, 0x6c, 0xd6, 2140x6e, 0x00, 0x14, 0x6e, 0x24, 0x00, 0x10, 0x69, 0xee, 0x06,
2150x01, 0x00, 0x19, 0x00, 0x00, 0xe2, 0x0a, 0x40, 0x48, 0x6f, 2150x01, 0xa1, 0x07, 0x12, 0x6c, 0xd6, 0x01, 0x00, 0x19, 0x00,
2160x6c, 0x64, 0x59, 0x00, 0x13, 0x43, 0x08, 0x07, 0x00, 0x9a, 2160x00, 0xe2, 0x0a, 0x40, 0x48, 0x6f, 0x6c, 0x64, 0x59, 0x00,
2170x02, 0x4a, 0x68, 0x69, 0x66, 0x74, 0xda, 0x02, 0x00, 0x43, 2170x13, 0x43, 0x08, 0x07, 0x00, 0x9a, 0x02, 0x4a, 0x68, 0x69,
2180x02, 0x10, 0x72, 0x9d, 0x0b, 0x00, 0x55, 0x01, 0x00, 0xdd, 2180x66, 0x74, 0xda, 0x02, 0x00, 0x43, 0x02, 0x10, 0x72, 0x9d,
2190x03, 0x43, 0x77, 0x69, 0x73, 0x65, 0x78, 0x03, 0x00, 0xfb, 2190x0b, 0x00, 0x55, 0x01, 0x00, 0xdd, 0x03, 0x43, 0x77, 0x69,
2200x06, 0x01, 0x95, 0x00, 0x0a, 0x0b, 0x09, 0x05, 0xc3, 0x01, 2200x73, 0x65, 0x78, 0x03, 0x00, 0xfb, 0x06, 0x01, 0x95, 0x00,
2210x03, 0x1c, 0x01, 0x02, 0x3b, 0x04, 0x32, 0x64, 0x69, 0x72, 2210x0a, 0x0b, 0x09, 0x05, 0xc3, 0x01, 0x03, 0x1c, 0x01, 0x02,
2220xc0, 0x07, 0x00, 0xdb, 0x00, 0x14, 0x28, 0x81, 0x0b, 0x11, 2220x3b, 0x04, 0x32, 0x64, 0x69, 0x72, 0xc0, 0x07, 0x00, 0xdb,
2230x61, 0x13, 0x00, 0x00, 0x68, 0x09, 0x52, 0x73, 0x63, 0x72, 2230x00, 0x14, 0x28, 0x81, 0x0b, 0x11, 0x61, 0x13, 0x00, 0x00,
2240x69, 0x62, 0x5f, 0x01, 0x13, 0x73, 0xe9, 0x07, 0x33, 0x32, 2240x68, 0x09, 0x52, 0x73, 0x63, 0x72, 0x69, 0x62, 0x5f, 0x01,
2250x2e, 0x31, 0xca, 0x09, 0x35, 0x73, 0x6f, 0x00, 0x30, 0x09, 2250x13, 0x73, 0xe9, 0x07, 0x33, 0x32, 0x2e, 0x31, 0xca, 0x09,
2260x22, 0x2e, 0x29, 0xf1, 0x07, 0x15, 0x32, 0xf1, 0x07, 0xb2, 2260x35, 0x73, 0x6f, 0x00, 0x30, 0x09, 0x22, 0x2e, 0x29, 0xf1,
2270x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 2270x07, 0x15, 0x32, 0xf1, 0x07, 0xb2, 0x70, 0x61, 0x72, 0x61,
2280x20, 0xbc, 0x05, 0x00, 0x7c, 0x03, 0x05, 0x14, 0x00, 0x02, 2280x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0xbc, 0x05, 0x00,
2290x43, 0x00, 0x05, 0x6e, 0x09, 0x01, 0x07, 0x02, 0x07, 0x6f, 2290x7c, 0x03, 0x05, 0x14, 0x00, 0x02, 0x43, 0x00, 0x05, 0x6e,
2300x09, 0x71, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 2300x09, 0x01, 0x07, 0x02, 0x07, 0x6f, 0x09, 0x71, 0x2e, 0x2e,
2310x00, 0x03, 0x9b, 0x07, 0xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 2310x2e, 0x27, 0x00, 0x6f, 0x70, 0x71, 0x00, 0x03, 0x9b, 0x07,
2320x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xad, 0x00, 0x10, 0x4d, 2320xb0, 0x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65,
2330xf1, 0x08, 0x03, 0x07, 0x00, 0x28, 0x6f, 0x66, 0x56, 0x09, 2330x6e, 0x75, 0xad, 0x00, 0x10, 0x4d, 0xf1, 0x08, 0x03, 0x07,
2340x16, 0x28, 0x16, 0x09, 0x27, 0x6f, 0x72, 0x1e, 0x0b, 0x00, 2340x00, 0x28, 0x6f, 0x66, 0x56, 0x09, 0x16, 0x28, 0x16, 0x09,
2350xa2, 0x00, 0x94, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x28, 0x73, 2350x27, 0x6f, 0x72, 0x1e, 0x0b, 0x00, 0xa2, 0x00, 0x94, 0x53,
2360x2a, 0x73, 0x0d, 0x00, 0x00, 0x3c, 0x00, 0x02, 0xb4, 0x02, 2360x69, 0x7a, 0x65, 0x20, 0x28, 0x73, 0x2a, 0x73, 0x0d, 0x00,
2370x36, 0x00, 0x00, 0x44, 0x0f, 0x0a, 0x14, 0x00, 0x81, 0x01, 2370x00, 0x3c, 0x00, 0x02, 0xb4, 0x02, 0x36, 0x00, 0x00, 0x44,
2380x02, 0xac, 0x02, 0x07, 0x29, 0x0a, 0x04, 0x97, 0x0d, 0x50, 2380x0f, 0x0a, 0x14, 0x00, 0x81, 0x01, 0x02, 0xac, 0x02, 0x07,
2390x65, 0x6e, 0x65, 0x72, 0x61, 0x0c, 0x09, 0x02, 0x76, 0x00, 2390x29, 0x0a, 0x04, 0x97, 0x0d, 0x50, 0x65, 0x6e, 0x65, 0x72,
2400x10, 0x2e, 0xa8, 0x09, 0x03, 0x59, 0x0a, 0x02, 0x4d, 0x0a, 2400x61, 0x0c, 0x09, 0x02, 0x76, 0x00, 0x10, 0x2e, 0xa8, 0x09,
2410x0f, 0x1d, 0x0a, 0x0e, 0x0f, 0x0b, 0x0a, 0x1d, 0x50, 0x41, 2410x03, 0x59, 0x0a, 0x02, 0x4d, 0x0a, 0x0f, 0x1d, 0x0a, 0x0e,
2420x74, 0x00, 0x52, 0x65, 0xc5, 0x01, 0x34, 0x69, 0x76, 0x65, 2420x0f, 0x0b, 0x0a, 0x1d, 0x50, 0x41, 0x74, 0x00, 0x52, 0x65,
2430xb0, 0x0a, 0x01, 0x59, 0x0d, 0x0f, 0xb5, 0x0a, 0x1b, 0x82, 2430xc5, 0x01, 0x34, 0x69, 0x76, 0x65, 0xb0, 0x0a, 0x01, 0x59,
2440x00, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0xc1, 0x02, 2440x0d, 0x0f, 0xb5, 0x0a, 0x1b, 0x82, 0x00, 0x62, 0x61, 0x63,
2450x04, 0x12, 0x08, 0x06, 0x47, 0x04, 0x00, 0x29, 0x04, 0x01, 2450x6b, 0x74, 0x72, 0x61, 0xc1, 0x02, 0x04, 0x12, 0x08, 0x06,
2460x11, 0x03, 0x31, 0x6f, 0x6c, 0x75, 0x80, 0x01, 0x93, 0x73, 2460x47, 0x04, 0x00, 0x29, 0x04, 0x01, 0x11, 0x03, 0x31, 0x6f,
2470x68, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x73, 0x74, 0x2b, 0x00, 2470x6c, 0x75, 0x80, 0x01, 0x93, 0x73, 0x68, 0x6f, 0x75, 0x6c,
2480x71, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x2e, 0x8f, 0x07, 2480x64, 0x00, 0x73, 0x74, 0x2b, 0x00, 0x71, 0x75, 0x6e, 0x69,
2490x02, 0x89, 0x0c, 0x00, 0x1f, 0x02, 0x04, 0x81, 0x0e, 0x03, 2490x71, 0x75, 0x65, 0x2e, 0x8f, 0x07, 0x02, 0x89, 0x0c, 0x00,
2500x49, 0x00, 0x00, 0xfe, 0x08, 0x31, 0x72, 0x65, 0x61, 0x66, 2500x1f, 0x02, 0x04, 0x81, 0x0e, 0x03, 0x49, 0x00, 0x00, 0xfe,
2510x06, 0x00, 0xef, 0x0e, 0x60, 0x6d, 0x70, 0x6c, 0x65, 0x78, 2510x08, 0x31, 0x72, 0x65, 0x61, 0x66, 0x06, 0x00, 0xef, 0x0e,
2520x00, 0x12, 0x00, 0x21, 0x6f, 0x6e, 0x73, 0x03, 0x00, 0x3c, 2520x60, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x00, 0x12, 0x00, 0x21,
2530x02, 0x30, 0x6f, 0x69, 0x64, 0x59, 0x08, 0x03, 0x10, 0x00, 2530x6f, 0x6e, 0x73, 0x03, 0x00, 0x3c, 0x02, 0x30, 0x6f, 0x69,
2540xb0, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x6b, 2540x64, 0x59, 0x08, 0x03, 0x10, 0x00, 0xb0, 0x62, 0x61, 0x63,
2550x2e, 0x00, 2550x6b, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x2e, 0x00,
256}; 256};
257 257
258const unsigned short help_text_len = 3934; 258const unsigned short help_text_len = 3954;
259const unsigned short help_text_words = 730; 259const unsigned short help_text_words = 731;
260const bool help_valid = true;
260const char quick_help_text[] = "Complete the latin square in accordance with the > signs."; 261const char quick_help_text[] = "Complete the latin square in accordance with the > signs.";
diff --git a/apps/plugins/puzzles/help/unruly.c b/apps/plugins/puzzles/help/unruly.c
index b098642a72..97348e5c26 100644
--- a/apps/plugins/puzzles/help/unruly.c
+++ b/apps/plugins/puzzles/help/unruly.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,142 +6,144 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 99, TEXT_CENTER | C_RED }, 9 { 100, TEXT_CENTER | C_RED },
10 { 221, TEXT_CENTER | C_RED }, 10 { 222, TEXT_CENTER | C_RED },
11 { 238, TEXT_UNDERLINE },
12 { 239, TEXT_UNDERLINE }, 11 { 239, TEXT_UNDERLINE },
13 { 266, TEXT_UNDERLINE }, 12 { 240, TEXT_UNDERLINE },
14 { 278, TEXT_UNDERLINE }, 13 { 267, TEXT_UNDERLINE },
14 { 279, TEXT_UNDERLINE },
15 LAST_STYLE_ITEM 15 LAST_STYLE_ITEM
16}; 16};
17 17
18/* orig 1688 comp 1225 ratio 0.725711 level 10 saved 463 */ 18/* orig 1707 comp 1231 ratio 0.721148 level 10 saved 476 */
19const char help_text[] = { 19const char help_text[] = {
200xf0, 0x4d, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 200xfd, 0x06, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
210x33, 0x38, 0x3a, 0x20, 0x55, 0x6e, 0x72, 0x75, 0x6c, 0x79, 210x33, 0x38, 0x3a, 0x20, 0x55, 0x6e, 0x72, 0x75, 0x6c, 0x79,
220x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 220x20, 0x00, 0x2d, 0x01, 0x00, 0xf0, 0x3a, 0x00, 0x00, 0x00,
230x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 230x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65, 0x00, 0x67, 0x69,
240x67, 0x72, 0x69, 0x64, 0x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 240x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x72, 0x69, 0x64,
250x75, 0x61, 0x72, 0x65, 0x73, 0x2c, 0x00, 0x77, 0x68, 0x69, 250x00, 0x6f, 0x66, 0x00, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65,
260x63, 0x68, 0x00, 0x79, 0x6f, 0x75, 0x00, 0x6d, 0x75, 0x73, 260x73, 0x2c, 0x00, 0x77, 0x68, 0x69, 0x63, 0x68, 0x00, 0x79,
270x74, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x00, 0x65, 270x6f, 0x75, 0x00, 0x6d, 0x75, 0x73, 0x74, 0x00, 0x63, 0x6f,
280x69, 0x74, 0x68, 0x65, 0x72, 0x00, 0x62, 0x6c, 0x61, 0x63, 280x6c, 0x6f, 0x75, 0x72, 0x00, 0x65, 0x69, 0x74, 0x68, 0x65,
290x6b, 0x00, 0x6f, 0x72, 0x26, 0x00, 0x84, 0x74, 0x65, 0x2e, 290x72, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x00, 0x6f, 0x72,
300x00, 0x53, 0x6f, 0x6d, 0x65, 0x3b, 0x00, 0x01, 0x57, 0x00, 300x26, 0x00, 0x84, 0x74, 0x65, 0x2e, 0x00, 0x53, 0x6f, 0x6d,
310xf1, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 310x65, 0x3b, 0x00, 0x01, 0x57, 0x00, 0xf1, 0x0c, 0x70, 0x72,
320x00, 0x61, 0x73, 0x00, 0x63, 0x6c, 0x75, 0x65, 0x73, 0x3b, 320x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x00, 0x61, 0x73, 0x00,
330x00, 0x74, 0x68, 0x65, 0x00, 0x72, 0x65, 0x73, 0x74, 0x20, 330x63, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x00, 0x74, 0x68, 0x65,
340x00, 0x81, 0x6c, 0x65, 0x66, 0x74, 0x00, 0x66, 0x6f, 0x72, 340x00, 0x72, 0x65, 0x73, 0x74, 0x20, 0x00, 0x81, 0x6c, 0x65,
350x61, 0x00, 0xf0, 0x09, 0x74, 0x6f, 0x00, 0x66, 0x69, 0x6c, 350x66, 0x74, 0x00, 0x66, 0x6f, 0x72, 0x61, 0x00, 0xf0, 0x09,
360x6c, 0x00, 0x69, 0x6e, 0x2e, 0x00, 0x45, 0x61, 0x63, 0x68, 360x74, 0x6f, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x00, 0x69, 0x6e,
370x00, 0x72, 0x6f, 0x77, 0x00, 0x61, 0x6e, 0x64, 0x75, 0x00, 370x2e, 0x00, 0x45, 0x61, 0x63, 0x68, 0x00, 0x72, 0x6f, 0x77,
380x34, 0x75, 0x6d, 0x6e, 0x81, 0x00, 0x51, 0x6e, 0x74, 0x61, 380x00, 0x61, 0x6e, 0x64, 0x75, 0x00, 0x34, 0x75, 0x6d, 0x6e,
390x69, 0x6e, 0x47, 0x00, 0xb0, 0x73, 0x61, 0x6d, 0x65, 0x00, 390x81, 0x00, 0x51, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x00,
400x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xb4, 0x00, 0x02, 0x8e, 400xb0, 0x73, 0x61, 0x6d, 0x65, 0x00, 0x6e, 0x75, 0x6d, 0x62,
410x00, 0x00, 0x31, 0x00, 0x01, 0x8f, 0x00, 0x06, 0xc4, 0x00, 410x65, 0x72, 0xb4, 0x00, 0x02, 0x8e, 0x00, 0x00, 0x31, 0x00,
420x00, 0x13, 0x00, 0x21, 0x6e, 0x6f, 0x4f, 0x00, 0x25, 0x6f, 420x01, 0x8f, 0x00, 0x06, 0xc4, 0x00, 0x00, 0x13, 0x00, 0x21,
430x72, 0x4e, 0x00, 0x27, 0x61, 0x79, 0x4d, 0x00, 0x30, 0x72, 430x6e, 0x6f, 0x4f, 0x00, 0x25, 0x6f, 0x72, 0x4e, 0x00, 0x27,
440x65, 0x65, 0x0e, 0x00, 0x76, 0x73, 0x65, 0x63, 0x75, 0x74, 440x61, 0x79, 0x4d, 0x00, 0x30, 0x72, 0x65, 0x65, 0x0e, 0x00,
450x69, 0x76, 0xc5, 0x00, 0x26, 0x6f, 0x66, 0x6a, 0x00, 0x02, 450x76, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0xc5, 0x00,
460xfc, 0x00, 0xf1, 0x2d, 0x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 460x26, 0x6f, 0x66, 0x6a, 0x00, 0x02, 0xfc, 0x00, 0xf1, 0x2d,
470x69, 0x73, 0x00, 0x70, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 470x2e, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x00, 0x70,
480x74, 0x79, 0x70, 0x65, 0x00, 0x77, 0x61, 0x73, 0x00, 0x69, 480x75, 0x7a, 0x7a, 0x6c, 0x65, 0x00, 0x74, 0x79, 0x70, 0x65,
490x6e, 0x76, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x00, 0x62, 0x79, 490x00, 0x77, 0x61, 0x73, 0x00, 0x69, 0x6e, 0x76, 0x65, 0x6e,
500x00, 0x41, 0x64, 0x6f, 0x6c, 0x66, 0x6f, 0x00, 0x5a, 0x61, 500x74, 0x65, 0x64, 0x00, 0x62, 0x79, 0x00, 0x41, 0x64, 0x6f,
510x6e, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x2c, 0x00, 0x75, 510x6c, 0x66, 0x6f, 0x00, 0x5a, 0x61, 0x6e, 0x65, 0x6c, 0x6c,
520x6e, 0x64, 0x65, 0x72, 0x4c, 0x00, 0x10, 0x6e, 0x4c, 0x00, 520x61, 0x74, 0x69, 0x2c, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72,
530xf1, 0x09, 0x60, 0x54, 0x6f, 0x68, 0x75, 0x00, 0x77, 0x61, 530x4c, 0x00, 0x10, 0x6e, 0x4c, 0x00, 0xf1, 0x09, 0x60, 0x54,
540x00, 0x56, 0x6f, 0x68, 0x75, 0x27, 0x2e, 0x00, 0x53, 0x65, 540x6f, 0x68, 0x75, 0x00, 0x77, 0x61, 0x00, 0x56, 0x6f, 0x68,
550x65, 0x00, 0x5b, 0x32, 0x31, 0x5d, 0x0d, 0x01, 0xc0, 0x6d, 550x75, 0x27, 0x2e, 0x00, 0x53, 0x65, 0x65, 0x00, 0x5b, 0x32,
560x6f, 0x72, 0x65, 0x00, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 560x31, 0x5d, 0x0d, 0x01, 0xc0, 0x6d, 0x6f, 0x72, 0x65, 0x00,
570x73, 0x6f, 0x00, 0x02, 0xaf, 0x01, 0x01, 0x65, 0x00, 0x00, 570x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x6f, 0x00, 0x02,
580xb2, 0x00, 0x40, 0x72, 0x69, 0x62, 0x75, 0x68, 0x00, 0x60, 580xc2, 0x01, 0x01, 0x65, 0x00, 0x00, 0xb2, 0x00, 0x40, 0x72,
590x74, 0x6f, 0x00, 0x74, 0x68, 0x69, 0x14, 0x00, 0x80, 0x6c, 590x69, 0x62, 0x75, 0x68, 0x00, 0x60, 0x74, 0x6f, 0x00, 0x74,
600x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x7b, 0x00, 0xe0, 600x68, 0x69, 0x14, 0x00, 0x80, 0x6c, 0x6c, 0x65, 0x63, 0x74,
610x4c, 0x65, 0x6e, 0x6e, 0x61, 0x72, 0x64, 0x00, 0x53, 0x70, 610x69, 0x6f, 0x6e, 0x7b, 0x00, 0xe0, 0x4c, 0x65, 0x6e, 0x6e,
620x72, 0x6f, 0x6e, 0x67, 0x3f, 0x00, 0x01, 0x58, 0x00, 0xf0, 620x61, 0x72, 0x64, 0x00, 0x53, 0x70, 0x72, 0x6f, 0x6e, 0x67,
630x0d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 630x3f, 0x00, 0x01, 0x58, 0x00, 0xf0, 0x0d, 0x68, 0x74, 0x74,
640x77, 0x2e, 0x6a, 0x61, 0x6e, 0x6b, 0x6f, 0x2e, 0x61, 0x74, 640x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6a, 0x61,
650x2f, 0x52, 0x61, 0x65, 0x74, 0x73, 0x65, 0x6c, 0x2f, 0x8c, 650x6e, 0x6b, 0x6f, 0x2e, 0x61, 0x74, 0x2f, 0x52, 0x61, 0x65,
660x00, 0x40, 0x2d, 0x57, 0x61, 0x2d, 0x8c, 0x00, 0xf4, 0x02, 660x74, 0x73, 0x65, 0x6c, 0x2f, 0x8c, 0x00, 0x40, 0x2d, 0x57,
670x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 670x61, 0x2d, 0x8c, 0x00, 0xf4, 0x02, 0x2f, 0x69, 0x6e, 0x64,
680x00, 0x00, 0x00, 0x33, 0x38, 0x2e, 0x31, 0x2d, 0x02, 0x01, 680x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x33,
690x7a, 0x00, 0x40, 0x6f, 0x6c, 0x73, 0x20, 0x00, 0x01, 0x63, 690x38, 0x2e, 0x31, 0x40, 0x02, 0x01, 0x7a, 0x00, 0x40, 0x6f,
700x6f, 0x00, 0x70, 0x6c, 0x61, 0x79, 0x99, 0x00, 0x71, 0x2c, 700x6c, 0x73, 0x20, 0x00, 0x01, 0x63, 0x6f, 0x00, 0x70, 0x6c,
710x00, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0xdd, 0x00, 0x70, 0x6d, 710x61, 0x79, 0x99, 0x00, 0x71, 0x2c, 0x00, 0x63, 0x6c, 0x69,
720x6f, 0x75, 0x73, 0x65, 0x00, 0x69, 0x4b, 0x02, 0x02, 0x43, 720x63, 0x6b, 0xdd, 0x00, 0x70, 0x6d, 0x6f, 0x75, 0x73, 0x65,
730x01, 0x00, 0xa6, 0x00, 0xa5, 0x63, 0x68, 0x61, 0x6e, 0x67, 730x00, 0x69, 0x4b, 0x02, 0x02, 0x43, 0x01, 0x00, 0xa6, 0x00,
740x65, 0x00, 0x69, 0x74, 0x73, 0x44, 0x01, 0x51, 0x4c, 0x65, 740xa5, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x69, 0x74,
750x66, 0x74, 0x2d, 0x37, 0x00, 0xc4, 0x69, 0x6e, 0x67, 0x00, 750x73, 0x44, 0x01, 0x51, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x37,
760x61, 0x6e, 0x00, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x34, 0x00, 760x00, 0xc4, 0x69, 0x6e, 0x67, 0x00, 0x61, 0x6e, 0x00, 0x65,
770x10, 0x77, 0x07, 0x02, 0x72, 0x74, 0x75, 0x72, 0x6e, 0x00, 770x6d, 0x70, 0x74, 0x79, 0x34, 0x00, 0x10, 0x77, 0x07, 0x02,
780x69, 0x74, 0xd7, 0x01, 0x02, 0xc5, 0x01, 0x47, 0x72, 0x69, 780x72, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x69, 0x74, 0xd7, 0x01,
790x67, 0x68, 0x37, 0x00, 0x09, 0x27, 0x00, 0x03, 0x83, 0x02, 790x02, 0xc5, 0x01, 0x47, 0x72, 0x69, 0x67, 0x68, 0x37, 0x00,
800x55, 0x4b, 0x65, 0x65, 0x70, 0x00, 0x22, 0x00, 0x05, 0xbc, 800x09, 0x27, 0x00, 0x03, 0x83, 0x02, 0x55, 0x4b, 0x65, 0x65,
810x01, 0x61, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x8d, 0x00, 810x70, 0x00, 0x22, 0x00, 0x05, 0xbc, 0x01, 0x61, 0x62, 0x75,
820x40, 0x79, 0x63, 0x6c, 0x65, 0xf2, 0x01, 0x41, 0x6f, 0x75, 820x74, 0x74, 0x6f, 0x6e, 0x8d, 0x00, 0x40, 0x79, 0x63, 0x6c,
830x67, 0x68, 0x21, 0x00, 0x02, 0xfe, 0x01, 0xf1, 0x00, 0x70, 830x65, 0xf2, 0x01, 0x41, 0x6f, 0x75, 0x67, 0x68, 0x21, 0x00,
840x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x00, 0x73, 0x74, 840x02, 0xfe, 0x01, 0xf1, 0x00, 0x70, 0x6f, 0x73, 0x73, 0x69,
850x61, 0x74, 0x65, 0x73, 0x89, 0x01, 0x24, 0x74, 0x68, 0x0a, 850x62, 0x6c, 0x65, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73,
860x02, 0x42, 0x2e, 0x00, 0x49, 0x66, 0x06, 0x03, 0x52, 0x69, 860x89, 0x01, 0x24, 0x74, 0x68, 0x0a, 0x02, 0x42, 0x2e, 0x00,
870x64, 0x64, 0x6c, 0x65, 0x84, 0x00, 0x09, 0xe8, 0x00, 0x22, 870x49, 0x66, 0x06, 0x03, 0x52, 0x69, 0x64, 0x64, 0x6c, 0x65,
880x69, 0x74, 0x90, 0x00, 0x11, 0x62, 0xdf, 0x02, 0x20, 0x65, 880x84, 0x00, 0x09, 0xe8, 0x00, 0x22, 0x69, 0x74, 0x90, 0x00,
890x74, 0x6c, 0x00, 0x01, 0xd5, 0x00, 0x13, 0x2e, 0x67, 0x03, 890x11, 0x62, 0xdf, 0x02, 0x20, 0x65, 0x74, 0x6c, 0x00, 0x01,
900x90, 0x63, 0x61, 0x6e, 0x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 900xd5, 0x00, 0x13, 0x2e, 0x67, 0x03, 0x90, 0x63, 0x61, 0x6e,
910x22, 0x01, 0x00, 0x5a, 0x00, 0xb0, 0x63, 0x75, 0x72, 0x73, 910x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x22, 0x01, 0x00, 0x5a,
920x6f, 0x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x2d, 0x00, 0xb1, 920x00, 0xb0, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x00, 0x6b,
930x6d, 0x6f, 0x76, 0x65, 0x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 930x65, 0x79, 0x73, 0x2d, 0x00, 0xb1, 0x6d, 0x6f, 0x76, 0x65,
940x64, 0x1f, 0x00, 0x00, 0x8b, 0x03, 0x74, 0x2e, 0x00, 0x50, 940x00, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x1f, 0x00, 0x00,
950x72, 0x65, 0x73, 0x73, 0xcb, 0x00, 0x21, 0x72, 0x65, 0xee, 950x8b, 0x03, 0x74, 0x2e, 0x00, 0x50, 0x72, 0x65, 0x73, 0x73,
960x00, 0x82, 0x6f, 0x72, 0x00, 0x73, 0x70, 0x61, 0x63, 0x65, 960xcb, 0x00, 0x21, 0x72, 0x65, 0xee, 0x00, 0x82, 0x6f, 0x72,
970x3b, 0x00, 0x06, 0x06, 0x01, 0x0c, 0x47, 0x01, 0x0a, 0x9f, 970x00, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3b, 0x00, 0x06, 0x06,
980x03, 0x00, 0x97, 0x00, 0x30, 0x70, 0x65, 0x63, 0xdf, 0x02, 980x01, 0x0c, 0x47, 0x01, 0x0a, 0x9f, 0x03, 0x00, 0x97, 0x00,
990x52, 0x6c, 0x79, 0x00, 0x28, 0x61, 0x67, 0x00, 0x15, 0x6e, 990x30, 0x70, 0x65, 0x63, 0xdf, 0x02, 0x52, 0x6c, 0x79, 0x00,
1000x11, 0x01, 0x04, 0xe1, 0x02, 0x28, 0x73, 0x00, 0x5f, 0x03, 1000x28, 0x61, 0x67, 0x00, 0x15, 0x6e, 0x11, 0x01, 0x04, 0xe1,
1010x30, 0x77, 0x61, 0x79, 0xbd, 0x03, 0x06, 0xdc, 0x01, 0x02, 1010x02, 0x28, 0x73, 0x00, 0x5f, 0x03, 0x30, 0x77, 0x61, 0x79,
1020x4a, 0x01, 0x22, 0x73, 0x29, 0x92, 0x01, 0x14, 0x70, 0x9f, 1020xbd, 0x03, 0x06, 0xdc, 0x01, 0x02, 0x4a, 0x01, 0x22, 0x73,
1030x00, 0x42, 0x42, 0x61, 0x63, 0x6b, 0x95, 0x00, 0x01, 0x90, 1030x29, 0x92, 0x01, 0x14, 0x70, 0x9f, 0x00, 0x42, 0x42, 0x61,
1040x00, 0x02, 0x03, 0x01, 0x08, 0x05, 0x02, 0x05, 0x0c, 0x01, 1040x63, 0x6b, 0x95, 0x00, 0x01, 0x90, 0x00, 0x02, 0x03, 0x01,
1050x41, 0x28, 0x41, 0x6c, 0x6c, 0x50, 0x00, 0x11, 0x61, 0xb4, 1050x08, 0x05, 0x02, 0x05, 0x0c, 0x01, 0x41, 0x28, 0x41, 0x6c,
1060x02, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 1060x6c, 0x50, 0x00, 0x11, 0x61, 0xb4, 0x02, 0xb0, 0x73, 0x00,
1070x62, 0x65, 0x64, 0x79, 0x00, 0x13, 0x73, 0xca, 0x02, 0x31, 1070x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x79,
1080x32, 0x2e, 0x31, 0x22, 0x04, 0x01, 0x32, 0x01, 0xb2, 0x61, 1080x00, 0x13, 0x73, 0xca, 0x02, 0x31, 0x32, 0x2e, 0x31, 0x22,
1090x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 1090x04, 0x01, 0x32, 0x01, 0xb2, 0x61, 0x76, 0x61, 0x69, 0x6c,
1100x96, 0x02, 0x14, 0x32, 0x96, 0x02, 0x92, 0x70, 0x61, 0x72, 1100x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0x96, 0x02, 0x14, 0x32,
1110x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x98, 0x02, 0x56, 0x68, 1110x96, 0x02, 0x92, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
1120x65, 0x73, 0x65, 0x00, 0x14, 0x00, 0x02, 0x42, 0x00, 0x04, 1120x65, 0x72, 0x98, 0x02, 0x56, 0x68, 0x65, 0x73, 0x65, 0x00,
1130x3d, 0x00, 0x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x7a, 0x00, 1130x14, 0x00, 0x02, 0x42, 0x00, 0x04, 0x3d, 0x00, 0x51, 0x00,
1140xe1, 0x60, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 1140x66, 0x72, 0x6f, 0x6d, 0x7a, 0x00, 0xe1, 0x60, 0x43, 0x75,
1150x2e, 0x27, 0x00, 0x6f, 0x70, 0x70, 0x00, 0x22, 0x6f, 0x6e, 1150x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f,
1160x1a, 0x00, 0xa0, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 1160x70, 0x70, 0x00, 0x22, 0x6f, 0x6e, 0x1a, 0x00, 0xa0, 0x54,
1170x65, 0x6e, 0x75, 0xac, 0x00, 0x90, 0x57, 0x69, 0x64, 0x74, 1170x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0xac,
1180x68, 0x2c, 0x00, 0x48, 0x65, 0x79, 0x02, 0x70, 0x00, 0x00, 1180x00, 0x90, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x00, 0x48,
1190x00, 0x53, 0x69, 0x7a, 0x65, 0x14, 0x04, 0x01, 0x27, 0x05, 1190x65, 0x79, 0x02, 0x70, 0x00, 0x00, 0x00, 0x53, 0x69, 0x7a,
1200x24, 0x69, 0x6e, 0x27, 0x04, 0x60, 0x2e, 0x00, 0x28, 0x4e, 1200x65, 0x14, 0x04, 0x01, 0x27, 0x05, 0x24, 0x69, 0x6e, 0x27,
1210x6f, 0x74, 0x45, 0x01, 0x22, 0x61, 0x74, 0xa9, 0x01, 0x26, 1210x04, 0x60, 0x2e, 0x00, 0x28, 0x4e, 0x6f, 0x74, 0x45, 0x01,
1220x75, 0x6c, 0x3d, 0x04, 0x30, 0x67, 0x61, 0x6d, 0x7d, 0x01, 1220x22, 0x61, 0x74, 0xa9, 0x01, 0x26, 0x75, 0x6c, 0x3d, 0x04,
1230x30, 0x71, 0x75, 0x69, 0x94, 0x01, 0x22, 0x6f, 0x74, 0x76, 1230x30, 0x67, 0x61, 0x6d, 0x7d, 0x01, 0x30, 0x71, 0x75, 0x69,
1240x02, 0x10, 0x77, 0x5b, 0x00, 0x01, 0x44, 0x01, 0x12, 0x68, 1240x94, 0x01, 0x22, 0x6f, 0x74, 0x76, 0x02, 0x10, 0x77, 0x5b,
1250x5e, 0x00, 0x20, 0x74, 0x6f, 0x3c, 0x02, 0x10, 0x65, 0x88, 1250x00, 0x01, 0x44, 0x01, 0x12, 0x68, 0x5e, 0x00, 0x20, 0x74,
1260x05, 0x02, 0xd4, 0x04, 0x11, 0x73, 0xee, 0x00, 0xe3, 0x44, 1260x6f, 0x3c, 0x02, 0x10, 0x65, 0x88, 0x05, 0x02, 0xd4, 0x04,
1270x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x00, 1270x11, 0x73, 0xee, 0x00, 0xe3, 0x44, 0x69, 0x66, 0x66, 0x69,
1280x00, 0x00, 0x43, 0x85, 0x03, 0x01, 0x42, 0x00, 0x16, 0x64, 1280x63, 0x75, 0x6c, 0x74, 0x79, 0x00, 0x00, 0x00, 0x43, 0x85,
1290x1a, 0x00, 0x04, 0x6a, 0x00, 0x50, 0x65, 0x6e, 0x65, 0x72, 1290x03, 0x01, 0x42, 0x00, 0x16, 0x64, 0x1a, 0x00, 0x04, 0x6a,
1300x61, 0x1c, 0x04, 0x02, 0x9d, 0x04, 0x02, 0x3d, 0x04, 0xe2, 1300x00, 0x50, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x1c, 0x04, 0x02,
1310x69, 0x71, 0x75, 0x65, 0x20, 0x72, 0x6f, 0x77, 0x73, 0x20, 1310x9d, 0x04, 0x02, 0x3d, 0x04, 0xe2, 0x69, 0x71, 0x75, 0x65,
1320x61, 0x6e, 0x64, 0x20, 0xff, 0x04, 0x30, 0x73, 0x00, 0x00, 1320x20, 0x72, 0x6f, 0x77, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20,
1330xd2, 0x02, 0x20, 0x65, 0x6e, 0x19, 0x01, 0x20, 0x64, 0x2c, 1330xff, 0x04, 0x30, 0x73, 0x00, 0x00, 0xd2, 0x02, 0x20, 0x65,
1340x1f, 0x05, 0x21, 0x74, 0x77, 0x23, 0x05, 0x03, 0xb9, 0x05, 1340x6e, 0x19, 0x01, 0x20, 0x64, 0x2c, 0x1f, 0x05, 0x21, 0x74,
1350x53, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x66, 0x04, 0xc6, 0x68, 1350x77, 0x23, 0x05, 0x03, 0xb9, 0x05, 0x53, 0x65, 0x72, 0x6d,
1360x61, 0x76, 0x65, 0x00, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 1360x69, 0x74, 0x66, 0x04, 0xc6, 0x68, 0x61, 0x76, 0x65, 0x00,
1370x79, 0x1a, 0x02, 0x72, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 1370x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x1a, 0x02, 0x72,
1380x6e, 0x08, 0x02, 0x83, 0x6c, 0x69, 0x6b, 0x65, 0x77, 0x69, 1380x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x08, 0x02, 0x83,
1390x73, 0x65, 0x5e, 0x05, 0x00, 0x12, 0x01, 0x15, 0x41, 0xc0, 1390x6c, 0x69, 0x6b, 0x65, 0x77, 0x69, 0x73, 0x65, 0x5e, 0x05,
1400x05, 0x14, 0x61, 0x74, 0x05, 0x20, 0x63, 0x61, 0x78, 0x05, 1400x00, 0x12, 0x01, 0x15, 0x41, 0xc0, 0x05, 0x14, 0x61, 0x74,
1410xe0, 0x74, 0x63, 0x68, 0x2c, 0x00, 0x74, 0x68, 0x6f, 0x75, 1410x05, 0x20, 0x63, 0x61, 0x78, 0x05, 0xe0, 0x74, 0x63, 0x68,
1420x67, 0x68, 0x2e, 0x29, 0x00, 1420x2c, 0x00, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x2e, 0x29,
1430x00,
143}; 144};
144 145
145const unsigned short help_text_len = 1688; 146const unsigned short help_text_len = 1707;
146const unsigned short help_text_words = 305; 147const unsigned short help_text_words = 306;
148const bool help_valid = true;
147const char quick_help_text[] = "Fill in the black and white grid to avoid runs of three."; 149const char quick_help_text[] = "Fill in the black and white grid to avoid runs of three.";
diff --git a/apps/plugins/puzzles/help/untangle.c b/apps/plugins/puzzles/help/untangle.c
index f7f5c05f24..88b6e39d5a 100644
--- a/apps/plugins/puzzles/help/untangle.c
+++ b/apps/plugins/puzzles/help/untangle.c
@@ -1,4 +1,4 @@
1/* auto-generated on Dec 7 2020 by genhelp.sh */ 1/* auto-generated by genhelp.sh */
2/* help text is compressed using LZ4; see compress.c for details */ 2/* help text is compressed using LZ4; see compress.c for details */
3/* DO NOT EDIT! */ 3/* DO NOT EDIT! */
4 4
@@ -6,79 +6,96 @@
6 6
7struct style_text help_text_style[] = { 7struct style_text help_text_style[] = {
8 { 0, TEXT_CENTER | C_RED }, 8 { 0, TEXT_CENTER | C_RED },
9 { 64, TEXT_CENTER | C_RED }, 9 { 65, TEXT_CENTER | C_RED },
10 { 100, TEXT_CENTER | C_RED }, 10 { 136, TEXT_CENTER | C_RED },
11 { 119, TEXT_UNDERLINE }, 11 { 155, TEXT_UNDERLINE },
12 LAST_STYLE_ITEM 12 LAST_STYLE_ITEM
13}; 13};
14 14
15/* orig 751 comp 627 ratio 0.834887 level 4 saved 124 */ 15/* orig 974 comp 787 ratio 0.808008 level 4 saved 187 */
16const char help_text[] = { 16const char help_text[] = {
170xf0, 0x2f, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20, 170xff, 0x08, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x20,
180x31, 0x38, 0x3a, 0x20, 0x55, 0x6e, 0x74, 0x61, 0x6e, 0x67, 180x31, 0x38, 0x3a, 0x20, 0x55, 0x6e, 0x74, 0x61, 0x6e, 0x67,
190x6c, 0x65, 0x20, 0x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 190x6c, 0x65, 0x20, 0x00, 0x2d, 0x01, 0x00, 0x00, 0xf0, 0x1a,
200x61, 0x72, 0x65, 0x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 200x00, 0x00, 0x00, 0x59, 0x6f, 0x75, 0x00, 0x61, 0x72, 0x65,
210x61, 0x00, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x6f, 210x00, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x00, 0x61, 0x00, 0x6e,
220x66, 0x00, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x2c, 0x00, 220x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x00, 0x70,
230x73, 0x6f, 0x6d, 0x65, 0x10, 0x00, 0xf1, 0x15, 0x77, 0x68, 230x6f, 0x69, 0x6e, 0x74, 0x73, 0x2c, 0x00, 0x73, 0x6f, 0x6d,
240x69, 0x63, 0x68, 0x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x6c, 240x65, 0x10, 0x00, 0xf1, 0x15, 0x77, 0x68, 0x69, 0x63, 0x68,
250x69, 0x6e, 0x65, 0x73, 0x00, 0x64, 0x72, 0x61, 0x77, 0x6e, 250x00, 0x68, 0x61, 0x76, 0x65, 0x00, 0x6c, 0x69, 0x6e, 0x65,
260x00, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x00, 0x74, 260x73, 0x00, 0x64, 0x72, 0x61, 0x77, 0x6e, 0x00, 0x62, 0x65,
270x68, 0x65, 0x6d, 0x2e, 0x4f, 0x00, 0x80, 0x63, 0x61, 0x6e, 270x74, 0x77, 0x65, 0x65, 0x6e, 0x00, 0x74, 0x68, 0x65, 0x6d,
280x00, 0x6d, 0x6f, 0x76, 0x65, 0x13, 0x00, 0x03, 0x46, 0x00, 280x2e, 0x4f, 0x00, 0x80, 0x63, 0x61, 0x6e, 0x00, 0x6d, 0x6f,
290xf8, 0x1c, 0x00, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x61, 290x76, 0x65, 0x13, 0x00, 0x03, 0x46, 0x00, 0xf8, 0x1c, 0x00,
300x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x69, 0x6c, 0x79, 300x61, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x61, 0x72, 0x62, 0x69,
310x3b, 0x00, 0x79, 0x6f, 0x75, 0x72, 0x00, 0x61, 0x69, 0x6d, 310x74, 0x72, 0x61, 0x72, 0x69, 0x6c, 0x79, 0x3b, 0x00, 0x79,
320x00, 0x69, 0x73, 0x00, 0x74, 0x6f, 0x00, 0x70, 0x6f, 0x73, 320x6f, 0x75, 0x72, 0x00, 0x61, 0x69, 0x6d, 0x00, 0x69, 0x73,
330x69, 0x74, 0x69, 0x6f, 0x6e, 0x36, 0x00, 0xa1, 0x73, 0x6f, 330x00, 0x74, 0x6f, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69,
340x00, 0x74, 0x68, 0x61, 0x74, 0x00, 0x6e, 0x6f, 0x73, 0x00, 340x6f, 0x6e, 0x36, 0x00, 0xa1, 0x73, 0x6f, 0x00, 0x74, 0x68,
350xf2, 0x1c, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x65, 0x73, 350x61, 0x74, 0x00, 0x6e, 0x6f, 0x73, 0x00, 0xf2, 0x1c, 0x00,
360x00, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x00, 360x63, 0x72, 0x6f, 0x73, 0x73, 0x65, 0x73, 0x00, 0x61, 0x6e,
370x00, 0x00, 0x49, 0x00, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 370x6f, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x00, 0x49,
380x61, 0x6c, 0x6c, 0x79, 0x00, 0x73, 0x61, 0x77, 0x00, 0x74, 380x00, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x6c,
390x68, 0x69, 0x73, 0x00, 0x69, 0x47, 0x00, 0x40, 0x66, 0x6f, 390x79, 0x00, 0x73, 0x61, 0x77, 0x00, 0x74, 0x68, 0x69, 0x73,
400x72, 0x6d, 0xbb, 0x00, 0xf0, 0x2b, 0x61, 0x00, 0x46, 0x6c, 400x00, 0x69, 0x47, 0x00, 0x40, 0x66, 0x6f, 0x72, 0x6d, 0xbb,
410x61, 0x73, 0x68, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x63, 410x00, 0xf0, 0x2b, 0x61, 0x00, 0x46, 0x6c, 0x61, 0x73, 0x68,
420x61, 0x6c, 0x6c, 0x65, 0x64, 0x00, 0x50, 0x6c, 0x61, 0x6e, 420x00, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x63, 0x61, 0x6c, 0x6c,
430x61, 0x72, 0x69, 0x74, 0x79, 0x00, 0x5b, 0x37, 0x5d, 0x2c, 430x65, 0x64, 0x00, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x69,
440x00, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x62, 440x74, 0x79, 0x00, 0x5b, 0x37, 0x5d, 0x2c, 0x00, 0x77, 0x72,
450x79, 0x00, 0x4a, 0x6f, 0x68, 0x6e, 0x00, 0x54, 0x61, 0x6e, 450x69, 0x74, 0x74, 0x65, 0x6e, 0x00, 0x62, 0x79, 0x00, 0x4a,
460x74, 0x61, 0x6c, 0x6f, 0x63, 0x00, 0xc4, 0x5b, 0x37, 0x5d, 460x6f, 0x68, 0x6e, 0x00, 0x54, 0x61, 0x6e, 0x74, 0x61, 0x6c,
470x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x35, 470x6f, 0x63, 0x00, 0xc4, 0x5b, 0x37, 0x5d, 0x00, 0x68, 0x74,
480x00, 0xb6, 0x2e, 0x6e, 0x65, 0x74, 0x00, 0x00, 0x00, 0x31, 480x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x35, 0x00, 0xb6, 0x2e,
490x38, 0x2e, 0x31, 0x4f, 0x01, 0x80, 0x63, 0x6f, 0x6e, 0x74, 490x6e, 0x65, 0x74, 0x00, 0x00, 0x00, 0x31, 0x38, 0x2e, 0x31,
500x72, 0x6f, 0x6c, 0x73, 0x58, 0x01, 0x22, 0x54, 0x6f, 0x04, 500x64, 0x01, 0xe2, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
510x01, 0x12, 0x61, 0xcc, 0x00, 0xf1, 0x03, 0x2c, 0x00, 0x63, 510x73, 0x20, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x04, 0x01, 0x12,
520x6c, 0x69, 0x63, 0x6b, 0x00, 0x6f, 0x6e, 0x00, 0x69, 0x74, 520x61, 0xcc, 0x00, 0xf1, 0x03, 0x2c, 0x00, 0x63, 0x6c, 0x69,
530x00, 0x77, 0x69, 0x74, 0x68, 0xa1, 0x00, 0xf0, 0x06, 0x6c, 530x63, 0x6b, 0x00, 0x6f, 0x6e, 0x00, 0x69, 0x74, 0x00, 0x77,
540x65, 0x66, 0x74, 0x00, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 540x69, 0x74, 0x68, 0xa1, 0x00, 0xf0, 0x06, 0x6c, 0x65, 0x66,
550x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x00, 0x61, 0x6e, 0x64, 550x74, 0x00, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x00, 0x62, 0x75,
560x59, 0x01, 0x10, 0x67, 0x27, 0x00, 0x40, 0x69, 0x6e, 0x74, 560x74, 0x74, 0x6f, 0x6e, 0x00, 0x61, 0x6e, 0x64, 0x59, 0x01,
570x6f, 0x93, 0x01, 0x25, 0x65, 0x77, 0x1e, 0x01, 0x00, 0x90, 570x10, 0x67, 0x27, 0x00, 0x40, 0x69, 0x6e, 0x74, 0x6f, 0x93,
580x00, 0x41, 0x28, 0x41, 0x6c, 0x6c, 0x3e, 0x00, 0x20, 0x61, 580x01, 0x25, 0x65, 0x77, 0x1e, 0x01, 0x00, 0x90, 0x00, 0xf0,
590x63, 0x13, 0x00, 0xb0, 0x73, 0x00, 0x64, 0x65, 0x73, 0x63, 590x11, 0x54, 0x68, 0x65, 0x00, 0x63, 0x75, 0x72, 0x73, 0x6f,
600x72, 0x69, 0x62, 0x65, 0x64, 0xf8, 0x00, 0x21, 0x73, 0x65, 600x72, 0x00, 0x6b, 0x65, 0x79, 0x73, 0x00, 0x6d, 0x61, 0x79,
610x16, 0x00, 0x41, 0x00, 0x32, 0x2e, 0x31, 0xd9, 0x01, 0xf2, 610x00, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x62, 0x65, 0x00, 0x75,
620x01, 0x61, 0x6c, 0x73, 0x6f, 0x00, 0x61, 0x76, 0x61, 0x69, 620x73, 0x65, 0x64, 0x4e, 0x01, 0xf7, 0x01, 0x6e, 0x61, 0x76,
630x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29, 0xb6, 0x00, 0x16, 630x69, 0x67, 0x61, 0x74, 0x65, 0x00, 0x61, 0x6d, 0x6f, 0x6e,
640x32, 0xb6, 0x00, 0x92, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 640x67, 0x73, 0x74, 0x56, 0x01, 0xa1, 0x2e, 0x00, 0x50, 0x72,
650x74, 0x65, 0x72, 0xb8, 0x00, 0x40, 0x68, 0x65, 0x72, 0x65, 650x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x15, 0x00, 0x41, 0x45,
660x93, 0x01, 0x95, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x6e, 660x6e, 0x74, 0x65, 0x49, 0x00, 0xc1, 0x00, 0x77, 0x69, 0x6c,
670x65, 0x00, 0x20, 0x00, 0x06, 0x46, 0x00, 0x51, 0x00, 0x66, 670x6c, 0x00, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x83, 0x00,
680x72, 0x6f, 0x6d, 0x83, 0x00, 0xe1, 0x60, 0x43, 0x75, 0x73, 680x14, 0x67, 0x23, 0x00, 0xf2, 0x07, 0x63, 0x75, 0x72, 0x72,
690x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x6f, 0x70, 690x65, 0x6e, 0x74, 0x6c, 0x79, 0x2d, 0x00, 0x68, 0x69, 0x67,
700x79, 0x00, 0x03, 0xc3, 0x01, 0xf1, 0x01, 0x60, 0x54, 0x79, 700x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x4f, 0x00,
710x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e, 0x75, 0x3a, 0x00, 710x07, 0x4e, 0x00, 0xc2, 0x54, 0x61, 0x62, 0x00, 0x6f, 0x72,
720x00, 0x00, 0x4e, 0x58, 0x02, 0x43, 0x20, 0x6f, 0x66, 0x20, 720x00, 0x53, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x00, 0x40, 0x63,
730xdc, 0x01, 0x33, 0x00, 0x00, 0x43, 0x2f, 0x01, 0x01, 0x2f, 730x79, 0x63, 0x6c, 0x07, 0x02, 0x99, 0x72, 0x6f, 0x75, 0x67,
740x00, 0x31, 0x73, 0x69, 0x7a, 0x66, 0x02, 0x01, 0xfe, 0x01, 740x68, 0x00, 0x61, 0x6c, 0x6c, 0x87, 0x00, 0x43, 0x00, 0x00,
750x60, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c, 0x8c, 0x01, 0xa1, 750x28, 0x41, 0x13, 0x00, 0x20, 0x61, 0x63, 0xdd, 0x00, 0xb0,
760x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 760x73, 0x00, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
770x1a, 0x00, 0x0c, 0x9e, 0x02, 0x04, 0xe9, 0x01, 0xf0, 0x02, 770x64, 0xc2, 0x01, 0x21, 0x73, 0x65, 0x16, 0x00, 0x41, 0x00,
780x32, 0x2e, 0x31, 0xa3, 0x02, 0x01, 0xe4, 0x00, 0xb2, 0x61,
790x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x29,
800x80, 0x01, 0x16, 0x32, 0x80, 0x01, 0x92, 0x70, 0x61, 0x72,
810x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x82, 0x01, 0x40, 0x68,
820x65, 0x72, 0x65, 0x5d, 0x02, 0x95, 0x6f, 0x6e, 0x6c, 0x79,
830x00, 0x6f, 0x6e, 0x65, 0x00, 0x20, 0x00, 0x06, 0x46, 0x00,
840x51, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x83, 0x00, 0xe1, 0x60,
850x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2e, 0x2e, 0x2e, 0x27,
860x00, 0x6f, 0x70, 0x79, 0x00, 0x03, 0x8d, 0x02, 0xf1, 0x01,
870x60, 0x54, 0x79, 0x70, 0x65, 0x27, 0x00, 0x6d, 0x65, 0x6e,
880x75, 0x3a, 0x00, 0x00, 0x00, 0x4e, 0x22, 0x03, 0x43, 0x20,
890x6f, 0x66, 0x20, 0xa6, 0x02, 0x33, 0x00, 0x00, 0x43, 0xf9,
900x01, 0x01, 0x2f, 0x00, 0x31, 0x73, 0x69, 0x7a, 0x30, 0x03,
910x01, 0xeb, 0x00, 0x60, 0x75, 0x7a, 0x7a, 0x6c, 0x65, 0x2c,
920x56, 0x02, 0x74, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79,
930x54, 0x01, 0x0c, 0x68, 0x03, 0x04, 0xb3, 0x02, 0xf0, 0x02,
780x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x00, 940x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x00,
790x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x00, 950x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x00,
80}; 96};
81 97
82const unsigned short help_text_len = 751; 98const unsigned short help_text_len = 974;
83const unsigned short help_text_words = 138; 99const unsigned short help_text_words = 174;
100const bool help_valid = true;
84const char quick_help_text[] = "Reposition the points so that the lines do not cross."; 101const char quick_help_text[] = "Reposition the points so that the lines do not cross.";
diff --git a/apps/plugins/puzzles/puzzles.make b/apps/plugins/puzzles/puzzles.make
index 604208cbdd..8c5bc1de40 100644
--- a/apps/plugins/puzzles/puzzles.make
+++ b/apps/plugins/puzzles/puzzles.make
@@ -25,6 +25,8 @@ PUZZLES_OBJ = $(call c2obj, $(PUZZLES_SRC))
25PUZZLES_ROCKS = $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock))) 25PUZZLES_ROCKS = $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock)))
26 26
27OTHER_SRC += $(PUZZLES_SRC) 27OTHER_SRC += $(PUZZLES_SRC)
28OTHER_INC += -I$(PUZZLES_SRCDIR)/src -I $(PUZZLES_SRCDIR)
29
28ROCKS += $(PUZZLES_ROCKS) 30ROCKS += $(PUZZLES_ROCKS)
29 31
30PUZZLES_OPTIMIZE = -O2 32PUZZLES_OPTIMIZE = -O2
@@ -49,6 +51,13 @@ $(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/%.o $(PUZZLES_OBJDIR)/help/%
49 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map 51 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map
50 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@) 52 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@)
51 53
54$(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/unfinished/%.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
55 $(call PRINTS,LD $(@F))$(CC) $(PLUGINFLAGS) -o $(PUZZLES_OBJDIR)/$*.elf \
56 $(filter %.o, $^) \
57 $(filter %.a, $+) \
58 -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map
59 $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@)
60
52$(PUZZLES_SRCDIR)/rbcompat.h: $(APPSDIR)/plugin.h \ 61$(PUZZLES_SRCDIR)/rbcompat.h: $(APPSDIR)/plugin.h \
53 $(APPSDIR)/plugins/lib/pluginlib_exit.h \ 62 $(APPSDIR)/plugins/lib/pluginlib_exit.h \
54 $(BUILDDIR)/sysfont.h \ 63 $(BUILDDIR)/sysfont.h \
diff --git a/apps/plugins/puzzles/rbcompat.h b/apps/plugins/puzzles/rbcompat.h
index 9069c1e659..f37ac8068e 100644
--- a/apps/plugins/puzzles/rbcompat.h
+++ b/apps/plugins/puzzles/rbcompat.h
@@ -34,6 +34,8 @@ int sscanf_wrapper(const char *ibuf, const char *fmt, ...);
34double atof_wrapper(const char *s); 34double atof_wrapper(const char *s);
35double acos_wrapper(double x); 35double acos_wrapper(double x);
36 36
37#define NO_TGMATH_H
38
37#define acos acos_wrapper 39#define acos acos_wrapper
38#define atan atan_wrapper 40#define atan atan_wrapper
39#define atan2 atan2_wrapper 41#define atan2 atan2_wrapper
diff --git a/apps/plugins/puzzles/resync.sh b/apps/plugins/puzzles/resync.sh
index 76679971a4..3431a6f695 100755
--- a/apps/plugins/puzzles/resync.sh
+++ b/apps/plugins/puzzles/resync.sh
@@ -19,19 +19,58 @@ then
19fi 19fi
20 20
21echo "=== POTENTIALLY DANGEROUS OPERATION ===" 21echo "=== POTENTIALLY DANGEROUS OPERATION ==="
22echo "Are you sure you want to remove all files in src/?" 22echo "Are you sure you want to remove all files in src/ and help/?"
23echo -n "If so, type \"yes\" in all caps: " 23echo -n "If so, type \"yes\" in all caps: "
24read ans 24read ans
25if [ "YES" == $ans ] 25if [ "YES" == $ans ]
26then 26then
27 pushd "$(dirname "$0")" > /dev/null 27 pushd "$(dirname "$0")" > /dev/null
28 ROOT="$PWD"
28 29
29 echo "[1/5] Removing current src/ directory" 30 echo "[1/5] Removing current src/ directory"
30 rm -rf src 31 rm -rf src
31 echo "[2/5] Copying new sources" 32 echo "[2/5] Copying new sources"
32 mkdir src 33 mkdir -p src/unfinished
33 cp -r "$1"/{*.c,*.h,*.R,*.but,LICENCE,README} src 34 cp -r "$1"/{*.h,puzzles.but,LICENCE,README,CMakeLists.txt,unfinished} src
35
36 # Parse out definitions of core, core_obj, and common from
37 # CMakeLists. Extract the .c filenames, except malloc.c, and store
38 # in SOURCES.core.
39 cat src/CMakeLists.txt | awk '/add_library\(/{p=1} p{printf $0" "} /\)/{if(p) print; p=0}' | grep -E "core|common" | grep -Po "[a-z0-9\-]*?\.c" | sort -n | grep -vE 'malloc\.c|ps\.c' | awk '{print "src/"$0}' | uniq > SOURCES.core
40 echo "src/printing.c" >> SOURCES.core
41
42 # Parse out puzzle definitions to build SOURCES.games, but
43 # preserve the ability to disable puzzles based on memory size.
44 cat src/CMakeLists.txt | awk '/puzzle\(/{p=1} p{print} /\)/{p=0}' | grep -Eo "\(.*$" | tr -dc "a-z\n" | grep -v nullgame | awk '$0!~/loopy|pearl|solo/' | awk '{print "src/"$0".c"}' > SOURCES.games
45
46 SRC="$(cat SOURCES.games SOURCES.core | sed 's/src\///' | tr '\n' ' ' | head -c-1) loopy.c pearl.c solo.c"
47 echo "Detected sources:" $SRC
48 pushd "$1" > /dev/null
49 cp -r $SRC "$ROOT"/src
50 popd > /dev/null
51
52 cat src/unfinished/CMakeLists.txt | awk '/puzzle\(/{p=1} p{print} /\)/{p=0}' | grep -Eo "\(.*$" | tr -dc "a-z\n" | awk '{print "src/unfinished/"$0".c"}' | grep -v "group" | grep -v "separate" >> SOURCES.games
53
54 cat <<EOF >> SOURCES.games
55
56/* no c200v2 */
57#if PLUGIN_BUFFER_SIZE > 0x14000
58src/loopy.c
59src/pearl.c
60src/solo.c
61#endif
62EOF
63
64 cat <<EOF > SOURCES
65/* Auto-generated by resync.sh */
66EOF
67 cat SOURCES.rockbox | cpp | grep -vE "^#" >> SOURCES
68 echo -e "\n/* puzzles core sources */" >> SOURCES
69 cat SOURCES.core >> SOURCES
70 rm SOURCES.core
71
34 echo "[3/5] Regenerating help" 72 echo "[3/5] Regenerating help"
73 rm -rf help
35 ./genhelp.sh 74 ./genhelp.sh
36 75
37 echo "[4/5] Staging for commit" 76 echo "[4/5] Staging for commit"
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 263a19f421..27060208fc 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -7,7 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2016-2020 Franklin Wei 10 * Copyright (C) 2016-2024 Franklin Wei
11 * 11 *
12 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -24,10 +24,9 @@
24 * ================================ 24 * ================================
25 * 25 *
26 * This file contains the majority of the rockbox-specific code for 26 * This file contains the majority of the rockbox-specific code for
27 * the sgt-puzzles port. It implements a set of functions for the 27 * the sgt-puzzles port. It implements an API for the backend to call
28 * backend to call to actually run the games, as well as rockbox UI 28 * to run the games, as well as the rockbox UI code (menus, input,
29 * code (menus, input, etc). For a good overview of the rest of the 29 * etc). For a good overview of the rest of the puzzles code, see:
30 * puzzles code, see:
31 * 30 *
32 * <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/>. 31 * <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/>.
33 * 32 *
@@ -37,7 +36,7 @@
37 * Contents of this file 36 * Contents of this file
38 * --------------------- 37 * ---------------------
39 * 38 *
40 * By rough order of appearnce in this file: 39 * By rough order of appearance in this file:
41 * 40 *
42 * 1) "Zoom" feature 41 * 1) "Zoom" feature
43 * 42 *
@@ -62,8 +61,7 @@
62 * mode switching. In commit 5094aaa, this behavior was changed so 61 * mode switching. In commit 5094aaa, this behavior was changed so
63 * that the frontend can now query the backend for the on-screen 62 * that the frontend can now query the backend for the on-screen
64 * cursor location and move the viewport accordingly through the 63 * cursor location and move the viewport accordingly through the
65 * new midend_get_cursor_location() API (which is not yet merged 64 * new midend_get_cursor_location() API.
66 * into Simon's tree as of October 2020).
67 * 65 *
68 * 2) Font management 66 * 2) Font management
69 * 67 *
@@ -82,8 +80,8 @@
82 * 80 *
83 * 3) Drawing API 81 * 3) Drawing API
84 * 82 *
85 * The sgt-puzzles backend wants a set of function pointers to the 83 * The sgt-puzzles backend wants a set of function pointers to
86 * usual drawing primitives. [1] If the `zoom_enabled' switch is 84 * typical drawing primitives. [1] If the `zoom_enabled' switch is
87 * on, these call upon the "zoomed" drawing routines in (1). 85 * on, these call upon the "zoomed" drawing routines in (1).
88 * 86 *
89 * In the normal un-zoomed case, these functions generally rely on 87 * In the normal un-zoomed case, these functions generally rely on
@@ -108,7 +106,7 @@
108 * a) Mouse mode 106 * a) Mouse mode
109 * 107 *
110 * This mode is designed to accommodate puzzles without a 108 * This mode is designed to accommodate puzzles without a
111 * keyboard or cursor interface (currently only "Loopy"). We 109 * keyboard or cursor interface (currently only Loopyx). We
112 * remap the cursor keys to move an on-screen cursor rather 110 * remap the cursor keys to move an on-screen cursor rather
113 * than sending arrow keys to the game. 111 * than sending arrow keys to the game.
114 * 112 *
@@ -163,6 +161,11 @@
163 * cases by waiting until a key has been released before we 161 * cases by waiting until a key has been released before we
164 * send the input keystroke(s) to the game. 162 * send the input keystroke(s) to the game.
165 * 163 *
164 * e) Key repeat
165 *
166 * In some games, we would like to send repeated key events to
167 * allow long drags. Currently, this is only used in Untangle.
168 *
166 * 5) Game configuration and preset management 169 * 5) Game configuration and preset management
167 * 170 *
168 * The backend games specify a hierarchy of user-adjustable game 171 * The backend games specify a hierarchy of user-adjustable game
@@ -170,6 +173,10 @@
170 * generation, etc. Also supplied are a set of "presets" that 173 * generation, etc. Also supplied are a set of "presets" that
171 * specify a predetermined set of configuration parameters. 174 * specify a predetermined set of configuration parameters.
172 * 175 *
176 * In 2023, Simon introduced a User Preferences system that allows
177 * further customization of the game UI (e.g., "snap to grid" in
178 * Untangle). Rockbox support for this was added in July 2024.
179 *
173 * 6) In-game help 180 * 6) In-game help
174 * 181 *
175 * The sgt-puzzles manual (src/puzzles.but) contains a chapter 182 * The sgt-puzzles manual (src/puzzles.but) contains a chapter
@@ -315,6 +322,7 @@ static struct viewport clip_rect;
315static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false; 322static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false;
316 323
317static int mouse_x, mouse_y; 324static int mouse_x, mouse_y;
325static bool mouse_dragging = false; /* for sticky mode only */
318 326
319extern bool audiobuf_available; /* defined in rbmalloc.c */ 327extern bool audiobuf_available; /* defined in rbmalloc.c */
320 328
@@ -339,6 +347,7 @@ static struct {
339 bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */ 347 bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */
340 bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */ 348 bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */
341 bool numerical_chooser; /* repurpose select to activate a numerical chooser */ 349 bool numerical_chooser; /* repurpose select to activate a numerical chooser */
350 bool sticky_mouse; /* if mouse left button should be persistent and toggled on/off */
342} input_settings; 351} input_settings;
343 352
344static bool accept_input = true; 353static bool accept_input = true;
@@ -741,12 +750,13 @@ static void rb_color(int n)
741 fatal("bad color %d", n); 750 fatal("bad color %d", n);
742 return; 751 return;
743 } 752 }
744 rb->lcd_set_foreground(colors[n]); 753 if(colors)
754 rb->lcd_set_foreground(colors[n]);
745} 755}
746 756
747/* clipping is implemented through viewports and offsetting 757/* clipping is implemented through viewports and offsetting
748 * coordinates */ 758 * coordinates */
749static void rb_clip(void *handle, int x, int y, int w, int h) 759static void rb_clip(drawing *dr, int x, int y, int w, int h)
750{ 760{
751 if(!zoom_enabled) 761 if(!zoom_enabled)
752 { 762 {
@@ -776,7 +786,7 @@ static void rb_clip(void *handle, int x, int y, int w, int h)
776 } 786 }
777} 787}
778 788
779static void rb_unclip(void *handle) 789static void rb_unclip(drawing *dr)
780{ 790{
781 if(!zoom_enabled) 791 if(!zoom_enabled)
782 { 792 {
@@ -793,7 +803,7 @@ static void rb_unclip(void *handle)
793 } 803 }
794} 804}
795 805
796static void rb_draw_text(void *handle, int x, int y, int fonttype, 806static void rb_draw_text(drawing *dr, int x, int y, int fonttype,
797 int fontsize, int align, int color, const char *text) 807 int fontsize, int align, int color, const char *text)
798{ 808{
799 (void) fontsize; 809 (void) fontsize;
@@ -858,7 +868,7 @@ static void rb_draw_text(void *handle, int x, int y, int fonttype,
858 } 868 }
859} 869}
860 870
861static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color) 871static void rb_draw_rect(drawing *dr, int x, int y, int w, int h, int color)
862{ 872{
863 rb_color(color); 873 rb_color(color);
864 if(!zoom_enabled) 874 if(!zoom_enabled)
@@ -1000,7 +1010,7 @@ static void draw_antialiased_line(fb_data *fb, int w, int h, int x0, int y0, int
1000 } 1010 }
1001} 1011}
1002 1012
1003static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2, 1013static void rb_draw_line(drawing *dr, int x1, int y1, int x2, int y2,
1004 int color) 1014 int color)
1005{ 1015{
1006 rb_color(color); 1016 rb_color(color);
@@ -1028,349 +1038,7 @@ static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2,
1028 } 1038 }
1029} 1039}
1030 1040
1031#if 0 1041static void rb_draw_circle(drawing *dr, int cx, int cy, int radius,
1032/*
1033 * draw filled polygon
1034 * originally by Sebastian Leonhardt (ulmutul)
1035 * 'count' : number of coordinate pairs
1036 * 'pxy': array of coordinates. pxy[0]=x0,pxy[1]=y0,...
1037 * note: provide space for one extra coordinate, because the starting point
1038 * will automatically be inserted as end point.
1039 */
1040
1041/*
1042 * helper function:
1043 * find points of intersection between polygon and scanline
1044 */
1045
1046#define MAX_INTERSECTION 32
1047
1048static void fill_poly_line(int scanline, int count, int *pxy)
1049{
1050 int i;
1051 int j;
1052 int num_of_intersects;
1053 int direct, old_direct;
1054 //intersections of every line with scanline (y-coord)
1055 int intersection[MAX_INTERSECTION];
1056 /* add starting point as ending point */
1057 pxy[count*2] = pxy[0];
1058 pxy[count*2+1] = pxy[1];
1059
1060 old_direct=0;
1061 num_of_intersects=0;
1062 for (i=0; i<count*2; i+=2) {
1063 int x1=pxy[i];
1064 int y1=pxy[i+1];
1065 int x2=pxy[i+2];
1066 int y2=pxy[i+3];
1067 // skip if line is outside of scanline
1068 if (y1 < y2) {
1069 if (scanline < y1 || scanline > y2)
1070 continue;
1071 }
1072 else {
1073 if (scanline < y2 || scanline > y1)
1074 continue;
1075 }
1076 // calculate x-coord of intersection
1077 if (y1==y2) {
1078 direct=0;
1079 }
1080 else {
1081 direct = y1>y2 ? 1 : -1;
1082 // omit double intersections, if both lines lead in the same direction
1083 intersection[num_of_intersects] =
1084 x1+((scanline-y1)*(x2-x1))/(y2-y1);
1085 if ( (direct!=old_direct)
1086 || (intersection[num_of_intersects] != intersection[num_of_intersects-1])
1087 )
1088 ++num_of_intersects;
1089 }
1090 old_direct = direct;
1091 }
1092
1093 // sort points of intersection
1094 for (i=0; i<num_of_intersects-1; ++i) {
1095 for (j=i+1; j<num_of_intersects; ++j) {
1096 if (intersection[j]<intersection[i]) {
1097 int temp=intersection[i];
1098 intersection[i]=intersection[j];
1099 intersection[j]=temp;
1100 }
1101 }
1102 }
1103 // draw
1104 for (i=0; i<num_of_intersects; i+=2) {
1105 rb->lcd_hline(intersection[i], intersection[i+1], scanline);
1106 }
1107}
1108
1109/* two extra elements at end of pxy needed */
1110static void v_fillarea(int count, int *pxy)
1111{
1112 int i;
1113 int y1, y2;
1114
1115 // find min and max y coords
1116 y1=y2=pxy[1];
1117 for (i=3; i<count*2; i+=2) {
1118 if (pxy[i] < y1) y1 = pxy[i];
1119 else if (pxy[i] > y2) y2 = pxy[i];
1120 }
1121
1122 for (i=y1; i<=y2; ++i) {
1123 fill_poly_line(i, count, pxy);
1124 }
1125}
1126#endif
1127
1128/* I'm a horrible person: this was copy-pasta'd straight from
1129 * xlcd_draw.c */
1130
1131/* sort the given coordinates by increasing x value */
1132static void sort_points_by_increasing_x(int* x1, int* y1,
1133 int* x2, int* y2,
1134 int* x3, int* y3)
1135{
1136 int x, y;
1137 if (*x1 > *x3)
1138 {
1139 if (*x2 < *x3) /* x2 < x3 < x1 */
1140 {
1141 x = *x1; *x1 = *x2; *x2 = *x3; *x3 = x;
1142 y = *y1; *y1 = *y2; *y2 = *y3; *y3 = y;
1143 }
1144 else if (*x2 > *x1) /* x3 < x1 < x2 */
1145 {
1146 x = *x1; *x1 = *x3; *x3 = *x2; *x2 = x;
1147 y = *y1; *y1 = *y3; *y3 = *y2; *y2 = y;
1148 }
1149 else /* x3 <= x2 <= x1 */
1150 {
1151 x = *x1; *x1 = *x3; *x3 = x;
1152 y = *y1; *y1 = *y3; *y3 = y;
1153 }
1154 }
1155 else
1156 {
1157 if (*x2 < *x1) /* x2 < x1 <= x3 */
1158 {
1159 x = *x1; *x1 = *x2; *x2 = x;
1160 y = *y1; *y1 = *y2; *y2 = y;
1161 }
1162 else if (*x2 > *x3) /* x1 <= x3 < x2 */
1163 {
1164 x = *x2; *x2 = *x3; *x3 = x;
1165 y = *y2; *y2 = *y3; *y3 = y;
1166 }
1167 /* else already sorted */
1168 }
1169}
1170
1171#define sort_points_by_increasing_y(x1, y1, x2, y2, x3, y3) \
1172 sort_points_by_increasing_x(y1, x1, y2, x2, y3, x3)
1173
1174/* draw a filled triangle, using horizontal lines for speed */
1175static void zoom_filltriangle(int x1, int y1,
1176 int x2, int y2,
1177 int x3, int y3)
1178{
1179 long fp_x1, fp_x2, fp_dx1, fp_dx2;
1180 int y;
1181 sort_points_by_increasing_y(&x1, &y1, &x2, &y2, &x3, &y3);
1182
1183 if (y1 < y3) /* draw */
1184 {
1185 fp_dx1 = ((x3 - x1) << 16) / (y3 - y1);
1186 fp_x1 = (x1 << 16) + (1<<15) + (fp_dx1 >> 1);
1187
1188 if (y1 < y2) /* first part */
1189 {
1190 fp_dx2 = ((x2 - x1) << 16) / (y2 - y1);
1191 fp_x2 = (x1 << 16) + (1<<15) + (fp_dx2 >> 1);
1192 for (y = y1; y < y2; y++)
1193 {
1194 zoom_hline(fp_x1 >> 16, fp_x2 >> 16, y);
1195 fp_x1 += fp_dx1;
1196 fp_x2 += fp_dx2;
1197 }
1198 }
1199 if (y2 < y3) /* second part */
1200 {
1201 fp_dx2 = ((x3 - x2) << 16) / (y3 - y2);
1202 fp_x2 = (x2 << 16) + (1<<15) + (fp_dx2 >> 1);
1203 for (y = y2; y < y3; y++)
1204 {
1205 zoom_hline(fp_x1 >> 16, fp_x2 >> 16, y);
1206 fp_x1 += fp_dx1;
1207 fp_x2 += fp_dx2;
1208 }
1209 }
1210 }
1211}
1212
1213/* Should probably refactor this */
1214static void rb_draw_poly(void *handle, int *coords, int npoints,
1215 int fillcolor, int outlinecolor)
1216{
1217 if(!zoom_enabled)
1218 {
1219 LOGF("rb_draw_poly");
1220
1221 if(fillcolor >= 0)
1222 {
1223 rb_color(fillcolor);
1224#if 1
1225 /* serious hack: draw a bunch of triangles between adjacent points */
1226 /* this generally works, even with some concave polygons */
1227 for(int i = 2; i < npoints; ++i)
1228 {
1229 int x1, y1, x2, y2, x3, y3;
1230 x1 = coords[0];
1231 y1 = coords[1];
1232 x2 = coords[(i - 1) * 2];
1233 y2 = coords[(i - 1) * 2 + 1];
1234 x3 = coords[i * 2];
1235 y3 = coords[i * 2 + 1];
1236 offset_coords(&x1, &y1);
1237 offset_coords(&x2, &y2);
1238 offset_coords(&x3, &y3);
1239 xlcd_filltriangle(x1, y1,
1240 x2, y2,
1241 x3, y3);
1242
1243#ifdef DEBUG_MENU
1244 if(debug_settings.polyanim)
1245 {
1246 rb->lcd_update();
1247 rb->sleep(HZ/4);
1248 }
1249#endif
1250#if 0
1251 /* debug code */
1252 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0));
1253 rb->lcd_drawpixel(x1, y1);
1254 rb->lcd_drawpixel(x2, y2);
1255 rb->lcd_drawpixel(x3, y3);
1256 rb->lcd_update();
1257 rb->sleep(HZ);
1258 rb_color(fillcolor);
1259 rb->lcd_drawpixel(x1, y1);
1260 rb->lcd_drawpixel(x2, y2);
1261 rb->lcd_drawpixel(x3, y3);
1262 rb->lcd_update();
1263#endif
1264 }
1265#else
1266 int *pxy = smalloc(sizeof(int) * 2 * npoints + 2);
1267 /* copy points, offsetted */
1268 for(int i = 0; i < npoints; ++i)
1269 {
1270 pxy[2 * i + 0] = coords[2 * i + 0];
1271 pxy[2 * i + 1] = coords[2 * i + 1];
1272 offset_coords(&pxy[2*i+0], &pxy[2*i+1]);
1273 }
1274 v_fillarea(npoints, pxy);
1275 sfree(pxy);
1276#endif
1277 }
1278
1279 /* draw outlines last so they're not covered by the fill */
1280 assert(outlinecolor >= 0);
1281 rb_color(outlinecolor);
1282
1283 for(int i = 1; i < npoints; ++i)
1284 {
1285 int x1, y1, x2, y2;
1286 x1 = coords[2 * (i - 1)];
1287 y1 = coords[2 * (i - 1) + 1];
1288 x2 = coords[2 * i];
1289 y2 = coords[2 * i + 1];
1290 if(debug_settings.no_aa)
1291 {
1292 offset_coords(&x1, &y1);
1293 offset_coords(&x2, &y2);
1294 rb->lcd_drawline(x1, y1,
1295 x2, y2);
1296 }
1297 else
1298 draw_antialiased_line(lcd_fb, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2);
1299
1300#ifdef DEBUG_MENU
1301 if(debug_settings.polyanim)
1302 {
1303 rb->lcd_update();
1304 rb->sleep(HZ/4);
1305 }
1306#endif
1307 }
1308
1309 int x1, y1, x2, y2;
1310 x1 = coords[0];
1311 y1 = coords[1];
1312 x2 = coords[2 * (npoints - 1)];
1313 y2 = coords[2 * (npoints - 1) + 1];
1314 if(debug_settings.no_aa)
1315 {
1316 offset_coords(&x1, &y1);
1317 offset_coords(&x2, &y2);
1318
1319 rb->lcd_drawline(x1, y1,
1320 x2, y2);
1321 }
1322 else
1323 draw_antialiased_line(lcd_fb, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2);
1324 }
1325 else
1326 {
1327 LOGF("rb_draw_poly");
1328
1329 if(fillcolor >= 0)
1330 {
1331 rb_color(fillcolor);
1332
1333 /* serious hack: draw a bunch of triangles between adjacent points */
1334 /* this generally works, even with some concave polygons */
1335 for(int i = 2; i < npoints; ++i)
1336 {
1337 int x1, y1, x2, y2, x3, y3;
1338 x1 = coords[0];
1339 y1 = coords[1];
1340 x2 = coords[(i - 1) * 2];
1341 y2 = coords[(i - 1) * 2 + 1];
1342 x3 = coords[i * 2];
1343 y3 = coords[i * 2 + 1];
1344 zoom_filltriangle(x1, y1,
1345 x2, y2,
1346 x3, y3);
1347 }
1348 }
1349
1350 /* draw outlines last so they're not covered by the fill */
1351 assert(outlinecolor >= 0);
1352 rb_color(outlinecolor);
1353
1354 for(int i = 1; i < npoints; ++i)
1355 {
1356 int x1, y1, x2, y2;
1357 x1 = coords[2 * (i - 1)];
1358 y1 = coords[2 * (i - 1) + 1];
1359 x2 = coords[2 * i];
1360 y2 = coords[2 * i + 1];
1361 draw_antialiased_line(zoom_fb, zoom_w, zoom_h, x1, y1, x2, y2);
1362 }
1363
1364 int x1, y1, x2, y2;
1365 x1 = coords[0];
1366 y1 = coords[1];
1367 x2 = coords[2 * (npoints - 1)];
1368 y2 = coords[2 * (npoints - 1) + 1];
1369 draw_antialiased_line(zoom_fb, zoom_w, zoom_h, x1, y1, x2, y2);
1370 }
1371}
1372
1373static void rb_draw_circle(void *handle, int cx, int cy, int radius,
1374 int fillcolor, int outlinecolor) 1042 int fillcolor, int outlinecolor)
1375{ 1043{
1376 if(!zoom_enabled) 1044 if(!zoom_enabled)
@@ -1442,7 +1110,7 @@ static void trim_rect(int *x, int *y, int *w, int *h)
1442 *h = y1 - y0; 1110 *h = y1 - y0;
1443} 1111}
1444 1112
1445static blitter *rb_blitter_new(void *handle, int w, int h) 1113static blitter *rb_blitter_new(drawing *dr, int w, int h)
1446{ 1114{
1447 LOGF("rb_blitter_new"); 1115 LOGF("rb_blitter_new");
1448 blitter *b = snew(blitter); 1116 blitter *b = snew(blitter);
@@ -1453,7 +1121,7 @@ static blitter *rb_blitter_new(void *handle, int w, int h)
1453 return b; 1121 return b;
1454} 1122}
1455 1123
1456static void rb_blitter_free(void *handle, blitter *bl) 1124static void rb_blitter_free(drawing *dr, blitter *bl)
1457{ 1125{
1458 LOGF("rb_blitter_free"); 1126 LOGF("rb_blitter_free");
1459 sfree(bl->bmp.data); 1127 sfree(bl->bmp.data);
@@ -1462,7 +1130,7 @@ static void rb_blitter_free(void *handle, blitter *bl)
1462} 1130}
1463 1131
1464/* copy a section of the framebuffer */ 1132/* copy a section of the framebuffer */
1465static void rb_blitter_save(void *handle, blitter *bl, int x, int y) 1133static void rb_blitter_save(drawing *dr, blitter *bl, int x, int y)
1466{ 1134{
1467 /* no viewport offset */ 1135 /* no viewport offset */
1468#if LCD_STRIDEFORMAT == VERTICAL_STRIDE 1136#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
@@ -1491,7 +1159,7 @@ static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
1491#endif 1159#endif
1492} 1160}
1493 1161
1494static void rb_blitter_load(void *handle, blitter *bl, int x, int y) 1162static void rb_blitter_load(drawing *dr, blitter *bl, int x, int y)
1495{ 1163{
1496 LOGF("rb_blitter_load"); 1164 LOGF("rb_blitter_load");
1497 if(!bl->have_data) 1165 if(!bl->have_data)
@@ -1521,7 +1189,7 @@ static void rb_blitter_load(void *handle, blitter *bl, int x, int y)
1521 } 1189 }
1522} 1190}
1523 1191
1524static void rb_draw_update(void *handle, int x, int y, int w, int h) 1192static void rb_draw_update(drawing *dr, int x, int y, int w, int h)
1525{ 1193{
1526 LOGF("rb_draw_update(%d, %d, %d, %d)", x, y, w, h); 1194 LOGF("rb_draw_update(%d, %d, %d, %d)", x, y, w, h);
1527 1195
@@ -1545,9 +1213,9 @@ static void rb_draw_update(void *handle, int x, int y, int w, int h)
1545 need_draw_update = true; 1213 need_draw_update = true;
1546} 1214}
1547 1215
1548static void rb_start_draw(void *handle) 1216static void rb_start_draw(drawing *dr)
1549{ 1217{
1550 (void) handle; 1218 (void) dr;
1551 1219
1552 /* ... mumble mumble ... not ... reentrant ... mumble mumble ... */ 1220 /* ... mumble mumble ... not ... reentrant ... mumble mumble ... */
1553 1221
@@ -1558,9 +1226,9 @@ static void rb_start_draw(void *handle)
1558 ud_d = LCD_HEIGHT; 1226 ud_d = LCD_HEIGHT;
1559} 1227}
1560 1228
1561static void rb_end_draw(void *handle) 1229static void rb_end_draw(drawing *dr)
1562{ 1230{
1563 (void) handle; 1231 (void) dr;
1564 1232
1565 if(debug_settings.highlight_cursor) 1233 if(debug_settings.highlight_cursor)
1566 { 1234 {
@@ -1587,7 +1255,7 @@ static void rb_end_draw(void *handle)
1587#endif 1255#endif
1588} 1256}
1589 1257
1590static void rb_status_bar(void *handle, const char *text) 1258static void rb_status_bar(drawing *dr, const char *text)
1591{ 1259{
1592 if(titlebar) 1260 if(titlebar)
1593 sfree(titlebar); 1261 sfree(titlebar);
@@ -1619,7 +1287,8 @@ static void draw_title(bool clear_first)
1619 rb->lcd_setfont(cur_font = FONT_UI); 1287 rb->lcd_setfont(cur_font = FONT_UI);
1620 rb->lcd_getstringsize(str, &w, &h); 1288 rb->lcd_getstringsize(str, &w, &h);
1621 1289
1622 rb->lcd_set_foreground(BG_COLOR); 1290
1291 rb->lcd_set_foreground(colors ? colors[0] : BG_COLOR);
1623 rb->lcd_fillrect(0, LCD_HEIGHT - h, clear_first ? LCD_WIDTH : w, h); 1292 rb->lcd_fillrect(0, LCD_HEIGHT - h, clear_first ? LCD_WIDTH : w, h);
1624 1293
1625 rb->lcd_set_drawmode(DRMODE_FG); 1294 rb->lcd_set_drawmode(DRMODE_FG);
@@ -1685,7 +1354,7 @@ static void draw_mouse(void)
1685 * glyph exists in a font) */ 1354 * glyph exists in a font) */
1686#if 0 1355#if 0
1687/* See: https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/drawing.html#drawing-text-fallback */ 1356/* See: https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/drawing.html#drawing-text-fallback */
1688static char *rb_text_fallback(void *handle, const char *const *strings, 1357static char *rb_text_fallback(drawing *dr, const char *const *strings,
1689 int nstrings) 1358 int nstrings)
1690{ 1359{
1691 struct font *pf = rb->font_get(cur_font); 1360 struct font *pf = rb->font_get(cur_font);
@@ -1718,10 +1387,11 @@ static char *rb_text_fallback(void *handle, const char *const *strings,
1718#endif 1387#endif
1719 1388
1720const drawing_api rb_drawing = { 1389const drawing_api rb_drawing = {
1390 1,
1721 rb_draw_text, 1391 rb_draw_text,
1722 rb_draw_rect, 1392 rb_draw_rect,
1723 rb_draw_line, 1393 rb_draw_line,
1724 rb_draw_poly, 1394 draw_polygon_fallback,
1725 rb_draw_circle, 1395 rb_draw_circle,
1726 rb_draw_update, 1396 rb_draw_update,
1727 rb_clip, 1397 rb_clip,
@@ -2016,9 +1686,17 @@ static int process_input(int tmo, bool do_pausemenu)
2016 LOGF("sending left click"); 1686 LOGF("sending left click");
2017 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */ 1687 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */
2018 } 1688 }
2019 } 1689 } else if(input_settings.sticky_mouse) {
2020 else 1690 if(pressed & BTN_FIRE) {
2021 { 1691 send_click(LEFT_BUTTON, false);
1692 accept_input = false;
1693 mouse_dragging = !mouse_dragging;
1694 } else if(mouse_dragging) {
1695 send_click(LEFT_DRAG, false);
1696 } else {
1697 send_click(LEFT_RELEASE, false);
1698 }
1699 } else {
2022 if(pressed & BTN_FIRE) { 1700 if(pressed & BTN_FIRE) {
2023 send_click(LEFT_BUTTON, false); 1701 send_click(LEFT_BUTTON, false);
2024 accept_input = false; 1702 accept_input = false;
@@ -2268,7 +1946,7 @@ static void zoom(void)
2268 zoom_clipl = 0; 1946 zoom_clipl = 0;
2269 zoom_clipr = zoom_w; 1947 zoom_clipr = zoom_w;
2270 1948
2271 midend_size(me, &zoom_w, &zoom_h, true); 1949 midend_size(me, &zoom_w, &zoom_h, true, 1.0);
2272 1950
2273 /* Allocating the framebuffer will mostly likely grab the 1951 /* Allocating the framebuffer will mostly likely grab the
2274 * audiobuffer, which will make impossible to load new fonts, and 1952 * audiobuffer, which will make impossible to load new fonts, and
@@ -2448,7 +2126,6 @@ static int list_choose(const char *list_str, const char *title, int sel)
2448 struct gui_synclist list; 2126 struct gui_synclist list;
2449 2127
2450 rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL); 2128 rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
2451 rb->gui_synclist_set_icon_callback(&list, NULL);
2452 rb->gui_synclist_set_nb_items(&list, n); 2129 rb->gui_synclist_set_nb_items(&list, n);
2453 2130
2454 rb->gui_synclist_select_item(&list, sel); 2131 rb->gui_synclist_select_item(&list, sel);
@@ -2634,10 +2311,10 @@ const char *config_formatter(int sel, void *data, char *buf, size_t len)
2634 return buf; 2311 return buf;
2635} 2312}
2636 2313
2637static bool config_menu(void) 2314static bool config_menu_core(int which)
2638{ 2315{
2639 char *title; 2316 char *title;
2640 config_item *config = midend_get_config(me, CFG_SETTINGS, &title); 2317 config_item *config = midend_get_config(me, which, &title);
2641 2318
2642 rb->lcd_setfont(cur_font = FONT_UI); 2319 rb->lcd_setfont(cur_font = FONT_UI);
2643 2320
@@ -2661,7 +2338,6 @@ static bool config_menu(void)
2661 struct gui_synclist list; 2338 struct gui_synclist list;
2662 2339
2663 rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL); 2340 rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
2664 rb->gui_synclist_set_icon_callback(&list, NULL);
2665 rb->gui_synclist_set_nb_items(&list, n); 2341 rb->gui_synclist_set_nb_items(&list, n);
2666 2342
2667 rb->gui_synclist_select_item(&list, 0); 2343 rb->gui_synclist_select_item(&list, 0);
@@ -2689,7 +2365,7 @@ static bool config_menu(void)
2689 old_str = dupstr(old.u.string.sval); 2365 old_str = dupstr(old.u.string.sval);
2690 2366
2691 bool freed_str = do_configure_item(config, pos); 2367 bool freed_str = do_configure_item(config, pos);
2692 const char *err = midend_set_config(me, CFG_SETTINGS, config); 2368 const char *err = midend_set_config(me, which, config);
2693 2369
2694 if(err) 2370 if(err)
2695 { 2371 {
@@ -2728,6 +2404,16 @@ done:
2728 return success; 2404 return success;
2729} 2405}
2730 2406
2407static bool config_menu(void)
2408{
2409 return config_menu_core(CFG_SETTINGS);
2410}
2411
2412static bool preferences_menu(void)
2413{
2414 return config_menu_core(CFG_PREFS);
2415}
2416
2731static const char *preset_formatter(int sel, void *data, char *buf, size_t len) 2417static const char *preset_formatter(int sel, void *data, char *buf, size_t len)
2732{ 2418{
2733 struct preset_menu *menu = data; 2419 struct preset_menu *menu = data;
@@ -2746,7 +2432,6 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
2746 struct gui_synclist list; 2432 struct gui_synclist list;
2747 2433
2748 rb->gui_synclist_init(&list, &preset_formatter, menu, false, 1, NULL); 2434 rb->gui_synclist_init(&list, &preset_formatter, menu, false, 1, NULL);
2749 rb->gui_synclist_set_icon_callback(&list, NULL);
2750 rb->gui_synclist_set_nb_items(&list, menu->n_entries); 2435 rb->gui_synclist_set_nb_items(&list, menu->n_entries);
2751 2436
2752 rb->gui_synclist_select_item(&list, selected); 2437 rb->gui_synclist_select_item(&list, selected);
@@ -2809,6 +2494,7 @@ static bool presets_menu(void)
2809 2494
2810static void quick_help(void) 2495static void quick_help(void)
2811{ 2496{
2497#ifndef NO_HELP_TEXT
2812#if defined(FOR_REAL) && defined(DEBUG_MENU) 2498#if defined(FOR_REAL) && defined(DEBUG_MENU)
2813 if(++help_times >= 5) 2499 if(++help_times >= 5)
2814 { 2500 {
@@ -2819,11 +2505,12 @@ static void quick_help(void)
2819 2505
2820 rb->splash(0, quick_help_text); 2506 rb->splash(0, quick_help_text);
2821 rb->button_get(true); 2507 rb->button_get(true);
2822 return; 2508#endif
2823} 2509}
2824 2510
2825static void full_help(const char *name) 2511static void full_help(const char *name)
2826{ 2512{
2513#ifndef NO_HELP_TEXT
2827 unsigned old_bg = rb->lcd_get_background(); 2514 unsigned old_bg = rb->lcd_get_background();
2828 2515
2829 bool orig_clipped = clipped; 2516 bool orig_clipped = clipped;
@@ -2878,6 +2565,7 @@ static void full_help(const char *name)
2878 2565
2879 if(orig_clipped) 2566 if(orig_clipped)
2880 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height); 2567 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
2568#endif
2881} 2569}
2882 2570
2883static void init_default_settings(void) 2571static void init_default_settings(void)
@@ -3028,6 +2716,11 @@ static int pausemenu_cb(int action,
3028 if(!midend_which_game(me)->can_solve) 2716 if(!midend_which_game(me)->can_solve)
3029 return ACTION_EXIT_MENUITEM; 2717 return ACTION_EXIT_MENUITEM;
3030 break; 2718 break;
2719 case 7:
2720 case 8:
2721 if(!help_valid)
2722 return ACTION_EXIT_MENUITEM;
2723 break;
3031 case 9: 2724 case 9:
3032 if(audiobuf_available) 2725 if(audiobuf_available)
3033 break; 2726 break;
@@ -3078,7 +2771,7 @@ static void reset_drawing(void)
3078 rb->lcd_set_viewport(NULL); 2771 rb->lcd_set_viewport(NULL);
3079 rb->lcd_set_backdrop(NULL); 2772 rb->lcd_set_backdrop(NULL);
3080 rb->lcd_set_foreground(LCD_BLACK); 2773 rb->lcd_set_foreground(LCD_BLACK);
3081 rb->lcd_set_background(BG_COLOR); 2774 rb->lcd_set_background(colors ? colors[0] : BG_COLOR);
3082} 2775}
3083 2776
3084/* Make a new game, but tell the user through a splash so they don't 2777/* Make a new game, but tell the user through a splash so they don't
@@ -3108,8 +2801,9 @@ static int pause_menu(void)
3108 "Game Type", // 10 2801 "Game Type", // 10
3109 "Debug Menu", // 11 2802 "Debug Menu", // 11
3110 "Configure Game", // 12 2803 "Configure Game", // 12
3111 "Quit without Saving", // 13 2804 "Preferences", // 13
3112 "Quit"); // 14 2805 "Quit without Saving", // 14
2806 "Quit"); // 15
3113 2807
3114#if defined(FOR_REAL) && defined(DEBUG_MENU) 2808#if defined(FOR_REAL) && defined(DEBUG_MENU)
3115 help_times = 0; 2809 help_times = 0;
@@ -3190,15 +2884,19 @@ static int pause_menu(void)
3190 quit = true; 2884 quit = true;
3191 } 2885 }
3192 break; 2886 break;
3193 case 13: 2887 case 13:
3194 return -2; 2888 preferences_menu();
2889 // do not go straight into game.
2890 break;
3195 case 14: 2891 case 14:
2892 return -2;
2893 case 15:
3196 return -3; 2894 return -3;
3197 default: 2895 default:
3198 break; 2896 break;
3199 } 2897 }
3200 } 2898 }
3201 rb->lcd_set_background(BG_COLOR); 2899 rb->lcd_set_background(colors ? colors[0] : BG_COLOR);
3202 rb->lcd_clear_display(); 2900 rb->lcd_clear_display();
3203 midend_force_redraw(me); 2901 midend_force_redraw(me);
3204 rb->lcd_update(); 2902 rb->lcd_update();
@@ -3217,7 +2915,7 @@ static void fix_size(void)
3217 rb->lcd_setfont(cur_font = FONT_UI); 2915 rb->lcd_setfont(cur_font = FONT_UI);
3218 rb->lcd_getstringsize("X", NULL, &h_x); 2916 rb->lcd_getstringsize("X", NULL, &h_x);
3219 h -= h_x; 2917 h -= h_x;
3220 midend_size(me, &w, &h, true); 2918 midend_size(me, &w, &h, true, 1.0);
3221} 2919}
3222 2920
3223static void init_tlsf(void) 2921static void init_tlsf(void)
@@ -3245,6 +2943,7 @@ static void init_colors(void)
3245 float *floatcolors = midend_colors(me, &ncolors); 2943 float *floatcolors = midend_colors(me, &ncolors);
3246 2944
3247 /* convert them to packed RGB */ 2945 /* convert them to packed RGB */
2946 sfree(colors);
3248 colors = smalloc(ncolors * sizeof(unsigned)); 2947 colors = smalloc(ncolors * sizeof(unsigned));
3249 unsigned *ptr = colors; 2948 unsigned *ptr = colors;
3250 float *floatptr = floatcolors; 2949 float *floatptr = floatcolors;
@@ -3277,32 +2976,39 @@ static bool string_in_list(const char *target, const char **list)
3277static void tune_input(const char *name) 2976static void tune_input(const char *name)
3278{ 2977{
3279 static const char *want_spacebar[] = { 2978 static const char *want_spacebar[] = {
2979 "Black Box",
2980 "Bridges",
2981 "Galaxies",
2982 "Keen",
3280 "Magnets", 2983 "Magnets",
3281 "Map", 2984 "Map",
3282 "Mines", 2985 "Mines",
3283 "Palisade", 2986 "Palisade",
2987 "Pattern",
3284 "Rectangles", 2988 "Rectangles",
2989 "Signpost",
2990 "Singles",
2991 "Solo",
2992 "Tents",
2993 "Towers",
2994 "Unequal",
2995 "Group",
3285 NULL 2996 NULL
3286 }; 2997 };
3287 2998
3288 /* these get a spacebar on long click - you must also add to the 2999 /* these get a spacebar on long click - this implicitly enables
3289 * falling_edge list below! */ 3000 * falling-edge button events (see below)! */
3290 input_settings.want_spacebar = string_in_list(name, want_spacebar); 3001 input_settings.want_spacebar = string_in_list(name, want_spacebar);
3291 3002
3292 static const char *falling_edge[] = { 3003 static const char *falling_edge[] = {
3293 "Inertia", 3004 "Inertia",
3294 "Magnets",
3295 "Map",
3296 "Mines",
3297 "Palisade",
3298 "Rectangles",
3299 NULL 3005 NULL
3300 }; 3006 };
3301 3007
3302 /* wait until a key is released to send an action (useful for 3008 /* wait until a key is released to send an action (useful for
3303 * chording in Inertia; must be enabled if the game needs a 3009 * chording in Inertia; must be enabled if the game needs a
3304 * spacebar) */ 3010 * spacebar) */
3305 input_settings.falling_edge = string_in_list(name, falling_edge); 3011 input_settings.falling_edge = string_in_list(name, falling_edge) || input_settings.want_spacebar;
3306 3012
3307 /* For want_spacebar to work, events must be sent on the falling 3013 /* For want_spacebar to work, events must be sent on the falling
3308 * edge */ 3014 * edge */
@@ -3322,6 +3028,7 @@ static void tune_input(const char *name)
3322 static const char *no_rclick_on_hold[] = { 3028 static const char *no_rclick_on_hold[] = {
3323 "Map", 3029 "Map",
3324 "Signpost", 3030 "Signpost",
3031 "Slide",
3325 "Untangle", 3032 "Untangle",
3326 NULL 3033 NULL
3327 }; 3034 };
@@ -3330,11 +3037,21 @@ static void tune_input(const char *name)
3330 3037
3331 static const char *mouse_games[] = { 3038 static const char *mouse_games[] = {
3332 "Loopy", 3039 "Loopy",
3040 "Slide",
3333 NULL 3041 NULL
3334 }; 3042 };
3335 3043
3336 mouse_mode = string_in_list(name, mouse_games); 3044 mouse_mode = string_in_list(name, mouse_games);
3337 3045
3046 static const char *sticky_mouse_games[] = {
3047 "Map",
3048 "Signpost",
3049 "Slide",
3050 "Untangle",
3051 };
3052
3053 input_settings.sticky_mouse = string_in_list(name, sticky_mouse_games);
3054
3338 static const char *number_chooser_games[] = { 3055 static const char *number_chooser_games[] = {
3339 "Filling", 3056 "Filling",
3340 "Keen", 3057 "Keen",
@@ -3627,8 +3344,11 @@ static int mainmenu_cb(int action,
3627 if(!load_success) 3344 if(!load_success)
3628 return ACTION_EXIT_MENUITEM; 3345 return ACTION_EXIT_MENUITEM;
3629 break; 3346 break;
3347 case 2:
3630 case 3: 3348 case 3:
3631 break; 3349 if(!help_valid)
3350 return ACTION_EXIT_MENUITEM;
3351 break;
3632 case 4: 3352 case 4:
3633 if(audiobuf_available) 3353 if(audiobuf_available)
3634 break; 3354 break;
@@ -3691,8 +3411,9 @@ static void puzzles_main(void)
3691 "Playback Control", // 4 3411 "Playback Control", // 4
3692 "Game Type", // 5 3412 "Game Type", // 5
3693 "Configure Game", // 6 3413 "Configure Game", // 6
3694 "Quit without Saving", // 7 3414 "Preferences", // 7
3695 "Quit"); // 8 3415 "Quit without Saving", // 8
3416 "Quit"); // 9
3696 3417
3697 bool quit = false; 3418 bool quit = false;
3698 int sel = 0; 3419 int sel = 0;
@@ -3738,11 +3459,14 @@ static void puzzles_main(void)
3738 goto game_loop; 3459 goto game_loop;
3739 } 3460 }
3740 break; 3461 break;
3741 case 8: 3462 case 7:
3463 preferences_menu();
3464 break;
3465 case 9:
3742 if(load_success) 3466 if(load_success)
3743 save_game(); 3467 save_game();
3744 /* fall through */ 3468 /* fall through */
3745 case 7: 3469 case 8:
3746 /* we don't care about freeing anything because tlsf will 3470 /* we don't care about freeing anything because tlsf will
3747 * be wiped out the next time around */ 3471 * be wiped out the next time around */
3748 return; 3472 return;
@@ -3787,12 +3511,14 @@ static void puzzles_main(void)
3787 /* quit without saving */ 3511 /* quit without saving */
3788 midend_free(me); 3512 midend_free(me);
3789 sfree(colors); 3513 sfree(colors);
3514 colors = NULL;
3790 return; 3515 return;
3791 case -3: 3516 case -3:
3792 /* save and quit */ 3517 /* save and quit */
3793 save_game(); 3518 save_game();
3794 midend_free(me); 3519 midend_free(me);
3795 sfree(colors); 3520 sfree(colors);
3521 colors = NULL;
3796 return; 3522 return;
3797 default: 3523 default:
3798 break; 3524 break;
@@ -3822,6 +3548,7 @@ static void puzzles_main(void)
3822 rb->yield(); 3548 rb->yield();
3823 } 3549 }
3824 sfree(colors); 3550 sfree(colors);
3551 colors = NULL;
3825 } 3552 }
3826} 3553}
3827 3554
diff --git a/apps/plugins/puzzles/src/CMakeLists.txt b/apps/plugins/puzzles/src/CMakeLists.txt
new file mode 100644
index 0000000000..ce3ce3d3df
--- /dev/null
+++ b/apps/plugins/puzzles/src/CMakeLists.txt
@@ -0,0 +1,291 @@
1cmake_minimum_required(VERSION 3.5)
2
3project(puzzles
4 LANGUAGES C)
5
6include(cmake/setup.cmake)
7
8add_library(core_obj OBJECT
9 combi.c divvy.c draw-poly.c drawing.c dsf.c findloop.c grid.c
10 latin.c laydomino.c loopgen.c malloc.c matching.c midend.c misc.c
11 penrose.c penrose-legacy.c ps.c random.c sort.c tdq.c tree234.c
12 version.c
13 ${platform_common_sources})
14add_library(core $<TARGET_OBJECTS:core_obj>)
15add_library(common $<TARGET_OBJECTS:core_obj> hat.c spectre.c)
16
17cliprogram(polygon-test draw-poly.c
18 SDL2_LIB COMPILE_DEFINITIONS STANDALONE_POLYGON)
19
20include_directories(${CMAKE_CURRENT_SOURCE_DIR})
21
22puzzle(blackbox
23 DISPLAYNAME "Black Box"
24 DESCRIPTION "Ball-finding puzzle"
25 OBJECTIVE "Find the hidden balls in the box by bouncing laser beams \
26off them.")
27
28puzzle(bridges
29 DISPLAYNAME "Bridges"
30 DESCRIPTION "Bridge-placing puzzle"
31 OBJECTIVE "Connect all the islands with a network of bridges.")
32
33puzzle(cube
34 DISPLAYNAME "Cube"
35 DESCRIPTION "Rolling cube puzzle"
36 OBJECTIVE "Pick up all the blue squares by rolling the cube over them.")
37
38puzzle(dominosa
39 DISPLAYNAME "Dominosa"
40 DESCRIPTION "Domino tiling puzzle"
41 OBJECTIVE "Tile the rectangle with a full set of dominoes.")
42solver(dominosa)
43
44puzzle(fifteen
45 DISPLAYNAME "Fifteen"
46 DESCRIPTION "Sliding block puzzle"
47 OBJECTIVE "Slide the tiles around to arrange them into order.")
48solver(fifteen)
49
50puzzle(filling
51 DISPLAYNAME "Filling"
52 DESCRIPTION "Polyomino puzzle"
53 OBJECTIVE "Mark every square with the area of its containing region.")
54solver(filling)
55
56puzzle(flip
57 DISPLAYNAME "Flip"
58 DESCRIPTION "Tile inversion puzzle"
59 OBJECTIVE "Flip groups of squares to light them all up at once.")
60
61puzzle(flood
62 DISPLAYNAME "Flood"
63 DESCRIPTION "Flood-filling puzzle"
64 OBJECTIVE "Turn the grid the same colour in as few flood fills as possible.")
65
66puzzle(galaxies
67 DISPLAYNAME "Galaxies"
68 DESCRIPTION "Symmetric polyomino puzzle"
69 OBJECTIVE "Divide the grid into rotationally symmetric regions each \
70centred on a dot.")
71solver(galaxies)
72cliprogram(galaxiespicture galaxies.c
73 COMPILE_DEFINITIONS STANDALONE_PICTURE_GENERATOR)
74guiprogram(galaxieseditor galaxies.c
75 COMPILE_DEFINITIONS EDITOR)
76
77puzzle(guess
78 DISPLAYNAME "Guess"
79 DESCRIPTION "Combination-guessing puzzle"
80 OBJECTIVE "Guess the hidden combination of colours.")
81
82puzzle(inertia
83 DISPLAYNAME "Inertia"
84 DESCRIPTION "Gem-collecting puzzle"
85 OBJECTIVE "Collect all the gems without running into any of the mines.")
86
87puzzle(keen
88 DISPLAYNAME "Keen"
89 DESCRIPTION "Arithmetic Latin square puzzle"
90 OBJECTIVE "Complete the latin square in accordance with the \
91arithmetic clues.")
92solver(keen latin.c)
93
94puzzle(lightup
95 DISPLAYNAME "Light Up"
96 DESCRIPTION "Light-bulb placing puzzle"
97 OBJECTIVE "Place bulbs to light up all the squares.")
98solver(lightup)
99
100puzzle(loopy
101 DISPLAYNAME "Loopy"
102 DESCRIPTION "Loop-drawing puzzle"
103 OBJECTIVE "Draw a single closed loop, given clues about number of \
104adjacent edges.")
105solver(loopy)
106
107puzzle(magnets
108 DISPLAYNAME "Magnets"
109 DESCRIPTION "Magnet-placing puzzle"
110 OBJECTIVE "Place magnets to satisfy the clues and avoid like poles \
111touching.")
112solver(magnets)
113
114puzzle(map
115 DISPLAYNAME "Map"
116 DESCRIPTION "Map-colouring puzzle"
117 OBJECTIVE "Colour the map so that adjacent regions are never the \
118same colour.")
119solver(map)
120
121puzzle(mines
122 DISPLAYNAME "Mines"
123 DESCRIPTION "Mine-finding puzzle"
124 OBJECTIVE "Find all the mines without treading on any of them.")
125cliprogram(mineobfusc mines.c COMPILE_DEFINITIONS STANDALONE_OBFUSCATOR)
126
127puzzle(mosaic
128 DISPLAYNAME "Mosaic"
129 DESCRIPTION "Grid-filling puzzle"
130 OBJECTIVE "Fill in the grid given clues about number of \
131nearby black squares.")
132
133puzzle(net
134 # The Windows Net shouldn't be called 'net.exe', since Windows
135 # already has a reasonably important utility program by that name!
136 WINDOWS_EXE_NAME netgame
137
138 DISPLAYNAME "Net"
139 DESCRIPTION "Network jigsaw puzzle"
140 OBJECTIVE "Rotate each tile to reassemble the network.")
141
142puzzle(netslide
143 DISPLAYNAME "Netslide"
144 DESCRIPTION "Toroidal sliding network puzzle"
145 OBJECTIVE "Slide a row at a time to reassemble the network.")
146
147puzzle(nullgame)
148
149puzzle(palisade
150 DISPLAYNAME "Palisade"
151 DESCRIPTION "Grid-division puzzle"
152 OBJECTIVE "Divide the grid into equal-sized areas in accordance with\
153 the clues.")
154
155puzzle(pattern
156 DISPLAYNAME "Pattern"
157 DESCRIPTION "Pattern puzzle"
158 OBJECTIVE "Fill in the pattern in the grid, given only the lengths \
159of runs of black squares.")
160solver(pattern)
161cliprogram(patternpicture pattern.c
162 COMPILE_DEFINITIONS STANDALONE_PICTURE_GENERATOR)
163
164puzzle(pearl
165 DISPLAYNAME "Pearl"
166 DESCRIPTION "Loop-drawing puzzle"
167 OBJECTIVE "Draw a single closed loop, given clues about corner and \
168straight squares.")
169solver(pearl)
170cliprogram(pearlbench pearl.c COMPILE_DEFINITIONS STANDALONE_SOLVER)
171
172puzzle(pegs
173 DISPLAYNAME "Pegs"
174 DESCRIPTION "Peg solitaire puzzle"
175 OBJECTIVE "Jump pegs over each other to remove all but one.")
176
177puzzle(range
178 DISPLAYNAME "Range"
179 DESCRIPTION "Visible-distance puzzle"
180 OBJECTIVE "Place black squares to limit the visible distance from \
181each numbered cell.")
182
183puzzle(rect
184 DISPLAYNAME "Rectangles"
185 DESCRIPTION "Rectangles puzzle"
186 OBJECTIVE "Divide the grid into rectangles with areas equal to the \
187numbers.")
188
189puzzle(samegame
190 DISPLAYNAME "Same Game"
191 DESCRIPTION "Block-clearing puzzle"
192 OBJECTIVE "Clear the grid by removing touching groups of the same \
193colour squares.")
194
195puzzle(signpost
196 DISPLAYNAME "Signpost"
197 DESCRIPTION "Square-connecting puzzle"
198 OBJECTIVE "Connect the squares into a path following the arrows.")
199solver(signpost)
200
201puzzle(singles
202 DISPLAYNAME "Singles"
203 DESCRIPTION "Number-removing puzzle"
204 OBJECTIVE "Black out the right set of duplicate numbers.")
205solver(singles)
206
207puzzle(sixteen
208 DISPLAYNAME "Sixteen"
209 DESCRIPTION "Toroidal sliding block puzzle"
210 OBJECTIVE "Slide a row at a time to arrange the tiles into order.")
211
212puzzle(slant
213 DISPLAYNAME "Slant"
214 DESCRIPTION "Maze-drawing puzzle"
215 OBJECTIVE "Draw a maze of slanting lines that matches the clues.")
216solver(slant)
217
218puzzle(solo
219 DISPLAYNAME "Solo"
220 DESCRIPTION "Number placement puzzle"
221 OBJECTIVE "Fill in the grid so that each row, column and square \
222block contains one of every digit.")
223solver(solo)
224
225puzzle(tents
226 DISPLAYNAME "Tents"
227 DESCRIPTION "Tent-placing puzzle"
228 OBJECTIVE "Place a tent next to each tree.")
229solver(tents)
230
231puzzle(towers
232 DISPLAYNAME "Towers"
233 DESCRIPTION "Tower-placing Latin square puzzle"
234 OBJECTIVE "Complete the latin square of towers in accordance with \
235the clues.")
236solver(towers latin.c)
237
238puzzle(tracks
239 DISPLAYNAME "Tracks"
240 DESCRIPTION "Path-finding railway track puzzle"
241 OBJECTIVE "Fill in the railway track according to the clues.")
242solver(tracks)
243
244puzzle(twiddle
245 DISPLAYNAME "Twiddle"
246 DESCRIPTION "Rotational sliding block puzzle"
247 OBJECTIVE "Rotate the tiles around themselves to arrange them into order.")
248
249puzzle(undead
250 DISPLAYNAME "Undead"
251 DESCRIPTION "Monster-placing puzzle"
252 OBJECTIVE "Place ghosts, vampires and zombies so that the right \
253numbers of them can be seen in mirrors.")
254
255puzzle(unequal
256 DISPLAYNAME "Unequal"
257 DESCRIPTION "Latin square puzzle"
258 OBJECTIVE "Complete the latin square in accordance with the > signs.")
259solver(unequal latin.c)
260
261puzzle(unruly
262 DISPLAYNAME "Unruly"
263 DESCRIPTION "Black and white grid puzzle"
264 OBJECTIVE "Fill in the black and white grid to avoid runs of three.")
265solver(unruly)
266
267puzzle(untangle
268 DISPLAYNAME "Untangle"
269 DESCRIPTION "Planar graph layout puzzle"
270 OBJECTIVE "Reposition the points so that the lines do not cross.")
271
272add_subdirectory(unfinished)
273add_subdirectory(auxiliary)
274
275if(build_cli_programs)
276 write_generated_games_header()
277 include(CheckFunctionExists)
278 check_function_exists(HF_ITER HAVE_HF_ITER)
279 set(WITH_LIBFUZZER OFF
280 CACHE BOOL "Build fuzzpuzz using Clang's libFuzzer")
281 cliprogram(fuzzpuzz fuzzpuzz.c list.c ${puzzle_sources}
282 COMPILE_DEFINITIONS COMBINED $<$<BOOL:${WITH_LIBFUZZER}>:OMIT_MAIN>
283 $<$<BOOL:${HAVE_HF_ITER}>:HAVE_HF_ITER>)
284 target_include_directories(fuzzpuzz PRIVATE ${generated_include_dir})
285 if(WITH_LIBFUZZER)
286 target_compile_options(fuzzpuzz PRIVATE -fsanitize=fuzzer)
287 set_target_properties(fuzzpuzz PROPERTIES LINK_FLAGS -fsanitize=fuzzer)
288 endif()
289endif()
290
291build_extras()
diff --git a/apps/plugins/puzzles/src/LICENCE b/apps/plugins/puzzles/src/LICENCE
index 85f67ba795..d33dae80af 100644
--- a/apps/plugins/puzzles/src/LICENCE
+++ b/apps/plugins/puzzles/src/LICENCE
@@ -1,9 +1,9 @@
1This software is copyright (c) 2004-2014 Simon Tatham. 1This software is copyright (c) 2004-2024 Simon Tatham.
2 2
3Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas 3Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
4Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd 4Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd
5Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens, Michael 5Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens, Michael
6Quevillon and Asher Gordon. 6Quevillon, Asher Gordon, Didi Kohen and Ben Harris.
7 7
8Permission is hereby granted, free of charge, to any person 8Permission is hereby granted, free of charge, to any person
9obtaining a copy of this software and associated documentation files 9obtaining a copy of this software and associated documentation files
diff --git a/apps/plugins/puzzles/src/README b/apps/plugins/puzzles/src/README
index 00830126e8..dd76edc19b 100644
--- a/apps/plugins/puzzles/src/README
+++ b/apps/plugins/puzzles/src/README
@@ -2,48 +2,12 @@ This is the README accompanying the source code to Simon Tatham's
2puzzle collection. The collection's web site is at 2puzzle collection. The collection's web site is at
3<https://www.chiark.greenend.org.uk/~sgtatham/puzzles/>. 3<https://www.chiark.greenend.org.uk/~sgtatham/puzzles/>.
4 4
5If you've obtained the source code by downloading a .tar.gz archive 5The puzzle collection is built using CMake <https://cmake.org/>. To
6from the Puzzles web site, you should find several Makefiles in the 6compile in the simplest way (on any of Linux, Windows or Mac), run
7source code. However, if you've checked the source code out from the 7these commands in the source directory:
8Puzzles git repository, you won't find the Makefiles: they're
9automatically generated by `mkfiles.pl', so run that to create them.
10 8
11The Makefiles include: 9 cmake .
12 10 cmake --build .
13 - `Makefile.am', together with the static `configure.ac', is intended
14 as input to automake. Run `mkauto.sh' to turn these into a
15 configure script and Makefile.in, after which you can then run
16 `./configure' to create an actual Unix Makefile.
17
18 - `Makefile.vc' should work under MS Visual C++ on Windows. Run
19 'nmake /f Makefile.vc' in a Visual Studio command prompt.
20
21 - `Makefile.cyg' should work under Cygwin / MinGW. With appropriate
22 tweaks and setting of TOOLPATH, it should work for both compiling
23 on Windows and cross-compiling on Unix.
24
25 - `Makefile.osx' should work under Mac OS X, provided the Xcode
26 tools are installed. It builds a single monolithic OS X
27 application capable of running any of the puzzles, or even more
28 than one of them at a time.
29
30 - `Makefile.wce' should work under MS eMbedded Visual C++ on
31 Windows and the Pocket PC SDK; it builds Pocket PC binaries.
32
33Many of these Makefiles build a program called `nullgame' in
34addition to the actual game binaries. This program doesn't do
35anything; it's just a template for people to start from when adding
36a new game to the collection, and it's compiled every time to ensure
37that it _does_ compile and link successfully (because otherwise it
38wouldn't be much use as a template). Once it's built, you can run it
39if you really want to (but it's very boring), and then you should
40ignore it.
41
42DO NOT EDIT THE MAKEFILES DIRECTLY, if you plan to send any changes
43back to the maintainer. The makefiles are generated automatically by
44the Perl script `mkfiles.pl' from the file `Recipe' and the various
45.R files. If you need to change the makefiles as part of a patch,
46you should change Recipe, *.R, and/or mkfiles.pl.
47 11
48The manual is provided in Windows Help format for the Windows build; 12The manual is provided in Windows Help format for the Windows build;
49in text format for anyone who needs it; and in HTML for the Mac OS X 13in text format for anyone who needs it; and in HTML for the Mac OS X
diff --git a/apps/plugins/puzzles/src/blackbox.R b/apps/plugins/puzzles/src/blackbox.R
deleted file mode 100644
index 116225206d..0000000000
--- a/apps/plugins/puzzles/src/blackbox.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3blackbox : [X] GTK COMMON blackbox blackbox-icon|no-icon
4
5blackbox : [G] WINDOWS COMMON blackbox blackbox.res|noicon.res
6
7ALL += blackbox[COMBINED]
8
9!begin am gtk
10GAMES += blackbox
11!end
12
13!begin >list.c
14 A(blackbox) \
15!end
16
17!begin >gamedesc.txt
18blackbox:blackbox.exe:Black Box:Ball-finding puzzle:Find the hidden balls in the box by bouncing laser beams off them.
19!end
diff --git a/apps/plugins/puzzles/src/blackbox.c b/apps/plugins/puzzles/src/blackbox.c
index a9c1f88261..ab8e382d0a 100644
--- a/apps/plugins/puzzles/src/blackbox.c
+++ b/apps/plugins/puzzles/src/blackbox.c
@@ -7,7 +7,11 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#ifdef NO_TGMATH_H
11# include <math.h>
12#else
13# include <tgmath.h>
14#endif
11 15
12#include "puzzles.h" 16#include "puzzles.h"
13 17
@@ -192,12 +196,14 @@ static const char *validate_params(const game_params *params, bool full)
192 * types, and could be worked around if required. */ 196 * types, and could be worked around if required. */
193 if (params->w > 255 || params->h > 255) 197 if (params->w > 255 || params->h > 255)
194 return "Widths and heights greater than 255 are not supported"; 198 return "Widths and heights greater than 255 are not supported";
199 if (params->minballs < 0)
200 return "Negative number of balls";
201 if (params->minballs < 1)
202 return "Number of balls must be at least one";
195 if (params->minballs > params->maxballs) 203 if (params->minballs > params->maxballs)
196 return "Minimum number of balls may not be greater than maximum"; 204 return "Minimum number of balls may not be greater than maximum";
197 if (params->minballs >= params->w * params->h) 205 if (params->minballs >= params->w * params->h)
198 return "Too many balls to fit in grid"; 206 return "Too many balls to fit in grid";
199 if (params->minballs < 1)
200 return "Number of balls must be at least one";
201 return NULL; 207 return NULL;
202} 208}
203 209
@@ -307,7 +313,7 @@ struct game_state {
307 313
308#define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)]) 314#define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)])
309 315
310#define RANGECHECK(s,x) ((x) >= 0 && (x) <= (s)->nlasers) 316#define RANGECHECK(s,x) ((x) >= 0 && (x) < (s)->nlasers)
311 317
312/* specify numbers because they must match array indexes. */ 318/* specify numbers because they must match array indexes. */
313enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 }; 319enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 };
@@ -468,16 +474,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
468 return dupstr("S"); 474 return dupstr("S");
469} 475}
470 476
471static bool game_can_format_as_text_now(const game_params *params)
472{
473 return true;
474}
475
476static char *game_text_format(const game_state *state)
477{
478 return NULL;
479}
480
481struct game_ui { 477struct game_ui {
482 int flash_laserno; 478 int flash_laserno;
483 int errors; 479 int errors;
@@ -495,7 +491,7 @@ static game_ui *new_ui(const game_state *state)
495 ui->newmove = false; 491 ui->newmove = false;
496 492
497 ui->cur_x = ui->cur_y = 1; 493 ui->cur_x = ui->cur_y = 1;
498 ui->cur_visible = false; 494 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
499 495
500 ui->flash_laser = 0; 496 ui->flash_laser = 0;
501 497
@@ -517,7 +513,8 @@ static char *encode_ui(const game_ui *ui)
517 return dupstr(buf); 513 return dupstr(buf);
518} 514}
519 515
520static void decode_ui(game_ui *ui, const char *encoding) 516static void decode_ui(game_ui *ui, const char *encoding,
517 const game_state *state)
521{ 518{
522 sscanf(encoding, "E%d", &ui->errors); 519 sscanf(encoding, "E%d", &ui->errors);
523} 520}
@@ -534,6 +531,41 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
534 ui->newmove = false; 531 ui->newmove = false;
535} 532}
536 533
534static const char *current_key_label(const game_ui *ui,
535 const game_state *state, int button)
536{
537 if (IS_CURSOR_SELECT(button) && ui->cur_visible && !state->reveal) {
538 int gx = ui->cur_x, gy = ui->cur_y, rangeno = -1;
539 if (gx == 0 && gy == 0 && button == CURSOR_SELECT) return "Check";
540 if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) {
541 /* Cursor somewhere in the arena. */
542 if (button == CURSOR_SELECT && !(GRID(state, gx,gy) & BALL_LOCK))
543 return (GRID(state, gx, gy) & BALL_GUESS) ? "Clear" : "Ball";
544 if (button == CURSOR_SELECT2)
545 return (GRID(state, gx, gy) & BALL_LOCK) ? "Unlock" : "Lock";
546 }
547 if (grid2range(state, gx, gy, &rangeno)) {
548 if (button == CURSOR_SELECT &&
549 state->exits[rangeno] == LASER_EMPTY)
550 return "Fire";
551 if (button == CURSOR_SELECT2) {
552 int n = 0;
553 /* Row or column lock or unlock. */
554 if (gy == 0 || gy > state->h) { /* Column lock */
555 for (gy = 1; gy <= state->h; gy++)
556 n += !!(GRID(state, gx, gy) & BALL_LOCK);
557 return n > state->h/2 ? "Unlock" : "Lock";
558 } else { /* Row lock */
559 for (gx = 1; gx <= state->w; gx++)
560 n += !!(GRID(state, gx, gy) & BALL_LOCK);
561 return n > state->w/2 ? "Unlock" : "Lock";
562 }
563 }
564 }
565 }
566 return "";
567}
568
537#define OFFSET(gx,gy,o) do { \ 569#define OFFSET(gx,gy,o) do { \
538 int off = (4 + (o) % 4) % 4; \ 570 int off = (4 + (o) % 4) % 4; \
539 (gx) += offsets[off].x; \ 571 (gx) += offsets[off].x; \
@@ -875,7 +907,7 @@ done:
875#define TILE_SIZE (ds->tilesize) 907#define TILE_SIZE (ds->tilesize)
876 908
877#define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2)) 909#define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2))
878#define FROMDRAW(x) (((x) - (TILE_SIZE / 2)) / TILE_SIZE) 910#define FROMDRAW(x) (((x) + (TILE_SIZE / 2)) / TILE_SIZE - 1)
879 911
880#define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \ 912#define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \
881 (state)->nguesses <= (state)->maxballs && \ 913 (state)->nguesses <= (state)->maxballs && \
@@ -900,7 +932,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
900 if (IS_CURSOR_MOVE(button)) { 932 if (IS_CURSOR_MOVE(button)) {
901 int cx = ui->cur_x, cy = ui->cur_y; 933 int cx = ui->cur_x, cy = ui->cur_y;
902 934
903 move_cursor(button, &cx, &cy, state->w+2, state->h+2, false); 935 move_cursor(button, &cx, &cy, state->w+2, state->h+2, false, NULL);
904 if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) || 936 if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) ||
905 (cx == 0 && cy == state->h+1) || 937 (cx == 0 && cy == state->h+1) ||
906 (cx == state->w+1 && cy == 0) || 938 (cx == state->w+1 && cy == 0) ||
@@ -909,7 +941,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
909 ui->cur_x = cx; 941 ui->cur_x = cx;
910 ui->cur_y = cy; 942 ui->cur_y = cy;
911 ui->cur_visible = true; 943 ui->cur_visible = true;
912 return UI_UPDATE; 944 return MOVE_UI_UPDATE;
913 } 945 }
914 946
915 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 947 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
@@ -919,7 +951,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
919 wouldflash = 1; 951 wouldflash = 1;
920 } else if (button == LEFT_RELEASE) { 952 } else if (button == LEFT_RELEASE) {
921 ui->flash_laser = 0; 953 ui->flash_laser = 0;
922 return UI_UPDATE; 954 return MOVE_UI_UPDATE;
923 } else if (IS_CURSOR_SELECT(button)) { 955 } else if (IS_CURSOR_SELECT(button)) {
924 if (ui->cur_visible) { 956 if (ui->cur_visible) {
925 gx = ui->cur_x; 957 gx = ui->cur_x;
@@ -928,7 +960,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
928 wouldflash = 2; 960 wouldflash = 2;
929 } else { 961 } else {
930 ui->cur_visible = true; 962 ui->cur_visible = true;
931 return UI_UPDATE; 963 return MOVE_UI_UPDATE;
932 } 964 }
933 /* Fix up 'button' for the below logic. */ 965 /* Fix up 'button' for the below logic. */
934 if (button == CURSOR_SELECT2) button = RIGHT_BUTTON; 966 if (button == CURSOR_SELECT2) button = RIGHT_BUTTON;
@@ -977,9 +1009,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
977 return nullret; 1009 return nullret;
978 ui->flash_laserno = rangeno; 1010 ui->flash_laserno = rangeno;
979 ui->flash_laser = wouldflash; 1011 ui->flash_laser = wouldflash;
980 nullret = UI_UPDATE; 1012 nullret = MOVE_UI_UPDATE;
981 if (state->exits[rangeno] != LASER_EMPTY) 1013 if (state->exits[rangeno] != LASER_EMPTY)
982 return UI_UPDATE; 1014 return MOVE_UI_UPDATE;
983 sprintf(buf, "F%d", rangeno); 1015 sprintf(buf, "F%d", rangeno);
984 break; 1016 break;
985 1017
@@ -1034,10 +1066,10 @@ static game_state *execute_move(const game_state *from, const char *move)
1034 1066
1035 case 'F': 1067 case 'F':
1036 sscanf(move+1, "%d", &rangeno); 1068 sscanf(move+1, "%d", &rangeno);
1037 if (ret->exits[rangeno] != LASER_EMPTY)
1038 goto badmove;
1039 if (!RANGECHECK(ret, rangeno)) 1069 if (!RANGECHECK(ret, rangeno))
1040 goto badmove; 1070 goto badmove;
1071 if (ret->exits[rangeno] != LASER_EMPTY)
1072 goto badmove;
1041 fire_laser(ret, rangeno); 1073 fire_laser(ret, rangeno);
1042 break; 1074 break;
1043 1075
@@ -1119,7 +1151,7 @@ static void game_get_cursor_location(const game_ui *ui,
1119 */ 1151 */
1120 1152
1121static void game_compute_size(const game_params *params, int tilesize, 1153static void game_compute_size(const game_params *params, int tilesize,
1122 int *x, int *y) 1154 const game_ui *ui, int *x, int *y)
1123{ 1155{
1124 /* Border is ts/2, to make things easier. 1156 /* Border is ts/2, to make things easier.
1125 * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles 1157 * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles
@@ -1379,10 +1411,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1379 int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1; 1411 int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1;
1380 int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2); 1412 int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2);
1381 1413
1382 draw_rect(dr, 0, 0,
1383 TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3),
1384 COL_BACKGROUND);
1385
1386 /* clockwise around the outline starting at pt behind (1,1). */ 1414 /* clockwise around the outline starting at pt behind (1,1). */
1387 draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT); 1415 draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT);
1388 draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT); 1416 draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT);
@@ -1429,14 +1457,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1429 int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0) 1457 int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0)
1430 ? COL_CURSOR : COL_BALL; 1458 ? COL_CURSOR : COL_BALL;
1431 clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1); 1459 clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1);
1432 draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad, 1460 draw_circle(dr, TODRAW(0) + ds->crad-1, TODRAW(0) + ds->crad-1, ds->crad-1,
1433 outline, outline); 1461 outline, outline);
1434 draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad-2, 1462 draw_circle(dr, TODRAW(0) + ds->crad-1, TODRAW(0) + ds->crad-1, ds->crad-3,
1435 COL_BUTTON, COL_BUTTON); 1463 COL_BUTTON, COL_BUTTON);
1436 unclip(dr); 1464 unclip(dr);
1437 } else { 1465 } else {
1438 draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1, 1466 draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1,
1439 TILE_SIZE+1, TILE_SIZE+1, COL_BACKGROUND); 1467 TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
1440 } 1468 }
1441 draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE); 1469 draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE);
1442 ds->reveal = state->reveal; 1470 ds->reveal = state->reveal;
@@ -1509,19 +1537,6 @@ static int game_status(const game_state *state)
1509 return 0; 1537 return 0;
1510} 1538}
1511 1539
1512static bool game_timing_state(const game_state *state, game_ui *ui)
1513{
1514 return true;
1515}
1516
1517static void game_print_size(const game_params *params, float *x, float *y)
1518{
1519}
1520
1521static void game_print(drawing *dr, const game_state *state, int tilesize)
1522{
1523}
1524
1525#ifdef COMBINED 1540#ifdef COMBINED
1526#define thegame blackbox 1541#define thegame blackbox
1527#endif 1542#endif
@@ -1542,13 +1557,15 @@ const struct game thegame = {
1542 dup_game, 1557 dup_game,
1543 free_game, 1558 free_game,
1544 true, solve_game, 1559 true, solve_game,
1545 false, game_can_format_as_text_now, game_text_format, 1560 false, NULL, NULL, /* can_format_as_text_now, text_format */
1561 NULL, NULL, /* get_prefs, set_prefs */
1546 new_ui, 1562 new_ui,
1547 free_ui, 1563 free_ui,
1548 encode_ui, 1564 encode_ui,
1549 decode_ui, 1565 decode_ui,
1550 NULL, /* game_request_keys */ 1566 NULL, /* game_request_keys */
1551 game_changed_state, 1567 game_changed_state,
1568 current_key_label,
1552 interpret_move, 1569 interpret_move,
1553 execute_move, 1570 execute_move,
1554 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1571 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1560,9 +1577,9 @@ const struct game thegame = {
1560 game_flash_length, 1577 game_flash_length,
1561 game_get_cursor_location, 1578 game_get_cursor_location,
1562 game_status, 1579 game_status,
1563 false, false, game_print_size, game_print, 1580 false, false, NULL, NULL, /* print_size, print */
1564 true, /* wants_statusbar */ 1581 true, /* wants_statusbar */
1565 false, game_timing_state, 1582 false, NULL, /* timing_state */
1566 REQUIRE_RBUTTON, /* flags */ 1583 REQUIRE_RBUTTON, /* flags */
1567}; 1584};
1568 1585
diff --git a/apps/plugins/puzzles/src/bridges.R b/apps/plugins/puzzles/src/bridges.R
deleted file mode 100644
index 75df309152..0000000000
--- a/apps/plugins/puzzles/src/bridges.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3BRIDGES_EXTRA = dsf findloop
4
5bridges : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon
6
7bridges : [G] WINDOWS COMMON bridges BRIDGES_EXTRA bridges.res|noicon.res
8
9ALL += bridges[COMBINED] BRIDGES_EXTRA
10
11!begin am gtk
12GAMES += bridges
13!end
14
15!begin >list.c
16 A(bridges) \
17!end
18
19!begin >gamedesc.txt
20bridges:bridges.exe:Bridges:Bridge-placing puzzle:Connect all the islands with a network of bridges.
21!end
diff --git a/apps/plugins/puzzles/src/bridges.c b/apps/plugins/puzzles/src/bridges.c
index 83086c9761..7e87764d6d 100644
--- a/apps/plugins/puzzles/src/bridges.c
+++ b/apps/plugins/puzzles/src/bridges.c
@@ -72,11 +72,15 @@
72#include <string.h> 72#include <string.h>
73#include <assert.h> 73#include <assert.h>
74#include <ctype.h> 74#include <ctype.h>
75#include <math.h> 75#include <limits.h>
76#ifdef NO_TGMATH_H
77# include <math.h>
78#else
79# include <tgmath.h>
80#endif
76 81
77#include "puzzles.h" 82#include "puzzles.h"
78 83
79/* Turn this on for hints about which lines are considered possibilities. */
80#undef DRAW_GRID 84#undef DRAW_GRID
81 85
82/* --- structures for params, state, etc. --- */ 86/* --- structures for params, state, etc. --- */
@@ -134,8 +138,8 @@ struct game_params {
134typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */ 138typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */
135 139
136struct solver_state { 140struct solver_state {
137 int *dsf, *comptspaces; 141 DSF *dsf, *tmpdsf;
138 int *tmpdsf, *tmpcompspaces; 142 int *comptspaces, *tmpcompspaces;
139 int refcount; 143 int refcount;
140}; 144};
141 145
@@ -183,7 +187,7 @@ struct game_state {
183 187
184#define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0) 188#define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0)
185 189
186#define WITHIN2(x,min,max) ((x) >= (min) && (x) < (max)) 190#define WITHIN2(x,min,max) ((x) >= (min) && (x) <= (max))
187#define WITHIN(x,min,max) ((min) > (max) ? \ 191#define WITHIN(x,min,max) ((min) > (max) ? \
188 WITHIN2(x,max,min) : WITHIN2(x,min,max)) 192 WITHIN2(x,max,min) : WITHIN2(x,min,max))
189 193
@@ -803,6 +807,8 @@ static const char *validate_params(const game_params *params, bool full)
803{ 807{
804 if (params->w < 3 || params->h < 3) 808 if (params->w < 3 || params->h < 3)
805 return "Width and height must be at least 3"; 809 return "Width and height must be at least 3";
810 if (params->w > INT_MAX / params->h)
811 return "Width times height must not be unreasonably large";
806 if (params->maxb < 1 || params->maxb > MAX_BRIDGES) 812 if (params->maxb < 1 || params->maxb > MAX_BRIDGES)
807 return "Too many bridges."; 813 return "Too many bridges.";
808 if (full) { 814 if (full) {
@@ -1133,13 +1139,13 @@ static bool map_hasloops(game_state *state, bool mark)
1133 1139
1134static void map_group(game_state *state) 1140static void map_group(game_state *state)
1135{ 1141{
1136 int i, wh = state->w*state->h, d1, d2; 1142 int i, d1, d2;
1137 int x, y, x2, y2; 1143 int x, y, x2, y2;
1138 int *dsf = state->solver->dsf; 1144 DSF *dsf = state->solver->dsf;
1139 struct island *is, *is_join; 1145 struct island *is, *is_join;
1140 1146
1141 /* Initialise dsf. */ 1147 /* Initialise dsf. */
1142 dsf_init(dsf, wh); 1148 dsf_reinit(dsf);
1143 1149
1144 /* For each island, find connected islands right or down 1150 /* For each island, find connected islands right or down
1145 * and merge the dsf for the island squares as well as the 1151 * and merge the dsf for the island squares as well as the
@@ -1160,7 +1166,7 @@ static void map_group(game_state *state)
1160 if (!is_join) continue; 1166 if (!is_join) continue;
1161 1167
1162 d2 = DINDEX(is_join->x, is_join->y); 1168 d2 = DINDEX(is_join->x, is_join->y);
1163 if (dsf_canonify(dsf,d1) == dsf_canonify(dsf,d2)) { 1169 if (dsf_equivalent(dsf, d1, d2)) {
1164 ; /* we have a loop. See comment in map_hasloops. */ 1170 ; /* we have a loop. See comment in map_hasloops. */
1165 /* However, we still want to merge all squares joining 1171 /* However, we still want to merge all squares joining
1166 * this side-that-makes-a-loop. */ 1172 * this side-that-makes-a-loop. */
@@ -1180,7 +1186,8 @@ static void map_group(game_state *state)
1180static bool map_group_check(game_state *state, int canon, bool warn, 1186static bool map_group_check(game_state *state, int canon, bool warn,
1181 int *nislands_r) 1187 int *nislands_r)
1182{ 1188{
1183 int *dsf = state->solver->dsf, nislands = 0; 1189 DSF *dsf = state->solver->dsf;
1190 int nislands = 0;
1184 int x, y, i; 1191 int x, y, i;
1185 bool allfull = true; 1192 bool allfull = true;
1186 struct island *is; 1193 struct island *is;
@@ -1212,7 +1219,8 @@ static bool map_group_check(game_state *state, int canon, bool warn,
1212 1219
1213static bool map_group_full(game_state *state, int *ngroups_r) 1220static bool map_group_full(game_state *state, int *ngroups_r)
1214{ 1221{
1215 int *dsf = state->solver->dsf, ngroups = 0; 1222 DSF *dsf = state->solver->dsf;
1223 int ngroups = 0;
1216 int i; 1224 int i;
1217 bool anyfull = false; 1225 bool anyfull = false;
1218 struct island *is; 1226 struct island *is;
@@ -1267,7 +1275,8 @@ static void map_clear(game_state *state)
1267static void solve_join(struct island *is, int direction, int n, bool is_max) 1275static void solve_join(struct island *is, int direction, int n, bool is_max)
1268{ 1276{
1269 struct island *is_orth; 1277 struct island *is_orth;
1270 int d1, d2, *dsf = is->state->solver->dsf; 1278 int d1, d2;
1279 DSF *dsf = is->state->solver->dsf;
1271 game_state *state = is->state; /* for DINDEX */ 1280 game_state *state = is->state; /* for DINDEX */
1272 1281
1273 is_orth = INDEX(is->state, gridi, 1282 is_orth = INDEX(is->state, gridi,
@@ -1281,7 +1290,7 @@ static void solve_join(struct island *is, int direction, int n, bool is_max)
1281 if (n > 0 && !is_max) { 1290 if (n > 0 && !is_max) {
1282 d1 = DINDEX(is->x, is->y); 1291 d1 = DINDEX(is->x, is->y);
1283 d2 = DINDEX(is_orth->x, is_orth->y); 1292 d2 = DINDEX(is_orth->x, is_orth->y);
1284 if (dsf_canonify(dsf, d1) != dsf_canonify(dsf, d2)) 1293 if (!dsf_equivalent(dsf, d1, d2))
1285 dsf_merge(dsf, d1, d2); 1294 dsf_merge(dsf, d1, d2);
1286 } 1295 }
1287} 1296}
@@ -1382,7 +1391,8 @@ static bool solve_island_stage1(struct island *is, bool *didsth_r)
1382static bool solve_island_checkloop(struct island *is, int direction) 1391static bool solve_island_checkloop(struct island *is, int direction)
1383{ 1392{
1384 struct island *is_orth; 1393 struct island *is_orth;
1385 int *dsf = is->state->solver->dsf, d1, d2; 1394 DSF *dsf = is->state->solver->dsf;
1395 int d1, d2;
1386 game_state *state = is->state; 1396 game_state *state = is->state;
1387 1397
1388 if (is->state->allowloops) 1398 if (is->state->allowloops)
@@ -1399,7 +1409,7 @@ static bool solve_island_checkloop(struct island *is, int direction)
1399 1409
1400 d1 = DINDEX(is->x, is->y); 1410 d1 = DINDEX(is->x, is->y);
1401 d2 = DINDEX(is_orth->x, is_orth->y); 1411 d2 = DINDEX(is_orth->x, is_orth->y);
1402 if (dsf_canonify(dsf, d1) == dsf_canonify(dsf, d2)) { 1412 if (dsf_equivalent(dsf, d1, d2)) {
1403 /* two islands are connected already; don't join them. */ 1413 /* two islands are connected already; don't join them. */
1404 return true; 1414 return true;
1405 } 1415 }
@@ -1457,7 +1467,8 @@ static bool solve_island_stage2(struct island *is, bool *didsth_r)
1457static bool solve_island_subgroup(struct island *is, int direction) 1467static bool solve_island_subgroup(struct island *is, int direction)
1458{ 1468{
1459 struct island *is_join; 1469 struct island *is_join;
1460 int nislands, *dsf = is->state->solver->dsf; 1470 int nislands;
1471 DSF *dsf = is->state->solver->dsf;
1461 game_state *state = is->state; 1472 game_state *state = is->state;
1462 1473
1463 debug(("..checking subgroups.\n")); 1474 debug(("..checking subgroups.\n"));
@@ -1520,7 +1531,6 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1520{ 1531{
1521 int i, n, x, y, missing, spc, curr, maxb; 1532 int i, n, x, y, missing, spc, curr, maxb;
1522 bool didsth = false; 1533 bool didsth = false;
1523 int wh = is->state->w * is->state->h;
1524 struct solver_state *ss = is->state->solver; 1534 struct solver_state *ss = is->state->solver;
1525 1535
1526 assert(didsth_r); 1536 assert(didsth_r);
@@ -1544,7 +1554,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1544 maxb = -1; 1554 maxb = -1;
1545 /* We have to squirrel the dsf away and restore it afterwards; 1555 /* We have to squirrel the dsf away and restore it afterwards;
1546 * it is additive only, and can't be removed from. */ 1556 * it is additive only, and can't be removed from. */
1547 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); 1557 dsf_copy(ss->tmpdsf, ss->dsf);
1548 for (n = curr+1; n <= curr+spc; n++) { 1558 for (n = curr+1; n <= curr+spc; n++) {
1549 solve_join(is, i, n, false); 1559 solve_join(is, i, n, false);
1550 map_update_possibles(is->state); 1560 map_update_possibles(is->state);
@@ -1560,7 +1570,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1560 } 1570 }
1561 } 1571 }
1562 solve_join(is, i, curr, false); /* put back to before. */ 1572 solve_join(is, i, curr, false); /* put back to before. */
1563 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); 1573 dsf_copy(ss->dsf, ss->tmpdsf);
1564 1574
1565 if (maxb != -1) { 1575 if (maxb != -1) {
1566 /*debug_state(is->state);*/ 1576 /*debug_state(is->state);*/
@@ -1629,7 +1639,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1629 is->adj.points[j].dx ? G_LINEH : G_LINEV); 1639 is->adj.points[j].dx ? G_LINEH : G_LINEV);
1630 if (before[i] != 0) continue; /* this idea is pointless otherwise */ 1640 if (before[i] != 0) continue; /* this idea is pointless otherwise */
1631 1641
1632 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); 1642 dsf_copy(ss->tmpdsf, ss->dsf);
1633 1643
1634 for (j = 0; j < is->adj.npoints; j++) { 1644 for (j = 0; j < is->adj.npoints; j++) {
1635 spc = island_adjspace(is, true, missing, j); 1645 spc = island_adjspace(is, true, missing, j);
@@ -1644,7 +1654,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1644 1654
1645 for (j = 0; j < is->adj.npoints; j++) 1655 for (j = 0; j < is->adj.npoints; j++)
1646 solve_join(is, j, before[j], false); 1656 solve_join(is, j, before[j], false);
1647 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); 1657 dsf_copy(ss->dsf, ss->tmpdsf);
1648 1658
1649 if (got) { 1659 if (got) {
1650 debug(("island at (%d,%d) must connect in direction (%d,%d) to" 1660 debug(("island at (%d,%d) must connect in direction (%d,%d) to"
@@ -1763,8 +1773,8 @@ static game_state *new_state(const game_params *params)
1763 ret->completed = false; 1773 ret->completed = false;
1764 1774
1765 ret->solver = snew(struct solver_state); 1775 ret->solver = snew(struct solver_state);
1766 ret->solver->dsf = snew_dsf(wh); 1776 ret->solver->dsf = dsf_new(wh);
1767 ret->solver->tmpdsf = snewn(wh, int); 1777 ret->solver->tmpdsf = dsf_new(wh);
1768 1778
1769 ret->solver->refcount = 1; 1779 ret->solver->refcount = 1;
1770 1780
@@ -1813,8 +1823,8 @@ static game_state *dup_game(const game_state *state)
1813static void free_game(game_state *state) 1823static void free_game(game_state *state)
1814{ 1824{
1815 if (--state->solver->refcount <= 0) { 1825 if (--state->solver->refcount <= 0) {
1816 sfree(state->solver->dsf); 1826 dsf_free(state->solver->dsf);
1817 sfree(state->solver->tmpdsf); 1827 dsf_free(state->solver->tmpdsf);
1818 sfree(state->solver); 1828 sfree(state->solver);
1819 } 1829 }
1820 1830
@@ -2004,28 +2014,38 @@ generated:
2004 2014
2005static const char *validate_desc(const game_params *params, const char *desc) 2015static const char *validate_desc(const game_params *params, const char *desc)
2006{ 2016{
2007 int i, wh = params->w * params->h; 2017 int i, j, wh = params->w * params->h, nislands = 0;
2018 bool *last_row = snewn(params->w, bool);
2008 2019
2020 memset(last_row, 0, params->w * sizeof(bool));
2009 for (i = 0; i < wh; i++) { 2021 for (i = 0; i < wh; i++) {
2010 if (*desc >= '1' && *desc <= '9') 2022 if ((*desc >= '1' && *desc <= '9') || (*desc >= 'A' && *desc <= 'G')) {
2011 /* OK */; 2023 nislands++;
2012 else if (*desc >= 'a' && *desc <= 'z') 2024 /* Look for other islands to the left and above. */
2025 if ((i % params->w > 0 && last_row[i % params->w - 1]) ||
2026 last_row[i % params->w]) {
2027 sfree(last_row);
2028 return "Game description contains joined islands";
2029 }
2030 last_row[i % params->w] = true;
2031 } else if (*desc >= 'a' && *desc <= 'z') {
2032 for (j = 0; j < *desc - 'a' + 1; j++)
2033 last_row[(i + j) % params->w] = false;
2013 i += *desc - 'a'; /* plus the i++ */ 2034 i += *desc - 'a'; /* plus the i++ */
2014 else if (*desc >= 'A' && *desc <= 'G') 2035 } else if (!*desc) {
2015 /* OK */; 2036 sfree(last_row);
2016 else if (*desc == 'V' || *desc == 'W' ||
2017 *desc == 'X' || *desc == 'Y' ||
2018 *desc == 'H' || *desc == 'I' ||
2019 *desc == 'J' || *desc == 'K')
2020 /* OK */;
2021 else if (!*desc)
2022 return "Game description shorter than expected"; 2037 return "Game description shorter than expected";
2023 else 2038 } else {
2039 sfree(last_row);
2024 return "Game description contains unexpected character"; 2040 return "Game description contains unexpected character";
2041 }
2025 desc++; 2042 desc++;
2026 } 2043 }
2044 sfree(last_row);
2027 if (*desc || i > wh) 2045 if (*desc || i > wh)
2028 return "Game description longer than expected"; 2046 return "Game description longer than expected";
2047 if (nislands < 2)
2048 return "Game description has too few islands";
2029 2049
2030 return NULL; 2050 return NULL;
2031} 2051}
@@ -2105,32 +2125,47 @@ static char *ui_cancel_drag(game_ui *ui)
2105 ui->dragx_src = ui->dragy_src = -1; 2125 ui->dragx_src = ui->dragy_src = -1;
2106 ui->dragx_dst = ui->dragy_dst = -1; 2126 ui->dragx_dst = ui->dragy_dst = -1;
2107 ui->dragging = false; 2127 ui->dragging = false;
2108 return UI_UPDATE; 2128 return MOVE_UI_UPDATE;
2109} 2129}
2110 2130
2111static game_ui *new_ui(const game_state *state) 2131static game_ui *new_ui(const game_state *state)
2112{ 2132{
2113 game_ui *ui = snew(game_ui); 2133 game_ui *ui = snew(game_ui);
2114 ui_cancel_drag(ui); 2134 ui_cancel_drag(ui);
2115 ui->cur_x = state->islands[0].x; 2135 if (state != NULL) {
2116 ui->cur_y = state->islands[0].y; 2136 ui->cur_x = state->islands[0].x;
2117 ui->cur_visible = false; 2137 ui->cur_y = state->islands[0].y;
2138 }
2139 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2118 ui->show_hints = false; 2140 ui->show_hints = false;
2119 return ui; 2141 return ui;
2120} 2142}
2121 2143
2122static void free_ui(game_ui *ui) 2144static config_item *get_prefs(game_ui *ui)
2123{ 2145{
2124 sfree(ui); 2146 config_item *ret;
2147
2148 ret = snewn(2, config_item);
2149
2150 ret[0].name = "Show possible bridge locations";
2151 ret[0].kw = "show-hints";
2152 ret[0].type = C_BOOLEAN;
2153 ret[0].u.boolean.bval = ui->show_hints;
2154
2155 ret[1].name = NULL;
2156 ret[1].type = C_END;
2157
2158 return ret;
2125} 2159}
2126 2160
2127static char *encode_ui(const game_ui *ui) 2161static void set_prefs(game_ui *ui, const config_item *cfg)
2128{ 2162{
2129 return NULL; 2163 ui->show_hints = cfg[0].u.boolean.bval;
2130} 2164}
2131 2165
2132static void decode_ui(game_ui *ui, const char *encoding) 2166static void free_ui(game_ui *ui)
2133{ 2167{
2168 sfree(ui);
2134} 2169}
2135 2170
2136static void game_changed_state(game_ui *ui, const game_state *oldstate, 2171static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -2138,6 +2173,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2138{ 2173{
2139} 2174}
2140 2175
2176static const char *current_key_label(const game_ui *ui,
2177 const game_state *state, int button)
2178{
2179 if (IS_CURSOR_SELECT(button)) {
2180 if (!ui->cur_visible)
2181 return ""; /* Actually shows cursor. */
2182 if (ui->dragging || button == CURSOR_SELECT2)
2183 return "Finished";
2184 if (GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND)
2185 return "Select";
2186 }
2187 return "";
2188}
2189
2141struct game_drawstate { 2190struct game_drawstate {
2142 int tilesize; 2191 int tilesize;
2143 int w, h; 2192 int w, h;
@@ -2307,7 +2356,7 @@ static char *update_drag_dst(const game_state *state, game_ui *ui,
2307 /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n", 2356 /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n",
2308 ui->dragx_src, ui->dragy_src, dx, dy, 2357 ui->dragx_src, ui->dragy_src, dx, dy,
2309 ui->dragx_dst, ui->dragy_dst));*/ 2358 ui->dragx_dst, ui->dragy_dst));*/
2310 return UI_UPDATE; 2359 return MOVE_UI_UPDATE;
2311} 2360}
2312 2361
2313static char *finish_drag(const game_state *state, game_ui *ui) 2362static char *finish_drag(const game_state *state, game_ui *ui)
@@ -2342,15 +2391,15 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2342 char buf[80], *ret; 2391 char buf[80], *ret;
2343 grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0; 2392 grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0;
2344 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 2393 bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
2345 button &= ~MOD_MASK; 2394 button = STRIP_BUTTON_MODIFIERS(button);
2346 2395
2347 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2396 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2348 if (!INGRID(state, gx, gy)) return NULL; 2397 if (!INGRID(state, gx, gy)) return MOVE_UNUSED;
2349 ui->cur_visible = false; 2398 ui->cur_visible = false;
2350 if (ggrid & G_ISLAND) { 2399 if (ggrid & G_ISLAND) {
2351 ui->dragx_src = gx; 2400 ui->dragx_src = gx;
2352 ui->dragy_src = gy; 2401 ui->dragy_src = gy;
2353 return UI_UPDATE; 2402 return MOVE_UI_UPDATE;
2354 } else 2403 } else
2355 return ui_cancel_drag(ui); 2404 return ui_cancel_drag(ui);
2356 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) { 2405 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
@@ -2364,7 +2413,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2364 /* cancel a drag when we go back to the starting point */ 2413 /* cancel a drag when we go back to the starting point */
2365 ui->dragx_dst = -1; 2414 ui->dragx_dst = -1;
2366 ui->dragy_dst = -1; 2415 ui->dragy_dst = -1;
2367 return UI_UPDATE; 2416 return MOVE_UI_UPDATE;
2368 } 2417 }
2369 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) { 2418 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
2370 if (ui->dragging) { 2419 if (ui->dragging) {
@@ -2375,8 +2424,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2375 return ui_cancel_drag(ui); 2424 return ui_cancel_drag(ui);
2376 } 2425 }
2377 ui_cancel_drag(ui); 2426 ui_cancel_drag(ui);
2378 if (!INGRID(state, gx, gy)) return NULL; 2427 if (!INGRID(state, gx, gy)) return MOVE_UNUSED;
2379 if (!(GRID(state, gx, gy) & G_ISLAND)) return NULL; 2428 if (!(GRID(state, gx, gy) & G_ISLAND)) return MOVE_NO_EFFECT;
2380 sprintf(buf, "M%d,%d", gx, gy); 2429 sprintf(buf, "M%d,%d", gx, gy);
2381 return dupstr(buf); 2430 return dupstr(buf);
2382 } 2431 }
@@ -2397,9 +2446,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2397 if (ui->dragging) { 2446 if (ui->dragging) {
2398 int nx = ui->cur_x, ny = ui->cur_y; 2447 int nx = ui->cur_x, ny = ui->cur_y;
2399 2448
2400 move_cursor(button, &nx, &ny, state->w, state->h, false); 2449 move_cursor(button, &nx, &ny, state->w, state->h, false, NULL);
2401 if (nx == ui->cur_x && ny == ui->cur_y) 2450 if (nx == ui->cur_x && ny == ui->cur_y)
2402 return NULL; 2451 return MOVE_NO_EFFECT;
2403 update_drag_dst(state, ui, ds, 2452 update_drag_dst(state, ui, ds,
2404 COORD(nx)+TILE_SIZE/2, 2453 COORD(nx)+TILE_SIZE/2,
2405 COORD(ny)+TILE_SIZE/2); 2454 COORD(ny)+TILE_SIZE/2);
@@ -2451,19 +2500,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2451 2500
2452 if (!dingrid) break; 2501 if (!dingrid) break;
2453 } 2502 }
2454 if (!oingrid) return UI_UPDATE; 2503 if (!oingrid) return MOVE_UI_UPDATE;
2455 } 2504 }
2456 /* not reached */ 2505 /* not reached */
2457 2506
2458found: 2507found:
2459 ui->cur_x = nx; 2508 ui->cur_x = nx;
2460 ui->cur_y = ny; 2509 ui->cur_y = ny;
2461 return UI_UPDATE; 2510 return MOVE_UI_UPDATE;
2462 } 2511 }
2463 } else if (IS_CURSOR_SELECT(button)) { 2512 } else if (IS_CURSOR_SELECT(button)) {
2464 if (!ui->cur_visible) { 2513 if (!ui->cur_visible) {
2465 ui->cur_visible = true; 2514 ui->cur_visible = true;
2466 return UI_UPDATE; 2515 return MOVE_UI_UPDATE;
2467 } 2516 }
2468 if (ui->dragging || button == CURSOR_SELECT2) { 2517 if (ui->dragging || button == CURSOR_SELECT2) {
2469 ui_cancel_drag(ui); 2518 ui_cancel_drag(ui);
@@ -2471,7 +2520,7 @@ found:
2471 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y); 2520 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y);
2472 return dupstr(buf); 2521 return dupstr(buf);
2473 } else 2522 } else
2474 return UI_UPDATE; 2523 return MOVE_UI_UPDATE;
2475 } else { 2524 } else {
2476 grid_type v = GRID(state, ui->cur_x, ui->cur_y); 2525 grid_type v = GRID(state, ui->cur_x, ui->cur_y);
2477 if (v & G_ISLAND) { 2526 if (v & G_ISLAND) {
@@ -2480,7 +2529,7 @@ found:
2480 ui->dragy_src = ui->cur_y; 2529 ui->dragy_src = ui->cur_y;
2481 ui->dragx_dst = ui->dragy_dst = -1; 2530 ui->dragx_dst = ui->dragy_dst = -1;
2482 ui->drag_is_noline = (button == CURSOR_SELECT2); 2531 ui->drag_is_noline = (button == CURSOR_SELECT2);
2483 return UI_UPDATE; 2532 return MOVE_UI_UPDATE;
2484 } 2533 }
2485 } 2534 }
2486 } else if ((button >= '0' && button <= '9') || 2535 } else if ((button >= '0' && button <= '9') ||
@@ -2498,7 +2547,7 @@ found:
2498 2547
2499 if (!ui->cur_visible) { 2548 if (!ui->cur_visible) {
2500 ui->cur_visible = true; 2549 ui->cur_visible = true;
2501 return UI_UPDATE; 2550 return MOVE_UI_UPDATE;
2502 } 2551 }
2503 2552
2504 for (i = 0; i < state->n_islands; ++i) { 2553 for (i = 0; i < state->n_islands; ++i) {
@@ -2525,15 +2574,15 @@ found:
2525 if (best_x != -1 && best_y != -1) { 2574 if (best_x != -1 && best_y != -1) {
2526 ui->cur_x = best_x; 2575 ui->cur_x = best_x;
2527 ui->cur_y = best_y; 2576 ui->cur_y = best_y;
2528 return UI_UPDATE; 2577 return MOVE_UI_UPDATE;
2529 } else 2578 } else
2530 return NULL; 2579 return MOVE_NO_EFFECT;
2531 } else if (button == 'g' || button == 'G') { 2580 } else if (button == 'g' || button == 'G') {
2532 ui->show_hints = !ui->show_hints; 2581 ui->show_hints = !ui->show_hints;
2533 return UI_UPDATE; 2582 return MOVE_UI_UPDATE;
2534 } 2583 }
2535 2584
2536 return NULL; 2585 return MOVE_UNUSED;
2537} 2586}
2538 2587
2539static game_state *execute_move(const game_state *state, const char *move) 2588static game_state *execute_move(const game_state *state, const char *move)
@@ -2557,6 +2606,8 @@ static game_state *execute_move(const game_state *state, const char *move)
2557 goto badmove; 2606 goto badmove;
2558 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) 2607 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2559 goto badmove; 2608 goto badmove;
2609 /* Precisely one co-ordinate must differ between islands. */
2610 if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
2560 is1 = INDEX(ret, gridi, x1, y1); 2611 is1 = INDEX(ret, gridi, x1, y1);
2561 is2 = INDEX(ret, gridi, x2, y2); 2612 is2 = INDEX(ret, gridi, x2, y2);
2562 if (!is1 || !is2) goto badmove; 2613 if (!is1 || !is2) goto badmove;
@@ -2568,6 +2619,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2568 goto badmove; 2619 goto badmove;
2569 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) 2620 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2570 goto badmove; 2621 goto badmove;
2622 if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
2571 is1 = INDEX(ret, gridi, x1, y1); 2623 is1 = INDEX(ret, gridi, x1, y1);
2572 is2 = INDEX(ret, gridi, x2, y2); 2624 is2 = INDEX(ret, gridi, x2, y2);
2573 if (!is1 || !is2) goto badmove; 2625 if (!is1 || !is2) goto badmove;
@@ -2636,7 +2688,7 @@ static char *solve_game(const game_state *state, const game_state *currstate,
2636 */ 2688 */
2637 2689
2638static void game_compute_size(const game_params *params, int tilesize, 2690static void game_compute_size(const game_params *params, int tilesize,
2639 int *x, int *y) 2691 const game_ui *ui, int *x, int *y)
2640{ 2692{
2641 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2693 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2642 struct { int tilesize; } ads, *ds = &ads; 2694 struct { int tilesize; } ads, *ds = &ads;
@@ -2987,9 +3039,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2987 3039
2988 /* Clear screen, if required. */ 3040 /* Clear screen, if required. */
2989 if (!ds->started) { 3041 if (!ds->started) {
2990 draw_rect(dr, 0, 0,
2991 TILE_SIZE * ds->w + 2 * BORDER,
2992 TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2993#ifdef DRAW_GRID 3042#ifdef DRAW_GRID
2994 draw_rect_outline(dr, 3043 draw_rect_outline(dr,
2995 COORD(0)-1, COORD(0)-1, 3044 COORD(0)-1, COORD(0)-1,
@@ -3177,22 +3226,19 @@ static int game_status(const game_state *state)
3177 return state->completed ? +1 : 0; 3226 return state->completed ? +1 : 0;
3178} 3227}
3179 3228
3180static bool game_timing_state(const game_state *state, game_ui *ui) 3229static void game_print_size(const game_params *params, const game_ui *ui,
3181{ 3230 float *x, float *y)
3182 return true;
3183}
3184
3185static void game_print_size(const game_params *params, float *x, float *y)
3186{ 3231{
3187 int pw, ph; 3232 int pw, ph;
3188 3233
3189 /* 10mm squares by default. */ 3234 /* 10mm squares by default. */
3190 game_compute_size(params, 1000, &pw, &ph); 3235 game_compute_size(params, 1000, ui, &pw, &ph);
3191 *x = pw / 100.0F; 3236 *x = pw / 100.0F;
3192 *y = ph / 100.0F; 3237 *y = ph / 100.0F;
3193} 3238}
3194 3239
3195static void game_print(drawing *dr, const game_state *state, int ts) 3240static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3241 int ts)
3196{ 3242{
3197 int ink = print_mono_colour(dr, 0); 3243 int ink = print_mono_colour(dr, 0);
3198 int paper = print_mono_colour(dr, 1); 3244 int paper = print_mono_colour(dr, 1);
@@ -3266,12 +3312,14 @@ const struct game thegame = {
3266 free_game, 3312 free_game,
3267 true, solve_game, 3313 true, solve_game,
3268 true, game_can_format_as_text_now, game_text_format, 3314 true, game_can_format_as_text_now, game_text_format,
3315 get_prefs, set_prefs,
3269 new_ui, 3316 new_ui,
3270 free_ui, 3317 free_ui,
3271 encode_ui, 3318 NULL, /* encode_ui */
3272 decode_ui, 3319 NULL, /* decode_ui */
3273 NULL, /* game_request_keys */ 3320 NULL, /* game_request_keys */
3274 game_changed_state, 3321 game_changed_state,
3322 current_key_label,
3275 interpret_move, 3323 interpret_move,
3276 execute_move, 3324 execute_move,
3277 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3325 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3285,7 +3333,7 @@ const struct game thegame = {
3285 game_status, 3333 game_status,
3286 true, false, game_print_size, game_print, 3334 true, false, game_print_size, game_print,
3287 false, /* wants_statusbar */ 3335 false, /* wants_statusbar */
3288 false, game_timing_state, 3336 false, NULL, /* timing_state */
3289 REQUIRE_RBUTTON, /* flags */ 3337 REQUIRE_RBUTTON, /* flags */
3290}; 3338};
3291 3339
diff --git a/apps/plugins/puzzles/src/combi.c b/apps/plugins/puzzles/src/combi.c
index 3460183c9c..132d74027e 100644
--- a/apps/plugins/puzzles/src/combi.c
+++ b/apps/plugins/puzzles/src/combi.c
@@ -71,40 +71,3 @@ void free_combi(combi_ctx *combi)
71 sfree(combi->a); 71 sfree(combi->a);
72 sfree(combi); 72 sfree(combi);
73} 73}
74
75/* compile this with:
76 * gcc -o combi.exe -DSTANDALONE_COMBI_TEST combi.c malloc.c
77 */
78#ifdef STANDALONE_COMBI_TEST
79
80#include <stdio.h>
81
82void fatal(const char *fmt, ...)
83{
84 abort();
85}
86
87int main(int argc, char *argv[])
88{
89 combi_ctx *c;
90 int i, r, n;
91
92 if (argc < 3) {
93 fprintf(stderr, "Usage: combi R N\n");
94 exit(1);
95 }
96
97 r = atoi(argv[1]); n = atoi(argv[2]);
98 c = new_combi(r, n);
99 printf("combi %d of %d, %d elements.\n", c->r, c->n, c->total);
100
101 while (next_combi(c)) {
102 for (i = 0; i < c->r; i++) {
103 printf("%d ", c->a[i]);
104 }
105 printf("\n");
106 }
107 free_combi(c);
108}
109
110#endif
diff --git a/apps/plugins/puzzles/src/cube.R b/apps/plugins/puzzles/src/cube.R
deleted file mode 100644
index 85b081ec76..0000000000
--- a/apps/plugins/puzzles/src/cube.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3cube : [X] GTK COMMON cube cube-icon|no-icon
4
5cube : [G] WINDOWS COMMON cube cube.res|noicon.res
6
7ALL += cube[COMBINED]
8
9!begin am gtk
10GAMES += cube
11!end
12
13!begin >list.c
14 A(cube) \
15!end
16
17!begin >gamedesc.txt
18cube:cube.exe:Cube:Rolling cube puzzle:Pick up all the blue squares by rolling the cube over them.
19!end
diff --git a/apps/plugins/puzzles/src/cube.c b/apps/plugins/puzzles/src/cube.c
index 8c8c46faed..08788cbd65 100644
--- a/apps/plugins/puzzles/src/cube.c
+++ b/apps/plugins/puzzles/src/cube.c
@@ -7,7 +7,11 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#ifdef NO_TGMATH_H
11# include <math.h>
12#else
13# include <tgmath.h>
14#endif
11 15
12#include "puzzles.h" 16#include "puzzles.h"
13 17
@@ -171,7 +175,7 @@ enum { LEFT, RIGHT, UP, DOWN, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT };
171 (ra)[0] = rx; (ra)[1] = ry; (ra)[2] = rz; \ 175 (ra)[0] = rx; (ra)[1] = ry; (ra)[2] = rz; \
172} while (0) 176} while (0)
173 177
174#define APPROXEQ(x,y) ( SQ(x-y) < 0.1 ) 178#define APPROXEQ(x,y) ( SQ(x-y) < 0.1F )
175 179
176struct grid_square { 180struct grid_square {
177 float x, y; 181 float x, y;
@@ -202,8 +206,8 @@ struct game_grid {
202}; 206};
203 207
204#define SET_SQUARE(state, i, val) \ 208#define SET_SQUARE(state, i, val) \
205 ((state)->bluemask[(i)/32] &= ~(1 << ((i)%32)), \ 209 ((state)->bluemask[(i)/32] &= ~(1UL << ((i)%32)), \
206 (state)->bluemask[(i)/32] |= ((!!val) << ((i)%32))) 210 (state)->bluemask[(i)/32] |= ((unsigned long)(!!val) << ((i)%32)))
207#define GET_SQUARE(state, i) \ 211#define GET_SQUARE(state, i) \
208 (((state)->bluemask[(i)/32] >> ((i)%32)) & 1) 212 (((state)->bluemask[(i)/32] >> ((i)%32)) & 1)
209 213
@@ -542,12 +546,38 @@ static const char *validate_params(const game_params *params, bool full)
542 if (params->solid < 0 || params->solid >= lenof(solids)) 546 if (params->solid < 0 || params->solid >= lenof(solids))
543 return "Unrecognised solid type"; 547 return "Unrecognised solid type";
544 548
549 if (params->d1 < 0 || params->d2 < 0)
550 return "Grid dimensions may not be negative";
551
545 if (solids[params->solid]->order == 4) { 552 if (solids[params->solid]->order == 4) {
546 if (params->d1 <= 1 || params->d2 <= 1) 553 if (params->d1 <= 1 || params->d2 <= 1)
547 return "Both grid dimensions must be greater than one"; 554 return "Both grid dimensions must be greater than one";
555 if (params->d2 > INT_MAX / params->d1)
556 return "Grid area must not be unreasonably large";
548 } else { 557 } else {
549 if (params->d1 <= 0 && params->d2 <= 0) 558 if (params->d1 <= 0 && params->d2 <= 0)
550 return "At least one grid dimension must be greater than zero"; 559 return "At least one grid dimension must be greater than zero";
560
561 /*
562 * Check whether d1^2 + d2^2 + 4 d1 d2 > INT_MAX, without overflow:
563 *
564 * First check d1^2 doesn't overflow by itself.
565 *
566 * Then check d2^2 doesn't exceed the remaining space between
567 * d1^2 and INT_MAX.
568 *
569 * If that's all OK then we know both d1 and d2 are
570 * individually less than the square root of INT_MAX, so we
571 * can safely multiply them and compare against the
572 * _remaining_ space.
573 */
574 if ((params->d1 > 0 && params->d1 > INT_MAX / params->d1) ||
575 (params->d2 > 0 &&
576 params->d2 > (INT_MAX - params->d1*params->d1) / params->d2) ||
577 (params->d2 > 0 &&
578 params->d1*params->d2 > (INT_MAX - params->d1*params->d1 -
579 params->d2*params->d2) / params->d2))
580 return "Grid area must not be unreasonably large";
551 } 581 }
552 582
553 for (i = 0; i < 4; i++) 583 for (i = 0; i < 4; i++)
@@ -761,7 +791,7 @@ static bool align_poly(const struct solid *solid, struct grid_square *sq,
761 dist += SQ(solid->vertices[i*3+1] * flip - sq->points[j*2+1] + sq->y); 791 dist += SQ(solid->vertices[i*3+1] * flip - sq->points[j*2+1] + sq->y);
762 dist += SQ(solid->vertices[i*3+2] - zmin); 792 dist += SQ(solid->vertices[i*3+2] - zmin);
763 793
764 if (dist < 0.1) { 794 if (dist < 0.1F) {
765 matches++; 795 matches++;
766 index = i; 796 index = i;
767 } 797 }
@@ -811,7 +841,7 @@ static struct solid *transform_poly(const struct solid *solid, bool flip,
811 */ 841 */
812 vx = ret->vertices[key1*3+0] - ret->vertices[key0*3+0]; 842 vx = ret->vertices[key1*3+0] - ret->vertices[key0*3+0];
813 vy = ret->vertices[key1*3+1] - ret->vertices[key0*3+1]; 843 vy = ret->vertices[key1*3+1] - ret->vertices[key0*3+1];
814 assert(APPROXEQ(vx*vx + vy*vy, 1.0)); 844 assert(APPROXEQ(vx*vx + vy*vy, 1.0F));
815 845
816 vmatrix[0] = vx; vmatrix[3] = vy; vmatrix[6] = 0; 846 vmatrix[0] = vx; vmatrix[3] = vy; vmatrix[6] = 0;
817 vmatrix[1] = -vy; vmatrix[4] = vx; vmatrix[7] = 0; 847 vmatrix[1] = -vy; vmatrix[4] = vx; vmatrix[7] = 0;
@@ -999,22 +1029,6 @@ static void free_game(game_state *state)
999 sfree(state); 1029 sfree(state);
1000} 1030}
1001 1031
1002static char *solve_game(const game_state *state, const game_state *currstate,
1003 const char *aux, const char **error)
1004{
1005 return NULL;
1006}
1007
1008static bool game_can_format_as_text_now(const game_params *params)
1009{
1010 return true;
1011}
1012
1013static char *game_text_format(const game_state *state)
1014{
1015 return NULL;
1016}
1017
1018static game_ui *new_ui(const game_state *state) 1032static game_ui *new_ui(const game_state *state)
1019{ 1033{
1020 return NULL; 1034 return NULL;
@@ -1024,15 +1038,6 @@ static void free_ui(game_ui *ui)
1024{ 1038{
1025} 1039}
1026 1040
1027static char *encode_ui(const game_ui *ui)
1028{
1029 return NULL;
1030}
1031
1032static void decode_ui(game_ui *ui, const char *encoding)
1033{
1034}
1035
1036static void game_changed_state(game_ui *ui, const game_state *oldstate, 1041static void game_changed_state(game_ui *ui, const game_state *oldstate,
1037 const game_state *newstate) 1042 const game_state *newstate)
1038{ 1043{
@@ -1081,11 +1086,11 @@ static int find_move_dest(const game_state *from, int direction,
1081 for (j = 0; j < from->grid->squares[i].npoints; j++) { 1086 for (j = 0; j < from->grid->squares[i].npoints; j++) {
1082 dist = (SQ(from->grid->squares[i].points[j*2] - points[0]) + 1087 dist = (SQ(from->grid->squares[i].points[j*2] - points[0]) +
1083 SQ(from->grid->squares[i].points[j*2+1] - points[1])); 1088 SQ(from->grid->squares[i].points[j*2+1] - points[1]));
1084 if (dist < 0.1) 1089 if (dist < 0.1F)
1085 dkey[match++] = j; 1090 dkey[match++] = j;
1086 dist = (SQ(from->grid->squares[i].points[j*2] - points[2]) + 1091 dist = (SQ(from->grid->squares[i].points[j*2] - points[2]) +
1087 SQ(from->grid->squares[i].points[j*2+1] - points[3])); 1092 SQ(from->grid->squares[i].points[j*2+1] - points[3]));
1088 if (dist < 0.1) 1093 if (dist < 0.1F)
1089 dkey[match++] = j; 1094 dkey[match++] = j;
1090 } 1095 }
1091 1096
@@ -1140,7 +1145,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1140 cy = (int)(state->grid->squares[state->current].y * GRID_SCALE) + ds->oy; 1145 cy = (int)(state->grid->squares[state->current].y * GRID_SCALE) + ds->oy;
1141 1146
1142 if (x == cx && y == cy) 1147 if (x == cx && y == cy)
1143 return NULL; /* clicked in exact centre! */ 1148 return MOVE_NO_EFFECT; /* clicked in exact centre! */
1144 angle = atan2(y - cy, x - cx); 1149 angle = atan2(y - cy, x - cx);
1145 1150
1146 /* 1151 /*
@@ -1191,11 +1196,11 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1191 direction = RIGHT; 1196 direction = RIGHT;
1192 } 1197 }
1193 } else 1198 } else
1194 return NULL; 1199 return MOVE_UNUSED;
1195 1200
1196 mask = state->grid->squares[state->current].directions[direction]; 1201 mask = state->grid->squares[state->current].directions[direction];
1197 if (mask == 0) 1202 if (mask == 0)
1198 return NULL; 1203 return MOVE_NO_EFFECT;
1199 1204
1200 /* 1205 /*
1201 * Translate diagonal directions into orthogonal ones. 1206 * Translate diagonal directions into orthogonal ones.
@@ -1210,14 +1215,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1210 } 1215 }
1211 1216
1212 if (find_move_dest(state, direction, skey, dkey) < 0) 1217 if (find_move_dest(state, direction, skey, dkey) < 0)
1213 return NULL; 1218 return MOVE_NO_EFFECT;
1214 1219
1215 if (direction == LEFT) return dupstr("L"); 1220 if (direction == LEFT) return dupstr("L");
1216 if (direction == RIGHT) return dupstr("R"); 1221 if (direction == RIGHT) return dupstr("R");
1217 if (direction == UP) return dupstr("U"); 1222 if (direction == UP) return dupstr("U");
1218 if (direction == DOWN) return dupstr("D"); 1223 if (direction == DOWN) return dupstr("D");
1219 1224
1220 return NULL; /* should never happen */ 1225 return MOVE_NO_EFFECT; /* should never happen */
1221} 1226}
1222 1227
1223static game_state *execute_move(const game_state *from, const char *move) 1228static game_state *execute_move(const game_state *from, const char *move)
@@ -1484,7 +1489,7 @@ static struct bbox find_bbox(const game_params *params)
1484 ((int)(((bb).d - (bb).u + 2*(solid)->border) * gs)) 1489 ((int)(((bb).d - (bb).u + 2*(solid)->border) * gs))
1485 1490
1486static void game_compute_size(const game_params *params, int tilesize, 1491static void game_compute_size(const game_params *params, int tilesize,
1487 int *x, int *y) 1492 const game_ui *ui, int *x, int *y)
1488{ 1493{
1489 struct bbox bb = find_bbox(params); 1494 struct bbox bb = find_bbox(params);
1490 1495
@@ -1734,19 +1739,6 @@ static int game_status(const game_state *state)
1734 return state->completed ? +1 : 0; 1739 return state->completed ? +1 : 0;
1735} 1740}
1736 1741
1737static bool game_timing_state(const game_state *state, game_ui *ui)
1738{
1739 return true;
1740}
1741
1742static void game_print_size(const game_params *params, float *x, float *y)
1743{
1744}
1745
1746static void game_print(drawing *dr, const game_state *state, int tilesize)
1747{
1748}
1749
1750#ifdef COMBINED 1742#ifdef COMBINED
1751#define thegame cube 1743#define thegame cube
1752#endif 1744#endif
@@ -1766,14 +1758,16 @@ const struct game thegame = {
1766 new_game, 1758 new_game,
1767 dup_game, 1759 dup_game,
1768 free_game, 1760 free_game,
1769 false, solve_game, 1761 false, NULL, /* solve */
1770 false, game_can_format_as_text_now, game_text_format, 1762 false, NULL, NULL, /* can_format_as_text_now, text_format */
1763 NULL, NULL, /* get_prefs, set_prefs */
1771 new_ui, 1764 new_ui,
1772 free_ui, 1765 free_ui,
1773 encode_ui, 1766 NULL, /* encode_ui */
1774 decode_ui, 1767 NULL, /* decode_ui */
1775 NULL, /* game_request_keys */ 1768 NULL, /* game_request_keys */
1776 game_changed_state, 1769 game_changed_state,
1770 NULL, /* current_key_label */
1777 interpret_move, 1771 interpret_move,
1778 execute_move, 1772 execute_move,
1779 PREFERRED_GRID_SCALE, game_compute_size, game_set_size, 1773 PREFERRED_GRID_SCALE, game_compute_size, game_set_size,
@@ -1785,8 +1779,8 @@ const struct game thegame = {
1785 game_flash_length, 1779 game_flash_length,
1786 game_get_cursor_location, 1780 game_get_cursor_location,
1787 game_status, 1781 game_status,
1788 false, false, game_print_size, game_print, 1782 false, false, NULL, NULL, /* print_size, print */
1789 true, /* wants_statusbar */ 1783 true, /* wants_statusbar */
1790 false, game_timing_state, 1784 false, NULL, /* timing_state */
1791 0, /* flags */ 1785 0, /* flags */
1792}; 1786};
diff --git a/apps/plugins/puzzles/src/devel.but b/apps/plugins/puzzles/src/devel.but
deleted file mode 100644
index 7521596df9..0000000000
--- a/apps/plugins/puzzles/src/devel.but
+++ /dev/null
@@ -1,5061 +0,0 @@
1\cfg{text-indent}{0}
2\cfg{text-width}{72}
3\cfg{text-title-align}{left}
4\cfg{text-chapter-align}{left}
5\cfg{text-chapter-numeric}{true}
6\cfg{text-chapter-suffix}{. }
7\cfg{text-chapter-underline}{-}
8\cfg{text-section-align}{0}{left}
9\cfg{text-section-numeric}{0}{true}
10\cfg{text-section-suffix}{0}{. }
11\cfg{text-section-underline}{0}{-}
12\cfg{text-section-align}{1}{left}
13\cfg{text-section-numeric}{1}{true}
14\cfg{text-section-suffix}{1}{. }
15\cfg{text-section-underline}{1}{-}
16\cfg{text-versionid}{0}
17
18\cfg{html-contents-filename}{index.html}
19\cfg{html-template-filename}{%k.html}
20\cfg{html-index-filename}{docindex.html}
21\cfg{html-leaf-level}{1}
22\cfg{html-contents-depth-0}{1}
23\cfg{html-contents-depth-1}{3}
24\cfg{html-leaf-contains-contents}{true}
25
26\define{dash} \u2013{-}
27
28\title Developer documentation for Simon Tatham's puzzle collection
29
30This is a guide to the internal structure of Simon Tatham's Portable
31Puzzle Collection (henceforth referred to simply as \q{Puzzles}),
32for use by anyone attempting to implement a new puzzle or port to a
33new platform.
34
35This guide is believed correct as of r6190. Hopefully it will be
36updated along with the code in future, but if not, I've at least
37left this version number in here so you can figure out what's
38changed by tracking commit comments from there onwards.
39
40\C{intro} Introduction
41
42The Puzzles code base is divided into four parts: a set of
43interchangeable front ends, a set of interchangeable back ends, a
44universal \q{middle end} which acts as a buffer between the two, and
45a bunch of miscellaneous utility functions. In the following
46sections I give some general discussion of each of these parts.
47
48\H{intro-frontend} Front end
49
50The front end is the non-portable part of the code: it's the bit
51that you replace completely when you port to a different platform.
52So it's responsible for all system calls, all GUI interaction, and
53anything else platform-specific.
54
55The current front ends in the main code base are for Windows, GTK
56and MacOS X; I also know of a third-party front end for PalmOS.
57
58The front end contains \cw{main()} or the local platform's
59equivalent. Top-level control over the application's execution flow
60belongs to the front end (it isn't, for example, a set of functions
61called by a universal \cw{main()} somewhere else).
62
63The front end has complete freedom to design the GUI for any given
64port of Puzzles. There is no centralised mechanism for maintaining
65the menu layout, for example. This has a cost in consistency (when I
66\e{do} want the same menu layout on more than one platform, I have
67to edit two pieces of code in parallel every time I make a change),
68but the advantage is that local GUI conventions can be conformed to
69and local constraints adapted to. For example, MacOS X has strict
70human interface guidelines which specify a different menu layout
71from the one I've used on Windows and GTK; there's nothing stopping
72the OS X front end from providing a menu layout consistent with
73those guidelines.
74
75Although the front end is mostly caller rather than the callee in
76its interactions with other parts of the code, it is required to
77implement a small API for other modules to call, mostly of drawing
78functions for games to use when drawing their graphics. The drawing
79API is documented in \k{drawing}; the other miscellaneous front end
80API functions are documented in \k{frontend-api}.
81
82\H{intro-backend} Back end
83
84A \q{back end}, in this collection, is synonymous with a \q{puzzle}.
85Each back end implements a different game.
86
87At the top level, a back end is simply a data structure, containing
88a few constants (flag words, preferred pixel size) and a large
89number of function pointers. Back ends are almost invariably callee
90rather than caller, which means there's a limitation on what a back
91end can do on its own initiative.
92
93The persistent state in a back end is divided into a number of data
94structures, which are used for different purposes and therefore
95likely to be switched around, changed without notice, and otherwise
96updated by the rest of the code. It is important when designing a
97back end to put the right pieces of data into the right structures,
98or standard midend-provided features (such as Undo) may fail to
99work.
100
101The functions and variables provided in the back end data structure
102are documented in \k{backend}.
103
104\H{intro-midend} Middle end
105
106Puzzles has a single and universal \q{middle end}. This code is
107common to all platforms and all games; it sits in between the front
108end and the back end and provides standard functionality everywhere.
109
110People adding new back ends or new front ends should generally not
111need to edit the middle end. On rare occasions there might be a
112change that can be made to the middle end to permit a new game to do
113something not currently anticipated by the middle end's present
114design; however, this is terribly easy to get wrong and should
115probably not be undertaken without consulting the primary maintainer
116(me). Patch submissions containing unannounced mid-end changes will
117be treated on their merits like any other patch; this is just a
118friendly warning that mid-end changes will need quite a lot of
119merits to make them acceptable.
120
121Functionality provided by the mid-end includes:
122
123\b Maintaining a list of game state structures and moving back and
124forth along that list to provide Undo and Redo.
125
126\b Handling timers (for move animations, flashes on completion, and
127in some cases actually timing the game).
128
129\b Handling the container format of game IDs: receiving them,
130picking them apart into parameters, description and/or random seed,
131and so on. The game back end need only handle the individual parts
132of a game ID (encoded parameters and encoded game description);
133everything else is handled centrally by the mid-end.
134
135\b Handling standard keystrokes and menu commands, such as \q{New
136Game}, \q{Restart Game} and \q{Quit}.
137
138\b Pre-processing mouse events so that the game back ends can rely
139on them arriving in a sensible order (no missing button-release
140events, no sudden changes of which button is currently pressed,
141etc).
142
143\b Handling the dialog boxes which ask the user for a game ID.
144
145\b Handling serialisation of entire games (for loading and saving a
146half-finished game to a disk file, or for handling application
147shutdown and restart on platforms such as PalmOS where state is
148expected to be saved).
149
150Thus, there's a lot of work done once by the mid-end so that
151individual back ends don't have to worry about it. All the back end
152has to do is cooperate in ensuring the mid-end can do its work
153properly.
154
155The API of functions provided by the mid-end to be called by the
156front end is documented in \k{midend}.
157
158\H{intro-utils} Miscellaneous utilities
159
160In addition to these three major structural components, the Puzzles
161code also contains a variety of utility modules usable by all of the
162above components. There is a set of functions to provide
163platform-independent random number generation; functions to make
164memory allocation easier; functions which implement a balanced tree
165structure to be used as necessary in complex algorithms; and a few
166other miscellaneous functions. All of these are documented in
167\k{utils}.
168
169\H{intro-structure} Structure of this guide
170
171There are a number of function call interfaces within Puzzles, and
172this guide will discuss each one in a chapter of its own. After
173that, \k{writing} discusses how to design new games, with some
174general design thoughts and tips.
175
176\C{backend} Interface to the back end
177
178This chapter gives a detailed discussion of the interface that each
179back end must implement.
180
181At the top level, each back end source file exports a single global
182symbol, which is a \c{const struct game} containing a large number
183of function pointers and a small amount of constant data. This
184structure is called by different names depending on what kind of
185platform the puzzle set is being compiled on:
186
187\b On platforms such as Windows and GTK, which build a separate
188binary for each puzzle, the game structure in every back end has the
189same name, \cq{thegame}; the front end refers directly to this name,
190so that compiling the same front end module against a different back
191end module builds a different puzzle.
192
193\b On platforms such as MacOS X and PalmOS, which build all the
194puzzles into a single monolithic binary, the game structure in each
195back end must have a different name, and there's a helper module
196\c{list.c} (constructed automatically by the same Perl script that
197builds the \cw{Makefile}s) which contains a complete list of those
198game structures.
199
200On the latter type of platform, source files may assume that the
201preprocessor symbol \c{COMBINED} has been defined. Thus, the usual
202code to declare the game structure looks something like this:
203
204\c #ifdef COMBINED
205\c #define thegame net /* or whatever this game is called */
206\e iii iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
207\c #endif
208\c
209\c const struct game thegame = {
210\c /* lots of structure initialisation in here */
211\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
212\c };
213
214Game back ends must also internally define a number of data
215structures, for storing their various persistent state. This chapter
216will first discuss the nature and use of those structures, and then
217go on to give details of every element of the game structure.
218
219\H{backend-structs} Data structures
220
221Each game is required to define four separate data structures. This
222section discusses each one and suggests what sorts of things need to
223be put in it.
224
225\S{backend-game-params} \c{game_params}
226
227The \c{game_params} structure contains anything which affects the
228automatic generation of new puzzles. So if puzzle generation is
229parametrised in any way, those parameters need to be stored in
230\c{game_params}.
231
232Most puzzles currently in this collection are played on a grid of
233squares, meaning that the most obvious parameter is the grid size.
234Many puzzles have additional parameters; for example, Mines allows
235you to control the number of mines in the grid independently of its
236size, Net can be wrapping or non-wrapping, Solo has difficulty
237levels and symmetry settings, and so on.
238
239A simple rule for deciding whether a data item needs to go in
240\c{game_params} is: would the user expect to be able to control this
241data item from either the preset-game-types menu or the \q{Custom}
242game type configuration? If so, it's part of \c{game_params}.
243
244\c{game_params} structures are permitted to contain pointers to
245subsidiary data if they need to. The back end is required to provide
246functions to create and destroy \c{game_params}, and those functions
247can allocate and free additional memory if necessary. (It has not
248yet been necessary to do this in any puzzle so far, but the
249capability is there just in case.)
250
251\c{game_params} is also the only structure which the game's
252\cw{compute_size()} function may refer to; this means that any
253aspect of the game which affects the size of the window it needs to
254be drawn in must be stored in \c{game_params}. In particular, this
255imposes the fundamental limitation that random game generation may
256not have a random effect on the window size: game generation
257algorithms are constrained to work by starting from the grid size
258rather than generating it as an emergent phenomenon. (Although this
259is a restriction in theory, it has not yet seemed to be a problem.)
260
261\S{backend-game-state} \c{game_state}
262
263While the user is actually playing a puzzle, the \c{game_state}
264structure stores all the data corresponding to the current state of
265play.
266
267The mid-end keeps \c{game_state}s in a list, and adds to the list
268every time the player makes a move; the Undo and Redo functions step
269back and forth through that list.
270
271Therefore, a good means of deciding whether a data item needs to go
272in \c{game_state} is: would a player expect that data item to be
273restored on undo? If so, put it in \c{game_state}, and this will
274automatically happen without you having to lift a finger. If not
275\dash for example, the deaths counter in Mines is precisely
276something that does \e{not} want to be reset to its previous state
277on an undo \dash then you might have found a data item that needs to
278go in \c{game_ui} instead.
279
280During play, \c{game_state}s are often passed around without an
281accompanying \c{game_params} structure. Therefore, any information
282in \c{game_params} which is important during play (such as the grid
283size) must be duplicated within the \c{game_state}. One simple
284method of doing this is to have the \c{game_state} structure
285\e{contain} a \c{game_params} structure as one of its members,
286although this isn't obligatory if you prefer to do it another way.
287
288\S{backend-game-drawstate} \c{game_drawstate}
289
290\c{game_drawstate} carries persistent state relating to the current
291graphical contents of the puzzle window. The same \c{game_drawstate}
292is passed to every call to the game redraw function, so that it can
293remember what it has already drawn and what needs redrawing.
294
295A typical use for a \c{game_drawstate} is to have an array mirroring
296the array of grid squares in the \c{game_state}; then every time the
297redraw function was passed a \c{game_state}, it would loop over all
298the squares, and physically redraw any whose description in the
299\c{game_state} (i.e. what the square needs to look like when the
300redraw is completed) did not match its description in the
301\c{game_drawstate} (i.e. what the square currently looks like).
302
303\c{game_drawstate} is occasionally completely torn down and
304reconstructed by the mid-end, if the user somehow forces a full
305redraw. Therefore, no data should be stored in \c{game_drawstate}
306which is \e{not} related to the state of the puzzle window, because
307it might be unexpectedly destroyed.
308
309The back end provides functions to create and destroy
310\c{game_drawstate}, which means it can contain pointers to
311subsidiary allocated data if it needs to. A common thing to want to
312allocate in a \c{game_drawstate} is a \c{blitter}; see
313\k{drawing-blitter} for more on this subject.
314
315\S{backend-game-ui} \c{game_ui}
316
317\c{game_ui} contains whatever doesn't fit into the above three
318structures!
319
320A new \c{game_ui} is created when the user begins playing a new
321instance of a puzzle (i.e. during \q{New Game} or after entering a
322game ID etc). It persists until the user finishes playing that game
323and begins another one (or closes the window); in particular,
324\q{Restart Game} does \e{not} destroy the \c{game_ui}.
325
326\c{game_ui} is useful for implementing user-interface state which is
327not part of \c{game_state}. Common examples are keyboard control
328(you wouldn't want to have to separately Undo through every cursor
329motion) and mouse dragging. See \k{writing-keyboard-cursor} and
330\k{writing-howto-dragging}, respectively, for more details.
331
332Another use for \c{game_ui} is to store highly persistent data such
333as the Mines death counter. This is conceptually rather different:
334where the Net cursor position was \e{not important enough} to
335preserve for the player to restore by Undo, the Mines death counter
336is \e{too important} to permit the player to revert by Undo!
337
338A final use for \c{game_ui} is to pass information to the redraw
339function about recent changes to the game state. This is used in
340Mines, for example, to indicate whether a requested \q{flash} should
341be a white flash for victory or a red flash for defeat; see
342\k{writing-flash-types}.
343
344\H{backend-simple} Simple data in the back end
345
346In this section I begin to discuss each individual element in the
347back end structure. To begin with, here are some simple
348self-contained data elements.
349
350\S{backend-name} \c{name}
351
352\c const char *name;
353
354This is a simple ASCII string giving the name of the puzzle. This
355name will be used in window titles, in game selection menus on
356monolithic platforms, and anywhere else that the front end needs to
357know the name of a game.
358
359\S{backend-winhelp} \c{winhelp_topic}
360
361\c const char *winhelp_topic;
362
363This member is used on Windows only, to provide online help.
364Although the Windows front end provides a separate binary for each
365puzzle, it has a single monolithic help file; so when a user selects
366\q{Help} from the menu, the program needs to open the help file and
367jump to the chapter describing that particular puzzle.
368
369Therefore, each chapter in \c{puzzles.but} is labelled with a
370\e{help topic} name, similar to this:
371
372\c \cfg{winhelp-topic}{games.net}
373
374And then the corresponding game back end encodes the topic string
375(here \cq{games.net}) in the \c{winhelp_topic} element of the game
376structure.
377
378\H{backend-params} Handling game parameter sets
379
380In this section I present the various functions which handle the
381\c{game_params} structure.
382
383\S{backend-default-params} \cw{default_params()}
384
385\c game_params *(*default_params)(void);
386
387This function allocates a new \c{game_params} structure, fills it
388with the default values, and returns a pointer to it.
389
390\S{backend-fetch-preset} \cw{fetch_preset()}
391
392\c bool (*fetch_preset)(int i, char **name, game_params **params);
393
394This function is one of the two APIs a back end can provide to
395populate the \q{Type} menu, which provides a list of conveniently
396accessible preset parameters for most games.
397
398The function is called with \c{i} equal to the index of the preset
399required (numbering from zero). It returns \cw{false} if that preset
400does not exist (if \c{i} is less than zero or greater than the
401largest preset index). Otherwise, it sets \c{*params} to point at a
402newly allocated \c{game_params} structure containing the preset
403information, sets \c{*name} to point at a newly allocated C string
404containing the preset title (to go on the \q{Type} menu), and
405returns \cw{true}.
406
407If the game does not wish to support any presets at all, this
408function is permitted to return \cw{false} always.
409
410If the game wants to return presets in the form of a hierarchical menu
411instead of a flat list (and, indeed, even if it doesn't), then it may
412set this function pointer to \cw{NULL}, and instead fill in the
413alternative function pointer \cw{preset_menu}
414(\k{backend-preset-menu}).
415
416\S{backend-preset-menu} \cw{preset_menu()}
417
418\c struct preset_menu *(*preset_menu)(void);
419
420This function is the more flexible of the two APIs by which a back end
421can define a collection of preset game parameters.
422
423This function simply returns a complete menu hierarchy, in the form of
424a \c{struct preset_menu} (see \k{midend-get-presets}) and further
425submenus (if it wishes) dangling off it. There are utility functions
426described in \k{utils-presets} to make it easy for the back end to
427construct this menu.
428
429If the game has no need to return a hierarchy of menus, it may instead
430opt to implement the \cw{fetch_preset()} function (see
431\k{backend-fetch-preset}).
432
433The game need not fill in the \c{id} fields in the preset menu
434structures. The mid-end will do that after it receives the structure
435from the game, and before passing it on to the front end.
436
437\S{backend-encode-params} \cw{encode_params()}
438
439\c char *(*encode_params)(const game_params *params, bool full);
440
441The job of this function is to take a \c{game_params}, and encode it
442in a string form for use in game IDs. The return value must be a
443newly allocated C string, and \e{must} not contain a colon or a hash
444(since those characters are used to mark the end of the parameter
445section in a game ID).
446
447Ideally, it should also not contain any other potentially
448controversial punctuation; bear in mind when designing a string
449parameter format that it will probably be used on both Windows and
450Unix command lines under a variety of exciting shell quoting and
451metacharacter rules. Sticking entirely to alphanumerics is the
452safest thing; if you really need punctuation, you can probably get
453away with commas, periods or underscores without causing anybody any
454major inconvenience. If you venture far beyond that, you're likely
455to irritate \e{somebody}.
456
457(At the time of writing this, all existing games have purely
458alphanumeric string parameter formats. Usually these involve a
459letter denoting a parameter, followed optionally by a number giving
460the value of that parameter, with a few mandatory parts at the
461beginning such as numeric width and height separated by \cq{x}.)
462
463If the \c{full} parameter is \cw{true}, this function should encode
464absolutely everything in the \c{game_params}, such that a subsequent
465call to \cw{decode_params()} (\k{backend-decode-params}) will yield
466an identical structure. If \c{full} is \cw{false}, however, you
467should leave out anything which is not necessary to describe a
468\e{specific puzzle instance}, i.e. anything which only takes effect
469when a new puzzle is \e{generated}. For example, the Solo
470\c{game_params} includes a difficulty rating used when constructing
471new puzzles; but a Solo game ID need not explicitly include the
472difficulty, since to describe a puzzle once generated it's
473sufficient to give the grid dimensions and the location and contents
474of the clue squares. (Indeed, one might very easily type in a puzzle
475out of a newspaper without \e{knowing} what its difficulty level is
476in Solo's terminology.) Therefore, Solo's \cw{encode_params()} only
477encodes the difficulty level if \c{full} is set.
478
479\S{backend-decode-params} \cw{decode_params()}
480
481\c void (*decode_params)(game_params *params, char const *string);
482
483This function is the inverse of \cw{encode_params()}
484(\k{backend-encode-params}). It parses the supplied string and fills
485in the supplied \c{game_params} structure. Note that the structure
486will \e{already} have been allocated: this function is not expected
487to create a \e{new} \c{game_params}, but to modify an existing one.
488
489This function can receive a string which only encodes a subset of
490the parameters. The most obvious way in which this can happen is if
491the string was constructed by \cw{encode_params()} with its \c{full}
492parameter set to \cw{FALSE}; however, it could also happen if the
493user typed in a parameter set manually and missed something out. Be
494prepared to deal with a wide range of possibilities.
495
496When dealing with a parameter which is not specified in the input
497string, what to do requires a judgment call on the part of the
498programmer. Sometimes it makes sense to adjust other parameters to
499bring them into line with the new ones. In Mines, for example, you
500would probably not want to keep the same mine count if the user
501dropped the grid size and didn't specify one, since you might easily
502end up with more mines than would actually fit in the grid! On the
503other hand, sometimes it makes sense to leave the parameter alone: a
504Solo player might reasonably expect to be able to configure size and
505difficulty independently of one another.
506
507This function currently has no direct means of returning an error if
508the string cannot be parsed at all. However, the returned
509\c{game_params} is almost always subsequently passed to
510\cw{validate_params()} (\k{backend-validate-params}), so if you
511really want to signal parse errors, you could always have a \c{char
512*} in your parameters structure which stored an error message, and
513have \cw{validate_params()} return it if it is non-\cw{NULL}.
514
515\S{backend-free-params} \cw{free_params()}
516
517\c void (*free_params)(game_params *params);
518
519This function frees a \c{game_params} structure, and any subsidiary
520allocations contained within it.
521
522\S{backend-dup-params} \cw{dup_params()}
523
524\c game_params *(*dup_params)(const game_params *params);
525
526This function allocates a new \c{game_params} structure and
527initialises it with an exact copy of the information in the one
528provided as input. It returns a pointer to the new duplicate.
529
530\S{backend-can-configure} \c{can_configure}
531
532\c bool can_configure;
533
534This data element is set to \cw{true} if the back end supports custom
535parameter configuration via a dialog box. If it is \cw{true}, then the
536functions \cw{configure()} and \cw{custom_params()} are expected to
537work. See \k{backend-configure} and \k{backend-custom-params} for more
538details.
539
540\S{backend-configure} \cw{configure()}
541
542\c config_item *(*configure)(const game_params *params);
543
544This function is called when the user requests a dialog box for
545custom parameter configuration. It returns a newly allocated array
546of \cw{config_item} structures, describing the GUI elements required
547in the dialog box. The array should have one more element than the
548number of controls, since it is terminated with a \cw{C_END} marker
549(see below). Each array element describes the control together with
550its initial value; the front end will modify the value fields and
551return the updated array to \cw{custom_params()} (see
552\k{backend-custom-params}).
553
554The \cw{config_item} structure contains the following elements:
555
556\c char *name;
557\c int type;
558\c union { /* type-specific fields */ } u;
559\e iiiiiiiiiiiiiiiiiiiiiiiiii
560
561\c{name} is an ASCII string giving the textual label for a GUI
562control. It is \e{not} expected to be dynamically allocated.
563
564\c{type} contains one of a small number of \c{enum} values defining
565what type of control is being described. The usable member of the
566union field \c{u} depends on \c{type}. The valid type values are:
567
568\dt \c{C_STRING}
569
570\dd Describes a text input box. (This is also used for numeric
571input. The back end does not bother informing the front end that the
572box is numeric rather than textual; some front ends do have the
573capacity to take this into account, but I decided it wasn't worth
574the extra complexity in the interface.)
575
576\lcont{
577
578For controls of this type, \c{u.string} contains a single field
579
580\c char *sval;
581
582which stores a dynamically allocated string representing the contents
583of the input box.
584
585}
586
587\dt \c{C_BOOLEAN}
588
589\dd Describes a simple checkbox.
590
591\lcont{
592
593For controls of this type, \c{u.boolean} contains a single field
594
595\c int bval;
596
597which is either \cw{TRUE} or \cw{FALSE}.
598
599}
600
601\dt \c{C_CHOICES}
602
603\dd Describes a drop-down list presenting one of a small number of
604fixed choices.
605
606\lcont{
607
608For controls of this type, \c{u.choices} contains two fields:
609
610\c const char *choicenames;
611\c int selected;
612
613\c{choicenames} contains a list of strings describing the choices. The
614very first character of \c{sval} is used as a delimiter when
615processing the rest (so that the strings \cq{:zero:one:two},
616\cq{!zero!one!two} and \cq{xzeroxonextwo} all define a three-element
617list containing \cq{zero}, \cq{one} and \cq{two}).
618
619\c{selected} contains the index of the currently selected element,
620numbering from zero (so that in the above example, 0 would mean
621\cq{zero} and 2 would mean \cq{two}).
622
623Note that \c{u.choices.choicenames} is \e{not} dynamically allocated,
624unlike \c{u.string.sval}.
625
626}
627
628\dt \c{C_END}
629
630\dd Marks the end of the array of \c{config_item}s. There is no
631associated member of the union field \c{u} for this type.
632
633The array returned from this function is expected to have filled in
634the initial values of all the controls according to the input
635\c{game_params} structure.
636
637If the game's \c{can_configure} flag is set to \cw{FALSE}, this
638function is never called and need not do anything at all.
639
640\S{backend-custom-params} \cw{custom_params()}
641
642\c game_params *(*custom_params)(const config_item *cfg);
643
644This function is the counterpart to \cw{configure()}
645(\k{backend-configure}). It receives as input an array of
646\c{config_item}s which was originally created by \cw{configure()},
647but in which the control values have since been changed in
648accordance with user input. Its function is to read the new values
649out of the controls and return a newly allocated \c{game_params}
650structure representing the user's chosen parameter set.
651
652(The front end will have modified the controls' \e{values}, but
653there will still always be the same set of controls, in the same
654order, as provided by \cw{configure()}. It is not necessary to check
655the \c{name} and \c{type} fields, although you could use
656\cw{assert()} if you were feeling energetic.)
657
658This function is not expected to (and indeed \e{must not}) free the
659input \c{config_item} array. (If the parameters fail to validate,
660the dialog box will stay open.)
661
662If the game's \c{can_configure} flag is set to \cw{FALSE}, this
663function is never called and need not do anything at all.
664
665\S{backend-validate-params} \cw{validate_params()}
666
667\c const char *(*validate_params)(const game_params *params,
668\c bool full);
669
670This function takes a \c{game_params} structure as input, and checks
671that the parameters described in it fall within sensible limits. (At
672the very least, grid dimensions should almost certainly be strictly
673positive, for example.)
674
675Return value is \cw{NULL} if no problems were found, or
676alternatively a (non-dynamically-allocated) ASCII string describing
677the error in human-readable form.
678
679If the \c{full} parameter is set, full validation should be
680performed: any set of parameters which would not permit generation
681of a sensible puzzle should be faulted. If \c{full} is \e{not} set,
682the implication is that these parameters are not going to be used
683for \e{generating} a puzzle; so parameters which can't even sensibly
684\e{describe} a valid puzzle should still be faulted, but parameters
685which only affect puzzle generation should not be.
686
687(The \c{full} option makes a difference when parameter combinations
688are non-orthogonal. For example, Net has a boolean option
689controlling whether it enforces a unique solution; it turns out that
690it's impossible to generate a uniquely soluble puzzle with wrapping
691walls and width 2, so \cw{validate_params()} will complain if you
692ask for one. However, if the user had just been playing a unique
693wrapping puzzle of a more sensible width, and then pastes in a game
694ID acquired from somebody else which happens to describe a
695\e{non}-unique wrapping width-2 puzzle, then \cw{validate_params()}
696will be passed a \c{game_params} containing the width and wrapping
697settings from the new game ID and the uniqueness setting from the
698old one. This would be faulted, if it weren't for the fact that
699\c{full} is not set during this call, so Net ignores the
700inconsistency. The resulting \c{game_params} is never subsequently
701used to generate a puzzle; this is a promise made by the mid-end
702when it asks for a non-full validation.)
703
704\H{backend-descs} Handling game descriptions
705
706In this section I present the functions that deal with a textual
707description of a puzzle, i.e. the part that comes after the colon in
708a descriptive-format game ID.
709
710\S{backend-new-desc} \cw{new_desc()}
711
712\c char *(*new_desc)(const game_params *params, random_state *rs,
713\c char **aux, bool interactive);
714
715This function is where all the really hard work gets done. This is
716the function whose job is to randomly generate a new puzzle,
717ensuring solubility and uniqueness as appropriate.
718
719As input it is given a \c{game_params} structure and a random state
720(see \k{utils-random} for the random number API). It must invent a
721puzzle instance, encode it in string form, and return a dynamically
722allocated C string containing that encoding.
723
724Additionally, it may return a second dynamically allocated string in
725\c{*aux}. (If it doesn't want to, then it can leave that parameter
726completely alone; it isn't required to set it to \cw{NULL}, although
727doing so is harmless.) That string, if present, will be passed to
728\cw{solve()} (\k{backend-solve}) later on; so if the puzzle is
729generated in such a way that a solution is known, then information
730about that solution can be saved in \c{*aux} for \cw{solve()} to
731use.
732
733The \c{interactive} parameter should be ignored by almost all
734puzzles. Its purpose is to distinguish between generating a puzzle
735within a GUI context for immediate play, and generating a puzzle in
736a command-line context for saving to be played later. The only
737puzzle that currently uses this distinction (and, I fervently hope,
738the only one which will \e{ever} need to use it) is Mines, which
739chooses a random first-click location when generating puzzles
740non-interactively, but which waits for the user to place the first
741click when interactive. If you think you have come up with another
742puzzle which needs to make use of this parameter, please think for
743at least ten minutes about whether there is \e{any} alternative!
744
745Note that game description strings are not required to contain an
746encoding of parameters such as grid size; a game description is
747never separated from the \c{game_params} it was generated with, so
748any information contained in that structure need not be encoded
749again in the game description.
750
751\S{backend-validate-desc} \cw{validate_desc()}
752
753\c const char *(*validate_desc)(const game_params *params,
754\c const char *desc);
755
756This function is given a game description, and its job is to
757validate that it describes a puzzle which makes sense.
758
759To some extent it's up to the user exactly how far they take the
760phrase \q{makes sense}; there are no particularly strict rules about
761how hard the user is permitted to shoot themself in the foot when
762typing in a bogus game description by hand. (For example, Rectangles
763will not verify that the sum of all the numbers in the grid equals
764the grid's area. So a user could enter a puzzle which was provably
765not soluble, and the program wouldn't complain; there just wouldn't
766happen to be any sequence of moves which solved it.)
767
768The one non-negotiable criterion is that any game description which
769makes it through \cw{validate_desc()} \e{must not} subsequently
770cause a crash or an assertion failure when fed to \cw{new_game()}
771and thence to the rest of the back end.
772
773The return value is \cw{NULL} on success, or a
774non-dynamically-allocated C string containing an error message.
775
776\S{backend-new-game} \cw{new_game()}
777
778\c game_state *(*new_game)(midend *me, const game_params *params,
779\c const char *desc);
780
781This function takes a game description as input, together with its
782accompanying \c{game_params}, and constructs a \c{game_state}
783describing the initial state of the puzzle. It returns a newly
784allocated \c{game_state} structure.
785
786Almost all puzzles should ignore the \c{me} parameter. It is
787required by Mines, which needs it for later passing to
788\cw{midend_supersede_game_desc()} (see \k{backend-supersede}) once
789the user has placed the first click. I fervently hope that no other
790puzzle will be awkward enough to require it, so everybody else
791should ignore it. As with the \c{interactive} parameter in
792\cw{new_desc()} (\k{backend-new-desc}), if you think you have a
793reason to need this parameter, please try very hard to think of an
794alternative approach!
795
796\H{backend-states} Handling game states
797
798This section describes the functions which create and destroy
799\c{game_state} structures.
800
801(Well, except \cw{new_game()}, which is in \k{backend-new-game}
802instead of under here; but it deals with game descriptions \e{and}
803game states and it had to go in one section or the other.)
804
805\S{backend-dup-game} \cw{dup_game()}
806
807\c game_state *(*dup_game)(const game_state *state);
808
809This function allocates a new \c{game_state} structure and
810initialises it with an exact copy of the information in the one
811provided as input. It returns a pointer to the new duplicate.
812
813\S{backend-free-game} \cw{free_game()}
814
815\c void (*free_game)(game_state *state);
816
817This function frees a \c{game_state} structure, and any subsidiary
818allocations contained within it.
819
820\H{backend-ui} Handling \c{game_ui}
821
822\S{backend-new-ui} \cw{new_ui()}
823
824\c game_ui *(*new_ui)(const game_state *state);
825
826This function allocates and returns a new \c{game_ui} structure for
827playing a particular puzzle. It is passed a pointer to the initial
828\c{game_state}, in case it needs to refer to that when setting up
829the initial values for the new game.
830
831\S{backend-free-ui} \cw{free_ui()}
832
833\c void (*free_ui)(game_ui *ui);
834
835This function frees a \c{game_ui} structure, and any subsidiary
836allocations contained within it.
837
838\S{backend-encode-ui} \cw{encode_ui()}
839
840\c char *(*encode_ui)(const game_ui *ui);
841
842This function encodes any \e{important} data in a \c{game_ui}
843structure in string form. It is only called when saving a
844half-finished game to a file.
845
846It should be used sparingly. Almost all data in a \c{game_ui} is not
847important enough to save. The location of the keyboard-controlled
848cursor, for example, can be reset to a default position on reloading
849the game without impacting the user experience. If the user should
850somehow manage to save a game while a mouse drag was in progress,
851then discarding that mouse drag would be an outright \e{feature}.
852
853A typical thing that \e{would} be worth encoding in this function is
854the Mines death counter: it's in the \c{game_ui} rather than the
855\c{game_state} because it's too important to allow the user to
856revert it by using Undo, and therefore it's also too important to
857allow the user to revert it by saving and reloading. (Of course, the
858user could edit the save file by hand... But if the user is \e{that}
859determined to cheat, they could just as easily modify the game's
860source.)
861
862\S{backend-decode-ui} \cw{decode_ui()}
863
864\c void (*decode_ui)(game_ui *ui, const char *encoding);
865
866This function parses a string previously output by \cw{encode_ui()},
867and writes the decoded data back into the provided \c{game_ui}
868structure.
869
870\S{backend-changed-state} \cw{changed_state()}
871
872\c void (*changed_state)(game_ui *ui, const game_state *oldstate,
873\c const game_state *newstate);
874
875This function is called by the mid-end whenever the current game
876state changes, for any reason. Those reasons include:
877
878\b a fresh move being made by \cw{interpret_move()} and
879\cw{execute_move()}
880
881\b a solve operation being performed by \cw{solve()} and
882\cw{execute_move()}
883
884\b the user moving back and forth along the undo list by means of
885the Undo and Redo operations
886
887\b the user selecting Restart to go back to the initial game state.
888
889The job of \cw{changed_state()} is to update the \c{game_ui} for
890consistency with the new game state, if any update is necessary. For
891example, Same Game stores data about the currently selected tile
892group in its \c{game_ui}, and this data is intrinsically related to
893the game state it was derived from. So it's very likely to become
894invalid when the game state changes; thus, Same Game's
895\cw{changed_state()} function clears the current selection whenever
896it is called.
897
898When \cw{anim_length()} or \cw{flash_length()} are called, you can
899be sure that there has been a previous call to \cw{changed_state()}.
900So \cw{changed_state()} can set up data in the \c{game_ui} which will
901be read by \cw{anim_length()} and \cw{flash_length()}, and those
902functions will not have to worry about being called without the data
903having been initialised.
904
905\H{backend-moves} Making moves
906
907This section describes the functions which actually make moves in
908the game: that is, the functions which process user input and end up
909producing new \c{game_state}s.
910
911\S{backend-interpret-move} \cw{interpret_move()}
912
913\c char *(*interpret_move)(const game_state *state, game_ui *ui,
914\c const game_drawstate *ds,
915\c int x, int y, int button);
916
917This function receives user input and processes it. Its input
918parameters are the current \c{game_state}, the current \c{game_ui}
919and the current \c{game_drawstate}, plus details of the input event.
920\c{button} is either an ASCII value or a special code (listed below)
921indicating an arrow or function key or a mouse event; when
922\c{button} is a mouse event, \c{x} and \c{y} contain the pixel
923coordinates of the mouse pointer relative to the top left of the
924puzzle's drawing area.
925
926(The pointer to the \c{game_drawstate} is marked \c{const}, because
927\c{interpret_move} should not write to it. The normal use of that
928pointer will be to read the game's tile size parameter in order to
929divide mouse coordinates by it.)
930
931\cw{interpret_move()} may return in three different ways:
932
933\b Returning \cw{NULL} indicates that no action whatsoever occurred
934in response to the input event; the puzzle was not interested in it
935at all.
936
937\b Returning the special value \cw{UI_UPDATE} indicates that the input
938event has resulted in a change being made to the \c{game_ui} which
939will require a redraw of the game window, but that no actual \e{move}
940was made (i.e. no new \c{game_state} needs to be created).
941
942\b Returning anything else indicates that a move was made and that a
943new \c{game_state} must be created. However, instead of actually
944constructing a new \c{game_state} itself, this function is required
945to return a string description of the details of the move. This
946string will be passed to \cw{execute_move()}
947(\k{backend-execute-move}) to actually create the new
948\c{game_state}. (Encoding moves as strings in this way means that
949the mid-end can keep the strings as well as the game states, and the
950strings can be written to disk when saving the game and fed to
951\cw{execute_move()} again on reloading.)
952
953The return value from \cw{interpret_move()} is expected to be
954dynamically allocated if and only if it is not either \cw{NULL}
955\e{or} the special string constant \c{UI_UPDATE}.
956
957After this function is called, the back end is permitted to rely on
958some subsequent operations happening in sequence:
959
960\b \cw{execute_move()} will be called to convert this move
961description into a new \c{game_state}
962
963\b \cw{changed_state()} will be called with the new \c{game_state}.
964
965This means that if \cw{interpret_move()} needs to do updates to the
966\c{game_ui} which are easier to perform by referring to the new
967\c{game_state}, it can safely leave them to be done in
968\cw{changed_state()} and not worry about them failing to happen.
969
970(Note, however, that \cw{execute_move()} may \e{also} be called in
971other circumstances. It is only \cw{interpret_move()} which can rely
972on a subsequent call to \cw{changed_state()}.)
973
974The special key codes supported by this function are:
975
976\dt \cw{LEFT_BUTTON}, \cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}
977
978\dd Indicate that one of the mouse buttons was pressed down.
979
980\dt \cw{LEFT_DRAG}, \cw{MIDDLE_DRAG}, \cw{RIGHT_DRAG}
981
982\dd Indicate that the mouse was moved while one of the mouse buttons
983was still down. The mid-end guarantees that when one of these events
984is received, it will always have been preceded by a button-down
985event (and possibly other drag events) for the same mouse button,
986and no event involving another mouse button will have appeared in
987between.
988
989\dt \cw{LEFT_RELEASE}, \cw{MIDDLE_RELEASE}, \cw{RIGHT_RELEASE}
990
991\dd Indicate that a mouse button was released. The mid-end
992guarantees that when one of these events is received, it will always
993have been preceded by a button-down event (and possibly some drag
994events) for the same mouse button, and no event involving another
995mouse button will have appeared in between.
996
997\dt \cw{CURSOR_UP}, \cw{CURSOR_DOWN}, \cw{CURSOR_LEFT},
998\cw{CURSOR_RIGHT}
999
1000\dd Indicate that an arrow key was pressed.
1001
1002\dt \cw{CURSOR_SELECT}
1003
1004\dd On platforms which have a prominent \q{select} button alongside
1005their cursor keys, indicates that that button was pressed.
1006
1007In addition, there are some modifiers which can be bitwise-ORed into
1008the \c{button} parameter:
1009
1010\dt \cw{MOD_CTRL}, \cw{MOD_SHFT}
1011
1012\dd These indicate that the Control or Shift key was pressed
1013alongside the key. They only apply to the cursor keys, not to mouse
1014buttons or anything else.
1015
1016\dt \cw{MOD_NUM_KEYPAD}
1017
1018\dd This applies to some ASCII values, and indicates that the key
1019code was input via the numeric keypad rather than the main keyboard.
1020Some puzzles may wish to treat this differently (for example, a
1021puzzle might want to use the numeric keypad as an eight-way
1022directional pad), whereas others might not (a game involving numeric
1023input probably just wants to treat the numeric keypad as numbers).
1024
1025\dt \cw{MOD_MASK}
1026
1027\dd This mask is the bitwise OR of all the available modifiers; you
1028can bitwise-AND with \cw{~MOD_MASK} to strip all the modifiers off
1029any input value.
1030
1031\S{backend-execute-move} \cw{execute_move()}
1032
1033\c game_state *(*execute_move)(const game_state *state, char *move);
1034
1035This function takes an input \c{game_state} and a move string as
1036output from \cw{interpret_move()}. It returns a newly allocated
1037\c{game_state} which contains the result of applying the specified
1038move to the input game state.
1039
1040This function may return \cw{NULL} if it cannot parse the move
1041string (and this is definitely preferable to crashing or failing an
1042assertion, since one way this can happen is if loading a corrupt
1043save file). However, it must not return \cw{NULL} for any move
1044string that really was output from \cw{interpret_move()}: this is
1045punishable by assertion failure in the mid-end.
1046
1047\S{backend-can-solve} \c{can_solve}
1048
1049\c bool can_solve;
1050
1051This field is set to \cw{true} if the game's \cw{solve()} function
1052does something. If it's set to \cw{false}, the game will not even
1053offer the \q{Solve} menu option.
1054
1055\S{backend-solve} \cw{solve()}
1056
1057\c char *(*solve)(const game_state *orig, const game_state *curr,
1058\c const char *aux, const char **error);
1059
1060This function is called when the user selects the \q{Solve} option
1061from the menu.
1062
1063It is passed two input game states: \c{orig} is the game state from
1064the very start of the puzzle, and \c{curr} is the current one.
1065(Different games find one or other or both of these convenient.) It
1066is also passed the \c{aux} string saved by \cw{new_desc()}
1067(\k{backend-new-desc}), in case that encodes important information
1068needed to provide the solution.
1069
1070If this function is unable to produce a solution (perhaps, for
1071example, the game has no in-built solver so it can only solve
1072puzzles it invented internally and has an \c{aux} string for) then
1073it may return \cw{NULL}. If it does this, it must also set
1074\c{*error} to an error message to be presented to the user (such as
1075\q{Solution not known for this puzzle}); that error message is not
1076expected to be dynamically allocated.
1077
1078If this function \e{does} produce a solution, it returns a move string
1079suitable for feeding to \cw{execute_move()}
1080(\k{backend-execute-move}). Like a (non-empty) string returned from
1081\cw{interpret_move()}, the returned string should be dynamically
1082allocated.
1083
1084\H{backend-drawing} Drawing the game graphics
1085
1086This section discusses the back end functions that deal with
1087drawing.
1088
1089\S{backend-new-drawstate} \cw{new_drawstate()}
1090
1091\c game_drawstate *(*new_drawstate)(drawing *dr,
1092\c const game_state *state);
1093
1094This function allocates and returns a new \c{game_drawstate}
1095structure for drawing a particular puzzle. It is passed a pointer to
1096a \c{game_state}, in case it needs to refer to that when setting up
1097any initial data.
1098
1099This function may not rely on the puzzle having been newly started;
1100a new draw state can be constructed at any time if the front end
1101requests a forced redraw. For games like Pattern, in which initial
1102game states are much simpler than general ones, this might be
1103important to keep in mind.
1104
1105The parameter \c{dr} is a drawing object (see \k{drawing}) which the
1106function might need to use to allocate blitters. (However, this
1107isn't recommended; it's usually more sensible to wait to allocate a
1108blitter until \cw{set_size()} is called, because that way you can
1109tailor it to the scale at which the puzzle is being drawn.)
1110
1111\S{backend-free-drawstate} \cw{free_drawstate()}
1112
1113\c void (*free_drawstate)(drawing *dr, game_drawstate *ds);
1114
1115This function frees a \c{game_drawstate} structure, and any
1116subsidiary allocations contained within it.
1117
1118The parameter \c{dr} is a drawing object (see \k{drawing}), which
1119might be required if you are freeing a blitter.
1120
1121\S{backend-preferred-tilesize} \c{preferred_tilesize}
1122
1123\c int preferred_tilesize;
1124
1125Each game is required to define a single integer parameter which
1126expresses, in some sense, the scale at which it is drawn. This is
1127described in the APIs as \cq{tilesize}, since most puzzles are on a
1128square (or possibly triangular or hexagonal) grid and hence a
1129sensible interpretation of this parameter is to define it as the
1130size of one grid tile in pixels; however, there's no actual
1131requirement that the \q{tile size} be proportional to the game
1132window size. Window size is required to increase monotonically with
1133\q{tile size}, however.
1134
1135The data element \c{preferred_tilesize} indicates the tile size
1136which should be used in the absence of a good reason to do otherwise
1137(such as the screen being too small, or the user explicitly
1138requesting a resize if that ever gets implemented).
1139
1140\S{backend-compute-size} \cw{compute_size()}
1141
1142\c void (*compute_size)(const game_params *params, int tilesize,
1143\c int *x, int *y);
1144
1145This function is passed a \c{game_params} structure and a tile size.
1146It returns, in \c{*x} and \c{*y}, the size in pixels of the drawing
1147area that would be required to render a puzzle with those parameters
1148at that tile size.
1149
1150\S{backend-set-size} \cw{set_size()}
1151
1152\c void (*set_size)(drawing *dr, game_drawstate *ds,
1153\c const game_params *params, int tilesize);
1154
1155This function is responsible for setting up a \c{game_drawstate} to
1156draw at a given tile size. Typically this will simply involve
1157copying the supplied \c{tilesize} parameter into a \c{tilesize}
1158field inside the draw state; for some more complex games it might
1159also involve setting up other dimension fields, or possibly
1160allocating a blitter (see \k{drawing-blitter}).
1161
1162The parameter \c{dr} is a drawing object (see \k{drawing}), which is
1163required if a blitter needs to be allocated.
1164
1165Back ends may assume (and may enforce by assertion) that this
1166function will be called at most once for any \c{game_drawstate}. If
1167a puzzle needs to be redrawn at a different size, the mid-end will
1168create a fresh drawstate.
1169
1170\S{backend-colours} \cw{colours()}
1171
1172\c float *(*colours)(frontend *fe, int *ncolours);
1173
1174This function is responsible for telling the front end what colours
1175the puzzle will need to draw itself.
1176
1177It returns the number of colours required in \c{*ncolours}, and the
1178return value from the function itself is a dynamically allocated
1179array of three times that many \c{float}s, containing the red, green
1180and blue components of each colour respectively as numbers in the
1181range [0,1].
1182
1183The second parameter passed to this function is a front end handle.
1184The only things it is permitted to do with this handle are to call
1185the front-end function called \cw{frontend_default_colour()} (see
1186\k{frontend-default-colour}) or the utility function called
1187\cw{game_mkhighlight()} (see \k{utils-game-mkhighlight}). (The
1188latter is a wrapper on the former, so front end implementors only
1189need to provide \cw{frontend_default_colour()}.) This allows
1190\cw{colours()} to take local configuration into account when
1191deciding on its own colour allocations. Most games use the front
1192end's default colour as their background, apart from a few which
1193depend on drawing relief highlights so they adjust the background
1194colour if it's too light for highlights to show up against it.
1195
1196Note that the colours returned from this function are for
1197\e{drawing}, not for printing. Printing has an entirely different
1198colour allocation policy.
1199
1200\S{backend-anim-length} \cw{anim_length()}
1201
1202\c float (*anim_length)(const game_state *oldstate,
1203\c const game_state *newstate,
1204\c int dir, game_ui *ui);
1205
1206This function is called when a move is made, undone or redone. It is
1207given the old and the new \c{game_state}, and its job is to decide
1208whether the transition between the two needs to be animated or can
1209be instant.
1210
1211\c{oldstate} is the state that was current until this call;
1212\c{newstate} is the state that will be current after it. \c{dir}
1213specifies the chronological order of those states: if it is
1214positive, then the transition is the result of a move or a redo (and
1215so \c{newstate} is the later of the two moves), whereas if it is
1216negative then the transition is the result of an undo (so that
1217\c{newstate} is the \e{earlier} move).
1218
1219If this function decides the transition should be animated, it
1220returns the desired length of the animation in seconds. If not, it
1221returns zero.
1222
1223State changes as a result of a Restart operation are never animated;
1224the mid-end will handle them internally and never consult this
1225function at all. State changes as a result of Solve operations are
1226also not animated by default, although you can change this for a
1227particular game by setting a flag in \c{flags} (\k{backend-flags}).
1228
1229The function is also passed a pointer to the local \c{game_ui}. It
1230may refer to information in here to help with its decision (see
1231\k{writing-conditional-anim} for an example of this), and/or it may
1232\e{write} information about the nature of the animation which will
1233be read later by \cw{redraw()}.
1234
1235When this function is called, it may rely on \cw{changed_state()}
1236having been called previously, so if \cw{anim_length()} needs to
1237refer to information in the \c{game_ui}, then \cw{changed_state()}
1238is a reliable place to have set that information up.
1239
1240Move animations do not inhibit further input events. If the user
1241continues playing before a move animation is complete, the animation
1242will be abandoned and the display will jump straight to the final
1243state.
1244
1245\S{backend-flash-length} \cw{flash_length()}
1246
1247\c float (*flash_length)(const game_state *oldstate,
1248\c const game_state *newstate,
1249\c int dir, game_ui *ui);
1250
1251This function is called when a move is completed. (\q{Completed}
1252means that not only has the move been made, but any animation which
1253accompanied it has finished.) It decides whether the transition from
1254\c{oldstate} to \c{newstate} merits a \q{flash}.
1255
1256A flash is much like a move animation, but it is \e{not} interrupted
1257by further user interface activity; it runs to completion in
1258parallel with whatever else might be going on on the display. The
1259only thing which will rush a flash to completion is another flash.
1260
1261The purpose of flashes is to indicate that the game has been
1262completed. They were introduced as a separate concept from move
1263animations because of Net: the habit of most Net players (and
1264certainly me) is to rotate a tile into place and immediately lock
1265it, then move on to another tile. When you make your last move, at
1266the instant the final tile is rotated into place the screen starts
1267to flash to indicate victory \dash but if you then press the lock
1268button out of habit, then the move animation is cancelled, and the
1269victory flash does not complete. (And if you \e{don't} press the
1270lock button, the completed grid will look untidy because there will
1271be one unlocked square.) Therefore, I introduced a specific concept
1272of a \q{flash} which is separate from a move animation and can
1273proceed in parallel with move animations and any other display
1274activity, so that the victory flash in Net is not cancelled by that
1275final locking move.
1276
1277The input parameters to \cw{flash_length()} are exactly the same as
1278the ones to \cw{anim_length()}.
1279
1280Just like \cw{anim_length()}, when this function is called, it may
1281rely on \cw{changed_state()} having been called previously, so if it
1282needs to refer to information in the \c{game_ui} then
1283\cw{changed_state()} is a reliable place to have set that
1284information up.
1285
1286(Some games use flashes to indicate defeat as well as victory;
1287Mines, for example, flashes in a different colour when you tread on
1288a mine from the colour it uses when you complete the game. In order
1289to achieve this, its \cw{flash_length()} function has to store a
1290flag in the \c{game_ui} to indicate which flash type is required.)
1291
1292\S{backend-get-cursor-location} \cw{get_cursor_location()}
1293
1294\c void (*get_cursor_location)(const game_ui *ui,
1295\c const game_drawstate *ds,
1296\c const game_state *state,
1297\c const game_params *params,
1298\c int *x, int *y,
1299\c int *w, int *h);
1300
1301This function queries the backend for the rectangular region
1302containing the cursor (in games that have one), or other region of
1303interest.
1304
1305This function is called by only
1306\cw{midend_get_cursor_location()}(\k{midend-get-cursor-location}). Its
1307purpose is to allow front ends to query the location of the backend's
1308cursor. With knowledge of this location, a front end can, for example,
1309ensure that the region of interest remains visible if the puzzle is
1310too big to fit on the screen at once.
1311
1312On returning, \cw{*x}, \cw{*y} should be set to the X and Y
1313coordinates of the upper-left corner of the rectangular region of
1314interest, and \cw{*w} and \cw{*h} should be the width and height of
1315that region, respectively. In the event that a cursor is not visible
1316on screen, this function should return and leave the return parameters
1317untouched \dash the midend will notice this. The backend need not
1318bother checking that \cw{x}, \cw{y}, \cw{w} and \cw{h} are
1319non-\cw{NULL} \dash the midend guarantees that they will not be.
1320
1321Defining what constitutes a \q{region of interest} is left up to the
1322backend. If a game provides a conventional cursor \dash such as Mines,
1323Solo, or any of the other grid-based games \dash the most logical
1324choice is of course the location of the cursor itself. However, in
1325other cases such as Cube or Inertia, there is no \q{cursor} in the
1326conventional sense \dash the player instead controls an object moving
1327around the screen. In these cases, it makes sense to define the region
1328of interest as the bounding box of the player object or another
1329sensible region \dash such as the grid square the player is sitting on
1330in Cube.
1331
1332If a backend does not provide a cursor mechanism at all, the backend
1333is free to provide an empty implementation of this function, or a
1334\cw{NULL} pointer in the \cw{game} structure \dash the midend will
1335notice either of these cases and behave appropriately.
1336
1337\S{backend-status} \cw{status()}
1338
1339\c int (*status)(const game_state *state);
1340
1341This function returns a status value indicating whether the current
1342game is still in play, or has been won, or has been conclusively lost.
1343The mid-end uses this to implement \cw{midend_status()}
1344(\k{midend-status}).
1345
1346The return value should be +1 if the game has been successfully
1347solved. If the game has been lost in a situation where further play is
1348unlikely, the return value should be -1. If neither is true (so play
1349is still ongoing), return zero.
1350
1351Front ends may wish to use a non-zero status as a cue to proactively
1352offer the option of starting a new game. Therefore, back ends should
1353not return -1 if the game has been \e{technically} lost but undoing
1354and continuing is still a realistic possibility.
1355
1356(For instance, games with hidden information such as Guess or Mines
1357might well return a non-zero status whenever they reveal the solution,
1358whether or not the player guessed it correctly, on the grounds that a
1359player would be unlikely to hide the solution and continue playing
1360after the answer was spoiled. On the other hand, games where you can
1361merely get into a dead end such as Same Game or Inertia might choose
1362to return 0 in that situation, on the grounds that the player would
1363quite likely press Undo and carry on playing.)
1364
1365\S{backend-redraw} \cw{redraw()}
1366
1367\c void (*redraw)(drawing *dr, game_drawstate *ds,
1368\c const game_state *oldstate,
1369\c const game_state *newstate,
1370\c int dir, const game_ui *ui,
1371\c float anim_time, float flash_time);
1372
1373This function is responsible for actually drawing the contents of
1374the game window, and for redrawing every time the game state or the
1375\c{game_ui} changes.
1376
1377The parameter \c{dr} is a drawing object which may be passed to the
1378drawing API functions (see \k{drawing} for documentation of the
1379drawing API). This function may not save \c{dr} and use it
1380elsewhere; it must only use it for calling back to the drawing API
1381functions within its own lifetime.
1382
1383\c{ds} is the local \c{game_drawstate}, of course, and \c{ui} is the
1384local \c{game_ui}.
1385
1386\c{newstate} is the semantically-current game state, and is always
1387non-\cw{NULL}. If \c{oldstate} is also non-\cw{NULL}, it means that
1388a move has recently been made and the game is still in the process
1389of displaying an animation linking the old and new states; in this
1390situation, \c{anim_time} will give the length of time (in seconds)
1391that the animation has already been running. If \c{oldstate} is
1392\cw{NULL}, then \c{anim_time} is unused (and will hopefully be set
1393to zero to avoid confusion).
1394
1395\c{flash_time}, if it is is non-zero, denotes that the game is in
1396the middle of a flash, and gives the time since the start of the
1397flash. See \k{backend-flash-length} for general discussion of
1398flashes.
1399
1400The very first time this function is called for a new
1401\c{game_drawstate}, it is expected to redraw the \e{entire} drawing
1402area. Since this often involves drawing visual furniture which is
1403never subsequently altered, it is often simplest to arrange this by
1404having a special \q{first time} flag in the draw state, and
1405resetting it after the first redraw.
1406
1407When this function (or any subfunction) calls the drawing API, it is
1408expected to pass colour indices which were previously defined by the
1409\cw{colours()} function.
1410
1411\H{backend-printing} Printing functions
1412
1413This section discusses the back end functions that deal with
1414printing puzzles out on paper.
1415
1416\S{backend-can-print} \c{can_print}
1417
1418\c bool can_print;
1419
1420This flag is set to \cw{true} if the puzzle is capable of printing
1421itself on paper. (This makes sense for some puzzles, such as Solo,
1422which can be filled in with a pencil. Other puzzles, such as
1423Twiddle, inherently involve moving things around and so would not
1424make sense to print.)
1425
1426If this flag is \cw{false}, then the functions \cw{print_size()}
1427and \cw{print()} will never be called.
1428
1429\S{backend-can-print-in-colour} \c{can_print_in_colour}
1430
1431\c bool can_print_in_colour;
1432
1433This flag is set to \cw{true} if the puzzle is capable of printing
1434itself differently when colour is available. For example, Map can
1435actually print coloured regions in different \e{colours} rather than
1436resorting to cross-hatching.
1437
1438If the \c{can_print} flag is \cw{false}, then this flag will be
1439ignored.
1440
1441\S{backend-print-size} \cw{print_size()}
1442
1443\c void (*print_size)(const game_params *params, float *x, float *y);
1444
1445This function is passed a \c{game_params} structure and a tile size.
1446It returns, in \c{*x} and \c{*y}, the preferred size in
1447\e{millimetres} of that puzzle if it were to be printed out on paper.
1448
1449If the \c{can_print} flag is \cw{FALSE}, this function will never be
1450called.
1451
1452\S{backend-print} \cw{print()}
1453
1454\c void (*print)(drawing *dr, const game_state *state, int tilesize);
1455
1456This function is called when a puzzle is to be printed out on paper.
1457It should use the drawing API functions (see \k{drawing}) to print
1458itself.
1459
1460This function is separate from \cw{redraw()} because it is often
1461very different:
1462
1463\b The printing function may not depend on pixel accuracy, since
1464printer resolution is variable. Draw as if your canvas had infinite
1465resolution.
1466
1467\b The printing function sometimes needs to display things in a
1468completely different style. Net, for example, is very different as
1469an on-screen puzzle and as a printed one.
1470
1471\b The printing function is often much simpler since it has no need
1472to deal with repeated partial redraws.
1473
1474However, there's no reason the printing and redraw functions can't
1475share some code if they want to.
1476
1477When this function (or any subfunction) calls the drawing API, the
1478colour indices it passes should be colours which have been allocated
1479by the \cw{print_*_colour()} functions within this execution of
1480\cw{print()}. This is very different from the fixed small number of
1481colours used in \cw{redraw()}, because printers do not have a
1482limitation on the total number of colours that may be used. Some
1483puzzles' printing functions might wish to allocate only one \q{ink}
1484colour and use it for all drawing; others might wish to allocate
1485\e{more} colours than are used on screen.
1486
1487One possible colour policy worth mentioning specifically is that a
1488puzzle's printing function might want to allocate the \e{same}
1489colour indices as are used by the redraw function, so that code
1490shared between drawing and printing does not have to keep switching
1491its colour indices. In order to do this, the simplest thing is to
1492make use of the fact that colour indices returned from
1493\cw{print_*_colour()} are guaranteed to be in increasing order from
1494zero. So if you have declared an \c{enum} defining three colours
1495\cw{COL_BACKGROUND}, \cw{COL_THIS} and \cw{COL_THAT}, you might then
1496write
1497
1498\c int c;
1499\c c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1500\c c = print_mono_colour(dr, 0); assert(c == COL_THIS);
1501\c c = print_mono_colour(dr, 0); assert(c == COL_THAT);
1502
1503If the \c{can_print} flag is \cw{FALSE}, this function will never be
1504called.
1505
1506\H{backend-misc} Miscellaneous
1507
1508\S{backend-can-format-as-text-ever} \c{can_format_as_text_ever}
1509
1510\c bool can_format_as_text_ever;
1511
1512This field is \cw{true} if the game supports formatting a
1513game state as ASCII text (typically ASCII art) for copying to the
1514clipboard and pasting into other applications. If it is \cw{false},
1515front ends will not offer the \q{Copy} command at all.
1516
1517If this field is \cw{true}, the game does not necessarily have to
1518support text formatting for \e{all} games: e.g. a game which can be
1519played on a square grid or a triangular one might only support copy
1520and paste for the former, because triangular grids in ASCII art are
1521just too difficult.
1522
1523If this field is \cw{false}, the functions
1524\cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now})
1525and \cw{text_format()} (\k{backend-text-format}) are never called.
1526
1527\S{backend-can-format-as-text-now} \c{can_format_as_text_now()}
1528
1529\c bool (*can_format_as_text_now)(const game_params *params);
1530
1531This function is passed a \c{game_params}, and returns \cw{true} if
1532the game can support ASCII text output for this particular game type.
1533If it returns \cw{false}, front ends will grey out or otherwise
1534disable the \q{Copy} command.
1535
1536Games may enable and disable the copy-and-paste function for
1537different game \e{parameters}, but are currently constrained to
1538return the same answer from this function for all game \e{states}
1539sharing the same parameters. In other words, the \q{Copy} function
1540may enable or disable itself when the player changes game preset,
1541but will never change during play of a single game or when another
1542game of exactly the same type is generated.
1543
1544This function should not take into account aspects of the game
1545parameters which are not encoded by \cw{encode_params()}
1546(\k{backend-encode-params}) when the \c{full} parameter is set to
1547\cw{FALSE}. Such parameters will not necessarily match up between a
1548call to this function and a subsequent call to \cw{text_format()}
1549itself. (For instance, game \e{difficulty} should not affect whether
1550the game can be copied to the clipboard. Only the actual visible
1551\e{shape} of the game can affect that.)
1552
1553\S{backend-text-format} \cw{text_format()}
1554
1555\c char *(*text_format)(const game_state *state);
1556
1557This function is passed a \c{game_state}, and returns a newly
1558allocated C string containing an ASCII representation of that game
1559state. It is used to implement the \q{Copy} operation in many front
1560ends.
1561
1562This function will only ever be called if the back end field
1563\c{can_format_as_text_ever} (\k{backend-can-format-as-text-ever}) is
1564\cw{TRUE} \e{and} the function \cw{can_format_as_text_now()}
1565(\k{backend-can-format-as-text-now}) has returned \cw{TRUE} for the
1566currently selected game parameters.
1567
1568The returned string may contain line endings (and will probably want
1569to), using the normal C internal \cq{\\n} convention. For
1570consistency between puzzles, all multi-line textual puzzle
1571representations should \e{end} with a newline as well as containing
1572them internally. (There are currently no puzzles which have a
1573one-line ASCII representation, so there's no precedent yet for
1574whether that should come with a newline or not.)
1575
1576\S{backend-wants-statusbar} \cw{wants_statusbar}
1577
1578\c bool wants_statusbar;
1579
1580This field is set to \cw{true} if the puzzle has a use for a textual
1581status line (to display score, completion status, currently active
1582tiles, etc).
1583
1584\S{backend-is-timed} \c{is_timed}
1585
1586\c bool is_timed;
1587
1588This field is \cw{true} if the puzzle is time-critical. If
1589so, the mid-end will maintain a game timer while the user plays.
1590
1591If this field is \cw{false}, then \cw{timing_state()} will never be
1592called and need not do anything.
1593
1594\S{backend-timing-state} \cw{timing_state()}
1595
1596\c bool (*timing_state)(const game_state *state, game_ui *ui);
1597
1598This function is passed the current \c{game_state} and the local
1599\c{game_ui}; it returns \cw{true} if the game timer should currently
1600be running.
1601
1602A typical use for the \c{game_ui} in this function is to note when
1603the game was first completed (by setting a flag in
1604\cw{changed_state()} \dash see \k{backend-changed-state}), and
1605freeze the timer thereafter so that the user can undo back through
1606their solution process without altering their time.
1607
1608\S{backend-request-keys} \cw{request_keys()}
1609
1610\c key_label *(*request_keys)(const game_params *params, int *nkeys);
1611
1612This function returns a dynamically allocated array of \cw{key_label}
1613items containing the buttons the back end deems absolutely
1614\e{necessary} for gameplay, not an exhaustive list of every button the
1615back end could accept. For example, Keen only returns the digits up to
1616the game size and the backspace character, \cw{\\b}, even though it
1617\e{could} accept \cw{M}, as only these buttons are actually needed to
1618play the game. Each \cw{key_label} item contains the following fields:
1619
1620\c struct key_label {
1621\c const char *label; /* label for frontend use */
1622\c int button; /* button to pass to midend */
1623\c } key_label;
1624
1625The \cw{label} field of this structure can (and often will) be set by
1626the backend to \cw{NULL}, in which case the midend will instead call
1627\c{button2label()} (\k{utils-button2label}) and fill in a generic
1628label. The \cw{button} field is the associated code that can be passed
1629to the midend when the frontend deems appropriate.
1630
1631The backend should set \cw{*nkeys} to the number of elements in the
1632returned array.
1633
1634The field for this function pointer in the \cw{game} structure might
1635be set to \cw{NULL} (and indeed it is for the majority of the games)
1636to indicate that no additional buttons (apart from the cursor keys)
1637are required to play the game.
1638
1639This function should not be called directly by frontends. Instead,
1640frontends should use \cw{midend_request_keys()}
1641(\k{midend-request-keys}).
1642
1643\S{backend-flags} \c{flags}
1644
1645\c int flags;
1646
1647This field contains miscellaneous per-backend flags. It consists of
1648the bitwise OR of some combination of the following:
1649
1650\dt \cw{BUTTON_BEATS(x,y)}
1651
1652\dd Given any \cw{x} and \cw{y} from the set \{\cw{LEFT_BUTTON},
1653\cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}\}, this macro evaluates to a
1654bit flag which indicates that when buttons \cw{x} and \cw{y} are
1655both pressed simultaneously, the mid-end should consider \cw{x} to
1656have priority. (In the absence of any such flags, the mid-end will
1657always consider the most recently pressed button to have priority.)
1658
1659\dt \cw{SOLVE_ANIMATES}
1660
1661\dd This flag indicates that moves generated by \cw{solve()}
1662(\k{backend-solve}) are candidates for animation just like any other
1663move. For most games, solve moves should not be animated, so the
1664mid-end doesn't even bother calling \cw{anim_length()}
1665(\k{backend-anim-length}), thus saving some special-case code in
1666each game. On the rare occasion that animated solve moves are
1667actually required, you can set this flag.
1668
1669\dt \cw{REQUIRE_RBUTTON}
1670
1671\dd This flag indicates that the puzzle cannot be usefully played
1672without the use of mouse buttons other than the left one. On some
1673PDA platforms, this flag is used by the front end to enable
1674right-button emulation through an appropriate gesture. Note that a
1675puzzle is not required to set this just because it \e{uses} the
1676right button, but only if its use of the right button is critical to
1677playing the game. (Slant, for example, uses the right button to
1678cycle through the three square states in the opposite order from the
1679left button, and hence can manage fine without it.)
1680
1681\dt \cw{REQUIRE_NUMPAD}
1682
1683\dd This flag indicates that the puzzle cannot be usefully played
1684without the use of number-key input. On some PDA platforms it causes
1685an emulated number pad to appear on the screen. Similarly to
1686\cw{REQUIRE_RBUTTON}, a puzzle need not specify this simply if its
1687use of the number keys is not critical.
1688
1689\H{backend-initiative} Things a back end may do on its own initiative
1690
1691This section describes a couple of things that a back end may choose
1692to do by calling functions elsewhere in the program, which would not
1693otherwise be obvious.
1694
1695\S{backend-newrs} Create a random state
1696
1697If a back end needs random numbers at some point during normal play,
1698it can create a fresh \c{random_state} by first calling
1699\c{get_random_seed} (\k{frontend-get-random-seed}) and then passing
1700the returned seed data to \cw{random_new()}.
1701
1702This is likely not to be what you want. If a puzzle needs randomness
1703in the middle of play, it's likely to be more sensible to store some
1704sort of random state within the \c{game_state}, so that the random
1705numbers are tied to the particular game state and hence the player
1706can't simply keep undoing their move until they get numbers they
1707like better.
1708
1709This facility is currently used only in Net, to implement the
1710\q{jumble} command, which sets every unlocked tile to a new random
1711orientation. This randomness \e{is} a reasonable use of the feature,
1712because it's non-adversarial \dash there's no advantage to the user
1713in getting different random numbers.
1714
1715\S{backend-supersede} Supersede its own game description
1716
1717In response to a move, a back end is (reluctantly) permitted to call
1718\cw{midend_supersede_game_desc()}:
1719
1720\c void midend_supersede_game_desc(midend *me,
1721\c char *desc, char *privdesc);
1722
1723When the user selects \q{New Game}, the mid-end calls
1724\cw{new_desc()} (\k{backend-new-desc}) to get a new game
1725description, and (as well as using that to generate an initial game
1726state) stores it for the save file and for telling to the user. The
1727function above overwrites that game description, and also splits it
1728in two. \c{desc} becomes the new game description which is provided
1729to the user on request, and is also the one used to construct a new
1730initial game state if the user selects \q{Restart}. \c{privdesc} is
1731a \q{private} game description, used to reconstruct the game's
1732initial state when reloading.
1733
1734The distinction between the two, as well as the need for this
1735function at all, comes from Mines. Mines begins with a blank grid
1736and no idea of where the mines actually are; \cw{new_desc()} does
1737almost no work in interactive mode, and simply returns a string
1738encoding the \c{random_state}. When the user first clicks to open a
1739tile, \e{then} Mines generates the mine positions, in such a way
1740that the game is soluble from that starting point. Then it uses this
1741function to supersede the random-state game description with a
1742proper one. But it needs two: one containing the initial click
1743location (because that's what you want to happen if you restart the
1744game, and also what you want to send to a friend so that they play
1745\e{the same game} as you), and one without the initial click
1746location (because when you save and reload the game, you expect to
1747see the same blank initial state as you had before saving).
1748
1749I should stress again that this function is a horrid hack. Nobody
1750should use it if they're not Mines; if you think you need to use it,
1751think again repeatedly in the hope of finding a better way to do
1752whatever it was you needed to do.
1753
1754\C{drawing} The drawing API
1755
1756The back end function \cw{redraw()} (\k{backend-redraw}) is required
1757to draw the puzzle's graphics on the window's drawing area, or on
1758paper if the puzzle is printable. To do this portably, it is
1759provided with a drawing API allowing it to talk directly to the
1760front end. In this chapter I document that API, both for the benefit
1761of back end authors trying to use it and for front end authors
1762trying to implement it.
1763
1764The drawing API as seen by the back end is a collection of global
1765functions, each of which takes a pointer to a \c{drawing} structure
1766(a \q{drawing object}). These objects are supplied as parameters to
1767the back end's \cw{redraw()} and \cw{print()} functions.
1768
1769In fact these global functions are not implemented directly by the
1770front end; instead, they are implemented centrally in \c{drawing.c}
1771and form a small piece of middleware. The drawing API as supplied by
1772the front end is a structure containing a set of function pointers,
1773plus a \cq{void *} handle which is passed to each of those
1774functions. This enables a single front end to switch between
1775multiple implementations of the drawing API if necessary. For
1776example, the Windows API supplies a printing mechanism integrated
1777into the same GDI which deals with drawing in windows, and therefore
1778the same API implementation can handle both drawing and printing;
1779but on Unix, the most common way for applications to print is by
1780producing PostScript output directly, and although it would be
1781\e{possible} to write a single (say) \cw{draw_rect()} function which
1782checked a global flag to decide whether to do GTK drawing operations
1783or output PostScript to a file, it's much nicer to have two separate
1784functions and switch between them as appropriate.
1785
1786When drawing, the puzzle window is indexed by pixel coordinates,
1787with the top left pixel defined as \cw{(0,0)} and the bottom right
1788pixel \cw{(w-1,h-1)}, where \c{w} and \c{h} are the width and height
1789values returned by the back end function \cw{compute_size()}
1790(\k{backend-compute-size}).
1791
1792When printing, the puzzle's print area is indexed in exactly the
1793same way (with an arbitrary tile size provided by the printing
1794module \c{printing.c}), to facilitate sharing of code between the
1795drawing and printing routines. However, when printing, puzzles may
1796no longer assume that the coordinate unit has any relationship to a
1797pixel; the printer's actual resolution might very well not even be
1798known at print time, so the coordinate unit might be smaller or
1799larger than a pixel. Puzzles' print functions should restrict
1800themselves to drawing geometric shapes rather than fiddly pixel
1801manipulation.
1802
1803\e{Puzzles' redraw functions may assume that the surface they draw
1804on is persistent}. It is the responsibility of every front end to
1805preserve the puzzle's window contents in the face of GUI window
1806expose issues and similar. It is not permissible to request that the
1807back end redraw any part of a window that it has already drawn,
1808unless something has actually changed as a result of making moves in
1809the puzzle.
1810
1811Most front ends accomplish this by having the drawing routines draw
1812on a stored bitmap rather than directly on the window, and copying
1813the bitmap to the window every time a part of the window needs to be
1814redrawn. Therefore, it is vitally important that whenever the back
1815end does any drawing it informs the front end of which parts of the
1816window it has accessed, and hence which parts need repainting. This
1817is done by calling \cw{draw_update()} (\k{drawing-draw-update}).
1818
1819Persistence of old drawing is convenient. However, a puzzle should
1820be very careful about how it updates its drawing area. The problem
1821is that some front ends do anti-aliased drawing: rather than simply
1822choosing between leaving each pixel untouched or painting it a
1823specified colour, an antialiased drawing function will \e{blend} the
1824original and new colours in pixels at a figure's boundary according
1825to the proportion of the pixel occupied by the figure (probably
1826modified by some heuristic fudge factors). All of this produces a
1827smoother appearance for curves and diagonal lines.
1828
1829An unfortunate effect of drawing an anti-aliased figure repeatedly
1830is that the pixels around the figure's boundary come steadily more
1831saturated with \q{ink} and the boundary appears to \q{spread out}.
1832Worse, redrawing a figure in a different colour won't fully paint
1833over the old boundary pixels, so the end result is a rather ugly
1834smudge.
1835
1836A good strategy to avoid unpleasant anti-aliasing artifacts is to
1837identify a number of rectangular areas which need to be redrawn,
1838clear them to the background colour, and then redraw their contents
1839from scratch, being careful all the while not to stray beyond the
1840boundaries of the original rectangles. The \cw{clip()} function
1841(\k{drawing-clip}) comes in very handy here. Games based on a square
1842grid can often do this fairly easily. Other games may need to be
1843somewhat more careful. For example, Loopy's redraw function first
1844identifies portions of the display which need to be updated. Then,
1845if the changes are fairly well localised, it clears and redraws a
1846rectangle containing each changed area. Otherwise, it gives up and
1847redraws the entire grid from scratch.
1848
1849It is possible to avoid clearing to background and redrawing from
1850scratch if one is very careful about which drawing functions one
1851uses: if a function is documented as not anti-aliasing under some
1852circumstances, you can rely on each pixel in a drawing either being
1853left entirely alone or being set to the requested colour, with no
1854blending being performed.
1855
1856In the following sections I first discuss the drawing API as seen by
1857the back end, and then the \e{almost} identical function-pointer
1858form seen by the front end.
1859
1860\H{drawing-backend} Drawing API as seen by the back end
1861
1862This section documents the back-end drawing API, in the form of
1863functions which take a \c{drawing} object as an argument.
1864
1865\S{drawing-draw-rect} \cw{draw_rect()}
1866
1867\c void draw_rect(drawing *dr, int x, int y, int w, int h,
1868\c int colour);
1869
1870Draws a filled rectangle in the puzzle window.
1871
1872\c{x} and \c{y} give the coordinates of the top left pixel of the
1873rectangle. \c{w} and \c{h} give its width and height. Thus, the
1874horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1875inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1876inclusive.
1877
1878\c{colour} is an integer index into the colours array returned by
1879the back end function \cw{colours()} (\k{backend-colours}).
1880
1881There is no separate pixel-plotting function. If you want to plot a
1882single pixel, the approved method is to use \cw{draw_rect()} with
1883width and height set to 1.
1884
1885Unlike many of the other drawing functions, this function is
1886guaranteed to be pixel-perfect: the rectangle will be sharply
1887defined and not anti-aliased or anything like that.
1888
1889This function may be used for both drawing and printing.
1890
1891\S{drawing-draw-rect-outline} \cw{draw_rect_outline()}
1892
1893\c void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
1894\c int colour);
1895
1896Draws an outline rectangle in the puzzle window.
1897
1898\c{x} and \c{y} give the coordinates of the top left pixel of the
1899rectangle. \c{w} and \c{h} give its width and height. Thus, the
1900horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1901inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1902inclusive.
1903
1904\c{colour} is an integer index into the colours array returned by
1905the back end function \cw{colours()} (\k{backend-colours}).
1906
1907From a back end perspective, this function may be considered to be
1908part of the drawing API. However, front ends are not required to
1909implement it, since it is actually implemented centrally (in
1910\cw{misc.c}) as a wrapper on \cw{draw_polygon()}.
1911
1912This function may be used for both drawing and printing.
1913
1914\S{drawing-draw-line} \cw{draw_line()}
1915
1916\c void draw_line(drawing *dr, int x1, int y1, int x2, int y2,
1917\c int colour);
1918
1919Draws a straight line in the puzzle window.
1920
1921\c{x1} and \c{y1} give the coordinates of one end of the line.
1922\c{x2} and \c{y2} give the coordinates of the other end. The line
1923drawn includes both those points.
1924
1925\c{colour} is an integer index into the colours array returned by
1926the back end function \cw{colours()} (\k{backend-colours}).
1927
1928Some platforms may perform anti-aliasing on this function.
1929Therefore, do not assume that you can erase a line by drawing the
1930same line over it in the background colour; anti-aliasing might lead
1931to perceptible ghost artefacts around the vanished line. Horizontal
1932and vertical lines, however, are pixel-perfect and not anti-aliased.
1933
1934This function may be used for both drawing and printing.
1935
1936\S{drawing-draw-polygon} \cw{draw_polygon()}
1937
1938\c void draw_polygon(drawing *dr, int *coords, int npoints,
1939\c int fillcolour, int outlinecolour);
1940
1941Draws an outlined or filled polygon in the puzzle window.
1942
1943\c{coords} is an array of \cw{(2*npoints)} integers, containing the
1944\c{x} and \c{y} coordinates of \c{npoints} vertices.
1945
1946\c{fillcolour} and \c{outlinecolour} are integer indices into the
1947colours array returned by the back end function \cw{colours()}
1948(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1949indicate that the polygon should be outlined only.
1950
1951The polygon defined by the specified list of vertices is first
1952filled in \c{fillcolour}, if specified, and then outlined in
1953\c{outlinecolour}.
1954
1955\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1956(and front ends are permitted to enforce this by assertion). This is
1957because different platforms disagree on whether a filled polygon
1958should include its boundary line or not, so drawing \e{only} a
1959filled polygon would have non-portable effects. If you want your
1960filled polygon not to have a visible outline, you must set
1961\c{outlinecolour} to the same as \c{fillcolour}.
1962
1963Some platforms may perform anti-aliasing on this function.
1964Therefore, do not assume that you can erase a polygon by drawing the
1965same polygon over it in the background colour. Also, be prepared for
1966the polygon to extend a pixel beyond its obvious bounding box as a
1967result of this; if you really need it not to do this to avoid
1968interfering with other delicate graphics, you should probably use
1969\cw{clip()} (\k{drawing-clip}). You can rely on horizontal and
1970vertical lines not being anti-aliased.
1971
1972This function may be used for both drawing and printing.
1973
1974\S{drawing-draw-circle} \cw{draw_circle()}
1975
1976\c void draw_circle(drawing *dr, int cx, int cy, int radius,
1977\c int fillcolour, int outlinecolour);
1978
1979Draws an outlined or filled circle in the puzzle window.
1980
1981\c{cx} and \c{cy} give the coordinates of the centre of the circle.
1982\c{radius} gives its radius. The total horizontal pixel extent of
1983the circle is from \c{cx-radius+1} to \c{cx+radius-1} inclusive, and
1984the vertical extent similarly around \c{cy}.
1985
1986\c{fillcolour} and \c{outlinecolour} are integer indices into the
1987colours array returned by the back end function \cw{colours()}
1988(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1989indicate that the circle should be outlined only.
1990
1991The circle is first filled in \c{fillcolour}, if specified, and then
1992outlined in \c{outlinecolour}.
1993
1994\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1995(and front ends are permitted to enforce this by assertion). This is
1996because different platforms disagree on whether a filled circle
1997should include its boundary line or not, so drawing \e{only} a
1998filled circle would have non-portable effects. If you want your
1999filled circle not to have a visible outline, you must set
2000\c{outlinecolour} to the same as \c{fillcolour}.
2001
2002Some platforms may perform anti-aliasing on this function.
2003Therefore, do not assume that you can erase a circle by drawing the
2004same circle over it in the background colour. Also, be prepared for
2005the circle to extend a pixel beyond its obvious bounding box as a
2006result of this; if you really need it not to do this to avoid
2007interfering with other delicate graphics, you should probably use
2008\cw{clip()} (\k{drawing-clip}).
2009
2010This function may be used for both drawing and printing.
2011
2012\S{drawing-draw-thick-line} \cw{draw_thick_line()}
2013
2014\c void draw_thick_line(drawing *dr, float thickness,
2015\c float x1, float y1, float x2, float y2,
2016\c int colour)
2017
2018Draws a line in the puzzle window, giving control over the line's
2019thickness.
2020
2021\c{x1} and \c{y1} give the coordinates of one end of the line.
2022\c{x2} and \c{y2} give the coordinates of the other end.
2023\c{thickness} gives the thickness of the line, in pixels.
2024
2025Note that the coordinates and thickness are floating-point: the
2026continuous coordinate system is in effect here. It's important to
2027be able to address points with better-than-pixel precision in this
2028case, because one can't otherwise properly express the endpoints of
2029lines with both odd and even thicknesses.
2030
2031Some platforms may perform anti-aliasing on this function. The
2032precise pixels affected by a thick-line drawing operation may vary
2033between platforms, and no particular guarantees are provided.
2034Indeed, even horizontal or vertical lines may be anti-aliased.
2035
2036This function may be used for both drawing and printing.
2037
2038If the specified thickness is less than 1.0, 1.0 is used.
2039This ensures that thin lines are visible even at small scales.
2040
2041\S{drawing-draw-text} \cw{draw_text()}
2042
2043\c void draw_text(drawing *dr, int x, int y, int fonttype,
2044\c int fontsize, int align, int colour,
2045\c const char *text);
2046
2047Draws text in the puzzle window.
2048
2049\c{x} and \c{y} give the coordinates of a point. The relation of
2050this point to the location of the text is specified by \c{align},
2051which is a bitwise OR of horizontal and vertical alignment flags:
2052
2053\dt \cw{ALIGN_VNORMAL}
2054
2055\dd Indicates that \c{y} is aligned with the baseline of the text.
2056
2057\dt \cw{ALIGN_VCENTRE}
2058
2059\dd Indicates that \c{y} is aligned with the vertical centre of the
2060text. (In fact, it's aligned with the vertical centre of normal
2061\e{capitalised} text: displaying two pieces of text with
2062\cw{ALIGN_VCENTRE} at the same \cw{y}-coordinate will cause their
2063baselines to be aligned with one another, even if one is an ascender
2064and the other a descender.)
2065
2066\dt \cw{ALIGN_HLEFT}
2067
2068\dd Indicates that \c{x} is aligned with the left-hand end of the
2069text.
2070
2071\dt \cw{ALIGN_HCENTRE}
2072
2073\dd Indicates that \c{x} is aligned with the horizontal centre of
2074the text.
2075
2076\dt \cw{ALIGN_HRIGHT}
2077
2078\dd Indicates that \c{x} is aligned with the right-hand end of the
2079text.
2080
2081\c{fonttype} is either \cw{FONT_FIXED} or \cw{FONT_VARIABLE}, for a
2082monospaced or proportional font respectively. (No more detail than
2083that may be specified; it would only lead to portability issues
2084between different platforms.)
2085
2086\c{fontsize} is the desired size, in pixels, of the text. This size
2087corresponds to the overall point size of the text, not to any
2088internal dimension such as the cap-height.
2089
2090\c{colour} is an integer index into the colours array returned by
2091the back end function \cw{colours()} (\k{backend-colours}).
2092
2093This function may be used for both drawing and printing.
2094
2095The character set used to encode the text passed to this function is
2096specified \e{by the drawing object}, although it must be a superset
2097of ASCII. If a puzzle wants to display text that is not contained in
2098ASCII, it should use the \cw{text_fallback()} function
2099(\k{drawing-text-fallback}) to query the drawing object for an
2100appropriate representation of the characters it wants.
2101
2102\S{drawing-text-fallback} \cw{text_fallback()}
2103
2104\c char *text_fallback(drawing *dr, const char *const *strings,
2105\c int nstrings);
2106
2107This function is used to request a translation of UTF-8 text into
2108whatever character encoding is expected by the drawing object's
2109implementation of \cw{draw_text()}.
2110
2111The input is a list of strings encoded in UTF-8: \cw{nstrings} gives
2112the number of strings in the list, and \cw{strings[0]},
2113\cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings
2114themselves.
2115
2116The returned string (which is dynamically allocated and must be
2117freed when finished with) is derived from the first string in the
2118list that the drawing object expects to be able to display reliably;
2119it will consist of that string translated into the character set
2120expected by \cw{draw_text()}.
2121
2122Drawing implementations are not required to handle anything outside
2123ASCII, but are permitted to assume that \e{some} string will be
2124successfully translated. So every call to this function must include
2125a string somewhere in the list (presumably the last element) which
2126consists of nothing but ASCII, to be used by any front end which
2127cannot handle anything else.
2128
2129For example, if a puzzle wished to display a string including a
2130multiplication sign (U+00D7 in Unicode, represented by the bytes C3
213197 in UTF-8), it might do something like this:
2132
2133\c static const char *const times_signs[] = { "\xC3\x97", "x" };
2134\c char *times_sign = text_fallback(dr, times_signs, 2);
2135\c sprintf(buffer, "%d%s%d", width, times_sign, height);
2136\c draw_text(dr, x, y, font, size, align, colour, buffer);
2137\c sfree(buffer);
2138
2139which would draw a string with a times sign in the middle on
2140platforms that support it, and fall back to a simple ASCII \cq{x}
2141where there was no alternative.
2142
2143\S{drawing-clip} \cw{clip()}
2144
2145\c void clip(drawing *dr, int x, int y, int w, int h);
2146
2147Establishes a clipping rectangle in the puzzle window.
2148
2149\c{x} and \c{y} give the coordinates of the top left pixel of the
2150clipping rectangle. \c{w} and \c{h} give its width and height. Thus,
2151the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2152inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2153inclusive. (These are exactly the same semantics as
2154\cw{draw_rect()}.)
2155
2156After this call, no drawing operation will affect anything outside
2157the specified rectangle. The effect can be reversed by calling
2158\cw{unclip()} (\k{drawing-unclip}). The clipping rectangle is
2159pixel-perfect: pixels within the rectangle are affected as usual by
2160drawing functions; pixels outside are completely untouched.
2161
2162Back ends should not assume that a clipping rectangle will be
2163automatically cleared up by the front end if it's left lying around;
2164that might work on current front ends, but shouldn't be relied upon.
2165Always explicitly call \cw{unclip()}.
2166
2167This function may be used for both drawing and printing.
2168
2169\S{drawing-unclip} \cw{unclip()}
2170
2171\c void unclip(drawing *dr);
2172
2173Reverts the effect of a previous call to \cw{clip()}. After this
2174call, all drawing operations will be able to affect the entire
2175puzzle window again.
2176
2177This function may be used for both drawing and printing.
2178
2179\S{drawing-draw-update} \cw{draw_update()}
2180
2181\c void draw_update(drawing *dr, int x, int y, int w, int h);
2182
2183Informs the front end that a rectangular portion of the puzzle
2184window has been drawn on and needs to be updated.
2185
2186\c{x} and \c{y} give the coordinates of the top left pixel of the
2187update rectangle. \c{w} and \c{h} give its width and height. Thus,
2188the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2189inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2190inclusive. (These are exactly the same semantics as
2191\cw{draw_rect()}.)
2192
2193The back end redraw function \e{must} call this function to report
2194any changes it has made to the window. Otherwise, those changes may
2195not become immediately visible, and may then appear at an
2196unpredictable subsequent time such as the next time the window is
2197covered and re-exposed.
2198
2199This function is only important when drawing. It may be called when
2200printing as well, but doing so is not compulsory, and has no effect.
2201(So if you have a shared piece of code between the drawing and
2202printing routines, that code may safely call \cw{draw_update()}.)
2203
2204\S{drawing-status-bar} \cw{status_bar()}
2205
2206\c void status_bar(drawing *dr, const char *text);
2207
2208Sets the text in the game's status bar to \c{text}. The text is copied
2209from the supplied buffer, so the caller is free to deallocate or
2210modify the buffer after use.
2211
2212(This function is not exactly a \e{drawing} function, but it shares
2213with the drawing API the property that it may only be called from
2214within the back end redraw function, so this is as good a place as
2215any to document it.)
2216
2217The supplied text is filtered through the mid-end for optional
2218rewriting before being passed on to the front end; the mid-end will
2219prepend the current game time if the game is timed (and may in
2220future perform other rewriting if it seems like a good idea).
2221
2222This function is for drawing only; it must never be called during
2223printing.
2224
2225\S{drawing-blitter} Blitter functions
2226
2227This section describes a group of related functions which save and
2228restore a section of the puzzle window. This is most commonly used
2229to implement user interfaces involving dragging a puzzle element
2230around the window: at the end of each call to \cw{redraw()}, if an
2231object is currently being dragged, the back end saves the window
2232contents under that location and then draws the dragged object, and
2233at the start of the next \cw{redraw()} the first thing it does is to
2234restore the background.
2235
2236The front end defines an opaque type called a \c{blitter}, which is
2237capable of storing a rectangular area of a specified size.
2238
2239Blitter functions are for drawing only; they must never be called
2240during printing.
2241
2242\S2{drawing-blitter-new} \cw{blitter_new()}
2243
2244\c blitter *blitter_new(drawing *dr, int w, int h);
2245
2246Creates a new blitter object which stores a rectangle of size \c{w}
2247by \c{h} pixels. Returns a pointer to the blitter object.
2248
2249Blitter objects are best stored in the \c{game_drawstate}. A good
2250time to create them is in the \cw{set_size()} function
2251(\k{backend-set-size}), since it is at this point that you first
2252know how big a rectangle they will need to save.
2253
2254\S2{drawing-blitter-free} \cw{blitter_free()}
2255
2256\c void blitter_free(drawing *dr, blitter *bl);
2257
2258Disposes of a blitter object. Best called in \cw{free_drawstate()}.
2259(However, check that the blitter object is not \cw{NULL} before
2260attempting to free it; it is possible that a draw state might be
2261created and freed without ever having \cw{set_size()} called on it
2262in between.)
2263
2264\S2{drawing-blitter-save} \cw{blitter_save()}
2265
2266\c void blitter_save(drawing *dr, blitter *bl, int x, int y);
2267
2268This is a true drawing API function, in that it may only be called
2269from within the game redraw routine. It saves a rectangular portion
2270of the puzzle window into the specified blitter object.
2271
2272\c{x} and \c{y} give the coordinates of the top left corner of the
2273saved rectangle. The rectangle's width and height are the ones
2274specified when the blitter object was created.
2275
2276This function is required to cope and do the right thing if \c{x}
2277and \c{y} are out of range. (The right thing probably means saving
2278whatever part of the blitter rectangle overlaps with the visible
2279area of the puzzle window.)
2280
2281\S2{drawing-blitter-load} \cw{blitter_load()}
2282
2283\c void blitter_load(drawing *dr, blitter *bl, int x, int y);
2284
2285This is a true drawing API function, in that it may only be called
2286from within the game redraw routine. It restores a rectangular
2287portion of the puzzle window from the specified blitter object.
2288
2289\c{x} and \c{y} give the coordinates of the top left corner of the
2290rectangle to be restored. The rectangle's width and height are the
2291ones specified when the blitter object was created.
2292
2293Alternatively, you can specify both \c{x} and \c{y} as the special
2294value \cw{BLITTER_FROMSAVED}, in which case the rectangle will be
2295restored to exactly where it was saved from. (This is probably what
2296you want to do almost all the time, if you're using blitters to
2297implement draggable puzzle elements.)
2298
2299This function is required to cope and do the right thing if \c{x}
2300and \c{y} (or the equivalent ones saved in the blitter) are out of
2301range. (The right thing probably means restoring whatever part of
2302the blitter rectangle overlaps with the visible area of the puzzle
2303window.)
2304
2305If this function is called on a blitter which had previously been
2306saved from a partially out-of-range rectangle, then the parts of the
2307saved bitmap which were not visible at save time are undefined. If
2308the blitter is restored to a different position so as to make those
2309parts visible, the effect on the drawing area is undefined.
2310
2311\S{print-mono-colour} \cw{print_mono_colour()}
2312
2313\c int print_mono_colour(drawing *dr, int grey);
2314
2315This function allocates a colour index for a simple monochrome
2316colour during printing.
2317
2318\c{grey} must be 0 or 1. If \c{grey} is 0, the colour returned is
2319black; if \c{grey} is 1, the colour is white.
2320
2321\S{print-grey-colour} \cw{print_grey_colour()}
2322
2323\c int print_grey_colour(drawing *dr, float grey);
2324
2325This function allocates a colour index for a grey-scale colour
2326during printing.
2327
2328\c{grey} may be any number between 0 (black) and 1 (white); for
2329example, 0.5 indicates a medium grey.
2330
2331The chosen colour will be rendered to the limits of the printer's
2332halftoning capability.
2333
2334\S{print-hatched-colour} \cw{print_hatched_colour()}
2335
2336\c int print_hatched_colour(drawing *dr, int hatch);
2337
2338This function allocates a colour index which does not represent a
2339literal \e{colour}. Instead, regions shaded in this colour will be
2340hatched with parallel lines. The \c{hatch} parameter defines what
2341type of hatching should be used in place of this colour:
2342
2343\dt \cw{HATCH_SLASH}
2344
2345\dd This colour will be hatched by lines slanting to the right at 45
2346degrees.
2347
2348\dt \cw{HATCH_BACKSLASH}
2349
2350\dd This colour will be hatched by lines slanting to the left at 45
2351degrees.
2352
2353\dt \cw{HATCH_HORIZ}
2354
2355\dd This colour will be hatched by horizontal lines.
2356
2357\dt \cw{HATCH_VERT}
2358
2359\dd This colour will be hatched by vertical lines.
2360
2361\dt \cw{HATCH_PLUS}
2362
2363\dd This colour will be hatched by criss-crossing horizontal and
2364vertical lines.
2365
2366\dt \cw{HATCH_X}
2367
2368\dd This colour will be hatched by criss-crossing diagonal lines.
2369
2370Colours defined to use hatching may not be used for drawing lines or
2371text; they may only be used for filling areas. That is, they may be
2372used as the \c{fillcolour} parameter to \cw{draw_circle()} and
2373\cw{draw_polygon()}, and as the colour parameter to
2374\cw{draw_rect()}, but may not be used as the \c{outlinecolour}
2375parameter to \cw{draw_circle()} or \cw{draw_polygon()}, or with
2376\cw{draw_line()} or \cw{draw_text()}.
2377
2378\S{print-rgb-mono-colour} \cw{print_rgb_mono_colour()}
2379
2380\c int print_rgb_mono_colour(drawing *dr, float r, float g,
2381\c float b, float grey);
2382
2383This function allocates a colour index for a fully specified RGB
2384colour during printing.
2385
2386\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2387
2388If printing in black and white only, these values will be ignored,
2389and either pure black or pure white will be used instead, according
2390to the \q{grey} parameter. (The fallback colour is the same as the
2391one which would be allocated by \cw{print_mono_colour(grey)}.)
2392
2393\S{print-rgb-grey-colour} \cw{print_rgb_grey_colour()}
2394
2395\c int print_rgb_grey_colour(drawing *dr, float r, float g,
2396\c float b, float grey);
2397
2398This function allocates a colour index for a fully specified RGB
2399colour during printing.
2400
2401\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2402
2403If printing in black and white only, these values will be ignored,
2404and a shade of grey given by the \c{grey} parameter will be used
2405instead. (The fallback colour is the same as the one which would be
2406allocated by \cw{print_grey_colour(grey)}.)
2407
2408\S{print-rgb-hatched-colour} \cw{print_rgb_hatched_colour()}
2409
2410\c int print_rgb_hatched_colour(drawing *dr, float r, float g,
2411\c float b, float hatched);
2412
2413This function allocates a colour index for a fully specified RGB
2414colour during printing.
2415
2416\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2417
2418If printing in black and white only, these values will be ignored,
2419and a form of cross-hatching given by the \c{hatch} parameter will
2420be used instead; see \k{print-hatched-colour} for the possible
2421values of this parameter. (The fallback colour is the same as the
2422one which would be allocated by \cw{print_hatched_colour(hatch)}.)
2423
2424\S{print-line-width} \cw{print_line_width()}
2425
2426\c void print_line_width(drawing *dr, int width);
2427
2428This function is called to set the thickness of lines drawn during
2429printing. It is meaningless in drawing: all lines drawn by
2430\cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} are one
2431pixel in thickness. However, in printing there is no clear
2432definition of a pixel and so line widths must be explicitly
2433specified.
2434
2435The line width is specified in the usual coordinate system. Note,
2436however, that it is a hint only: the central printing system may
2437choose to vary line thicknesses at user request or due to printer
2438capabilities.
2439
2440\S{print-line-dotted} \cw{print_line_dotted()}
2441
2442\c void print_line_dotted(drawing *dr, bool dotted);
2443
2444This function is called to toggle the drawing of dotted lines during
2445printing. It is not supported during drawing.
2446
2447Setting \cq{dotted} to \cw{true} means that future lines drawn by
2448\cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} will be
2449dotted. Setting it to \cw{false} means that they will be solid.
2450
2451Some front ends may impose restrictions on the width of dotted
2452lines. Asking for a dotted line via this front end will override any
2453line width request if the front end requires it.
2454
2455\H{drawing-frontend} The drawing API as implemented by the front end
2456
2457This section describes the drawing API in the function-pointer form
2458in which it is implemented by a front end.
2459
2460(It isn't only platform-specific front ends which implement this
2461API; the platform-independent module \c{ps.c} also provides an
2462implementation of it which outputs PostScript. Thus, any platform
2463which wants to do PS printing can do so with minimum fuss.)
2464
2465The following entries all describe function pointer fields in a
2466structure called \c{drawing_api}. Each of the functions takes a
2467\cq{void *} context pointer, which it should internally cast back to
2468a more useful type. Thus, a drawing \e{object} (\c{drawing *)}
2469suitable for passing to the back end redraw or printing functions
2470is constructed by passing a \c{drawing_api} and a \cq{void *} to the
2471function \cw{drawing_new()} (see \k{drawing-new}).
2472
2473\S{drawingapi-draw-text} \cw{draw_text()}
2474
2475\c void (*draw_text)(void *handle, int x, int y, int fonttype,
2476\c int fontsize, int align, int colour,
2477\c const char *text);
2478
2479This function behaves exactly like the back end \cw{draw_text()}
2480function; see \k{drawing-draw-text}.
2481
2482\S{drawingapi-draw-rect} \cw{draw_rect()}
2483
2484\c void (*draw_rect)(void *handle, int x, int y, int w, int h,
2485\c int colour);
2486
2487This function behaves exactly like the back end \cw{draw_rect()}
2488function; see \k{drawing-draw-rect}.
2489
2490\S{drawingapi-draw-line} \cw{draw_line()}
2491
2492\c void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
2493\c int colour);
2494
2495This function behaves exactly like the back end \cw{draw_line()}
2496function; see \k{drawing-draw-line}.
2497
2498\S{drawingapi-draw-polygon} \cw{draw_polygon()}
2499
2500\c void (*draw_polygon)(void *handle, int *coords, int npoints,
2501\c int fillcolour, int outlinecolour);
2502
2503This function behaves exactly like the back end \cw{draw_polygon()}
2504function; see \k{drawing-draw-polygon}.
2505
2506\S{drawingapi-draw-circle} \cw{draw_circle()}
2507
2508\c void (*draw_circle)(void *handle, int cx, int cy, int radius,
2509\c int fillcolour, int outlinecolour);
2510
2511This function behaves exactly like the back end \cw{draw_circle()}
2512function; see \k{drawing-draw-circle}.
2513
2514\S{drawingapi-draw-thick-line} \cw{draw_thick_line()}
2515
2516\c void draw_thick_line(drawing *dr, float thickness,
2517\c float x1, float y1, float x2, float y2,
2518\c int colour)
2519
2520This function behaves exactly like the back end
2521\cw{draw_thick_line()} function; see \k{drawing-draw-thick-line}.
2522
2523An implementation of this API which doesn't provide high-quality
2524rendering of thick lines is permitted to define this function
2525pointer to be \cw{NULL}. The middleware in \cw{drawing.c} will notice
2526and provide a low-quality alternative using \cw{draw_polygon()}.
2527
2528\S{drawingapi-draw-update} \cw{draw_update()}
2529
2530\c void (*draw_update)(void *handle, int x, int y, int w, int h);
2531
2532This function behaves exactly like the back end \cw{draw_update()}
2533function; see \k{drawing-draw-update}.
2534
2535An implementation of this API which only supports printing is
2536permitted to define this function pointer to be \cw{NULL} rather
2537than bothering to define an empty function. The middleware in
2538\cw{drawing.c} will notice and avoid calling it.
2539
2540\S{drawingapi-clip} \cw{clip()}
2541
2542\c void (*clip)(void *handle, int x, int y, int w, int h);
2543
2544This function behaves exactly like the back end \cw{clip()}
2545function; see \k{drawing-clip}.
2546
2547\S{drawingapi-unclip} \cw{unclip()}
2548
2549\c void (*unclip)(void *handle);
2550
2551This function behaves exactly like the back end \cw{unclip()}
2552function; see \k{drawing-unclip}.
2553
2554\S{drawingapi-start-draw} \cw{start_draw()}
2555
2556\c void (*start_draw)(void *handle);
2557
2558This function is called at the start of drawing. It allows the front
2559end to initialise any temporary data required to draw with, such as
2560device contexts.
2561
2562Implementations of this API which do not provide drawing services
2563may define this function pointer to be \cw{NULL}; it will never be
2564called unless drawing is attempted.
2565
2566\S{drawingapi-end-draw} \cw{end_draw()}
2567
2568\c void (*end_draw)(void *handle);
2569
2570This function is called at the end of drawing. It allows the front
2571end to do cleanup tasks such as deallocating device contexts and
2572scheduling appropriate GUI redraw events.
2573
2574Implementations of this API which do not provide drawing services
2575may define this function pointer to be \cw{NULL}; it will never be
2576called unless drawing is attempted.
2577
2578\S{drawingapi-status-bar} \cw{status_bar()}
2579
2580\c void (*status_bar)(void *handle, const char *text);
2581
2582This function behaves exactly like the back end \cw{status_bar()}
2583function; see \k{drawing-status-bar}.
2584
2585Front ends implementing this function need not worry about it being
2586called repeatedly with the same text; the middleware code in
2587\cw{status_bar()} will take care of this.
2588
2589Implementations of this API which do not provide drawing services
2590may define this function pointer to be \cw{NULL}; it will never be
2591called unless drawing is attempted.
2592
2593\S{drawingapi-blitter-new} \cw{blitter_new()}
2594
2595\c blitter *(*blitter_new)(void *handle, int w, int h);
2596
2597This function behaves exactly like the back end \cw{blitter_new()}
2598function; see \k{drawing-blitter-new}.
2599
2600Implementations of this API which do not provide drawing services
2601may define this function pointer to be \cw{NULL}; it will never be
2602called unless drawing is attempted.
2603
2604\S{drawingapi-blitter-free} \cw{blitter_free()}
2605
2606\c void (*blitter_free)(void *handle, blitter *bl);
2607
2608This function behaves exactly like the back end \cw{blitter_free()}
2609function; see \k{drawing-blitter-free}.
2610
2611Implementations of this API which do not provide drawing services
2612may define this function pointer to be \cw{NULL}; it will never be
2613called unless drawing is attempted.
2614
2615\S{drawingapi-blitter-save} \cw{blitter_save()}
2616
2617\c void (*blitter_save)(void *handle, blitter *bl, int x, int y);
2618
2619This function behaves exactly like the back end \cw{blitter_save()}
2620function; see \k{drawing-blitter-save}.
2621
2622Implementations of this API which do not provide drawing services
2623may define this function pointer to be \cw{NULL}; it will never be
2624called unless drawing is attempted.
2625
2626\S{drawingapi-blitter-load} \cw{blitter_load()}
2627
2628\c void (*blitter_load)(void *handle, blitter *bl, int x, int y);
2629
2630This function behaves exactly like the back end \cw{blitter_load()}
2631function; see \k{drawing-blitter-load}.
2632
2633Implementations of this API which do not provide drawing services
2634may define this function pointer to be \cw{NULL}; it will never be
2635called unless drawing is attempted.
2636
2637\S{drawingapi-begin-doc} \cw{begin_doc()}
2638
2639\c void (*begin_doc)(void *handle, int pages);
2640
2641This function is called at the beginning of a printing run. It gives
2642the front end an opportunity to initialise any required printing
2643subsystem. It also provides the number of pages in advance.
2644
2645Implementations of this API which do not provide printing services
2646may define this function pointer to be \cw{NULL}; it will never be
2647called unless printing is attempted.
2648
2649\S{drawingapi-begin-page} \cw{begin_page()}
2650
2651\c void (*begin_page)(void *handle, int number);
2652
2653This function is called during printing, at the beginning of each
2654page. It gives the page number (numbered from 1 rather than 0, so
2655suitable for use in user-visible contexts).
2656
2657Implementations of this API which do not provide printing services
2658may define this function pointer to be \cw{NULL}; it will never be
2659called unless printing is attempted.
2660
2661\S{drawingapi-begin-puzzle} \cw{begin_puzzle()}
2662
2663\c void (*begin_puzzle)(void *handle, float xm, float xc,
2664\c float ym, float yc, int pw, int ph, float wmm);
2665
2666This function is called during printing, just before printing a
2667single puzzle on a page. It specifies the size and location of the
2668puzzle on the page.
2669
2670\c{xm} and \c{xc} specify the horizontal position of the puzzle on
2671the page, as a linear function of the page width. The front end is
2672expected to multiply the page width by \c{xm}, add \c{xc} (measured
2673in millimetres), and use the resulting x-coordinate as the left edge
2674of the puzzle.
2675
2676Similarly, \c{ym} and \c{yc} specify the vertical position of the
2677puzzle as a function of the page height: the page height times
2678\c{ym}, plus \c{yc} millimetres, equals the desired distance from
2679the top of the page to the top of the puzzle.
2680
2681(This unwieldy mechanism is required because not all printing
2682systems can communicate the page size back to the software. The
2683PostScript back end, for example, writes out PS which determines the
2684page size at print time by means of calling \cq{clippath}, and
2685centres the puzzles within that. Thus, exactly the same PS file
2686works on A4 or on US Letter paper without needing local
2687configuration, which simplifies matters.)
2688
2689\cw{pw} and \cw{ph} give the size of the puzzle in drawing API
2690coordinates. The printing system will subsequently call the puzzle's
2691own print function, which will in turn call drawing API functions in
2692the expectation that an area \cw{pw} by \cw{ph} units is available
2693to draw the puzzle on.
2694
2695Finally, \cw{wmm} gives the desired width of the puzzle in
2696millimetres. (The aspect ratio is expected to be preserved, so if
2697the desired puzzle height is also needed then it can be computed as
2698\cw{wmm*ph/pw}.)
2699
2700Implementations of this API which do not provide printing services
2701may define this function pointer to be \cw{NULL}; it will never be
2702called unless printing is attempted.
2703
2704\S{drawingapi-end-puzzle} \cw{end_puzzle()}
2705
2706\c void (*end_puzzle)(void *handle);
2707
2708This function is called after the printing of a specific puzzle is
2709complete.
2710
2711Implementations of this API which do not provide printing services
2712may define this function pointer to be \cw{NULL}; it will never be
2713called unless printing is attempted.
2714
2715\S{drawingapi-end-page} \cw{end_page()}
2716
2717\c void (*end_page)(void *handle, int number);
2718
2719This function is called after the printing of a page is finished.
2720
2721Implementations of this API which do not provide printing services
2722may define this function pointer to be \cw{NULL}; it will never be
2723called unless printing is attempted.
2724
2725\S{drawingapi-end-doc} \cw{end_doc()}
2726
2727\c void (*end_doc)(void *handle);
2728
2729This function is called after the printing of the entire document is
2730finished. This is the moment to close files, send things to the
2731print spooler, or whatever the local convention is.
2732
2733Implementations of this API which do not provide printing services
2734may define this function pointer to be \cw{NULL}; it will never be
2735called unless printing is attempted.
2736
2737\S{drawingapi-line-width} \cw{line_width()}
2738
2739\c void (*line_width)(void *handle, float width);
2740
2741This function is called to set the line thickness, during printing
2742only. Note that the width is a \cw{float} here, where it was an
2743\cw{int} as seen by the back end. This is because \cw{drawing.c} may
2744have scaled it on the way past.
2745
2746However, the width is still specified in the same coordinate system
2747as the rest of the drawing.
2748
2749Implementations of this API which do not provide printing services
2750may define this function pointer to be \cw{NULL}; it will never be
2751called unless printing is attempted.
2752
2753\S{drawingapi-text-fallback} \cw{text_fallback()}
2754
2755\c char *(*text_fallback)(void *handle, const char *const *strings,
2756\c int nstrings);
2757
2758This function behaves exactly like the back end \cw{text_fallback()}
2759function; see \k{drawing-text-fallback}.
2760
2761Implementations of this API which do not support any characters
2762outside ASCII may define this function pointer to be \cw{NULL}, in
2763which case the central code in \cw{drawing.c} will provide a default
2764implementation.
2765
2766\H{drawingapi-frontend} The drawing API as called by the front end
2767
2768There are a small number of functions provided in \cw{drawing.c}
2769which the front end needs to \e{call}, rather than helping to
2770implement. They are described in this section.
2771
2772\S{drawing-new} \cw{drawing_new()}
2773
2774\c drawing *drawing_new(const drawing_api *api, midend *me,
2775\c void *handle);
2776
2777This function creates a drawing object. It is passed a
2778\c{drawing_api}, which is a structure containing nothing but
2779function pointers; and also a \cq{void *} handle. The handle is
2780passed back to each function pointer when it is called.
2781
2782The \c{midend} parameter is used for rewriting the status bar
2783contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call
2784a function in the mid-end which might rewrite the status bar text.
2785If the drawing object is to be used only for printing, or if the
2786game is known not to call \cw{status_bar()}, this parameter may be
2787\cw{NULL}.
2788
2789\S{drawing-free} \cw{drawing_free()}
2790
2791\c void drawing_free(drawing *dr);
2792
2793This function frees a drawing object. Note that the \cq{void *}
2794handle is not freed; if that needs cleaning up it must be done by
2795the front end.
2796
2797\S{drawing-print-get-colour} \cw{print_get_colour()}
2798
2799\c void print_get_colour(drawing *dr, int colour, int printincolour,
2800\c int *hatch, float *r, float *g, float *b)
2801
2802This function is called by the implementations of the drawing API
2803functions when they are called in a printing context. It takes a
2804colour index as input, and returns the description of the colour as
2805requested by the back end.
2806
2807\c{printincolour} is \cw{TRUE} iff the implementation is printing in
2808colour. This will alter the results returned if the colour in
2809question was specified with a black-and-white fallback value.
2810
2811If the colour should be rendered by hatching, \c{*hatch} is filled
2812with the type of hatching desired. See \k{print-grey-colour} for
2813details of the values this integer can take.
2814
2815If the colour should be rendered as solid colour, \c{*hatch} is
2816given a negative value, and \c{*r}, \c{*g} and \c{*b} are filled
2817with the RGB values of the desired colour (if printing in colour),
2818or all filled with the grey-scale value (if printing in black and
2819white).
2820
2821\C{midend} The API provided by the mid-end
2822
2823This chapter documents the API provided by the mid-end to be called
2824by the front end. You probably only need to read this if you are a
2825front end implementor, i.e. you are porting Puzzles to a new
2826platform. If you're only interested in writing new puzzles, you can
2827safely skip this chapter.
2828
2829All the persistent state in the mid-end is encapsulated within a
2830\c{midend} structure, to facilitate having multiple mid-ends in any
2831port which supports multiple puzzle windows open simultaneously.
2832Each \c{midend} is intended to handle the contents of a single
2833puzzle window.
2834
2835\H{midend-new} \cw{midend_new()}
2836
2837\c midend *midend_new(frontend *fe, const game *ourgame,
2838\c const drawing_api *drapi, void *drhandle)
2839
2840Allocates and returns a new mid-end structure.
2841
2842The \c{fe} argument is stored in the mid-end. It will be used when
2843calling back to functions such as \cw{activate_timer()}
2844(\k{frontend-activate-timer}), and will be passed on to the back end
2845function \cw{colours()} (\k{backend-colours}).
2846
2847The parameters \c{drapi} and \c{drhandle} are passed to
2848\cw{drawing_new()} (\k{drawing-new}) to construct a drawing object
2849which will be passed to the back end function \cw{redraw()}
2850(\k{backend-redraw}). Hence, all drawing-related function pointers
2851defined in \c{drapi} can expect to be called with \c{drhandle} as
2852their first argument.
2853
2854The \c{ourgame} argument points to a container structure describing
2855a game back end. The mid-end thus created will only be capable of
2856handling that one game. (So even in a monolithic front end
2857containing all the games, this imposes the constraint that any
2858individual puzzle window is tied to a single game. Unless, of
2859course, you feel brave enough to change the mid-end for the window
2860without closing the window...)
2861
2862\H{midend-free} \cw{midend_free()}
2863
2864\c void midend_free(midend *me);
2865
2866Frees a mid-end structure and all its associated data.
2867
2868\H{midend-tilesize} \cw{midend_tilesize()}
2869
2870\c int midend_tilesize(midend *me);
2871
2872Returns the \cq{tilesize} parameter being used to display the
2873current puzzle (\k{backend-preferred-tilesize}).
2874
2875\H{midend-set-params} \cw{midend_set_params()}
2876
2877\c void midend_set_params(midend *me, game_params *params);
2878
2879Sets the current game parameters for a mid-end. Subsequent games
2880generated by \cw{midend_new_game()} (\k{midend-new-game}) will use
2881these parameters until further notice.
2882
2883The usual way in which the front end will have an actual
2884\c{game_params} structure to pass to this function is if it had
2885previously got it from \cw{midend_get_presets()}
2886(\k{midend-get-presets}). Thus, this function is usually called in
2887response to the user making a selection from the presets menu.
2888
2889\H{midend-get-params} \cw{midend_get_params()}
2890
2891\c game_params *midend_get_params(midend *me);
2892
2893Returns the current game parameters stored in this mid-end.
2894
2895The returned value is dynamically allocated, and should be freed
2896when finished with by passing it to the game's own
2897\cw{free_params()} function (see \k{backend-free-params}).
2898
2899\H{midend-size} \cw{midend_size()}
2900
2901\c void midend_size(midend *me, int *x, int *y, bool user_size);
2902
2903Tells the mid-end to figure out its window size.
2904
2905On input, \c{*x} and \c{*y} should contain the maximum or requested
2906size for the window. (Typically this will be the size of the screen
2907that the window has to fit on, or similar.) The mid-end will
2908repeatedly call the back end function \cw{compute_size()}
2909(\k{backend-compute-size}), searching for a tile size that best
2910satisfies the requirements. On exit, \c{*x} and \c{*y} will contain
2911the size needed for the puzzle window's drawing area. (It is of
2912course up to the front end to adjust this for any additional window
2913furniture such as menu bars and window borders, if necessary. The
2914status bar is also not included in this size.)
2915
2916Use \c{user_size} to indicate whether \c{*x} and \c{*y} are a
2917requested size, or just a maximum size.
2918
2919If \c{user_size} is set to \cw{true}, the mid-end will treat the
2920input size as a request, and will pick a tile size which
2921approximates it \e{as closely as possible}, going over the game's
2922preferred tile size if necessary to achieve this. The mid-end will
2923also use the resulting tile size as its preferred one until further
2924notice, on the assumption that this size was explicitly requested
2925by the user. Use this option if you want your front end to support
2926dynamic resizing of the puzzle window with automatic scaling of the
2927puzzle to fit.
2928
2929If \c{user_size} is set to \cw{FALSE}, then the game's tile size
2930will never go over its preferred one, although it may go under in
2931order to fit within the maximum bounds specified by \c{*x} and
2932\c{*y}. This is the recommended approach when opening a new window
2933at default size: the game will use its preferred size unless it has
2934to use a smaller one to fit on the screen. If the tile size is
2935shrunk for this reason, the change will not persist; if a smaller
2936grid is subsequently chosen, the tile size will recover.
2937
2938The mid-end will try as hard as it can to return a size which is
2939less than or equal to the input size, in both dimensions. In extreme
2940circumstances it may fail (if even the lowest possible tile size
2941gives window dimensions greater than the input), in which case it
2942will return a size greater than the input size. Front ends should be
2943prepared for this to happen (i.e. don't crash or fail an assertion),
2944but may handle it in any way they see fit: by rejecting the game
2945parameters which caused the problem, by opening a window larger than
2946the screen regardless of inconvenience, by introducing scroll bars
2947on the window, by drawing on a large bitmap and scaling it into a
2948smaller window, or by any other means you can think of. It is likely
2949that when the tile size is that small the game will be unplayable
2950anyway, so don't put \e{too} much effort into handling it
2951creatively.
2952
2953If your platform has no limit on window size (or if you're planning
2954to use scroll bars for large puzzles), you can pass dimensions of
2955\cw{INT_MAX} as input to this function. You should probably not do
2956that \e{and} set the \c{user_size} flag, though!
2957
2958The midend relies on the frontend calling \cw{midend_new_game()}
2959(\k{midend-new-game}) before calling \cw{midend_size()}.
2960
2961\H{midend-reset-tilesize} \cw{midend_reset_tilesize()}
2962
2963\c void midend_reset_tilesize(midend *me);
2964
2965This function resets the midend's preferred tile size to that of the
2966standard puzzle.
2967
2968As discussed in \k{midend-size}, puzzle resizes are typically
2969'sticky', in that once the user has dragged the puzzle to a different
2970window size, the resulting tile size will be remembered and used when
2971the puzzle configuration changes. If you \e{don't} want that, e.g. if
2972you want to provide a command to explicitly reset the puzzle size back
2973to its default, then you can call this just before calling
2974\cw{midend_size()} (which, in turn, you would probably call with
2975\c{user_size} set to \cw{FALSE}).
2976
2977\H{midend-new-game} \cw{midend_new_game()}
2978
2979\c void midend_new_game(midend *me);
2980
2981Causes the mid-end to begin a new game. Normally the game will be a
2982new randomly generated puzzle. However, if you have previously
2983called \cw{midend_game_id()} or \cw{midend_set_config()}, the game
2984generated might be dictated by the results of those functions. (In
2985particular, you \e{must} call \cw{midend_new_game()} after calling
2986either of those functions, or else no immediate effect will be
2987visible.)
2988
2989You will probably need to call \cw{midend_size()} after calling this
2990function, because if the game parameters have been changed since the
2991last new game then the window size might need to change. (If you
2992know the parameters \e{haven't} changed, you don't need to do this.)
2993
2994This function will create a new \c{game_drawstate}, but does not
2995actually perform a redraw (since you often need to call
2996\cw{midend_size()} before the redraw can be done). So after calling
2997this function and after calling \cw{midend_size()}, you should then
2998call \cw{midend_redraw()}. (It is not necessary to call
2999\cw{midend_force_redraw()}; that will discard the draw state and
3000create a fresh one, which is unnecessary in this case since there's
3001a fresh one already. It would work, but it's usually excessive.)
3002
3003\H{midend-restart-game} \cw{midend_restart_game()}
3004
3005\c void midend_restart_game(midend *me);
3006
3007This function causes the current game to be restarted. This is done
3008by placing a new copy of the original game state on the end of the
3009undo list (so that an accidental restart can be undone).
3010
3011This function automatically causes a redraw, i.e. the front end can
3012expect its drawing API to be called from \e{within} a call to this
3013function. Some back ends require that \cw{midend_size()}
3014(\k{midend-size}) is called before \cw{midend_restart_game()}.
3015
3016\H{midend-force-redraw} \cw{midend_force_redraw()}
3017
3018\c void midend_force_redraw(midend *me);
3019
3020Forces a complete redraw of the puzzle window, by means of
3021discarding the current \c{game_drawstate} and creating a new one
3022from scratch before calling the game's \cw{redraw()} function.
3023
3024The front end can expect its drawing API to be called from within a
3025call to this function. Some back ends require that \cw{midend_size()}
3026(\k{midend-size}) is called before \cw{midend_force_redraw()}.
3027
3028\H{midend-redraw} \cw{midend_redraw()}
3029
3030\c void midend_redraw(midend *me);
3031
3032Causes a partial redraw of the puzzle window, by means of simply
3033calling the game's \cw{redraw()} function. (That is, the only things
3034redrawn will be things that have changed since the last redraw.)
3035
3036The front end can expect its drawing API to be called from within a
3037call to this function. Some back ends require that \cw{midend_size()}
3038(\k{midend-size}) is called before \cw{midend_redraw()}.
3039
3040\H{midend-process-key} \cw{midend_process_key()}
3041
3042\c bool midend_process_key(midend *me, int x, int y, int button);
3043
3044The front end calls this function to report a mouse or keyboard
3045event. The parameters \c{x}, \c{y} and \c{button} are almost
3046identical to the ones passed to the back end function
3047\cw{interpret_move()} (\k{backend-interpret-move}), except that the
3048front end is \e{not} required to provide the guarantees about mouse
3049event ordering. The mid-end will sort out multiple simultaneous
3050button presses and changes of button; the front end's responsibility
3051is simply to pass on the mouse events it receives as accurately as
3052possible.
3053
3054(Some platforms may need to emulate absent mouse buttons by means of
3055using a modifier key such as Shift with another mouse button. This
3056tends to mean that if Shift is pressed or released in the middle of
3057a mouse drag, the mid-end will suddenly stop receiving, say,
3058\cw{LEFT_DRAG} events and start receiving \cw{RIGHT_DRAG}s, with no
3059intervening button release or press events. This too is something
3060which the mid-end will sort out for you; the front end has no
3061obligation to maintain sanity in this area.)
3062
3063The front end \e{should}, however, always eventually send some kind
3064of button release. On some platforms this requires special effort:
3065Windows, for example, requires a call to the system API function
3066\cw{SetCapture()} in order to ensure that your window receives a
3067mouse-up event even if the pointer has left the window by the time
3068the mouse button is released. On any platform that requires this
3069sort of thing, the front end \e{is} responsible for doing it.
3070
3071Calling this function is very likely to result in calls back to the
3072front end's drawing API and/or \cw{activate_timer()}
3073(\k{frontend-activate-timer}).
3074
3075The return value from \cw{midend_process_key()} is \cw{true} unless
3076the effect of the keypress was to request termination of the program.
3077A front end should shut down the puzzle in response to a \cw{false}
3078return.
3079
3080\H{midend-request-keys} \cw{midend_request_keys()}
3081
3082\c key_label *midend_request_keys(midend *me, int *nkeys);
3083
3084This function behaves similarly to the backend's \cw{request_keys()}
3085function (\k{backend-request-keys}). If the backend does not provide
3086\cw{request_keys()}, this function will return \cw{NULL} and set
3087\cw{*nkeys} to zero. Otherwise, this function will fill in the generic
3088labels (i.e. the \cw{key_label} items that have their \cw{label}
3089fields set to \cw{NULL}) by using \cw{button2label()}
3090(\k{utils-button2label}).
3091
3092\H{midend-colours} \cw{midend_colours()}
3093
3094\c float *midend_colours(midend *me, int *ncolours);
3095
3096Returns an array of the colours required by the game, in exactly the
3097same format as that returned by the back end function \cw{colours()}
3098(\k{backend-colours}). Front ends should call this function rather
3099than calling the back end's version directly, since the mid-end adds
3100standard customisation facilities. (At the time of writing, those
3101customisation facilities are implemented hackily by means of
3102environment variables, but it's not impossible that they may become
3103more full and formal in future.)
3104
3105\H{midend-timer} \cw{midend_timer()}
3106
3107\c void midend_timer(midend *me, float tplus);
3108
3109If the mid-end has called \cw{activate_timer()}
3110(\k{frontend-activate-timer}) to request regular callbacks for
3111purposes of animation or timing, this is the function the front end
3112should call on a regular basis. The argument \c{tplus} gives the
3113time, in seconds, since the last time either this function was
3114called or \cw{activate_timer()} was invoked.
3115
3116One of the major purposes of timing in the mid-end is to perform
3117move animation. Therefore, calling this function is very likely to
3118result in calls back to the front end's drawing API.
3119
3120\H{midend-get-presets} \cw{midend_get_presets()}
3121
3122\c struct preset_menu *midend_get_presets(midend *me, int *id_limit);
3123
3124Returns a data structure describing this game's collection of preset
3125game parameters, organised into a hierarchical structure of menus and
3126submenus.
3127
3128The return value is a pointer to a data structure containing the
3129following fields (among others, which are not intended for front end
3130use):
3131
3132\c struct preset_menu {
3133\c int n_entries;
3134\c struct preset_menu_entry *entries;
3135\c /* and other things */
3136\e iiiiiiiiiiiiiiiiiiiiii
3137\c };
3138
3139Those fields describe the intended contents of one particular menu in
3140the hierarchy. \cq{entries} points to an array of \cq{n_entries}
3141items, each of which is a structure containing the following fields:
3142
3143\c struct preset_menu_entry {
3144\c char *title;
3145\c game_params *params;
3146\c struct preset_menu *submenu;
3147\c int id;
3148\c };
3149
3150Of these fields, \cq{title} and \cq{id} are present in every entry,
3151giving (respectively) the textual name of the menu item and an integer
3152identifier for it. The integer id will correspond to the one returned
3153by \c{midend_which_preset} (\k{midend-which-preset}), when that preset
3154is the one selected.
3155
3156The other two fields are mutually exclusive. Each \c{struct
3157preset_menu_entry} will have one of those fields \cw{NULL} and the
3158other one non-null. If the menu item is an actual preset, then
3159\cq{params} will point to the set of game parameters that go with the
3160name; if it's a submenu, then \cq{submenu} instead will be non-null,
3161and will point at a subsidiary \c{struct preset_menu}.
3162
3163The complete hierarchy of these structures is owned by the mid-end,
3164and will be freed when the mid-end is freed. The front end should not
3165attempt to free any of it.
3166
3167The integer identifiers will be allocated densely from 0 upwards, so
3168that it's reasonable for the front end to allocate an array which uses
3169them as indices, if it needs to store information per preset menu
3170item. For this purpose, the front end may pass the second parameter
3171\cq{id_limit} to \cw{midend_get_presets} as the address of an \c{int}
3172variable, into which \cw{midend_get_presets} will write an integer one
3173larger than the largest id number actually used (i.e. the number of
3174elements the front end would need in the array).
3175
3176Submenu-type entries also have integer identifiers.
3177
3178\H{midend-which-preset} \cw{midend_which_preset()}
3179
3180\c int midend_which_preset(midend *me);
3181
3182Returns the numeric index of the preset game parameter structure
3183which matches the current game parameters, or a negative number if
3184no preset matches. Front ends could use this to maintain a tick
3185beside one of the items in the menu (or tick the \q{Custom} option
3186if the return value is less than zero).
3187
3188The returned index value (if non-negative) will match the \c{id} field
3189of the corresponding \cw{struct preset_menu_entry} returned by
3190\c{midend_get_presets()} (\k{midend-get-presets}).
3191
3192\H{midend-wants-statusbar} \cw{midend_wants_statusbar()}
3193
3194\c bool midend_wants_statusbar(midend *me);
3195
3196This function returns \cw{true} if the puzzle has a use for a
3197textual status line (to display score, completion status, currently
3198active tiles, time, or anything else).
3199
3200Front ends should call this function rather than talking directly to
3201the back end.
3202
3203\H{midend-get-config} \cw{midend_get_config()}
3204
3205\c config_item *midend_get_config(midend *me, int which,
3206\c char **wintitle);
3207
3208Returns a dialog box description for user configuration.
3209
3210On input, \cw{which} should be set to one of three values, which
3211select which of the various dialog box descriptions is returned:
3212
3213\dt \cw{CFG_SETTINGS}
3214
3215\dd Requests the GUI parameter configuration box generated by the
3216puzzle itself. This should be used when the user selects \q{Custom}
3217from the game types menu (or equivalent). The mid-end passes this
3218request on to the back end function \cw{configure()}
3219(\k{backend-configure}).
3220
3221\dt \cw{CFG_DESC}
3222
3223\dd Requests a box suitable for entering a descriptive game ID (and
3224viewing the existing one). The mid-end generates this dialog box
3225description itself. This should be used when the user selects
3226\q{Specific} from the game menu (or equivalent).
3227
3228\dt \cw{CFG_SEED}
3229
3230\dd Requests a box suitable for entering a random-seed game ID (and
3231viewing the existing one). The mid-end generates this dialog box
3232description itself. This should be used when the user selects
3233\q{Random Seed} from the game menu (or equivalent).
3234
3235The returned value is an array of \cw{config_item}s, exactly as
3236described in \k{backend-configure}. Another returned value is an
3237ASCII string giving a suitable title for the configuration window,
3238in \c{*wintitle}.
3239
3240Both returned values are dynamically allocated and will need to be
3241freed. The window title can be freed in the obvious way; the
3242\cw{config_item} array is a slightly complex structure, so a utility
3243function \cw{free_cfg()} is provided to free it for you. See
3244\k{utils-free-cfg}.
3245
3246(Of course, you will probably not want to free the \cw{config_item}
3247array until the dialog box is dismissed, because before then you
3248will probably need to pass it to \cw{midend_set_config}.)
3249
3250\H{midend-set-config} \cw{midend_set_config()}
3251
3252\c const char *midend_set_config(midend *me, int which,
3253\c config_item *cfg);
3254
3255Passes the mid-end the results of a configuration dialog box.
3256\c{which} should have the same value which it had when
3257\cw{midend_get_config()} was called; \c{cfg} should be the array of
3258\c{config_item}s returned from \cw{midend_get_config()}, modified to
3259contain the results of the user's editing operations.
3260
3261This function returns \cw{NULL} on success, or otherwise (if the
3262configuration data was in some way invalid) an ASCII string
3263containing an error message suitable for showing to the user.
3264
3265If the function succeeds, it is likely that the game parameters will
3266have been changed and it is certain that a new game will be
3267requested. The front end should therefore call
3268\cw{midend_new_game()}, and probably also re-think the window size
3269using \cw{midend_size()} and eventually perform a refresh using
3270\cw{midend_redraw()}.
3271
3272\H{midend-game-id} \cw{midend_game_id()}
3273
3274\c const char *midend_game_id(midend *me, const char *id);
3275
3276Passes the mid-end a string game ID (of any of the valid forms
3277\cq{params}, \cq{params:description} or \cq{params#seed}) which the
3278mid-end will process and use for the next generated game.
3279
3280This function returns \cw{NULL} on success, or otherwise (if the
3281configuration data was in some way invalid) an ASCII string
3282containing an error message (not dynamically allocated) suitable for
3283showing to the user. In the event of an error, the mid-end's
3284internal state will be left exactly as it was before the call.
3285
3286If the function succeeds, it is likely that the game parameters will
3287have been changed and it is certain that a new game will be
3288requested. The front end should therefore call
3289\cw{midend_new_game()}, and probably also re-think the window size
3290using \cw{midend_size()} and eventually case a refresh using
3291\cw{midend_redraw()}.
3292
3293\H{midend-get-game-id} \cw{midend_get_game_id()}
3294
3295\c char *midend_get_game_id(midend *me)
3296
3297Returns a descriptive game ID (i.e. one in the form
3298\cq{params:description}) describing the game currently active in the
3299mid-end. The returned string is dynamically allocated.
3300
3301\H{midend-get-random-seed} \cw{midend_get_random_seed()}
3302
3303\c char *midend_get_random_seed(midend *me)
3304
3305Returns a random game ID (i.e. one in the form \cq{params#seedstring})
3306describing the game currently active in the mid-end, if there is one.
3307If the game was created by entering a description, no random seed will
3308currently exist and this function will return \cw{NULL}.
3309
3310The returned string, if it is non-\cw{NULL}, is dynamically allocated.
3311
3312\H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()}
3313
3314\c bool midend_can_format_as_text_now(midend *me);
3315
3316Returns \cw{true} if the game code is capable of formatting puzzles
3317of the currently selected game type as ASCII.
3318
3319If this returns \cw{false}, then \cw{midend_text_format()}
3320(\k{midend-text-format}) will return \cw{NULL}.
3321
3322\H{midend-text-format} \cw{midend_text_format()}
3323
3324\c char *midend_text_format(midend *me);
3325
3326Formats the current game's current state as ASCII text suitable for
3327copying to the clipboard. The returned string is dynamically
3328allocated.
3329
3330If the game's \c{can_format_as_text_ever} flag is \cw{FALSE}, or if
3331its \cw{can_format_as_text_now()} function returns \cw{FALSE}, then
3332this function will return \cw{NULL}.
3333
3334If the returned string contains multiple lines (which is likely), it
3335will use the normal C line ending convention (\cw{\\n} only). On
3336platforms which use a different line ending convention for data in
3337the clipboard, it is the front end's responsibility to perform the
3338conversion.
3339
3340\H{midend-solve} \cw{midend_solve()}
3341
3342\c const char *midend_solve(midend *me);
3343
3344Requests the mid-end to perform a Solve operation.
3345
3346On success, \cw{NULL} is returned. On failure, an error message (not
3347dynamically allocated) is returned, suitable for showing to the
3348user.
3349
3350The front end can expect its drawing API and/or
3351\cw{activate_timer()} to be called from within a call to this
3352function. Some back ends require that \cw{midend_size()}
3353(\k{midend-size}) is called before \cw{midend_solve()}.
3354
3355\H{midend-get-cursor-location} \cw{midend_get_cursor_location()}
3356
3357\c bool midend_get_cursor_location(midend *me,
3358\c int *x, int *y,
3359\c int *w, int *h);
3360
3361This function requests the location of the back end's on-screen cursor
3362or other region of interest.
3363
3364What exactly this region contains is up to the backend, but in general
3365the region will be an area that the player is controlling with the
3366cursor keys \dash such as the player location in Cube and Inertia, or
3367the cursor in any of the conventional grid-based games. With knowledge
3368of this location, a front end can, for example, ensure that the region
3369of interest remains visible even if the entire puzzle is too big to
3370fit on the screen.
3371
3372On success, this function returns \cw{true}, and the locations pointed
3373to by \cw{x}, \cw{y}, \cw{w} and \cw{h} are updated to describe the
3374cursor region, which has an upper-left corner located at \cw{(*x,*y)}
3375and a size of \cw{*w} pixels wide by \cw{*h} pixels tall. The caller
3376may pass \cw{NULL} for any number of these pointers, which will be
3377ignored.
3378
3379On failure, this function returns \cw{false}. Failure can occur if
3380there is currently no active cursor region, or if the back end lacks
3381cursor support.
3382
3383\H{midend-status} \cw{midend_status()}
3384
3385\c int midend_status(midend *me);
3386
3387This function returns +1 if the midend is currently displaying a game
3388in a solved state, -1 if the game is in a permanently lost state, or 0
3389otherwise. This function just calls the back end's \cw{status()}
3390function. Front ends may wish to use this as a cue to proactively
3391offer the option of starting a new game.
3392
3393(See \k{backend-status} for more detail about the back end's
3394\cw{status()} function and discussion of what should count as which
3395status code.)
3396
3397\H{midend-can-undo} \cw{midend_can_undo()}
3398
3399\c bool midend_can_undo(midend *me);
3400
3401Returns \cw{true} if the midend is currently in a state where the undo
3402operation is meaningful (i.e. at least one position exists on the undo
3403chain before the present one). Front ends may wish to use this to
3404visually activate and deactivate an undo button.
3405
3406\H{midend-can-redo} \cw{midend_can_redo()}
3407
3408\c bool midend_can_redo(midend *me);
3409
3410Returns \cw{true} if the midend is currently in a state where the redo
3411operation is meaningful (i.e. at least one position exists on the redo
3412chain after the present one). Front ends may wish to use this to
3413visually activate and deactivate a redo button.
3414
3415\H{midend-serialise} \cw{midend_serialise()}
3416
3417\c void midend_serialise(midend *me,
3418\c void (*write)(void *ctx, const void *buf, int len), void *wctx);
3419
3420Calling this function causes the mid-end to convert its entire
3421internal state into a long ASCII text string, and to pass that
3422string (piece by piece) to the supplied \c{write} function.
3423
3424Desktop implementations can use this function to save a game in any
3425state (including half-finished) to a disk file, by supplying a
3426\c{write} function which is a wrapper on \cw{fwrite()} (or local
3427equivalent). Other implementations may find other uses for it, such
3428as compressing the large and sprawling mid-end state into a
3429manageable amount of memory when a palmtop application is suspended
3430so that another one can run; in this case \cw{write} might want to
3431write to a memory buffer rather than a file. There may be other uses
3432for it as well.
3433
3434This function will call back to the supplied \c{write} function a
3435number of times, with the first parameter (\c{ctx}) equal to
3436\c{wctx}, and the other two parameters pointing at a piece of the
3437output string.
3438
3439\H{midend-deserialise} \cw{midend_deserialise()}
3440
3441\c const char *midend_deserialise(midend *me,
3442\c bool (*read)(void *ctx, void *buf, int len), void *rctx);
3443
3444This function is the counterpart to \cw{midend_serialise()}. It
3445calls the supplied \cw{read} function repeatedly to read a quantity
3446of data, and attempts to interpret that data as a serialised mid-end
3447as output by \cw{midend_serialise()}.
3448
3449The \cw{read} function is called with the first parameter (\c{ctx})
3450equal to \c{rctx}, and should attempt to read \c{len} bytes of data
3451into the buffer pointed to by \c{buf}. It should return \cw{false}
3452on failure or \cw{true} on success. It should not report success
3453unless it has filled the entire buffer; on platforms which might be
3454reading from a pipe or other blocking data source, \c{read} is
3455responsible for looping until the whole buffer has been filled.
3456
3457If the de-serialisation operation is successful, the mid-end's
3458internal data structures will be replaced by the results of the
3459load, and \cw{NULL} will be returned. Otherwise, the mid-end's state
3460will be completely unchanged and an error message (typically some
3461variation on \q{save file is corrupt}) will be returned. As usual,
3462the error message string is not dynamically allocated.
3463
3464If this function succeeds, it is likely that the game parameters
3465will have been changed. The front end should therefore probably
3466re-think the window size using \cw{midend_size()}, and probably
3467cause a refresh using \cw{midend_redraw()}.
3468
3469Because each mid-end is tied to a specific game back end, this
3470function will fail if you attempt to read in a save file generated by
3471a different game from the one configured in this mid-end, even if your
3472application is a monolithic one containing all the puzzles. See
3473\k{identify-game} for a helper function which will allow you to
3474identify a save file before you instantiate your mid-end in the first
3475place.
3476
3477\H{identify-game} \cw{identify_game()}
3478
3479\c const char *identify_game(char **name,
3480\c bool (*read)(void *ctx, void *buf, int len), void *rctx);
3481
3482This function examines a serialised midend stream, of the same kind
3483used by \cw{midend_serialise()} and \cw{midend_deserialise()}, and
3484returns the \cw{name} field of the game back end from which it was
3485saved.
3486
3487You might want this if your front end was a monolithic one containing
3488all the puzzles, and you wanted to be able to load an arbitrary save
3489file and automatically switch to the right game. Probably your next
3490step would be to iterate through \cw{gamelist} (\k{frontend-backend})
3491looking for a game structure whose \cw{name} field matched the
3492returned string, and give an error if you didn't find one.
3493
3494On success, the return value of this function is \cw{NULL}, and the
3495game name string is written into \cw{*name}. The caller should free
3496that string after using it.
3497
3498On failure, \cw{*name} is \cw{NULL}, and the return value is an error
3499message (which does not need freeing at all).
3500
3501(This isn't strictly speaking a midend function, since it doesn't
3502accept or return a pointer to a midend. You'd probably call it just
3503\e{before} deciding what kind of midend you wanted to instantiate.)
3504
3505\H{midend-request-id-changes} \cw{midend_request_id_changes()}
3506
3507\c void midend_request_id_changes(midend *me,
3508\c void (*notify)(void *), void *ctx);
3509
3510This function is called by the front end to request notification by
3511the mid-end when the current game IDs (either descriptive or
3512random-seed) change. This can occur as a result of keypresses ('n' for
3513New Game, for example) or when a puzzle supersedes its game
3514description (see \k{backend-supersede}). After this function is
3515called, any change of the game ids will cause the mid-end to call
3516\cw{notify(ctx)} after the change.
3517
3518This is for use by puzzles which want to present the game description
3519to the user constantly (e.g. as an HTML hyperlink) instead of only
3520showing it when the user explicitly requests it.
3521
3522This is a function I anticipate few front ends needing to implement,
3523so I make it a callback rather than a static function in order to
3524relieve most front ends of the need to provide an empty
3525implementation.
3526
3527\H{frontend-backend} Direct reference to the back end structure by
3528the front end
3529
3530Although \e{most} things the front end needs done should be done by
3531calling the mid-end, there are a few situations in which the front
3532end needs to refer directly to the game back end structure.
3533
3534The most obvious of these is
3535
3536\b passing the game back end as a parameter to \cw{midend_new()}.
3537
3538There are a few other back end features which are not wrapped by the
3539mid-end because there didn't seem much point in doing so:
3540
3541\b fetching the \c{name} field to use in window titles and similar
3542
3543\b reading the \c{can_configure}, \c{can_solve} and
3544\c{can_format_as_text_ever} fields to decide whether to add those
3545items to the menu bar or equivalent
3546
3547\b reading the \c{winhelp_topic} field (Windows only)
3548
3549\b the GTK front end provides a \cq{--generate} command-line option
3550which directly calls the back end to do most of its work. This is
3551not really part of the main front end code, though, and I'm not sure
3552it counts.
3553
3554In order to find the game back end structure, the front end does one
3555of two things:
3556
3557\b If the particular front end is compiling a separate binary per
3558game, then the back end structure is a global variable with the
3559standard name \cq{thegame}:
3560
3561\lcont{
3562
3563\c extern const game thegame;
3564
3565}
3566
3567\b If the front end is compiled as a monolithic application
3568containing all the puzzles together (in which case the preprocessor
3569symbol \cw{COMBINED} must be defined when compiling most of the code
3570base), then there will be two global variables defined:
3571
3572\lcont{
3573
3574\c extern const game *gamelist[];
3575\c extern const int gamecount;
3576
3577\c{gamelist} will be an array of \c{gamecount} game structures,
3578declared in the automatically constructed source module \c{list.c}.
3579The application should search that array for the game it wants,
3580probably by reaching into each game structure and looking at its
3581\c{name} field.
3582
3583}
3584
3585\H{frontend-api} Mid-end to front-end calls
3586
3587This section describes the small number of functions which a front
3588end must provide to be called by the mid-end or other standard
3589utility modules.
3590
3591\H{frontend-get-random-seed} \cw{get_random_seed()}
3592
3593\c void get_random_seed(void **randseed, int *randseedsize);
3594
3595This function is called by a new mid-end, and also occasionally by
3596game back ends. Its job is to return a piece of data suitable for
3597using as a seed for initialisation of a new \c{random_state}.
3598
3599On exit, \c{*randseed} should be set to point at a newly allocated
3600piece of memory containing some seed data, and \c{*randseedsize}
3601should be set to the length of that data.
3602
3603A simple and entirely adequate implementation is to return a piece
3604of data containing the current system time at the highest
3605conveniently available resolution.
3606
3607\H{frontend-activate-timer} \cw{activate_timer()}
3608
3609\c void activate_timer(frontend *fe);
3610
3611This is called by the mid-end to request that the front end begin
3612calling it back at regular intervals.
3613
3614The timeout interval is left up to the front end; the finer it is,
3615the smoother move animations will be, but the more CPU time will be
3616used. Current front ends use values around 20ms (i.e. 50Hz).
3617
3618After this function is called, the mid-end will expect to receive
3619calls to \cw{midend_timer()} on a regular basis.
3620
3621\H{frontend-deactivate-timer} \cw{deactivate_timer()}
3622
3623\c void deactivate_timer(frontend *fe);
3624
3625This is called by the mid-end to request that the front end stop
3626calling \cw{midend_timer()}.
3627
3628\H{frontend-fatal} \cw{fatal()}
3629
3630\c void fatal(const char *fmt, ...);
3631
3632This is called by some utility functions if they encounter a
3633genuinely fatal error such as running out of memory. It is a
3634variadic function in the style of \cw{printf()}, and is expected to
3635show the formatted error message to the user any way it can and then
3636terminate the application. It must not return.
3637
3638\H{frontend-default-colour} \cw{frontend_default_colour()}
3639
3640\c void frontend_default_colour(frontend *fe, float *output);
3641
3642This function expects to be passed a pointer to an array of three
3643\cw{float}s. It returns the platform's local preferred background
3644colour in those three floats, as red, green and blue values (in that
3645order) ranging from \cw{0.0} to \cw{1.0}.
3646
3647This function should only ever be called by the back end function
3648\cw{colours()} (\k{backend-colours}). (Thus, it isn't a
3649\e{midend}-to-frontend function as such, but there didn't seem to be
3650anywhere else particularly good to put it. Sorry.)
3651
3652\C{utils} Utility APIs
3653
3654This chapter documents a variety of utility APIs provided for the
3655general use of the rest of the Puzzles code.
3656
3657\H{utils-random} Random number generation
3658
3659Platforms' local random number generators vary widely in quality and
3660seed size. Puzzles therefore supplies its own high-quality random
3661number generator, with the additional advantage of giving the same
3662results if fed the same seed data on different platforms. This
3663allows game random seeds to be exchanged between different ports of
3664Puzzles and still generate the same games.
3665
3666Unlike the ANSI C \cw{rand()} function, the Puzzles random number
3667generator has an \e{explicit} state object called a
3668\c{random_state}. One of these is managed by each mid-end, for
3669example, and passed to the back end to generate a game with.
3670
3671\S{utils-random-init} \cw{random_new()}
3672
3673\c random_state *random_new(char *seed, int len);
3674
3675Allocates, initialises and returns a new \c{random_state}. The input
3676data is used as the seed for the random number stream (i.e. using
3677the same seed at a later time will generate the same stream).
3678
3679The seed data can be any data at all; there is no requirement to use
3680printable ASCII, or NUL-terminated strings, or anything like that.
3681
3682\S{utils-random-copy} \cw{random_copy()}
3683
3684\c random_state *random_copy(random_state *tocopy);
3685
3686Allocates a new \c{random_state}, copies the contents of another
3687\c{random_state} into it, and returns the new state. If exactly the
3688same sequence of functions is subseqently called on both the copy and
3689the original, the results will be identical. This may be useful for
3690speculatively performing some operation using a given random state,
3691and later replaying that operation precisely.
3692
3693\S{utils-random-free} \cw{random_free()}
3694
3695\c void random_free(random_state *state);
3696
3697Frees a \c{random_state}.
3698
3699\S{utils-random-bits} \cw{random_bits()}
3700
3701\c unsigned long random_bits(random_state *state, int bits);
3702
3703Returns a random number from 0 to \cw{2^bits-1} inclusive. \c{bits}
3704should be between 1 and 32 inclusive.
3705
3706\S{utils-random-upto} \cw{random_upto()}
3707
3708\c unsigned long random_upto(random_state *state, unsigned long limit);
3709
3710Returns a random number from 0 to \cw{limit-1} inclusive.
3711
3712\S{utils-random-state-encode} \cw{random_state_encode()}
3713
3714\c char *random_state_encode(random_state *state);
3715
3716Encodes the entire contents of a \c{random_state} in printable
3717ASCII. Returns a dynamically allocated string containing that
3718encoding. This can subsequently be passed to
3719\cw{random_state_decode()} to reconstruct the same \c{random_state}.
3720
3721\S{utils-random-state-decode} \cw{random_state_decode()}
3722
3723\c random_state *random_state_decode(char *input);
3724
3725Decodes a string generated by \cw{random_state_encode()} and
3726reconstructs an equivalent \c{random_state} to the one encoded, i.e.
3727it should produce the same stream of random numbers.
3728
3729This function has no error reporting; if you pass it an invalid
3730string it will simply generate an arbitrary random state, which may
3731turn out to be noticeably non-random.
3732
3733\S{utils-shuffle} \cw{shuffle()}
3734
3735\c void shuffle(void *array, int nelts, int eltsize, random_state *rs);
3736
3737Shuffles an array into a random order. The interface is much like
3738ANSI C \cw{qsort()}, except that there's no need for a compare
3739function.
3740
3741\c{array} is a pointer to the first element of the array. \c{nelts}
3742is the number of elements in the array; \c{eltsize} is the size of a
3743single element (typically measured using \c{sizeof}). \c{rs} is a
3744\c{random_state} used to generate all the random numbers for the
3745shuffling process.
3746
3747\H{utils-presets} Presets menu management
3748
3749The function \c{midend_get_presets()} (\k{midend-get-presets}) returns
3750a data structure describing a menu hierarchy. Back ends can also
3751choose to provide such a structure to the mid-end, if they want to
3752group their presets hierarchically. To make this easy, there are a few
3753utility functions to construct preset menu structures, and also one
3754intended for front-end use.
3755
3756\S{utils-preset-menu-new} \cw{preset_menu_new()}
3757
3758\c struct preset_menu *preset_menu_new(void);
3759
3760Allocates a new \c{struct preset_menu}, and initialises it to hold no
3761menu items.
3762
3763\S{utils-preset-menu-add_submenu} \cw{preset_menu_add_submenu()}
3764
3765\c struct preset_menu *preset_menu_add_submenu
3766\c (struct preset_menu *parent, char *title);
3767
3768Adds a new submenu to the end of an existing preset menu, and returns
3769a pointer to a newly allocated \c{struct preset_menu} describing the
3770submenu.
3771
3772The string parameter \cq{title} must be dynamically allocated by the
3773caller. The preset-menu structure will take ownership of it, so the
3774caller must not free it.
3775
3776\S{utils-preset-menu-add-preset} \cw{preset_menu_add_preset()}
3777
3778\c void preset_menu_add_preset
3779\c (struct preset_menu *menu, char *title, game_params *params);
3780
3781Adds a preset game configuration to the end of a preset menu.
3782
3783Both the string parameter \cq{title} and the game parameter structure
3784\cq{params} itself must be dynamically allocated by the caller. The
3785preset-menu structure will take ownership of it, so the caller must
3786not free it.
3787
3788\S{utils-preset-menu-lookup-by-id} \cw{preset_menu_lookup_by_id()}
3789
3790\c game_params *preset_menu_lookup_by_id
3791\c (struct preset_menu *menu, int id);
3792
3793Given a numeric index, searches recursively through a preset menu
3794hierarchy to find the corresponding menu entry, and returns a pointer
3795to its existing \c{game_params} structure.
3796
3797This function is intended for front end use (but front ends need not
3798use it if they prefer to do things another way). If a front end finds
3799it inconvenient to store anything more than a numeric index alongside
3800each menu item, then this function provides an easy way for the front
3801end to get back the actual game parameters corresponding to a menu
3802item that the user has selected.
3803
3804\H{utils-alloc} Memory allocation
3805
3806Puzzles has some central wrappers on the standard memory allocation
3807functions, which provide compile-time type checking, and run-time
3808error checking by means of quitting the application if it runs out
3809of memory. This doesn't provide the best possible recovery from
3810memory shortage, but on the other hand it greatly simplifies the
3811rest of the code, because nothing else anywhere needs to worry about
3812\cw{NULL} returns from allocation.
3813
3814\S{utils-snew} \cw{snew()}
3815
3816\c var = snew(type);
3817\e iii iiii
3818
3819This macro takes a single argument which is a \e{type name}. It
3820allocates space for one object of that type. If allocation fails it
3821will call \cw{fatal()} and not return; so if it does return, you can
3822be confident that its return value is non-\cw{NULL}.
3823
3824The return value is cast to the specified type, so that the compiler
3825will type-check it against the variable you assign it into. Thus,
3826this ensures you don't accidentally allocate memory the size of the
3827wrong type and assign it into a variable of the right one (or vice
3828versa!).
3829
3830\S{utils-snewn} \cw{snewn()}
3831
3832\c var = snewn(n, type);
3833\e iii i iiii
3834
3835This macro is the array form of \cw{snew()}. It takes two arguments;
3836the first is a number, and the second is a type name. It allocates
3837space for that many objects of that type, and returns a type-checked
3838non-\cw{NULL} pointer just as \cw{snew()} does.
3839
3840\S{utils-sresize} \cw{sresize()}
3841
3842\c var = sresize(var, n, type);
3843\e iii iii i iiii
3844
3845This macro is a type-checked form of \cw{realloc()}. It takes three
3846arguments: an input memory block, a new size in elements, and a
3847type. It re-sizes the input memory block to a size sufficient to
3848contain that many elements of that type. It returns a type-checked
3849non-\cw{NULL} pointer, like \cw{snew()} and \cw{snewn()}.
3850
3851The input memory block can be \cw{NULL}, in which case this function
3852will behave exactly like \cw{snewn()}. (In principle any
3853ANSI-compliant \cw{realloc()} implementation ought to cope with
3854this, but I've never quite trusted it to work everywhere.)
3855
3856\S{utils-sfree} \cw{sfree()}
3857
3858\c void sfree(void *p);
3859
3860This function is pretty much equivalent to \cw{free()}. It is
3861provided with a dynamically allocated block, and frees it.
3862
3863The input memory block can be \cw{NULL}, in which case this function
3864will do nothing. (In principle any ANSI-compliant \cw{free()}
3865implementation ought to cope with this, but I've never quite trusted
3866it to work everywhere.)
3867
3868\S{utils-dupstr} \cw{dupstr()}
3869
3870\c char *dupstr(const char *s);
3871
3872This function dynamically allocates a duplicate of a C string. Like
3873the \cw{snew()} functions, it guarantees to return non-\cw{NULL} or
3874not return at all.
3875
3876(Many platforms provide the function \cw{strdup()}. As well as
3877guaranteeing never to return \cw{NULL}, my version has the advantage
3878of being defined \e{everywhere}, rather than inconveniently not
3879quite everywhere.)
3880
3881\S{utils-free-cfg} \cw{free_cfg()}
3882
3883\c void free_cfg(config_item *cfg);
3884
3885This function correctly frees an array of \c{config_item}s, including
3886walking the array until it gets to the end and freeing any subsidiary
3887data items in each \c{u} sub-union which are expected to be
3888dynamically allocated.
3889
3890(See \k{backend-configure} for details of the \c{config_item}
3891structure.)
3892
3893\H{utils-tree234} Sorted and counted tree functions
3894
3895Many games require complex algorithms for generating random puzzles,
3896and some require moderately complex algorithms even during play. A
3897common requirement during these algorithms is for a means of
3898maintaining sorted or unsorted lists of items, such that items can
3899be removed and added conveniently.
3900
3901For general use, Puzzles provides the following set of functions
3902which maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced
3903tree structure, with the property that all lookups, insertions,
3904deletions, splits and joins can be done in \cw{O(log N)} time.)
3905
3906All these functions expect you to be storing a tree of \c{void *}
3907pointers. You can put anything you like in those pointers.
3908
3909By the use of per-node element counts, these tree structures have
3910the slightly unusual ability to look elements up by their numeric
3911index within the list represented by the tree. This means that they
3912can be used to store an unsorted list (in which case, every time you
3913insert a new element, you must explicitly specify the position where
3914you wish to insert it). They can also do numeric lookups in a sorted
3915tree, which might be useful for (for example) tracking the median of
3916a changing data set.
3917
3918As well as storing sorted lists, these functions can be used for
3919storing \q{maps} (associative arrays), by defining each element of a
3920tree to be a (key, value) pair.
3921
3922\S{utils-newtree234} \cw{newtree234()}
3923
3924\c tree234 *newtree234(cmpfn234 cmp);
3925
3926Creates a new empty tree, and returns a pointer to it.
3927
3928The parameter \c{cmp} determines the sorting criterion on the tree.
3929Its prototype is
3930
3931\c typedef int (*cmpfn234)(void *, void *);
3932
3933If you want a sorted tree, you should provide a function matching
3934this prototype, which returns like \cw{strcmp()} does (negative if
3935the first argument is smaller than the second, positive if it is
3936bigger, zero if they compare equal). In this case, the function
3937\cw{addpos234()} will not be usable on your tree (because all
3938insertions must respect the sorting order).
3939
3940If you want an unsorted tree, pass \cw{NULL}. In this case you will
3941not be able to use either \cw{add234()} or \cw{del234()}, or any
3942other function such as \cw{find234()} which depends on a sorting
3943order. Your tree will become something more like an array, except
3944that it will efficiently support insertion and deletion as well as
3945lookups by numeric index.
3946
3947\S{utils-freetree234} \cw{freetree234()}
3948
3949\c void freetree234(tree234 *t);
3950
3951Frees a tree. This function will not free the \e{elements} of the
3952tree (because they might not be dynamically allocated, or you might
3953be storing the same set of elements in more than one tree); it will
3954just free the tree structure itself. If you want to free all the
3955elements of a tree, you should empty it before passing it to
3956\cw{freetree234()}, by means of code along the lines of
3957
3958\c while ((element = delpos234(tree, 0)) != NULL)
3959\c sfree(element); /* or some more complicated free function */
3960\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
3961
3962\S{utils-add234} \cw{add234()}
3963
3964\c void *add234(tree234 *t, void *e);
3965
3966Inserts a new element \c{e} into the tree \c{t}. This function
3967expects the tree to be sorted; the new element is inserted according
3968to the sort order.
3969
3970If an element comparing equal to \c{e} is already in the tree, then
3971the insertion will fail, and the return value will be the existing
3972element. Otherwise, the insertion succeeds, and \c{e} is returned.
3973
3974\S{utils-addpos234} \cw{addpos234()}
3975
3976\c void *addpos234(tree234 *t, void *e, int index);
3977
3978Inserts a new element into an unsorted tree. Since there is no
3979sorting order to dictate where the new element goes, you must
3980specify where you want it to go. Setting \c{index} to zero puts the
3981new element right at the start of the list; setting \c{index} to the
3982current number of elements in the tree puts the new element at the
3983end.
3984
3985Return value is \c{e}, in line with \cw{add234()} (although this
3986function cannot fail except by running out of memory, in which case
3987it will bomb out and die rather than returning an error indication).
3988
3989\S{utils-index234} \cw{index234()}
3990
3991\c void *index234(tree234 *t, int index);
3992
3993Returns a pointer to the \c{index}th element of the tree, or
3994\cw{NULL} if \c{index} is out of range. Elements of the tree are
3995numbered from zero.
3996
3997\S{utils-find234} \cw{find234()}
3998
3999\c void *find234(tree234 *t, void *e, cmpfn234 cmp);
4000
4001Searches for an element comparing equal to \c{e} in a sorted tree.
4002
4003If \c{cmp} is \cw{NULL}, the tree's ordinary comparison function
4004will be used to perform the search. However, sometimes you don't
4005want that; suppose, for example, each of your elements is a big
4006structure containing a \c{char *} name field, and you want to find
4007the element with a given name. You \e{could} achieve this by
4008constructing a fake element structure, setting its name field
4009appropriately, and passing it to \cw{find234()}, but you might find
4010it more convenient to pass \e{just} a name string to \cw{find234()},
4011supplying an alternative comparison function which expects one of
4012its arguments to be a bare name and the other to be a large
4013structure containing a name field.
4014
4015Therefore, if \c{cmp} is not \cw{NULL}, then it will be used to
4016compare \c{e} to elements of the tree. The first argument passed to
4017\c{cmp} will always be \c{e}; the second will be an element of the
4018tree.
4019
4020(See \k{utils-newtree234} for the definition of the \c{cmpfn234}
4021function pointer type.)
4022
4023The returned value is the element found, or \cw{NULL} if the search
4024is unsuccessful.
4025
4026\S{utils-findrel234} \cw{findrel234()}
4027
4028\c void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation);
4029
4030This function is like \cw{find234()}, but has the additional ability
4031to do a \e{relative} search. The additional parameter \c{relation}
4032can be one of the following values:
4033
4034\dt \cw{REL234_EQ}
4035
4036\dd Find only an element that compares equal to \c{e}. This is
4037exactly the behaviour of \cw{find234()}.
4038
4039\dt \cw{REL234_LT}
4040
4041\dd Find the greatest element that compares strictly less than
4042\c{e}. \c{e} may be \cw{NULL}, in which case it finds the greatest
4043element in the whole tree (which could also be done by
4044\cw{index234(t, count234(t)-1)}).
4045
4046\dt \cw{REL234_LE}
4047
4048\dd Find the greatest element that compares less than or equal to
4049\c{e}. (That is, find an element that compares equal to \c{e} if
4050possible, but failing that settle for something just less than it.)
4051
4052\dt \cw{REL234_GT}
4053
4054\dd Find the smallest element that compares strictly greater than
4055\c{e}. \c{e} may be \cw{NULL}, in which case it finds the smallest
4056element in the whole tree (which could also be done by
4057\cw{index234(t, 0)}).
4058
4059\dt \cw{REL234_GE}
4060
4061\dd Find the smallest element that compares greater than or equal to
4062\c{e}. (That is, find an element that compares equal to \c{e} if
4063possible, but failing that settle for something just bigger than
4064it.)
4065
4066Return value, as before, is the element found or \cw{NULL} if no
4067element satisfied the search criterion.
4068
4069\S{utils-findpos234} \cw{findpos234()}
4070
4071\c void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index);
4072
4073This function is like \cw{find234()}, but has the additional feature
4074of returning the index of the element found in the tree; that index
4075is written to \c{*index} in the event of a successful search (a
4076non-\cw{NULL} return value).
4077
4078\c{index} may be \cw{NULL}, in which case this function behaves
4079exactly like \cw{find234()}.
4080
4081\S{utils-findrelpos234} \cw{findrelpos234()}
4082
4083\c void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation,
4084\c int *index);
4085
4086This function combines all the features of \cw{findrel234()} and
4087\cw{findpos234()}.
4088
4089\S{utils-del234} \cw{del234()}
4090
4091\c void *del234(tree234 *t, void *e);
4092
4093Finds an element comparing equal to \c{e} in the tree, deletes it,
4094and returns it.
4095
4096The input tree must be sorted.
4097
4098The element found might be \c{e} itself, or might merely compare
4099equal to it.
4100
4101Return value is \cw{NULL} if no such element is found.
4102
4103\S{utils-delpos234} \cw{delpos234()}
4104
4105\c void *delpos234(tree234 *t, int index);
4106
4107Deletes the element at position \c{index} in the tree, and returns
4108it.
4109
4110Return value is \cw{NULL} if the index is out of range.
4111
4112\S{utils-count234} \cw{count234()}
4113
4114\c int count234(tree234 *t);
4115
4116Returns the number of elements currently in the tree.
4117
4118\S{utils-splitpos234} \cw{splitpos234()}
4119
4120\c tree234 *splitpos234(tree234 *t, int index, bool before);
4121
4122Splits the input tree into two pieces at a given position, and
4123creates a new tree containing all the elements on one side of that
4124position.
4125
4126If \c{before} is \cw{true}, then all the items at or after position
4127\c{index} are left in the input tree, and the items before that
4128point are returned in the new tree. Otherwise, the reverse happens:
4129all the items at or after \c{index} are moved into the new tree, and
4130those before that point are left in the old one.
4131
4132If \c{index} is equal to 0 or to the number of elements in the input
4133tree, then one of the two trees will end up empty (and this is not
4134an error condition). If \c{index} is further out of range in either
4135direction, the operation will fail completely and return \cw{NULL}.
4136
4137This operation completes in \cw{O(log N)} time, no matter how large
4138the tree or how balanced or unbalanced the split.
4139
4140\S{utils-split234} \cw{split234()}
4141
4142\c tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel);
4143
4144Splits a sorted tree according to its sort order.
4145
4146\c{rel} can be any of the relation constants described in
4147\k{utils-findrel234}, \e{except} for \cw{REL234_EQ}. All the
4148elements having that relation to \c{e} will be transferred into the
4149new tree; the rest will be left in the old one.
4150
4151The parameter \c{cmp} has the same semantics as it does in
4152\cw{find234()}: if it is not \cw{NULL}, it will be used in place of
4153the tree's own comparison function when comparing elements to \c{e},
4154in such a way that \c{e} itself is always the first of its two
4155operands.
4156
4157Again, this operation completes in \cw{O(log N)} time, no matter how
4158large the tree or how balanced or unbalanced the split.
4159
4160\S{utils-join234} \cw{join234()}
4161
4162\c tree234 *join234(tree234 *t1, tree234 *t2);
4163
4164Joins two trees together by concatenating the lists they represent.
4165All the elements of \c{t2} are moved into \c{t1}, in such a way that
4166they appear \e{after} the elements of \c{t1}. The tree \c{t2} is
4167freed; the return value is \c{t1}.
4168
4169If you apply this function to a sorted tree and it violates the sort
4170order (i.e. the smallest element in \c{t2} is smaller than or equal
4171to the largest element in \c{t1}), the operation will fail and
4172return \cw{NULL}.
4173
4174This operation completes in \cw{O(log N)} time, no matter how large
4175the trees being joined together.
4176
4177\S{utils-join234r} \cw{join234r()}
4178
4179\c tree234 *join234r(tree234 *t1, tree234 *t2);
4180
4181Joins two trees together in exactly the same way as \cw{join234()},
4182but this time the combined tree is returned in \c{t2}, and \c{t1} is
4183destroyed. The elements in \c{t1} still appear before those in
4184\c{t2}.
4185
4186Again, this operation completes in \cw{O(log N)} time, no matter how
4187large the trees being joined together.
4188
4189\S{utils-copytree234} \cw{copytree234()}
4190
4191\c tree234 *copytree234(tree234 *t, copyfn234 copyfn,
4192\c void *copyfnstate);
4193
4194Makes a copy of an entire tree.
4195
4196If \c{copyfn} is \cw{NULL}, the tree will be copied but the elements
4197will not be; i.e. the new tree will contain pointers to exactly the
4198same physical elements as the old one.
4199
4200If you want to copy each actual element during the operation, you
4201can instead pass a function in \c{copyfn} which makes a copy of each
4202element. That function has the prototype
4203
4204\c typedef void *(*copyfn234)(void *state, void *element);
4205
4206and every time it is called, the \c{state} parameter will be set to
4207the value you passed in as \c{copyfnstate}.
4208
4209\H{utils-misc} Miscellaneous utility functions and macros
4210
4211This section contains all the utility functions which didn't
4212sensibly fit anywhere else.
4213
4214\S{utils-truefalse} \cw{TRUE} and \cw{FALSE}
4215
4216The main Puzzles header file defines the macros \cw{TRUE} and
4217\cw{FALSE}, which are used throughout the code in place of 1 and 0
4218(respectively) to indicate that the values are in a boolean context.
4219For code base consistency, I'd prefer it if submissions of new code
4220followed this convention as well.
4221
4222\S{utils-maxmin} \cw{max()} and \cw{min()}
4223
4224The main Puzzles header file defines the pretty standard macros
4225\cw{max()} and \cw{min()}, each of which is given two arguments and
4226returns the one which compares greater or less respectively.
4227
4228These macros may evaluate their arguments multiple times. Avoid side
4229effects.
4230
4231\S{utils-pi} \cw{PI}
4232
4233The main Puzzles header file defines a macro \cw{PI} which expands
4234to a floating-point constant representing pi.
4235
4236(I've never understood why ANSI's \cw{<math.h>} doesn't define this.
4237It'd be so useful!)
4238
4239\S{utils-obfuscate-bitmap} \cw{obfuscate_bitmap()}
4240
4241\c void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
4242
4243This function obscures the contents of a piece of data, by
4244cryptographic methods. It is useful for games of hidden information
4245(such as Mines, Guess or Black Box), in which the game ID
4246theoretically reveals all the information the player is supposed to
4247be trying to guess. So in order that players should be able to send
4248game IDs to one another without accidentally spoiling the resulting
4249game by looking at them, these games obfuscate their game IDs using
4250this function.
4251
4252Although the obfuscation function is cryptographic, it cannot
4253properly be called encryption because it has no key. Therefore,
4254anybody motivated enough can re-implement it, or hack it out of the
4255Puzzles source, and strip the obfuscation off one of these game IDs
4256to see what lies beneath. (Indeed, they could usually do it much
4257more easily than that, by entering the game ID into their own copy
4258of the puzzle and hitting Solve.) The aim is not to protect against
4259a determined attacker; the aim is simply to protect people who
4260wanted to play the game honestly from \e{accidentally} spoiling
4261their own fun.
4262
4263The input argument \c{bmp} points at a piece of memory to be
4264obfuscated. \c{bits} gives the length of the data. Note that that
4265length is in \e{bits} rather than bytes: if you ask for obfuscation
4266of a partial number of bytes, then you will get it. Bytes are
4267considered to be used from the top down: thus, for example, setting
4268\c{bits} to 10 will cover the whole of \cw{bmp[0]} and the \e{top
4269two} bits of \cw{bmp[1]}. The remainder of a partially used byte is
4270undefined (i.e. it may be corrupted by the function).
4271
4272The parameter \c{decode} is \cw{FALSE} for an encoding operation,
4273and \cw{TRUE} for a decoding operation. Each is the inverse of the
4274other. (There's no particular reason you shouldn't obfuscate by
4275decoding and restore cleartext by encoding, if you really wanted to;
4276it should still work.)
4277
4278The input bitmap is processed in place.
4279
4280\S{utils-bin2hex} \cw{bin2hex()}
4281
4282\c char *bin2hex(const unsigned char *in, int inlen);
4283
4284This function takes an input byte array and converts it into an
4285ASCII string encoding those bytes in (lower-case) hex. It returns a
4286dynamically allocated string containing that encoding.
4287
4288This function is useful for encoding the result of
4289\cw{obfuscate_bitmap()} in printable ASCII for use in game IDs.
4290
4291\S{utils-hex2bin} \cw{hex2bin()}
4292
4293\c unsigned char *hex2bin(const char *in, int outlen);
4294
4295This function takes an ASCII string containing hex digits, and
4296converts it back into a byte array of length \c{outlen}. If there
4297aren't enough hex digits in the string, the contents of the
4298resulting array will be undefined.
4299
4300This function is the inverse of \cw{bin2hex()}.
4301
4302\S{utils-game-mkhighlight} \cw{game_mkhighlight()}
4303
4304\c void game_mkhighlight(frontend *fe, float *ret,
4305\c int background, int highlight, int lowlight);
4306
4307It's reasonably common for a puzzle game's graphics to use
4308highlights and lowlights to indicate \q{raised} or \q{lowered}
4309sections. Fifteen, Sixteen and Twiddle are good examples of this.
4310
4311Puzzles using this graphical style are running a risk if they just
4312use whatever background colour is supplied to them by the front end,
4313because that background colour might be too light to see any
4314highlights on at all. (In particular, it's not unheard of for the
4315front end to specify a default background colour of white.)
4316
4317Therefore, such puzzles can call this utility function from their
4318\cw{colours()} routine (\k{backend-colours}). You pass it your front
4319end handle, a pointer to the start of your return array, and three
4320colour indices. It will:
4321
4322\b call \cw{frontend_default_colour()} (\k{frontend-default-colour})
4323to fetch the front end's default background colour
4324
4325\b alter the brightness of that colour if it's unsuitable
4326
4327\b define brighter and darker variants of the colour to be used as
4328highlights and lowlights
4329
4330\b write those results into the relevant positions in the \c{ret}
4331array.
4332
4333Thus, \cw{ret[background*3]} to \cw{ret[background*3+2]} will be set
4334to RGB values defining a sensible background colour, and similary
4335\c{highlight} and \c{lowlight} will be set to sensible colours.
4336
4337\S{utils-button2label} \cw{button2label()}
4338
4339\c char *button2label(int button);
4340
4341This function generates a descriptive text label for \cw{button},
4342which should be a button code that can be passed to the midend. For
4343example, calling this function with \cw{CURSOR_UP} will result in the
4344string \cw{"Up"}. This function should only be called when the
4345\cw{key_label} item returned by a backend's \cw{request_keys()}
4346(\k{backend-request-keys}) function has its \cw{label} field set to
4347\cw{NULL}; in this case, the corresponding \cw{button} field can be
4348passed to this function to obtain an appropriate label. If, however,
4349the field is not \cw{NULL}, this function should not be called with
4350the corresponding \cw{button} field.
4351
4352The returned string is dynamically allocated and should be
4353\cw{sfree}'d by the caller.
4354
4355\C{writing} How to write a new puzzle
4356
4357This chapter gives a guide to how to actually write a new puzzle:
4358where to start, what to do first, how to solve common problems.
4359
4360The previous chapters have been largely composed of facts. This one
4361is mostly advice.
4362
4363\H{writing-editorial} Choosing a puzzle
4364
4365Before you start writing a puzzle, you have to choose one. Your
4366taste in puzzle games is up to you, of course; and, in fact, you're
4367probably reading this guide because you've \e{already} thought of a
4368game you want to write. But if you want to get it accepted into the
4369official Puzzles distribution, then there's a criterion it has to
4370meet.
4371
4372The current Puzzles editorial policy is that all games should be
4373\e{fair}. A fair game is one which a player can only fail to
4374complete through demonstrable lack of skill \dash that is, such that
4375a better player in the same situation would have \e{known} to do
4376something different.
4377
4378For a start, that means every game presented to the user must have
4379\e{at least one solution}. Giving the unsuspecting user a puzzle
4380which is actually impossible is not acceptable. (There is an
4381exception: if the user has selected some non-default option which is
4382clearly labelled as potentially unfair, \e{then} you're allowed to
4383generate possibly insoluble puzzles, because the user isn't
4384unsuspecting any more. Same Game and Mines both have options of this
4385type.)
4386
4387Also, this actually \e{rules out} games such as Klondike, or the
4388normal form of Mahjong Solitaire. Those games have the property that
4389even if there is a solution (i.e. some sequence of moves which will
4390get from the start state to the solved state), the player doesn't
4391necessarily have enough information to \e{find} that solution. In
4392both games, it is possible to reach a dead end because you had an
4393arbitrary choice to make and made it the wrong way. This violates
4394the fairness criterion, because a better player couldn't have known
4395they needed to make the other choice.
4396
4397(GNOME has a variant on Mahjong Solitaire which makes it fair: there
4398is a Shuffle operation which randomly permutes all the remaining
4399tiles without changing their positions, which allows you to get out
4400of a sticky situation. Using this operation adds a 60-second penalty
4401to your solution time, so it's to the player's advantage to try to
4402minimise the chance of having to use it. It's still possible to
4403render the game uncompletable if you end up with only two tiles
4404vertically stacked, but that's easy to foresee and avoid using a
4405shuffle operation. This form of the game \e{is} fair. Implementing
4406it in Puzzles would require an infrastructure change so that the
4407back end could communicate time penalties to the mid-end, but that
4408would be easy enough.)
4409
4410Providing a \e{unique} solution is a little more negotiable; it
4411depends on the puzzle. Solo would have been of unacceptably low
4412quality if it didn't always have a unique solution, whereas Twiddle
4413inherently has multiple solutions by its very nature and it would
4414have been meaningless to even \e{suggest} making it uniquely
4415soluble. Somewhere in between, Flip could reasonably be made to have
4416unique solutions (by enforcing a zero-dimension kernel in every
4417generated matrix) but it doesn't seem like a serious quality problem
4418that it doesn't.
4419
4420Of course, you don't \e{have} to care about all this. There's
4421nothing stopping you implementing any puzzle you want to if you're
4422happy to maintain your puzzle yourself, distribute it from your own
4423web site, fork the Puzzles code completely, or anything like that.
4424It's free software; you can do what you like with it. But any game
4425that you want to be accepted into \e{my} Puzzles code base has to
4426satisfy the fairness criterion, which means all randomly generated
4427puzzles must have a solution (unless the user has deliberately
4428chosen otherwise) and it must be possible \e{in theory} to find that
4429solution without having to guess.
4430
4431\H{writing-gs} Getting started
4432
4433The simplest way to start writing a new puzzle is to copy
4434\c{nullgame.c}. This is a template puzzle source file which does
4435almost nothing, but which contains all the back end function
4436prototypes and declares the back end data structure correctly. It is
4437built every time the rest of Puzzles is built, to ensure that it
4438doesn't get out of sync with the code and remains buildable.
4439
4440So start by copying \c{nullgame.c} into your new source file. Then
4441you'll gradually add functionality until the very boring Null Game
4442turns into your real game.
4443
4444Next you'll need to add your puzzle to the Makefiles, in order to
4445compile it conveniently. \e{Do not edit the Makefiles}: they are
4446created automatically by the script \c{mkfiles.pl}, from the file
4447called \c{Recipe}. Edit \c{Recipe}, and then re-run \c{mkfiles.pl}.
4448
4449Also, don't forget to add your puzzle to \c{list.c}: if you don't,
4450then it will still run fine on platforms which build each puzzle
4451separately, but Mac OS X and other monolithic platforms will not
4452include your new puzzle in their single binary.
4453
4454Once your source file is building, you can move on to the fun bit.
4455
4456\S{writing-generation} Puzzle generation
4457
4458Randomly generating instances of your puzzle is almost certain to be
4459the most difficult part of the code, and also the task with the
4460highest chance of turning out to be completely infeasible. Therefore
4461I strongly recommend doing it \e{first}, so that if it all goes
4462horribly wrong you haven't wasted any more time than you absolutely
4463had to. What I usually do is to take an unmodified \c{nullgame.c},
4464and start adding code to \cw{new_game_desc()} which tries to
4465generate a puzzle instance and print it out using \cw{printf()}.
4466Once that's working, \e{then} I start connecting it up to the return
4467value of \cw{new_game_desc()}, populating other structures like
4468\c{game_params}, and generally writing the rest of the source file.
4469
4470There are many ways to generate a puzzle which is known to be
4471soluble. In this section I list all the methods I currently know of,
4472in case any of them can be applied to your puzzle. (Not all of these
4473methods will work, or in some cases even make sense, for all
4474puzzles.)
4475
4476Some puzzles are mathematically tractable, meaning you can work out
4477in advance which instances are soluble. Sixteen, for example, has a
4478parity constraint in some settings which renders exactly half the
4479game space unreachable, but it can be mathematically proved that any
4480position not in that half \e{is} reachable. Therefore, Sixteen's
4481grid generation simply consists of selecting at random from a well
4482defined subset of the game space. Cube in its default state is even
4483easier: \e{every} possible arrangement of the blue squares and the
4484cube's starting position is soluble!
4485
4486Another option is to redefine what you mean by \q{soluble}. Black
4487Box takes this approach. There are layouts of balls in the box which
4488are completely indistinguishable from one another no matter how many
4489beams you fire into the box from which angles, which would normally
4490be grounds for declaring those layouts unfair; but fortunately,
4491detecting that indistinguishability is computationally easy. So
4492Black Box doesn't demand that your ball placements match its own; it
4493merely demands that your ball placements be \e{indistinguishable}
4494from the ones it was thinking of. If you have an ambiguous puzzle,
4495then any of the possible answers is considered to be a solution.
4496Having redefined the rules in that way, any puzzle is soluble again.
4497
4498Those are the simple techniques. If they don't work, you have to get
4499cleverer.
4500
4501One way to generate a soluble puzzle is to start from the solved
4502state and make inverse moves until you reach a starting state. Then
4503you know there's a solution, because you can just list the inverse
4504moves you made and make them in the opposite order to return to the
4505solved state.
4506
4507This method can be simple and effective for puzzles where you get to
4508decide what's a starting state and what's not. In Pegs, for example,
4509the generator begins with one peg in the centre of the board and
4510makes inverse moves until it gets bored; in this puzzle, valid
4511inverse moves are easy to detect, and \e{any} state that's reachable
4512from the solved state by inverse moves is a reasonable starting
4513position. So Pegs just continues making inverse moves until the
4514board satisfies some criteria about extent and density, and then
4515stops and declares itself done.
4516
4517For other puzzles, it can be a lot more difficult. Same Game uses
4518this strategy too, and it's lucky to get away with it at all: valid
4519inverse moves aren't easy to find (because although it's easy to
4520insert additional squares in a Same Game position, it's difficult to
4521arrange that \e{after} the insertion they aren't adjacent to any
4522other squares of the same colour), so you're constantly at risk of
4523running out of options and having to backtrack or start again. Also,
4524Same Game grids never start off half-empty, which means you can't
4525just stop when you run out of moves \dash you have to find a way to
4526fill the grid up \e{completely}.
4527
4528The other way to generate a puzzle that's soluble is to start from
4529the other end, and actually write a \e{solver}. This tends to ensure
4530that a puzzle has a \e{unique} solution over and above having a
4531solution at all, so it's a good technique to apply to puzzles for
4532which that's important.
4533
4534One theoretical drawback of generating soluble puzzles by using a
4535solver is that your puzzles are restricted in difficulty to those
4536which the solver can handle. (Most solvers are not fully general:
4537many sets of puzzle rules are NP-complete or otherwise nasty, so
4538most solvers can only handle a subset of the theoretically soluble
4539puzzles.) It's been my experience in practice, however, that this
4540usually isn't a problem; computers are good at very different things
4541from humans, and what the computer thinks is nice and easy might
4542still be pleasantly challenging for a human. For example, when
4543solving Dominosa puzzles I frequently find myself using a variety of
4544reasoning techniques that my solver doesn't know about; in
4545principle, therefore, I should be able to solve the puzzle using
4546only those techniques it \e{does} know about, but this would involve
4547repeatedly searching the entire grid for the one simple deduction I
4548can make. Computers are good at this sort of exhaustive search, but
4549it's been my experience that human solvers prefer to do more complex
4550deductions than to spend ages searching for simple ones. So in many
4551cases I don't find my own playing experience to be limited by the
4552restrictions on the solver.
4553
4554(This isn't \e{always} the case. Solo is a counter-example;
4555generating Solo puzzles using a simple solver does lead to
4556qualitatively easier puzzles. Therefore I had to make the Solo
4557solver rather more advanced than most of them.)
4558
4559There are several different ways to apply a solver to the problem of
4560generating a soluble puzzle. I list a few of them below.
4561
4562The simplest approach is brute force: randomly generate a puzzle,
4563use the solver to see if it's soluble, and if not, throw it away and
4564try again until you get lucky. This is often a viable technique if
4565all else fails, but it tends not to scale well: for many puzzle
4566types, the probability of finding a uniquely soluble instance
4567decreases sharply as puzzle size goes up, so this technique might
4568work reasonably fast for small puzzles but take (almost) forever at
4569larger sizes. Still, if there's no other alternative it can be
4570usable: Pattern and Dominosa both use this technique. (However,
4571Dominosa has a means of tweaking the randomly generated grids to
4572increase the \e{probability} of them being soluble, by ruling out
4573one of the most common ambiguous cases. This improved generation
4574speed by over a factor of 10 on the highest preset!)
4575
4576An approach which can be more scalable involves generating a grid
4577and then tweaking it to make it soluble. This is the technique used
4578by Mines and also by Net: first a random puzzle is generated, and
4579then the solver is run to see how far it gets. Sometimes the solver
4580will get stuck; when that happens, examine the area it's having
4581trouble with, and make a small random change in that area to allow
4582it to make more progress. Continue solving (possibly even without
4583restarting the solver), tweaking as necessary, until the solver
4584finishes. Then restart the solver from the beginning to ensure that
4585the tweaks haven't caused new problems in the process of solving old
4586ones (which can sometimes happen).
4587
4588This strategy works well in situations where the usual solver
4589failure mode is to get stuck in an easily localised spot. Thus it
4590works well for Net and Mines, whose most common failure mode tends
4591to be that most of the grid is fine but there are a few widely
4592separated ambiguous sections; but it would work less well for
4593Dominosa, in which the way you get stuck is to have scoured the
4594whole grid and not found anything you can deduce \e{anywhere}. Also,
4595it relies on there being a low probability that tweaking the grid
4596introduces a new problem at the same time as solving the old one;
4597Mines and Net also have the property that most of their deductions
4598are local, so that it's very unlikely for a tweak to affect
4599something half way across the grid from the location where it was
4600applied. In Dominosa, by contrast, a lot of deductions use
4601information about half the grid (\q{out of all the sixes, only one
4602is next to a three}, which can depend on the values of up to 32 of
4603the 56 squares in the default setting!), so this tweaking strategy
4604would be rather less likely to work well.
4605
4606A more specialised strategy is that used in Solo and Slant. These
4607puzzles have the property that they derive their difficulty from not
4608presenting all the available clues. (In Solo's case, if all the
4609possible clues were provided then the puzzle would already be
4610solved; in Slant it would still require user action to fill in the
4611lines, but it would present no challenge at all). Therefore, a
4612simple generation technique is to leave the decision of which clues
4613to provide until the last minute. In other words, first generate a
4614random \e{filled} grid with all possible clues present, and then
4615gradually remove clues for as long as the solver reports that it's
4616still soluble. Unlike the methods described above, this technique
4617\e{cannot} fail \dash once you've got a filled grid, nothing can
4618stop you from being able to convert it into a viable puzzle.
4619However, it wouldn't even be meaningful to apply this technique to
4620(say) Pattern, in which clues can never be left out, so the only way
4621to affect the set of clues is by altering the solution.
4622
4623(Unfortunately, Solo is complicated by the need to provide puzzles
4624at varying difficulty levels. It's easy enough to generate a puzzle
4625of \e{at most} a given level of difficulty; you just have a solver
4626with configurable intelligence, and you set it to a given level and
4627apply the above technique, thus guaranteeing that the resulting grid
4628is solvable by someone with at most that much intelligence. However,
4629generating a puzzle of \e{at least} a given level of difficulty is
4630rather harder; if you go for \e{at most} Intermediate level, you're
4631likely to find that you've accidentally generated a Trivial grid a
4632lot of the time, because removing just one number is sufficient to
4633take the puzzle from Trivial straight to Ambiguous. In that
4634situation Solo has no remaining options but to throw the puzzle away
4635and start again.)
4636
4637A final strategy is to use the solver \e{during} puzzle
4638construction: lay out a bit of the grid, run the solver to see what
4639it allows you to deduce, and then lay out a bit more to allow the
4640solver to make more progress. There are articles on the web that
4641recommend constructing Sudoku puzzles by this method (which is
4642completely the opposite way round to how Solo does it); for Sudoku
4643it has the advantage that you get to specify your clue squares in
4644advance (so you can have them make pretty patterns).
4645
4646Rectangles uses a strategy along these lines. First it generates a
4647grid by placing the actual rectangles; then it has to decide where
4648in each rectangle to place a number. It uses a solver to help it
4649place the numbers in such a way as to ensure a unique solution. It
4650does this by means of running a test solver, but it runs the solver
4651\e{before} it's placed any of the numbers \dash which means the
4652solver must be capable of coping with uncertainty about exactly
4653where the numbers are! It runs the solver as far as it can until it
4654gets stuck; then it narrows down the possible positions of a number
4655in order to allow the solver to make more progress, and so on. Most
4656of the time this process terminates with the grid fully solved, at
4657which point any remaining number-placement decisions can be made at
4658random from the options not so far ruled out. Note that unlike the
4659Net/Mines tweaking strategy described above, this algorithm does not
4660require a checking run after it completes: if it finishes
4661successfully at all, then it has definitely produced a uniquely
4662soluble puzzle.
4663
4664Most of the strategies described above are not 100% reliable. Each
4665one has a failure rate: every so often it has to throw out the whole
4666grid and generate a fresh one from scratch. (Solo's strategy would
4667be the exception, if it weren't for the need to provide configurable
4668difficulty levels.) Occasional failures are not a fundamental
4669problem in this sort of work, however: it's just a question of
4670dividing the grid generation time by the success rate (if it takes
467110ms to generate a candidate grid and 1/5 of them work, then it will
4672take 50ms on average to generate a viable one), and seeing whether
4673the expected time taken to \e{successfully} generate a puzzle is
4674unacceptably slow. Dominosa's generator has a very low success rate
4675(about 1 out of 20 candidate grids turn out to be usable, and if you
4676think \e{that's} bad then go and look at the source code and find
4677the comment showing what the figures were before the generation-time
4678tweaks!), but the generator itself is very fast so this doesn't
4679matter. Rectangles has a slower generator, but fails well under 50%
4680of the time.
4681
4682So don't be discouraged if you have an algorithm that doesn't always
4683work: if it \e{nearly} always works, that's probably good enough.
4684The one place where reliability is important is that your algorithm
4685must never produce false positives: it must not claim a puzzle is
4686soluble when it isn't. It can produce false negatives (failing to
4687notice that a puzzle is soluble), and it can fail to generate a
4688puzzle at all, provided it doesn't do either so often as to become
4689slow.
4690
4691One last piece of advice: for grid-based puzzles, when writing and
4692testing your generation algorithm, it's almost always a good idea
4693\e{not} to test it initially on a grid that's square (i.e.
4694\cw{w==h}), because if the grid is square then you won't notice if
4695you mistakenly write \c{h} instead of \c{w} (or vice versa)
4696somewhere in the code. Use a rectangular grid for testing, and any
4697size of grid will be likely to work after that.
4698
4699\S{writing-textformats} Designing textual description formats
4700
4701Another aspect of writing a puzzle which is worth putting some
4702thought into is the design of the various text description formats:
4703the format of the game parameter encoding, the game description
4704encoding, and the move encoding.
4705
4706The first two of these should be reasonably intuitive for a user to
4707type in; so provide some flexibility where possible. Suppose, for
4708example, your parameter format consists of two numbers separated by
4709an \c{x} to specify the grid dimensions (\c{10x10} or \c{20x15}),
4710and then has some suffixes to specify other aspects of the game
4711type. It's almost always a good idea in this situation to arrange
4712that \cw{decode_params()} can handle the suffixes appearing in any
4713order, even if \cw{encode_params()} only ever generates them in one
4714order.
4715
4716These formats will also be expected to be reasonably stable: users
4717will expect to be able to exchange game IDs with other users who
4718aren't running exactly the same version of your game. So make them
4719robust and stable: don't build too many assumptions into the game ID
4720format which will have to be changed every time something subtle
4721changes in the puzzle code.
4722
4723\H{writing-howto} Common how-to questions
4724
4725This section lists some common things people want to do when writing
4726a puzzle, and describes how to achieve them within the Puzzles
4727framework.
4728
4729\S{writing-howto-cursor} Drawing objects at only one position
4730
4731A common phenomenon is to have an object described in the
4732\c{game_state} or the \c{game_ui} which can only be at one position.
4733A cursor \dash probably specified in the \c{game_ui} \dash is a good
4734example.
4735
4736In the \c{game_ui}, it would \e{obviously} be silly to have an array
4737covering the whole game grid with a boolean flag stating whether the
4738cursor was at each position. Doing that would waste space, would
4739make it difficult to find the cursor in order to do anything with
4740it, and would introduce the potential for synchronisation bugs in
4741which you ended up with two cursors or none. The obviously sensible
4742way to store a cursor in the \c{game_ui} is to have fields directly
4743encoding the cursor's coordinates.
4744
4745However, it is a mistake to assume that the same logic applies to
4746the \c{game_drawstate}. If you replicate the cursor position fields
4747in the draw state, the redraw code will get very complicated. In the
4748draw state, in fact, it \e{is} probably the right thing to have a
4749cursor flag for every position in the grid. You probably have an
4750array for the whole grid in the drawstate already (stating what is
4751currently displayed in the window at each position); the sensible
4752approach is to add a \q{cursor} flag to each element of that array.
4753Then the main redraw loop will look something like this
4754(pseudo-code):
4755
4756\c for (y = 0; y < h; y++) {
4757\c for (x = 0; x < w; x++) {
4758\c int value = state->symbol_at_position[y][x];
4759\c if (x == ui->cursor_x && y == ui->cursor_y)
4760\c value |= CURSOR;
4761\c if (ds->symbol_at_position[y][x] != value) {
4762\c symbol_drawing_subroutine(dr, ds, x, y, value);
4763\c ds->symbol_at_position[y][x] = value;
4764\c }
4765\c }
4766\c }
4767
4768This loop is very simple, pretty hard to get wrong, and
4769\e{automatically} deals both with erasing the previous cursor and
4770drawing the new one, with no special case code required.
4771
4772This type of loop is generally a sensible way to write a redraw
4773function, in fact. The best thing is to ensure that the information
4774stored in the draw state for each position tells you \e{everything}
4775about what was drawn there. A good way to ensure that is to pass
4776precisely the same information, and \e{only} that information, to a
4777subroutine that does the actual drawing; then you know there's no
4778additional information which affects the drawing but which you don't
4779notice changes in.
4780
4781\S{writing-keyboard-cursor} Implementing a keyboard-controlled cursor
4782
4783It is often useful to provide a keyboard control method in a
4784basically mouse-controlled game. A keyboard-controlled cursor is
4785best implemented by storing its location in the \c{game_ui} (since
4786if it were in the \c{game_state} then the user would have to
4787separately undo every cursor move operation). So the procedure would
4788be:
4789
4790\b Put cursor position fields in the \c{game_ui}.
4791
4792\b \cw{interpret_move()} responds to arrow keys by modifying the
4793cursor position fields and returning \cw{""}.
4794
4795\b \cw{interpret_move()} responds to some sort of fire button by
4796actually performing a move based on the current cursor location.
4797
4798\b You might want an additional \c{game_ui} field stating whether
4799the cursor is currently visible, and having it disappear when a
4800mouse action occurs (so that it doesn't clutter the display when not
4801actually in use).
4802
4803\b You might also want to automatically hide the cursor in
4804\cw{changed_state()} when the current game state changes to one in
4805which there is no move to make (which is the case in some types of
4806completed game).
4807
4808\b \cw{redraw()} draws the cursor using the technique described in
4809\k{writing-howto-cursor}.
4810
4811\S{writing-howto-dragging} Implementing draggable sprites
4812
4813Some games have a user interface which involves dragging some sort
4814of game element around using the mouse. If you need to show a
4815graphic moving smoothly over the top of other graphics, use a
4816blitter (see \k{drawing-blitter} for the blitter API) to save the
4817background underneath it. The typical scenario goes:
4818
4819\b Have a blitter field in the \c{game_drawstate}.
4820
4821\b Set the blitter field to \cw{NULL} in the game's
4822\cw{new_drawstate()} function, since you don't yet know how big the
4823piece of saved background needs to be.
4824
4825\b In the game's \cw{set_size()} function, once you know the size of
4826the object you'll be dragging around the display and hence the
4827required size of the blitter, actually allocate the blitter.
4828
4829\b In \cw{free_drawstate()}, free the blitter if it's not \cw{NULL}.
4830
4831\b In \cw{interpret_move()}, respond to mouse-down and mouse-drag
4832events by updating some fields in the \cw{game_ui} which indicate
4833that a drag is in progress.
4834
4835\b At the \e{very end} of \cw{redraw()}, after all other drawing has
4836been done, draw the moving object if there is one. First save the
4837background under the object in the blitter; then set a clip
4838rectangle covering precisely the area you just saved (just in case
4839anti-aliasing or some other error causes your drawing to go beyond
4840the area you saved). Then draw the object, and call \cw{unclip()}.
4841Finally, set a flag in the \cw{game_drawstate} that indicates that
4842the blitter needs restoring.
4843
4844\b At the very start of \cw{redraw()}, before doing anything else at
4845all, check the flag in the \cw{game_drawstate}, and if it says the
4846blitter needs restoring then restore it. (Then clear the flag, so
4847that this won't happen again in the next redraw if no moving object
4848is drawn this time.)
4849
4850This way, you will be able to write the rest of the redraw function
4851completely ignoring the dragged object, as if it were floating above
4852your bitmap and being completely separate.
4853
4854\S{writing-ref-counting} Sharing large invariant data between all
4855game states
4856
4857In some puzzles, there is a large amount of data which never changes
4858between game states. The array of numbers in Dominosa is a good
4859example.
4860
4861You \e{could} dynamically allocate a copy of that array in every
4862\c{game_state}, and have \cw{dup_game()} make a fresh copy of it for
4863every new \c{game_state}; but it would waste memory and time. A
4864more efficient way is to use a reference-counted structure.
4865
4866\b Define a structure type containing the data in question, and also
4867containing an integer reference count.
4868
4869\b Have a field in \c{game_state} which is a pointer to this
4870structure.
4871
4872\b In \cw{new_game()}, when creating a fresh game state at the start
4873of a new game, create an instance of this structure, initialise it
4874with the invariant data, and set its reference count to 1.
4875
4876\b In \cw{dup_game()}, rather than making a copy of the structure
4877for the new game state, simply set the new game state to point at
4878the same copy of the structure, and increment its reference count.
4879
4880\b In \cw{free_game()}, decrement the reference count in the
4881structure pointed to by the game state; if the count reaches zero,
4882free the structure.
4883
4884This way, the invariant data will persist for only as long as it's
4885genuinely needed; \e{as soon} as the last game state for a
4886particular puzzle instance is freed, the invariant data for that
4887puzzle will vanish as well. Reference counting is a very efficient
4888form of garbage collection, when it works at all. (Which it does in
4889this instance, of course, because there's no possibility of circular
4890references.)
4891
4892\S{writing-flash-types} Implementing multiple types of flash
4893
4894In some games you need to flash in more than one different way.
4895Mines, for example, flashes white when you win, and flashes red when
4896you tread on a mine and die.
4897
4898The simple way to do this is:
4899
4900\b Have a field in the \c{game_ui} which describes the type of flash.
4901
4902\b In \cw{flash_length()}, examine the old and new game states to
4903decide whether a flash is required and what type. Write the type of
4904flash to the \c{game_ui} field whenever you return non-zero.
4905
4906\b In \cw{redraw()}, when you detect that \c{flash_time} is
4907non-zero, examine the field in \c{game_ui} to decide which type of
4908flash to draw.
4909
4910\cw{redraw()} will never be called with \c{flash_time} non-zero
4911unless \cw{flash_length()} was first called to tell the mid-end that
4912a flash was required; so whenever \cw{redraw()} notices that
4913\c{flash_time} is non-zero, you can be sure that the field in
4914\c{game_ui} is correctly set.
4915
4916\S{writing-move-anim} Animating game moves
4917
4918A number of puzzle types benefit from a quick animation of each move
4919you make.
4920
4921For some games, such as Fifteen, this is particularly easy. Whenever
4922\cw{redraw()} is called with \c{oldstate} non-\cw{NULL}, Fifteen
4923simply compares the position of each tile in the two game states,
4924and if the tile is not in the same place then it draws it some
4925fraction of the way from its old position to its new position. This
4926method copes automatically with undo.
4927
4928Other games are less obvious. In Sixteen, for example, you can't
4929just draw each tile a fraction of the way from its old to its new
4930position: if you did that, the end tile would zip very rapidly past
4931all the others to get to the other end and that would look silly.
4932(Worse, it would look inconsistent if the end tile was drawn on top
4933going one way and on the bottom going the other way.)
4934
4935A useful trick here is to define a field or two in the game state
4936that indicates what the last move was.
4937
4938\b Add a \q{last move} field to the \c{game_state} (or two or more
4939fields if the move is complex enough to need them).
4940
4941\b \cw{new_game()} initialises this field to a null value for a new
4942game state.
4943
4944\b \cw{execute_move()} sets up the field to reflect the move it just
4945performed.
4946
4947\b \cw{redraw()} now needs to examine its \c{dir} parameter. If
4948\c{dir} is positive, it determines the move being animated by
4949looking at the last-move field in \c{newstate}; but if \c{dir} is
4950negative, it has to look at the last-move field in \c{oldstate}, and
4951invert whatever move it finds there.
4952
4953Note also that Sixteen needs to store the \e{direction} of the move,
4954because you can't quite determine it by examining the row or column
4955in question. You can in almost all cases, but when the row is
4956precisely two squares long it doesn't work since a move in either
4957direction looks the same. (You could argue that since moving a
49582-element row left and right has the same effect, it doesn't matter
4959which one you animate; but in fact it's very disorienting to click
4960the arrow left and find the row moving right, and almost as bad to
4961undo a move to the right and find the game animating \e{another}
4962move to the right.)
4963
4964\S{writing-conditional-anim} Animating drag operations
4965
4966In Untangle, moves are made by dragging a node from an old position
4967to a new position. Therefore, at the time when the move is initially
4968made, it should not be animated, because the node has already been
4969dragged to the right place and doesn't need moving there. However,
4970it's nice to animate the same move if it's later undone or redone.
4971This requires a bit of fiddling.
4972
4973The obvious approach is to have a flag in the \c{game_ui} which
4974inhibits move animation, and to set that flag in
4975\cw{interpret_move()}. The question is, when would the flag be reset
4976again? The obvious place to do so is \cw{changed_state()}, which
4977will be called once per move. But it will be called \e{before}
4978\cw{anim_length()}, so if it resets the flag then \cw{anim_length()}
4979will never see the flag set at all.
4980
4981The solution is to have \e{two} flags in a queue.
4982
4983\b Define two flags in \c{game_ui}; let's call them \q{current} and
4984\q{next}.
4985
4986\b Set both to \cw{FALSE} in \c{new_ui()}.
4987
4988\b When a drag operation completes in \cw{interpret_move()}, set the
4989\q{next} flag to \cw{TRUE}.
4990
4991\b Every time \cw{changed_state()} is called, set the value of
4992\q{current} to the value in \q{next}, and then set the value of
4993\q{next} to \cw{FALSE}.
4994
4995\b That way, \q{current} will be \cw{TRUE} \e{after} a call to
4996\cw{changed_state()} if and only if that call to
4997\cw{changed_state()} was the result of a drag operation processed by
4998\cw{interpret_move()}. Any other call to \cw{changed_state()}, due
4999to an Undo or a Redo or a Restart or a Solve, will leave \q{current}
5000\cw{FALSE}.
5001
5002\b So now \cw{anim_length()} can request a move animation if and
5003only if the \q{current} flag is \e{not} set.
5004
5005\S{writing-cheating} Inhibiting the victory flash when Solve is used
5006
5007Many games flash when you complete them, as a visual congratulation
5008for having got to the end of the puzzle. It often seems like a good
5009idea to disable that flash when the puzzle is brought to a solved
5010state by means of the Solve operation.
5011
5012This is easily done:
5013
5014\b Add a \q{cheated} flag to the \c{game_state}.
5015
5016\b Set this flag to \cw{FALSE} in \cw{new_game()}.
5017
5018\b Have \cw{solve()} return a move description string which clearly
5019identifies the move as a solve operation.
5020
5021\b Have \cw{execute_move()} respond to that clear identification by
5022setting the \q{cheated} flag in the returned \c{game_state}. The
5023flag will then be propagated to all subsequent game states, even if
5024the user continues fiddling with the game after it is solved.
5025
5026\b \cw{flash_length()} now returns non-zero if \c{oldstate} is not
5027completed and \c{newstate} is, \e{and} neither state has the
5028\q{cheated} flag set.
5029
5030\H{writing-testing} Things to test once your puzzle is written
5031
5032Puzzle implementations written in this framework are self-testing as
5033far as I could make them.
5034
5035Textual game and move descriptions, for example, are generated and
5036parsed as part of the normal process of play. Therefore, if you can
5037make moves in the game \e{at all} you can be reasonably confident
5038that the mid-end serialisation interface will function correctly and
5039you will be able to save your game. (By contrast, if I'd stuck with
5040a single \cw{make_move()} function performing the jobs of both
5041\cw{interpret_move()} and \cw{execute_move()}, and had separate
5042functions to encode and decode a game state in string form, then
5043those functions would not be used during normal play; so they could
5044have been completely broken, and you'd never know it until you tried
5045to save the game \dash which would have meant you'd have to test
5046game saving \e{extensively} and make sure to test every possible
5047type of game state. As an added bonus, doing it the way I did leads
5048to smaller save files.)
5049
5050There is one exception to this, which is the string encoding of the
5051\c{game_ui}. Most games do not store anything permanent in the
5052\c{game_ui}, and hence do not need to put anything in its encode and
5053decode functions; but if there is anything in there, you do need to
5054test game loading and saving to ensure those functions work
5055properly.
5056
5057It's also worth testing undo and redo of all operations, to ensure
5058that the redraw and the animations (if any) work properly. Failing
5059to animate undo properly seems to be a common error.
5060
5061Other than that, just use your common sense.
diff --git a/apps/plugins/puzzles/src/divvy.c b/apps/plugins/puzzles/src/divvy.c
index ea018010cf..61b04eb80d 100644
--- a/apps/plugins/puzzles/src/divvy.c
+++ b/apps/plugins/puzzles/src/divvy.c
@@ -260,9 +260,10 @@ static bool addremcommon(int w, int h, int x, int y, int *own, int val)
260 * In both of the above suggested use cases, the user would 260 * In both of the above suggested use cases, the user would
261 * probably want w==h==k, but that isn't a requirement. 261 * probably want w==h==k, but that isn't a requirement.
262 */ 262 */
263static int *divvy_internal(int w, int h, int k, random_state *rs) 263DSF *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
264{ 264{
265 int *order, *queue, *tmp, *own, *sizes, *addable, *retdsf; 265 int *order, *queue, *tmp, *own, *sizes, *addable;
266 DSF *retdsf, *tmpdsf;
266 bool *removable; 267 bool *removable;
267 int wh = w*h; 268 int wh = w*h;
268 int i, j, n, x, y, qhead, qtail; 269 int i, j, n, x, y, qhead, qtail;
@@ -277,6 +278,7 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
277 queue = snewn(n, int); 278 queue = snewn(n, int);
278 addable = snewn(wh*4, int); 279 addable = snewn(wh*4, int);
279 removable = snewn(wh, bool); 280 removable = snewn(wh, bool);
281 retdsf = tmpdsf = NULL;
280 282
281 /* 283 /*
282 * Permute the grid squares into a random order, which will be 284 * Permute the grid squares into a random order, which will be
@@ -609,7 +611,7 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
609 assert(own[i] >= 0 && own[i] < n); 611 assert(own[i] >= 0 && own[i] < n);
610 tmp[own[i]] = i; 612 tmp[own[i]] = i;
611 } 613 }
612 retdsf = snew_dsf(wh); 614 retdsf = dsf_new(wh);
613 for (i = 0; i < wh; i++) { 615 for (i = 0; i < wh; i++) {
614 dsf_merge(retdsf, i, tmp[own[i]]); 616 dsf_merge(retdsf, i, tmp[own[i]]);
615 } 617 }
@@ -619,18 +621,18 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
619 * the ominoes really are k-ominoes and we haven't 621 * the ominoes really are k-ominoes and we haven't
620 * accidentally split one into two disconnected pieces. 622 * accidentally split one into two disconnected pieces.
621 */ 623 */
622 dsf_init(tmp, wh); 624 tmpdsf = dsf_new(wh);
623 for (y = 0; y < h; y++) 625 for (y = 0; y < h; y++)
624 for (x = 0; x+1 < w; x++) 626 for (x = 0; x+1 < w; x++)
625 if (own[y*w+x] == own[y*w+(x+1)]) 627 if (own[y*w+x] == own[y*w+(x+1)])
626 dsf_merge(tmp, y*w+x, y*w+(x+1)); 628 dsf_merge(tmpdsf, y*w+x, y*w+(x+1));
627 for (x = 0; x < w; x++) 629 for (x = 0; x < w; x++)
628 for (y = 0; y+1 < h; y++) 630 for (y = 0; y+1 < h; y++)
629 if (own[y*w+x] == own[(y+1)*w+x]) 631 if (own[y*w+x] == own[(y+1)*w+x])
630 dsf_merge(tmp, y*w+x, (y+1)*w+x); 632 dsf_merge(tmpdsf, y*w+x, (y+1)*w+x);
631 for (i = 0; i < wh; i++) { 633 for (i = 0; i < wh; i++) {
632 j = dsf_canonify(retdsf, i); 634 j = dsf_canonify(retdsf, i);
633 assert(dsf_canonify(tmp, j) == dsf_canonify(tmp, i)); 635 assert(dsf_equivalent(tmpdsf, j, i));
634 } 636 }
635 637
636 cleanup: 638 cleanup:
@@ -640,6 +642,7 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
640 */ 642 */
641 sfree(order); 643 sfree(order);
642 sfree(tmp); 644 sfree(tmp);
645 dsf_free(tmpdsf);
643 sfree(own); 646 sfree(own);
644 sfree(sizes); 647 sfree(sizes);
645 sfree(queue); 648 sfree(queue);
@@ -652,131 +655,13 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
652 return retdsf; 655 return retdsf;
653} 656}
654 657
655#ifdef TESTMODE 658DSF *divvy_rectangle(int w, int h, int k, random_state *rs)
656static int fail_counter = 0;
657#endif
658
659int *divvy_rectangle(int w, int h, int k, random_state *rs)
660{ 659{
661 int *ret; 660 DSF *ret;
662 661
663 do { 662 do {
664 ret = divvy_internal(w, h, k, rs); 663 ret = divvy_rectangle_attempt(w, h, k, rs);
665
666#ifdef TESTMODE
667 if (!ret)
668 fail_counter++;
669#endif
670
671 } while (!ret); 664 } while (!ret);
672 665
673 return ret; 666 return ret;
674} 667}
675
676#ifdef TESTMODE
677
678/*
679 * gcc -g -O0 -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
680 *
681 * or to debug
682 *
683 * gcc -g -O0 -DDIVVY_DIAGNOSTICS -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
684 */
685
686int main(int argc, char **argv)
687{
688 int *dsf;
689 int i;
690 int w = 9, h = 4, k = 6, tries = 100;
691 random_state *rs;
692
693 rs = random_new("123456", 6);
694
695 if (argc > 1)
696 w = atoi(argv[1]);
697 if (argc > 2)
698 h = atoi(argv[2]);
699 if (argc > 3)
700 k = atoi(argv[3]);
701 if (argc > 4)
702 tries = atoi(argv[4]);
703
704 for (i = 0; i < tries; i++) {
705 int x, y;
706
707 dsf = divvy_rectangle(w, h, k, rs);
708 assert(dsf);
709
710 for (y = 0; y <= 2*h; y++) {
711 for (x = 0; x <= 2*w; x++) {
712 int miny = y/2 - 1, maxy = y/2;
713 int minx = x/2 - 1, maxx = x/2;
714 int classes[4], tx, ty;
715 for (ty = 0; ty < 2; ty++)
716 for (tx = 0; tx < 2; tx++) {
717 int cx = minx+tx, cy = miny+ty;
718 if (cx < 0 || cx >= w || cy < 0 || cy >= h)
719 classes[ty*2+tx] = -1;
720 else
721 classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
722 }
723 switch (y%2 * 2 + x%2) {
724 case 0: /* corner */
725 /*
726 * Cases for the corner:
727 *
728 * - if all four surrounding squares belong
729 * to the same omino, we print a space.
730 *
731 * - if the top two are the same and the
732 * bottom two are the same, we print a
733 * horizontal line.
734 *
735 * - if the left two are the same and the
736 * right two are the same, we print a
737 * vertical line.
738 *
739 * - otherwise, we print a cross.
740 */
741 if (classes[0] == classes[1] &&
742 classes[1] == classes[2] &&
743 classes[2] == classes[3])
744 printf(" ");
745 else if (classes[0] == classes[1] &&
746 classes[2] == classes[3])
747 printf("-");
748 else if (classes[0] == classes[2] &&
749 classes[1] == classes[3])
750 printf("|");
751 else
752 printf("+");
753 break;
754 case 1: /* horiz edge */
755 if (classes[1] == classes[3])
756 printf(" ");
757 else
758 printf("--");
759 break;
760 case 2: /* vert edge */
761 if (classes[2] == classes[3])
762 printf(" ");
763 else
764 printf("|");
765 break;
766 case 3: /* square centre */
767 printf(" ");
768 break;
769 }
770 }
771 printf("\n");
772 }
773 printf("\n");
774 sfree(dsf);
775 }
776
777 printf("%d retries needed for %d successes\n", fail_counter, tries);
778
779 return 0;
780}
781
782#endif
diff --git a/apps/plugins/puzzles/src/dominosa.R b/apps/plugins/puzzles/src/dominosa.R
deleted file mode 100644
index b85e7dc1e0..0000000000
--- a/apps/plugins/puzzles/src/dominosa.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3DOMINOSA_EXTRA = laydomino dsf sort findloop
4
5dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon
6
7dominosa : [G] WINDOWS COMMON dominosa DOMINOSA_EXTRA dominosa.res|noicon.res
8
9ALL += dominosa[COMBINED] DOMINOSA_EXTRA
10
11dominosasolver : [U] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
12dominosasolver : [C] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
13
14!begin am gtk
15GAMES += dominosa
16!end
17
18!begin >list.c
19 A(dominosa) \
20!end
21
22!begin >gamedesc.txt
23dominosa:dominosa.exe:Dominosa:Domino tiling puzzle:Tile the rectangle with a full set of dominoes.
24!end
diff --git a/apps/plugins/puzzles/src/dominosa.c b/apps/plugins/puzzles/src/dominosa.c
index 758db4f506..3ac723e6e1 100644
--- a/apps/plugins/puzzles/src/dominosa.c
+++ b/apps/plugins/puzzles/src/dominosa.c
@@ -47,7 +47,12 @@
47#include <string.h> 47#include <string.h>
48#include <assert.h> 48#include <assert.h>
49#include <ctype.h> 49#include <ctype.h>
50#include <math.h> 50#include <limits.h>
51#ifdef NO_TGMATH_H
52# include <math.h>
53#else
54# include <tgmath.h>
55#endif
51 56
52#include "puzzles.h" 57#include "puzzles.h"
53 58
@@ -243,6 +248,10 @@ static const char *validate_params(const game_params *params, bool full)
243{ 248{
244 if (params->n < 1) 249 if (params->n < 1)
245 return "Maximum face number must be at least one"; 250 return "Maximum face number must be at least one";
251 if (params->n > INT_MAX - 2 ||
252 params->n + 2 > INT_MAX / (params->n + 1))
253 return "Maximum face number must not be unreasonably large";
254
246 if (params->diff >= DIFFCOUNT) 255 if (params->diff >= DIFFCOUNT)
247 return "Unknown difficulty rating"; 256 return "Unknown difficulty rating";
248 return NULL; 257 return NULL;
@@ -254,9 +263,9 @@ static const char *validate_params(const game_params *params, bool full)
254 263
255#ifdef STANDALONE_SOLVER 264#ifdef STANDALONE_SOLVER
256#define SOLVER_DIAGNOSTICS 265#define SOLVER_DIAGNOSTICS
257bool solver_diagnostics = false; 266static bool solver_diagnostics = false;
258#elif defined SOLVER_DIAGNOSTICS 267#elif defined SOLVER_DIAGNOSTICS
259const bool solver_diagnostics = true; 268static const bool solver_diagnostics = true;
260#endif 269#endif
261 270
262struct solver_domino; 271struct solver_domino;
@@ -342,6 +351,7 @@ struct solver_scratch {
342 struct findloopstate *fls; 351 struct findloopstate *fls;
343 bool squares_by_number_initialised; 352 bool squares_by_number_initialised;
344 int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; 353 int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch;
354 DSF *dsf_scratch;
345}; 355};
346 356
347static struct solver_scratch *solver_make_scratch(int n) 357static struct solver_scratch *solver_make_scratch(int n)
@@ -473,6 +483,7 @@ static struct solver_scratch *solver_make_scratch(int n)
473 sc->wh_scratch = NULL; 483 sc->wh_scratch = NULL;
474 sc->pc_scratch = sc->pc_scratch2 = NULL; 484 sc->pc_scratch = sc->pc_scratch2 = NULL;
475 sc->dc_scratch = NULL; 485 sc->dc_scratch = NULL;
486 sc->dsf_scratch = NULL;
476 487
477 return sc; 488 return sc;
478} 489}
@@ -500,6 +511,7 @@ static void solver_free_scratch(struct solver_scratch *sc)
500 sfree(sc->pc_scratch); 511 sfree(sc->pc_scratch);
501 sfree(sc->pc_scratch2); 512 sfree(sc->pc_scratch2);
502 sfree(sc->dc_scratch); 513 sfree(sc->dc_scratch);
514 dsf_free(sc->dsf_scratch);
503 sfree(sc); 515 sfree(sc);
504} 516}
505 517
@@ -925,7 +937,7 @@ struct parity_findloop_ctx {
925 int i; 937 int i;
926}; 938};
927 939
928int parity_neighbour(int vertex, void *vctx) 940static int parity_neighbour(int vertex, void *vctx)
929{ 941{
930 struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx; 942 struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx;
931 struct solver_placement *p; 943 struct solver_placement *p;
@@ -1416,21 +1428,23 @@ static bool deduce_forcing_chain(struct solver_scratch *sc)
1416 sc->pc_scratch2 = snewn(sc->pc, int); 1428 sc->pc_scratch2 = snewn(sc->pc, int);
1417 if (!sc->dc_scratch) 1429 if (!sc->dc_scratch)
1418 sc->dc_scratch = snewn(sc->dc, int); 1430 sc->dc_scratch = snewn(sc->dc, int);
1431 if (!sc->dsf_scratch)
1432 sc->dsf_scratch = dsf_new_flip(sc->pc);
1419 1433
1420 /* 1434 /*
1421 * Start by identifying chains of placements which must all occur 1435 * Start by identifying chains of placements which must all occur
1422 * together if any of them occurs. We do this by making 1436 * together if any of them occurs. We do this by making
1423 * pc_scratch2 an edsf binding the placements into an equivalence 1437 * dsf_scratch a flip dsf binding the placements into an equivalence
1424 * class for each entire forcing chain, with the two possible sets 1438 * class for each entire forcing chain, with the two possible sets
1425 * of dominoes for the chain listed as inverses. 1439 * of dominoes for the chain listed as inverses.
1426 */ 1440 */
1427 dsf_init(sc->pc_scratch2, sc->pc); 1441 dsf_reinit(sc->dsf_scratch);
1428 for (si = 0; si < sc->wh; si++) { 1442 for (si = 0; si < sc->wh; si++) {
1429 struct solver_square *sq = &sc->squares[si]; 1443 struct solver_square *sq = &sc->squares[si];
1430 if (sq->nplacements == 2) 1444 if (sq->nplacements == 2)
1431 edsf_merge(sc->pc_scratch2, 1445 dsf_merge_flip(sc->dsf_scratch,
1432 sq->placements[0]->index, 1446 sq->placements[0]->index,
1433 sq->placements[1]->index, true); 1447 sq->placements[1]->index, true);
1434 } 1448 }
1435 /* 1449 /*
1436 * Now read out the whole dsf into pc_scratch, flattening its 1450 * Now read out the whole dsf into pc_scratch, flattening its
@@ -1443,7 +1457,7 @@ static bool deduce_forcing_chain(struct solver_scratch *sc)
1443 */ 1457 */
1444 for (pi = 0; pi < sc->pc; pi++) { 1458 for (pi = 0; pi < sc->pc; pi++) {
1445 bool inv; 1459 bool inv;
1446 int c = edsf_canonify(sc->pc_scratch2, pi, &inv); 1460 int c = dsf_canonify_flip(sc->dsf_scratch, pi, &inv);
1447 sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); 1461 sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0);
1448 } 1462 }
1449 1463
@@ -2708,7 +2722,7 @@ static game_ui *new_ui(const game_state *state)
2708{ 2722{
2709 game_ui *ui = snew(game_ui); 2723 game_ui *ui = snew(game_ui);
2710 ui->cur_x = ui->cur_y = 0; 2724 ui->cur_x = ui->cur_y = 0;
2711 ui->cur_visible = false; 2725 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2712 ui->highlight_1 = ui->highlight_2 = -1; 2726 ui->highlight_1 = ui->highlight_2 = -1;
2713 return ui; 2727 return ui;
2714} 2728}
@@ -2718,15 +2732,6 @@ static void free_ui(game_ui *ui)
2718 sfree(ui); 2732 sfree(ui);
2719} 2733}
2720 2734
2721static char *encode_ui(const game_ui *ui)
2722{
2723 return NULL;
2724}
2725
2726static void decode_ui(game_ui *ui, const char *encoding)
2727{
2728}
2729
2730static void game_changed_state(game_ui *ui, const game_state *oldstate, 2735static void game_changed_state(game_ui *ui, const game_state *oldstate,
2731 const game_state *newstate) 2736 const game_state *newstate)
2732{ 2737{
@@ -2734,6 +2739,33 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2734 ui->cur_visible = false; 2739 ui->cur_visible = false;
2735} 2740}
2736 2741
2742static const char *current_key_label(const game_ui *ui,
2743 const game_state *state, int button)
2744{
2745 if (IS_CURSOR_SELECT(button)) {
2746 int d1, d2, w = state->w;
2747
2748 if (!((ui->cur_x ^ ui->cur_y) & 1))
2749 return ""; /* must have exactly one dimension odd */
2750 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
2751 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
2752
2753 /* We can't mark an edge next to any domino. */
2754 if (button == CURSOR_SELECT2 &&
2755 (state->grid[d1] != d1 || state->grid[d2] != d2))
2756 return "";
2757 if (button == CURSOR_SELECT) {
2758 if (state->grid[d1] == d2) return "Remove";
2759 return "Place";
2760 } else {
2761 int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B;
2762 if (state->edges[d1] & edge) return "Remove";
2763 return "Line";
2764 }
2765 }
2766 return "";
2767}
2768
2737#define PREFERRED_TILESIZE 32 2769#define PREFERRED_TILESIZE 32
2738#define TILESIZE (ds->tilesize) 2770#define TILESIZE (ds->tilesize)
2739#define BORDER (TILESIZE * 3 / 4) 2771#define BORDER (TILESIZE * 3 / 4)
@@ -2746,7 +2778,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2746#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) 2778#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
2747 2779
2748struct game_drawstate { 2780struct game_drawstate {
2749 bool started;
2750 int w, h, tilesize; 2781 int w, h, tilesize;
2751 unsigned long *visible; 2782 unsigned long *visible;
2752}; 2783};
@@ -2768,7 +2799,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2768 int d1, d2; 2799 int d1, d2;
2769 2800
2770 if (tx < 0 || tx >= w || ty < 0 || ty >= h) 2801 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
2771 return NULL; 2802 return MOVE_UNUSED;
2772 2803
2773 /* 2804 /*
2774 * Now we know which square the click was in, decide which 2805 * Now we know which square the click was in, decide which
@@ -2786,29 +2817,26 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2786 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) 2817 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h)
2787 d1 = t, d2 = t + w; /* clicked in top half of domino */ 2818 d1 = t, d2 = t + w; /* clicked in top half of domino */
2788 else 2819 else
2789 return NULL; 2820 return MOVE_NO_EFFECT; /* clicked precisely on a diagonal */
2790 2821
2791 /* 2822 /*
2792 * We can't mark an edge next to any domino. 2823 * We can't mark an edge next to any domino.
2793 */ 2824 */
2794 if (button == RIGHT_BUTTON && 2825 if (button == RIGHT_BUTTON &&
2795 (state->grid[d1] != d1 || state->grid[d2] != d2)) 2826 (state->grid[d1] != d1 || state->grid[d2] != d2))
2796 return NULL; 2827 return MOVE_NO_EFFECT;
2797 2828
2798 ui->cur_visible = false; 2829 ui->cur_visible = false;
2799 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); 2830 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2);
2800 return dupstr(buf); 2831 return dupstr(buf);
2801 } else if (IS_CURSOR_MOVE(button)) { 2832 } else if (IS_CURSOR_MOVE(button)) {
2802 ui->cur_visible = true; 2833 return move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false,
2803 2834 &ui->cur_visible);
2804 move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false);
2805
2806 return UI_UPDATE;
2807 } else if (IS_CURSOR_SELECT(button)) { 2835 } else if (IS_CURSOR_SELECT(button)) {
2808 int d1, d2; 2836 int d1, d2;
2809 2837
2810 if (!((ui->cur_x ^ ui->cur_y) & 1)) 2838 if (!((ui->cur_x ^ ui->cur_y) & 1))
2811 return NULL; /* must have exactly one dimension odd */ 2839 return MOVE_NO_EFFECT; /* must have exactly one dimension odd */
2812 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); 2840 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
2813 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); 2841 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
2814 2842
@@ -2817,14 +2845,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2817 */ 2845 */
2818 if (button == CURSOR_SELECT2 && 2846 if (button == CURSOR_SELECT2 &&
2819 (state->grid[d1] != d1 || state->grid[d2] != d2)) 2847 (state->grid[d1] != d1 || state->grid[d2] != d2))
2820 return NULL; 2848 return MOVE_NO_EFFECT;
2821 2849
2822 sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); 2850 sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2);
2823 return dupstr(buf); 2851 return dupstr(buf);
2824 } else if (isdigit(button)) { 2852 } else if (isdigit(button)) {
2825 int n = state->params.n, num = button - '0'; 2853 int n = state->params.n, num = button - '0';
2826 if (num > n) { 2854 if (num > n) {
2827 return NULL; 2855 return MOVE_UNUSED;
2828 } else if (ui->highlight_1 == num) { 2856 } else if (ui->highlight_1 == num) {
2829 ui->highlight_1 = -1; 2857 ui->highlight_1 = -1;
2830 } else if (ui->highlight_2 == num) { 2858 } else if (ui->highlight_2 == num) {
@@ -2834,12 +2862,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2834 } else if (ui->highlight_2 == -1) { 2862 } else if (ui->highlight_2 == -1) {
2835 ui->highlight_2 = num; 2863 ui->highlight_2 = num;
2836 } else { 2864 } else {
2837 return NULL; 2865 return MOVE_NO_EFFECT;
2838 } 2866 }
2839 return UI_UPDATE; 2867 return MOVE_UI_UPDATE;
2840 } 2868 }
2841 2869
2842 return NULL; 2870 return MOVE_UNUSED;
2843} 2871}
2844 2872
2845static game_state *execute_move(const game_state *state, const char *move) 2873static game_state *execute_move(const game_state *state, const char *move)
@@ -2865,7 +2893,8 @@ static game_state *execute_move(const game_state *state, const char *move)
2865 move++; 2893 move++;
2866 } else if (move[0] == 'D' && 2894 } else if (move[0] == 'D' &&
2867 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && 2895 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
2868 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) { 2896 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
2897 (d2 - d1 == 1 || d2 - d1 == w)) {
2869 2898
2870 /* 2899 /*
2871 * Toggle domino presence between d1 and d2. 2900 * Toggle domino presence between d1 and d2.
@@ -2933,7 +2962,8 @@ static game_state *execute_move(const game_state *state, const char *move)
2933 } else if (move[0] == 'E' && 2962 } else if (move[0] == 'E' &&
2934 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && 2963 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
2935 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && 2964 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
2936 ret->grid[d1] == d1 && ret->grid[d2] == d2) { 2965 ret->grid[d1] == d1 && ret->grid[d2] == d2 &&
2966 (d2 - d1 == 1 || d2 - d1 == w)) {
2937 2967
2938 /* 2968 /*
2939 * Toggle edge presence between d1 and d2. 2969 * Toggle edge presence between d1 and d2.
@@ -2998,7 +3028,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2998 */ 3028 */
2999 3029
3000static void game_compute_size(const game_params *params, int tilesize, 3030static void game_compute_size(const game_params *params, int tilesize,
3001 int *x, int *y) 3031 const game_ui *ui, int *x, int *y)
3002{ 3032{
3003 int n = params->n, w = n+2, h = n+1; 3033 int n = params->n, w = n+2, h = n+1;
3004 3034
@@ -3059,7 +3089,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
3059 struct game_drawstate *ds = snew(struct game_drawstate); 3089 struct game_drawstate *ds = snew(struct game_drawstate);
3060 int i; 3090 int i;
3061 3091
3062 ds->started = false;
3063 ds->w = state->w; 3092 ds->w = state->w;
3064 ds->h = state->h; 3093 ds->h = state->h;
3065 ds->visible = snewn(ds->w * ds->h, unsigned long); 3094 ds->visible = snewn(ds->w * ds->h, unsigned long);
@@ -3225,14 +3254,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3225 int x, y, i; 3254 int x, y, i;
3226 unsigned char *used; 3255 unsigned char *used;
3227 3256
3228 if (!ds->started) {
3229 int pw, ph;
3230 game_compute_size(&state->params, TILESIZE, &pw, &ph);
3231 draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND);
3232 draw_update(dr, 0, 0, pw, ph);
3233 ds->started = true;
3234 }
3235
3236 /* 3257 /*
3237 * See how many dominoes of each type there are, so we can 3258 * See how many dominoes of each type there are, so we can
3238 * highlight clashes in red. 3259 * highlight clashes in red.
@@ -3347,24 +3368,21 @@ static int game_status(const game_state *state)
3347 return state->completed ? +1 : 0; 3368 return state->completed ? +1 : 0;
3348} 3369}
3349 3370
3350static bool game_timing_state(const game_state *state, game_ui *ui) 3371static void game_print_size(const game_params *params, const game_ui *ui,
3351{ 3372 float *x, float *y)
3352 return true;
3353}
3354
3355static void game_print_size(const game_params *params, float *x, float *y)
3356{ 3373{
3357 int pw, ph; 3374 int pw, ph;
3358 3375
3359 /* 3376 /*
3360 * I'll use 6mm squares by default. 3377 * I'll use 6mm squares by default.
3361 */ 3378 */
3362 game_compute_size(params, 600, &pw, &ph); 3379 game_compute_size(params, 600, ui, &pw, &ph);
3363 *x = pw / 100.0F; 3380 *x = pw / 100.0F;
3364 *y = ph / 100.0F; 3381 *y = ph / 100.0F;
3365} 3382}
3366 3383
3367static void game_print(drawing *dr, const game_state *state, int tilesize) 3384static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3385 int tilesize)
3368{ 3386{
3369 int w = state->w, h = state->h; 3387 int w = state->w, h = state->h;
3370 int c, x, y; 3388 int c, x, y;
@@ -3421,12 +3439,14 @@ const struct game thegame = {
3421 free_game, 3439 free_game,
3422 true, solve_game, 3440 true, solve_game,
3423 true, game_can_format_as_text_now, game_text_format, 3441 true, game_can_format_as_text_now, game_text_format,
3442 NULL, NULL, /* get_prefs, set_prefs */
3424 new_ui, 3443 new_ui,
3425 free_ui, 3444 free_ui,
3426 encode_ui, 3445 NULL, /* encode_ui */
3427 decode_ui, 3446 NULL, /* decode_ui */
3428 NULL, /* game_request_keys */ 3447 NULL, /* game_request_keys */
3429 game_changed_state, 3448 game_changed_state,
3449 current_key_label,
3430 interpret_move, 3450 interpret_move,
3431 execute_move, 3451 execute_move,
3432 PREFERRED_TILESIZE, game_compute_size, game_set_size, 3452 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -3440,7 +3460,7 @@ const struct game thegame = {
3440 game_status, 3460 game_status,
3441 true, false, game_print_size, game_print, 3461 true, false, game_print_size, game_print,
3442 false, /* wants_statusbar */ 3462 false, /* wants_statusbar */
3443 false, game_timing_state, 3463 false, NULL, /* timing_state */
3444 0, /* flags */ 3464 0, /* flags */
3445}; 3465};
3446 3466
diff --git a/apps/plugins/puzzles/src/draw-poly.c b/apps/plugins/puzzles/src/draw-poly.c
new file mode 100644
index 0000000000..d4c9975d97
--- /dev/null
+++ b/apps/plugins/puzzles/src/draw-poly.c
@@ -0,0 +1,302 @@
1/*
2 * draw-poly.c: Fallback polygon drawing function.
3 */
4
5#include <assert.h>
6
7#include "puzzles.h"
8
9struct edge {
10 int x1, y1;
11 int x2, y2;
12 bool active;
13
14 /* whether y1 is a closed endpoint (i.e. this edge should be
15 * active when y == y1) */
16 bool closed_y1;
17
18 /* (x2 - x1) / (y2 - y1) as 16.16 signed fixed point; precomputed
19 * for speed */
20 long inverse_slope;
21};
22
23#define FRACBITS 16
24#define ONEHALF (1 << (FRACBITS-1))
25
26void draw_polygon_fallback(drawing *dr, const int *coords, int npoints,
27 int fillcolour, int outlinecolour)
28{
29 struct edge *edges;
30 int min_y = INT_MAX, max_y = INT_MIN, i, y;
31 int n_edges = 0;
32 int *intersections;
33
34 if(npoints < 3)
35 return;
36
37 if(fillcolour < 0)
38 goto draw_outline;
39
40 /* This uses a basic scanline rasterization algorithm for polygon
41 * filling. First, an "edge table" is constructed for each pair of
42 * neighboring points. Horizontal edges are excluded. Then, the
43 * algorithm iterates a horizontal "scan line" over the vertical
44 * (Y) extent of the polygon. At each Y level, it maintains a set
45 * of "active" edges, which are those which intersect the scan
46 * line at the current Y level. The X coordinates where the scan
47 * line intersects each active edge are then computed via
48 * fixed-point arithmetic and stored. Finally, horizontal lines
49 * are drawn between each successive pair of intersection points,
50 * in the order of ascending X coordinate. This has the effect of
51 * "even-odd" filling when the polygon is self-intersecting.
52 *
53 * I (vaguely) based this implementation off the slides below:
54 *
55 * https://www.khoury.northeastern.edu/home/fell/CS4300/Lectures/CS4300F2012-9-ScanLineFill.pdf
56 *
57 * I'm fairly confident that this current implementation is
58 * correct (i.e. draws the right polygon, free from artifacts),
59 * but it isn't quite as efficient as it could be. Namely, it
60 * currently maintains the active edge set by setting the `active`
61 * flag in the `edge` array, which is quite inefficient. Perhaps
62 * future optimization could see this replaced with a tree
63 * set. Additionally, one could perhaps replace the current linear
64 * search for edge endpoints (i.e. the inner loop over `edges`) by
65 * sorting the edge table by upper and lower Y coordinate.
66 *
67 * A final caveat comes from the use of fixed point arithmetic,
68 * which is motivated by performance considerations on FPU-less
69 * platforms (e.g. most Rockbox devices, and maybe others?). I'm
70 * currently using 16 fractional bits to compute the edge
71 * intersections, which (in the case of a 32-bit int) limits the
72 * acceptable range of coordinates to roughly (-2^14, +2^14). This
73 * ought to be acceptable for the forseeable future, but
74 * ultra-high DPI screens might one day exceed this. In that case,
75 * options include switching to int64_t (but that comes with its
76 * own portability headaches), reducing the number of fractional
77 * bits, or just giving up and using floating point.
78 */
79
80 /* Build edge table from coords. Horizontal edges are filtered
81 * out, so n_edges <= n_points in general. */
82 edges = smalloc(npoints * sizeof(struct edge));
83
84 for(i = 0; i < npoints; i++) {
85 int x1, y1, x2, y2;
86
87 x1 = coords[(2*i+0)];
88 y1 = coords[(2*i+1)];
89 x2 = coords[(2*i+2) % (npoints * 2)];
90 y2 = coords[(2*i+3) % (npoints * 2)];
91
92 if(y1 < min_y)
93 min_y = y1;
94 if(y1 > max_y)
95 max_y = y1;
96
97#define COORD_LIMIT (1<<(sizeof(int)*CHAR_BIT-2 - FRACBITS))
98 /* Prevent integer overflow when computing `inverse_slope',
99 * which shifts the coordinates left by FRACBITS, and for
100 * which we'd like to avoid relying on `long long'. */
101 /* If this ever causes issues, see the above comment about
102 possible solutions. */
103 assert(x1 < COORD_LIMIT && y1 < COORD_LIMIT);
104
105 /* Only create non-horizontal edges, and require y1 < y2. */
106 if(y1 != y2) {
107 bool swap = y1 > y2;
108 int lower_endpoint = swap ? (i + 1) : i;
109
110 /* Compute index of the point adjacent to lower end of
111 * this edge (which is not the upper end of this edge). */
112 int lower_neighbor = swap ? (lower_endpoint + 1) % npoints : (lower_endpoint + npoints - 1) % npoints;
113
114 struct edge *edge = edges + (n_edges++);
115
116 edge->active = false;
117 edge->x1 = swap ? x2 : x1;
118 edge->y1 = swap ? y2 : y1;
119 edge->x2 = swap ? x1 : x2;
120 edge->y2 = swap ? y1 : y2;
121 edge->inverse_slope = ((edge->x2 - edge->x1) << FRACBITS) / (edge->y2 - edge->y1);
122 edge->closed_y1 = edge->y1 < coords[2*lower_neighbor+1];
123 }
124 }
125
126 /* a generous upper bound on number of intersections is n_edges */
127 intersections = smalloc(n_edges * sizeof(int));
128
129 for(y = min_y; y <= max_y; y++) {
130 int n_intersections = 0;
131 for(i = 0; i < n_edges; i++) {
132 struct edge *edge = edges + i;
133 /* Update active edge set. These conditions are mutually
134 * exclusive because of the invariant that y1 < y2. */
135 if(edge->y1 + (edge->closed_y1 ? 0 : 1) == y)
136 edge->active = true;
137 else if(edge->y2 + 1 == y)
138 edge->active = false;
139
140 if(edge->active) {
141 int x = edges[i].x1;
142 x += (edges[i].inverse_slope * (y - edges[i].y1) + ONEHALF) >> FRACBITS;
143 intersections[n_intersections++] = x;
144 }
145 }
146
147 qsort(intersections, n_intersections, sizeof(int), compare_integers);
148
149 assert(n_intersections % 2 == 0);
150 assert(n_intersections <= n_edges);
151
152 /* Draw horizontal lines between successive pairs of
153 * intersections of the scanline with active edges. */
154 for(i = 0; i + 1 < n_intersections; i += 2) {
155 draw_line(dr,
156 intersections[i], y,
157 intersections[i+1], y,
158 fillcolour);
159 }
160 }
161
162 sfree(intersections);
163 sfree(edges);
164
165draw_outline:
166 assert(outlinecolour >= 0);
167 for (i = 0; i < 2 * npoints; i += 2)
168 draw_line(dr,
169 coords[i], coords[i+1],
170 coords[(i+2)%(2*npoints)], coords[(i+3)%(2*npoints)],
171 outlinecolour);
172}
173
174#ifdef STANDALONE_POLYGON
175
176/*
177 * Standalone program to test draw_polygon_fallback(). By default,
178 * creates a window and allows clicking points to build a
179 * polygon. Optionally, can draw a randomly growing polygon in
180 * "screensaver" mode.
181 */
182
183#include <SDL.h>
184
185void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
186{
187 SDL_Renderer *renderer = GET_HANDLE_AS_TYPE(dr, SDL_Renderer);
188 SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
189}
190
191#define WINDOW_WIDTH 800
192#define WINDOW_HEIGHT 600
193#define MAX_SCREENSAVER_POINTS 1000
194
195int main(int argc, char *argv[]) {
196 SDL_Window* window = NULL;
197 SDL_Event event;
198 SDL_Renderer *renderer = NULL;
199 int running = 1;
200 int i;
201 drawing dr;
202 bool screensaver = false;
203
204 if(argc >= 2) {
205 if(!strcmp(argv[1], "--screensaver"))
206 screensaver = true;
207 else
208 printf("usage: %s [--screensaver]\n", argv[0]);
209 }
210
211 int *poly = NULL;
212 int n_points = 0;
213
214 /* Initialize SDL */
215 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
216 printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
217 return 1;
218 }
219
220 /* Create window */
221 window = SDL_CreateWindow("Polygon Drawing Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
222 if (!window) {
223 printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
224 SDL_Quit();
225 return 1;
226 }
227
228 /* Create renderer */
229 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
230 if (!renderer) {
231 printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
232 SDL_DestroyWindow(window);
233 SDL_Quit();
234 return 1;
235 }
236
237 dr.handle = renderer;
238
239 if(!screensaver)
240 printf("Click points in the window to create vertices. Pressing C resets.\n");
241
242 while (running) {
243 while (SDL_PollEvent(&event) != 0) {
244 if (event.type == SDL_QUIT) {
245 running = 0;
246 }
247 else if (event.type == SDL_MOUSEBUTTONDOWN) {
248 if (event.button.button == SDL_BUTTON_LEFT) {
249 int mouseX = event.button.x;
250 int mouseY = event.button.y;
251
252 poly = realloc(poly, ++n_points * 2 * sizeof(int));
253 poly[2 * (n_points - 1)] = mouseX;
254 poly[2 * (n_points - 1) + 1] = mouseY;
255 }
256 }
257 else if (event.type == SDL_KEYDOWN) {
258 if (event.key.keysym.sym == SDLK_c) {
259 free(poly);
260 poly = NULL;
261 n_points = 0;
262 }
263 }
264 }
265
266 if(screensaver) {
267 poly = realloc(poly, ++n_points * 2 * sizeof(int));
268 poly[2 * (n_points - 1)] = rand() % WINDOW_WIDTH;
269 poly[2 * (n_points - 1) + 1] = rand() % WINDOW_HEIGHT;
270
271 if(n_points >= MAX_SCREENSAVER_POINTS) {
272 free(poly);
273 poly = NULL;
274 n_points = 0;
275 }
276 }
277
278 /* Clear the screen with a black color */
279 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
280 SDL_RenderClear(renderer);
281
282 /* Set draw color to white */
283 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
284
285 draw_polygon_fallback(&dr, poly, n_points, 1, 1);
286
287 SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
288 for(i = 0; i < 2*n_points; i+=2)
289 SDL_RenderDrawPoint(renderer, poly[i], poly[i+1]);
290
291 /* Present the back buffer */
292 SDL_RenderPresent(renderer);
293 }
294
295 /* Clean up and quit SDL */
296 SDL_DestroyRenderer(renderer);
297 SDL_DestroyWindow(window);
298 SDL_Quit();
299
300 return 0;
301}
302#endif
diff --git a/apps/plugins/puzzles/src/drawing.c b/apps/plugins/puzzles/src/drawing.c
index a8eb8cfd5f..a754b068f3 100644
--- a/apps/plugins/puzzles/src/drawing.c
+++ b/apps/plugins/puzzles/src/drawing.c
@@ -27,7 +27,11 @@
27#include <stdlib.h> 27#include <stdlib.h>
28#include <string.h> 28#include <string.h>
29#include <assert.h> 29#include <assert.h>
30#include <math.h> 30#ifdef NO_TGMATH_H
31# include <math.h>
32#else
33# include <tgmath.h>
34#endif
31 35
32#include "puzzles.h" 36#include "puzzles.h"
33 37
@@ -38,9 +42,12 @@ struct print_colour {
38 float grey; 42 float grey;
39}; 43};
40 44
41struct drawing { 45typedef struct drawing_internal {
42 const drawing_api *api; 46 /* we implement data hiding by casting `struct drawing*` pointers
43 void *handle; 47 * to `struct drawing_internal*` */
48 struct drawing pub;
49
50 /* private data */
44 struct print_colour *colours; 51 struct print_colour *colours;
45 int ncolours, coloursize; 52 int ncolours, coloursize;
46 float scale; 53 float scale;
@@ -48,61 +55,78 @@ struct drawing {
48 * this may set it to NULL. */ 55 * this may set it to NULL. */
49 midend *me; 56 midend *me;
50 char *laststatus; 57 char *laststatus;
51}; 58} drawing_internal;
59
60#define PRIVATE_CAST(dr) ((drawing_internal*)(dr))
61#define PUBLIC_CAST(dri) ((drawing*)(dri))
62
63/* See puzzles.h for a description of the version number. */
64#define DRAWING_API_VERSION 1
52 65
53drawing *drawing_new(const drawing_api *api, midend *me, void *handle) 66drawing *drawing_new(const drawing_api *api, midend *me, void *handle)
54{ 67{
55 drawing *dr = snew(drawing); 68 if(api->version != DRAWING_API_VERSION) {
56 dr->api = api; 69 fatal("Drawing API version mismatch: expected: %d, actual: %d\n", DRAWING_API_VERSION, api->version);
57 dr->handle = handle; 70 /* shouldn't get here */
58 dr->colours = NULL; 71 return NULL;
59 dr->ncolours = dr->coloursize = 0; 72 }
60 dr->scale = 1.0F; 73
61 dr->me = me; 74 drawing_internal *dri = snew(drawing_internal);
62 dr->laststatus = NULL; 75 dri->pub.api = api;
63 return dr; 76 dri->pub.handle = handle;
77 dri->colours = NULL;
78 dri->ncolours = dri->coloursize = 0;
79 dri->scale = 1.0F;
80 dri->me = me;
81 dri->laststatus = NULL;
82 return PUBLIC_CAST(dri);
64} 83}
65 84
66void drawing_free(drawing *dr) 85void drawing_free(drawing *dr)
67{ 86{
68 sfree(dr->laststatus); 87 drawing_internal *dri = PRIVATE_CAST(dr);
69 sfree(dr->colours); 88 sfree(dri->laststatus);
70 sfree(dr); 89 sfree(dri->colours);
90 sfree(dri);
71} 91}
72 92
73void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, 93void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
74 int align, int colour, const char *text) 94 int align, int colour, const char *text)
75{ 95{
76 dr->api->draw_text(dr->handle, x, y, fonttype, fontsize, align, 96 drawing_internal *dri = PRIVATE_CAST(dr);
77 colour, text); 97 dri->pub.api->draw_text(dr, x, y, fonttype, fontsize, align,
98 colour, text);
78} 99}
79 100
80void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) 101void draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
81{ 102{
82 dr->api->draw_rect(dr->handle, x, y, w, h, colour); 103 drawing_internal *dri = PRIVATE_CAST(dr);
104 dri->pub.api->draw_rect(dr, x, y, w, h, colour);
83} 105}
84 106
85void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) 107void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
86{ 108{
87 dr->api->draw_line(dr->handle, x1, y1, x2, y2, colour); 109 drawing_internal *dri = PRIVATE_CAST(dr);
110 dri->pub.api->draw_line(dr, x1, y1, x2, y2, colour);
88} 111}
89 112
90void draw_thick_line(drawing *dr, float thickness, 113void draw_thick_line(drawing *dr, float thickness,
91 float x1, float y1, float x2, float y2, int colour) 114 float x1, float y1, float x2, float y2, int colour)
92{ 115{
93 if (thickness < 1.0) 116 drawing_internal *dri = PRIVATE_CAST(dr);
94 thickness = 1.0; 117 if (thickness < 1.0F)
95 if (dr->api->draw_thick_line) { 118 thickness = 1.0F;
96 dr->api->draw_thick_line(dr->handle, thickness, 119 if (dri->pub.api->draw_thick_line) {
97 x1, y1, x2, y2, colour); 120 dri->pub.api->draw_thick_line(dr, thickness,
121 x1, y1, x2, y2, colour);
98 } else { 122 } else {
99 /* We'll fake it up with a filled polygon. The tweak to the 123 /* We'll fake it up with a filled polygon. The tweak to the
100 * thickness empirically compensates for rounding errors, because 124 * thickness empirically compensates for rounding errors, because
101 * polygon rendering uses integer coordinates. 125 * polygon rendering uses integer coordinates.
102 */ 126 */
103 float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); 127 float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
104 float tvhatx = (x2 - x1)/len * (thickness/2 - 0.2); 128 float tvhatx = (x2 - x1)/len * (thickness/2 - 0.2F);
105 float tvhaty = (y2 - y1)/len * (thickness/2 - 0.2); 129 float tvhaty = (y2 - y1)/len * (thickness/2 - 0.2F);
106 int p[8]; 130 int p[8];
107 131
108 p[0] = x1 - tvhaty; 132 p[0] = x1 - tvhaty;
@@ -113,59 +137,67 @@ void draw_thick_line(drawing *dr, float thickness,
113 p[5] = y2 - tvhatx; 137 p[5] = y2 - tvhatx;
114 p[6] = x1 + tvhaty; 138 p[6] = x1 + tvhaty;
115 p[7] = y1 - tvhatx; 139 p[7] = y1 - tvhatx;
116 dr->api->draw_polygon(dr->handle, p, 4, colour, colour); 140 dri->pub.api->draw_polygon(dr, p, 4, colour, colour);
117 } 141 }
118} 142}
119 143
120void draw_polygon(drawing *dr, int *coords, int npoints, 144void draw_polygon(drawing *dr, const int *coords, int npoints,
121 int fillcolour, int outlinecolour) 145 int fillcolour, int outlinecolour)
122{ 146{
123 dr->api->draw_polygon(dr->handle, coords, npoints, fillcolour, 147 drawing_internal *dri = PRIVATE_CAST(dr);
124 outlinecolour); 148 dri->pub.api->draw_polygon(dr, coords, npoints, fillcolour,
149 outlinecolour);
125} 150}
126 151
127void draw_circle(drawing *dr, int cx, int cy, int radius, 152void draw_circle(drawing *dr, int cx, int cy, int radius,
128 int fillcolour, int outlinecolour) 153 int fillcolour, int outlinecolour)
129{ 154{
130 dr->api->draw_circle(dr->handle, cx, cy, radius, fillcolour, 155 drawing_internal *dri = PRIVATE_CAST(dr);
131 outlinecolour); 156 dri->pub.api->draw_circle(dr, cx, cy, radius, fillcolour,
157 outlinecolour);
132} 158}
133 159
134void draw_update(drawing *dr, int x, int y, int w, int h) 160void draw_update(drawing *dr, int x, int y, int w, int h)
135{ 161{
136 if (dr->api->draw_update) 162 drawing_internal *dri = PRIVATE_CAST(dr);
137 dr->api->draw_update(dr->handle, x, y, w, h); 163 if (dri->pub.api->draw_update)
164 dri->pub.api->draw_update(dr, x, y, w, h);
138} 165}
139 166
140void clip(drawing *dr, int x, int y, int w, int h) 167void clip(drawing *dr, int x, int y, int w, int h)
141{ 168{
142 dr->api->clip(dr->handle, x, y, w, h); 169 drawing_internal *dri = PRIVATE_CAST(dr);
170 dri->pub.api->clip(dr, x, y, w, h);
143} 171}
144 172
145void unclip(drawing *dr) 173void unclip(drawing *dr)
146{ 174{
147 dr->api->unclip(dr->handle); 175 drawing_internal *dri = PRIVATE_CAST(dr);
176 dri->pub.api->unclip(dr);
148} 177}
149 178
150void start_draw(drawing *dr) 179void start_draw(drawing *dr)
151{ 180{
152 dr->api->start_draw(dr->handle); 181 drawing_internal *dri = PRIVATE_CAST(dr);
182 dri->pub.api->start_draw(dr);
153} 183}
154 184
155void end_draw(drawing *dr) 185void end_draw(drawing *dr)
156{ 186{
157 dr->api->end_draw(dr->handle); 187 drawing_internal *dri = PRIVATE_CAST(dr);
188 dri->pub.api->end_draw(dr);
158} 189}
159 190
160char *text_fallback(drawing *dr, const char *const *strings, int nstrings) 191char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
161{ 192{
193 drawing_internal *dri = PRIVATE_CAST(dr);
162 int i; 194 int i;
163 195
164 /* 196 /*
165 * If the drawing implementation provides one of these, use it. 197 * If the drawing implementation provides one of these, use it.
166 */ 198 */
167 if (dr && dr->api->text_fallback) 199 if (dr && dri->pub.api->text_fallback)
168 return dr->api->text_fallback(dr->handle, strings, nstrings); 200 return dri->pub.api->text_fallback(dr, strings, nstrings);
169 201
170 /* 202 /*
171 * Otherwise, do the simple thing and just pick the first string 203 * Otherwise, do the simple thing and just pick the first string
@@ -192,18 +224,19 @@ char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
192 224
193void status_bar(drawing *dr, const char *text) 225void status_bar(drawing *dr, const char *text)
194{ 226{
227 drawing_internal *dri = PRIVATE_CAST(dr);
195 char *rewritten; 228 char *rewritten;
196 229
197 if (!dr->api->status_bar) 230 if (!dri->pub.api->status_bar)
198 return; 231 return;
199 232
200 assert(dr->me); 233 assert(dri->me);
201 234
202 rewritten = midend_rewrite_statusbar(dr->me, text); 235 rewritten = midend_rewrite_statusbar(dri->me, text);
203 if (!dr->laststatus || strcmp(rewritten, dr->laststatus)) { 236 if (!dri->laststatus || strcmp(rewritten, dri->laststatus)) {
204 dr->api->status_bar(dr->handle, rewritten); 237 dri->pub.api->status_bar(dr, rewritten);
205 sfree(dr->laststatus); 238 sfree(dri->laststatus);
206 dr->laststatus = rewritten; 239 dri->laststatus = rewritten;
207 } else { 240 } else {
208 sfree(rewritten); 241 sfree(rewritten);
209 } 242 }
@@ -211,74 +244,85 @@ void status_bar(drawing *dr, const char *text)
211 244
212blitter *blitter_new(drawing *dr, int w, int h) 245blitter *blitter_new(drawing *dr, int w, int h)
213{ 246{
214 return dr->api->blitter_new(dr->handle, w, h); 247 drawing_internal *dri = PRIVATE_CAST(dr);
248 return dri->pub.api->blitter_new(dr, w, h);
215} 249}
216 250
217void blitter_free(drawing *dr, blitter *bl) 251void blitter_free(drawing *dr, blitter *bl)
218{ 252{
219 dr->api->blitter_free(dr->handle, bl); 253 drawing_internal *dri = PRIVATE_CAST(dr);
254 dri->pub.api->blitter_free(dr, bl);
220} 255}
221 256
222void blitter_save(drawing *dr, blitter *bl, int x, int y) 257void blitter_save(drawing *dr, blitter *bl, int x, int y)
223{ 258{
224 dr->api->blitter_save(dr->handle, bl, x, y); 259 drawing_internal *dri = PRIVATE_CAST(dr);
260 dri->pub.api->blitter_save(dr, bl, x, y);
225} 261}
226 262
227void blitter_load(drawing *dr, blitter *bl, int x, int y) 263void blitter_load(drawing *dr, blitter *bl, int x, int y)
228{ 264{
229 dr->api->blitter_load(dr->handle, bl, x, y); 265 drawing_internal *dri = PRIVATE_CAST(dr);
266 dri->pub.api->blitter_load(dr, bl, x, y);
230} 267}
231 268
232void print_begin_doc(drawing *dr, int pages) 269void print_begin_doc(drawing *dr, int pages)
233{ 270{
234 dr->api->begin_doc(dr->handle, pages); 271 drawing_internal *dri = PRIVATE_CAST(dr);
272 dri->pub.api->begin_doc(dr, pages);
235} 273}
236 274
237void print_begin_page(drawing *dr, int number) 275void print_begin_page(drawing *dr, int number)
238{ 276{
239 dr->api->begin_page(dr->handle, number); 277 drawing_internal *dri = PRIVATE_CAST(dr);
278 dri->pub.api->begin_page(dr, number);
240} 279}
241 280
242void print_begin_puzzle(drawing *dr, float xm, float xc, 281void print_begin_puzzle(drawing *dr, float xm, float xc,
243 float ym, float yc, int pw, int ph, float wmm, 282 float ym, float yc, int pw, int ph, float wmm,
244 float scale) 283 float scale)
245{ 284{
246 dr->scale = scale; 285 drawing_internal *dri = PRIVATE_CAST(dr);
247 dr->ncolours = 0; 286 dri->scale = scale;
248 dr->api->begin_puzzle(dr->handle, xm, xc, ym, yc, pw, ph, wmm); 287 dri->ncolours = 0;
288 dri->pub.api->begin_puzzle(dr, xm, xc, ym, yc, pw, ph, wmm);
249} 289}
250 290
251void print_end_puzzle(drawing *dr) 291void print_end_puzzle(drawing *dr)
252{ 292{
253 dr->api->end_puzzle(dr->handle); 293 drawing_internal *dri = PRIVATE_CAST(dr);
254 dr->scale = 1.0F; 294 dri->pub.api->end_puzzle(dr);
295 dri->scale = 1.0F;
255} 296}
256 297
257void print_end_page(drawing *dr, int number) 298void print_end_page(drawing *dr, int number)
258{ 299{
259 dr->api->end_page(dr->handle, number); 300 drawing_internal *dri = PRIVATE_CAST(dr);
301 dri->pub.api->end_page(dr, number);
260} 302}
261 303
262void print_end_doc(drawing *dr) 304void print_end_doc(drawing *dr)
263{ 305{
264 dr->api->end_doc(dr->handle); 306 drawing_internal *dri = PRIVATE_CAST(dr);
307 dri->pub.api->end_doc(dr);
265} 308}
266 309
267void print_get_colour(drawing *dr, int colour, bool printing_in_colour, 310void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
268 int *hatch, float *r, float *g, float *b) 311 int *hatch, float *r, float *g, float *b)
269{ 312{
270 assert(colour >= 0 && colour < dr->ncolours); 313 drawing_internal *dri = PRIVATE_CAST(dr);
271 if (dr->colours[colour].hatch_when == 2 || 314 assert(colour >= 0 && colour < dri->ncolours);
272 (dr->colours[colour].hatch_when == 1 && !printing_in_colour)) { 315 if (dri->colours[colour].hatch_when == 2 ||
273 *hatch = dr->colours[colour].hatch; 316 (dri->colours[colour].hatch_when == 1 && !printing_in_colour)) {
317 *hatch = dri->colours[colour].hatch;
274 } else { 318 } else {
275 *hatch = -1; 319 *hatch = -1;
276 if (printing_in_colour) { 320 if (printing_in_colour) {
277 *r = dr->colours[colour].r; 321 *r = dri->colours[colour].r;
278 *g = dr->colours[colour].g; 322 *g = dri->colours[colour].g;
279 *b = dr->colours[colour].b; 323 *b = dri->colours[colour].b;
280 } else { 324 } else {
281 *r = *g = *b = dr->colours[colour].grey; 325 *r = *g = *b = dri->colours[colour].grey;
282 } 326 }
283 } 327 }
284} 328}
@@ -286,18 +330,19 @@ void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
286static int print_generic_colour(drawing *dr, float r, float g, float b, 330static int print_generic_colour(drawing *dr, float r, float g, float b,
287 float grey, int hatch, int hatch_when) 331 float grey, int hatch, int hatch_when)
288{ 332{
289 if (dr->ncolours >= dr->coloursize) { 333 drawing_internal *dri = PRIVATE_CAST(dr);
290 dr->coloursize = dr->ncolours + 16; 334 if (dri->ncolours >= dri->coloursize) {
291 dr->colours = sresize(dr->colours, dr->coloursize, 335 dri->coloursize = dri->ncolours + 16;
336 dri->colours = sresize(dri->colours, dri->coloursize,
292 struct print_colour); 337 struct print_colour);
293 } 338 }
294 dr->colours[dr->ncolours].hatch = hatch; 339 dri->colours[dri->ncolours].hatch = hatch;
295 dr->colours[dr->ncolours].hatch_when = hatch_when; 340 dri->colours[dri->ncolours].hatch_when = hatch_when;
296 dr->colours[dr->ncolours].r = r; 341 dri->colours[dri->ncolours].r = r;
297 dr->colours[dr->ncolours].g = g; 342 dri->colours[dri->ncolours].g = g;
298 dr->colours[dr->ncolours].b = b; 343 dri->colours[dri->ncolours].b = b;
299 dr->colours[dr->ncolours].grey = grey; 344 dri->colours[dri->ncolours].grey = grey;
300 return dr->ncolours++; 345 return dri->ncolours++;
301} 346}
302 347
303int print_mono_colour(drawing *dr, int grey) 348int print_mono_colour(drawing *dr, int grey)
@@ -332,6 +377,8 @@ int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch)
332 377
333void print_line_width(drawing *dr, int width) 378void print_line_width(drawing *dr, int width)
334{ 379{
380 drawing_internal *dri = PRIVATE_CAST(dr);
381
335 /* 382 /*
336 * I don't think it's entirely sensible to have line widths be 383 * I don't think it's entirely sensible to have line widths be
337 * entirely relative to the puzzle size; there is a point 384 * entirely relative to the puzzle size; there is a point
@@ -344,10 +391,11 @@ void print_line_width(drawing *dr, int width)
344 * _square root_ of the main puzzle scale. Double the puzzle 391 * _square root_ of the main puzzle scale. Double the puzzle
345 * size, and the line width multiplies by 1.4. 392 * size, and the line width multiplies by 1.4.
346 */ 393 */
347 dr->api->line_width(dr->handle, (float)sqrt(dr->scale) * width); 394 dri->pub.api->line_width(dr, (float)sqrt(dri->scale) * width);
348} 395}
349 396
350void print_line_dotted(drawing *dr, bool dotted) 397void print_line_dotted(drawing *dr, bool dotted)
351{ 398{
352 dr->api->line_dotted(dr->handle, dotted); 399 drawing_internal *dri = PRIVATE_CAST(dr);
400 dri->pub.api->line_dotted(dr, dotted);
353} 401}
diff --git a/apps/plugins/puzzles/src/dsf.c b/apps/plugins/puzzles/src/dsf.c
index 832bb3005a..bed564f8ec 100644
--- a/apps/plugins/puzzles/src/dsf.c
+++ b/apps/plugins/puzzles/src/dsf.c
@@ -5,188 +5,301 @@
5 */ 5 */
6 6
7#include <assert.h> 7#include <assert.h>
8#include <limits.h>
8#include <string.h> 9#include <string.h>
9 10
10#include "puzzles.h" 11#include "puzzles.h"
11 12
12/*void print_dsf(int *dsf, int size) 13#define DSF_INDEX_MASK (UINT_MAX >> 1)
14#define DSF_FLAG_CANONICAL (UINT_MAX & ~(UINT_MAX >> 1))
15#define DSF_MAX (DSF_INDEX_MASK + 1)
16
17struct DSF {
18 /*
19 * Size of the dsf.
20 */
21 size_t size;
22
23 /*
24 * Main array storing the data structure.
25 *
26 * If n is the canonical element of an equivalence class,
27 * parent_or_size[n] holds the number of elements in that class,
28 * bitwise-ORed with DSF_FLAG_CANONICAL.
29 *
30 * If n is not the canonical element, parent_or_size[n] holds the
31 * index of another element nearer to the root of the tree for
32 * that class.
33 */
34 unsigned *parent_or_size;
35
36 /*
37 * Extra storage for flip tracking.
38 *
39 * If n is not a canonical element, flip[n] indicates whether the
40 * sense of this element is flipped relative to parent_or_size[n].
41 *
42 * If n is a canonical element, flip[n] is unused.
43 */
44 unsigned char *flip;
45
46 /*
47 * Extra storage for minimal-element tracking.
48 *
49 * If n is a canonical element, min[n] holds the index of the
50 * smallest value in n's equivalence class.
51 *
52 * If n is not a canonical element, min[n] is unused.
53 */
54 unsigned *min;
55};
56
57static DSF *dsf_new_internal(int size, bool flip, bool min)
13{ 58{
14 int *printed_elements = snewn(size, int); 59 DSF *dsf;
15 int *equal_elements = snewn(size, int);
16 int *inverse_elements = snewn(size, int);
17 int printed_count = 0, equal_count, inverse_count;
18 int i, n;
19 bool inverse;
20 60
21 memset(printed_elements, -1, sizeof(int) * size); 61 assert(0 < size && size <= DSF_MAX && "Bad dsf size");
22 62
23 while (1) { 63 dsf = snew(DSF);
24 equal_count = 0; 64 dsf->size = size;
25 inverse_count = 0; 65 dsf->parent_or_size = snewn(size, unsigned);
26 for (i = 0; i < size; ++i) { 66 dsf->flip = flip ? snewn(size, unsigned char) : NULL;
27 if (!memchr(printed_elements, i, sizeof(int) * size)) 67 dsf->min = min ? snewn(size, unsigned) : NULL;
28 break; 68
29 } 69 dsf_reinit(dsf);
30 if (i == size)
31 goto done;
32
33 i = dsf_canonify(dsf, i);
34
35 for (n = 0; n < size; ++n) {
36 if (edsf_canonify(dsf, n, &inverse) == i) {
37 if (inverse)
38 inverse_elements[inverse_count++] = n;
39 else
40 equal_elements[equal_count++] = n;
41 }
42 }
43
44 for (n = 0; n < equal_count; ++n) {
45 fprintf(stderr, "%d ", equal_elements[n]);
46 printed_elements[printed_count++] = equal_elements[n];
47 }
48 if (inverse_count) {
49 fprintf(stderr, "!= ");
50 for (n = 0; n < inverse_count; ++n) {
51 fprintf(stderr, "%d ", inverse_elements[n]);
52 printed_elements[printed_count++] = inverse_elements[n];
53 }
54 }
55 fprintf(stderr, "\n");
56 }
57done:
58 70
59 sfree(printed_elements); 71 return dsf;
60 sfree(equal_elements); 72}
61 sfree(inverse_elements);
62}*/
63 73
64void dsf_init(int *dsf, int size) 74DSF *dsf_new(int size)
65{ 75{
66 int i; 76 return dsf_new_internal(size, false, false);
77}
67 78
68 for (i = 0; i < size; i++) dsf[i] = 6; 79DSF *dsf_new_flip(int size)
69 /* Bottom bit of each element of this array stores whether that 80{
70 * element is opposite to its parent, which starts off as 81 return dsf_new_internal(size, true, false);
71 * false. Second bit of each element stores whether that element
72 * is the root of its tree or not. If it's not the root, the
73 * remaining 30 bits are the parent, otherwise the remaining 30
74 * bits are the number of elements in the tree. */
75} 82}
76 83
77int *snew_dsf(int size) 84DSF *dsf_new_min(int size)
78{ 85{
79 int *ret; 86 return dsf_new_internal(size, false, true);
80 87}
81 ret = snewn(size, int); 88
82 dsf_init(ret, size); 89void dsf_reinit(DSF *dsf)
90{
91 size_t i;
92
93 /* Every element starts as the root of an equivalence class of size 1 */
94 for (i = 0; i < dsf->size; i++)
95 dsf->parent_or_size[i] = DSF_FLAG_CANONICAL | 1;
83 96
84 /*print_dsf(ret, size); */ 97 /* If we're tracking minima then every element is also its own min */
98 if (dsf->min)
99 for (i = 0; i < dsf->size; i++)
100 dsf->min[i] = i;
85 101
86 return ret; 102 /* No need to initialise dsf->flip, even if it exists, because
103 * only the entries for non-root elements are meaningful, and
104 * currently there are none. */
87} 105}
88 106
89int dsf_canonify(int *dsf, int index) 107void dsf_copy(DSF *to, DSF *from)
90{ 108{
91 return edsf_canonify(dsf, index, NULL); 109 assert(to->size == from->size && "Mismatch in dsf_copy");
110 memcpy(to->parent_or_size, from->parent_or_size,
111 to->size * sizeof(*to->parent_or_size));
112 if (to->flip) {
113 assert(from->flip && "Copying a non-flip dsf to a flip one");
114 memcpy(to->flip, from->flip, to->size * sizeof(*to->flip));
115 }
116 if (to->min) {
117 assert(from->min && "Copying a non-min dsf to a min one");
118 memcpy(to->min, from->min, to->size * sizeof(*to->min));
119 }
92} 120}
93 121
94void dsf_merge(int *dsf, int v1, int v2) 122
123void dsf_free(DSF *dsf)
95{ 124{
96 edsf_merge(dsf, v1, v2, false); 125 if (dsf) {
126 sfree(dsf->parent_or_size);
127 sfree(dsf->flip);
128 sfree(dsf->min);
129 sfree(dsf);
130 }
97} 131}
98 132
99int dsf_size(int *dsf, int index) { 133static inline size_t dsf_find_root(DSF *dsf, size_t n)
100 return dsf[dsf_canonify(dsf, index)] >> 2; 134{
135 while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL))
136 n = dsf->parent_or_size[n];
137 return n;
138}
139
140static inline void dsf_path_compress(DSF *dsf, size_t n, size_t root)
141{
142 while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) {
143 size_t prev = n;
144 n = dsf->parent_or_size[n];
145 dsf->parent_or_size[prev] = root;
146 }
147 assert(n == root);
101} 148}
102 149
103int edsf_canonify(int *dsf, int index, bool *inverse_return) 150int dsf_canonify(DSF *dsf, int n)
104{ 151{
105 int start_index = index, canonical_index; 152 size_t root;
106 bool inverse = false;
107 153
108/* fprintf(stderr, "dsf = %p\n", dsf); */ 154 assert(0 <= n && n < dsf->size && "Overrun in dsf_canonify");
109/* fprintf(stderr, "Canonify %2d\n", index); */
110 155
111 assert(index >= 0); 156 root = dsf_find_root(dsf, n);
157 dsf_path_compress(dsf, n, root);
158 return root;
159}
160
161void dsf_merge(DSF *dsf, int n1, int n2)
162{
163 size_t r1, r2, s1, s2, root;
164
165 assert(0 <= n1 && n1 < dsf->size && "Overrun in dsf_merge");
166 assert(0 <= n2 && n2 < dsf->size && "Overrun in dsf_merge");
167 assert(!dsf->flip && "dsf_merge on a flip dsf");
168
169 /* Find the root elements */
170 r1 = dsf_find_root(dsf, n1);
171 r2 = dsf_find_root(dsf, n2);
172
173 if (r1 == r2) {
174 /* Classes are already the same, so we have a common root */
175 root = r1;
176 } else {
177 /* Classes must be merged */
178
179 /* Decide which one to use as the overall root, based on size */
180 s1 = dsf->parent_or_size[r1] & DSF_INDEX_MASK;
181 s2 = dsf->parent_or_size[r2] & DSF_INDEX_MASK;
182 if (s1 > s2) {
183 dsf->parent_or_size[r2] = root = r1;
184 } else {
185 dsf->parent_or_size[r1] = root = r2;
186 }
187 dsf->parent_or_size[root] = (s1 + s2) | DSF_FLAG_CANONICAL;
112 188
113 /* Find the index of the canonical element of the 'equivalence class' of 189 if (dsf->min) {
114 * which start_index is a member, and figure out whether start_index is the 190 /* Update the min of the merged class */
115 * same as or inverse to that. */ 191 unsigned m1 = dsf->min[r1], m2 = dsf->min[r2];
116 while ((dsf[index] & 2) == 0) { 192 dsf->min[root] = m1 < m2 ? m1 : m2;
117 inverse ^= (dsf[index] & 1); 193 }
118 index = dsf[index] >> 2;
119/* fprintf(stderr, "index = %2d, ", index); */
120/* fprintf(stderr, "inverse = %d\n", inverse); */
121 } 194 }
122 canonical_index = index; 195
123 196 /* Path-compress both paths from n1 and n2 so they point at the new root */
124 if (inverse_return) 197 dsf_path_compress(dsf, n1, root);
125 *inverse_return = inverse; 198 dsf_path_compress(dsf, n2, root);
126 199}
127 /* Update every member of this 'equivalence class' to point directly at the 200
128 * canonical member. */ 201bool dsf_equivalent(DSF *dsf, int n1, int n2)
129 index = start_index; 202{
130 while (index != canonical_index) { 203 return dsf_canonify(dsf, n1) == dsf_canonify(dsf, n2);
131 int nextindex = dsf[index] >> 2; 204}
132 bool nextinverse = inverse ^ (dsf[index] & 1); 205
133 dsf[index] = (canonical_index << 2) | inverse; 206int dsf_size(DSF *dsf, int n)
134 inverse = nextinverse; 207{
135 index = nextindex; 208 size_t root = dsf_canonify(dsf, n);
209 return dsf->parent_or_size[root] & DSF_INDEX_MASK;
210}
211
212static inline size_t dsf_find_root_flip(DSF *dsf, size_t n, unsigned *flip)
213{
214 *flip = 0;
215 while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) {
216 *flip ^= dsf->flip[n];
217 n = dsf->parent_or_size[n];
218 }
219 return n;
220}
221
222static inline void dsf_path_compress_flip(DSF *dsf, size_t n, size_t root,
223 unsigned flip)
224{
225 while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) {
226 size_t prev = n;
227 unsigned flip_prev = flip;
228 n = dsf->parent_or_size[n];
229 flip ^= dsf->flip[prev];
230 dsf->flip[prev] = flip_prev;
231 dsf->parent_or_size[prev] = root;
136 } 232 }
233 assert(n == root);
234}
235
236int dsf_canonify_flip(DSF *dsf, int n, bool *inverse)
237{
238 size_t root;
239 unsigned flip;
240
241 assert(0 <= n && n < dsf->size && "Overrun in dsf_canonify_flip");
242 assert(dsf->flip && "dsf_canonify_flip on a non-flip dsf");
243
244 root = dsf_find_root_flip(dsf, n, &flip);
245 dsf_path_compress_flip(dsf, n, root, flip);
246 *inverse = flip;
247 return root;
248}
249
250void dsf_merge_flip(DSF *dsf, int n1, int n2, bool inverse)
251{
252 size_t r1, r2, s1, s2, root;
253 unsigned f1, f2;
254
255 assert(0 <= n1 && n1 < dsf->size && "Overrun in dsf_merge_flip");
256 assert(0 <= n2 && n2 < dsf->size && "Overrun in dsf_merge_flip");
257 assert(dsf->flip && "dsf_merge_flip on a non-flip dsf");
258
259 /* Find the root elements */
260 r1 = dsf_find_root_flip(dsf, n1, &f1);
261 r2 = dsf_find_root_flip(dsf, n2, &f2);
262
263 if (r1 == r2) {
264 /* Classes are already the same, so we have a common root */
265 assert((f1 ^ f2 ^ inverse) == 0 && "Inconsistency in dsf_merge_flip");
266 root = r1;
267 } else {
268 /* Classes must be merged */
137 269
138 assert(!inverse); 270 /* Decide which one to use as the overall root, based on size */
139 271 s1 = dsf->parent_or_size[r1] & DSF_INDEX_MASK;
140/* fprintf(stderr, "Return %2d\n", index); */ 272 s2 = dsf->parent_or_size[r2] & DSF_INDEX_MASK;
141 273 if (s1 > s2) {
142 return index; 274 dsf->parent_or_size[r2] = root = r1;
143} 275 dsf->flip[r2] = f1 ^ f2 ^ inverse;
144 276 f2 ^= dsf->flip[r2];
145void edsf_merge(int *dsf, int v1, int v2, bool inverse) 277 } else {
146{ 278 root = r2;
147 bool i1, i2; 279 dsf->parent_or_size[r1] = root = r2;
148 280 dsf->flip[r1] = f1 ^ f2 ^ inverse;
149/* fprintf(stderr, "dsf = %p\n", dsf); */ 281 f1 ^= dsf->flip[r1];
150/* fprintf(stderr, "Merge [%2d,%2d], %d\n", v1, v2, inverse); */ 282 }
151 283 dsf->parent_or_size[root] = (s1 + s2) | DSF_FLAG_CANONICAL;
152 v1 = edsf_canonify(dsf, v1, &i1); 284
153 assert(dsf[v1] & 2); 285 if (dsf->min) {
154 inverse ^= i1; 286 /* Update the min of the merged class */
155 v2 = edsf_canonify(dsf, v2, &i2); 287 unsigned m1 = dsf->min[r1], m2 = dsf->min[r2];
156 assert(dsf[v2] & 2); 288 dsf->min[root] = m1 < m2 ? m1 : m2;
157 inverse ^= i2; 289 }
158
159/* fprintf(stderr, "Doing [%2d,%2d], %d\n", v1, v2, inverse); */
160
161 if (v1 == v2)
162 assert(!inverse);
163 else {
164 /*
165 * We always make the smaller of v1 and v2 the new canonical
166 * element. This ensures that the canonical element of any
167 * class in this structure is always the first element in
168 * it. 'Keen' depends critically on this property.
169 *
170 * (Jonas Koelker previously had this code choosing which
171 * way round to connect the trees by examining the sizes of
172 * the classes being merged, so that the root of the
173 * larger-sized class became the new root. This gives better
174 * asymptotic performance, but I've changed it to do it this
175 * way because I like having a deterministic canonical
176 * element.)
177 */
178 if (v1 > v2) {
179 int v3 = v1;
180 v1 = v2;
181 v2 = v3;
182 }
183 dsf[v1] += (dsf[v2] >> 2) << 2;
184 dsf[v2] = (v1 << 2) | inverse;
185 } 290 }
186
187 v2 = edsf_canonify(dsf, v2, &i2);
188 assert(v2 == v1);
189 assert(i2 == inverse);
190 291
191/* fprintf(stderr, "dsf[%2d] = %2d\n", v2, dsf[v2]); */ 292 /* Path-compress both paths from n1 and n2 so they point at the new root */
293 dsf_path_compress_flip(dsf, n1, root, f1);
294 dsf_path_compress_flip(dsf, n2, root, f2);
295}
296
297int dsf_minimal(DSF *dsf, int n)
298{
299 size_t root;
300
301 assert(dsf->min && "dsf_minimal on a non-min dsf");
302
303 root = dsf_canonify(dsf, n);
304 return dsf->min[root];
192} 305}
diff --git a/apps/plugins/puzzles/src/emcc.c b/apps/plugins/puzzles/src/emcc.c
deleted file mode 100644
index 9a6c742482..0000000000
--- a/apps/plugins/puzzles/src/emcc.c
+++ /dev/null
@@ -1,978 +0,0 @@
1/*
2 * emcc.c: the C component of an Emscripten-based web/Javascript front
3 * end for Puzzles.
4 *
5 * The Javascript parts of this system live in emcclib.js and
6 * emccpre.js. It also depends on being run in the context of a web
7 * page containing an appropriate collection of bits and pieces (a
8 * canvas, some buttons and links etc), which is generated for each
9 * puzzle by the script html/jspage.pl.
10 */
11
12/*
13 * Further thoughts on possible enhancements:
14 *
15 * - I think it might be feasible to have these JS puzzles permit
16 * loading and saving games in disk files. Saving would be done by
17 * constructing a data: URI encapsulating the save file, and then
18 * telling the browser to visit that URI with the effect that it
19 * would naturally pop up a 'where would you like to save this'
20 * dialog box. Loading, more or less similarly, might be feasible
21 * by using the DOM File API to ask the user to select a file and
22 * permit us to see its contents.
23 *
24 * - I should think about whether these webified puzzles can support
25 * touchscreen-based tablet browsers (assuming there are any that
26 * can cope with the reasonably modern JS and run it fast enough to
27 * be worthwhile).
28 *
29 * - think about making use of localStorage. It might be useful to
30 * let the user save games into there as an alternative to disk
31 * files - disk files are all very well for getting the save right
32 * out of your browser to (e.g.) email to me as a bug report, but
33 * for just resuming a game you were in the middle of, you'd
34 * probably rather have a nice simple 'quick save' and 'quick load'
35 * button pair. Also, that might be a useful place to store
36 * preferences, if I ever get round to writing a preferences UI.
37 *
38 * - some CSS to make the button bar and configuration dialogs a
39 * little less ugly would probably not go amiss.
40 *
41 * - this is a downright silly idea, but it does occur to me that if
42 * I were to write a PDF output driver for the Puzzles printing
43 * API, then I might be able to implement a sort of 'printing'
44 * feature in this front end, using data: URIs again. (Ask the user
45 * exactly what they want printed, then construct an appropriate
46 * PDF and embed it in a gigantic data: URI. Then they can print
47 * that using whatever they normally use to print PDFs!)
48 */
49
50#include <assert.h>
51#include <stdio.h>
52#include <string.h>
53#include <stdarg.h>
54
55#include "puzzles.h"
56
57/*
58 * Extern references to Javascript functions provided in emcclib.js.
59 */
60extern void js_debug(const char *);
61extern void js_error_box(const char *message);
62extern void js_remove_type_dropdown(void);
63extern void js_remove_solve_button(void);
64extern void js_add_preset(int menuid, const char *name, int value);
65extern int js_add_preset_submenu(int menuid, const char *name);
66extern int js_get_selected_preset(void);
67extern void js_select_preset(int n);
68extern void js_get_date_64(unsigned *p);
69extern void js_update_permalinks(const char *desc, const char *seed);
70extern void js_enable_undo_redo(bool undo, bool redo);
71extern void js_activate_timer();
72extern void js_deactivate_timer();
73extern void js_canvas_start_draw(void);
74extern void js_canvas_draw_update(int x, int y, int w, int h);
75extern void js_canvas_end_draw(void);
76extern void js_canvas_draw_rect(int x, int y, int w, int h,
77 const char *colour);
78extern void js_canvas_clip_rect(int x, int y, int w, int h);
79extern void js_canvas_unclip(void);
80extern void js_canvas_draw_line(float x1, float y1, float x2, float y2,
81 int width, const char *colour);
82extern void js_canvas_draw_poly(int *points, int npoints,
83 const char *fillcolour,
84 const char *outlinecolour);
85extern void js_canvas_draw_circle(int x, int y, int r,
86 const char *fillcolour,
87 const char *outlinecolour);
88extern int js_canvas_find_font_midpoint(int height, const char *fontptr);
89extern void js_canvas_draw_text(int x, int y, int halign,
90 const char *colptr, const char *fontptr,
91 const char *text);
92extern int js_canvas_new_blitter(int w, int h);
93extern void js_canvas_free_blitter(int id);
94extern void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h);
95extern void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h);
96extern void js_canvas_make_statusbar(void);
97extern void js_canvas_set_statusbar(const char *text);
98extern void js_canvas_set_size(int w, int h);
99
100extern void js_dialog_init(const char *title);
101extern void js_dialog_string(int i, const char *title, const char *initvalue);
102extern void js_dialog_choices(int i, const char *title, const char *choicelist,
103 int initvalue);
104extern void js_dialog_boolean(int i, const char *title, bool initvalue);
105extern void js_dialog_launch(void);
106extern void js_dialog_cleanup(void);
107extern void js_focus_canvas(void);
108
109/*
110 * Call JS to get the date, and use that to initialise our random
111 * number generator to invent the first game seed.
112 */
113void get_random_seed(void **randseed, int *randseedsize)
114{
115 unsigned *ret = snewn(2, unsigned);
116 js_get_date_64(ret);
117 *randseed = ret;
118 *randseedsize = 2*sizeof(unsigned);
119}
120
121/*
122 * Fatal error, called in cases of complete despair such as when
123 * malloc() has returned NULL.
124 */
125void fatal(const char *fmt, ...)
126{
127 char buf[512];
128 va_list ap;
129
130 strcpy(buf, "puzzle fatal error: ");
131
132 va_start(ap, fmt);
133 vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap);
134 va_end(ap);
135
136 js_error_box(buf);
137}
138
139void debug_printf(const char *fmt, ...)
140{
141 char buf[512];
142 va_list ap;
143 va_start(ap, fmt);
144 vsnprintf(buf, sizeof(buf), fmt, ap);
145 va_end(ap);
146 js_debug(buf);
147}
148
149/*
150 * Helper function that makes it easy to test strings that might be
151 * NULL.
152 */
153int strnullcmp(const char *a, const char *b)
154{
155 if (a == NULL || b == NULL)
156 return a != NULL ? +1 : b != NULL ? -1 : 0;
157 return strcmp(a, b);
158}
159
160/*
161 * HTMLish names for the colours allocated by the puzzle.
162 */
163char **colour_strings;
164int ncolours;
165
166/*
167 * The global midend object.
168 */
169midend *me;
170
171/* ----------------------------------------------------------------------
172 * Timing functions.
173 */
174bool timer_active = false;
175void deactivate_timer(frontend *fe)
176{
177 js_deactivate_timer();
178 timer_active = false;
179}
180void activate_timer(frontend *fe)
181{
182 if (!timer_active) {
183 js_activate_timer();
184 timer_active = true;
185 }
186}
187void timer_callback(double tplus)
188{
189 if (timer_active)
190 midend_timer(me, tplus);
191}
192
193/* ----------------------------------------------------------------------
194 * Helper functions to resize the canvas, and variables to remember
195 * its size for other functions (e.g. trimming blitter rectangles).
196 */
197static int canvas_w, canvas_h;
198
199/* Called when we resize as a result of changing puzzle settings */
200static void resize(void)
201{
202 int w, h;
203 w = h = INT_MAX;
204 midend_size(me, &w, &h, false);
205 js_canvas_set_size(w, h);
206 canvas_w = w;
207 canvas_h = h;
208}
209
210/* Called from JS when the user uses the resize handle */
211void resize_puzzle(int w, int h)
212{
213 midend_size(me, &w, &h, true);
214 if (canvas_w != w || canvas_h != h) {
215 js_canvas_set_size(w, h);
216 canvas_w = w;
217 canvas_h = h;
218 midend_force_redraw(me);
219 }
220}
221
222/* Called from JS when the user uses the restore button */
223void restore_puzzle_size(int w, int h)
224{
225 midend_reset_tilesize(me);
226 resize();
227 midend_force_redraw(me);
228}
229
230/*
231 * HTML doesn't give us a default frontend colour of its own, so we
232 * just make up a lightish grey ourselves.
233 */
234void frontend_default_colour(frontend *fe, float *output)
235{
236 output[0] = output[1] = output[2] = 0.9F;
237}
238
239/*
240 * Helper function called from all over the place to ensure the undo
241 * and redo buttons get properly enabled and disabled after every move
242 * or undo or new-game event.
243 */
244static void update_undo_redo(void)
245{
246 js_enable_undo_redo(midend_can_undo(me), midend_can_redo(me));
247}
248
249/*
250 * Mouse event handlers called from JS.
251 */
252void mousedown(int x, int y, int button)
253{
254 button = (button == 0 ? LEFT_BUTTON :
255 button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON);
256 midend_process_key(me, x, y, button);
257 update_undo_redo();
258}
259
260void mouseup(int x, int y, int button)
261{
262 button = (button == 0 ? LEFT_RELEASE :
263 button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE);
264 midend_process_key(me, x, y, button);
265 update_undo_redo();
266}
267
268void mousemove(int x, int y, int buttons)
269{
270 int button = (buttons & 2 ? MIDDLE_DRAG :
271 buttons & 4 ? RIGHT_DRAG : LEFT_DRAG);
272 midend_process_key(me, x, y, button);
273 update_undo_redo();
274}
275
276/*
277 * Keyboard handler called from JS.
278 */
279void key(int keycode, int charcode, const char *key, const char *chr,
280 bool shift, bool ctrl)
281{
282 int keyevent = -1;
283
284 if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Del") ||
285 keycode == 8 || keycode == 46) {
286 keyevent = 127; /* Backspace / Delete */
287 } else if (!strnullcmp(key, "Enter") || keycode == 13) {
288 keyevent = 13; /* return */
289 } else if (!strnullcmp(key, "Left") || keycode == 37) {
290 keyevent = CURSOR_LEFT;
291 } else if (!strnullcmp(key, "Up") || keycode == 38) {
292 keyevent = CURSOR_UP;
293 } else if (!strnullcmp(key, "Right") || keycode == 39) {
294 keyevent = CURSOR_RIGHT;
295 } else if (!strnullcmp(key, "Down") || keycode == 40) {
296 keyevent = CURSOR_DOWN;
297 } else if (!strnullcmp(key, "End") || keycode == 35) {
298 /*
299 * We interpret Home, End, PgUp and PgDn as numeric keypad
300 * controls regardless of whether they're the ones on the
301 * numeric keypad (since we can't tell). The effect of
302 * this should only be that the non-numeric-pad versions
303 * of those keys generate directions in 8-way movement
304 * puzzles like Cube and Inertia.
305 */
306 keyevent = MOD_NUM_KEYPAD | '1';
307 } else if (!strnullcmp(key, "PageDown") || keycode==34) {
308 keyevent = MOD_NUM_KEYPAD | '3';
309 } else if (!strnullcmp(key, "Home") || keycode==36) {
310 keyevent = MOD_NUM_KEYPAD | '7';
311 } else if (!strnullcmp(key, "PageUp") || keycode==33) {
312 keyevent = MOD_NUM_KEYPAD | '9';
313 } else if (shift && ctrl && (keycode & 0x1F) == 26) {
314 keyevent = UI_REDO;
315 } else if (chr && chr[0] && !chr[1]) {
316 keyevent = chr[0] & 0xFF;
317 } else if (keycode >= 96 && keycode < 106) {
318 keyevent = MOD_NUM_KEYPAD | ('0' + keycode - 96);
319 } else if (keycode >= 65 && keycode <= 90) {
320 keyevent = keycode + (shift ? 0 : 32);
321 } else if (keycode >= 48 && keycode <= 57) {
322 keyevent = keycode;
323 } else if (keycode == 32) { /* space / CURSOR_SELECT2 */
324 keyevent = keycode;
325 }
326
327 if (keyevent >= 0) {
328 if (shift && (keyevent >= 0x100 && !IS_UI_FAKE_KEY(keyevent)))
329 keyevent |= MOD_SHFT;
330
331 if (ctrl && !IS_UI_FAKE_KEY(keyevent)) {
332 if (keyevent >= 0x100)
333 keyevent |= MOD_CTRL;
334 else
335 keyevent &= 0x1F;
336 }
337
338 midend_process_key(me, 0, 0, keyevent);
339 update_undo_redo();
340 }
341}
342
343/*
344 * Helper function called from several places to update the permalinks
345 * whenever a new game is created.
346 */
347static void update_permalinks(void)
348{
349 char *desc, *seed;
350 desc = midend_get_game_id(me);
351 seed = midend_get_random_seed(me);
352 js_update_permalinks(desc, seed);
353 sfree(desc);
354 sfree(seed);
355}
356
357/*
358 * Callback from the midend when the game ids change, so we can update
359 * the permalinks.
360 */
361static void ids_changed(void *ignored)
362{
363 update_permalinks();
364}
365
366/* ----------------------------------------------------------------------
367 * Implementation of the drawing API by calling Javascript canvas
368 * drawing functions. (Well, half of it; the other half is on the JS
369 * side.)
370 */
371static void js_start_draw(void *handle)
372{
373 js_canvas_start_draw();
374}
375
376static void js_clip(void *handle, int x, int y, int w, int h)
377{
378 js_canvas_clip_rect(x, y, w, h);
379}
380
381static void js_unclip(void *handle)
382{
383 js_canvas_unclip();
384}
385
386static void js_draw_text(void *handle, int x, int y, int fonttype,
387 int fontsize, int align, int colour,
388 const char *text)
389{
390 char fontstyle[80];
391 int halign;
392
393 sprintf(fontstyle, "%dpx %s", fontsize,
394 fonttype == FONT_FIXED ? "monospace" : "sans-serif");
395
396 if (align & ALIGN_VCENTRE)
397 y += js_canvas_find_font_midpoint(fontsize, fontstyle);
398
399 if (align & ALIGN_HCENTRE)
400 halign = 1;
401 else if (align & ALIGN_HRIGHT)
402 halign = 2;
403 else
404 halign = 0;
405
406 js_canvas_draw_text(x, y, halign, colour_strings[colour], fontstyle, text);
407}
408
409static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour)
410{
411 js_canvas_draw_rect(x, y, w, h, colour_strings[colour]);
412}
413
414static void js_draw_line(void *handle, int x1, int y1, int x2, int y2,
415 int colour)
416{
417 js_canvas_draw_line(x1, y1, x2, y2, 1, colour_strings[colour]);
418}
419
420static void js_draw_thick_line(void *handle, float thickness,
421 float x1, float y1, float x2, float y2,
422 int colour)
423{
424 js_canvas_draw_line(x1, y1, x2, y2, thickness, colour_strings[colour]);
425}
426
427static void js_draw_poly(void *handle, int *coords, int npoints,
428 int fillcolour, int outlinecolour)
429{
430 js_canvas_draw_poly(coords, npoints,
431 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
432 colour_strings[outlinecolour]);
433}
434
435static void js_draw_circle(void *handle, int cx, int cy, int radius,
436 int fillcolour, int outlinecolour)
437{
438 js_canvas_draw_circle(cx, cy, radius,
439 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
440 colour_strings[outlinecolour]);
441}
442
443struct blitter {
444 int id; /* allocated on the js side */
445 int w, h; /* easier to retain here */
446};
447
448static blitter *js_blitter_new(void *handle, int w, int h)
449{
450 blitter *bl = snew(blitter);
451 bl->w = w;
452 bl->h = h;
453 bl->id = js_canvas_new_blitter(w, h);
454 return bl;
455}
456
457static void js_blitter_free(void *handle, blitter *bl)
458{
459 js_canvas_free_blitter(bl->id);
460 sfree(bl);
461}
462
463static void trim_rect(int *x, int *y, int *w, int *h)
464{
465 int x0, x1, y0, y1;
466
467 /*
468 * Reduce the size of the copied rectangle to stop it going
469 * outside the bounds of the canvas.
470 */
471
472 /* Transform from x,y,w,h form into coordinates of all edges */
473 x0 = *x;
474 y0 = *y;
475 x1 = *x + *w;
476 y1 = *y + *h;
477
478 /* Clip each coordinate at both extremes of the canvas */
479 x0 = (x0 < 0 ? 0 : x0 > canvas_w ? canvas_w : x0);
480 x1 = (x1 < 0 ? 0 : x1 > canvas_w ? canvas_w : x1);
481 y0 = (y0 < 0 ? 0 : y0 > canvas_h ? canvas_h : y0);
482 y1 = (y1 < 0 ? 0 : y1 > canvas_h ? canvas_h : y1);
483
484 /* Transform back into x,y,w,h to return */
485 *x = x0;
486 *y = y0;
487 *w = x1 - x0;
488 *h = y1 - y0;
489}
490
491static void js_blitter_save(void *handle, blitter *bl, int x, int y)
492{
493 int w = bl->w, h = bl->h;
494 trim_rect(&x, &y, &w, &h);
495 if (w > 0 && h > 0)
496 js_canvas_copy_to_blitter(bl->id, x, y, w, h);
497}
498
499static void js_blitter_load(void *handle, blitter *bl, int x, int y)
500{
501 int w = bl->w, h = bl->h;
502 trim_rect(&x, &y, &w, &h);
503 if (w > 0 && h > 0)
504 js_canvas_copy_from_blitter(bl->id, x, y, w, h);
505}
506
507static void js_draw_update(void *handle, int x, int y, int w, int h)
508{
509 trim_rect(&x, &y, &w, &h);
510 if (w > 0 && h > 0)
511 js_canvas_draw_update(x, y, w, h);
512}
513
514static void js_end_draw(void *handle)
515{
516 js_canvas_end_draw();
517}
518
519static void js_status_bar(void *handle, const char *text)
520{
521 js_canvas_set_statusbar(text);
522}
523
524static char *js_text_fallback(void *handle, const char *const *strings,
525 int nstrings)
526{
527 return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */
528}
529
530const struct drawing_api js_drawing = {
531 js_draw_text,
532 js_draw_rect,
533 js_draw_line,
534 js_draw_poly,
535 js_draw_circle,
536 js_draw_update,
537 js_clip,
538 js_unclip,
539 js_start_draw,
540 js_end_draw,
541 js_status_bar,
542 js_blitter_new,
543 js_blitter_free,
544 js_blitter_save,
545 js_blitter_load,
546 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
547 NULL, NULL, /* line_width, line_dotted */
548 js_text_fallback,
549 js_draw_thick_line,
550};
551
552/* ----------------------------------------------------------------------
553 * Presets and game-configuration dialog support.
554 */
555static game_params **presets;
556static int npresets;
557bool have_presets_dropdown;
558
559void populate_js_preset_menu(int menuid, struct preset_menu *menu)
560{
561 int i;
562 for (i = 0; i < menu->n_entries; i++) {
563 struct preset_menu_entry *entry = &menu->entries[i];
564 if (entry->params) {
565 presets[entry->id] = entry->params;
566 js_add_preset(menuid, entry->title, entry->id);
567 } else {
568 int js_submenu = js_add_preset_submenu(menuid, entry->title);
569 populate_js_preset_menu(js_submenu, entry->submenu);
570 }
571 }
572}
573
574void select_appropriate_preset(void)
575{
576 if (have_presets_dropdown) {
577 int preset = midend_which_preset(me);
578 js_select_preset(preset < 0 ? -1 : preset);
579 }
580}
581
582static config_item *cfg = NULL;
583static int cfg_which;
584
585/*
586 * Set up a dialog box. This is pretty easy on the C side; most of the
587 * work is done in JS.
588 */
589static void cfg_start(int which)
590{
591 char *title;
592 int i;
593
594 cfg = midend_get_config(me, which, &title);
595 cfg_which = which;
596
597 js_dialog_init(title);
598 sfree(title);
599
600 for (i = 0; cfg[i].type != C_END; i++) {
601 switch (cfg[i].type) {
602 case C_STRING:
603 js_dialog_string(i, cfg[i].name, cfg[i].u.string.sval);
604 break;
605 case C_BOOLEAN:
606 js_dialog_boolean(i, cfg[i].name, cfg[i].u.boolean.bval);
607 break;
608 case C_CHOICES:
609 js_dialog_choices(i, cfg[i].name, cfg[i].u.choices.choicenames,
610 cfg[i].u.choices.selected);
611 break;
612 }
613 }
614
615 js_dialog_launch();
616}
617
618/*
619 * Callbacks from JS when the OK button is clicked, to return the
620 * final state of each control.
621 */
622void dlg_return_sval(int index, const char *val)
623{
624 config_item *i = cfg + index;
625 switch (i->type) {
626 case C_STRING:
627 sfree(i->u.string.sval);
628 i->u.string.sval = dupstr(val);
629 break;
630 default:
631 assert(0 && "Bad type for return_sval");
632 }
633}
634void dlg_return_ival(int index, int val)
635{
636 config_item *i = cfg + index;
637 switch (i->type) {
638 case C_BOOLEAN:
639 i->u.boolean.bval = val;
640 break;
641 case C_CHOICES:
642 i->u.choices.selected = val;
643 break;
644 default:
645 assert(0 && "Bad type for return_ival");
646 }
647}
648
649/*
650 * Called when the user clicks OK or Cancel. use_results will be true
651 * or false respectively, in those cases. We terminate the dialog box,
652 * unless the user selected an invalid combination of parameters.
653 */
654static void cfg_end(bool use_results)
655{
656 if (use_results) {
657 /*
658 * User hit OK.
659 */
660 const char *err = midend_set_config(me, cfg_which, cfg);
661
662 if (err) {
663 /*
664 * The settings were unacceptable, so leave the config box
665 * open for the user to adjust them and try again.
666 */
667 js_error_box(err);
668 } else {
669 /*
670 * New settings are fine; start a new game and close the
671 * dialog.
672 */
673 select_appropriate_preset();
674 midend_new_game(me);
675 resize();
676 midend_redraw(me);
677 free_cfg(cfg);
678 js_dialog_cleanup();
679 }
680 } else {
681 /*
682 * User hit Cancel. Close the dialog, but also we must still
683 * reselect the right element of the dropdown list.
684 *
685 * (Because: imagine you have a preset selected, and then you
686 * select Custom from the list, but change your mind and hit
687 * Esc. The Custom option will now still be selected in the
688 * list, whereas obviously it should show the preset you still
689 * _actually_ have selected. Worse still, it'll be the visible
690 * rather than invisible Custom option - see the comment in
691 * js_add_preset in emcclib.js - so you won't even be able to
692 * select Custom without a faffy workaround.)
693 */
694 select_appropriate_preset();
695
696 free_cfg(cfg);
697 js_dialog_cleanup();
698 }
699}
700
701/* ----------------------------------------------------------------------
702 * Called from JS when a command is given to the puzzle by clicking a
703 * button or control of some sort.
704 */
705void command(int n)
706{
707 switch (n) {
708 case 0: /* specific game ID */
709 cfg_start(CFG_DESC);
710 break;
711 case 1: /* random game seed */
712 cfg_start(CFG_SEED);
713 break;
714 case 2: /* game parameter dropdown changed */
715 {
716 int i = js_get_selected_preset();
717 if (i < 0) {
718 /*
719 * The user selected 'Custom', so launch the config
720 * box.
721 */
722 if (thegame.can_configure) /* (double-check just in case) */
723 cfg_start(CFG_SETTINGS);
724 } else {
725 /*
726 * The user selected a preset, so just switch straight
727 * to that.
728 */
729 assert(i < npresets);
730 midend_set_params(me, presets[i]);
731 midend_new_game(me);
732 resize();
733 midend_redraw(me);
734 update_undo_redo();
735 js_focus_canvas();
736 select_appropriate_preset();
737 }
738 }
739 break;
740 case 3: /* OK clicked in a config box */
741 cfg_end(true);
742 update_undo_redo();
743 break;
744 case 4: /* Cancel clicked in a config box */
745 cfg_end(false);
746 update_undo_redo();
747 break;
748 case 5: /* New Game */
749 midend_process_key(me, 0, 0, UI_NEWGAME);
750 update_undo_redo();
751 js_focus_canvas();
752 break;
753 case 6: /* Restart */
754 midend_restart_game(me);
755 update_undo_redo();
756 js_focus_canvas();
757 break;
758 case 7: /* Undo */
759 midend_process_key(me, 0, 0, UI_UNDO);
760 update_undo_redo();
761 js_focus_canvas();
762 break;
763 case 8: /* Redo */
764 midend_process_key(me, 0, 0, UI_REDO);
765 update_undo_redo();
766 js_focus_canvas();
767 break;
768 case 9: /* Solve */
769 if (thegame.can_solve) {
770 const char *msg = midend_solve(me);
771 if (msg)
772 js_error_box(msg);
773 }
774 update_undo_redo();
775 js_focus_canvas();
776 break;
777 }
778}
779
780/* ----------------------------------------------------------------------
781 * Called from JS to prepare a save-game file, and free one after it's
782 * been used.
783 */
784
785struct savefile_write_ctx {
786 char *buffer;
787 size_t pos;
788};
789
790static void savefile_write(void *vctx, const void *buf, int len)
791{
792 struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)vctx;
793 if (ctx->buffer)
794 memcpy(ctx->buffer + ctx->pos, buf, len);
795 ctx->pos += len;
796}
797
798char *get_save_file(void)
799{
800 struct savefile_write_ctx ctx;
801 size_t size;
802
803 /* First pass, to count up the size */
804 ctx.buffer = NULL;
805 ctx.pos = 0;
806 midend_serialise(me, savefile_write, &ctx);
807 size = ctx.pos;
808
809 /* Second pass, to actually write out the data. We have to put a
810 * terminating \0 on the end (which we expect never to show up in
811 * the actual serialisation format - it's text, not binary) so
812 * that the Javascript side can easily find out the length. */
813 ctx.buffer = snewn(size+1, char);
814 ctx.pos = 0;
815 midend_serialise(me, savefile_write, &ctx);
816 assert(ctx.pos == size);
817 ctx.buffer[ctx.pos] = '\0';
818
819 return ctx.buffer;
820}
821
822void free_save_file(char *buffer)
823{
824 sfree(buffer);
825}
826
827struct savefile_read_ctx {
828 const char *buffer;
829 int len_remaining;
830};
831
832static bool savefile_read(void *vctx, void *buf, int len)
833{
834 struct savefile_read_ctx *ctx = (struct savefile_read_ctx *)vctx;
835 if (ctx->len_remaining < len)
836 return false;
837 memcpy(buf, ctx->buffer, len);
838 ctx->len_remaining -= len;
839 ctx->buffer += len;
840 return true;
841}
842
843void load_game(const char *buffer, int len)
844{
845 struct savefile_read_ctx ctx;
846 const char *err;
847
848 ctx.buffer = buffer;
849 ctx.len_remaining = len;
850 err = midend_deserialise(me, savefile_read, &ctx);
851
852 if (err) {
853 js_error_box(err);
854 } else {
855 select_appropriate_preset();
856 resize();
857 midend_redraw(me);
858 }
859}
860
861/* ----------------------------------------------------------------------
862 * Setup function called at page load time. It's called main() because
863 * that's the most convenient thing in Emscripten, but it's not main()
864 * in the usual sense of bounding the program's entire execution.
865 * Instead, this function returns once the initial puzzle is set up
866 * and working, and everything thereafter happens by means of JS event
867 * handlers sending us callbacks.
868 */
869int main(int argc, char **argv)
870{
871 const char *param_err;
872 float *colours;
873 int i;
874
875 /*
876 * Instantiate a midend.
877 */
878 me = midend_new(NULL, &thegame, &js_drawing, NULL);
879
880 /*
881 * Chuck in the HTML fragment ID if we have one (trimming the
882 * leading # off the front first). If that's invalid, we retain
883 * the error message and will display it at the end, after setting
884 * up a random puzzle as usual.
885 */
886 if (argc > 1 && argv[1][0] == '#' && argv[1][1] != '\0')
887 param_err = midend_game_id(me, argv[1] + 1);
888 else
889 param_err = NULL;
890
891 /*
892 * Create either a random game or the specified one, and set the
893 * canvas size appropriately.
894 */
895 midend_new_game(me);
896 resize();
897
898 /*
899 * Create a status bar, if needed.
900 */
901 if (midend_wants_statusbar(me))
902 js_canvas_make_statusbar();
903
904 /*
905 * Set up the game-type dropdown with presets and/or the Custom
906 * option.
907 */
908 {
909 struct preset_menu *menu = midend_get_presets(me, &npresets);
910 presets = snewn(npresets, game_params *);
911 for (i = 0; i < npresets; i++)
912 presets[i] = NULL;
913
914 populate_js_preset_menu(0, menu);
915
916 if (thegame.can_configure)
917 js_add_preset(0, "Custom", -1);
918
919 have_presets_dropdown = true;
920
921 /*
922 * Now ensure the appropriate element of the presets menu
923 * starts off selected, in case it isn't the first one in the
924 * list (e.g. Slant).
925 */
926 select_appropriate_preset();
927 }
928
929 /*
930 * Remove the Solve button if the game doesn't support it.
931 */
932 if (!thegame.can_solve)
933 js_remove_solve_button();
934
935 /*
936 * Retrieve the game's colours, and convert them into #abcdef type
937 * hex ID strings.
938 */
939 colours = midend_colours(me, &ncolours);
940 colour_strings = snewn(ncolours, char *);
941 for (i = 0; i < ncolours; i++) {
942 char col[40];
943 sprintf(col, "#%02x%02x%02x",
944 (unsigned)(0.5 + 255 * colours[i*3+0]),
945 (unsigned)(0.5 + 255 * colours[i*3+1]),
946 (unsigned)(0.5 + 255 * colours[i*3+2]));
947 colour_strings[i] = dupstr(col);
948 }
949
950 /*
951 * Request notification when the game ids change (e.g. if the user
952 * presses 'n', and also when Mines supersedes its game
953 * description), so that we can proactively update the permalink.
954 */
955 midend_request_id_changes(me, ids_changed, NULL);
956
957 /*
958 * Draw the puzzle's initial state, and set up the permalinks and
959 * undo/redo greying out.
960 */
961 midend_redraw(me);
962 update_permalinks();
963 update_undo_redo();
964
965 /*
966 * If we were given an erroneous game ID in argv[1], now's the
967 * time to put up the error box about it, after we've fully set up
968 * a random puzzle. Then when the user clicks 'ok', we have a
969 * puzzle for them.
970 */
971 if (param_err)
972 js_error_box(param_err);
973
974 /*
975 * Done. Return to JS, and await callbacks!
976 */
977 return 0;
978}
diff --git a/apps/plugins/puzzles/src/fifteen.R b/apps/plugins/puzzles/src/fifteen.R
deleted file mode 100644
index b2292acc69..0000000000
--- a/apps/plugins/puzzles/src/fifteen.R
+++ /dev/null
@@ -1,22 +0,0 @@
1# -*- makefile -*-
2
3fifteen : [X] GTK COMMON fifteen fifteen-icon|no-icon
4
5fifteen : [G] WINDOWS COMMON fifteen fifteen.res|noicon.res
6
7fifteensolver : [U] fifteen[STANDALONE_SOLVER] STANDALONE
8fifteensolver : [C] fifteen[STANDALONE_SOLVER] STANDALONE
9
10ALL += fifteen[COMBINED]
11
12!begin am gtk
13GAMES += fifteen
14!end
15
16!begin >list.c
17 A(fifteen) \
18!end
19
20!begin >gamedesc.txt
21fifteen:fifteen.exe:Fifteen:Sliding block puzzle:Slide the tiles around to arrange them into order.
22!end
diff --git a/apps/plugins/puzzles/src/fifteen.c b/apps/plugins/puzzles/src/fifteen.c
index 4b877dc098..a2a9554523 100644
--- a/apps/plugins/puzzles/src/fifteen.c
+++ b/apps/plugins/puzzles/src/fifteen.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13 18
@@ -138,6 +143,8 @@ static const char *validate_params(const game_params *params, bool full)
138{ 143{
139 if (params->w < 2 || params->h < 2) 144 if (params->w < 2 || params->h < 2)
140 return "Width and height must both be at least two"; 145 return "Width and height must both be at least two";
146 if (params->w > INT_MAX / params->h)
147 return "Width times height must not be unreasonably large";
141 148
142 return NULL; 149 return NULL;
143} 150}
@@ -156,6 +163,14 @@ static int perm_parity(int *perm, int n)
156 return ret; 163 return ret;
157} 164}
158 165
166static int is_completed(int *tiles, int n) {
167 int p;
168 for (p = 0; p < n; p++)
169 if (tiles[p] != (p < n-1 ? p+1 : 0))
170 return 0;
171 return 1;
172}
173
159static char *new_game_desc(const game_params *params, random_state *rs, 174static char *new_game_desc(const game_params *params, random_state *rs,
160 char **aux, bool interactive) 175 char **aux, bool interactive)
161{ 176{
@@ -171,81 +186,83 @@ static char *new_game_desc(const game_params *params, random_state *rs,
171 tiles = snewn(n, int); 186 tiles = snewn(n, int);
172 used = snewn(n, bool); 187 used = snewn(n, bool);
173 188
174 for (i = 0; i < n; i++) { 189 do {
175 tiles[i] = -1; 190 for (i = 0; i < n; i++) {
176 used[i] = false; 191 tiles[i] = -1;
177 } 192 used[i] = false;
193 }
178 194
179 gap = random_upto(rs, n); 195 gap = random_upto(rs, n);
180 tiles[gap] = 0; 196 tiles[gap] = 0;
181 used[0] = true; 197 used[0] = true;
182 198
183 /* 199 /*
184 * Place everything else except the last two tiles. 200 * Place everything else except the last two tiles.
185 */ 201 */
186 for (x = 0, i = n-1; i > 2; i--) { 202 for (x = 0, i = n - 1; i > 2; i--) {
187 int k = random_upto(rs, i); 203 int k = random_upto(rs, i);
188 int j; 204 int j;
189 205
190 for (j = 0; j < n; j++) 206 for (j = 0; j < n; j++)
191 if (!used[j] && (k-- == 0)) 207 if (!used[j] && (k-- == 0))
192 break; 208 break;
193 209
194 assert(j < n && !used[j]); 210 assert(j < n && !used[j]);
195 used[j] = true; 211 used[j] = true;
212
213 while (tiles[x] >= 0)
214 x++;
215 assert(x < n);
216 tiles[x] = j;
217 }
196 218
219 /*
220 * Find the last two locations, and the last two pieces.
221 */
197 while (tiles[x] >= 0) 222 while (tiles[x] >= 0)
198 x++; 223 x++;
199 assert(x < n); 224 assert(x < n);
200 tiles[x] = j; 225 x1 = x;
201 }
202
203 /*
204 * Find the last two locations, and the last two pieces.
205 */
206 while (tiles[x] >= 0)
207 x++;
208 assert(x < n);
209 x1 = x;
210 x++;
211 while (tiles[x] >= 0)
212 x++; 226 x++;
213 assert(x < n); 227 while (tiles[x] >= 0)
214 x2 = x; 228 x++;
215 229 assert(x < n);
216 for (i = 0; i < n; i++) 230 x2 = x;
217 if (!used[i])
218 break;
219 p1 = i;
220 for (i = p1+1; i < n; i++)
221 if (!used[i])
222 break;
223 p2 = i;
224 231
225 /* 232 for (i = 0; i < n; i++)
226 * Determine the required parity of the overall permutation. 233 if (!used[i])
227 * This is the XOR of: 234 break;
228 * 235 p1 = i;
229 * - The chessboard parity ((x^y)&1) of the gap square. The 236 for (i = p1 + 1; i < n; i++)
230 * bottom right counts as even. 237 if (!used[i])
231 * 238 break;
232 * - The parity of n. (The target permutation is 1,...,n-1,0 239 p2 = i;
233 * rather than 0,...,n-1; this is a cyclic permutation of
234 * the starting point and hence is odd iff n is even.)
235 */
236 parity = PARITY_P(params, gap);
237 240
238 /* 241 /*
239 * Try the last two tiles one way round. If that fails, swap 242 * Determine the required parity of the overall permutation.
240 * them. 243 * This is the XOR of:
241 */ 244 *
242 tiles[x1] = p1; 245 * - The chessboard parity ((x^y)&1) of the gap square. The
243 tiles[x2] = p2; 246 * bottom right counts as even.
244 if (perm_parity(tiles, n) != parity) { 247 *
245 tiles[x1] = p2; 248 * - The parity of n. (The target permutation is 1,...,n-1,0
246 tiles[x2] = p1; 249 * rather than 0,...,n-1; this is a cyclic permutation of
247 assert(perm_parity(tiles, n) == parity); 250 * the starting point and hence is odd iff n is even.)
248 } 251 */
252 parity = PARITY_P(params, gap);
253
254 /*
255 * Try the last two tiles one way round. If that fails, swap
256 * them.
257 */
258 tiles[x1] = p1;
259 tiles[x2] = p2;
260 if (perm_parity(tiles, n) != parity) {
261 tiles[x1] = p2;
262 tiles[x2] = p1;
263 assert(perm_parity(tiles, n) == parity);
264 }
265 } while (is_completed(tiles, n));
249 266
250 /* 267 /*
251 * Now construct the game description, by describing the tile 268 * Now construct the game description, by describing the tile
@@ -432,22 +449,68 @@ static char *game_text_format(const game_state *state)
432 return ret; 449 return ret;
433} 450}
434 451
452struct game_ui {
453 /*
454 * User-preference option: invert the direction of arrow-key
455 * control, so that the arrow on the key you press indicates in
456 * which direction you want the _space_ to move, rather than in
457 * which direction you want a tile to move to fill the space.
458 */
459 bool invert_cursor;
460};
461
462static void legacy_prefs_override(struct game_ui *ui_out)
463{
464 static bool initialised = false;
465 static int invert_cursor = -1;
466
467 if (!initialised) {
468 initialised = true;
469 invert_cursor = getenv_bool("FIFTEEN_INVERT_CURSOR", -1);
470 }
471
472 if (invert_cursor != -1)
473 ui_out->invert_cursor = invert_cursor;
474}
475
435static game_ui *new_ui(const game_state *state) 476static game_ui *new_ui(const game_state *state)
436{ 477{
437 return NULL; 478 struct game_ui *ui = snew(struct game_ui);
479
480 ui->invert_cursor = false;
481
482 legacy_prefs_override(ui);
483
484 return ui;
438} 485}
439 486
440static void free_ui(game_ui *ui) 487static config_item *get_prefs(game_ui *ui)
441{ 488{
489 config_item *ret;
490
491 ret = snewn(2, config_item);
492
493 ret[0].name = "Sense of arrow keys";
494 ret[0].kw = "arrow-semantics";
495 ret[0].type = C_CHOICES;
496 ret[0].u.choices.choicenames = ":Move the tile:Move the gap";
497 ret[0].u.choices.choicekws = ":tile:gap";
498 ret[0].u.choices.selected = ui->invert_cursor;
499
500 ret[1].name = NULL;
501 ret[1].type = C_END;
502
503 return ret;
442} 504}
443 505
444static char *encode_ui(const game_ui *ui) 506static void set_prefs(game_ui *ui, const config_item *cfg)
445{ 507{
446 return NULL; 508 ui->invert_cursor = cfg[0].u.choices.selected;
447} 509}
448 510
449static void decode_ui(game_ui *ui, const char *encoding) 511static void free_ui(game_ui *ui)
450{ 512{
513 sfree(ui);
451} 514}
452 515
453static void game_changed_state(game_ui *ui, const game_state *oldstate, 516static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -692,28 +755,23 @@ static char *interpret_move(const game_state *state, game_ui *ui,
692 int cy = Y(state, state->gap_pos), ny = cy; 755 int cy = Y(state, state->gap_pos), ny = cy;
693 char buf[80]; 756 char buf[80];
694 757
695 button &= ~MOD_MASK; 758 button = STRIP_BUTTON_MODIFIERS(button);
696 759
697 if (button == LEFT_BUTTON) { 760 if (button == LEFT_BUTTON) {
698 nx = FROMCOORD(x); 761 nx = FROMCOORD(x);
699 ny = FROMCOORD(y); 762 ny = FROMCOORD(y);
700 if (nx < 0 || nx >= state->w || ny < 0 || ny >= state->h) 763 if (nx < 0 || nx >= state->w || ny < 0 || ny >= state->h)
701 return NULL; /* out of bounds */ 764 return MOVE_UNUSED; /* out of bounds */
702 } else if (IS_CURSOR_MOVE(button)) { 765 } else if (IS_CURSOR_MOVE(button)) {
703 static int invert_cursor = -1;
704 if (invert_cursor == -1) {
705 char *env = getenv("FIFTEEN_INVERT_CURSOR");
706 invert_cursor = (env && (env[0] == 'y' || env[0] == 'Y'));
707 }
708 button = flip_cursor(button); /* the default */ 766 button = flip_cursor(button); /* the default */
709 if (invert_cursor) 767 if (ui->invert_cursor)
710 button = flip_cursor(button); /* undoes the first flip */ 768 button = flip_cursor(button); /* undoes the first flip */
711 move_cursor(button, &nx, &ny, state->w, state->h, false); 769 move_cursor(button, &nx, &ny, state->w, state->h, false, NULL);
712 } else if ((button == 'h' || button == 'H') && !state->completed) { 770 } else if ((button == 'h' || button == 'H') && !state->completed) {
713 if (!compute_hint(state, &nx, &ny)) 771 if (!compute_hint(state, &nx, &ny))
714 return NULL; /* shouldn't happen, since ^^we^^checked^^ */ 772 return MOVE_NO_EFFECT;/* shouldn't happen, since ^^we^^checked^^ */
715 } else 773 } else
716 return NULL; /* no move */ 774 return MOVE_UNUSED; /* no move */
717 775
718 /* 776 /*
719 * Any click location should be equal to the gap location 777 * Any click location should be equal to the gap location
@@ -724,7 +782,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
724 return dupstr(buf); 782 return dupstr(buf);
725 } 783 }
726 784
727 return NULL; 785 return MOVE_NO_EFFECT;
728} 786}
729 787
730static game_state *execute_move(const game_state *from, const char *move) 788static game_state *execute_move(const game_state *from, const char *move)
@@ -786,11 +844,8 @@ static game_state *execute_move(const game_state *from, const char *move)
786 /* 844 /*
787 * See if the game has been completed. 845 * See if the game has been completed.
788 */ 846 */
789 if (!ret->completed) { 847 if (!ret->completed && is_completed(ret->tiles, ret->n)) {
790 ret->completed = ret->movecount; 848 ret->completed = ret->movecount;
791 for (p = 0; p < ret->n; p++)
792 if (ret->tiles[p] != (p < ret->n-1 ? p+1 : 0))
793 ret->completed = 0;
794 } 849 }
795 850
796 return ret; 851 return ret;
@@ -801,7 +856,7 @@ static game_state *execute_move(const game_state *from, const char *move)
801 */ 856 */
802 857
803static void game_compute_size(const game_params *params, int tilesize, 858static void game_compute_size(const game_params *params, int tilesize,
804 int *x, int *y) 859 const game_ui *ui, int *x, int *y)
805{ 860{
806 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 861 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
807 struct { int tilesize; } ads, *ds = &ads; 862 struct { int tilesize; } ads, *ds = &ads;
@@ -904,13 +959,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
904 if (!ds->started) { 959 if (!ds->started) {
905 int coords[10]; 960 int coords[10];
906 961
907 draw_rect(dr, 0, 0,
908 TILE_SIZE * state->w + 2 * BORDER,
909 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
910 draw_update(dr, 0, 0,
911 TILE_SIZE * state->w + 2 * BORDER,
912 TILE_SIZE * state->h + 2 * BORDER);
913
914 /* 962 /*
915 * Recessed area containing the whole puzzle. 963 * Recessed area containing the whole puzzle.
916 */ 964 */
@@ -1077,19 +1125,6 @@ static int game_status(const game_state *state)
1077 return state->completed ? +1 : 0; 1125 return state->completed ? +1 : 0;
1078} 1126}
1079 1127
1080static bool game_timing_state(const game_state *state, game_ui *ui)
1081{
1082 return true;
1083}
1084
1085static void game_print_size(const game_params *params, float *x, float *y)
1086{
1087}
1088
1089static void game_print(drawing *dr, const game_state *state, int tilesize)
1090{
1091}
1092
1093#ifdef COMBINED 1128#ifdef COMBINED
1094#define thegame fifteen 1129#define thegame fifteen
1095#endif 1130#endif
@@ -1111,12 +1146,14 @@ const struct game thegame = {
1111 free_game, 1146 free_game,
1112 true, solve_game, 1147 true, solve_game,
1113 true, game_can_format_as_text_now, game_text_format, 1148 true, game_can_format_as_text_now, game_text_format,
1149 get_prefs, set_prefs,
1114 new_ui, 1150 new_ui,
1115 free_ui, 1151 free_ui,
1116 encode_ui, 1152 NULL, /* encode_ui */
1117 decode_ui, 1153 NULL, /* decode_ui */
1118 NULL, /* game_request_keys */ 1154 NULL, /* game_request_keys */
1119 game_changed_state, 1155 game_changed_state,
1156 NULL, /* current_key_label */
1120 interpret_move, 1157 interpret_move,
1121 execute_move, 1158 execute_move,
1122 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1159 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1128,9 +1165,9 @@ const struct game thegame = {
1128 game_flash_length, 1165 game_flash_length,
1129 game_get_cursor_location, 1166 game_get_cursor_location,
1130 game_status, 1167 game_status,
1131 false, false, game_print_size, game_print, 1168 false, false, NULL, NULL, /* print_size, print */
1132 true, /* wants_statusbar */ 1169 true, /* wants_statusbar */
1133 false, game_timing_state, 1170 false, NULL, /* timing_state */
1134 0, /* flags */ 1171 0, /* flags */
1135}; 1172};
1136 1173
diff --git a/apps/plugins/puzzles/src/filling.R b/apps/plugins/puzzles/src/filling.R
deleted file mode 100644
index cffbafaa0a..0000000000
--- a/apps/plugins/puzzles/src/filling.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3FILLING_EXTRA = dsf
4
5fillingsolver : [U] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
6fillingsolver : [C] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
7
8filling : [X] GTK COMMON filling FILLING_EXTRA filling-icon|no-icon
9
10filling : [G] WINDOWS COMMON filling FILLING_EXTRA filling.res|noicon.res
11
12ALL += filling[COMBINED] FILLING_EXTRA
13
14!begin am gtk
15GAMES += filling
16!end
17
18!begin >list.c
19 A(filling) \
20!end
21
22!begin >gamedesc.txt
23filling:filling.exe:Filling:Polyomino puzzle:Mark every square with the area of its containing region.
24!end
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c
index 6d9beb5c28..a0c144714b 100644
--- a/apps/plugins/puzzles/src/filling.c
+++ b/apps/plugins/puzzles/src/filling.c
@@ -1,4 +1,4 @@
1/* -*- tab-width: 8; indent-tabs-mode: t -*- 1/*
2 * filling.c: An implementation of the Nikoli game fillomino. 2 * filling.c: An implementation of the Nikoli game fillomino.
3 * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license. 3 * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license.
4 */ 4 */
@@ -58,7 +58,11 @@
58 58
59#include <assert.h> 59#include <assert.h>
60#include <ctype.h> 60#include <ctype.h>
61#include <math.h> 61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
62#include <stdarg.h> 66#include <stdarg.h>
63#include <stdio.h> 67#include <stdio.h>
64#include <stdlib.h> 68#include <stdlib.h>
@@ -68,16 +72,11 @@
68 72
69static bool verbose; 73static bool verbose;
70 74
71static void printv(const char *fmt, ...) { 75#ifdef STANDALONE_SOLVER
72#if !defined(PALM) && !defined(ROCKBOX) 76#define printv if (!verbose); else printf
73 if (verbose) { 77#else
74 va_list va; 78#define printv(...)
75 va_start(va, fmt);
76 vprintf(fmt, va);
77 va_end(va);
78 }
79#endif 79#endif
80}
81 80
82/***************************************************************************** 81/*****************************************************************************
83 * GAME CONFIGURATION AND PARAMETERS * 82 * GAME CONFIGURATION AND PARAMETERS *
@@ -188,6 +187,8 @@ static const char *validate_params(const game_params *params, bool full)
188{ 187{
189 if (params->w < 1) return "Width must be at least one"; 188 if (params->w < 1) return "Width must be at least one";
190 if (params->h < 1) return "Height must be at least one"; 189 if (params->h < 1) return "Height must be at least one";
190 if (params->w > INT_MAX / params->h)
191 return "Width times height must not be unreasonably large";
191 192
192 return NULL; 193 return NULL;
193} 194}
@@ -289,14 +290,15 @@ static const int dy[4] = {0, 0, -1, 1};
289 290
290struct solver_state 291struct solver_state
291{ 292{
292 int *dsf; 293 DSF *dsf;
293 int *board; 294 int *board;
294 int *connected; 295 int *connected;
295 int nempty; 296 int nempty;
296 297
297 /* Used internally by learn_bitmap_deductions; kept here to avoid 298 /* Used internally by learn_bitmap_deductions; kept here to avoid
298 * mallocing/freeing them every time that function is called. */ 299 * mallocing/freeing them every time that function is called. */
299 int *bm, *bmdsf, *bmminsize; 300 int *bm, *bmminsize;
301 DSF *bmdsf;
300}; 302};
301 303
302static void print_board(int *board, int w, int h) { 304static void print_board(int *board, int w, int h) {
@@ -310,7 +312,7 @@ static void print_board(int *board, int w, int h) {
310static game_state *new_game(midend *, const game_params *, const char *); 312static game_state *new_game(midend *, const game_params *, const char *);
311static void free_game(game_state *); 313static void free_game(game_state *);
312 314
313#define SENTINEL sz 315#define SENTINEL (sz+1)
314 316
315static bool mark_region(int *board, int w, int h, int i, int n, int m) { 317static bool mark_region(int *board, int w, int h, int i, int n, int m) {
316 int j; 318 int j;
@@ -390,7 +392,8 @@ static void make_board(int *board, int w, int h, random_state *rs) {
390 /* Note that if 1 in {w, h} then it's impossible to have a region 392 /* Note that if 1 in {w, h} then it's impossible to have a region
391 * of size > w*h, so the special case only affects w=h=2. */ 393 * of size > w*h, so the special case only affects w=h=2. */
392 394
393 int i, *dsf; 395 int i;
396 DSF *dsf;
394 bool change; 397 bool change;
395 398
396 assert(w >= 1); 399 assert(w >= 1);
@@ -401,9 +404,9 @@ static void make_board(int *board, int w, int h, random_state *rs) {
401 * contains a shuffled list of numbers {0, ..., sz-1}. */ 404 * contains a shuffled list of numbers {0, ..., sz-1}. */
402 for (i = 0; i < sz; ++i) board[i] = i; 405 for (i = 0; i < sz; ++i) board[i] = i;
403 406
404 dsf = snewn(sz, int); 407 dsf = dsf_new(sz);
405retry: 408retry:
406 dsf_init(dsf, sz); 409 dsf_reinit(dsf);
407 shuffle(board, sz, sizeof (int), rs); 410 shuffle(board, sz, sizeof (int), rs);
408 411
409 do { 412 do {
@@ -414,10 +417,15 @@ retry:
414 int merge = SENTINEL, min = maxsize - size + 1; 417 int merge = SENTINEL, min = maxsize - size + 1;
415 bool error = false; 418 bool error = false;
416 int neighbour, neighbour_size, j; 419 int neighbour, neighbour_size, j;
420 int directions[4];
421
422 for (j = 0; j < 4; ++j)
423 directions[j] = j;
424 shuffle(directions, 4, sizeof(int), rs);
417 425
418 for (j = 0; j < 4; ++j) { 426 for (j = 0; j < 4; ++j) {
419 const int x = (board[i] % w) + dx[j]; 427 const int x = (board[i] % w) + dx[directions[j]];
420 const int y = (board[i] / w) + dy[j]; 428 const int y = (board[i] / w) + dy[directions[j]];
421 if (x < 0 || x >= w || y < 0 || y >= h) continue; 429 if (x < 0 || x >= w || y < 0 || y >= h) continue;
422 430
423 neighbour = dsf_canonify(dsf, w*y + x); 431 neighbour = dsf_canonify(dsf, w*y + x);
@@ -429,7 +437,7 @@ retry:
429 /* find the smallest neighbour to merge with, which 437 /* find the smallest neighbour to merge with, which
430 * wouldn't make the region too large. (This is 438 * wouldn't make the region too large. (This is
431 * guaranteed by the initial value of `min'.) */ 439 * guaranteed by the initial value of `min'.) */
432 if (neighbour_size < min) { 440 if (neighbour_size < min && random_upto(rs, 10)) {
433 min = neighbour_size; 441 min = neighbour_size;
434 merge = neighbour; 442 merge = neighbour;
435 } 443 }
@@ -453,10 +461,10 @@ retry:
453 for (i = 0; i < sz; ++i) board[i] = dsf_size(dsf, i); 461 for (i = 0; i < sz; ++i) board[i] = dsf_size(dsf, i);
454 merge_ones(board, w, h); 462 merge_ones(board, w, h);
455 463
456 sfree(dsf); 464 dsf_free(dsf);
457} 465}
458 466
459static void merge(int *dsf, int *connected, int a, int b) { 467static void merge(DSF *dsf, int *connected, int a, int b) {
460 int c; 468 int c;
461 assert(dsf); 469 assert(dsf);
462 assert(connected); 470 assert(connected);
@@ -532,7 +540,7 @@ static bool check_capacity(int *board, int w, int h, int i) {
532 return n == 0; 540 return n == 0;
533} 541}
534 542
535static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) { 543static int expandsize(const int *board, DSF *dsf, int w, int h, int i, int n) {
536 int j; 544 int j;
537 int nhits = 0; 545 int nhits = 0;
538 int hits[4]; 546 int hits[4];
@@ -548,7 +556,7 @@ static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) {
548 root = dsf_canonify(dsf, idx); 556 root = dsf_canonify(dsf, idx);
549 for (m = 0; m < nhits && root != hits[m]; ++m); 557 for (m = 0; m < nhits && root != hits[m]; ++m);
550 if (m < nhits) continue; 558 if (m < nhits) continue;
551 printv("\t (%d, %d) contrib %d to size\n", x, y, dsf[root] >> 2); 559 printv("\t (%d, %d) contrib %d to size\n", x, y, dsf_size(dsf, root));
552 size += dsf_size(dsf, root); 560 size += dsf_size(dsf, root);
553 assert(dsf_size(dsf, root) >= 1); 561 assert(dsf_size(dsf, root) >= 1);
554 hits[nhits++] = root; 562 hits[nhits++] = root;
@@ -833,7 +841,7 @@ static bool learn_bitmap_deductions(struct solver_state *s, int w, int h)
833{ 841{
834 const int sz = w * h; 842 const int sz = w * h;
835 int *bm = s->bm; 843 int *bm = s->bm;
836 int *dsf = s->bmdsf; 844 DSF *dsf = s->bmdsf;
837 int *minsize = s->bmminsize; 845 int *minsize = s->bmminsize;
838 int x, y, i, j, n; 846 int x, y, i, j, n;
839 bool learn = false; 847 bool learn = false;
@@ -933,7 +941,7 @@ static bool learn_bitmap_deductions(struct solver_state *s, int w, int h)
933 * have a completely new n-region in it. 941 * have a completely new n-region in it.
934 */ 942 */
935 for (n = 1; n <= 9; n++) { 943 for (n = 1; n <= 9; n++) {
936 dsf_init(dsf, sz); 944 dsf_reinit(dsf);
937 945
938 /* Build the dsf */ 946 /* Build the dsf */
939 for (y = 0; y < h; y++) 947 for (y = 0; y < h; y++)
@@ -1076,12 +1084,12 @@ static bool solver(const int *orig, int w, int h, char **solution) {
1076 1084
1077 struct solver_state ss; 1085 struct solver_state ss;
1078 ss.board = memdup(orig, sz, sizeof (int)); 1086 ss.board = memdup(orig, sz, sizeof (int));
1079 ss.dsf = snew_dsf(sz); /* eqv classes: connected components */ 1087 ss.dsf = dsf_new(sz); /* eqv classes: connected components */
1080 ss.connected = snewn(sz, int); /* connected[n] := n.next; */ 1088 ss.connected = snewn(sz, int); /* connected[n] := n.next; */
1081 /* cyclic disjoint singly linked lists, same partitioning as dsf. 1089 /* cyclic disjoint singly linked lists, same partitioning as dsf.
1082 * The lists lets you iterate over a partition given any member */ 1090 * The lists lets you iterate over a partition given any member */
1083 ss.bm = snewn(sz, int); 1091 ss.bm = snewn(sz, int);
1084 ss.bmdsf = snew_dsf(sz); 1092 ss.bmdsf = dsf_new(sz);
1085 ss.bmminsize = snewn(sz, int); 1093 ss.bmminsize = snewn(sz, int);
1086 1094
1087 printv("trying to solve this:\n"); 1095 printv("trying to solve this:\n");
@@ -1105,28 +1113,26 @@ static bool solver(const int *orig, int w, int h, char **solution) {
1105 **solution = 's'; 1113 **solution = 's';
1106 for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0'; 1114 for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0';
1107 (*solution)[sz + 1] = '\0'; 1115 (*solution)[sz + 1] = '\0';
1108 /* We don't need the \0 for execute_move (the only user)
1109 * I'm just being printf-friendly in case I wanna print */
1110 } 1116 }
1111 1117
1112 sfree(ss.dsf); 1118 dsf_free(ss.dsf);
1113 sfree(ss.board); 1119 sfree(ss.board);
1114 sfree(ss.connected); 1120 sfree(ss.connected);
1115 sfree(ss.bm); 1121 sfree(ss.bm);
1116 sfree(ss.bmdsf); 1122 dsf_free(ss.bmdsf);
1117 sfree(ss.bmminsize); 1123 sfree(ss.bmminsize);
1118 1124
1119 return !ss.nempty; 1125 return !ss.nempty;
1120} 1126}
1121 1127
1122static int *make_dsf(int *dsf, int *board, const int w, const int h) { 1128static DSF *make_dsf(DSF *dsf, int *board, const int w, const int h) {
1123 const int sz = w * h; 1129 const int sz = w * h;
1124 int i; 1130 int i;
1125 1131
1126 if (!dsf) 1132 if (!dsf)
1127 dsf = snew_dsf(w * h); 1133 dsf = dsf_new_min(w * h);
1128 else 1134 else
1129 dsf_init(dsf, w * h); 1135 dsf_reinit(dsf);
1130 1136
1131 for (i = 0; i < sz; ++i) { 1137 for (i = 0; i < sz; ++i) {
1132 int j; 1138 int j;
@@ -1145,7 +1151,8 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs)
1145{ 1151{
1146 const int sz = w * h; 1152 const int sz = w * h;
1147 int *shuf = snewn(sz, int), i; 1153 int *shuf = snewn(sz, int), i;
1148 int *dsf, *next; 1154 DSF *dsf;
1155 int *next;
1149 1156
1150 for (i = 0; i < sz; ++i) shuf[i] = i; 1157 for (i = 0; i < sz; ++i) shuf[i] = i;
1151 shuffle(shuf, sz, sizeof (int), rs); 1158 shuffle(shuf, sz, sizeof (int), rs);
@@ -1162,14 +1169,14 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs)
1162 dsf = make_dsf(NULL, board, w, h); 1169 dsf = make_dsf(NULL, board, w, h);
1163 next = snewn(sz, int); 1170 next = snewn(sz, int);
1164 for (i = 0; i < sz; ++i) { 1171 for (i = 0; i < sz; ++i) {
1165 int j = dsf_canonify(dsf, i); 1172 int j = dsf_minimal(dsf, i);
1166 if (i == j) { 1173 if (i == j) {
1167 /* First cell of a region; set next[i] = -1 to indicate 1174 /* First cell of a region; set next[i] = -1 to indicate
1168 * end-of-list. */ 1175 * end-of-list. */
1169 next[i] = -1; 1176 next[i] = -1;
1170 } else { 1177 } else {
1171 /* Add this cell to a region which already has a 1178 /* Add this cell to a region which already has a
1172 * linked-list head, by pointing the canonical element j 1179 * linked-list head, by pointing the minimal element j
1173 * at this one, and pointing this one in turn at wherever 1180 * at this one, and pointing this one in turn at wherever
1174 * j previously pointed. (This should end up with the 1181 * j previously pointed. (This should end up with the
1175 * elements linked in the order 1,n,n-1,n-2,...,2, which 1182 * elements linked in the order 1,n,n-1,n-2,...,2, which
@@ -1197,7 +1204,7 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs)
1197 * if we can. 1204 * if we can.
1198 */ 1205 */
1199 for (i = 0; i < sz; ++i) { 1206 for (i = 0; i < sz; ++i) {
1200 int j = dsf_canonify(dsf, shuf[i]); 1207 int j = dsf_minimal(dsf, shuf[i]);
1201 if (next[j] != -2) { 1208 if (next[j] != -2) {
1202 int tmp = board[j]; 1209 int tmp = board[j];
1203 int k; 1210 int k;
@@ -1217,7 +1224,7 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs)
1217 } 1224 }
1218 } 1225 }
1219 sfree(next); 1226 sfree(next);
1220 sfree(dsf); 1227 dsf_free(dsf);
1221 1228
1222 /* 1229 /*
1223 * Now go through individual cells, in the same shuffled order, 1230 * Now go through individual cells, in the same shuffled order,
@@ -1391,7 +1398,7 @@ static game_ui *new_ui(const game_state *state)
1391 1398
1392 ui->sel = NULL; 1399 ui->sel = NULL;
1393 ui->cur_x = ui->cur_y = 0; 1400 ui->cur_x = ui->cur_y = 0;
1394 ui->cur_visible = false; 1401 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1395 ui->keydragging = false; 1402 ui->keydragging = false;
1396 1403
1397 return ui; 1404 return ui;
@@ -1404,15 +1411,6 @@ static void free_ui(game_ui *ui)
1404 sfree(ui); 1411 sfree(ui);
1405} 1412}
1406 1413
1407static char *encode_ui(const game_ui *ui)
1408{
1409 return NULL;
1410}
1411
1412static void decode_ui(game_ui *ui, const char *encoding)
1413{
1414}
1415
1416static void game_changed_state(game_ui *ui, const game_state *oldstate, 1414static void game_changed_state(game_ui *ui, const game_state *oldstate,
1417 const game_state *newstate) 1415 const game_state *newstate)
1418{ 1416{
@@ -1424,6 +1422,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1424 ui->keydragging = false; 1422 ui->keydragging = false;
1425} 1423}
1426 1424
1425static const char *current_key_label(const game_ui *ui,
1426 const game_state *state, int button)
1427{
1428 const int w = state->shared->params.w;
1429
1430 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
1431 if (button == CURSOR_SELECT) {
1432 if (ui->keydragging) return "Stop";
1433 return "Multiselect";
1434 }
1435 if (button == CURSOR_SELECT2 &&
1436 !state->shared->clues[w*ui->cur_y + ui->cur_x])
1437 return (ui->sel[w*ui->cur_y + ui->cur_x]) ? "Deselect" : "Select";
1438 }
1439 return "";
1440}
1441
1427#define PREFERRED_TILE_SIZE 32 1442#define PREFERRED_TILE_SIZE 32
1428#define TILE_SIZE (ds->tilesize) 1443#define TILE_SIZE (ds->tilesize)
1429#define BORDER (TILE_SIZE / 2) 1444#define BORDER (TILE_SIZE / 2)
@@ -1434,7 +1449,8 @@ struct game_drawstate {
1434 int tilesize; 1449 int tilesize;
1435 bool started; 1450 bool started;
1436 int *v, *flags; 1451 int *v, *flags;
1437 int *dsf_scratch, *border_scratch; 1452 DSF *dsf_scratch;
1453 int *border_scratch;
1438}; 1454};
1439 1455
1440static char *interpret_move(const game_state *state, game_ui *ui, 1456static char *interpret_move(const game_state *state, game_ui *ui,
@@ -1453,7 +1469,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1453 assert(ui); 1469 assert(ui);
1454 assert(ds); 1470 assert(ds);
1455 1471
1456 button &= ~MOD_MASK; 1472 button = STRIP_BUTTON_MODIFIERS(button);
1457 1473
1458 if (button == LEFT_BUTTON || button == LEFT_DRAG) { 1474 if (button == LEFT_BUTTON || button == LEFT_DRAG) {
1459 /* A left-click anywhere will clear the current selection. */ 1475 /* A left-click anywhere will clear the current selection. */
@@ -1472,22 +1488,22 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1472 ui->sel[w*ty+tx] = true; 1488 ui->sel[w*ty+tx] = true;
1473 } 1489 }
1474 ui->cur_visible = false; 1490 ui->cur_visible = false;
1475 return UI_UPDATE; 1491 return MOVE_UI_UPDATE;
1476 } 1492 }
1477 1493
1478 if (IS_CURSOR_MOVE(button)) { 1494 if (IS_CURSOR_MOVE(button)) {
1479 ui->cur_visible = true; 1495 ui->cur_visible = true;
1480 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false); 1496 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false, NULL);
1481 if (ui->keydragging) goto select_square; 1497 if (ui->keydragging) goto select_square;
1482 return UI_UPDATE; 1498 return MOVE_UI_UPDATE;
1483 } 1499 }
1484 if (button == CURSOR_SELECT) { 1500 if (button == CURSOR_SELECT) {
1485 if (!ui->cur_visible) { 1501 if (!ui->cur_visible) {
1486 ui->cur_visible = true; 1502 ui->cur_visible = true;
1487 return UI_UPDATE; 1503 return MOVE_UI_UPDATE;
1488 } 1504 }
1489 ui->keydragging = !ui->keydragging; 1505 ui->keydragging = !ui->keydragging;
1490 if (!ui->keydragging) return UI_UPDATE; 1506 if (!ui->keydragging) return MOVE_UI_UPDATE;
1491 1507
1492 select_square: 1508 select_square:
1493 if (!ui->sel) { 1509 if (!ui->sel) {
@@ -1496,12 +1512,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1496 } 1512 }
1497 if (!state->shared->clues[w*ui->cur_y + ui->cur_x]) 1513 if (!state->shared->clues[w*ui->cur_y + ui->cur_x])
1498 ui->sel[w*ui->cur_y + ui->cur_x] = true; 1514 ui->sel[w*ui->cur_y + ui->cur_x] = true;
1499 return UI_UPDATE; 1515 return MOVE_UI_UPDATE;
1500 } 1516 }
1501 if (button == CURSOR_SELECT2) { 1517 if (button == CURSOR_SELECT2) {
1502 if (!ui->cur_visible) { 1518 if (!ui->cur_visible) {
1503 ui->cur_visible = true; 1519 ui->cur_visible = true;
1504 return UI_UPDATE; 1520 return MOVE_UI_UPDATE;
1505 } 1521 }
1506 if (!ui->sel) { 1522 if (!ui->sel) {
1507 ui->sel = snewn(w*h, bool); 1523 ui->sel = snewn(w*h, bool);
@@ -1515,19 +1531,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1515 sfree(ui->sel); 1531 sfree(ui->sel);
1516 ui->sel = NULL; 1532 ui->sel = NULL;
1517 } 1533 }
1518 return UI_UPDATE; 1534 return MOVE_UI_UPDATE;
1519 } 1535 }
1520 1536
1521 if (button == '\b' || button == 27) { 1537 if (button == '\b' || button == 27) {
1522 sfree(ui->sel); 1538 sfree(ui->sel);
1523 ui->sel = NULL; 1539 ui->sel = NULL;
1524 ui->keydragging = false; 1540 ui->keydragging = false;
1525 return UI_UPDATE; 1541 return MOVE_UI_UPDATE;
1526 } 1542 }
1527 1543
1528 if (button < '0' || button > '9') return NULL; 1544 if (button < '0' || button > '9') return MOVE_UNUSED;
1529 button -= '0'; 1545 button -= '0';
1530 if (button > (w == 2 && h == 2 ? 3 : max(w, h))) return NULL; 1546 if (button > (w == 2 && h == 2 ? 3 : max(w, h))) return MOVE_UNUSED;
1531 ui->keydragging = false; 1547 ui->keydragging = false;
1532 1548
1533 for (i = 0; i < w*h; i++) { 1549 for (i = 0; i < w*h; i++) {
@@ -1553,11 +1569,11 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1553 move = srealloc(move, strlen(move)+strlen(buf)+1); 1569 move = srealloc(move, strlen(move)+strlen(buf)+1);
1554 strcat(move, buf); 1570 strcat(move, buf);
1555 } 1571 }
1556 if (!ui->sel) return move ? move : NULL; 1572 if (!ui->sel) return move ? move : MOVE_NO_EFFECT;
1557 sfree(ui->sel); 1573 sfree(ui->sel);
1558 ui->sel = NULL; 1574 ui->sel = NULL;
1559 /* Need to update UI at least, as we cleared the selection */ 1575 /* Need to update UI at least, as we cleared the selection */
1560 return move ? move : UI_UPDATE; 1576 return move ? move : MOVE_UI_UPDATE;
1561} 1577}
1562 1578
1563static game_state *execute_move(const game_state *state, const char *move) 1579static game_state *execute_move(const game_state *state, const char *move)
@@ -1567,6 +1583,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1567 1583
1568 if (*move == 's') { 1584 if (*move == 's') {
1569 int i = 0; 1585 int i = 0;
1586 if (strlen(move) != sz + 1) return NULL;
1570 new_state = dup_game(state); 1587 new_state = dup_game(state);
1571 for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0'; 1588 for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0';
1572 new_state->cheated = true; 1589 new_state->cheated = true;
@@ -1596,10 +1613,10 @@ static game_state *execute_move(const game_state *state, const char *move)
1596 const int w = new_state->shared->params.w; 1613 const int w = new_state->shared->params.w;
1597 const int h = new_state->shared->params.h; 1614 const int h = new_state->shared->params.h;
1598 const int sz = w * h; 1615 const int sz = w * h;
1599 int *dsf = make_dsf(NULL, new_state->board, w, h); 1616 DSF *dsf = make_dsf(NULL, new_state->board, w, h);
1600 int i; 1617 int i;
1601 for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i); 1618 for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i);
1602 sfree(dsf); 1619 dsf_free(dsf);
1603 if (i == sz) 1620 if (i == sz)
1604 new_state->completed = true; 1621 new_state->completed = true;
1605 } 1622 }
@@ -1630,7 +1647,7 @@ enum {
1630}; 1647};
1631 1648
1632static void game_compute_size(const game_params *params, int tilesize, 1649static void game_compute_size(const game_params *params, int tilesize,
1633 int *x, int *y) 1650 const game_ui *ui, int *x, int *y)
1634{ 1651{
1635 *x = (params->w + 1) * tilesize; 1652 *x = (params->w + 1) * tilesize;
1636 *y = (params->h + 1) * tilesize; 1653 *y = (params->h + 1) * tilesize;
@@ -1652,9 +1669,9 @@ static float *game_colours(frontend *fe, int *ncolours)
1652 ret[COL_GRID * 3 + 1] = 0.0F; 1669 ret[COL_GRID * 3 + 1] = 0.0F;
1653 ret[COL_GRID * 3 + 2] = 0.0F; 1670 ret[COL_GRID * 3 + 2] = 0.0F;
1654 1671
1655 ret[COL_HIGHLIGHT * 3 + 0] = 0.85F * ret[COL_BACKGROUND * 3 + 0]; 1672 ret[COL_HIGHLIGHT * 3 + 0] = 0.7F * ret[COL_BACKGROUND * 3 + 0];
1656 ret[COL_HIGHLIGHT * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1]; 1673 ret[COL_HIGHLIGHT * 3 + 1] = 0.7F * ret[COL_BACKGROUND * 3 + 1];
1657 ret[COL_HIGHLIGHT * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2]; 1674 ret[COL_HIGHLIGHT * 3 + 2] = 0.7F * ret[COL_BACKGROUND * 3 + 2];
1658 1675
1659 ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0]; 1676 ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0];
1660 ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1]; 1677 ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1];
@@ -1699,7 +1716,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1699 sfree(ds->v); 1716 sfree(ds->v);
1700 sfree(ds->flags); 1717 sfree(ds->flags);
1701 sfree(ds->border_scratch); 1718 sfree(ds->border_scratch);
1702 sfree(ds->dsf_scratch); 1719 dsf_free(ds->dsf_scratch);
1703 sfree(ds); 1720 sfree(ds);
1704} 1721}
1705 1722
@@ -2016,17 +2033,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2016 (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3); 2033 (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3);
2017 2034
2018 if (!ds->started) { 2035 if (!ds->started) {
2019 /*
2020 * The initial contents of the window are not guaranteed and
2021 * can vary with front ends. To be on the safe side, all games
2022 * should start by drawing a big background-colour rectangle
2023 * covering the whole window.
2024 */
2025 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2026 COL_BACKGROUND);
2027
2028 /* 2036 /*
2029 * Smaller black rectangle which is the main grid. 2037 * Black rectangle which is the main grid.
2030 */ 2038 */
2031 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, 2039 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2032 w*TILE_SIZE + 2*BORDER_WIDTH + 1, 2040 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
@@ -2079,24 +2087,21 @@ static int game_status(const game_state *state)
2079 return state->completed ? +1 : 0; 2087 return state->completed ? +1 : 0;
2080} 2088}
2081 2089
2082static bool game_timing_state(const game_state *state, game_ui *ui) 2090static void game_print_size(const game_params *params, const game_ui *ui,
2083{ 2091 float *x, float *y)
2084 return true;
2085}
2086
2087static void game_print_size(const game_params *params, float *x, float *y)
2088{ 2092{
2089 int pw, ph; 2093 int pw, ph;
2090 2094
2091 /* 2095 /*
2092 * I'll use 6mm squares by default. 2096 * I'll use 6mm squares by default.
2093 */ 2097 */
2094 game_compute_size(params, 600, &pw, &ph); 2098 game_compute_size(params, 600, ui, &pw, &ph);
2095 *x = pw / 100.0F; 2099 *x = pw / 100.0F;
2096 *y = ph / 100.0F; 2100 *y = ph / 100.0F;
2097} 2101}
2098 2102
2099static void game_print(drawing *dr, const game_state *state, int tilesize) 2103static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2104 int tilesize)
2100{ 2105{
2101 const int w = state->shared->params.w; 2106 const int w = state->shared->params.w;
2102 const int h = state->shared->params.h; 2107 const int h = state->shared->params.h;
@@ -2164,12 +2169,14 @@ const struct game thegame = {
2164 free_game, 2169 free_game,
2165 true, solve_game, 2170 true, solve_game,
2166 true, game_can_format_as_text_now, game_text_format, 2171 true, game_can_format_as_text_now, game_text_format,
2172 NULL, NULL, /* get_prefs, set_prefs */
2167 new_ui, 2173 new_ui,
2168 free_ui, 2174 free_ui,
2169 encode_ui, 2175 NULL, /* encode_ui */
2170 decode_ui, 2176 NULL, /* decode_ui */
2171 game_request_keys, 2177 game_request_keys,
2172 game_changed_state, 2178 game_changed_state,
2179 current_key_label,
2173 interpret_move, 2180 interpret_move,
2174 execute_move, 2181 execute_move,
2175 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2182 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2183,13 +2190,18 @@ const struct game thegame = {
2183 game_status, 2190 game_status,
2184 true, false, game_print_size, game_print, 2191 true, false, game_print_size, game_print,
2185 false, /* wants_statusbar */ 2192 false, /* wants_statusbar */
2186 false, game_timing_state, 2193 false, NULL, /* timing_state */
2187 REQUIRE_NUMPAD, /* flags */ 2194 REQUIRE_NUMPAD, /* flags */
2188}; 2195};
2189 2196
2190#ifdef STANDALONE_SOLVER /* solver? hah! */ 2197#ifdef STANDALONE_SOLVER /* solver? hah! */
2191 2198
2192int main(int argc, char **argv) { 2199int main(int argc, char **argv) {
2200 if (!strcmp(argv[1], "--verbose")) {
2201 verbose = true;
2202 argv++;
2203 }
2204
2193 while (*++argv) { 2205 while (*++argv) {
2194 game_params *params; 2206 game_params *params;
2195 game_state *state; 2207 game_state *state;
diff --git a/apps/plugins/puzzles/src/flip.R b/apps/plugins/puzzles/src/flip.R
deleted file mode 100644
index 03241f015b..0000000000
--- a/apps/plugins/puzzles/src/flip.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3FLIP_EXTRA = tree234
4
5flip : [X] GTK COMMON flip FLIP_EXTRA flip-icon|no-icon
6
7flip : [G] WINDOWS COMMON flip FLIP_EXTRA flip.res|noicon.res
8
9ALL += flip[COMBINED] FLIP_EXTRA
10
11!begin am gtk
12GAMES += flip
13!end
14
15!begin >list.c
16 A(flip) \
17!end
18
19!begin >gamedesc.txt
20flip:flip.exe:Flip:Tile inversion puzzle:Flip groups of squares to light them all up at once.
21!end
diff --git a/apps/plugins/puzzles/src/flip.c b/apps/plugins/puzzles/src/flip.c
index 5d4f2250aa..044d6c30c3 100644
--- a/apps/plugins/puzzles/src/flip.c
+++ b/apps/plugins/puzzles/src/flip.c
@@ -8,7 +8,12 @@
8#include <string.h> 8#include <string.h>
9#include <assert.h> 9#include <assert.h>
10#include <ctype.h> 10#include <ctype.h>
11#include <math.h> 11#include <limits.h>
12#ifdef NO_TGMATH_H
13# include <math.h>
14#else
15# include <tgmath.h>
16#endif
12 17
13#include "puzzles.h" 18#include "puzzles.h"
14#include "tree234.h" 19#include "tree234.h"
@@ -181,9 +186,16 @@ static game_params *custom_params(const config_item *cfg)
181 186
182static const char *validate_params(const game_params *params, bool full) 187static const char *validate_params(const game_params *params, bool full)
183{ 188{
189 int wh;
190
184 if (params->w <= 0 || params->h <= 0) 191 if (params->w <= 0 || params->h <= 0)
185 return "Width and height must both be greater than zero"; 192 return "Width and height must both be greater than zero";
186 return NULL; 193 if (params->w > (INT_MAX - 3) / params->h)
194 return "Width times height must not be unreasonably large";
195 wh = params->w * params->h;
196 if (wh > (INT_MAX - 3) / wh)
197 return "Width times height is too large";
198 return NULL;
187} 199}
188 200
189static char *encode_bitmap(unsigned char *bmp, int len) 201static char *encode_bitmap(unsigned char *bmp, int len)
@@ -905,7 +917,7 @@ static game_ui *new_ui(const game_state *state)
905{ 917{
906 game_ui *ui = snew(game_ui); 918 game_ui *ui = snew(game_ui);
907 ui->cx = ui->cy = 0; 919 ui->cx = ui->cy = 0;
908 ui->cdraw = false; 920 ui->cdraw = getenv_bool("PUZZLES_SHOW_CURSOR", false);
909 return ui; 921 return ui;
910} 922}
911 923
@@ -914,18 +926,16 @@ static void free_ui(game_ui *ui)
914 sfree(ui); 926 sfree(ui);
915} 927}
916 928
917static char *encode_ui(const game_ui *ui) 929static void game_changed_state(game_ui *ui, const game_state *oldstate,
918{ 930 const game_state *newstate)
919 return NULL;
920}
921
922static void decode_ui(game_ui *ui, const char *encoding)
923{ 931{
924} 932}
925 933
926static void game_changed_state(game_ui *ui, const game_state *oldstate, 934static const char *current_key_label(const game_ui *ui,
927 const game_state *newstate) 935 const game_state *state, int button)
928{ 936{
937 if (IS_CURSOR_SELECT(button)) return "Flip";
938 return "";
929} 939}
930 940
931struct game_drawstate { 941struct game_drawstate {
@@ -940,7 +950,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
940 int x, int y, int button) 950 int x, int y, int button)
941{ 951{
942 int w = state->w, h = state->h, wh = w * h; 952 int w = state->w, h = state->h, wh = w * h;
943 char buf[80], *nullret = NULL; 953 char buf[80], *nullret = MOVE_UNUSED;
944 954
945 if (button == LEFT_BUTTON || IS_CURSOR_SELECT(button)) { 955 if (button == LEFT_BUTTON || IS_CURSOR_SELECT(button)) {
946 int tx, ty; 956 int tx, ty;
@@ -951,7 +961,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
951 tx = ui->cx; ty = ui->cy; 961 tx = ui->cx; ty = ui->cy;
952 ui->cdraw = true; 962 ui->cdraw = true;
953 } 963 }
954 nullret = UI_UPDATE; 964 nullret = MOVE_UI_UPDATE;
955 965
956 if (tx >= 0 && tx < w && ty >= 0 && ty < h) { 966 if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
957 /* 967 /*
@@ -969,25 +979,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
969 sprintf(buf, "M%d,%d", tx, ty); 979 sprintf(buf, "M%d,%d", tx, ty);
970 return dupstr(buf); 980 return dupstr(buf);
971 } else { 981 } else {
972 return NULL; 982 return MOVE_NO_EFFECT;
973 } 983 }
974 } 984 }
975 } 985 } else if (IS_CURSOR_MOVE(button))
976 else if (IS_CURSOR_MOVE(button)) { 986 nullret = move_cursor(button, &ui->cx, &ui->cy, state->w, state->h,
977 int dx = 0, dy = 0; 987 false, &ui->cdraw);
978 switch (button) {
979 case CURSOR_UP: dy = -1; break;
980 case CURSOR_DOWN: dy = 1; break;
981 case CURSOR_RIGHT: dx = 1; break;
982 case CURSOR_LEFT: dx = -1; break;
983 default: assert(!"shouldn't get here");
984 }
985 ui->cx += dx; ui->cy += dy;
986 ui->cx = min(max(ui->cx, 0), state->w - 1);
987 ui->cy = min(max(ui->cy, 0), state->h - 1);
988 ui->cdraw = true;
989 nullret = UI_UPDATE;
990 }
991 988
992 return nullret; 989 return nullret;
993} 990}
@@ -1045,7 +1042,7 @@ static game_state *execute_move(const game_state *from, const char *move)
1045 */ 1042 */
1046 1043
1047static void game_compute_size(const game_params *params, int tilesize, 1044static void game_compute_size(const game_params *params, int tilesize,
1048 int *x, int *y) 1045 const game_ui *ui, int *x, int *y)
1049{ 1046{
1050 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1047 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1051 struct { int tilesize; } ads, *ds = &ads; 1048 struct { int tilesize; } ads, *ds = &ads;
@@ -1145,7 +1142,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1145 coords[7] = by + TILE_SIZE - (int)((float)TILE_SIZE * animtime); 1142 coords[7] = by + TILE_SIZE - (int)((float)TILE_SIZE * animtime);
1146 1143
1147 colour = (tile & 1 ? COL_WRONG : COL_RIGHT); 1144 colour = (tile & 1 ? COL_WRONG : COL_RIGHT);
1148 if (animtime < 0.5) 1145 if (animtime < 0.5F)
1149 colour = COL_WRONG + COL_RIGHT - colour; 1146 colour = COL_WRONG + COL_RIGHT - colour;
1150 1147
1151 draw_polygon(dr, coords, 4, colour, COL_GRID); 1148 draw_polygon(dr, coords, 4, colour, COL_GRID);
@@ -1159,7 +1156,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1159 for (j = 0; j < w; j++) 1156 for (j = 0; j < w; j++)
1160 if (state->matrix->matrix[(y*w+x)*wh + i*w+j]) { 1157 if (state->matrix->matrix[(y*w+x)*wh + i*w+j]) {
1161 int ox = j - x, oy = i - y; 1158 int ox = j - x, oy = i - y;
1162 int td = TILE_SIZE / 16; 1159 int td = TILE_SIZE / 16 ? TILE_SIZE / 16 : 1;
1163 int cx = (bx + TILE_SIZE/2) + (2 * ox - 1) * td; 1160 int cx = (bx + TILE_SIZE/2) + (2 * ox - 1) * td;
1164 int cy = (by + TILE_SIZE/2) + (2 * oy - 1) * td; 1161 int cy = (by + TILE_SIZE/2) + (2 * oy - 1) * td;
1165 if (ox == 0 && oy == 0) 1162 if (ox == 0 && oy == 0)
@@ -1202,9 +1199,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1202 int i, flashframe; 1199 int i, flashframe;
1203 1200
1204 if (!ds->started) { 1201 if (!ds->started) {
1205 draw_rect(dr, 0, 0, TILE_SIZE * w + 2 * BORDER,
1206 TILE_SIZE * h + 2 * BORDER, COL_BACKGROUND);
1207
1208 /* 1202 /*
1209 * Draw the grid lines. 1203 * Draw the grid lines.
1210 */ 1204 */
@@ -1309,19 +1303,6 @@ static int game_status(const game_state *state)
1309 return state->completed ? +1 : 0; 1303 return state->completed ? +1 : 0;
1310} 1304}
1311 1305
1312static bool game_timing_state(const game_state *state, game_ui *ui)
1313{
1314 return true;
1315}
1316
1317static void game_print_size(const game_params *params, float *x, float *y)
1318{
1319}
1320
1321static void game_print(drawing *dr, const game_state *state, int tilesize)
1322{
1323}
1324
1325#ifdef COMBINED 1306#ifdef COMBINED
1326#define thegame flip 1307#define thegame flip
1327#endif 1308#endif
@@ -1343,12 +1324,14 @@ const struct game thegame = {
1343 free_game, 1324 free_game,
1344 true, solve_game, 1325 true, solve_game,
1345 true, game_can_format_as_text_now, game_text_format, 1326 true, game_can_format_as_text_now, game_text_format,
1327 NULL, NULL, /* get_prefs, set_prefs */
1346 new_ui, 1328 new_ui,
1347 free_ui, 1329 free_ui,
1348 encode_ui, 1330 NULL, /* encode_ui */
1349 decode_ui, 1331 NULL, /* decode_ui */
1350 NULL, /* game_request_keys */ 1332 NULL, /* game_request_keys */
1351 game_changed_state, 1333 game_changed_state,
1334 current_key_label,
1352 interpret_move, 1335 interpret_move,
1353 execute_move, 1336 execute_move,
1354 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1337 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1360,8 +1343,8 @@ const struct game thegame = {
1360 game_flash_length, 1343 game_flash_length,
1361 game_get_cursor_location, 1344 game_get_cursor_location,
1362 game_status, 1345 game_status,
1363 false, false, game_print_size, game_print, 1346 false, false, NULL, NULL, /* print_size, print */
1364 true, /* wants_statusbar */ 1347 true, /* wants_statusbar */
1365 false, game_timing_state, 1348 false, NULL, /* timing_state */
1366 0, /* flags */ 1349 0, /* flags */
1367}; 1350};
diff --git a/apps/plugins/puzzles/src/flood.R b/apps/plugins/puzzles/src/flood.R
deleted file mode 100644
index 359bbb5dce..0000000000
--- a/apps/plugins/puzzles/src/flood.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3flood : [X] GTK COMMON flood flood-icon|no-icon
4
5flood : [G] WINDOWS COMMON flood flood.res|noicon.res
6
7ALL += flood[COMBINED]
8
9!begin am gtk
10GAMES += flood
11!end
12
13!begin >list.c
14 A(flood) \
15!end
16
17!begin >gamedesc.txt
18flood:flood.exe:Flood:Flood-filling puzzle:Turn the grid the same colour in as few flood fills as possible.
19!end
diff --git a/apps/plugins/puzzles/src/flood.c b/apps/plugins/puzzles/src/flood.c
index 74214a50b6..fe809745db 100644
--- a/apps/plugins/puzzles/src/flood.c
+++ b/apps/plugins/puzzles/src/flood.c
@@ -31,7 +31,12 @@
31#include <string.h> 31#include <string.h>
32#include <assert.h> 32#include <assert.h>
33#include <ctype.h> 33#include <ctype.h>
34#include <math.h> 34#include <limits.h>
35#ifdef NO_TGMATH_H
36# include <math.h>
37#else
38# include <tgmath.h>
39#endif
35 40
36#include "puzzles.h" 41#include "puzzles.h"
37 42
@@ -140,13 +145,13 @@ static void decode_params(game_params *ret, char const *string)
140 if (*string == 'c') { 145 if (*string == 'c') {
141 string++; 146 string++;
142 ret->colours = atoi(string); 147 ret->colours = atoi(string);
143 while (string[1] && isdigit((unsigned char)string[1])) string++; 148 while (*string && isdigit((unsigned char)*string)) string++;
144 } else if (*string == 'm') { 149 } else if (*string == 'm') {
145 string++; 150 string++;
146 ret->leniency = atoi(string); 151 ret->leniency = atoi(string);
147 while (string[1] && isdigit((unsigned char)string[1])) string++; 152 while (*string && isdigit((unsigned char)*string)) string++;
148 } 153 } else
149 string++; 154 string++;
150 } 155 }
151} 156}
152 157
@@ -210,7 +215,9 @@ static const char *validate_params(const game_params *params, bool full)
210 if (params->w * params->h < 2) 215 if (params->w * params->h < 2)
211 return "Grid must contain at least two squares"; 216 return "Grid must contain at least two squares";
212 if (params->w < 1 || params->h < 1) 217 if (params->w < 1 || params->h < 1)
213 return "Width and height must both be at least one"; 218 return "Width and height must be at least one";
219 if (params->w > INT_MAX / params->h)
220 return "Width times height must not be unreasonably large";
214 if (params->colours < 3 || params->colours > 10) 221 if (params->colours < 3 || params->colours > 10)
215 return "Must have between 3 and 10 colours"; 222 return "Must have between 3 and 10 colours";
216 if (params->leniency < 0) 223 if (params->leniency < 0)
@@ -554,8 +561,10 @@ static char *new_game_desc(const game_params *params, random_state *rs,
554 /* 561 /*
555 * Invent a random grid. 562 * Invent a random grid.
556 */ 563 */
557 for (i = 0; i < wh; i++) 564 do {
558 scratch->grid[i] = random_upto(rs, params->colours); 565 for (i = 0; i < wh; i++)
566 scratch->grid[i] = random_upto(rs, params->colours);
567 } while (completed(w, h, scratch->grid));
559 568
560 /* 569 /*
561 * Run the solver, and count how many moves it uses. 570 * Run the solver, and count how many moves it uses.
@@ -770,7 +779,7 @@ struct game_ui {
770static game_ui *new_ui(const game_state *state) 779static game_ui *new_ui(const game_state *state)
771{ 780{
772 struct game_ui *ui = snew(struct game_ui); 781 struct game_ui *ui = snew(struct game_ui);
773 ui->cursor_visible = false; 782 ui->cursor_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
774 ui->cx = FILLX; 783 ui->cx = FILLX;
775 ui->cy = FILLY; 784 ui->cy = FILLY;
776 return ui; 785 return ui;
@@ -781,18 +790,21 @@ static void free_ui(game_ui *ui)
781 sfree(ui); 790 sfree(ui);
782} 791}
783 792
784static char *encode_ui(const game_ui *ui) 793static void game_changed_state(game_ui *ui, const game_state *oldstate,
785{ 794 const game_state *newstate)
786 return NULL;
787}
788
789static void decode_ui(game_ui *ui, const char *encoding)
790{ 795{
791} 796}
792 797
793static void game_changed_state(game_ui *ui, const game_state *oldstate, 798static const char *current_key_label(const game_ui *ui,
794 const game_state *newstate) 799 const game_state *state, int button)
795{ 800{
801 if (button == CURSOR_SELECT &&
802 state->grid[0] != state->grid[ui->cy*state->w+ui->cx])
803 return "Fill";
804 if (button == CURSOR_SELECT2 &&
805 state->soln && state->solnpos < state->soln->nmoves)
806 return "Advance";
807 return "";
796} 808}
797 809
798struct game_drawstate { 810struct game_drawstate {
@@ -818,35 +830,26 @@ static char *interpret_move(const game_state *state, game_ui *ui,
818{ 830{
819 int w = state->w, h = state->h; 831 int w = state->w, h = state->h;
820 int tx = -1, ty = -1, move = -1; 832 int tx = -1, ty = -1, move = -1;
833 char *nullret = MOVE_NO_EFFECT;
821 834
822 if (button == LEFT_BUTTON) { 835 if (button == LEFT_BUTTON) {
823 tx = FROMCOORD(x); 836 tx = FROMCOORD(x);
824 ty = FROMCOORD(y); 837 ty = FROMCOORD(y);
825 ui->cursor_visible = false; 838 if (ui->cursor_visible) {
826 } else if (button == CURSOR_LEFT && ui->cx > 0) { 839 ui->cursor_visible = false;
827 ui->cx--; 840 nullret = MOVE_UI_UPDATE;
828 ui->cursor_visible = true; 841 }
829 return UI_UPDATE; 842 } else if (IS_CURSOR_MOVE(button)) {
830 } else if (button == CURSOR_RIGHT && ui->cx+1 < w) { 843 return move_cursor(button, &ui->cx, &ui->cy, w, h, false,
831 ui->cx++; 844 &ui->cursor_visible);
832 ui->cursor_visible = true;
833 return UI_UPDATE;
834 } else if (button == CURSOR_UP && ui->cy > 0) {
835 ui->cy--;
836 ui->cursor_visible = true;
837 return UI_UPDATE;
838 } else if (button == CURSOR_DOWN && ui->cy+1 < h) {
839 ui->cy++;
840 ui->cursor_visible = true;
841 return UI_UPDATE;
842 } else if (button == CURSOR_SELECT) { 845 } else if (button == CURSOR_SELECT) {
843 tx = ui->cx; 846 tx = ui->cx;
844 ty = ui->cy; 847 ty = ui->cy;
845 } else if (button == CURSOR_SELECT2 && 848 } else if (button == CURSOR_SELECT2) {
846 state->soln && state->solnpos < state->soln->nmoves) { 849 if (state->soln && state->solnpos < state->soln->nmoves)
847 move = state->soln->moves[state->solnpos]; 850 move = state->soln->moves[state->solnpos];
848 } else { 851 } else {
849 return NULL; 852 return MOVE_UNUSED;
850 } 853 }
851 854
852 if (tx >= 0 && tx < w && ty >= 0 && ty < h && 855 if (tx >= 0 && tx < w && ty >= 0 && ty < h &&
@@ -859,7 +862,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
859 return dupstr(buf); 862 return dupstr(buf);
860 } 863 }
861 864
862 return NULL; 865 return nullret;
863} 866}
864 867
865static game_state *execute_move(const game_state *state, const char *move) 868static game_state *execute_move(const game_state *state, const char *move)
@@ -869,7 +872,8 @@ static game_state *execute_move(const game_state *state, const char *move)
869 872
870 if (move[0] == 'M' && 873 if (move[0] == 'M' &&
871 sscanf(move+1, "%d", &c) == 1 && 874 sscanf(move+1, "%d", &c) == 1 &&
872 c >= 0 && 875 c >= 0 && c < state->colours &&
876 c != state->grid[FILLY * state->w + FILLX] &&
873 !state->complete) { 877 !state->complete) {
874 int *queue = snewn(state->w * state->h, int); 878 int *queue = snewn(state->w * state->h, int);
875 ret = dup_game(state); 879 ret = dup_game(state);
@@ -920,11 +924,23 @@ static game_state *execute_move(const game_state *state, const char *move)
920 924
921 sol->moves = snewn(sol->nmoves, char); 925 sol->moves = snewn(sol->nmoves, char);
922 for (i = 0, p = move; i < sol->nmoves; i++) { 926 for (i = 0, p = move; i < sol->nmoves; i++) {
923 assert(*p); 927 if (!*p) {
928 badsolve:
929 sfree(sol->moves);
930 sfree(sol);
931 return NULL;
932 };
924 sol->moves[i] = atoi(p); 933 sol->moves[i] = atoi(p);
934 if (sol->moves[i] < 0 || sol->moves[i] >= state->colours ||
935 (i == 0 ?
936 sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
937 sol->moves[i] == sol->moves[i-1]))
938 /* Solution contains a fill with an invalid colour or
939 * the current colour. */
940 goto badsolve;
925 p += strspn(p, "0123456789"); 941 p += strspn(p, "0123456789");
926 if (*p) { 942 if (*p) {
927 assert(*p == ','); 943 if (*p != ',') goto badsolve;
928 p++; 944 p++;
929 } 945 }
930 } 946 }
@@ -949,7 +965,7 @@ static game_state *execute_move(const game_state *state, const char *move)
949 */ 965 */
950 966
951static void game_compute_size(const game_params *params, int tilesize, 967static void game_compute_size(const game_params *params, int tilesize,
952 int *x, int *y) 968 const game_ui *ui, int *x, int *y)
953{ 969{
954 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 970 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
955 struct { int tilesize; } ads, *ds = &ads; 971 struct { int tilesize; } ads, *ds = &ads;
@@ -1076,31 +1092,33 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
1076 colour += COL_1; 1092 colour += COL_1;
1077 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour); 1093 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour);
1078 1094
1079 if (tile & BORDER_L) 1095 if (SEP_WIDTH > 0) {
1080 draw_rect(dr, tx, ty, 1096 if (tile & BORDER_L)
1081 SEP_WIDTH, TILESIZE, COL_SEPARATOR); 1097 draw_rect(dr, tx, ty,
1082 if (tile & BORDER_R) 1098 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1083 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty, 1099 if (tile & BORDER_R)
1084 SEP_WIDTH, TILESIZE, COL_SEPARATOR); 1100 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1085 if (tile & BORDER_U) 1101 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1086 draw_rect(dr, tx, ty, 1102 if (tile & BORDER_U)
1087 TILESIZE, SEP_WIDTH, COL_SEPARATOR); 1103 draw_rect(dr, tx, ty,
1088 if (tile & BORDER_D) 1104 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1089 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH, 1105 if (tile & BORDER_D)
1090 TILESIZE, SEP_WIDTH, COL_SEPARATOR); 1106 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1091 1107 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1092 if (tile & CORNER_UL) 1108
1093 draw_rect(dr, tx, ty, 1109 if (tile & CORNER_UL)
1094 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1110 draw_rect(dr, tx, ty,
1095 if (tile & CORNER_UR) 1111 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1096 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty, 1112 if (tile & CORNER_UR)
1097 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1113 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1098 if (tile & CORNER_DL) 1114 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1099 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH, 1115 if (tile & CORNER_DL)
1100 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1116 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1101 if (tile & CORNER_DR) 1117 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1102 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH, 1118 if (tile & CORNER_DR)
1103 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1119 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH,
1120 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1121 }
1104 1122
1105 if (tile & CURSOR) 1123 if (tile & CURSOR)
1106 draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET, 1124 draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET,
@@ -1130,13 +1148,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1130 if (!ds->started) { 1148 if (!ds->started) {
1131 int coords[10]; 1149 int coords[10];
1132 1150
1133 draw_rect(dr, 0, 0,
1134 TILESIZE * w + 2 * BORDER,
1135 TILESIZE * h + 2 * BORDER, COL_BACKGROUND);
1136 draw_update(dr, 0, 0,
1137 TILESIZE * w + 2 * BORDER,
1138 TILESIZE * h + 2 * BORDER);
1139
1140 /* 1151 /*
1141 * Recessed area containing the whole puzzle. 1152 * Recessed area containing the whole puzzle.
1142 */ 1153 */
@@ -1326,19 +1337,6 @@ static float game_flash_length(const game_state *oldstate,
1326 return 0.0F; 1337 return 0.0F;
1327} 1338}
1328 1339
1329static bool game_timing_state(const game_state *state, game_ui *ui)
1330{
1331 return true;
1332}
1333
1334static void game_print_size(const game_params *params, float *x, float *y)
1335{
1336}
1337
1338static void game_print(drawing *dr, const game_state *state, int tilesize)
1339{
1340}
1341
1342#ifdef COMBINED 1340#ifdef COMBINED
1343#define thegame flood 1341#define thegame flood
1344#endif 1342#endif
@@ -1360,12 +1358,14 @@ const struct game thegame = {
1360 free_game, 1358 free_game,
1361 true, solve_game, 1359 true, solve_game,
1362 true, game_can_format_as_text_now, game_text_format, 1360 true, game_can_format_as_text_now, game_text_format,
1361 NULL, NULL, /* get_prefs, set_prefs */
1363 new_ui, 1362 new_ui,
1364 free_ui, 1363 free_ui,
1365 encode_ui, 1364 NULL, /* encode_ui */
1366 decode_ui, 1365 NULL, /* decode_ui */
1367 NULL, /* game_request_keys */ 1366 NULL, /* game_request_keys */
1368 game_changed_state, 1367 game_changed_state,
1368 current_key_label,
1369 interpret_move, 1369 interpret_move,
1370 execute_move, 1370 execute_move,
1371 PREFERRED_TILESIZE, game_compute_size, game_set_size, 1371 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -1377,8 +1377,8 @@ const struct game thegame = {
1377 game_flash_length, 1377 game_flash_length,
1378 game_get_cursor_location, 1378 game_get_cursor_location,
1379 game_status, 1379 game_status,
1380 false, false, game_print_size, game_print, 1380 false, false, NULL, NULL, /* print_size, print */
1381 true, /* wants_statusbar */ 1381 true, /* wants_statusbar */
1382 false, game_timing_state, 1382 false, NULL, /* timing_state */
1383 0, /* flags */ 1383 0, /* flags */
1384}; 1384};
diff --git a/apps/plugins/puzzles/src/galaxies.R b/apps/plugins/puzzles/src/galaxies.R
deleted file mode 100644
index 957e5dad90..0000000000
--- a/apps/plugins/puzzles/src/galaxies.R
+++ /dev/null
@@ -1,28 +0,0 @@
1# -*- makefile -*-
2
3GALAXIES_EXTRA = dsf
4
5galaxies : [X] GTK COMMON galaxies GALAXIES_EXTRA galaxies-icon|no-icon
6
7galaxies : [G] WINDOWS COMMON galaxies GALAXIES_EXTRA galaxies.res|noicon.res
8
9galaxiessolver : [U] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE m.lib
10galaxiessolver : [C] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE
11
12galaxiespicture : [U] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
13 + m.lib
14galaxiespicture : [C] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
15
16ALL += galaxies[COMBINED] GALAXIES_EXTRA
17
18!begin am gtk
19GAMES += galaxies
20!end
21
22!begin >list.c
23 A(galaxies) \
24!end
25
26!begin >gamedesc.txt
27galaxies:galaxies.exe:Galaxies:Symmetric polyomino puzzle:Divide the grid into rotationally symmetric regions each centred on a dot.
28!end
diff --git a/apps/plugins/puzzles/src/galaxies.c b/apps/plugins/puzzles/src/galaxies.c
index 9172b90e12..8859917558 100644
--- a/apps/plugins/puzzles/src/galaxies.c
+++ b/apps/plugins/puzzles/src/galaxies.c
@@ -13,9 +13,46 @@
13 * Edges have on/off state; obviously the actual edges of the 13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off. 14 * board are fixed to on, and everything else starts as off.
15 * 15 *
16 * TTD: 16 * Future solver directions:
17 * Cleverer solver 17 *
18 * Think about how to display remote groups of tiles? 18 * - Non-local version of the exclave extension? Suppose you have an
19 * exclave with multiple potential paths back home, but all of them
20 * go through the same tile somewhere in the middle of the path.
21 * Then _that_ critical square can be assigned to the home dot,
22 * even if we don't yet know the details of the path from it to
23 * either existing region.
24 *
25 * - Permit non-simply-connected puzzle instances in sub-Unreasonable
26 * mode? Even the simplest case 5x3:ubb is graded Unreasonable at
27 * present, because we have no solution technique short of
28 * recursion that can handle it.
29 *
30 * The reasoning a human uses for that puzzle is to observe that
31 * the centre left square has to connect to the centre dot, so it
32 * must have _some_ path back there. It could go round either side
33 * of the dot in the way. But _whichever_ way it goes, that rules
34 * out the left dot extending to the squares above and below it,
35 * because if it did that, that would block _both_ routes back to
36 * the centre.
37 *
38 * But the exclave-extending deduction we have at present is only
39 * capable of extending an exclave with _one_ liberty. This has
40 * two, so the only technique we have available is to try them one
41 * by one via recursion.
42 *
43 * My vague plan to fix this would be to re-run the exclave
44 * extension on a per-dot basis (probably after working out a
45 * non-local version as described above): instead of trying to find
46 * all exclaves at once, try it for one exclave at a time, or
47 * perhaps all exclaves relating to a particular home dot H. The
48 * point of this is that then you could spot pairs of squares with
49 * _two_ possible dots, one of which is H, and which are opposite
50 * to each other with respect to their other dot D (such as the
51 * squares above/below the left dot in this example). And then you
52 * merge those into one vertex of the connectivity graph, on the
53 * grounds that they're either both H or both D - and _then_ you
54 * have an exclave with only one path back home, and can make
55 * progress.
19 * 56 *
20 * Bugs: 57 * Bugs:
21 * 58 *
@@ -42,15 +79,22 @@
42#include <string.h> 79#include <string.h>
43#include <assert.h> 80#include <assert.h>
44#include <ctype.h> 81#include <ctype.h>
45#include <math.h> 82#include <limits.h>
83#ifdef NO_TGMATH_H
84# include <math.h>
85#else
86# include <tgmath.h>
87#endif
46 88
47#include "puzzles.h" 89#include "puzzles.h"
48 90
49#ifdef DEBUGGING 91#ifdef DEBUGGING
50#define solvep debug 92#define solvep debug
51#else 93#elif defined STANDALONE_SOLVER
52static bool solver_show_working; 94static bool solver_show_working;
53#define solvep(x) do { if (solver_show_working) { printf x; } } while(0) 95#define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
96#else
97#define solvep(x) ((void)0)
54#endif 98#endif
55 99
56#ifdef STANDALONE_PICTURE_GENERATOR 100#ifdef STANDALONE_PICTURE_GENERATOR
@@ -148,13 +192,15 @@ struct game_state {
148 or -1 if stale. */ 192 or -1 if stale. */
149}; 193};
150 194
151static bool check_complete(const game_state *state, int *dsf, int *colours); 195static bool check_complete(const game_state *state, DSF *dsf, int *colours);
196static int solver_state_inner(game_state *state, int maxdiff, int depth);
152static int solver_state(game_state *state, int maxdiff); 197static int solver_state(game_state *state, int maxdiff);
153static int solver_obvious(game_state *state); 198static int solver_obvious(game_state *state);
154static int solver_obvious_dot(game_state *state, space *dot); 199static int solver_obvious_dot(game_state *state, space *dot);
155static space *space_opposite_dot(const game_state *state, const space *sp, 200static space *space_opposite_dot(const game_state *state, const space *sp,
156 const space *dot); 201 const space *dot);
157static space *tile_opposite(const game_state *state, const space *sp); 202static space *tile_opposite(const game_state *state, const space *sp);
203static game_state *execute_move(const game_state *state, const char *move);
158 204
159/* ---------------------------------------------------------- 205/* ----------------------------------------------------------
160 * Game parameters and presets 206 * Game parameters and presets
@@ -168,7 +214,9 @@ static const game_params galaxies_presets[] = {
168 { 7, 7, DIFF_NORMAL }, 214 { 7, 7, DIFF_NORMAL },
169 { 7, 7, DIFF_UNREASONABLE }, 215 { 7, 7, DIFF_UNREASONABLE },
170 { 10, 10, DIFF_NORMAL }, 216 { 10, 10, DIFF_NORMAL },
217 { 10, 10, DIFF_UNREASONABLE },
171 { 15, 15, DIFF_NORMAL }, 218 { 15, 15, DIFF_NORMAL },
219 { 15, 15, DIFF_UNREASONABLE },
172}; 220};
173 221
174static bool game_fetch_preset(int i, char **name, game_params **params) 222static bool game_fetch_preset(int i, char **name, game_params **params)
@@ -281,6 +329,10 @@ static const char *validate_params(const game_params *params, bool full)
281{ 329{
282 if (params->w < 3 || params->h < 3) 330 if (params->w < 3 || params->h < 3)
283 return "Width and height must both be at least 3"; 331 return "Width and height must both be at least 3";
332 if (params->w > INT_MAX / 2 || params->h > INT_MAX / 2 ||
333 params->w > (INT_MAX - params->w*2 - params->h*2 - 1) / 4 / params->h)
334 return "Width times height must not be unreasonably large";
335
284 /* 336 /*
285 * This shouldn't be able to happen at all, since decode_params 337 * This shouldn't be able to happen at all, since decode_params
286 * and custom_params will never generate anything that isn't 338 * and custom_params will never generate anything that isn't
@@ -352,6 +404,8 @@ static bool ok_to_add_assoc_with_opposite_internal(
352 int *colors; 404 int *colors;
353 bool toret; 405 bool toret;
354 406
407 if (tile->type != s_tile)
408 return false;
355 if (tile->flags & F_DOT) 409 if (tile->flags & F_DOT)
356 return false; 410 return false;
357 if (opposite == NULL) 411 if (opposite == NULL)
@@ -372,20 +426,21 @@ static bool ok_to_add_assoc_with_opposite_internal(
372 return toret; 426 return toret;
373} 427}
374 428
429#ifndef EDITOR
375static bool ok_to_add_assoc_with_opposite( 430static bool ok_to_add_assoc_with_opposite(
376 const game_state *state, space *tile, space *dot) 431 const game_state *state, space *tile, space *dot)
377{ 432{
378 space *opposite = space_opposite_dot(state, tile, dot); 433 space *opposite = space_opposite_dot(state, tile, dot);
379 return ok_to_add_assoc_with_opposite_internal(state, tile, opposite); 434 return ok_to_add_assoc_with_opposite_internal(state, tile, opposite);
380} 435}
436#endif
381 437
382static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) { 438static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
383 space *opposite = space_opposite_dot(state, tile, dot); 439 space *opposite = space_opposite_dot(state, tile, dot);
384 440
385 if(opposite) 441 if(opposite && ok_to_add_assoc_with_opposite_internal(
442 state, tile, opposite))
386 { 443 {
387 assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
388
389 remove_assoc_with_opposite(state, tile); 444 remove_assoc_with_opposite(state, tile);
390 add_assoc(state, tile, dot); 445 add_assoc(state, tile, dot);
391 remove_assoc_with_opposite(state, opposite); 446 remove_assoc_with_opposite(state, opposite);
@@ -393,12 +448,14 @@ static void add_assoc_with_opposite(game_state *state, space *tile, space *dot)
393 } 448 }
394} 449}
395 450
451#ifndef EDITOR
396static space *sp2dot(const game_state *state, int x, int y) 452static space *sp2dot(const game_state *state, int x, int y)
397{ 453{
398 space *sp = &SPACE(state, x, y); 454 space *sp = &SPACE(state, x, y);
399 if (!(sp->flags & F_TILE_ASSOC)) return NULL; 455 if (!(sp->flags & F_TILE_ASSOC)) return NULL;
400 return &SPACE(state, sp->dotx, sp->doty); 456 return &SPACE(state, sp->dotx, sp->doty);
401} 457}
458#endif
402 459
403#define IS_VERTICAL_EDGE(x) ((x % 2) == 0) 460#define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
404 461
@@ -407,8 +464,24 @@ static bool game_can_format_as_text_now(const game_params *params)
407 return true; 464 return true;
408} 465}
409 466
467static char *encode_game(const game_state *state);
468
410static char *game_text_format(const game_state *state) 469static char *game_text_format(const game_state *state)
411{ 470{
471#ifdef EDITOR
472 game_params par;
473 char *params, *desc, *ret;
474 par.w = state->w;
475 par.h = state->h;
476 par.diff = DIFF_MAX; /* shouldn't be used */
477 params = encode_params(&par, false);
478 desc = encode_game(state);
479 ret = snewn(strlen(params) + strlen(desc) + 2, char);
480 sprintf(ret, "%s:%s", params, desc);
481 sfree(params);
482 sfree(desc);
483 return ret;
484#else
412 int maxlen = (state->sx+1)*state->sy, x, y; 485 int maxlen = (state->sx+1)*state->sy, x, y;
413 char *ret, *p; 486 char *ret, *p;
414 space *sp; 487 space *sp;
@@ -462,6 +535,7 @@ static char *game_text_format(const game_state *state)
462 *p = '\0'; 535 *p = '\0';
463 536
464 return ret; 537 return ret;
538#endif
465} 539}
466 540
467static void dbg_state(const game_state *state) 541static void dbg_state(const game_state *state)
@@ -684,7 +758,7 @@ static void tiles_from_edge(game_state *state, space *sp, space **ts)
684/* Returns a move string for use by 'solve', including the initial 758/* Returns a move string for use by 'solve', including the initial
685 * 'S' if issolve is true. */ 759 * 'S' if issolve is true. */
686static char *diff_game(const game_state *src, const game_state *dest, 760static char *diff_game(const game_state *src, const game_state *dest,
687 bool issolve) 761 bool issolve, int set_cdiff)
688{ 762{
689 int movelen = 0, movesize = 256, x, y, len; 763 int movelen = 0, movesize = 256, x, y, len;
690 char *move = snewn(movesize, char), buf[80]; 764 char *move = snewn(movesize, char), buf[80];
@@ -698,6 +772,26 @@ static char *diff_game(const game_state *src, const game_state *dest,
698 move[movelen++] = 'S'; 772 move[movelen++] = 'S';
699 sep = ";"; 773 sep = ";";
700 } 774 }
775#ifdef EDITOR
776 if (set_cdiff >= 0) {
777 switch (set_cdiff) {
778 case DIFF_IMPOSSIBLE:
779 movelen += sprintf(move+movelen, "%sII", sep);
780 break;
781 case DIFF_AMBIGUOUS:
782 movelen += sprintf(move+movelen, "%sIA", sep);
783 break;
784 case DIFF_UNFINISHED:
785 movelen += sprintf(move+movelen, "%sIU", sep);
786 break;
787 default:
788 movelen += sprintf(move+movelen, "%si%c",
789 sep, galaxies_diffchars[set_cdiff]);
790 break;
791 }
792 sep = ";";
793 }
794#endif
701 move[movelen] = '\0'; 795 move[movelen] = '\0';
702 for (x = 0; x < src->sx; x++) { 796 for (x = 0; x < src->sx; x++) {
703 for (y = 0; y < src->sy; y++) { 797 for (y = 0; y < src->sy; y++) {
@@ -747,7 +841,8 @@ static char *diff_game(const game_state *src, const game_state *dest,
747 841
748/* Returns true if a dot here would not be too close to any other dots 842/* Returns true if a dot here would not be too close to any other dots
749 * (and would avoid other game furniture). */ 843 * (and would avoid other game furniture). */
750static bool dot_is_possible(game_state *state, space *sp, bool allow_assoc) 844static bool dot_is_possible(const game_state *state, space *sp,
845 bool allow_assoc)
751{ 846{
752 int bx = 0, by = 0, dx, dy; 847 int bx = 0, by = 0, dx, dy;
753 space *adj; 848 space *adj;
@@ -921,7 +1016,7 @@ static void free_game(game_state *state)
921 * an edit mode. 1016 * an edit mode.
922 */ 1017 */
923 1018
924static char *encode_game(game_state *state) 1019static char *encode_game(const game_state *state)
925{ 1020{
926 char *desc, *p; 1021 char *desc, *p;
927 int run, x, y, area; 1022 int run, x, y, area;
@@ -1229,10 +1324,7 @@ static bool generate_try_block(game_state *state, random_state *rs,
1229} 1324}
1230 1325
1231#ifdef STANDALONE_SOLVER 1326#ifdef STANDALONE_SOLVER
1232int maxtries; 1327static bool one_try; /* override for soak testing */
1233#define MAXTRIES maxtries
1234#else
1235#define MAXTRIES 50
1236#endif 1328#endif
1237 1329
1238#define GP_DOTS 1 1330#define GP_DOTS 1
@@ -1242,6 +1334,8 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
1242{ 1334{
1243 int sz = state->sx*state->sy, nspc, i, ret; 1335 int sz = state->sx*state->sy, nspc, i, ret;
1244 1336
1337 /* Random list of squares to try and process, one-by-one. */
1338 for (i = 0; i < sz; i++) scratch[i] = i;
1245 shuffle(scratch, sz, sizeof(int), rs); 1339 shuffle(scratch, sz, sizeof(int), rs);
1246 1340
1247 /* This bug took me a, er, little while to track down. On PalmOS, 1341 /* This bug took me a, er, little while to track down. On PalmOS,
@@ -1297,30 +1391,94 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
1297 dbg_state(state); 1391 dbg_state(state);
1298} 1392}
1299 1393
1394/*
1395 * We try several times to generate a grid at all, before even feeding
1396 * it to the solver. Then we pick whichever of the resulting grids was
1397 * the most 'wiggly', as measured by the number of inward corners in
1398 * the shape of any region.
1399 *
1400 * Rationale: wiggly shapes are what make this puzzle fun, and it's
1401 * disappointing to be served a game whose entire solution is a
1402 * collection of rectangles. But we also don't want to introduce a
1403 * _hard requirement_ of wiggliness, because a player who knew that
1404 * was there would be able to use it as an extra clue. This way, we
1405 * just probabilistically skew in favour of wiggliness.
1406 */
1407#define GENERATE_TRIES 10
1408
1409static bool is_wiggle(const game_state *state, int x, int y, int dx, int dy)
1410{
1411 int x1 = x+2*dx, y1 = y+2*dy;
1412 int x2 = x-2*dy, y2 = y+2*dx;
1413 space *t, *t1, *t2;
1414
1415 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
1416 return false;
1417
1418 t = &SPACE(state, x, y);
1419 t1 = &SPACE(state, x1, y1);
1420 t2 = &SPACE(state, x2, y2);
1421 return ((t1->dotx == t2->dotx && t1->doty == t2->doty) &&
1422 !(t1->dotx == t->dotx && t1->doty == t->doty));
1423}
1424
1425static int measure_wiggliness(const game_state *state, int *scratch)
1426{
1427 int sz = state->sx*state->sy;
1428 int x, y, nwiggles = 0;
1429 memset(scratch, 0, sz);
1430
1431 for (y = 1; y < state->sy; y += 2) {
1432 for (x = 1; x < state->sx; x += 2) {
1433 if (y+2 < state->sy) {
1434 nwiggles += is_wiggle(state, x, y, 0, +1);
1435 nwiggles += is_wiggle(state, x, y, 0, -1);
1436 nwiggles += is_wiggle(state, x, y, +1, 0);
1437 nwiggles += is_wiggle(state, x, y, -1, 0);
1438 }
1439 }
1440 }
1441
1442 return nwiggles;
1443}
1444
1300static char *new_game_desc(const game_params *params, random_state *rs, 1445static char *new_game_desc(const game_params *params, random_state *rs,
1301 char **aux, bool interactive) 1446 char **aux, bool interactive)
1302{ 1447{
1303 game_state *state = blank_game(params->w, params->h), *copy; 1448 game_state *state = blank_game(params->w, params->h), *copy;
1304 char *desc; 1449 char *desc;
1305 int *scratch, sz = state->sx*state->sy, i; 1450 int *scratch, sz = state->sx*state->sy, i;
1306 int diff, ntries = 0; 1451 int diff, best_wiggliness;
1307 bool cc; 1452 bool cc;
1308 1453
1309 /* Random list of squares to try and process, one-by-one. */
1310 scratch = snewn(sz, int); 1454 scratch = snewn(sz, int);
1311 for (i = 0; i < sz; i++) scratch[i] = i;
1312 1455
1313generate: 1456generate:
1314 clear_game(state, true); 1457 best_wiggliness = -1;
1315 ntries++; 1458 copy = NULL;
1316 1459 for (i = 0; i < GENERATE_TRIES; i++) {
1317 /* generate_pass(state, rs, scratch, 10, GP_DOTS); */ 1460 int this_wiggliness;
1318 /* generate_pass(state, rs, scratch, 100, 0); */ 1461
1319 generate_pass(state, rs, scratch, 100, GP_DOTS); 1462 do {
1320 1463 clear_game(state, true);
1321 game_update_dots(state); 1464 generate_pass(state, rs, scratch, 100, GP_DOTS);
1322 1465 game_update_dots(state);
1323 if (state->ndots == 1) goto generate; 1466 } while (state->ndots == 1);
1467
1468 this_wiggliness = measure_wiggliness(state, scratch);
1469 debug(("Grid gen #%d: wiggliness=%d", i, this_wiggliness));
1470 if (this_wiggliness > best_wiggliness) {
1471 best_wiggliness = this_wiggliness;
1472 if (copy)
1473 free_game(copy);
1474 copy = dup_game(state);
1475 debug((" new best"));
1476 }
1477 debug(("\n"));
1478 }
1479 assert(copy);
1480 free_game(state);
1481 state = copy;
1324 1482
1325#ifdef DEBUGGING 1483#ifdef DEBUGGING
1326 { 1484 {
@@ -1345,12 +1503,17 @@ generate:
1345 assert(diff != DIFF_IMPOSSIBLE); 1503 assert(diff != DIFF_IMPOSSIBLE);
1346 if (diff != params->diff) { 1504 if (diff != params->diff) {
1347 /* 1505 /*
1348 * We'll grudgingly accept a too-easy puzzle, but we must 1506 * If the puzzle was insoluble at this difficulty level (i.e.
1349 * _not_ permit a too-hard one (one which the solver 1507 * too hard), _or_ soluble at a lower level (too easy), go
1350 * couldn't handle at all). 1508 * round again.
1509 *
1510 * An exception is in soak-testing mode, where we return the
1511 * first puzzle we got regardless.
1351 */ 1512 */
1352 if (diff > params->diff || 1513#ifdef STANDALONE_SOLVER
1353 ntries < MAXTRIES) goto generate; 1514 if (!one_try)
1515#endif
1516 goto generate;
1354 } 1517 }
1355 1518
1356#ifdef STANDALONE_PICTURE_GENERATOR 1519#ifdef STANDALONE_PICTURE_GENERATOR
@@ -1527,6 +1690,10 @@ generate:
1527 dbg_state(state); 1690 dbg_state(state);
1528#endif 1691#endif
1529 1692
1693 game_state *blank = blank_game(params->w, params->h);
1694 *aux = diff_game(blank, state, true, -1);
1695 free_game(blank);
1696
1530 free_game(state); 1697 free_game(state);
1531 sfree(scratch); 1698 sfree(scratch);
1532 1699
@@ -1623,13 +1790,17 @@ static game_state *new_game(midend *me, const game_params *params,
1623 * Solver and all its little wizards. 1790 * Solver and all its little wizards.
1624 */ 1791 */
1625 1792
1793#if defined DEBUGGING || defined STANDALONE_SOLVER
1626static int solver_recurse_depth; 1794static int solver_recurse_depth;
1795#define STATIC_RECURSION_DEPTH
1796#endif
1627 1797
1628typedef struct solver_ctx { 1798typedef struct solver_ctx {
1629 game_state *state; 1799 game_state *state;
1630 int sz; /* state->sx * state->sy */ 1800 int sz; /* state->sx * state->sy */
1631 space **scratch; /* size sz */ 1801 space **scratch; /* size sz */
1632 1802 DSF *dsf; /* size sz */
1803 int *iscratch; /* size sz */
1633} solver_ctx; 1804} solver_ctx;
1634 1805
1635static solver_ctx *new_solver(game_state *state) 1806static solver_ctx *new_solver(game_state *state)
@@ -1638,12 +1809,16 @@ static solver_ctx *new_solver(game_state *state)
1638 sctx->state = state; 1809 sctx->state = state;
1639 sctx->sz = state->sx*state->sy; 1810 sctx->sz = state->sx*state->sy;
1640 sctx->scratch = snewn(sctx->sz, space *); 1811 sctx->scratch = snewn(sctx->sz, space *);
1812 sctx->dsf = dsf_new(sctx->sz);
1813 sctx->iscratch = snewn(sctx->sz, int);
1641 return sctx; 1814 return sctx;
1642} 1815}
1643 1816
1644static void free_solver(solver_ctx *sctx) 1817static void free_solver(solver_ctx *sctx)
1645{ 1818{
1646 sfree(sctx->scratch); 1819 sfree(sctx->scratch);
1820 dsf_free(sctx->dsf);
1821 sfree(sctx->iscratch);
1647 sfree(sctx); 1822 sfree(sctx);
1648} 1823}
1649 1824
@@ -1804,7 +1979,7 @@ static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx)
1804 if (!(edge_opp->flags & F_EDGE_SET)) { 1979 if (!(edge_opp->flags & F_EDGE_SET)) {
1805 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n", 1980 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1806 solver_recurse_depth*4, "", 1981 solver_recurse_depth*4, "",
1807 tile_opp->x-dx, tile_opp->y-dy, edge->x, edge->y)); 1982 tile_opp->x+dx, tile_opp->y+dy, edge->x, edge->y));
1808 edge_opp->flags |= F_EDGE_SET; 1983 edge_opp->flags |= F_EDGE_SET;
1809 didsth = 1; 1984 didsth = 1;
1810 } 1985 }
@@ -2097,6 +2272,177 @@ static int solver_expand_dots(game_state *state, solver_ctx *sctx)
2097 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx); 2272 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx);
2098} 2273}
2099 2274
2275static int solver_extend_exclaves(game_state *state, solver_ctx *sctx)
2276{
2277 int x, y, done_something = 0;
2278
2279 /*
2280 * Make a dsf by unifying any two adjacent tiles associated with
2281 * the same dot. This will identify separate connected components
2282 * of the tiles belonging to a given dot. Any such component that
2283 * doesn't contain its own dot is an 'exclave', and will need some
2284 * kind of path of tiles to connect it back to the dot.
2285 */
2286 dsf_reinit(sctx->dsf);
2287 for (x = 1; x < state->sx; x += 2) {
2288 for (y = 1; y < state->sy; y += 2) {
2289 int dotx, doty;
2290 space *tile, *othertile;
2291
2292 tile = &SPACE(state, x, y);
2293 if (!(tile->flags & F_TILE_ASSOC))
2294 continue; /* not associated with any dot */
2295 dotx = tile->dotx;
2296 doty = tile->doty;
2297
2298 if (INGRID(state, x+2, y)) {
2299 othertile = &SPACE(state, x+2, y);
2300 if ((othertile->flags & F_TILE_ASSOC) &&
2301 othertile->dotx == dotx && othertile->doty == doty)
2302 dsf_merge(sctx->dsf, y*state->sx+x, y*state->sx+(x+2));
2303 }
2304
2305 if (INGRID(state, x, y+2)) {
2306 othertile = &SPACE(state, x, y+2);
2307 if ((othertile->flags & F_TILE_ASSOC) &&
2308 othertile->dotx == dotx && othertile->doty == doty)
2309 dsf_merge(sctx->dsf, y*state->sx+x, (y+2)*state->sx+x);
2310 }
2311 }
2312 }
2313
2314 /*
2315 * Go through the grid again, and count the 'liberties' of each
2316 * connected component, in the Go sense, i.e. the number of
2317 * currently unassociated squares adjacent to the component. The
2318 * idea is that if an exclave has just one liberty, then that
2319 * square _must_ extend the exclave, or else it will be completely
2320 * cut off from connecting back to its home dot.
2321 *
2322 * We need to count each adjacent square just once, even if it
2323 * borders the component on multiple edges. So we'll go through
2324 * each unassociated square, check all four of its neighbours, and
2325 * de-duplicate them.
2326 *
2327 * We'll store the count of liberties in the entry of iscratch
2328 * corresponding to the square centre (i.e. with odd coordinates).
2329 * Every time we find a liberty, we store its index in the square
2330 * to the left of that, so that when a component has exactly one
2331 * liberty we can remember what it was.
2332 *
2333 * Square centres that are not the canonical dsf element of a
2334 * connected component will get their liberty count set to -1,
2335 * which will allow us to identify them in the later loop (after
2336 * we start making changes and need to spot that an associated
2337 * square _now_ was not associated when the dsf was built).
2338 */
2339
2340 /* Initialise iscratch */
2341 for (x = 1; x < state->sx; x += 2) {
2342 for (y = 1; y < state->sy; y += 2) {
2343 int index = y * state->sx + x;
2344 if (!(SPACE(state, x, y).flags & F_TILE_ASSOC) ||
2345 dsf_canonify(sctx->dsf, index) != index) {
2346 sctx->iscratch[index] = -1; /* mark as not a component */
2347 } else {
2348 sctx->iscratch[index] = 0; /* zero liberty count */
2349 sctx->iscratch[index-1] = 0; /* initialise neighbour id */
2350 }
2351 }
2352 }
2353
2354 /* Find each unassociated square and see what it's a liberty of */
2355 for (x = 1; x < state->sx; x += 2) {
2356 for (y = 1; y < state->sy; y += 2) {
2357 int dx, dy, ni[4], nn, i;
2358
2359 if ((SPACE(state, x, y).flags & F_TILE_ASSOC))
2360 continue; /* not an unassociated square */
2361
2362 /* Find distinct indices of adjacent components */
2363 nn = 0;
2364 for (dx = -1; dx <= 1; dx++) {
2365 for (dy = -1; dy <= 1; dy++) {
2366 if (dx != 0 && dy != 0) continue;
2367 if (dx == 0 && dy == 0) continue;
2368
2369 if (INGRID(state, x+2*dx, y+2*dy) &&
2370 (SPACE(state, x+2*dx, y+2*dy).flags & F_TILE_ASSOC)) {
2371 /* Find id of the component adjacent to x,y */
2372 int nindex = (y+2*dy) * state->sx + (x+2*dx);
2373 nindex = dsf_canonify(sctx->dsf, nindex);
2374
2375 /* See if we've seen it before in another direction */
2376 for (i = 0; i < nn; i++)
2377 if (ni[i] == nindex)
2378 break;
2379 if (i == nn) {
2380 /* No, it's new. Mark x,y as a liberty of it */
2381 sctx->iscratch[nindex]++;
2382 assert(nindex > 0);
2383 sctx->iscratch[nindex-1] = y * state->sx + x;
2384
2385 /* And record this component as having been seen */
2386 ni[nn++] = nindex;
2387 }
2388 }
2389 }
2390 }
2391 }
2392 }
2393
2394 /*
2395 * Now we have all the data we need to find exclaves with exactly
2396 * one liberty. In each case, associate the unique liberty square
2397 * with the same dot.
2398 */
2399 for (x = 1; x < state->sx; x += 2) {
2400 for (y = 1; y < state->sy; y += 2) {
2401 int index, dotx, doty, ex, ey, added;
2402 space *tile;
2403
2404 index = y*state->sx+x;
2405 if (sctx->iscratch[index] == -1)
2406 continue; /* wasn't canonical when dsf was built */
2407
2408 tile = &SPACE(state, x, y);
2409 if (!(tile->flags & F_TILE_ASSOC))
2410 continue; /* not associated with any dot */
2411 dotx = tile->dotx;
2412 doty = tile->doty;
2413
2414 if (index == dsf_canonify(
2415 sctx->dsf, (doty | 1) * state->sx + (dotx | 1)))
2416 continue; /* not an exclave - contains its own dot */
2417
2418 if (sctx->iscratch[index] == 0) {
2419 solvep(("%*sExclave at %d,%d has no liberties!\n",
2420 solver_recurse_depth*4, "", x, y));
2421 return -1;
2422 }
2423
2424 if (sctx->iscratch[index] != 1)
2425 continue; /* more than one liberty, can't be sure which */
2426
2427 assert(sctx->iscratch[index-1] != 0);
2428 ex = sctx->iscratch[index-1] % state->sx;
2429 ey = sctx->iscratch[index-1] / state->sx;
2430 tile = &SPACE(state, ex, ey);
2431 if (tile->flags & F_TILE_ASSOC)
2432 continue; /* already done by earlier iteration of this loop */
2433
2434 added = solver_add_assoc(state, tile, dotx, doty,
2435 "to connect exclave");
2436 if (added < 0)
2437 return -1;
2438 if (added > 0)
2439 done_something = 1;
2440 }
2441 }
2442
2443 return done_something;
2444}
2445
2100struct recurse_ctx { 2446struct recurse_ctx {
2101 space *best; 2447 space *best;
2102 int bestn; 2448 int bestn;
@@ -2124,14 +2470,14 @@ static int solver_recurse_cb(game_state *state, space *tile, void *ctx)
2124 2470
2125#define MAXRECURSE 5 2471#define MAXRECURSE 5
2126 2472
2127static int solver_recurse(game_state *state, int maxdiff) 2473static int solver_recurse(game_state *state, int maxdiff, int depth)
2128{ 2474{
2129 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy; 2475 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy;
2130 space *ingrid, *outgrid = NULL, *bestopp; 2476 space *ingrid, *outgrid = NULL, *bestopp;
2131 struct recurse_ctx rctx; 2477 struct recurse_ctx rctx;
2132 2478
2133 if (solver_recurse_depth >= MAXRECURSE) { 2479 if (depth >= MAXRECURSE) {
2134 solvep(("Limiting recursion to %d, returning.", MAXRECURSE)); 2480 solvep(("Limiting recursion to %d, returning.\n", MAXRECURSE));
2135 return DIFF_UNFINISHED; 2481 return DIFF_UNFINISHED;
2136 } 2482 }
2137 2483
@@ -2149,10 +2495,6 @@ static int solver_recurse(game_state *state, int maxdiff)
2149 solver_recurse_depth*4, "", 2495 solver_recurse_depth*4, "",
2150 rctx.best->x, rctx.best->y, rctx.bestn)); 2496 rctx.best->x, rctx.best->y, rctx.bestn));
2151 2497
2152#ifdef STANDALONE_SOLVER
2153 solver_recurse_depth++;
2154#endif
2155
2156 ingrid = snewn(gsz, space); 2498 ingrid = snewn(gsz, space);
2157 memcpy(ingrid, state->grid, gsz * sizeof(space)); 2499 memcpy(ingrid, state->grid, gsz * sizeof(space));
2158 2500
@@ -2166,7 +2508,11 @@ static int solver_recurse(game_state *state, int maxdiff)
2166 state->dots[n]->x, state->dots[n]->y, 2508 state->dots[n]->x, state->dots[n]->y,
2167 "Attempting for recursion"); 2509 "Attempting for recursion");
2168 2510
2169 ret = solver_state(state, maxdiff); 2511 ret = solver_state_inner(state, maxdiff, depth + 1);
2512
2513#ifdef STATIC_RECURSION_DEPTH
2514 solver_recurse_depth = depth; /* restore after recursion returns */
2515#endif
2170 2516
2171 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) { 2517 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
2172 /* we found our first solved grid; copy it away. */ 2518 /* we found our first solved grid; copy it away. */
@@ -2198,10 +2544,6 @@ static int solver_recurse(game_state *state, int maxdiff)
2198 break; 2544 break;
2199 } 2545 }
2200 2546
2201#ifdef STANDALONE_SOLVER
2202 solver_recurse_depth--;
2203#endif
2204
2205 if (outgrid) { 2547 if (outgrid) {
2206 /* we found (at least one) soln; copy it back to state */ 2548 /* we found (at least one) soln; copy it back to state */
2207 memcpy(state->grid, outgrid, gsz * sizeof(space)); 2549 memcpy(state->grid, outgrid, gsz * sizeof(space));
@@ -2211,7 +2553,7 @@ static int solver_recurse(game_state *state, int maxdiff)
2211 return diff; 2553 return diff;
2212} 2554}
2213 2555
2214static int solver_state(game_state *state, int maxdiff) 2556static int solver_state_inner(game_state *state, int maxdiff, int depth)
2215{ 2557{
2216 solver_ctx *sctx = new_solver(state); 2558 solver_ctx *sctx = new_solver(state);
2217 int ret, diff = DIFF_NORMAL; 2559 int ret, diff = DIFF_NORMAL;
@@ -2223,6 +2565,10 @@ static int solver_state(game_state *state, int maxdiff)
2223 picture = NULL; 2565 picture = NULL;
2224#endif 2566#endif
2225 2567
2568#ifdef STATIC_RECURSION_DEPTH
2569 solver_recurse_depth = depth;
2570#endif
2571
2226 ret = solver_obvious(state); 2572 ret = solver_obvious(state);
2227 if (ret < 0) { 2573 if (ret < 0) {
2228 diff = DIFF_IMPOSSIBLE; 2574 diff = DIFF_IMPOSSIBLE;
@@ -2247,6 +2593,9 @@ cont:
2247 ret = solver_expand_dots(state, sctx); 2593 ret = solver_expand_dots(state, sctx);
2248 CHECKRET(DIFF_NORMAL); 2594 CHECKRET(DIFF_NORMAL);
2249 2595
2596 ret = solver_extend_exclaves(state, sctx);
2597 CHECKRET(DIFF_NORMAL);
2598
2250 if (maxdiff <= DIFF_NORMAL) 2599 if (maxdiff <= DIFF_NORMAL)
2251 break; 2600 break;
2252 2601
@@ -2259,7 +2608,7 @@ cont:
2259 if (check_complete(state, NULL, NULL)) goto got_result; 2608 if (check_complete(state, NULL, NULL)) goto got_result;
2260 2609
2261 diff = (maxdiff >= DIFF_UNREASONABLE) ? 2610 diff = (maxdiff >= DIFF_UNREASONABLE) ?
2262 solver_recurse(state, maxdiff) : DIFF_UNFINISHED; 2611 solver_recurse(state, maxdiff, depth) : DIFF_UNFINISHED;
2263 2612
2264got_result: 2613got_result:
2265 free_solver(sctx); 2614 free_solver(sctx);
@@ -2275,6 +2624,11 @@ got_result:
2275 return diff; 2624 return diff;
2276} 2625}
2277 2626
2627static int solver_state(game_state *state, int maxdiff)
2628{
2629 return solver_state_inner(state, maxdiff, 0);
2630}
2631
2278#ifndef EDITOR 2632#ifndef EDITOR
2279static char *solve_game(const game_state *state, const game_state *currstate, 2633static char *solve_game(const game_state *state, const game_state *currstate,
2280 const char *aux, const char **error) 2634 const char *aux, const char **error)
@@ -2284,21 +2638,26 @@ static char *solve_game(const game_state *state, const game_state *currstate,
2284 int i; 2638 int i;
2285 int diff; 2639 int diff;
2286 2640
2287 tosolve = dup_game(currstate); 2641 if (aux) {
2288 diff = solver_state(tosolve, DIFF_UNREASONABLE); 2642 tosolve = execute_move(state, aux);
2289 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2290 debug(("solve_game solved with current state.\n"));
2291 goto solved; 2643 goto solved;
2292 } 2644 } else {
2293 free_game(tosolve); 2645 tosolve = dup_game(currstate);
2646 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2647 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2648 debug(("solve_game solved with current state.\n"));
2649 goto solved;
2650 }
2651 free_game(tosolve);
2294 2652
2295 tosolve = dup_game(state); 2653 tosolve = dup_game(state);
2296 diff = solver_state(tosolve, DIFF_UNREASONABLE); 2654 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2297 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) { 2655 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2298 debug(("solve_game solved with original state.\n")); 2656 debug(("solve_game solved with original state.\n"));
2299 goto solved; 2657 goto solved;
2658 }
2659 free_game(tosolve);
2300 } 2660 }
2301 free_game(tosolve);
2302 2661
2303 return NULL; 2662 return NULL;
2304 2663
@@ -2309,7 +2668,7 @@ solved:
2309 */ 2668 */
2310 for (i = 0; i < tosolve->sx*tosolve->sy; i++) 2669 for (i = 0; i < tosolve->sx*tosolve->sy; i++)
2311 tosolve->grid[i].flags &= ~F_TILE_ASSOC; 2670 tosolve->grid[i].flags &= ~F_TILE_ASSOC;
2312 ret = diff_game(currstate, tosolve, true); 2671 ret = diff_game(currstate, tosolve, true, -1);
2313 free_game(tosolve); 2672 free_game(tosolve);
2314 return ret; 2673 return ret;
2315} 2674}
@@ -2333,7 +2692,7 @@ static game_ui *new_ui(const game_state *state)
2333 game_ui *ui = snew(game_ui); 2692 game_ui *ui = snew(game_ui);
2334 ui->dragging = false; 2693 ui->dragging = false;
2335 ui->cur_x = ui->cur_y = 1; 2694 ui->cur_x = ui->cur_y = 1;
2336 ui->cur_visible = false; 2695 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2337 return ui; 2696 return ui;
2338} 2697}
2339 2698
@@ -2342,15 +2701,6 @@ static void free_ui(game_ui *ui)
2342 sfree(ui); 2701 sfree(ui);
2343} 2702}
2344 2703
2345static char *encode_ui(const game_ui *ui)
2346{
2347 return NULL;
2348}
2349
2350static void decode_ui(game_ui *ui, const char *encoding)
2351{
2352}
2353
2354static void game_changed_state(game_ui *ui, const game_state *oldstate, 2704static void game_changed_state(game_ui *ui, const game_state *oldstate,
2355 const game_state *newstate) 2705 const game_state *newstate)
2356{ 2706{
@@ -2383,7 +2733,7 @@ struct game_drawstate {
2383 blitter *blmirror; 2733 blitter *blmirror;
2384 2734
2385 bool dragging; 2735 bool dragging;
2386 int dragx, dragy; 2736 int dragx, dragy, oppx, oppy;
2387 2737
2388 int *colour_scratch; 2738 int *colour_scratch;
2389 2739
@@ -2445,34 +2795,74 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2445 int px, py; 2795 int px, py;
2446 space *sp; 2796 space *sp;
2447 2797
2448 px = 2*FROMCOORD((float)x) + 0.5; 2798 px = 2*FROMCOORD((float)x) + 0.5F;
2449 py = 2*FROMCOORD((float)y) + 0.5; 2799 py = 2*FROMCOORD((float)y) + 0.5F;
2450
2451 state->cdiff = -1;
2452 2800
2453 if (button == 'C' || button == 'c') return dupstr("C"); 2801 if (button == 'C' || button == 'c') return dupstr("C");
2454 2802
2455 if (button == 'S' || button == 's') { 2803 if (button == 'S' || button == 's') {
2456 char *ret; 2804 char *ret;
2457 game_state *tmp = dup_game(state); 2805 game_state *tmp = dup_game(state);
2458 state->cdiff = solver_state(tmp, DIFF_UNREASONABLE-1); 2806 int cdiff = solver_state(tmp, DIFF_UNREASONABLE-1);
2459 ret = diff_game(state, tmp, 0); 2807 ret = diff_game(state, tmp, 0, cdiff);
2460 free_game(tmp); 2808 free_game(tmp);
2461 return ret; 2809 return ret;
2462 } 2810 }
2463 2811
2464 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2812 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2465 if (!INUI(state, px, py)) return NULL; 2813 if (!INUI(state, px, py)) return MOVE_UNUSED;
2466 sp = &SPACE(state, px, py); 2814 sp = &SPACE(state, px, py);
2467 if (!dot_is_possible(state, sp, 1)) return NULL; 2815 if (!dot_is_possible(state, sp, 1)) return MOVE_NO_EFFECT;
2468 sprintf(buf, "%c%d,%d", 2816 sprintf(buf, "%c%d,%d",
2469 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py); 2817 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py);
2470 return dupstr(buf); 2818 return dupstr(buf);
2471 } 2819 }
2472 2820
2473 return NULL; 2821 return MOVE_UNUSED;
2474} 2822}
2475#else 2823#else
2824static bool edge_placement_legal(const game_state *state, int x, int y)
2825{
2826 space *sp = &SPACE(state, x, y);
2827 if (sp->type != s_edge)
2828 return false; /* this is a face-centre or a grid vertex */
2829
2830 /* Check this line doesn't actually intersect a dot */
2831 unsigned int flags = (GRID(state, grid, x, y).flags |
2832 GRID(state, grid, x & ~1U, y & ~1U).flags |
2833 GRID(state, grid, (x+1) & ~1U, (y+1) & ~1U).flags);
2834 if (flags & F_DOT)
2835 return false;
2836 return true;
2837}
2838
2839static const char *current_key_label(const game_ui *ui,
2840 const game_state *state, int button)
2841{
2842 space *sp;
2843
2844 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
2845 sp = &SPACE(state, ui->cur_x, ui->cur_y);
2846 if (ui->dragging) {
2847 if (ui->cur_x == ui->srcx && ui->cur_y == ui->srcy)
2848 return "Cancel";
2849 if (ok_to_add_assoc_with_opposite(
2850 state, &SPACE(state, ui->cur_x, ui->cur_y),
2851 &SPACE(state, ui->dotx, ui->doty)))
2852 return "Place";
2853 return (ui->srcx == ui->dotx && ui->srcy == ui->doty) ?
2854 "Cancel" : "Remove";
2855 } else if (sp->flags & F_DOT)
2856 return "New arrow";
2857 else if (sp->flags & F_TILE_ASSOC)
2858 return "Move arrow";
2859 else if (sp->type == s_edge &&
2860 edge_placement_legal(state, ui->cur_x, ui->cur_y))
2861 return (sp->flags & F_EDGE_SET) ? "Clear" : "Edge";
2862 }
2863 return "";
2864}
2865
2476static char *interpret_move(const game_state *state, game_ui *ui, 2866static char *interpret_move(const game_state *state, game_ui *ui,
2477 const game_drawstate *ds, 2867 const game_drawstate *ds,
2478 int x, int y, int button) 2868 int x, int y, int button)
@@ -2499,7 +2889,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2499 char *ret; 2889 char *ret;
2500 game_state *tmp = dup_game(state); 2890 game_state *tmp = dup_game(state);
2501 solver_obvious(tmp); 2891 solver_obvious(tmp);
2502 ret = diff_game(state, tmp, false); 2892 ret = diff_game(state, tmp, false, -1);
2503 free_game(tmp); 2893 free_game(tmp);
2504 return ret; 2894 return ret;
2505 } 2895 }
@@ -2509,21 +2899,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2509 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y), 2899 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y),
2510 &px, &py); 2900 &px, &py);
2511 2901
2512 if (!INUI(state, px, py)) return NULL; 2902 if (!INUI(state, px, py)) return MOVE_UNUSED;
2903 if (!edge_placement_legal(state, px, py))
2904 return MOVE_NO_EFFECT;
2513 2905
2514 sp = &SPACE(state, px, py); 2906 sprintf(buf, "E%d,%d", px, py);
2515 assert(sp->type == s_edge); 2907 return dupstr(buf);
2516 {
2517 sprintf(buf, "E%d,%d", px, py);
2518 return dupstr(buf);
2519 }
2520 } else if (button == RIGHT_BUTTON) { 2908 } else if (button == RIGHT_BUTTON) {
2521 int px1, py1; 2909 int px1, py1;
2522 2910
2523 ui->cur_visible = false; 2911 ui->cur_visible = false;
2524 2912
2525 px = (int)(2*FROMCOORD((float)x) + 0.5); 2913 px = (int)(2*FROMCOORD((float)x) + 0.5F);
2526 py = (int)(2*FROMCOORD((float)y) + 0.5); 2914 py = (int)(2*FROMCOORD((float)y) + 0.5F);
2527 2915
2528 dot = NULL; 2916 dot = NULL;
2529 2917
@@ -2575,28 +2963,29 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2575 ui->dy = y; 2963 ui->dy = y;
2576 ui->dotx = dot->x; 2964 ui->dotx = dot->x;
2577 ui->doty = dot->y; 2965 ui->doty = dot->y;
2578 return UI_UPDATE; 2966 return MOVE_UI_UPDATE;
2579 } 2967 }
2968 return MOVE_NO_EFFECT;
2580 } else if (button == RIGHT_DRAG && ui->dragging) { 2969 } else if (button == RIGHT_DRAG && ui->dragging) {
2581 /* just move the drag coords. */ 2970 /* just move the drag coords. */
2582 ui->dx = x; 2971 ui->dx = x;
2583 ui->dy = y; 2972 ui->dy = y;
2584 return UI_UPDATE; 2973 return MOVE_UI_UPDATE;
2585 } else if (button == RIGHT_RELEASE && ui->dragging) { 2974 } else if (button == RIGHT_RELEASE && ui->dragging) {
2586 ui->dragging = false;
2587
2588 /* 2975 /*
2589 * Drags are always targeted at a single square. 2976 * Drags are always targeted at a single square.
2590 */ 2977 */
2591 px = 2*FROMCOORD(x+TILE_SIZE) - 1; 2978 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2592 py = 2*FROMCOORD(y+TILE_SIZE) - 1; 2979 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2593 2980
2981 dropped: /* We arrive here from the end of a keyboard drag. */
2982 ui->dragging = false;
2594 /* 2983 /*
2595 * Dragging an arrow on to the same square it started from 2984 * Dragging an arrow on to the same square it started from
2596 * is a null move; just update the ui and finish. 2985 * is a null move; just update the ui and finish.
2597 */ 2986 */
2598 if (px == ui->srcx && py == ui->srcy) 2987 if (px == ui->srcx && py == ui->srcy)
2599 return UI_UPDATE; 2988 return MOVE_UI_UPDATE;
2600 2989
2601 /* 2990 /*
2602 * Otherwise, we remove the arrow from its starting 2991 * Otherwise, we remove the arrow from its starting
@@ -2630,43 +3019,33 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2630 if (buf[0]) 3019 if (buf[0])
2631 return dupstr(buf); 3020 return dupstr(buf);
2632 else 3021 else
2633 return UI_UPDATE; 3022 return MOVE_UI_UPDATE;
2634 } else if (IS_CURSOR_MOVE(button)) { 3023 } else if (IS_CURSOR_MOVE(button)) {
2635 move_cursor(button, &ui->cur_x, &ui->cur_y, state->sx-1, state->sy-1, false); 3024 int cx = ui->cur_x - 1, cy = ui->cur_y - 1;
2636 if (ui->cur_x < 1) ui->cur_x = 1; 3025 char *ret = move_cursor(button, &cx, &cy, state->sx-2, state->sy-2,
2637 if (ui->cur_y < 1) ui->cur_y = 1; 3026 false, &ui->cur_visible);
2638 ui->cur_visible = true; 3027 ui->cur_x = cx + 1, ui->cur_y = cy + 1;
2639 if (ui->dragging) { 3028 if (ui->dragging) {
2640 ui->dx = SCOORD(ui->cur_x); 3029 ui->dx = SCOORD(ui->cur_x);
2641 ui->dy = SCOORD(ui->cur_y); 3030 ui->dy = SCOORD(ui->cur_y);
2642 } 3031 }
2643 return UI_UPDATE; 3032 return ret;
2644 } else if (IS_CURSOR_SELECT(button)) { 3033 } else if (IS_CURSOR_SELECT(button)) {
2645 if (!ui->cur_visible) { 3034 if (!ui->cur_visible) {
2646 ui->cur_visible = true; 3035 ui->cur_visible = true;
2647 return UI_UPDATE; 3036 return MOVE_UI_UPDATE;
2648 } 3037 }
2649 sp = &SPACE(state, ui->cur_x, ui->cur_y); 3038 sp = &SPACE(state, ui->cur_x, ui->cur_y);
2650 if (ui->dragging) { 3039 if (ui->dragging) {
2651 ui->dragging = false; 3040 px = ui->cur_x; py = ui->cur_y;
2652 3041 goto dropped;
2653 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2654 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2655 sprintf(buf, "%sU%d,%d", sep, ui->srcx, ui->srcy);
2656 sep = ";";
2657 }
2658 if (sp->type == s_tile && !(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) {
2659 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
2660 sep, ui->cur_x, ui->cur_y, ui->dotx, ui->doty);
2661 }
2662 return dupstr(buf);
2663 } else if (sp->flags & F_DOT) { 3042 } else if (sp->flags & F_DOT) {
2664 ui->dragging = true; 3043 ui->dragging = true;
2665 ui->dx = SCOORD(ui->cur_x); 3044 ui->dx = SCOORD(ui->cur_x);
2666 ui->dy = SCOORD(ui->cur_y); 3045 ui->dy = SCOORD(ui->cur_y);
2667 ui->dotx = ui->srcx = ui->cur_x; 3046 ui->dotx = ui->srcx = ui->cur_x;
2668 ui->doty = ui->srcy = ui->cur_y; 3047 ui->doty = ui->srcy = ui->cur_y;
2669 return UI_UPDATE; 3048 return MOVE_UI_UPDATE;
2670 } else if (sp->flags & F_TILE_ASSOC) { 3049 } else if (sp->flags & F_TILE_ASSOC) {
2671 assert(sp->type == s_tile); 3050 assert(sp->type == s_tile);
2672 ui->dragging = true; 3051 ui->dragging = true;
@@ -2676,18 +3055,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2676 ui->doty = sp->doty; 3055 ui->doty = sp->doty;
2677 ui->srcx = ui->cur_x; 3056 ui->srcx = ui->cur_x;
2678 ui->srcy = ui->cur_y; 3057 ui->srcy = ui->cur_y;
2679 return UI_UPDATE; 3058 return MOVE_UI_UPDATE;
2680 } else if (sp->type == s_edge) { 3059 } else if (sp->type == s_edge &&
3060 edge_placement_legal(state, ui->cur_x, ui->cur_y)) {
2681 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y); 3061 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y);
2682 return dupstr(buf); 3062 return dupstr(buf);
2683 } 3063 }
2684 } 3064 }
2685 3065
2686 return NULL; 3066 return MOVE_UNUSED;
2687} 3067}
2688#endif 3068#endif
2689 3069
2690static bool check_complete(const game_state *state, int *dsf, int *colours) 3070static bool check_complete(const game_state *state, DSF *dsf, int *colours)
2691{ 3071{
2692 int w = state->w, h = state->h; 3072 int w = state->w, h = state->h;
2693 int x, y, i; 3073 int x, y, i;
@@ -2702,10 +3082,10 @@ static bool check_complete(const game_state *state, int *dsf, int *colours)
2702 } *sqdata; 3082 } *sqdata;
2703 3083
2704 if (!dsf) { 3084 if (!dsf) {
2705 dsf = snew_dsf(w*h); 3085 dsf = dsf_new(w*h);
2706 free_dsf = true; 3086 free_dsf = true;
2707 } else { 3087 } else {
2708 dsf_init(dsf, w*h); 3088 dsf_reinit(dsf);
2709 free_dsf = false; 3089 free_dsf = false;
2710 } 3090 }
2711 3091
@@ -2861,7 +3241,7 @@ static bool check_complete(const game_state *state, int *dsf, int *colours)
2861 3241
2862 sfree(sqdata); 3242 sfree(sqdata);
2863 if (free_dsf) 3243 if (free_dsf)
2864 sfree(dsf); 3244 dsf_free(dsf);
2865 3245
2866 return ret; 3246 return ret;
2867} 3247}
@@ -2959,6 +3339,35 @@ static game_state *execute_move(const game_state *state, const char *move)
2959 } else if (c == 'C') { 3339 } else if (c == 'C') {
2960 move++; 3340 move++;
2961 clear_game(ret, true); 3341 clear_game(ret, true);
3342 } else if (c == 'i') {
3343 int diff;
3344 move++;
3345 for (diff = 0; diff <= DIFF_UNREASONABLE; diff++)
3346 if (*move == galaxies_diffchars[diff])
3347 break;
3348 if (diff > DIFF_UNREASONABLE)
3349 goto badmove;
3350
3351 ret->cdiff = diff;
3352 move++;
3353 } else if (c == 'I') {
3354 int diff;
3355 move++;
3356 switch (*move) {
3357 case 'A':
3358 diff = DIFF_AMBIGUOUS;
3359 break;
3360 case 'I':
3361 diff = DIFF_IMPOSSIBLE;
3362 break;
3363 case 'U':
3364 diff = DIFF_UNFINISHED;
3365 break;
3366 default:
3367 goto badmove;
3368 }
3369 ret->cdiff = diff;
3370 move++;
2962#endif 3371#endif
2963 } else if (c == 'S') { 3372 } else if (c == 'S') {
2964 move++; 3373 move++;
@@ -2997,7 +3406,7 @@ badmove:
2997 */ 3406 */
2998 3407
2999static void game_compute_size(const game_params *params, int sz, 3408static void game_compute_size(const game_params *params, int sz,
3000 int *x, int *y) 3409 const game_ui *ui, int *x, int *y)
3001{ 3410{
3002 struct { int tilesize, w, h; } ads, *ds = &ads; 3411 struct { int tilesize, w, h; } ads, *ds = &ads;
3003 3412
@@ -3098,7 +3507,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
3098 ds->bl = NULL; 3507 ds->bl = NULL;
3099 ds->blmirror = NULL; 3508 ds->blmirror = NULL;
3100 ds->dragging = false; 3509 ds->dragging = false;
3101 ds->dragx = ds->dragy = 0; 3510 ds->dragx = ds->dragy = ds->oppx = ds->oppy = 0;
3102 3511
3103 ds->colour_scratch = snewn(ds->w * ds->h, int); 3512 ds->colour_scratch = snewn(ds->w * ds->h, int);
3104 3513
@@ -3145,7 +3554,10 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
3145static void draw_arrow(drawing *dr, game_drawstate *ds, 3554static void draw_arrow(drawing *dr, game_drawstate *ds,
3146 int cx, int cy, int ddx, int ddy, int col) 3555 int cx, int cy, int ddx, int ddy, int col)
3147{ 3556{
3148 float vlen = (float)sqrt(ddx*ddx+ddy*ddy); 3557 int sqdist = ddx*ddx+ddy*ddy;
3558 if (sqdist == 0)
3559 return; /* avoid division by zero */
3560 float vlen = (float)sqrt(sqdist);
3149 float xdx = ddx/vlen, xdy = ddy/vlen; 3561 float xdx = ddx/vlen, xdy = ddy/vlen;
3150 float ydx = -xdy, ydy = xdx; 3562 float ydx = -xdy, ydy = xdx;
3151 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3); 3563 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3);
@@ -3257,7 +3669,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3257 int w = ds->w, h = ds->h; 3669 int w = ds->w, h = ds->h;
3258 int x, y; 3670 int x, y;
3259 bool flashing = false; 3671 bool flashing = false;
3260 int oppx, oppy;
3261 3672
3262 if (flashtime > 0) { 3673 if (flashtime > 0) {
3263 int frame = (int)(flashtime / FLASH_TIME); 3674 int frame = (int)(flashtime / FLASH_TIME);
@@ -3267,14 +3678,10 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3267 if (ds->dragging) { 3678 if (ds->dragging) {
3268 assert(ds->bl); 3679 assert(ds->bl);
3269 assert(ds->blmirror); 3680 assert(ds->blmirror);
3270 calculate_opposite_point(ui, ds, ds->dragx + TILE_SIZE/2, 3681 blitter_load(dr, ds->blmirror, ds->oppx, ds->oppy);
3271 ds->dragy + TILE_SIZE/2, &oppx, &oppy); 3682 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3272 oppx -= TILE_SIZE/2;
3273 oppy -= TILE_SIZE/2;
3274 blitter_load(dr, ds->bl, ds->dragx, ds->dragy); 3683 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
3275 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE); 3684 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3276 blitter_load(dr, ds->blmirror, oppx, oppy);
3277 draw_update(dr, oppx, oppy, TILE_SIZE, TILE_SIZE);
3278 ds->dragging = false; 3685 ds->dragging = false;
3279 } 3686 }
3280 if (ds->cur_visible) { 3687 if (ds->cur_visible) {
@@ -3285,7 +3692,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3285 } 3692 }
3286 3693
3287 if (!ds->started) { 3694 if (!ds->started) {
3288 draw_rect(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT, COL_BACKGROUND);
3289 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1, 3695 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1,
3290 w*TILE_SIZE + EDGE_THICKNESS*2 - 1, 3696 w*TILE_SIZE + EDGE_THICKNESS*2 - 1,
3291 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE); 3697 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE);
@@ -3433,16 +3839,28 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3433 } 3839 }
3434 3840
3435 if (ui->dragging) { 3841 if (ui->dragging) {
3842 int oppx, oppy;
3843
3436 ds->dragging = true; 3844 ds->dragging = true;
3437 ds->dragx = ui->dx - TILE_SIZE/2; 3845 ds->dragx = ui->dx - TILE_SIZE/2;
3438 ds->dragy = ui->dy - TILE_SIZE/2; 3846 ds->dragy = ui->dy - TILE_SIZE/2;
3439 calculate_opposite_point(ui, ds, ui->dx, ui->dy, &oppx, &oppy); 3847 calculate_opposite_point(ui, ds, ui->dx, ui->dy, &oppx, &oppy);
3848 ds->oppx = oppx - TILE_SIZE/2;
3849 ds->oppy = oppy - TILE_SIZE/2;
3850
3440 blitter_save(dr, ds->bl, ds->dragx, ds->dragy); 3851 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
3441 blitter_save(dr, ds->blmirror, oppx - TILE_SIZE/2, oppy - TILE_SIZE/2); 3852 clip(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3442 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx, 3853 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx,
3443 SCOORD(ui->doty) - ui->dy, COL_ARROW); 3854 SCOORD(ui->doty) - ui->dy, COL_ARROW);
3855 unclip(dr);
3856 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3857
3858 blitter_save(dr, ds->blmirror, ds->oppx, ds->oppy);
3859 clip(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3444 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx, 3860 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx,
3445 SCOORD(ui->doty) - oppy, COL_ARROW); 3861 SCOORD(ui->doty) - oppy, COL_ARROW);
3862 unclip(dr);
3863 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3446 } 3864 }
3447#ifdef EDITOR 3865#ifdef EDITOR
3448 { 3866 {
@@ -3508,13 +3926,9 @@ static int game_status(const game_state *state)
3508 return state->completed ? +1 : 0; 3926 return state->completed ? +1 : 0;
3509} 3927}
3510 3928
3511static bool game_timing_state(const game_state *state, game_ui *ui)
3512{
3513 return true;
3514}
3515
3516#ifndef EDITOR 3929#ifndef EDITOR
3517static void game_print_size(const game_params *params, float *x, float *y) 3930static void game_print_size(const game_params *params, const game_ui *ui,
3931 float *x, float *y)
3518{ 3932{
3519 int pw, ph; 3933 int pw, ph;
3520 3934
@@ -3522,17 +3936,19 @@ static void game_print_size(const game_params *params, float *x, float *y)
3522 * 8mm squares by default. (There isn't all that much detail 3936 * 8mm squares by default. (There isn't all that much detail
3523 * that needs to go in each square.) 3937 * that needs to go in each square.)
3524 */ 3938 */
3525 game_compute_size(params, 800, &pw, &ph); 3939 game_compute_size(params, 800, ui, &pw, &ph);
3526 *x = pw / 100.0F; 3940 *x = pw / 100.0F;
3527 *y = ph / 100.0F; 3941 *y = ph / 100.0F;
3528} 3942}
3529 3943
3530static void game_print(drawing *dr, const game_state *state, int sz) 3944static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3945 int sz)
3531{ 3946{
3532 int w = state->w, h = state->h; 3947 int w = state->w, h = state->h;
3533 int white, black, blackish; 3948 int white, black, blackish;
3534 int x, y, i, j; 3949 int x, y, i, j;
3535 int *colours, *dsf; 3950 int *colours;
3951 DSF *dsf;
3536 int *coords = NULL; 3952 int *coords = NULL;
3537 int ncoords = 0, coordsize = 0; 3953 int ncoords = 0, coordsize = 0;
3538 3954
@@ -3547,7 +3963,7 @@ static void game_print(drawing *dr, const game_state *state, int sz)
3547 /* 3963 /*
3548 * Get the completion information. 3964 * Get the completion information.
3549 */ 3965 */
3550 dsf = snewn(w * h, int); 3966 dsf = dsf_new(w * h);
3551 colours = snewn(w * h, int); 3967 colours = snewn(w * h, int);
3552 check_complete(state, dsf, colours); 3968 check_complete(state, dsf, colours);
3553 3969
@@ -3683,7 +4099,7 @@ static void game_print(drawing *dr, const game_state *state, int sz)
3683 black : white), black); 4099 black : white), black);
3684 } 4100 }
3685 4101
3686 sfree(dsf); 4102 dsf_free(dsf);
3687 sfree(colours); 4103 sfree(colours);
3688 sfree(coords); 4104 sfree(coords);
3689} 4105}
@@ -3714,12 +4130,18 @@ const struct game thegame = {
3714 true, solve_game, 4130 true, solve_game,
3715#endif 4131#endif
3716 true, game_can_format_as_text_now, game_text_format, 4132 true, game_can_format_as_text_now, game_text_format,
4133 NULL, NULL, /* get_prefs, set_prefs */
3717 new_ui, 4134 new_ui,
3718 free_ui, 4135 free_ui,
3719 encode_ui, 4136 NULL, /* encode_ui */
3720 decode_ui, 4137 NULL, /* decode_ui */
3721 NULL, /* game_request_keys */ 4138 NULL, /* game_request_keys */
3722 game_changed_state, 4139 game_changed_state,
4140#ifdef EDITOR
4141 NULL,
4142#else
4143 current_key_label,
4144#endif
3723 interpret_move, 4145 interpret_move,
3724 execute_move, 4146 execute_move,
3725 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 4147 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3738,13 +4160,13 @@ const struct game thegame = {
3738 true, false, game_print_size, game_print, 4160 true, false, game_print_size, game_print,
3739 false, /* wants_statusbar */ 4161 false, /* wants_statusbar */
3740#endif 4162#endif
3741 false, game_timing_state, 4163 false, NULL, /* timing_state */
3742 REQUIRE_RBUTTON, /* flags */ 4164 REQUIRE_RBUTTON, /* flags */
3743}; 4165};
3744 4166
3745#ifdef STANDALONE_SOLVER 4167#ifdef STANDALONE_SOLVER
3746 4168
3747const char *quis; 4169static const char *quis;
3748 4170
3749#include <time.h> 4171#include <time.h>
3750 4172
@@ -3802,7 +4224,7 @@ static void soak(game_params *p, random_state *rs)
3802#endif 4224#endif
3803 tt_start = tt_now = time(NULL); 4225 tt_start = tt_now = time(NULL);
3804 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0; 4226 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0;
3805 maxtries = 1; 4227 one_try = true;
3806 4228
3807 printf("Soak-generating a %dx%d grid, max. diff %s.\n", 4229 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
3808 p->w, p->h, galaxies_diffnames[p->diff]); 4230 p->w, p->h, galaxies_diffnames[p->diff]);
@@ -3812,7 +4234,9 @@ static void soak(game_params *p, random_state *rs)
3812 printf("]\n"); 4234 printf("]\n");
3813 4235
3814 while (1) { 4236 while (1) {
3815 desc = new_game_desc(p, rs, NULL, false); 4237 char *aux;
4238 desc = new_game_desc(p, rs, &aux, false);
4239 sfree(aux);
3816 st = new_game(NULL, p, desc); 4240 st = new_game(NULL, p, desc);
3817 diff = solver_state(st, p->diff); 4241 diff = solver_state(st, p->diff);
3818 nspaces += st->w*st->h; 4242 nspaces += st->w*st->h;
@@ -3866,8 +4290,6 @@ int main(int argc, char **argv)
3866 } 4290 }
3867 } 4291 }
3868 4292
3869 maxtries = 50;
3870
3871 p = default_params(); 4293 p = default_params();
3872 rs = random_new((void*)&seed, sizeof(time_t)); 4294 rs = random_new((void*)&seed, sizeof(time_t));
3873 4295
diff --git a/apps/plugins/puzzles/src/grid.c b/apps/plugins/puzzles/src/grid.c
index 5ea37439d4..04bb8a36cd 100644
--- a/apps/plugins/puzzles/src/grid.c
+++ b/apps/plugins/puzzles/src/grid.c
@@ -11,13 +11,21 @@
11#include <string.h> 11#include <string.h>
12#include <assert.h> 12#include <assert.h>
13#include <ctype.h> 13#include <ctype.h>
14#include <math.h>
15#include <float.h> 14#include <float.h>
15#include <limits.h>
16#ifdef NO_TGMATH_H
17# include <math.h>
18#else
19# include <tgmath.h>
20#endif
16 21
17#include "puzzles.h" 22#include "puzzles.h"
18#include "tree234.h" 23#include "tree234.h"
19#include "grid.h" 24#include "grid.h"
25#include "penrose-legacy.h"
20#include "penrose.h" 26#include "penrose.h"
27#include "hat.h"
28#include "spectre.h"
21 29
22/* Debugging options */ 30/* Debugging options */
23 31
@@ -36,12 +44,17 @@ void grid_free(grid *g)
36 if (g->refcount == 0) { 44 if (g->refcount == 0) {
37 int i; 45 int i;
38 for (i = 0; i < g->num_faces; i++) { 46 for (i = 0; i < g->num_faces; i++) {
39 sfree(g->faces[i].dots); 47 sfree(g->faces[i]->dots);
40 sfree(g->faces[i].edges); 48 sfree(g->faces[i]->edges);
49 sfree(g->faces[i]);
41 } 50 }
42 for (i = 0; i < g->num_dots; i++) { 51 for (i = 0; i < g->num_dots; i++) {
43 sfree(g->dots[i].faces); 52 sfree(g->dots[i]->faces);
44 sfree(g->dots[i].edges); 53 sfree(g->dots[i]->edges);
54 sfree(g->dots[i]);
55 }
56 for (i = 0; i < g->num_edges; i++) {
57 sfree(g->edges[i]);
45 } 58 }
46 sfree(g->faces); 59 sfree(g->faces);
47 sfree(g->edges); 60 sfree(g->edges);
@@ -59,6 +72,7 @@ static grid *grid_empty(void)
59 g->edges = NULL; 72 g->edges = NULL;
60 g->dots = NULL; 73 g->dots = NULL;
61 g->num_faces = g->num_edges = g->num_dots = 0; 74 g->num_faces = g->num_edges = g->num_dots = 0;
75 g->size_faces = g->size_edges = g->size_dots = 0;
62 g->refcount = 1; 76 g->refcount = 1;
63 g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0; 77 g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0;
64 return g; 78 return g;
@@ -116,7 +130,7 @@ grid_edge *grid_nearest_edge(grid *g, int x, int y)
116 best_edge = NULL; 130 best_edge = NULL;
117 131
118 for (i = 0; i < g->num_edges; i++) { 132 for (i = 0; i < g->num_edges; i++) {
119 grid_edge *e = &g->edges[i]; 133 grid_edge *e = g->edges[i];
120 long e2; /* squared length of edge */ 134 long e2; /* squared length of edge */
121 long a2, b2; /* squared lengths of other sides */ 135 long a2, b2; /* squared lengths of other sides */
122 double dist; 136 double dist;
@@ -185,7 +199,7 @@ xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
185 if (which & SVG_FACES) { 199 if (which & SVG_FACES) {
186 fprintf(fp, "<g>\n"); 200 fprintf(fp, "<g>\n");
187 for (i = 0; i < g->num_faces; i++) { 201 for (i = 0; i < g->num_faces; i++) {
188 grid_face *f = g->faces + i; 202 grid_face *f = g->faces[i];
189 fprintf(fp, "<polygon points=\""); 203 fprintf(fp, "<polygon points=\"");
190 for (j = 0; j < f->order; j++) { 204 for (j = 0; j < f->order; j++) {
191 grid_dot *d = f->dots[j]; 205 grid_dot *d = f->dots[j];
@@ -200,7 +214,7 @@ xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
200 if (which & SVG_EDGES) { 214 if (which & SVG_EDGES) {
201 fprintf(fp, "<g>\n"); 215 fprintf(fp, "<g>\n");
202 for (i = 0; i < g->num_edges; i++) { 216 for (i = 0; i < g->num_edges; i++) {
203 grid_edge *e = g->edges + i; 217 grid_edge *e = g->edges[i];
204 grid_dot *d1 = e->dot1, *d2 = e->dot2; 218 grid_dot *d1 = e->dot1, *d2 = e->dot2;
205 219
206 fprintf(fp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" " 220 fprintf(fp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
@@ -213,7 +227,7 @@ xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
213 if (which & SVG_DOTS) { 227 if (which & SVG_DOTS) {
214 fprintf(fp, "<g>\n"); 228 fprintf(fp, "<g>\n");
215 for (i = 0; i < g->num_dots; i++) { 229 for (i = 0; i < g->num_dots; i++) {
216 grid_dot *d = g->dots + i; 230 grid_dot *d = g->dots[i];
217 fprintf(fp, "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" fill=\"%s\" />", 231 fprintf(fp, "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" fill=\"%s\" />",
218 d->x, d->y, g->tilesize/20, g->tilesize/20, DOT_COLOUR); 232 d->x, d->y, g->tilesize/20, g->tilesize/20, DOT_COLOUR);
219 } 233 }
@@ -251,13 +265,17 @@ static void grid_debug_basic(grid *g)
251#ifdef DEBUG_GRID 265#ifdef DEBUG_GRID
252 int i; 266 int i;
253 printf("--- Basic Grid Data ---\n"); 267 printf("--- Basic Grid Data ---\n");
268 for (i = 0; i < g->num_dots; i++) {
269 grid_dot *d = g->dots[i];
270 printf("Dot %d at (%d,%d)\n", i, d->x, d->y);
271 }
254 for (i = 0; i < g->num_faces; i++) { 272 for (i = 0; i < g->num_faces; i++) {
255 grid_face *f = g->faces + i; 273 grid_face *f = g->faces[i];
256 printf("Face %d: dots[", i); 274 printf("Face %d: dots[", i);
257 int j; 275 int j;
258 for (j = 0; j < f->order; j++) { 276 for (j = 0; j < f->order; j++) {
259 grid_dot *d = f->dots[j]; 277 grid_dot *d = f->dots[j];
260 printf("%s%d", j ? "," : "", (int)(d - g->dots)); 278 printf("%s%d", j ? "," : "", (int)(d->index));
261 } 279 }
262 printf("]\n"); 280 printf("]\n");
263 } 281 }
@@ -275,38 +293,38 @@ static void grid_debug_derived(grid *g)
275 int i; 293 int i;
276 printf("--- Derived Grid Data ---\n"); 294 printf("--- Derived Grid Data ---\n");
277 for (i = 0; i < g->num_edges; i++) { 295 for (i = 0; i < g->num_edges; i++) {
278 grid_edge *e = g->edges + i; 296 grid_edge *e = g->edges[i];
279 printf("Edge %d: dots[%d,%d] faces[%d,%d]\n", 297 printf("Edge %d: dots[%d,%d] faces[%d,%d]\n",
280 i, (int)(e->dot1 - g->dots), (int)(e->dot2 - g->dots), 298 i, (int)(e->dot1->index), (int)(e->dot2->index),
281 e->face1 ? (int)(e->face1 - g->faces) : -1, 299 e->face1 ? (int)(e->face1->index) : -1,
282 e->face2 ? (int)(e->face2 - g->faces) : -1); 300 e->face2 ? (int)(e->face2->index) : -1);
283 } 301 }
284 /* faces */ 302 /* faces */
285 for (i = 0; i < g->num_faces; i++) { 303 for (i = 0; i < g->num_faces; i++) {
286 grid_face *f = g->faces + i; 304 grid_face *f = g->faces[i];
287 int j; 305 int j;
288 printf("Face %d: faces[", i); 306 printf("Face %d: faces[", i);
289 for (j = 0; j < f->order; j++) { 307 for (j = 0; j < f->order; j++) {
290 grid_edge *e = f->edges[j]; 308 grid_edge *e = f->edges[j];
291 grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1; 309 grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1;
292 printf("%s%d", j ? "," : "", f2 ? (int)(f2 - g->faces) : -1); 310 printf("%s%d", j ? "," : "", f2 ? f2->index : -1);
293 } 311 }
294 printf("]\n"); 312 printf("]\n");
295 } 313 }
296 /* dots */ 314 /* dots */
297 for (i = 0; i < g->num_dots; i++) { 315 for (i = 0; i < g->num_dots; i++) {
298 grid_dot *d = g->dots + i; 316 grid_dot *d = g->dots[i];
299 int j; 317 int j;
300 printf("Dot %d: dots[", i); 318 printf("Dot %d: dots[", i);
301 for (j = 0; j < d->order; j++) { 319 for (j = 0; j < d->order; j++) {
302 grid_edge *e = d->edges[j]; 320 grid_edge *e = d->edges[j];
303 grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1; 321 grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1;
304 printf("%s%d", j ? "," : "", (int)(d2 - g->dots)); 322 printf("%s%d", j ? "," : "", d2->index);
305 } 323 }
306 printf("] faces["); 324 printf("] faces[");
307 for (j = 0; j < d->order; j++) { 325 for (j = 0; j < d->order; j++) {
308 grid_face *f = d->faces[j]; 326 grid_face *f = d->faces[j];
309 printf("%s%d", j ? "," : "", f ? (int)(f - g->faces) : -1); 327 printf("%s%d", j ? "," : "", f ? f->index : -1);
310 } 328 }
311 printf("]\n"); 329 printf("]\n");
312 } 330 }
@@ -324,21 +342,23 @@ static int grid_edge_bydots_cmpfn(void *v1, void *v2)
324 grid_edge *b = v2; 342 grid_edge *b = v2;
325 grid_dot *da, *db; 343 grid_dot *da, *db;
326 344
327 /* Pointer subtraction is valid here, because all dots point into the 345 /* Edges are not "normalised" - the 2 dots could be stored in any order,
328 * same dot-list (g->dots).
329 * Edges are not "normalised" - the 2 dots could be stored in any order,
330 * so we need to take this into account when comparing edges. */ 346 * so we need to take this into account when comparing edges. */
331 347
332 /* Compare first dots */ 348 /* Compare first dots */
333 da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2; 349 da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2;
334 db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2; 350 db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2;
335 if (da != db) 351 if (da->index < db->index)
336 return db - da; 352 return -1;
353 if (da->index > db->index)
354 return +1;
337 /* Compare last dots */ 355 /* Compare last dots */
338 da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1; 356 da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1;
339 db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1; 357 db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1;
340 if (da != db) 358 if (da->index < db->index)
341 return db - da; 359 return -1;
360 if (da->index > db->index)
361 return +1;
342 362
343 return 0; 363 return 0;
344} 364}
@@ -358,7 +378,7 @@ static int grid_edge_bydots_cmpfn(void *v1, void *v2)
358static void grid_trim_vigorously(grid *g) 378static void grid_trim_vigorously(grid *g)
359{ 379{
360 int *dotpairs, *faces, *dots; 380 int *dotpairs, *faces, *dots;
361 int *dsf; 381 DSF *dsf;
362 int i, j, k, size, newfaces, newdots; 382 int i, j, k, size, newfaces, newdots;
363 383
364 /* 384 /*
@@ -371,10 +391,10 @@ static void grid_trim_vigorously(grid *g)
371 for (j = 0; j < g->num_dots; j++) 391 for (j = 0; j < g->num_dots; j++)
372 dotpairs[i*g->num_dots+j] = -1; 392 dotpairs[i*g->num_dots+j] = -1;
373 for (i = 0; i < g->num_faces; i++) { 393 for (i = 0; i < g->num_faces; i++) {
374 grid_face *f = g->faces + i; 394 grid_face *f = g->faces[i];
375 int dot0 = f->dots[f->order-1] - g->dots; 395 int dot0 = f->dots[f->order-1]->index;
376 for (j = 0; j < f->order; j++) { 396 for (j = 0; j < f->order; j++) {
377 int dot1 = f->dots[j] - g->dots; 397 int dot1 = f->dots[j]->index;
378 dotpairs[dot0 * g->num_dots + dot1] = i; 398 dotpairs[dot0 * g->num_dots + dot1] = i;
379 dot0 = dot1; 399 dot0 = dot1;
380 } 400 }
@@ -398,7 +418,7 @@ static void grid_trim_vigorously(grid *g)
398 * Now identify connected pairs of landlocked dots, and form a dsf 418 * Now identify connected pairs of landlocked dots, and form a dsf
399 * unifying them. 419 * unifying them.
400 */ 420 */
401 dsf = snew_dsf(g->num_dots); 421 dsf = dsf_new(g->num_dots);
402 for (i = 0; i < g->num_dots; i++) 422 for (i = 0; i < g->num_dots; i++)
403 for (j = 0; j < i; j++) 423 for (j = 0; j < i; j++)
404 if (dots[i] && dots[j] && 424 if (dots[i] && dots[j] &&
@@ -434,60 +454,52 @@ static void grid_trim_vigorously(grid *g)
434 for (i = 0; i < g->num_dots; i++) 454 for (i = 0; i < g->num_dots; i++)
435 dots[i] = 0; 455 dots[i] = 0;
436 for (i = 0; i < g->num_faces; i++) { 456 for (i = 0; i < g->num_faces; i++) {
437 grid_face *f = g->faces + i; 457 grid_face *f = g->faces[i];
438 bool keep = false; 458 bool keep = false;
439 for (k = 0; k < f->order; k++) 459 for (k = 0; k < f->order; k++)
440 if (dsf_canonify(dsf, f->dots[k] - g->dots) == j) 460 if (dsf_canonify(dsf, f->dots[k]->index) == j)
441 keep = true; 461 keep = true;
442 if (keep) { 462 if (keep) {
443 faces[i] = 1; 463 faces[i] = 1;
444 for (k = 0; k < f->order; k++) 464 for (k = 0; k < f->order; k++)
445 dots[f->dots[k]-g->dots] = 1; 465 dots[f->dots[k]->index] = 1;
446 } 466 }
447 } 467 }
448 468
449 /* 469 /*
450 * Work out the new indices of those faces and dots, when we 470 * Compact the faces array, rewriting the faces' indices and
451 * compact the arrays containing them. 471 * freeing the unwanted ones.
452 */
453 for (i = newfaces = 0; i < g->num_faces; i++)
454 faces[i] = (faces[i] ? newfaces++ : -1);
455 for (i = newdots = 0; i < g->num_dots; i++)
456 dots[i] = (dots[i] ? newdots++ : -1);
457
458 /*
459 * Free the dynamically allocated 'dots' pointer lists in faces
460 * we're going to discard.
461 */ 472 */
462 for (i = 0; i < g->num_faces; i++) 473 for (i = newfaces = 0; i < g->num_faces; i++) {
463 if (faces[i] < 0) 474 grid_face *f = g->faces[i];
464 sfree(g->faces[i].dots); 475 if (faces[i]) {
476 f->index = newfaces++;
477 g->faces[f->index] = f;
478 } else {
479 sfree(f->dots);
480 sfree(f);
481 }
482 }
483 g->num_faces = newfaces;
465 484
466 /* 485 /*
467 * Go through and compact the arrays. 486 * Compact the dots array, similarly.
468 */ 487 */
469 for (i = 0; i < g->num_dots; i++) 488 for (i = newdots = 0; i < g->num_dots; i++) {
470 if (dots[i] >= 0) { 489 grid_dot *d = g->dots[i];
471 grid_dot *dnew = g->dots + dots[i], *dold = g->dots + i; 490 if (dots[i]) {
472 *dnew = *dold; /* structure copy */ 491 d->index = newdots++;
473 } 492 g->dots[d->index] = d;
474 for (i = 0; i < g->num_faces; i++) 493 } else {
475 if (faces[i] >= 0) { 494 sfree(d->edges);
476 grid_face *fnew = g->faces + faces[i], *fold = g->faces + i; 495 sfree(d->faces);
477 *fnew = *fold; /* structure copy */ 496 sfree(d);
478 for (j = 0; j < fnew->order; j++) {
479 /*
480 * Reindex the dots in this face.
481 */
482 k = fnew->dots[j] - g->dots;
483 fnew->dots[j] = g->dots + dots[k];
484 }
485 } 497 }
486 g->num_faces = newfaces; 498 }
487 g->num_dots = newdots; 499 g->num_dots = newdots;
488 500
489 sfree(dotpairs); 501 sfree(dotpairs);
490 sfree(dsf); 502 dsf_free(dsf);
491 sfree(dots); 503 sfree(dots);
492 sfree(faces); 504 sfree(faces);
493} 505}
@@ -505,7 +517,6 @@ static void grid_make_consistent(grid *g)
505{ 517{
506 int i; 518 int i;
507 tree234 *incomplete_edges; 519 tree234 *incomplete_edges;
508 grid_edge *next_new_edge; /* Where new edge will go into g->edges */
509 520
510 grid_debug_basic(g); 521 grid_debug_basic(g);
511 522
@@ -513,14 +524,6 @@ static void grid_make_consistent(grid *g)
513 * Generate edges 524 * Generate edges
514 */ 525 */
515 526
516 /* We know how many dots and faces there are, so we can find the exact
517 * number of edges from Euler's polyhedral formula: F + V = E + 2 .
518 * We use "-1", not "-2" here, because Euler's formula includes the
519 * infinite face, which we don't count. */
520 g->num_edges = g->num_faces + g->num_dots - 1;
521 g->edges = snewn(g->num_edges, grid_edge);
522 next_new_edge = g->edges;
523
524 /* Iterate over faces, and over each face's dots, generating edges as we 527 /* Iterate over faces, and over each face's dots, generating edges as we
525 * go. As we find each new edge, we can immediately fill in the edge's 528 * go. As we find each new edge, we can immediately fill in the edge's
526 * dots, but only one of the edge's faces. Later on in the iteration, we 529 * dots, but only one of the edge's faces. Later on in the iteration, we
@@ -530,7 +533,7 @@ static void grid_make_consistent(grid *g)
530 * their dots. */ 533 * their dots. */
531 incomplete_edges = newtree234(grid_edge_bydots_cmpfn); 534 incomplete_edges = newtree234(grid_edge_bydots_cmpfn);
532 for (i = 0; i < g->num_faces; i++) { 535 for (i = 0; i < g->num_faces; i++) {
533 grid_face *f = g->faces + i; 536 grid_face *f = g->faces[i];
534 int j; 537 int j;
535 for (j = 0; j < f->order; j++) { 538 for (j = 0; j < f->order; j++) {
536 grid_edge e; /* fake edge for searching */ 539 grid_edge e; /* fake edge for searching */
@@ -548,18 +551,28 @@ static void grid_make_consistent(grid *g)
548 * Edge is already removed from incomplete_edges. */ 551 * Edge is already removed from incomplete_edges. */
549 edge_found->face2 = f; 552 edge_found->face2 = f;
550 } else { 553 } else {
551 assert(next_new_edge - g->edges < g->num_edges); 554 grid_edge *new_edge = snew(grid_edge);
552 next_new_edge->dot1 = e.dot1; 555 new_edge->dot1 = e.dot1;
553 next_new_edge->dot2 = e.dot2; 556 new_edge->dot2 = e.dot2;
554 next_new_edge->face1 = f; 557 new_edge->face1 = f;
555 next_new_edge->face2 = NULL; /* potentially infinite face */ 558 new_edge->face2 = NULL; /* potentially infinite face */
556 add234(incomplete_edges, next_new_edge); 559 add234(incomplete_edges, new_edge);
557 ++next_new_edge; 560
561 /* And add it to g->edges. */
562 if (g->num_edges >= g->size_edges) {
563 int increment = g->num_edges / 4 + 128;
564 g->size_edges = (increment < INT_MAX - g->num_edges ?
565 g->num_edges + increment : INT_MAX);
566 g->edges = sresize(g->edges, g->size_edges, grid_edge *);
567 }
568 assert(g->num_edges < INT_MAX);
569 new_edge->index = g->num_edges++;
570 g->edges[new_edge->index] = new_edge;
558 } 571 }
559 } 572 }
560 } 573 }
561 freetree234(incomplete_edges); 574 freetree234(incomplete_edges);
562 575
563 /* ====== Stage 2 ====== 576 /* ====== Stage 2 ======
564 * For each face, build its edge list. 577 * For each face, build its edge list.
565 */ 578 */
@@ -567,7 +580,7 @@ static void grid_make_consistent(grid *g)
567 /* Allocate space for each edge list. Can do this, because each face's 580 /* Allocate space for each edge list. Can do this, because each face's
568 * edge-list is the same size as its dot-list. */ 581 * edge-list is the same size as its dot-list. */
569 for (i = 0; i < g->num_faces; i++) { 582 for (i = 0; i < g->num_faces; i++) {
570 grid_face *f = g->faces + i; 583 grid_face *f = g->faces[i];
571 int j; 584 int j;
572 f->edges = snewn(f->order, grid_edge*); 585 f->edges = snewn(f->order, grid_edge*);
573 /* Preload with NULLs, to help detect potential bugs. */ 586 /* Preload with NULLs, to help detect potential bugs. */
@@ -579,7 +592,7 @@ static void grid_make_consistent(grid *g)
579 * the face's edge-list, after finding where it should go in the 592 * the face's edge-list, after finding where it should go in the
580 * sequence. */ 593 * sequence. */
581 for (i = 0; i < g->num_edges; i++) { 594 for (i = 0; i < g->num_edges; i++) {
582 grid_edge *e = g->edges + i; 595 grid_edge *e = g->edges[i];
583 int j; 596 int j;
584 for (j = 0; j < 2; j++) { 597 for (j = 0; j < 2; j++) {
585 grid_face *f = j ? e->face2 : e->face1; 598 grid_face *f = j ? e->face2 : e->face1;
@@ -641,16 +654,16 @@ static void grid_make_consistent(grid *g)
641 * allocate the right space for these lists. Pre-compute the sizes by 654 * allocate the right space for these lists. Pre-compute the sizes by
642 * iterating over each edge and recording a tally against each dot. */ 655 * iterating over each edge and recording a tally against each dot. */
643 for (i = 0; i < g->num_dots; i++) { 656 for (i = 0; i < g->num_dots; i++) {
644 g->dots[i].order = 0; 657 g->dots[i]->order = 0;
645 } 658 }
646 for (i = 0; i < g->num_edges; i++) { 659 for (i = 0; i < g->num_edges; i++) {
647 grid_edge *e = g->edges + i; 660 grid_edge *e = g->edges[i];
648 ++(e->dot1->order); 661 ++(e->dot1->order);
649 ++(e->dot2->order); 662 ++(e->dot2->order);
650 } 663 }
651 /* Now we have the sizes, pre-allocate the edge and face lists. */ 664 /* Now we have the sizes, pre-allocate the edge and face lists. */
652 for (i = 0; i < g->num_dots; i++) { 665 for (i = 0; i < g->num_dots; i++) {
653 grid_dot *d = g->dots + i; 666 grid_dot *d = g->dots[i];
654 int j; 667 int j;
655 assert(d->order >= 2); /* sanity check */ 668 assert(d->order >= 2); /* sanity check */
656 d->edges = snewn(d->order, grid_edge*); 669 d->edges = snewn(d->order, grid_edge*);
@@ -663,7 +676,7 @@ static void grid_make_consistent(grid *g)
663 /* For each dot, need to find a face that touches it, so we can seed 676 /* For each dot, need to find a face that touches it, so we can seed
664 * the edge-face-edge-face process around each dot. */ 677 * the edge-face-edge-face process around each dot. */
665 for (i = 0; i < g->num_faces; i++) { 678 for (i = 0; i < g->num_faces; i++) {
666 grid_face *f = g->faces + i; 679 grid_face *f = g->faces[i];
667 int j; 680 int j;
668 for (j = 0; j < f->order; j++) { 681 for (j = 0; j < f->order; j++) {
669 grid_dot *d = f->dots[j]; 682 grid_dot *d = f->dots[j];
@@ -677,7 +690,7 @@ static void grid_make_consistent(grid *g)
677 * blocks progress. But there's only one such face, so we will 690 * blocks progress. But there's only one such face, so we will
678 * succeed in finding every edge and face this way. */ 691 * succeed in finding every edge and face this way. */
679 for (i = 0; i < g->num_dots; i++) { 692 for (i = 0; i < g->num_dots; i++) {
680 grid_dot *d = g->dots + i; 693 grid_dot *d = g->dots[i];
681 int current_face1 = 0; /* ascends clockwise */ 694 int current_face1 = 0; /* ascends clockwise */
682 int current_face2 = 0; /* descends anticlockwise */ 695 int current_face2 = 0; /* descends anticlockwise */
683 696
@@ -774,7 +787,7 @@ static void grid_make_consistent(grid *g)
774 787
775 /* Bounding rectangle */ 788 /* Bounding rectangle */
776 for (i = 0; i < g->num_dots; i++) { 789 for (i = 0; i < g->num_dots; i++) {
777 grid_dot *d = g->dots + i; 790 grid_dot *d = g->dots[i];
778 if (i == 0) { 791 if (i == 0) {
779 g->lowest_x = g->highest_x = d->x; 792 g->lowest_x = g->highest_x = d->x;
780 g->lowest_y = g->highest_y = d->y; 793 g->lowest_y = g->highest_y = d->y;
@@ -802,36 +815,52 @@ static int grid_point_cmp_fn(void *v1, void *v2)
802 else 815 else
803 return p2->x - p1->x; 816 return p2->x - p1->x;
804} 817}
805/* Add a new face to the grid, with its dot list allocated. 818/* Add a new face to the grid, with its dot list allocated. */
806 * Assumes there's enough space allocated for the new face in grid->faces */
807static void grid_face_add_new(grid *g, int face_size) 819static void grid_face_add_new(grid *g, int face_size)
808{ 820{
809 int i; 821 int i;
810 grid_face *new_face = g->faces + g->num_faces; 822 grid_face *new_face = snew(grid_face);
823 assert(g->num_faces < INT_MAX);
824 if (g->num_faces >= g->size_faces) {
825 int increment = g->num_faces / 4 + 128;
826 g->size_faces = (increment < INT_MAX - g->num_faces ?
827 g->num_faces + increment : INT_MAX);
828 g->faces = sresize(g->faces, g->size_faces, grid_face *);
829 }
830 new_face->index = g->num_faces++;
831 g->faces[new_face->index] = new_face;
832
811 new_face->order = face_size; 833 new_face->order = face_size;
812 new_face->dots = snewn(face_size, grid_dot*); 834 new_face->dots = snewn(face_size, grid_dot*);
813 for (i = 0; i < face_size; i++) 835 for (i = 0; i < face_size; i++)
814 new_face->dots[i] = NULL; 836 new_face->dots[i] = NULL;
815 new_face->edges = NULL; 837 new_face->edges = NULL;
816 new_face->has_incentre = false; 838 new_face->has_incentre = false;
817 g->num_faces++;
818} 839}
819/* Assumes dot list has enough space */
820static grid_dot *grid_dot_add_new(grid *g, int x, int y) 840static grid_dot *grid_dot_add_new(grid *g, int x, int y)
821{ 841{
822 grid_dot *new_dot = g->dots + g->num_dots; 842 grid_dot *new_dot = snew(grid_dot);
843 if (g->num_dots >= g->size_dots) {
844 int increment = g->num_dots / 4 + 128;
845 g->size_dots = (increment < INT_MAX - g->num_dots ?
846 g->num_dots + increment : INT_MAX);
847 g->dots = sresize(g->dots, g->size_dots, grid_dot *);
848 }
849 assert(g->num_dots < INT_MAX);
850 new_dot->index = g->num_dots++;
851 g->dots[new_dot->index] = new_dot;
852
823 new_dot->order = 0; 853 new_dot->order = 0;
824 new_dot->edges = NULL; 854 new_dot->edges = NULL;
825 new_dot->faces = NULL; 855 new_dot->faces = NULL;
826 new_dot->x = x; 856 new_dot->x = x;
827 new_dot->y = y; 857 new_dot->y = y;
828 g->num_dots++; 858
829 return new_dot; 859 return new_dot;
830} 860}
831/* Retrieve a dot with these (x,y) coordinates. Either return an existing dot 861/* Retrieve a dot with these (x,y) coordinates. Either return an existing dot
832 * in the dot_list, or add a new dot to the grid (and the dot_list) and 862 * in the dot_list, or add a new dot to the grid (and the dot_list) and
833 * return that. 863 * return that. */
834 * Assumes g->dots has enough capacity allocated */
835static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y) 864static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y)
836{ 865{
837 grid_dot test, *ret; 866 grid_dot test, *ret;
@@ -855,7 +884,7 @@ static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y)
855 * previously been added, with the required number of dots allocated) */ 884 * previously been added, with the required number of dots allocated) */
856static void grid_face_set_dot(grid *g, grid_dot *d, int position) 885static void grid_face_set_dot(grid *g, grid_dot *d, int position)
857{ 886{
858 grid_face *last_face = g->faces + g->num_faces - 1; 887 grid_face *last_face = g->faces[g->num_faces - 1];
859 last_face->dots[position] = d; 888 last_face->dots[position] = d;
860} 889}
861 890
@@ -1388,6 +1417,15 @@ void grid_find_incentre(grid_face *f)
1388 1417
1389#define SQUARE_TILESIZE 20 1418#define SQUARE_TILESIZE 20
1390 1419
1420static const char *grid_validate_params_square(int width, int height)
1421{
1422 if (width > INT_MAX / SQUARE_TILESIZE || /* xextent */
1423 height > INT_MAX / SQUARE_TILESIZE || /* yextent */
1424 width + 1 > INT_MAX / (height + 1)) /* max_dots */
1425 return "Grid size must not be unreasonably large";
1426 return NULL;
1427}
1428
1391static void grid_size_square(int width, int height, 1429static void grid_size_square(int width, int height,
1392 int *tilesize, int *xextent, int *yextent) 1430 int *tilesize, int *xextent, int *yextent)
1393{ 1431{
@@ -1404,16 +1442,10 @@ static grid *grid_new_square(int width, int height, const char *desc)
1404 /* Side length */ 1442 /* Side length */
1405 int a = SQUARE_TILESIZE; 1443 int a = SQUARE_TILESIZE;
1406 1444
1407 /* Upper bounds - don't have to be exact */
1408 int max_faces = width * height;
1409 int max_dots = (width + 1) * (height + 1);
1410
1411 tree234 *points; 1445 tree234 *points;
1412 1446
1413 grid *g = grid_empty(); 1447 grid *g = grid_empty();
1414 g->tilesize = a; 1448 g->tilesize = a;
1415 g->faces = snewn(max_faces, grid_face);
1416 g->dots = snewn(max_dots, grid_dot);
1417 1449
1418 points = newtree234(grid_point_cmp_fn); 1450 points = newtree234(grid_point_cmp_fn);
1419 1451
@@ -1438,8 +1470,6 @@ static grid *grid_new_square(int width, int height, const char *desc)
1438 } 1470 }
1439 1471
1440 freetree234(points); 1472 freetree234(points);
1441 assert(g->num_faces <= max_faces);
1442 assert(g->num_dots <= max_dots);
1443 1473
1444 grid_make_consistent(g); 1474 grid_make_consistent(g);
1445 return g; 1475 return g;
@@ -1450,6 +1480,18 @@ static grid *grid_new_square(int width, int height, const char *desc)
1450#define HONEY_A 15 1480#define HONEY_A 15
1451#define HONEY_B 26 1481#define HONEY_B 26
1452 1482
1483static const char *grid_validate_params_honeycomb(int width, int height)
1484{
1485 int a = HONEY_A;
1486 int b = HONEY_B;
1487
1488 if (width - 1 > (INT_MAX - 4*a) / (3 * a) || /* xextent */
1489 height - 1 > (INT_MAX - 3*b) / (2 * b) || /* yextent */
1490 width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
1491 return "Grid size must not be unreasonably large";
1492 return NULL;
1493}
1494
1453static void grid_size_honeycomb(int width, int height, 1495static void grid_size_honeycomb(int width, int height,
1454 int *tilesize, int *xextent, int *yextent) 1496 int *tilesize, int *xextent, int *yextent)
1455{ 1497{
@@ -1467,16 +1509,10 @@ static grid *grid_new_honeycomb(int width, int height, const char *desc)
1467 int a = HONEY_A; 1509 int a = HONEY_A;
1468 int b = HONEY_B; 1510 int b = HONEY_B;
1469 1511
1470 /* Upper bounds - don't have to be exact */
1471 int max_faces = width * height;
1472 int max_dots = 2 * (width + 1) * (height + 1);
1473
1474 tree234 *points; 1512 tree234 *points;
1475 1513
1476 grid *g = grid_empty(); 1514 grid *g = grid_empty();
1477 g->tilesize = HONEY_TILESIZE; 1515 g->tilesize = HONEY_TILESIZE;
1478 g->faces = snewn(max_faces, grid_face);
1479 g->dots = snewn(max_dots, grid_dot);
1480 1516
1481 points = newtree234(grid_point_cmp_fn); 1517 points = newtree234(grid_point_cmp_fn);
1482 1518
@@ -1507,8 +1543,6 @@ static grid *grid_new_honeycomb(int width, int height, const char *desc)
1507 } 1543 }
1508 1544
1509 freetree234(points); 1545 freetree234(points);
1510 assert(g->num_faces <= max_faces);
1511 assert(g->num_dots <= max_dots);
1512 1546
1513 grid_make_consistent(g); 1547 grid_make_consistent(g);
1514 return g; 1548 return g;
@@ -1519,6 +1553,18 @@ static grid *grid_new_honeycomb(int width, int height, const char *desc)
1519#define TRIANGLE_VEC_X 15 1553#define TRIANGLE_VEC_X 15
1520#define TRIANGLE_VEC_Y 26 1554#define TRIANGLE_VEC_Y 26
1521 1555
1556static const char *grid_validate_params_triangular(int width, int height)
1557{
1558 int vec_x = TRIANGLE_VEC_X;
1559 int vec_y = TRIANGLE_VEC_Y;
1560
1561 if (width > INT_MAX / (2 * vec_x) - 1 || /* xextent */
1562 height > INT_MAX / vec_y || /* yextent */
1563 width + 1 > INT_MAX / 4 / (height + 1)) /* max_dots */
1564 return "Grid size must not be unreasonably large";
1565 return NULL;
1566}
1567
1522static void grid_size_triangular(int width, int height, 1568static void grid_size_triangular(int width, int height,
1523 int *tilesize, int *xextent, int *yextent) 1569 int *tilesize, int *xextent, int *yextent)
1524{ 1570{
@@ -1582,16 +1628,18 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1582 * 5x6t1:a022a212h1a1d1a12c2b11a012b1a20d1a0a12e 1628 * 5x6t1:a022a212h1a1d1a12c2b11a012b1a20d1a0a12e
1583 */ 1629 */
1584 1630
1585 g->num_faces = width * height * 2; 1631 g->num_faces = g->size_faces = width * height * 2;
1586 g->num_dots = (width + 1) * (height + 1); 1632 g->num_dots = g->size_dots = (width + 1) * (height + 1);
1587 g->faces = snewn(g->num_faces, grid_face); 1633 g->faces = snewn(g->size_faces, grid_face *);
1588 g->dots = snewn(g->num_dots, grid_dot); 1634 g->dots = snewn(g->size_dots, grid_dot *);
1589 1635
1590 /* generate dots */ 1636 /* generate dots */
1591 index = 0; 1637 index = 0;
1592 for (y = 0; y <= height; y++) { 1638 for (y = 0; y <= height; y++) {
1593 for (x = 0; x <= width; x++) { 1639 for (x = 0; x <= width; x++) {
1594 grid_dot *d = g->dots + index; 1640 grid_dot *d = snew(grid_dot);
1641 d->index = index;
1642 g->dots[d->index] = d;
1595 /* odd rows are offset to the right */ 1643 /* odd rows are offset to the right */
1596 d->order = 0; 1644 d->order = 0;
1597 d->edges = NULL; 1645 d->edges = NULL;
@@ -1607,8 +1655,11 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1607 for (y = 0; y < height; y++) { 1655 for (y = 0; y < height; y++) {
1608 for (x = 0; x < width; x++) { 1656 for (x = 0; x < width; x++) {
1609 /* initialise two faces for this (x,y) */ 1657 /* initialise two faces for this (x,y) */
1610 grid_face *f1 = g->faces + index; 1658 grid_face *f1 = snew(grid_face), *f2 = snew(grid_face);
1611 grid_face *f2 = f1 + 1; 1659 f1->index = index;
1660 f2->index = index + 1;
1661 g->faces[f1->index] = f1;
1662 g->faces[f2->index] = f2;
1612 f1->edges = NULL; 1663 f1->edges = NULL;
1613 f1->order = 3; 1664 f1->order = 3;
1614 f1->dots = snewn(f1->order, grid_dot*); 1665 f1->dots = snewn(f1->order, grid_dot*);
@@ -1621,19 +1672,19 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1621 /* face descriptions depend on whether the row-number is 1672 /* face descriptions depend on whether the row-number is
1622 * odd or even */ 1673 * odd or even */
1623 if (y % 2) { 1674 if (y % 2) {
1624 f1->dots[0] = g->dots + y * w + x; 1675 f1->dots[0] = g->dots[y * w + x];
1625 f1->dots[1] = g->dots + (y + 1) * w + x + 1; 1676 f1->dots[1] = g->dots[(y + 1) * w + x + 1];
1626 f1->dots[2] = g->dots + (y + 1) * w + x; 1677 f1->dots[2] = g->dots[(y + 1) * w + x];
1627 f2->dots[0] = g->dots + y * w + x; 1678 f2->dots[0] = g->dots[y * w + x];
1628 f2->dots[1] = g->dots + y * w + x + 1; 1679 f2->dots[1] = g->dots[y * w + x + 1];
1629 f2->dots[2] = g->dots + (y + 1) * w + x + 1; 1680 f2->dots[2] = g->dots[(y + 1) * w + x + 1];
1630 } else { 1681 } else {
1631 f1->dots[0] = g->dots + y * w + x; 1682 f1->dots[0] = g->dots[y * w + x];
1632 f1->dots[1] = g->dots + y * w + x + 1; 1683 f1->dots[1] = g->dots[y * w + x + 1];
1633 f1->dots[2] = g->dots + (y + 1) * w + x; 1684 f1->dots[2] = g->dots[(y + 1) * w + x];
1634 f2->dots[0] = g->dots + y * w + x + 1; 1685 f2->dots[0] = g->dots[y * w + x + 1];
1635 f2->dots[1] = g->dots + (y + 1) * w + x + 1; 1686 f2->dots[1] = g->dots[(y + 1) * w + x + 1];
1636 f2->dots[2] = g->dots + (y + 1) * w + x; 1687 f2->dots[2] = g->dots[(y + 1) * w + x];
1637 } 1688 }
1638 index += 2; 1689 index += 2;
1639 } 1690 }
@@ -1650,12 +1701,6 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1650 * 5x6t1:0_a1212c22c2a02a2f22a0c12a110d0e1c0c0a101121a1 1701 * 5x6t1:0_a1212c22c2a02a2f22a0c12a110d0e1c0c0a101121a1
1651 */ 1702 */
1652 tree234 *points = newtree234(grid_point_cmp_fn); 1703 tree234 *points = newtree234(grid_point_cmp_fn);
1653 /* Upper bounds - don't have to be exact */
1654 int max_faces = height * (2*width+1);
1655 int max_dots = (height+1) * (width+1) * 4;
1656
1657 g->faces = snewn(max_faces, grid_face);
1658 g->dots = snewn(max_dots, grid_dot);
1659 1704
1660 for (y = 0; y < height; y++) { 1705 for (y = 0; y < height; y++) {
1661 /* 1706 /*
@@ -1703,8 +1748,6 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1703 } 1748 }
1704 1749
1705 freetree234(points); 1750 freetree234(points);
1706 assert(g->num_faces <= max_faces);
1707 assert(g->num_dots <= max_dots);
1708 } 1751 }
1709 1752
1710 grid_make_consistent(g); 1753 grid_make_consistent(g);
@@ -1716,6 +1759,19 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
1716#define SNUBSQUARE_A 15 1759#define SNUBSQUARE_A 15
1717#define SNUBSQUARE_B 26 1760#define SNUBSQUARE_B 26
1718 1761
1762static const char *grid_validate_params_snubsquare(int width, int height)
1763{
1764 int a = SNUBSQUARE_A;
1765 int b = SNUBSQUARE_B;
1766
1767 if (width-1 > (INT_MAX - (a + b)) / (a+b) || /* xextent */
1768 height > (INT_MAX - (a + b)) / (a+b) || /* yextent */
1769 width > INT_MAX / 3 / height || /* max_faces */
1770 width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
1771 return "Grid size must not be unreasonably large";
1772 return NULL;
1773}
1774
1719static void grid_size_snubsquare(int width, int height, 1775static void grid_size_snubsquare(int width, int height,
1720 int *tilesize, int *xextent, int *yextent) 1776 int *tilesize, int *xextent, int *yextent)
1721{ 1777{
@@ -1733,16 +1789,10 @@ static grid *grid_new_snubsquare(int width, int height, const char *desc)
1733 int a = SNUBSQUARE_A; 1789 int a = SNUBSQUARE_A;
1734 int b = SNUBSQUARE_B; 1790 int b = SNUBSQUARE_B;
1735 1791
1736 /* Upper bounds - don't have to be exact */
1737 int max_faces = 3 * width * height;
1738 int max_dots = 2 * (width + 1) * (height + 1);
1739
1740 tree234 *points; 1792 tree234 *points;
1741 1793
1742 grid *g = grid_empty(); 1794 grid *g = grid_empty();
1743 g->tilesize = SNUBSQUARE_TILESIZE; 1795 g->tilesize = SNUBSQUARE_TILESIZE;
1744 g->faces = snewn(max_faces, grid_face);
1745 g->dots = snewn(max_dots, grid_dot);
1746 1796
1747 points = newtree234(grid_point_cmp_fn); 1797 points = newtree234(grid_point_cmp_fn);
1748 1798
@@ -1818,8 +1868,6 @@ static grid *grid_new_snubsquare(int width, int height, const char *desc)
1818 } 1868 }
1819 1869
1820 freetree234(points); 1870 freetree234(points);
1821 assert(g->num_faces <= max_faces);
1822 assert(g->num_dots <= max_dots);
1823 1871
1824 grid_make_consistent(g); 1872 grid_make_consistent(g);
1825 return g; 1873 return g;
@@ -1830,6 +1878,18 @@ static grid *grid_new_snubsquare(int width, int height, const char *desc)
1830#define CAIRO_A 14 1878#define CAIRO_A 14
1831#define CAIRO_B 31 1879#define CAIRO_B 31
1832 1880
1881static const char *grid_validate_params_cairo(int width, int height)
1882{
1883 int b = CAIRO_B; /* a unused in determining grid size. */
1884
1885 if (width - 1 > (INT_MAX - 2*b) / (2*b) || /* xextent */
1886 height - 1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
1887 width > INT_MAX / 2 / height || /* max_faces */
1888 width + 1 > INT_MAX / 3 / (height + 1)) /* max_dots */
1889 return "Grid size must not be unreasonably large";
1890 return NULL;
1891}
1892
1833static void grid_size_cairo(int width, int height, 1893static void grid_size_cairo(int width, int height,
1834 int *tilesize, int *xextent, int *yextent) 1894 int *tilesize, int *xextent, int *yextent)
1835{ 1895{
@@ -1846,16 +1906,10 @@ static grid *grid_new_cairo(int width, int height, const char *desc)
1846 int a = CAIRO_A; 1906 int a = CAIRO_A;
1847 int b = CAIRO_B; 1907 int b = CAIRO_B;
1848 1908
1849 /* Upper bounds - don't have to be exact */
1850 int max_faces = 2 * width * height;
1851 int max_dots = 3 * (width + 1) * (height + 1);
1852
1853 tree234 *points; 1909 tree234 *points;
1854 1910
1855 grid *g = grid_empty(); 1911 grid *g = grid_empty();
1856 g->tilesize = CAIRO_TILESIZE; 1912 g->tilesize = CAIRO_TILESIZE;
1857 g->faces = snewn(max_faces, grid_face);
1858 g->dots = snewn(max_dots, grid_dot);
1859 1913
1860 points = newtree234(grid_point_cmp_fn); 1914 points = newtree234(grid_point_cmp_fn);
1861 1915
@@ -1924,8 +1978,6 @@ static grid *grid_new_cairo(int width, int height, const char *desc)
1924 } 1978 }
1925 1979
1926 freetree234(points); 1980 freetree234(points);
1927 assert(g->num_faces <= max_faces);
1928 assert(g->num_dots <= max_dots);
1929 1981
1930 grid_make_consistent(g); 1982 grid_make_consistent(g);
1931 return g; 1983 return g;
@@ -1936,6 +1988,18 @@ static grid *grid_new_cairo(int width, int height, const char *desc)
1936#define GREATHEX_A 15 1988#define GREATHEX_A 15
1937#define GREATHEX_B 26 1989#define GREATHEX_B 26
1938 1990
1991static const char *grid_validate_params_greathexagonal(int width, int height)
1992{
1993 int a = GREATHEX_A;
1994 int b = GREATHEX_B;
1995
1996 if (width-1 > (INT_MAX - 4*a) / (3*a + b) || /* xextent */
1997 height-1 > (INT_MAX - (3*b + a)) / (2*a + 2*b) || /* yextent */
1998 width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
1999 return "Grid size must not be unreasonably large";
2000 return NULL;
2001}
2002
1939static void grid_size_greathexagonal(int width, int height, 2003static void grid_size_greathexagonal(int width, int height,
1940 int *tilesize, int *xextent, int *yextent) 2004 int *tilesize, int *xextent, int *yextent)
1941{ 2005{
@@ -1953,16 +2017,10 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc)
1953 int a = GREATHEX_A; 2017 int a = GREATHEX_A;
1954 int b = GREATHEX_B; 2018 int b = GREATHEX_B;
1955 2019
1956 /* Upper bounds - don't have to be exact */
1957 int max_faces = 6 * (width + 1) * (height + 1);
1958 int max_dots = 6 * width * height;
1959
1960 tree234 *points; 2020 tree234 *points;
1961 2021
1962 grid *g = grid_empty(); 2022 grid *g = grid_empty();
1963 g->tilesize = GREATHEX_TILESIZE; 2023 g->tilesize = GREATHEX_TILESIZE;
1964 g->faces = snewn(max_faces, grid_face);
1965 g->dots = snewn(max_dots, grid_dot);
1966 2024
1967 points = newtree234(grid_point_cmp_fn); 2025 points = newtree234(grid_point_cmp_fn);
1968 2026
@@ -2054,8 +2112,6 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc)
2054 } 2112 }
2055 2113
2056 freetree234(points); 2114 freetree234(points);
2057 assert(g->num_faces <= max_faces);
2058 assert(g->num_dots <= max_dots);
2059 2115
2060 grid_make_consistent(g); 2116 grid_make_consistent(g);
2061 return g; 2117 return g;
@@ -2066,6 +2122,18 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc)
2066#define KAGOME_A 15 2122#define KAGOME_A 15
2067#define KAGOME_B 26 2123#define KAGOME_B 26
2068 2124
2125static const char *grid_validate_params_kagome(int width, int height)
2126{
2127 int a = KAGOME_A;
2128 int b = KAGOME_B;
2129
2130 if (width-1 > (INT_MAX - 6*a) / (4*a) || /* xextent */
2131 height-1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
2132 width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
2133 return "Grid size must not be unreasonably large";
2134 return NULL;
2135}
2136
2069static void grid_size_kagome(int width, int height, 2137static void grid_size_kagome(int width, int height,
2070 int *tilesize, int *xextent, int *yextent) 2138 int *tilesize, int *xextent, int *yextent)
2071{ 2139{
@@ -2083,16 +2151,10 @@ static grid *grid_new_kagome(int width, int height, const char *desc)
2083 int a = KAGOME_A; 2151 int a = KAGOME_A;
2084 int b = KAGOME_B; 2152 int b = KAGOME_B;
2085 2153
2086 /* Upper bounds - don't have to be exact */
2087 int max_faces = 6 * (width + 1) * (height + 1);
2088 int max_dots = 6 * width * height;
2089
2090 tree234 *points; 2154 tree234 *points;
2091 2155
2092 grid *g = grid_empty(); 2156 grid *g = grid_empty();
2093 g->tilesize = KAGOME_TILESIZE; 2157 g->tilesize = KAGOME_TILESIZE;
2094 g->faces = snewn(max_faces, grid_face);
2095 g->dots = snewn(max_dots, grid_dot);
2096 2158
2097 points = newtree234(grid_point_cmp_fn); 2159 points = newtree234(grid_point_cmp_fn);
2098 2160
@@ -2150,8 +2212,6 @@ static grid *grid_new_kagome(int width, int height, const char *desc)
2150 } 2212 }
2151 2213
2152 freetree234(points); 2214 freetree234(points);
2153 assert(g->num_faces <= max_faces);
2154 assert(g->num_dots <= max_dots);
2155 2215
2156 grid_make_consistent(g); 2216 grid_make_consistent(g);
2157 return g; 2217 return g;
@@ -2162,6 +2222,18 @@ static grid *grid_new_kagome(int width, int height, const char *desc)
2162#define OCTAGONAL_A 29 2222#define OCTAGONAL_A 29
2163#define OCTAGONAL_B 41 2223#define OCTAGONAL_B 41
2164 2224
2225static const char *grid_validate_params_octagonal(int width, int height)
2226{
2227 int a = OCTAGONAL_A;
2228 int b = OCTAGONAL_B;
2229
2230 if (width > INT_MAX / (2*a + b) || /* xextent */
2231 height > INT_MAX / (2*a + b) || /* yextent */
2232 height + 1 > INT_MAX / 4 / (width + 1)) /* max_dots */
2233 return "Grid size must not be unreasonably large";
2234 return NULL;
2235}
2236
2165static void grid_size_octagonal(int width, int height, 2237static void grid_size_octagonal(int width, int height,
2166 int *tilesize, int *xextent, int *yextent) 2238 int *tilesize, int *xextent, int *yextent)
2167{ 2239{
@@ -2179,16 +2251,10 @@ static grid *grid_new_octagonal(int width, int height, const char *desc)
2179 int a = OCTAGONAL_A; 2251 int a = OCTAGONAL_A;
2180 int b = OCTAGONAL_B; 2252 int b = OCTAGONAL_B;
2181 2253
2182 /* Upper bounds - don't have to be exact */
2183 int max_faces = 2 * width * height;
2184 int max_dots = 4 * (width + 1) * (height + 1);
2185
2186 tree234 *points; 2254 tree234 *points;
2187 2255
2188 grid *g = grid_empty(); 2256 grid *g = grid_empty();
2189 g->tilesize = OCTAGONAL_TILESIZE; 2257 g->tilesize = OCTAGONAL_TILESIZE;
2190 g->faces = snewn(max_faces, grid_face);
2191 g->dots = snewn(max_dots, grid_dot);
2192 2258
2193 points = newtree234(grid_point_cmp_fn); 2259 points = newtree234(grid_point_cmp_fn);
2194 2260
@@ -2233,8 +2299,6 @@ static grid *grid_new_octagonal(int width, int height, const char *desc)
2233 } 2299 }
2234 2300
2235 freetree234(points); 2301 freetree234(points);
2236 assert(g->num_faces <= max_faces);
2237 assert(g->num_dots <= max_dots);
2238 2302
2239 grid_make_consistent(g); 2303 grid_make_consistent(g);
2240 return g; 2304 return g;
@@ -2245,6 +2309,18 @@ static grid *grid_new_octagonal(int width, int height, const char *desc)
2245#define KITE_A 15 2309#define KITE_A 15
2246#define KITE_B 26 2310#define KITE_B 26
2247 2311
2312static const char *grid_validate_params_kites(int width, int height)
2313{
2314 int a = KITE_A;
2315 int b = KITE_B;
2316
2317 if (width > (INT_MAX - 2*b) / (4*b) || /* xextent */
2318 height - 1 > (INT_MAX - 8*a) / (6*a) || /* yextent */
2319 width + 1 > INT_MAX / 6 / (height + 1)) /* max_dots */
2320 return "Grid size must not be unreasonably large";
2321 return NULL;
2322}
2323
2248static void grid_size_kites(int width, int height, 2324static void grid_size_kites(int width, int height,
2249 int *tilesize, int *xextent, int *yextent) 2325 int *tilesize, int *xextent, int *yextent)
2250{ 2326{
@@ -2262,16 +2338,10 @@ static grid *grid_new_kites(int width, int height, const char *desc)
2262 int a = KITE_A; 2338 int a = KITE_A;
2263 int b = KITE_B; 2339 int b = KITE_B;
2264 2340
2265 /* Upper bounds - don't have to be exact */
2266 int max_faces = 6 * width * height;
2267 int max_dots = 6 * (width + 1) * (height + 1);
2268
2269 tree234 *points; 2341 tree234 *points;
2270 2342
2271 grid *g = grid_empty(); 2343 grid *g = grid_empty();
2272 g->tilesize = KITE_TILESIZE; 2344 g->tilesize = KITE_TILESIZE;
2273 g->faces = snewn(max_faces, grid_face);
2274 g->dots = snewn(max_dots, grid_dot);
2275 2345
2276 points = newtree234(grid_point_cmp_fn); 2346 points = newtree234(grid_point_cmp_fn);
2277 2347
@@ -2353,8 +2423,6 @@ static grid *grid_new_kites(int width, int height, const char *desc)
2353 } 2423 }
2354 2424
2355 freetree234(points); 2425 freetree234(points);
2356 assert(g->num_faces <= max_faces);
2357 assert(g->num_dots <= max_dots);
2358 2426
2359 grid_make_consistent(g); 2427 grid_make_consistent(g);
2360 return g; 2428 return g;
@@ -2367,6 +2435,20 @@ static grid *grid_new_kites(int width, int height, const char *desc)
2367#define FLORET_PX 75 2435#define FLORET_PX 75
2368#define FLORET_PY -26 2436#define FLORET_PY -26
2369 2437
2438static const char *grid_validate_params_floret(int width, int height)
2439{
2440 int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
2441 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
2442 int ry = qy-py;
2443 /* rx unused in determining grid size. */
2444
2445 if (width - 1 > (INT_MAX - (4*qx + 2*px)) / ((6*px+3*qx)/2) ||/* xextent */
2446 height - 1 > (INT_MAX - (4*qy + 2*ry)) / (5*qy-4*py) || /* yextent */
2447 width + 1 > INT_MAX / 9 / (height + 1)) /* max_dots */
2448 return "Grid size must not be unreasonably large";
2449 return NULL;
2450}
2451
2370static void grid_size_floret(int width, int height, 2452static void grid_size_floret(int width, int height,
2371 int *tilesize, int *xextent, int *yextent) 2453 int *tilesize, int *xextent, int *yextent)
2372{ 2454{
@@ -2393,16 +2475,10 @@ static grid *grid_new_floret(int width, int height, const char *desc)
2393 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */ 2475 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
2394 int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */ 2476 int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */
2395 2477
2396 /* Upper bounds - don't have to be exact */
2397 int max_faces = 6 * width * height;
2398 int max_dots = 9 * (width + 1) * (height + 1);
2399
2400 tree234 *points; 2478 tree234 *points;
2401 2479
2402 grid *g = grid_empty(); 2480 grid *g = grid_empty();
2403 g->tilesize = FLORET_TILESIZE; 2481 g->tilesize = FLORET_TILESIZE;
2404 g->faces = snewn(max_faces, grid_face);
2405 g->dots = snewn(max_dots, grid_dot);
2406 2482
2407 points = newtree234(grid_point_cmp_fn); 2483 points = newtree234(grid_point_cmp_fn);
2408 2484
@@ -2463,8 +2539,6 @@ static grid *grid_new_floret(int width, int height, const char *desc)
2463 } 2539 }
2464 2540
2465 freetree234(points); 2541 freetree234(points);
2466 assert(g->num_faces <= max_faces);
2467 assert(g->num_dots <= max_dots);
2468 2542
2469 grid_make_consistent(g); 2543 grid_make_consistent(g);
2470 return g; 2544 return g;
@@ -2476,6 +2550,18 @@ static grid *grid_new_floret(int width, int height, const char *desc)
2476#define DODEC_A 15 2550#define DODEC_A 15
2477#define DODEC_B 26 2551#define DODEC_B 26
2478 2552
2553static const char *grid_validate_params_dodecagonal(int width, int height)
2554{
2555 int a = DODEC_A;
2556 int b = DODEC_B;
2557
2558 if (width - 1 > (INT_MAX - 3*(2*a + b)) / (4*a + 2*b) || /* xextent */
2559 height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 2*b) || /* yextent */
2560 width > INT_MAX / 14 / height) /* max_dots */
2561 return "Grid size must not be unreasonably large";
2562 return NULL;
2563}
2564
2479static void grid_size_dodecagonal(int width, int height, 2565static void grid_size_dodecagonal(int width, int height,
2480 int *tilesize, int *xextent, int *yextent) 2566 int *tilesize, int *xextent, int *yextent)
2481{ 2567{
@@ -2493,16 +2579,10 @@ static grid *grid_new_dodecagonal(int width, int height, const char *desc)
2493 int a = DODEC_A; 2579 int a = DODEC_A;
2494 int b = DODEC_B; 2580 int b = DODEC_B;
2495 2581
2496 /* Upper bounds - don't have to be exact */
2497 int max_faces = 3 * width * height;
2498 int max_dots = 14 * width * height;
2499
2500 tree234 *points; 2582 tree234 *points;
2501 2583
2502 grid *g = grid_empty(); 2584 grid *g = grid_empty();
2503 g->tilesize = DODEC_TILESIZE; 2585 g->tilesize = DODEC_TILESIZE;
2504 g->faces = snewn(max_faces, grid_face);
2505 g->dots = snewn(max_dots, grid_dot);
2506 2586
2507 points = newtree234(grid_point_cmp_fn); 2587 points = newtree234(grid_point_cmp_fn);
2508 2588
@@ -2549,13 +2629,23 @@ static grid *grid_new_dodecagonal(int width, int height, const char *desc)
2549 } 2629 }
2550 2630
2551 freetree234(points); 2631 freetree234(points);
2552 assert(g->num_faces <= max_faces);
2553 assert(g->num_dots <= max_dots);
2554 2632
2555 grid_make_consistent(g); 2633 grid_make_consistent(g);
2556 return g; 2634 return g;
2557} 2635}
2558 2636
2637static const char *grid_validate_params_greatdodecagonal(int width, int height)
2638{
2639 int a = DODEC_A;
2640 int b = DODEC_B;
2641
2642 if (width - 1 > (INT_MAX - (2*(2*a + b) + 3*a + b)) / (6*a + 2*b) ||
2643 height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 3*b) || /* yextent */
2644 width > INT_MAX / 200 / height) /* max_dots */
2645 return "Grid size must not be unreasonably large";
2646 return NULL;
2647}
2648
2559static void grid_size_greatdodecagonal(int width, int height, 2649static void grid_size_greatdodecagonal(int width, int height,
2560 int *tilesize, int *xextent, int *yextent) 2650 int *tilesize, int *xextent, int *yextent)
2561{ 2651{
@@ -2574,16 +2664,10 @@ static grid *grid_new_greatdodecagonal(int width, int height, const char *desc)
2574 int a = DODEC_A; 2664 int a = DODEC_A;
2575 int b = DODEC_B; 2665 int b = DODEC_B;
2576 2666
2577 /* Upper bounds - don't have to be exact */
2578 int max_faces = 30 * width * height;
2579 int max_dots = 200 * width * height;
2580
2581 tree234 *points; 2667 tree234 *points;
2582 2668
2583 grid *g = grid_empty(); 2669 grid *g = grid_empty();
2584 g->tilesize = DODEC_TILESIZE; 2670 g->tilesize = DODEC_TILESIZE;
2585 g->faces = snewn(max_faces, grid_face);
2586 g->dots = snewn(max_dots, grid_dot);
2587 2671
2588 points = newtree234(grid_point_cmp_fn); 2672 points = newtree234(grid_point_cmp_fn);
2589 2673
@@ -2663,13 +2747,24 @@ static grid *grid_new_greatdodecagonal(int width, int height, const char *desc)
2663 } 2747 }
2664 2748
2665 freetree234(points); 2749 freetree234(points);
2666 assert(g->num_faces <= max_faces);
2667 assert(g->num_dots <= max_dots);
2668 2750
2669 grid_make_consistent(g); 2751 grid_make_consistent(g);
2670 return g; 2752 return g;
2671} 2753}
2672 2754
2755static const char *grid_validate_params_greatgreatdodecagonal(
2756 int width, int height)
2757{
2758 int a = DODEC_A;
2759 int b = DODEC_B;
2760
2761 if (width-1 > (INT_MAX - (2*(2*a + b) + 2*a + 2*b)) / (4*a + 4*b) ||
2762 height-1 > (INT_MAX - 2*(2*a + b)) / (6*a + 2*b) || /* yextent */
2763 width > INT_MAX / 300 / height) /* max_dots */
2764 return "Grid size must not be unreasonably large";
2765 return NULL;
2766}
2767
2673static void grid_size_greatgreatdodecagonal(int width, int height, 2768static void grid_size_greatgreatdodecagonal(int width, int height,
2674 int *tilesize, int *xextent, int *yextent) 2769 int *tilesize, int *xextent, int *yextent)
2675{ 2770{
@@ -2688,16 +2783,10 @@ static grid *grid_new_greatgreatdodecagonal(int width, int height, const char *d
2688 int a = DODEC_A; 2783 int a = DODEC_A;
2689 int b = DODEC_B; 2784 int b = DODEC_B;
2690 2785
2691 /* Upper bounds - don't have to be exact */
2692 int max_faces = 50 * width * height;
2693 int max_dots = 300 * width * height;
2694
2695 tree234 *points; 2786 tree234 *points;
2696 2787
2697 grid *g = grid_empty(); 2788 grid *g = grid_empty();
2698 g->tilesize = DODEC_TILESIZE; 2789 g->tilesize = DODEC_TILESIZE;
2699 g->faces = snewn(max_faces, grid_face);
2700 g->dots = snewn(max_dots, grid_dot);
2701 2790
2702 points = newtree234(grid_point_cmp_fn); 2791 points = newtree234(grid_point_cmp_fn);
2703 2792
@@ -2747,7 +2836,7 @@ static grid *grid_new_greatgreatdodecagonal(int width, int height, const char *d
2747 d = grid_get_dot(g, points, px + (2*a + 2*b), py - 2*a); grid_face_set_dot(g, d, 5); 2836 d = grid_get_dot(g, points, px + (2*a + 2*b), py - 2*a); grid_face_set_dot(g, d, 5);
2748 } 2837 }
2749 2838
2750 /* hexagon on bottom right of dodecagon */ 2839 /* hexagon on bottom right of dodecagon */
2751 if ((y < height - 1) && (x < width - 1 || !(y % 2))) { 2840 if ((y < height - 1) && (x < width - 1 || !(y % 2))) {
2752 grid_face_add_new(g, 6); 2841 grid_face_add_new(g, 6);
2753 d = grid_get_dot(g, points, px + (a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 0); 2842 d = grid_get_dot(g, points, px + (a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 0);
@@ -2832,29 +2921,165 @@ static grid *grid_new_greatgreatdodecagonal(int width, int height, const char *d
2832 } 2921 }
2833 2922
2834 freetree234(points); 2923 freetree234(points);
2835 assert(g->num_faces <= max_faces);
2836 assert(g->num_dots <= max_dots);
2837 2924
2838 grid_make_consistent(g); 2925 grid_make_consistent(g);
2839 return g; 2926 return g;
2840} 2927}
2841 2928
2842typedef struct setface_ctx 2929static const char *grid_validate_params_compassdodecagonal(
2930 int width, int height)
2931{
2932 int a = DODEC_A;
2933 int b = DODEC_B;
2934
2935 if (width > INT_MAX / (4*a + 2*b) || /* xextent */
2936 height > INT_MAX / (4*a + 2*b) || /* yextent */
2937 width > INT_MAX / 18 / height) /* max_dots */
2938 return "Grid must not be unreasonably large";
2939 return NULL;
2940}
2941
2942static void grid_size_compassdodecagonal(int width, int height,
2943 int *tilesize, int *xextent, int *yextent)
2944{
2945 int a = DODEC_A;
2946 int b = DODEC_B;
2947
2948 *tilesize = DODEC_TILESIZE;
2949 *xextent = (4*a + 2*b) * width;
2950 *yextent = (4*a + 2*b) * height;
2951}
2952
2953static grid *grid_new_compassdodecagonal(int width, int height, const char *desc)
2843{ 2954{
2955 int x, y;
2956 /* Vector for side of triangle - ratio is close to sqrt(3) */
2957 int a = DODEC_A;
2958 int b = DODEC_B;
2959
2960 tree234 *points;
2961
2962 grid *g = grid_empty();
2963 g->tilesize = DODEC_TILESIZE;
2964
2965 points = newtree234(grid_point_cmp_fn);
2966
2967 for (y = 0; y < height; y++) {
2968 for (x = 0; x < width; x++) {
2969 grid_dot *d;
2970 /* centre of dodecagon */
2971 int px = (4*a + 2*b) * x;
2972 int py = (4*a + 2*b) * y;
2973
2974 /* dodecagon */
2975 grid_face_add_new(g, 12);
2976 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2977 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
2978 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
2979 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
2980 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
2981 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
2982 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
2983 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
2984 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
2985 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
2986 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
2987 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
2988
2989 if (x < width - 1 && y < height - 1) {
2990 /* north triangle */
2991 grid_face_add_new(g, 3);
2992 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 0);
2993 d = grid_get_dot(g, points, px + (3*a + b), py + ( a + b)); grid_face_set_dot(g, d, 1);
2994 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 2);
2995
2996 /* east triangle */
2997 grid_face_add_new(g, 3);
2998 d = grid_get_dot(g, points, px + (3*a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 0);
2999 d = grid_get_dot(g, points, px + (3*a + b), py + (3*a + b)); grid_face_set_dot(g, d, 1);
3000 d = grid_get_dot(g, points, px + (3*a + b), py + ( a + b)); grid_face_set_dot(g, d, 2);
3001
3002 /* south triangle */
3003 grid_face_add_new(g, 3);
3004 d = grid_get_dot(g, points, px + (3*a + b), py + (3*a + b)); grid_face_set_dot(g, d, 0);
3005 d = grid_get_dot(g, points, px + (2*a + b), py + (3*a + 2*b)); grid_face_set_dot(g, d, 1);
3006 d = grid_get_dot(g, points, px + ( a + b), py + (3*a + b)); grid_face_set_dot(g, d, 2);
3007
3008 /* west triangle */
3009 grid_face_add_new(g, 3);
3010 d = grid_get_dot(g, points, px + (a + b), py + ( a + b)); grid_face_set_dot(g, d, 0);
3011 d = grid_get_dot(g, points, px + (a + b), py + (3*a + b)); grid_face_set_dot(g, d, 1);
3012 d = grid_get_dot(g, points, px + (a ), py + (2*a + b)); grid_face_set_dot(g, d, 2);
3013
3014 /* square in center */
3015 grid_face_add_new(g, 4);
3016 d = grid_get_dot(g, points, px + (3*a + b), py + ( a + b)); grid_face_set_dot(g, d, 0);
3017 d = grid_get_dot(g, points, px + (3*a + b), py + (3*a + b)); grid_face_set_dot(g, d, 1);
3018 d = grid_get_dot(g, points, px + ( a + b), py + (3*a + b)); grid_face_set_dot(g, d, 2);
3019 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 3);
3020 }
3021 }
3022 }
3023
3024 freetree234(points);
3025
3026 grid_make_consistent(g);
3027 return g;
3028}
3029
3030/*
3031 * Penrose tilings. For historical reasons, we support two totally
3032 * different generation algorithms: the legacy one is only supported
3033 * by grid_new_penrose, for backwards compatibility with game
3034 * descriptions generated before we switched. New grid generation uses
3035 * only the new system.
3036 */
3037
3038#define PENROSE_TILESIZE 100
3039
3040static const char *grid_validate_params_penrose(int width, int height)
3041{
3042 int l = PENROSE_TILESIZE;
3043
3044 if (width > INT_MAX / l || /* xextent */
3045 height > INT_MAX / l || /* yextent */
3046 width > INT_MAX / (3 * 3 * 4 * height)) /* max_dots */
3047 return "Grid must not be unreasonably large";
3048 return NULL;
3049}
3050
3051static void grid_size_penrose(int width, int height,
3052 int *tilesize, int *xextent, int *yextent)
3053{
3054 int l = PENROSE_TILESIZE;
3055
3056 *tilesize = l;
3057 *xextent = l * width;
3058 *yextent = l * height;
3059}
3060
3061/*
3062 * Legacy generation by selecting a patch of tiling from the expansion
3063 * of a big triangle.
3064 */
3065
3066typedef struct penrose_legacy_set_faces_ctx {
2844 int xmin, xmax, ymin, ymax; 3067 int xmin, xmax, ymin, ymax;
2845 3068
2846 grid *g; 3069 grid *g;
2847 tree234 *points; 3070 tree234 *points;
2848} setface_ctx; 3071} penrose_legacy_set_faces_ctx;
2849 3072
2850static double round_int_nearest_away(double r) 3073static double round_int_nearest_away(double r)
2851{ 3074{
2852 return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); 3075 return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
2853} 3076}
2854 3077
2855static int set_faces(penrose_state *state, vector *vs, int n, int depth) 3078static int penrose_legacy_set_faces(penrose_legacy_state *state, vector *vs,
3079 int n, int depth)
2856{ 3080{
2857 setface_ctx *sf_ctx = (setface_ctx *)state->ctx; 3081 penrose_legacy_set_faces_ctx *sf_ctx =
3082 (penrose_legacy_set_faces_ctx *)state->ctx;
2858 int i; 3083 int i;
2859 int xs[4], ys[4]; 3084 int xs[4], ys[4];
2860 3085
@@ -2864,7 +3089,7 @@ static int set_faces(penrose_state *state, vector *vs, int n, int depth)
2864#endif 3089#endif
2865 3090
2866 for (i = 0; i < n; i++) { 3091 for (i = 0; i < n; i++) {
2867 double tx = v_x(vs, i), ty = v_y(vs, i); 3092 double tx = penrose_legacy_vx(vs, i), ty = penrose_legacy_vy(vs, i);
2868 3093
2869 xs[i] = (int)round_int_nearest_away(tx); 3094 xs[i] = (int)round_int_nearest_away(tx);
2870 ys[i] = (int)round_int_nearest_away(ty); 3095 ys[i] = (int)round_int_nearest_away(ty);
@@ -2875,88 +3100,24 @@ static int set_faces(penrose_state *state, vector *vs, int n, int depth)
2875 3100
2876 grid_face_add_new(sf_ctx->g, n); 3101 grid_face_add_new(sf_ctx->g, n);
2877 debug(("penrose: new face l=%f gen=%d...", 3102 debug(("penrose: new face l=%f gen=%d...",
2878 penrose_side_length(state->start_size, depth), depth)); 3103 penrose_legacy_side_length(state->start_size, depth), depth));
2879 for (i = 0; i < n; i++) { 3104 for (i = 0; i < n; i++) {
2880 grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points, 3105 grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points,
2881 xs[i], ys[i]); 3106 xs[i], ys[i]);
2882 grid_face_set_dot(sf_ctx->g, d, i); 3107 grid_face_set_dot(sf_ctx->g, d, i);
2883 debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)", 3108 debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)",
2884 d, d->x, d->y, v_x(vs, i), v_y(vs, i))); 3109 d, d->x, d->y, penrose_legacy_vx(vs, i),
3110 penrose_legacy_vy(vs, i)));
2885 } 3111 }
2886 3112
2887 return 0; 3113 return 0;
2888} 3114}
2889 3115
2890#define PENROSE_TILESIZE 100 3116static grid *grid_new_penrose_legacy(int width, int height, int which,
2891 3117 const char *desc);
2892static void grid_size_penrose(int width, int height,
2893 int *tilesize, int *xextent, int *yextent)
2894{
2895 int l = PENROSE_TILESIZE;
2896
2897 *tilesize = l;
2898 *xextent = l * width;
2899 *yextent = l * height;
2900}
2901
2902static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */
2903
2904static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
2905{
2906 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
2907 double outer_radius;
2908 int inner_radius;
2909 char gd[255];
2910 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
2911 grid *g;
2912
2913 while (1) {
2914 /* We want to produce a random bit of penrose tiling, so we
2915 * calculate a random offset (within the patch that penrose.c
2916 * calculates for us) and an angle (multiple of 36) to rotate
2917 * the patch. */
2918
2919 penrose_calculate_size(which, tilesize, width, height,
2920 &outer_radius, &startsz, &depth);
2921
2922 /* Calculate radius of (circumcircle of) patch, subtract from
2923 * radius calculated. */
2924 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2925
2926 /* Pick a random offset (the easy way: choose within outer
2927 * square, discarding while it's outside the circle) */
2928 do {
2929 xoff = random_upto(rs, 2*inner_radius) - inner_radius;
2930 yoff = random_upto(rs, 2*inner_radius) - inner_radius;
2931 } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
2932
2933 aoff = random_upto(rs, 360/36) * 36;
2934
2935 debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
2936 tilesize, width, height, outer_radius, inner_radius));
2937 debug((" -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
2938
2939 sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
2940
2941 /*
2942 * Now test-generate our grid, to make sure it actually
2943 * produces something.
2944 */
2945 g = grid_new_penrose(width, height, which, gd);
2946 if (g) {
2947 grid_free(g);
2948 break;
2949 }
2950 /* If not, go back to the top of this while loop and try again
2951 * with a different random offset. */
2952 }
2953
2954 return dupstr(gd);
2955}
2956 3118
2957static const char *grid_validate_desc_penrose(grid_type type, 3119static const char *grid_validate_desc_penrose_legacy(
2958 int width, int height, 3120 grid_type type, int width, int height, const char *desc)
2959 const char *desc)
2960{ 3121{
2961 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius; 3122 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
2962 double outer_radius; 3123 double outer_radius;
@@ -2966,8 +3127,8 @@ static const char *grid_validate_desc_penrose(grid_type type,
2966 if (!desc) 3127 if (!desc)
2967 return "Missing grid description string."; 3128 return "Missing grid description string.";
2968 3129
2969 penrose_calculate_size(which, tilesize, width, height, 3130 penrose_legacy_calculate_size(which, tilesize, width, height,
2970 &outer_radius, &startsz, &depth); 3131 &outer_radius, &startsz, &depth);
2971 inner_radius = (int)(outer_radius - sqrt(width*width + height*height)); 3132 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2972 3133
2973 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3) 3134 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
@@ -2982,7 +3143,7 @@ static const char *grid_validate_desc_penrose(grid_type type,
2982 * Test-generate to ensure these parameters don't end us up with 3143 * Test-generate to ensure these parameters don't end us up with
2983 * no grid at all. 3144 * no grid at all.
2984 */ 3145 */
2985 g = grid_new_penrose(width, height, which, desc); 3146 g = grid_new_penrose_legacy(width, height, which, desc);
2986 if (!g) 3147 if (!g)
2987 return "Patch coordinates do not identify a usable grid fragment"; 3148 return "Patch coordinates do not identify a usable grid fragment";
2988 grid_free(g); 3149 grid_free(g);
@@ -2990,40 +3151,30 @@ static const char *grid_validate_desc_penrose(grid_type type,
2990 return NULL; 3151 return NULL;
2991} 3152}
2992 3153
2993/* 3154static grid *grid_new_penrose_legacy(int width, int height, int which,
2994 * We're asked for a grid of a particular size, and we generate enough 3155 const char *desc)
2995 * of the tiling so we can be sure to have enough random grid from which
2996 * to pick.
2997 */
2998
2999static grid *grid_new_penrose(int width, int height, int which, const char *desc)
3000{ 3156{
3001 int max_faces, max_dots, tilesize = PENROSE_TILESIZE; 3157 int tilesize = PENROSE_TILESIZE;
3002 int xsz, ysz, xoff, yoff, aoff; 3158 int xsz, ysz, xoff, yoff, aoff;
3003 double rradius; 3159 double rradius;
3004 3160
3005 tree234 *points; 3161 tree234 *points;
3006 grid *g; 3162 grid *g;
3007 3163
3008 penrose_state ps; 3164 penrose_legacy_state ps;
3009 setface_ctx sf_ctx; 3165 penrose_legacy_set_faces_ctx sf_ctx;
3010 3166
3011 penrose_calculate_size(which, tilesize, width, height, 3167 penrose_legacy_calculate_size(which, tilesize, width, height,
3012 &rradius, &ps.start_size, &ps.max_depth); 3168 &rradius, &ps.start_size, &ps.max_depth);
3013 3169
3014 debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d", 3170 debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d",
3015 width, height, tilesize, ps.start_size, ps.max_depth)); 3171 width, height, tilesize, ps.start_size, ps.max_depth));
3016 3172
3017 ps.new_tile = set_faces; 3173 ps.new_tile = penrose_legacy_set_faces;
3018 ps.ctx = &sf_ctx; 3174 ps.ctx = &sf_ctx;
3019 3175
3020 max_faces = (width*3) * (height*3); /* somewhat paranoid... */
3021 max_dots = max_faces * 4; /* ditto... */
3022
3023 g = grid_empty(); 3176 g = grid_empty();
3024 g->tilesize = tilesize; 3177 g->tilesize = tilesize;
3025 g->faces = snewn(max_faces, grid_face);
3026 g->dots = snewn(max_dots, grid_dot);
3027 3178
3028 points = newtree234(grid_point_cmp_fn); 3179 points = newtree234(grid_point_cmp_fn);
3029 3180
@@ -3051,11 +3202,9 @@ static grid *grid_new_penrose(int width, int height, int which, const char *desc
3051 debug(("penrose: x range (%f --> %f), y range (%f --> %f)", 3202 debug(("penrose: x range (%f --> %f), y range (%f --> %f)",
3052 sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax)); 3203 sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax));
3053 3204
3054 penrose(&ps, which, aoff); 3205 penrose_legacy(&ps, which, aoff);
3055 3206
3056 freetree234(points); 3207 freetree234(points);
3057 assert(g->num_faces <= max_faces);
3058 assert(g->num_dots <= max_dots);
3059 3208
3060 debug(("penrose: %d faces total (equivalent to %d wide by %d high)", 3209 debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
3061 g->num_faces, g->num_faces/height, g->num_faces/width)); 3210 g->num_faces, g->num_faces/height, g->num_faces/width));
@@ -3091,6 +3240,223 @@ static grid *grid_new_penrose(int width, int height, int which, const char *desc
3091 return g; 3240 return g;
3092} 3241}
3093 3242
3243/*
3244 * Combinatorial-coordinate generation.
3245 *
3246 * We receive coordinates from the generator in the form of x,y pairs
3247 * each of which is an integer combination of 1 and sqrt(5), but those
3248 * pairs have different scale units in the x and y directions. The
3249 * scale units are 1/4 for x and sin(pi/5)/2 for y, which makes their
3250 * ratio equal to 2 sin(pi/5) ~= 1.1756. We fudge that irrational
3251 * aspect ratio into a rational approximation, by simply taking a pair
3252 * of integer scale factors for the x and y dimensions; this distorts
3253 * the output tiling slightly, but the distortion is consistent, and
3254 * doesn't accumulate over a large patch of tiling, so it won't make
3255 * anything end up totally out of place.
3256 *
3257 * (However, we compute the subsequent combination of 1 and sqrt(5)
3258 * exactly, because using an approximation to sqrt(5) _could_ mean
3259 * that in a sufficiently large patch of tiling two such combinations
3260 * ended up misordered.)
3261 *
3262 * Adding to the confusion, we also flip round the x and y
3263 * coordinates, because it's slightly nicer to have vertical edges in
3264 * the tiling rather than horizontal ones. (Both for aesthetics, and
3265 * also because if two P3 thin rhombs are separated by a horizontal
3266 * line and both contain numeric clues then the clue numbers look a
3267 * bit crowded, due to digits being taller than they are wide.)
3268 *
3269 * Finally, we have different base unit sizes for the two tiling
3270 * types, because sensible sizes for the two are actually different.
3271 * Each of P2 and P3 can be subdivided into the other, via dividing
3272 * the larger triangle type in two, so that L large and S small become
3273 * L+S large and L small. In the limit, this means that you expect the
3274 * number of triangles (hence tiles) to grow by a factor of phi in
3275 * each of those subdivisions (and hence by a factor of phi^2 in a
3276 * full subdivision of P2 to a finer P2). So a sensible size ratio
3277 * between the two tilings is one that makes them fit about the same
3278 * number of tiles into the same area - and since tile area is
3279 * proportional to _square_ of length, it follows that the P2 and P3
3280 * length unit should differ by a factor of sqrt(phi).
3281 */
3282#define PENROSE_XUNIT_P2 37
3283#define PENROSE_YUNIT_P2 44
3284#define PENROSE_XUNIT_P3 30
3285#define PENROSE_YUNIT_P3 35
3286
3287struct size { int w, h; };
3288static struct size api_size_penrose(int width, int height, int which)
3289{
3290 int xunit = (which == PENROSE_P2 ? PENROSE_XUNIT_P2 : PENROSE_XUNIT_P3);
3291 int yunit = (which == PENROSE_P2 ? PENROSE_YUNIT_P2 : PENROSE_YUNIT_P3);
3292 struct size size = {
3293 width * PENROSE_TILESIZE / yunit,
3294 height * PENROSE_TILESIZE / xunit,
3295 };
3296 return size;
3297}
3298
3299static grid *grid_new_penrose(int width, int height, int which,
3300 const char *desc); /* forward reference */
3301
3302static char *grid_new_desc_penrose(grid_type type, int width, int height,
3303 random_state *rs)
3304{
3305 char *buf;
3306 struct PenrosePatchParams params;
3307 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
3308 struct size size = api_size_penrose(width, height, which);
3309
3310 penrose_tiling_randomise(&params, which, size.h, size.w, rs);
3311
3312 buf = snewn(params.ncoords + 3, char);
3313 buf[0] = '0' + params.orientation;
3314 buf[1] = '0' + params.start_vertex;
3315 memcpy(buf + 2, params.coords, params.ncoords);
3316 buf[2 + params.ncoords] = '\0';
3317
3318 sfree(params.coords);
3319 return buf;
3320}
3321
3322/* Shared code between validating and reading grid descs.
3323 * Always allocates params->coords, whether or not it returns an error. */
3324static const char *grid_desc_to_penrose_params(
3325 const char *desc, int which, struct PenrosePatchParams *params)
3326{
3327 int i;
3328
3329 if (!*desc)
3330 return "empty grid description";
3331
3332 params->ncoords = strlen(desc) - 2;
3333 params->coords = snewn(params->ncoords, char);
3334
3335 {
3336 char c = desc[0];
3337 if (isdigit((unsigned char)c))
3338 params->orientation = c - '0';
3339 else
3340 return "expected digit at start of grid description";
3341
3342 c = desc[1];
3343 if (c >= '0' && c < '3')
3344 params->start_vertex = c - '0';
3345 else
3346 return "expected digit as second char of grid description";
3347 }
3348
3349 for (i = 0; i < params->ncoords; i++) {
3350 char c = desc[i+2];
3351 if (!penrose_valid_letter(c, which))
3352 return "expected tile letter in grid description";
3353 params->coords[i] = c;
3354 }
3355
3356 return NULL;
3357}
3358
3359static const char *grid_validate_desc_penrose(grid_type type,
3360 int width, int height,
3361 const char *desc)
3362{
3363 struct PenrosePatchParams params;
3364 const char *error = NULL;
3365 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
3366
3367 if (!desc)
3368 return "Missing grid description string.";
3369
3370 if (*desc == 'G')
3371 return grid_validate_desc_penrose_legacy(type, width, height, desc);
3372
3373 error = grid_desc_to_penrose_params(desc, which, &params);
3374 if (!error)
3375 error = penrose_tiling_params_invalid(&params, which);
3376
3377 sfree(params.coords);
3378 return error;
3379}
3380
3381struct penrosecontext {
3382 grid *g;
3383 tree234 *points;
3384 int xunit, yunit;
3385};
3386
3387static void grid_penrose_callback(void *vctx, const int *coords)
3388{
3389 struct penrosecontext *ctx = (struct penrosecontext *)vctx;
3390 size_t i;
3391
3392 grid_face_add_new(ctx->g, 4);
3393 for (i = 0; i < 4; i++) {
3394 grid_dot *d = grid_get_dot(
3395 ctx->g, ctx->points,
3396 coords[4*i+2] * ctx->yunit + n_times_root_k(
3397 coords[4*i+3] * ctx->yunit, 5),
3398 coords[4*i+0] * ctx->xunit + n_times_root_k(
3399 coords[4*i+1] * ctx->xunit, 5));
3400 grid_face_set_dot(ctx->g, d, i);
3401 }
3402}
3403
3404static grid *grid_new_penrose(int width, int height, int which,
3405 const char *desc)
3406{
3407 struct PenrosePatchParams params;
3408 const char *error = NULL;
3409 struct penrosecontext ctx[1];
3410 struct size size;
3411
3412 if (*desc == 'G')
3413 return grid_new_penrose_legacy(width, height, which, desc);
3414
3415 error = grid_desc_to_penrose_params(desc, which, &params);
3416 assert(error == NULL && "grid_validate_desc_penrose should have failed");
3417
3418 ctx->g = grid_empty();
3419 ctx->g->tilesize = PENROSE_TILESIZE;
3420
3421 ctx->points = newtree234(grid_point_cmp_fn);
3422
3423 ctx->xunit = (which == PENROSE_P2 ? PENROSE_XUNIT_P2 : PENROSE_XUNIT_P3);
3424 ctx->yunit = (which == PENROSE_P2 ? PENROSE_YUNIT_P2 : PENROSE_YUNIT_P3);
3425
3426 size = api_size_penrose(width, height, which);
3427 penrose_tiling_generate(&params, size.h, size.w,
3428 grid_penrose_callback, ctx);
3429
3430 freetree234(ctx->points);
3431 sfree(params.coords);
3432
3433 grid_trim_vigorously(ctx->g);
3434 grid_make_consistent(ctx->g);
3435
3436 /*
3437 * Centre the grid in its originally promised rectangle.
3438 */
3439 {
3440 int w = width * PENROSE_TILESIZE, h = height * PENROSE_TILESIZE;
3441 ctx->g->lowest_x -= (w - (ctx->g->highest_x - ctx->g->lowest_x))/2;
3442 ctx->g->lowest_y -= (h - (ctx->g->highest_y - ctx->g->lowest_y))/2;
3443 ctx->g->highest_x = ctx->g->lowest_x + w;
3444 ctx->g->highest_y = ctx->g->lowest_y + h;
3445 }
3446
3447 return ctx->g;
3448}
3449
3450static const char *grid_validate_params_penrose_p2_kite(int width, int height)
3451{
3452 return grid_validate_params_penrose(width, height);
3453}
3454
3455static const char *grid_validate_params_penrose_p3_thick(int width, int height)
3456{
3457 return grid_validate_params_penrose(width, height);
3458}
3459
3094static void grid_size_penrose_p2_kite(int width, int height, 3460static void grid_size_penrose_p2_kite(int width, int height,
3095 int *tilesize, int *xextent, int *yextent) 3461 int *tilesize, int *xextent, int *yextent)
3096{ 3462{
@@ -3113,18 +3479,349 @@ static grid *grid_new_penrose_p3_thick(int width, int height, const char *desc)
3113 return grid_new_penrose(width, height, PENROSE_P3, desc); 3479 return grid_new_penrose(width, height, PENROSE_P3, desc);
3114} 3480}
3115 3481
3482#define HATS_TILESIZE 32
3483#define HATS_XSQUARELEN 4
3484#define HATS_YSQUARELEN 6
3485#define HATS_XUNIT 14
3486#define HATS_YUNIT 8
3487
3488static const char *grid_validate_params_hats(
3489 int width, int height)
3490{
3491 int l = HATS_TILESIZE;
3492
3493 if (width > INT_MAX / l || /* xextent */
3494 height > INT_MAX / l || /* yextent */
3495 width > INT_MAX / (6 * height)) /* max_dots */
3496 return "Grid must not be unreasonably large";
3497 return NULL;
3498}
3499
3500static void grid_size_hats(int width, int height,
3501 int *tilesize, int *xextent, int *yextent)
3502{
3503 *tilesize = HATS_TILESIZE;
3504 *xextent = width * HATS_XUNIT * HATS_XSQUARELEN;
3505 *yextent = height * HATS_YUNIT * HATS_YSQUARELEN;
3506}
3507
3508static char *grid_new_desc_hats(
3509 grid_type type, int width, int height, random_state *rs)
3510{
3511 char *buf, *p;
3512 size_t bufmax, i;
3513 struct HatPatchParams hp;
3514
3515 hat_tiling_randomise(&hp, width, height, rs);
3516
3517 bufmax = 3 * hp.ncoords + 2;
3518 buf = snewn(bufmax, char);
3519 p = buf;
3520 for (i = 0; i < hp.ncoords; i++) {
3521 assert(hp.coords[i] < 100); /* at most 2 digits */
3522 assert(p - buf <= bufmax-4); /* room for 2 digits, comma and NUL */
3523 p += sprintf(p, "%d,", (int)hp.coords[i]);
3524 }
3525 assert(p - buf <= bufmax-2); /* room for final letter and NUL */
3526 p[0] = hp.final_metatile;
3527 p[1] = '\0';
3528
3529 sfree(hp.coords);
3530 return buf;
3531}
3532
3533/* Shared code between validating and reading grid descs.
3534 * Always allocates hp->coords, whether or not it returns an error. */
3535static const char *grid_desc_to_hat_params(
3536 const char *desc, struct HatPatchParams *hp)
3537{
3538 size_t maxcoords;
3539 const char *p = desc;
3540
3541 maxcoords = (strlen(desc) + 1) / 2;
3542 hp->coords = snewn(maxcoords, unsigned char);
3543 hp->ncoords = 0;
3544
3545 while (isdigit((unsigned char)*p)) {
3546 const char *p_orig = p;
3547 int n = atoi(p);
3548 while (*p && isdigit((unsigned char)*p)) p++;
3549 if (*p != ',')
3550 return "expected ',' in grid description";
3551 if (p - p_orig > 2 || n > 0xFF)
3552 return "too-large coordinate in grid description";
3553 p++; /* eat the comma */
3554
3555 /* This assert should be guaranteed by the way we calculated
3556 * maxcoords, so a failure of this check is a bug in this
3557 * function, not an indication of an invalid input string */
3558 assert(hp->ncoords < maxcoords);
3559 hp->coords[hp->ncoords++] = n;
3560 }
3561
3562 if (*p == 'H' || *p == 'T' || *p == 'P' || *p == 'F')
3563 hp->final_metatile = *p;
3564 else
3565 return "invalid character in grid description";
3566
3567 return NULL;
3568}
3569
3570static const char *grid_validate_desc_hats(
3571 grid_type type, int width, int height, const char *desc)
3572{
3573 struct HatPatchParams hp;
3574 const char *error = NULL;
3575
3576 if (!desc)
3577 return "Missing grid description string.";
3578
3579 error = grid_desc_to_hat_params(desc, &hp);
3580 if (!error)
3581 error = hat_tiling_params_invalid(&hp);
3582
3583 sfree(hp.coords);
3584 return error;
3585}
3586
3587struct hatcontext {
3588 grid *g;
3589 tree234 *points;
3590};
3591
3592static void grid_hats_callback(void *vctx, size_t nvertices, int *coords)
3593{
3594 struct hatcontext *ctx = (struct hatcontext *)vctx;
3595 size_t i;
3596
3597 grid_face_add_new(ctx->g, nvertices);
3598 for (i = 0; i < nvertices; i++) {
3599 grid_dot *d = grid_get_dot(
3600 ctx->g, ctx->points,
3601 coords[2*i] * HATS_XUNIT,
3602 coords[2*i+1] * HATS_YUNIT);
3603 grid_face_set_dot(ctx->g, d, i);
3604 }
3605}
3606
3607static grid *grid_new_hats(int width, int height, const char *desc)
3608{
3609 struct HatPatchParams hp;
3610 const char *error = NULL;
3611
3612 error = grid_desc_to_hat_params(desc, &hp);
3613 assert(error == NULL && "grid_validate_desc_hats should have failed");
3614
3615 struct hatcontext ctx[1];
3616
3617 ctx->g = grid_empty();
3618 ctx->g->tilesize = HATS_TILESIZE;
3619
3620 ctx->points = newtree234(grid_point_cmp_fn);
3621
3622 hat_tiling_generate(&hp, width, height, grid_hats_callback, ctx);
3623
3624 freetree234(ctx->points);
3625 sfree(hp.coords);
3626
3627 grid_trim_vigorously(ctx->g);
3628 grid_make_consistent(ctx->g);
3629 return ctx->g;
3630}
3631
3632#define SPECTRE_TILESIZE 32
3633#define SPECTRE_SQUARELEN 7
3634#define SPECTRE_UNIT 8
3635
3636static const char *grid_validate_params_spectres(
3637 int width, int height)
3638{
3639 int l = SPECTRE_UNIT * SPECTRE_SQUARELEN;
3640
3641 if (width > INT_MAX / l || /* xextent */
3642 height > INT_MAX / l || /* yextent */
3643 width > (INT_MAX / SPECTRE_SQUARELEN /
3644 SPECTRE_SQUARELEN / height)) /* max_faces */
3645 return "Grid must not be unreasonably large";
3646 return NULL;
3647}
3648
3649static void grid_size_spectres(int width, int height,
3650 int *tilesize, int *xextent, int *yextent)
3651{
3652 *tilesize = SPECTRE_TILESIZE;
3653 *xextent = width * SPECTRE_UNIT * SPECTRE_SQUARELEN;
3654 *yextent = height * SPECTRE_UNIT * SPECTRE_SQUARELEN;
3655}
3656
3657static char *grid_new_desc_spectres(
3658 grid_type type, int width, int height, random_state *rs)
3659{
3660 char *buf;
3661 size_t i;
3662 struct SpectrePatchParams sp;
3663
3664 spectre_tiling_randomise(&sp, width * SPECTRE_SQUARELEN,
3665 height * SPECTRE_SQUARELEN, rs);
3666
3667 buf = snewn(sp.ncoords + 3, char);
3668 buf[0] = (sp.orientation < 10 ? '0' + sp.orientation :
3669 'A' + sp.orientation - 10);
3670 for (i = 0; i < sp.ncoords; i++) {
3671 assert(sp.coords[i] < 10); /* all indices are 1 digit */
3672 buf[i+1] = '0' + sp.coords[i];
3673 }
3674 buf[sp.ncoords+1] = sp.final_hex;
3675 buf[sp.ncoords+2] = '\0';
3676
3677 sfree(sp.coords);
3678 return buf;
3679}
3680
3681/* Shared code between validating and reading grid descs.
3682 * Always allocates sp->coords, whether or not it returns an error. */
3683static const char *grid_desc_to_spectre_params(
3684 const char *desc, struct SpectrePatchParams *sp)
3685{
3686 size_t i;
3687
3688 if (!*desc)
3689 return "empty grid description";
3690
3691 sp->ncoords = strlen(desc) - 2;
3692 sp->coords = snewn(sp->ncoords, unsigned char);
3693
3694 {
3695 char c = desc[0];
3696 if (isdigit((unsigned char)c))
3697 sp->orientation = c - '0';
3698 else if (c == 'A' || c == 'B')
3699 sp->orientation = 10 + c - 'A';
3700 else
3701 return "expected digit or A,B at start of grid description";
3702 }
3703
3704 for (i = 0; i < sp->ncoords; i++) {
3705 char c = desc[i+1];
3706 if (!isdigit((unsigned char)c))
3707 return "expected digit in grid description";
3708 sp->coords[i] = c - '0';
3709 }
3710
3711 sp->final_hex = desc[sp->ncoords+1];
3712
3713 return NULL;
3714}
3715
3716static const char *grid_validate_desc_spectres(
3717 grid_type type, int width, int height, const char *desc)
3718{
3719 struct SpectrePatchParams sp;
3720 const char *error = NULL;
3721
3722 if (!desc)
3723 return "Missing grid description string.";
3724
3725 error = grid_desc_to_spectre_params(desc, &sp);
3726 if (!error)
3727 error = spectre_tiling_params_invalid(&sp);
3728
3729 sfree(sp.coords);
3730 return error;
3731}
3732
3733struct spectrecontext {
3734 grid *g;
3735 tree234 *points;
3736};
3737
3738static void grid_spectres_callback(void *vctx, const int *coords)
3739{
3740 struct spectrecontext *ctx = (struct spectrecontext *)vctx;
3741 size_t i;
3742
3743 grid_face_add_new(ctx->g, SPECTRE_NVERTICES);
3744 for (i = 0; i < SPECTRE_NVERTICES; i++) {
3745 grid_dot *d = grid_get_dot(
3746 ctx->g, ctx->points,
3747 (coords[4*i+0] * SPECTRE_UNIT +
3748 n_times_root_k(coords[4*i+1] * SPECTRE_UNIT, 3)),
3749 (coords[4*i+2] * SPECTRE_UNIT +
3750 n_times_root_k(coords[4*i+3] * SPECTRE_UNIT, 3)));
3751 grid_face_set_dot(ctx->g, d, i);
3752 }
3753}
3754
3755static grid *grid_new_spectres(int width, int height, const char *desc)
3756{
3757 struct SpectrePatchParams sp;
3758 const char *error = NULL;
3759 int width2 = width * SPECTRE_SQUARELEN;
3760 int height2 = height * SPECTRE_SQUARELEN;
3761
3762 error = grid_desc_to_spectre_params(desc, &sp);
3763 assert(error == NULL && "grid_validate_desc_spectres should have failed");
3764
3765 struct spectrecontext ctx[1];
3766
3767 ctx->g = grid_empty();
3768 ctx->g->tilesize = SPECTRE_TILESIZE;
3769
3770 ctx->points = newtree234(grid_point_cmp_fn);
3771
3772 spectre_tiling_generate(&sp, width2, height2, grid_spectres_callback, ctx);
3773
3774 freetree234(ctx->points);
3775 sfree(sp.coords);
3776
3777 grid_trim_vigorously(ctx->g);
3778 grid_make_consistent(ctx->g);
3779
3780 /*
3781 * As with the Penrose tiling, we're likely to have different
3782 * sized margins due to the lack of a neat grid that this tiling
3783 * fits on. So now we know what tiles we're left with, recentre
3784 * them.
3785 */
3786 {
3787 int w = width2 * SPECTRE_UNIT, h = height2 * SPECTRE_UNIT;
3788 ctx->g->lowest_x -= (w - (ctx->g->highest_x - ctx->g->lowest_x))/2;
3789 ctx->g->lowest_y -= (h - (ctx->g->highest_y - ctx->g->lowest_y))/2;
3790 ctx->g->highest_x = ctx->g->lowest_x + w;
3791 ctx->g->highest_y = ctx->g->lowest_y + h;
3792 }
3793
3794 return ctx->g;
3795}
3796
3116/* ----------- End of grid generators ------------- */ 3797/* ----------- End of grid generators ------------- */
3117 3798
3799#define FNVAL(upper,lower) &grid_validate_params_ ## lower,
3118#define FNNEW(upper,lower) &grid_new_ ## lower, 3800#define FNNEW(upper,lower) &grid_new_ ## lower,
3119#define FNSZ(upper,lower) &grid_size_ ## lower, 3801#define FNSZ(upper,lower) &grid_size_ ## lower,
3120 3802
3803static const char *(*(grid_validate_paramses[]))(int, int) =
3804 { GRIDGEN_LIST(FNVAL) };
3121static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) }; 3805static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
3122static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) }; 3806static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
3123 3807
3808/* Work out if a grid can be made, and complain if not. */
3809
3810const char *grid_validate_params(grid_type type, int width, int height)
3811{
3812 if (width <= 0 || height <= 0)
3813 return "Width and height must both be positive";
3814 return grid_validate_paramses[type](width, height);
3815}
3816
3124char *grid_new_desc(grid_type type, int width, int height, random_state *rs) 3817char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
3125{ 3818{
3126 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) { 3819 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
3127 return grid_new_desc_penrose(type, width, height, rs); 3820 return grid_new_desc_penrose(type, width, height, rs);
3821 } else if (type == GRID_HATS) {
3822 return grid_new_desc_hats(type, width, height, rs);
3823 } else if (type == GRID_SPECTRES) {
3824 return grid_new_desc_spectres(type, width, height, rs);
3128 } else if (type == GRID_TRIANGULAR) { 3825 } else if (type == GRID_TRIANGULAR) {
3129 return dupstr("0"); /* up-to-date version of triangular grid */ 3826 return dupstr("0"); /* up-to-date version of triangular grid */
3130 } else { 3827 } else {
@@ -3137,6 +3834,10 @@ const char *grid_validate_desc(grid_type type, int width, int height,
3137{ 3834{
3138 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) { 3835 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
3139 return grid_validate_desc_penrose(type, width, height, desc); 3836 return grid_validate_desc_penrose(type, width, height, desc);
3837 } else if (type == GRID_HATS) {
3838 return grid_validate_desc_hats(type, width, height, desc);
3839 } else if (type == GRID_SPECTRES) {
3840 return grid_validate_desc_spectres(type, width, height, desc);
3140 } else if (type == GRID_TRIANGULAR) { 3841 } else if (type == GRID_TRIANGULAR) {
3141 return grid_validate_desc_triangular(type, width, height, desc); 3842 return grid_validate_desc_triangular(type, width, height, desc);
3142 } else { 3843 } else {
diff --git a/apps/plugins/puzzles/src/grid.h b/apps/plugins/puzzles/src/grid.h
index 7ca3ff1d5f..77c8dd800f 100644
--- a/apps/plugins/puzzles/src/grid.h
+++ b/apps/plugins/puzzles/src/grid.h
@@ -33,6 +33,7 @@ typedef struct grid_edge grid_edge;
33typedef struct grid_dot grid_dot; 33typedef struct grid_dot grid_dot;
34 34
35struct grid_face { 35struct grid_face {
36 int index; /* index in grid->faces[] where this face appears */
36 int order; /* Number of edges, also the number of dots */ 37 int order; /* Number of edges, also the number of dots */
37 grid_edge **edges; /* edges around this face */ 38 grid_edge **edges; /* edges around this face */
38 grid_dot **dots; /* corners of this face */ 39 grid_dot **dots; /* corners of this face */
@@ -56,8 +57,10 @@ struct grid_face {
56struct grid_edge { 57struct grid_edge {
57 grid_dot *dot1, *dot2; 58 grid_dot *dot1, *dot2;
58 grid_face *face1, *face2; /* Use NULL for the infinite outside face */ 59 grid_face *face1, *face2; /* Use NULL for the infinite outside face */
60 int index; /* index in grid->edges[] where this edge appears */
59}; 61};
60struct grid_dot { 62struct grid_dot {
63 int index; /* index in grid->dots[] where this dot appears */
61 int order; 64 int order;
62 grid_edge **edges; 65 grid_edge **edges;
63 grid_face **faces; /* A NULL grid_face* means infinite outside face */ 66 grid_face **faces; /* A NULL grid_face* means infinite outside face */
@@ -69,11 +72,13 @@ struct grid_dot {
69 int x, y; 72 int x, y;
70}; 73};
71typedef struct grid { 74typedef struct grid {
72 /* These are (dynamically allocated) arrays of all the 75 /* Arrays of all the faces, edges, dots that are in the grid.
73 * faces, edges, dots that are in the grid. */ 76 * The arrays themselves are dynamically allocated, and so is each object
74 int num_faces; grid_face *faces; 77 * inside them. num_foo indicates the number of things actually stored,
75 int num_edges; grid_edge *edges; 78 * and size_foo indicates the allocated size of the array. */
76 int num_dots; grid_dot *dots; 79 int num_faces, size_faces; grid_face **faces;
80 int num_edges, size_edges; grid_edge **edges;
81 int num_dots, size_dots; grid_dot **dots;
77 82
78 /* Cache the bounding-box of the grid, so the drawing-code can quickly 83 /* Cache the bounding-box of the grid, so the drawing-code can quickly
79 * figure out the proper scaling to draw onto a given area. */ 84 * figure out the proper scaling to draw onto a given area. */
@@ -107,13 +112,19 @@ typedef struct grid {
107 A(DODECAGONAL,dodecagonal) \ 112 A(DODECAGONAL,dodecagonal) \
108 A(GREATDODECAGONAL,greatdodecagonal) \ 113 A(GREATDODECAGONAL,greatdodecagonal) \
109 A(GREATGREATDODECAGONAL,greatgreatdodecagonal) \ 114 A(GREATGREATDODECAGONAL,greatgreatdodecagonal) \
115 A(COMPASSDODECAGONAL,compassdodecagonal) \
110 A(PENROSE_P2,penrose_p2_kite) \ 116 A(PENROSE_P2,penrose_p2_kite) \
111 A(PENROSE_P3,penrose_p3_thick) 117 A(PENROSE_P3,penrose_p3_thick) \
118 A(HATS,hats) \
119 A(SPECTRES,spectres) \
120 /* end of list */
112 121
113#define ENUM(upper,lower) GRID_ ## upper, 122#define ENUM(upper,lower) GRID_ ## upper,
114typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type; 123typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
115#undef ENUM 124#undef ENUM
116 125
126const char *grid_validate_params(grid_type type, int width, int height);
127
117/* Free directly after use if non-NULL. Will never contain an underscore 128/* Free directly after use if non-NULL. Will never contain an underscore
118 * (so clients can safely use that as a separator). */ 129 * (so clients can safely use that as a separator). */
119char *grid_new_desc(grid_type type, int width, int height, random_state *rs); 130char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
diff --git a/apps/plugins/puzzles/src/gtk.c b/apps/plugins/puzzles/src/gtk.c
deleted file mode 100644
index 7588ee0dc6..0000000000
--- a/apps/plugins/puzzles/src/gtk.c
+++ /dev/null
@@ -1,4031 +0,0 @@
1/*
2 * gtk.c: GTK front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <stdlib.h>
8#include <time.h>
9#include <stdarg.h>
10#include <string.h>
11#include <errno.h>
12#include <math.h>
13
14#include <sys/time.h>
15#include <sys/resource.h>
16
17#include <gtk/gtk.h>
18#include <gdk/gdkkeysyms.h>
19
20#include <gdk-pixbuf/gdk-pixbuf.h>
21
22#include <gdk/gdkx.h>
23#include <X11/Xlib.h>
24#include <X11/Xutil.h>
25#include <X11/Xatom.h>
26
27#include "puzzles.h"
28
29#if GTK_CHECK_VERSION(2,0,0)
30# define USE_PANGO
31# ifdef PANGO_VERSION_CHECK
32# if PANGO_VERSION_CHECK(1,8,0)
33# define HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
34# endif
35# endif
36#endif
37#if !GTK_CHECK_VERSION(2,4,0)
38# define OLD_FILESEL
39#endif
40#if GTK_CHECK_VERSION(2,8,0)
41# define USE_CAIRO
42# if GTK_CHECK_VERSION(3,0,0) || defined(GDK_DISABLE_DEPRECATED)
43# define USE_CAIRO_WITHOUT_PIXMAP
44# endif
45#endif
46
47#if defined USE_CAIRO && GTK_CHECK_VERSION(2,10,0)
48/* We can only use printing if we are using Cairo for drawing and we
49 have a GTK version >= 2.10 (when GtkPrintOperation was added). */
50# define USE_PRINTING
51# if GTK_CHECK_VERSION(2,18,0)
52/* We can embed the page setup. Before 2.18, we needed to have a
53 separate page setup. */
54# define USE_EMBED_PAGE_SETUP
55# endif
56#endif
57
58#if GTK_CHECK_VERSION(3,0,0)
59/* The old names are still more concise! */
60#define gtk_hbox_new(x,y) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,y)
61#define gtk_vbox_new(x,y) gtk_box_new(GTK_ORIENTATION_VERTICAL,y)
62/* GTK 3 has retired stock button labels */
63#define LABEL_OK "_OK"
64#define LABEL_CANCEL "_Cancel"
65#define LABEL_NO "_No"
66#define LABEL_YES "_Yes"
67#define LABEL_SAVE "_Save"
68#define LABEL_OPEN "_Open"
69#define gtk_button_new_with_our_label gtk_button_new_with_mnemonic
70#else
71#define LABEL_OK GTK_STOCK_OK
72#define LABEL_CANCEL GTK_STOCK_CANCEL
73#define LABEL_NO GTK_STOCK_NO
74#define LABEL_YES GTK_STOCK_YES
75#define LABEL_SAVE GTK_STOCK_SAVE
76#define LABEL_OPEN GTK_STOCK_OPEN
77#define gtk_button_new_with_our_label gtk_button_new_from_stock
78#endif
79
80/* #undef USE_CAIRO */
81/* #define NO_THICK_LINE */
82#ifdef DEBUGGING
83static FILE *debug_fp = NULL;
84
85void dputs(const char *buf)
86{
87 if (!debug_fp) {
88 debug_fp = fopen("debug.log", "w");
89 }
90
91 fputs(buf, stderr);
92
93 if (debug_fp) {
94 fputs(buf, debug_fp);
95 fflush(debug_fp);
96 }
97}
98
99void debug_printf(const char *fmt, ...)
100{
101 char buf[4096];
102 va_list ap;
103
104 va_start(ap, fmt);
105 vsprintf(buf, fmt, ap);
106 dputs(buf);
107 va_end(ap);
108}
109#endif
110
111/* ----------------------------------------------------------------------
112 * Error reporting functions used elsewhere.
113 */
114
115void fatal(const char *fmt, ...)
116{
117 va_list ap;
118
119 fprintf(stderr, "fatal error: ");
120
121 va_start(ap, fmt);
122 vfprintf(stderr, fmt, ap);
123 va_end(ap);
124
125 fprintf(stderr, "\n");
126 exit(1);
127}
128
129/* ----------------------------------------------------------------------
130 * GTK front end to puzzles.
131 */
132
133static void changed_preset(frontend *fe);
134
135struct font {
136#ifdef USE_PANGO
137 PangoFontDescription *desc;
138#else
139 GdkFont *font;
140#endif
141 int type;
142 int size;
143};
144
145/*
146 * An internal API for functions which need to be different for
147 * printing and drawing.
148 */
149struct internal_drawing_api {
150 void (*set_colour)(frontend *fe, int colour);
151#ifdef USE_CAIRO
152 void (*fill)(frontend *fe);
153 void (*fill_preserve)(frontend *fe);
154#endif
155};
156
157/*
158 * This structure holds all the data relevant to a single window.
159 * In principle this would allow us to open multiple independent
160 * puzzle windows, although I can't currently see any real point in
161 * doing so. I'm just coding cleanly because there's no
162 * particularly good reason not to.
163 */
164struct frontend {
165 bool headless; /* true if we're running without GTK, for --screenshot */
166
167 GtkWidget *window;
168 GtkAccelGroup *dummy_accelgroup;
169 GtkWidget *area;
170 GtkWidget *statusbar;
171 GtkWidget *menubar;
172#if GTK_CHECK_VERSION(3,20,0)
173 GtkCssProvider *css_provider;
174#endif
175 guint statusctx;
176 int w, h;
177 midend *me;
178#ifdef USE_CAIRO
179 const float *colours;
180 cairo_t *cr;
181 cairo_surface_t *image;
182#ifndef USE_CAIRO_WITHOUT_PIXMAP
183 GdkPixmap *pixmap;
184#endif
185 GdkColor background; /* for painting outside puzzle area */
186#else
187 GdkPixmap *pixmap;
188 GdkGC *gc;
189 GdkColor *colours;
190 GdkColormap *colmap;
191 int backgroundindex; /* which of colours[] is background */
192#endif
193 int ncolours;
194 int bbox_l, bbox_r, bbox_u, bbox_d;
195 bool timer_active;
196 int timer_id;
197 struct timeval last_time;
198 struct font *fonts;
199 int nfonts, fontsize;
200 config_item *cfg;
201 int cfg_which;
202 bool cfgret;
203 GtkWidget *cfgbox;
204 void *paste_data;
205 int paste_data_len;
206 int pw, ph, ps; /* pixmap size (w, h are area size, s is GDK scale) */
207 int ox, oy; /* offset of pixmap in drawing area */
208#ifdef OLD_FILESEL
209 char *filesel_name;
210#endif
211 GSList *preset_radio;
212 bool preset_threaded;
213 GtkWidget *preset_custom;
214 GtkWidget *copy_menu_item;
215#if !GTK_CHECK_VERSION(3,0,0)
216 bool drawing_area_shrink_pending;
217 bool menubar_is_local;
218#endif
219#if GTK_CHECK_VERSION(3,0,0)
220 /*
221 * This is used to get round an annoying lack of GTK notification
222 * message. If we request a window resize with
223 * gtk_window_resize(), we normally get back a "configure" event
224 * on the window and on its drawing area, and we respond to the
225 * latter by doing an appropriate resize of the puzzle. If the
226 * window is maximised, so that gtk_window_resize() _doesn't_
227 * change its size, then that configure event never shows up. But
228 * if we requested the resize in response to a change of puzzle
229 * parameters (say, the user selected a differently-sized preset
230 * from the menu), then we would still like to be _notified_ that
231 * the window size was staying the same, so that we can respond by
232 * choosing an appropriate tile size for the new puzzle preset in
233 * the existing window size.
234 *
235 * Fortunately, in GTK 3, we may not get a "configure" event on
236 * the drawing area in this situation, but we still get a
237 * "size_allocate" event on the whole window (which, in other
238 * situations when we _do_ get a "configure" on the area, turns up
239 * second). So we treat _that_ event as indicating that if the
240 * "configure" event hasn't already shown up then it's not going
241 * to arrive.
242 *
243 * This flag is where we bookkeep this system. On
244 * gtk_window_resize we set this flag to true; the area's
245 * configure handler sets it back to false; then if that doesn't
246 * happen, the window's size_allocate handler does a fallback
247 * puzzle resize when it sees this flag still set to true.
248 */
249 bool awaiting_resize_ack;
250#endif
251#ifdef USE_CAIRO
252 int printcount, printw, printh;
253 float printscale;
254 bool printsolns, printcolour;
255 int hatch;
256 float hatchthick, hatchspace;
257 drawing *print_dr;
258 document *doc;
259#endif
260#ifdef USE_PRINTING
261 GtkPrintOperation *printop;
262 GtkPrintContext *printcontext;
263 GtkSpinButton *printcount_spin_button, *printw_spin_button,
264 *printh_spin_button, *printscale_spin_button;
265 GtkCheckButton *soln_check_button, *colour_check_button;
266#endif
267 const struct internal_drawing_api *dr_api;
268};
269
270struct blitter {
271#ifdef USE_CAIRO
272 cairo_surface_t *image;
273#else
274 GdkPixmap *pixmap;
275#endif
276 int w, h, x, y;
277};
278
279void get_random_seed(void **randseed, int *randseedsize)
280{
281 struct timeval *tvp = snew(struct timeval);
282 gettimeofday(tvp, NULL);
283 *randseed = (void *)tvp;
284 *randseedsize = sizeof(struct timeval);
285}
286
287void frontend_default_colour(frontend *fe, float *output)
288{
289#if !GTK_CHECK_VERSION(3,0,0)
290 if (!fe->headless) {
291 /*
292 * If we have a widget and it has a style that specifies a
293 * default background colour, use that as the background for
294 * the puzzle drawing area.
295 */
296 GdkColor col = gtk_widget_get_style(fe->window)->bg[GTK_STATE_NORMAL];
297 output[0] = col.red / 65535.0;
298 output[1] = col.green / 65535.0;
299 output[2] = col.blue / 65535.0;
300 }
301#endif
302
303 /*
304 * GTK 3 has decided that there's no such thing as a 'default
305 * background colour' any more, because widget styles might set
306 * the background to something more complicated like a background
307 * image. We don't want to get into overlaying our entire puzzle
308 * on an arbitrary background image, so we'll just make up a
309 * reasonable shade of grey.
310 *
311 * This is also what we do on GTK 2 in headless mode, where we
312 * don't have a widget style to query.
313 */
314 output[0] = output[1] = output[2] = 0.9F;
315}
316
317void gtk_status_bar(void *handle, const char *text)
318{
319 frontend *fe = (frontend *)handle;
320
321 if (fe->headless)
322 return;
323
324 assert(fe->statusbar);
325
326 gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
327 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
328}
329
330/* ----------------------------------------------------------------------
331 * Cairo drawing functions.
332 */
333
334#ifdef USE_CAIRO
335
336static void setup_drawing(frontend *fe)
337{
338 fe->cr = cairo_create(fe->image);
339 cairo_scale(fe->cr, fe->ps, fe->ps);
340 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY);
341 cairo_set_line_width(fe->cr, 1.0);
342 cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE);
343 cairo_set_line_join(fe->cr, CAIRO_LINE_JOIN_ROUND);
344}
345
346static void teardown_drawing(frontend *fe)
347{
348 cairo_destroy(fe->cr);
349 fe->cr = NULL;
350
351#ifndef USE_CAIRO_WITHOUT_PIXMAP
352 if (!fe->headless) {
353 cairo_t *cr = gdk_cairo_create(fe->pixmap);
354 cairo_set_source_surface(cr, fe->image, 0, 0);
355 cairo_rectangle(cr,
356 fe->bbox_l - 1,
357 fe->bbox_u - 1,
358 fe->bbox_r - fe->bbox_l + 2,
359 fe->bbox_d - fe->bbox_u + 2);
360 cairo_fill(cr);
361 cairo_destroy(cr);
362 }
363#endif
364}
365
366static void snaffle_colours(frontend *fe)
367{
368 fe->colours = midend_colours(fe->me, &fe->ncolours);
369}
370
371static void draw_set_colour(frontend *fe, int colour)
372{
373 cairo_set_source_rgb(fe->cr,
374 fe->colours[3*colour + 0],
375 fe->colours[3*colour + 1],
376 fe->colours[3*colour + 2]);
377}
378
379static void print_set_colour(frontend *fe, int colour)
380{
381 float r, g, b;
382
383 print_get_colour(fe->print_dr, colour, fe->printcolour,
384 &(fe->hatch), &r, &g, &b);
385
386 if (fe->hatch < 0)
387 cairo_set_source_rgb(fe->cr, r, g, b);
388}
389
390static void set_window_background(frontend *fe, int colour)
391{
392#if GTK_CHECK_VERSION(3,20,0)
393 char css_buf[512];
394 sprintf(css_buf, ".background { "
395 "background-color: #%02x%02x%02x; }",
396 (unsigned)(fe->colours[3*colour + 0] * 255),
397 (unsigned)(fe->colours[3*colour + 1] * 255),
398 (unsigned)(fe->colours[3*colour + 2] * 255));
399 if (!fe->css_provider)
400 fe->css_provider = gtk_css_provider_new();
401 if (!gtk_css_provider_load_from_data(
402 GTK_CSS_PROVIDER(fe->css_provider), css_buf, -1, NULL))
403 assert(0 && "Couldn't load CSS");
404 gtk_style_context_add_provider(
405 gtk_widget_get_style_context(fe->window),
406 GTK_STYLE_PROVIDER(fe->css_provider),
407 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
408 gtk_style_context_add_provider(
409 gtk_widget_get_style_context(fe->area),
410 GTK_STYLE_PROVIDER(fe->css_provider),
411 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
412#elif GTK_CHECK_VERSION(3,0,0)
413 GdkRGBA rgba;
414 rgba.red = fe->colours[3*colour + 0];
415 rgba.green = fe->colours[3*colour + 1];
416 rgba.blue = fe->colours[3*colour + 2];
417 rgba.alpha = 1.0;
418 gdk_window_set_background_rgba(gtk_widget_get_window(fe->area), &rgba);
419 gdk_window_set_background_rgba(gtk_widget_get_window(fe->window), &rgba);
420#else
421 GdkColormap *colmap;
422
423 colmap = gdk_colormap_get_system();
424 fe->background.red = fe->colours[3*colour + 0] * 65535;
425 fe->background.green = fe->colours[3*colour + 1] * 65535;
426 fe->background.blue = fe->colours[3*colour + 2] * 65535;
427 if (!gdk_colormap_alloc_color(colmap, &fe->background, false, false)) {
428 g_error("couldn't allocate background (#%02x%02x%02x)\n",
429 fe->background.red >> 8, fe->background.green >> 8,
430 fe->background.blue >> 8);
431 }
432 gdk_window_set_background(gtk_widget_get_window(fe->area),
433 &fe->background);
434 gdk_window_set_background(gtk_widget_get_window(fe->window),
435 &fe->background);
436#endif
437}
438
439static PangoLayout *make_pango_layout(frontend *fe)
440{
441 return (pango_cairo_create_layout(fe->cr));
442}
443
444static void draw_pango_layout(frontend *fe, PangoLayout *layout,
445 int x, int y)
446{
447 cairo_move_to(fe->cr, x, y);
448 pango_cairo_show_layout(fe->cr, layout);
449}
450
451static void save_screenshot_png(frontend *fe, const char *screenshot_file)
452{
453 cairo_surface_write_to_png(fe->image, screenshot_file);
454}
455
456static void do_hatch(frontend *fe)
457{
458 double i, x, y, width, height, maxdim;
459
460 /* Get the dimensions of the region to be hatched. */
461 cairo_path_extents(fe->cr, &x, &y, &width, &height);
462
463 maxdim = max(width, height);
464
465 cairo_save(fe->cr);
466
467 /* Set the line color and width. */
468 cairo_set_source_rgb(fe->cr, 0, 0, 0);
469 cairo_set_line_width(fe->cr, fe->hatchthick);
470 /* Clip to the region. */
471 cairo_clip(fe->cr);
472 /* Hatch the bounding area of the fill region. */
473 if (fe->hatch == HATCH_VERT || fe->hatch == HATCH_PLUS) {
474 for (i = 0.0; i <= width; i += fe->hatchspace) {
475 cairo_move_to(fe->cr, i, 0);
476 cairo_rel_line_to(fe->cr, 0, height);
477 }
478 }
479 if (fe->hatch == HATCH_HORIZ || fe->hatch == HATCH_PLUS) {
480 for (i = 0.0; i <= height; i += fe->hatchspace) {
481 cairo_move_to(fe->cr, 0, i);
482 cairo_rel_line_to(fe->cr, width, 0);
483 }
484 }
485 if (fe->hatch == HATCH_SLASH || fe->hatch == HATCH_X) {
486 for (i = -height; i <= width; i += fe->hatchspace * ROOT2) {
487 cairo_move_to(fe->cr, i, 0);
488 cairo_rel_line_to(fe->cr, maxdim, maxdim);
489 }
490 }
491 if (fe->hatch == HATCH_BACKSLASH || fe->hatch == HATCH_X) {
492 for (i = 0.0; i <= width + height; i += fe->hatchspace * ROOT2) {
493 cairo_move_to(fe->cr, i, 0);
494 cairo_rel_line_to(fe->cr, -maxdim, maxdim);
495 }
496 }
497 cairo_stroke(fe->cr);
498
499 cairo_restore(fe->cr);
500}
501
502static void do_draw_fill(frontend *fe)
503{
504 cairo_fill(fe->cr);
505}
506
507static void do_draw_fill_preserve(frontend *fe)
508{
509 cairo_fill_preserve(fe->cr);
510}
511
512static void do_print_fill(frontend *fe)
513{
514 if (fe->hatch < 0)
515 cairo_fill(fe->cr);
516 else
517 do_hatch(fe);
518}
519
520static void do_print_fill_preserve(frontend *fe)
521{
522 if (fe->hatch < 0) {
523 cairo_fill_preserve(fe->cr);
524 } else {
525 cairo_path_t *oldpath;
526 oldpath = cairo_copy_path(fe->cr);
527 do_hatch(fe);
528 cairo_append_path(fe->cr, oldpath);
529 }
530}
531
532static void do_clip(frontend *fe, int x, int y, int w, int h)
533{
534 cairo_new_path(fe->cr);
535 cairo_rectangle(fe->cr, x, y, w, h);
536 cairo_clip(fe->cr);
537}
538
539static void do_unclip(frontend *fe)
540{
541 cairo_reset_clip(fe->cr);
542}
543
544static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
545{
546 cairo_save(fe->cr);
547 cairo_new_path(fe->cr);
548 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE);
549 cairo_rectangle(fe->cr, x, y, w, h);
550 fe->dr_api->fill(fe);
551 cairo_restore(fe->cr);
552}
553
554static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
555{
556 cairo_new_path(fe->cr);
557 cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5);
558 cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5);
559 cairo_stroke(fe->cr);
560}
561
562static void do_draw_thick_line(frontend *fe, float thickness,
563 float x1, float y1, float x2, float y2)
564{
565 cairo_save(fe->cr);
566 cairo_set_line_width(fe->cr, thickness);
567 cairo_new_path(fe->cr);
568 cairo_move_to(fe->cr, x1, y1);
569 cairo_line_to(fe->cr, x2, y2);
570 cairo_stroke(fe->cr);
571 cairo_restore(fe->cr);
572}
573
574static void do_draw_poly(frontend *fe, int *coords, int npoints,
575 int fillcolour, int outlinecolour)
576{
577 int i;
578
579 cairo_new_path(fe->cr);
580 for (i = 0; i < npoints; i++)
581 cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5);
582 cairo_close_path(fe->cr);
583 if (fillcolour >= 0) {
584 fe->dr_api->set_colour(fe, fillcolour);
585 fe->dr_api->fill_preserve(fe);
586 }
587 assert(outlinecolour >= 0);
588 fe->dr_api->set_colour(fe, outlinecolour);
589 cairo_stroke(fe->cr);
590}
591
592static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
593 int fillcolour, int outlinecolour)
594{
595 cairo_new_path(fe->cr);
596 cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI);
597 cairo_close_path(fe->cr); /* Just in case... */
598 if (fillcolour >= 0) {
599 fe->dr_api->set_colour(fe, fillcolour);
600 fe->dr_api->fill_preserve(fe);
601 }
602 assert(outlinecolour >= 0);
603 fe->dr_api->set_colour(fe, outlinecolour);
604 cairo_stroke(fe->cr);
605}
606
607static void setup_blitter(blitter *bl, int w, int h)
608{
609 bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
610}
611
612static void teardown_blitter(blitter *bl)
613{
614 cairo_surface_destroy(bl->image);
615}
616
617static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
618{
619 cairo_t *cr = cairo_create(bl->image);
620
621 cairo_set_source_surface(cr, fe->image, -x, -y);
622 cairo_paint(cr);
623 cairo_destroy(cr);
624}
625
626static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
627{
628 cairo_set_source_surface(fe->cr, bl->image, x, y);
629 cairo_paint(fe->cr);
630}
631
632static void clear_backing_store(frontend *fe)
633{
634 fe->image = NULL;
635}
636
637static void wipe_and_maybe_destroy_cairo(frontend *fe, cairo_t *cr,
638 bool destroy)
639{
640 cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]);
641 cairo_paint(cr);
642 if (destroy)
643 cairo_destroy(cr);
644}
645
646static void setup_backing_store(frontend *fe)
647{
648#ifndef USE_CAIRO_WITHOUT_PIXMAP
649 if (!fe->headless) {
650 fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area),
651 fe->pw*fe->ps, fe->ph*fe->ps, -1);
652 } else {
653 fe->pixmap = NULL;
654 }
655#endif
656
657 fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
658 fe->pw*fe->ps, fe->ph*fe->ps);
659
660 wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), true);
661#ifndef USE_CAIRO_WITHOUT_PIXMAP
662 if (!fe->headless)
663 wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), true);
664#endif
665 if (!fe->headless) {
666#if GTK_CHECK_VERSION(3,22,0)
667 GdkWindow *gdkwin;
668 cairo_region_t *region;
669 GdkDrawingContext *drawctx;
670 cairo_t *cr;
671
672 gdkwin = gtk_widget_get_window(fe->area);
673 region = gdk_window_get_clip_region(gdkwin);
674 drawctx = gdk_window_begin_draw_frame(gdkwin, region);
675 cr = gdk_drawing_context_get_cairo_context(drawctx);
676 wipe_and_maybe_destroy_cairo(fe, cr, false);
677 gdk_window_end_draw_frame(gdkwin, drawctx);
678 cairo_region_destroy(region);
679#else
680 wipe_and_maybe_destroy_cairo(
681 fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), true);
682#endif
683 }
684}
685
686static bool backing_store_ok(frontend *fe)
687{
688 return fe->image != NULL;
689}
690
691static void teardown_backing_store(frontend *fe)
692{
693 cairo_surface_destroy(fe->image);
694#ifndef USE_CAIRO_WITHOUT_PIXMAP
695 gdk_pixmap_unref(fe->pixmap);
696#endif
697 fe->image = NULL;
698}
699
700#endif
701
702/* ----------------------------------------------------------------------
703 * GDK drawing functions.
704 */
705
706#ifndef USE_CAIRO
707
708static void setup_drawing(frontend *fe)
709{
710 fe->gc = gdk_gc_new(fe->area->window);
711}
712
713static void teardown_drawing(frontend *fe)
714{
715 gdk_gc_unref(fe->gc);
716 fe->gc = NULL;
717}
718
719static void snaffle_colours(frontend *fe)
720{
721 int i, ncolours;
722 float *colours;
723 gboolean *success;
724
725 fe->colmap = gdk_colormap_get_system();
726 colours = midend_colours(fe->me, &ncolours);
727 fe->ncolours = ncolours;
728 fe->colours = snewn(ncolours, GdkColor);
729 for (i = 0; i < ncolours; i++) {
730 fe->colours[i].red = colours[i*3] * 0xFFFF;
731 fe->colours[i].green = colours[i*3+1] * 0xFFFF;
732 fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
733 }
734 success = snewn(ncolours, gboolean);
735 gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
736 false, false, success);
737 for (i = 0; i < ncolours; i++) {
738 if (!success[i]) {
739 g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
740 i, fe->colours[i].red >> 8,
741 fe->colours[i].green >> 8,
742 fe->colours[i].blue >> 8);
743 }
744 }
745}
746
747static void set_window_background(frontend *fe, int colour)
748{
749 fe->backgroundindex = colour;
750 gdk_window_set_background(fe->area->window, &fe->colours[colour]);
751 gdk_window_set_background(fe->window->window, &fe->colours[colour]);
752}
753
754static void draw_set_colour(frontend *fe, int colour)
755{
756 gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
757}
758
759#ifdef USE_PANGO
760static PangoLayout *make_pango_layout(frontend *fe)
761{
762 return (pango_layout_new(gtk_widget_get_pango_context(fe->area)));
763}
764
765static void draw_pango_layout(frontend *fe, PangoLayout *layout,
766 int x, int y)
767{
768 gdk_draw_layout(fe->pixmap, fe->gc, x, y, layout);
769}
770#endif
771
772static void save_screenshot_png(frontend *fe, const char *screenshot_file)
773{
774 GdkPixbuf *pb;
775 GError *gerror = NULL;
776
777 midend_redraw(fe->me);
778
779 pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap,
780 NULL, 0, 0, 0, 0, -1, -1);
781 gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL);
782}
783
784static void do_clip(frontend *fe, int x, int y, int w, int h)
785{
786 GdkRectangle rect;
787
788 rect.x = x;
789 rect.y = y;
790 rect.width = w;
791 rect.height = h;
792 gdk_gc_set_clip_rectangle(fe->gc, &rect);
793}
794
795static void do_unclip(frontend *fe)
796{
797 GdkRectangle rect;
798
799 rect.x = 0;
800 rect.y = 0;
801 rect.width = fe->w;
802 rect.height = fe->h;
803 gdk_gc_set_clip_rectangle(fe->gc, &rect);
804}
805
806static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
807{
808 gdk_draw_rectangle(fe->pixmap, fe->gc, 1, x, y, w, h);
809}
810
811static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
812{
813 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
814}
815
816static void do_draw_thick_line(frontend *fe, float thickness,
817 float x1, float y1, float x2, float y2)
818{
819 GdkGCValues save;
820
821 gdk_gc_get_values(fe->gc, &save);
822 gdk_gc_set_line_attributes(fe->gc,
823 thickness,
824 GDK_LINE_SOLID,
825 GDK_CAP_BUTT,
826 GDK_JOIN_BEVEL);
827 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
828 gdk_gc_set_line_attributes(fe->gc,
829 save.line_width,
830 save.line_style,
831 save.cap_style,
832 save.join_style);
833}
834
835static void do_draw_poly(frontend *fe, int *coords, int npoints,
836 int fillcolour, int outlinecolour)
837{
838 GdkPoint *points = snewn(npoints, GdkPoint);
839 int i;
840
841 for (i = 0; i < npoints; i++) {
842 points[i].x = coords[i*2];
843 points[i].y = coords[i*2+1];
844 }
845
846 if (fillcolour >= 0) {
847 fe->dr_api->set_colour(fe, fillcolour);
848 gdk_draw_polygon(fe->pixmap, fe->gc, true, points, npoints);
849 }
850 assert(outlinecolour >= 0);
851 fe->dr_api->set_colour(fe, outlinecolour);
852
853 /*
854 * In principle we ought to be able to use gdk_draw_polygon for
855 * the outline as well. In fact, it turns out to interact badly
856 * with a clipping region, for no terribly obvious reason, so I
857 * draw the outline as a sequence of lines instead.
858 */
859 for (i = 0; i < npoints; i++)
860 gdk_draw_line(fe->pixmap, fe->gc,
861 points[i].x, points[i].y,
862 points[(i+1)%npoints].x, points[(i+1)%npoints].y);
863
864 sfree(points);
865}
866
867static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
868 int fillcolour, int outlinecolour)
869{
870 if (fillcolour >= 0) {
871 fe->dr_api->set_colour(fe, fillcolour);
872 gdk_draw_arc(fe->pixmap, fe->gc, true,
873 cx - radius, cy - radius,
874 2 * radius, 2 * radius, 0, 360 * 64);
875 }
876
877 assert(outlinecolour >= 0);
878 fe->dr_api->set_colour(fe, outlinecolour);
879 gdk_draw_arc(fe->pixmap, fe->gc, false,
880 cx - radius, cy - radius,
881 2 * radius, 2 * radius, 0, 360 * 64);
882}
883
884static void setup_blitter(blitter *bl, int w, int h)
885{
886 /*
887 * We can't create the pixmap right now, because fe->window
888 * might not yet exist. So we just cache w and h and create it
889 * during the firs call to blitter_save.
890 */
891 bl->pixmap = NULL;
892}
893
894static void teardown_blitter(blitter *bl)
895{
896 if (bl->pixmap)
897 gdk_pixmap_unref(bl->pixmap);
898}
899
900static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
901{
902 if (!bl->pixmap)
903 bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
904 gdk_draw_pixmap(bl->pixmap,
905 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
906 fe->pixmap,
907 x, y, 0, 0, bl->w, bl->h);
908}
909
910static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
911{
912 assert(bl->pixmap);
913 gdk_draw_pixmap(fe->pixmap,
914 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
915 bl->pixmap,
916 0, 0, x, y, bl->w, bl->h);
917}
918
919static void clear_backing_store(frontend *fe)
920{
921 fe->pixmap = NULL;
922}
923
924static void setup_backing_store(frontend *fe)
925{
926 GdkGC *gc;
927
928 if (fe->headless) {
929 fprintf(stderr, "headless mode does not work with GDK drawing\n");
930 exit(1);
931 }
932
933 fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1);
934
935 gc = gdk_gc_new(fe->area->window);
936 gdk_gc_set_foreground(gc, &fe->colours[0]);
937 gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph);
938 gdk_draw_rectangle(fe->area->window, gc, 1, 0, 0, fe->w, fe->h);
939 gdk_gc_unref(gc);
940}
941
942static int backing_store_ok(frontend *fe)
943{
944 return (!!fe->pixmap);
945}
946
947static void teardown_backing_store(frontend *fe)
948{
949 gdk_pixmap_unref(fe->pixmap);
950 fe->pixmap = NULL;
951}
952
953#endif
954
955#ifndef USE_CAIRO_WITHOUT_PIXMAP
956static void repaint_rectangle(frontend *fe, GtkWidget *widget,
957 int x, int y, int w, int h)
958{
959 GdkGC *gc = gdk_gc_new(gtk_widget_get_window(widget));
960#ifdef USE_CAIRO
961 gdk_gc_set_foreground(gc, &fe->background);
962#else
963 gdk_gc_set_foreground(gc, &fe->colours[fe->backgroundindex]);
964#endif
965 if (x < fe->ox) {
966 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
967 true, x, y, fe->ox - x, h);
968 w -= (fe->ox - x);
969 x = fe->ox;
970 }
971 if (y < fe->oy) {
972 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
973 true, x, y, w, fe->oy - y);
974 h -= (fe->oy - y);
975 y = fe->oy;
976 }
977 if (w > fe->pw) {
978 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
979 true, x + fe->pw, y, w - fe->pw, h);
980 w = fe->pw;
981 }
982 if (h > fe->ph) {
983 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
984 true, x, y + fe->ph, w, h - fe->ph);
985 h = fe->ph;
986 }
987 gdk_draw_pixmap(gtk_widget_get_window(widget), gc, fe->pixmap,
988 x - fe->ox, y - fe->oy, x, y, w, h);
989 gdk_gc_unref(gc);
990}
991#endif
992
993/* ----------------------------------------------------------------------
994 * Pango font functions.
995 */
996
997#ifdef USE_PANGO
998
999static void add_font(frontend *fe, int index, int fonttype, int fontsize)
1000{
1001 /*
1002 * Use Pango to find the closest match to the requested
1003 * font.
1004 */
1005 PangoFontDescription *fd;
1006
1007 fd = pango_font_description_new();
1008 /* `Monospace' and `Sans' are meta-families guaranteed to exist */
1009 pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
1010 "Monospace" : "Sans");
1011 pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
1012 /*
1013 * I found some online Pango documentation which
1014 * described a function called
1015 * pango_font_description_set_absolute_size(), which is
1016 * _exactly_ what I want here. Unfortunately, none of
1017 * my local Pango installations have it (presumably
1018 * they're too old), so I'm going to have to hack round
1019 * it by figuring out the point size myself. This
1020 * limits me to X and probably also breaks in later
1021 * Pango installations, so ideally I should add another
1022 * CHECK_VERSION type ifdef and use set_absolute_size
1023 * where available. All very annoying.
1024 */
1025#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
1026 pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
1027#else
1028 {
1029 Display *d = GDK_DISPLAY();
1030 int s = DefaultScreen(d);
1031 double resolution =
1032 (PANGO_SCALE * 72.27 / 25.4) *
1033 ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
1034 pango_font_description_set_size(fd, resolution * fontsize);
1035 }
1036#endif
1037 fe->fonts[index].desc = fd;
1038}
1039
1040static void align_and_draw_text(frontend *fe,
1041 int index, int align, int x, int y,
1042 const char *text)
1043{
1044 PangoLayout *layout;
1045 PangoRectangle rect;
1046
1047 layout = make_pango_layout(fe);
1048
1049 /*
1050 * Create a layout.
1051 */
1052 pango_layout_set_font_description(layout, fe->fonts[index].desc);
1053 pango_layout_set_text(layout, text, strlen(text));
1054 pango_layout_get_pixel_extents(layout, NULL, &rect);
1055
1056 if (align & ALIGN_VCENTRE)
1057 rect.y -= rect.height / 2;
1058 else
1059 rect.y -= rect.height;
1060
1061 if (align & ALIGN_HCENTRE)
1062 rect.x -= rect.width / 2;
1063 else if (align & ALIGN_HRIGHT)
1064 rect.x -= rect.width;
1065
1066 draw_pango_layout(fe, layout, rect.x + x, rect.y + y);
1067
1068 g_object_unref(layout);
1069}
1070
1071#endif
1072
1073/* ----------------------------------------------------------------------
1074 * Old-fashioned font functions.
1075 */
1076
1077#ifndef USE_PANGO
1078
1079static void add_font(int index, int fonttype, int fontsize)
1080{
1081 /*
1082 * In GTK 1.2, I don't know of any plausible way to
1083 * pick a suitable font, so I'm just going to be
1084 * tedious.
1085 */
1086 fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
1087 "fixed" : "variable");
1088}
1089
1090static void align_and_draw_text(int index, int align, int x, int y,
1091 const char *text)
1092{
1093 int lb, rb, wid, asc, desc;
1094
1095 /*
1096 * Measure vertical string extents with respect to the same
1097 * string always...
1098 */
1099 gdk_string_extents(fe->fonts[i].font,
1100 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
1101 &lb, &rb, &wid, &asc, &desc);
1102 if (align & ALIGN_VCENTRE)
1103 y += asc - (asc+desc)/2;
1104 else
1105 y += asc;
1106
1107 /*
1108 * ... but horizontal extents with respect to the provided
1109 * string. This means that multiple pieces of text centred
1110 * on the same y-coordinate don't have different baselines.
1111 */
1112 gdk_string_extents(fe->fonts[i].font, text,
1113 &lb, &rb, &wid, &asc, &desc);
1114
1115 if (align & ALIGN_HCENTRE)
1116 x -= wid / 2;
1117 else if (align & ALIGN_HRIGHT)
1118 x -= wid;
1119
1120 /*
1121 * Actually draw the text.
1122 */
1123 gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text);
1124}
1125
1126#endif
1127
1128/* ----------------------------------------------------------------------
1129 * The exported drawing functions.
1130 */
1131
1132void gtk_start_draw(void *handle)
1133{
1134 frontend *fe = (frontend *)handle;
1135 fe->bbox_l = fe->w;
1136 fe->bbox_r = 0;
1137 fe->bbox_u = fe->h;
1138 fe->bbox_d = 0;
1139 setup_drawing(fe);
1140}
1141
1142void gtk_clip(void *handle, int x, int y, int w, int h)
1143{
1144 frontend *fe = (frontend *)handle;
1145 do_clip(fe, x, y, w, h);
1146}
1147
1148void gtk_unclip(void *handle)
1149{
1150 frontend *fe = (frontend *)handle;
1151 do_unclip(fe);
1152}
1153
1154void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
1155 int align, int colour, const char *text)
1156{
1157 frontend *fe = (frontend *)handle;
1158 int i;
1159
1160 /*
1161 * Find or create the font.
1162 */
1163 for (i = 0; i < fe->nfonts; i++)
1164 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
1165 break;
1166
1167 if (i == fe->nfonts) {
1168 if (fe->fontsize <= fe->nfonts) {
1169 fe->fontsize = fe->nfonts + 10;
1170 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
1171 }
1172
1173 fe->nfonts++;
1174
1175 fe->fonts[i].type = fonttype;
1176 fe->fonts[i].size = fontsize;
1177 add_font(fe, i, fonttype, fontsize);
1178 }
1179
1180 /*
1181 * Do the job.
1182 */
1183 fe->dr_api->set_colour(fe, colour);
1184 align_and_draw_text(fe, i, align, x, y, text);
1185}
1186
1187void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour)
1188{
1189 frontend *fe = (frontend *)handle;
1190 fe->dr_api->set_colour(fe, colour);
1191 do_draw_rect(fe, x, y, w, h);
1192}
1193
1194void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
1195{
1196 frontend *fe = (frontend *)handle;
1197 fe->dr_api->set_colour(fe, colour);
1198 do_draw_line(fe, x1, y1, x2, y2);
1199}
1200
1201void gtk_draw_thick_line(void *handle, float thickness,
1202 float x1, float y1, float x2, float y2, int colour)
1203{
1204 frontend *fe = (frontend *)handle;
1205 fe->dr_api->set_colour(fe, colour);
1206 do_draw_thick_line(fe, thickness, x1, y1, x2, y2);
1207}
1208
1209void gtk_draw_poly(void *handle, int *coords, int npoints,
1210 int fillcolour, int outlinecolour)
1211{
1212 frontend *fe = (frontend *)handle;
1213 do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour);
1214}
1215
1216void gtk_draw_circle(void *handle, int cx, int cy, int radius,
1217 int fillcolour, int outlinecolour)
1218{
1219 frontend *fe = (frontend *)handle;
1220 do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour);
1221}
1222
1223blitter *gtk_blitter_new(void *handle, int w, int h)
1224{
1225 blitter *bl = snew(blitter);
1226 setup_blitter(bl, w, h);
1227 bl->w = w;
1228 bl->h = h;
1229 return bl;
1230}
1231
1232void gtk_blitter_free(void *handle, blitter *bl)
1233{
1234 teardown_blitter(bl);
1235 sfree(bl);
1236}
1237
1238void gtk_blitter_save(void *handle, blitter *bl, int x, int y)
1239{
1240 frontend *fe = (frontend *)handle;
1241 do_blitter_save(fe, bl, x, y);
1242 bl->x = x;
1243 bl->y = y;
1244}
1245
1246void gtk_blitter_load(void *handle, blitter *bl, int x, int y)
1247{
1248 frontend *fe = (frontend *)handle;
1249 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
1250 x = bl->x;
1251 y = bl->y;
1252 }
1253 do_blitter_load(fe, bl, x, y);
1254}
1255
1256void gtk_draw_update(void *handle, int x, int y, int w, int h)
1257{
1258 frontend *fe = (frontend *)handle;
1259 if (fe->bbox_l > x ) fe->bbox_l = x ;
1260 if (fe->bbox_r < x+w) fe->bbox_r = x+w;
1261 if (fe->bbox_u > y ) fe->bbox_u = y ;
1262 if (fe->bbox_d < y+h) fe->bbox_d = y+h;
1263}
1264
1265void gtk_end_draw(void *handle)
1266{
1267 frontend *fe = (frontend *)handle;
1268
1269 teardown_drawing(fe);
1270
1271 if (fe->bbox_l < fe->bbox_r && fe->bbox_u < fe->bbox_d && !fe->headless) {
1272#ifdef USE_CAIRO_WITHOUT_PIXMAP
1273 gtk_widget_queue_draw_area(fe->area,
1274 fe->bbox_l - 1 + fe->ox,
1275 fe->bbox_u - 1 + fe->oy,
1276 fe->bbox_r - fe->bbox_l + 2,
1277 fe->bbox_d - fe->bbox_u + 2);
1278#else
1279 repaint_rectangle(fe, fe->area,
1280 fe->bbox_l - 1 + fe->ox,
1281 fe->bbox_u - 1 + fe->oy,
1282 fe->bbox_r - fe->bbox_l + 2,
1283 fe->bbox_d - fe->bbox_u + 2);
1284#endif
1285 }
1286}
1287
1288#ifdef USE_PANGO
1289char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings)
1290{
1291 /*
1292 * We assume Pango can cope with any UTF-8 likely to be emitted
1293 * by a puzzle.
1294 */
1295 return dupstr(strings[0]);
1296}
1297#endif
1298
1299#ifdef USE_PRINTING
1300void gtk_begin_doc(void *handle, int pages)
1301{
1302 frontend *fe = (frontend *)handle;
1303 gtk_print_operation_set_n_pages(fe->printop, pages);
1304}
1305
1306void gtk_begin_page(void *handle, int number)
1307{
1308}
1309
1310void gtk_begin_puzzle(void *handle, float xm, float xc,
1311 float ym, float yc, int pw, int ph, float wmm)
1312{
1313 frontend *fe = (frontend *)handle;
1314 double ppw, pph, pox, poy, dpmmx, dpmmy;
1315 double scale;
1316
1317 ppw = gtk_print_context_get_width(fe->printcontext);
1318 pph = gtk_print_context_get_height(fe->printcontext);
1319 dpmmx = gtk_print_context_get_dpi_x(fe->printcontext) / 25.4;
1320 dpmmy = gtk_print_context_get_dpi_y(fe->printcontext) / 25.4;
1321
1322 /*
1323 * Compute the puzzle's position in pixels on the logical page.
1324 */
1325 pox = xm * ppw + xc * dpmmx;
1326 poy = ym * pph + yc * dpmmy;
1327
1328 /*
1329 * And determine the scale.
1330 *
1331 * I need a scale such that the maximum puzzle-coordinate
1332 * extent of the rectangle (pw * scale) is equal to the pixel
1333 * equivalent of the puzzle's millimetre width (wmm * dpmmx).
1334 */
1335 scale = wmm * dpmmx / pw;
1336
1337 /*
1338 * Now instruct Cairo to transform points based on our calculated
1339 * values (order here *is* important).
1340 */
1341 cairo_save(fe->cr);
1342 cairo_translate(fe->cr, pox, poy);
1343 cairo_scale(fe->cr, scale, scale);
1344
1345 fe->hatchthick = 0.2 * pw / wmm;
1346 fe->hatchspace = 1.0 * pw / wmm;
1347}
1348
1349void gtk_end_puzzle(void *handle)
1350{
1351 frontend *fe = (frontend *)handle;
1352 cairo_restore(fe->cr);
1353}
1354
1355void gtk_end_page(void *handle, int number)
1356{
1357}
1358
1359void gtk_end_doc(void *handle)
1360{
1361}
1362
1363void gtk_line_width(void *handle, float width)
1364{
1365 frontend *fe = (frontend *)handle;
1366 cairo_set_line_width(fe->cr, width);
1367}
1368
1369void gtk_line_dotted(void *handle, bool dotted)
1370{
1371 frontend *fe = (frontend *)handle;
1372
1373 if (dotted) {
1374 const double dash = 35.0;
1375 cairo_set_dash(fe->cr, &dash, 1, 0);
1376 } else {
1377 cairo_set_dash(fe->cr, NULL, 0, 0);
1378 }
1379}
1380#endif /* USE_PRINTING */
1381
1382const struct internal_drawing_api internal_drawing = {
1383 draw_set_colour,
1384#ifdef USE_CAIRO
1385 do_draw_fill,
1386 do_draw_fill_preserve,
1387#endif
1388};
1389
1390#ifdef USE_CAIRO
1391const struct internal_drawing_api internal_printing = {
1392 print_set_colour,
1393 do_print_fill,
1394 do_print_fill_preserve,
1395};
1396#endif
1397
1398const struct drawing_api gtk_drawing = {
1399 gtk_draw_text,
1400 gtk_draw_rect,
1401 gtk_draw_line,
1402 gtk_draw_poly,
1403 gtk_draw_circle,
1404 gtk_draw_update,
1405 gtk_clip,
1406 gtk_unclip,
1407 gtk_start_draw,
1408 gtk_end_draw,
1409 gtk_status_bar,
1410 gtk_blitter_new,
1411 gtk_blitter_free,
1412 gtk_blitter_save,
1413 gtk_blitter_load,
1414#ifdef USE_PRINTING
1415 gtk_begin_doc,
1416 gtk_begin_page,
1417 gtk_begin_puzzle,
1418 gtk_end_puzzle,
1419 gtk_end_page,
1420 gtk_end_doc,
1421 gtk_line_width,
1422 gtk_line_dotted,
1423#else
1424 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
1425 NULL, NULL, /* line_width, line_dotted */
1426#endif
1427#ifdef USE_PANGO
1428 gtk_text_fallback,
1429#else
1430 NULL,
1431#endif
1432#ifdef NO_THICK_LINE
1433 NULL,
1434#else
1435 gtk_draw_thick_line,
1436#endif
1437};
1438
1439static void destroy(GtkWidget *widget, gpointer data)
1440{
1441 frontend *fe = (frontend *)data;
1442 deactivate_timer(fe);
1443 midend_free(fe->me);
1444 gtk_main_quit();
1445}
1446
1447static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
1448{
1449 frontend *fe = (frontend *)data;
1450 int keyval;
1451 int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0;
1452 int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0;
1453
1454 if (!backing_store_ok(fe))
1455 return true;
1456
1457 /* Handle mnemonics. */
1458 if (gtk_window_activate_key(GTK_WINDOW(fe->window), event))
1459 return true;
1460
1461 if (event->keyval == GDK_KEY_Up)
1462 keyval = shift | ctrl | CURSOR_UP;
1463 else if (event->keyval == GDK_KEY_KP_Up ||
1464 event->keyval == GDK_KEY_KP_8)
1465 keyval = MOD_NUM_KEYPAD | '8';
1466 else if (event->keyval == GDK_KEY_Down)
1467 keyval = shift | ctrl | CURSOR_DOWN;
1468 else if (event->keyval == GDK_KEY_KP_Down ||
1469 event->keyval == GDK_KEY_KP_2)
1470 keyval = MOD_NUM_KEYPAD | '2';
1471 else if (event->keyval == GDK_KEY_Left)
1472 keyval = shift | ctrl | CURSOR_LEFT;
1473 else if (event->keyval == GDK_KEY_KP_Left ||
1474 event->keyval == GDK_KEY_KP_4)
1475 keyval = MOD_NUM_KEYPAD | '4';
1476 else if (event->keyval == GDK_KEY_Right)
1477 keyval = shift | ctrl | CURSOR_RIGHT;
1478 else if (event->keyval == GDK_KEY_KP_Right ||
1479 event->keyval == GDK_KEY_KP_6)
1480 keyval = MOD_NUM_KEYPAD | '6';
1481 else if (event->keyval == GDK_KEY_KP_Home ||
1482 event->keyval == GDK_KEY_KP_7)
1483 keyval = MOD_NUM_KEYPAD | '7';
1484 else if (event->keyval == GDK_KEY_KP_End ||
1485 event->keyval == GDK_KEY_KP_1)
1486 keyval = MOD_NUM_KEYPAD | '1';
1487 else if (event->keyval == GDK_KEY_KP_Page_Up ||
1488 event->keyval == GDK_KEY_KP_9)
1489 keyval = MOD_NUM_KEYPAD | '9';
1490 else if (event->keyval == GDK_KEY_KP_Page_Down ||
1491 event->keyval == GDK_KEY_KP_3)
1492 keyval = MOD_NUM_KEYPAD | '3';
1493 else if (event->keyval == GDK_KEY_KP_Insert ||
1494 event->keyval == GDK_KEY_KP_0)
1495 keyval = MOD_NUM_KEYPAD | '0';
1496 else if (event->keyval == GDK_KEY_KP_Begin ||
1497 event->keyval == GDK_KEY_KP_5)
1498 keyval = MOD_NUM_KEYPAD | '5';
1499 else if (event->keyval == GDK_KEY_BackSpace ||
1500 event->keyval == GDK_KEY_Delete ||
1501 event->keyval == GDK_KEY_KP_Delete)
1502 keyval = '\177';
1503 else if ((event->keyval == 'z' || event->keyval == 'Z') && shift && ctrl)
1504 keyval = UI_REDO;
1505 else if (event->string[0] && !event->string[1])
1506 keyval = (unsigned char)event->string[0];
1507 else
1508 keyval = -1;
1509
1510 if (keyval >= 0 &&
1511 !midend_process_key(fe->me, 0, 0, keyval))
1512 gtk_widget_destroy(fe->window);
1513
1514 return true;
1515}
1516
1517static gint button_event(GtkWidget *widget, GdkEventButton *event,
1518 gpointer data)
1519{
1520 frontend *fe = (frontend *)data;
1521 int button;
1522
1523 if (!backing_store_ok(fe))
1524 return true;
1525
1526 if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1527 return true;
1528
1529 if (event->button == 2 || (event->state & GDK_SHIFT_MASK))
1530 button = MIDDLE_BUTTON;
1531 else if (event->button == 3 || (event->state & GDK_MOD1_MASK))
1532 button = RIGHT_BUTTON;
1533 else if (event->button == 1)
1534 button = LEFT_BUTTON;
1535 else if (event->button == 8 && event->type == GDK_BUTTON_PRESS)
1536 button = 'u';
1537 else if (event->button == 9 && event->type == GDK_BUTTON_PRESS)
1538 button = 'r';
1539 else
1540 return false; /* don't even know what button! */
1541
1542 if (event->type == GDK_BUTTON_RELEASE && button >= LEFT_BUTTON)
1543 button += LEFT_RELEASE - LEFT_BUTTON;
1544
1545 if (!midend_process_key(fe->me, event->x - fe->ox,
1546 event->y - fe->oy, button))
1547 gtk_widget_destroy(fe->window);
1548
1549 return true;
1550}
1551
1552static gint motion_event(GtkWidget *widget, GdkEventMotion *event,
1553 gpointer data)
1554{
1555 frontend *fe = (frontend *)data;
1556 int button;
1557
1558 if (!backing_store_ok(fe))
1559 return true;
1560
1561 if (event->state & (GDK_BUTTON2_MASK | GDK_SHIFT_MASK))
1562 button = MIDDLE_DRAG;
1563 else if (event->state & GDK_BUTTON1_MASK)
1564 button = LEFT_DRAG;
1565 else if (event->state & GDK_BUTTON3_MASK)
1566 button = RIGHT_DRAG;
1567 else
1568 return false; /* don't even know what button! */
1569
1570 if (!midend_process_key(fe->me, event->x - fe->ox,
1571 event->y - fe->oy, button))
1572 gtk_widget_destroy(fe->window);
1573#if GTK_CHECK_VERSION(2,12,0)
1574 gdk_event_request_motions(event);
1575#else
1576 gdk_window_get_pointer(gtk_widget_get_window(widget), NULL, NULL, NULL);
1577#endif
1578
1579 return true;
1580}
1581
1582#if GTK_CHECK_VERSION(3,0,0)
1583static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
1584{
1585 frontend *fe = (frontend *)data;
1586 GdkRectangle dirtyrect;
1587
1588 cairo_surface_t *target_surface = cairo_get_target(cr);
1589 cairo_matrix_t m;
1590 cairo_get_matrix(cr, &m);
1591 double orig_sx, orig_sy;
1592 cairo_surface_get_device_scale(target_surface, &orig_sx, &orig_sy);
1593 cairo_surface_set_device_scale(target_surface, 1.0, 1.0);
1594 cairo_translate(cr, m.x0 * (orig_sx - 1.0), m.y0 * (orig_sy - 1.0));
1595
1596 gdk_cairo_get_clip_rectangle(cr, &dirtyrect);
1597 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1598 cairo_rectangle(cr, dirtyrect.x, dirtyrect.y,
1599 dirtyrect.width, dirtyrect.height);
1600 cairo_fill(cr);
1601
1602 cairo_surface_set_device_scale(target_surface, orig_sx, orig_sy);
1603
1604 return true;
1605}
1606#else
1607static gint expose_area(GtkWidget *widget, GdkEventExpose *event,
1608 gpointer data)
1609{
1610 frontend *fe = (frontend *)data;
1611
1612 if (backing_store_ok(fe)) {
1613#ifdef USE_CAIRO_WITHOUT_PIXMAP
1614 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
1615 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1616 cairo_rectangle(cr, event->area.x, event->area.y,
1617 event->area.width, event->area.height);
1618 cairo_fill(cr);
1619 cairo_destroy(cr);
1620#else
1621 repaint_rectangle(fe, widget,
1622 event->area.x, event->area.y,
1623 event->area.width, event->area.height);
1624#endif
1625 }
1626 return true;
1627}
1628#endif
1629
1630static gint map_window(GtkWidget *widget, GdkEvent *event,
1631 gpointer data)
1632{
1633 frontend *fe = (frontend *)data;
1634
1635 /*
1636 * Apparently we need to do this because otherwise the status
1637 * bar will fail to update immediately. Annoying, but there we
1638 * go.
1639 */
1640 gtk_widget_queue_draw(fe->window);
1641
1642 return true;
1643}
1644
1645static void resize_puzzle_to_area(frontend *fe, int x, int y)
1646{
1647 int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph;
1648 int oldps = fe->ps;
1649
1650 fe->w = x;
1651 fe->h = y;
1652 midend_size(fe->me, &x, &y, true);
1653 fe->pw = x;
1654 fe->ph = y;
1655#if GTK_CHECK_VERSION(3,10,0)
1656 fe->ps = gtk_widget_get_scale_factor(fe->area);
1657#else
1658 fe->ps = 1;
1659#endif
1660 fe->ox = (fe->w - fe->pw) / 2;
1661 fe->oy = (fe->h - fe->ph) / 2;
1662
1663 if (oldw != fe->w || oldpw != fe->pw || oldps != fe->ps ||
1664 oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) {
1665 if (backing_store_ok(fe))
1666 teardown_backing_store(fe);
1667 setup_backing_store(fe);
1668 }
1669
1670 midend_force_redraw(fe->me);
1671}
1672
1673static gint configure_area(GtkWidget *widget,
1674 GdkEventConfigure *event, gpointer data)
1675{
1676 frontend *fe = (frontend *)data;
1677
1678 resize_puzzle_to_area(fe, event->width, event->height);
1679#if GTK_CHECK_VERSION(3,0,0)
1680 fe->awaiting_resize_ack = false;
1681#endif
1682 return true;
1683}
1684
1685#if GTK_CHECK_VERSION(3,0,0)
1686static void window_size_alloc(GtkWidget *widget, GtkAllocation *allocation,
1687 gpointer data)
1688{
1689 frontend *fe = (frontend *)data;
1690 if (fe->awaiting_resize_ack) {
1691 GtkAllocation a;
1692 gtk_widget_get_allocation(fe->area, &a);
1693 resize_puzzle_to_area(fe, a.width, a.height);
1694 fe->awaiting_resize_ack = false;
1695 }
1696}
1697#endif
1698
1699static gint timer_func(gpointer data)
1700{
1701 frontend *fe = (frontend *)data;
1702
1703 if (fe->timer_active) {
1704 struct timeval now;
1705 float elapsed;
1706 gettimeofday(&now, NULL);
1707 elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
1708 (now.tv_sec - fe->last_time.tv_sec));
1709 midend_timer(fe->me, elapsed); /* may clear timer_active */
1710 fe->last_time = now;
1711 }
1712
1713 return fe->timer_active;
1714}
1715
1716void deactivate_timer(frontend *fe)
1717{
1718 if (!fe)
1719 return; /* can happen due to --generate */
1720 if (fe->timer_active)
1721 g_source_remove(fe->timer_id);
1722 fe->timer_active = false;
1723}
1724
1725void activate_timer(frontend *fe)
1726{
1727 if (!fe)
1728 return; /* can happen due to --generate */
1729 if (!fe->timer_active) {
1730 fe->timer_id = g_timeout_add(20, timer_func, fe);
1731 gettimeofday(&fe->last_time, NULL);
1732 }
1733 fe->timer_active = true;
1734}
1735
1736static void window_destroy(GtkWidget *widget, gpointer data)
1737{
1738 gtk_main_quit();
1739}
1740
1741static gint win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
1742{
1743 GObject *cancelbutton = G_OBJECT(data);
1744
1745 /*
1746 * `Escape' effectively clicks the cancel button
1747 */
1748 if (event->keyval == GDK_KEY_Escape) {
1749 g_signal_emit_by_name(cancelbutton, "clicked");
1750 return true;
1751 }
1752
1753 return false;
1754}
1755
1756enum { MB_OK, MB_YESNO };
1757
1758static void align_label(GtkLabel *label, double x, double y)
1759{
1760#if GTK_CHECK_VERSION(3,16,0)
1761 gtk_label_set_xalign(label, x);
1762 gtk_label_set_yalign(label, y);
1763#elif GTK_CHECK_VERSION(3,14,0)
1764 gtk_widget_set_halign(GTK_WIDGET(label),
1765 x == 0 ? GTK_ALIGN_START :
1766 x == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1767 gtk_widget_set_valign(GTK_WIDGET(label),
1768 y == 0 ? GTK_ALIGN_START :
1769 y == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1770#else
1771 gtk_misc_set_alignment(GTK_MISC(label), x, y);
1772#endif
1773}
1774
1775#if GTK_CHECK_VERSION(3,0,0)
1776bool message_box(GtkWidget *parent, const char *title, const char *msg,
1777 bool centre, int type)
1778{
1779 GtkWidget *window;
1780 gint ret;
1781
1782 window = gtk_message_dialog_new
1783 (GTK_WINDOW(parent),
1784 (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1785 (type == MB_OK ? GTK_MESSAGE_INFO : GTK_MESSAGE_QUESTION),
1786 (type == MB_OK ? GTK_BUTTONS_OK : GTK_BUTTONS_YES_NO),
1787 "%s", msg);
1788 gtk_window_set_title(GTK_WINDOW(window), title);
1789 ret = gtk_dialog_run(GTK_DIALOG(window));
1790 gtk_widget_destroy(window);
1791 return (type == MB_OK ? true : (ret == GTK_RESPONSE_YES));
1792}
1793#else /* GTK_CHECK_VERSION(3,0,0) */
1794static void msgbox_button_clicked(GtkButton *button, gpointer data)
1795{
1796 GtkWidget *window = GTK_WIDGET(data);
1797 int v, *ip;
1798
1799 ip = (int *)g_object_get_data(G_OBJECT(window), "user-data");
1800 v = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "user-data"));
1801 *ip = v;
1802
1803 gtk_widget_destroy(GTK_WIDGET(data));
1804}
1805
1806bool message_box(GtkWidget *parent, const char *title, const char *msg,
1807 bool centre, int type)
1808{
1809 GtkWidget *window, *hbox, *text, *button;
1810 char *titles;
1811 int i, def, cancel;
1812
1813 window = gtk_dialog_new();
1814 text = gtk_label_new(msg);
1815 align_label(GTK_LABEL(text), 0.0, 0.0);
1816 hbox = gtk_hbox_new(false, 0);
1817 gtk_box_pack_start(GTK_BOX(hbox), text, false, false, 20);
1818 gtk_box_pack_start
1819 (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
1820 hbox, false, false, 20);
1821 gtk_widget_show(text);
1822 gtk_widget_show(hbox);
1823 gtk_window_set_title(GTK_WINDOW(window), title);
1824 gtk_label_set_line_wrap(GTK_LABEL(text), true);
1825
1826 if (type == MB_OK) {
1827 titles = LABEL_OK "\0";
1828 def = cancel = 0;
1829 } else {
1830 assert(type == MB_YESNO);
1831 titles = LABEL_NO "\0" LABEL_YES "\0";
1832 def = 1;
1833 cancel = 0;
1834 }
1835 i = 0;
1836
1837 while (*titles) {
1838 button = gtk_button_new_with_our_label(titles);
1839 gtk_box_pack_end
1840 (GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(window))),
1841 button, false, false, 0);
1842 gtk_widget_show(button);
1843 if (i == def) {
1844 gtk_widget_set_can_default(button, true);
1845 gtk_window_set_default(GTK_WINDOW(window), button);
1846 }
1847 if (i == cancel) {
1848 g_signal_connect(G_OBJECT(window), "key_press_event",
1849 G_CALLBACK(win_key_press), button);
1850 }
1851 g_signal_connect(G_OBJECT(button), "clicked",
1852 G_CALLBACK(msgbox_button_clicked), window);
1853 g_object_set_data(G_OBJECT(button), "user-data",
1854 GINT_TO_POINTER(i));
1855 titles += strlen(titles)+1;
1856 i++;
1857 }
1858 g_object_set_data(G_OBJECT(window), "user-data", &i);
1859 g_signal_connect(G_OBJECT(window), "destroy",
1860 G_CALLBACK(window_destroy), NULL);
1861 gtk_window_set_modal(GTK_WINDOW(window), true);
1862 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
1863 /* set_transient_window_pos(parent, window); */
1864 gtk_widget_show(window);
1865 i = -1;
1866 gtk_main();
1867 return (type == MB_YESNO ? i == 1 : true);
1868}
1869#endif /* GTK_CHECK_VERSION(3,0,0) */
1870
1871void error_box(GtkWidget *parent, const char *msg)
1872{
1873 message_box(parent, "Error", msg, false, MB_OK);
1874}
1875
1876static void config_ok_button_clicked(GtkButton *button, gpointer data)
1877{
1878 frontend *fe = (frontend *)data;
1879 const char *err;
1880
1881 err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
1882
1883 if (err)
1884 error_box(fe->cfgbox, err);
1885 else {
1886 fe->cfgret = true;
1887 gtk_widget_destroy(fe->cfgbox);
1888 changed_preset(fe);
1889 }
1890}
1891
1892static void config_cancel_button_clicked(GtkButton *button, gpointer data)
1893{
1894 frontend *fe = (frontend *)data;
1895
1896 gtk_widget_destroy(fe->cfgbox);
1897}
1898
1899static gint editbox_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1900{
1901 /*
1902 * GtkEntry has a nasty habit of eating the Return key, which
1903 * is unhelpful since it doesn't actually _do_ anything with it
1904 * (it calls gtk_widget_activate, but our edit boxes never need
1905 * activating). So I catch Return before GtkEntry sees it, and
1906 * pass it straight on to the parent widget. Effect: hitting
1907 * Return in an edit box will now activate the default button
1908 * in the dialog just like it will everywhere else.
1909 */
1910 if (event->keyval == GDK_KEY_Return &&
1911 gtk_widget_get_parent(widget) != NULL) {
1912 gint return_val;
1913 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1914 g_signal_emit_by_name(G_OBJECT(gtk_widget_get_parent(widget)),
1915 "key_press_event", event, &return_val);
1916 return return_val;
1917 }
1918 return false;
1919}
1920
1921static void editbox_changed(GtkEditable *ed, gpointer data)
1922{
1923 config_item *i = (config_item *)data;
1924
1925 assert(i->type == C_STRING);
1926 sfree(i->u.string.sval);
1927 i->u.string.sval = dupstr(gtk_entry_get_text(GTK_ENTRY(ed)));
1928}
1929
1930static void button_toggled(GtkToggleButton *tb, gpointer data)
1931{
1932 config_item *i = (config_item *)data;
1933
1934 assert(i->type == C_BOOLEAN);
1935 i->u.boolean.bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb));
1936}
1937
1938static void droplist_sel(GtkComboBox *combo, gpointer data)
1939{
1940 config_item *i = (config_item *)data;
1941
1942 assert(i->type == C_CHOICES);
1943 i->u.choices.selected = gtk_combo_box_get_active(combo);
1944}
1945
1946static bool get_config(frontend *fe, int which)
1947{
1948 GtkWidget *w, *table, *cancel;
1949 GtkBox *content_box, *button_box;
1950 char *title;
1951 config_item *i;
1952 int y;
1953
1954 fe->cfg = midend_get_config(fe->me, which, &title);
1955 fe->cfg_which = which;
1956 fe->cfgret = false;
1957
1958#if GTK_CHECK_VERSION(3,0,0)
1959 /* GtkDialog isn't quite flexible enough */
1960 fe->cfgbox = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1961 content_box = GTK_BOX(gtk_vbox_new(false, 8));
1962 g_object_set(G_OBJECT(content_box), "margin", 8, (const char *)NULL);
1963 gtk_widget_show(GTK_WIDGET(content_box));
1964 gtk_container_add(GTK_CONTAINER(fe->cfgbox), GTK_WIDGET(content_box));
1965 button_box = GTK_BOX(gtk_hbox_new(false, 8));
1966 gtk_widget_show(GTK_WIDGET(button_box));
1967 gtk_box_pack_end(content_box, GTK_WIDGET(button_box), false, false, 0);
1968 {
1969 GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1970 gtk_widget_show(sep);
1971 gtk_box_pack_end(content_box, sep, false, false, 0);
1972 }
1973#else
1974 fe->cfgbox = gtk_dialog_new();
1975 content_box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(fe->cfgbox)));
1976 button_box = GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(fe->cfgbox)));
1977#endif
1978 gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title);
1979 sfree(title);
1980
1981 w = gtk_button_new_with_our_label(LABEL_CANCEL);
1982 gtk_box_pack_end(button_box, w, false, false, 0);
1983 gtk_widget_show(w);
1984 g_signal_connect(G_OBJECT(w), "clicked",
1985 G_CALLBACK(config_cancel_button_clicked), fe);
1986 cancel = w;
1987
1988 w = gtk_button_new_with_our_label(LABEL_OK);
1989 gtk_box_pack_end(button_box, w, false, false, 0);
1990 gtk_widget_show(w);
1991 gtk_widget_set_can_default(w, true);
1992 gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w);
1993 g_signal_connect(G_OBJECT(w), "clicked",
1994 G_CALLBACK(config_ok_button_clicked), fe);
1995
1996#if GTK_CHECK_VERSION(3,0,0)
1997 table = gtk_grid_new();
1998#else
1999 table = gtk_table_new(1, 2, false);
2000#endif
2001 y = 0;
2002 gtk_box_pack_start(content_box, table, false, false, 0);
2003 gtk_widget_show(table);
2004
2005 for (i = fe->cfg; i->type != C_END; i++) {
2006#if !GTK_CHECK_VERSION(3,0,0)
2007 gtk_table_resize(GTK_TABLE(table), y+1, 2);
2008#endif
2009
2010 switch (i->type) {
2011 case C_STRING:
2012 /*
2013 * Edit box with a label beside it.
2014 */
2015
2016 w = gtk_label_new(i->name);
2017 align_label(GTK_LABEL(w), 0.0, 0.5);
2018#if GTK_CHECK_VERSION(3,0,0)
2019 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
2020#else
2021 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
2022 GTK_SHRINK | GTK_FILL,
2023 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2024 3, 3);
2025#endif
2026 gtk_widget_show(w);
2027
2028 w = gtk_entry_new();
2029#if GTK_CHECK_VERSION(3,0,0)
2030 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
2031 g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL);
2032#else
2033 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
2034 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2035 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2036 3, 3);
2037#endif
2038 gtk_entry_set_text(GTK_ENTRY(w), i->u.string.sval);
2039 g_signal_connect(G_OBJECT(w), "changed",
2040 G_CALLBACK(editbox_changed), i);
2041 g_signal_connect(G_OBJECT(w), "key_press_event",
2042 G_CALLBACK(editbox_key), NULL);
2043 gtk_widget_show(w);
2044
2045 break;
2046
2047 case C_BOOLEAN:
2048 /*
2049 * Simple checkbox.
2050 */
2051 w = gtk_check_button_new_with_label(i->name);
2052 g_signal_connect(G_OBJECT(w), "toggled",
2053 G_CALLBACK(button_toggled), i);
2054#if GTK_CHECK_VERSION(3,0,0)
2055 gtk_grid_attach(GTK_GRID(table), w, 0, y, 2, 1);
2056 g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL);
2057#else
2058 gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1,
2059 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2060 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2061 3, 3);
2062#endif
2063 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),
2064 i->u.boolean.bval);
2065 gtk_widget_show(w);
2066 break;
2067
2068 case C_CHOICES:
2069 /*
2070 * Drop-down list (GtkComboBox).
2071 */
2072
2073 w = gtk_label_new(i->name);
2074 align_label(GTK_LABEL(w), 0.0, 0.5);
2075#if GTK_CHECK_VERSION(3,0,0)
2076 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
2077#else
2078 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
2079 GTK_SHRINK | GTK_FILL,
2080 GTK_EXPAND | GTK_SHRINK | GTK_FILL ,
2081 3, 3);
2082#endif
2083 gtk_widget_show(w);
2084
2085 {
2086 int c;
2087 const char *p, *q;
2088 char *name;
2089 GtkListStore *model;
2090 GtkCellRenderer *cr;
2091 GtkTreeIter iter;
2092
2093 model = gtk_list_store_new(1, G_TYPE_STRING);
2094
2095 c = *i->u.choices.choicenames;
2096 p = i->u.choices.choicenames+1;
2097
2098 while (*p) {
2099 q = p;
2100 while (*q && *q != c)
2101 q++;
2102
2103 name = snewn(q-p+1, char);
2104 strncpy(name, p, q-p);
2105 name[q-p] = '\0';
2106
2107 if (*q) q++; /* eat delimiter */
2108
2109 gtk_list_store_append(model, &iter);
2110 gtk_list_store_set(model, &iter, 0, name, -1);
2111
2112 p = q;
2113 }
2114
2115 w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
2116
2117 gtk_combo_box_set_active(GTK_COMBO_BOX(w),
2118 i->u.choices.selected);
2119
2120 cr = gtk_cell_renderer_text_new();
2121 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, true);
2122 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,
2123 "text", 0, NULL);
2124
2125 g_signal_connect(G_OBJECT(w), "changed",
2126 G_CALLBACK(droplist_sel), i);
2127 }
2128
2129#if GTK_CHECK_VERSION(3,0,0)
2130 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
2131 g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL);
2132#else
2133 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
2134 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2135 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
2136 3, 3);
2137#endif
2138 gtk_widget_show(w);
2139 break;
2140 }
2141
2142 y++;
2143 }
2144
2145 g_signal_connect(G_OBJECT(fe->cfgbox), "destroy",
2146 G_CALLBACK(window_destroy), NULL);
2147 g_signal_connect(G_OBJECT(fe->cfgbox), "key_press_event",
2148 G_CALLBACK(win_key_press), cancel);
2149 gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), true);
2150 gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox),
2151 GTK_WINDOW(fe->window));
2152 /* set_transient_window_pos(fe->window, fe->cfgbox); */
2153 gtk_widget_show(fe->cfgbox);
2154 gtk_main();
2155
2156 free_cfg(fe->cfg);
2157
2158 return fe->cfgret;
2159}
2160
2161static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
2162{
2163 frontend *fe = (frontend *)data;
2164 int key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
2165 "user-data"));
2166 if (!midend_process_key(fe->me, 0, 0, key))
2167 gtk_widget_destroy(fe->window);
2168}
2169
2170static void get_size(frontend *fe, int *px, int *py)
2171{
2172 int x, y;
2173
2174 /*
2175 * Currently I don't want to make the GTK port scale large
2176 * puzzles to fit on the screen. This is because X does permit
2177 * extremely large windows and many window managers provide a
2178 * means of navigating round them, and the users I consulted
2179 * before deciding said that they'd rather have enormous puzzle
2180 * windows spanning multiple screen pages than have them
2181 * shrunk. I could change my mind later or introduce
2182 * configurability; this would be the place to do so, by
2183 * replacing the initial values of x and y with the screen
2184 * dimensions.
2185 */
2186 x = INT_MAX;
2187 y = INT_MAX;
2188 midend_size(fe->me, &x, &y, false);
2189 *px = x;
2190 *py = y;
2191}
2192
2193#if !GTK_CHECK_VERSION(2,0,0)
2194#define gtk_window_resize(win, x, y) \
2195 gdk_window_resize(GTK_WIDGET(win)->window, x, y)
2196#endif
2197
2198/*
2199 * Called when any other code in this file has changed the
2200 * selected game parameters.
2201 */
2202static void changed_preset(frontend *fe)
2203{
2204 int n = midend_which_preset(fe->me);
2205
2206 fe->preset_threaded = true;
2207 if (n < 0 && fe->preset_custom) {
2208 gtk_check_menu_item_set_active(
2209 GTK_CHECK_MENU_ITEM(fe->preset_custom),
2210 true);
2211 } else {
2212 GSList *gs = fe->preset_radio;
2213 GSList *found = NULL;
2214
2215 for (gs = fe->preset_radio; gs; gs = gs->next) {
2216 struct preset_menu_entry *entry =
2217 (struct preset_menu_entry *)g_object_get_data(
2218 G_OBJECT(gs->data), "user-data");
2219 if (!entry || entry->id != n)
2220 gtk_check_menu_item_set_active(
2221 GTK_CHECK_MENU_ITEM(gs->data), false);
2222 else
2223 found = gs;
2224 }
2225 if (found)
2226 gtk_check_menu_item_set_active(
2227 GTK_CHECK_MENU_ITEM(found->data), true);
2228 }
2229 fe->preset_threaded = false;
2230
2231 /*
2232 * Update the greying on the Copy menu option.
2233 */
2234 if (fe->copy_menu_item) {
2235 bool enabled = midend_can_format_as_text_now(fe->me);
2236 gtk_widget_set_sensitive(fe->copy_menu_item, enabled);
2237 }
2238}
2239
2240#if !GTK_CHECK_VERSION(3,0,0)
2241static bool not_size_allocated_yet(GtkWidget *w)
2242{
2243 /*
2244 * This function tests whether a widget has not yet taken up space
2245 * on the screen which it will occupy in future. (Therefore, it
2246 * returns true only if the widget does exist but does not have a
2247 * size allocation. A null widget is already taking up all the
2248 * space it ever will.)
2249 */
2250 if (!w)
2251 return false; /* nonexistent widgets aren't a problem */
2252
2253#if GTK_CHECK_VERSION(2,18,0) /* skip if no gtk_widget_get_allocation */
2254 {
2255 GtkAllocation a;
2256 gtk_widget_get_allocation(w, &a);
2257 if (a.height == 0 || a.width == 0)
2258 return true; /* widget exists but has no size yet */
2259 }
2260#endif
2261
2262 return false;
2263}
2264
2265static void try_shrink_drawing_area(frontend *fe)
2266{
2267 if (fe->drawing_area_shrink_pending &&
2268 (!fe->menubar_is_local || !not_size_allocated_yet(fe->menubar)) &&
2269 !not_size_allocated_yet(fe->statusbar)) {
2270 /*
2271 * In order to permit the user to resize the window smaller as
2272 * well as bigger, we call this function after the window size
2273 * has ended up where we want it. This shouldn't shrink the
2274 * window immediately; it just arranges that the next time the
2275 * user tries to shrink it, they can.
2276 *
2277 * However, at puzzle creation time, we defer the first of
2278 * these operations until after the menu bar and status bar
2279 * are actually visible. On Ubuntu 12.04 I've found that these
2280 * can take a while to be displayed, and that it's a mistake
2281 * to reduce the drawing area's size allocation before they've
2282 * turned up or else the drawing area makes room for them by
2283 * shrinking to less than the size we intended.
2284 */
2285 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
2286 fe->drawing_area_shrink_pending = false;
2287 }
2288}
2289#endif /* !GTK_CHECK_VERSION(3,0,0) */
2290
2291static gint configure_window(GtkWidget *widget,
2292 GdkEventConfigure *event, gpointer data)
2293{
2294#if !GTK_CHECK_VERSION(3,0,0)
2295 /*
2296 * When the main puzzle window changes size, it might be because
2297 * the menu bar or status bar has turned up after starting off
2298 * absent, in which case we should have another go at enacting a
2299 * pending shrink of the drawing area.
2300 */
2301 frontend *fe = (frontend *)data;
2302 try_shrink_drawing_area(fe);
2303#endif
2304 return false;
2305}
2306
2307#if GTK_CHECK_VERSION(3,0,0)
2308static int window_extra_height(frontend *fe)
2309{
2310 int ret = 0;
2311 if (fe->menubar) {
2312 GtkRequisition req;
2313 gtk_widget_get_preferred_size(fe->menubar, &req, NULL);
2314 ret += req.height;
2315 }
2316 if (fe->statusbar) {
2317 GtkRequisition req;
2318 gtk_widget_get_preferred_size(fe->statusbar, &req, NULL);
2319 ret += req.height;
2320 }
2321 return ret;
2322}
2323#endif
2324
2325static void resize_fe(frontend *fe)
2326{
2327 int x, y;
2328
2329 get_size(fe, &x, &y);
2330
2331#if GTK_CHECK_VERSION(3,0,0)
2332 gtk_window_resize(GTK_WINDOW(fe->window), x, y + window_extra_height(fe));
2333 fe->awaiting_resize_ack = true;
2334#else
2335 fe->drawing_area_shrink_pending = false;
2336 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
2337 {
2338 GtkRequisition req;
2339 gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
2340 gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
2341 }
2342 fe->drawing_area_shrink_pending = true;
2343 try_shrink_drawing_area(fe);
2344#endif
2345}
2346
2347static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
2348{
2349 frontend *fe = (frontend *)data;
2350 struct preset_menu_entry *entry =
2351 (struct preset_menu_entry *)g_object_get_data(
2352 G_OBJECT(menuitem), "user-data");
2353
2354 if (fe->preset_threaded ||
2355 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
2356 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
2357 return;
2358 midend_set_params(fe->me, entry->params);
2359 midend_new_game(fe->me);
2360 changed_preset(fe);
2361 resize_fe(fe);
2362 midend_redraw(fe->me);
2363}
2364
2365GdkAtom compound_text_atom, utf8_string_atom;
2366bool paste_initialised = false;
2367
2368static void set_selection(frontend *fe, GdkAtom selection)
2369{
2370 if (!paste_initialised) {
2371 compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", false);
2372 utf8_string_atom = gdk_atom_intern("UTF8_STRING", false);
2373 paste_initialised = true;
2374 }
2375
2376 /*
2377 * For this simple application we can safely assume that the
2378 * data passed to this function is pure ASCII, which means we
2379 * can return precisely the same stuff for types STRING,
2380 * COMPOUND_TEXT or UTF8_STRING.
2381 */
2382
2383 if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) {
2384 gtk_selection_clear_targets(fe->area, selection);
2385 gtk_selection_add_target(fe->area, selection,
2386 GDK_SELECTION_TYPE_STRING, 1);
2387 gtk_selection_add_target(fe->area, selection, compound_text_atom, 1);
2388 gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1);
2389 }
2390}
2391
2392void write_clip(frontend *fe, char *data)
2393{
2394 if (fe->paste_data)
2395 sfree(fe->paste_data);
2396
2397 fe->paste_data = data;
2398 fe->paste_data_len = strlen(data);
2399
2400 set_selection(fe, GDK_SELECTION_PRIMARY);
2401 set_selection(fe, GDK_SELECTION_CLIPBOARD);
2402}
2403
2404void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2405 guint info, guint time_stamp, gpointer data)
2406{
2407 frontend *fe = (frontend *)data;
2408 gtk_selection_data_set(seldata, gtk_selection_data_get_target(seldata), 8,
2409 fe->paste_data, fe->paste_data_len);
2410}
2411
2412gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2413 gpointer data)
2414{
2415 frontend *fe = (frontend *)data;
2416
2417 if (fe->paste_data)
2418 sfree(fe->paste_data);
2419 fe->paste_data = NULL;
2420 fe->paste_data_len = 0;
2421 return true;
2422}
2423
2424static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
2425{
2426 frontend *fe = (frontend *)data;
2427 char *text;
2428
2429 text = midend_text_format(fe->me);
2430
2431 if (text) {
2432 write_clip(fe, text);
2433 } else {
2434 gdk_display_beep(gdk_display_get_default());
2435 }
2436}
2437
2438#ifdef OLD_FILESEL
2439
2440static void filesel_ok(GtkButton *button, gpointer data)
2441{
2442 frontend *fe = (frontend *)data;
2443
2444 gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data");
2445
2446 const char *name =
2447 gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
2448
2449 fe->filesel_name = dupstr(name);
2450}
2451
2452static char *file_selector(frontend *fe, const char *title, int save)
2453{
2454 GtkWidget *filesel =
2455 gtk_file_selection_new(title);
2456
2457 fe->filesel_name = NULL;
2458
2459 gtk_window_set_modal(GTK_WINDOW(filesel), true);
2460 g_object_set_data
2461 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
2462 (gpointer)filesel);
2463 g_signal_connect
2464 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2465 G_CALLBACK(filesel_ok), fe);
2466 g_signal_connect_swapped
2467 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2468 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2469 g_signal_connect_object
2470 (G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
2471 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2472 g_signal_connect(G_OBJECT(filesel), "destroy",
2473 G_CALLBACK(window_destroy), NULL);
2474 gtk_widget_show(filesel);
2475 gtk_window_set_transient_for(GTK_WINDOW(filesel), GTK_WINDOW(fe->window));
2476 gtk_main();
2477
2478 return fe->filesel_name;
2479}
2480
2481#else
2482
2483static char *file_selector(frontend *fe, const char *title, bool save)
2484{
2485 char *filesel_name = NULL;
2486
2487 GtkWidget *filesel =
2488 gtk_file_chooser_dialog_new(title,
2489 GTK_WINDOW(fe->window),
2490 save ? GTK_FILE_CHOOSER_ACTION_SAVE :
2491 GTK_FILE_CHOOSER_ACTION_OPEN,
2492 LABEL_CANCEL, GTK_RESPONSE_CANCEL,
2493 save ? LABEL_SAVE : LABEL_OPEN,
2494 GTK_RESPONSE_ACCEPT,
2495 NULL);
2496
2497 if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
2498 char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
2499 filesel_name = dupstr(name);
2500 g_free(name);
2501 }
2502
2503 gtk_widget_destroy(filesel);
2504
2505 return filesel_name;
2506}
2507
2508#endif
2509
2510#ifdef USE_PRINTING
2511GObject *create_print_widget(GtkPrintOperation *print, gpointer data)
2512{
2513 GtkLabel *count_label, *width_label, *height_label,
2514 *scale_llabel, *scale_rlabel;
2515 GtkBox *scale_hbox;
2516 GtkWidget *grid;
2517 frontend *fe = (frontend *)data;
2518
2519 fe->printcount_spin_button =
2520 GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 999, 1));
2521 gtk_spin_button_set_numeric(fe->printcount_spin_button, true);
2522 gtk_spin_button_set_snap_to_ticks(fe->printcount_spin_button, true);
2523 fe->printw_spin_button =
2524 GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 99, 1));
2525 gtk_spin_button_set_numeric(fe->printw_spin_button, true);
2526 gtk_spin_button_set_snap_to_ticks(fe->printw_spin_button, true);
2527 fe->printh_spin_button =
2528 GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 99, 1));
2529 gtk_spin_button_set_numeric(fe->printh_spin_button, true);
2530 gtk_spin_button_set_snap_to_ticks(fe->printh_spin_button, true);
2531 fe->printscale_spin_button =
2532 GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 1000, 1));
2533 gtk_spin_button_set_digits(fe->printscale_spin_button, 1);
2534 gtk_spin_button_set_numeric(fe->printscale_spin_button, true);
2535 if (thegame.can_solve) {
2536 fe->soln_check_button =
2537 GTK_CHECK_BUTTON(
2538 gtk_check_button_new_with_label("Print solutions"));
2539 }
2540 if (thegame.can_print_in_colour) {
2541 fe->colour_check_button =
2542 GTK_CHECK_BUTTON(
2543 gtk_check_button_new_with_label("Print in color"));
2544 }
2545
2546 /* Set defaults to what was selected last time. */
2547 gtk_spin_button_set_value(fe->printcount_spin_button,
2548 (gdouble)fe->printcount);
2549 gtk_spin_button_set_value(fe->printw_spin_button,
2550 (gdouble)fe->printw);
2551 gtk_spin_button_set_value(fe->printh_spin_button,
2552 (gdouble)fe->printh);
2553 gtk_spin_button_set_value(fe->printscale_spin_button,
2554 (gdouble)fe->printscale);
2555 if (thegame.can_solve) {
2556 gtk_toggle_button_set_active(
2557 GTK_TOGGLE_BUTTON(fe->soln_check_button), fe->printsolns);
2558 }
2559 if (thegame.can_print_in_colour) {
2560 gtk_toggle_button_set_active(
2561 GTK_TOGGLE_BUTTON(fe->colour_check_button), fe->printcolour);
2562 }
2563
2564 count_label = GTK_LABEL(gtk_label_new("Puzzles to print:"));
2565 width_label = GTK_LABEL(gtk_label_new("Puzzles across:"));
2566 height_label = GTK_LABEL(gtk_label_new("Puzzles down:"));
2567 scale_llabel = GTK_LABEL(gtk_label_new("Puzzle scale:"));
2568 scale_rlabel = GTK_LABEL(gtk_label_new("%"));
2569#if GTK_CHECK_VERSION(3,0,0)
2570 gtk_widget_set_halign(GTK_WIDGET(count_label), GTK_ALIGN_START);
2571 gtk_widget_set_halign(GTK_WIDGET(width_label), GTK_ALIGN_START);
2572 gtk_widget_set_halign(GTK_WIDGET(height_label), GTK_ALIGN_START);
2573 gtk_widget_set_halign(GTK_WIDGET(scale_llabel), GTK_ALIGN_START);
2574#else
2575 gtk_misc_set_alignment(GTK_MISC(count_label), 0, 0);
2576 gtk_misc_set_alignment(GTK_MISC(width_label), 0, 0);
2577 gtk_misc_set_alignment(GTK_MISC(height_label), 0, 0);
2578 gtk_misc_set_alignment(GTK_MISC(scale_llabel), 0, 0);
2579#endif
2580
2581 scale_hbox = GTK_BOX(gtk_hbox_new(false, 6));
2582 gtk_box_pack_start(scale_hbox, GTK_WIDGET(fe->printscale_spin_button),
2583 false, false, 0);
2584 gtk_box_pack_start(scale_hbox, GTK_WIDGET(scale_rlabel),
2585 false, false, 0);
2586
2587#if GTK_CHECK_VERSION(3,0,0)
2588 grid = gtk_grid_new();
2589 gtk_grid_set_column_spacing(GTK_GRID(grid), 18);
2590 gtk_grid_set_row_spacing(GTK_GRID(grid), 18);
2591 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(count_label), 0, 0, 1, 1);
2592 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(width_label), 0, 1, 1, 1);
2593 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(height_label), 0, 2, 1, 1);
2594 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scale_llabel), 0, 3, 1, 1);
2595 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printcount_spin_button),
2596 1, 0, 1, 1);
2597 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printw_spin_button),
2598 1, 1, 1, 1);
2599 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printh_spin_button),
2600 1, 2, 1, 1);
2601 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scale_hbox), 1, 3, 1, 1);
2602 if (thegame.can_solve) {
2603 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->soln_check_button),
2604 0, 4, 1, 1);
2605 }
2606 if (thegame.can_print_in_colour) {
2607 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->colour_check_button),
2608 thegame.can_solve, 4, 1, 1);
2609 }
2610#else
2611 grid = gtk_table_new((thegame.can_solve || thegame.can_print_in_colour) ?
2612 5 : 4, 2, false);
2613 gtk_table_set_col_spacings(GTK_TABLE(grid), 18);
2614 gtk_table_set_row_spacings(GTK_TABLE(grid), 18);
2615 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(count_label), 0, 1, 0, 1,
2616 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2617 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(width_label), 0, 1, 1, 2,
2618 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2619 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(height_label), 0, 1, 2, 3,
2620 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2621 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(scale_llabel), 0, 1, 3, 4,
2622 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2623 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printcount_spin_button),
2624 1, 2, 0, 1,
2625 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2626 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printw_spin_button),
2627 1, 2, 1, 2,
2628 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2629 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printh_spin_button),
2630 1, 2, 2, 3,
2631 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2632 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(scale_hbox), 1, 2, 3, 4,
2633 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2634 if (thegame.can_solve) {
2635 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->soln_check_button),
2636 0, 1, 4, 5,
2637 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2638 }
2639 if (thegame.can_print_in_colour) {
2640 gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->colour_check_button),
2641 thegame.can_solve, thegame.can_solve + 1, 4, 5,
2642 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2643 }
2644#endif
2645 gtk_container_set_border_width(GTK_CONTAINER(grid), 12);
2646
2647 gtk_widget_show_all(grid);
2648
2649 return G_OBJECT(grid);
2650}
2651
2652void apply_print_widget(GtkPrintOperation *print,
2653 GtkWidget *widget, gpointer data)
2654{
2655 frontend *fe = (frontend *)data;
2656
2657 /* We ignore `widget' because it is easier and faster to store the
2658 widgets we need in `fe' then to get the children of `widget'. */
2659 fe->printcount =
2660 gtk_spin_button_get_value_as_int(fe->printcount_spin_button);
2661 fe->printw = gtk_spin_button_get_value_as_int(fe->printw_spin_button);
2662 fe->printh = gtk_spin_button_get_value_as_int(fe->printh_spin_button);
2663 fe->printscale = gtk_spin_button_get_value(fe->printscale_spin_button);
2664 if (thegame.can_solve) {
2665 fe->printsolns =
2666 gtk_toggle_button_get_active(
2667 GTK_TOGGLE_BUTTON(fe->soln_check_button));
2668 }
2669 if (thegame.can_print_in_colour) {
2670 fe->printcolour =
2671 gtk_toggle_button_get_active(
2672 GTK_TOGGLE_BUTTON(fe->colour_check_button));
2673 }
2674}
2675
2676void print_begin(GtkPrintOperation *printop,
2677 GtkPrintContext *context, gpointer data)
2678{
2679 frontend *fe = (frontend *)data;
2680 midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
2681 int i;
2682
2683 fe->printcontext = context;
2684 fe->cr = gtk_print_context_get_cairo_context(context);
2685
2686 /*
2687 * Create our document structure and fill it up with puzzles.
2688 */
2689 fe->doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
2690
2691 for (i = 0; i < fe->printcount; i++) {
2692 const char *err;
2693
2694 if (i == 0) {
2695 err = midend_print_puzzle(fe->me, fe->doc, fe->printsolns);
2696 } else {
2697 if (!nme) {
2698 game_params *params;
2699
2700 nme = midend_new(NULL, &thegame, NULL, NULL);
2701
2702 /*
2703 * Set the non-interactive mid-end to have the same
2704 * parameters as the standard one.
2705 */
2706 params = midend_get_params(fe->me);
2707 midend_set_params(nme, params);
2708 thegame.free_params(params);
2709 }
2710
2711 midend_new_game(nme);
2712 err = midend_print_puzzle(nme, fe->doc, fe->printsolns);
2713 }
2714
2715 if (err) {
2716 error_box(fe->window, err);
2717 return;
2718 }
2719 }
2720
2721 if (nme)
2722 midend_free(nme);
2723
2724 /* Begin the document. */
2725 document_begin(fe->doc, fe->print_dr);
2726}
2727
2728void draw_page(GtkPrintOperation *printop,
2729 GtkPrintContext *context,
2730 gint page_nr, gpointer data)
2731{
2732 frontend *fe = (frontend *)data;
2733 document_print_page(fe->doc, fe->print_dr, page_nr);
2734}
2735
2736void print_end(GtkPrintOperation *printop,
2737 GtkPrintContext *context, gpointer data)
2738{
2739 frontend *fe = (frontend *)data;
2740
2741 /* End and free the document. */
2742 document_end(fe->doc, fe->print_dr);
2743 document_free(fe->doc);
2744 fe->doc = NULL;
2745}
2746
2747static void print_dialog(frontend *fe)
2748{
2749 GError *error;
2750 static GtkPrintSettings *settings = NULL;
2751 static GtkPageSetup *page_setup = NULL;
2752#ifndef USE_EMBED_PAGE_SETUP
2753 GtkPageSetup *new_page_setup;
2754#endif
2755
2756 fe->printop = gtk_print_operation_new();
2757 gtk_print_operation_set_use_full_page(fe->printop, true);
2758 gtk_print_operation_set_custom_tab_label(fe->printop, "Puzzle Settings");
2759 g_signal_connect(fe->printop, "create-custom-widget",
2760 G_CALLBACK(create_print_widget), fe);
2761 g_signal_connect(fe->printop, "custom-widget-apply",
2762 G_CALLBACK(apply_print_widget), fe);
2763 g_signal_connect(fe->printop, "begin-print", G_CALLBACK(print_begin), fe);
2764 g_signal_connect(fe->printop, "draw-page", G_CALLBACK(draw_page), fe);
2765 g_signal_connect(fe->printop, "end-print", G_CALLBACK(print_end), fe);
2766#ifdef USE_EMBED_PAGE_SETUP
2767 gtk_print_operation_set_embed_page_setup(fe->printop, true);
2768#else
2769 if (page_setup == NULL) {
2770 page_setup =
2771 g_object_ref(
2772 gtk_print_operation_get_default_page_setup(fe->printop));
2773 }
2774 if (settings == NULL) {
2775 settings =
2776 g_object_ref(gtk_print_operation_get_print_settings(fe->printop));
2777 }
2778 new_page_setup = gtk_print_run_page_setup_dialog(GTK_WINDOW(fe->window),
2779 page_setup, settings);
2780 g_object_unref(page_setup);
2781 page_setup = new_page_setup;
2782 gtk_print_operation_set_default_page_setup(fe->printop, page_setup);
2783#endif
2784
2785 if (settings != NULL)
2786 gtk_print_operation_set_print_settings(fe->printop, settings);
2787 if (page_setup != NULL)
2788 gtk_print_operation_set_default_page_setup(fe->printop, page_setup);
2789
2790 switch (gtk_print_operation_run(fe->printop,
2791 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2792 GTK_WINDOW(fe->window), &error)) {
2793 case GTK_PRINT_OPERATION_RESULT_ERROR:
2794 error_box(fe->window, error->message);
2795 g_error_free(error);
2796 break;
2797 case GTK_PRINT_OPERATION_RESULT_APPLY:
2798 if (settings != NULL)
2799 g_object_unref(settings);
2800 settings =
2801 g_object_ref(gtk_print_operation_get_print_settings(fe->printop));
2802#ifdef USE_EMBED_PAGE_SETUP
2803 if (page_setup != NULL)
2804 g_object_unref(page_setup);
2805 page_setup =
2806 g_object_ref(
2807 gtk_print_operation_get_default_page_setup(fe->printop));
2808#endif
2809 break;
2810 default:
2811 /* Don't error out on -Werror=switch. */
2812 break;
2813 }
2814
2815 g_object_unref(fe->printop);
2816 fe->printop = NULL;
2817 fe->printcontext = NULL;
2818}
2819#endif /* USE_PRINTING */
2820
2821struct savefile_write_ctx {
2822 FILE *fp;
2823 int error;
2824};
2825
2826static void savefile_write(void *wctx, const void *buf, int len)
2827{
2828 struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx;
2829 if (fwrite(buf, 1, len, ctx->fp) < len)
2830 ctx->error = errno;
2831}
2832
2833static bool savefile_read(void *wctx, void *buf, int len)
2834{
2835 FILE *fp = (FILE *)wctx;
2836 int ret;
2837
2838 ret = fread(buf, 1, len, fp);
2839 return (ret == len);
2840}
2841
2842static void menu_save_event(GtkMenuItem *menuitem, gpointer data)
2843{
2844 frontend *fe = (frontend *)data;
2845 char *name;
2846
2847 name = file_selector(fe, "Enter name of game file to save", true);
2848
2849 if (name) {
2850 FILE *fp;
2851
2852 if ((fp = fopen(name, "r")) != NULL) {
2853 char buf[256 + FILENAME_MAX];
2854 fclose(fp);
2855 /* file exists */
2856
2857 sprintf(buf, "Are you sure you want to overwrite the"
2858 " file \"%.*s\"?",
2859 FILENAME_MAX, name);
2860 if (!message_box(fe->window, "Question", buf, true, MB_YESNO))
2861 goto free_and_return;
2862 }
2863
2864 fp = fopen(name, "w");
2865
2866 if (!fp) {
2867 error_box(fe->window, "Unable to open save file");
2868 goto free_and_return;
2869 }
2870
2871 {
2872 struct savefile_write_ctx ctx;
2873 ctx.fp = fp;
2874 ctx.error = 0;
2875 midend_serialise(fe->me, savefile_write, &ctx);
2876 fclose(fp);
2877 if (ctx.error) {
2878 char boxmsg[512];
2879 sprintf(boxmsg, "Error writing save file: %.400s",
2880 strerror(ctx.error));
2881 error_box(fe->window, boxmsg);
2882 goto free_and_return;
2883 }
2884 }
2885 free_and_return:
2886 sfree(name);
2887 }
2888}
2889
2890static void menu_load_event(GtkMenuItem *menuitem, gpointer data)
2891{
2892 frontend *fe = (frontend *)data;
2893 char *name;
2894 const char *err;
2895
2896 name = file_selector(fe, "Enter name of saved game file to load", false);
2897
2898 if (name) {
2899 FILE *fp = fopen(name, "r");
2900 sfree(name);
2901
2902 if (!fp) {
2903 error_box(fe->window, "Unable to open saved game file");
2904 return;
2905 }
2906
2907 err = midend_deserialise(fe->me, savefile_read, fp);
2908
2909 fclose(fp);
2910
2911 if (err) {
2912 error_box(fe->window, err);
2913 return;
2914 }
2915
2916 changed_preset(fe);
2917 resize_fe(fe);
2918 midend_redraw(fe->me);
2919 }
2920}
2921
2922#ifdef USE_PRINTING
2923static void menu_print_event(GtkMenuItem *menuitem, gpointer data)
2924{
2925 frontend *fe = (frontend *)data;
2926
2927 print_dialog(fe);
2928}
2929#endif
2930
2931static void menu_solve_event(GtkMenuItem *menuitem, gpointer data)
2932{
2933 frontend *fe = (frontend *)data;
2934 const char *msg;
2935
2936 msg = midend_solve(fe->me);
2937
2938 if (msg)
2939 error_box(fe->window, msg);
2940}
2941
2942static void menu_restart_event(GtkMenuItem *menuitem, gpointer data)
2943{
2944 frontend *fe = (frontend *)data;
2945
2946 midend_restart_game(fe->me);
2947}
2948
2949static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
2950{
2951 frontend *fe = (frontend *)data;
2952 int which = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
2953 "user-data"));
2954
2955 if (fe->preset_threaded ||
2956 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
2957 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
2958 return;
2959 changed_preset(fe); /* Put the old preset back! */
2960 if (!get_config(fe, which))
2961 return;
2962
2963 midend_new_game(fe->me);
2964 resize_fe(fe);
2965 midend_redraw(fe->me);
2966}
2967
2968static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
2969{
2970 frontend *fe = (frontend *)data;
2971
2972#if GTK_CHECK_VERSION(3,0,0)
2973# define ABOUT_PARAMS \
2974 "program-name", thegame.name, \
2975 "version", ver, \
2976 "comments", "Part of Simon Tatham's Portable Puzzle Collection"
2977
2978 extern char *const *const xpm_icons[];
2979 extern const int n_xpm_icons;
2980
2981 if (n_xpm_icons) {
2982 GdkPixbuf *icon = gdk_pixbuf_new_from_xpm_data
2983 ((const gchar **)xpm_icons[n_xpm_icons-1]);
2984
2985 gtk_show_about_dialog
2986 (GTK_WINDOW(fe->window),
2987 ABOUT_PARAMS,
2988 "logo", icon,
2989 (const gchar *)NULL);
2990 g_object_unref(G_OBJECT(icon));
2991 }
2992 else {
2993 gtk_show_about_dialog
2994 (GTK_WINDOW(fe->window),
2995 ABOUT_PARAMS,
2996 (const gchar *)NULL);
2997 }
2998#else
2999 char titlebuf[256];
3000 char textbuf[1024];
3001
3002 sprintf(titlebuf, "About %.200s", thegame.name);
3003 sprintf(textbuf,
3004 "%.200s\n\n"
3005 "from Simon Tatham's Portable Puzzle Collection\n\n"
3006 "%.500s", thegame.name, ver);
3007
3008 message_box(fe->window, titlebuf, textbuf, true, MB_OK);
3009#endif
3010}
3011
3012static GtkWidget *add_menu_ui_item(
3013 frontend *fe, GtkContainer *cont, const char *text, int action,
3014 int accel_key, int accel_keyqual)
3015{
3016 GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
3017 gtk_container_add(cont, menuitem);
3018 g_object_set_data(G_OBJECT(menuitem), "user-data",
3019 GINT_TO_POINTER(action));
3020 g_signal_connect(G_OBJECT(menuitem), "activate",
3021 G_CALLBACK(menu_key_event), fe);
3022
3023 if (accel_key) {
3024 /*
3025 * Display a keyboard accelerator alongside this menu item.
3026 * Actually this won't be processed via the usual GTK
3027 * accelerator system, because we add it to a dummy
3028 * accelerator group which is never actually activated on the
3029 * main window; this permits back ends to override special
3030 * keys like 'n' and 'r' and 'u' in some UI states. So
3031 * whatever keystroke we display here will still go to
3032 * key_event and be handled in the normal way.
3033 */
3034 gtk_widget_add_accelerator(menuitem,
3035 "activate", fe->dummy_accelgroup,
3036 accel_key, accel_keyqual,
3037 GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
3038 }
3039
3040 gtk_widget_show(menuitem);
3041 return menuitem;
3042}
3043
3044static void add_menu_separator(GtkContainer *cont)
3045{
3046 GtkWidget *menuitem = gtk_menu_item_new();
3047 gtk_container_add(cont, menuitem);
3048 gtk_widget_show(menuitem);
3049}
3050
3051static void populate_gtk_preset_menu(frontend *fe, struct preset_menu *menu,
3052 GtkWidget *gtkmenu)
3053{
3054 int i;
3055
3056 for (i = 0; i < menu->n_entries; i++) {
3057 struct preset_menu_entry *entry = &menu->entries[i];
3058 GtkWidget *menuitem;
3059
3060 if (entry->params) {
3061 menuitem = gtk_radio_menu_item_new_with_label(
3062 fe->preset_radio, entry->title);
3063 fe->preset_radio = gtk_radio_menu_item_get_group(
3064 GTK_RADIO_MENU_ITEM(menuitem));
3065 g_object_set_data(G_OBJECT(menuitem), "user-data", entry);
3066 g_signal_connect(G_OBJECT(menuitem), "activate",
3067 G_CALLBACK(menu_preset_event), fe);
3068 } else {
3069 GtkWidget *submenu;
3070 menuitem = gtk_menu_item_new_with_label(entry->title);
3071 submenu = gtk_menu_new();
3072 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
3073 populate_gtk_preset_menu(fe, entry->submenu, submenu);
3074 }
3075
3076 gtk_container_add(GTK_CONTAINER(gtkmenu), menuitem);
3077 gtk_widget_show(menuitem);
3078 }
3079}
3080
3081enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */
3082
3083static frontend *new_window(
3084 char *arg, int argtype, char **error, bool headless)
3085{
3086 frontend *fe;
3087#ifdef USE_PRINTING
3088 frontend *print_fe = NULL;
3089#endif
3090 GtkBox *vbox, *hbox;
3091 GtkWidget *menu, *menuitem;
3092 GList *iconlist;
3093 int x, y, n;
3094 char errbuf[1024];
3095 extern char *const *const xpm_icons[];
3096 extern const int n_xpm_icons;
3097 struct preset_menu *preset_menu;
3098
3099 fe = snew(frontend);
3100 memset(fe, 0, sizeof(frontend));
3101
3102#ifndef USE_CAIRO
3103 if (headless) {
3104 fprintf(stderr, "headless mode not supported for non-Cairo drawing\n");
3105 exit(1);
3106 }
3107#else
3108 fe->headless = headless;
3109 fe->ps = 1; /* in headless mode, configure_area won't have set this */
3110#endif
3111
3112 fe->timer_active = false;
3113 fe->timer_id = -1;
3114
3115 fe->me = midend_new(fe, &thegame, &gtk_drawing, fe);
3116
3117 fe->dr_api = &internal_drawing;
3118
3119#ifdef USE_PRINTING
3120 if (thegame.can_print) {
3121 print_fe = snew(frontend);
3122 memset(print_fe, 0, sizeof(frontend));
3123
3124 /* Defaults */
3125 print_fe->printcount = print_fe->printw = print_fe->printh = 1;
3126 print_fe->printscale = 100;
3127 print_fe->printsolns = false;
3128 print_fe->printcolour = thegame.can_print_in_colour;
3129
3130 /*
3131 * We need to use the same midend as the main frontend because
3132 * we need midend_print_puzzle() to be able to print the
3133 * current puzzle.
3134 */
3135 print_fe->me = fe->me;
3136
3137 print_fe->print_dr = drawing_new(&gtk_drawing, print_fe->me, print_fe);
3138
3139 print_fe->dr_api = &internal_printing;
3140 }
3141#endif
3142
3143 if (arg) {
3144 const char *err;
3145 FILE *fp;
3146
3147 errbuf[0] = '\0';
3148
3149 switch (argtype) {
3150 case ARG_ID:
3151 err = midend_game_id(fe->me, arg);
3152 if (!err)
3153 midend_new_game(fe->me);
3154 else
3155 sprintf(errbuf, "Invalid game ID: %.800s", err);
3156 break;
3157 case ARG_SAVE:
3158 fp = fopen(arg, "r");
3159 if (!fp) {
3160 sprintf(errbuf, "Error opening file: %.800s", strerror(errno));
3161 } else {
3162 err = midend_deserialise(fe->me, savefile_read, fp);
3163 if (err)
3164 sprintf(errbuf, "Invalid save file: %.800s", err);
3165 fclose(fp);
3166 }
3167 break;
3168 default /*case ARG_EITHER*/:
3169 /*
3170 * First try treating the argument as a game ID.
3171 */
3172 err = midend_game_id(fe->me, arg);
3173 if (!err) {
3174 /*
3175 * It's a valid game ID.
3176 */
3177 midend_new_game(fe->me);
3178 } else {
3179 FILE *fp = fopen(arg, "r");
3180 if (!fp) {
3181 sprintf(errbuf, "Supplied argument is neither a game ID (%.400s)"
3182 " nor a save file (%.400s)", err, strerror(errno));
3183 } else {
3184 err = midend_deserialise(fe->me, savefile_read, fp);
3185 if (err)
3186 sprintf(errbuf, "%.800s", err);
3187 fclose(fp);
3188 }
3189 }
3190 break;
3191 }
3192 if (*errbuf) {
3193 *error = dupstr(errbuf);
3194 midend_free(fe->me);
3195 sfree(fe);
3196#ifdef USE_PRINTING
3197 if (thegame.can_print) {
3198 drawing_free(print_fe->print_dr);
3199 sfree(print_fe);
3200 }
3201#endif
3202 return NULL;
3203 }
3204
3205 } else {
3206 midend_new_game(fe->me);
3207 }
3208
3209 if (headless) {
3210 snaffle_colours(fe);
3211 get_size(fe, &fe->pw, &fe->ph);
3212 setup_backing_store(fe);
3213 return fe;
3214 }
3215
3216#if !GTK_CHECK_VERSION(3,0,0)
3217 {
3218 /*
3219 * try_shrink_drawing_area() will do some fiddling with the
3220 * window size request (see comment in that function) after
3221 * all the bits and pieces such as the menu bar and status bar
3222 * have appeared in the puzzle window.
3223 *
3224 * However, on Unity systems, the menu bar _doesn't_ appear in
3225 * the puzzle window, because the Unity shell hijacks it into
3226 * the menu bar at the very top of the screen. We therefore
3227 * try to detect that situation here, so that we don't sit
3228 * here forever waiting for a menu bar.
3229 */
3230 const char prop[] = "gtk-shell-shows-menubar";
3231 GtkSettings *settings = gtk_settings_get_default();
3232 if (!g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
3233 prop)) {
3234 fe->menubar_is_local = true;
3235 } else {
3236 int unity_mode;
3237 g_object_get(gtk_settings_get_default(),
3238 prop, &unity_mode,
3239 (const gchar *)NULL);
3240 fe->menubar_is_local = !unity_mode;
3241 }
3242 }
3243#endif
3244
3245#if GTK_CHECK_VERSION(3,0,0)
3246 fe->awaiting_resize_ack = false;
3247#endif
3248
3249 fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3250 gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name);
3251
3252 vbox = GTK_BOX(gtk_vbox_new(false, 0));
3253 gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox));
3254 gtk_widget_show(GTK_WIDGET(vbox));
3255
3256 fe->dummy_accelgroup = gtk_accel_group_new();
3257 /*
3258 * Intentionally _not_ added to the window via
3259 * gtk_window_add_accel_group; see menu_key_event
3260 */
3261
3262 hbox = GTK_BOX(gtk_hbox_new(false, 0));
3263 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), false, false, 0);
3264 gtk_widget_show(GTK_WIDGET(hbox));
3265
3266 fe->menubar = gtk_menu_bar_new();
3267 gtk_box_pack_start(hbox, fe->menubar, true, true, 0);
3268 gtk_widget_show(fe->menubar);
3269
3270 menuitem = gtk_menu_item_new_with_mnemonic("_Game");
3271 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
3272 gtk_widget_show(menuitem);
3273
3274 menu = gtk_menu_new();
3275 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
3276
3277 add_menu_ui_item(fe, GTK_CONTAINER(menu), "New", UI_NEWGAME, 'n', 0);
3278
3279 menuitem = gtk_menu_item_new_with_label("Restart");
3280 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3281 g_signal_connect(G_OBJECT(menuitem), "activate",
3282 G_CALLBACK(menu_restart_event), fe);
3283 gtk_widget_show(menuitem);
3284
3285 menuitem = gtk_menu_item_new_with_label("Specific...");
3286 g_object_set_data(G_OBJECT(menuitem), "user-data",
3287 GINT_TO_POINTER(CFG_DESC));
3288 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3289 g_signal_connect(G_OBJECT(menuitem), "activate",
3290 G_CALLBACK(menu_config_event), fe);
3291 gtk_widget_show(menuitem);
3292
3293 menuitem = gtk_menu_item_new_with_label("Random Seed...");
3294 g_object_set_data(G_OBJECT(menuitem), "user-data",
3295 GINT_TO_POINTER(CFG_SEED));
3296 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3297 g_signal_connect(G_OBJECT(menuitem), "activate",
3298 G_CALLBACK(menu_config_event), fe);
3299 gtk_widget_show(menuitem);
3300
3301 fe->preset_radio = NULL;
3302 fe->preset_custom = NULL;
3303 fe->preset_threaded = false;
3304
3305 preset_menu = midend_get_presets(fe->me, NULL);
3306 if (preset_menu->n_entries > 0 || thegame.can_configure) {
3307 GtkWidget *submenu;
3308
3309 menuitem = gtk_menu_item_new_with_mnemonic("_Type");
3310 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
3311 gtk_widget_show(menuitem);
3312
3313 submenu = gtk_menu_new();
3314 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
3315
3316 populate_gtk_preset_menu(fe, preset_menu, submenu);
3317
3318 if (thegame.can_configure) {
3319 menuitem = fe->preset_custom =
3320 gtk_radio_menu_item_new_with_label(fe->preset_radio,
3321 "Custom...");
3322 fe->preset_radio =
3323 gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
3324 gtk_container_add(GTK_CONTAINER(submenu), menuitem);
3325 g_object_set_data(G_OBJECT(menuitem), "user-data",
3326 GINT_TO_POINTER(CFG_SETTINGS));
3327 g_signal_connect(G_OBJECT(menuitem), "activate",
3328 G_CALLBACK(menu_config_event), fe);
3329 gtk_widget_show(menuitem);
3330 }
3331
3332 }
3333
3334 add_menu_separator(GTK_CONTAINER(menu));
3335 menuitem = gtk_menu_item_new_with_label("Load...");
3336 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3337 g_signal_connect(G_OBJECT(menuitem), "activate",
3338 G_CALLBACK(menu_load_event), fe);
3339 gtk_widget_show(menuitem);
3340 menuitem = gtk_menu_item_new_with_label("Save...");
3341 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3342 g_signal_connect(G_OBJECT(menuitem), "activate",
3343 G_CALLBACK(menu_save_event), fe);
3344 gtk_widget_show(menuitem);
3345#ifdef USE_PRINTING
3346 if (thegame.can_print) {
3347 add_menu_separator(GTK_CONTAINER(menu));
3348 menuitem = gtk_menu_item_new_with_label("Print...");
3349 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3350 g_signal_connect(G_OBJECT(menuitem), "activate",
3351 G_CALLBACK(menu_print_event), print_fe);
3352 gtk_widget_show(menuitem);
3353 }
3354#endif
3355#ifndef STYLUS_BASED
3356 add_menu_separator(GTK_CONTAINER(menu));
3357 add_menu_ui_item(fe, GTK_CONTAINER(menu), "Undo", UI_UNDO, 'u', 0);
3358 add_menu_ui_item(fe, GTK_CONTAINER(menu), "Redo", UI_REDO, 'r', 0);
3359#endif
3360 if (thegame.can_format_as_text_ever) {
3361 add_menu_separator(GTK_CONTAINER(menu));
3362 menuitem = gtk_menu_item_new_with_label("Copy");
3363 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3364 g_signal_connect(G_OBJECT(menuitem), "activate",
3365 G_CALLBACK(menu_copy_event), fe);
3366 gtk_widget_show(menuitem);
3367 fe->copy_menu_item = menuitem;
3368 } else {
3369 fe->copy_menu_item = NULL;
3370 }
3371 if (thegame.can_solve) {
3372 add_menu_separator(GTK_CONTAINER(menu));
3373 menuitem = gtk_menu_item_new_with_label("Solve");
3374 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3375 g_signal_connect(G_OBJECT(menuitem), "activate",
3376 G_CALLBACK(menu_solve_event), fe);
3377 gtk_widget_show(menuitem);
3378 }
3379 add_menu_separator(GTK_CONTAINER(menu));
3380 add_menu_ui_item(fe, GTK_CONTAINER(menu), "Exit", UI_QUIT, 'q', 0);
3381
3382 menuitem = gtk_menu_item_new_with_mnemonic("_Help");
3383 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
3384 gtk_widget_show(menuitem);
3385
3386 menu = gtk_menu_new();
3387 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
3388
3389 menuitem = gtk_menu_item_new_with_label("About");
3390 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3391 g_signal_connect(G_OBJECT(menuitem), "activate",
3392 G_CALLBACK(menu_about_event), fe);
3393 gtk_widget_show(menuitem);
3394
3395#ifdef STYLUS_BASED
3396 menuitem=gtk_button_new_with_mnemonic("_Redo");
3397 g_object_set_data(G_OBJECT(menuitem), "user-data",
3398 GINT_TO_POINTER(UI_REDO));
3399 g_signal_connect(G_OBJECT(menuitem), "clicked",
3400 G_CALLBACK(menu_key_event), fe);
3401 gtk_box_pack_end(hbox, menuitem, false, false, 0);
3402 gtk_widget_show(menuitem);
3403
3404 menuitem=gtk_button_new_with_mnemonic("_Undo");
3405 g_object_set_data(G_OBJECT(menuitem), "user-data",
3406 GINT_TO_POINTER(UI_UNDO));
3407 g_signal_connect(G_OBJECT(menuitem), "clicked",
3408 G_CALLBACK(menu_key_event), fe);
3409 gtk_box_pack_end(hbox, menuitem, false, false, 0);
3410 gtk_widget_show(menuitem);
3411
3412 if (thegame.flags & REQUIRE_NUMPAD) {
3413 hbox = GTK_BOX(gtk_hbox_new(false, 0));
3414 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), false, false, 0);
3415 gtk_widget_show(GTK_WIDGET(hbox));
3416
3417 *((int*)errbuf)=0;
3418 errbuf[1]='\0';
3419 for(errbuf[0]='0';errbuf[0]<='9';errbuf[0]++) {
3420 menuitem=gtk_button_new_with_label(errbuf);
3421 g_object_set_data(G_OBJECT(menuitem), "user-data",
3422 GINT_TO_POINTER((int)(errbuf[0])));
3423 g_signal_connect(G_OBJECT(menuitem), "clicked",
3424 G_CALLBACK(menu_key_event), fe);
3425 gtk_box_pack_start(hbox, menuitem, true, true, 0);
3426 gtk_widget_show(menuitem);
3427 }
3428 }
3429#endif /* STYLUS_BASED */
3430
3431 changed_preset(fe);
3432
3433 snaffle_colours(fe);
3434
3435 if (midend_wants_statusbar(fe->me)) {
3436 GtkWidget *viewport;
3437 GtkRequisition req;
3438
3439 viewport = gtk_viewport_new(NULL, NULL);
3440 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
3441 fe->statusbar = gtk_statusbar_new();
3442 gtk_container_add(GTK_CONTAINER(viewport), fe->statusbar);
3443 gtk_widget_show(viewport);
3444 gtk_box_pack_end(vbox, viewport, false, false, 0);
3445 gtk_widget_show(fe->statusbar);
3446 fe->statusctx = gtk_statusbar_get_context_id
3447 (GTK_STATUSBAR(fe->statusbar), "game");
3448 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx,
3449 DEFAULT_STATUSBAR_TEXT);
3450#if GTK_CHECK_VERSION(3,0,0)
3451 gtk_widget_get_preferred_size(fe->statusbar, &req, NULL);
3452#else
3453 gtk_widget_size_request(fe->statusbar, &req);
3454#endif
3455 gtk_widget_set_size_request(viewport, -1, req.height);
3456 } else
3457 fe->statusbar = NULL;
3458
3459 fe->area = gtk_drawing_area_new();
3460#if GTK_CHECK_VERSION(2,0,0) && !GTK_CHECK_VERSION(3,0,0)
3461 gtk_widget_set_double_buffered(fe->area, false);
3462#endif
3463 {
3464 GdkGeometry geom;
3465 geom.base_width = 0;
3466#if GTK_CHECK_VERSION(3,0,0)
3467 geom.base_height = window_extra_height(fe);
3468 gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), NULL,
3469 &geom, GDK_HINT_BASE_SIZE);
3470#else
3471 geom.base_height = 0;
3472 gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), fe->area,
3473 &geom, GDK_HINT_BASE_SIZE);
3474#endif
3475 }
3476 fe->w = -1;
3477 fe->h = -1;
3478 get_size(fe, &x, &y);
3479#if GTK_CHECK_VERSION(3,0,0)
3480 gtk_window_set_default_size(GTK_WINDOW(fe->window),
3481 x, y + window_extra_height(fe));
3482#else
3483 fe->drawing_area_shrink_pending = false;
3484 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
3485#endif
3486
3487 gtk_box_pack_end(vbox, fe->area, true, true, 0);
3488
3489 clear_backing_store(fe);
3490 fe->fonts = NULL;
3491 fe->nfonts = fe->fontsize = 0;
3492
3493 fe->paste_data = NULL;
3494 fe->paste_data_len = 0;
3495
3496 g_signal_connect(G_OBJECT(fe->window), "destroy",
3497 G_CALLBACK(destroy), fe);
3498 g_signal_connect(G_OBJECT(fe->window), "key_press_event",
3499 G_CALLBACK(key_event), fe);
3500 g_signal_connect(G_OBJECT(fe->area), "button_press_event",
3501 G_CALLBACK(button_event), fe);
3502 g_signal_connect(G_OBJECT(fe->area), "button_release_event",
3503 G_CALLBACK(button_event), fe);
3504 g_signal_connect(G_OBJECT(fe->area), "motion_notify_event",
3505 G_CALLBACK(motion_event), fe);
3506 g_signal_connect(G_OBJECT(fe->area), "selection_get",
3507 G_CALLBACK(selection_get), fe);
3508 g_signal_connect(G_OBJECT(fe->area), "selection_clear_event",
3509 G_CALLBACK(selection_clear), fe);
3510#if GTK_CHECK_VERSION(3,0,0)
3511 g_signal_connect(G_OBJECT(fe->area), "draw",
3512 G_CALLBACK(draw_area), fe);
3513#else
3514 g_signal_connect(G_OBJECT(fe->area), "expose_event",
3515 G_CALLBACK(expose_area), fe);
3516#endif
3517 g_signal_connect(G_OBJECT(fe->window), "map_event",
3518 G_CALLBACK(map_window), fe);
3519 g_signal_connect(G_OBJECT(fe->area), "configure_event",
3520 G_CALLBACK(configure_area), fe);
3521 g_signal_connect(G_OBJECT(fe->window), "configure_event",
3522 G_CALLBACK(configure_window), fe);
3523#if GTK_CHECK_VERSION(3,0,0)
3524 g_signal_connect(G_OBJECT(fe->window), "size_allocate",
3525 G_CALLBACK(window_size_alloc), fe);
3526#endif
3527
3528 gtk_widget_add_events(GTK_WIDGET(fe->area),
3529 GDK_BUTTON_PRESS_MASK |
3530 GDK_BUTTON_RELEASE_MASK |
3531 GDK_BUTTON_MOTION_MASK |
3532 GDK_POINTER_MOTION_HINT_MASK);
3533
3534 if (n_xpm_icons) {
3535 gtk_window_set_icon(GTK_WINDOW(fe->window),
3536 gdk_pixbuf_new_from_xpm_data
3537 ((const gchar **)xpm_icons[0]));
3538
3539 iconlist = NULL;
3540 for (n = 0; n < n_xpm_icons; n++) {
3541 iconlist =
3542 g_list_append(iconlist,
3543 gdk_pixbuf_new_from_xpm_data((const gchar **)
3544 xpm_icons[n]));
3545 }
3546 gtk_window_set_icon_list(GTK_WINDOW(fe->window), iconlist);
3547 }
3548
3549 gtk_widget_show(fe->area);
3550 gtk_widget_show(fe->window);
3551
3552#if !GTK_CHECK_VERSION(3,0,0)
3553 fe->drawing_area_shrink_pending = true;
3554 try_shrink_drawing_area(fe);
3555#endif
3556
3557 set_window_background(fe, 0);
3558
3559 return fe;
3560}
3561
3562static void list_presets_from_menu(struct preset_menu *menu)
3563{
3564 int i;
3565
3566 for (i = 0; i < menu->n_entries; i++) {
3567 if (menu->entries[i].params) {
3568 char *paramstr = thegame.encode_params(
3569 menu->entries[i].params, true);
3570 printf("%s %s\n", paramstr, menu->entries[i].title);
3571 sfree(paramstr);
3572 } else {
3573 list_presets_from_menu(menu->entries[i].submenu);
3574 }
3575 }
3576}
3577
3578int main(int argc, char **argv)
3579{
3580 char *pname = argv[0];
3581 char *error;
3582 int ngenerate = 0, px = 1, py = 1;
3583 bool print = false;
3584 bool time_generation = false, test_solve = false, list_presets = false;
3585 bool soln = false, colour = false;
3586 float scale = 1.0F;
3587 float redo_proportion = 0.0F;
3588 const char *savefile = NULL, *savesuffix = NULL;
3589 char *arg = NULL;
3590 int argtype = ARG_EITHER;
3591 char *screenshot_file = NULL;
3592 bool doing_opts = true;
3593 int ac = argc;
3594 char **av = argv;
3595 char errbuf[500];
3596
3597 /*
3598 * Command line parsing in this function is rather fiddly,
3599 * because GTK wants to have a go at argc/argv _first_ - and
3600 * yet we can't let it, because gtk_init() will bomb out if it
3601 * can't open an X display, whereas in fact we want to permit
3602 * our --generate and --print modes to run without an X
3603 * display.
3604 *
3605 * So what we do is:
3606 * - we parse the command line ourselves, without modifying
3607 * argc/argv
3608 * - if we encounter an error which might plausibly be the
3609 * result of a GTK command line (i.e. not detailed errors in
3610 * particular options of ours) we store the error message
3611 * and terminate parsing.
3612 * - if we got enough out of the command line to know it
3613 * specifies a non-X mode of operation, we either display
3614 * the stored error and return failure, or if there is no
3615 * stored error we do the non-X operation and return
3616 * success.
3617 * - otherwise, we go straight to gtk_init().
3618 */
3619
3620 errbuf[0] = '\0';
3621 while (--ac > 0) {
3622 char *p = *++av;
3623 if (doing_opts && !strcmp(p, "--version")) {
3624 printf("%s, from Simon Tatham's Portable Puzzle Collection\n%s\n",
3625 thegame.name, ver);
3626 return 0;
3627 } else if (doing_opts && !strcmp(p, "--generate")) {
3628 if (--ac > 0) {
3629 ngenerate = atoi(*++av);
3630 if (!ngenerate) {
3631 fprintf(stderr, "%s: '--generate' expected a number\n",
3632 pname);
3633 return 1;
3634 }
3635 } else
3636 ngenerate = 1;
3637 } else if (doing_opts && !strcmp(p, "--time-generation")) {
3638 time_generation = true;
3639 } else if (doing_opts && !strcmp(p, "--test-solve")) {
3640 test_solve = true;
3641 } else if (doing_opts && !strcmp(p, "--list-presets")) {
3642 list_presets = true;
3643 } else if (doing_opts && !strcmp(p, "--save")) {
3644 if (--ac > 0) {
3645 savefile = *++av;
3646 } else {
3647 fprintf(stderr, "%s: '--save' expected a filename\n",
3648 pname);
3649 return 1;
3650 }
3651 } else if (doing_opts && (!strcmp(p, "--save-suffix") ||
3652 !strcmp(p, "--savesuffix"))) {
3653 if (--ac > 0) {
3654 savesuffix = *++av;
3655 } else {
3656 fprintf(stderr, "%s: '--save-suffix' expected a filename\n",
3657 pname);
3658 return 1;
3659 }
3660 } else if (doing_opts && !strcmp(p, "--print")) {
3661 if (!thegame.can_print) {
3662 fprintf(stderr, "%s: this game does not support printing\n",
3663 pname);
3664 return 1;
3665 }
3666 print = true;
3667 if (--ac > 0) {
3668 char *dim = *++av;
3669 if (sscanf(dim, "%dx%d", &px, &py) != 2) {
3670 fprintf(stderr, "%s: unable to parse argument '%s' to "
3671 "'--print'\n", pname, dim);
3672 return 1;
3673 }
3674 } else {
3675 px = py = 1;
3676 }
3677 } else if (doing_opts && !strcmp(p, "--scale")) {
3678 if (--ac > 0) {
3679 scale = atof(*++av);
3680 } else {
3681 fprintf(stderr, "%s: no argument supplied to '--scale'\n",
3682 pname);
3683 return 1;
3684 }
3685 } else if (doing_opts && !strcmp(p, "--redo")) {
3686 /*
3687 * This is an internal option which I don't expect
3688 * users to have any particular use for. The effect of
3689 * --redo is that once the game has been loaded and
3690 * initialised, the next move in the redo chain is
3691 * replayed, and the game screen is redrawn part way
3692 * through the making of the move. This is only
3693 * meaningful if there _is_ a next move in the redo
3694 * chain, which means in turn that this option is only
3695 * useful if you're also passing a save file on the
3696 * command line.
3697 *
3698 * This option is used by the script which generates
3699 * the puzzle icons and website screenshots, and I
3700 * don't imagine it's useful for anything else.
3701 * (Unless, I suppose, users don't like my screenshots
3702 * and want to generate their own in the same way for
3703 * some repackaged version of the puzzles.)
3704 */
3705 if (--ac > 0) {
3706 redo_proportion = atof(*++av);
3707 } else {
3708 fprintf(stderr, "%s: no argument supplied to '--redo'\n",
3709 pname);
3710 return 1;
3711 }
3712 } else if (doing_opts && !strcmp(p, "--screenshot")) {
3713 /*
3714 * Another internal option for the icon building
3715 * script. This causes a screenshot of the central
3716 * drawing area (i.e. not including the menu bar or
3717 * status bar) to be saved to a PNG file once the
3718 * window has been drawn, and then the application
3719 * quits immediately.
3720 */
3721 if (--ac > 0) {
3722 screenshot_file = *++av;
3723 } else {
3724 fprintf(stderr, "%s: no argument supplied to '--screenshot'\n",
3725 pname);
3726 return 1;
3727 }
3728 } else if (doing_opts && (!strcmp(p, "--with-solutions") ||
3729 !strcmp(p, "--with-solution") ||
3730 !strcmp(p, "--with-solns") ||
3731 !strcmp(p, "--with-soln") ||
3732 !strcmp(p, "--solutions") ||
3733 !strcmp(p, "--solution") ||
3734 !strcmp(p, "--solns") ||
3735 !strcmp(p, "--soln"))) {
3736 soln = true;
3737 } else if (doing_opts && !strcmp(p, "--colour")) {
3738 if (!thegame.can_print_in_colour) {
3739 fprintf(stderr, "%s: this game does not support colour"
3740 " printing\n", pname);
3741 return 1;
3742 }
3743 colour = true;
3744 } else if (doing_opts && !strcmp(p, "--load")) {
3745 argtype = ARG_SAVE;
3746 } else if (doing_opts && !strcmp(p, "--game")) {
3747 argtype = ARG_ID;
3748 } else if (doing_opts && !strcmp(p, "--")) {
3749 doing_opts = false;
3750 } else if (!doing_opts || p[0] != '-') {
3751 if (arg) {
3752 fprintf(stderr, "%s: more than one argument supplied\n",
3753 pname);
3754 return 1;
3755 }
3756 arg = p;
3757 } else {
3758 sprintf(errbuf, "%.100s: unrecognised option '%.100s'\n",
3759 pname, p);
3760 break;
3761 }
3762 }
3763
3764 /*
3765 * Special standalone mode for generating puzzle IDs on the
3766 * command line. Useful for generating puzzles to be printed
3767 * out and solved offline (for puzzles where that even makes
3768 * sense - Solo, for example, is a lot more pencil-and-paper
3769 * friendly than Twiddle!)
3770 *
3771 * Usage:
3772 *
3773 * <puzzle-name> --generate [<n> [<params>]]
3774 *
3775 * <n>, if present, is the number of puzzle IDs to generate.
3776 * <params>, if present, is the same type of parameter string
3777 * you would pass to the puzzle when running it in GUI mode,
3778 * including optional extras such as the expansion factor in
3779 * Rectangles and the difficulty level in Solo.
3780 *
3781 * If you specify <params>, you must also specify <n> (although
3782 * you may specify it to be 1). Sorry; that was the
3783 * simplest-to-parse command-line syntax I came up with.
3784 */
3785 if (ngenerate > 0 || print || savefile || savesuffix) {
3786 int i, n = 1;
3787 midend *me;
3788 char *id;
3789 document *doc = NULL;
3790
3791 /*
3792 * If we're in this branch, we should display any pending
3793 * error message from the command line, since GTK isn't going
3794 * to take another crack at making sense of it.
3795 */
3796 if (*errbuf) {
3797 fputs(errbuf, stderr);
3798 return 1;
3799 }
3800
3801 n = ngenerate;
3802
3803 me = midend_new(NULL, &thegame, NULL, NULL);
3804 i = 0;
3805
3806 if (savefile && !savesuffix)
3807 savesuffix = "";
3808 if (!savefile && savesuffix)
3809 savefile = "";
3810
3811 if (print)
3812 doc = document_new(px, py, scale);
3813
3814 /*
3815 * In this loop, we either generate a game ID or read one
3816 * from stdin depending on whether we're in generate mode;
3817 * then we either write it to stdout or print it, depending
3818 * on whether we're in print mode. Thus, this loop handles
3819 * generate-to-stdout, print-from-stdin and generate-and-
3820 * immediately-print modes.
3821 *
3822 * (It could also handle a copy-stdin-to-stdout mode,
3823 * although there's currently no combination of options
3824 * which will cause this loop to be activated in that mode.
3825 * It wouldn't be _entirely_ pointless, though, because
3826 * stdin could contain bare params strings or random-seed
3827 * IDs, and stdout would contain nothing but fully
3828 * generated descriptive game IDs.)
3829 */
3830 while (ngenerate == 0 || i < n) {
3831 char *pstr, *seed;
3832 const char *err;
3833 struct rusage before, after;
3834
3835 if (ngenerate == 0) {
3836 pstr = fgetline(stdin);
3837 if (!pstr)
3838 break;
3839 pstr[strcspn(pstr, "\r\n")] = '\0';
3840 } else {
3841 if (arg) {
3842 pstr = snewn(strlen(arg) + 40, char);
3843
3844 strcpy(pstr, arg);
3845 if (i > 0 && strchr(arg, '#'))
3846 sprintf(pstr + strlen(pstr), "-%d", i);
3847 } else
3848 pstr = NULL;
3849 }
3850
3851 if (pstr) {
3852 err = midend_game_id(me, pstr);
3853 if (err) {
3854 fprintf(stderr, "%s: error parsing '%s': %s\n",
3855 pname, pstr, err);
3856 return 1;
3857 }
3858 }
3859
3860 if (time_generation)
3861 getrusage(RUSAGE_SELF, &before);
3862
3863 midend_new_game(me);
3864
3865 seed = midend_get_random_seed(me);
3866
3867 if (time_generation) {
3868 double elapsed;
3869
3870 getrusage(RUSAGE_SELF, &after);
3871
3872 elapsed = (after.ru_utime.tv_sec -
3873 before.ru_utime.tv_sec);
3874 elapsed += (after.ru_utime.tv_usec -
3875 before.ru_utime.tv_usec) / 1000000.0;
3876
3877 printf("%s %s: %.6f\n", thegame.name, seed, elapsed);
3878 }
3879
3880 if (test_solve && thegame.can_solve) {
3881 /*
3882 * Now destroy the aux_info in the midend, by means of
3883 * re-entering the same game id, and then try to solve
3884 * it.
3885 */
3886 char *game_id;
3887
3888 game_id = midend_get_game_id(me);
3889 err = midend_game_id(me, game_id);
3890 if (err) {
3891 fprintf(stderr, "%s %s: game id re-entry error: %s\n",
3892 thegame.name, seed, err);
3893 return 1;
3894 }
3895 midend_new_game(me);
3896 sfree(game_id);
3897
3898 err = midend_solve(me);
3899 /*
3900 * If the solve operation returned the error "Solution
3901 * not known for this puzzle", that's OK, because that
3902 * just means it's a puzzle for which we don't have an
3903 * algorithmic solver and hence can't solve it without
3904 * the aux_info, e.g. Netslide. Any other error is a
3905 * problem, though.
3906 */
3907 if (err && strcmp(err, "Solution not known for this puzzle")) {
3908 fprintf(stderr, "%s %s: solve error: %s\n",
3909 thegame.name, seed, err);
3910 return 1;
3911 }
3912 }
3913
3914 sfree(pstr);
3915 sfree(seed);
3916
3917 if (doc) {
3918 err = midend_print_puzzle(me, doc, soln);
3919 if (err) {
3920 fprintf(stderr, "%s: error in printing: %s\n", pname, err);
3921 return 1;
3922 }
3923 }
3924 if (savefile) {
3925 struct savefile_write_ctx ctx;
3926 char *realname = snewn(40 + strlen(savefile) +
3927 strlen(savesuffix), char);
3928 sprintf(realname, "%s%d%s", savefile, i, savesuffix);
3929
3930 if (soln) {
3931 const char *err = midend_solve(me);
3932 if (err) {
3933 fprintf(stderr, "%s: unable to show solution: %s\n",
3934 realname, err);
3935 return 1;
3936 }
3937 }
3938
3939 ctx.fp = fopen(realname, "w");
3940 if (!ctx.fp) {
3941 fprintf(stderr, "%s: open: %s\n", realname,
3942 strerror(errno));
3943 return 1;
3944 }
3945 ctx.error = 0;
3946 midend_serialise(me, savefile_write, &ctx);
3947 if (ctx.error) {
3948 fprintf(stderr, "%s: write: %s\n", realname,
3949 strerror(ctx.error));
3950 return 1;
3951 }
3952 if (fclose(ctx.fp)) {
3953 fprintf(stderr, "%s: close: %s\n", realname,
3954 strerror(errno));
3955 return 1;
3956 }
3957 sfree(realname);
3958 }
3959 if (!doc && !savefile && !time_generation) {
3960 id = midend_get_game_id(me);
3961 puts(id);
3962 sfree(id);
3963 }
3964
3965 i++;
3966 }
3967
3968 if (doc) {
3969 psdata *ps = ps_init(stdout, colour);
3970 document_print(doc, ps_drawing_api(ps));
3971 document_free(doc);
3972 ps_free(ps);
3973 }
3974
3975 midend_free(me);
3976
3977 return 0;
3978 } else if (list_presets) {
3979 /*
3980 * Another specialist mode which causes the puzzle to list the
3981 * game_params strings for all its preset configurations.
3982 */
3983 midend *me;
3984 struct preset_menu *menu;
3985
3986 me = midend_new(NULL, &thegame, NULL, NULL);
3987 menu = midend_get_presets(me, NULL);
3988 list_presets_from_menu(menu);
3989 midend_free(me);
3990 return 0;
3991 } else {
3992 frontend *fe;
3993 bool headless = screenshot_file != NULL;
3994
3995 if (!headless)
3996 gtk_init(&argc, &argv);
3997
3998 fe = new_window(arg, argtype, &error, headless);
3999
4000 if (!fe) {
4001 fprintf(stderr, "%s: %s\n", pname, error);
4002 return 1;
4003 }
4004
4005 if (screenshot_file) {
4006 /*
4007 * Some puzzles will not redraw their entire area if
4008 * given a partially completed animation, which means
4009 * we must redraw now and _then_ redraw again after
4010 * freezing the move timer.
4011 */
4012 midend_force_redraw(fe->me);
4013 }
4014
4015 if (redo_proportion) {
4016 /* Start a redo. */
4017 midend_process_key(fe->me, 0, 0, 'r');
4018 /* And freeze the timer at the specified position. */
4019 midend_freeze_timer(fe->me, redo_proportion);
4020 }
4021
4022 if (screenshot_file) {
4023 save_screenshot_png(fe, screenshot_file);
4024 exit(0);
4025 }
4026
4027 gtk_main();
4028 }
4029
4030 return 0;
4031}
diff --git a/apps/plugins/puzzles/src/gtk.h b/apps/plugins/puzzles/src/gtk.h
new file mode 100644
index 0000000000..a039f0dc36
--- /dev/null
+++ b/apps/plugins/puzzles/src/gtk.h
@@ -0,0 +1,7 @@
1#ifndef PUZZLES_GTK_H
2#define PUZZLES_GTK_H
3
4extern const char *const *const xpm_icons[];
5extern const int n_xpm_icons;
6
7#endif /* PUZZLES_GTK_H */
diff --git a/apps/plugins/puzzles/src/guess.R b/apps/plugins/puzzles/src/guess.R
deleted file mode 100644
index 0e1a00c073..0000000000
--- a/apps/plugins/puzzles/src/guess.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3guess : [X] GTK COMMON guess guess-icon|no-icon
4
5guess : [G] WINDOWS COMMON guess guess.res|noicon.res
6
7ALL += guess[COMBINED]
8
9!begin am gtk
10GAMES += guess
11!end
12
13!begin >list.c
14 A(guess) \
15!end
16
17!begin >gamedesc.txt
18guess:guess.exe:Guess:Combination-guessing puzzle:Guess the hidden combination of colours.
19!end
diff --git a/apps/plugins/puzzles/src/guess.c b/apps/plugins/puzzles/src/guess.c
index a501579442..5c5f941230 100644
--- a/apps/plugins/puzzles/src/guess.c
+++ b/apps/plugins/puzzles/src/guess.c
@@ -7,7 +7,11 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#ifdef NO_TGMATH_H
11# include <math.h>
12#else
13# include <tgmath.h>
14#endif
11 15
12#include "puzzles.h" 16#include "puzzles.h"
13 17
@@ -35,6 +39,11 @@ typedef struct pegrow {
35 int *pegs; /* 0 is 'empty' */ 39 int *pegs; /* 0 is 'empty' */
36 int *feedback; /* may well be unused */ 40 int *feedback; /* may well be unused */
37} *pegrow; 41} *pegrow;
42/* Pegs can have these flags OR'ed into them. */
43#define PEG_CURSOR 0x1000
44#define PEG_HOLD 0x2000
45#define PEG_LABELLED 0x4000
46#define PEG_FLAGS (PEG_CURSOR | PEG_HOLD | PEG_LABELLED)
38 47
39struct game_state { 48struct game_state {
40 game_params params; 49 game_params params;
@@ -88,13 +97,7 @@ static bool game_fetch_preset(int i, char **name, game_params **params)
88 return false; 97 return false;
89 98
90 *name = dupstr(guess_presets[i].name); 99 *name = dupstr(guess_presets[i].name);
91 /* 100 *params = dup_params(&guess_presets[i].params);
92 * get round annoying const issues
93 */
94 {
95 game_params tmp = guess_presets[i].params;
96 *params = dup_params(&tmp);
97 }
98 101
99 return true; 102 return true;
100} 103}
@@ -365,16 +368,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
365 return dupstr("S"); 368 return dupstr("S");
366} 369}
367 370
368static bool game_can_format_as_text_now(const game_params *params)
369{
370 return true;
371}
372
373static char *game_text_format(const game_state *state)
374{
375 return NULL;
376}
377
378static bool is_markable(const game_params *params, pegrow pegs) 371static bool is_markable(const game_params *params, pegrow pegs)
379{ 372{
380 int i, nset = 0, nrequired; 373 int i, nset = 0, nrequired;
@@ -386,6 +379,7 @@ static bool is_markable(const game_params *params, pegrow pegs)
386 for (i = 0; i < params->npegs; i++) { 379 for (i = 0; i < params->npegs; i++) {
387 int c = pegs->pegs[i]; 380 int c = pegs->pegs[i];
388 if (c > 0) { 381 if (c > 0) {
382 assert(c <= params->ncolours);
389 colcount->pegs[c-1]++; 383 colcount->pegs[c-1]++;
390 nset++; 384 nset++;
391 } 385 }
@@ -414,7 +408,7 @@ struct game_ui {
414 int drag_col, drag_x, drag_y; /* x and y are *center* of peg! */ 408 int drag_col, drag_x, drag_y; /* x and y are *center* of peg! */
415 int drag_opeg; /* peg index, if dragged from a peg (from current guess), otherwise -1 */ 409 int drag_opeg; /* peg index, if dragged from a peg (from current guess), otherwise -1 */
416 410
417 bool show_labels; /* label the colours with letters */ 411 bool show_labels; /* label the colours with numbers */
418 pegrow hint; 412 pegrow hint;
419}; 413};
420 414
@@ -422,19 +416,45 @@ static game_ui *new_ui(const game_state *state)
422{ 416{
423 game_ui *ui = snew(game_ui); 417 game_ui *ui = snew(game_ui);
424 memset(ui, 0, sizeof(game_ui)); 418 memset(ui, 0, sizeof(game_ui));
425 ui->params = state->params; /* structure copy */ 419 if (state != NULL) {
426 ui->curr_pegs = new_pegrow(state->params.npegs); 420 ui->params = state->params; /* structure copy */
427 ui->holds = snewn(state->params.npegs, bool); 421 ui->curr_pegs = new_pegrow(state->params.npegs);
428 memset(ui->holds, 0, sizeof(bool)*state->params.npegs); 422 ui->holds = snewn(state->params.npegs, bool);
423 memset(ui->holds, 0, sizeof(bool)*state->params.npegs);
424 }
425 ui->display_cur = getenv_bool("PUZZLES_SHOW_CURSOR", false);
429 ui->drag_opeg = -1; 426 ui->drag_opeg = -1;
430 return ui; 427 return ui;
431} 428}
432 429
430static config_item *get_prefs(game_ui *ui)
431{
432 config_item *ret;
433
434 ret = snewn(2, config_item);
435
436 ret[0].name = "Label colours with numbers";
437 ret[0].kw = "show-labels";
438 ret[0].type = C_BOOLEAN;
439 ret[0].u.boolean.bval = ui->show_labels;
440
441 ret[1].name = NULL;
442 ret[1].type = C_END;
443
444 return ret;
445}
446
447static void set_prefs(game_ui *ui, const config_item *cfg)
448{
449 ui->show_labels = cfg[0].u.boolean.bval;
450}
451
433static void free_ui(game_ui *ui) 452static void free_ui(game_ui *ui)
434{ 453{
435 if (ui->hint) 454 if (ui->hint)
436 free_pegrow(ui->hint); 455 free_pegrow(ui->hint);
437 free_pegrow(ui->curr_pegs); 456 if (ui->curr_pegs)
457 free_pegrow(ui->curr_pegs);
438 sfree(ui->holds); 458 sfree(ui->holds);
439 sfree(ui); 459 sfree(ui);
440} 460}
@@ -462,12 +482,16 @@ static char *encode_ui(const game_ui *ui)
462 return sresize(ret, p - ret, char); 482 return sresize(ret, p - ret, char);
463} 483}
464 484
465static void decode_ui(game_ui *ui, const char *encoding) 485static void decode_ui(game_ui *ui, const char *encoding,
486 const game_state *state)
466{ 487{
467 int i; 488 int i;
468 const char *p = encoding; 489 const char *p = encoding;
469 for (i = 0; i < ui->curr_pegs->npegs; i++) { 490 for (i = 0; i < ui->curr_pegs->npegs; i++) {
470 ui->curr_pegs->pegs[i] = atoi(p); 491 ui->curr_pegs->pegs[i] = atoi(p);
492 if (ui->curr_pegs->pegs[i] < 0 ||
493 ui->curr_pegs->pegs[i] > ui->params.ncolours)
494 ui->curr_pegs->pegs[i] = 0; /* Remove invalid pegs. */
471 while (*p && isdigit((unsigned char)*p)) p++; 495 while (*p && isdigit((unsigned char)*p)) p++;
472 if (*p == '_') { 496 if (*p == '_') {
473 /* NB: old versions didn't store holds */ 497 /* NB: old versions didn't store holds */
@@ -507,7 +531,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
507 ui->markable = is_markable(&newstate->params, ui->curr_pegs); 531 ui->markable = is_markable(&newstate->params, ui->curr_pegs);
508 /* Clean up cursor position */ 532 /* Clean up cursor position */
509 if (!ui->markable && ui->peg_cur == newstate->solution->npegs) 533 if (!ui->markable && ui->peg_cur == newstate->solution->npegs)
510 ui->peg_cur--; 534 ui->peg_cur = 0;
535}
536
537static const char *current_key_label(const game_ui *ui,
538 const game_state *state, int button)
539{
540 if (state->solved) return "";
541 if (button == CURSOR_SELECT) {
542 if (ui->peg_cur == state->params.npegs) return "Submit";
543 return "Place";
544 }
545 if (button == CURSOR_SELECT2 && ui->peg_cur != state->params.npegs)
546 return "Hold";
547 return "";
511} 548}
512 549
513#define PEGSZ (ds->pegsz) 550#define PEGSZ (ds->pegsz)
@@ -700,7 +737,11 @@ static void compute_hint(const game_state *state, game_ui *ui)
700 for (j = 0; j < state->params.npegs; ++j) 737 for (j = 0; j < state->params.npegs; ++j)
701 if (state->guesses[i]->pegs[j] > maxcolour) 738 if (state->guesses[i]->pegs[j] > maxcolour)
702 maxcolour = state->guesses[i]->pegs[j]; 739 maxcolour = state->guesses[i]->pegs[j];
703 maxcolour = min(maxcolour + 1, state->params.ncolours); 740 if (state->params.allow_multiple)
741 maxcolour = min(maxcolour + 1, state->params.ncolours);
742 else
743 maxcolour = min(maxcolour + state->params.npegs,
744 state->params.ncolours);
704 745
705increase_mincolour: 746increase_mincolour:
706 for (i = 0; i < state->next_go; ++i) { 747 for (i = 0; i < state->next_go; ++i) {
@@ -722,6 +763,7 @@ increase_mincolour:
722 } 763 }
723 764
724 while (ui->hint->pegs[0] <= state->params.ncolours) { 765 while (ui->hint->pegs[0] <= state->params.ncolours) {
766 if (!is_markable(&state->params, ui->hint)) goto increment_pegrow;
725 for (i = 0; i < state->next_go; ++i) { 767 for (i = 0; i < state->next_go; ++i) {
726 mark_pegs(ui->hint, state->guesses[i], maxcolour); 768 mark_pegs(ui->hint, state->guesses[i], maxcolour);
727 for (j = 0; j < state->params.npegs; ++j) 769 for (j = 0; j < state->params.npegs; ++j)
@@ -777,7 +819,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
777 */ 819 */
778 if (button == 'l' || button == 'L') { 820 if (button == 'l' || button == 'L') {
779 ui->show_labels = !ui->show_labels; 821 ui->show_labels = !ui->show_labels;
780 return UI_UPDATE; 822 return MOVE_UI_UPDATE;
781 } 823 }
782 824
783 if (from->solved) return NULL; 825 if (from->solved) return NULL;
@@ -834,13 +876,13 @@ static char *interpret_move(const game_state *from, game_ui *ui,
834 ui->drag_y = y; 876 ui->drag_y = y;
835 debug(("Start dragging, col = %d, (%d,%d)", 877 debug(("Start dragging, col = %d, (%d,%d)",
836 ui->drag_col, ui->drag_x, ui->drag_y)); 878 ui->drag_col, ui->drag_x, ui->drag_y));
837 ret = UI_UPDATE; 879 ret = MOVE_UI_UPDATE;
838 } 880 }
839 } else if (button == LEFT_DRAG && ui->drag_col) { 881 } else if (button == LEFT_DRAG && ui->drag_col) {
840 ui->drag_x = x; 882 ui->drag_x = x;
841 ui->drag_y = y; 883 ui->drag_y = y;
842 debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y)); 884 debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y));
843 ret = UI_UPDATE; 885 ret = MOVE_UI_UPDATE;
844 } else if (button == LEFT_RELEASE && ui->drag_col) { 886 } else if (button == LEFT_RELEASE && ui->drag_col) {
845 if (over_guess > -1) { 887 if (over_guess > -1) {
846 debug(("Dropping colour %d onto guess peg %d", 888 debug(("Dropping colour %d onto guess peg %d",
@@ -857,13 +899,13 @@ static char *interpret_move(const game_state *from, game_ui *ui,
857 ui->drag_opeg = -1; 899 ui->drag_opeg = -1;
858 ui->display_cur = false; 900 ui->display_cur = false;
859 debug(("Stop dragging.")); 901 debug(("Stop dragging."));
860 ret = UI_UPDATE; 902 ret = MOVE_UI_UPDATE;
861 } else if (button == RIGHT_BUTTON) { 903 } else if (button == RIGHT_BUTTON) {
862 if (over_guess > -1) { 904 if (over_guess > -1) {
863 /* we use ths feedback in the game_ui to signify 905 /* we use ths feedback in the game_ui to signify
864 * 'carry this peg to the next guess as well'. */ 906 * 'carry this peg to the next guess as well'. */
865 ui->holds[over_guess] ^= 1; 907 ui->holds[over_guess] ^= 1;
866 ret = UI_UPDATE; 908 ret = MOVE_UI_UPDATE;
867 } 909 }
868 } else if (button == LEFT_RELEASE && over_hint && ui->markable) { 910 } else if (button == LEFT_RELEASE && over_hint && ui->markable) {
869 /* NB this won't trigger if on the end of a drag; that's on 911 /* NB this won't trigger if on the end of a drag; that's on
@@ -872,44 +914,47 @@ static char *interpret_move(const game_state *from, game_ui *ui,
872 } 914 }
873 915
874 /* keyboard input */ 916 /* keyboard input */
875 if (button == CURSOR_UP || button == CURSOR_DOWN) { 917 if (IS_CURSOR_MOVE(button)) {
876 ui->display_cur = true;
877 if (button == CURSOR_DOWN && (ui->colour_cur+1) < from->params.ncolours)
878 ui->colour_cur++;
879 if (button == CURSOR_UP && ui->colour_cur > 0)
880 ui->colour_cur--;
881 ret = UI_UPDATE;
882 } else if (button == 'h' || button == 'H' || button == '?') {
883 compute_hint(from, ui);
884 ret = UI_UPDATE;
885 } else if (button == CURSOR_LEFT || button == CURSOR_RIGHT) {
886 int maxcur = from->params.npegs; 918 int maxcur = from->params.npegs;
887 if (ui->markable) maxcur++; 919 if (ui->markable) maxcur++;
888 920
889 ui->display_cur = true; 921 ret = move_cursor(button, &ui->peg_cur, &ui->colour_cur,
890 if (button == CURSOR_RIGHT && (ui->peg_cur+1) < maxcur) 922 maxcur, from->params.ncolours,
891 ui->peg_cur++; 923 false, &ui->display_cur);
892 if (button == CURSOR_LEFT && ui->peg_cur > 0) 924 } else if (button == 'h' || button == 'H' || button == '?') {
893 ui->peg_cur--; 925 compute_hint(from, ui);
894 ret = UI_UPDATE; 926 ret = MOVE_UI_UPDATE;
895 } else if (IS_CURSOR_SELECT(button)) { 927 } else if (button == CURSOR_SELECT) {
896 ui->display_cur = true; 928 ui->display_cur = true;
897 if (ui->peg_cur == from->params.npegs) { 929 if (ui->peg_cur == from->params.npegs) {
898 ret = encode_move(from, ui); 930 ret = encode_move(from, ui);
899 } else { 931 } else {
900 set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1); 932 set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1);
901 ret = UI_UPDATE; 933 ret = MOVE_UI_UPDATE;
902 } 934 }
903 } else if (button == 'D' || button == 'd' || button == '\b') { 935 } else if (((button >= '1' && button <= '0' + from->params.ncolours) ||
936 (button == '0' && from->params.ncolours == 10)) &&
937 ui->peg_cur < from->params.npegs) {
904 ui->display_cur = true; 938 ui->display_cur = true;
905 set_peg(&from->params, ui, ui->peg_cur, 0); 939 /* Number keys insert a peg and advance the cursor. */
906 ret = UI_UPDATE; 940 set_peg(&from->params, ui, ui->peg_cur,
941 button == '0' ? 10 : button - '0');
942 if (ui->peg_cur + 1 < from->params.npegs + ui->markable)
943 ui->peg_cur++;
944 ret = MOVE_UI_UPDATE;
945 } else if (button == 'D' || button == 'd' || button == '\b') {
946 if (!ui->display_cur || ui->curr_pegs->pegs[ui->peg_cur] != 0) {
947 ui->display_cur = true;
948 set_peg(&from->params, ui, ui->peg_cur, 0);
949 ret = MOVE_UI_UPDATE;
950 } else
951 ret = MOVE_NO_EFFECT;
907 } else if (button == CURSOR_SELECT2) { 952 } else if (button == CURSOR_SELECT2) {
908 if (ui->peg_cur == from->params.npegs) 953 if (ui->peg_cur == from->params.npegs)
909 return NULL; 954 return NULL;
910 ui->display_cur = true; 955 ui->display_cur = true;
911 ui->holds[ui->peg_cur] ^= 1; 956 ui->holds[ui->peg_cur] ^= 1;
912 ret = UI_UPDATE; 957 ret = MOVE_UI_UPDATE;
913 } 958 }
914 return ret; 959 return ret;
915} 960}
@@ -925,6 +970,8 @@ static game_state *execute_move(const game_state *from, const char *move)
925 ret->solved = -1; 970 ret->solved = -1;
926 return ret; 971 return ret;
927 } else if (move[0] == 'G') { 972 } else if (move[0] == 'G') {
973 /* No guesses are allowed once the game is solved. */
974 if (from->solved) return NULL;
928 p = move+1; 975 p = move+1;
929 976
930 ret = dup_game(from); 977 ret = dup_game(from);
@@ -975,7 +1022,7 @@ static game_state *execute_move(const game_state *from, const char *move)
975#define BORDER 0.5 1022#define BORDER 0.5
976 1023
977static void game_compute_size(const game_params *params, int tilesize, 1024static void game_compute_size(const game_params *params, int tilesize,
978 int *x, int *y) 1025 const game_ui *ui, int *x, int *y)
979{ 1026{
980 double hmul, vmul_c, vmul_g, vmul; 1027 double hmul, vmul_c, vmul_g, vmul;
981 int hintw = (params->npegs+1)/2; 1028 int hintw = (params->npegs+1)/2;
@@ -1019,7 +1066,8 @@ static void game_set_size(drawing *dr, game_drawstate *ds,
1019 guessh = ((ds->pegsz + ds->gapsz) * params->nguesses); /* guesses */ 1066 guessh = ((ds->pegsz + ds->gapsz) * params->nguesses); /* guesses */
1020 guessh += ds->gapsz + ds->pegsz; /* solution */ 1067 guessh += ds->gapsz + ds->pegsz; /* solution */
1021 1068
1022 game_compute_size(params, tilesize, &ds->w, &ds->h); 1069 /* We know we don't need anything from the game_ui we haven't got */
1070 game_compute_size(params, tilesize, NULL, &ds->w, &ds->h);
1023 ds->colx = ds->border; 1071 ds->colx = ds->border;
1024 ds->coly = (ds->h - colh) / 2; 1072 ds->coly = (ds->h - colh) / 2;
1025 1073
@@ -1196,7 +1244,7 @@ static void draw_peg(drawing *dr, game_drawstate *ds, int cx, int cy,
1196 1244
1197 if (labelled && col) { 1245 if (labelled && col) {
1198 char buf[2]; 1246 char buf[2];
1199 buf[0] = 'a'-1 + col; 1247 buf[0] = '0' + (col % 10);
1200 buf[1] = '\0'; 1248 buf[1] = '\0';
1201 draw_text(dr, cx+PEGRAD, cy+PEGRAD, FONT_VARIABLE, PEGRAD, 1249 draw_text(dr, cx+PEGRAD, cy+PEGRAD, FONT_VARIABLE, PEGRAD,
1202 ALIGN_HCENTRE|ALIGN_VCENTRE, COL_FRAME, buf); 1250 ALIGN_HCENTRE|ALIGN_VCENTRE, COL_FRAME, buf);
@@ -1233,23 +1281,25 @@ static void guess_redraw(drawing *dr, game_drawstate *ds, int guess,
1233 for (i = 0; i < dest->npegs; i++) { 1281 for (i = 0; i < dest->npegs; i++) {
1234 scol = src ? src->pegs[i] : 0; 1282 scol = src ? src->pegs[i] : 0;
1235 if (i == cur_col) 1283 if (i == cur_col)
1236 scol |= 0x1000; 1284 scol |= PEG_CURSOR;
1237 if (holds && holds[i]) 1285 if (holds && holds[i])
1238 scol |= 0x2000; 1286 scol |= PEG_HOLD;
1239 if (labelled) 1287 if (labelled)
1240 scol |= 0x4000; 1288 scol |= PEG_LABELLED;
1241 if ((dest->pegs[i] != scol) || force) { 1289 if ((dest->pegs[i] != scol) || force) {
1242 draw_peg(dr, ds, rowx + PEGOFF * i, rowy, false, labelled, 1290 draw_peg(dr, ds, rowx + PEGOFF * i, rowy, false, labelled,
1243 scol &~ 0x7000); 1291 scol &~ PEG_FLAGS);
1292 if (scol & PEG_CURSOR)
1293 draw_cursor(dr, ds, rowx + PEGOFF * i, rowy);
1244 /* 1294 /*
1245 * Hold marker. 1295 * Hold marker.
1246 */ 1296 */
1247 draw_rect(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, 1297 if (scol & PEG_HOLD) {
1248 PEGSZ, 2, (scol & 0x2000 ? COL_HOLD : COL_BACKGROUND)); 1298 draw_rect(dr, rowx + PEGOFF * i,
1249 draw_update(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, 1299 rowy + PEGSZ + ds->gapsz/2 - 2, PEGSZ, 2, COL_HOLD);
1250 PEGSZ, 2); 1300 }
1251 if (scol & 0x1000) 1301 draw_update(dr, rowx + PEGOFF * i,
1252 draw_cursor(dr, ds, rowx + PEGOFF * i, rowy); 1302 rowy + PEGSZ + ds->gapsz/2 - 2, PEGSZ, 2);
1253 } 1303 }
1254 dest->pegs[i] = scol; 1304 dest->pegs[i] = scol;
1255 } 1305 }
@@ -1276,9 +1326,9 @@ static void hint_redraw(drawing *dr, game_drawstate *ds, int guess,
1276 for (i = 0; i < dest->npegs; i++) { 1326 for (i = 0; i < dest->npegs; i++) {
1277 scol = src ? src->feedback[i] : 0; 1327 scol = src ? src->feedback[i] : 0;
1278 if (i == 0 && cursor) 1328 if (i == 0 && cursor)
1279 scol |= 0x1000; 1329 scol |= PEG_CURSOR;
1280 if (i == 0 && markable) 1330 if (i == 0 && markable)
1281 scol |= 0x2000; 1331 scol |= PEG_HOLD;
1282 if ((scol != dest->feedback[i]) || force) { 1332 if ((scol != dest->feedback[i]) || force) {
1283 need_redraw = true; 1333 need_redraw = true;
1284 } 1334 }
@@ -1349,7 +1399,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1349 new_move = (state->next_go != ds->next_go) || !ds->started; 1399 new_move = (state->next_go != ds->next_go) || !ds->started;
1350 1400
1351 if (!ds->started) { 1401 if (!ds->started) {
1352 draw_rect(dr, 0, 0, ds->w, ds->h, COL_BACKGROUND);
1353 draw_rect(dr, SOLN_OX, SOLN_OY - ds->gapsz - 1, SOLN_W, 2, COL_FRAME); 1402 draw_rect(dr, SOLN_OX, SOLN_OY - ds->gapsz - 1, SOLN_W, 2, COL_FRAME);
1354 draw_update(dr, 0, 0, ds->w, ds->h); 1403 draw_update(dr, 0, 0, ds->w, ds->h);
1355 } 1404 }
@@ -1364,19 +1413,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1364 for (i = 0; i < state->params.ncolours; i++) { 1413 for (i = 0; i < state->params.ncolours; i++) {
1365 int val = i+1; 1414 int val = i+1;
1366 if (ui->display_cur && ui->colour_cur == i) 1415 if (ui->display_cur && ui->colour_cur == i)
1367 val |= 0x1000; 1416 val |= PEG_CURSOR;
1368 if (ui->show_labels) 1417 if (ui->show_labels)
1369 val |= 0x2000; 1418 val |= PEG_HOLD;
1370 if (ds->colours->pegs[i] != val) { 1419 if (ds->colours->pegs[i] != val) {
1371 draw_peg(dr, ds, COL_X(i), COL_Y(i), false, ui->show_labels, i+1); 1420 draw_peg(dr, ds, COL_X(i), COL_Y(i), false, ui->show_labels, i+1);
1372 if (val & 0x1000) 1421 if (val & PEG_CURSOR)
1373 draw_cursor(dr, ds, COL_X(i), COL_Y(i)); 1422 draw_cursor(dr, ds, COL_X(i), COL_Y(i));
1374 ds->colours->pegs[i] = val; 1423 ds->colours->pegs[i] = val;
1375 } 1424 }
1376 } 1425 }
1377 1426
1378 /* draw the guesses (so far) and the hints 1427 /* draw the guesses (so far) and the hints (postponing the
1379 * (in reverse order to avoid trampling holds, and postponing the
1380 * next_go'th to not overrender the top of the circular cursor) */ 1428 * next_go'th to not overrender the top of the circular cursor) */
1381 for (i = state->params.nguesses - 1; i >= 0; i--) { 1429 for (i = state->params.nguesses - 1; i >= 0; i--) {
1382 if (i < state->next_go || state->solved) { 1430 if (i < state->next_go || state->solved) {
@@ -1473,19 +1521,6 @@ static int game_status(const game_state *state)
1473 return state->solved; 1521 return state->solved;
1474} 1522}
1475 1523
1476static bool game_timing_state(const game_state *state, game_ui *ui)
1477{
1478 return true;
1479}
1480
1481static void game_print_size(const game_params *params, float *x, float *y)
1482{
1483}
1484
1485static void game_print(drawing *dr, const game_state *state, int tilesize)
1486{
1487}
1488
1489#ifdef COMBINED 1524#ifdef COMBINED
1490#define thegame guess 1525#define thegame guess
1491#endif 1526#endif
@@ -1506,13 +1541,15 @@ const struct game thegame = {
1506 dup_game, 1541 dup_game,
1507 free_game, 1542 free_game,
1508 true, solve_game, 1543 true, solve_game,
1509 false, game_can_format_as_text_now, game_text_format, 1544 false, NULL, NULL, /* can_format_as_text_now, text_format */
1545 get_prefs, set_prefs,
1510 new_ui, 1546 new_ui,
1511 free_ui, 1547 free_ui,
1512 encode_ui, 1548 encode_ui,
1513 decode_ui, 1549 decode_ui,
1514 NULL, /* game_request_keys */ 1550 NULL, /* game_request_keys */
1515 game_changed_state, 1551 game_changed_state,
1552 current_key_label,
1516 interpret_move, 1553 interpret_move,
1517 execute_move, 1554 execute_move,
1518 PEG_PREFER_SZ, game_compute_size, game_set_size, 1555 PEG_PREFER_SZ, game_compute_size, game_set_size,
@@ -1524,9 +1561,9 @@ const struct game thegame = {
1524 game_flash_length, 1561 game_flash_length,
1525 game_get_cursor_location, 1562 game_get_cursor_location,
1526 game_status, 1563 game_status,
1527 false, false, game_print_size, game_print, 1564 false, false, NULL, NULL, /* print_size, print */
1528 false, /* wants_statusbar */ 1565 false, /* wants_statusbar */
1529 false, game_timing_state, 1566 false, NULL, /* timing_state */
1530 0, /* flags */ 1567 0, /* flags */
1531}; 1568};
1532 1569
diff --git a/apps/plugins/puzzles/src/hat-internal.h b/apps/plugins/puzzles/src/hat-internal.h
new file mode 100644
index 0000000000..2be96327a8
--- /dev/null
+++ b/apps/plugins/puzzles/src/hat-internal.h
@@ -0,0 +1,271 @@
1/*
2 * Internal definitions for the hat.c tiling generator, shared between
3 * hat.c itself and hat-test.c.
4 */
5
6#include "puzzles.h"
7
8/*
9 * Coordinate system:
10 *
11 * The output of this code lives on the tiling known to grid.c as
12 * 'Kites', which can be viewed as a tiling of hexagons each of which
13 * is subdivided into six kites sharing their pointy vertex, or
14 * (equivalently) a tiling of equilateral triangles each subdivided
15 * into three kits sharing their blunt vertex.
16 *
17 * We express coordinates in this system relative to the basis (1, r)
18 * where r = (1 + sqrt(3)i) / 2 is a primitive 6th root of unity. This
19 * gives us a system in which two integer coordinates can address any
20 * grid point, provided we scale up so that the side length of the
21 * equilateral triangles in the tiling is 6.
22 */
23
24typedef struct Point {
25 int x, y; /* represents x + yr */
26} Point;
27
28static inline Point pointscale(int scale, Point a)
29{
30 Point r = { scale * a.x, scale * a.y };
31 return r;
32}
33
34static inline Point pointadd(Point a, Point b)
35{
36 Point r = { a.x + b.x, a.y + b.y };
37 return r;
38}
39
40/*
41 * We identify a single kite by the coordinates of its four vertices.
42 * This allows us to construct the coordinates of an adjacent kite by
43 * taking affine transformations of the original kite's vertices.
44 *
45 * This is a useful way to do it because it means that if you reflect
46 * the kite (by swapping its left and right vertices) then these
47 * transformations also perform in a reflected way. This will be
48 * useful in the code below that outputs the coordinates of each hat,
49 * because this way it can work by walking around its 8 kites using a
50 * fixed set of steps, and if the hat is reflected, then we just
51 * reflect the starting kite before doing that, and everything still
52 * works.
53 */
54
55typedef struct Kite {
56 Point centre, left, right, outer;
57} Kite;
58
59static inline Kite kite_left(Kite k)
60{
61 Kite r;
62 r.centre = k.centre;
63 r.right = k.left;
64 r.outer = pointadd(pointscale(2, k.left), pointscale(-1, k.outer));
65 r.left = pointadd(pointadd(k.centre, k.left), pointscale(-1, k.right));
66 return r;
67}
68
69static inline Kite kite_right(Kite k)
70{
71 Kite r;
72 r.centre = k.centre;
73 r.left = k.right;
74 r.outer = pointadd(pointscale(2, k.right), pointscale(-1, k.outer));
75 r.right = pointadd(pointadd(k.centre, k.right), pointscale(-1, k.left));
76 return r;
77}
78
79static inline Kite kite_forward_left(Kite k)
80{
81 Kite r;
82 r.outer = k.outer;
83 r.right = k.left;
84 r.centre = pointadd(pointscale(2, k.left), pointscale(-1, k.centre));
85 r.left = pointadd(pointadd(k.right, k.left), pointscale(-1, k.centre));
86 return r;
87}
88
89static inline Kite kite_forward_right(Kite k)
90{
91 Kite r;
92 r.outer = k.outer;
93 r.left = k.right;
94 r.centre = pointadd(pointscale(2, k.right), pointscale(-1, k.centre));
95 r.right = pointadd(pointadd(k.left, k.right), pointscale(-1, k.centre));
96 return r;
97}
98
99typedef enum KiteStep { KS_LEFT, KS_RIGHT, KS_F_LEFT, KS_F_RIGHT } KiteStep;
100
101static inline Kite kite_step(Kite k, KiteStep step)
102{
103 switch (step) {
104 case KS_LEFT: return kite_left(k);
105 case KS_RIGHT: return kite_right(k);
106 case KS_F_LEFT: return kite_forward_left(k);
107 default /* case KS_F_RIGHT */: return kite_forward_right(k);
108 }
109}
110
111/*
112 * Functiond to enumerate the kites in a rectangular region, in a
113 * serpentine-raster fashion so that every kite delivered shares an
114 * edge with a recent previous one.
115 */
116#define KE_NKEEP 3
117typedef struct KiteEnum {
118 /* Fields private to the enumerator */
119 int state;
120 int x, y, w, h;
121 unsigned curr_index;
122
123 /* Fields the client can legitimately read out */
124 Kite *curr;
125 Kite recent[KE_NKEEP];
126 unsigned last_index;
127 KiteStep last_step; /* step that got curr from recent[last_index] */
128} KiteEnum;
129void hat_kiteenum_first(KiteEnum *s, int w, int h);
130bool hat_kiteenum_next(KiteEnum *s);
131
132/*
133 * Assorted useful definitions.
134 */
135typedef enum TileType { TT_H, TT_T, TT_P, TT_F, TT_KITE, TT_HAT } TileType;
136static const char tilechars[] = "HTPF";
137
138#define HAT_KITES 8 /* number of kites in a hat */
139#define MT_MAXEXPAND 13 /* largest number of metatiles in any expansion */
140
141/*
142 * Definitions for the autogenerated hat-tables.h header file that
143 * defines all the lookup tables.
144 */
145typedef struct KitemapEntry {
146 int kite, hat, meta; /* all -1 if impossible */
147} KitemapEntry;
148
149typedef struct MetamapEntry {
150 int meta, meta2;
151} MetamapEntry;
152
153static inline size_t kitemap_index(KiteStep step, unsigned kite,
154 unsigned hat, unsigned meta)
155{
156 return step + 4 * (kite + 8 * (hat + 4 * meta));
157}
158
159static inline size_t metamap_index(unsigned meta, unsigned meta2)
160{
161 return meta2 * MT_MAXEXPAND + meta;
162}
163
164/*
165 * Coordinate system for tracking kites within a randomly selected
166 * part of the recursively expanded hat tiling.
167 *
168 * HatCoords will store an array of HatCoord, in little-endian
169 * arrangement. So hc->c[0] will always have type TT_KITE and index a
170 * single kite within a hat; hc->c[1] will have type TT_HAT and index
171 * a hat within a first-order metatile; hc->c[2] will be the smallest
172 * metatile containing this hat, and hc->c[3, 4, 5, ...] will be
173 * higher-order metatiles as needed.
174 *
175 * The last coordinate stored, hc->c[hc->nc-1], will have a tile type
176 * but no index (represented by index==-1). This means "we haven't
177 * decided yet what this level of metatile needs to be". If we need to
178 * refer to this level during the hatctx_step algorithm, we make it up
179 * at random, based on a table of what metatiles each type can
180 * possibly be part of, at what index.
181 */
182typedef struct HatCoord {
183 int index; /* index within that tile, or -1 if not yet known */
184 TileType type; /* type of this tile */
185} HatCoord;
186
187typedef struct HatCoords {
188 HatCoord *c;
189 size_t nc, csize;
190} HatCoords;
191
192HatCoords *hat_coords_new(void);
193void hat_coords_free(HatCoords *hc);
194void hat_coords_make_space(HatCoords *hc, size_t size);
195HatCoords *hat_coords_copy(HatCoords *hc_in);
196
197#ifdef HAT_COORDS_DEBUG
198static inline void hat_coords_debug(const char *prefix, HatCoords *hc,
199 const char *suffix)
200{
201 const char *sep = "";
202 static const char *const types[] = {"H","T","P","F","kite","hat"};
203
204 fputs(prefix, stderr);
205 for (size_t i = 0; i < hc->nc; i++) {
206 fprintf(stderr, "%s %s ", sep, types[hc->c[i].type]);
207 sep = " .";
208 if (hc->c[i].index == -1)
209 fputs("?", stderr);
210 else
211 fprintf(stderr, "%d", hc->c[i].index);
212 }
213 fputs(suffix, stderr);
214}
215#else
216#define hat_coords_debug(p,c,s) ((void)0)
217#endif
218
219/*
220 * HatContext is the shared context of a whole run of the algorithm.
221 * Its 'prototype' HatCoords object represents the coordinates of the
222 * starting kite, and is extended as necessary; any other HatCoord
223 * that needs extending will copy the higher-order values from
224 * ctx->prototype as needed, so that once each choice has been made,
225 * it remains consistent.
226 *
227 * When we're inventing a random piece of tiling in the first place,
228 * we append to ctx->prototype by choosing a random (but legal)
229 * higher-level metatile for the current topmost one to turn out to be
230 * part of. When we're replaying a generation whose parameters are
231 * already stored, we don't have a random_state, and we make fixed
232 * decisions if not enough coordinates were provided.
233 *
234 * (Of course another approach would be to reject grid descriptions
235 * that didn't define enough coordinates! But that would involve a
236 * whole extra iteration over the whole grid region just for
237 * validation, and that seems like more timewasting than really
238 * needed. So we tolerate short descriptions, and do something
239 * deterministic with them.)
240 */
241
242typedef struct HatContext {
243 random_state *rs;
244 HatCoords *prototype;
245} HatContext;
246
247void hatctx_init_random(HatContext *ctx, random_state *rs);
248void hatctx_cleanup(HatContext *ctx);
249HatCoords *hatctx_initial_coords(HatContext *ctx);
250void hatctx_extend_coords(HatContext *ctx, HatCoords *hc, size_t n);
251HatCoords *hatctx_step(HatContext *ctx, HatCoords *hc_in, KiteStep step);
252
253/*
254 * Subroutine of hat_tiling_generate, called for each kite in the grid
255 * as we iterate over it, to decide whether to generate an output hat
256 * and pass it to the client. Exposed in this header file so that
257 * hat-test can reuse it.
258 *
259 * We do this by starting from kite #0 of each hat, and tracing round
260 * the boundary. If the whole boundary is within the caller's bounding
261 * region, we return it; if it goes off the edge, we don't.
262 *
263 * (Of course, every hat we _do_ want to return will have all its
264 * kites inside the rectangle, so its kite #0 will certainly be caught
265 * by this iteration.)
266 */
267
268typedef void (*internal_hat_callback_fn)(void *ctx, Kite kite0, HatCoords *hc,
269 int *coords);
270void maybe_report_hat(int w, int h, Kite kite, HatCoords *hc,
271 internal_hat_callback_fn cb, void *cbctx);
diff --git a/apps/plugins/puzzles/src/hat-tables.h b/apps/plugins/puzzles/src/hat-tables.h
new file mode 100644
index 0000000000..b55fbb405b
--- /dev/null
+++ b/apps/plugins/puzzles/src/hat-tables.h
@@ -0,0 +1,2120 @@
1/*
2 * Header file autogenerated by auxiliary/hatgen.c
3 *
4 * To regenerate, run 'hatgen --tables > hat-tables.h'
5 */
6
7static const unsigned hats_in_metatile[] = { 4, 1, 2, 2, };
8
9static const TileType children_H[] = {
10 TT_H, TT_H, TT_H, TT_T, TT_P, TT_P, TT_P, TT_F, TT_F, TT_F, TT_F, TT_F, TT_F,
11};
12static const TileType children_T[] = {
13 TT_H, TT_P, TT_P, TT_P, TT_F, TT_F, TT_F,
14};
15static const TileType children_P[] = {
16 TT_H, TT_H, TT_P, TT_P, TT_P, TT_F, TT_F, TT_F, TT_F, TT_F, TT_F,
17};
18static const TileType children_F[] = {
19 TT_H, TT_H, TT_P, TT_P, TT_F, TT_F, TT_F, TT_F, TT_F, TT_F, TT_F,
20};
21static const TileType *const children[] = {
22 children_H,
23 children_T,
24 children_P,
25 children_F,
26};
27static const size_t nchildren[] = {
28 13,
29 7,
30 11,
31 11,
32};
33
34static const KitemapEntry kitemap_H[] = {
35 /* hat #0 in metatile #0 (type H) */
36 {1,0,0}, {7,3,0}, {3,0,4}, {4,0,4},
37 {4,3,0}, {0,0,0}, {5,0,0}, {2,0,0},
38 {3,0,4}, {3,0,0}, {1,0,0}, {5,0,0},
39 {2,0,0}, {1,2,1}, {4,0,0}, {6,2,1},
40 {3,0,3}, {5,0,0}, {6,2,1}, {3,0,0},
41 {4,0,0}, {6,0,0}, {2,0,0}, {1,0,0},
42 {5,0,0}, {7,0,0}, {4,3,0}, {3,3,0},
43 {6,0,0}, {2,0,3}, {7,1,0}, {0,0,3},
44 /* hat #1 in metatile #0 (type H) */
45 {1,1,0}, {2,0,5}, {7,1,8}, {0,0,5},
46 {3,0,2}, {0,1,0}, {5,1,0}, {2,1,0},
47 {7,1,8}, {3,1,0}, {1,1,0}, {5,1,0},
48 {2,1,0}, {4,1,8}, {4,1,0}, {0,3,0},
49 {2,3,0}, {5,1,0}, {0,3,0}, {3,1,0},
50 {4,1,0}, {6,1,0}, {2,1,0}, {1,1,0},
51 {5,1,0}, {7,1,0}, {3,0,2}, {4,0,2},
52 {6,1,0}, {3,3,0}, {0,0,3}, {7,0,0},
53 /* hat #2 in metatile #0 (type H) */
54 {1,2,0}, {1,0,7}, {7,1,4}, {6,0,7},
55 {3,0,8}, {0,2,0}, {5,2,0}, {2,2,0},
56 {7,1,4}, {3,2,0}, {1,2,0}, {5,2,0},
57 {2,2,0}, {4,1,4}, {4,2,0}, {6,3,0},
58 {1,3,0}, {5,2,0}, {6,3,0}, {3,2,0},
59 {4,2,0}, {6,2,0}, {2,2,0}, {1,2,0},
60 {5,2,0}, {7,2,0}, {3,0,8}, {4,0,8},
61 {6,2,0}, {0,3,0}, {3,1,8}, {4,1,8},
62 /* hat #3 in metatile #0 (type H) */
63 {7,2,0}, {1,3,0}, {3,1,0}, {4,1,0},
64 {0,3,0}, {4,2,0}, {2,3,0}, {5,3,0},
65 {3,3,0}, {4,1,0}, {5,3,0}, {1,3,0},
66 {7,1,0}, {2,3,0}, {6,0,0}, {4,3,0},
67 {5,3,0}, {1,0,0}, {3,3,0}, {6,0,0},
68 {6,3,0}, {4,3,0}, {1,3,0}, {2,3,0},
69 {7,3,0}, {5,3,0}, {3,2,0}, {4,2,0},
70 {0,0,0}, {6,3,0}, {3,1,4}, {4,1,4},
71 /* hat #0 in metatile #1 (type H) */
72 {1,0,1}, {7,3,1}, {3,0,9}, {4,0,9},
73 {4,3,1}, {0,0,1}, {5,0,1}, {2,0,1},
74 {3,0,9}, {3,0,1}, {1,0,1}, {5,0,1},
75 {2,0,1}, {1,0,10}, {4,0,1}, {6,0,10},
76 {0,0,6}, {5,0,1}, {6,0,10}, {3,0,1},
77 {4,0,1}, {6,0,1}, {2,0,1}, {1,0,1},
78 {5,0,1}, {7,0,1}, {4,3,1}, {3,3,1},
79 {6,0,1}, {1,0,6}, {7,1,1}, {6,0,6},
80 /* hat #1 in metatile #1 (type H) */
81 {1,1,1}, {1,1,2}, {7,0,3}, {6,1,2},
82 {0,1,6}, {0,1,1}, {5,1,1}, {2,1,1},
83 {7,0,3}, {3,1,1}, {1,1,1}, {5,1,1},
84 {2,1,1}, {4,0,3}, {4,1,1}, {0,3,1},
85 {2,3,1}, {5,1,1}, {0,3,1}, {3,1,1},
86 {4,1,1}, {6,1,1}, {2,1,1}, {1,1,1},
87 {5,1,1}, {7,1,1}, {0,1,6}, {7,0,6},
88 {6,1,1}, {3,3,1}, {6,0,6}, {7,0,1},
89 /* hat #2 in metatile #1 (type H) */
90 {1,2,1}, {2,0,4}, {7,1,9}, {0,0,4},
91 {3,0,0}, {0,2,1}, {5,2,1}, {2,2,1},
92 {7,1,9}, {3,2,1}, {1,2,1}, {5,2,1},
93 {2,2,1}, {4,1,9}, {4,2,1}, {6,3,1},
94 {1,3,1}, {5,2,1}, {6,3,1}, {3,2,1},
95 {4,2,1}, {6,2,1}, {2,2,1}, {1,2,1},
96 {5,2,1}, {7,2,1}, {3,0,0}, {4,0,0},
97 {6,2,1}, {0,3,1}, {3,0,3}, {4,0,3},
98 /* hat #3 in metatile #1 (type H) */
99 {7,2,1}, {1,3,1}, {3,1,1}, {4,1,1},
100 {0,3,1}, {4,2,1}, {2,3,1}, {5,3,1},
101 {3,3,1}, {4,1,1}, {5,3,1}, {1,3,1},
102 {7,1,1}, {2,3,1}, {6,0,1}, {4,3,1},
103 {5,3,1}, {1,0,1}, {3,3,1}, {6,0,1},
104 {6,3,1}, {4,3,1}, {1,3,1}, {2,3,1},
105 {7,3,1}, {5,3,1}, {3,2,1}, {4,2,1},
106 {0,0,1}, {6,3,1}, {3,1,9}, {4,1,9},
107 /* hat #0 in metatile #2 (type H) */
108 {1,0,2}, {7,3,2}, {3,0,5}, {4,0,5},
109 {4,3,2}, {0,0,2}, {5,0,2}, {2,0,2},
110 {3,0,5}, {3,0,2}, {1,0,2}, {5,0,2},
111 {2,0,2}, {1,1,0}, {4,0,2}, {6,1,0},
112 {0,0,3}, {5,0,2}, {6,1,0}, {3,0,2},
113 {4,0,2}, {6,0,2}, {2,0,2}, {1,0,2},
114 {5,0,2}, {7,0,2}, {4,3,2}, {3,3,2},
115 {6,0,2}, {1,0,3}, {7,1,2}, {6,0,3},
116 /* hat #1 in metatile #2 (type H) */
117 {1,1,2}, {1,1,6}, {7,1,12}, {6,1,6},
118 {0,1,1}, {0,1,2}, {5,1,2}, {2,1,2},
119 {7,1,12}, {3,1,2}, {1,1,2}, {5,1,2},
120 {2,1,2}, {4,1,12}, {4,1,2}, {0,3,2},
121 {2,3,2}, {5,1,2}, {0,3,2}, {3,1,2},
122 {4,1,2}, {6,1,2}, {2,1,2}, {1,1,2},
123 {5,1,2}, {7,1,2}, {0,1,1}, {7,0,3},
124 {6,1,2}, {3,3,2}, {6,0,3}, {7,0,2},
125 /* hat #2 in metatile #2 (type H) */
126 {1,2,2}, {1,0,11}, {7,1,5}, {6,0,11},
127 {3,0,12}, {0,2,2}, {5,2,2}, {2,2,2},
128 {7,1,5}, {3,2,2}, {1,2,2}, {5,2,2},
129 {2,2,2}, {4,1,5}, {4,2,2}, {6,3,2},
130 {1,3,2}, {5,2,2}, {6,3,2}, {3,2,2},
131 {4,2,2}, {6,2,2}, {2,2,2}, {1,2,2},
132 {5,2,2}, {7,2,2}, {3,0,12}, {4,0,12},
133 {6,2,2}, {0,3,2}, {3,1,12}, {4,1,12},
134 /* hat #3 in metatile #2 (type H) */
135 {7,2,2}, {1,3,2}, {3,1,2}, {4,1,2},
136 {0,3,2}, {4,2,2}, {2,3,2}, {5,3,2},
137 {3,3,2}, {4,1,2}, {5,3,2}, {1,3,2},
138 {7,1,2}, {2,3,2}, {6,0,2}, {4,3,2},
139 {5,3,2}, {1,0,2}, {3,3,2}, {6,0,2},
140 {6,3,2}, {4,3,2}, {1,3,2}, {2,3,2},
141 {7,3,2}, {5,3,2}, {3,2,2}, {4,2,2},
142 {0,0,2}, {6,3,2}, {3,1,5}, {4,1,5},
143 /* hat #0 in metatile #3 (type T) */
144 {1,0,3}, {4,0,2}, {7,0,0}, {7,1,0},
145 {7,0,2}, {0,0,3}, {5,0,3}, {2,0,3},
146 {7,0,0}, {3,0,3}, {1,0,3}, {5,0,3},
147 {2,0,3}, {4,0,0}, {4,0,3}, {7,2,1},
148 {3,1,1}, {5,0,3}, {7,2,1}, {3,0,3},
149 {4,0,3}, {6,0,3}, {2,0,3}, {1,0,3},
150 {5,0,3}, {7,0,3}, {7,0,2}, {7,1,2},
151 {6,0,3}, {2,1,1}, {6,1,2}, {0,1,1},
152 /* hat #1 in metatile #3 (type T) does not exist */
153 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
154 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
155 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
156 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
157 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
158 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
159 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
160 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
161 /* hat #2 in metatile #3 (type T) does not exist */
162 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
163 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
164 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
165 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
166 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
167 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
168 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
169 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
170 /* hat #3 in metatile #3 (type T) does not exist */
171 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
172 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
173 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
174 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
175 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
176 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
177 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
178 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
179 /* hat #0 in metatile #4 (type P) */
180 {1,0,4}, {-1,-1,-1}, {0,2,1}, {7,1,9},
181 {-1,-1,-1}, {0,0,4}, {5,0,4}, {2,0,4},
182 {0,2,1}, {3,0,4}, {1,0,4}, {5,0,4},
183 {2,0,4}, {2,0,0}, {4,0,4}, {0,0,0},
184 {3,1,4}, {5,0,4}, {0,0,0}, {3,0,4},
185 {4,0,4}, {6,0,4}, {2,0,4}, {1,0,4},
186 {5,0,4}, {7,0,4}, {-1,-1,-1}, {-1,-1,-1},
187 {6,0,4}, {2,1,4}, {-1,-1,-1}, {0,1,4},
188 /* hat #1 in metatile #4 (type P) */
189 {1,1,4}, {-1,-1,-1}, {7,0,4}, {-1,-1,-1},
190 {0,1,7}, {0,1,4}, {5,1,4}, {2,1,4},
191 {7,0,4}, {3,1,4}, {1,1,4}, {5,1,4},
192 {2,1,4}, {4,0,4}, {4,1,4}, {7,3,0},
193 {3,2,0}, {5,1,4}, {7,3,0}, {3,1,4},
194 {4,1,4}, {6,1,4}, {2,1,4}, {1,1,4},
195 {5,1,4}, {7,1,4}, {0,1,7}, {7,0,7},
196 {6,1,4}, {2,2,0}, {6,0,7}, {0,2,0},
197 /* hat #2 in metatile #4 (type P) does not exist */
198 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
199 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
200 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
201 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
202 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
203 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
204 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
205 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
206 /* hat #3 in metatile #4 (type P) does not exist */
207 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
208 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
209 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
210 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
211 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
212 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
213 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
214 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
215 /* hat #0 in metatile #5 (type P) */
216 {1,0,5}, {-1,-1,-1}, {0,1,0}, {7,1,8},
217 {-1,-1,-1}, {0,0,5}, {5,0,5}, {2,0,5},
218 {0,1,0}, {3,0,5}, {1,0,5}, {5,0,5},
219 {2,0,5}, {2,0,2}, {4,0,5}, {0,0,2},
220 {3,1,5}, {5,0,5}, {0,0,2}, {3,0,5},
221 {4,0,5}, {6,0,5}, {2,0,5}, {1,0,5},
222 {5,0,5}, {7,0,5}, {-1,-1,-1}, {-1,-1,-1},
223 {6,0,5}, {2,1,5}, {-1,-1,-1}, {0,1,5},
224 /* hat #1 in metatile #5 (type P) */
225 {1,1,5}, {-1,-1,-1}, {7,0,5}, {-1,-1,-1},
226 {0,1,11}, {0,1,5}, {5,1,5}, {2,1,5},
227 {7,0,5}, {3,1,5}, {1,1,5}, {5,1,5},
228 {2,1,5}, {4,0,5}, {4,1,5}, {7,3,2},
229 {3,2,2}, {5,1,5}, {7,3,2}, {3,1,5},
230 {4,1,5}, {6,1,5}, {2,1,5}, {1,1,5},
231 {5,1,5}, {7,1,5}, {0,1,11}, {7,0,11},
232 {6,1,5}, {2,2,2}, {6,0,11}, {0,2,2},
233 /* hat #2 in metatile #5 (type P) does not exist */
234 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
235 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
236 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
237 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
238 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
239 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
240 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
241 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
242 /* hat #3 in metatile #5 (type P) does not exist */
243 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
244 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
245 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
246 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
247 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
248 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
249 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
250 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
251 /* hat #0 in metatile #6 (type P) */
252 {1,0,6}, {4,0,1}, {0,1,10}, {7,0,10},
253 {7,0,1}, {0,0,6}, {5,0,6}, {2,0,6},
254 {0,1,10}, {3,0,6}, {1,0,6}, {5,0,6},
255 {2,0,6}, {-1,-1,-1}, {4,0,6}, {-1,-1,-1},
256 {3,1,6}, {5,0,6}, {-1,-1,-1}, {3,0,6},
257 {4,0,6}, {6,0,6}, {2,0,6}, {1,0,6},
258 {5,0,6}, {7,0,6}, {7,0,1}, {7,1,1},
259 {6,0,6}, {2,1,6}, {6,1,1}, {0,1,6},
260 /* hat #1 in metatile #6 (type P) */
261 {1,1,6}, {1,1,1}, {7,0,6}, {6,1,1},
262 {0,1,2}, {0,1,6}, {5,1,6}, {2,1,6},
263 {7,0,6}, {3,1,6}, {1,1,6}, {5,1,6},
264 {2,1,6}, {4,0,6}, {4,1,6}, {-1,-1,-1},
265 {-1,-1,-1}, {5,1,6}, {-1,-1,-1}, {3,1,6},
266 {4,1,6}, {6,1,6}, {2,1,6}, {1,1,6},
267 {5,1,6}, {7,1,6}, {0,1,2}, {7,1,12},
268 {6,1,6}, {-1,-1,-1}, {6,1,12}, {-1,-1,-1},
269 /* hat #2 in metatile #6 (type P) does not exist */
270 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
271 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
272 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
273 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
274 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
275 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
276 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
277 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
278 /* hat #3 in metatile #6 (type P) does not exist */
279 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
280 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
281 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
282 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
283 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
284 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
285 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
286 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
287 /* hat #0 in metatile #7 (type F) */
288 {1,0,7}, {2,0,8}, {-1,-1,-1}, {0,0,8},
289 {0,2,0}, {0,0,7}, {5,0,7}, {2,0,7},
290 {-1,-1,-1}, {3,0,7}, {1,0,7}, {5,0,7},
291 {2,0,7}, {-1,-1,-1}, {4,0,7}, {-1,-1,-1},
292 {3,1,7}, {5,0,7}, {-1,-1,-1}, {3,0,7},
293 {4,0,7}, {6,0,7}, {2,0,7}, {1,0,7},
294 {5,0,7}, {7,0,7}, {0,2,0}, {7,1,4},
295 {6,0,7}, {2,1,7}, {6,1,4}, {0,1,7},
296 /* hat #1 in metatile #7 (type F) */
297 {1,1,7}, {1,1,4}, {7,0,7}, {6,1,4},
298 {-1,-1,-1}, {0,1,7}, {5,1,7}, {2,1,7},
299 {7,0,7}, {3,1,7}, {1,1,7}, {5,1,7},
300 {2,1,7}, {4,0,7}, {4,1,7}, {-1,-1,-1},
301 {-1,-1,-1}, {5,1,7}, {-1,-1,-1}, {3,1,7},
302 {4,1,7}, {6,1,7}, {2,1,7}, {1,1,7},
303 {5,1,7}, {7,1,7}, {-1,-1,-1}, {-1,-1,-1},
304 {6,1,7}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
305 /* hat #2 in metatile #7 (type F) does not exist */
306 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
307 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
308 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
309 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
310 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
311 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
312 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
313 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
314 /* hat #3 in metatile #7 (type F) does not exist */
315 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
316 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
317 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
318 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
319 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
320 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
321 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
322 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
323 /* hat #0 in metatile #8 (type F) */
324 {1,0,8}, {-1,-1,-1}, {0,0,7}, {-1,-1,-1},
325 {-1,-1,-1}, {0,0,8}, {5,0,8}, {2,0,8},
326 {0,0,7}, {3,0,8}, {1,0,8}, {5,0,8},
327 {2,0,8}, {1,2,0}, {4,0,8}, {6,2,0},
328 {3,1,8}, {5,0,8}, {6,2,0}, {3,0,8},
329 {4,0,8}, {6,0,8}, {2,0,8}, {1,0,8},
330 {5,0,8}, {7,0,8}, {-1,-1,-1}, {-1,-1,-1},
331 {6,0,8}, {2,1,8}, {-1,-1,-1}, {0,1,8},
332 /* hat #1 in metatile #8 (type F) */
333 {1,1,8}, {-1,-1,-1}, {7,0,8}, {-1,-1,-1},
334 {-1,-1,-1}, {0,1,8}, {5,1,8}, {2,1,8},
335 {7,0,8}, {3,1,8}, {1,1,8}, {5,1,8},
336 {2,1,8}, {4,0,8}, {4,1,8}, {7,2,0},
337 {3,1,0}, {5,1,8}, {7,2,0}, {3,1,8},
338 {4,1,8}, {6,1,8}, {2,1,8}, {1,1,8},
339 {5,1,8}, {7,1,8}, {-1,-1,-1}, {-1,-1,-1},
340 {6,1,8}, {2,1,0}, {0,0,5}, {0,1,0},
341 /* hat #2 in metatile #8 (type F) does not exist */
342 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
343 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
344 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
345 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
346 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
347 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
348 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
349 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
350 /* hat #3 in metatile #8 (type F) does not exist */
351 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
352 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
353 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
354 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
355 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
356 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
357 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
358 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
359 /* hat #0 in metatile #9 (type F) */
360 {1,0,9}, {-1,-1,-1}, {0,0,10}, {-1,-1,-1},
361 {-1,-1,-1}, {0,0,9}, {5,0,9}, {2,0,9},
362 {0,0,10}, {3,0,9}, {1,0,9}, {5,0,9},
363 {2,0,9}, {2,0,1}, {4,0,9}, {0,0,1},
364 {3,1,9}, {5,0,9}, {0,0,1}, {3,0,9},
365 {4,0,9}, {6,0,9}, {2,0,9}, {1,0,9},
366 {5,0,9}, {7,0,9}, {-1,-1,-1}, {-1,-1,-1},
367 {6,0,9}, {2,1,9}, {-1,-1,-1}, {0,1,9},
368 /* hat #1 in metatile #9 (type F) */
369 {1,1,9}, {-1,-1,-1}, {7,0,9}, {-1,-1,-1},
370 {-1,-1,-1}, {0,1,9}, {5,1,9}, {2,1,9},
371 {7,0,9}, {3,1,9}, {1,1,9}, {5,1,9},
372 {2,1,9}, {4,0,9}, {4,1,9}, {7,3,1},
373 {3,2,1}, {5,1,9}, {7,3,1}, {3,1,9},
374 {4,1,9}, {6,1,9}, {2,1,9}, {1,1,9},
375 {5,1,9}, {7,1,9}, {-1,-1,-1}, {-1,-1,-1},
376 {6,1,9}, {2,2,1}, {0,0,4}, {0,2,1},
377 /* hat #2 in metatile #9 (type F) does not exist */
378 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
379 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
380 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
381 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
382 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
383 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
384 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
385 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
386 /* hat #3 in metatile #9 (type F) does not exist */
387 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
388 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
389 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
390 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
391 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
392 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
393 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
394 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
395 /* hat #0 in metatile #10 (type F) */
396 {1,0,10}, {2,0,9}, {-1,-1,-1}, {0,0,9},
397 {3,0,1}, {0,0,10}, {5,0,10}, {2,0,10},
398 {-1,-1,-1}, {3,0,10}, {1,0,10}, {5,0,10},
399 {2,0,10}, {-1,-1,-1}, {4,0,10}, {-1,-1,-1},
400 {3,1,10}, {5,0,10}, {-1,-1,-1}, {3,0,10},
401 {4,0,10}, {6,0,10}, {2,0,10}, {1,0,10},
402 {5,0,10}, {7,0,10}, {3,0,1}, {4,0,1},
403 {6,0,10}, {2,1,10}, {0,0,6}, {0,1,10},
404 /* hat #1 in metatile #10 (type F) */
405 {1,1,10}, {2,0,6}, {7,0,10}, {0,0,6},
406 {-1,-1,-1}, {0,1,10}, {5,1,10}, {2,1,10},
407 {7,0,10}, {3,1,10}, {1,1,10}, {5,1,10},
408 {2,1,10}, {4,0,10}, {4,1,10}, {-1,-1,-1},
409 {-1,-1,-1}, {5,1,10}, {-1,-1,-1}, {3,1,10},
410 {4,1,10}, {6,1,10}, {2,1,10}, {1,1,10},
411 {5,1,10}, {7,1,10}, {-1,-1,-1}, {-1,-1,-1},
412 {6,1,10}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
413 /* hat #2 in metatile #10 (type F) does not exist */
414 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
415 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
416 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
417 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
418 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
419 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
420 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
421 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
422 /* hat #3 in metatile #10 (type F) does not exist */
423 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
424 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
425 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
426 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
427 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
428 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
429 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
430 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
431 /* hat #0 in metatile #11 (type F) */
432 {1,0,11}, {2,0,12}, {-1,-1,-1}, {0,0,12},
433 {0,2,2}, {0,0,11}, {5,0,11}, {2,0,11},
434 {-1,-1,-1}, {3,0,11}, {1,0,11}, {5,0,11},
435 {2,0,11}, {-1,-1,-1}, {4,0,11}, {-1,-1,-1},
436 {3,1,11}, {5,0,11}, {-1,-1,-1}, {3,0,11},
437 {4,0,11}, {6,0,11}, {2,0,11}, {1,0,11},
438 {5,0,11}, {7,0,11}, {0,2,2}, {7,1,5},
439 {6,0,11}, {2,1,11}, {6,1,5}, {0,1,11},
440 /* hat #1 in metatile #11 (type F) */
441 {1,1,11}, {1,1,5}, {7,0,11}, {6,1,5},
442 {-1,-1,-1}, {0,1,11}, {5,1,11}, {2,1,11},
443 {7,0,11}, {3,1,11}, {1,1,11}, {5,1,11},
444 {2,1,11}, {4,0,11}, {4,1,11}, {-1,-1,-1},
445 {-1,-1,-1}, {5,1,11}, {-1,-1,-1}, {3,1,11},
446 {4,1,11}, {6,1,11}, {2,1,11}, {1,1,11},
447 {5,1,11}, {7,1,11}, {-1,-1,-1}, {-1,-1,-1},
448 {6,1,11}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
449 /* hat #2 in metatile #11 (type F) does not exist */
450 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
451 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
452 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
453 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
454 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
455 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
456 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
457 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
458 /* hat #3 in metatile #11 (type F) does not exist */
459 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
460 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
461 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
462 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
463 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
464 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
465 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
466 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
467 /* hat #0 in metatile #12 (type F) */
468 {1,0,12}, {-1,-1,-1}, {0,0,11}, {-1,-1,-1},
469 {-1,-1,-1}, {0,0,12}, {5,0,12}, {2,0,12},
470 {0,0,11}, {3,0,12}, {1,0,12}, {5,0,12},
471 {2,0,12}, {1,2,2}, {4,0,12}, {6,2,2},
472 {3,1,12}, {5,0,12}, {6,2,2}, {3,0,12},
473 {4,0,12}, {6,0,12}, {2,0,12}, {1,0,12},
474 {5,0,12}, {7,0,12}, {-1,-1,-1}, {-1,-1,-1},
475 {6,0,12}, {2,1,12}, {-1,-1,-1}, {0,1,12},
476 /* hat #1 in metatile #12 (type F) */
477 {1,1,12}, {-1,-1,-1}, {7,0,12}, {-1,-1,-1},
478 {-1,-1,-1}, {0,1,12}, {5,1,12}, {2,1,12},
479 {7,0,12}, {3,1,12}, {1,1,12}, {5,1,12},
480 {2,1,12}, {4,0,12}, {4,1,12}, {7,2,2},
481 {3,1,2}, {5,1,12}, {7,2,2}, {3,1,12},
482 {4,1,12}, {6,1,12}, {2,1,12}, {1,1,12},
483 {5,1,12}, {7,1,12}, {-1,-1,-1}, {7,1,6},
484 {6,1,12}, {2,1,2}, {6,1,6}, {0,1,2},
485 /* hat #2 in metatile #12 (type F) does not exist */
486 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
487 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
488 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
489 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
490 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
491 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
492 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
493 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
494 /* hat #3 in metatile #12 (type F) does not exist */
495 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
496 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
497 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
498 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
499 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
500 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
501 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
502 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
503};
504static const KitemapEntry kitemap_T[] = {
505 /* hat #0 in metatile #0 (type H) */
506 {1,0,0}, {7,3,0}, {3,0,3}, {4,0,3},
507 {4,3,0}, {0,0,0}, {5,0,0}, {2,0,0},
508 {3,0,3}, {3,0,0}, {1,0,0}, {5,0,0},
509 {2,0,0}, {1,1,6}, {4,0,0}, {6,1,6},
510 {0,0,2}, {5,0,0}, {6,1,6}, {3,0,0},
511 {4,0,0}, {6,0,0}, {2,0,0}, {1,0,0},
512 {5,0,0}, {7,0,0}, {4,3,0}, {3,3,0},
513 {6,0,0}, {1,0,2}, {7,1,0}, {6,0,2},
514 /* hat #1 in metatile #0 (type H) */
515 {1,1,0}, {1,1,4}, {7,1,1}, {6,1,4},
516 {0,1,2}, {0,1,0}, {5,1,0}, {2,1,0},
517 {7,1,1}, {3,1,0}, {1,1,0}, {5,1,0},
518 {2,1,0}, {4,1,1}, {4,1,0}, {0,3,0},
519 {2,3,0}, {5,1,0}, {0,3,0}, {3,1,0},
520 {4,1,0}, {6,1,0}, {2,1,0}, {1,1,0},
521 {5,1,0}, {7,1,0}, {0,1,2}, {7,0,2},
522 {6,1,0}, {3,3,0}, {6,0,2}, {7,0,0},
523 /* hat #2 in metatile #0 (type H) */
524 {1,2,0}, {1,1,5}, {7,1,3}, {6,1,5},
525 {3,0,1}, {0,2,0}, {5,2,0}, {2,2,0},
526 {7,1,3}, {3,2,0}, {1,2,0}, {5,2,0},
527 {2,2,0}, {4,1,3}, {4,2,0}, {6,3,0},
528 {1,3,0}, {5,2,0}, {6,3,0}, {3,2,0},
529 {4,2,0}, {6,2,0}, {2,2,0}, {1,2,0},
530 {5,2,0}, {7,2,0}, {3,0,1}, {4,0,1},
531 {6,2,0}, {0,3,0}, {3,1,1}, {4,1,1},
532 /* hat #3 in metatile #0 (type H) */
533 {7,2,0}, {1,3,0}, {3,1,0}, {4,1,0},
534 {0,3,0}, {4,2,0}, {2,3,0}, {5,3,0},
535 {3,3,0}, {4,1,0}, {5,3,0}, {1,3,0},
536 {7,1,0}, {2,3,0}, {6,0,0}, {4,3,0},
537 {5,3,0}, {1,0,0}, {3,3,0}, {6,0,0},
538 {6,3,0}, {4,3,0}, {1,3,0}, {2,3,0},
539 {7,3,0}, {5,3,0}, {3,2,0}, {4,2,0},
540 {0,0,0}, {6,3,0}, {3,1,3}, {4,1,3},
541 /* hat #0 in metatile #1 (type P) */
542 {1,0,1}, {-1,-1,-1}, {0,1,5}, {7,0,5},
543 {-1,-1,-1}, {0,0,1}, {5,0,1}, {2,0,1},
544 {0,1,5}, {3,0,1}, {1,0,1}, {5,0,1},
545 {2,0,1}, {1,2,0}, {4,0,1}, {6,2,0},
546 {3,1,1}, {5,0,1}, {6,2,0}, {3,0,1},
547 {4,0,1}, {6,0,1}, {2,0,1}, {1,0,1},
548 {5,0,1}, {7,0,1}, {-1,-1,-1}, {-1,-1,-1},
549 {6,0,1}, {2,1,1}, {-1,-1,-1}, {0,1,1},
550 /* hat #1 in metatile #1 (type P) */
551 {1,1,1}, {-1,-1,-1}, {7,0,1}, {-1,-1,-1},
552 {-1,-1,-1}, {0,1,1}, {5,1,1}, {2,1,1},
553 {7,0,1}, {3,1,1}, {1,1,1}, {5,1,1},
554 {2,1,1}, {4,0,1}, {4,1,1}, {7,2,0},
555 {3,1,0}, {5,1,1}, {7,2,0}, {3,1,1},
556 {4,1,1}, {6,1,1}, {2,1,1}, {1,1,1},
557 {5,1,1}, {7,1,1}, {-1,-1,-1}, {7,1,4},
558 {6,1,1}, {2,1,0}, {6,1,4}, {0,1,0},
559 /* hat #2 in metatile #1 (type P) does not exist */
560 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
561 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
562 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
563 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
564 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
565 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
566 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
567 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
568 /* hat #3 in metatile #1 (type P) does not exist */
569 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
570 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
571 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
572 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
573 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
574 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
575 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
576 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
577 /* hat #0 in metatile #2 (type P) */
578 {1,0,2}, {4,0,0}, {-1,-1,-1}, {7,1,6},
579 {7,0,0}, {0,0,2}, {5,0,2}, {2,0,2},
580 {-1,-1,-1}, {3,0,2}, {1,0,2}, {5,0,2},
581 {2,0,2}, {-1,-1,-1}, {4,0,2}, {-1,-1,-1},
582 {3,1,2}, {5,0,2}, {-1,-1,-1}, {3,0,2},
583 {4,0,2}, {6,0,2}, {2,0,2}, {1,0,2},
584 {5,0,2}, {7,0,2}, {7,0,0}, {7,1,0},
585 {6,0,2}, {2,1,2}, {6,1,0}, {0,1,2},
586 /* hat #1 in metatile #2 (type P) */
587 {1,1,2}, {1,1,0}, {7,0,2}, {6,1,0},
588 {0,1,4}, {0,1,2}, {5,1,2}, {2,1,2},
589 {7,0,2}, {3,1,2}, {1,1,2}, {5,1,2},
590 {2,1,2}, {4,0,2}, {4,1,2}, {-1,-1,-1},
591 {-1,-1,-1}, {5,1,2}, {-1,-1,-1}, {3,1,2},
592 {4,1,2}, {6,1,2}, {2,1,2}, {1,1,2},
593 {5,1,2}, {7,1,2}, {0,1,4}, {7,0,4},
594 {6,1,2}, {-1,-1,-1}, {6,0,4}, {-1,-1,-1},
595 /* hat #2 in metatile #2 (type P) does not exist */
596 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
597 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
598 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
599 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
600 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
601 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
602 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
603 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
604 /* hat #3 in metatile #2 (type P) does not exist */
605 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
606 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
607 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
608 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
609 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
610 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
611 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
612 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
613 /* hat #0 in metatile #3 (type P) */
614 {1,0,3}, {-1,-1,-1}, {0,1,6}, {7,0,6},
615 {-1,-1,-1}, {0,0,3}, {5,0,3}, {2,0,3},
616 {0,1,6}, {3,0,3}, {1,0,3}, {5,0,3},
617 {2,0,3}, {2,0,0}, {4,0,3}, {0,0,0},
618 {3,1,3}, {5,0,3}, {0,0,0}, {3,0,3},
619 {4,0,3}, {6,0,3}, {2,0,3}, {1,0,3},
620 {5,0,3}, {7,0,3}, {-1,-1,-1}, {-1,-1,-1},
621 {6,0,3}, {2,1,3}, {-1,-1,-1}, {0,1,3},
622 /* hat #1 in metatile #3 (type P) */
623 {1,1,3}, {-1,-1,-1}, {7,0,3}, {-1,-1,-1},
624 {-1,-1,-1}, {0,1,3}, {5,1,3}, {2,1,3},
625 {7,0,3}, {3,1,3}, {1,1,3}, {5,1,3},
626 {2,1,3}, {4,0,3}, {4,1,3}, {7,3,0},
627 {3,2,0}, {5,1,3}, {7,3,0}, {3,1,3},
628 {4,1,3}, {6,1,3}, {2,1,3}, {1,1,3},
629 {5,1,3}, {7,1,3}, {-1,-1,-1}, {7,1,5},
630 {6,1,3}, {2,2,0}, {6,1,5}, {0,2,0},
631 /* hat #2 in metatile #3 (type P) does not exist */
632 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
633 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
634 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
635 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
636 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
637 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
638 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
639 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
640 /* hat #3 in metatile #3 (type P) does not exist */
641 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
642 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
643 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
644 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
645 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
646 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
647 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
648 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
649 /* hat #0 in metatile #4 (type F) */
650 {1,0,4}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
651 {-1,-1,-1}, {0,0,4}, {5,0,4}, {2,0,4},
652 {-1,-1,-1}, {3,0,4}, {1,0,4}, {5,0,4},
653 {2,0,4}, {-1,-1,-1}, {4,0,4}, {-1,-1,-1},
654 {3,1,4}, {5,0,4}, {-1,-1,-1}, {3,0,4},
655 {4,0,4}, {6,0,4}, {2,0,4}, {1,0,4},
656 {5,0,4}, {7,0,4}, {-1,-1,-1}, {7,1,2},
657 {6,0,4}, {2,1,4}, {6,1,2}, {0,1,4},
658 /* hat #1 in metatile #4 (type F) */
659 {1,1,4}, {1,1,2}, {7,0,4}, {6,1,2},
660 {0,1,0}, {0,1,4}, {5,1,4}, {2,1,4},
661 {7,0,4}, {3,1,4}, {1,1,4}, {5,1,4},
662 {2,1,4}, {4,0,4}, {4,1,4}, {-1,-1,-1},
663 {-1,-1,-1}, {5,1,4}, {-1,-1,-1}, {3,1,4},
664 {4,1,4}, {6,1,4}, {2,1,4}, {1,1,4},
665 {5,1,4}, {7,1,4}, {0,1,0}, {7,1,1},
666 {6,1,4}, {-1,-1,-1}, {6,1,1}, {-1,-1,-1},
667 /* hat #2 in metatile #4 (type F) does not exist */
668 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
669 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
670 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
671 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
672 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
673 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
674 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
675 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
676 /* hat #3 in metatile #4 (type F) does not exist */
677 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
678 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
679 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
680 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
681 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
682 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
683 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
684 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
685 /* hat #0 in metatile #5 (type F) */
686 {1,0,5}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
687 {-1,-1,-1}, {0,0,5}, {5,0,5}, {2,0,5},
688 {-1,-1,-1}, {3,0,5}, {1,0,5}, {5,0,5},
689 {2,0,5}, {-1,-1,-1}, {4,0,5}, {-1,-1,-1},
690 {3,1,5}, {5,0,5}, {-1,-1,-1}, {3,0,5},
691 {4,0,5}, {6,0,5}, {2,0,5}, {1,0,5},
692 {5,0,5}, {7,0,5}, {-1,-1,-1}, {-1,-1,-1},
693 {6,0,5}, {2,1,5}, {0,0,1}, {0,1,5},
694 /* hat #1 in metatile #5 (type F) */
695 {1,1,5}, {2,0,1}, {7,0,5}, {0,0,1},
696 {0,2,0}, {0,1,5}, {5,1,5}, {2,1,5},
697 {7,0,5}, {3,1,5}, {1,1,5}, {5,1,5},
698 {2,1,5}, {4,0,5}, {4,1,5}, {-1,-1,-1},
699 {-1,-1,-1}, {5,1,5}, {-1,-1,-1}, {3,1,5},
700 {4,1,5}, {6,1,5}, {2,1,5}, {1,1,5},
701 {5,1,5}, {7,1,5}, {0,2,0}, {7,1,3},
702 {6,1,5}, {-1,-1,-1}, {6,1,3}, {-1,-1,-1},
703 /* hat #2 in metatile #5 (type F) does not exist */
704 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
705 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
706 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
707 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
708 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
709 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
710 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
711 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
712 /* hat #3 in metatile #5 (type F) does not exist */
713 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
714 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
715 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
716 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
717 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
718 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
719 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
720 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
721 /* hat #0 in metatile #6 (type F) */
722 {1,0,6}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
723 {-1,-1,-1}, {0,0,6}, {5,0,6}, {2,0,6},
724 {-1,-1,-1}, {3,0,6}, {1,0,6}, {5,0,6},
725 {2,0,6}, {-1,-1,-1}, {4,0,6}, {-1,-1,-1},
726 {3,1,6}, {5,0,6}, {-1,-1,-1}, {3,0,6},
727 {4,0,6}, {6,0,6}, {2,0,6}, {1,0,6},
728 {5,0,6}, {7,0,6}, {-1,-1,-1}, {-1,-1,-1},
729 {6,0,6}, {2,1,6}, {0,0,3}, {0,1,6},
730 /* hat #1 in metatile #6 (type F) */
731 {1,1,6}, {2,0,3}, {7,0,6}, {0,0,3},
732 {3,0,0}, {0,1,6}, {5,1,6}, {2,1,6},
733 {7,0,6}, {3,1,6}, {1,1,6}, {5,1,6},
734 {2,1,6}, {4,0,6}, {4,1,6}, {-1,-1,-1},
735 {-1,-1,-1}, {5,1,6}, {-1,-1,-1}, {3,1,6},
736 {4,1,6}, {6,1,6}, {2,1,6}, {1,1,6},
737 {5,1,6}, {7,1,6}, {3,0,0}, {4,0,0},
738 {6,1,6}, {-1,-1,-1}, {0,0,2}, {-1,-1,-1},
739 /* hat #2 in metatile #6 (type F) does not exist */
740 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
741 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
742 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
743 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
744 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
745 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
746 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
747 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
748 /* hat #3 in metatile #6 (type F) does not exist */
749 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
750 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
751 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
752 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
753 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
754 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
755 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
756 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
757};
758static const KitemapEntry kitemap_P[] = {
759 /* hat #0 in metatile #0 (type H) */
760 {1,0,0}, {7,3,0}, {3,0,4}, {4,0,4},
761 {4,3,0}, {0,0,0}, {5,0,0}, {2,0,0},
762 {3,0,4}, {3,0,0}, {1,0,0}, {5,0,0},
763 {2,0,0}, {1,1,9}, {4,0,0}, {6,1,9},
764 {0,0,3}, {5,0,0}, {6,1,9}, {3,0,0},
765 {4,0,0}, {6,0,0}, {2,0,0}, {1,0,0},
766 {5,0,0}, {7,0,0}, {4,3,0}, {3,3,0},
767 {6,0,0}, {1,0,3}, {7,1,0}, {6,0,3},
768 /* hat #1 in metatile #0 (type H) */
769 {1,1,0}, {1,1,8}, {7,1,5}, {6,1,8},
770 {0,1,3}, {0,1,0}, {5,1,0}, {2,1,0},
771 {7,1,5}, {3,1,0}, {1,1,0}, {5,1,0},
772 {2,1,0}, {4,1,5}, {4,1,0}, {0,3,0},
773 {2,3,0}, {5,1,0}, {0,3,0}, {3,1,0},
774 {4,1,0}, {6,1,0}, {2,1,0}, {1,1,0},
775 {5,1,0}, {7,1,0}, {0,1,3}, {7,0,3},
776 {6,1,0}, {3,3,0}, {6,0,3}, {7,0,0},
777 /* hat #2 in metatile #0 (type H) */
778 {1,2,0}, {1,0,6}, {7,1,4}, {6,0,6},
779 {3,0,5}, {0,2,0}, {5,2,0}, {2,2,0},
780 {7,1,4}, {3,2,0}, {1,2,0}, {5,2,0},
781 {2,2,0}, {4,1,4}, {4,2,0}, {6,3,0},
782 {1,3,0}, {5,2,0}, {6,3,0}, {3,2,0},
783 {4,2,0}, {6,2,0}, {2,2,0}, {1,2,0},
784 {5,2,0}, {7,2,0}, {3,0,5}, {4,0,5},
785 {6,2,0}, {0,3,0}, {3,1,5}, {4,1,5},
786 /* hat #3 in metatile #0 (type H) */
787 {7,2,0}, {1,3,0}, {3,1,0}, {4,1,0},
788 {0,3,0}, {4,2,0}, {2,3,0}, {5,3,0},
789 {3,3,0}, {4,1,0}, {5,3,0}, {1,3,0},
790 {7,1,0}, {2,3,0}, {6,0,0}, {4,3,0},
791 {5,3,0}, {1,0,0}, {3,3,0}, {6,0,0},
792 {6,3,0}, {4,3,0}, {1,3,0}, {2,3,0},
793 {7,3,0}, {5,3,0}, {3,2,0}, {4,2,0},
794 {0,0,0}, {6,3,0}, {3,1,4}, {4,1,4},
795 /* hat #0 in metatile #1 (type H) */
796 {1,0,1}, {7,3,1}, {3,0,10}, {4,0,10},
797 {4,3,1}, {0,0,1}, {5,0,1}, {2,0,1},
798 {3,0,10}, {3,0,1}, {1,0,1}, {5,0,1},
799 {2,0,1}, {1,0,9}, {4,0,1}, {6,0,9},
800 {0,0,4}, {5,0,1}, {6,0,9}, {3,0,1},
801 {4,0,1}, {6,0,1}, {2,0,1}, {1,0,1},
802 {5,0,1}, {7,0,1}, {4,3,1}, {3,3,1},
803 {6,0,1}, {1,0,4}, {7,1,1}, {6,0,4},
804 /* hat #1 in metatile #1 (type H) */
805 {1,1,1}, {1,1,6}, {7,1,2}, {6,1,6},
806 {0,1,4}, {0,1,1}, {5,1,1}, {2,1,1},
807 {7,1,2}, {3,1,1}, {1,1,1}, {5,1,1},
808 {2,1,1}, {4,1,2}, {4,1,1}, {0,3,1},
809 {2,3,1}, {5,1,1}, {0,3,1}, {3,1,1},
810 {4,1,1}, {6,1,1}, {2,1,1}, {1,1,1},
811 {5,1,1}, {7,1,1}, {0,1,4}, {7,0,4},
812 {6,1,1}, {3,3,1}, {6,0,4}, {7,0,1},
813 /* hat #2 in metatile #1 (type H) */
814 {1,2,1}, {1,1,7}, {7,1,10}, {6,1,7},
815 {3,0,2}, {0,2,1}, {5,2,1}, {2,2,1},
816 {7,1,10}, {3,2,1}, {1,2,1}, {5,2,1},
817 {2,2,1}, {4,1,10}, {4,2,1}, {6,3,1},
818 {1,3,1}, {5,2,1}, {6,3,1}, {3,2,1},
819 {4,2,1}, {6,2,1}, {2,2,1}, {1,2,1},
820 {5,2,1}, {7,2,1}, {3,0,2}, {4,0,2},
821 {6,2,1}, {0,3,1}, {3,1,2}, {4,1,2},
822 /* hat #3 in metatile #1 (type H) */
823 {7,2,1}, {1,3,1}, {3,1,1}, {4,1,1},
824 {0,3,1}, {4,2,1}, {2,3,1}, {5,3,1},
825 {3,3,1}, {4,1,1}, {5,3,1}, {1,3,1},
826 {7,1,1}, {2,3,1}, {6,0,1}, {4,3,1},
827 {5,3,1}, {1,0,1}, {3,3,1}, {6,0,1},
828 {6,3,1}, {4,3,1}, {1,3,1}, {2,3,1},
829 {7,3,1}, {5,3,1}, {3,2,1}, {4,2,1},
830 {0,0,1}, {6,3,1}, {3,1,10}, {4,1,10},
831 /* hat #0 in metatile #2 (type P) */
832 {1,0,2}, {-1,-1,-1}, {0,1,7}, {7,0,7},
833 {-1,-1,-1}, {0,0,2}, {5,0,2}, {2,0,2},
834 {0,1,7}, {3,0,2}, {1,0,2}, {5,0,2},
835 {2,0,2}, {1,2,1}, {4,0,2}, {6,2,1},
836 {3,1,2}, {5,0,2}, {6,2,1}, {3,0,2},
837 {4,0,2}, {6,0,2}, {2,0,2}, {1,0,2},
838 {5,0,2}, {7,0,2}, {-1,-1,-1}, {-1,-1,-1},
839 {6,0,2}, {2,1,2}, {-1,-1,-1}, {0,1,2},
840 /* hat #1 in metatile #2 (type P) */
841 {1,1,2}, {-1,-1,-1}, {7,0,2}, {-1,-1,-1},
842 {-1,-1,-1}, {0,1,2}, {5,1,2}, {2,1,2},
843 {7,0,2}, {3,1,2}, {1,1,2}, {5,1,2},
844 {2,1,2}, {4,0,2}, {4,1,2}, {7,2,1},
845 {3,1,1}, {5,1,2}, {7,2,1}, {3,1,2},
846 {4,1,2}, {6,1,2}, {2,1,2}, {1,1,2},
847 {5,1,2}, {7,1,2}, {-1,-1,-1}, {7,1,6},
848 {6,1,2}, {2,1,1}, {6,1,6}, {0,1,1},
849 /* hat #2 in metatile #2 (type P) does not exist */
850 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
851 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
852 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
853 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
854 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
855 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
856 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
857 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
858 /* hat #3 in metatile #2 (type P) does not exist */
859 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
860 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
861 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
862 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
863 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
864 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
865 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
866 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
867 /* hat #0 in metatile #3 (type P) */
868 {1,0,3}, {4,0,0}, {-1,-1,-1}, {7,1,9},
869 {7,0,0}, {0,0,3}, {5,0,3}, {2,0,3},
870 {-1,-1,-1}, {3,0,3}, {1,0,3}, {5,0,3},
871 {2,0,3}, {-1,-1,-1}, {4,0,3}, {-1,-1,-1},
872 {3,1,3}, {5,0,3}, {-1,-1,-1}, {3,0,3},
873 {4,0,3}, {6,0,3}, {2,0,3}, {1,0,3},
874 {5,0,3}, {7,0,3}, {7,0,0}, {7,1,0},
875 {6,0,3}, {2,1,3}, {6,1,0}, {0,1,3},
876 /* hat #1 in metatile #3 (type P) */
877 {1,1,3}, {1,1,0}, {7,0,3}, {6,1,0},
878 {0,1,8}, {0,1,3}, {5,1,3}, {2,1,3},
879 {7,0,3}, {3,1,3}, {1,1,3}, {5,1,3},
880 {2,1,3}, {4,0,3}, {4,1,3}, {-1,-1,-1},
881 {-1,-1,-1}, {5,1,3}, {-1,-1,-1}, {3,1,3},
882 {4,1,3}, {6,1,3}, {2,1,3}, {1,1,3},
883 {5,1,3}, {7,1,3}, {0,1,8}, {7,0,8},
884 {6,1,3}, {-1,-1,-1}, {6,0,8}, {-1,-1,-1},
885 /* hat #2 in metatile #3 (type P) does not exist */
886 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
887 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
888 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
889 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
890 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
891 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
892 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
893 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
894 /* hat #3 in metatile #3 (type P) does not exist */
895 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
896 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
897 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
898 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
899 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
900 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
901 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
902 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
903 /* hat #0 in metatile #4 (type P) */
904 {1,0,4}, {4,0,1}, {0,1,9}, {7,0,9},
905 {7,0,1}, {0,0,4}, {5,0,4}, {2,0,4},
906 {0,1,9}, {3,0,4}, {1,0,4}, {5,0,4},
907 {2,0,4}, {2,0,0}, {4,0,4}, {0,0,0},
908 {3,1,4}, {5,0,4}, {0,0,0}, {3,0,4},
909 {4,0,4}, {6,0,4}, {2,0,4}, {1,0,4},
910 {5,0,4}, {7,0,4}, {7,0,1}, {7,1,1},
911 {6,0,4}, {2,1,4}, {6,1,1}, {0,1,4},
912 /* hat #1 in metatile #4 (type P) */
913 {1,1,4}, {1,1,1}, {7,0,4}, {6,1,1},
914 {0,1,6}, {0,1,4}, {5,1,4}, {2,1,4},
915 {7,0,4}, {3,1,4}, {1,1,4}, {5,1,4},
916 {2,1,4}, {4,0,4}, {4,1,4}, {7,3,0},
917 {3,2,0}, {5,1,4}, {7,3,0}, {3,1,4},
918 {4,1,4}, {6,1,4}, {2,1,4}, {1,1,4},
919 {5,1,4}, {7,1,4}, {0,1,6}, {7,0,6},
920 {6,1,4}, {2,2,0}, {6,0,6}, {0,2,0},
921 /* hat #2 in metatile #4 (type P) does not exist */
922 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
923 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
924 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
925 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
926 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
927 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
928 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
929 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
930 /* hat #3 in metatile #4 (type P) does not exist */
931 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
932 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
933 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
934 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
935 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
936 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
937 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
938 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
939 /* hat #0 in metatile #5 (type F) */
940 {1,0,5}, {-1,-1,-1}, {0,0,6}, {-1,-1,-1},
941 {-1,-1,-1}, {0,0,5}, {5,0,5}, {2,0,5},
942 {0,0,6}, {3,0,5}, {1,0,5}, {5,0,5},
943 {2,0,5}, {1,2,0}, {4,0,5}, {6,2,0},
944 {3,1,5}, {5,0,5}, {6,2,0}, {3,0,5},
945 {4,0,5}, {6,0,5}, {2,0,5}, {1,0,5},
946 {5,0,5}, {7,0,5}, {-1,-1,-1}, {-1,-1,-1},
947 {6,0,5}, {2,1,5}, {-1,-1,-1}, {0,1,5},
948 /* hat #1 in metatile #5 (type F) */
949 {1,1,5}, {-1,-1,-1}, {7,0,5}, {-1,-1,-1},
950 {-1,-1,-1}, {0,1,5}, {5,1,5}, {2,1,5},
951 {7,0,5}, {3,1,5}, {1,1,5}, {5,1,5},
952 {2,1,5}, {4,0,5}, {4,1,5}, {7,2,0},
953 {3,1,0}, {5,1,5}, {7,2,0}, {3,1,5},
954 {4,1,5}, {6,1,5}, {2,1,5}, {1,1,5},
955 {5,1,5}, {7,1,5}, {-1,-1,-1}, {7,1,8},
956 {6,1,5}, {2,1,0}, {6,1,8}, {0,1,0},
957 /* hat #2 in metatile #5 (type F) does not exist */
958 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
959 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
960 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
961 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
962 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
963 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
964 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
965 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
966 /* hat #3 in metatile #5 (type F) does not exist */
967 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
968 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
969 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
970 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
971 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
972 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
973 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
974 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
975 /* hat #0 in metatile #6 (type F) */
976 {1,0,6}, {2,0,5}, {-1,-1,-1}, {0,0,5},
977 {0,2,0}, {0,0,6}, {5,0,6}, {2,0,6},
978 {-1,-1,-1}, {3,0,6}, {1,0,6}, {5,0,6},
979 {2,0,6}, {-1,-1,-1}, {4,0,6}, {-1,-1,-1},
980 {3,1,6}, {5,0,6}, {-1,-1,-1}, {3,0,6},
981 {4,0,6}, {6,0,6}, {2,0,6}, {1,0,6},
982 {5,0,6}, {7,0,6}, {0,2,0}, {7,1,4},
983 {6,0,6}, {2,1,6}, {6,1,4}, {0,1,6},
984 /* hat #1 in metatile #6 (type F) */
985 {1,1,6}, {1,1,4}, {7,0,6}, {6,1,4},
986 {0,1,1}, {0,1,6}, {5,1,6}, {2,1,6},
987 {7,0,6}, {3,1,6}, {1,1,6}, {5,1,6},
988 {2,1,6}, {4,0,6}, {4,1,6}, {-1,-1,-1},
989 {-1,-1,-1}, {5,1,6}, {-1,-1,-1}, {3,1,6},
990 {4,1,6}, {6,1,6}, {2,1,6}, {1,1,6},
991 {5,1,6}, {7,1,6}, {0,1,1}, {7,1,2},
992 {6,1,6}, {-1,-1,-1}, {6,1,2}, {-1,-1,-1},
993 /* hat #2 in metatile #6 (type F) does not exist */
994 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
995 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
996 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
997 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
998 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
999 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1000 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1001 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1002 /* hat #3 in metatile #6 (type F) does not exist */
1003 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1004 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1005 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1006 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1007 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1008 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1009 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1010 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1011 /* hat #0 in metatile #7 (type F) */
1012 {1,0,7}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1013 {-1,-1,-1}, {0,0,7}, {5,0,7}, {2,0,7},
1014 {-1,-1,-1}, {3,0,7}, {1,0,7}, {5,0,7},
1015 {2,0,7}, {-1,-1,-1}, {4,0,7}, {-1,-1,-1},
1016 {3,1,7}, {5,0,7}, {-1,-1,-1}, {3,0,7},
1017 {4,0,7}, {6,0,7}, {2,0,7}, {1,0,7},
1018 {5,0,7}, {7,0,7}, {-1,-1,-1}, {-1,-1,-1},
1019 {6,0,7}, {2,1,7}, {0,0,2}, {0,1,7},
1020 /* hat #1 in metatile #7 (type F) */
1021 {1,1,7}, {2,0,2}, {7,0,7}, {0,0,2},
1022 {0,2,1}, {0,1,7}, {5,1,7}, {2,1,7},
1023 {7,0,7}, {3,1,7}, {1,1,7}, {5,1,7},
1024 {2,1,7}, {4,0,7}, {4,1,7}, {-1,-1,-1},
1025 {-1,-1,-1}, {5,1,7}, {-1,-1,-1}, {3,1,7},
1026 {4,1,7}, {6,1,7}, {2,1,7}, {1,1,7},
1027 {5,1,7}, {7,1,7}, {0,2,1}, {7,1,10},
1028 {6,1,7}, {-1,-1,-1}, {6,1,10}, {-1,-1,-1},
1029 /* hat #2 in metatile #7 (type F) does not exist */
1030 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1031 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1032 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1033 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1034 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1035 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1036 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1037 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1038 /* hat #3 in metatile #7 (type F) does not exist */
1039 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1040 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1041 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1042 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1043 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1044 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1045 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1046 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1047 /* hat #0 in metatile #8 (type F) */
1048 {1,0,8}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1049 {-1,-1,-1}, {0,0,8}, {5,0,8}, {2,0,8},
1050 {-1,-1,-1}, {3,0,8}, {1,0,8}, {5,0,8},
1051 {2,0,8}, {-1,-1,-1}, {4,0,8}, {-1,-1,-1},
1052 {3,1,8}, {5,0,8}, {-1,-1,-1}, {3,0,8},
1053 {4,0,8}, {6,0,8}, {2,0,8}, {1,0,8},
1054 {5,0,8}, {7,0,8}, {-1,-1,-1}, {7,1,3},
1055 {6,0,8}, {2,1,8}, {6,1,3}, {0,1,8},
1056 /* hat #1 in metatile #8 (type F) */
1057 {1,1,8}, {1,1,3}, {7,0,8}, {6,1,3},
1058 {0,1,0}, {0,1,8}, {5,1,8}, {2,1,8},
1059 {7,0,8}, {3,1,8}, {1,1,8}, {5,1,8},
1060 {2,1,8}, {4,0,8}, {4,1,8}, {-1,-1,-1},
1061 {-1,-1,-1}, {5,1,8}, {-1,-1,-1}, {3,1,8},
1062 {4,1,8}, {6,1,8}, {2,1,8}, {1,1,8},
1063 {5,1,8}, {7,1,8}, {0,1,0}, {7,1,5},
1064 {6,1,8}, {-1,-1,-1}, {6,1,5}, {-1,-1,-1},
1065 /* hat #2 in metatile #8 (type F) does not exist */
1066 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1067 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1068 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1069 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1070 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1071 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1072 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1073 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1074 /* hat #3 in metatile #8 (type F) does not exist */
1075 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1076 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1077 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1078 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1079 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1080 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1081 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1082 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1083 /* hat #0 in metatile #9 (type F) */
1084 {1,0,9}, {2,0,10}, {-1,-1,-1}, {0,0,10},
1085 {3,0,1}, {0,0,9}, {5,0,9}, {2,0,9},
1086 {-1,-1,-1}, {3,0,9}, {1,0,9}, {5,0,9},
1087 {2,0,9}, {-1,-1,-1}, {4,0,9}, {-1,-1,-1},
1088 {3,1,9}, {5,0,9}, {-1,-1,-1}, {3,0,9},
1089 {4,0,9}, {6,0,9}, {2,0,9}, {1,0,9},
1090 {5,0,9}, {7,0,9}, {3,0,1}, {4,0,1},
1091 {6,0,9}, {2,1,9}, {0,0,4}, {0,1,9},
1092 /* hat #1 in metatile #9 (type F) */
1093 {1,1,9}, {2,0,4}, {7,0,9}, {0,0,4},
1094 {3,0,0}, {0,1,9}, {5,1,9}, {2,1,9},
1095 {7,0,9}, {3,1,9}, {1,1,9}, {5,1,9},
1096 {2,1,9}, {4,0,9}, {4,1,9}, {-1,-1,-1},
1097 {-1,-1,-1}, {5,1,9}, {-1,-1,-1}, {3,1,9},
1098 {4,1,9}, {6,1,9}, {2,1,9}, {1,1,9},
1099 {5,1,9}, {7,1,9}, {3,0,0}, {4,0,0},
1100 {6,1,9}, {-1,-1,-1}, {0,0,3}, {-1,-1,-1},
1101 /* hat #2 in metatile #9 (type F) does not exist */
1102 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1103 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1104 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1105 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1106 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1107 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1108 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1109 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1110 /* hat #3 in metatile #9 (type F) does not exist */
1111 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1112 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1113 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1114 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1115 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1116 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1117 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1118 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1119 /* hat #0 in metatile #10 (type F) */
1120 {1,0,10}, {-1,-1,-1}, {0,0,9}, {-1,-1,-1},
1121 {-1,-1,-1}, {0,0,10}, {5,0,10}, {2,0,10},
1122 {0,0,9}, {3,0,10}, {1,0,10}, {5,0,10},
1123 {2,0,10}, {2,0,1}, {4,0,10}, {0,0,1},
1124 {3,1,10}, {5,0,10}, {0,0,1}, {3,0,10},
1125 {4,0,10}, {6,0,10}, {2,0,10}, {1,0,10},
1126 {5,0,10}, {7,0,10}, {-1,-1,-1}, {-1,-1,-1},
1127 {6,0,10}, {2,1,10}, {-1,-1,-1}, {0,1,10},
1128 /* hat #1 in metatile #10 (type F) */
1129 {1,1,10}, {-1,-1,-1}, {7,0,10}, {-1,-1,-1},
1130 {-1,-1,-1}, {0,1,10}, {5,1,10}, {2,1,10},
1131 {7,0,10}, {3,1,10}, {1,1,10}, {5,1,10},
1132 {2,1,10}, {4,0,10}, {4,1,10}, {7,3,1},
1133 {3,2,1}, {5,1,10}, {7,3,1}, {3,1,10},
1134 {4,1,10}, {6,1,10}, {2,1,10}, {1,1,10},
1135 {5,1,10}, {7,1,10}, {-1,-1,-1}, {7,1,7},
1136 {6,1,10}, {2,2,1}, {6,1,7}, {0,2,1},
1137 /* hat #2 in metatile #10 (type F) does not exist */
1138 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1139 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1140 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1141 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1142 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1143 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1144 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1145 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1146 /* hat #3 in metatile #10 (type F) does not exist */
1147 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1148 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1149 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1150 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1151 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1152 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1153 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1154 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1155};
1156static const KitemapEntry kitemap_F[] = {
1157 /* hat #0 in metatile #0 (type H) */
1158 {1,0,0}, {7,3,0}, {3,0,3}, {4,0,3},
1159 {4,3,0}, {0,0,0}, {5,0,0}, {2,0,0},
1160 {3,0,3}, {3,0,0}, {1,0,0}, {5,0,0},
1161 {2,0,0}, {1,1,9}, {4,0,0}, {6,1,9},
1162 {0,0,2}, {5,0,0}, {6,1,9}, {3,0,0},
1163 {4,0,0}, {6,0,0}, {2,0,0}, {1,0,0},
1164 {5,0,0}, {7,0,0}, {4,3,0}, {3,3,0},
1165 {6,0,0}, {1,0,2}, {7,1,0}, {6,0,2},
1166 /* hat #1 in metatile #0 (type H) */
1167 {1,1,0}, {1,1,8}, {7,1,4}, {6,1,8},
1168 {0,1,2}, {0,1,0}, {5,1,0}, {2,1,0},
1169 {7,1,4}, {3,1,0}, {1,1,0}, {5,1,0},
1170 {2,1,0}, {4,1,4}, {4,1,0}, {0,3,0},
1171 {2,3,0}, {5,1,0}, {0,3,0}, {3,1,0},
1172 {4,1,0}, {6,1,0}, {2,1,0}, {1,1,0},
1173 {5,1,0}, {7,1,0}, {0,1,2}, {7,0,2},
1174 {6,1,0}, {3,3,0}, {6,0,2}, {7,0,0},
1175 /* hat #2 in metatile #0 (type H) */
1176 {1,2,0}, {1,0,5}, {7,1,3}, {6,0,5},
1177 {3,0,4}, {0,2,0}, {5,2,0}, {2,2,0},
1178 {7,1,3}, {3,2,0}, {1,2,0}, {5,2,0},
1179 {2,2,0}, {4,1,3}, {4,2,0}, {6,3,0},
1180 {1,3,0}, {5,2,0}, {6,3,0}, {3,2,0},
1181 {4,2,0}, {6,2,0}, {2,2,0}, {1,2,0},
1182 {5,2,0}, {7,2,0}, {3,0,4}, {4,0,4},
1183 {6,2,0}, {0,3,0}, {3,1,4}, {4,1,4},
1184 /* hat #3 in metatile #0 (type H) */
1185 {7,2,0}, {1,3,0}, {3,1,0}, {4,1,0},
1186 {0,3,0}, {4,2,0}, {2,3,0}, {5,3,0},
1187 {3,3,0}, {4,1,0}, {5,3,0}, {1,3,0},
1188 {7,1,0}, {2,3,0}, {6,0,0}, {4,3,0},
1189 {5,3,0}, {1,0,0}, {3,3,0}, {6,0,0},
1190 {6,3,0}, {4,3,0}, {1,3,0}, {2,3,0},
1191 {7,3,0}, {5,3,0}, {3,2,0}, {4,2,0},
1192 {0,0,0}, {6,3,0}, {3,1,3}, {4,1,3},
1193 /* hat #0 in metatile #1 (type H) */
1194 {1,0,1}, {7,3,1}, {3,0,10}, {4,0,10},
1195 {4,3,1}, {0,0,1}, {5,0,1}, {2,0,1},
1196 {3,0,10}, {3,0,1}, {1,0,1}, {5,0,1},
1197 {2,0,1}, {1,0,9}, {4,0,1}, {6,0,9},
1198 {0,0,3}, {5,0,1}, {6,0,9}, {3,0,1},
1199 {4,0,1}, {6,0,1}, {2,0,1}, {1,0,1},
1200 {5,0,1}, {7,0,1}, {4,3,1}, {3,3,1},
1201 {6,0,1}, {1,0,3}, {7,1,1}, {6,0,3},
1202 /* hat #1 in metatile #1 (type H) */
1203 {1,1,1}, {1,1,5}, {7,1,6}, {6,1,5},
1204 {0,1,3}, {0,1,1}, {5,1,1}, {2,1,1},
1205 {7,1,6}, {3,1,1}, {1,1,1}, {5,1,1},
1206 {2,1,1}, {4,1,6}, {4,1,1}, {0,3,1},
1207 {2,3,1}, {5,1,1}, {0,3,1}, {3,1,1},
1208 {4,1,1}, {6,1,1}, {2,1,1}, {1,1,1},
1209 {5,1,1}, {7,1,1}, {0,1,3}, {7,0,3},
1210 {6,1,1}, {3,3,1}, {6,0,3}, {7,0,1},
1211 /* hat #2 in metatile #1 (type H) */
1212 {1,2,1}, {1,0,7}, {7,1,10}, {6,0,7},
1213 {3,0,6}, {0,2,1}, {5,2,1}, {2,2,1},
1214 {7,1,10}, {3,2,1}, {1,2,1}, {5,2,1},
1215 {2,2,1}, {4,1,10}, {4,2,1}, {6,3,1},
1216 {1,3,1}, {5,2,1}, {6,3,1}, {3,2,1},
1217 {4,2,1}, {6,2,1}, {2,2,1}, {1,2,1},
1218 {5,2,1}, {7,2,1}, {3,0,6}, {4,0,6},
1219 {6,2,1}, {0,3,1}, {3,1,6}, {4,1,6},
1220 /* hat #3 in metatile #1 (type H) */
1221 {7,2,1}, {1,3,1}, {3,1,1}, {4,1,1},
1222 {0,3,1}, {4,2,1}, {2,3,1}, {5,3,1},
1223 {3,3,1}, {4,1,1}, {5,3,1}, {1,3,1},
1224 {7,1,1}, {2,3,1}, {6,0,1}, {4,3,1},
1225 {5,3,1}, {1,0,1}, {3,3,1}, {6,0,1},
1226 {6,3,1}, {4,3,1}, {1,3,1}, {2,3,1},
1227 {7,3,1}, {5,3,1}, {3,2,1}, {4,2,1},
1228 {0,0,1}, {6,3,1}, {3,1,10}, {4,1,10},
1229 /* hat #0 in metatile #2 (type P) */
1230 {1,0,2}, {4,0,0}, {-1,-1,-1}, {7,1,9},
1231 {7,0,0}, {0,0,2}, {5,0,2}, {2,0,2},
1232 {-1,-1,-1}, {3,0,2}, {1,0,2}, {5,0,2},
1233 {2,0,2}, {-1,-1,-1}, {4,0,2}, {-1,-1,-1},
1234 {3,1,2}, {5,0,2}, {-1,-1,-1}, {3,0,2},
1235 {4,0,2}, {6,0,2}, {2,0,2}, {1,0,2},
1236 {5,0,2}, {7,0,2}, {7,0,0}, {7,1,0},
1237 {6,0,2}, {2,1,2}, {6,1,0}, {0,1,2},
1238 /* hat #1 in metatile #2 (type P) */
1239 {1,1,2}, {1,1,0}, {7,0,2}, {6,1,0},
1240 {0,1,8}, {0,1,2}, {5,1,2}, {2,1,2},
1241 {7,0,2}, {3,1,2}, {1,1,2}, {5,1,2},
1242 {2,1,2}, {4,0,2}, {4,1,2}, {-1,-1,-1},
1243 {-1,-1,-1}, {5,1,2}, {-1,-1,-1}, {3,1,2},
1244 {4,1,2}, {6,1,2}, {2,1,2}, {1,1,2},
1245 {5,1,2}, {7,1,2}, {0,1,8}, {7,0,8},
1246 {6,1,2}, {-1,-1,-1}, {6,0,8}, {-1,-1,-1},
1247 /* hat #2 in metatile #2 (type P) does not exist */
1248 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1249 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1250 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1251 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1252 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1253 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1254 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1255 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1256 /* hat #3 in metatile #2 (type P) does not exist */
1257 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1258 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1259 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1260 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1261 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1262 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1263 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1264 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1265 /* hat #0 in metatile #3 (type P) */
1266 {1,0,3}, {4,0,1}, {0,1,9}, {7,0,9},
1267 {7,0,1}, {0,0,3}, {5,0,3}, {2,0,3},
1268 {0,1,9}, {3,0,3}, {1,0,3}, {5,0,3},
1269 {2,0,3}, {2,0,0}, {4,0,3}, {0,0,0},
1270 {3,1,3}, {5,0,3}, {0,0,0}, {3,0,3},
1271 {4,0,3}, {6,0,3}, {2,0,3}, {1,0,3},
1272 {5,0,3}, {7,0,3}, {7,0,1}, {7,1,1},
1273 {6,0,3}, {2,1,3}, {6,1,1}, {0,1,3},
1274 /* hat #1 in metatile #3 (type P) */
1275 {1,1,3}, {1,1,1}, {7,0,3}, {6,1,1},
1276 {0,1,5}, {0,1,3}, {5,1,3}, {2,1,3},
1277 {7,0,3}, {3,1,3}, {1,1,3}, {5,1,3},
1278 {2,1,3}, {4,0,3}, {4,1,3}, {7,3,0},
1279 {3,2,0}, {5,1,3}, {7,3,0}, {3,1,3},
1280 {4,1,3}, {6,1,3}, {2,1,3}, {1,1,3},
1281 {5,1,3}, {7,1,3}, {0,1,5}, {7,0,5},
1282 {6,1,3}, {2,2,0}, {6,0,5}, {0,2,0},
1283 /* hat #2 in metatile #3 (type P) does not exist */
1284 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1285 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1286 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1287 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1288 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1289 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1290 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1291 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1292 /* hat #3 in metatile #3 (type P) does not exist */
1293 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1294 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1295 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1296 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1297 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1298 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1299 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1300 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1301 /* hat #0 in metatile #4 (type F) */
1302 {1,0,4}, {-1,-1,-1}, {0,0,5}, {-1,-1,-1},
1303 {-1,-1,-1}, {0,0,4}, {5,0,4}, {2,0,4},
1304 {0,0,5}, {3,0,4}, {1,0,4}, {5,0,4},
1305 {2,0,4}, {1,2,0}, {4,0,4}, {6,2,0},
1306 {3,1,4}, {5,0,4}, {6,2,0}, {3,0,4},
1307 {4,0,4}, {6,0,4}, {2,0,4}, {1,0,4},
1308 {5,0,4}, {7,0,4}, {-1,-1,-1}, {-1,-1,-1},
1309 {6,0,4}, {2,1,4}, {-1,-1,-1}, {0,1,4},
1310 /* hat #1 in metatile #4 (type F) */
1311 {1,1,4}, {-1,-1,-1}, {7,0,4}, {-1,-1,-1},
1312 {-1,-1,-1}, {0,1,4}, {5,1,4}, {2,1,4},
1313 {7,0,4}, {3,1,4}, {1,1,4}, {5,1,4},
1314 {2,1,4}, {4,0,4}, {4,1,4}, {7,2,0},
1315 {3,1,0}, {5,1,4}, {7,2,0}, {3,1,4},
1316 {4,1,4}, {6,1,4}, {2,1,4}, {1,1,4},
1317 {5,1,4}, {7,1,4}, {-1,-1,-1}, {7,1,8},
1318 {6,1,4}, {2,1,0}, {6,1,8}, {0,1,0},
1319 /* hat #2 in metatile #4 (type F) does not exist */
1320 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1321 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1322 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1323 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1324 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1325 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1326 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1327 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1328 /* hat #3 in metatile #4 (type F) does not exist */
1329 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1330 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1331 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1332 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1333 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1334 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1335 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1336 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1337 /* hat #0 in metatile #5 (type F) */
1338 {1,0,5}, {2,0,4}, {-1,-1,-1}, {0,0,4},
1339 {0,2,0}, {0,0,5}, {5,0,5}, {2,0,5},
1340 {-1,-1,-1}, {3,0,5}, {1,0,5}, {5,0,5},
1341 {2,0,5}, {-1,-1,-1}, {4,0,5}, {-1,-1,-1},
1342 {3,1,5}, {5,0,5}, {-1,-1,-1}, {3,0,5},
1343 {4,0,5}, {6,0,5}, {2,0,5}, {1,0,5},
1344 {5,0,5}, {7,0,5}, {0,2,0}, {7,1,3},
1345 {6,0,5}, {2,1,5}, {6,1,3}, {0,1,5},
1346 /* hat #1 in metatile #5 (type F) */
1347 {1,1,5}, {1,1,3}, {7,0,5}, {6,1,3},
1348 {0,1,1}, {0,1,5}, {5,1,5}, {2,1,5},
1349 {7,0,5}, {3,1,5}, {1,1,5}, {5,1,5},
1350 {2,1,5}, {4,0,5}, {4,1,5}, {-1,-1,-1},
1351 {-1,-1,-1}, {5,1,5}, {-1,-1,-1}, {3,1,5},
1352 {4,1,5}, {6,1,5}, {2,1,5}, {1,1,5},
1353 {5,1,5}, {7,1,5}, {0,1,1}, {7,1,6},
1354 {6,1,5}, {-1,-1,-1}, {6,1,6}, {-1,-1,-1},
1355 /* hat #2 in metatile #5 (type F) does not exist */
1356 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1357 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1358 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1359 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1360 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1361 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1362 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1363 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1364 /* hat #3 in metatile #5 (type F) does not exist */
1365 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1366 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1367 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1368 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1369 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1370 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1371 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1372 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1373 /* hat #0 in metatile #6 (type F) */
1374 {1,0,6}, {-1,-1,-1}, {0,0,7}, {-1,-1,-1},
1375 {-1,-1,-1}, {0,0,6}, {5,0,6}, {2,0,6},
1376 {0,0,7}, {3,0,6}, {1,0,6}, {5,0,6},
1377 {2,0,6}, {1,2,1}, {4,0,6}, {6,2,1},
1378 {3,1,6}, {5,0,6}, {6,2,1}, {3,0,6},
1379 {4,0,6}, {6,0,6}, {2,0,6}, {1,0,6},
1380 {5,0,6}, {7,0,6}, {-1,-1,-1}, {-1,-1,-1},
1381 {6,0,6}, {2,1,6}, {-1,-1,-1}, {0,1,6},
1382 /* hat #1 in metatile #6 (type F) */
1383 {1,1,6}, {-1,-1,-1}, {7,0,6}, {-1,-1,-1},
1384 {-1,-1,-1}, {0,1,6}, {5,1,6}, {2,1,6},
1385 {7,0,6}, {3,1,6}, {1,1,6}, {5,1,6},
1386 {2,1,6}, {4,0,6}, {4,1,6}, {7,2,1},
1387 {3,1,1}, {5,1,6}, {7,2,1}, {3,1,6},
1388 {4,1,6}, {6,1,6}, {2,1,6}, {1,1,6},
1389 {5,1,6}, {7,1,6}, {-1,-1,-1}, {7,1,5},
1390 {6,1,6}, {2,1,1}, {6,1,5}, {0,1,1},
1391 /* hat #2 in metatile #6 (type F) does not exist */
1392 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1393 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1394 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1395 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1396 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1397 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1398 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1399 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1400 /* hat #3 in metatile #6 (type F) does not exist */
1401 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1402 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1403 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1404 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1405 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1406 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1407 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1408 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1409 /* hat #0 in metatile #7 (type F) */
1410 {1,0,7}, {2,0,6}, {-1,-1,-1}, {0,0,6},
1411 {0,2,1}, {0,0,7}, {5,0,7}, {2,0,7},
1412 {-1,-1,-1}, {3,0,7}, {1,0,7}, {5,0,7},
1413 {2,0,7}, {-1,-1,-1}, {4,0,7}, {-1,-1,-1},
1414 {3,1,7}, {5,0,7}, {-1,-1,-1}, {3,0,7},
1415 {4,0,7}, {6,0,7}, {2,0,7}, {1,0,7},
1416 {5,0,7}, {7,0,7}, {0,2,1}, {7,1,10},
1417 {6,0,7}, {2,1,7}, {6,1,10}, {0,1,7},
1418 /* hat #1 in metatile #7 (type F) */
1419 {1,1,7}, {1,1,10}, {7,0,7}, {6,1,10},
1420 {-1,-1,-1}, {0,1,7}, {5,1,7}, {2,1,7},
1421 {7,0,7}, {3,1,7}, {1,1,7}, {5,1,7},
1422 {2,1,7}, {4,0,7}, {4,1,7}, {-1,-1,-1},
1423 {-1,-1,-1}, {5,1,7}, {-1,-1,-1}, {3,1,7},
1424 {4,1,7}, {6,1,7}, {2,1,7}, {1,1,7},
1425 {5,1,7}, {7,1,7}, {-1,-1,-1}, {-1,-1,-1},
1426 {6,1,7}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1427 /* hat #2 in metatile #7 (type F) does not exist */
1428 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1429 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1430 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1431 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1432 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1433 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1434 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1435 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1436 /* hat #3 in metatile #7 (type F) does not exist */
1437 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1438 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1439 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1440 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1441 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1442 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1443 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1444 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1445 /* hat #0 in metatile #8 (type F) */
1446 {1,0,8}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1447 {-1,-1,-1}, {0,0,8}, {5,0,8}, {2,0,8},
1448 {-1,-1,-1}, {3,0,8}, {1,0,8}, {5,0,8},
1449 {2,0,8}, {-1,-1,-1}, {4,0,8}, {-1,-1,-1},
1450 {3,1,8}, {5,0,8}, {-1,-1,-1}, {3,0,8},
1451 {4,0,8}, {6,0,8}, {2,0,8}, {1,0,8},
1452 {5,0,8}, {7,0,8}, {-1,-1,-1}, {7,1,2},
1453 {6,0,8}, {2,1,8}, {6,1,2}, {0,1,8},
1454 /* hat #1 in metatile #8 (type F) */
1455 {1,1,8}, {1,1,2}, {7,0,8}, {6,1,2},
1456 {0,1,0}, {0,1,8}, {5,1,8}, {2,1,8},
1457 {7,0,8}, {3,1,8}, {1,1,8}, {5,1,8},
1458 {2,1,8}, {4,0,8}, {4,1,8}, {-1,-1,-1},
1459 {-1,-1,-1}, {5,1,8}, {-1,-1,-1}, {3,1,8},
1460 {4,1,8}, {6,1,8}, {2,1,8}, {1,1,8},
1461 {5,1,8}, {7,1,8}, {0,1,0}, {7,1,4},
1462 {6,1,8}, {-1,-1,-1}, {6,1,4}, {-1,-1,-1},
1463 /* hat #2 in metatile #8 (type F) does not exist */
1464 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1465 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1466 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1467 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1468 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1469 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1470 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1471 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1472 /* hat #3 in metatile #8 (type F) does not exist */
1473 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1474 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1475 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1476 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1477 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1478 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1479 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1480 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1481 /* hat #0 in metatile #9 (type F) */
1482 {1,0,9}, {2,0,10}, {-1,-1,-1}, {0,0,10},
1483 {3,0,1}, {0,0,9}, {5,0,9}, {2,0,9},
1484 {-1,-1,-1}, {3,0,9}, {1,0,9}, {5,0,9},
1485 {2,0,9}, {-1,-1,-1}, {4,0,9}, {-1,-1,-1},
1486 {3,1,9}, {5,0,9}, {-1,-1,-1}, {3,0,9},
1487 {4,0,9}, {6,0,9}, {2,0,9}, {1,0,9},
1488 {5,0,9}, {7,0,9}, {3,0,1}, {4,0,1},
1489 {6,0,9}, {2,1,9}, {0,0,3}, {0,1,9},
1490 /* hat #1 in metatile #9 (type F) */
1491 {1,1,9}, {2,0,3}, {7,0,9}, {0,0,3},
1492 {3,0,0}, {0,1,9}, {5,1,9}, {2,1,9},
1493 {7,0,9}, {3,1,9}, {1,1,9}, {5,1,9},
1494 {2,1,9}, {4,0,9}, {4,1,9}, {-1,-1,-1},
1495 {-1,-1,-1}, {5,1,9}, {-1,-1,-1}, {3,1,9},
1496 {4,1,9}, {6,1,9}, {2,1,9}, {1,1,9},
1497 {5,1,9}, {7,1,9}, {3,0,0}, {4,0,0},
1498 {6,1,9}, {-1,-1,-1}, {0,0,2}, {-1,-1,-1},
1499 /* hat #2 in metatile #9 (type F) does not exist */
1500 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1501 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1502 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1503 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1504 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1505 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1506 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1507 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1508 /* hat #3 in metatile #9 (type F) does not exist */
1509 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1510 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1511 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1512 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1513 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1514 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1515 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1516 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1517 /* hat #0 in metatile #10 (type F) */
1518 {1,0,10}, {-1,-1,-1}, {0,0,9}, {-1,-1,-1},
1519 {-1,-1,-1}, {0,0,10}, {5,0,10}, {2,0,10},
1520 {0,0,9}, {3,0,10}, {1,0,10}, {5,0,10},
1521 {2,0,10}, {2,0,1}, {4,0,10}, {0,0,1},
1522 {3,1,10}, {5,0,10}, {0,0,1}, {3,0,10},
1523 {4,0,10}, {6,0,10}, {2,0,10}, {1,0,10},
1524 {5,0,10}, {7,0,10}, {-1,-1,-1}, {-1,-1,-1},
1525 {6,0,10}, {2,1,10}, {-1,-1,-1}, {0,1,10},
1526 /* hat #1 in metatile #10 (type F) */
1527 {1,1,10}, {-1,-1,-1}, {7,0,10}, {-1,-1,-1},
1528 {0,1,7}, {0,1,10}, {5,1,10}, {2,1,10},
1529 {7,0,10}, {3,1,10}, {1,1,10}, {5,1,10},
1530 {2,1,10}, {4,0,10}, {4,1,10}, {7,3,1},
1531 {3,2,1}, {5,1,10}, {7,3,1}, {3,1,10},
1532 {4,1,10}, {6,1,10}, {2,1,10}, {1,1,10},
1533 {5,1,10}, {7,1,10}, {0,1,7}, {7,0,7},
1534 {6,1,10}, {2,2,1}, {6,0,7}, {0,2,1},
1535 /* hat #2 in metatile #10 (type F) does not exist */
1536 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1537 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1538 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1539 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1540 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1541 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1542 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1543 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1544 /* hat #3 in metatile #10 (type F) does not exist */
1545 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1546 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1547 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1548 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1549 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1550 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1551 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1552 {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1}, {-1,-1,-1},
1553};
1554static const KitemapEntry *const kitemap[] = {
1555 kitemap_H,
1556 kitemap_T,
1557 kitemap_P,
1558 kitemap_F,
1559};
1560
1561static const MetamapEntry metamap_H[] = {
1562 /* 0, 0 -> */ {0, 0}, /* no alternatives */
1563 /* 1, 0 -> */ {1, 0}, /* no alternatives */
1564 /* 2, 0 -> */ {2, 0}, /* no alternatives */
1565 /* 3, 0 -> */ {3, 0}, /* no alternatives */
1566 /* 4, 0 -> */ {3, 4},
1567 /* 5, 0 -> */ {2, 8},
1568 /* 6, 0 -> */ {3, 3},
1569 /* 7, 0 -> */ {8, 4},
1570 /* 8, 0 -> */ {9, 8},
1571 /* 9, 0 -> */ {9, 4},
1572 /* 10, 0 -> */ {8, 1},
1573 /* 11, 0 -> */ {10, 5},
1574 /* 12, 0 -> */ {5, 3},
1575 /* 0, 1 -> */ {0, 1}, /* no alternatives */
1576 /* 1, 1 -> */ {1, 1}, /* no alternatives */
1577 /* 2, 1 -> */ {2, 1}, /* no alternatives */
1578 /* 3, 1 -> */ {3, 1}, /* no alternatives */
1579 /* 4, 1 -> */ {2, 9},
1580 /* 5, 1 -> */ {2, 3},
1581 /* 6, 1 -> */ {2, 6},
1582 /* 7, 1 -> */ {10, 4},
1583 /* 8, 1 -> */ {6, 3},
1584 /* 9, 1 -> */ {9, 9},
1585 /* 10, 1 -> */ {6, 10},
1586 /* 11, 1 -> */ {4, 3},
1587 /* 12, 1 -> */ {6, 6},
1588 /* 0, 2 -> */ {0, 2}, /* no alternatives */
1589 /* 1, 2 -> */ {1, 2}, /* no alternatives */
1590 /* 2, 2 -> */ {2, 2}, /* no alternatives */
1591 /* 3, 2 -> */ {3, 2}, /* no alternatives */
1592 /* 4, 2 -> */ {3, 5},
1593 /* 5, 2 -> */ {2, 12},
1594 /* 6, 2 -> */ {1, 3},
1595 /* 7, 2 -> */ {8, 5},
1596 /* 8, 2 -> */ {9, 12},
1597 /* 9, 2 -> */ {9, 5},
1598 /* 10, 2 -> */ {12, 0},
1599 /* 11, 2 -> */ {5, 6},
1600 /* 12, 2 -> */ {11, 1},
1601 /* 0, 3 -> */ {0, 3}, /* no alternatives */
1602 /* 1, 3 -> */ {6, 2},
1603 /* 2, 3 -> */ {5, 1},
1604 /* 3, 3 -> */ {6, 0},
1605 /* 4, 3 -> */ {12, 2},
1606 /* 5, 3 -> */ {10, 2},
1607 /* 6, 3 -> */ {10, 0},
1608 /* 7, 3 -> */ {-1,-1}, /* does not exist */
1609 /* 8, 3 -> */ {-1,-1}, /* does not exist */
1610 /* 9, 3 -> */ {-1,-1}, /* does not exist */
1611 /* 10, 3 -> */ {-1,-1}, /* does not exist */
1612 /* 11, 3 -> */ {-1,-1}, /* does not exist */
1613 /* 12, 3 -> */ {-1,-1}, /* does not exist */
1614 /* 0, 4 -> */ {0, 4}, /* no alternatives */
1615 /* 1, 4 -> */ {1, 4}, /* no alternatives */
1616 /* 2, 4 -> */ {2, 4}, /* no alternatives */
1617 /* 3, 4 -> */ {4, 0},
1618 /* 4, 4 -> */ {4, 4}, /* no alternatives */
1619 /* 5, 4 -> */ {5, 7},
1620 /* 6, 4 -> */ {6, 4}, /* no alternatives */
1621 /* 7, 4 -> */ {4, 9},
1622 /* 8, 4 -> */ {6, 7},
1623 /* 9, 4 -> */ {9, 0},
1624 /* 10, 4 -> */ {8, 9},
1625 /* 11, 4 -> */ {-1,-1}, /* does not exist */
1626 /* 12, 4 -> */ {-1,-1}, /* does not exist */
1627 /* 0, 5 -> */ {0, 5}, /* no alternatives */
1628 /* 1, 5 -> */ {1, 5}, /* no alternatives */
1629 /* 2, 5 -> */ {2, 5}, /* no alternatives */
1630 /* 3, 5 -> */ {4, 2},
1631 /* 4, 5 -> */ {4, 5}, /* no alternatives */
1632 /* 5, 5 -> */ {5, 11},
1633 /* 6, 5 -> */ {6, 5}, /* no alternatives */
1634 /* 7, 5 -> */ {4, 8},
1635 /* 8, 5 -> */ {6, 11},
1636 /* 9, 5 -> */ {9, 2},
1637 /* 10, 5 -> */ {8, 8},
1638 /* 11, 5 -> */ {-1,-1}, /* does not exist */
1639 /* 12, 5 -> */ {-1,-1}, /* does not exist */
1640 /* 0, 6 -> */ {0, 6}, /* no alternatives */
1641 /* 1, 6 -> */ {1, 6}, /* no alternatives */
1642 /* 2, 6 -> */ {6, 1},
1643 /* 3, 6 -> */ {3, 6}, /* no alternatives */
1644 /* 4, 6 -> */ {4, 6}, /* no alternatives */
1645 /* 5, 6 -> */ {8, 12},
1646 /* 6, 6 -> */ {12, 1},
1647 /* 7, 6 -> */ {10, 1},
1648 /* 8, 6 -> */ {4, 12},
1649 /* 9, 6 -> */ {9, 6}, /* no alternatives */
1650 /* 10, 6 -> */ {5, 10},
1651 /* 11, 6 -> */ {-1,-1}, /* does not exist */
1652 /* 12, 6 -> */ {-1,-1}, /* does not exist */
1653 /* 0, 7 -> */ {0, 7}, /* no alternatives */
1654 /* 1, 7 -> */ {1, 7}, /* no alternatives */
1655 /* 2, 7 -> */ {2, 7}, /* no alternatives */
1656 /* 3, 7 -> */ {3, 7}, /* no alternatives */
1657 /* 4, 7 -> */ {4, 7}, /* no alternatives */
1658 /* 5, 7 -> */ {5, 4},
1659 /* 6, 7 -> */ {7, 0},
1660 /* 7, 7 -> */ {10, 8},
1661 /* 8, 7 -> */ {8, 7}, /* no alternatives */
1662 /* 9, 7 -> */ {9, 7}, /* no alternatives */
1663 /* 10, 7 -> */ {10, 7}, /* no alternatives */
1664 /* 11, 7 -> */ {-1,-1}, /* does not exist */
1665 /* 12, 7 -> */ {-1,-1}, /* does not exist */
1666 /* 0, 8 -> */ {0, 8}, /* no alternatives */
1667 /* 1, 8 -> */ {1, 8}, /* no alternatives */
1668 /* 2, 8 -> */ {5, 0},
1669 /* 3, 8 -> */ {3, 8}, /* no alternatives */
1670 /* 4, 8 -> */ {7, 5},
1671 /* 5, 8 -> */ {5, 8}, /* no alternatives */
1672 /* 6, 8 -> */ {6, 8}, /* no alternatives */
1673 /* 7, 8 -> */ {7, 8}, /* no alternatives */
1674 /* 8, 8 -> */ {11, 0},
1675 /* 9, 8 -> */ {8, 0},
1676 /* 10, 8 -> */ {7, 7},
1677 /* 11, 8 -> */ {-1,-1}, /* does not exist */
1678 /* 12, 8 -> */ {-1,-1}, /* does not exist */
1679 /* 0, 9 -> */ {0, 9}, /* no alternatives */
1680 /* 1, 9 -> */ {1, 9}, /* no alternatives */
1681 /* 2, 9 -> */ {4, 1},
1682 /* 3, 9 -> */ {3, 9}, /* no alternatives */
1683 /* 4, 9 -> */ {7, 4},
1684 /* 5, 9 -> */ {5, 9}, /* no alternatives */
1685 /* 6, 9 -> */ {6, 9}, /* no alternatives */
1686 /* 7, 9 -> */ {7, 9}, /* no alternatives */
1687 /* 8, 9 -> */ {7, 1},
1688 /* 9, 9 -> */ {9, 1},
1689 /* 10, 9 -> */ {7, 10},
1690 /* 11, 9 -> */ {-1,-1}, /* does not exist */
1691 /* 12, 9 -> */ {-1,-1}, /* does not exist */
1692 /* 0, 10 -> */ {0, 10}, /* no alternatives */
1693 /* 1, 10 -> */ {1, 10}, /* no alternatives */
1694 /* 2, 10 -> */ {2, 10}, /* no alternatives */
1695 /* 3, 10 -> */ {3, 10}, /* no alternatives */
1696 /* 4, 10 -> */ {4, 10}, /* no alternatives */
1697 /* 5, 10 -> */ {10, 6},
1698 /* 6, 10 -> */ {7, 6},
1699 /* 7, 10 -> */ {10, 9},
1700 /* 8, 10 -> */ {8, 10}, /* no alternatives */
1701 /* 9, 10 -> */ {9, 10}, /* no alternatives */
1702 /* 10, 10 -> */ {10, 10}, /* no alternatives */
1703 /* 11, 10 -> */ {-1,-1}, /* does not exist */
1704 /* 12, 10 -> */ {-1,-1}, /* does not exist */
1705 /* 0, 11 -> */ {0, 11}, /* no alternatives */
1706 /* 1, 11 -> */ {1, 11}, /* no alternatives */
1707 /* 2, 11 -> */ {2, 11}, /* no alternatives */
1708 /* 3, 11 -> */ {3, 11}, /* no alternatives */
1709 /* 4, 11 -> */ {4, 11}, /* no alternatives */
1710 /* 5, 11 -> */ {5, 5},
1711 /* 6, 11 -> */ {7, 2},
1712 /* 7, 11 -> */ {10, 12},
1713 /* 8, 11 -> */ {8, 11}, /* no alternatives */
1714 /* 9, 11 -> */ {9, 11}, /* no alternatives */
1715 /* 10, 11 -> */ {10, 11}, /* no alternatives */
1716 /* 11, 11 -> */ {-1,-1}, /* does not exist */
1717 /* 12, 11 -> */ {-1,-1}, /* does not exist */
1718 /* 0, 12 -> */ {0, 12}, /* no alternatives */
1719 /* 1, 12 -> */ {1, 12}, /* no alternatives */
1720 /* 2, 12 -> */ {5, 2},
1721 /* 3, 12 -> */ {3, 12}, /* no alternatives */
1722 /* 4, 12 -> */ {8, 6},
1723 /* 5, 12 -> */ {5, 12}, /* no alternatives */
1724 /* 6, 12 -> */ {6, 12}, /* no alternatives */
1725 /* 7, 12 -> */ {7, 12}, /* no alternatives */
1726 /* 8, 12 -> */ {11, 2},
1727 /* 9, 12 -> */ {8, 2},
1728 /* 10, 12 -> */ {7, 11},
1729 /* 11, 12 -> */ {-1,-1}, /* does not exist */
1730 /* 12, 12 -> */ {-1,-1}, /* does not exist */
1731};
1732static const MetamapEntry metamap_T[] = {
1733 /* 0, 0 -> */ {0, 0}, /* no alternatives */
1734 /* 1, 0 -> */ {1, 0}, /* no alternatives */
1735 /* 2, 0 -> */ {2, 0}, /* no alternatives */
1736 /* 3, 0 -> */ {3, 0}, /* no alternatives */
1737 /* 4, 0 -> */ {3, 3},
1738 /* 5, 0 -> */ {3, 1},
1739 /* 6, 0 -> */ {2, 2},
1740 /* 7, 0 -> */ {8, 3},
1741 /* 8, 0 -> */ {9, 1},
1742 /* 9, 0 -> */ {9, 3},
1743 /* 10, 0 -> */ {4, 6},
1744 /* 11, 0 -> */ {4, 4},
1745 /* 12, 0 -> */ {6, 2},
1746 /* 0, 1 -> */ {0, 1}, /* no alternatives */
1747 /* 1, 1 -> */ {1, 1}, /* no alternatives */
1748 /* 2, 1 -> */ {2, 1}, /* no alternatives */
1749 /* 3, 1 -> */ {5, 0},
1750 /* 4, 1 -> */ {4, 1}, /* no alternatives */
1751 /* 5, 1 -> */ {8, 4},
1752 /* 6, 1 -> */ {6, 1}, /* no alternatives */
1753 /* 7, 1 -> */ {6, 5},
1754 /* 8, 1 -> */ {11, 0},
1755 /* 9, 1 -> */ {8, 0},
1756 /* 10, 1 -> */ {5, 5},
1757 /* 11, 1 -> */ {-1,-1}, /* does not exist */
1758 /* 12, 1 -> */ {-1,-1}, /* does not exist */
1759 /* 0, 2 -> */ {0, 2}, /* no alternatives */
1760 /* 1, 2 -> */ {1, 2}, /* no alternatives */
1761 /* 2, 2 -> */ {6, 0},
1762 /* 3, 2 -> */ {3, 2}, /* no alternatives */
1763 /* 4, 2 -> */ {4, 2}, /* no alternatives */
1764 /* 5, 2 -> */ {5, 4},
1765 /* 6, 2 -> */ {12, 0},
1766 /* 7, 2 -> */ {10, 0},
1767 /* 8, 2 -> */ {6, 4},
1768 /* 9, 2 -> */ {9, 2}, /* no alternatives */
1769 /* 10, 2 -> */ {8, 6},
1770 /* 11, 2 -> */ {-1,-1}, /* does not exist */
1771 /* 12, 2 -> */ {-1,-1}, /* does not exist */
1772 /* 0, 3 -> */ {0, 3}, /* no alternatives */
1773 /* 1, 3 -> */ {1, 3}, /* no alternatives */
1774 /* 2, 3 -> */ {2, 3}, /* no alternatives */
1775 /* 3, 3 -> */ {4, 0},
1776 /* 4, 3 -> */ {4, 3}, /* no alternatives */
1777 /* 5, 3 -> */ {8, 5},
1778 /* 6, 3 -> */ {6, 3}, /* no alternatives */
1779 /* 7, 3 -> */ {6, 6},
1780 /* 8, 3 -> */ {4, 5},
1781 /* 9, 3 -> */ {9, 0},
1782 /* 10, 3 -> */ {5, 6},
1783 /* 11, 3 -> */ {-1,-1}, /* does not exist */
1784 /* 12, 3 -> */ {-1,-1}, /* does not exist */
1785 /* 0, 4 -> */ {0, 4}, /* no alternatives */
1786 /* 1, 4 -> */ {1, 4}, /* no alternatives */
1787 /* 2, 4 -> */ {2, 4}, /* no alternatives */
1788 /* 3, 4 -> */ {3, 4}, /* no alternatives */
1789 /* 4, 4 -> */ {8, 1},
1790 /* 5, 4 -> */ {5, 2},
1791 /* 6, 4 -> */ {8, 2},
1792 /* 7, 4 -> */ {7, 4}, /* no alternatives */
1793 /* 8, 4 -> */ {5, 1},
1794 /* 9, 4 -> */ {9, 4}, /* no alternatives */
1795 /* 10, 4 -> */ {10, 4}, /* no alternatives */
1796 /* 11, 4 -> */ {-1,-1}, /* does not exist */
1797 /* 12, 4 -> */ {-1,-1}, /* does not exist */
1798 /* 0, 5 -> */ {0, 5}, /* no alternatives */
1799 /* 1, 5 -> */ {1, 5}, /* no alternatives */
1800 /* 2, 5 -> */ {2, 5}, /* no alternatives */
1801 /* 3, 5 -> */ {3, 5}, /* no alternatives */
1802 /* 4, 5 -> */ {7, 0},
1803 /* 5, 5 -> */ {10, 1},
1804 /* 6, 5 -> */ {7, 1},
1805 /* 7, 5 -> */ {7, 5}, /* no alternatives */
1806 /* 8, 5 -> */ {5, 3},
1807 /* 9, 5 -> */ {9, 5}, /* no alternatives */
1808 /* 10, 5 -> */ {10, 5}, /* no alternatives */
1809 /* 11, 5 -> */ {-1,-1}, /* does not exist */
1810 /* 12, 5 -> */ {-1,-1}, /* does not exist */
1811 /* 0, 6 -> */ {0, 6}, /* no alternatives */
1812 /* 1, 6 -> */ {1, 6}, /* no alternatives */
1813 /* 2, 6 -> */ {2, 6}, /* no alternatives */
1814 /* 3, 6 -> */ {3, 6}, /* no alternatives */
1815 /* 4, 6 -> */ {7, 2},
1816 /* 5, 6 -> */ {10, 3},
1817 /* 6, 6 -> */ {7, 3},
1818 /* 7, 6 -> */ {7, 6}, /* no alternatives */
1819 /* 8, 6 -> */ {10, 2},
1820 /* 9, 6 -> */ {9, 6}, /* no alternatives */
1821 /* 10, 6 -> */ {10, 6}, /* no alternatives */
1822 /* 11, 6 -> */ {-1,-1}, /* does not exist */
1823 /* 12, 6 -> */ {-1,-1}, /* does not exist */
1824};
1825static const MetamapEntry metamap_P[] = {
1826 /* 0, 0 -> */ {0, 0}, /* no alternatives */
1827 /* 1, 0 -> */ {1, 0}, /* no alternatives */
1828 /* 2, 0 -> */ {2, 0}, /* no alternatives */
1829 /* 3, 0 -> */ {3, 0}, /* no alternatives */
1830 /* 4, 0 -> */ {3, 4},
1831 /* 5, 0 -> */ {2, 5},
1832 /* 6, 0 -> */ {2, 3},
1833 /* 7, 0 -> */ {8, 4},
1834 /* 8, 0 -> */ {9, 5},
1835 /* 9, 0 -> */ {9, 4},
1836 /* 10, 0 -> */ {4, 9},
1837 /* 11, 0 -> */ {4, 8},
1838 /* 12, 0 -> */ {6, 3},
1839 /* 0, 1 -> */ {0, 1}, /* no alternatives */
1840 /* 1, 1 -> */ {1, 1}, /* no alternatives */
1841 /* 2, 1 -> */ {2, 1}, /* no alternatives */
1842 /* 3, 1 -> */ {3, 1}, /* no alternatives */
1843 /* 4, 1 -> */ {2, 10},
1844 /* 5, 1 -> */ {3, 2},
1845 /* 6, 1 -> */ {2, 4},
1846 /* 7, 1 -> */ {8, 10},
1847 /* 8, 1 -> */ {9, 2},
1848 /* 9, 1 -> */ {9, 10},
1849 /* 10, 1 -> */ {7, 4},
1850 /* 11, 1 -> */ {8, 2},
1851 /* 12, 1 -> */ {6, 4},
1852 /* 0, 2 -> */ {0, 2}, /* no alternatives */
1853 /* 1, 2 -> */ {1, 2}, /* no alternatives */
1854 /* 2, 2 -> */ {2, 2}, /* no alternatives */
1855 /* 3, 2 -> */ {5, 1},
1856 /* 4, 2 -> */ {4, 2}, /* no alternatives */
1857 /* 5, 2 -> */ {8, 6},
1858 /* 6, 2 -> */ {6, 2}, /* no alternatives */
1859 /* 7, 2 -> */ {6, 7},
1860 /* 8, 2 -> */ {4, 6},
1861 /* 9, 2 -> */ {8, 1},
1862 /* 10, 2 -> */ {5, 7},
1863 /* 11, 2 -> */ {-1,-1}, /* does not exist */
1864 /* 12, 2 -> */ {-1,-1}, /* does not exist */
1865 /* 0, 3 -> */ {0, 3}, /* no alternatives */
1866 /* 1, 3 -> */ {1, 3}, /* no alternatives */
1867 /* 2, 3 -> */ {6, 0},
1868 /* 3, 3 -> */ {3, 3}, /* no alternatives */
1869 /* 4, 3 -> */ {4, 3}, /* no alternatives */
1870 /* 5, 3 -> */ {5, 8},
1871 /* 6, 3 -> */ {12, 0},
1872 /* 7, 3 -> */ {10, 0},
1873 /* 8, 3 -> */ {6, 8},
1874 /* 9, 3 -> */ {9, 3}, /* no alternatives */
1875 /* 10, 3 -> */ {8, 9},
1876 /* 11, 3 -> */ {-1,-1}, /* does not exist */
1877 /* 12, 3 -> */ {-1,-1}, /* does not exist */
1878 /* 0, 4 -> */ {0, 4}, /* no alternatives */
1879 /* 1, 4 -> */ {1, 4}, /* no alternatives */
1880 /* 2, 4 -> */ {6, 1},
1881 /* 3, 4 -> */ {4, 0},
1882 /* 4, 4 -> */ {4, 4}, /* no alternatives */
1883 /* 5, 4 -> */ {5, 6},
1884 /* 6, 4 -> */ {12, 1},
1885 /* 7, 4 -> */ {6, 9},
1886 /* 8, 4 -> */ {6, 6},
1887 /* 9, 4 -> */ {9, 0},
1888 /* 10, 4 -> */ {5, 9},
1889 /* 11, 4 -> */ {-1,-1}, /* does not exist */
1890 /* 12, 4 -> */ {-1,-1}, /* does not exist */
1891 /* 0, 5 -> */ {0, 5}, /* no alternatives */
1892 /* 1, 5 -> */ {1, 5}, /* no alternatives */
1893 /* 2, 5 -> */ {5, 0},
1894 /* 3, 5 -> */ {3, 5}, /* no alternatives */
1895 /* 4, 5 -> */ {8, 8},
1896 /* 5, 5 -> */ {5, 5}, /* no alternatives */
1897 /* 6, 5 -> */ {6, 5}, /* no alternatives */
1898 /* 7, 5 -> */ {7, 5}, /* no alternatives */
1899 /* 8, 5 -> */ {11, 0},
1900 /* 9, 5 -> */ {8, 0},
1901 /* 10, 5 -> */ {7, 6},
1902 /* 11, 5 -> */ {-1,-1}, /* does not exist */
1903 /* 12, 5 -> */ {-1,-1}, /* does not exist */
1904 /* 0, 6 -> */ {0, 6}, /* no alternatives */
1905 /* 1, 6 -> */ {1, 6}, /* no alternatives */
1906 /* 2, 6 -> */ {2, 6}, /* no alternatives */
1907 /* 3, 6 -> */ {3, 6}, /* no alternatives */
1908 /* 4, 6 -> */ {11, 1},
1909 /* 5, 6 -> */ {5, 4},
1910 /* 6, 6 -> */ {7, 0},
1911 /* 7, 6 -> */ {10, 5},
1912 /* 8, 6 -> */ {5, 2},
1913 /* 9, 6 -> */ {9, 6}, /* no alternatives */
1914 /* 10, 6 -> */ {10, 6}, /* no alternatives */
1915 /* 11, 6 -> */ {-1,-1}, /* does not exist */
1916 /* 12, 6 -> */ {-1,-1}, /* does not exist */
1917 /* 0, 7 -> */ {0, 7}, /* no alternatives */
1918 /* 1, 7 -> */ {1, 7}, /* no alternatives */
1919 /* 2, 7 -> */ {2, 7}, /* no alternatives */
1920 /* 3, 7 -> */ {3, 7}, /* no alternatives */
1921 /* 4, 7 -> */ {7, 1},
1922 /* 5, 7 -> */ {10, 2},
1923 /* 6, 7 -> */ {7, 2},
1924 /* 7, 7 -> */ {7, 7}, /* no alternatives */
1925 /* 8, 7 -> */ {4, 10},
1926 /* 9, 7 -> */ {9, 7}, /* no alternatives */
1927 /* 10, 7 -> */ {10, 7}, /* no alternatives */
1928 /* 11, 7 -> */ {-1,-1}, /* does not exist */
1929 /* 12, 7 -> */ {-1,-1}, /* does not exist */
1930 /* 0, 8 -> */ {0, 8}, /* no alternatives */
1931 /* 1, 8 -> */ {1, 8}, /* no alternatives */
1932 /* 2, 8 -> */ {2, 8}, /* no alternatives */
1933 /* 3, 8 -> */ {3, 8}, /* no alternatives */
1934 /* 4, 8 -> */ {8, 5},
1935 /* 5, 8 -> */ {5, 3},
1936 /* 6, 8 -> */ {8, 3},
1937 /* 7, 8 -> */ {7, 8}, /* no alternatives */
1938 /* 8, 8 -> */ {4, 5},
1939 /* 9, 8 -> */ {9, 8}, /* no alternatives */
1940 /* 10, 8 -> */ {10, 8}, /* no alternatives */
1941 /* 11, 8 -> */ {-1,-1}, /* does not exist */
1942 /* 12, 8 -> */ {-1,-1}, /* does not exist */
1943 /* 0, 9 -> */ {0, 9}, /* no alternatives */
1944 /* 1, 9 -> */ {1, 9}, /* no alternatives */
1945 /* 2, 9 -> */ {2, 9}, /* no alternatives */
1946 /* 3, 9 -> */ {3, 9}, /* no alternatives */
1947 /* 4, 9 -> */ {7, 3},
1948 /* 5, 9 -> */ {10, 4},
1949 /* 6, 9 -> */ {10, 1},
1950 /* 7, 9 -> */ {10, 10},
1951 /* 8, 9 -> */ {10, 3},
1952 /* 9, 9 -> */ {9, 9}, /* no alternatives */
1953 /* 10, 9 -> */ {10, 9}, /* no alternatives */
1954 /* 11, 9 -> */ {-1,-1}, /* does not exist */
1955 /* 12, 9 -> */ {-1,-1}, /* does not exist */
1956 /* 0, 10 -> */ {0, 10}, /* no alternatives */
1957 /* 1, 10 -> */ {1, 10}, /* no alternatives */
1958 /* 2, 10 -> */ {4, 1},
1959 /* 3, 10 -> */ {3, 10}, /* no alternatives */
1960 /* 4, 10 -> */ {8, 7},
1961 /* 5, 10 -> */ {5, 10}, /* no alternatives */
1962 /* 6, 10 -> */ {6, 10}, /* no alternatives */
1963 /* 7, 10 -> */ {7, 10}, /* no alternatives */
1964 /* 8, 10 -> */ {4, 7},
1965 /* 9, 10 -> */ {9, 1},
1966 /* 10, 10 -> */ {7, 9},
1967 /* 11, 10 -> */ {-1,-1}, /* does not exist */
1968 /* 12, 10 -> */ {-1,-1}, /* does not exist */
1969};
1970static const MetamapEntry metamap_F[] = {
1971 /* 0, 0 -> */ {0, 0}, /* no alternatives */
1972 /* 1, 0 -> */ {1, 0}, /* no alternatives */
1973 /* 2, 0 -> */ {2, 0}, /* no alternatives */
1974 /* 3, 0 -> */ {3, 0}, /* no alternatives */
1975 /* 4, 0 -> */ {3, 3},
1976 /* 5, 0 -> */ {2, 4},
1977 /* 6, 0 -> */ {2, 2},
1978 /* 7, 0 -> */ {8, 3},
1979 /* 8, 0 -> */ {9, 4},
1980 /* 9, 0 -> */ {9, 3},
1981 /* 10, 0 -> */ {4, 9},
1982 /* 11, 0 -> */ {4, 8},
1983 /* 12, 0 -> */ {6, 2},
1984 /* 0, 1 -> */ {0, 1}, /* no alternatives */
1985 /* 1, 1 -> */ {1, 1}, /* no alternatives */
1986 /* 2, 1 -> */ {2, 1}, /* no alternatives */
1987 /* 3, 1 -> */ {3, 1}, /* no alternatives */
1988 /* 4, 1 -> */ {2, 10},
1989 /* 5, 1 -> */ {2, 6},
1990 /* 6, 1 -> */ {2, 3},
1991 /* 7, 1 -> */ {8, 10},
1992 /* 8, 1 -> */ {9, 6},
1993 /* 9, 1 -> */ {9, 10},
1994 /* 10, 1 -> */ {7, 3},
1995 /* 11, 1 -> */ {8, 6},
1996 /* 12, 1 -> */ {6, 3},
1997 /* 0, 2 -> */ {0, 2}, /* no alternatives */
1998 /* 1, 2 -> */ {1, 2}, /* no alternatives */
1999 /* 2, 2 -> */ {6, 0},
2000 /* 3, 2 -> */ {3, 2}, /* no alternatives */
2001 /* 4, 2 -> */ {4, 2}, /* no alternatives */
2002 /* 5, 2 -> */ {5, 8},
2003 /* 6, 2 -> */ {12, 0},
2004 /* 7, 2 -> */ {10, 0},
2005 /* 8, 2 -> */ {6, 8},
2006 /* 9, 2 -> */ {9, 2}, /* no alternatives */
2007 /* 10, 2 -> */ {8, 9},
2008 /* 11, 2 -> */ {-1,-1}, /* does not exist */
2009 /* 12, 2 -> */ {-1,-1}, /* does not exist */
2010 /* 0, 3 -> */ {0, 3}, /* no alternatives */
2011 /* 1, 3 -> */ {1, 3}, /* no alternatives */
2012 /* 2, 3 -> */ {6, 1},
2013 /* 3, 3 -> */ {4, 0},
2014 /* 4, 3 -> */ {4, 3}, /* no alternatives */
2015 /* 5, 3 -> */ {5, 5},
2016 /* 6, 3 -> */ {12, 1},
2017 /* 7, 3 -> */ {6, 9},
2018 /* 8, 3 -> */ {6, 5},
2019 /* 9, 3 -> */ {9, 0},
2020 /* 10, 3 -> */ {5, 9},
2021 /* 11, 3 -> */ {-1,-1}, /* does not exist */
2022 /* 12, 3 -> */ {-1,-1}, /* does not exist */
2023 /* 0, 4 -> */ {0, 4}, /* no alternatives */
2024 /* 1, 4 -> */ {1, 4}, /* no alternatives */
2025 /* 2, 4 -> */ {5, 0},
2026 /* 3, 4 -> */ {3, 4}, /* no alternatives */
2027 /* 4, 4 -> */ {8, 8},
2028 /* 5, 4 -> */ {5, 4}, /* no alternatives */
2029 /* 6, 4 -> */ {6, 4}, /* no alternatives */
2030 /* 7, 4 -> */ {7, 4}, /* no alternatives */
2031 /* 8, 4 -> */ {11, 0},
2032 /* 9, 4 -> */ {8, 0},
2033 /* 10, 4 -> */ {7, 5},
2034 /* 11, 4 -> */ {-1,-1}, /* does not exist */
2035 /* 12, 4 -> */ {-1,-1}, /* does not exist */
2036 /* 0, 5 -> */ {0, 5}, /* no alternatives */
2037 /* 1, 5 -> */ {1, 5}, /* no alternatives */
2038 /* 2, 5 -> */ {2, 5}, /* no alternatives */
2039 /* 3, 5 -> */ {3, 5}, /* no alternatives */
2040 /* 4, 5 -> */ {11, 1},
2041 /* 5, 5 -> */ {5, 3},
2042 /* 6, 5 -> */ {7, 0},
2043 /* 7, 5 -> */ {10, 4},
2044 /* 8, 5 -> */ {4, 6},
2045 /* 9, 5 -> */ {9, 5}, /* no alternatives */
2046 /* 10, 5 -> */ {10, 5}, /* no alternatives */
2047 /* 11, 5 -> */ {-1,-1}, /* does not exist */
2048 /* 12, 5 -> */ {-1,-1}, /* does not exist */
2049 /* 0, 6 -> */ {0, 6}, /* no alternatives */
2050 /* 1, 6 -> */ {1, 6}, /* no alternatives */
2051 /* 2, 6 -> */ {5, 1},
2052 /* 3, 6 -> */ {3, 6}, /* no alternatives */
2053 /* 4, 6 -> */ {8, 5},
2054 /* 5, 6 -> */ {5, 6}, /* no alternatives */
2055 /* 6, 6 -> */ {6, 6}, /* no alternatives */
2056 /* 7, 6 -> */ {7, 6}, /* no alternatives */
2057 /* 8, 6 -> */ {4, 5},
2058 /* 9, 6 -> */ {8, 1},
2059 /* 10, 6 -> */ {7, 7},
2060 /* 11, 6 -> */ {-1,-1}, /* does not exist */
2061 /* 12, 6 -> */ {-1,-1}, /* does not exist */
2062 /* 0, 7 -> */ {0, 7}, /* no alternatives */
2063 /* 1, 7 -> */ {1, 7}, /* no alternatives */
2064 /* 2, 7 -> */ {2, 7}, /* no alternatives */
2065 /* 3, 7 -> */ {3, 7}, /* no alternatives */
2066 /* 4, 7 -> */ {4, 7}, /* no alternatives */
2067 /* 5, 7 -> */ {4, 10},
2068 /* 6, 7 -> */ {7, 1},
2069 /* 7, 7 -> */ {10, 6},
2070 /* 8, 7 -> */ {8, 7}, /* no alternatives */
2071 /* 9, 7 -> */ {9, 7}, /* no alternatives */
2072 /* 10, 7 -> */ {10, 7}, /* no alternatives */
2073 /* 11, 7 -> */ {-1,-1}, /* does not exist */
2074 /* 12, 7 -> */ {-1,-1}, /* does not exist */
2075 /* 0, 8 -> */ {0, 8}, /* no alternatives */
2076 /* 1, 8 -> */ {1, 8}, /* no alternatives */
2077 /* 2, 8 -> */ {2, 8}, /* no alternatives */
2078 /* 3, 8 -> */ {3, 8}, /* no alternatives */
2079 /* 4, 8 -> */ {8, 4},
2080 /* 5, 8 -> */ {5, 2},
2081 /* 6, 8 -> */ {8, 2},
2082 /* 7, 8 -> */ {7, 8}, /* no alternatives */
2083 /* 8, 8 -> */ {4, 4},
2084 /* 9, 8 -> */ {9, 8}, /* no alternatives */
2085 /* 10, 8 -> */ {10, 8}, /* no alternatives */
2086 /* 11, 8 -> */ {-1,-1}, /* does not exist */
2087 /* 12, 8 -> */ {-1,-1}, /* does not exist */
2088 /* 0, 9 -> */ {0, 9}, /* no alternatives */
2089 /* 1, 9 -> */ {1, 9}, /* no alternatives */
2090 /* 2, 9 -> */ {2, 9}, /* no alternatives */
2091 /* 3, 9 -> */ {3, 9}, /* no alternatives */
2092 /* 4, 9 -> */ {7, 2},
2093 /* 5, 9 -> */ {10, 3},
2094 /* 6, 9 -> */ {10, 1},
2095 /* 7, 9 -> */ {10, 10},
2096 /* 8, 9 -> */ {10, 2},
2097 /* 9, 9 -> */ {9, 9}, /* no alternatives */
2098 /* 10, 9 -> */ {10, 9}, /* no alternatives */
2099 /* 11, 9 -> */ {-1,-1}, /* does not exist */
2100 /* 12, 9 -> */ {-1,-1}, /* does not exist */
2101 /* 0, 10 -> */ {0, 10}, /* no alternatives */
2102 /* 1, 10 -> */ {1, 10}, /* no alternatives */
2103 /* 2, 10 -> */ {4, 1},
2104 /* 3, 10 -> */ {3, 10}, /* no alternatives */
2105 /* 4, 10 -> */ {5, 7},
2106 /* 5, 10 -> */ {5, 10}, /* no alternatives */
2107 /* 6, 10 -> */ {6, 10}, /* no alternatives */
2108 /* 7, 10 -> */ {7, 10}, /* no alternatives */
2109 /* 8, 10 -> */ {6, 7},
2110 /* 9, 10 -> */ {9, 1},
2111 /* 10, 10 -> */ {7, 9},
2112 /* 11, 10 -> */ {-1,-1}, /* does not exist */
2113 /* 12, 10 -> */ {-1,-1}, /* does not exist */
2114};
2115static const MetamapEntry *const metamap[] = {
2116 metamap_H,
2117 metamap_T,
2118 metamap_P,
2119 metamap_F,
2120};
diff --git a/apps/plugins/puzzles/src/hat.c b/apps/plugins/puzzles/src/hat.c
new file mode 100644
index 0000000000..176d1f049e
--- /dev/null
+++ b/apps/plugins/puzzles/src/hat.c
@@ -0,0 +1,891 @@
1/*
2 * Code to generate patches of the aperiodic 'hat' tiling discovered
3 * in 2023.
4 *
5 * This uses the 'combinatorial coordinates' system documented in my
6 * public article
7 * https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/
8 *
9 * The internal document auxiliary/doc/hats.html also contains an
10 * explanation of the basic ideas of this algorithm (less polished but
11 * containing more detail).
12 *
13 * Neither of those documents can really be put in a source file,
14 * because they just have too many complicated diagrams. So read at
15 * least one of those first; the comments in here will refer to it.
16 *
17 * Discoverers' website: https://cs.uwaterloo.ca/~csk/hat/
18 * Preprint of paper: https://arxiv.org/abs/2303.10798
19 */
20
21#include <assert.h>
22#ifdef NO_TGMATH_H
23# include <math.h>
24#else
25# include <tgmath.h>
26#endif
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "puzzles.h"
33#include "hat.h"
34#include "hat-internal.h"
35
36void hat_kiteenum_first(KiteEnum *s, int w, int h)
37{
38 Kite start = { {0,0}, {0, 3}, {3, 0}, {2, 2} };
39 size_t i;
40
41 for (i = 0; i < KE_NKEEP; i++)
42 s->recent[i] = start; /* initialise to *something* */
43 s->curr_index = 0;
44 s->curr = &s->recent[s->curr_index];
45 s->state = 1;
46 s->w = w;
47 s->h = h;
48 s->x = 0;
49 s->y = 0;
50}
51
52bool hat_kiteenum_next(KiteEnum *s)
53{
54 unsigned lastbut1 = s->last_index;
55 s->last_index = s->curr_index;
56 s->curr_index = (s->curr_index + 1) % KE_NKEEP;
57 s->curr = &s->recent[s->curr_index];
58
59 switch (s->state) {
60 /* States 1,2,3 walk rightwards along the upper side of a
61 * horizontal grid line with a pointy kite end at the start
62 * point */
63 case 1:
64 s->last_step = KS_F_RIGHT;
65 s->state = 2;
66 break;
67
68 case 2:
69 if (s->x+1 >= s->w) {
70 s->last_step = KS_F_RIGHT;
71 s->state = 4;
72 break;
73 }
74 s->last_step = KS_RIGHT;
75 s->state = 3;
76 s->x++;
77 break;
78
79 case 3:
80 s->last_step = KS_RIGHT;
81 s->state = 1;
82 break;
83
84 /* State 4 is special: we've just moved up into a row below a
85 * grid line, but we can't produce the rightmost tile of that
86 * row because it's not adjacent any tile so far emitted. So
87 * instead, emit the second-rightmost tile, and next time,
88 * we'll emit the rightmost. */
89 case 4:
90 s->last_step = KS_LEFT;
91 s->state = 5;
92 break;
93
94 /* And now we have to emit the third-rightmost tile relative
95 * to the last but one tile we emitted (the one from state 2,
96 * not state 4). */
97 case 5:
98 s->last_step = KS_RIGHT;
99 s->last_index = lastbut1;
100 s->state = 6;
101 break;
102
103 /* Now states 6-8 handle the general case of walking leftwards
104 * along the lower side of a line, starting from a
105 * right-angled kite end. */
106 case 6:
107 if (s->x <= 0) {
108 if (s->y+1 >= s->h) {
109 s->state = 0;
110 return false;
111 }
112 s->last_step = KS_RIGHT;
113 s->state = 9;
114 s->y++;
115 break;
116 }
117 s->last_step = KS_F_RIGHT;
118 s->state = 7;
119 s->x--;
120 break;
121
122 case 7:
123 s->last_step = KS_RIGHT;
124 s->state = 8;
125 break;
126
127 case 8:
128 s->last_step = KS_RIGHT;
129 s->state = 6;
130 break;
131
132 /* States 9,10,11 walk rightwards along the upper side of a
133 * horizontal grid line with a right-angled kite end at the
134 * start point. This time there's no awkward transition from
135 * the previous row. */
136 case 9:
137 s->last_step = KS_RIGHT;
138 s->state = 10;
139 break;
140
141 case 10:
142 s->last_step = KS_RIGHT;
143 s->state = 11;
144 break;
145
146 case 11:
147 if (s->x+1 >= s->w) {
148 /* Another awkward transition to the next row, where we
149 * have to generate it based on the previous state-9 tile.
150 * But this time at least we generate the rightmost tile
151 * of the new row, so the next states will be simple. */
152 s->last_step = KS_F_RIGHT;
153 s->last_index = lastbut1;
154 s->state = 12;
155 break;
156 }
157 s->last_step = KS_F_RIGHT;
158 s->state = 9;
159 s->x++;
160 break;
161
162 /* States 12,13,14 walk leftwards along the upper edge of a
163 * horizontal grid line with a pointy kite end at the start
164 * point */
165 case 12:
166 s->last_step = KS_F_RIGHT;
167 s->state = 13;
168 break;
169
170 case 13:
171 if (s->x <= 0) {
172 if (s->y+1 >= s->h) {
173 s->state = 0;
174 return false;
175 }
176 s->last_step = KS_LEFT;
177 s->state = 1;
178 s->y++;
179 break;
180 }
181 s->last_step = KS_RIGHT;
182 s->state = 14;
183 s->x--;
184 break;
185
186 case 14:
187 s->last_step = KS_RIGHT;
188 s->state = 12;
189 break;
190
191 default:
192 return false;
193 }
194
195 *s->curr = kite_step(s->recent[s->last_index], s->last_step);
196 return true;
197}
198
199/*
200 * The actual tables.
201 */
202#include "hat-tables.h"
203
204/*
205 * One set of tables that we write by hand: the permitted ways to
206 * extend the coordinate system outwards from a given metatile.
207 *
208 * One obvious approach would be to make a table of all the places
209 * each metatile can appear in the expansion of another (e.g. H can be
210 * subtile 0, 1 or 2 of another H, subtile 0 of a T, or 0 or 1 of a P
211 * or an F), and when we need to decide what our current topmost tile
212 * turns out to be a subtile of, choose equiprobably at random from
213 * those options.
214 *
215 * That's what I did originally, but a better approach is to skew the
216 * probabilities. We'd like to generate our patch of actual tiling
217 * uniformly at random, in the sense that if you selected uniformly
218 * from a very large region of the plane, the distribution of possible
219 * finite patches of tiling would converge to some limit as that
220 * region tended to infinity, and we'd be picking from that limiting
221 * distribution on finite patches.
222 *
223 * For this we have to refer back to the original paper, which
224 * indicates the subset of each metatile's expansion that can be
225 * considered to 'belong' to that metatile, such that every subtile
226 * belongs to exactly one parent metatile, and the overlaps are
227 * eliminated. Reading out the diagrams from their Figure 2.8:
228 *
229 * - H: we discard three of the outer F subtiles, in the symmetric
230 * positions index by our coordinates as 7, 10, 11. So we keep the
231 * remaining subtiles {0,1,2,3,4,5,6,8,9,12}, which consist of
232 * three H, one T, three P and three F.
233 *
234 * - T: only the central H expanded from a T is considered to belong
235 * to it, so we just keep {0}, a single H.
236 *
237 * - P: we discard everything intersected by a long edge of the
238 * parallelogram, leaving the central three tiles and the endmost
239 * pair of F. That is, we keep {0,1,4,5,10}, consisting of two H,
240 * one P and two F.
241 *
242 * - F: looks like P at one end, and we retain the corresponding set
243 * of tiles there, but at the other end we keep the two F on either
244 * side of the endmost one. So we keep {0,1,3,6,8,10}, consisting of
245 * two H, one P and _three_ F.
246 *
247 * Adding up the tile numbers gives us this matrix system:
248 *
249 * (H_1) (3 1 2 2)(H_0)
250 * (T_1) = (1 0 0 0)(T_0)
251 * (P_1) (3 0 1 1)(P_0)
252 * (F_1) (3 0 2 3)(F_0)
253 *
254 * which says that if you have a patch of metatiling consisting of H_0
255 * H tiles, T_0 T tiles etc, then this matrix shows the number H_1 of
256 * smaller H tiles, etc, expanded from it.
257 *
258 * If you expand _many_ times, that's equivalent to raising the matrix
259 * to a power:
260 *
261 * n
262 * (H_n) (3 1 2 2) (H_0)
263 * (T_n) = (1 0 0 0) (T_0)
264 * (P_n) (3 0 1 1) (P_0)
265 * (F_n) (3 0 2 3) (F_0)
266 *
267 * The limiting distribution of metatiles is obtained by looking at
268 * the four-way ratio between H_n, T_n, P_n and F_n as n tends to
269 * infinity. To calculate this, we find the eigenvalues and
270 * eigenvectors of the matrix, and extract the eigenvector
271 * corresponding to the eigenvalue of largest magnitude. (Things get
272 * more complicated in cases where there isn't a _unique_ eigenvalue
273 * of largest magnitude, but here, there is.)
274 *
275 * That eigenvector is
276 *
277 * [ 1 ] [ 1 ]
278 * [ (7 - 3 sqrt(5)) / 2 ] ~= [ 0.14589803375031545538 ]
279 * [ 3 sqrt(5) - 6 ] [ 0.70820393249936908922 ]
280 * [ (9 - 3 sqrt(5)) / 2 ] [ 1.14589803375031545538 ]
281 *
282 * So those are the limiting relative proportions of metatiles.
283 *
284 * So if we have a particular metatile, how likely is it for its
285 * parent to be one of those? We have to adjust by the number of
286 * metatiles of each type that each tile has as its children. For
287 * example, the P and F tiles have one P child each, but the H has
288 * three P children. So if we have a P, the proportion of H in its
289 * potential ancestry is three times what's shown here. (And T can't
290 * occur at all as a parent.)
291 *
292 * In other words, we should choose _each coordinate_ with probability
293 * corresponding to one of those numbers (scaled down so they all sum
294 * to 1). Continuing to use P as an example, it will be:
295 *
296 * - child 4 of H with relative probability 1
297 * - child 5 of H with relative probability 1
298 * - child 6 of H with relative probability 1
299 * - child 4 of P with relative probability 0.70820393249936908922
300 * - child 3 of F with relative probability 1.14589803375031545538
301 *
302 * and then we obtain the true probabilities by scaling those values
303 * down so that they sum to 1.
304 *
305 * The tables below give a reasonable approximation in 32-bit
306 * integers to these proportions.
307 */
308
309typedef struct MetatilePossibleParent {
310 TileType type;
311 unsigned index;
312 unsigned long probability;
313} MetatilePossibleParent;
314
315/* The above probabilities scaled up by 10000000 */
316#define PROB_H 10000000
317#define PROB_T 1458980
318#define PROB_P 7082039
319#define PROB_F 11458980
320
321static const MetatilePossibleParent parents_H[] = {
322 { TT_H, 0, PROB_H },
323 { TT_H, 1, PROB_H },
324 { TT_H, 2, PROB_H },
325 { TT_T, 0, PROB_T },
326 { TT_P, 0, PROB_P },
327 { TT_P, 1, PROB_P },
328 { TT_F, 0, PROB_F },
329 { TT_F, 1, PROB_F },
330};
331static const MetatilePossibleParent parents_T[] = {
332 { TT_H, 3, PROB_H },
333};
334static const MetatilePossibleParent parents_P[] = {
335 { TT_H, 4, PROB_H },
336 { TT_H, 5, PROB_H },
337 { TT_H, 6, PROB_H },
338 { TT_P, 4, PROB_P },
339 { TT_F, 3, PROB_F },
340};
341static const MetatilePossibleParent parents_F[] = {
342 { TT_H, 8, PROB_H },
343 { TT_H, 9, PROB_H },
344 { TT_H, 12, PROB_H },
345 { TT_P, 5, PROB_P },
346 { TT_P, 10, PROB_P },
347 { TT_F, 6, PROB_F },
348 { TT_F, 8, PROB_F },
349 { TT_F, 10, PROB_F },
350};
351
352static const MetatilePossibleParent *const possible_parents[] = {
353 parents_H, parents_T, parents_P, parents_F,
354};
355static const size_t n_possible_parents[] = {
356 lenof(parents_H), lenof(parents_T), lenof(parents_P), lenof(parents_F),
357};
358
359/*
360 * Similarly, we also want to choose our absolute starting hat with
361 * close to uniform probability, which again we do by looking at the
362 * limiting ratio of the metatile types, and this time, scaling by the
363 * number of hats in each metatile.
364 *
365 * We cheatingly use the same MetatilePossibleParent struct, because
366 * it's got all the right fields, even if it has an inappropriate
367 * name.
368 */
369static const MetatilePossibleParent starting_hats[] = {
370 { TT_H, 0, PROB_H },
371 { TT_H, 1, PROB_H },
372 { TT_H, 2, PROB_H },
373 { TT_H, 3, PROB_H },
374 { TT_T, 0, PROB_P },
375 { TT_P, 0, PROB_P },
376 { TT_P, 1, PROB_P },
377 { TT_F, 0, PROB_F },
378 { TT_F, 1, PROB_F },
379};
380
381#undef PROB_H
382#undef PROB_T
383#undef PROB_P
384#undef PROB_F
385
386HatCoords *hat_coords_new(void)
387{
388 HatCoords *hc = snew(HatCoords);
389 hc->nc = hc->csize = 0;
390 hc->c = NULL;
391 return hc;
392}
393
394void hat_coords_free(HatCoords *hc)
395{
396 if (hc) {
397 sfree(hc->c);
398 sfree(hc);
399 }
400}
401
402void hat_coords_make_space(HatCoords *hc, size_t size)
403{
404 if (hc->csize < size) {
405 hc->csize = hc->csize * 5 / 4 + 16;
406 if (hc->csize < size)
407 hc->csize = size;
408 hc->c = sresize(hc->c, hc->csize, HatCoord);
409 }
410}
411
412HatCoords *hat_coords_copy(HatCoords *hc_in)
413{
414 HatCoords *hc_out = hat_coords_new();
415 hat_coords_make_space(hc_out, hc_in->nc);
416 memcpy(hc_out->c, hc_in->c, hc_in->nc * sizeof(*hc_out->c));
417 hc_out->nc = hc_in->nc;
418 return hc_out;
419}
420
421static const MetatilePossibleParent *choose_mpp(
422 random_state *rs, const MetatilePossibleParent *parents, size_t nparents)
423{
424 /*
425 * If we needed to do this _efficiently_, we'd rewrite all those
426 * tables above as cumulative frequency tables and use binary
427 * search. But this happens about log n times in a grid of area n,
428 * so it hardly matters, and it's easier to keep the tables
429 * legible.
430 */
431 unsigned long limit = 0, value;
432 size_t i;
433
434 for (i = 0; i < nparents; i++)
435 limit += parents[i].probability;
436
437 value = random_upto(rs, limit);
438
439 for (i = 0; i+1 < nparents; i++) {
440 if (value < parents[i].probability)
441 return &parents[i];
442 value -= parents[i].probability;
443 }
444
445 assert(i == nparents - 1);
446 assert(value < parents[i].probability);
447 return &parents[i];
448}
449void hatctx_init_random(HatContext *ctx, random_state *rs)
450{
451 const MetatilePossibleParent *starting_hat = choose_mpp(
452 rs, starting_hats, lenof(starting_hats));
453
454 ctx->rs = rs;
455 ctx->prototype = hat_coords_new();
456 hat_coords_make_space(ctx->prototype, 3);
457 ctx->prototype->c[2].type = starting_hat->type;
458 ctx->prototype->c[2].index = -1;
459 ctx->prototype->c[1].type = TT_HAT;
460 ctx->prototype->c[1].index = starting_hat->index;
461 ctx->prototype->c[0].type = TT_KITE;
462 ctx->prototype->c[0].index = random_upto(rs, HAT_KITES);
463 ctx->prototype->nc = 3;
464}
465
466static inline int metatile_char_to_enum(char metatile)
467{
468 return (metatile == 'H' ? TT_H :
469 metatile == 'T' ? TT_T :
470 metatile == 'P' ? TT_P :
471 metatile == 'F' ? TT_F : -1);
472}
473
474static void init_coords_params(HatContext *ctx,
475 const struct HatPatchParams *hp)
476{
477 size_t i;
478
479 ctx->rs = NULL;
480 ctx->prototype = hat_coords_new();
481
482 assert(hp->ncoords >= 3);
483
484 hat_coords_make_space(ctx->prototype, hp->ncoords + 1);
485 ctx->prototype->nc = hp->ncoords + 1;
486
487 for (i = 0; i < hp->ncoords; i++)
488 ctx->prototype->c[i].index = hp->coords[i];
489
490 ctx->prototype->c[hp->ncoords].type =
491 metatile_char_to_enum(hp->final_metatile);
492 ctx->prototype->c[hp->ncoords].index = -1;
493
494 ctx->prototype->c[0].type = TT_KITE;
495 ctx->prototype->c[1].type = TT_HAT;
496
497 for (i = hp->ncoords - 1; i > 1; i--) {
498 TileType metatile = ctx->prototype->c[i+1].type;
499 assert(hp->coords[i] < nchildren[metatile]);
500 ctx->prototype->c[i].type = children[metatile][hp->coords[i]];
501 }
502
503 assert(hp->coords[0] < 8);
504}
505
506HatCoords *hatctx_initial_coords(HatContext *ctx)
507{
508 return hat_coords_copy(ctx->prototype);
509}
510
511/*
512 * Extend hc until it has at least n coordinates in, by copying from
513 * ctx->prototype if needed, and extending ctx->prototype if needed in
514 * order to do that.
515 */
516void hatctx_extend_coords(HatContext *ctx, HatCoords *hc, size_t n)
517{
518 if (ctx->prototype->nc < n) {
519 hat_coords_make_space(ctx->prototype, n);
520 while (ctx->prototype->nc < n) {
521 TileType type = ctx->prototype->c[ctx->prototype->nc - 1].type;
522 assert(ctx->prototype->c[ctx->prototype->nc - 1].index == -1);
523 const MetatilePossibleParent *parent;
524
525 if (ctx->rs)
526 parent = choose_mpp(ctx->rs, possible_parents[type],
527 n_possible_parents[type]);
528 else
529 parent = possible_parents[type];
530
531 ctx->prototype->c[ctx->prototype->nc - 1].index = parent->index;
532 ctx->prototype->c[ctx->prototype->nc].index = -1;
533 ctx->prototype->c[ctx->prototype->nc].type = parent->type;
534 ctx->prototype->nc++;
535 }
536 }
537
538 hat_coords_make_space(hc, n);
539 while (hc->nc < n) {
540 assert(hc->c[hc->nc - 1].index == -1);
541 assert(hc->c[hc->nc - 1].type == ctx->prototype->c[hc->nc - 1].type);
542 hc->c[hc->nc - 1].index = ctx->prototype->c[hc->nc - 1].index;
543 hc->c[hc->nc].index = -1;
544 hc->c[hc->nc].type = ctx->prototype->c[hc->nc].type;
545 hc->nc++;
546 }
547}
548
549void hatctx_cleanup(HatContext *ctx)
550{
551 hat_coords_free(ctx->prototype);
552}
553
554/*
555 * The actual system for finding the coordinates of an adjacent kite.
556 */
557
558/*
559 * Kitemap step: ensure we have enough coordinates to know two levels
560 * of meta-tiling, and use the kite map for the outer layer to move
561 * around the individual kites. If this fails, return NULL.
562 */
563static HatCoords *try_step_coords_kitemap(
564 HatContext *ctx, HatCoords *hc_in, KiteStep step)
565{
566 hatctx_extend_coords(ctx, hc_in, 4);
567 hat_coords_debug(" try kitemap ", hc_in, "\n");
568 unsigned kite = hc_in->c[0].index;
569 unsigned hat = hc_in->c[1].index;
570 unsigned meta = hc_in->c[2].index;
571 TileType meta2type = hc_in->c[3].type;
572 const KitemapEntry *ke = &kitemap[meta2type][
573 kitemap_index(step, kite, hat, meta)];
574 if (ke->kite >= 0) {
575 /*
576 * Success! We've got coordinates for the next kite in this
577 * direction.
578 */
579 HatCoords *hc_out = hat_coords_copy(hc_in);
580
581 hc_out->c[2].index = ke->meta;
582 hc_out->c[2].type = children[meta2type][ke->meta];
583 hc_out->c[1].index = ke->hat;
584 hc_out->c[1].type = TT_HAT;
585 hc_out->c[0].index = ke->kite;
586 hc_out->c[0].type = TT_KITE;
587
588 hat_coords_debug(" success! ", hc_out, "\n");
589 return hc_out;
590 }
591
592 return NULL;
593}
594
595/*
596 * Recursive metamap step. Try using the metamap to rewrite the
597 * coordinates at hc->c[depth] and hc->c[depth+1] (using the metamap
598 * for the tile type described in hc->c[depth+2]). If successful,
599 * recurse back down to see if this led to a successful step via the
600 * kitemap. If even that fails (so that we need to try a higher-order
601 * metamap rewrite), return NULL.
602 */
603static HatCoords *try_step_coords_metamap(
604 HatContext *ctx, HatCoords *hc_in, KiteStep step, size_t depth)
605{
606 HatCoords *hc_tmp = NULL, *hc_out;
607
608 hatctx_extend_coords(ctx, hc_in, depth+3);
609#ifdef HAT_COORDS_DEBUG
610 fprintf(stderr, " try meta %-4d", (int)depth);
611 hat_coords_debug("", hc_in, "\n");
612#endif
613 unsigned meta_orig = hc_in->c[depth].index;
614 unsigned meta2_orig = hc_in->c[depth+1].index;
615 TileType meta3type = hc_in->c[depth+2].type;
616
617 unsigned meta = meta_orig, meta2 = meta2_orig;
618
619 while (true) {
620 const MetamapEntry *me;
621 HatCoords *hc_curr = hc_tmp ? hc_tmp : hc_in;
622
623 if (depth > 2)
624 hc_out = try_step_coords_metamap(ctx, hc_curr, step, depth - 1);
625 else
626 hc_out = try_step_coords_kitemap(ctx, hc_curr, step);
627 if (hc_out) {
628 hat_coords_free(hc_tmp);
629 return hc_out;
630 }
631
632 me = &metamap[meta3type][metamap_index(meta, meta2)];
633 assert(me->meta != -1);
634 if (me->meta == meta_orig && me->meta2 == meta2_orig) {
635 hat_coords_free(hc_tmp);
636 return NULL;
637 }
638
639 meta = me->meta;
640 meta2 = me->meta2;
641
642 /*
643 * We must do the rewrite in a copy of hc_in. It's not
644 * _necessarily_ obvious that that's the case (any successful
645 * rewrite leaves the coordinates still valid and still
646 * referring to the same kite, right?). But the problem is
647 * that we might do a rewrite at this level more than once,
648 * and in between, a metamap rewrite at the next level down
649 * might have modified _one_ of the two coordinates we're
650 * messing about with. So it's easiest to let the recursion
651 * just use a separate copy.
652 */
653 if (!hc_tmp)
654 hc_tmp = hat_coords_copy(hc_in);
655
656 hc_tmp->c[depth+1].index = meta2;
657 hc_tmp->c[depth+1].type = children[meta3type][meta2];
658 hc_tmp->c[depth].index = meta;
659 hc_tmp->c[depth].type = children[hc_tmp->c[depth+1].type][meta];
660
661 hat_coords_debug(" rewritten -> ", hc_tmp, "\n");
662 }
663}
664
665/*
666 * The top-level algorithm for finding the next tile.
667 */
668HatCoords *hatctx_step(HatContext *ctx, HatCoords *hc_in, KiteStep step)
669{
670 HatCoords *hc_out;
671 size_t depth;
672
673#ifdef HAT_COORDS_DEBUG
674 static const char *const directions[] = {
675 " left\n", " right\n", " forward left\n", " forward right\n" };
676 hat_coords_debug("step start ", hc_in, directions[step]);
677#endif
678
679 /*
680 * First, just try a kitemap step immediately. If that succeeds,
681 * we're done.
682 */
683 if ((hc_out = try_step_coords_kitemap(ctx, hc_in, step)) != NULL)
684 return hc_out;
685
686 /*
687 * Otherwise, try metamap rewrites at successively higher layers
688 * until one works. Each one will recurse back down to the
689 * kitemap, as described above.
690 */
691 for (depth = 2;; depth++) {
692 if ((hc_out = try_step_coords_metamap(
693 ctx, hc_in, step, depth)) != NULL)
694 return hc_out;
695 }
696}
697
698/*
699 * Generate a random set of parameters for a tiling of a given size.
700 * To do this, we iterate over the whole tiling via hat_kiteenum_first
701 * and hat_kiteenum_next, and for each kite, calculate its
702 * coordinates. But then we throw the coordinates away and don't do
703 * anything with them!
704 *
705 * But the side effect of _calculating_ all those coordinates is that
706 * we found out how far ctx->prototype needed to be extended, and did
707 * so, pulling random choices out of our random_state. So after this
708 * iteration, ctx->prototype contains everything we need to replicate
709 * the same piece of tiling next time.
710 */
711void hat_tiling_randomise(struct HatPatchParams *hp, int w, int h,
712 random_state *rs)
713{
714 HatContext ctx[1];
715 HatCoords *coords[KE_NKEEP];
716 KiteEnum s[1];
717 size_t i;
718
719 hatctx_init_random(ctx, rs);
720 for (i = 0; i < lenof(coords); i++)
721 coords[i] = NULL;
722
723 hat_kiteenum_first(s, w, h);
724 coords[s->curr_index] = hatctx_initial_coords(ctx);
725
726 while (hat_kiteenum_next(s)) {
727 hat_coords_free(coords[s->curr_index]);
728 coords[s->curr_index] = hatctx_step(
729 ctx, coords[s->last_index], s->last_step);
730 }
731
732 hp->ncoords = ctx->prototype->nc - 1;
733 hp->coords = snewn(hp->ncoords, unsigned char);
734 for (i = 0; i < hp->ncoords; i++)
735 hp->coords[i] = ctx->prototype->c[i].index;
736 hp->final_metatile = tilechars[ctx->prototype->c[hp->ncoords].type];
737
738 hatctx_cleanup(ctx);
739 for (i = 0; i < lenof(coords); i++)
740 hat_coords_free(coords[i]);
741}
742
743const char *hat_tiling_params_invalid(const struct HatPatchParams *hp)
744{
745 TileType metatile;
746 size_t i;
747
748 if (hp->ncoords < 3)
749 return "Grid parameters require at least three coordinates";
750 if (metatile_char_to_enum(hp->final_metatile) < 0)
751 return "Grid parameters contain an invalid final metatile";
752 if (hp->coords[0] >= 8)
753 return "Grid parameters contain an invalid kite index";
754
755 metatile = metatile_char_to_enum(hp->final_metatile);
756 for (i = hp->ncoords - 1; i > 1; i--) {
757 if (hp->coords[i] >= nchildren[metatile])
758 return "Grid parameters contain an invalid metatile index";
759 metatile = children[metatile][hp->coords[i]];
760 }
761
762 if (hp->coords[1] >= hats_in_metatile[metatile])
763 return "Grid parameters contain an invalid hat index";
764
765 return NULL;
766}
767
768void maybe_report_hat(int w, int h, Kite kite, HatCoords *hc,
769 internal_hat_callback_fn cb, void *cbctx)
770{
771 Kite kite0;
772 Point vertices[14];
773 size_t i, j;
774 bool reversed = false;
775 int coords[28];
776
777 /* Only iterate from kite #0 of a hat */
778 if (hc->c[0].index != 0)
779 return;
780 kite0 = kite;
781
782 /*
783 * Identify reflected hats: they are always hat #3 of an H
784 * metatile. If we find one, reflect the starting kite so that the
785 * kite_step operations below will go in the other direction.
786 */
787 if (hc->c[2].type == TT_H && hc->c[1].index == 3) {
788 reversed = true;
789 Point tmp = kite.left;
790 kite.left = kite.right;
791 kite.right = tmp;
792 }
793
794 vertices[0] = kite.centre;
795 vertices[1] = kite.right;
796 vertices[2] = kite.outer;
797 vertices[3] = kite.left;
798 kite = kite_left(kite); /* now on kite #1 */
799 kite = kite_forward_right(kite); /* now on kite #2 */
800 vertices[4] = kite.centre;
801 kite = kite_right(kite); /* now on kite #3 */
802 vertices[5] = kite.right;
803 vertices[6] = kite.outer;
804 kite = kite_forward_left(kite); /* now on kite #4 */
805 vertices[7] = kite.left;
806 vertices[8] = kite.centre;
807 kite = kite_right(kite); /* now on kite #5 */
808 kite = kite_right(kite); /* now on kite #6 */
809 kite = kite_right(kite); /* now on kite #7 */
810 vertices[9] = kite.right;
811 vertices[10] = kite.outer;
812 vertices[11] = kite.left;
813 kite = kite_left(kite); /* now on kite #6 again */
814 vertices[12] = kite.outer;
815 vertices[13] = kite.left;
816
817 if (reversed) {
818 /* For a reversed kite, also reverse the vertex order, so that
819 * we report every polygon in a consistent orientation */
820 for (i = 0, j = 13; i < j; i++, j--) {
821 Point tmp = vertices[i];
822 vertices[i] = vertices[j];
823 vertices[j] = tmp;
824 }
825 }
826
827 /*
828 * Convert from our internal coordinate system into the orthogonal
829 * one used in this module's external API. In the same loop, we
830 * might as well do the bounds check.
831 */
832 for (i = 0; i < 14; i++) {
833 Point v = vertices[i];
834 int x = (v.x * 2 + v.y) / 3, y = v.y;
835
836 if (x < 0 || x > 4*w || y < 0 || y > 6*h)
837 return; /* a vertex of this kite is out of bounds */
838
839 coords[2*i] = x;
840 coords[2*i+1] = y;
841 }
842
843 cb(cbctx, kite0, hc, coords);
844}
845
846struct internal_ctx {
847 hat_tile_callback_fn external_cb;
848 void *external_cbctx;
849};
850static void report_hat(void *vctx, Kite kite0, HatCoords *hc, int *coords)
851{
852 struct internal_ctx *ctx = (struct internal_ctx *)vctx;
853 ctx->external_cb(ctx->external_cbctx, 14, coords);
854}
855
856/*
857 * Generate a hat tiling from a previously generated set of parameters.
858 */
859void hat_tiling_generate(const struct HatPatchParams *hp, int w, int h,
860 hat_tile_callback_fn cb, void *cbctx)
861{
862 HatContext ctx[1];
863 HatCoords *coords[KE_NKEEP];
864 KiteEnum s[1];
865 size_t i;
866 struct internal_ctx report_hat_ctx[1];
867
868 report_hat_ctx->external_cb = cb;
869 report_hat_ctx->external_cbctx = cbctx;
870
871 init_coords_params(ctx, hp);
872 for (i = 0; i < lenof(coords); i++)
873 coords[i] = NULL;
874
875 hat_kiteenum_first(s, w, h);
876 coords[s->curr_index] = hatctx_initial_coords(ctx);
877 maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
878 report_hat, report_hat_ctx);
879
880 while (hat_kiteenum_next(s)) {
881 hat_coords_free(coords[s->curr_index]);
882 coords[s->curr_index] = hatctx_step(
883 ctx, coords[s->last_index], s->last_step);
884 maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
885 report_hat, report_hat_ctx);
886 }
887
888 hatctx_cleanup(ctx);
889 for (i = 0; i < lenof(coords); i++)
890 hat_coords_free(coords[i]);
891}
diff --git a/apps/plugins/puzzles/src/hat.h b/apps/plugins/puzzles/src/hat.h
new file mode 100644
index 0000000000..39e2d31a66
--- /dev/null
+++ b/apps/plugins/puzzles/src/hat.h
@@ -0,0 +1,67 @@
1#ifndef PUZZLES_HAT_H
2#define PUZZLES_HAT_H
3
4struct HatPatchParams {
5 /*
6 * A patch of hat tiling is identified by giving the coordinates
7 * of the kite in one corner, using a multi-level coordinate
8 * system based on metatile expansions. Coordinates are a sequence
9 * of small non-negative integers. The valid range for each
10 * coordinate depends on the next coordinate, or on final_metatile
11 * if it's the last one in the list. The largest valid range is
12 * {0,...,12}.
13 *
14 * 'final_metatile' is one of the characters 'H', 'T', 'P' or 'F'.
15 */
16 size_t ncoords;
17 unsigned char *coords;
18 char final_metatile;
19};
20
21/*
22 * Fill in HatPatchParams with a randomly selected set of coordinates,
23 * in enough detail to generate a patch of tiling covering an area of
24 * w x h 'squares' of a kite tiling.
25 *
26 * The kites grid is considered to be oriented so that it includes
27 * horizontal lines and not vertical ones. So each of the smallest
28 * equilateral triangles in the grid has a bounding rectangle whose
29 * height is sqrt(3)/2 times its width, and either the top or the
30 * bottom of that bounding rectangle is the horizontal edge of the
31 * triangle. A 'square' of a kite tiling (for convenience of choosing
32 * grid dimensions) counts as one of those bounding rectangles.
33 *
34 * The 'coords' field of the structure will be filled in with a new
35 * dynamically allocated array. Any previous pointer in that field
36 * will be overwritten.
37 */
38void hat_tiling_randomise(struct HatPatchParams *params, int w, int h,
39 random_state *rs);
40
41/*
42 * Validate a HatPatchParams to ensure it contains no illegal
43 * coordinates. Returns NULL if it's acceptable, or an error string if
44 * not.
45 */
46const char *hat_tiling_params_invalid(const struct HatPatchParams *params);
47
48/*
49 * Generate the actual set of hat tiles from a HatPatchParams, passing
50 * each one to a callback. The callback receives the vertices of each
51 * point, as a sequence of 2*nvertices integers, with x,y coordinates
52 * interleaved.
53 *
54 * The x coordinates are measured in units of 1/4 of the side length
55 * of the smallest equilateral triangle, or equivalently, 1/2 the
56 * length of one of the long edges of a single kite. The y coordinates
57 * are measured in units of 1/6 the height of the triangle, which is
58 * also 1/2 the length of the short edge of a kite. Therefore, you can
59 * expect x to go up to 4*w and y up to 6*h.
60 */
61typedef void (*hat_tile_callback_fn)(void *ctx, size_t nvertices,
62 int *coords);
63
64void hat_tiling_generate(const struct HatPatchParams *params, int w, int h,
65 hat_tile_callback_fn cb, void *cbctx);
66
67#endif
diff --git a/apps/plugins/puzzles/src/inertia.R b/apps/plugins/puzzles/src/inertia.R
deleted file mode 100644
index e6e86beaec..0000000000
--- a/apps/plugins/puzzles/src/inertia.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3inertia : [X] GTK COMMON inertia inertia-icon|no-icon
4
5inertia : [G] WINDOWS COMMON inertia inertia.res|noicon.res
6
7ALL += inertia[COMBINED]
8
9!begin am gtk
10GAMES += inertia
11!end
12
13!begin >list.c
14 A(inertia) \
15!end
16
17!begin >gamedesc.txt
18inertia:inertia.exe:Inertia:Gem-collecting puzzle:Collect all the gems without running into any of the mines.
19!end
diff --git a/apps/plugins/puzzles/src/inertia.c b/apps/plugins/puzzles/src/inertia.c
index 726c89c7dd..fe174f3b56 100644
--- a/apps/plugins/puzzles/src/inertia.c
+++ b/apps/plugins/puzzles/src/inertia.c
@@ -11,7 +11,12 @@
11#include <string.h> 11#include <string.h>
12#include <assert.h> 12#include <assert.h>
13#include <ctype.h> 13#include <ctype.h>
14#include <math.h> 14#include <limits.h>
15#ifdef NO_TGMATH_H
16# include <math.h>
17#else
18# include <tgmath.h>
19#endif
15 20
16#include "puzzles.h" 21#include "puzzles.h"
17 22
@@ -202,6 +207,8 @@ static const char *validate_params(const game_params *params, bool full)
202 */ 207 */
203 if (params->w < 2 || params->h < 2) 208 if (params->w < 2 || params->h < 2)
204 return "Width and height must both be at least two"; 209 return "Width and height must both be at least two";
210 if (params->w > INT_MAX / params->h)
211 return "Width times height must not be unreasonably large";
205 212
206 /* 213 /*
207 * The grid construction algorithm creates 1/5 as many gems as 214 * The grid construction algorithm creates 1/5 as many gems as
@@ -715,18 +722,6 @@ static int move_goes_to(int w, int h, char *grid, int x, int y, int d)
715 return (y*w+x)*DP1+dr; 722 return (y*w+x)*DP1+dr;
716} 723}
717 724
718static int compare_integers(const void *av, const void *bv)
719{
720 const int *a = (const int *)av;
721 const int *b = (const int *)bv;
722 if (*a < *b)
723 return -1;
724 else if (*a > *b)
725 return +1;
726 else
727 return 0;
728}
729
730static char *solve_game(const game_state *state, const game_state *currstate, 725static char *solve_game(const game_state *state, const game_state *currstate,
731 const char *aux, const char **error) 726 const char *aux, const char **error)
732{ 727{
@@ -1520,7 +1515,8 @@ static char *encode_ui(const game_ui *ui)
1520 return dupstr(buf); 1515 return dupstr(buf);
1521} 1516}
1522 1517
1523static void decode_ui(game_ui *ui, const char *encoding) 1518static void decode_ui(game_ui *ui, const char *encoding,
1519 const game_state *state)
1524{ 1520{
1525 int p = 0; 1521 int p = 0;
1526 sscanf(encoding, "D%d%n", &ui->deaths, &p); 1522 sscanf(encoding, "D%d%n", &ui->deaths, &p);
@@ -1545,6 +1541,15 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1545 ui->just_made_move = false; 1541 ui->just_made_move = false;
1546} 1542}
1547 1543
1544static const char *current_key_label(const game_ui *ui,
1545 const game_state *state, int button)
1546{
1547 if (IS_CURSOR_SELECT(button) &&
1548 state->soln && state->solnpos < state->soln->len)
1549 return "Advance";
1550 return "";
1551}
1552
1548struct game_drawstate { 1553struct game_drawstate {
1549 game_params p; 1554 game_params p;
1550 int tilesize; 1555 int tilesize;
@@ -1593,7 +1598,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1593 * end up the right way round. */ 1598 * end up the right way round. */
1594 angle = atan2(dx, -dy); 1599 angle = atan2(dx, -dy);
1595 1600
1596 angle = (angle + (PI/8)) / (PI/4); 1601 angle = (angle + (float)(PI/8)) / (float)(PI/4);
1597 assert(angle > -16.0F); 1602 assert(angle > -16.0F);
1598 dir = (int)(angle + 16.0F) & 7; 1603 dir = (int)(angle + 16.0F) & 7;
1599 } 1604 }
@@ -1618,20 +1623,20 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1618 dir = state->soln->list[state->solnpos]; 1623 dir = state->soln->list[state->solnpos];
1619 1624
1620 if (dir < 0) 1625 if (dir < 0)
1621 return NULL; 1626 return MOVE_UNUSED;
1622 1627
1623 /* 1628 /*
1624 * Reject the move if we can't make it at all due to a wall 1629 * Reject the move if we can't make it at all due to a wall
1625 * being in the way. 1630 * being in the way.
1626 */ 1631 */
1627 if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL) 1632 if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL)
1628 return NULL; 1633 return MOVE_NO_EFFECT;
1629 1634
1630 /* 1635 /*
1631 * Reject the move if we're dead! 1636 * Reject the move if we're dead!
1632 */ 1637 */
1633 if (state->dead) 1638 if (state->dead)
1634 return NULL; 1639 return MOVE_NO_EFFECT;
1635 1640
1636 /* 1641 /*
1637 * Otherwise, we can make the move. All we need to specify is 1642 * Otherwise, we can make the move. All we need to specify is
@@ -1685,6 +1690,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1685 * This is a solve move, so we don't actually _change_ the 1690 * This is a solve move, so we don't actually _change_ the
1686 * grid but merely set up a stored solution path. 1691 * grid but merely set up a stored solution path.
1687 */ 1692 */
1693 if (move[1] == '\0') return NULL; /* Solution must be non-empty. */
1688 ret = dup_game(state); 1694 ret = dup_game(state);
1689 install_new_solution(ret, move); 1695 install_new_solution(ret, move);
1690 return ret; 1696 return ret;
@@ -1729,11 +1735,10 @@ static game_state *execute_move(const game_state *state, const char *move)
1729 if (ret->soln) { 1735 if (ret->soln) {
1730 if (ret->dead || ret->gems == 0) 1736 if (ret->dead || ret->gems == 0)
1731 discard_solution(ret); 1737 discard_solution(ret);
1732 else if (ret->soln->list[ret->solnpos] == dir) { 1738 else if (ret->soln->list[ret->solnpos] == dir &&
1739 ret->solnpos+1 < ret->soln->len)
1733 ++ret->solnpos; 1740 ++ret->solnpos;
1734 assert(ret->solnpos < ret->soln->len); /* or gems == 0 */ 1741 else {
1735 assert(!ret->dead); /* or not a solution */
1736 } else {
1737 const char *error = NULL; 1742 const char *error = NULL;
1738 char *soln = solve_game(NULL, ret, NULL, &error); 1743 char *soln = solve_game(NULL, ret, NULL, &error);
1739 if (!error) { 1744 if (!error) {
@@ -1751,7 +1756,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1751 */ 1756 */
1752 1757
1753static void game_compute_size(const game_params *params, int tilesize, 1758static void game_compute_size(const game_params *params, int tilesize,
1754 int *x, int *y) 1759 const game_ui *ui, int *x, int *y)
1755{ 1760{
1756 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1761 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1757 struct { int tilesize; } ads, *ds = &ads; 1762 struct { int tilesize; } ads, *ds = &ads;
@@ -1869,11 +1874,6 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1869 coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2); 1874 coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2);
1870 coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2); 1875 coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2);
1871 } 1876 }
1872 /* rockbox hack */
1873 int tmp[2] = { coords[0], coords[1] };
1874 memmove(coords, coords + 2, sizeof(int) * DIRECTIONS * 4 - 2);
1875 memcpy(coords + DIRECTIONS * 4 - 2, tmp, 2 * sizeof(int));
1876
1877 draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE); 1877 draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE);
1878 } else { 1878 } else {
1879 draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2, 1879 draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2,
@@ -1889,6 +1889,8 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1889 int coords[14], *c; 1889 int coords[14], *c;
1890 1890
1891 c = coords; 1891 c = coords;
1892 *c++ = ox + px/9;
1893 *c++ = oy + py/9;
1892 *c++ = ox + px/9 + ax*2/3; 1894 *c++ = ox + px/9 + ax*2/3;
1893 *c++ = oy + py/9 + ay*2/3; 1895 *c++ = oy + py/9 + ay*2/3;
1894 *c++ = ox + px/3 + ax*2/3; 1896 *c++ = ox + px/3 + ax*2/3;
@@ -1901,8 +1903,6 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1901 *c++ = oy - py/9 + ay*2/3; 1903 *c++ = oy - py/9 + ay*2/3;
1902 *c++ = ox - px/9; 1904 *c++ = ox - px/9;
1903 *c++ = oy - py/9; 1905 *c++ = oy - py/9;
1904 *c++ = ox + px/9;
1905 *c++ = oy + py/9;
1906 draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE); 1906 draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE);
1907 } 1907 }
1908 1908
@@ -2013,15 +2013,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2013 * Initialise a fresh drawstate. 2013 * Initialise a fresh drawstate.
2014 */ 2014 */
2015 if (!ds->started) { 2015 if (!ds->started) {
2016 int wid, ht;
2017
2018 /*
2019 * Blank out the window initially.
2020 */
2021 game_compute_size(&ds->p, TILESIZE, &wid, &ht);
2022 draw_rect(dr, 0, 0, wid, ht, COL_BACKGROUND);
2023 draw_update(dr, 0, 0, wid, ht);
2024
2025 /* 2016 /*
2026 * Draw the grid lines. 2017 * Draw the grid lines.
2027 */ 2018 */
@@ -2207,19 +2198,6 @@ static int game_status(const game_state *state)
2207 return state->gems == 0 ? +1 : 0; 2198 return state->gems == 0 ? +1 : 0;
2208} 2199}
2209 2200
2210static bool game_timing_state(const game_state *state, game_ui *ui)
2211{
2212 return true;
2213}
2214
2215static void game_print_size(const game_params *params, float *x, float *y)
2216{
2217}
2218
2219static void game_print(drawing *dr, const game_state *state, int tilesize)
2220{
2221}
2222
2223#ifdef COMBINED 2201#ifdef COMBINED
2224#define thegame inertia 2202#define thegame inertia
2225#endif 2203#endif
@@ -2241,12 +2219,14 @@ const struct game thegame = {
2241 free_game, 2219 free_game,
2242 true, solve_game, 2220 true, solve_game,
2243 true, game_can_format_as_text_now, game_text_format, 2221 true, game_can_format_as_text_now, game_text_format,
2222 NULL, NULL, /* get_prefs, set_prefs */
2244 new_ui, 2223 new_ui,
2245 free_ui, 2224 free_ui,
2246 encode_ui, 2225 encode_ui,
2247 decode_ui, 2226 decode_ui,
2248 NULL, /* game_request_keys */ 2227 NULL, /* game_request_keys */
2249 game_changed_state, 2228 game_changed_state,
2229 current_key_label,
2250 interpret_move, 2230 interpret_move,
2251 execute_move, 2231 execute_move,
2252 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2232 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2258,8 +2238,8 @@ const struct game thegame = {
2258 game_flash_length, 2238 game_flash_length,
2259 game_get_cursor_location, 2239 game_get_cursor_location,
2260 game_status, 2240 game_status,
2261 false, false, game_print_size, game_print, 2241 false, false, NULL, NULL, /* print_size, print */
2262 true, /* wants_statusbar */ 2242 true, /* wants_statusbar */
2263 false, game_timing_state, 2243 false, NULL, /* timing_state */
2264 0, /* flags */ 2244 0, /* flags */
2265}; 2245};
diff --git a/apps/plugins/puzzles/src/keen.R b/apps/plugins/puzzles/src/keen.R
deleted file mode 100644
index 46bd09a33d..0000000000
--- a/apps/plugins/puzzles/src/keen.R
+++ /dev/null
@@ -1,25 +0,0 @@
1# -*- makefile -*-
2
3KEEN_EXTRA = dsf LATIN
4KEEN_EXTRA_SOLVER = dsf LATIN_SOLVER
5
6keen : [X] GTK COMMON keen KEEN_EXTRA keen-icon|no-icon
7
8keen : [G] WINDOWS COMMON keen KEEN_EXTRA keen.res|noicon.res
9
10keensolver : [U] keen[STANDALONE_SOLVER] KEEN_EXTRA_SOLVER STANDALONE
11keensolver : [C] keen[STANDALONE_SOLVER] KEEN_EXTRA_SOLVER STANDALONE
12
13ALL += keen[COMBINED] KEEN_EXTRA
14
15!begin am gtk
16GAMES += keen
17!end
18
19!begin >list.c
20 A(keen) \
21!end
22
23!begin >gamedesc.txt
24keen:keen.exe:Keen:Arithmetic Latin square puzzle:Complete the latin square in accordance with the arithmetic clues.
25!end
diff --git a/apps/plugins/puzzles/src/keen.c b/apps/plugins/puzzles/src/keen.c
index 6b9610dbcd..7e2f71c8f9 100644
--- a/apps/plugins/puzzles/src/keen.c
+++ b/apps/plugins/puzzles/src/keen.c
@@ -8,7 +8,11 @@
8#include <string.h> 8#include <string.h>
9#include <assert.h> 9#include <assert.h>
10#include <ctype.h> 10#include <ctype.h>
11#include <math.h> 11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
12 16
13#include "puzzles.h" 17#include "puzzles.h"
14#include "latin.h" 18#include "latin.h"
@@ -69,7 +73,7 @@ struct game_params {
69struct clues { 73struct clues {
70 int refcount; 74 int refcount;
71 int w; 75 int w;
72 int *dsf; 76 DSF *dsf;
73 long *clues; 77 long *clues;
74}; 78};
75 79
@@ -677,7 +681,7 @@ static bool keen_valid(struct latin_solver *solver, void *vctx)
677 return true; 681 return true;
678} 682}
679 683
680static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff) 684static int solver(int w, DSF *dsf, long *clues, digit *soln, int maxdiff)
681{ 685{
682 int a = w*w; 686 int a = w*w;
683 struct solver_ctx ctx; 687 struct solver_ctx ctx;
@@ -704,11 +708,11 @@ static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff)
704 ctx.clues = snewn(ctx.nboxes, long); 708 ctx.clues = snewn(ctx.nboxes, long);
705 ctx.whichbox = snewn(a, int); 709 ctx.whichbox = snewn(a, int);
706 for (n = m = i = 0; i < a; i++) 710 for (n = m = i = 0; i < a; i++)
707 if (dsf_canonify(dsf, i) == i) { 711 if (dsf_minimal(dsf, i) == i) {
708 ctx.clues[n] = clues[i]; 712 ctx.clues[n] = clues[i];
709 ctx.boxes[n] = m; 713 ctx.boxes[n] = m;
710 for (j = 0; j < a; j++) 714 for (j = 0; j < a; j++)
711 if (dsf_canonify(dsf, j) == i) { 715 if (dsf_minimal(dsf, j) == i) {
712 ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */ 716 ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */
713 ctx.whichbox[ctx.boxlist[m-1]] = n; 717 ctx.whichbox[ctx.boxlist[m-1]] = n;
714 } 718 }
@@ -740,7 +744,7 @@ static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff)
740 * Grid generation. 744 * Grid generation.
741 */ 745 */
742 746
743static char *encode_block_structure(char *p, int w, int *dsf) 747static char *encode_block_structure(char *p, int w, DSF *dsf)
744{ 748{
745 int i, currrun = 0; 749 int i, currrun = 0;
746 char *orig, *q, *r, c; 750 char *orig, *q, *r, c;
@@ -777,7 +781,7 @@ static char *encode_block_structure(char *p, int w, int *dsf)
777 p0 = y*w+x; 781 p0 = y*w+x;
778 p1 = (y+1)*w+x; 782 p1 = (y+1)*w+x;
779 } 783 }
780 edge = (dsf_canonify(dsf, p0) != dsf_canonify(dsf, p1)); 784 edge = !dsf_equivalent(dsf, p0, p1);
781 } 785 }
782 786
783 if (edge) { 787 if (edge) {
@@ -815,13 +819,12 @@ static char *encode_block_structure(char *p, int w, int *dsf)
815 return q; 819 return q;
816} 820}
817 821
818static const char *parse_block_structure(const char **p, int w, int *dsf) 822static const char *parse_block_structure(const char **p, int w, DSF *dsf)
819{ 823{
820 int a = w*w;
821 int pos = 0; 824 int pos = 0;
822 int repc = 0, repn = 0; 825 int repc = 0, repn = 0;
823 826
824 dsf_init(dsf, a); 827 dsf_reinit(dsf);
825 828
826 while (**p && (repn > 0 || **p != ',')) { 829 while (**p && (repn > 0 || **p != ',')) {
827 int c; 830 int c;
@@ -890,7 +893,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
890{ 893{
891 int w = params->w, a = w*w; 894 int w = params->w, a = w*w;
892 digit *grid, *soln; 895 digit *grid, *soln;
893 int *order, *revorder, *singletons, *dsf; 896 int *order, *revorder, *singletons;
897 DSF *dsf;
894 long *clues, *cluevals; 898 long *clues, *cluevals;
895 int i, j, k, n, x, y, ret; 899 int i, j, k, n, x, y, ret;
896 int diff = params->diff; 900 int diff = params->diff;
@@ -927,7 +931,7 @@ done
927 order = snewn(a, int); 931 order = snewn(a, int);
928 revorder = snewn(a, int); 932 revorder = snewn(a, int);
929 singletons = snewn(a, int); 933 singletons = snewn(a, int);
930 dsf = snew_dsf(a); 934 dsf = dsf_new_min(a);
931 clues = snewn(a, long); 935 clues = snewn(a, long);
932 cluevals = snewn(a, long); 936 cluevals = snewn(a, long);
933 soln = snewn(a, digit); 937 soln = snewn(a, digit);
@@ -955,7 +959,7 @@ done
955 for (i = 0; i < a; i++) 959 for (i = 0; i < a; i++)
956 singletons[i] = true; 960 singletons[i] = true;
957 961
958 dsf_init(dsf, a); 962 dsf_reinit(dsf);
959 963
960 /* Place dominoes. */ 964 /* Place dominoes. */
961 for (i = 0; i < a; i++) { 965 for (i = 0; i < a; i++) {
@@ -1037,11 +1041,10 @@ done
1037 * integer quotient, of course), but we rule out (or try to 1041 * integer quotient, of course), but we rule out (or try to
1038 * avoid) some clues because they're of low quality. 1042 * avoid) some clues because they're of low quality.
1039 * 1043 *
1040 * Hence, we iterate once over the grid, stopping at the 1044 * Hence, we iterate once over the grid, stopping at the first
1041 * canonical element of every >2 block and the _non_- 1045 * element in every >2 block and the _last_ element of every
1042 * canonical element of every 2-block; the latter means that 1046 * 2-block; the latter means that we can make our decision
1043 * we can make our decision about a 2-block in the knowledge 1047 * about a 2-block in the knowledge of both numbers in it.
1044 * of both numbers in it.
1045 * 1048 *
1046 * We reuse the 'singletons' array (finished with in the 1049 * We reuse the 'singletons' array (finished with in the
1047 * above loop) to hold information about which blocks are 1050 * above loop) to hold information about which blocks are
@@ -1055,7 +1058,7 @@ done
1055 1058
1056 for (i = 0; i < a; i++) { 1059 for (i = 0; i < a; i++) {
1057 singletons[i] = 0; 1060 singletons[i] = 0;
1058 j = dsf_canonify(dsf, i); 1061 j = dsf_minimal(dsf, i);
1059 k = dsf_size(dsf, j); 1062 k = dsf_size(dsf, j);
1060 if (params->multiplication_only) 1063 if (params->multiplication_only)
1061 singletons[j] = F_MUL; 1064 singletons[j] = F_MUL;
@@ -1178,7 +1181,7 @@ done
1178 * Having chosen the clue types, calculate the clue values. 1181 * Having chosen the clue types, calculate the clue values.
1179 */ 1182 */
1180 for (i = 0; i < a; i++) { 1183 for (i = 0; i < a; i++) {
1181 j = dsf_canonify(dsf, i); 1184 j = dsf_minimal(dsf, i);
1182 if (j == i) { 1185 if (j == i) {
1183 cluevals[j] = grid[i]; 1186 cluevals[j] = grid[i];
1184 } else { 1187 } else {
@@ -1206,7 +1209,7 @@ done
1206 } 1209 }
1207 1210
1208 for (i = 0; i < a; i++) { 1211 for (i = 0; i < a; i++) {
1209 j = dsf_canonify(dsf, i); 1212 j = dsf_minimal(dsf, i);
1210 if (j == i) { 1213 if (j == i) {
1211 clues[j] |= cluevals[j]; 1214 clues[j] |= cluevals[j];
1212 } 1215 }
@@ -1252,7 +1255,7 @@ done
1252 p = encode_block_structure(p, w, dsf); 1255 p = encode_block_structure(p, w, dsf);
1253 *p++ = ','; 1256 *p++ = ',';
1254 for (i = 0; i < a; i++) { 1257 for (i = 0; i < a; i++) {
1255 j = dsf_canonify(dsf, i); 1258 j = dsf_minimal(dsf, i);
1256 if (j == i) { 1259 if (j == i) {
1257 switch (clues[j] & CMASK) { 1260 switch (clues[j] & CMASK) {
1258 case C_ADD: *p++ = 'a'; break; 1261 case C_ADD: *p++ = 'a'; break;
@@ -1280,7 +1283,7 @@ done
1280 sfree(order); 1283 sfree(order);
1281 sfree(revorder); 1284 sfree(revorder);
1282 sfree(singletons); 1285 sfree(singletons);
1283 sfree(dsf); 1286 dsf_free(dsf);
1284 sfree(clues); 1287 sfree(clues);
1285 sfree(cluevals); 1288 sfree(cluevals);
1286 sfree(soln); 1289 sfree(soln);
@@ -1295,7 +1298,7 @@ done
1295static const char *validate_desc(const game_params *params, const char *desc) 1298static const char *validate_desc(const game_params *params, const char *desc)
1296{ 1299{
1297 int w = params->w, a = w*w; 1300 int w = params->w, a = w*w;
1298 int *dsf; 1301 DSF *dsf;
1299 const char *ret; 1302 const char *ret;
1300 const char *p = desc; 1303 const char *p = desc;
1301 int i; 1304 int i;
@@ -1303,15 +1306,17 @@ static const char *validate_desc(const game_params *params, const char *desc)
1303 /* 1306 /*
1304 * Verify that the block structure makes sense. 1307 * Verify that the block structure makes sense.
1305 */ 1308 */
1306 dsf = snew_dsf(a); 1309 dsf = dsf_new_min(a);
1307 ret = parse_block_structure(&p, w, dsf); 1310 ret = parse_block_structure(&p, w, dsf);
1308 if (ret) { 1311 if (ret) {
1309 sfree(dsf); 1312 dsf_free(dsf);
1310 return ret; 1313 return ret;
1311 } 1314 }
1312 1315
1313 if (*p != ',') 1316 if (*p != ',') {
1317 dsf_free(dsf);
1314 return "Expected ',' after block structure description"; 1318 return "Expected ',' after block structure description";
1319 }
1315 p++; 1320 p++;
1316 1321
1317 /* 1322 /*
@@ -1319,21 +1324,26 @@ static const char *validate_desc(const game_params *params, const char *desc)
1319 * and DIV clues don't apply to blocks of the wrong size. 1324 * and DIV clues don't apply to blocks of the wrong size.
1320 */ 1325 */
1321 for (i = 0; i < a; i++) { 1326 for (i = 0; i < a; i++) {
1322 if (dsf_canonify(dsf, i) == i) { 1327 if (dsf_minimal(dsf, i) == i) {
1323 if (*p == 'a' || *p == 'm') { 1328 if (*p == 'a' || *p == 'm') {
1324 /* these clues need no validation */ 1329 /* these clues need no validation */
1325 } else if (*p == 'd' || *p == 's') { 1330 } else if (*p == 'd' || *p == 's') {
1326 if (dsf_size(dsf, i) != 2) 1331 if (dsf_size(dsf, i) != 2) {
1332 dsf_free(dsf);
1327 return "Subtraction and division blocks must have area 2"; 1333 return "Subtraction and division blocks must have area 2";
1334 }
1328 } else if (!*p) { 1335 } else if (!*p) {
1336 dsf_free(dsf);
1329 return "Too few clues for block structure"; 1337 return "Too few clues for block structure";
1330 } else { 1338 } else {
1339 dsf_free(dsf);
1331 return "Unrecognised clue type"; 1340 return "Unrecognised clue type";
1332 } 1341 }
1333 p++; 1342 p++;
1334 while (*p && isdigit((unsigned char)*p)) p++; 1343 while (*p && isdigit((unsigned char)*p)) p++;
1335 } 1344 }
1336 } 1345 }
1346 dsf_free(dsf);
1337 if (*p) 1347 if (*p)
1338 return "Too many clues for block structure"; 1348 return "Too many clues for block structure";
1339 1349
@@ -1373,7 +1383,7 @@ static game_state *new_game(midend *me, const game_params *params,
1373 state->clues = snew(struct clues); 1383 state->clues = snew(struct clues);
1374 state->clues->refcount = 1; 1384 state->clues->refcount = 1;
1375 state->clues->w = w; 1385 state->clues->w = w;
1376 state->clues->dsf = snew_dsf(a); 1386 state->clues->dsf = dsf_new_min(a);
1377 parse_block_structure(&p, w, state->clues->dsf); 1387 parse_block_structure(&p, w, state->clues->dsf);
1378 1388
1379 assert(*p == ','); 1389 assert(*p == ',');
@@ -1381,7 +1391,7 @@ static game_state *new_game(midend *me, const game_params *params,
1381 1391
1382 state->clues->clues = snewn(a, long); 1392 state->clues->clues = snewn(a, long);
1383 for (i = 0; i < a; i++) { 1393 for (i = 0; i < a; i++) {
1384 if (dsf_canonify(state->clues->dsf, i) == i) { 1394 if (dsf_minimal(state->clues->dsf, i) == i) {
1385 long clue = 0; 1395 long clue = 0;
1386 switch (*p) { 1396 switch (*p) {
1387 case 'a': 1397 case 'a':
@@ -1448,7 +1458,7 @@ static void free_game(game_state *state)
1448 sfree(state->grid); 1458 sfree(state->grid);
1449 sfree(state->pencil); 1459 sfree(state->pencil);
1450 if (--state->clues->refcount <= 0) { 1460 if (--state->clues->refcount <= 0) {
1451 sfree(state->clues->dsf); 1461 dsf_free(state->clues->dsf);
1452 sfree(state->clues->clues); 1462 sfree(state->clues->clues);
1453 sfree(state->clues); 1463 sfree(state->clues);
1454 } 1464 }
@@ -1490,16 +1500,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1490 return out; 1500 return out;
1491} 1501}
1492 1502
1493static bool game_can_format_as_text_now(const game_params *params)
1494{
1495 return true;
1496}
1497
1498static char *game_text_format(const game_state *state)
1499{
1500 return NULL;
1501}
1502
1503struct game_ui { 1503struct game_ui {
1504 /* 1504 /*
1505 * These are the coordinates of the currently highlighted 1505 * These are the coordinates of the currently highlighted
@@ -1525,6 +1525,17 @@ struct game_ui {
1525 * allowed on immutable squares. 1525 * allowed on immutable squares.
1526 */ 1526 */
1527 bool hcursor; 1527 bool hcursor;
1528
1529 /*
1530 * User preference option: if the user right-clicks in a square
1531 * and presses a number key to add/remove a pencil mark, do we
1532 * hide the mouse highlight again afterwards?
1533 *
1534 * Historically our answer was yes. The Android port prefers no.
1535 * There are advantages both ways, depending how much you dislike
1536 * the highlight cluttering your view. So it's a preference.
1537 */
1538 bool pencil_keep_highlight;
1528}; 1539};
1529 1540
1530static game_ui *new_ui(const game_state *state) 1541static game_ui *new_ui(const game_state *state)
@@ -1533,8 +1544,9 @@ static game_ui *new_ui(const game_state *state)
1533 1544
1534 ui->hx = ui->hy = 0; 1545 ui->hx = ui->hy = 0;
1535 ui->hpencil = false; 1546 ui->hpencil = false;
1536 ui->hshow = false; 1547 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1537 ui->hcursor = false; 1548
1549 ui->pencil_keep_highlight = false;
1538 1550
1539 return ui; 1551 return ui;
1540} 1552}
@@ -1544,13 +1556,26 @@ static void free_ui(game_ui *ui)
1544 sfree(ui); 1556 sfree(ui);
1545} 1557}
1546 1558
1547static char *encode_ui(const game_ui *ui) 1559static config_item *get_prefs(game_ui *ui)
1548{ 1560{
1549 return NULL; 1561 config_item *ret;
1562
1563 ret = snewn(2, config_item);
1564
1565 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1566 ret[0].kw = "pencil-keep-highlight";
1567 ret[0].type = C_BOOLEAN;
1568 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1569
1570 ret[1].name = NULL;
1571 ret[1].type = C_END;
1572
1573 return ret;
1550} 1574}
1551 1575
1552static void decode_ui(game_ui *ui, const char *encoding) 1576static void set_prefs(game_ui *ui, const config_item *cfg)
1553{ 1577{
1578 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1554} 1579}
1555 1580
1556static void game_changed_state(game_ui *ui, const game_state *oldstate, 1581static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1569,6 +1594,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1569 } 1594 }
1570} 1595}
1571 1596
1597static const char *current_key_label(const game_ui *ui,
1598 const game_state *state, int button)
1599{
1600 if (ui->hshow && (button == CURSOR_SELECT))
1601 return ui->hpencil ? "Ink" : "Pencil";
1602 return "";
1603}
1604
1572#define PREFERRED_TILESIZE 48 1605#define PREFERRED_TILESIZE 48
1573#define TILESIZE (ds->tilesize) 1606#define TILESIZE (ds->tilesize)
1574#define BORDER (TILESIZE / 2) 1607#define BORDER (TILESIZE / 2)
@@ -1613,7 +1646,7 @@ static bool check_errors(const game_state *state, long *errors)
1613 for (i = 0; i < a; i++) { 1646 for (i = 0; i < a; i++) {
1614 long clue; 1647 long clue;
1615 1648
1616 j = dsf_canonify(state->clues->dsf, i); 1649 j = dsf_minimal(state->clues->dsf, i);
1617 if (j == i) { 1650 if (j == i) {
1618 cluevals[i] = state->grid[i]; 1651 cluevals[i] = state->grid[i];
1619 } else { 1652 } else {
@@ -1647,7 +1680,7 @@ static bool check_errors(const game_state *state, long *errors)
1647 } 1680 }
1648 1681
1649 for (i = 0; i < a; i++) { 1682 for (i = 0; i < a; i++) {
1650 j = dsf_canonify(state->clues->dsf, i); 1683 j = dsf_minimal(state->clues->dsf, i);
1651 if (j == i) { 1684 if (j == i) {
1652 if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) { 1685 if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) {
1653 errs = true; 1686 errs = true;
@@ -1709,7 +1742,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1709 int tx, ty; 1742 int tx, ty;
1710 char buf[80]; 1743 char buf[80];
1711 1744
1712 button &= ~MOD_MASK; 1745 button = STRIP_BUTTON_MODIFIERS(button);
1713 1746
1714 tx = FROMCOORD(x); 1747 tx = FROMCOORD(x);
1715 ty = FROMCOORD(y); 1748 ty = FROMCOORD(y);
@@ -1726,7 +1759,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1726 ui->hpencil = false; 1759 ui->hpencil = false;
1727 } 1760 }
1728 ui->hcursor = false; 1761 ui->hcursor = false;
1729 return UI_UPDATE; 1762 return MOVE_UI_UPDATE;
1730 } 1763 }
1731 if (button == RIGHT_BUTTON) { 1764 if (button == RIGHT_BUTTON) {
1732 /* 1765 /*
@@ -1746,20 +1779,18 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1746 ui->hshow = false; 1779 ui->hshow = false;
1747 } 1780 }
1748 ui->hcursor = false; 1781 ui->hcursor = false;
1749 return UI_UPDATE; 1782 return MOVE_UI_UPDATE;
1750 } 1783 }
1751 } 1784 }
1752 if (IS_CURSOR_MOVE(button)) { 1785 if (IS_CURSOR_MOVE(button)) {
1753 move_cursor(button, &ui->hx, &ui->hy, w, w, false);
1754 ui->hshow = true;
1755 ui->hcursor = true; 1786 ui->hcursor = true;
1756 return UI_UPDATE; 1787 return move_cursor(button, &ui->hx, &ui->hy, w, w, false, &ui->hshow);
1757 } 1788 }
1758 if (ui->hshow && 1789 if (ui->hshow &&
1759 (button == CURSOR_SELECT)) { 1790 (button == CURSOR_SELECT)) {
1760 ui->hpencil ^= 1; 1791 ui->hpencil ^= 1;
1761 ui->hcursor = true; 1792 ui->hcursor = true;
1762 return UI_UPDATE; 1793 return MOVE_UI_UPDATE;
1763 } 1794 }
1764 1795
1765 if (ui->hshow && 1796 if (ui->hshow &&
@@ -1776,10 +1807,31 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1776 if (ui->hpencil && state->grid[ui->hy*w+ui->hx]) 1807 if (ui->hpencil && state->grid[ui->hy*w+ui->hx])
1777 return NULL; 1808 return NULL;
1778 1809
1810 /*
1811 * If you ask to fill a square with what it already contains,
1812 * or blank it when it's already empty, that has no effect...
1813 */
1814 if ((!ui->hpencil || n == 0) && state->grid[ui->hy*w+ui->hx] == n &&
1815 state->pencil[ui->hy*w+ui->hx] == 0) {
1816 /* ... expect to remove the cursor in mouse mode. */
1817 if (!ui->hcursor) {
1818 ui->hshow = false;
1819 return MOVE_UI_UPDATE;
1820 }
1821 return NULL;
1822 }
1823
1779 sprintf(buf, "%c%d,%d,%d", 1824 sprintf(buf, "%c%d,%d,%d",
1780 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 1825 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1781 1826
1782 if (!ui->hcursor) ui->hshow = false; 1827 /*
1828 * Hide the highlight after a keypress, if it was mouse-
1829 * generated. Also, don't hide it if this move has changed
1830 * pencil marks and the user preference says not to hide the
1831 * highlight in that situation.
1832 */
1833 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1834 ui->hshow = false;
1783 1835
1784 return dupstr(buf); 1836 return dupstr(buf);
1785 } 1837 }
@@ -1854,7 +1906,7 @@ static game_state *execute_move(const game_state *from, const char *move)
1854#define SIZE(w) ((w) * TILESIZE + 2*BORDER) 1906#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
1855 1907
1856static void game_compute_size(const game_params *params, int tilesize, 1908static void game_compute_size(const game_params *params, int tilesize,
1857 int *x, int *y) 1909 const game_ui *ui, int *x, int *y)
1858{ 1910{
1859 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1911 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1860 struct { int tilesize; } ads, *ds = &ads; 1912 struct { int tilesize; } ads, *ds = &ads;
@@ -1939,6 +1991,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1939 int tx, ty, tw, th; 1991 int tx, ty, tw, th;
1940 int cx, cy, cw, ch; 1992 int cx, cy, cw, ch;
1941 char str[64]; 1993 char str[64];
1994 bool draw_clue = dsf_minimal(clues->dsf, y*w+x) == y*w+x;
1942 1995
1943 tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA; 1996 tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA;
1944 ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA; 1997 ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA;
@@ -1948,13 +2001,13 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1948 cw = tw = TILESIZE-1-2*GRIDEXTRA; 2001 cw = tw = TILESIZE-1-2*GRIDEXTRA;
1949 ch = th = TILESIZE-1-2*GRIDEXTRA; 2002 ch = th = TILESIZE-1-2*GRIDEXTRA;
1950 2003
1951 if (x > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x-1)) 2004 if (x > 0 && dsf_equivalent(clues->dsf, y*w+x, y*w+x-1))
1952 cx -= GRIDEXTRA, cw += GRIDEXTRA; 2005 cx -= GRIDEXTRA, cw += GRIDEXTRA;
1953 if (x+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x+1)) 2006 if (x+1 < w && dsf_equivalent(clues->dsf, y*w+x, y*w+x+1))
1954 cw += GRIDEXTRA; 2007 cw += GRIDEXTRA;
1955 if (y > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y-1)*w+x)) 2008 if (y > 0 && dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x))
1956 cy -= GRIDEXTRA, ch += GRIDEXTRA; 2009 cy -= GRIDEXTRA, ch += GRIDEXTRA;
1957 if (y+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y+1)*w+x)) 2010 if (y+1 < w && dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x))
1958 ch += GRIDEXTRA; 2011 ch += GRIDEXTRA;
1959 2012
1960 clip(dr, cx, cy, cw, ch); 2013 clip(dr, cx, cy, cw, ch);
@@ -1979,17 +2032,21 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1979 * Draw the corners of thick lines in corner-adjacent squares, 2032 * Draw the corners of thick lines in corner-adjacent squares,
1980 * which jut into this square by one pixel. 2033 * which jut into this square by one pixel.
1981 */ 2034 */
1982 if (x > 0 && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x-1)) 2035 if (x > 0 && y > 0 && !dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x-1))
1983 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2036 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA,
1984 if (x+1 < w && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x+1)) 2037 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1985 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2038 if (x+1 < w && y > 0 && !dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x+1))
1986 if (x > 0 && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x-1)) 2039 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA,
1987 draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2040 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1988 if (x+1 < w && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x+1)) 2041 if (x > 0 && y+1 < w && !dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x-1))
1989 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2042 draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA,
2043 GRIDEXTRA, GRIDEXTRA, COL_GRID);
2044 if (x+1 < w && y+1 < w && !dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x+1))
2045 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA,
2046 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1990 2047
1991 /* Draw the box clue. */ 2048 /* Draw the box clue. */
1992 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2049 if (draw_clue) {
1993 long clue = clues->clues[y*w+x]; 2050 long clue = clues->clues[y*w+x];
1994 long cluetype = clue & CMASK, clueval = clue & ~CMASK; 2051 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
1995 int size = dsf_size(clues->dsf, y*w+x); 2052 int size = dsf_size(clues->dsf, y*w+x);
@@ -2043,7 +2100,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
2043 pr = pl + TILESIZE - GRIDEXTRA; 2100 pr = pl + TILESIZE - GRIDEXTRA;
2044 pt = ty + GRIDEXTRA; 2101 pt = ty + GRIDEXTRA;
2045 pb = pt + TILESIZE - GRIDEXTRA; 2102 pb = pt + TILESIZE - GRIDEXTRA;
2046 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2103 if (draw_clue) {
2047 /* 2104 /*
2048 * Make space for the clue text. 2105 * Make space for the clue text.
2049 */ 2106 */
@@ -2097,7 +2154,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
2097 * And move it down a bit if it's collided with some 2154 * And move it down a bit if it's collided with some
2098 * clue text. 2155 * clue text.
2099 */ 2156 */
2100 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2157 if (draw_clue) {
2101 pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4); 2158 pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4);
2102 } 2159 }
2103 2160
@@ -2134,14 +2191,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2134 2191
2135 if (!ds->started) { 2192 if (!ds->started) {
2136 /* 2193 /*
2137 * The initial contents of the window are not guaranteed and
2138 * can vary with front ends. To be on the safe side, all
2139 * games should start by drawing a big background-colour
2140 * rectangle covering the whole window.
2141 */
2142 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
2143
2144 /*
2145 * Big containing rectangle. 2194 * Big containing rectangle.
2146 */ 2195 */
2147 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA, 2196 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
@@ -2217,21 +2266,15 @@ static int game_status(const game_state *state)
2217 return state->completed ? +1 : 0; 2266 return state->completed ? +1 : 0;
2218} 2267}
2219 2268
2220static bool game_timing_state(const game_state *state, game_ui *ui) 2269static void game_print_size(const game_params *params, const game_ui *ui,
2221{ 2270 float *x, float *y)
2222 if (state->completed)
2223 return false;
2224 return true;
2225}
2226
2227static void game_print_size(const game_params *params, float *x, float *y)
2228{ 2271{
2229 int pw, ph; 2272 int pw, ph;
2230 2273
2231 /* 2274 /*
2232 * We use 9mm squares by default, like Solo. 2275 * We use 9mm squares by default, like Solo.
2233 */ 2276 */
2234 game_compute_size(params, 900, &pw, &ph); 2277 game_compute_size(params, 900, ui, &pw, &ph);
2235 *x = pw / 100.0F; 2278 *x = pw / 100.0F;
2236 *y = ph / 100.0F; 2279 *y = ph / 100.0F;
2237} 2280}
@@ -2245,7 +2288,7 @@ static void game_print_size(const game_params *params, float *x, float *y)
2245 * single polygon. 2288 * single polygon.
2246 */ 2289 */
2247static void outline_block_structure(drawing *dr, game_drawstate *ds, 2290static void outline_block_structure(drawing *dr, game_drawstate *ds,
2248 int w, int *dsf, int ink) 2291 int w, DSF *dsf, int ink)
2249{ 2292{
2250 int a = w*w; 2293 int a = w*w;
2251 int *coords; 2294 int *coords;
@@ -2258,7 +2301,7 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2258 * Iterate over all the blocks. 2301 * Iterate over all the blocks.
2259 */ 2302 */
2260 for (i = 0; i < a; i++) { 2303 for (i = 0; i < a; i++) {
2261 if (dsf_canonify(dsf, i) != i) 2304 if (dsf_minimal(dsf, i) != i)
2262 continue; 2305 continue;
2263 2306
2264 /* 2307 /*
@@ -2297,11 +2340,11 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2297 tx = x - dy + dx; 2340 tx = x - dy + dx;
2298 ty = y + dx + dy; 2341 ty = y + dx + dy;
2299 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && 2342 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2300 dsf_canonify(dsf, ty*w+tx) == i); 2343 dsf_minimal(dsf, ty*w+tx) == i);
2301 tx = x - dy; 2344 tx = x - dy;
2302 ty = y + dx; 2345 ty = y + dx;
2303 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && 2346 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2304 dsf_canonify(dsf, ty*w+tx) == i); 2347 dsf_minimal(dsf, ty*w+tx) == i);
2305 if (nin == 0) { 2348 if (nin == 0) {
2306 /* 2349 /*
2307 * Turn right. 2350 * Turn right.
@@ -2338,9 +2381,9 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2338 * somewhere sensible. 2381 * somewhere sensible.
2339 */ 2382 */
2340 assert(x >= 0 && x < w && y >= 0 && y < w && 2383 assert(x >= 0 && x < w && y >= 0 && y < w &&
2341 dsf_canonify(dsf, y*w+x) == i); 2384 dsf_minimal(dsf, y*w+x) == i);
2342 assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w || 2385 assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w ||
2343 dsf_canonify(dsf, (y+dy)*w+(x+dx)) != i); 2386 dsf_minimal(dsf, (y+dy)*w+(x+dx)) != i);
2344 2387
2345 /* 2388 /*
2346 * Record the point we just went past at one end of the 2389 * Record the point we just went past at one end of the
@@ -2367,7 +2410,8 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2367 sfree(coords); 2410 sfree(coords);
2368} 2411}
2369 2412
2370static void game_print(drawing *dr, const game_state *state, int tilesize) 2413static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2414 int tilesize)
2371{ 2415{
2372 int w = state->par.w; 2416 int w = state->par.w;
2373 int ink = print_mono_colour(dr, 0); 2417 int ink = print_mono_colour(dr, 0);
@@ -2413,7 +2457,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
2413 */ 2457 */
2414 for (y = 0; y < w; y++) 2458 for (y = 0; y < w; y++)
2415 for (x = 0; x < w; x++) 2459 for (x = 0; x < w; x++)
2416 if (dsf_canonify(state->clues->dsf, y*w+x) == y*w+x) { 2460 if (dsf_minimal(state->clues->dsf, y*w+x) == y*w+x) {
2417 long clue = state->clues->clues[y*w+x]; 2461 long clue = state->clues->clues[y*w+x];
2418 long cluetype = clue & CMASK, clueval = clue & ~CMASK; 2462 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
2419 int size = dsf_size(state->clues->dsf, y*w+x); 2463 int size = dsf_size(state->clues->dsf, y*w+x);
@@ -2478,13 +2522,15 @@ const struct game thegame = {
2478 dup_game, 2522 dup_game,
2479 free_game, 2523 free_game,
2480 true, solve_game, 2524 true, solve_game,
2481 false, game_can_format_as_text_now, game_text_format, 2525 false, NULL, NULL, /* can_format_as_text_now, text_format */
2526 get_prefs, set_prefs,
2482 new_ui, 2527 new_ui,
2483 free_ui, 2528 free_ui,
2484 encode_ui, 2529 NULL, /* encode_ui */
2485 decode_ui, 2530 NULL, /* decode_ui */
2486 game_request_keys, 2531 game_request_keys,
2487 game_changed_state, 2532 game_changed_state,
2533 current_key_label,
2488 interpret_move, 2534 interpret_move,
2489 execute_move, 2535 execute_move,
2490 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2536 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2498,7 +2544,7 @@ const struct game thegame = {
2498 game_status, 2544 game_status,
2499 true, false, game_print_size, game_print, 2545 true, false, game_print_size, game_print,
2500 false, /* wants_statusbar */ 2546 false, /* wants_statusbar */
2501 false, game_timing_state, 2547 false, NULL, /* timing_state */
2502 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2548 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2503}; 2549};
2504 2550
diff --git a/apps/plugins/puzzles/src/latin.c b/apps/plugins/puzzles/src/latin.c
index 39930166e7..a2d5713b30 100644
--- a/apps/plugins/puzzles/src/latin.c
+++ b/apps/plugins/puzzles/src/latin.c
@@ -563,7 +563,7 @@ void latin_solver_free_scratch(struct latin_solver_scratch *scratch)
563 sfree(scratch); 563 sfree(scratch);
564} 564}
565 565
566void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o) 566bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
567{ 567{
568 int x, y; 568 int x, y;
569 569
@@ -577,14 +577,23 @@ void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
577 memset(solver->row, 0, o*o); 577 memset(solver->row, 0, o*o);
578 memset(solver->col, 0, o*o); 578 memset(solver->col, 0, o*o);
579 579
580 for (x = 0; x < o; x++)
581 for (y = 0; y < o; y++)
582 if (grid[y*o+x])
583 latin_solver_place(solver, x, y, grid[y*o+x]);
584
585#ifdef STANDALONE_SOLVER 580#ifdef STANDALONE_SOLVER
586 solver->names = NULL; 581 solver->names = NULL;
587#endif 582#endif
583
584 for (x = 0; x < o; x++) {
585 for (y = 0; y < o; y++) {
586 int n = grid[y*o+x];
587 if (n) {
588 if (cube(x, y, n))
589 latin_solver_place(solver, x, y, n);
590 else
591 return false; /* puzzle is already inconsistent */
592 }
593 }
594 }
595
596 return true;
588} 597}
589 598
590void latin_solver_free(struct latin_solver *solver) 599void latin_solver_free(struct latin_solver *solver)
@@ -810,15 +819,17 @@ static int latin_solver_recurse
810 } else { 819 } else {
811 newctx = ctx; 820 newctx = ctx;
812 } 821 }
813 latin_solver_alloc(&subsolver, outgrid, o);
814#ifdef STANDALONE_SOLVER 822#ifdef STANDALONE_SOLVER
815 subsolver.names = solver->names; 823 subsolver.names = solver->names;
816#endif 824#endif
817 ret = latin_solver_top(&subsolver, diff_recursive, 825 if (latin_solver_alloc(&subsolver, outgrid, o))
818 diff_simple, diff_set_0, diff_set_1, 826 ret = latin_solver_top(&subsolver, diff_recursive,
819 diff_forcing, diff_recursive, 827 diff_simple, diff_set_0, diff_set_1,
820 usersolvers, valid, newctx, 828 diff_forcing, diff_recursive,
821 ctxnew, ctxfree); 829 usersolvers, valid, newctx,
830 ctxnew, ctxfree);
831 else
832 ret = diff_impossible;
822 latin_solver_free(&subsolver); 833 latin_solver_free(&subsolver);
823 if (ctxnew) 834 if (ctxnew)
824 ctxfree(newctx); 835 ctxfree(newctx);
@@ -1059,11 +1070,13 @@ int latin_solver(digit *grid, int o, int maxdiff,
1059 struct latin_solver solver; 1070 struct latin_solver solver;
1060 int diff; 1071 int diff;
1061 1072
1062 latin_solver_alloc(&solver, grid, o); 1073 if (latin_solver_alloc(&solver, grid, o))
1063 diff = latin_solver_main(&solver, maxdiff, 1074 diff = latin_solver_main(&solver, maxdiff,
1064 diff_simple, diff_set_0, diff_set_1, 1075 diff_simple, diff_set_0, diff_set_1,
1065 diff_forcing, diff_recursive, 1076 diff_forcing, diff_recursive,
1066 usersolvers, valid, ctx, ctxnew, ctxfree); 1077 usersolvers, valid, ctx, ctxnew, ctxfree);
1078 else
1079 diff = diff_impossible;
1067 latin_solver_free(&solver); 1080 latin_solver_free(&solver);
1068 return diff; 1081 return diff;
1069} 1082}
@@ -1298,121 +1311,4 @@ bool latin_check(digit *sq, int order)
1298 return ret; 1311 return ret;
1299} 1312}
1300 1313
1301
1302/* --------------------------------------------------------
1303 * Testing (and printing).
1304 */
1305
1306#ifdef STANDALONE_LATIN_TEST
1307
1308#include <stdio.h>
1309#include <time.h>
1310
1311const char *quis;
1312
1313static void latin_print(digit *sq, int order)
1314{
1315 int x, y;
1316
1317 for (y = 0; y < order; y++) {
1318 for (x = 0; x < order; x++) {
1319 printf("%2u ", ELT(sq, x, y));
1320 }
1321 printf("\n");
1322 }
1323 printf("\n");
1324}
1325
1326static void gen(int order, random_state *rs, int debug)
1327{
1328 digit *sq;
1329
1330 solver_show_working = debug;
1331
1332 sq = latin_generate(order, rs);
1333 latin_print(sq, order);
1334 if (latin_check(sq, order)) {
1335 fprintf(stderr, "Square is not a latin square!");
1336 exit(1);
1337 }
1338
1339 sfree(sq);
1340}
1341
1342void test_soak(int order, random_state *rs)
1343{
1344 digit *sq;
1345 int n = 0;
1346 time_t tt_start, tt_now, tt_last;
1347
1348 solver_show_working = 0;
1349 tt_now = tt_start = time(NULL);
1350
1351 while(1) {
1352 sq = latin_generate(order, rs);
1353 sfree(sq);
1354 n++;
1355
1356 tt_last = time(NULL);
1357 if (tt_last > tt_now) {
1358 tt_now = tt_last;
1359 printf("%d total, %3.1f/s\n", n,
1360 (double)n / (double)(tt_now - tt_start));
1361 }
1362 }
1363}
1364
1365void usage_exit(const char *msg)
1366{
1367 if (msg)
1368 fprintf(stderr, "%s: %s\n", quis, msg);
1369 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
1370 exit(1);
1371}
1372
1373int main(int argc, char *argv[])
1374{
1375 int i, soak = 0;
1376 random_state *rs;
1377 time_t seed = time(NULL);
1378
1379 quis = argv[0];
1380 while (--argc > 0) {
1381 const char *p = *++argv;
1382 if (!strcmp(p, "--soak"))
1383 soak = 1;
1384 else if (!strcmp(p, "--seed")) {
1385 if (argc == 0)
1386 usage_exit("--seed needs an argument");
1387 seed = (time_t)atoi(*++argv);
1388 argc--;
1389 } else if (*p == '-')
1390 usage_exit("unrecognised option");
1391 else
1392 break; /* finished options */
1393 }
1394
1395 rs = random_new((void*)&seed, sizeof(time_t));
1396
1397 if (soak == 1) {
1398 if (argc != 1) usage_exit("only one argument for --soak");
1399 test_soak(atoi(*argv), rs);
1400 } else {
1401 if (argc > 0) {
1402 for (i = 0; i < argc; i++) {
1403 gen(atoi(*argv++), rs, 1);
1404 }
1405 } else {
1406 while (1) {
1407 i = random_upto(rs, 20) + 1;
1408 gen(i, rs, 0);
1409 }
1410 }
1411 }
1412 random_free(rs);
1413 return 0;
1414}
1415
1416#endif
1417
1418/* vim: set shiftwidth=4 tabstop=8: */ 1314/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/latin.h b/apps/plugins/puzzles/src/latin.h
index bb172ec3c7..96a0480491 100644
--- a/apps/plugins/puzzles/src/latin.h
+++ b/apps/plugins/puzzles/src/latin.h
@@ -61,10 +61,13 @@ int latin_solver_forcing(struct latin_solver *solver,
61/* --- Solver allocation --- */ 61/* --- Solver allocation --- */
62 62
63/* Fills in (and allocates members for) a latin_solver struct. 63/* Fills in (and allocates members for) a latin_solver struct.
64 * Will allocate members of snew, but not snew itself 64 * Will allocate members of solver, but not solver itself
65 * (allowing 'struct latin_solver' to be the first element in a larger 65 * (allowing 'struct latin_solver' to be the first element in a larger
66 * struct, for example). */ 66 * struct, for example).
67void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o); 67 *
68 * latin_solver_alloc returns false if the digits already in the grid
69 * could not be legally placed. */
70bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o);
68void latin_solver_free(struct latin_solver *solver); 71void latin_solver_free(struct latin_solver *solver);
69 72
70/* Allocates scratch space (for _set and _forcing) */ 73/* Allocates scratch space (for _set and _forcing) */
diff --git a/apps/plugins/puzzles/src/lightup.R b/apps/plugins/puzzles/src/lightup.R
deleted file mode 100644
index a474de815d..0000000000
--- a/apps/plugins/puzzles/src/lightup.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3LIGHTUP_EXTRA = combi
4
5lightup : [X] GTK COMMON lightup LIGHTUP_EXTRA lightup-icon|no-icon
6
7lightup : [G] WINDOWS COMMON lightup LIGHTUP_EXTRA lightup.res|noicon.res
8
9lightupsolver : [U] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
10lightupsolver : [C] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
11
12ALL += lightup[COMBINED] LIGHTUP_EXTRA
13
14!begin am gtk
15GAMES += lightup
16!end
17
18!begin >list.c
19 A(lightup) \
20!end
21
22!begin >gamedesc.txt
23lightup:lightup.exe:Light Up:Light-bulb placing puzzle:Place bulbs to light up all the squares.
24!end
diff --git a/apps/plugins/puzzles/src/lightup.c b/apps/plugins/puzzles/src/lightup.c
index ab4be9ec87..e779663e01 100644
--- a/apps/plugins/puzzles/src/lightup.c
+++ b/apps/plugins/puzzles/src/lightup.c
@@ -47,7 +47,12 @@
47#include <string.h> 47#include <string.h>
48#include <assert.h> 48#include <assert.h>
49#include <ctype.h> 49#include <ctype.h>
50#include <math.h> 50#include <limits.h>
51#ifdef NO_TGMATH_H
52# include <math.h>
53#else
54# include <tgmath.h>
55#endif
51 56
52#include "puzzles.h" 57#include "puzzles.h"
53 58
@@ -58,7 +63,7 @@
58 */ 63 */
59#if defined STANDALONE_SOLVER 64#if defined STANDALONE_SOLVER
60#define SOLVER_DIAGNOSTICS 65#define SOLVER_DIAGNOSTICS
61int verbose = 0; 66static int verbose = 0;
62#undef debug 67#undef debug
63#define debug(x) printf x 68#define debug(x) printf x
64#elif defined SOLVER_DIAGNOSTICS 69#elif defined SOLVER_DIAGNOSTICS
@@ -346,6 +351,8 @@ static const char *validate_params(const game_params *params, bool full)
346{ 351{
347 if (params->w < 2 || params->h < 2) 352 if (params->w < 2 || params->h < 2)
348 return "Width and height must be at least 2"; 353 return "Width and height must be at least 2";
354 if (params->w > INT_MAX / params->h)
355 return "Width times height must not be unreasonably large";
349 if (full) { 356 if (full) {
350 if (params->blackpc < 5 || params->blackpc > 100) 357 if (params->blackpc < 5 || params->blackpc > 100)
351 return "Percentage of black squares must be between 5% and 100%"; 358 return "Percentage of black squares must be between 5% and 100%";
@@ -353,6 +360,8 @@ static const char *validate_params(const game_params *params, bool full)
353 if (params->symm == SYMM_ROT4) 360 if (params->symm == SYMM_ROT4)
354 return "4-fold symmetry is only available with square grids"; 361 return "4-fold symmetry is only available with square grids";
355 } 362 }
363 if ((params->symm == SYMM_ROT4 || params->symm == SYMM_REF4) && params->w < 3 && params->h < 3)
364 return "Width or height must be at least 3 for 4-way symmetry";
356 if (params->symm < 0 || params->symm >= SYMM_MAX) 365 if (params->symm < 0 || params->symm >= SYMM_MAX)
357 return "Unknown symmetry type"; 366 return "Unknown symmetry type";
358 if (params->difficulty < 0 || params->difficulty > DIFFCOUNT) 367 if (params->difficulty < 0 || params->difficulty > DIFFCOUNT)
@@ -411,6 +420,8 @@ static void debug_state(game_state *state)
411 int x, y; 420 int x, y;
412 char c = '?'; 421 char c = '?';
413 422
423 (void)c; /* placate -Wunused-but-set-variable if debug() does nothing */
424
414 for (y = 0; y < state->h; y++) { 425 for (y = 0; y < state->h; y++) {
415 for (x = 0; x < state->w; x++) { 426 for (x = 0; x < state->w; x++) {
416 c = '.'; 427 c = '.';
@@ -1822,30 +1833,65 @@ static char *game_text_format(const game_state *state)
1822struct game_ui { 1833struct game_ui {
1823 int cur_x, cur_y; 1834 int cur_x, cur_y;
1824 bool cur_visible; 1835 bool cur_visible;
1836
1837 /*
1838 * User preference: when a square contains both a black blob for
1839 * 'user is convinced this isn't a light' and a yellow highlight
1840 * for 'this square is lit by a light', both of which rule out it
1841 * being a light, should we still bother to show the blob?
1842 */
1843 bool draw_blobs_when_lit;
1825}; 1844};
1826 1845
1846static void legacy_prefs_override(struct game_ui *ui_out)
1847{
1848 static bool initialised = false;
1849 static int draw_blobs_when_lit = -1;
1850
1851 if (!initialised) {
1852 initialised = true;
1853 draw_blobs_when_lit = getenv_bool("LIGHTUP_LIT_BLOBS", -1);
1854 }
1855
1856 if (draw_blobs_when_lit != -1)
1857 ui_out->draw_blobs_when_lit = draw_blobs_when_lit;
1858}
1859
1827static game_ui *new_ui(const game_state *state) 1860static game_ui *new_ui(const game_state *state)
1828{ 1861{
1829 game_ui *ui = snew(game_ui); 1862 game_ui *ui = snew(game_ui);
1830 ui->cur_x = ui->cur_y = 0; 1863 ui->cur_x = ui->cur_y = 0;
1831 ui->cur_visible = false; 1864 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1865 ui->draw_blobs_when_lit = true;
1866 legacy_prefs_override(ui);
1832 return ui; 1867 return ui;
1833} 1868}
1834 1869
1835static void free_ui(game_ui *ui) 1870static config_item *get_prefs(game_ui *ui)
1836{ 1871{
1837 sfree(ui); 1872 config_item *ret;
1873
1874 ret = snewn(2, config_item);
1875
1876 ret[0].name = "Draw non-light marks even when lit";
1877 ret[0].kw = "show-lit-blobs";
1878 ret[0].type = C_BOOLEAN;
1879 ret[0].u.boolean.bval = ui->draw_blobs_when_lit;
1880
1881 ret[1].name = NULL;
1882 ret[1].type = C_END;
1883
1884 return ret;
1838} 1885}
1839 1886
1840static char *encode_ui(const game_ui *ui) 1887static void set_prefs(game_ui *ui, const config_item *cfg)
1841{ 1888{
1842 /* nothing to encode. */ 1889 ui->draw_blobs_when_lit = cfg[0].u.boolean.bval;
1843 return NULL;
1844} 1890}
1845 1891
1846static void decode_ui(game_ui *ui, const char *encoding) 1892static void free_ui(game_ui *ui)
1847{ 1893{
1848 /* nothing to decode. */ 1894 sfree(ui);
1849} 1895}
1850 1896
1851static void game_changed_state(game_ui *ui, const game_state *oldstate, 1897static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1855,6 +1901,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1855 ui->cur_visible = false; 1901 ui->cur_visible = false;
1856} 1902}
1857 1903
1904static const char *current_key_label(const game_ui *ui,
1905 const game_state *state, int button)
1906{
1907 int cx = ui->cur_x, cy = ui->cur_y;
1908 unsigned int flags = GRID(state, flags, cx, cy);
1909
1910 if (!ui->cur_visible) return "";
1911 if (button == CURSOR_SELECT) {
1912 if (flags & (F_BLACK | F_IMPOSSIBLE)) return "";
1913 if (flags & F_LIGHT) return "Clear";
1914 return "Light";
1915 }
1916 if (button == CURSOR_SELECT2) {
1917 if (flags & (F_BLACK | F_LIGHT)) return "";
1918 if (flags & F_IMPOSSIBLE) return "Clear";
1919 return "Mark";
1920 }
1921 return "";
1922}
1923
1858#define DF_BLACK 1 /* black square */ 1924#define DF_BLACK 1 /* black square */
1859#define DF_NUMBERED 2 /* black square with number */ 1925#define DF_NUMBERED 2 /* black square with number */
1860#define DF_LIT 4 /* display (white) square lit up */ 1926#define DF_LIT 4 /* display (white) square lit up */
@@ -1888,7 +1954,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1888 enum { NONE, FLIP_LIGHT, FLIP_IMPOSSIBLE } action = NONE; 1954 enum { NONE, FLIP_LIGHT, FLIP_IMPOSSIBLE } action = NONE;
1889 int cx = -1, cy = -1; 1955 int cx = -1, cy = -1;
1890 unsigned int flags; 1956 unsigned int flags;
1891 char buf[80], *nullret = UI_UPDATE, *empty = UI_UPDATE, c; 1957 char buf[80], *nullret = MOVE_NO_EFFECT, *empty = MOVE_UI_UPDATE, c;
1892 1958
1893 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1959 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1894 if (ui->cur_visible) 1960 if (ui->cur_visible)
@@ -1898,8 +1964,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1898 cy = FROMCOORD(y); 1964 cy = FROMCOORD(y);
1899 action = (button == LEFT_BUTTON) ? FLIP_LIGHT : FLIP_IMPOSSIBLE; 1965 action = (button == LEFT_BUTTON) ? FLIP_LIGHT : FLIP_IMPOSSIBLE;
1900 } else if (IS_CURSOR_SELECT(button) || 1966 } else if (IS_CURSOR_SELECT(button) ||
1901 button == 'i' || button == 'I' || 1967 button == 'i' || button == 'I') {
1902 button == ' ' || button == '\r' || button == '\n') {
1903 if (ui->cur_visible) { 1968 if (ui->cur_visible) {
1904 /* Only allow cursor-effect operations if the cursor is visible 1969 /* Only allow cursor-effect operations if the cursor is visible
1905 * (otherwise you have no idea which square it might be affecting) */ 1970 * (otherwise you have no idea which square it might be affecting) */
@@ -1910,11 +1975,10 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1910 } 1975 }
1911 ui->cur_visible = true; 1976 ui->cur_visible = true;
1912 } else if (IS_CURSOR_MOVE(button)) { 1977 } else if (IS_CURSOR_MOVE(button)) {
1913 move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, false); 1978 nullret = move_cursor(button, &ui->cur_x, &ui->cur_y,
1914 ui->cur_visible = true; 1979 state->w, state->h, false, &ui->cur_visible);
1915 nullret = empty;
1916 } else 1980 } else
1917 return NULL; 1981 return MOVE_UNUSED;
1918 1982
1919 switch (action) { 1983 switch (action) {
1920 case FLIP_LIGHT: 1984 case FLIP_LIGHT:
@@ -2002,7 +2066,7 @@ badmove:
2002 2066
2003/* XXX entirely cloned from fifteen.c; separate out? */ 2067/* XXX entirely cloned from fifteen.c; separate out? */
2004static void game_compute_size(const game_params *params, int tilesize, 2068static void game_compute_size(const game_params *params, int tilesize,
2005 int *x, int *y) 2069 const game_ui *ui, int *x, int *y)
2006{ 2070{
2007 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2071 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2008 struct { int tilesize; } ads, *ds = &ads; 2072 struct { int tilesize; } ads, *ds = &ads;
@@ -2111,7 +2175,7 @@ static unsigned int tile_flags(game_drawstate *ds, const game_state *state,
2111 return ret; 2175 return ret;
2112} 2176}
2113 2177
2114static void tile_redraw(drawing *dr, game_drawstate *ds, 2178static void tile_redraw(drawing *dr, game_drawstate *ds, const game_ui *ui,
2115 const game_state *state, int x, int y) 2179 const game_state *state, int x, int y)
2116{ 2180{
2117 unsigned int ds_flags = GRID(ds, flags, x, y); 2181 unsigned int ds_flags = GRID(ds, flags, x, y);
@@ -2141,13 +2205,7 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
2141 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS, 2205 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS,
2142 lcol, COL_BLACK); 2206 lcol, COL_BLACK);
2143 } else if ((ds_flags & DF_IMPOSSIBLE)) { 2207 } else if ((ds_flags & DF_IMPOSSIBLE)) {
2144 static int draw_blobs_when_lit = -1; 2208 if (!(ds_flags & DF_LIT) || ui->draw_blobs_when_lit) {
2145 if (draw_blobs_when_lit < 0) {
2146 char *env = getenv("LIGHTUP_LIT_BLOBS");
2147 draw_blobs_when_lit = (!env || (env[0] == 'y' ||
2148 env[0] == 'Y'));
2149 }
2150 if (!(ds_flags & DF_LIT) || draw_blobs_when_lit) {
2151 int rlen = TILE_SIZE / 4; 2209 int rlen = TILE_SIZE / 4;
2152 draw_rect(dr, dx + TILE_SIZE/2 - rlen/2, 2210 draw_rect(dr, dx + TILE_SIZE/2 - rlen/2,
2153 dy + TILE_SIZE/2 - rlen/2, 2211 dy + TILE_SIZE/2 - rlen/2,
@@ -2176,10 +2234,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2176 if (flashtime) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1; 2234 if (flashtime) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1;
2177 2235
2178 if (!ds->started) { 2236 if (!ds->started) {
2179 draw_rect(dr, 0, 0,
2180 TILE_SIZE * ds->w + 2 * BORDER,
2181 TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2182
2183 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, 2237 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
2184 TILE_SIZE * ds->w + 2, 2238 TILE_SIZE * ds->w + 2,
2185 TILE_SIZE * ds->h + 2, 2239 TILE_SIZE * ds->h + 2,
@@ -2196,7 +2250,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2196 unsigned int ds_flags = tile_flags(ds, state, ui, x, y, flashing); 2250 unsigned int ds_flags = tile_flags(ds, state, ui, x, y, flashing);
2197 if (ds_flags != GRID(ds, flags, x, y)) { 2251 if (ds_flags != GRID(ds, flags, x, y)) {
2198 GRID(ds, flags, x, y) = ds_flags; 2252 GRID(ds, flags, x, y) = ds_flags;
2199 tile_redraw(dr, ds, state, x, y); 2253 tile_redraw(dr, ds, ui, state, x, y);
2200 } 2254 }
2201 } 2255 }
2202 } 2256 }
@@ -2235,24 +2289,21 @@ static int game_status(const game_state *state)
2235 return state->completed ? +1 : 0; 2289 return state->completed ? +1 : 0;
2236} 2290}
2237 2291
2238static bool game_timing_state(const game_state *state, game_ui *ui) 2292static void game_print_size(const game_params *params, const game_ui *ui,
2239{ 2293 float *x, float *y)
2240 return true;
2241}
2242
2243static void game_print_size(const game_params *params, float *x, float *y)
2244{ 2294{
2245 int pw, ph; 2295 int pw, ph;
2246 2296
2247 /* 2297 /*
2248 * I'll use 6mm squares by default. 2298 * I'll use 6mm squares by default.
2249 */ 2299 */
2250 game_compute_size(params, 600, &pw, &ph); 2300 game_compute_size(params, 600, ui, &pw, &ph);
2251 *x = pw / 100.0F; 2301 *x = pw / 100.0F;
2252 *y = ph / 100.0F; 2302 *y = ph / 100.0F;
2253} 2303}
2254 2304
2255static void game_print(drawing *dr, const game_state *state, int tilesize) 2305static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2306 int tilesize)
2256{ 2307{
2257 int w = state->w, h = state->h; 2308 int w = state->w, h = state->h;
2258 int ink = print_mono_colour(dr, 0); 2309 int ink = print_mono_colour(dr, 0);
@@ -2323,12 +2374,14 @@ const struct game thegame = {
2323 free_game, 2374 free_game,
2324 true, solve_game, 2375 true, solve_game,
2325 true, game_can_format_as_text_now, game_text_format, 2376 true, game_can_format_as_text_now, game_text_format,
2377 get_prefs, set_prefs,
2326 new_ui, 2378 new_ui,
2327 free_ui, 2379 free_ui,
2328 encode_ui, 2380 NULL, /* encode_ui */
2329 decode_ui, 2381 NULL, /* decode_ui */
2330 NULL, /* game_request_keys */ 2382 NULL, /* game_request_keys */
2331 game_changed_state, 2383 game_changed_state,
2384 current_key_label,
2332 interpret_move, 2385 interpret_move,
2333 execute_move, 2386 execute_move,
2334 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2387 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2342,7 +2395,7 @@ const struct game thegame = {
2342 game_status, 2395 game_status,
2343 true, false, game_print_size, game_print, 2396 true, false, game_print_size, game_print,
2344 false, /* wants_statusbar */ 2397 false, /* wants_statusbar */
2345 false, game_timing_state, 2398 false, NULL, /* timing_state */
2346 0, /* flags */ 2399 0, /* flags */
2347}; 2400};
2348 2401
diff --git a/apps/plugins/puzzles/src/loopgen.c b/apps/plugins/puzzles/src/loopgen.c
index e738d4a26d..05097fe304 100644
--- a/apps/plugins/puzzles/src/loopgen.c
+++ b/apps/plugins/puzzles/src/loopgen.c
@@ -8,7 +8,11 @@
8#include <string.h> 8#include <string.h>
9#include <assert.h> 9#include <assert.h>
10#include <ctype.h> 10#include <ctype.h>
11#include <math.h> 11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
12 16
13#include "puzzles.h" 17#include "puzzles.h"
14#include "tree234.h" 18#include "tree234.h"
@@ -79,7 +83,7 @@ static bool can_colour_face(grid *g, char* board, int face_index,
79 enum face_colour colour) 83 enum face_colour colour)
80{ 84{
81 int i, j; 85 int i, j;
82 grid_face *test_face = g->faces + face_index; 86 grid_face *test_face = g->faces[face_index];
83 grid_face *starting_face, *current_face; 87 grid_face *starting_face, *current_face;
84 grid_dot *starting_dot; 88 grid_dot *starting_dot;
85 int transitions; 89 int transitions;
@@ -344,7 +348,7 @@ void generate_loop(grid *g, char *board, random_state *rs,
344 * to check every face of the board (the grid structure does not keep a 348 * to check every face of the board (the grid structure does not keep a
345 * list of the infinite face's neighbours). */ 349 * list of the infinite face's neighbours). */
346 for (i = 0; i < num_faces; i++) { 350 for (i = 0; i < num_faces; i++) {
347 grid_face *f = g->faces + i; 351 grid_face *f = g->faces[i];
348 struct face_score *fs = face_scores + i; 352 struct face_score *fs = face_scores + i;
349 if (board[i] != FACE_GREY) continue; 353 if (board[i] != FACE_GREY) continue;
350 /* We need the full colourability check here, it's not enough simply 354 /* We need the full colourability check here, it's not enough simply
@@ -426,7 +430,7 @@ void generate_loop(grid *g, char *board, random_state *rs,
426 del234(darkable_faces_sorted, fs); 430 del234(darkable_faces_sorted, fs);
427 431
428 /* Remember which face we've just coloured */ 432 /* Remember which face we've just coloured */
429 cur_face = g->faces + i; 433 cur_face = g->faces[i];
430 434
431 /* The face we've just coloured potentially affects the colourability 435 /* The face we've just coloured potentially affects the colourability
432 * and the scores of any neighbouring faces (touching at a corner or 436 * and the scores of any neighbouring faces (touching at a corner or
@@ -452,7 +456,7 @@ void generate_loop(grid *g, char *board, random_state *rs,
452 if (FACE_COLOUR(f) != FACE_GREY) continue; 456 if (FACE_COLOUR(f) != FACE_GREY) continue;
453 457
454 /* Find the face index and face_score* corresponding to f */ 458 /* Find the face index and face_score* corresponding to f */
455 fi = f - g->faces; 459 fi = f->index;
456 fs = face_scores + fi; 460 fs = face_scores + fi;
457 461
458 /* Remove from lightable list if it's in there. We do this, 462 /* Remove from lightable list if it's in there. We do this,
@@ -513,7 +517,7 @@ void generate_loop(grid *g, char *board, random_state *rs,
513 enum face_colour opp = 517 enum face_colour opp =
514 (board[j] == FACE_WHITE) ? FACE_BLACK : FACE_WHITE; 518 (board[j] == FACE_WHITE) ? FACE_BLACK : FACE_WHITE;
515 if (can_colour_face(g, board, j, opp)) { 519 if (can_colour_face(g, board, j, opp)) {
516 grid_face *face = g->faces +j; 520 grid_face *face = g->faces[j];
517 if (do_random_pass) { 521 if (do_random_pass) {
518 /* final random pass */ 522 /* final random pass */
519 if (!random_upto(rs, 10)) 523 if (!random_upto(rs, 10))
diff --git a/apps/plugins/puzzles/src/loopgen.h b/apps/plugins/puzzles/src/loopgen.h
index 079c87c576..bab95814d2 100644
--- a/apps/plugins/puzzles/src/loopgen.h
+++ b/apps/plugins/puzzles/src/loopgen.h
@@ -2,8 +2,8 @@
2 * loopgen.h: interface file for loop generation functions for grid.[ch]. 2 * loopgen.h: interface file for loop generation functions for grid.[ch].
3 */ 3 */
4 4
5#ifndef _LOOPGEN_H 5#ifndef PUZZLES_LOOPGEN_H
6#define _LOOPGEN_H 6#define PUZZLES_LOOPGEN_H
7 7
8#include "puzzles.h" 8#include "puzzles.h"
9#include "grid.h" 9#include "grid.h"
@@ -13,7 +13,7 @@ enum face_colour { FACE_WHITE, FACE_GREY, FACE_BLACK };
13/* face should be of type grid_face* here. */ 13/* face should be of type grid_face* here. */
14#define FACE_COLOUR(face) \ 14#define FACE_COLOUR(face) \
15 ( (face) == NULL ? FACE_BLACK : \ 15 ( (face) == NULL ? FACE_BLACK : \
16 board[(face) - g->faces] ) 16 board[(face)->index] )
17 17
18typedef int (*loopgen_bias_fn_t)(void *ctx, char *board, int face); 18typedef int (*loopgen_bias_fn_t)(void *ctx, char *board, int face);
19 19
diff --git a/apps/plugins/puzzles/src/loopy.R b/apps/plugins/puzzles/src/loopy.R
deleted file mode 100644
index f44560095d..0000000000
--- a/apps/plugins/puzzles/src/loopy.R
+++ /dev/null
@@ -1,31 +0,0 @@
1# -*- makefile -*-
2
3LOOPY_EXTRA = tree234 dsf grid penrose loopgen
4
5loopy : [X] GTK COMMON loopy LOOPY_EXTRA loopy-icon|no-icon
6
7loopy : [G] WINDOWS COMMON loopy LOOPY_EXTRA loopy.res|noicon.res
8
9loopysolver : [U] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE m.lib
10loopysolver : [C] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE
11
12#penrose : [U] penrose[TEST_PENROSE] STANDALONE m.lib
13#penrose : [C] penrose[TEST_PENROSE] STANDALONE
14
15#test-basis : [U] penrose[TEST_VECTORS] tree234 STANDALONE m.lib
16#test-basis : [C] penrose[TEST_VECTORS] tree234 STANDALONE
17
18
19ALL += loopy[COMBINED] LOOPY_EXTRA
20
21!begin am gtk
22GAMES += loopy
23!end
24
25!begin >list.c
26 A(loopy) \
27!end
28
29!begin >gamedesc.txt
30loopy:loopy.exe:Loopy:Loop-drawing puzzle:Draw a single closed loop, given clues about number of adjacent edges.
31!end
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
index 176b56285c..b30dd68f4d 100644
--- a/apps/plugins/puzzles/src/loopy.c
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -25,28 +25,28 @@
25 * outside respectively. So if you can track this for all 25 * outside respectively. So if you can track this for all
26 * faces, you figure out the state of the line between a pair 26 * faces, you figure out the state of the line between a pair
27 * once their relative insideness is known. 27 * once their relative insideness is known.
28 * + The way I envisage this working is simply to keep an edsf 28 * + The way I envisage this working is simply to keep a flip dsf
29 * of all _faces_, which indicates whether they're on 29 * of all _faces_, which indicates whether they're on
30 * opposite sides of the loop from one another. We also 30 * opposite sides of the loop from one another. We also
31 * include a special entry in the edsf for the infinite 31 * include a special entry in the dsf for the infinite
32 * exterior "face". 32 * exterior "face".
33 * + So, the simple way to do this is to just go through the 33 * + So, the simple way to do this is to just go through the
34 * edges: every time we see an edge in a state other than 34 * edges: every time we see an edge in a state other than
35 * LINE_UNKNOWN which separates two faces that aren't in the 35 * LINE_UNKNOWN which separates two faces that aren't in the
36 * same edsf class, we can rectify that by merging the 36 * same dsf class, we can rectify that by merging the
37 * classes. Then, conversely, an edge in LINE_UNKNOWN state 37 * classes. Then, conversely, an edge in LINE_UNKNOWN state
38 * which separates two faces that _are_ in the same edsf 38 * which separates two faces that _are_ in the same dsf
39 * class can immediately have its state determined. 39 * class can immediately have its state determined.
40 * + But you can go one better, if you're prepared to loop 40 * + But you can go one better, if you're prepared to loop
41 * over all _pairs_ of edges. Suppose we have edges A and B, 41 * over all _pairs_ of edges. Suppose we have edges A and B,
42 * which respectively separate faces A1,A2 and B1,B2. 42 * which respectively separate faces A1,A2 and B1,B2.
43 * Suppose that A,B are in the same edge-edsf class and that 43 * Suppose that A,B are in the same edge-dsf class and that
44 * A1,B1 (wlog) are in the same face-edsf class; then we can 44 * A1,B1 (wlog) are in the same face-dsf class; then we can
45 * immediately place A2,B2 into the same face-edsf class (as 45 * immediately place A2,B2 into the same face-dsf class (as
46 * each other, not as A1 and A2) one way round or the other. 46 * each other, not as A1 and A2) one way round or the other.
47 * And conversely again, if A1,B1 are in the same face-edsf 47 * And conversely again, if A1,B1 are in the same face-dsf
48 * class and so are A2,B2, then we can put A,B into the same 48 * class and so are A2,B2, then we can put A,B into the same
49 * face-edsf class. 49 * face-dsf class.
50 * * Of course, this deduction requires a quadratic-time 50 * * Of course, this deduction requires a quadratic-time
51 * loop over all pairs of edges in the grid, so it should 51 * loop over all pairs of edges in the grid, so it should
52 * be reserved until there's nothing easier left to be 52 * be reserved until there's nothing easier left to be
@@ -77,7 +77,11 @@
77#include <string.h> 77#include <string.h>
78#include <assert.h> 78#include <assert.h>
79#include <ctype.h> 79#include <ctype.h>
80#include <math.h> 80#ifdef NO_TGMATH_H
81# include <math.h>
82#else
83# include <tgmath.h>
84#endif
81 85
82#include "puzzles.h" 86#include "puzzles.h"
83#include "tree234.h" 87#include "tree234.h"
@@ -153,7 +157,7 @@ typedef struct solver_state {
153 char *face_yes_count; 157 char *face_yes_count;
154 char *face_no_count; 158 char *face_no_count;
155 bool *dot_solved, *face_solved; 159 bool *dot_solved, *face_solved;
156 int *dotdsf; 160 DSF *dotdsf;
157 161
158 /* Information for Normal level deductions: 162 /* Information for Normal level deductions:
159 * For each dline, store a bitmask for whether we know: 163 * For each dline, store a bitmask for whether we know:
@@ -162,7 +166,7 @@ typedef struct solver_state {
162 char *dlines; 166 char *dlines;
163 167
164 /* Hard level information */ 168 /* Hard level information */
165 int *linedsf; 169 DSF *linedsf;
166} solver_state; 170} solver_state;
167 171
168/* 172/*
@@ -279,6 +283,9 @@ static void check_caches(const solver_state* sstate);
279 A("Penrose (rhombs)",PENROSE_P3,3,3) \ 283 A("Penrose (rhombs)",PENROSE_P3,3,3) \
280 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \ 284 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \
281 A("Kagome",KAGOME,3,3) \ 285 A("Kagome",KAGOME,3,3) \
286 A("Compass-Dodecagonal",COMPASSDODECAGONAL,2,2) \
287 A("Hats",HATS,6,6) \
288 A("Spectres",SPECTRES,6,6) \
282 /* end of list */ 289 /* end of list */
283 290
284#define GRID_NAME(title,type,amin,omin) title, 291#define GRID_NAME(title,type,amin,omin) title,
@@ -380,7 +387,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
380 ret->solver_status = SOLVER_INCOMPLETE; 387 ret->solver_status = SOLVER_INCOMPLETE;
381 ret->diff = diff; 388 ret->diff = diff;
382 389
383 ret->dotdsf = snew_dsf(num_dots); 390 ret->dotdsf = dsf_new(num_dots);
384 ret->looplen = snewn(num_dots, int); 391 ret->looplen = snewn(num_dots, int);
385 392
386 for (i = 0; i < num_dots; i++) { 393 for (i = 0; i < num_dots; i++) {
@@ -411,7 +418,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
411 if (diff < DIFF_HARD) { 418 if (diff < DIFF_HARD) {
412 ret->linedsf = NULL; 419 ret->linedsf = NULL;
413 } else { 420 } else {
414 ret->linedsf = snew_dsf(state->game_grid->num_edges); 421 ret->linedsf = dsf_new_flip(state->game_grid->num_edges);
415 } 422 }
416 423
417 return ret; 424 return ret;
@@ -420,7 +427,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
420static void free_solver_state(solver_state *sstate) { 427static void free_solver_state(solver_state *sstate) {
421 if (sstate) { 428 if (sstate) {
422 free_game(sstate->state); 429 free_game(sstate->state);
423 sfree(sstate->dotdsf); 430 dsf_free(sstate->dotdsf);
424 sfree(sstate->looplen); 431 sfree(sstate->looplen);
425 sfree(sstate->dot_solved); 432 sfree(sstate->dot_solved);
426 sfree(sstate->face_solved); 433 sfree(sstate->face_solved);
@@ -431,7 +438,7 @@ static void free_solver_state(solver_state *sstate) {
431 438
432 /* OK, because sfree(NULL) is a no-op */ 439 /* OK, because sfree(NULL) is a no-op */
433 sfree(sstate->dlines); 440 sfree(sstate->dlines);
434 sfree(sstate->linedsf); 441 dsf_free(sstate->linedsf);
435 442
436 sfree(sstate); 443 sfree(sstate);
437 } 444 }
@@ -449,10 +456,9 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
449 ret->solver_status = sstate->solver_status; 456 ret->solver_status = sstate->solver_status;
450 ret->diff = sstate->diff; 457 ret->diff = sstate->diff;
451 458
452 ret->dotdsf = snewn(num_dots, int); 459 ret->dotdsf = dsf_new(num_dots);
453 ret->looplen = snewn(num_dots, int); 460 ret->looplen = snewn(num_dots, int);
454 memcpy(ret->dotdsf, sstate->dotdsf, 461 dsf_copy(ret->dotdsf, sstate->dotdsf);
455 num_dots * sizeof(int));
456 memcpy(ret->looplen, sstate->looplen, 462 memcpy(ret->looplen, sstate->looplen,
457 num_dots * sizeof(int)); 463 num_dots * sizeof(int));
458 464
@@ -480,9 +486,8 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
480 } 486 }
481 487
482 if (sstate->linedsf) { 488 if (sstate->linedsf) {
483 ret->linedsf = snewn(num_edges, int); 489 ret->linedsf = dsf_new_flip(num_edges);
484 memcpy(ret->linedsf, sstate->linedsf, 490 dsf_copy(ret->linedsf, sstate->linedsf);
485 num_edges * sizeof(int));
486 } else { 491 } else {
487 ret->linedsf = NULL; 492 ret->linedsf = NULL;
488 } 493 }
@@ -552,6 +557,9 @@ static const game_params loopy_presets_more[] = {
552 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 557 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
553 { 3, 3, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL }, 558 { 3, 3, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
554 { 3, 2, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL }, 559 { 3, 2, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
560 { 3, 3, DIFF_HARD, LOOPY_GRID_COMPASSDODECAGONAL },
561 { 6, 6, DIFF_HARD, LOOPY_GRID_HATS },
562 { 6, 6, DIFF_HARD, LOOPY_GRID_SPECTRES },
555#else 563#else
556 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, 564 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
557 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, 565 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
@@ -561,6 +569,9 @@ static const game_params loopy_presets_more[] = {
561 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 569 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
562 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL }, 570 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
563 { 5, 3, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL }, 571 { 5, 3, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
572 { 5, 4, DIFF_HARD, LOOPY_GRID_COMPASSDODECAGONAL },
573 { 10, 10, DIFF_HARD, LOOPY_GRID_HATS },
574 { 10, 10, DIFF_HARD, LOOPY_GRID_SPECTRES },
564#endif 575#endif
565}; 576};
566 577
@@ -681,6 +692,7 @@ static game_params *custom_params(const config_item *cfg)
681 692
682static const char *validate_params(const game_params *params, bool full) 693static const char *validate_params(const game_params *params, bool full)
683{ 694{
695 const char *err;
684 if (params->type < 0 || params->type >= NUM_GRID_TYPES) 696 if (params->type < 0 || params->type >= NUM_GRID_TYPES)
685 return "Illegal grid type"; 697 return "Illegal grid type";
686 if (params->w < grid_size_limits[params->type].amin || 698 if (params->w < grid_size_limits[params->type].amin ||
@@ -689,6 +701,8 @@ static const char *validate_params(const game_params *params, bool full)
689 if (params->w < grid_size_limits[params->type].omin && 701 if (params->w < grid_size_limits[params->type].omin &&
690 params->h < grid_size_limits[params->type].omin) 702 params->h < grid_size_limits[params->type].omin)
691 return grid_size_limits[params->type].oerr; 703 return grid_size_limits[params->type].oerr;
704 err = grid_validate_params(grid_types[params->type], params->w, params->h);
705 if (err != NULL) return err;
692 706
693 /* 707 /*
694 * This shouldn't be able to happen at all, since decode_params 708 * This shouldn't be able to happen at all, since decode_params
@@ -771,10 +785,13 @@ static const char *validate_desc(const game_params *params, const char *desc)
771 * know is the precise number of faces. */ 785 * know is the precise number of faces. */
772 grid_desc = extract_grid_desc(&desc); 786 grid_desc = extract_grid_desc(&desc);
773 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc); 787 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
774 if (ret) return ret; 788 if (ret) {
789 sfree(grid_desc);
790 return ret;
791 }
775 792
776 g = loopy_generate_grid(params, grid_desc); 793 g = loopy_generate_grid(params, grid_desc);
777 if (grid_desc) sfree(grid_desc); 794 sfree(grid_desc);
778 795
779 for (; *desc; ++desc) { 796 for (; *desc; ++desc) {
780 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) { 797 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
@@ -785,13 +802,18 @@ static const char *validate_desc(const game_params *params, const char *desc)
785 count += *desc - 'a' + 1; 802 count += *desc - 'a' + 1;
786 continue; 803 continue;
787 } 804 }
805 grid_free(g);
788 return "Unknown character in description"; 806 return "Unknown character in description";
789 } 807 }
790 808
791 if (count < g->num_faces) 809 if (count < g->num_faces) {
810 grid_free(g);
792 return "Description too short for board size"; 811 return "Description too short for board size";
793 if (count > g->num_faces) 812 }
813 if (count > g->num_faces) {
814 grid_free(g);
794 return "Description too long for board size"; 815 return "Description too long for board size";
816 }
795 817
796 grid_free(g); 818 grid_free(g);
797 819
@@ -850,22 +872,98 @@ static char *encode_solve_move(const game_state *state)
850 return ret; 872 return ret;
851} 873}
852 874
875struct game_ui {
876 /*
877 * User preference: should grid lines in LINE_NO state be drawn
878 * very faintly so users can still see where they are, or should
879 * they be completely invisible?
880 */
881 bool draw_faint_lines;
882
883 /*
884 * User preference: when clicking an edge that has only one
885 * possible edge connecting to one (or both) of its ends, should
886 * that edge also change to the same state as the edge we just
887 * clicked?
888 */
889 enum {
890 AF_OFF, /* no, all grid edges are independent in the UI */
891 AF_FIXED, /* yes, but only based on the grid itself */
892 AF_ADAPTIVE /* yes, and consider edges user has already set to NO */
893 } autofollow;
894};
895
896static void legacy_prefs_override(struct game_ui *ui_out)
897{
898 static bool initialised = false;
899 static int draw_faint_lines = -1;
900 static int autofollow = -1;
901
902 if (!initialised) {
903 char *env;
904
905 initialised = true;
906 draw_faint_lines = getenv_bool("LOOPY_FAINT_LINES", -1);
907
908 if ((env = getenv("LOOPY_AUTOFOLLOW")) != NULL) {
909 if (!strcmp(env, "off"))
910 autofollow = AF_OFF;
911 else if (!strcmp(env, "fixed"))
912 autofollow = AF_FIXED;
913 else if (!strcmp(env, "adaptive"))
914 autofollow = AF_ADAPTIVE;
915 }
916 }
917
918 if (draw_faint_lines != -1)
919 ui_out->draw_faint_lines = draw_faint_lines;
920 if (autofollow != -1)
921 ui_out->autofollow = autofollow;
922}
923
853static game_ui *new_ui(const game_state *state) 924static game_ui *new_ui(const game_state *state)
854{ 925{
855 return NULL; 926 game_ui *ui = snew(game_ui);
927 ui->draw_faint_lines = true;
928 ui->autofollow = AF_OFF;
929 legacy_prefs_override(ui);
930 return ui;
856} 931}
857 932
858static void free_ui(game_ui *ui) 933static void free_ui(game_ui *ui)
859{ 934{
935 sfree(ui);
860} 936}
861 937
862static char *encode_ui(const game_ui *ui) 938static config_item *get_prefs(game_ui *ui)
863{ 939{
864 return NULL; 940 config_item *ret;
941
942 ret = snewn(3, config_item);
943
944 ret[0].name = "Draw excluded grid lines faintly";
945 ret[0].kw = "draw-faint-lines";
946 ret[0].type = C_BOOLEAN;
947 ret[0].u.boolean.bval = ui->draw_faint_lines;
948
949 ret[1].name = "Auto-follow unique paths of edges";
950 ret[1].kw = "auto-follow";
951 ret[1].type = C_CHOICES;
952 ret[1].u.choices.choicenames =
953 ":No:Based on grid only:Based on grid and game state";
954 ret[1].u.choices.choicekws = ":off:fixed:adaptive";
955 ret[1].u.choices.selected = ui->autofollow;
956
957 ret[2].name = NULL;
958 ret[2].type = C_END;
959
960 return ret;
865} 961}
866 962
867static void decode_ui(game_ui *ui, const char *encoding) 963static void set_prefs(game_ui *ui, const config_item *cfg)
868{ 964{
965 ui->draw_faint_lines = cfg[0].u.boolean.bval;
966 ui->autofollow = cfg[1].u.choices.selected;
869} 967}
870 968
871static void game_changed_state(game_ui *ui, const game_state *oldstate, 969static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -874,7 +972,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
874} 972}
875 973
876static void game_compute_size(const game_params *params, int tilesize, 974static void game_compute_size(const game_params *params, int tilesize,
877 int *x, int *y) 975 const game_ui *ui, int *x, int *y)
878{ 976{
879 int grid_width, grid_height, rendered_width, rendered_height; 977 int grid_width, grid_height, rendered_width, rendered_height;
880 int g_tilesize; 978 int g_tilesize;
@@ -974,11 +1072,6 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
974 sfree(ds); 1072 sfree(ds);
975} 1073}
976 1074
977static bool game_timing_state(const game_state *state, game_ui *ui)
978{
979 return true;
980}
981
982static float game_anim_length(const game_state *oldstate, 1075static float game_anim_length(const game_state *oldstate,
983 const game_state *newstate, int dir, game_ui *ui) 1076 const game_state *newstate, int dir, game_ui *ui)
984{ 1077{
@@ -1004,7 +1097,7 @@ static char *game_text_format(const game_state *state)
1004 assert(state->grid_type == 0); 1097 assert(state->grid_type == 0);
1005 1098
1006 /* Work out the basic size unit */ 1099 /* Work out the basic size unit */
1007 f = g->faces; /* first face */ 1100 f = g->faces[0]; /* first face */
1008 assert(f->order == 4); 1101 assert(f->order == 4);
1009 /* The dots are ordered clockwise, so the two opposite 1102 /* The dots are ordered clockwise, so the two opposite
1010 * corners are guaranteed to span the square */ 1103 * corners are guaranteed to span the square */
@@ -1027,7 +1120,7 @@ static char *game_text_format(const game_state *state)
1027 1120
1028 /* Fill in edge info */ 1121 /* Fill in edge info */
1029 for (i = 0; i < g->num_edges; i++) { 1122 for (i = 0; i < g->num_edges; i++) {
1030 grid_edge *e = g->edges + i; 1123 grid_edge *e = g->edges[i];
1031 /* Cell coordinates, from (0,0) to (w-1,h-1) */ 1124 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1032 int x1 = (e->dot1->x - g->lowest_x) / cell_size; 1125 int x1 = (e->dot1->x - g->lowest_x) / cell_size;
1033 int x2 = (e->dot2->x - g->lowest_x) / cell_size; 1126 int x2 = (e->dot2->x - g->lowest_x) / cell_size;
@@ -1055,7 +1148,7 @@ static char *game_text_format(const game_state *state)
1055 for (i = 0; i < g->num_faces; i++) { 1148 for (i = 0; i < g->num_faces; i++) {
1056 int x1, x2, y1, y2; 1149 int x1, x2, y1, y2;
1057 1150
1058 f = g->faces + i; 1151 f = g->faces[i];
1059 assert(f->order == 4); 1152 assert(f->order == 4);
1060 /* Cell coordinates, from (0,0) to (w-1,h-1) */ 1153 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1061 x1 = (f->dots[0]->x - g->lowest_x) / cell_size; 1154 x1 = (f->dots[0]->x - g->lowest_x) / cell_size;
@@ -1135,26 +1228,26 @@ static bool solver_set_line(solver_state *sstate, int i,
1135#endif 1228#endif
1136 1229
1137 g = state->game_grid; 1230 g = state->game_grid;
1138 e = g->edges + i; 1231 e = g->edges[i];
1139 1232
1140 /* Update the cache for both dots and both faces affected by this. */ 1233 /* Update the cache for both dots and both faces affected by this. */
1141 if (line_new == LINE_YES) { 1234 if (line_new == LINE_YES) {
1142 sstate->dot_yes_count[e->dot1 - g->dots]++; 1235 sstate->dot_yes_count[e->dot1->index]++;
1143 sstate->dot_yes_count[e->dot2 - g->dots]++; 1236 sstate->dot_yes_count[e->dot2->index]++;
1144 if (e->face1) { 1237 if (e->face1) {
1145 sstate->face_yes_count[e->face1 - g->faces]++; 1238 sstate->face_yes_count[e->face1->index]++;
1146 } 1239 }
1147 if (e->face2) { 1240 if (e->face2) {
1148 sstate->face_yes_count[e->face2 - g->faces]++; 1241 sstate->face_yes_count[e->face2->index]++;
1149 } 1242 }
1150 } else { 1243 } else {
1151 sstate->dot_no_count[e->dot1 - g->dots]++; 1244 sstate->dot_no_count[e->dot1->index]++;
1152 sstate->dot_no_count[e->dot2 - g->dots]++; 1245 sstate->dot_no_count[e->dot2->index]++;
1153 if (e->face1) { 1246 if (e->face1) {
1154 sstate->face_no_count[e->face1 - g->faces]++; 1247 sstate->face_no_count[e->face1->index]++;
1155 } 1248 }
1156 if (e->face2) { 1249 if (e->face2) {
1157 sstate->face_no_count[e->face2 - g->faces]++; 1250 sstate->face_no_count[e->face2->index]++;
1158 } 1251 }
1159 } 1252 }
1160 1253
@@ -1178,10 +1271,10 @@ static bool merge_dots(solver_state *sstate, int edge_index)
1178{ 1271{
1179 int i, j, len; 1272 int i, j, len;
1180 grid *g = sstate->state->game_grid; 1273 grid *g = sstate->state->game_grid;
1181 grid_edge *e = g->edges + edge_index; 1274 grid_edge *e = g->edges[edge_index];
1182 1275
1183 i = e->dot1 - g->dots; 1276 i = e->dot1->index;
1184 j = e->dot2 - g->dots; 1277 j = e->dot2->index;
1185 1278
1186 i = dsf_canonify(sstate->dotdsf, i); 1279 i = dsf_canonify(sstate->dotdsf, i);
1187 j = dsf_canonify(sstate->dotdsf, j); 1280 j = dsf_canonify(sstate->dotdsf, j);
@@ -1211,12 +1304,12 @@ static bool merge_lines(solver_state *sstate, int i, int j, bool inverse
1211 assert(i < sstate->state->game_grid->num_edges); 1304 assert(i < sstate->state->game_grid->num_edges);
1212 assert(j < sstate->state->game_grid->num_edges); 1305 assert(j < sstate->state->game_grid->num_edges);
1213 1306
1214 i = edsf_canonify(sstate->linedsf, i, &inv_tmp); 1307 i = dsf_canonify_flip(sstate->linedsf, i, &inv_tmp);
1215 inverse ^= inv_tmp; 1308 inverse ^= inv_tmp;
1216 j = edsf_canonify(sstate->linedsf, j, &inv_tmp); 1309 j = dsf_canonify_flip(sstate->linedsf, j, &inv_tmp);
1217 inverse ^= inv_tmp; 1310 inverse ^= inv_tmp;
1218 1311
1219 edsf_merge(sstate->linedsf, i, j, inverse); 1312 dsf_merge_flip(sstate->linedsf, i, j, inverse);
1220 1313
1221#ifdef SHOW_WORKING 1314#ifdef SHOW_WORKING
1222 if (i != j) { 1315 if (i != j) {
@@ -1239,12 +1332,12 @@ static int dot_order(const game_state* state, int dot, char line_type)
1239{ 1332{
1240 int n = 0; 1333 int n = 0;
1241 grid *g = state->game_grid; 1334 grid *g = state->game_grid;
1242 grid_dot *d = g->dots + dot; 1335 grid_dot *d = g->dots[dot];
1243 int i; 1336 int i;
1244 1337
1245 for (i = 0; i < d->order; i++) { 1338 for (i = 0; i < d->order; i++) {
1246 grid_edge *e = d->edges[i]; 1339 grid_edge *e = d->edges[i];
1247 if (state->lines[e - g->edges] == line_type) 1340 if (state->lines[e->index] == line_type)
1248 ++n; 1341 ++n;
1249 } 1342 }
1250 return n; 1343 return n;
@@ -1256,12 +1349,12 @@ static int face_order(const game_state* state, int face, char line_type)
1256{ 1349{
1257 int n = 0; 1350 int n = 0;
1258 grid *g = state->game_grid; 1351 grid *g = state->game_grid;
1259 grid_face *f = g->faces + face; 1352 grid_face *f = g->faces[face];
1260 int i; 1353 int i;
1261 1354
1262 for (i = 0; i < f->order; i++) { 1355 for (i = 0; i < f->order; i++) {
1263 grid_edge *e = f->edges[i]; 1356 grid_edge *e = f->edges[i];
1264 if (state->lines[e - g->edges] == line_type) 1357 if (state->lines[e->index] == line_type)
1265 ++n; 1358 ++n;
1266 } 1359 }
1267 return n; 1360 return n;
@@ -1282,10 +1375,10 @@ static bool dot_setall(solver_state *sstate, int dot,
1282 return false; 1375 return false;
1283 1376
1284 g = state->game_grid; 1377 g = state->game_grid;
1285 d = g->dots + dot; 1378 d = g->dots[dot];
1286 1379
1287 for (i = 0; i < d->order; i++) { 1380 for (i = 0; i < d->order; i++) {
1288 int line_index = d->edges[i] - g->edges; 1381 int line_index = d->edges[i]->index;
1289 if (state->lines[line_index] == old_type) { 1382 if (state->lines[line_index] == old_type) {
1290 r = solver_set_line(sstate, line_index, new_type); 1383 r = solver_set_line(sstate, line_index, new_type);
1291 assert(r); 1384 assert(r);
@@ -1309,10 +1402,10 @@ static bool face_setall(solver_state *sstate, int face,
1309 return false; 1402 return false;
1310 1403
1311 g = state->game_grid; 1404 g = state->game_grid;
1312 f = g->faces + face; 1405 f = g->faces[face];
1313 1406
1314 for (i = 0; i < f->order; i++) { 1407 for (i = 0; i < f->order; i++) {
1315 int line_index = f->edges[i] - g->edges; 1408 int line_index = f->edges[i]->index;
1316 if (state->lines[line_index] == old_type) { 1409 if (state->lines[line_index] == old_type) {
1317 r = solver_set_line(sstate, line_index, new_type); 1410 r = solver_set_line(sstate, line_index, new_type);
1318 assert(r); 1411 assert(r);
@@ -1341,7 +1434,7 @@ static void add_full_clues(game_state *state, random_state *rs)
1341 * algorithm does work, and there aren't any GREY faces still there. */ 1434 * algorithm does work, and there aren't any GREY faces still there. */
1342 memset(clues, 0, g->num_faces); 1435 memset(clues, 0, g->num_faces);
1343 for (i = 0; i < g->num_edges; i++) { 1436 for (i = 0; i < g->num_edges; i++) {
1344 grid_edge *e = g->edges + i; 1437 grid_edge *e = g->edges[i];
1345 grid_face *f1 = e->face1; 1438 grid_face *f1 = e->face1;
1346 grid_face *f2 = e->face2; 1439 grid_face *f2 = e->face2;
1347 enum face_colour c1 = FACE_COLOUR(f1); 1440 enum face_colour c1 = FACE_COLOUR(f1);
@@ -1349,8 +1442,8 @@ static void add_full_clues(game_state *state, random_state *rs)
1349 assert(c1 != FACE_GREY); 1442 assert(c1 != FACE_GREY);
1350 assert(c2 != FACE_GREY); 1443 assert(c2 != FACE_GREY);
1351 if (c1 != c2) { 1444 if (c1 != c2) {
1352 if (f1) clues[f1 - g->faces]++; 1445 if (f1) clues[f1->index]++;
1353 if (f2) clues[f2 - g->faces]++; 1446 if (f2) clues[f2->index]++;
1354 } 1447 }
1355 } 1448 }
1356 sfree(board); 1449 sfree(board);
@@ -1361,7 +1454,7 @@ static bool game_has_unique_soln(const game_state *state, int diff)
1361{ 1454{
1362 bool ret; 1455 bool ret;
1363 solver_state *sstate_new; 1456 solver_state *sstate_new;
1364 solver_state *sstate = new_solver_state((game_state *)state, diff); 1457 solver_state *sstate = new_solver_state(state, diff);
1365 1458
1366 sstate_new = solve_game_rec(sstate); 1459 sstate_new = solve_game_rec(sstate);
1367 1460
@@ -1541,7 +1634,8 @@ static bool check_completion(game_state *state)
1541 grid *g = state->game_grid; 1634 grid *g = state->game_grid;
1542 int i; 1635 int i;
1543 bool ret; 1636 bool ret;
1544 int *dsf, *component_state; 1637 DSF *dsf;
1638 int *component_state;
1545 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize; 1639 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1546 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY }; 1640 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1547 1641
@@ -1626,13 +1720,13 @@ static bool check_completion(game_state *state)
1626 * leave that one unhighlighted, and light the rest up in red. 1720 * leave that one unhighlighted, and light the rest up in red.
1627 */ 1721 */
1628 1722
1629 dsf = snew_dsf(g->num_dots); 1723 dsf = dsf_new(g->num_dots);
1630 1724
1631 /* Build the dsf. */ 1725 /* Build the dsf. */
1632 for (i = 0; i < g->num_edges; i++) { 1726 for (i = 0; i < g->num_edges; i++) {
1633 if (state->lines[i] == LINE_YES) { 1727 if (state->lines[i] == LINE_YES) {
1634 grid_edge *e = g->edges + i; 1728 grid_edge *e = g->edges[i];
1635 int d1 = e->dot1 - g->dots, d2 = e->dot2 - g->dots; 1729 int d1 = e->dot1->index, d2 = e->dot2->index;
1636 dsf_merge(dsf, d1, d2); 1730 dsf_merge(dsf, d1, d2);
1637 } 1731 }
1638 } 1732 }
@@ -1657,10 +1751,10 @@ static bool check_completion(game_state *state)
1657 int unknown = dot_order(state, i, LINE_UNKNOWN); 1751 int unknown = dot_order(state, i, LINE_UNKNOWN);
1658 if ((yes == 1 && unknown == 0) || (yes >= 3)) { 1752 if ((yes == 1 && unknown == 0) || (yes >= 3)) {
1659 /* violation, so mark all YES edges as errors */ 1753 /* violation, so mark all YES edges as errors */
1660 grid_dot *d = g->dots + i; 1754 grid_dot *d = g->dots[i];
1661 int j; 1755 int j;
1662 for (j = 0; j < d->order; j++) { 1756 for (j = 0; j < d->order; j++) {
1663 int e = d->edges[j] - g->edges; 1757 int e = d->edges[j]->index;
1664 if (state->lines[e] == LINE_YES) 1758 if (state->lines[e] == LINE_YES)
1665 state->line_errors[e] = true; 1759 state->line_errors[e] = true;
1666 } 1760 }
@@ -1723,8 +1817,8 @@ static bool check_completion(game_state *state)
1723 */ 1817 */
1724 for (i = 0; i < g->num_edges; i++) { 1818 for (i = 0; i < g->num_edges; i++) {
1725 if (state->lines[i] == LINE_YES) { 1819 if (state->lines[i] == LINE_YES) {
1726 grid_edge *e = g->edges + i; 1820 grid_edge *e = g->edges[i];
1727 int d1 = e->dot1 - g->dots; /* either endpoint is good enough */ 1821 int d1 = e->dot1->index; /* either endpoint is good enough */
1728 int comp = dsf_canonify(dsf, d1); 1822 int comp = dsf_canonify(dsf, d1);
1729 if ((component_state[comp] == COMP_PATH && 1823 if ((component_state[comp] == COMP_PATH &&
1730 -1 != largest_comp) || 1824 -1 != largest_comp) ||
@@ -1763,7 +1857,7 @@ static bool check_completion(game_state *state)
1763 } 1857 }
1764 1858
1765 sfree(component_state); 1859 sfree(component_state);
1766 sfree(dsf); 1860 dsf_free(dsf);
1767 1861
1768 return ret; 1862 return ret;
1769} 1863}
@@ -1782,7 +1876,7 @@ static bool check_completion(game_state *state)
1782 * know both or neither is on that's already stored more directly.) 1876 * know both or neither is on that's already stored more directly.)
1783 * 1877 *
1784 * Advanced Mode 1878 * Advanced Mode
1785 * Use edsf data structure to make equivalence classes of lines that are 1879 * Use flip dsf data structure to make equivalence classes of lines that are
1786 * known identical to or opposite to one another. 1880 * known identical to or opposite to one another.
1787 */ 1881 */
1788 1882
@@ -1822,11 +1916,10 @@ static int dline_index_from_dot(grid *g, grid_dot *d, int i)
1822 if (i2 == d->order) i2 = 0; 1916 if (i2 == d->order) i2 = 0;
1823 e2 = d->edges[i2]; 1917 e2 = d->edges[i2];
1824#endif 1918#endif
1825 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); 1919 ret = 2 * (e->index) + ((e->dot1 == d) ? 1 : 0);
1826#ifdef DEBUG_DLINES 1920#ifdef DEBUG_DLINES
1827 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n", 1921 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n",
1828 (int)(d - g->dots), i, (int)(e - g->edges), 1922 (int)(d->index), i, (int)(e->index), (int)(e2 ->index), ret);
1829 (int)(e2 - g->edges), ret);
1830#endif 1923#endif
1831 return ret; 1924 return ret;
1832} 1925}
@@ -1845,11 +1938,10 @@ static int dline_index_from_face(grid *g, grid_face *f, int i)
1845 if (i2 < 0) i2 += f->order; 1938 if (i2 < 0) i2 += f->order;
1846 e2 = f->edges[i2]; 1939 e2 = f->edges[i2];
1847#endif 1940#endif
1848 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); 1941 ret = 2 * (e->index) + ((e->dot1 == d) ? 1 : 0);
1849#ifdef DEBUG_DLINES 1942#ifdef DEBUG_DLINES
1850 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n", 1943 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n",
1851 (int)(f - g->faces), i, (int)(e - g->edges), 1944 (int)(f->index), i, (int)(e->index), (int)(e2->index), ret);
1852 (int)(e2 - g->edges), ret);
1853#endif 1945#endif
1854 return ret; 1946 return ret;
1855} 1947}
@@ -1907,9 +1999,9 @@ static bool dline_set_opp_atleastone(solver_state *sstate,
1907 opp2 = opp + 1; 1999 opp2 = opp + 1;
1908 if (opp2 == N) opp2 = 0; 2000 if (opp2 == N) opp2 = 0;
1909 /* Check if opp, opp2 point to LINE_UNKNOWNs */ 2001 /* Check if opp, opp2 point to LINE_UNKNOWNs */
1910 if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN) 2002 if (state->lines[d->edges[opp]->index] != LINE_UNKNOWN)
1911 continue; 2003 continue;
1912 if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN) 2004 if (state->lines[d->edges[opp2]->index] != LINE_UNKNOWN)
1913 continue; 2005 continue;
1914 /* Found opposite UNKNOWNS and they're next to each other */ 2006 /* Found opposite UNKNOWNS and they're next to each other */
1915 opp_dline_index = dline_index_from_dot(g, d, opp); 2007 opp_dline_index = dline_index_from_dot(g, d, opp);
@@ -1931,24 +2023,24 @@ static bool face_setall_identical(solver_state *sstate, int face_index,
1931 bool retval = false; 2023 bool retval = false;
1932 game_state *state = sstate->state; 2024 game_state *state = sstate->state;
1933 grid *g = state->game_grid; 2025 grid *g = state->game_grid;
1934 grid_face *f = g->faces + face_index; 2026 grid_face *f = g->faces[face_index];
1935 int N = f->order; 2027 int N = f->order;
1936 int i, j; 2028 int i, j;
1937 int can1, can2; 2029 int can1, can2;
1938 bool inv1, inv2; 2030 bool inv1, inv2;
1939 2031
1940 for (i = 0; i < N; i++) { 2032 for (i = 0; i < N; i++) {
1941 int line1_index = f->edges[i] - g->edges; 2033 int line1_index = f->edges[i]->index;
1942 if (state->lines[line1_index] != LINE_UNKNOWN) 2034 if (state->lines[line1_index] != LINE_UNKNOWN)
1943 continue; 2035 continue;
1944 for (j = i + 1; j < N; j++) { 2036 for (j = i + 1; j < N; j++) {
1945 int line2_index = f->edges[j] - g->edges; 2037 int line2_index = f->edges[j]->index;
1946 if (state->lines[line2_index] != LINE_UNKNOWN) 2038 if (state->lines[line2_index] != LINE_UNKNOWN)
1947 continue; 2039 continue;
1948 2040
1949 /* Found two UNKNOWNS */ 2041 /* Found two UNKNOWNS */
1950 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); 2042 can1 = dsf_canonify_flip(sstate->linedsf, line1_index, &inv1);
1951 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); 2043 can2 = dsf_canonify_flip(sstate->linedsf, line2_index, &inv2);
1952 if (can1 == can2 && inv1 == inv2) { 2044 if (can1 == can2 && inv1 == inv2) {
1953 solver_set_line(sstate, line1_index, line_new); 2045 solver_set_line(sstate, line1_index, line_new);
1954 solver_set_line(sstate, line2_index, line_new); 2046 solver_set_line(sstate, line2_index, line_new);
@@ -1966,9 +2058,8 @@ static void find_unknowns(game_state *state,
1966 int *e /* Returned edge indices */) 2058 int *e /* Returned edge indices */)
1967{ 2059{
1968 int c = 0; 2060 int c = 0;
1969 grid *g = state->game_grid;
1970 while (c < expected_count) { 2061 while (c < expected_count) {
1971 int line_index = *edge_list - g->edges; 2062 int line_index = (*edge_list)->index;
1972 if (state->lines[line_index] == LINE_UNKNOWN) { 2063 if (state->lines[line_index] == LINE_UNKNOWN) {
1973 e[c] = line_index; 2064 e[c] = line_index;
1974 c++; 2065 c++;
@@ -1989,7 +2080,7 @@ static int parity_deductions(solver_state *sstate,
1989{ 2080{
1990 game_state *state = sstate->state; 2081 game_state *state = sstate->state;
1991 int diff = DIFF_MAX; 2082 int diff = DIFF_MAX;
1992 int *linedsf = sstate->linedsf; 2083 DSF *linedsf = sstate->linedsf;
1993 2084
1994 if (unknown_count == 2) { 2085 if (unknown_count == 2) {
1995 /* Lines are known alike/opposite, depending on inv. */ 2086 /* Lines are known alike/opposite, depending on inv. */
@@ -2002,9 +2093,9 @@ static int parity_deductions(solver_state *sstate,
2002 int can[3]; /* canonical edges */ 2093 int can[3]; /* canonical edges */
2003 bool inv[3]; /* whether can[x] is inverse to e[x] */ 2094 bool inv[3]; /* whether can[x] is inverse to e[x] */
2004 find_unknowns(state, edge_list, 3, e); 2095 find_unknowns(state, edge_list, 3, e);
2005 can[0] = edsf_canonify(linedsf, e[0], inv); 2096 can[0] = dsf_canonify_flip(linedsf, e[0], inv);
2006 can[1] = edsf_canonify(linedsf, e[1], inv+1); 2097 can[1] = dsf_canonify_flip(linedsf, e[1], inv+1);
2007 can[2] = edsf_canonify(linedsf, e[2], inv+2); 2098 can[2] = dsf_canonify_flip(linedsf, e[2], inv+2);
2008 if (can[0] == can[1]) { 2099 if (can[0] == can[1]) {
2009 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ? 2100 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ?
2010 LINE_YES : LINE_NO)) 2101 LINE_YES : LINE_NO))
@@ -2025,10 +2116,10 @@ static int parity_deductions(solver_state *sstate,
2025 int can[4]; /* canonical edges */ 2116 int can[4]; /* canonical edges */
2026 bool inv[4]; /* whether can[x] is inverse to e[x] */ 2117 bool inv[4]; /* whether can[x] is inverse to e[x] */
2027 find_unknowns(state, edge_list, 4, e); 2118 find_unknowns(state, edge_list, 4, e);
2028 can[0] = edsf_canonify(linedsf, e[0], inv); 2119 can[0] = dsf_canonify_flip(linedsf, e[0], inv);
2029 can[1] = edsf_canonify(linedsf, e[1], inv+1); 2120 can[1] = dsf_canonify_flip(linedsf, e[1], inv+1);
2030 can[2] = edsf_canonify(linedsf, e[2], inv+2); 2121 can[2] = dsf_canonify_flip(linedsf, e[2], inv+2);
2031 can[3] = edsf_canonify(linedsf, e[3], inv+3); 2122 can[3] = dsf_canonify_flip(linedsf, e[3], inv+3);
2032 if (can[0] == can[1]) { 2123 if (can[0] == can[1]) {
2033 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1])) 2124 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1]))
2034 diff = min(diff, DIFF_HARD); 2125 diff = min(diff, DIFF_HARD);
@@ -2097,7 +2188,7 @@ static int trivial_deductions(solver_state *sstate)
2097 2188
2098 /* Per-face deductions */ 2189 /* Per-face deductions */
2099 for (i = 0; i < g->num_faces; i++) { 2190 for (i = 0; i < g->num_faces; i++) {
2100 grid_face *f = g->faces + i; 2191 grid_face *f = g->faces[i];
2101 2192
2102 if (sstate->face_solved[i]) 2193 if (sstate->face_solved[i])
2103 continue; 2194 continue;
@@ -2155,22 +2246,22 @@ static int trivial_deductions(solver_state *sstate)
2155 int j, k, e1, e2, e, d; 2246 int j, k, e1, e2, e, d;
2156 2247
2157 for (j = 0; j < f->order; j++) { 2248 for (j = 0; j < f->order; j++) {
2158 e1 = f->edges[j] - g->edges; 2249 e1 = f->edges[j]->index;
2159 e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges; 2250 e2 = f->edges[j+1 < f->order ? j+1 : 0]->index;
2160 2251
2161 if (g->edges[e1].dot1 == g->edges[e2].dot1 || 2252 if (g->edges[e1]->dot1 == g->edges[e2]->dot1 ||
2162 g->edges[e1].dot1 == g->edges[e2].dot2) { 2253 g->edges[e1]->dot1 == g->edges[e2]->dot2) {
2163 d = g->edges[e1].dot1 - g->dots; 2254 d = g->edges[e1]->dot1->index;
2164 } else { 2255 } else {
2165 assert(g->edges[e1].dot2 == g->edges[e2].dot1 || 2256 assert(g->edges[e1]->dot2 == g->edges[e2]->dot1 ||
2166 g->edges[e1].dot2 == g->edges[e2].dot2); 2257 g->edges[e1]->dot2 == g->edges[e2]->dot2);
2167 d = g->edges[e1].dot2 - g->dots; 2258 d = g->edges[e1]->dot2->index;
2168 } 2259 }
2169 2260
2170 if (state->lines[e1] == LINE_UNKNOWN && 2261 if (state->lines[e1] == LINE_UNKNOWN &&
2171 state->lines[e2] == LINE_UNKNOWN) { 2262 state->lines[e2] == LINE_UNKNOWN) {
2172 for (k = 0; k < g->dots[d].order; k++) { 2263 for (k = 0; k < g->dots[d]->order; k++) {
2173 int e = g->dots[d].edges[k] - g->edges; 2264 int e = g->dots[d]->edges[k]->index;
2174 if (state->lines[e] == LINE_YES) 2265 if (state->lines[e] == LINE_YES)
2175 goto found; /* multi-level break */ 2266 goto found; /* multi-level break */
2176 } 2267 }
@@ -2184,7 +2275,7 @@ static int trivial_deductions(solver_state *sstate)
2184 * they're e1 and e2. 2275 * they're e1 and e2.
2185 */ 2276 */
2186 for (j = 0; j < f->order; j++) { 2277 for (j = 0; j < f->order; j++) {
2187 e = f->edges[j] - g->edges; 2278 e = f->edges[j]->index;
2188 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) { 2279 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) {
2189 bool r = solver_set_line(sstate, e, LINE_YES); 2280 bool r = solver_set_line(sstate, e, LINE_YES);
2190 assert(r); 2281 assert(r);
@@ -2198,7 +2289,7 @@ static int trivial_deductions(solver_state *sstate)
2198 2289
2199 /* Per-dot deductions */ 2290 /* Per-dot deductions */
2200 for (i = 0; i < g->num_dots; i++) { 2291 for (i = 0; i < g->num_dots; i++) {
2201 grid_dot *d = g->dots + i; 2292 grid_dot *d = g->dots[i];
2202 int yes, no, unknown; 2293 int yes, no, unknown;
2203 2294
2204 if (sstate->dot_solved[i]) 2295 if (sstate->dot_solved[i])
@@ -2290,12 +2381,12 @@ static int dline_deductions(solver_state *sstate)
2290 * on that. We check this with an assertion, in case someone decides to 2381 * on that. We check this with an assertion, in case someone decides to
2291 * make a grid which has larger faces than this. Note, this algorithm 2382 * make a grid which has larger faces than this. Note, this algorithm
2292 * could get quite expensive if there are many large faces. */ 2383 * could get quite expensive if there are many large faces. */
2293#define MAX_FACE_SIZE 12 2384#define MAX_FACE_SIZE 14
2294 2385
2295 for (i = 0; i < g->num_faces; i++) { 2386 for (i = 0; i < g->num_faces; i++) {
2296 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE]; 2387 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE];
2297 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE]; 2388 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE];
2298 grid_face *f = g->faces + i; 2389 grid_face *f = g->faces[i];
2299 int N = f->order; 2390 int N = f->order;
2300 int j,m; 2391 int j,m;
2301 int clue = state->clues[i]; 2392 int clue = state->clues[i];
@@ -2306,7 +2397,7 @@ static int dline_deductions(solver_state *sstate)
2306 2397
2307 /* Calculate the (j,j+1) entries */ 2398 /* Calculate the (j,j+1) entries */
2308 for (j = 0; j < N; j++) { 2399 for (j = 0; j < N; j++) {
2309 int edge_index = f->edges[j] - g->edges; 2400 int edge_index = f->edges[j]->index;
2310 int dline_index; 2401 int dline_index;
2311 enum line_state line1 = state->lines[edge_index]; 2402 enum line_state line1 = state->lines[edge_index];
2312 enum line_state line2; 2403 enum line_state line2;
@@ -2317,7 +2408,7 @@ static int dline_deductions(solver_state *sstate)
2317 mins[j][k] = (line1 == LINE_YES) ? 1 : 0; 2408 mins[j][k] = (line1 == LINE_YES) ? 1 : 0;
2318 /* Calculate the (j,j+2) entries */ 2409 /* Calculate the (j,j+2) entries */
2319 dline_index = dline_index_from_face(g, f, k); 2410 dline_index = dline_index_from_face(g, f, k);
2320 edge_index = f->edges[k] - g->edges; 2411 edge_index = f->edges[k]->index;
2321 line2 = state->lines[edge_index]; 2412 line2 = state->lines[edge_index];
2322 k++; 2413 k++;
2323 if (k >= N) k = 0; 2414 if (k >= N) k = 0;
@@ -2362,7 +2453,7 @@ static int dline_deductions(solver_state *sstate)
2362 for (j = 0; j < N; j++) { 2453 for (j = 0; j < N; j++) {
2363 int k; 2454 int k;
2364 grid_edge *e = f->edges[j]; 2455 grid_edge *e = f->edges[j];
2365 int line_index = e - g->edges; 2456 int line_index = e->index;
2366 int dline_index; 2457 int dline_index;
2367 2458
2368 if (state->lines[line_index] != LINE_UNKNOWN) 2459 if (state->lines[line_index] != LINE_UNKNOWN)
@@ -2397,7 +2488,7 @@ static int dline_deductions(solver_state *sstate)
2397 if (sstate->diff >= DIFF_TRICKY) { 2488 if (sstate->diff >= DIFF_TRICKY) {
2398 /* Now see if we can make dline deduction for edges{j,j+1} */ 2489 /* Now see if we can make dline deduction for edges{j,j+1} */
2399 e = f->edges[k]; 2490 e = f->edges[k];
2400 if (state->lines[e - g->edges] != LINE_UNKNOWN) 2491 if (state->lines[e->index] != LINE_UNKNOWN)
2401 /* Only worth doing this for an UNKNOWN,UNKNOWN pair. 2492 /* Only worth doing this for an UNKNOWN,UNKNOWN pair.
2402 * Dlines where one of the edges is known, are handled in the 2493 * Dlines where one of the edges is known, are handled in the
2403 * dot-deductions */ 2494 * dot-deductions */
@@ -2429,7 +2520,7 @@ static int dline_deductions(solver_state *sstate)
2429 /* ------ Dot deductions ------ */ 2520 /* ------ Dot deductions ------ */
2430 2521
2431 for (i = 0; i < g->num_dots; i++) { 2522 for (i = 0; i < g->num_dots; i++) {
2432 grid_dot *d = g->dots + i; 2523 grid_dot *d = g->dots[i];
2433 int N = d->order; 2524 int N = d->order;
2434 int yes, no, unknown; 2525 int yes, no, unknown;
2435 int j; 2526 int j;
@@ -2447,8 +2538,8 @@ static int dline_deductions(solver_state *sstate)
2447 k = j + 1; 2538 k = j + 1;
2448 if (k >= N) k = 0; 2539 if (k >= N) k = 0;
2449 dline_index = dline_index_from_dot(g, d, j); 2540 dline_index = dline_index_from_dot(g, d, j);
2450 line1_index = d->edges[j] - g->edges; 2541 line1_index = d->edges[j]->index;
2451 line2_index = d->edges[k] - g->edges; 2542 line2_index = d->edges[k] ->index;
2452 line1 = state->lines[line1_index]; 2543 line1 = state->lines[line1_index];
2453 line2 = state->lines[line2_index]; 2544 line2 = state->lines[line2_index];
2454 2545
@@ -2545,7 +2636,7 @@ static int dline_deductions(solver_state *sstate)
2545 int opp_index; 2636 int opp_index;
2546 if (opp == j || opp == k) 2637 if (opp == j || opp == k)
2547 continue; 2638 continue;
2548 opp_index = d->edges[opp] - g->edges; 2639 opp_index = d->edges[opp]->index;
2549 if (state->lines[opp_index] == LINE_UNKNOWN) { 2640 if (state->lines[opp_index] == LINE_UNKNOWN) {
2550 solver_set_line(sstate, opp_index, 2641 solver_set_line(sstate, opp_index,
2551 LINE_YES); 2642 LINE_YES);
@@ -2596,7 +2687,7 @@ static int linedsf_deductions(solver_state *sstate)
2596 if (clue < 0) 2687 if (clue < 0)
2597 continue; 2688 continue;
2598 2689
2599 N = g->faces[i].order; 2690 N = g->faces[i]->order;
2600 yes = sstate->face_yes_count[i]; 2691 yes = sstate->face_yes_count[i];
2601 if (yes + 1 == clue) { 2692 if (yes + 1 == clue) {
2602 if (face_setall_identical(sstate, i, LINE_NO)) 2693 if (face_setall_identical(sstate, i, LINE_NO))
@@ -2614,14 +2705,14 @@ static int linedsf_deductions(solver_state *sstate)
2614 2705
2615 /* Deductions with small number of LINE_UNKNOWNs, based on overall 2706 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2616 * parity of lines. */ 2707 * parity of lines. */
2617 diff_tmp = parity_deductions(sstate, g->faces[i].edges, 2708 diff_tmp = parity_deductions(sstate, g->faces[i]->edges,
2618 (clue - yes) % 2, unknown); 2709 (clue - yes) % 2, unknown);
2619 diff = min(diff, diff_tmp); 2710 diff = min(diff, diff_tmp);
2620 } 2711 }
2621 2712
2622 /* ------ Dot deductions ------ */ 2713 /* ------ Dot deductions ------ */
2623 for (i = 0; i < g->num_dots; i++) { 2714 for (i = 0; i < g->num_dots; i++) {
2624 grid_dot *d = g->dots + i; 2715 grid_dot *d = g->dots[i];
2625 int N = d->order; 2716 int N = d->order;
2626 int j; 2717 int j;
2627 int yes, no, unknown; 2718 int yes, no, unknown;
@@ -2634,17 +2725,17 @@ static int linedsf_deductions(solver_state *sstate)
2634 int can1, can2; 2725 int can1, can2;
2635 bool inv1, inv2; 2726 bool inv1, inv2;
2636 int j2; 2727 int j2;
2637 line1_index = d->edges[j] - g->edges; 2728 line1_index = d->edges[j]->index;
2638 if (state->lines[line1_index] != LINE_UNKNOWN) 2729 if (state->lines[line1_index] != LINE_UNKNOWN)
2639 continue; 2730 continue;
2640 j2 = j + 1; 2731 j2 = j + 1;
2641 if (j2 == N) j2 = 0; 2732 if (j2 == N) j2 = 0;
2642 line2_index = d->edges[j2] - g->edges; 2733 line2_index = d->edges[j2]->index;
2643 if (state->lines[line2_index] != LINE_UNKNOWN) 2734 if (state->lines[line2_index] != LINE_UNKNOWN)
2644 continue; 2735 continue;
2645 /* Infer dline flags from linedsf */ 2736 /* Infer dline flags from linedsf */
2646 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); 2737 can1 = dsf_canonify_flip(sstate->linedsf, line1_index, &inv1);
2647 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); 2738 can2 = dsf_canonify_flip(sstate->linedsf, line2_index, &inv2);
2648 if (can1 == can2 && inv1 != inv2) { 2739 if (can1 == can2 && inv1 != inv2) {
2649 /* These are opposites, so set dline atmostone/atleastone */ 2740 /* These are opposites, so set dline atmostone/atleastone */
2650 if (set_atmostone(dlines, dline_index)) 2741 if (set_atmostone(dlines, dline_index))
@@ -2679,7 +2770,7 @@ static int linedsf_deductions(solver_state *sstate)
2679 int can; 2770 int can;
2680 bool inv; 2771 bool inv;
2681 enum line_state s; 2772 enum line_state s;
2682 can = edsf_canonify(sstate->linedsf, i, &inv); 2773 can = dsf_canonify_flip(sstate->linedsf, i, &inv);
2683 if (can == i) 2774 if (can == i)
2684 continue; 2775 continue;
2685 s = sstate->state->lines[can]; 2776 s = sstate->state->lines[can];
@@ -2704,7 +2795,6 @@ static int loop_deductions(solver_state *sstate)
2704 game_state *state = sstate->state; 2795 game_state *state = sstate->state;
2705 grid *g = state->game_grid; 2796 grid *g = state->game_grid;
2706 int shortest_chainlen = g->num_dots; 2797 int shortest_chainlen = g->num_dots;
2707 bool loop_found = false;
2708 int dots_connected; 2798 int dots_connected;
2709 bool progress = false; 2799 bool progress = false;
2710 int i; 2800 int i;
@@ -2717,7 +2807,7 @@ static int loop_deductions(solver_state *sstate)
2717 */ 2807 */
2718 for (i = 0; i < g->num_edges; i++) { 2808 for (i = 0; i < g->num_edges; i++) {
2719 if (state->lines[i] == LINE_YES) { 2809 if (state->lines[i] == LINE_YES) {
2720 loop_found |= merge_dots(sstate, i); 2810 merge_dots(sstate, i);
2721 edgecount++; 2811 edgecount++;
2722 } 2812 }
2723 } 2813 }
@@ -2762,9 +2852,9 @@ static int loop_deductions(solver_state *sstate)
2762 * loop it would create is a solution. 2852 * loop it would create is a solution.
2763 */ 2853 */
2764 for (i = 0; i < g->num_edges; i++) { 2854 for (i = 0; i < g->num_edges; i++) {
2765 grid_edge *e = g->edges + i; 2855 grid_edge *e = g->edges[i];
2766 int d1 = e->dot1 - g->dots; 2856 int d1 = e->dot1->index;
2767 int d2 = e->dot2 - g->dots; 2857 int d2 = e->dot2->index;
2768 int eqclass, val; 2858 int eqclass, val;
2769 if (state->lines[i] != LINE_UNKNOWN) 2859 if (state->lines[i] != LINE_UNKNOWN)
2770 continue; 2860 continue;
@@ -2801,13 +2891,13 @@ static int loop_deductions(solver_state *sstate)
2801 */ 2891 */
2802 sm1_nearby = 0; 2892 sm1_nearby = 0;
2803 if (e->face1) { 2893 if (e->face1) {
2804 int f = e->face1 - g->faces; 2894 int f = e->face1->index;
2805 int c = state->clues[f]; 2895 int c = state->clues[f];
2806 if (c >= 0 && sstate->face_yes_count[f] == c - 1) 2896 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2807 sm1_nearby++; 2897 sm1_nearby++;
2808 } 2898 }
2809 if (e->face2) { 2899 if (e->face2) {
2810 int f = e->face2 - g->faces; 2900 int f = e->face2->index;
2811 int c = state->clues[f]; 2901 int c = state->clues[f];
2812 if (c >= 0 && sstate->face_yes_count[f] == c - 1) 2902 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2813 sm1_nearby++; 2903 sm1_nearby++;
@@ -2958,7 +3048,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2958 char button_char = ' '; 3048 char button_char = ' ';
2959 enum line_state old_state; 3049 enum line_state old_state;
2960 3050
2961 button &= ~MOD_MASK; 3051 button = STRIP_BUTTON_MODIFIERS(button);
2962 3052
2963 /* Convert mouse-click (x,y) to grid coordinates */ 3053 /* Convert mouse-click (x,y) to grid coordinates */
2964 x -= BORDER(ds->tilesize); 3054 x -= BORDER(ds->tilesize);
@@ -2972,7 +3062,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2972 if (e == NULL) 3062 if (e == NULL)
2973 return NULL; 3063 return NULL;
2974 3064
2975 i = e - g->edges; 3065 i = e->index;
2976 3066
2977 /* I think it's only possible to play this game with mouse clicks, sorry */ 3067 /* I think it's only possible to play this game with mouse clicks, sorry */
2978 /* Maybe will add mouse drag support some time */ 3068 /* Maybe will add mouse drag support some time */
@@ -3020,73 +3110,58 @@ static char *interpret_move(const game_state *state, game_ui *ui,
3020 movesize = 80; 3110 movesize = 80;
3021 movebuf = snewn(movesize, char); 3111 movebuf = snewn(movesize, char);
3022 movelen = sprintf(movebuf, "%d%c", i, (int)button_char); 3112 movelen = sprintf(movebuf, "%d%c", i, (int)button_char);
3023 {
3024 static enum { OFF, FIXED, ADAPTIVE, DUNNO } autofollow = DUNNO;
3025 if (autofollow == DUNNO) {
3026 const char *env = getenv("LOOPY_AUTOFOLLOW");
3027 if (env && !strcmp(env, "off"))
3028 autofollow = OFF;
3029 else if (env && !strcmp(env, "fixed"))
3030 autofollow = FIXED;
3031 else if (env && !strcmp(env, "adaptive"))
3032 autofollow = ADAPTIVE;
3033 else
3034 autofollow = OFF;
3035 }
3036 3113
3037 if (autofollow != OFF) { 3114 if (ui->autofollow != AF_OFF) {
3038 int dotid; 3115 int dotid;
3039 for (dotid = 0; dotid < 2; dotid++) { 3116 for (dotid = 0; dotid < 2; dotid++) {
3040 grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2); 3117 grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2);
3041 grid_edge *e_this = e; 3118 grid_edge *e_this = e;
3042 3119
3043 while (1) { 3120 while (1) {
3044 int j, n_found; 3121 int j, n_found;
3045 grid_edge *e_next = NULL; 3122 grid_edge *e_next = NULL;
3046 3123
3047 for (j = n_found = 0; j < dot->order; j++) { 3124 for (j = n_found = 0; j < dot->order; j++) {
3048 grid_edge *e_candidate = dot->edges[j]; 3125 grid_edge *e_candidate = dot->edges[j];
3049 int i_candidate = e_candidate - g->edges; 3126 int i_candidate = e_candidate->index;
3050 if (e_candidate != e_this && 3127 if (e_candidate != e_this &&
3051 (autofollow == FIXED || 3128 (ui->autofollow == AF_FIXED ||
3052 state->lines[i] == LINE_NO || 3129 state->lines[i] == LINE_NO ||
3053 state->lines[i_candidate] != LINE_NO)) { 3130 state->lines[i_candidate] != LINE_NO)) {
3054 e_next = e_candidate; 3131 e_next = e_candidate;
3055 n_found++; 3132 n_found++;
3056 }
3057 } 3133 }
3134 }
3058 3135
3059 if (n_found != 1 || 3136 if (n_found != 1 ||
3060 state->lines[e_next - g->edges] != state->lines[i]) 3137 state->lines[e_next->index] != state->lines[i])
3061 break; 3138 break;
3062 3139
3063 if (e_next == e) { 3140 if (e_next == e) {
3064 /* 3141 /*
3065 * Special case: we might have come all the 3142 * Special case: we might have come all the way
3066 * way round a loop and found our way back to 3143 * round a loop and found our way back to the same
3067 * the same edge we started from. In that 3144 * edge we started from. In that situation, we
3068 * situation, we must terminate not only this 3145 * must terminate not only this while loop, but
3069 * while loop, but the 'for' outside it that 3146 * the 'for' outside it that was tracing in both
3070 * was tracing in both directions from the 3147 * directions from the starting edge, because if
3071 * starting edge, because if we let it trace 3148 * we let it trace in the second direction then
3072 * in the second direction then we'll only 3149 * we'll only find ourself traversing the same
3073 * find ourself traversing the same loop in 3150 * loop in the other order and generate an encoded
3074 * the other order and generate an encoded 3151 * move string that mentions the same set of edges
3075 * move string that mentions the same set of 3152 * twice.
3076 * edges twice. 3153 */
3077 */ 3154 goto autofollow_done;
3078 goto autofollow_done; 3155 }
3079 }
3080 3156
3081 dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2); 3157 dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2);
3082 if (movelen > movesize - 40) { 3158 if (movelen > movesize - 40) {
3083 movesize = movesize * 5 / 4 + 128; 3159 movesize = movesize * 5 / 4 + 128;
3084 movebuf = sresize(movebuf, movesize, char); 3160 movebuf = sresize(movebuf, movesize, char);
3085 }
3086 e_this = e_next;
3087 movelen += sprintf(movebuf+movelen, "%d%c",
3088 (int)(e_this - g->edges), button_char);
3089 } 3161 }
3162 e_this = e_next;
3163 movelen += sprintf(movebuf+movelen, "%d%c",
3164 (int)(e_this->index), button_char);
3090 } 3165 }
3091 autofollow_done:; 3166 autofollow_done:;
3092 } 3167 }
@@ -3159,7 +3234,7 @@ static void grid_to_screen(const game_drawstate *ds, const grid *g,
3159static void face_text_pos(const game_drawstate *ds, const grid *g, 3234static void face_text_pos(const game_drawstate *ds, const grid *g,
3160 grid_face *f, int *xret, int *yret) 3235 grid_face *f, int *xret, int *yret)
3161{ 3236{
3162 int faceindex = f - g->faces; 3237 int faceindex = f->index;
3163 3238
3164 /* 3239 /*
3165 * Return the cached position for this face, if we've already 3240 * Return the cached position for this face, if we've already
@@ -3192,9 +3267,9 @@ static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
3192 /* There seems to be a certain amount of trial-and-error involved 3267 /* There seems to be a certain amount of trial-and-error involved
3193 * in working out the correct bounding-box for the text. */ 3268 * in working out the correct bounding-box for the text. */
3194 3269
3195 *x = xx - ds->tilesize/4 - 1; 3270 *x = xx - ds->tilesize * 5 / 4 - 1;
3196 *y = yy - ds->tilesize/4 - 3; 3271 *y = yy - ds->tilesize/4 - 3;
3197 *w = ds->tilesize/2 + 2; 3272 *w = ds->tilesize * 5 / 2 + 2;
3198 *h = ds->tilesize/2 + 5; 3273 *h = ds->tilesize/2 + 5;
3199} 3274}
3200 3275
@@ -3202,7 +3277,7 @@ static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3202 const game_state *state, int i) 3277 const game_state *state, int i)
3203{ 3278{
3204 grid *g = state->game_grid; 3279 grid *g = state->game_grid;
3205 grid_face *f = g->faces + i; 3280 grid_face *f = g->faces[i];
3206 int x, y; 3281 int x, y;
3207 char c[20]; 3282 char c[20];
3208 3283
@@ -3228,10 +3303,10 @@ static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
3228 grid_to_screen(ds, g, x1, y1, &x1, &y1); 3303 grid_to_screen(ds, g, x1, y1, &x1, &y1);
3229 grid_to_screen(ds, g, x2, y2, &x2, &y2); 3304 grid_to_screen(ds, g, x2, y2, &x2, &y2);
3230 /* Allow extra margin for dots, and thickness of lines */ 3305 /* Allow extra margin for dots, and thickness of lines */
3231 xmin = min(x1, x2) - 2; 3306 xmin = min(x1, x2) - (ds->tilesize + 15) / 16;
3232 xmax = max(x1, x2) + 2; 3307 xmax = max(x1, x2) + (ds->tilesize + 15) / 16;
3233 ymin = min(y1, y2) - 2; 3308 ymin = min(y1, y2) - (ds->tilesize + 15) / 16;
3234 ymax = max(y1, y2) + 2; 3309 ymax = max(y1, y2) + (ds->tilesize + 15) / 16;
3235 3310
3236 *x = xmin; 3311 *x = xmin;
3237 *y = ymin; 3312 *y = ymin;
@@ -3243,13 +3318,19 @@ static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
3243 int *x, int *y, int *w, int *h) 3318 int *x, int *y, int *w, int *h)
3244{ 3319{
3245 int x1, y1; 3320 int x1, y1;
3321 int xmin, xmax, ymin, ymax;
3246 3322
3247 grid_to_screen(ds, g, d->x, d->y, &x1, &y1); 3323 grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
3248 3324
3249 *x = x1 - 2; 3325 xmin = x1 - (ds->tilesize * 5 + 63) / 64;
3250 *y = y1 - 2; 3326 xmax = x1 + (ds->tilesize * 5 + 63) / 64;
3251 *w = 5; 3327 ymin = y1 - (ds->tilesize * 5 + 63) / 64;
3252 *h = 5; 3328 ymax = y1 + (ds->tilesize * 5 + 63) / 64;
3329
3330 *x = xmin;
3331 *y = ymin;
3332 *w = xmax - xmin + 1;
3333 *h = ymax - ymin + 1;
3253} 3334}
3254 3335
3255static const int loopy_line_redraw_phases[] = { 3336static const int loopy_line_redraw_phases[] = {
@@ -3257,11 +3338,11 @@ static const int loopy_line_redraw_phases[] = {
3257}; 3338};
3258#define NPHASES lenof(loopy_line_redraw_phases) 3339#define NPHASES lenof(loopy_line_redraw_phases)
3259 3340
3260static void game_redraw_line(drawing *dr, game_drawstate *ds, 3341static void game_redraw_line(drawing *dr, game_drawstate *ds,const game_ui *ui,
3261 const game_state *state, int i, int phase) 3342 const game_state *state, int i, int phase)
3262{ 3343{
3263 grid *g = state->game_grid; 3344 grid *g = state->game_grid;
3264 grid_edge *e = g->edges + i; 3345 grid_edge *e = g->edges[i];
3265 int x1, x2, y1, y2; 3346 int x1, x2, y1, y2;
3266 int line_colour; 3347 int line_colour;
3267 3348
@@ -3283,16 +3364,13 @@ static void game_redraw_line(drawing *dr, game_drawstate *ds,
3283 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); 3364 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3284 3365
3285 if (line_colour == COL_FAINT) { 3366 if (line_colour == COL_FAINT) {
3286 static int draw_faint_lines = -1; 3367 if (ui->draw_faint_lines)
3287 if (draw_faint_lines < 0) { 3368 draw_thick_line(dr, ds->tilesize/24.0,
3288 char *env = getenv("LOOPY_FAINT_LINES"); 3369 x1 + 0.5, y1 + 0.5,
3289 draw_faint_lines = (!env || (env[0] == 'y' || 3370 x2 + 0.5, y2 + 0.5,
3290 env[0] == 'Y')); 3371 line_colour);
3291 }
3292 if (draw_faint_lines)
3293 draw_line(dr, x1, y1, x2, y2, line_colour);
3294 } else { 3372 } else {
3295 draw_thick_line(dr, 3.0, 3373 draw_thick_line(dr, ds->tilesize*3/32.0,
3296 x1 + 0.5, y1 + 0.5, 3374 x1 + 0.5, y1 + 0.5,
3297 x2 + 0.5, y2 + 0.5, 3375 x2 + 0.5, y2 + 0.5,
3298 line_colour); 3376 line_colour);
@@ -3303,11 +3381,11 @@ static void game_redraw_dot(drawing *dr, game_drawstate *ds,
3303 const game_state *state, int i) 3381 const game_state *state, int i)
3304{ 3382{
3305 grid *g = state->game_grid; 3383 grid *g = state->game_grid;
3306 grid_dot *d = g->dots + i; 3384 grid_dot *d = g->dots[i];
3307 int x, y; 3385 int x, y;
3308 3386
3309 grid_to_screen(ds, g, d->x, d->y, &x, &y); 3387 grid_to_screen(ds, g, d->x, d->y, &x, &y);
3310 draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND); 3388 draw_circle(dr, x, y, ds->tilesize*2.5/32.0, COL_FOREGROUND, COL_FOREGROUND);
3311} 3389}
3312 3390
3313static bool boxes_intersect(int x0, int y0, int w0, int h0, 3391static bool boxes_intersect(int x0, int y0, int w0, int h0,
@@ -3322,7 +3400,7 @@ static bool boxes_intersect(int x0, int y0, int w0, int h0,
3322} 3400}
3323 3401
3324static void game_redraw_in_rect(drawing *dr, game_drawstate *ds, 3402static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3325 const game_state *state, 3403 const game_ui *ui, const game_state *state,
3326 int x, int y, int w, int h) 3404 int x, int y, int w, int h)
3327{ 3405{
3328 grid *g = state->game_grid; 3406 grid *g = state->game_grid;
@@ -3334,20 +3412,20 @@ static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3334 3412
3335 for (i = 0; i < g->num_faces; i++) { 3413 for (i = 0; i < g->num_faces; i++) {
3336 if (state->clues[i] >= 0) { 3414 if (state->clues[i] >= 0) {
3337 face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh); 3415 face_text_bbox(ds, g, g->faces[i], &bx, &by, &bw, &bh);
3338 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3416 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3339 game_redraw_clue(dr, ds, state, i); 3417 game_redraw_clue(dr, ds, state, i);
3340 } 3418 }
3341 } 3419 }
3342 for (phase = 0; phase < NPHASES; phase++) { 3420 for (phase = 0; phase < NPHASES; phase++) {
3343 for (i = 0; i < g->num_edges; i++) { 3421 for (i = 0; i < g->num_edges; i++) {
3344 edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh); 3422 edge_bbox(ds, g, g->edges[i], &bx, &by, &bw, &bh);
3345 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3423 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3346 game_redraw_line(dr, ds, state, i, phase); 3424 game_redraw_line(dr, ds, ui, state, i, phase);
3347 } 3425 }
3348 } 3426 }
3349 for (i = 0; i < g->num_dots; i++) { 3427 for (i = 0; i < g->num_dots; i++) {
3350 dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh); 3428 dot_bbox(ds, g, g->dots[i], &bx, &by, &bw, &bh);
3351 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3429 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3352 game_redraw_dot(dr, ds, state, i); 3430 game_redraw_dot(dr, ds, state, i);
3353 } 3431 }
@@ -3407,7 +3485,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3407 3485
3408 /* First, trundle through the faces. */ 3486 /* First, trundle through the faces. */
3409 for (i = 0; i < g->num_faces; i++) { 3487 for (i = 0; i < g->num_faces; i++) {
3410 grid_face *f = g->faces + i; 3488 grid_face *f = g->faces[i];
3411 int sides = f->order; 3489 int sides = f->order;
3412 int yes_order, no_order; 3490 int yes_order, no_order;
3413 bool clue_mistake; 3491 bool clue_mistake;
@@ -3500,26 +3578,26 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3500 int w = grid_width * ds->tilesize / g->tilesize; 3578 int w = grid_width * ds->tilesize / g->tilesize;
3501 int h = grid_height * ds->tilesize / g->tilesize; 3579 int h = grid_height * ds->tilesize / g->tilesize;
3502 3580
3503 game_redraw_in_rect(dr, ds, state, 3581 game_redraw_in_rect(dr, ds, ui, state,
3504 0, 0, w + 2*border + 1, h + 2*border + 1); 3582 0, 0, w + 2*border + 1, h + 2*border + 1);
3505 } else { 3583 } else {
3506 3584
3507 /* Right. Now we roll up our sleeves. */ 3585 /* Right. Now we roll up our sleeves. */
3508 3586
3509 for (i = 0; i < nfaces; i++) { 3587 for (i = 0; i < nfaces; i++) {
3510 grid_face *f = g->faces + faces[i]; 3588 grid_face *f = g->faces[faces[i]];
3511 int x, y, w, h; 3589 int x, y, w, h;
3512 3590
3513 face_text_bbox(ds, g, f, &x, &y, &w, &h); 3591 face_text_bbox(ds, g, f, &x, &y, &w, &h);
3514 game_redraw_in_rect(dr, ds, state, x, y, w, h); 3592 game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
3515 } 3593 }
3516 3594
3517 for (i = 0; i < nedges; i++) { 3595 for (i = 0; i < nedges; i++) {
3518 grid_edge *e = g->edges + edges[i]; 3596 grid_edge *e = g->edges[edges[i]];
3519 int x, y, w, h; 3597 int x, y, w, h;
3520 3598
3521 edge_bbox(ds, g, e, &x, &y, &w, &h); 3599 edge_bbox(ds, g, e, &x, &y, &w, &h);
3522 game_redraw_in_rect(dr, ds, state, x, y, w, h); 3600 game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
3523 } 3601 }
3524 } 3602 }
3525 3603
@@ -3550,19 +3628,21 @@ static int game_status(const game_state *state)
3550 return state->solved ? +1 : 0; 3628 return state->solved ? +1 : 0;
3551} 3629}
3552 3630
3553static void game_print_size(const game_params *params, float *x, float *y) 3631static void game_print_size(const game_params *params, const game_ui *ui,
3632 float *x, float *y)
3554{ 3633{
3555 int pw, ph; 3634 int pw, ph;
3556 3635
3557 /* 3636 /*
3558 * I'll use 7mm "squares" by default. 3637 * I'll use 7mm "squares" by default.
3559 */ 3638 */
3560 game_compute_size(params, 700, &pw, &ph); 3639 game_compute_size(params, 700, ui, &pw, &ph);
3561 *x = pw / 100.0F; 3640 *x = pw / 100.0F;
3562 *y = ph / 100.0F; 3641 *y = ph / 100.0F;
3563} 3642}
3564 3643
3565static void game_print(drawing *dr, const game_state *state, int tilesize) 3644static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3645 int tilesize)
3566{ 3646{
3567 int ink = print_mono_colour(dr, 0); 3647 int ink = print_mono_colour(dr, 0);
3568 int i; 3648 int i;
@@ -3577,7 +3657,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3577 3657
3578 for (i = 0; i < g->num_dots; i++) { 3658 for (i = 0; i < g->num_dots; i++) {
3579 int x, y; 3659 int x, y;
3580 grid_to_screen(ds, g, g->dots[i].x, g->dots[i].y, &x, &y); 3660 grid_to_screen(ds, g, g->dots[i]->x, g->dots[i]->y, &x, &y);
3581 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink); 3661 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink);
3582 } 3662 }
3583 3663
@@ -3585,7 +3665,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3585 * Clues. 3665 * Clues.
3586 */ 3666 */
3587 for (i = 0; i < g->num_faces; i++) { 3667 for (i = 0; i < g->num_faces; i++) {
3588 grid_face *f = g->faces + i; 3668 grid_face *f = g->faces[i];
3589 int clue = state->clues[i]; 3669 int clue = state->clues[i];
3590 if (clue >= 0) { 3670 if (clue >= 0) {
3591 char c[20]; 3671 char c[20];
@@ -3603,7 +3683,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3603 */ 3683 */
3604 for (i = 0; i < g->num_edges; i++) { 3684 for (i = 0; i < g->num_edges; i++) {
3605 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150; 3685 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150;
3606 grid_edge *e = g->edges + i; 3686 grid_edge *e = g->edges[i];
3607 int x1, y1, x2, y2; 3687 int x1, y1, x2, y2;
3608 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1); 3688 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3609 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); 3689 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
@@ -3666,14 +3746,16 @@ const struct game thegame = {
3666 new_game, 3746 new_game,
3667 dup_game, 3747 dup_game,
3668 free_game, 3748 free_game,
3669 1, solve_game, 3749 true, solve_game,
3670 true, game_can_format_as_text_now, game_text_format, 3750 true, game_can_format_as_text_now, game_text_format,
3751 get_prefs, set_prefs,
3671 new_ui, 3752 new_ui,
3672 free_ui, 3753 free_ui,
3673 encode_ui, 3754 NULL, /* encode_ui */
3674 decode_ui, 3755 NULL, /* decode_ui */
3675 NULL, /* game_request_keys */ 3756 NULL, /* game_request_keys */
3676 game_changed_state, 3757 game_changed_state,
3758 NULL, /* current_key_label */
3677 interpret_move, 3759 interpret_move,
3678 execute_move, 3760 execute_move,
3679 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3761 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3687,7 +3769,7 @@ const struct game thegame = {
3687 game_status, 3769 game_status,
3688 true, false, game_print_size, game_print, 3770 true, false, game_print_size, game_print,
3689 false /* wants_statusbar */, 3771 false /* wants_statusbar */,
3690 false, game_timing_state, 3772 false, NULL, /* timing_state */
3691 0, /* mouse_priorities */ 3773 0, /* mouse_priorities */
3692}; 3774};
3693 3775
diff --git a/apps/plugins/puzzles/src/magnets.R b/apps/plugins/puzzles/src/magnets.R
deleted file mode 100644
index e55e4746ee..0000000000
--- a/apps/plugins/puzzles/src/magnets.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3MAGNETS_EXTRA = laydomino
4
5magnets : [X] GTK COMMON magnets MAGNETS_EXTRA magnets-icon|no-icon
6
7magnets : [G] WINDOWS COMMON magnets MAGNETS_EXTRA magnets.res|noicon.res
8
9magnetssolver : [U] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE m.lib
10magnetssolver : [C] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE
11
12ALL += magnets[COMBINED] MAGNETS_EXTRA
13
14!begin am gtk
15GAMES += magnets
16!end
17
18!begin >list.c
19 A(magnets) \
20!end
21
22!begin >gamedesc.txt
23magnets:magnets.exe:Magnets:Magnet-placing puzzle:Place magnets to satisfy the clues and avoid like poles touching.
24!end
diff --git a/apps/plugins/puzzles/src/magnets.c b/apps/plugins/puzzles/src/magnets.c
index edbb8490ad..7cb5fbc919 100644
--- a/apps/plugins/puzzles/src/magnets.c
+++ b/apps/plugins/puzzles/src/magnets.c
@@ -36,12 +36,17 @@
36#include <string.h> 36#include <string.h>
37#include <assert.h> 37#include <assert.h>
38#include <ctype.h> 38#include <ctype.h>
39#include <math.h> 39#include <limits.h>
40#ifdef NO_TGMATH_H
41# include <math.h>
42#else
43# include <tgmath.h>
44#endif
40 45
41#include "puzzles.h" 46#include "puzzles.h"
42 47
43#ifdef STANDALONE_SOLVER 48#ifdef STANDALONE_SOLVER
44bool verbose = 0; 49static bool verbose = false;
45#endif 50#endif
46 51
47enum { 52enum {
@@ -230,8 +235,17 @@ static game_params *custom_params(const config_item *cfg)
230 235
231static const char *validate_params(const game_params *params, bool full) 236static const char *validate_params(const game_params *params, bool full)
232{ 237{
233 if (params->w < 2) return "Width must be at least one"; 238 if (params->w < 2) return "Width must be at least two";
234 if (params->h < 2) return "Height must be at least one"; 239 if (params->h < 2) return "Height must be at least two";
240 if (params->w > INT_MAX / params->h)
241 return "Width times height must not be unreasonably large";
242 if (params->diff >= DIFF_TRICKY) {
243 if (params->w < 5 && params->h < 5)
244 return "Either width or height must be at least five for Tricky";
245 } else {
246 if (params->w < 3 && params->h < 3)
247 return "Either width or height must be at least three";
248 }
235 if (params->diff < 0 || params->diff >= DIFFCOUNT) 249 if (params->diff < 0 || params->diff >= DIFFCOUNT)
236 return "Unknown difficulty level"; 250 return "Unknown difficulty level";
237 251
@@ -510,7 +524,9 @@ nextchar:
510 * (i.e. each end points to the other) */ 524 * (i.e. each end points to the other) */
511 for (idx = 0; idx < state->wh; idx++) { 525 for (idx = 0; idx < state->wh; idx++) {
512 if (state->common->dominoes[idx] < 0 || 526 if (state->common->dominoes[idx] < 0 ||
513 state->common->dominoes[idx] > state->wh || 527 state->common->dominoes[idx] >= state->wh ||
528 (state->common->dominoes[idx] % state->w != idx % state->w &&
529 state->common->dominoes[idx] / state->w != idx / state->w) ||
514 state->common->dominoes[state->common->dominoes[idx]] != idx) { 530 state->common->dominoes[state->common->dominoes[idx]] != idx) {
515 *prob = "Domino descriptions inconsistent"; 531 *prob = "Domino descriptions inconsistent";
516 goto done; 532 goto done;
@@ -541,7 +557,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
541{ 557{
542 const char *prob; 558 const char *prob;
543 game_state *st = new_game_int(params, desc, &prob); 559 game_state *st = new_game_int(params, desc, &prob);
544 if (!st) return (char*)prob; 560 if (!st) return prob;
545 free_game(st); 561 free_game(st);
546 return NULL; 562 return NULL;
547} 563}
@@ -1574,6 +1590,7 @@ static int lay_dominoes(game_state *state, random_state *rs, int *scratch)
1574 } 1590 }
1575 1591
1576 debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2)); 1592 debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2));
1593 (void)nlaid;
1577 game_debug(state, "Final layout"); 1594 game_debug(state, "Final layout");
1578 return ret; 1595 return ret;
1579} 1596}
@@ -1720,7 +1737,7 @@ static game_ui *new_ui(const game_state *state)
1720{ 1737{
1721 game_ui *ui = snew(game_ui); 1738 game_ui *ui = snew(game_ui);
1722 ui->cur_x = ui->cur_y = 0; 1739 ui->cur_x = ui->cur_y = 0;
1723 ui->cur_visible = false; 1740 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1724 return ui; 1741 return ui;
1725} 1742}
1726 1743
@@ -1729,15 +1746,6 @@ static void free_ui(game_ui *ui)
1729 sfree(ui); 1746 sfree(ui);
1730} 1747}
1731 1748
1732static char *encode_ui(const game_ui *ui)
1733{
1734 return NULL;
1735}
1736
1737static void decode_ui(game_ui *ui, const char *encoding)
1738{
1739}
1740
1741static void game_changed_state(game_ui *ui, const game_state *oldstate, 1749static void game_changed_state(game_ui *ui, const game_state *oldstate,
1742 const game_state *newstate) 1750 const game_state *newstate)
1743{ 1751{
@@ -1745,6 +1753,36 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1745 ui->cur_visible = false; 1753 ui->cur_visible = false;
1746} 1754}
1747 1755
1756static const char *current_key_label(const game_ui *ui,
1757 const game_state *state, int button)
1758{
1759 int idx;
1760
1761 if (IS_CURSOR_SELECT(button)) {
1762 if (!ui->cur_visible) return "";
1763 idx = ui->cur_y * state->w + ui->cur_x;
1764 if (button == CURSOR_SELECT) {
1765 if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET)
1766 return "";
1767 switch (state->grid[idx]) {
1768 case EMPTY: return "+";
1769 case POSITIVE: return "-";
1770 case NEGATIVE: return "Clear";
1771 }
1772 }
1773 if (button == CURSOR_SELECT2) {
1774 if (state->grid[idx] != NEUTRAL) return "";
1775 if (state->flags[idx] & GS_SET) /* neutral */
1776 return "?";
1777 if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */
1778 return "Clear";
1779 else
1780 return "X";
1781 }
1782 }
1783 return "";
1784}
1785
1748struct game_drawstate { 1786struct game_drawstate {
1749 int tilesize; 1787 int tilesize;
1750 bool started, solved; 1788 bool started, solved;
@@ -1805,14 +1843,13 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1805 char *nullret = NULL, buf[80], movech; 1843 char *nullret = NULL, buf[80], movech;
1806 enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action; 1844 enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action;
1807 1845
1808 if (IS_CURSOR_MOVE(button)) { 1846 if (IS_CURSOR_MOVE(button))
1809 move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, false); 1847 return move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h,
1810 ui->cur_visible = true; 1848 false, &ui->cur_visible);
1811 return UI_UPDATE; 1849 else if (IS_CURSOR_SELECT(button)) {
1812 } else if (IS_CURSOR_SELECT(button)) {
1813 if (!ui->cur_visible) { 1850 if (!ui->cur_visible) {
1814 ui->cur_visible = true; 1851 ui->cur_visible = true;
1815 return UI_UPDATE; 1852 return MOVE_UI_UPDATE;
1816 } 1853 }
1817 action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL; 1854 action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL;
1818 gx = ui->cur_x; 1855 gx = ui->cur_x;
@@ -1821,7 +1858,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1821 (button == LEFT_BUTTON || button == RIGHT_BUTTON)) { 1858 (button == LEFT_BUTTON || button == RIGHT_BUTTON)) {
1822 if (ui->cur_visible) { 1859 if (ui->cur_visible) {
1823 ui->cur_visible = false; 1860 ui->cur_visible = false;
1824 nullret = UI_UPDATE; 1861 nullret = MOVE_UI_UPDATE;
1825 } 1862 }
1826 action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL; 1863 action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL;
1827 } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) { 1864 } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) {
@@ -1928,7 +1965,7 @@ badmove:
1928 */ 1965 */
1929 1966
1930static void game_compute_size(const game_params *params, int tilesize, 1967static void game_compute_size(const game_params *params, int tilesize,
1931 int *x, int *y) 1968 const game_ui *ui, int *x, int *y)
1932{ 1969{
1933 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1970 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1934 struct { int tilesize; } ads, *ds = &ads; 1971 struct { int tilesize; } ads, *ds = &ads;
@@ -2205,12 +2242,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2205 flash = (int)(flashtime * 5 / FLASH_TIME) % 2; 2242 flash = (int)(flashtime * 5 / FLASH_TIME) % 2;
2206 2243
2207 if (!ds->started) { 2244 if (!ds->started) {
2208 /* draw background, corner +-. */ 2245 /* draw corner +-. */
2209 draw_rect(dr, 0, 0,
2210 TILE_SIZE * (w+2) + 2 * BORDER,
2211 TILE_SIZE * (h+2) + 2 * BORDER,
2212 COL_BACKGROUND);
2213
2214 draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT); 2246 draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT);
2215 draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT); 2247 draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT);
2216 2248
@@ -2309,24 +2341,21 @@ static int game_status(const game_state *state)
2309 return state->completed ? +1 : 0; 2341 return state->completed ? +1 : 0;
2310} 2342}
2311 2343
2312static bool game_timing_state(const game_state *state, game_ui *ui) 2344static void game_print_size(const game_params *params, const game_ui *ui,
2313{ 2345 float *x, float *y)
2314 return true;
2315}
2316
2317static void game_print_size(const game_params *params, float *x, float *y)
2318{ 2346{
2319 int pw, ph; 2347 int pw, ph;
2320 2348
2321 /* 2349 /*
2322 * I'll use 6mm squares by default. 2350 * I'll use 6mm squares by default.
2323 */ 2351 */
2324 game_compute_size(params, 600, &pw, &ph); 2352 game_compute_size(params, 600, ui, &pw, &ph);
2325 *x = pw / 100.0F; 2353 *x = pw / 100.0F;
2326 *y = ph / 100.0F; 2354 *y = ph / 100.0F;
2327} 2355}
2328 2356
2329static void game_print(drawing *dr, const game_state *state, int tilesize) 2357static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2358 int tilesize)
2330{ 2359{
2331 int w = state->w, h = state->h; 2360 int w = state->w, h = state->h;
2332 int ink = print_mono_colour(dr, 0); 2361 int ink = print_mono_colour(dr, 0);
@@ -2430,12 +2459,14 @@ const struct game thegame = {
2430 free_game, 2459 free_game,
2431 true, solve_game, 2460 true, solve_game,
2432 true, game_can_format_as_text_now, game_text_format, 2461 true, game_can_format_as_text_now, game_text_format,
2462 NULL, NULL, /* get_prefs, set_prefs */
2433 new_ui, 2463 new_ui,
2434 free_ui, 2464 free_ui,
2435 encode_ui, 2465 NULL, /* encode_ui */
2436 decode_ui, 2466 NULL, /* decode_ui */
2437 NULL, /* game_request_keys */ 2467 NULL, /* game_request_keys */
2438 game_changed_state, 2468 game_changed_state,
2469 current_key_label,
2439 interpret_move, 2470 interpret_move,
2440 execute_move, 2471 execute_move,
2441 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2472 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2449,7 +2480,7 @@ const struct game thegame = {
2449 game_status, 2480 game_status,
2450 true, false, game_print_size, game_print, 2481 true, false, game_print_size, game_print,
2451 false, /* wants_statusbar */ 2482 false, /* wants_statusbar */
2452 false, game_timing_state, 2483 false, NULL, /* timing_state */
2453 REQUIRE_RBUTTON, /* flags */ 2484 REQUIRE_RBUTTON, /* flags */
2454}; 2485};
2455 2486
@@ -2458,14 +2489,14 @@ const struct game thegame = {
2458#include <time.h> 2489#include <time.h>
2459#include <stdarg.h> 2490#include <stdarg.h>
2460 2491
2461const char *quis = NULL; 2492static const char *quis = NULL;
2462bool csv = false; 2493static bool csv = false;
2463 2494
2464void usage(FILE *out) { 2495static void usage(FILE *out) {
2465 fprintf(out, "usage: %s [-v] [--print] <params>|<game id>\n", quis); 2496 fprintf(out, "usage: %s [-v] [--print] <params>|<game id>\n", quis);
2466} 2497}
2467 2498
2468void doprint(game_state *state) 2499static void doprint(game_state *state)
2469{ 2500{
2470 char *fmt = game_text_format(state); 2501 char *fmt = game_text_format(state);
2471 printf("%s", fmt); 2502 printf("%s", fmt);
@@ -2559,7 +2590,7 @@ static void start_soak(game_params *p, random_state *rs)
2559 sfree(aux); 2590 sfree(aux);
2560} 2591}
2561 2592
2562int main(int argc, const char *argv[]) 2593int main(int argc, char *argv[])
2563{ 2594{
2564 bool print = false, soak = false, solved = false; 2595 bool print = false, soak = false, solved = false;
2565 int ret; 2596 int ret;
@@ -2608,7 +2639,7 @@ int main(int argc, const char *argv[])
2608 decode_params(p, id); 2639 decode_params(p, id);
2609 err = validate_params(p, true); 2640 err = validate_params(p, true);
2610 if (err) { 2641 if (err) {
2611 fprintf(stderr, "%s: %s", argv[0], err); 2642 fprintf(stderr, "%s: %s\n", argv[0], err);
2612 goto done; 2643 goto done;
2613 } 2644 }
2614 2645
diff --git a/apps/plugins/puzzles/src/malloc.c b/apps/plugins/puzzles/src/malloc.c
deleted file mode 100644
index a7fa7c5adc..0000000000
--- a/apps/plugins/puzzles/src/malloc.c
+++ /dev/null
@@ -1,53 +0,0 @@
1/*
2 * malloc.c: safe wrappers around malloc, realloc, free, strdup
3 */
4
5#include <stdlib.h>
6#include <string.h>
7#include "puzzles.h"
8
9/*
10 * smalloc should guarantee to return a useful pointer - Halibut
11 * can do nothing except die when it's out of memory anyway.
12 */
13void *smalloc(size_t size) {
14 void *p;
15 p = malloc(size);
16 if (!p)
17 fatal("out of memory");
18 return p;
19}
20
21/*
22 * sfree should guaranteeably deal gracefully with freeing NULL
23 */
24void sfree(void *p) {
25 if (p) {
26 free(p);
27 }
28}
29
30/*
31 * srealloc should guaranteeably be able to realloc NULL
32 */
33void *srealloc(void *p, size_t size) {
34 void *q;
35 if (p) {
36 q = realloc(p, size);
37 } else {
38 q = malloc(size);
39 }
40 if (!q)
41 fatal("out of memory");
42 return q;
43}
44
45/*
46 * dupstr is like strdup, but with the never-return-NULL property
47 * of smalloc (and also reliably defined in all environments :-)
48 */
49char *dupstr(const char *s) {
50 char *r = smalloc(1+strlen(s));
51 strcpy(r,s);
52 return r;
53}
diff --git a/apps/plugins/puzzles/src/map.R b/apps/plugins/puzzles/src/map.R
deleted file mode 100644
index 1e702aa19d..0000000000
--- a/apps/plugins/puzzles/src/map.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3MAP_EXTRA = dsf
4
5map : [X] GTK COMMON map MAP_EXTRA map-icon|no-icon
6
7map : [G] WINDOWS COMMON map MAP_EXTRA map.res|noicon.res
8
9mapsolver : [U] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE m.lib
10mapsolver : [C] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE
11
12ALL += map[COMBINED] MAP_EXTRA
13
14!begin am gtk
15GAMES += map
16!end
17
18!begin >list.c
19 A(map) \
20!end
21
22!begin >gamedesc.txt
23map:map.exe:Map:Map-colouring puzzle:Colour the map so that adjacent regions are never the same colour.
24!end
diff --git a/apps/plugins/puzzles/src/map.c b/apps/plugins/puzzles/src/map.c
index 9df2d22b52..2ef156e72a 100644
--- a/apps/plugins/puzzles/src/map.c
+++ b/apps/plugins/puzzles/src/map.c
@@ -14,7 +14,12 @@
14#include <string.h> 14#include <string.h>
15#include <assert.h> 15#include <assert.h>
16#include <ctype.h> 16#include <ctype.h>
17#include <math.h> 17#include <limits.h>
18#ifdef NO_TGMATH_H
19# include <math.h>
20#else
21# include <tgmath.h>
22#endif
18 23
19#include "puzzles.h" 24#include "puzzles.h"
20 25
@@ -25,7 +30,7 @@
25 */ 30 */
26#if defined STANDALONE_SOLVER 31#if defined STANDALONE_SOLVER
27#define SOLVER_DIAGNOSTICS 32#define SOLVER_DIAGNOSTICS
28bool verbose = false; 33static bool verbose = false;
29#elif defined SOLVER_DIAGNOSTICS 34#elif defined SOLVER_DIAGNOSTICS
30#define verbose true 35#define verbose true
31#endif 36#endif
@@ -41,12 +46,6 @@ bool verbose = false;
41#define SIX (FOUR+2) 46#define SIX (FOUR+2)
42 47
43/* 48/*
44 * Ghastly run-time configuration option, just for Gareth (again).
45 */
46static int flash_type = -1;
47static float flash_length;
48
49/*
50 * Difficulty levels. I do some macro ickery here to ensure that my 49 * Difficulty levels. I do some macro ickery here to ensure that my
51 * enum and the various forms of my name list always match up. 50 * enum and the various forms of my name list always match up.
52 */ 51 */
@@ -180,7 +179,9 @@ static void decode_params(game_params *params, char const *string)
180 params->n = atoi(p); 179 params->n = atoi(p);
181 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; 180 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
182 } else { 181 } else {
183 params->n = params->w * params->h / 8; 182 if (params->h > 0 && params->w > 0 &&
183 params->w <= INT_MAX / params->h)
184 params->n = params->w * params->h / 8;
184 } 185 }
185 if (*p == 'd') { 186 if (*p == 'd') {
186 int i; 187 int i;
@@ -252,6 +253,8 @@ static const char *validate_params(const game_params *params, bool full)
252{ 253{
253 if (params->w < 2 || params->h < 2) 254 if (params->w < 2 || params->h < 2)
254 return "Width and height must be at least two"; 255 return "Width and height must be at least two";
256 if (params->w > INT_MAX / 2 / params->h)
257 return "Width times height must not be unreasonably large";
255 if (params->n < 5) 258 if (params->n < 5)
256 return "Must have at least five regions"; 259 return "Must have at least five regions";
257 if (params->n > params->w * params->h) 260 if (params->n > params->w * params->h)
@@ -1659,6 +1662,10 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1659 } 1662 }
1660 } 1663 }
1661 1664
1665 if (retlen + 10 >= retsize) {
1666 retsize = retlen + 256;
1667 ret = sresize(ret, retsize, char);
1668 }
1662 ret[retlen++] = 'a'-1 + run; 1669 ret[retlen++] = 'a'-1 + run;
1663 ret[retlen++] = ','; 1670 ret[retlen++] = ',';
1664 1671
@@ -1711,8 +1718,8 @@ static const char *parse_edge_list(const game_params *params,
1711 int i, k, pos; 1718 int i, k, pos;
1712 bool state; 1719 bool state;
1713 const char *p = *desc; 1720 const char *p = *desc;
1714 1721 const char *err = NULL;
1715 dsf_init(map+wh, wh); 1722 DSF *dsf = dsf_new(wh);
1716 1723
1717 pos = -1; 1724 pos = -1;
1718 state = false; 1725 state = false;
@@ -1723,8 +1730,10 @@ static const char *parse_edge_list(const game_params *params,
1723 * pairs of squares whenever the edge list shows a non-edge). 1730 * pairs of squares whenever the edge list shows a non-edge).
1724 */ 1731 */
1725 while (*p && *p != ',') { 1732 while (*p && *p != ',') {
1726 if (*p < 'a' || *p > 'z') 1733 if (*p < 'a' || *p > 'z') {
1727 return "Unexpected character in edge list"; 1734 err = "Unexpected character in edge list";
1735 goto out;
1736 }
1728 if (*p == 'z') 1737 if (*p == 'z')
1729 k = 25; 1738 k = 25;
1730 else 1739 else
@@ -1747,10 +1756,12 @@ static const char *parse_edge_list(const game_params *params,
1747 y = (pos - w*(h-1)) % h; 1756 y = (pos - w*(h-1)) % h;
1748 dx = 1; 1757 dx = 1;
1749 dy = 0; 1758 dy = 0;
1750 } else 1759 } else {
1751 return "Too much data in edge list"; 1760 err = "Too much data in edge list";
1761 goto out;
1762 }
1752 if (!state) 1763 if (!state)
1753 dsf_merge(map+wh, y*w+x, (y+dy)*w+(x+dx)); 1764 dsf_merge(dsf, y*w+x, (y+dy)*w+(x+dx));
1754 1765
1755 pos++; 1766 pos++;
1756 } 1767 }
@@ -1759,8 +1770,10 @@ static const char *parse_edge_list(const game_params *params,
1759 p++; 1770 p++;
1760 } 1771 }
1761 assert(pos <= 2*wh-w-h); 1772 assert(pos <= 2*wh-w-h);
1762 if (pos < 2*wh-w-h) 1773 if (pos < 2*wh-w-h) {
1763 return "Too little data in edge list"; 1774 err = "Too little data in edge list";
1775 goto out;
1776 }
1764 1777
1765 /* 1778 /*
1766 * Now go through again and allocate region numbers. 1779 * Now go through again and allocate region numbers.
@@ -1769,17 +1782,22 @@ static const char *parse_edge_list(const game_params *params,
1769 for (i = 0; i < wh; i++) 1782 for (i = 0; i < wh; i++)
1770 map[i] = -1; 1783 map[i] = -1;
1771 for (i = 0; i < wh; i++) { 1784 for (i = 0; i < wh; i++) {
1772 k = dsf_canonify(map+wh, i); 1785 k = dsf_canonify(dsf, i);
1773 if (map[k] < 0) 1786 if (map[k] < 0)
1774 map[k] = pos++; 1787 map[k] = pos++;
1775 map[i] = map[k]; 1788 map[i] = map[k];
1776 } 1789 }
1777 if (pos != n) 1790 if (pos != n) {
1778 return "Edge list defines the wrong number of regions"; 1791 err = "Edge list defines the wrong number of regions";
1792 goto out;
1793 }
1779 1794
1780 *desc = p; 1795 *desc = p;
1796 err = NULL; /* no error */
1781 1797
1782 return NULL; 1798 out:
1799 dsf_free(dsf);
1800 return err;
1783} 1801}
1784 1802
1785static const char *validate_desc(const game_params *params, const char *desc) 1803static const char *validate_desc(const game_params *params, const char *desc)
@@ -1789,7 +1807,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
1789 int *map; 1807 int *map;
1790 const char *ret; 1808 const char *ret;
1791 1809
1792 map = snewn(2*wh, int); 1810 map = snewn(wh, int);
1793 ret = parse_edge_list(params, &desc, map); 1811 ret = parse_edge_list(params, &desc, map);
1794 sfree(map); 1812 sfree(map);
1795 if (ret) 1813 if (ret)
@@ -2252,16 +2270,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
2252 return dupstr(aux); 2270 return dupstr(aux);
2253} 2271}
2254 2272
2255static bool game_can_format_as_text_now(const game_params *params)
2256{
2257 return true;
2258}
2259
2260static char *game_text_format(const game_state *state)
2261{
2262 return NULL;
2263}
2264
2265struct game_ui { 2273struct game_ui {
2266 /* 2274 /*
2267 * drag_colour: 2275 * drag_colour:
@@ -2275,11 +2283,41 @@ struct game_ui {
2275 int drag_pencil; 2283 int drag_pencil;
2276 int dragx, dragy; 2284 int dragx, dragy;
2277 bool show_numbers; 2285 bool show_numbers;
2286 bool large_stipples;
2278 2287
2279 int cur_x, cur_y, cur_lastmove; 2288 int cur_x, cur_y, cur_lastmove;
2280 bool cur_visible, cur_moved; 2289 bool cur_visible, cur_moved;
2290
2291 /*
2292 * User preference to enable alternative versions of the
2293 * completion flash. Some users have found the colour-cycling
2294 * default version to be a bit eye-twisting.
2295 */
2296 enum {
2297 FLASH_CYCLIC, /* cycle the four colours of the map */
2298 FLASH_EACH_TO_WHITE, /* turn each colour white in turn */
2299 FLASH_ALL_TO_WHITE /* flash the whole map to white in one go */
2300 } flash_type;
2281}; 2301};
2282 2302
2303static void legacy_prefs_override(struct game_ui *ui_out)
2304{
2305 static bool initialised = false;
2306 static int flash_type = -1;
2307
2308 if (!initialised) {
2309 char *env;
2310
2311 initialised = true;
2312
2313 if ((env = getenv("MAP_ALTERNATIVE_FLASH")) != NULL)
2314 flash_type = FLASH_EACH_TO_WHITE;
2315 }
2316
2317 if (flash_type != -1)
2318 ui_out->flash_type = flash_type;
2319}
2320
2283static game_ui *new_ui(const game_state *state) 2321static game_ui *new_ui(const game_state *state)
2284{ 2322{
2285 game_ui *ui = snew(game_ui); 2323 game_ui *ui = snew(game_ui);
@@ -2288,24 +2326,56 @@ static game_ui *new_ui(const game_state *state)
2288 ui->drag_pencil = 0; 2326 ui->drag_pencil = 0;
2289 ui->show_numbers = false; 2327 ui->show_numbers = false;
2290 ui->cur_x = ui->cur_y = 0; 2328 ui->cur_x = ui->cur_y = 0;
2291 ui->cur_visible = false; 2329 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2292 ui->cur_moved = false; 2330 ui->cur_moved = false;
2293 ui->cur_lastmove = 0; 2331 ui->cur_lastmove = 0;
2332 ui->flash_type = FLASH_CYCLIC;
2333 ui->large_stipples = false;
2334 legacy_prefs_override(ui);
2294 return ui; 2335 return ui;
2295} 2336}
2296 2337
2297static void free_ui(game_ui *ui) 2338static config_item *get_prefs(game_ui *ui)
2298{ 2339{
2299 sfree(ui); 2340 config_item *ret;
2341
2342 ret = snewn(4, config_item);
2343
2344 ret[0].name = "Victory flash effect";
2345 ret[0].kw = "flash-type";
2346 ret[0].type = C_CHOICES;
2347 ret[0].u.choices.choicenames = ":Cyclic:Each to white:All to white";
2348 ret[0].u.choices.choicekws = ":cyclic:each-white:all-white";
2349 ret[0].u.choices.selected = ui->flash_type;
2350
2351 ret[1].name = "Number regions";
2352 ret[1].kw = "show-numbers";
2353 ret[1].type = C_BOOLEAN;
2354 ret[1].u.boolean.bval = ui->show_numbers;
2355
2356 ret[2].name = "Display style for stipple marks";
2357 ret[2].kw = "stipple-style";
2358 ret[2].type = C_CHOICES;
2359 ret[2].u.choices.choicenames = ":Small:Large";
2360 ret[2].u.choices.choicekws = ":small:large";
2361 ret[2].u.choices.selected = ui->large_stipples;
2362
2363 ret[3].name = NULL;
2364 ret[3].type = C_END;
2365
2366 return ret;
2300} 2367}
2301 2368
2302static char *encode_ui(const game_ui *ui) 2369static void set_prefs(game_ui *ui, const config_item *cfg)
2303{ 2370{
2304 return NULL; 2371 ui->flash_type = cfg[0].u.choices.selected;
2372 ui->show_numbers = cfg[1].u.boolean.bval;
2373 ui->large_stipples = cfg[2].u.choices.selected;
2305} 2374}
2306 2375
2307static void decode_ui(game_ui *ui, const char *encoding) 2376static void free_ui(game_ui *ui)
2308{ 2377{
2378 sfree(ui);
2309} 2379}
2310 2380
2311static void game_changed_state(game_ui *ui, const game_state *oldstate, 2381static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -2388,6 +2458,26 @@ static int region_from_ui_cursor(const game_state *state, const game_ui *ui)
2388 EPSILON_Y(ui->cur_lastmove)); 2458 EPSILON_Y(ui->cur_lastmove));
2389} 2459}
2390 2460
2461static const char *current_key_label(const game_ui *ui,
2462 const game_state *state, int button)
2463{
2464 int r;
2465
2466 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
2467 if (ui->drag_colour == -2) return "Pick";
2468 r = region_from_ui_cursor(state, ui);
2469 if (state->map->immutable[r]) return "Cancel";
2470 if (!ui->cur_moved) return ui->drag_pencil ? "Cancel" : "Clear";
2471 if (button == CURSOR_SELECT2) {
2472 if (state->colouring[r] >= 0) return "Cancel";
2473 if (ui->drag_colour >= 0) return "Stipple";
2474 }
2475 if (ui->drag_pencil) return "Stipple";
2476 return ui->drag_colour >= 0 ? "Fill" : "Clear";
2477 }
2478 return "";
2479}
2480
2391static char *interpret_move(const game_state *state, game_ui *ui, 2481static char *interpret_move(const game_state *state, game_ui *ui,
2392 const game_drawstate *ds, 2482 const game_drawstate *ds,
2393 int x, int y, int button) 2483 int x, int y, int button)
@@ -2401,21 +2491,21 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2401 */ 2491 */
2402 if (button == 'l' || button == 'L') { 2492 if (button == 'l' || button == 'L') {
2403 ui->show_numbers = !ui->show_numbers; 2493 ui->show_numbers = !ui->show_numbers;
2404 return UI_UPDATE; 2494 return MOVE_UI_UPDATE;
2405 } 2495 }
2406 2496
2407 if (IS_CURSOR_MOVE(button)) { 2497 if (IS_CURSOR_MOVE(button)) {
2408 move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h, 2498 move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h,
2409 false); 2499 false, NULL);
2410 ui->cur_visible = true; 2500 ui->cur_visible = true;
2411 ui->cur_moved = true; 2501 ui->cur_moved = true;
2412 ui->cur_lastmove = button; 2502 ui->cur_lastmove = button;
2413 return UI_UPDATE; 2503 return MOVE_UI_UPDATE;
2414 } 2504 }
2415 if (IS_CURSOR_SELECT(button)) { 2505 if (IS_CURSOR_SELECT(button)) {
2416 if (!ui->cur_visible) { 2506 if (!ui->cur_visible) {
2417 ui->cur_visible = true; 2507 ui->cur_visible = true;
2418 return UI_UPDATE; 2508 return MOVE_UI_UPDATE;
2419 } 2509 }
2420 if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */ 2510 if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */
2421 int r = region_from_ui_cursor(state, ui); 2511 int r = region_from_ui_cursor(state, ui);
@@ -2427,7 +2517,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2427 ui->drag_pencil = 0; 2517 ui->drag_pencil = 0;
2428 } 2518 }
2429 ui->cur_moved = false; 2519 ui->cur_moved = false;
2430 return UI_UPDATE; 2520 return MOVE_UI_UPDATE;
2431 } else { /* currently cursor-dragging; drop the colour in the new region. */ 2521 } else { /* currently cursor-dragging; drop the colour in the new region. */
2432 alt_button = (button == CURSOR_SELECT2); 2522 alt_button = (button == CURSOR_SELECT2);
2433 /* Double-select removes current colour. */ 2523 /* Double-select removes current colour. */
@@ -2452,14 +2542,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2452 ui->dragx = x; 2542 ui->dragx = x;
2453 ui->dragy = y; 2543 ui->dragy = y;
2454 ui->cur_visible = false; 2544 ui->cur_visible = false;
2455 return UI_UPDATE; 2545 return MOVE_UI_UPDATE;
2456 } 2546 }
2457 2547
2458 if ((button == LEFT_DRAG || button == RIGHT_DRAG) && 2548 if ((button == LEFT_DRAG || button == RIGHT_DRAG) &&
2459 ui->drag_colour > -2) { 2549 ui->drag_colour > -2) {
2460 ui->dragx = x; 2550 ui->dragx = x;
2461 ui->dragy = y; 2551 ui->dragy = y;
2462 return UI_UPDATE; 2552 return MOVE_UI_UPDATE;
2463 } 2553 }
2464 2554
2465 if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) && 2555 if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) &&
@@ -2484,18 +2574,18 @@ drag_dropped:
2484 ui->drag_colour = -2; 2574 ui->drag_colour = -2;
2485 2575
2486 if (r < 0) 2576 if (r < 0)
2487 return UI_UPDATE; /* drag into border; do nothing else */ 2577 return MOVE_UI_UPDATE; /* drag into border; do nothing else */
2488 2578
2489 if (state->map->immutable[r]) 2579 if (state->map->immutable[r])
2490 return UI_UPDATE; /* can't change this region */ 2580 return MOVE_UI_UPDATE; /* can't change this region */
2491 2581
2492 if (state->colouring[r] == c && state->pencil[r] == p) 2582 if (state->colouring[r] == c && state->pencil[r] == p)
2493 return UI_UPDATE; /* don't _need_ to change this region */ 2583 return MOVE_UI_UPDATE; /* don't _need_ to change this region */
2494 2584
2495 if (alt_button) { 2585 if (alt_button) {
2496 if (state->colouring[r] >= 0) { 2586 if (state->colouring[r] >= 0) {
2497 /* Can't pencil on a coloured region */ 2587 /* Can't pencil on a coloured region */
2498 return UI_UPDATE; 2588 return MOVE_UI_UPDATE;
2499 } else if (c >= 0) { 2589 } else if (c >= 0) {
2500 /* Right-dragging from colour to blank toggles one pencil */ 2590 /* Right-dragging from colour to blank toggles one pencil */
2501 p = state->pencil[r] ^ (1 << c); 2591 p = state->pencil[r] ^ (1 << c);
@@ -2605,7 +2695,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2605 */ 2695 */
2606 2696
2607static void game_compute_size(const game_params *params, int tilesize, 2697static void game_compute_size(const game_params *params, int tilesize,
2608 int *x, int *y) 2698 const game_ui *ui, int *x, int *y)
2609{ 2699{
2610 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2700 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2611 struct { int tilesize; } ads, *ds = &ads; 2701 struct { int tilesize; } ads, *ds = &ads;
@@ -2729,7 +2819,7 @@ static void draw_error(drawing *dr, game_drawstate *ds, int x, int y)
2729 2819
2730static void draw_square(drawing *dr, game_drawstate *ds, 2820static void draw_square(drawing *dr, game_drawstate *ds,
2731 const game_params *params, struct map *map, 2821 const game_params *params, struct map *map,
2732 int x, int y, unsigned long v) 2822 int x, int y, unsigned long v, bool large_stipples)
2733{ 2823{
2734 int w = params->w, h = params->h, wh = w*h; 2824 int w = params->w, h = params->h, wh = w*h;
2735 int tv, bv, xo, yo, i, j, oldj; 2825 int tv, bv, xo, yo, i, j, oldj;
@@ -2802,7 +2892,8 @@ static void draw_square(drawing *dr, game_drawstate *ds,
2802 2892
2803 draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5, 2893 draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5,
2804 COORD(y) + (yo+1)*TILESIZE/5, 2894 COORD(y) + (yo+1)*TILESIZE/5,
2805 TILESIZE/4, COL_0 + c, COL_0 + c); 2895 large_stipples ? TILESIZE/4 : TILESIZE/7,
2896 COL_0 + c, COL_0 + c);
2806 } 2897 }
2807 2898
2808 /* 2899 /*
@@ -2857,6 +2948,11 @@ static void draw_square(drawing *dr, game_drawstate *ds,
2857 draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); 2948 draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
2858} 2949}
2859 2950
2951static float flash_length(const game_ui *ui)
2952{
2953 return (ui->flash_type == FLASH_EACH_TO_WHITE ? 0.50F : 0.30F);
2954}
2955
2860static void game_redraw(drawing *dr, game_drawstate *ds, 2956static void game_redraw(drawing *dr, game_drawstate *ds,
2861 const game_state *oldstate, const game_state *state, 2957 const game_state *oldstate, const game_state *state,
2862 int dir, const game_ui *ui, 2958 int dir, const game_ui *ui,
@@ -2872,29 +2968,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2872 ds->drag_visible = false; 2968 ds->drag_visible = false;
2873 } 2969 }
2874 2970
2875 /*
2876 * The initial contents of the window are not guaranteed and
2877 * can vary with front ends. To be on the safe side, all games
2878 * should start by drawing a big background-colour rectangle
2879 * covering the whole window.
2880 */
2881 if (!ds->started) { 2971 if (!ds->started) {
2882 int ww, wh;
2883
2884 game_compute_size(&state->p, TILESIZE, &ww, &wh);
2885 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
2886 draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1, 2972 draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1,
2887 COL_GRID); 2973 COL_GRID);
2888 2974 draw_update(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1);
2889 draw_update(dr, 0, 0, ww, wh);
2890 ds->started = true; 2975 ds->started = true;
2891 } 2976 }
2892 2977
2893 if (flashtime) { 2978 if (flashtime) {
2894 if (flash_type == 1) 2979 if (ui->flash_type == FLASH_EACH_TO_WHITE)
2895 flash = (int)(flashtime * FOUR / flash_length); 2980 flash = (int)(flashtime * FOUR / flash_length(ui));
2896 else 2981 else
2897 flash = 1 + (int)(flashtime * THREE / flash_length); 2982 flash = 1 + (int)(flashtime * THREE / flash_length(ui));
2898 } else 2983 } else
2899 flash = -1; 2984 flash = -1;
2900 2985
@@ -2913,12 +2998,12 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2913 bv = FOUR; 2998 bv = FOUR;
2914 2999
2915 if (flash >= 0) { 3000 if (flash >= 0) {
2916 if (flash_type == 1) { 3001 if (ui->flash_type == FLASH_EACH_TO_WHITE) {
2917 if (tv == flash) 3002 if (tv == flash)
2918 tv = FOUR; 3003 tv = FOUR;
2919 if (bv == flash) 3004 if (bv == flash)
2920 bv = FOUR; 3005 bv = FOUR;
2921 } else if (flash_type == 2) { 3006 } else if (ui->flash_type == FLASH_ALL_TO_WHITE) {
2922 if (flash % 2) 3007 if (flash % 2)
2923 tv = bv = FOUR; 3008 tv = bv = FOUR;
2924 } else { 3009 } else {
@@ -2990,7 +3075,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2990 for (x = 0; x < w; x++) { 3075 for (x = 0; x < w; x++) {
2991 unsigned long v = ds->todraw[y*w+x]; 3076 unsigned long v = ds->todraw[y*w+x];
2992 if (ds->drawn[y*w+x] != v) { 3077 if (ds->drawn[y*w+x] != v) {
2993 draw_square(dr, ds, &state->p, state->map, x, y, v); 3078 draw_square(dr, ds, &state->p, state->map, x, y, v, ui->large_stipples);
2994 ds->drawn[y*w+x] = v; 3079 ds->drawn[y*w+x] = v;
2995 } 3080 }
2996 } 3081 }
@@ -3048,15 +3133,7 @@ static float game_flash_length(const game_state *oldstate,
3048{ 3133{
3049 if (!oldstate->completed && newstate->completed && 3134 if (!oldstate->completed && newstate->completed &&
3050 !oldstate->cheated && !newstate->cheated) { 3135 !oldstate->cheated && !newstate->cheated) {
3051 if (flash_type < 0) { 3136 return flash_length(ui);
3052 char *env = getenv("MAP_ALTERNATIVE_FLASH");
3053 if (env)
3054 flash_type = atoi(env);
3055 else
3056 flash_type = 0;
3057 flash_length = (flash_type == 1 ? 0.50F : 0.30F);
3058 }
3059 return flash_length;
3060 } else 3137 } else
3061 return 0.0F; 3138 return 0.0F;
3062} 3139}
@@ -3079,12 +3156,8 @@ static int game_status(const game_state *state)
3079 return state->completed ? +1 : 0; 3156 return state->completed ? +1 : 0;
3080} 3157}
3081 3158
3082static bool game_timing_state(const game_state *state, game_ui *ui) 3159static void game_print_size(const game_params *params, const game_ui *ui,
3083{ 3160 float *x, float *y)
3084 return true;
3085}
3086
3087static void game_print_size(const game_params *params, float *x, float *y)
3088{ 3161{
3089 int pw, ph; 3162 int pw, ph;
3090 3163
@@ -3093,12 +3166,13 @@ static void game_print_size(const game_params *params, float *x, float *y)
3093 * compute this size is to compute the pixel puzzle size at a 3166 * compute this size is to compute the pixel puzzle size at a
3094 * given tile size and then scale. 3167 * given tile size and then scale.
3095 */ 3168 */
3096 game_compute_size(params, 400, &pw, &ph); 3169 game_compute_size(params, 400, ui, &pw, &ph);
3097 *x = pw / 100.0F; 3170 *x = pw / 100.0F;
3098 *y = ph / 100.0F; 3171 *y = ph / 100.0F;
3099} 3172}
3100 3173
3101static void game_print(drawing *dr, const game_state *state, int tilesize) 3174static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3175 int tilesize)
3102{ 3176{
3103 int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n; 3177 int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n;
3104 int ink, c[FOUR], i; 3178 int ink, c[FOUR], i;
@@ -3257,13 +3331,15 @@ const struct game thegame = {
3257 dup_game, 3331 dup_game,
3258 free_game, 3332 free_game,
3259 true, solve_game, 3333 true, solve_game,
3260 false, game_can_format_as_text_now, game_text_format, 3334 false, NULL, NULL, /* can_format_as_text_now, text_format */
3335 get_prefs, set_prefs,
3261 new_ui, 3336 new_ui,
3262 free_ui, 3337 free_ui,
3263 encode_ui, 3338 NULL, /* encode_ui */
3264 decode_ui, 3339 NULL, /* decode_ui */
3265 NULL, /* game_request_keys */ 3340 NULL, /* game_request_keys */
3266 game_changed_state, 3341 game_changed_state,
3342 current_key_label,
3267 interpret_move, 3343 interpret_move,
3268 execute_move, 3344 execute_move,
3269 20, game_compute_size, game_set_size, 3345 20, game_compute_size, game_set_size,
@@ -3277,7 +3353,7 @@ const struct game thegame = {
3277 game_status, 3353 game_status,
3278 true, true, game_print_size, game_print, 3354 true, true, game_print_size, game_print,
3279 false, /* wants_statusbar */ 3355 false, /* wants_statusbar */
3280 false, game_timing_state, 3356 false, NULL, /* timing_state */
3281 0, /* flags */ 3357 0, /* flags */
3282}; 3358};
3283 3359
diff --git a/apps/plugins/puzzles/src/matching.c b/apps/plugins/puzzles/src/matching.c
index 9078f6c36a..aabdae0846 100644
--- a/apps/plugins/puzzles/src/matching.c
+++ b/apps/plugins/puzzles/src/matching.c
@@ -319,35 +319,7 @@ int matching(int nl, int nr, int **adjlists, int *adjsizes,
319 return ret; 319 return ret;
320} 320}
321 321
322#ifdef STANDALONE_MATCHING_TEST 322void matching_witness(void *scratchv, int nl, int nr, int *witness)
323
324/*
325 * Diagnostic routine used in testing this algorithm. It is passed a
326 * pointer to a piece of scratch space that's just been used by
327 * matching_with_scratch, and extracts from it a labelling of the
328 * input graph that acts as a 'witness' to the maximality of the
329 * returned matching.
330 *
331 * The output parameter 'witness' should be an array of (nl+nr)
332 * integers, indexed such that witness[L] corresponds to an L-vertex (for
333 * L=0,1,...,nl-1) and witness[nl+R] corresponds to an R-vertex (for
334 * R=0,1,...,nr-1). On return, this array will assign each vertex a
335 * label which is either 0 or 1, and the following properties will
336 * hold:
337 *
338 * + all vertices not paired up by the matching are type L0 or R1
339 * + every L0->R1 edge is used by the matching
340 * + no L1->R0 edge is used by the matching.
341 *
342 * The mere existence of such a labelling is enough to prove the
343 * maximality of the matching, because if there is any larger matching
344 * then its symmetric difference with this one must include at least
345 * one 'augmenting path', which starts at a free L-vertex and ends at
346 * a free R-vertex, traversing only unused L->R edges and only used
347 * R->L edges. But that would mean it starts at an L0, ends at an R1,
348 * and never follows an edge that can get from an 0 to a 1.
349 */
350static void matching_witness(void *scratchv, int nl, int nr, int *witness)
351{ 323{
352 struct scratch *s = (struct scratch *)scratchv; 324 struct scratch *s = (struct scratch *)scratchv;
353 int i, j; 325 int i, j;
@@ -357,397 +329,3 @@ static void matching_witness(void *scratchv, int nl, int nr, int *witness)
357 for (j = 0; j < nr; j++) 329 for (j = 0; j < nr; j++)
358 witness[nl + j] = s->Rlayer[j] == -1; 330 witness[nl + j] = s->Rlayer[j] == -1;
359} 331}
360
361/*
362 * Standalone tool to run the matching algorithm.
363 */
364
365#include <string.h>
366#include <ctype.h>
367#include <time.h>
368
369#include "tree234.h"
370
371int nl, nr, count;
372int **adjlists, *adjsizes;
373int *adjdata, *outl, *outr, *witness;
374void *scratch;
375random_state *rs;
376
377void allocate(int nl_, int nr_, int maxedges)
378{
379 nl = nl_;
380 nr = nr_;
381 adjdata = snewn(maxedges, int);
382 adjlists = snewn(nl, int *);
383 adjsizes = snewn(nl, int);
384 outl = snewn(nl, int);
385 outr = snewn(nr, int);
386 witness = snewn(nl+nr, int);
387 scratch = smalloc(matching_scratch_size(nl, nr));
388}
389
390void deallocate(void)
391{
392 sfree(adjlists);
393 sfree(adjsizes);
394 sfree(adjdata);
395 sfree(outl);
396 sfree(outr);
397 sfree(witness);
398 sfree(scratch);
399}
400
401void find_and_check_matching(void)
402{
403 int i, j, k;
404
405 count = matching_with_scratch(scratch, nl, nr, adjlists, adjsizes,
406 rs, outl, outr);
407 matching_witness(scratch, nl, nr, witness);
408
409 for (i = j = 0; i < nl; i++) {
410 if (outl[i] != -1) {
411 assert(0 <= outl[i] && outl[i] < nr);
412 assert(outr[outl[i]] == i);
413 j++;
414
415 for (k = 0; k < adjsizes[i]; k++)
416 if (adjlists[i][k] == outl[i])
417 break;
418 assert(k < adjsizes[i]);
419 }
420 }
421 assert(j == count);
422
423 for (i = j = 0; i < nr; i++) {
424 if (outr[i] != -1) {
425 assert(0 <= outr[i] && outr[i] < nl);
426 assert(outl[outr[i]] == i);
427 j++;
428 }
429 }
430 assert(j == count);
431
432 for (i = 0; i < nl; i++) {
433 if (outl[i] == -1)
434 assert(witness[i] == 0);
435 }
436 for (i = 0; i < nr; i++) {
437 if (outr[i] == -1)
438 assert(witness[nl+i] == 1);
439 }
440 for (i = 0; i < nl; i++) {
441 for (j = 0; j < adjsizes[i]; j++) {
442 k = adjlists[i][j];
443
444 if (outl[i] == k)
445 assert(!(witness[i] == 1 && witness[nl+k] == 0));
446 else
447 assert(!(witness[i] == 0 && witness[nl+k] == 1));
448 }
449 }
450}
451
452struct nodename {
453 const char *name;
454 int index;
455};
456
457int compare_nodes(void *av, void *bv)
458{
459 const struct nodename *a = (const struct nodename *)av;
460 const struct nodename *b = (const struct nodename *)bv;
461 return strcmp(a->name, b->name);
462}
463
464int node_index(tree234 *n2i, tree234 *i2n, const char *name)
465{
466 struct nodename *nn, *nn_prev;
467 char *namedup = dupstr(name);
468
469 nn = snew(struct nodename);
470 nn->name = namedup;
471 nn->index = count234(n2i);
472
473 nn_prev = add234(n2i, nn);
474 if (nn_prev != nn) {
475 sfree(nn);
476 sfree(namedup);
477 } else {
478 addpos234(i2n, nn, nn->index);
479 }
480
481 return nn_prev->index;
482}
483
484struct edge {
485 int L, R;
486};
487
488int compare_edges(void *av, void *bv)
489{
490 const struct edge *a = (const struct edge *)av;
491 const struct edge *b = (const struct edge *)bv;
492 if (a->L < b->L) return -1;
493 if (a->L > b->L) return +1;
494 if (a->R < b->R) return -1;
495 if (a->R > b->R) return +1;
496 return 0;
497}
498
499void matching_from_user_input(FILE *fp, const char *filename)
500{
501 tree234 *Ln2i, *Li2n, *Rn2i, *Ri2n, *edges;
502 char *line = NULL;
503 struct edge *e;
504 int i, lineno = 0;
505 int *adjptr;
506
507 Ln2i = newtree234(compare_nodes);
508 Rn2i = newtree234(compare_nodes);
509 Li2n = newtree234(NULL);
510 Ri2n = newtree234(NULL);
511 edges = newtree234(compare_edges);
512
513 while (sfree(line), lineno++, (line = fgetline(fp)) != NULL) {
514 char *p, *Lname, *Rname;
515
516 p = line;
517 while (*p && isspace((unsigned char)*p)) p++;
518 if (!*p)
519 continue;
520
521 Lname = p;
522 while (*p && !isspace((unsigned char)*p)) p++;
523 if (*p)
524 *p++ = '\0';
525 while (*p && isspace((unsigned char)*p)) p++;
526
527 if (!*p) {
528 fprintf(stderr, "%s:%d: expected 2 words, found 1\n",
529 filename, lineno);
530 exit(1);
531 }
532
533 Rname = p;
534 while (*p && !isspace((unsigned char)*p)) p++;
535 if (*p)
536 *p++ = '\0';
537 while (*p && isspace((unsigned char)*p)) p++;
538
539 if (*p) {
540 fprintf(stderr, "%s:%d: expected 2 words, found more\n",
541 filename, lineno);
542 exit(1);
543 }
544
545 e = snew(struct edge);
546 e->L = node_index(Ln2i, Li2n, Lname);
547 e->R = node_index(Rn2i, Ri2n, Rname);
548 if (add234(edges, e) != e) {
549 fprintf(stderr, "%s:%d: duplicate edge\n",
550 filename, lineno);
551 exit(1);
552 }
553 }
554
555 allocate(count234(Ln2i), count234(Rn2i), count234(edges));
556
557 adjptr = adjdata;
558 for (i = 0; i < nl; i++)
559 adjlists[i] = NULL;
560 for (i = 0; (e = index234(edges, i)) != NULL; i++) {
561 if (!adjlists[e->L])
562 adjlists[e->L] = adjptr;
563 *adjptr++ = e->R;
564 adjsizes[e->L] = adjptr - adjlists[e->L];
565 }
566
567 find_and_check_matching();
568
569 for (i = 0; i < nl; i++) {
570 if (outl[i] != -1) {
571 struct nodename *Lnn = index234(Li2n, i);
572 struct nodename *Rnn = index234(Ri2n, outl[i]);
573 printf("%s %s\n", Lnn->name, Rnn->name);
574 }
575 }
576}
577
578void test_subsets(void)
579{
580 int b = 8;
581 int n = 1 << b;
582 int i, j, nruns, expected_size;
583 int *adjptr;
584 int *edgecounts;
585 struct stats {
586 int min, max;
587 double n, sx, sxx;
588 } *stats;
589 static const char seed[] = "fixed random seed for repeatability";
590
591 /*
592 * Generate a graph in which every subset of [b] = {1,...,b}
593 * (represented as a b-bit integer 0 <= i < n) has an edge going
594 * to every subset obtained by removing exactly one element.
595 *
596 * This graph is the disjoint union of the corresponding graph for
597 * each layer (collection of same-sized subset) of the power set
598 * of [b]. Each of those graphs has a matching of size equal to
599 * the smaller of its vertex sets. So we expect the overall size
600 * of the output matching to be less than n by the size of the
601 * largest layer, that is, to be n - binomial(n, floor(n/2)).
602 *
603 * We run the generation repeatedly, randomising it every time,
604 * and we expect to see every possible edge appear sooner or
605 * later.
606 */
607
608 rs = random_new(seed, strlen(seed));
609
610 allocate(n, n, n*b);
611 adjptr = adjdata;
612 expected_size = 0;
613 for (i = 0; i < n; i++) {
614 adjlists[i] = adjptr;
615 for (j = 0; j < b; j++) {
616 if (i & (1 << j))
617 *adjptr++ = i & ~(1 << j);
618 }
619 adjsizes[i] = adjptr - adjlists[i];
620 if (adjsizes[i] != b/2)
621 expected_size++;
622 }
623
624 edgecounts = snewn(n*b, int);
625 for (i = 0; i < n*b; i++)
626 edgecounts[i] = 0;
627
628 stats = snewn(b, struct stats);
629
630 nruns = 0;
631 while (nruns < 10000) {
632 nruns++;
633 find_and_check_matching();
634 assert(count == expected_size);
635
636 for (i = 0; i < n; i++)
637 for (j = 0; j < b; j++)
638 if ((i ^ outl[i]) == (1 << j))
639 edgecounts[b*i+j]++;
640
641 if (nruns % 1000 == 0) {
642 for (i = 0; i < b; i++) {
643 struct stats *st = &stats[i];
644 st->min = st->max = -1;
645 st->n = st->sx = st->sxx = 0;
646 }
647
648 for (i = 0; i < n; i++) {
649 int pop = 0;
650 for (j = 0; j < b; j++)
651 if (i & (1 << j))
652 pop++;
653 pop--;
654
655 for (j = 0; j < b; j++) {
656 if (i & (1 << j)) {
657 struct stats *st = &stats[pop];
658 int x = edgecounts[b*i+j];
659 if (st->max == -1 || st->max < x)
660 st->max = x;
661 if (st->min == -1 || st->min > x)
662 st->min = x;
663 st->n++;
664 st->sx += x;
665 st->sxx += (double)x*x;
666 } else {
667 assert(edgecounts[b*i+j] == 0);
668 }
669 }
670 }
671 }
672 }
673
674 printf("after %d runs:\n", nruns);
675 for (j = 0; j < b; j++) {
676 struct stats *st = &stats[j];
677 printf("edges between layers %d,%d:"
678 " min=%d max=%d mean=%f variance=%f\n",
679 j, j+1, st->min, st->max, st->sx/st->n,
680 (st->sxx - st->sx*st->sx/st->n) / st->n);
681 }
682}
683
684int main(int argc, char **argv)
685{
686 static const char stdin_identifier[] = "<standard input>";
687 const char *infile = NULL;
688 bool doing_opts = true;
689 enum { USER_INPUT, AUTOTEST } mode = USER_INPUT;
690
691 while (--argc > 0) {
692 const char *arg = *++argv;
693
694 if (doing_opts && arg[0] == '-' && arg[1]) {
695 if (!strcmp(arg, "--")) {
696 doing_opts = false;
697 } else if (!strcmp(arg, "--random")) {
698 char buf[64];
699 int len = sprintf(buf, "%lu", (unsigned long)time(NULL));
700 rs = random_new(buf, len);
701 } else if (!strcmp(arg, "--autotest")) {
702 mode = AUTOTEST;
703 } else {
704 fprintf(stderr, "matching: unrecognised option '%s'\n", arg);
705 return 1;
706 }
707 } else {
708 if (!infile) {
709 infile = (!strcmp(arg, "-") ? stdin_identifier : arg);
710 } else {
711 fprintf(stderr, "matching: too many arguments\n");
712 return 1;
713 }
714 }
715 }
716
717 if (mode == USER_INPUT) {
718 FILE *fp;
719
720 if (!infile)
721 infile = stdin_identifier;
722
723 if (infile != stdin_identifier) {
724 fp = fopen(infile, "r");
725 if (!fp) {
726 fprintf(stderr, "matching: could not open input file '%s'\n",
727 infile);
728 return 1;
729 }
730 } else {
731 fp = stdin;
732 }
733
734 matching_from_user_input(fp, infile);
735
736 if (infile != stdin_identifier)
737 fclose(fp);
738 }
739
740 if (mode == AUTOTEST) {
741 if (infile) {
742 fprintf(stderr, "matching: expected no filename argument "
743 "with --autotest\n");
744 return 1;
745 }
746
747 test_subsets();
748 }
749
750 return 0;
751}
752
753#endif /* STANDALONE_MATCHING_TEST */
diff --git a/apps/plugins/puzzles/src/matching.h b/apps/plugins/puzzles/src/matching.h
index a4d3098efa..dbf424d044 100644
--- a/apps/plugins/puzzles/src/matching.h
+++ b/apps/plugins/puzzles/src/matching.h
@@ -77,4 +77,32 @@ size_t matching_scratch_size(int nl, int nr);
77int matching(int nl, int nr, int **adjlists, int *adjsizes, 77int matching(int nl, int nr, int **adjlists, int *adjsizes,
78 random_state *rs, int *outl, int *outr); 78 random_state *rs, int *outl, int *outr);
79 79
80/*
81 * Diagnostic routine used in testing this algorithm. It is passed a
82 * pointer to a piece of scratch space that's just been used by
83 * matching_with_scratch, and extracts from it a labelling of the
84 * input graph that acts as a 'witness' to the maximality of the
85 * returned matching.
86 *
87 * The output parameter 'witness' should be an array of (nl+nr)
88 * integers, indexed such that witness[L] corresponds to an L-vertex (for
89 * L=0,1,...,nl-1) and witness[nl+R] corresponds to an R-vertex (for
90 * R=0,1,...,nr-1). On return, this array will assign each vertex a
91 * label which is either 0 or 1, and the following properties will
92 * hold:
93 *
94 * + all vertices not paired up by the matching are type L0 or R1
95 * + every L0->R1 edge is used by the matching
96 * + no L1->R0 edge is used by the matching.
97 *
98 * The mere existence of such a labelling is enough to prove the
99 * maximality of the matching, because if there is any larger matching
100 * then its symmetric difference with this one must include at least
101 * one 'augmenting path', which starts at a free L-vertex and ends at
102 * a free R-vertex, traversing only unused L->R edges and only used
103 * R->L edges. But that would mean it starts at an L0, ends at an R1,
104 * and never follows an edge that can get from an 0 to a 1.
105 */
106void matching_witness(void *scratch, int nl, int nr, int *witness);
107
80#endif /* MATCHING_MATCHING_H */ 108#endif /* MATCHING_MATCHING_H */
diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c
index f2d27d3e31..30fdf1b830 100644
--- a/apps/plugins/puzzles/src/midend.c
+++ b/apps/plugins/puzzles/src/midend.c
@@ -30,6 +30,11 @@ struct midend_serialise_buf {
30 int len, size; 30 int len, size;
31}; 31};
32 32
33struct midend_serialise_buf_read_ctx {
34 struct midend_serialise_buf *ser;
35 int len, pos;
36};
37
33struct midend { 38struct midend {
34 frontend *frontend; 39 frontend *frontend;
35 random_state *random; 40 random_state *random;
@@ -73,6 +78,7 @@ struct midend {
73 78
74 game_params *params, *curparams; 79 game_params *params, *curparams;
75 game_drawstate *drawstate; 80 game_drawstate *drawstate;
81 bool first_draw;
76 game_ui *ui; 82 game_ui *ui;
77 83
78 game_state *oldstate; 84 game_state *oldstate;
@@ -88,10 +94,15 @@ struct midend {
88 94
89 int pressed_mouse_button; 95 int pressed_mouse_button;
90 96
91 int preferred_tilesize, tilesize, winwidth, winheight; 97 struct midend_serialise_buf be_prefs;
98
99 int preferred_tilesize, preferred_tilesize_dpr, tilesize;
100 int winwidth, winheight;
92 101
93 void (*game_id_change_notify_function)(void *); 102 void (*game_id_change_notify_function)(void *);
94 void *game_id_change_notify_ctx; 103 void *game_id_change_notify_ctx;
104
105 bool one_key_shortcuts;
95}; 106};
96 107
97#define ensure(me) do { \ 108#define ensure(me) do { \
@@ -119,21 +130,33 @@ struct deserialise_data {
119}; 130};
120 131
121/* 132/*
122 * Forward reference. 133 * Forward references.
123 */ 134 */
124static const char *midend_deserialise_internal( 135static const char *midend_deserialise_internal(
125 midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx, 136 midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx,
126 const char *(*check)(void *ctx, midend *, const struct deserialise_data *), 137 const char *(*check)(void *ctx, midend *, const struct deserialise_data *),
127 void *cctx); 138 void *cctx);
139static void midend_serialise_prefs(
140 midend *me, game_ui *ui,
141 void (*write)(void *ctx, const void *buf, int len), void *wctx);
142static const char *midend_deserialise_prefs(
143 midend *me, game_ui *ui,
144 bool (*read)(void *ctx, void *buf, int len), void *rctx);
145static config_item *midend_get_prefs(midend *me, game_ui *ui);
146static void midend_set_prefs(midend *me, game_ui *ui, config_item *all_prefs);
147static void midend_apply_prefs(midend *me, game_ui *ui);
128 148
129void midend_reset_tilesize(midend *me) 149void midend_reset_tilesize(midend *me)
130{ 150{
131 me->preferred_tilesize = me->ourgame->preferred_tilesize; 151 me->preferred_tilesize = me->ourgame->preferred_tilesize;
152 me->preferred_tilesize_dpr = 1.0;
132 { 153 {
133 /* 154 /*
134 * Allow an environment-based override for the default tile 155 * Allow an environment-based override for the default tile
135 * size by defining a variable along the lines of 156 * size by defining a variable along the lines of
136 * `NET_TILESIZE=15'. 157 * `NET_TILESIZE=15'.
158 *
159 * XXX How should this interact with DPR?
137 */ 160 */
138 161
139 char buf[80], *e; 162 char buf[80], *e;
@@ -196,6 +219,7 @@ midend *midend_new(frontend *fe, const game *ourgame,
196 me->aux_info = NULL; 219 me->aux_info = NULL;
197 me->genmode = GOT_NOTHING; 220 me->genmode = GOT_NOTHING;
198 me->drawstate = NULL; 221 me->drawstate = NULL;
222 me->first_draw = true;
199 me->oldstate = NULL; 223 me->oldstate = NULL;
200 me->preset_menu = NULL; 224 me->preset_menu = NULL;
201 me->anim_time = me->anim_pos = 0.0F; 225 me->anim_time = me->anim_pos = 0.0F;
@@ -212,6 +236,11 @@ midend *midend_new(frontend *fe, const game *ourgame,
212 else 236 else
213 me->drawing = NULL; 237 me->drawing = NULL;
214 238
239 me->be_prefs.buf = NULL;
240 me->be_prefs.size = me->be_prefs.len = 0;
241
242 me->one_key_shortcuts = true;
243
215 midend_reset_tilesize(me); 244 midend_reset_tilesize(me);
216 245
217 sfree(randseed); 246 sfree(randseed);
@@ -280,6 +309,7 @@ void midend_free(midend *me)
280 sfree(me->privdesc); 309 sfree(me->privdesc);
281 sfree(me->seedstr); 310 sfree(me->seedstr);
282 sfree(me->aux_info); 311 sfree(me->aux_info);
312 sfree(me->be_prefs.buf);
283 me->ourgame->free_params(me->params); 313 me->ourgame->free_params(me->params);
284 midend_free_preset_menu(me, me->preset_menu); 314 midend_free_preset_menu(me, me->preset_menu);
285 if (me->ui) 315 if (me->ui)
@@ -297,14 +327,56 @@ static void midend_size_new_drawstate(midend *me)
297 * anyway yet. 327 * anyway yet.
298 */ 328 */
299 if (me->tilesize > 0) { 329 if (me->tilesize > 0) {
300 me->ourgame->compute_size(me->params, me->tilesize, 330 me->ourgame->compute_size(me->params, me->tilesize, me->ui,
301 &me->winwidth, &me->winheight); 331 &me->winwidth, &me->winheight);
302 me->ourgame->set_size(me->drawing, me->drawstate, 332 me->ourgame->set_size(me->drawing, me->drawstate,
303 me->params, me->tilesize); 333 me->params, me->tilesize);
304 } 334 }
305} 335}
306 336
307void midend_size(midend *me, int *x, int *y, bool user_size) 337/*
338 * There is no one correct way to convert tilesizes between device
339 * pixel ratios, because there's only a loosely-defined relationship
340 * between tilesize and the actual size of a puzzle. We define this
341 * function as the canonical conversion function so everything in the
342 * midend will be consistent.
343 */
344static int convert_tilesize(midend *me, int old_tilesize,
345 double old_dpr, double new_dpr)
346{
347 int x, y, rx, ry, min, max;
348 game_params *defaults;
349
350 if (new_dpr == old_dpr)
351 return old_tilesize;
352
353 defaults = me->ourgame->default_params();
354
355 me->ourgame->compute_size(defaults, old_tilesize, me->ui, &x, &y);
356 x *= new_dpr / old_dpr;
357 y *= new_dpr / old_dpr;
358
359 min = max = 1;
360 do {
361 max *= 2;
362 me->ourgame->compute_size(defaults, max, me->ui, &rx, &ry);
363 } while (rx <= x && ry <= y);
364
365 while (max - min > 1) {
366 int mid = (max + min) / 2;
367 me->ourgame->compute_size(defaults, mid, me->ui, &rx, &ry);
368 if (rx <= x && ry <= y)
369 min = mid;
370 else
371 max = mid;
372 }
373
374 me->ourgame->free_params(defaults);
375 return min;
376}
377
378void midend_size(midend *me, int *x, int *y, bool user_size,
379 double device_pixel_ratio)
308{ 380{
309 int min, max; 381 int min, max;
310 int rx, ry; 382 int rx, ry;
@@ -318,6 +390,7 @@ void midend_size(midend *me, int *x, int *y, bool user_size)
318 me->ourgame->free_drawstate(me->drawing, me->drawstate); 390 me->ourgame->free_drawstate(me->drawing, me->drawstate);
319 me->drawstate = me->ourgame->new_drawstate(me->drawing, 391 me->drawstate = me->ourgame->new_drawstate(me->drawing,
320 me->states[0].state); 392 me->states[0].state);
393 me->first_draw = true;
321 } 394 }
322 395
323 /* 396 /*
@@ -333,10 +406,12 @@ void midend_size(midend *me, int *x, int *y, bool user_size)
333 max = 1; 406 max = 1;
334 do { 407 do {
335 max *= 2; 408 max *= 2;
336 me->ourgame->compute_size(me->params, max, &rx, &ry); 409 me->ourgame->compute_size(me->params, max, me->ui, &rx, &ry);
337 } while (rx <= *x && ry <= *y); 410 } while (rx <= *x && ry <= *y);
338 } else 411 } else
339 max = me->preferred_tilesize + 1; 412 max = convert_tilesize(me, me->preferred_tilesize,
413 me->preferred_tilesize_dpr,
414 device_pixel_ratio) + 1;
340 min = 1; 415 min = 1;
341 416
342 /* 417 /*
@@ -347,7 +422,7 @@ void midend_size(midend *me, int *x, int *y, bool user_size)
347 */ 422 */
348 while (max - min > 1) { 423 while (max - min > 1) {
349 int mid = (max + min) / 2; 424 int mid = (max + min) / 2;
350 me->ourgame->compute_size(me->params, mid, &rx, &ry); 425 me->ourgame->compute_size(me->params, mid, me->ui, &rx, &ry);
351 if (rx <= *x && ry <= *y) 426 if (rx <= *x && ry <= *y)
352 min = mid; 427 min = mid;
353 else 428 else
@@ -359,9 +434,11 @@ void midend_size(midend *me, int *x, int *y, bool user_size)
359 */ 434 */
360 435
361 me->tilesize = min; 436 me->tilesize = min;
362 if (user_size) 437 if (user_size) {
363 /* If the user requested a change in size, make it permanent. */ 438 /* If the user requested a change in size, make it permanent. */
364 me->preferred_tilesize = me->tilesize; 439 me->preferred_tilesize = me->tilesize;
440 me->preferred_tilesize_dpr = device_pixel_ratio;
441 }
365 midend_size_new_drawstate(me); 442 midend_size_new_drawstate(me);
366 *x = me->winwidth; 443 *x = me->winwidth;
367 *y = me->winheight; 444 *y = me->winheight;
@@ -380,6 +457,28 @@ game_params *midend_get_params(midend *me)
380 return me->ourgame->dup_params(me->params); 457 return me->ourgame->dup_params(me->params);
381} 458}
382 459
460static char *encode_params(midend *me, const game_params *params, bool full)
461{
462 char *encoded = me->ourgame->encode_params(params, full);
463 int i;
464
465 /* Assert that the params consist of printable ASCII containing
466 * neither '#' nor ':'. */
467 for (i = 0; encoded[i]; i++)
468 assert(encoded[i] >= 32 && encoded[i] < 127 &&
469 encoded[i] != '#' && encoded[i] != ':');
470 return encoded;
471}
472
473static void assert_printable_ascii(char const *s)
474{
475 /* Assert that s is entirely printable ASCII, and hence safe for
476 * writing in a save file. */
477 int i;
478 for (i = 0; s[i]; i++)
479 assert(s[i] >= 32 && s[i] < 127);
480}
481
383static void midend_set_timer(midend *me) 482static void midend_set_timer(midend *me)
384{ 483{
385 me->timing = (me->ourgame->is_timed && 484 me->timing = (me->ourgame->is_timed &&
@@ -397,11 +496,12 @@ void midend_force_redraw(midend *me)
397 me->ourgame->free_drawstate(me->drawing, me->drawstate); 496 me->ourgame->free_drawstate(me->drawing, me->drawstate);
398 me->drawstate = me->ourgame->new_drawstate(me->drawing, 497 me->drawstate = me->ourgame->new_drawstate(me->drawing,
399 me->states[0].state); 498 me->states[0].state);
499 me->first_draw = true;
400 midend_size_new_drawstate(me); 500 midend_size_new_drawstate(me);
401 midend_redraw(me); 501 midend_redraw(me);
402} 502}
403 503
404static void newgame_serialise_write(void *ctx, const void *buf, int len) 504static void midend_serialise_buf_write(void *ctx, const void *buf, int len)
405{ 505{
406 struct midend_serialise_buf *ser = (struct midend_serialise_buf *)ctx; 506 struct midend_serialise_buf *ser = (struct midend_serialise_buf *)ctx;
407 int new_len; 507 int new_len;
@@ -416,6 +516,18 @@ static void newgame_serialise_write(void *ctx, const void *buf, int len)
416 ser->len = new_len; 516 ser->len = new_len;
417} 517}
418 518
519static bool midend_serialise_buf_read(void *ctx, void *buf, int len)
520{
521 struct midend_serialise_buf_read_ctx *const rctx = ctx;
522
523 if (len > rctx->len - rctx->pos)
524 return false;
525
526 memcpy(buf, rctx->ser->buf + rctx->pos, len);
527 rctx->pos += len;
528 return true;
529}
530
419void midend_new_game(midend *me) 531void midend_new_game(midend *me)
420{ 532{
421 me->newgame_undo.len = 0; 533 me->newgame_undo.len = 0;
@@ -435,7 +547,7 @@ void midend_new_game(midend *me)
435 * worse, valid but wrong. 547 * worse, valid but wrong.
436 */ 548 */
437 midend_purge_states(me); 549 midend_purge_states(me);
438 midend_serialise(me, newgame_serialise_write, &me->newgame_undo); 550 midend_serialise(me, midend_serialise_buf_write, &me->newgame_undo);
439 } 551 }
440 552
441 midend_stop_anim(me); 553 midend_stop_anim(me);
@@ -487,6 +599,7 @@ void midend_new_game(midend *me)
487 */ 599 */
488 me->desc = me->ourgame->new_desc(me->curparams, rs, 600 me->desc = me->ourgame->new_desc(me->curparams, rs,
489 &me->aux_info, (me->drawing != NULL)); 601 &me->aux_info, (me->drawing != NULL));
602 assert_printable_ascii(me->desc);
490 me->privdesc = NULL; 603 me->privdesc = NULL;
491 random_free(rs); 604 random_free(rs);
492 } 605 }
@@ -536,6 +649,7 @@ void midend_new_game(midend *me)
536 me->statepos = 1; 649 me->statepos = 1;
537 me->drawstate = me->ourgame->new_drawstate(me->drawing, 650 me->drawstate = me->ourgame->new_drawstate(me->drawing,
538 me->states[0].state); 651 me->states[0].state);
652 me->first_draw = true;
539 midend_size_new_drawstate(me); 653 midend_size_new_drawstate(me);
540 me->elapsed = 0.0F; 654 me->elapsed = 0.0F;
541 me->flash_pos = me->flash_time = 0.0F; 655 me->flash_pos = me->flash_time = 0.0F;
@@ -543,6 +657,7 @@ void midend_new_game(midend *me)
543 if (me->ui) 657 if (me->ui)
544 me->ourgame->free_ui(me->ui); 658 me->ourgame->free_ui(me->ui);
545 me->ui = me->ourgame->new_ui(me->states[0].state); 659 me->ui = me->ourgame->new_ui(me->states[0].state);
660 midend_apply_prefs(me, me->ui);
546 midend_set_timer(me); 661 midend_set_timer(me);
547 me->pressed_mouse_button = 0; 662 me->pressed_mouse_button = 0;
548 663
@@ -552,31 +667,28 @@ void midend_new_game(midend *me)
552 me->newgame_can_store_undo = true; 667 me->newgame_can_store_undo = true;
553} 668}
554 669
555bool midend_can_undo(midend *me) 670const char *midend_load_prefs(
671 midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx)
556{ 672{
557 return (me->statepos > 1 || me->newgame_undo.len); 673 const char *err = midend_deserialise_prefs(me, NULL, read, rctx);
674 return err;
558} 675}
559 676
560bool midend_can_redo(midend *me) 677void midend_save_prefs(midend *me,
678 void (*write)(void *ctx, const void *buf, int len),
679 void *wctx)
561{ 680{
562 return (me->statepos < me->nstates || me->newgame_redo.len); 681 midend_serialise_prefs(me, NULL, write, wctx);
563} 682}
564 683
565struct newgame_undo_deserialise_read_ctx { 684bool midend_can_undo(midend *me)
566 struct midend_serialise_buf *ser;
567 int len, pos;
568};
569
570static bool newgame_undo_deserialise_read(void *ctx, void *buf, int len)
571{ 685{
572 struct newgame_undo_deserialise_read_ctx *const rctx = ctx; 686 return (me->statepos > 1 || me->newgame_undo.len);
573 687}
574 if (len > rctx->len - rctx->pos)
575 return false;
576 688
577 memcpy(buf, rctx->ser->buf + rctx->pos, len); 689bool midend_can_redo(midend *me)
578 rctx->pos += len; 690{
579 return true; 691 return (me->statepos < me->nstates || me->newgame_redo.len);
580} 692}
581 693
582struct newgame_undo_deserialise_check_ctx { 694struct newgame_undo_deserialise_check_ctx {
@@ -613,8 +725,8 @@ static const char *newgame_undo_deserialise_check(
613 * We check both params and cparams, to be as safe as possible. 725 * We check both params and cparams, to be as safe as possible.
614 */ 726 */
615 727
616 old = me->ourgame->encode_params(me->params, true); 728 old = encode_params(me, me->params, true);
617 new = me->ourgame->encode_params(data->params, true); 729 new = encode_params(me, data->params, true);
618 if (strcmp(old, new)) { 730 if (strcmp(old, new)) {
619 /* Set a flag to distinguish this deserialise failure 731 /* Set a flag to distinguish this deserialise failure
620 * from one due to faulty decoding */ 732 * from one due to faulty decoding */
@@ -622,8 +734,8 @@ static const char *newgame_undo_deserialise_check(
622 return "Undoing this new-game operation would change params"; 734 return "Undoing this new-game operation would change params";
623 } 735 }
624 736
625 old = me->ourgame->encode_params(me->curparams, true); 737 old = encode_params(me, me->curparams, true);
626 new = me->ourgame->encode_params(data->cparams, true); 738 new = encode_params(me, data->cparams, true);
627 if (strcmp(old, new)) { 739 if (strcmp(old, new)) {
628 ctx->refused = true; 740 ctx->refused = true;
629 return "Undoing this new-game operation would change params"; 741 return "Undoing this new-game operation would change params";
@@ -648,7 +760,7 @@ static bool midend_undo(midend *me)
648 me->dir = -1; 760 me->dir = -1;
649 return true; 761 return true;
650 } else if (me->newgame_undo.len) { 762 } else if (me->newgame_undo.len) {
651 struct newgame_undo_deserialise_read_ctx rctx; 763 struct midend_serialise_buf_read_ctx rctx;
652 struct newgame_undo_deserialise_check_ctx cctx; 764 struct newgame_undo_deserialise_check_ctx cctx;
653 struct midend_serialise_buf serbuf; 765 struct midend_serialise_buf serbuf;
654 766
@@ -659,14 +771,14 @@ static bool midend_undo(midend *me)
659 */ 771 */
660 serbuf.buf = NULL; 772 serbuf.buf = NULL;
661 serbuf.len = serbuf.size = 0; 773 serbuf.len = serbuf.size = 0;
662 midend_serialise(me, newgame_serialise_write, &serbuf); 774 midend_serialise(me, midend_serialise_buf_write, &serbuf);
663 775
664 rctx.ser = &me->newgame_undo; 776 rctx.ser = &me->newgame_undo;
665 rctx.len = me->newgame_undo.len; /* copy for reentrancy safety */ 777 rctx.len = me->newgame_undo.len; /* copy for reentrancy safety */
666 rctx.pos = 0; 778 rctx.pos = 0;
667 cctx.refused = false; 779 cctx.refused = false;
668 deserialise_error = midend_deserialise_internal( 780 deserialise_error = midend_deserialise_internal(
669 me, newgame_undo_deserialise_read, &rctx, 781 me, midend_serialise_buf_read, &rctx,
670 newgame_undo_deserialise_check, &cctx); 782 newgame_undo_deserialise_check, &cctx);
671 if (cctx.refused) { 783 if (cctx.refused) {
672 /* 784 /*
@@ -699,7 +811,8 @@ static bool midend_undo(midend *me)
699 * the midend so that we can redo back into it later. 811 * the midend so that we can redo back into it later.
700 */ 812 */
701 me->newgame_redo.len = 0; 813 me->newgame_redo.len = 0;
702 newgame_serialise_write(&me->newgame_redo, serbuf.buf, serbuf.len); 814 midend_serialise_buf_write(&me->newgame_redo,
815 serbuf.buf, serbuf.len);
703 816
704 sfree(serbuf.buf); 817 sfree(serbuf.buf);
705 return true; 818 return true;
@@ -721,7 +834,7 @@ static bool midend_redo(midend *me)
721 me->dir = +1; 834 me->dir = +1;
722 return true; 835 return true;
723 } else if (me->newgame_redo.len) { 836 } else if (me->newgame_redo.len) {
724 struct newgame_undo_deserialise_read_ctx rctx; 837 struct midend_serialise_buf_read_ctx rctx;
725 struct newgame_undo_deserialise_check_ctx cctx; 838 struct newgame_undo_deserialise_check_ctx cctx;
726 struct midend_serialise_buf serbuf; 839 struct midend_serialise_buf serbuf;
727 840
@@ -732,14 +845,14 @@ static bool midend_redo(midend *me)
732 */ 845 */
733 serbuf.buf = NULL; 846 serbuf.buf = NULL;
734 serbuf.len = serbuf.size = 0; 847 serbuf.len = serbuf.size = 0;
735 midend_serialise(me, newgame_serialise_write, &serbuf); 848 midend_serialise(me, midend_serialise_buf_write, &serbuf);
736 849
737 rctx.ser = &me->newgame_redo; 850 rctx.ser = &me->newgame_redo;
738 rctx.len = me->newgame_redo.len; /* copy for reentrancy safety */ 851 rctx.len = me->newgame_redo.len; /* copy for reentrancy safety */
739 rctx.pos = 0; 852 rctx.pos = 0;
740 cctx.refused = false; 853 cctx.refused = false;
741 deserialise_error = midend_deserialise_internal( 854 deserialise_error = midend_deserialise_internal(
742 me, newgame_undo_deserialise_read, &rctx, 855 me, midend_serialise_buf_read, &rctx,
743 newgame_undo_deserialise_check, &cctx); 856 newgame_undo_deserialise_check, &cctx);
744 if (cctx.refused) { 857 if (cctx.refused) {
745 /* 858 /*
@@ -772,7 +885,8 @@ static bool midend_redo(midend *me)
772 * the midend so that we can undo back into it later. 885 * the midend so that we can undo back into it later.
773 */ 886 */
774 me->newgame_undo.len = 0; 887 me->newgame_undo.len = 0;
775 newgame_serialise_write(&me->newgame_undo, serbuf.buf, serbuf.len); 888 midend_serialise_buf_write(&me->newgame_undo,
889 serbuf.buf, serbuf.len);
776 890
777 sfree(serbuf.buf); 891 sfree(serbuf.buf);
778 return true; 892 return true;
@@ -859,12 +973,13 @@ void midend_restart_game(midend *me)
859 midend_set_timer(me); 973 midend_set_timer(me);
860} 974}
861 975
862static bool midend_really_process_key(midend *me, int x, int y, int button) 976static int midend_really_process_key(midend *me, int x, int y, int button)
863{ 977{
864 game_state *oldstate = 978 game_state *oldstate =
865 me->ourgame->dup_game(me->states[me->statepos - 1].state); 979 me->ourgame->dup_game(me->states[me->statepos - 1].state);
866 int type = MOVE; 980 int type = MOVE;
867 bool gottype = false, ret = true; 981 bool gottype = false;
982 int ret = PKR_NO_EFFECT;
868 float anim_time; 983 float anim_time;
869 game_state *s; 984 game_state *s;
870 char *movestr = NULL; 985 char *movestr = NULL;
@@ -875,40 +990,51 @@ static bool midend_really_process_key(midend *me, int x, int y, int button)
875 me->ui, me->drawstate, x, y, button); 990 me->ui, me->drawstate, x, y, button);
876 } 991 }
877 992
878 if (!movestr) { 993 if (movestr == NULL || movestr == MOVE_UNUSED) {
879 if (button == 'n' || button == 'N' || button == '\x0E' || 994 if ((me->one_key_shortcuts && (button == 'n' || button == 'N')) ||
880 button == UI_NEWGAME) { 995 button == '\x0E' || button == UI_NEWGAME) {
881 midend_new_game(me); 996 midend_new_game(me);
882 midend_redraw(me); 997 midend_redraw(me);
998 ret = PKR_SOME_EFFECT;
883 goto done; /* never animate */ 999 goto done; /* never animate */
884 } else if (button == 'u' || button == 'U' || 1000 } else if ((me->one_key_shortcuts && (button=='u' || button=='U')) ||
885 button == '\x1A' || button == '\x1F' || 1001 button == '*' || button == '\x1A' || button == '\x1F' ||
886 button == UI_UNDO) { 1002 button == UI_UNDO) {
887 midend_stop_anim(me); 1003 midend_stop_anim(me);
888 type = me->states[me->statepos-1].movetype; 1004 type = me->states[me->statepos-1].movetype;
889 gottype = true; 1005 gottype = true;
890 if (!midend_undo(me)) 1006 if (!midend_undo(me))
891 goto done; 1007 goto done;
892 } else if (button == 'r' || button == 'R' || 1008 ret = PKR_SOME_EFFECT;
893 button == '\x12' || button == '\x19' || 1009 } else if ((me->one_key_shortcuts && (button=='r' || button=='R')) ||
1010 button == '#' || button == '\x12' || button == '\x19' ||
894 button == UI_REDO) { 1011 button == UI_REDO) {
895 midend_stop_anim(me); 1012 midend_stop_anim(me);
896 if (!midend_redo(me)) 1013 if (!midend_redo(me))
897 goto done; 1014 goto done;
1015 ret = PKR_SOME_EFFECT;
898 } else if ((button == '\x13' || button == UI_SOLVE) && 1016 } else if ((button == '\x13' || button == UI_SOLVE) &&
899 me->ourgame->can_solve) { 1017 me->ourgame->can_solve) {
1018 ret = PKR_SOME_EFFECT;
900 if (midend_solve(me)) 1019 if (midend_solve(me))
901 goto done; 1020 goto done;
902 } else if (button == 'q' || button == 'Q' || button == '\x11' || 1021 } else if ((me->one_key_shortcuts && (button=='q' || button=='Q')) ||
903 button == UI_QUIT) { 1022 button == '\x11' || button == UI_QUIT) {
904 ret = false; 1023 ret = PKR_QUIT;
905 goto done; 1024 goto done;
906 } else 1025 } else {
1026 ret = PKR_UNUSED;
907 goto done; 1027 goto done;
1028 }
1029 } else if (movestr == MOVE_NO_EFFECT) {
1030 ret = PKR_NO_EFFECT;
1031 goto done;
908 } else { 1032 } else {
909 if (movestr == UI_UPDATE) 1033 ret = PKR_SOME_EFFECT;
1034 if (movestr == MOVE_UI_UPDATE)
910 s = me->states[me->statepos-1].state; 1035 s = me->states[me->statepos-1].state;
911 else { 1036 else {
1037 assert_printable_ascii(movestr);
912 s = me->ourgame->execute_move(me->states[me->statepos-1].state, 1038 s = me->ourgame->execute_move(me->states[me->statepos-1].state,
913 movestr); 1039 movestr);
914 assert(s != NULL); 1040 assert(s != NULL);
@@ -975,9 +1101,9 @@ static bool midend_really_process_key(midend *me, int x, int y, int button)
975 return ret; 1101 return ret;
976} 1102}
977 1103
978bool midend_process_key(midend *me, int x, int y, int button) 1104int midend_process_key(midend *me, int x, int y, int button)
979{ 1105{
980 bool ret = true; 1106 int ret = PKR_UNUSED, ret2;
981 1107
982 /* 1108 /*
983 * Harmonise mouse drag and release messages. 1109 * Harmonise mouse drag and release messages.
@@ -1052,6 +1178,10 @@ bool midend_process_key(midend *me, int x, int y, int button)
1052 * of '\n' etc for keyboard-based cursors. The choice of buttons 1178 * of '\n' etc for keyboard-based cursors. The choice of buttons
1053 * here could eventually be controlled by a runtime configuration 1179 * here could eventually be controlled by a runtime configuration
1054 * option. 1180 * option.
1181 *
1182 * We also handle converting MOD_CTRL|'a' etc into '\x01' etc,
1183 * specially recognising Ctrl+Shift+Z, and stripping modifier
1184 * flags off keys that aren't meant to have them.
1055 */ 1185 */
1056 if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) { 1186 if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
1057 if (me->pressed_mouse_button) { 1187 if (me->pressed_mouse_button) {
@@ -1076,11 +1206,34 @@ bool midend_process_key(midend *me, int x, int y, int button)
1076 /* 1206 /*
1077 * Fabricate a button-up for the previously pressed button. 1207 * Fabricate a button-up for the previously pressed button.
1078 */ 1208 */
1079 ret = ret && midend_really_process_key 1209 ret2 = midend_really_process_key
1080 (me, x, y, (me->pressed_mouse_button + 1210 (me, x, y, (me->pressed_mouse_button +
1081 (LEFT_RELEASE - LEFT_BUTTON))); 1211 (LEFT_RELEASE - LEFT_BUTTON)));
1082 } 1212 ret = min(ret, ret2);
1083 1213 }
1214
1215 /* Canonicalise CTRL+ASCII. */
1216 if ((button & MOD_CTRL) &&
1217 STRIP_BUTTON_MODIFIERS(button) >= 0x40 &&
1218 STRIP_BUTTON_MODIFIERS(button) < 0x80)
1219 button = button & (0x1f | (MOD_MASK & ~MOD_CTRL));
1220 /* Special handling to make CTRL+SHFT+Z into REDO. */
1221 if ((button & (~MOD_MASK | MOD_SHFT)) == (MOD_SHFT | '\x1A'))
1222 button = UI_REDO;
1223 /* interpret_move() expects CTRL and SHFT only on cursor keys, and
1224 * TAB (added as of 7/2024 to support Untangle). */
1225 if (!IS_CURSOR_MOVE(STRIP_BUTTON_MODIFIERS(button))) {
1226 /* reject CTRL+anything odd */
1227 if ((button & MOD_CTRL) && STRIP_BUTTON_MODIFIERS(button) >= 0x20)
1228 return PKR_UNUSED;
1229 /* otherwise strip them, except for tab */
1230 if (STRIP_BUTTON_MODIFIERS(button) != '\t')
1231 button &= ~(MOD_CTRL | MOD_SHFT);
1232 }
1233 /* interpret_move() expects NUM_KEYPAD only on numbers. */
1234 if (STRIP_BUTTON_MODIFIERS(button) < '0' ||
1235 STRIP_BUTTON_MODIFIERS(button) > '9')
1236 button &= ~MOD_NUM_KEYPAD;
1084 /* 1237 /*
1085 * Translate keyboard presses to cursor selection. 1238 * Translate keyboard presses to cursor selection.
1086 */ 1239 */
@@ -1101,7 +1254,8 @@ bool midend_process_key(midend *me, int x, int y, int button)
1101 /* 1254 /*
1102 * Now send on the event we originally received. 1255 * Now send on the event we originally received.
1103 */ 1256 */
1104 ret = ret && midend_really_process_key(me, x, y, button); 1257 ret2 = midend_really_process_key(me, x, y, button);
1258 ret = min(ret, ret2);
1105 1259
1106 /* 1260 /*
1107 * And update the currently pressed button. 1261 * And update the currently pressed button.
@@ -1121,7 +1275,7 @@ key_label *midend_request_keys(midend *me, int *n)
1121 1275
1122 if(me->ourgame->request_keys) 1276 if(me->ourgame->request_keys)
1123 { 1277 {
1124 keys = me->ourgame->request_keys(midend_get_params(me), &nkeys); 1278 keys = me->ourgame->request_keys(me->params, &nkeys);
1125 for(i = 0; i < nkeys; ++i) 1279 for(i = 0; i < nkeys; ++i)
1126 { 1280 {
1127 if(!keys[i].label) 1281 if(!keys[i].label)
@@ -1135,12 +1289,38 @@ key_label *midend_request_keys(midend *me, int *n)
1135 return keys; 1289 return keys;
1136} 1290}
1137 1291
1292/* Return a good label to show next to a key right now. */
1293const char *midend_current_key_label(midend *me, int button)
1294{
1295 assert(IS_CURSOR_SELECT(button));
1296 if (!me->ourgame->current_key_label) return "";
1297 return me->ourgame->current_key_label(
1298 me->ui, me->states[me->statepos-1].state, button);
1299}
1300
1138void midend_redraw(midend *me) 1301void midend_redraw(midend *me)
1139{ 1302{
1140 assert(me->drawing); 1303 assert(me->drawing);
1141 1304
1142 if (me->statepos > 0 && me->drawstate) { 1305 if (me->statepos > 0 && me->drawstate) {
1306 bool first_draw = me->first_draw;
1307 me->first_draw = false;
1308
1143 start_draw(me->drawing); 1309 start_draw(me->drawing);
1310
1311 if (first_draw) {
1312 /*
1313 * The initial contents of the window are not guaranteed
1314 * by the front end. But we also don't want to require
1315 * every single game to go to the effort of clearing the
1316 * window on setup. So we centralise here the operation of
1317 * covering the whole window with colour 0 (assumed to be
1318 * the puzzle's background colour) the first time we do a
1319 * redraw operation with a new drawstate.
1320 */
1321 draw_rect(me->drawing, 0, 0, me->winwidth, me->winheight, 0);
1322 }
1323
1144 if (me->oldstate && me->anim_time > 0 && 1324 if (me->oldstate && me->anim_time > 0 &&
1145 me->anim_pos < me->anim_time) { 1325 me->anim_pos < me->anim_time) {
1146 assert(me->dir != 0); 1326 assert(me->dir != 0);
@@ -1152,6 +1332,15 @@ void midend_redraw(midend *me)
1152 me->states[me->statepos-1].state, +1 /*shrug*/, 1332 me->states[me->statepos-1].state, +1 /*shrug*/,
1153 me->ui, 0.0, me->flash_pos); 1333 me->ui, 0.0, me->flash_pos);
1154 } 1334 }
1335
1336 if (first_draw) {
1337 /*
1338 * Call a big draw_update on the whole window, in case the
1339 * game backend didn't.
1340 */
1341 draw_update(me->drawing, 0, 0, me->winwidth, me->winheight);
1342 }
1343
1155 end_draw(me->drawing); 1344 end_draw(me->drawing);
1156 } 1345 }
1157} 1346}
@@ -1201,6 +1390,7 @@ float *midend_colours(midend *me, int *ncolours)
1201 float *ret; 1390 float *ret;
1202 1391
1203 ret = me->ourgame->colours(me->frontend, ncolours); 1392 ret = me->ourgame->colours(me->frontend, ncolours);
1393 assert(*ncolours >= 1);
1204 1394
1205 { 1395 {
1206 int i; 1396 int i;
@@ -1227,6 +1417,9 @@ float *midend_colours(midend *me, int *ncolours)
1227 ret[i*3 + 1] = g / 255.0F; 1417 ret[i*3 + 1] = g / 255.0F;
1228 ret[i*3 + 2] = b / 255.0F; 1418 ret[i*3 + 2] = b / 255.0F;
1229 } 1419 }
1420 assert(0.0F <= ret[i*3 + 0] && ret[i*3 + 0] <= 1.0F);
1421 assert(0.0F <= ret[i*3 + 1] && ret[i*3 + 1] <= 1.0F);
1422 assert(0.0F <= ret[i*3 + 2] && ret[i*3 + 2] <= 1.0F);
1230 } 1423 }
1231 } 1424 }
1232 1425
@@ -1359,7 +1552,7 @@ static void preset_menu_encode_params(midend *me, struct preset_menu *menu)
1359 for (i = 0; i < menu->n_entries; i++) { 1552 for (i = 0; i < menu->n_entries; i++) {
1360 if (menu->entries[i].params) { 1553 if (menu->entries[i].params) {
1361 me->encoded_presets[menu->entries[i].id] = 1554 me->encoded_presets[menu->entries[i].id] =
1362 me->ourgame->encode_params(menu->entries[i].params, true); 1555 encode_params(me, menu->entries[i].params, true);
1363 } else { 1556 } else {
1364 preset_menu_encode_params(me, menu->entries[i].submenu); 1557 preset_menu_encode_params(me, menu->entries[i].submenu);
1365 } 1558 }
@@ -1438,7 +1631,7 @@ struct preset_menu *midend_get_presets(midend *me, int *id_limit)
1438 1631
1439int midend_which_preset(midend *me) 1632int midend_which_preset(midend *me)
1440{ 1633{
1441 char *encoding = me->ourgame->encode_params(me->params, true); 1634 char *encoding = encode_params(me, me->params, true);
1442 int i, ret; 1635 int i, ret;
1443 1636
1444 ret = -1; 1637 ret = -1;
@@ -1496,6 +1689,10 @@ bool midend_get_cursor_location(midend *me,
1496void midend_supersede_game_desc(midend *me, const char *desc, 1689void midend_supersede_game_desc(midend *me, const char *desc,
1497 const char *privdesc) 1690 const char *privdesc)
1498{ 1691{
1692 /* Assert that the descriptions consists only of printable ASCII. */
1693 assert_printable_ascii(desc);
1694 if (privdesc)
1695 assert_printable_ascii(privdesc);
1499 sfree(me->desc); 1696 sfree(me->desc);
1500 sfree(me->privdesc); 1697 sfree(me->privdesc);
1501 me->desc = dupstr(desc); 1698 me->desc = dupstr(desc);
@@ -1545,7 +1742,7 @@ config_item *midend_get_config(midend *me, int which, char **wintitle)
1545 * the former is likely to persist across many code 1742 * the former is likely to persist across many code
1546 * changes). 1743 * changes).
1547 */ 1744 */
1548 parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED); 1745 parstr = encode_params(me, me->curparams, which == CFG_SEED);
1549 assert(parstr); 1746 assert(parstr);
1550 if (which == CFG_DESC) { 1747 if (which == CFG_DESC) {
1551 rest = me->desc ? me->desc : ""; 1748 rest = me->desc ? me->desc : "";
@@ -1562,6 +1759,10 @@ config_item *midend_get_config(midend *me, int which, char **wintitle)
1562 ret[1].name = NULL; 1759 ret[1].name = NULL;
1563 1760
1564 return ret; 1761 return ret;
1762 case CFG_PREFS:
1763 sprintf(titlebuf, "%s preferences", me->ourgame->name);
1764 *wintitle = titlebuf;
1765 return midend_get_prefs(me, NULL);
1565 } 1766 }
1566 1767
1567 assert(!"We shouldn't be here"); 1768 assert(!"We shouldn't be here");
@@ -1670,6 +1871,7 @@ static const char *midend_game_id_int(midend *me, const char *id, int defmode)
1670 newcurparams = me->ourgame->default_params(); 1871 newcurparams = me->ourgame->default_params();
1671 } 1872 }
1672 me->ourgame->decode_params(newcurparams, par); 1873 me->ourgame->decode_params(newcurparams, par);
1874 sfree(par);
1673 error = me->ourgame->validate_params(newcurparams, desc == NULL); 1875 error = me->ourgame->validate_params(newcurparams, desc == NULL);
1674 if (error) { 1876 if (error) {
1675 me->ourgame->free_params(newcurparams); 1877 me->ourgame->free_params(newcurparams);
@@ -1689,7 +1891,7 @@ static const char *midend_game_id_int(midend *me, const char *id, int defmode)
1689 1891
1690 newparams = me->ourgame->dup_params(me->params); 1892 newparams = me->ourgame->dup_params(me->params);
1691 1893
1692 tmpstr = me->ourgame->encode_params(newcurparams, false); 1894 tmpstr = encode_params(me, newcurparams, false);
1693 me->ourgame->decode_params(newparams, tmpstr); 1895 me->ourgame->decode_params(newparams, tmpstr);
1694 1896
1695 sfree(tmpstr); 1897 sfree(tmpstr);
@@ -1745,8 +1947,6 @@ static const char *midend_game_id_int(midend *me, const char *id, int defmode)
1745 me->genmode = GOT_SEED; 1947 me->genmode = GOT_SEED;
1746 } 1948 }
1747 1949
1748 sfree(par);
1749
1750 me->newgame_can_store_undo = false; 1950 me->newgame_can_store_undo = false;
1751 1951
1752 return NULL; 1952 return NULL;
@@ -1761,7 +1961,7 @@ char *midend_get_game_id(midend *me)
1761{ 1961{
1762 char *parstr, *ret; 1962 char *parstr, *ret;
1763 1963
1764 parstr = me->ourgame->encode_params(me->curparams, false); 1964 parstr = encode_params(me, me->curparams, false);
1765 assert(parstr); 1965 assert(parstr);
1766 assert(me->desc); 1966 assert(me->desc);
1767 ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char); 1967 ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
@@ -1777,7 +1977,7 @@ char *midend_get_random_seed(midend *me)
1777 if (!me->seedstr) 1977 if (!me->seedstr)
1778 return NULL; 1978 return NULL;
1779 1979
1780 parstr = me->ourgame->encode_params(me->curparams, true); 1980 parstr = encode_params(me, me->curparams, true);
1781 assert(parstr); 1981 assert(parstr);
1782 ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char); 1982 ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char);
1783 sprintf(ret, "%s#%s", parstr, me->seedstr); 1983 sprintf(ret, "%s#%s", parstr, me->seedstr);
@@ -1811,6 +2011,10 @@ const char *midend_set_config(midend *me, int which, config_item *cfg)
1811 if (error) 2011 if (error)
1812 return error; 2012 return error;
1813 break; 2013 break;
2014
2015 case CFG_PREFS:
2016 midend_set_prefs(me, me->ui, cfg);
2017 break;
1814 } 2018 }
1815 2019
1816 return NULL; 2020 return NULL;
@@ -1849,12 +2053,13 @@ const char *midend_solve(midend *me)
1849 movestr = me->ourgame->solve(me->states[0].state, 2053 movestr = me->ourgame->solve(me->states[0].state,
1850 me->states[me->statepos-1].state, 2054 me->states[me->statepos-1].state,
1851 me->aux_info, &msg); 2055 me->aux_info, &msg);
1852 assert(movestr != UI_UPDATE); 2056 assert(movestr != MOVE_UI_UPDATE);
1853 if (!movestr) { 2057 if (!movestr) {
1854 if (!msg) 2058 if (!msg)
1855 msg = "Solve operation failed"; /* _shouldn't_ happen, but can */ 2059 msg = "Solve operation failed"; /* _shouldn't_ happen, but can */
1856 return msg; 2060 return msg;
1857 } 2061 }
2062 assert_printable_ascii(movestr);
1858 s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr); 2063 s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
1859 assert(s); 2064 assert(s);
1860 2065
@@ -1961,7 +2166,9 @@ void midend_serialise(midend *me,
1961 char lbuf[9]; \ 2166 char lbuf[9]; \
1962 copy_left_justified(lbuf, sizeof(lbuf), h); \ 2167 copy_left_justified(lbuf, sizeof(lbuf), h); \
1963 sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \ 2168 sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \
2169 assert_printable_ascii(hbuf); \
1964 write(wctx, hbuf, strlen(hbuf)); \ 2170 write(wctx, hbuf, strlen(hbuf)); \
2171 assert_printable_ascii(str); \
1965 write(wctx, str, strlen(str)); \ 2172 write(wctx, str, strlen(str)); \
1966 write(wctx, "\n", 1); \ 2173 write(wctx, "\n", 1); \
1967} while (0) 2174} while (0)
@@ -1986,7 +2193,7 @@ void midend_serialise(midend *me,
1986 * The current long-term parameters structure, in full. 2193 * The current long-term parameters structure, in full.
1987 */ 2194 */
1988 if (me->params) { 2195 if (me->params) {
1989 char *s = me->ourgame->encode_params(me->params, true); 2196 char *s = encode_params(me, me->params, true);
1990 wr("PARAMS", s); 2197 wr("PARAMS", s);
1991 sfree(s); 2198 sfree(s);
1992 } 2199 }
@@ -1995,7 +2202,7 @@ void midend_serialise(midend *me,
1995 * The current short-term parameters structure, in full. 2202 * The current short-term parameters structure, in full.
1996 */ 2203 */
1997 if (me->curparams) { 2204 if (me->curparams) {
1998 char *s = me->ourgame->encode_params(me->curparams, true); 2205 char *s = encode_params(me, me->curparams, true);
1999 wr("CPARAMS", s); 2206 wr("CPARAMS", s);
2000 sfree(s); 2207 sfree(s);
2001 } 2208 }
@@ -2003,8 +2210,27 @@ void midend_serialise(midend *me,
2003 /* 2210 /*
2004 * The current game description, the privdesc, and the random seed. 2211 * The current game description, the privdesc, and the random seed.
2005 */ 2212 */
2006 if (me->seedstr) 2213 if (me->seedstr) {
2007 wr("SEED", me->seedstr); 2214 /*
2215 * Random seeds are not necessarily printable ASCII.
2216 * Hex-encode the seed if necessary. Printable ASCII seeds
2217 * are emitted unencoded for compatibility with older
2218 * versions.
2219 */
2220 int i;
2221
2222 for (i = 0; me->seedstr[i]; i++)
2223 if (me->seedstr[i] < 32 || me->seedstr[i] >= 127)
2224 break;
2225 if (me->seedstr[i]) {
2226 char *hexseed = bin2hex((unsigned char *)me->seedstr,
2227 strlen(me->seedstr));
2228
2229 wr("HEXSEED", hexseed);
2230 sfree(hexseed);
2231 } else
2232 wr("SEED", me->seedstr);
2233 }
2008 if (me->desc) 2234 if (me->desc)
2009 wr("DESC", me->desc); 2235 wr("DESC", me->desc);
2010 if (me->privdesc) 2236 if (me->privdesc)
@@ -2036,7 +2262,7 @@ void midend_serialise(midend *me,
2036 /* 2262 /*
2037 * Any required serialisation of the game_ui. 2263 * Any required serialisation of the game_ui.
2038 */ 2264 */
2039 if (me->ui) { 2265 if (me->ui && me->ourgame->encode_ui) {
2040 char *s = me->ourgame->encode_ui(me->ui); 2266 char *s = me->ourgame->encode_ui(me->ui);
2041 if (s) { 2267 if (s) {
2042 wr("UI", s); 2268 wr("UI", s);
@@ -2049,7 +2275,7 @@ void midend_serialise(midend *me,
2049 */ 2275 */
2050 if (me->ourgame->is_timed) { 2276 if (me->ourgame->is_timed) {
2051 char buf[80]; 2277 char buf[80];
2052 ftoa(buf, me->elapsed); 2278 sprintf(buf, "%g", me->elapsed);
2053 wr("TIME", buf); 2279 wr("TIME", buf);
2054 } 2280 }
2055 2281
@@ -2060,6 +2286,7 @@ void midend_serialise(midend *me,
2060 char buf[80]; 2286 char buf[80];
2061 sprintf(buf, "%d", me->nstates); 2287 sprintf(buf, "%d", me->nstates);
2062 wr("NSTATES", buf); 2288 wr("NSTATES", buf);
2289 assert(me->statepos >= 1 && me->statepos <= me->nstates);
2063 sprintf(buf, "%d", me->statepos); 2290 sprintf(buf, "%d", me->statepos);
2064 wr("STATEPOS", buf); 2291 wr("STATEPOS", buf);
2065 } 2292 }
@@ -2159,7 +2386,7 @@ static const char *midend_deserialise_internal(
2159 2386
2160 if (c == ':') { 2387 if (c == ':') {
2161 break; 2388 break;
2162 } else if (c >= '0' && c <= '9') { 2389 } else if (c >= '0' && c <= '9' && len < (INT_MAX - 10) / 10) {
2163 len = (len * 10) + (c - '0'); 2390 len = (len * 10) + (c - '0');
2164 } else { 2391 } else {
2165 if (started) 2392 if (started)
@@ -2171,10 +2398,17 @@ static const char *midend_deserialise_internal(
2171 2398
2172 val = snewn(len+1, char); 2399 val = snewn(len+1, char);
2173 if (!read(rctx, val, len)) { 2400 if (!read(rctx, val, len)) {
2174 if (started) 2401 /* unexpected EOF */
2175 goto cleanup; 2402 goto cleanup;
2176 } 2403 }
2177 val[len] = '\0'; 2404 val[len] = '\0';
2405 /* Validate that all values (apart from SEED) are printable ASCII. */
2406 if (strcmp(key, "SEED"))
2407 for (i = 0; val[i]; i++)
2408 if (val[i] < 32 || val[i] >= 127) {
2409 ret = "Forbidden characters in saved game file";
2410 goto cleanup;
2411 }
2178 2412
2179 if (!started) { 2413 if (!started) {
2180 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) { 2414 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
@@ -2204,6 +2438,15 @@ static const char *midend_deserialise_internal(
2204 sfree(data.cparstr); 2438 sfree(data.cparstr);
2205 data.cparstr = val; 2439 data.cparstr = val;
2206 val = NULL; 2440 val = NULL;
2441 } else if (!strcmp(key, "HEXSEED")) {
2442 unsigned char *tmp;
2443 int len = strlen(val) / 2; /* length in bytes */
2444 tmp = hex2bin(val, len);
2445 sfree(data.seed);
2446 data.seed = snewn(len + 1, char);
2447 memcpy(data.seed, tmp, len);
2448 data.seed[len] = '\0';
2449 sfree(tmp);
2207 } else if (!strcmp(key, "SEED")) { 2450 } else if (!strcmp(key, "SEED")) {
2208 sfree(data.seed); 2451 sfree(data.seed);
2209 data.seed = val; 2452 data.seed = val;
@@ -2234,15 +2477,15 @@ static const char *midend_deserialise_internal(
2234 } else if (!strcmp(key, "TIME")) { 2477 } else if (!strcmp(key, "TIME")) {
2235 data.elapsed = (float)atof(val); 2478 data.elapsed = (float)atof(val);
2236 } else if (!strcmp(key, "NSTATES")) { 2479 } else if (!strcmp(key, "NSTATES")) {
2480 if (data.states) {
2481 ret = "Two state counts provided in save file";
2482 goto cleanup;
2483 }
2237 data.nstates = atoi(val); 2484 data.nstates = atoi(val);
2238 if (data.nstates <= 0) { 2485 if (data.nstates <= 0) {
2239 ret = "Number of states in save file was negative"; 2486 ret = "Number of states in save file was negative";
2240 goto cleanup; 2487 goto cleanup;
2241 } 2488 }
2242 if (data.states) {
2243 ret = "Two state counts provided in save file";
2244 goto cleanup;
2245 }
2246 data.states = snewn(data.nstates, struct midend_state_entry); 2489 data.states = snewn(data.nstates, struct midend_state_entry);
2247 for (i = 0; i < data.nstates; i++) { 2490 for (i = 0; i < data.nstates; i++) {
2248 data.states[i].state = NULL; 2491 data.states[i].state = NULL;
@@ -2251,19 +2494,25 @@ static const char *midend_deserialise_internal(
2251 } 2494 }
2252 } else if (!strcmp(key, "STATEPOS")) { 2495 } else if (!strcmp(key, "STATEPOS")) {
2253 data.statepos = atoi(val); 2496 data.statepos = atoi(val);
2254 } else if (!strcmp(key, "MOVE")) { 2497 } else if (!strcmp(key, "MOVE") ||
2255 gotstates++; 2498 !strcmp(key, "SOLVE") ||
2256 data.states[gotstates].movetype = MOVE; 2499 !strcmp(key, "RESTART")) {
2257 data.states[gotstates].movestr = val; 2500 if (!data.states) {
2258 val = NULL; 2501 ret = "No state count provided in save file";
2259 } else if (!strcmp(key, "SOLVE")) { 2502 goto cleanup;
2260 gotstates++; 2503 }
2261 data.states[gotstates].movetype = SOLVE; 2504 if (data.statepos < 0) {
2262 data.states[gotstates].movestr = val; 2505 ret = "No game position provided in save file";
2263 val = NULL; 2506 goto cleanup;
2264 } else if (!strcmp(key, "RESTART")) { 2507 }
2265 gotstates++; 2508 gotstates++;
2266 data.states[gotstates].movetype = RESTART; 2509 assert(gotstates < data.nstates);
2510 if (!strcmp(key, "MOVE"))
2511 data.states[gotstates].movetype = MOVE;
2512 else if (!strcmp(key, "SOLVE"))
2513 data.states[gotstates].movetype = SOLVE;
2514 else
2515 data.states[gotstates].movetype = RESTART;
2267 data.states[gotstates].movestr = val; 2516 data.states[gotstates].movestr = val;
2268 val = NULL; 2517 val = NULL;
2269 } 2518 }
@@ -2274,12 +2523,20 @@ static const char *midend_deserialise_internal(
2274 } 2523 }
2275 2524
2276 data.params = me->ourgame->default_params(); 2525 data.params = me->ourgame->default_params();
2526 if (!data.parstr) {
2527 ret = "Long-term parameters in save file are missing";
2528 goto cleanup;
2529 }
2277 me->ourgame->decode_params(data.params, data.parstr); 2530 me->ourgame->decode_params(data.params, data.parstr);
2278 if (me->ourgame->validate_params(data.params, true)) { 2531 if (me->ourgame->validate_params(data.params, true)) {
2279 ret = "Long-term parameters in save file are invalid"; 2532 ret = "Long-term parameters in save file are invalid";
2280 goto cleanup; 2533 goto cleanup;
2281 } 2534 }
2282 data.cparams = me->ourgame->default_params(); 2535 data.cparams = me->ourgame->default_params();
2536 if (!data.cparstr) {
2537 ret = "Short-term parameters in save file are missing";
2538 goto cleanup;
2539 }
2283 me->ourgame->decode_params(data.cparams, data.cparstr); 2540 me->ourgame->decode_params(data.cparams, data.cparstr);
2284 if (me->ourgame->validate_params(data.cparams, false)) { 2541 if (me->ourgame->validate_params(data.cparams, false)) {
2285 ret = "Short-term parameters in save file are invalid"; 2542 ret = "Short-term parameters in save file are invalid";
@@ -2305,12 +2562,18 @@ static const char *midend_deserialise_internal(
2305 ret = "Game private description in save file is invalid"; 2562 ret = "Game private description in save file is invalid";
2306 goto cleanup; 2563 goto cleanup;
2307 } 2564 }
2308 if (data.statepos < 0 || data.statepos >= data.nstates) { 2565 if (data.statepos < 1 || data.statepos > data.nstates) {
2309 ret = "Game position in save file is out of range"; 2566 ret = "Game position in save file is out of range";
2567 goto cleanup;
2310 } 2568 }
2311 2569
2570 if (!data.states) {
2571 ret = "No state count provided in save file";
2572 goto cleanup;
2573 }
2312 data.states[0].state = me->ourgame->new_game( 2574 data.states[0].state = me->ourgame->new_game(
2313 me, data.cparams, data.privdesc ? data.privdesc : data.desc); 2575 me, data.cparams, data.privdesc ? data.privdesc : data.desc);
2576
2314 for (i = 1; i < data.nstates; i++) { 2577 for (i = 1; i < data.nstates; i++) {
2315 assert(data.states[i].movetype != NEWGAME); 2578 assert(data.states[i].movetype != NEWGAME);
2316 switch (data.states[i].movetype) { 2579 switch (data.states[i].movetype) {
@@ -2336,7 +2599,10 @@ static const char *midend_deserialise_internal(
2336 } 2599 }
2337 2600
2338 data.ui = me->ourgame->new_ui(data.states[0].state); 2601 data.ui = me->ourgame->new_ui(data.states[0].state);
2339 me->ourgame->decode_ui(data.ui, data.uistr); 2602 midend_apply_prefs(me, data.ui);
2603 if (data.uistr && me->ourgame->decode_ui)
2604 me->ourgame->decode_ui(data.ui, data.uistr,
2605 data.states[data.statepos-1].state);
2340 2606
2341 /* 2607 /*
2342 * Run the externally provided check function, and abort if it 2608 * Run the externally provided check function, and abort if it
@@ -2429,6 +2695,7 @@ static const char *midend_deserialise_internal(
2429 me->drawstate = 2695 me->drawstate =
2430 me->ourgame->new_drawstate(me->drawing, 2696 me->ourgame->new_drawstate(me->drawing,
2431 me->states[me->statepos-1].state); 2697 me->states[me->statepos-1].state);
2698 me->first_draw = true;
2432 midend_size_new_drawstate(me); 2699 midend_size_new_drawstate(me);
2433 if (me->game_id_change_notify_function) 2700 if (me->game_id_change_notify_function)
2434 me->game_id_change_notify_function(me->game_id_change_notify_ctx); 2701 me->game_id_change_notify_function(me->game_id_change_notify_ctx);
@@ -2528,7 +2795,7 @@ const char *identify_game(char **name,
2528 2795
2529 if (c == ':') { 2796 if (c == ':') {
2530 break; 2797 break;
2531 } else if (c >= '0' && c <= '9') { 2798 } else if (c >= '0' && c <= '9' && len < (INT_MAX - 10) / 10) {
2532 len = (len * 10) + (c - '0'); 2799 len = (len * 10) + (c - '0');
2533 } else { 2800 } else {
2534 if (started) 2801 if (started)
@@ -2540,7 +2807,7 @@ const char *identify_game(char **name,
2540 2807
2541 val = snewn(len+1, char); 2808 val = snewn(len+1, char);
2542 if (!read(rctx, val, len)) { 2809 if (!read(rctx, val, len)) {
2543 if (started) 2810 /* unexpected EOF */
2544 goto cleanup; 2811 goto cleanup;
2545 } 2812 }
2546 val[len] = '\0'; 2813 val[len] = '\0';
@@ -2605,14 +2872,340 @@ const char *midend_print_puzzle(midend *me, document *doc, bool with_soln)
2605 soln = NULL; 2872 soln = NULL;
2606 2873
2607 /* 2874 /*
2608 * This call passes over ownership of the two game_states and 2875 * This call passes over ownership of the two game_states, the
2609 * the game_params. Hence we duplicate the ones we want to 2876 * game_params and the game_ui. Hence we duplicate the ones we
2610 * keep, and we don't have to bother freeing soln if it was 2877 * want to keep, and we don't have to bother freeing soln if it
2611 * non-NULL. 2878 * was non-NULL.
2612 */ 2879 */
2880 game_ui *ui = me->ourgame->new_ui(me->states[0].state);
2881 midend_apply_prefs(me, ui);
2613 document_add_puzzle(doc, me->ourgame, 2882 document_add_puzzle(doc, me->ourgame,
2614 me->ourgame->dup_params(me->curparams), 2883 me->ourgame->dup_params(me->curparams), ui,
2615 me->ourgame->dup_game(me->states[0].state), soln); 2884 me->ourgame->dup_game(me->states[0].state), soln);
2616 2885
2617 return NULL; 2886 return NULL;
2618} 2887}
2888
2889static void midend_apply_prefs(midend *me, game_ui *ui)
2890{
2891 struct midend_serialise_buf_read_ctx rctx[1];
2892 rctx->ser = &me->be_prefs;
2893 rctx->len = me->be_prefs.len;
2894 rctx->pos = 0;
2895 const char *err = midend_deserialise_prefs(
2896 me, ui, midend_serialise_buf_read, rctx);
2897 /* This should have come from our own serialise function, so
2898 * it should never be invalid. */
2899 assert(!err && "Bad internal serialisation of preferences");
2900}
2901
2902static config_item *midend_get_prefs(midend *me, game_ui *ui)
2903{
2904 int n_be_prefs, n_me_prefs, pos, i;
2905 config_item *all_prefs, *be_prefs;
2906
2907 be_prefs = NULL;
2908 n_be_prefs = 0;
2909 if (me->ourgame->get_prefs) {
2910 if (ui) {
2911 be_prefs = me->ourgame->get_prefs(ui);
2912 } else if (me->ui) {
2913 be_prefs = me->ourgame->get_prefs(me->ui);
2914 } else {
2915 game_ui *tmp_ui = me->ourgame->new_ui(NULL);
2916 be_prefs = me->ourgame->get_prefs(tmp_ui);
2917 me->ourgame->free_ui(tmp_ui);
2918 }
2919 while (be_prefs[n_be_prefs].type != C_END)
2920 n_be_prefs++;
2921 }
2922
2923 n_me_prefs = 1;
2924 all_prefs = snewn(n_me_prefs + n_be_prefs + 1, config_item);
2925
2926 pos = 0;
2927
2928 assert(pos < n_me_prefs);
2929 all_prefs[pos].name = "Keyboard shortcuts without Ctrl";
2930 all_prefs[pos].kw = "one-key-shortcuts";
2931 all_prefs[pos].type = C_BOOLEAN;
2932 all_prefs[pos].u.boolean.bval = me->one_key_shortcuts;
2933 pos++;
2934
2935 for (i = 0; i < n_be_prefs; i++) {
2936 all_prefs[pos] = be_prefs[i]; /* structure copy */
2937 pos++;
2938 }
2939
2940 all_prefs[pos].name = NULL;
2941 all_prefs[pos].type = C_END;
2942
2943 if (be_prefs)
2944 /* We already copied each element, so don't free those with
2945 free_cfg(). */
2946 sfree(be_prefs);
2947
2948 return all_prefs;
2949}
2950
2951static void midend_set_prefs(midend *me, game_ui *ui, config_item *all_prefs)
2952{
2953 int pos = 0;
2954 game_ui *tmpui = NULL;
2955
2956 me->one_key_shortcuts = all_prefs[pos].u.boolean.bval;
2957 pos++;
2958
2959 if (me->ourgame->get_prefs) {
2960 if (!ui)
2961 ui = tmpui = me->ourgame->new_ui(NULL);
2962 me->ourgame->set_prefs(ui, all_prefs + pos);
2963 }
2964
2965 me->be_prefs.len = 0;
2966 midend_serialise_prefs(me, ui, midend_serialise_buf_write, &me->be_prefs);
2967
2968 if (tmpui)
2969 me->ourgame->free_ui(tmpui);
2970}
2971
2972static void midend_serialise_prefs(
2973 midend *me, game_ui *ui,
2974 void (*write)(void *ctx, const void *buf, int len), void *wctx)
2975{
2976 config_item *cfg;
2977 int i;
2978
2979 cfg = midend_get_prefs(me, ui);
2980
2981 assert(cfg);
2982
2983 for (i = 0; cfg[i].type != C_END; i++) {
2984 config_item *it = &cfg[i];
2985
2986 /* Expect keywords to be made up only of simple characters */
2987 assert(it->kw[strspn(it->kw, "abcdefghijklmnopqrstuvwxyz-")] == '\0');
2988
2989 write(wctx, it->kw, strlen(it->kw));
2990 write(wctx, "=", 1);
2991
2992 switch (it->type) {
2993 case C_BOOLEAN:
2994 if (it->u.boolean.bval)
2995 write(wctx, "true", 4);
2996 else
2997 write(wctx, "false", 5);
2998 break;
2999 case C_STRING: {
3000 const char *p = it->u.string.sval;
3001 while (*p) {
3002 char c = *p++;
3003 write(wctx, &c, 1);
3004 if (c == '\n')
3005 write(wctx, " ", 1);
3006 }
3007 break;
3008 }
3009 case C_CHOICES: {
3010 int n = it->u.choices.selected;
3011 const char *p = it->u.choices.choicekws;
3012 char sepstr[2];
3013
3014 sepstr[0] = *p++;
3015 sepstr[1] = '\0';
3016
3017 while (n > 0) {
3018 const char *q = strchr(p, sepstr[0]);
3019 assert(q != NULL && "Value out of range in C_CHOICES");
3020 p = q+1;
3021 n--;
3022 }
3023
3024 write(wctx, p, strcspn(p, sepstr));
3025 break;
3026 }
3027 }
3028
3029 write(wctx, "\n", 1);
3030 }
3031
3032 free_cfg(cfg);
3033}
3034
3035struct buffer {
3036 char *data;
3037 size_t len, size;
3038};
3039
3040static void buffer_append(struct buffer *buf, char c)
3041{
3042 if (buf->len + 2 > buf->size) {
3043 size_t new_size = buf->size + buf->size / 4 + 128;
3044 assert(new_size > buf->size);
3045 buf->data = sresize(buf->data, new_size, char);
3046 buf->size = new_size;
3047 assert(buf->len < buf->size);
3048 }
3049 buf->data[buf->len++] = c;
3050 assert(buf->len < buf->size);
3051 buf->data[buf->len] = '\0';
3052}
3053
3054static const char *midend_deserialise_prefs(
3055 midend *me, game_ui *ui,
3056 bool (*read)(void *ctx, void *buf, int len), void *rctx)
3057{
3058 config_item *cfg, *it;
3059 int i;
3060 struct buffer buf[1] = {{ NULL, 0, 0 }};
3061 const char *errmsg = NULL;
3062 char read_char;
3063 char ungot_char = '\0';
3064 bool have_ungot_a_char = false, eof = false;
3065
3066 cfg = midend_get_prefs(me, ui);
3067
3068 while (!eof) {
3069 if (have_ungot_a_char) {
3070 read_char = ungot_char;
3071 have_ungot_a_char = false;
3072 } else {
3073 if (!read(rctx, &read_char, 1))
3074 goto out; /* EOF at line start == success */
3075 }
3076
3077 if (read_char == '#' || read_char == '\n') {
3078 /* Skip comment or blank line */
3079 while (read_char != '\n') {
3080 if (!read(rctx, &read_char, 1))
3081 goto out; /* EOF during boring line == success */
3082 }
3083 continue;
3084 }
3085
3086 buf->len = 0;
3087 while (true) {
3088 buffer_append(buf, read_char);
3089 if (!read(rctx, &read_char, 1)) {
3090 errmsg = "Partial line at end of preferences file";
3091 goto out;
3092 }
3093 if (read_char == '\n') {
3094 errmsg = "Expected '=' after keyword";
3095 goto out;
3096 }
3097 if (read_char == '=')
3098 break;
3099 }
3100
3101 it = NULL;
3102 for (i = 0; cfg[i].type != C_END; i++)
3103 if (!strcmp(buf->data, cfg[i].kw))
3104 it = &cfg[i];
3105
3106 buf->len = 0;
3107 while (true) {
3108 if (!read(rctx, &read_char, 1)) {
3109 /* We tolerate missing \n at the end of the file, so
3110 * this is taken to mean we've got a complete config
3111 * directive. But set the eof flag so that we stop
3112 * after processing it. */
3113 eof = true;
3114 break;
3115 } else if (read_char == '\n') {
3116 /* Newline _might_ be the end of this config
3117 * directive, unless it's followed by a space, in
3118 * which case it's a space-stuffed line
3119 * continuation. */
3120 if (read(rctx, &read_char, 1)) {
3121 if (read_char == ' ') {
3122 buffer_append(buf, '\n');
3123 continue;
3124 } else {
3125 /* But if the next character wasn't a space,
3126 * then we must unget it so that it'll be
3127 * available to the next iteration of our
3128 * outer loop as the first character of the
3129 * next keyword. */
3130 ungot_char = read_char;
3131 have_ungot_a_char = true;
3132 break;
3133 }
3134 } else {
3135 /* And if the newline was followed by EOF, then we
3136 * should finish this iteration of the outer
3137 * loop normally, and then not go round again. */
3138 eof = true;
3139 break;
3140 }
3141 } else {
3142 /* Any other character is just added to the buffer. */
3143 buffer_append(buf, read_char);
3144 }
3145 }
3146
3147 if (!it) {
3148 /*
3149 * Tolerate unknown keywords in a preferences file, on the
3150 * assumption that they're from a different (probably
3151 * later) version of the game.
3152 */
3153 continue;
3154 }
3155
3156 switch (it->type) {
3157 case C_BOOLEAN:
3158 if (!strcmp(buf->data, "true"))
3159 it->u.boolean.bval = true;
3160 else if (!strcmp(buf->data, "false"))
3161 it->u.boolean.bval = false;
3162 else {
3163 errmsg = "Value for boolean was not 'true' or 'false'";
3164 goto out;
3165 }
3166 break;
3167 case C_STRING:
3168 sfree(it->u.string.sval);
3169 it->u.string.sval = buf->data;
3170 buf->data = NULL;
3171 buf->len = buf->size = 0;
3172 break;
3173 case C_CHOICES: {
3174 int n = 0;
3175 bool found = false;
3176 const char *p = it->u.choices.choicekws;
3177 char sepstr[2];
3178
3179 sepstr[0] = *p;
3180 sepstr[1] = '\0';
3181
3182 while (*p++) {
3183 int len = strcspn(p, sepstr);
3184 if (buf->len == len && !memcmp(p, buf->data, len)) {
3185 it->u.choices.selected = n;
3186 found = true;
3187 break;
3188 }
3189 p += len;
3190 n++;
3191 }
3192
3193 if (!found) {
3194 errmsg = "Invalid value for enumeration";
3195 goto out;
3196 }
3197
3198 break;
3199 }
3200 }
3201 }
3202
3203 out:
3204
3205 if (!errmsg)
3206 midend_set_prefs(me, ui, cfg);
3207
3208 free_cfg(cfg);
3209 sfree(buf->data);
3210 return errmsg;
3211}
diff --git a/apps/plugins/puzzles/src/mines.R b/apps/plugins/puzzles/src/mines.R
deleted file mode 100644
index 275c76c7ad..0000000000
--- a/apps/plugins/puzzles/src/mines.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3MINES_EXTRA = tree234
4
5mines : [X] GTK COMMON mines MINES_EXTRA mines-icon|no-icon
6
7mines : [G] WINDOWS COMMON mines MINES_EXTRA mines.res|noicon.res
8
9mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
10mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
11
12ALL += mines[COMBINED] MINES_EXTRA
13
14!begin am gtk
15GAMES += mines
16!end
17
18!begin >list.c
19 A(mines) \
20!end
21
22!begin >gamedesc.txt
23mines:mines.exe:Mines:Mine-finding puzzle:Find all the mines without treading on any of them.
24!end
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
index 706777c4f1..e1441fae13 100644
--- a/apps/plugins/puzzles/src/mines.c
+++ b/apps/plugins/puzzles/src/mines.c
@@ -3,8 +3,7 @@
3 * 3 *
4 * Still TODO: 4 * Still TODO:
5 * 5 *
6 * - think about configurably supporting question marks. Once, 6 * - think about configurably supporting question marks.
7 * that is, we've thought about configurability in general!
8 */ 7 */
9 8
10#include <stdio.h> 9#include <stdio.h>
@@ -12,7 +11,12 @@
12#include <string.h> 11#include <string.h>
13#include <assert.h> 12#include <assert.h>
14#include <ctype.h> 13#include <ctype.h>
15#include <math.h> 14#include <limits.h>
15#ifdef NO_TGMATH_H
16# include <math.h>
17#else
18# include <tgmath.h>
19#endif
16 20
17#include "tree234.h" 21#include "tree234.h"
18#include "puzzles.h" 22#include "puzzles.h"
@@ -34,8 +38,8 @@ enum {
34#else 38#else
35#define BORDER (TILE_SIZE * 3 / 2) 39#define BORDER (TILE_SIZE * 3 / 2)
36#endif 40#endif
37#define HIGHLIGHT_WIDTH (TILE_SIZE / 10) 41#define HIGHLIGHT_WIDTH (TILE_SIZE / 10 ? TILE_SIZE / 10 : 1)
38#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10) 42#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10 ? BORDER / 10 : 1)
39#define COORD(x) ( (x) * TILE_SIZE + BORDER ) 43#define COORD(x) ( (x) * TILE_SIZE + BORDER )
40#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) 44#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
41 45
@@ -162,7 +166,9 @@ static void decode_params(game_params *params, char const *string)
162 params->n = atoi(p); 166 params->n = atoi(p);
163 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; 167 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
164 } else { 168 } else {
165 params->n = params->w * params->h / 10; 169 if (params->h > 0 && params->w > 0 &&
170 params->w <= INT_MAX / params->h)
171 params->n = params->w * params->h / 10;
166 } 172 }
167 173
168 while (*p) { 174 while (*p) {
@@ -250,7 +256,7 @@ static const char *validate_params(const game_params *params, bool full)
250 * blocking the way and no idea what's behind them, or one mine 256 * blocking the way and no idea what's behind them, or one mine
251 * and no way to know which of the two rows it's in. If the 257 * and no way to know which of the two rows it's in. If the
252 * mine count is even you can create a soluble grid by packing 258 * mine count is even you can create a soluble grid by packing
253 * all the mines at one end (so what when you hit a two-mine 259 * all the mines at one end (so that when you hit a two-mine
254 * wall there are only as many covered squares left as there 260 * wall there are only as many covered squares left as there
255 * are mines); but if it's odd, you are doomed, because you 261 * are mines); but if it's odd, you are doomed, because you
256 * _have_ to have a gap somewhere which you can't determine the 262 * _have_ to have a gap somewhere which you can't determine the
@@ -258,12 +264,25 @@ static const char *validate_params(const game_params *params, bool full)
258 */ 264 */
259 if (full && params->unique && (params->w <= 2 || params->h <= 2)) 265 if (full && params->unique && (params->w <= 2 || params->h <= 2))
260 return "Width and height must both be greater than two"; 266 return "Width and height must both be greater than two";
267 if (params->w < 1 || params->h < 1)
268 return "Width and height must both be at least one";
269 if (params->w > SHRT_MAX || params->h > SHRT_MAX)
270 return "Neither width nor height may be unreasonably large";
271 /*
272 * We use random_upto() to place mines, and its maximum limit is 2^28-1.
273 */
274#if (1<<28)-1 < INT_MAX
275 if (params->w > ((1<<28)-1) / params->h)
276#else
277 if (params->w > INT_MAX / params->h)
278#endif
279 return "Width times height must not be unreasonably large";
261 if (params->n < 0) 280 if (params->n < 0)
262 return "Mine count may not be negative"; 281 return "Mine count may not be negative";
263 if (params->n > params->w * params->h - 9)
264 return "Too many mines for grid size";
265 if (params->n < 1) 282 if (params->n < 1)
266 return "Number of mines must be greater than zero"; 283 return "Number of mines must be greater than zero";
284 if (params->n > params->w * params->h - 9)
285 return "Too many mines for grid size";
267 286
268 /* 287 /*
269 * FIXME: Need more constraints here. Not sure what the 288 * FIXME: Need more constraints here. Not sure what the
@@ -428,7 +447,9 @@ static void ss_add(struct setstore *ss, int x, int y, int mask, int mines)
428 * Create a set structure and add it to the tree. 447 * Create a set structure and add it to the tree.
429 */ 448 */
430 s = snew(struct set); 449 s = snew(struct set);
450 assert(SHRT_MIN <= x && x <= SHRT_MAX);
431 s->x = x; 451 s->x = x;
452 assert(SHRT_MIN <= y && y <= SHRT_MAX);
432 s->y = y; 453 s->y = y;
433 s->mask = mask; 454 s->mask = mask;
434 s->mines = mines; 455 s->mines = mines;
@@ -499,7 +520,9 @@ static struct set **ss_overlap(struct setstore *ss, int x, int y, int mask)
499 /* 520 /*
500 * Find the first set with these top left coordinates. 521 * Find the first set with these top left coordinates.
501 */ 522 */
523 assert(SHRT_MIN <= xx && xx <= SHRT_MAX);
502 stmp.x = xx; 524 stmp.x = xx;
525 assert(SHRT_MIN <= yy && yy <= SHRT_MAX);
503 stmp.y = yy; 526 stmp.y = yy;
504 stmp.mask = 0; 527 stmp.mask = 0;
505 528
@@ -1880,72 +1903,7 @@ static char *describe_layout(bool *grid, int area, int x, int y,
1880static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique, 1903static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique,
1881 random_state *rs, char **game_desc) 1904 random_state *rs, char **game_desc)
1882{ 1905{
1883 bool *grid; 1906 bool *grid = minegen(w, h, n, x, y, unique, rs);
1884
1885#ifdef TEST_OBFUSCATION
1886 static int tested_obfuscation = false;
1887 if (!tested_obfuscation) {
1888 /*
1889 * A few simple test vectors for the obfuscator.
1890 *
1891 * First test: the 28-bit stream 1234567. This divides up
1892 * into 1234 and 567[0]. The SHA of 56 70 30 (appending
1893 * "0") is 15ce8ab946640340bbb99f3f48fd2c45d1a31d30. Thus,
1894 * we XOR the 16-bit string 15CE into the input 1234 to get
1895 * 07FA. Next, we SHA that with "0": the SHA of 07 FA 30 is
1896 * 3370135c5e3da4fed937adc004a79533962b6391. So we XOR the
1897 * 12-bit string 337 into the input 567 to get 650. Thus
1898 * our output is 07FA650.
1899 */
1900 {
1901 unsigned char bmp1[] = "\x12\x34\x56\x70";
1902 obfuscate_bitmap(bmp1, 28, false);
1903 printf("test 1 encode: %s\n",
1904 memcmp(bmp1, "\x07\xfa\x65\x00", 4) ? "failed" : "passed");
1905 obfuscate_bitmap(bmp1, 28, true);
1906 printf("test 1 decode: %s\n",
1907 memcmp(bmp1, "\x12\x34\x56\x70", 4) ? "failed" : "passed");
1908 }
1909 /*
1910 * Second test: a long string to make sure we switch from
1911 * one SHA to the next correctly. My input string this time
1912 * is simply fifty bytes of zeroes.
1913 */
1914 {
1915 unsigned char bmp2[50];
1916 unsigned char bmp2a[50];
1917 memset(bmp2, 0, 50);
1918 memset(bmp2a, 0, 50);
1919 obfuscate_bitmap(bmp2, 50 * 8, false);
1920 /*
1921 * SHA of twenty-five zero bytes plus "0" is
1922 * b202c07b990c01f6ff2d544707f60e506019b671. SHA of
1923 * twenty-five zero bytes plus "1" is
1924 * fcb1d8b5a2f6b592fe6780b36aa9d65dd7aa6db9. Thus our
1925 * first half becomes
1926 * b202c07b990c01f6ff2d544707f60e506019b671fcb1d8b5a2.
1927 *
1928 * SHA of that lot plus "0" is
1929 * 10b0af913db85d37ca27f52a9f78bba3a80030db. SHA of the
1930 * same string plus "1" is
1931 * 3d01d8df78e76d382b8106f480135a1bc751d725. So the
1932 * second half becomes
1933 * 10b0af913db85d37ca27f52a9f78bba3a80030db3d01d8df78.
1934 */
1935 printf("test 2 encode: %s\n",
1936 memcmp(bmp2, "\xb2\x02\xc0\x7b\x99\x0c\x01\xf6\xff\x2d\x54"
1937 "\x47\x07\xf6\x0e\x50\x60\x19\xb6\x71\xfc\xb1\xd8"
1938 "\xb5\xa2\x10\xb0\xaf\x91\x3d\xb8\x5d\x37\xca\x27"
1939 "\xf5\x2a\x9f\x78\xbb\xa3\xa8\x00\x30\xdb\x3d\x01"
1940 "\xd8\xdf\x78", 50) ? "failed" : "passed");
1941 obfuscate_bitmap(bmp2, 50 * 8, true);
1942 printf("test 2 decode: %s\n",
1943 memcmp(bmp2, bmp2a, 50) ? "failed" : "passed");
1944 }
1945 }
1946#endif
1947
1948 grid = minegen(w, h, n, x, y, unique, rs);
1949 1907
1950 if (game_desc) 1908 if (game_desc)
1951 *game_desc = describe_layout(grid, w * h, x, y, true); 1909 *game_desc = describe_layout(grid, w * h, x, y, true);
@@ -2001,6 +1959,8 @@ static const char *validate_desc(const game_params *params, const char *desc)
2001 desc++; 1959 desc++;
2002 if (!*desc || !isdigit((unsigned char)*desc)) 1960 if (!*desc || !isdigit((unsigned char)*desc))
2003 return "No initial mine count in game description"; 1961 return "No initial mine count in game description";
1962 if (atoi(desc) > wh - 9)
1963 return "Too many mines for grid size";
2004 while (*desc && isdigit((unsigned char)*desc)) 1964 while (*desc && isdigit((unsigned char)*desc))
2005 desc++; /* skip over mine count */ 1965 desc++; /* skip over mine count */
2006 if (*desc != ',') 1966 if (*desc != ',')
@@ -2139,6 +2099,8 @@ static int open_square(game_state *state, int x, int y)
2139 break; 2099 break;
2140 } 2100 }
2141 2101
2102 /* If the player has already lost, don't let them win as well. */
2103 if (state->dead) return 0;
2142 /* 2104 /*
2143 * Finally, scan the grid and see if exactly as many squares 2105 * Finally, scan the grid and see if exactly as many squares
2144 * are still covered as there are mines. If so, set the `won' 2106 * are still covered as there are mines. If so, set the `won'
@@ -2365,7 +2327,7 @@ static game_ui *new_ui(const game_state *state)
2365 ui->completed = false; 2327 ui->completed = false;
2366 ui->flash_is_death = false; /* *shrug* */ 2328 ui->flash_is_death = false; /* *shrug* */
2367 ui->cur_x = ui->cur_y = 0; 2329 ui->cur_x = ui->cur_y = 0;
2368 ui->cur_visible = false; 2330 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2369 return ui; 2331 return ui;
2370} 2332}
2371 2333
@@ -2387,7 +2349,8 @@ static char *encode_ui(const game_ui *ui)
2387 return dupstr(buf); 2349 return dupstr(buf);
2388} 2350}
2389 2351
2390static void decode_ui(game_ui *ui, const char *encoding) 2352static void decode_ui(game_ui *ui, const char *encoding,
2353 const game_state *state)
2391{ 2354{
2392 int p= 0; 2355 int p= 0;
2393 sscanf(encoding, "D%d%n", &ui->deaths, &p); 2356 sscanf(encoding, "D%d%n", &ui->deaths, &p);
@@ -2402,6 +2365,35 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2402 ui->completed = true; 2365 ui->completed = true;
2403} 2366}
2404 2367
2368static const char *current_key_label(const game_ui *ui,
2369 const game_state *state, int button)
2370{
2371 int cx = ui->cur_x, cy = ui->cur_y;
2372 int v = state->grid[cy * state->w + cx];
2373
2374 if (state->dead || state->won || !ui->cur_visible) return "";
2375 if (button == CURSOR_SELECT2) {
2376 if (v == -2) return "Mark";
2377 if (v == -1) return "Unmark";
2378 return "";
2379 }
2380 if (button == CURSOR_SELECT) {
2381 int dy, dx, n = 0;
2382 if (v == -2 || v == -3) return "Uncover";
2383 if (v == 0) return "";
2384 /* Count mine markers. */
2385 for (dy = -1; dy <= +1; dy++)
2386 for (dx = -1; dx <= +1; dx++)
2387 if (cx+dx >= 0 && cx+dx < state->w &&
2388 cy+dy >= 0 && cy+dy < state->h) {
2389 if (state->grid[(cy+dy)*state->w+(cx+dx)] == -1)
2390 n++;
2391 }
2392 if (n == v) return "Clear";
2393 }
2394 return "";
2395}
2396
2405struct game_drawstate { 2397struct game_drawstate {
2406 int w, h, tilesize, bg; 2398 int w, h, tilesize, bg;
2407 bool started; 2399 bool started;
@@ -2432,22 +2424,20 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2432 cx = FROMCOORD(x); 2424 cx = FROMCOORD(x);
2433 cy = FROMCOORD(y); 2425 cy = FROMCOORD(y);
2434 2426
2435 if (IS_CURSOR_MOVE(button)) { 2427 if (IS_CURSOR_MOVE(button))
2436 move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, false); 2428 return move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h,
2437 ui->cur_visible = true; 2429 false, &ui->cur_visible);
2438 return UI_UPDATE;
2439 }
2440 if (IS_CURSOR_SELECT(button)) { 2430 if (IS_CURSOR_SELECT(button)) {
2441 int v = from->grid[ui->cur_y * from->w + ui->cur_x]; 2431 int v = from->grid[ui->cur_y * from->w + ui->cur_x];
2442 2432
2443 if (!ui->cur_visible) { 2433 if (!ui->cur_visible) {
2444 ui->cur_visible = true; 2434 ui->cur_visible = true;
2445 return UI_UPDATE; 2435 return MOVE_UI_UPDATE;
2446 } 2436 }
2447 if (button == CURSOR_SELECT2) { 2437 if (button == CURSOR_SELECT2) {
2448 /* As for RIGHT_BUTTON; only works on covered square. */ 2438 /* As for RIGHT_BUTTON; only works on covered square. */
2449 if (v != -2 && v != -1) 2439 if (v != -2 && v != -1)
2450 return NULL; 2440 return MOVE_NO_EFFECT;
2451 sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y); 2441 sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y);
2452 return dupstr(buf); 2442 return dupstr(buf);
2453 } 2443 }
@@ -2468,7 +2458,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2468 if (button == LEFT_BUTTON || button == LEFT_DRAG || 2458 if (button == LEFT_BUTTON || button == LEFT_DRAG ||
2469 button == MIDDLE_BUTTON || button == MIDDLE_DRAG) { 2459 button == MIDDLE_BUTTON || button == MIDDLE_DRAG) {
2470 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2460 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2471 return NULL; 2461 return MOVE_UNUSED;
2472 2462
2473 /* 2463 /*
2474 * Mouse-downs and mouse-drags just cause highlighting 2464 * Mouse-downs and mouse-drags just cause highlighting
@@ -2482,12 +2472,12 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2482 else if (button == MIDDLE_BUTTON) 2472 else if (button == MIDDLE_BUTTON)
2483 ui->validradius = 1; 2473 ui->validradius = 1;
2484 ui->cur_visible = false; 2474 ui->cur_visible = false;
2485 return UI_UPDATE; 2475 return MOVE_UI_UPDATE;
2486 } 2476 }
2487 2477
2488 if (button == RIGHT_BUTTON) { 2478 if (button == RIGHT_BUTTON) {
2489 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2479 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2490 return NULL; 2480 return MOVE_UNUSED;
2491 2481
2492 /* 2482 /*
2493 * Right-clicking only works on a covered square, and it 2483 * Right-clicking only works on a covered square, and it
@@ -2498,7 +2488,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2498 */ 2488 */
2499 if (from->grid[cy * from->w + cx] != -2 && 2489 if (from->grid[cy * from->w + cx] != -2 &&
2500 from->grid[cy * from->w + cx] != -1) 2490 from->grid[cy * from->w + cx] != -1)
2501 return NULL; 2491 return MOVE_NO_EFFECT;
2502 2492
2503 sprintf(buf, "F%d,%d", cx, cy); 2493 sprintf(buf, "F%d,%d", cx, cy);
2504 return dupstr(buf); 2494 return dupstr(buf);
@@ -2509,11 +2499,12 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2509 ui->hradius = 0; 2499 ui->hradius = 0;
2510 2500
2511 /* 2501 /*
2512 * At this stage we must never return NULL: we have adjusted 2502 * At this stage we must never return MOVE_UNUSED or
2513 * the ui, so at worst we return UI_UPDATE. 2503 * MOVE_NO_EFFECT: we have adjusted the ui, so at worst we
2504 * return MOVE_UI_UPDATE.
2514 */ 2505 */
2515 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2506 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2516 return UI_UPDATE; 2507 return MOVE_UI_UPDATE;
2517 2508
2518 /* 2509 /*
2519 * Left-clicking on a covered square opens a tile. Not 2510 * Left-clicking on a covered square opens a tile. Not
@@ -2533,7 +2524,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2533 } 2524 }
2534 goto uncover; 2525 goto uncover;
2535 } 2526 }
2536 return NULL; 2527 return MOVE_UNUSED;
2537 2528
2538uncover: 2529uncover:
2539 { 2530 {
@@ -2591,7 +2582,7 @@ uncover:
2591 } 2582 }
2592 } 2583 }
2593 2584
2594 return UI_UPDATE; 2585 return MOVE_UI_UPDATE;
2595 } 2586 }
2596} 2587}
2597 2588
@@ -2603,6 +2594,7 @@ static game_state *execute_move(const game_state *from, const char *move)
2603 if (!strcmp(move, "S")) { 2594 if (!strcmp(move, "S")) {
2604 int yy, xx; 2595 int yy, xx;
2605 2596
2597 if (!from->layout->mines) return NULL; /* Game not started. */
2606 ret = dup_game(from); 2598 ret = dup_game(from);
2607 if (!ret->dead) { 2599 if (!ret->dead) {
2608 /* 2600 /*
@@ -2656,12 +2648,17 @@ static game_state *execute_move(const game_state *from, const char *move)
2656 2648
2657 return ret; 2649 return ret;
2658 } else { 2650 } else {
2651 /* Dead players should stop trying to move. */
2652 if (from->dead)
2653 return NULL;
2659 ret = dup_game(from); 2654 ret = dup_game(from);
2660 2655
2661 while (*move) { 2656 while (*move) {
2662 if (move[0] == 'F' && 2657 if (move[0] == 'F' &&
2663 sscanf(move+1, "%d,%d", &cx, &cy) == 2 && 2658 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
2664 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) { 2659 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h &&
2660 (ret->grid[cy * from->w + cx] == -1 ||
2661 ret->grid[cy * from->w + cx] == -2)) {
2665 ret->grid[cy * from->w + cx] ^= (-2 ^ -1); 2662 ret->grid[cy * from->w + cx] ^= (-2 ^ -1);
2666 } else if (move[0] == 'O' && 2663 } else if (move[0] == 'O' &&
2667 sscanf(move+1, "%d,%d", &cx, &cy) == 2 && 2664 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
@@ -2697,7 +2694,7 @@ static game_state *execute_move(const game_state *from, const char *move)
2697 */ 2694 */
2698 2695
2699static void game_compute_size(const game_params *params, int tilesize, 2696static void game_compute_size(const game_params *params, int tilesize,
2700 int *x, int *y) 2697 const game_ui *ui, int *x, int *y)
2701{ 2698{
2702 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2699 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2703 struct { int tilesize; } ads, *ds = &ads; 2700 struct { int tilesize; } ads, *ds = &ads;
@@ -2870,12 +2867,12 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
2870 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \ 2867 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \
2871 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \ 2868 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \
2872} while (0) 2869} while (0)
2873 SETCOORD(0, 0.6F, 0.7F); 2870 SETCOORD(0, 0.6F, 0.35F);
2874 SETCOORD(1, 0.8F, 0.8F); 2871 SETCOORD(1, 0.6F, 0.7F);
2875 SETCOORD(2, 0.25F, 0.8F); 2872 SETCOORD(2, 0.8F, 0.8F);
2876 SETCOORD(3, 0.55F, 0.7F); 2873 SETCOORD(3, 0.25F, 0.8F);
2877 SETCOORD(4, 0.55F, 0.35F); 2874 SETCOORD(4, 0.55F, 0.7F);
2878 SETCOORD(5, 0.6F, 0.35F); 2875 SETCOORD(5, 0.55F, 0.35F);
2879 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE); 2876 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
2880 2877
2881 SETCOORD(0, 0.6F, 0.2F); 2878 SETCOORD(0, 0.6F, 0.2F);
@@ -2980,13 +2977,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2980 if (!ds->started) { 2977 if (!ds->started) {
2981 int coords[10]; 2978 int coords[10];
2982 2979
2983 draw_rect(dr, 0, 0,
2984 TILE_SIZE * state->w + 2 * BORDER,
2985 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
2986 draw_update(dr, 0, 0,
2987 TILE_SIZE * state->w + 2 * BORDER,
2988 TILE_SIZE * state->h + 2 * BORDER);
2989
2990 /* 2980 /*
2991 * Recessed area containing the whole puzzle. 2981 * Recessed area containing the whole puzzle.
2992 */ 2982 */
@@ -3182,14 +3172,6 @@ static bool game_timing_state(const game_state *state, game_ui *ui)
3182 return true; 3172 return true;
3183} 3173}
3184 3174
3185static void game_print_size(const game_params *params, float *x, float *y)
3186{
3187}
3188
3189static void game_print(drawing *dr, const game_state *state, int tilesize)
3190{
3191}
3192
3193#ifdef COMBINED 3175#ifdef COMBINED
3194#define thegame mines 3176#define thegame mines
3195#endif 3177#endif
@@ -3211,12 +3193,14 @@ const struct game thegame = {
3211 free_game, 3193 free_game,
3212 true, solve_game, 3194 true, solve_game,
3213 true, game_can_format_as_text_now, game_text_format, 3195 true, game_can_format_as_text_now, game_text_format,
3196 NULL, NULL, /* get_prefs, set_prefs */
3214 new_ui, 3197 new_ui,
3215 free_ui, 3198 free_ui,
3216 encode_ui, 3199 encode_ui,
3217 decode_ui, 3200 decode_ui,
3218 NULL, /* game_request_keys */ 3201 NULL, /* game_request_keys */
3219 game_changed_state, 3202 game_changed_state,
3203 current_key_label,
3220 interpret_move, 3204 interpret_move,
3221 execute_move, 3205 execute_move,
3222 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3206 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3228,7 +3212,7 @@ const struct game thegame = {
3228 game_flash_length, 3212 game_flash_length,
3229 game_get_cursor_location, 3213 game_get_cursor_location,
3230 game_status, 3214 game_status,
3231 false, false, game_print_size, game_print, 3215 false, false, NULL, NULL, /* print_size, print */
3232 true, /* wants_statusbar */ 3216 true, /* wants_statusbar */
3233 true, game_timing_state, 3217 true, game_timing_state,
3234 BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON, 3218 BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON,
diff --git a/apps/plugins/puzzles/src/misc.c b/apps/plugins/puzzles/src/misc.c
index ed31acbbe4..8e96d67039 100644
--- a/apps/plugins/puzzles/src/misc.c
+++ b/apps/plugins/puzzles/src/misc.c
@@ -3,13 +3,21 @@
3 */ 3 */
4 4
5#include <assert.h> 5#include <assert.h>
6#include <ctype.h>
7#ifdef NO_TGMATH_H
8# include <math.h>
9#else
10# include <tgmath.h>
11#endif
6#include <stdlib.h> 12#include <stdlib.h>
7#include <string.h> 13#include <string.h>
8#include <stdio.h> 14#include <stdio.h>
9 15
10#include "puzzles.h" 16#include "puzzles.h"
11 17
12char UI_UPDATE[] = ""; 18char MOVE_UI_UPDATE[] = "";
19char MOVE_NO_EFFECT[] = "";
20char MOVE_UNUSED[] = "";
13 21
14void free_cfg(config_item *cfg) 22void free_cfg(config_item *cfg)
15{ 23{
@@ -197,30 +205,85 @@ char *fgetline(FILE *fp)
197 return ret; 205 return ret;
198} 206}
199 207
208int getenv_bool(const char *name, int dflt)
209{
210 char *env = getenv(name);
211 if (env == NULL) return dflt;
212 if (strchr("yYtT", env[0])) return true;
213 return false;
214}
215
216/* Utility functions for colour manipulation. */
217
218static float colour_distance(const float a[3], const float b[3])
219{
220 return (float)sqrt((a[0]-b[0]) * (a[0]-b[0]) +
221 (a[1]-b[1]) * (a[1]-b[1]) +
222 (a[2]-b[2]) * (a[2]-b[2]));
223}
224
225void colour_mix(const float src1[3], const float src2[3], float p, float dst[3])
226{
227 int i;
228 for (i = 0; i < 3; i++)
229 dst[i] = src1[i] * (1.0F - p) + src2[i] * p;
230}
231
200void game_mkhighlight_specific(frontend *fe, float *ret, 232void game_mkhighlight_specific(frontend *fe, float *ret,
201 int background, int highlight, int lowlight) 233 int background, int highlight, int lowlight)
202{ 234{
203 float max; 235 static const float black[3] = { 0.0F, 0.0F, 0.0F };
236 static const float white[3] = { 1.0F, 1.0F, 1.0F };
237 float db, dw;
204 int i; 238 int i;
205
206 /* 239 /*
207 * Drop the background colour so that the highlight is 240 * New geometric highlight-generation algorithm: Draw a line from
208 * noticeably brighter than it while still being under 1. 241 * the base colour to white. The point K distance along this line
242 * from the base colour is the highlight colour. Similarly, draw
243 * a line from the base colour to black. The point on this line
244 * at a distance K from the base colour is the shadow. If either
245 * of these colours is imaginary (for reasonable K at most one
246 * will be), _extrapolate_ the base colour along the same line
247 * until it's a distance K from white (or black) and start again
248 * with that as the base colour.
249 *
250 * This preserves the hue of the base colour, ensures that of the
251 * three the base colour is the most saturated, and only ever
252 * flattens the highlight and shadow to pure white or pure black.
253 *
254 * K must be at most sqrt(3)/2, or mid grey would be too close to
255 * both white and black. Here K is set to sqrt(3)/6 so that this
256 * code produces the same results as the former code in the common
257 * case where the background is grey and the highlight saturates
258 * to white.
209 */ 259 */
210 max = ret[background*3]; 260 const float k = sqrt(3)/6.0F;
211 for (i = 1; i < 3; i++) 261 if (lowlight >= 0) {
212 if (ret[background*3+i] > max) 262 db = colour_distance(&ret[background*3], black);
213 max = ret[background*3+i]; 263 if (db < k) {
214 if (max * 1.2F > 1.0F) { 264 for (i = 0; i < 3; i++) ret[lowlight*3+i] = black[i];
215 for (i = 0; i < 3; i++) 265 if (db == 0.0F)
216 ret[background*3+i] /= (max * 1.2F); 266 colour_mix(black, white, k/sqrt(3), &ret[background*3]);
267 else
268 colour_mix(black, &ret[background*3], k/db, &ret[background*3]);
269 } else {
270 colour_mix(&ret[background*3], black, k/db, &ret[lowlight*3]);
271 }
217 } 272 }
218 273 if (highlight >= 0) {
219 for (i = 0; i < 3; i++) { 274 dw = colour_distance(&ret[background*3], white);
220 if (highlight >= 0) 275 if (dw < k) {
221 ret[highlight * 3 + i] = ret[background * 3 + i] * 1.2F; 276 for (i = 0; i < 3; i++) ret[highlight*3+i] = white[i];
222 if (lowlight >= 0) 277 if (dw == 0.0F)
223 ret[lowlight * 3 + i] = ret[background * 3 + i] * 0.8F; 278 colour_mix(white, black, k/sqrt(3), &ret[background*3]);
279 else
280 colour_mix(white, &ret[background*3], k/dw, &ret[background*3]);
281 /* Background has changed; recalculate lowlight. */
282 if (lowlight >= 0)
283 colour_mix(&ret[background*3], black, k/db, &ret[lowlight*3]);
284 } else {
285 colour_mix(&ret[background*3], white, k/dw, &ret[highlight*3]);
286 }
224 } 287 }
225} 288}
226 289
@@ -231,7 +294,7 @@ void game_mkhighlight(frontend *fe, float *ret,
231 game_mkhighlight_specific(fe, ret, background, highlight, lowlight); 294 game_mkhighlight_specific(fe, ret, background, highlight, lowlight);
232} 295}
233 296
234static void swap_regions(void *av, void *bv, int size) 297void swap_regions(void *av, void *bv, size_t size)
235{ 298{
236 char tmpbuf[512]; 299 char tmpbuf[512];
237 char *a = av, *b = bv; 300 char *a = av, *b = bv;
@@ -288,15 +351,27 @@ void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col)
288 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col); 351 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col);
289} 352}
290 353
291void move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap) 354int compare_integers(const void *av, const void *bv) {
355 const int *a = (const int *)av;
356 const int *b = (const int *)bv;
357 if (*a < *b)
358 return -1;
359 else if (*a > *b)
360 return +1;
361 else
362 return 0;
363}
364
365char *move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap,
366 bool *visible)
292{ 367{
293 int dx = 0, dy = 0; 368 int dx = 0, dy = 0, ox = *x, oy = *y;
294 switch (button) { 369 switch (button) {
295 case CURSOR_UP: dy = -1; break; 370 case CURSOR_UP: dy = -1; break;
296 case CURSOR_DOWN: dy = 1; break; 371 case CURSOR_DOWN: dy = 1; break;
297 case CURSOR_RIGHT: dx = 1; break; 372 case CURSOR_RIGHT: dx = 1; break;
298 case CURSOR_LEFT: dx = -1; break; 373 case CURSOR_LEFT: dx = -1; break;
299 default: return; 374 default: return MOVE_UNUSED;
300 } 375 }
301 if (wrap) { 376 if (wrap) {
302 *x = (*x + dx + maxw) % maxw; 377 *x = (*x + dx + maxw) % maxw;
@@ -305,6 +380,13 @@ void move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap)
305 *x = min(max(*x+dx, 0), maxw - 1); 380 *x = min(max(*x+dx, 0), maxw - 1);
306 *y = min(max(*y+dy, 0), maxh - 1); 381 *y = min(max(*y+dy, 0), maxh - 1);
307 } 382 }
383 if (visible != NULL && !*visible) {
384 *visible = true;
385 return MOVE_UI_UPDATE;
386 }
387 if (*x != ox || *y != oy)
388 return MOVE_UI_UPDATE;
389 return MOVE_NO_EFFECT;
308} 390}
309 391
310/* Used in netslide.c and sixteen.c for cursor movement around edge. */ 392/* Used in netslide.c and sixteen.c for cursor movement around edge. */
@@ -402,12 +484,6 @@ void copy_left_justified(char *buf, size_t sz, const char *str)
402 buf[sz - 1] = 0; 484 buf[sz - 1] = 0;
403} 485}
404 486
405/* another kludge for platforms without %g support in *printf() */
406int ftoa(char *buf, float f)
407{
408 return sprintf(buf, "%d.%06d", (int)f, abs((int)((f - (int)f)*1e6)));
409}
410
411/* Returns a dynamically allocated label for a generic button. 487/* Returns a dynamically allocated label for a generic button.
412 * Game-specific buttons should go into the `label' field of key_label 488 * Game-specific buttons should go into the `label' field of key_label
413 * instead. */ 489 * instead. */
@@ -446,4 +522,161 @@ char *button2label(int button)
446 return NULL; 522 return NULL;
447} 523}
448 524
525char *make_prefs_path(const char *dir, const char *sep,
526 const game *game, const char *suffix)
527{
528 size_t dirlen = strlen(dir);
529 size_t seplen = strlen(sep);
530 size_t gamelen = strlen(game->name);
531 size_t suffixlen = strlen(suffix);
532 char *path, *p;
533 const char *q;
534
535 if (!dir || !sep || !game || !suffix)
536 return NULL;
537
538 path = snewn(dirlen + seplen + gamelen + suffixlen + 1, char);
539 p = path;
540
541 memcpy(p, dir, dirlen);
542 p += dirlen;
543
544 memcpy(p, sep, seplen);
545 p += seplen;
546
547 for (q = game->name; *q; q++)
548 if (*q != ' ')
549 *p++ = tolower((unsigned char)*q);
550
551 memcpy(p, suffix, suffixlen);
552 p += suffixlen;
553
554 *p = '\0';
555 return path;
556}
557
558/*
559 * Calculate the nearest integer to n*sqrt(k), via a bitwise algorithm
560 * that avoids floating point.
561 *
562 * (It would probably be OK in practice to use floating point, but I
563 * felt like overengineering it for fun. With FP, there's at least a
564 * theoretical risk of rounding the wrong way, due to the three
565 * successive roundings involved - rounding sqrt(k), rounding its
566 * product with n, and then rounding to the nearest integer. This
567 * approach avoids that: it's exact.)
568 */
569int n_times_root_k(int n_signed, int k)
570{
571 unsigned x, r, m;
572 int sign = n_signed < 0 ? -1 : +1;
573 unsigned n = n_signed * sign;
574 unsigned bitpos;
575
576 /*
577 * Method:
578 *
579 * We transform m gradually from zero into n, by multiplying it by
580 * 2 in each step and optionally adding 1, so that it's always
581 * floor(n/2^something).
582 *
583 * At the start of each step, x is the largest integer less than
584 * or equal to m*sqrt(k). We transform m to 2m+bit, and therefore
585 * we must transform x to 2x+something to match. The 'something'
586 * we add to 2x is at most floor(sqrt(k))+2. (Worst case is if m
587 * sqrt(k) was equal to x + 1-eps for some tiny eps, and then the
588 * incoming bit of m is 1, so that (2m+1)sqrt(k) =
589 * 2x+2+sqrt(k)-2eps.)
590 *
591 * To compute this, we also track the residual value r such that
592 * x^2+r = km^2.
593 *
594 * The algorithm below is very similar to the usual approach for
595 * taking the square root of an integer in binary. The wrinkle is
596 * that we have an integer multiplier, i.e. we're computing
597 * n*sqrt(k) rather than just sqrt(k). Of course in principle we
598 * could just take sqrt(n^2k), but we'd need an integer twice the
599 * width to hold n^2. Pulling out n and treating it specially
600 * makes overflow less likely.
601 */
602
603 x = r = m = 0;
604
605 for (bitpos = UINT_MAX & ~(UINT_MAX >> 1); bitpos; bitpos >>= 1) {
606 unsigned a, b = (n & bitpos) ? 1 : 0;
607
608 /*
609 * Check invariants. We expect that x^2 + r = km^2 (i.e. our
610 * residual term is correct), and also that r < 2x+1 (because
611 * if not, then we could replace x with x+1 and still get a
612 * value that made r non-negative, i.e. x would not be the
613 * _largest_ integer less than m sqrt(k)).
614 */
615 assert(x*x + r == k*m*m);
616 assert(r < 2*x+1);
617
618 /*
619 * We're going to replace m with 2m+b, and x with 2x+a for
620 * some a we haven't decided on yet.
621 *
622 * The new value of the residual will therefore be
623 *
624 * k (2m+b)^2 - (2x+a)^2
625 * = (4km^2 + 4kmb + kb^2) - (4x^2 + 4xa + a^2)
626 * = 4 (km^2 - x^2) + 4kmb + kb^2 - 4xa - a^2
627 * = 4r + 4kmb + kb^2 - 4xa - a^2 (because r = km^2 - x^2)
628 * = 4r + (4m + 1)kb - 4xa - a^2 (b is 0 or 1, so b = b^2)
629 */
630 for (a = 0;; a++) {
631 /* If we made this routine handle square roots of numbers
632 * significantly bigger than 3 or 5 then it would be
633 * sensible to make this a binary search. Here, it hardly
634 * seems important. */
635 unsigned pos = 4*r + k*b*(4*m + 1);
636 unsigned neg = 4*a*x + a*a;
637 if (pos < neg)
638 break; /* this value of a is too big */
639 }
640
641 /* The above loop will have terminated with a one too big. So
642 * now decrementing a will give us the right value to add. */
643 a--;
644
645 r = 4*r + b*k*(4*m + 1) - (4*a*x + a*a);
646 m = 2*m+b;
647 x = 2*x+a;
648 }
649
650 /*
651 * Finally, round to the nearest integer. At present, x is the
652 * largest integer that is _at most_ m sqrt(k). But we want the
653 * _nearest_ integer, whether that's rounded up or down. So check
654 * whether (x + 1/2) is still less than m sqrt(k), i.e. whether
655 * (x + 1/2)^2 < km^2; if it is, then we increment x.
656 *
657 * We have km^2 - (x + 1/2)^2 = km^2 - x^2 - x - 1/4
658 * = r - x - 1/4
659 *
660 * and since r and x are integers, this is greater than 0 if and
661 * only if r > x.
662 *
663 * (There's no need to worry about tie-breaking exact halfway
664 * rounding cases. sqrt(k) is irrational, so none such exist.)
665 */
666 if (r > x)
667 x++;
668
669 /*
670 * Put the sign back on, and convert back from unsigned to int.
671 */
672 if (sign == +1) {
673 return x;
674 } else {
675 /* Be a little careful to avoid compilers deciding I've just
676 * perpetrated signed-integer overflow. This should optimise
677 * down to no actual code. */
678 return INT_MIN + (int)(-x - (unsigned)INT_MIN);
679 }
680}
681
449/* vim: set shiftwidth=4 tabstop=8: */ 682/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/mosaic.c b/apps/plugins/puzzles/src/mosaic.c
new file mode 100644
index 0000000000..05a339ded5
--- /dev/null
+++ b/apps/plugins/puzzles/src/mosaic.c
@@ -0,0 +1,1626 @@
1/*
2 * mosaic.c: A puzzle based on a square grid, with some of the tiles
3 * having clues as to how many black squares are around them.
4 * the purpose of the game is to find what should be on all tiles (black or
5 * unmarked)
6 *
7 * The game is also known as: ArtMosaico, Count and Darken, Cuenta Y Sombrea,
8 * Fill-a-Pix, Fill-In, Komsu Karala, Magipic, Majipiku, Mosaico, Mosaik,
9 * Mozaiek, Nampre Puzzle, Nurie-Puzzle, Oekaki-Pix, Voisimage.
10 *
11 * Implementation is loosely based on https://github.com/mordechaim/Mosaic, UI
12 * interaction is based on the range puzzle in the collection.
13 */
14
15#include <assert.h>
16#include <ctype.h>
17#ifdef NO_TGMATH_H
18# include <math.h>
19#else
20# include <tgmath.h>
21#endif
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "puzzles.h"
27
28#define DEFAULT_SIZE 10
29#define DEFAULT_AGGRESSIVENESS true
30#define MAX_TILES 10000
31#define MAX_TILES_ERROR "Maximum size is 10000 tiles"
32#define DEFAULT_TILE_SIZE 32
33#define DEBUG_IMAGE 1
34#undef DEBUG_IMAGE
35#define FLASH_TIME 0.5F
36/* To enable debug prints define DEBUG_PRINTS */
37
38/* Getting the coordinates and returning NULL when out of scope
39 * The parentheses are needed to avoid order of operations issues
40 */
41#define get_coords(params, array, x, y) \
42 (((x) >= 0 && (y) >= 0) && ((x) < params->width && (y) < params->height)) \
43 ? array + ((y)*params->width) + x \
44 : NULL
45
46#define COORD_FROM_CELL(d) ((d * ds->tilesize) + ds->tilesize / 2) - 1
47
48enum {
49 COL_BACKGROUND = 0,
50 COL_UNMARKED,
51 COL_GRID,
52 COL_MARKED,
53 COL_BLANK,
54 COL_TEXT_SOLVED,
55 COL_ERROR,
56 COL_CURSOR,
57 NCOLOURS,
58 COL_TEXT_DARK = COL_MARKED,
59 COL_TEXT_LIGHT = COL_BLANK
60};
61
62enum cell_state {
63 STATE_UNMARKED = 0,
64 STATE_MARKED = 1,
65 STATE_BLANK = 2,
66 STATE_SOLVED = 4,
67 STATE_ERROR = 8,
68 STATE_UNMARKED_ERROR = STATE_ERROR | STATE_UNMARKED,
69 STATE_MARKED_ERROR = STATE_ERROR | STATE_MARKED,
70 STATE_BLANK_ERROR = STATE_ERROR | STATE_BLANK,
71 STATE_BLANK_SOLVED = STATE_SOLVED | STATE_BLANK,
72 STATE_MARKED_SOLVED = STATE_MARKED | STATE_SOLVED,
73 STATE_OK_NUM = STATE_BLANK | STATE_MARKED
74};
75
76struct game_params {
77 int width;
78 int height;
79 bool aggressive;
80};
81
82typedef struct board_state board_state;
83
84typedef struct needed_list_item needed_list_item;
85
86struct needed_list_item {
87 int x, y;
88 needed_list_item *next;
89};
90
91struct game_state {
92 bool cheating;
93 int not_completed_clues;
94 int width;
95 int height;
96 char *cells_contents;
97 board_state *board;
98};
99
100struct board_state {
101 unsigned int references;
102 struct board_cell *actual_board;
103};
104
105struct board_cell {
106 signed char clue;
107 bool shown;
108};
109
110struct solution_cell {
111 signed char cell;
112 bool solved;
113 bool needed;
114};
115
116struct desc_cell {
117 char clue;
118 bool shown;
119 bool value;
120 bool full;
121 bool empty;
122};
123
124struct game_ui {
125 bool solved;
126 bool in_progress;
127 int last_x, last_y, last_state;
128 int cur_x, cur_y;
129 bool cur_visible;
130};
131
132struct game_drawstate {
133 int tilesize;
134 int *state;
135};
136
137static game_params *default_params(void)
138{
139 game_params *ret = snew(game_params);
140
141 ret->width = DEFAULT_SIZE;
142 ret->height = DEFAULT_SIZE;
143 ret->aggressive = DEFAULT_AGGRESSIVENESS;
144
145 return ret;
146}
147
148static bool game_fetch_preset(int i, char **name, game_params **params)
149{
150 const int sizes[6] = { 3, 5, 10, 15, 25, 50 };
151 const bool aggressiveness[6] = { true, true, true, true, true, false };
152 if (i < 0 || i > 5) {
153 return false;
154 }
155 game_params *res = snew(game_params);
156 res->height = sizes[i];
157 res->width = sizes[i];
158 res->aggressive = aggressiveness[i];
159 *params = res;
160
161 char value[80];
162 sprintf(value, "Size: %dx%d", sizes[i], sizes[i]);
163 *name = dupstr(value);
164 return true;
165}
166
167static void free_params(game_params *params)
168{
169 sfree(params);
170}
171
172static game_params *dup_params(const game_params *params)
173{
174 game_params *ret = snew(game_params);
175 *ret = *params; /* structure copy */
176 return ret;
177}
178
179static void decode_params(game_params *params, char const *string)
180{
181 params->width = params->height = atoi(string);
182 while (*string && isdigit((unsigned char)*string)) string++;
183 if (*string == 'x') {
184 string++;
185 params->height = atoi(string);
186 while (*string && isdigit((unsigned char)*string)) string++;
187 }
188 if (*string == 'h') {
189 string++;
190 params->aggressive = atoi(string);
191 while (*string && isdigit((unsigned char)*string)) string++;
192 }
193}
194
195static char *encode_params(const game_params *params, bool full)
196{
197 char encoded[128];
198 int pos = 0;
199 pos += sprintf(encoded + pos, "%dx%d", params->width, params->height);
200 if (full) {
201 if (params->aggressive != DEFAULT_AGGRESSIVENESS)
202 pos += sprintf(encoded + pos, "h%d", params->aggressive);
203 }
204 return dupstr(encoded);
205}
206
207static config_item *game_configure(const game_params *params)
208{
209 config_item *config = snewn(4, config_item);
210 char value[80];
211
212 config[0].type = C_STRING;
213 config[0].name = "Height";
214 sprintf(value, "%d", params->height);
215 config[0].u.string.sval = dupstr(value);
216
217 config[1].type = C_STRING;
218 config[1].name = "Width";
219 sprintf(value, "%d", params->width);
220 config[1].u.string.sval = dupstr(value);
221
222 config[2].name = "Aggressive generation (longer)";
223 config[2].type = C_BOOLEAN;
224 config[2].u.boolean.bval = params->aggressive;
225
226 config[3].type = C_END;
227
228 return config;
229}
230
231static game_params *custom_params(const config_item *cfg)
232{
233 game_params *res = snew(game_params);
234 res->height = atol(cfg[0].u.string.sval);
235 res->width = atol(cfg[1].u.string.sval);
236 res->aggressive = cfg[2].u.boolean.bval;
237 return res;
238}
239
240static const char *validate_params(const game_params *params, bool full)
241{
242 if (params->height < 3 || params->width < 3) {
243 return "Minimal size is 3x3";
244 }
245 if (params->height > MAX_TILES / params->width) {
246 return MAX_TILES_ERROR;
247 }
248 return NULL;
249}
250
251static bool get_pixel(const game_params *params, const bool *image,
252 const int x, const int y)
253{
254 const bool *pixel;
255 pixel = get_coords(params, image, x, y);
256 if (pixel) {
257 return *pixel;
258 }
259 return 0;
260}
261
262static void populate_cell(const game_params *params, const bool *image,
263 const int x, const int y, bool edge,
264 struct desc_cell *desc)
265{
266 int clue = 0;
267 bool xEdge = false;
268 bool yEdge = false;
269 if (edge) {
270 if (x > 0) {
271 clue += get_pixel(params, image, x - 1, y);
272 if (y > 0) {
273 clue += get_pixel(params, image, x - 1, y - 1);
274 }
275 if (y < params->height - 1) {
276 clue += get_pixel(params, image, x - 1, y + 1);
277 }
278 } else {
279 xEdge = true;
280 }
281
282 if (y > 0) {
283 clue += get_pixel(params, image, x, y - 1);
284 } else {
285 yEdge = true;
286 }
287 if (x < params->width - 1) {
288 clue += get_pixel(params, image, x + 1, y);
289 if (y > 0) {
290 clue += get_pixel(params, image, x + 1, y - 1);
291 }
292 if (y < params->height - 1) {
293 clue += get_pixel(params, image, x + 1, y + 1);
294 }
295 } else {
296 xEdge = true;
297 }
298 if (y < params->height - 1) {
299 clue += get_pixel(params, image, x, y + 1);
300 } else {
301 yEdge = true;
302 }
303 } else {
304 clue += get_pixel(params, image, x - 1, y - 1);
305 clue += get_pixel(params, image, x - 1, y);
306 clue += get_pixel(params, image, x - 1, y + 1);
307 clue += get_pixel(params, image, x, y - 1);
308 clue += get_pixel(params, image, x, y + 1);
309 clue += get_pixel(params, image, x + 1, y - 1);
310 clue += get_pixel(params, image, x + 1, y);
311 clue += get_pixel(params, image, x + 1, y + 1);
312 }
313
314 desc->value = get_pixel(params, image, x, y);
315 clue += desc->value;
316 if (clue == 0) {
317 desc->empty = true;
318 desc->full = false;
319 } else {
320 desc->empty = false;
321 /* setting the default */
322 desc->full = false;
323 if (clue == 9) {
324 desc->full = true;
325 } else if (edge && ((xEdge && yEdge && clue == 4) ||
326 ((xEdge || yEdge) && clue == 6))) {
327
328 desc->full = true;
329 }
330 }
331 desc->shown = true;
332 desc->clue = clue;
333}
334
335static void count_around(const game_params *params,
336 struct solution_cell *sol, int x, int y,
337 int *marked, int *blank, int *total)
338{
339 int i, j;
340 struct solution_cell *curr = NULL;
341 (*total) = 0;
342 (*blank) = 0;
343 (*marked) = 0;
344
345 for (i = -1; i < 2; i++) {
346 for (j = -1; j < 2; j++) {
347 curr = get_coords(params, sol, x + i, y + j);
348 if (curr) {
349 (*total)++;
350 if ((curr->cell & STATE_BLANK) != 0) {
351 (*blank)++;
352 } else if ((curr->cell & STATE_MARKED) != 0) {
353 (*marked)++;
354 }
355 }
356 }
357 }
358}
359
360static void count_around_state(const game_state *state, int x, int y,
361 int *marked, int *blank, int *total)
362{
363 int i, j;
364 char *curr = NULL;
365 (*total) = 0;
366 (*blank) = 0;
367 (*marked) = 0;
368
369 for (i = -1; i < 2; i++) {
370 for (j = -1; j < 2; j++) {
371 curr = get_coords(state, state->cells_contents, x + i, y + j);
372 if (curr) {
373 (*total)++;
374 if ((*curr & STATE_BLANK) != 0) {
375 (*blank)++;
376 } else if ((*curr & STATE_MARKED) != 0) {
377 (*marked)++;
378 }
379 }
380 }
381 }
382}
383
384static void count_clues_around(const game_params *params,
385 struct desc_cell *desc, int x, int y,
386 int *clues, int *total)
387{
388 int i, j;
389 struct desc_cell *curr = NULL;
390 (*total) = 0;
391 (*clues) = 0;
392
393 for (i = -1; i < 2; i++) {
394 for (j = -1; j < 2; j++) {
395 curr = get_coords(params, desc, x + i, y + j);
396 if (curr) {
397 (*total)++;
398 if (curr->shown) {
399 (*clues)++;
400 }
401 }
402 }
403 }
404}
405
406static void mark_around(const game_params *params,
407 struct solution_cell *sol, int x, int y, int mark)
408{
409 int i, j;
410 struct solution_cell *curr;
411
412 for (i = -1; i < 2; i++) {
413 for (j = -1; j < 2; j++) {
414 curr = get_coords(params, sol, x + i, y + j);
415 if (curr) {
416 if (curr->cell == STATE_UNMARKED) {
417 curr->cell = mark;
418 }
419 }
420 }
421 }
422}
423
424static char solve_cell(const game_params *params, struct desc_cell *desc,
425 struct board_cell *board, struct solution_cell *sol,
426 int x, int y)
427{
428 struct desc_cell curr;
429
430 if (desc) {
431 curr.shown = desc[(y * params->width) + x].shown;
432 curr.clue = desc[(y * params->width) + x].clue;
433 curr.full = desc[(y * params->width) + x].full;
434 curr.empty = desc[(y * params->width) + x].empty;
435 } else {
436 curr.shown = board[(y * params->width) + x].shown;
437 curr.clue = board[(y * params->width) + x].clue;
438 curr.full = false;
439 curr.empty = false;
440 }
441 int marked = 0, total = 0, blank = 0;
442
443 if (sol[(y * params->width) + x].solved) {
444 return 0;
445 }
446 count_around(params, sol, x, y, &marked, &blank, &total);
447 if (curr.full && curr.shown) {
448 sol[(y * params->width) + x].solved = true;
449 if (marked + blank < total) {
450 sol[(y * params->width) + x].needed = true;
451 }
452 mark_around(params, sol, x, y, STATE_MARKED);
453 return 1;
454 }
455 if (curr.empty && curr.shown) {
456 sol[(y * params->width) + x].solved = true;
457 if (marked + blank < total) {
458 sol[(y * params->width) + x].needed = true;
459 }
460 mark_around(params, sol, x, y, STATE_BLANK);
461 return 1;
462 }
463 if (curr.shown) {
464 if (!sol[(y * params->width) + x].solved) {
465 if (marked == curr.clue) {
466 sol[(y * params->width) + x].solved = true;
467 if (total != marked + blank) {
468 sol[(y * params->width) + x].needed = true;
469 }
470 mark_around(params, sol, x, y, STATE_BLANK);
471 } else if (curr.clue == (total - blank)) {
472 sol[(y * params->width) + x].solved = true;
473 if (total != marked + blank) {
474 sol[(y * params->width) + x].needed = true;
475 }
476 mark_around(params, sol, x, y, STATE_MARKED);
477 } else if (total == marked + blank) {
478 return -1;
479 } else {
480 return 0;
481 }
482 return 1;
483 }
484 return 0;
485 } else if (total == marked + blank) {
486 sol[(y * params->width) + x].solved = true;
487 return 1;
488 } else {
489 return 0;
490 }
491}
492
493static bool solve_check(const game_params *params, struct desc_cell *desc,
494 random_state *rs, struct solution_cell **sol_return)
495{
496 int x, y, i;
497 int board_size = params->height * params->width;
498 struct solution_cell *sol = snewn(board_size, struct solution_cell),
499 *curr_sol;
500 bool made_progress = true, error = false;
501 int solved = 0, curr = 0, shown = 0;
502 needed_list_item *head = NULL, *curr_needed, **needed_array;
503 struct desc_cell *curr_desc;
504
505 memset(sol, 0, board_size * sizeof(*sol));
506 for (y = 0; y < params->height; y++) {
507 for (x = 0; x < params->width; x++) {
508 curr_desc = get_coords(params, desc, x, y);
509 if (curr_desc->shown) {
510 curr_needed = snew(needed_list_item);
511 curr_needed->next = head;
512 head = curr_needed;
513 curr_needed->x = x;
514 curr_needed->y = y;
515 shown++;
516 }
517 }
518 }
519 needed_array = snewn(shown, needed_list_item *);
520 curr_needed = head;
521 i = 0;
522 while (curr_needed) {
523 needed_array[i] = curr_needed;
524 curr_needed = curr_needed->next;
525 i++;
526 }
527 if (rs) {
528 shuffle(needed_array, shown, sizeof(*needed_array), rs);
529 }
530 solved = 0;
531 while (solved < shown && made_progress && !error) {
532 made_progress = false;
533 for (i = 0; i < shown; i++) {
534 curr = solve_cell(params, desc, NULL, sol, needed_array[i]->x,
535 needed_array[i]->y);
536 if (curr < 0) {
537 error = true;
538#ifdef DEBUG_PRINTS
539 printf("error in cell x=%d, y=%d\n", needed_array[i]->x,
540 needed_array[i]->y);
541#endif
542 break;
543 }
544 if (curr > 0) {
545 solved++;
546 made_progress = true;
547 }
548 }
549 }
550 while (head) {
551 curr_needed = head;
552 head = curr_needed->next;
553 sfree(curr_needed);
554 }
555 sfree(needed_array);
556 solved = 0;
557 /* verifying all the board is solved */
558 if (made_progress) {
559 for (y = 0; y < params->height; y++) {
560 for (x = 0; x < params->width; x++) {
561 curr_sol = get_coords(params, sol, x, y);
562 if ((curr_sol->cell & (STATE_MARKED | STATE_BLANK)) > 0) {
563 solved++;
564 }
565 }
566 }
567 }
568 if (sol_return) {
569 *sol_return = sol;
570 } else {
571 sfree(sol);
572 }
573 return solved == board_size;
574}
575
576static bool solve_game_actual(const game_params *params,
577 struct board_cell *desc,
578 struct solution_cell **sol_return)
579{
580 int x, y;
581 int board_size = params->height * params->width;
582 struct solution_cell *sol = snewn(board_size, struct solution_cell);
583 bool made_progress = true, error = false;
584 int solved = 0, curr = 0;
585
586 memset(sol, 0, params->height * params->width * sizeof(*sol));
587 solved = 0;
588 while (solved < params->height * params->width && made_progress
589 && !error) {
590 for (y = 0; y < params->height; y++) {
591 for (x = 0; x < params->width; x++) {
592 curr = solve_cell(params, NULL, desc, sol, x, y);
593 if (curr < 0) {
594 error = true;
595#ifdef DEBUG_PRINTS
596 printf("error in cell x=%d, y=%d\n", x, y);
597#endif
598 break;
599 }
600 if (curr > 0) {
601 made_progress = true;
602 }
603 solved += curr;
604 }
605 }
606 }
607 if (sol_return) {
608 *sol_return = sol;
609 } else {
610 sfree(sol);
611 }
612 return solved == params->height * params->width;
613}
614
615static void hide_clues(const game_params *params, struct desc_cell *desc,
616 random_state *rs)
617{
618 int shown, total, x, y, i;
619 int needed = 0;
620 struct desc_cell *curr;
621 struct solution_cell *sol = NULL, *curr_sol = NULL;
622 needed_list_item *head = NULL, *curr_needed, **needed_array;
623
624#ifdef DEBUG_PRINTS
625 printf("Hiding clues\n");
626#endif
627 solve_check(params, desc, rs, &sol);
628 for (y = 0; y < params->height; y++) {
629 for (x = 0; x < params->width; x++) {
630 count_clues_around(params, desc, x, y, &shown, &total);
631 curr = get_coords(params, desc, x, y);
632 curr_sol = get_coords(params, sol, x, y);
633 if (curr_sol->needed && params->aggressive) {
634 curr_needed = snew(needed_list_item);
635 curr_needed->x = x;
636 curr_needed->y = y;
637 curr_needed->next = head;
638 head = curr_needed;
639 needed++;
640 } else if (!curr_sol->needed) {
641 curr->shown = false;
642 }
643 }
644 }
645 if (params->aggressive) {
646 curr_needed = head;
647 needed_array = snewn(needed, needed_list_item *);
648 memset(needed_array, 0, needed * sizeof(*needed_array));
649 i = 0;
650 while (curr_needed) {
651 needed_array[i] = curr_needed;
652 curr_needed = curr_needed->next;
653 i++;
654 }
655 shuffle(needed_array, needed, sizeof(*needed_array), rs);
656 for (i = 0; i < needed; i++) {
657 curr_needed = needed_array[i];
658 curr =
659 get_coords(params, desc, curr_needed->x, curr_needed->y);
660 if (curr) {
661 curr->shown = false;
662 if (!solve_check(params, desc, NULL, NULL)) {
663#ifdef DEBUG_PRINTS
664 printf("Hiding cell %d, %d not possible.\n",
665 curr_needed->x, curr_needed->y);
666#endif
667 curr->shown = true;
668 }
669 sfree(curr_needed);
670 needed_array[i] = NULL;
671 }
672 curr_needed = NULL;
673 }
674 sfree(needed_array);
675 }
676#ifdef DEBUG_PRINTS
677 printf("needed %d\n", needed);
678#endif
679 sfree(sol);
680}
681
682static bool start_point_check(size_t size, struct desc_cell *desc)
683{
684 int i;
685 for (i = 0; i < size; i++) {
686 if (desc[i].empty || desc[i].full) {
687 return true;
688 }
689 }
690 return false;
691}
692
693static void game_get_cursor_location(const game_ui *ui,
694 const game_drawstate *ds,
695 const game_state *state,
696 const game_params *params, int *x,
697 int *y, int *w, int *h)
698{
699 if (ui->cur_visible) {
700 *x = COORD_FROM_CELL(ui->cur_x);
701 *y = COORD_FROM_CELL(ui->cur_y);
702 *w = *h = ds->tilesize;
703 }
704}
705
706static void generate_image(const game_params *params, random_state *rs,
707 bool *image)
708{
709 int x, y;
710 for (y = 0; y < params->height; y++) {
711 for (x = 0; x < params->width; x++) {
712 image[(y * params->width) + x] = random_bits(rs, 1);
713 }
714 }
715}
716
717static char *new_game_desc(const game_params *params, random_state *rs,
718 char **aux, bool interactive)
719{
720 bool *image = snewn(params->height * params->width, bool);
721 bool valid = false;
722 char *desc_string = snewn((params->height * params->width) + 1, char);
723 char *compressed_desc =
724 snewn((params->height * params->width) + 1, char);
725 char space_count;
726
727 struct desc_cell *desc =
728 snewn(params->height * params->width, struct desc_cell);
729 int x, y, location_in_str;
730
731 while (!valid) {
732 generate_image(params, rs, image);
733#ifdef DEBUG_IMAGE
734 image[0] = 1;
735 image[1] = 1;
736 image[2] = 0;
737 image[3] = 1;
738 image[4] = 1;
739 image[5] = 0;
740 image[6] = 0;
741 image[7] = 0;
742 image[8] = 0;
743#endif
744
745 for (y = 0; y < params->height; y++) {
746 for (x = 0; x < params->width; x++) {
747 populate_cell(params, image, x, y,
748 x * y == 0 || y == params->height - 1 ||
749 x == params->width - 1,
750 &desc[(y * params->width) + x]);
751 }
752 }
753 valid =
754 start_point_check((params->height - 1) * (params->width - 1),
755 desc);
756 if (!valid) {
757#ifdef DEBUG_PRINTS
758 printf("Not valid, regenerating.\n");
759#endif
760 } else {
761 valid = solve_check(params, desc, rs, NULL);
762 if (!valid) {
763#ifdef DEBUG_PRINTS
764 printf("Couldn't solve, regenerating.");
765#endif
766 } else {
767 hide_clues(params, desc, rs);
768 }
769 }
770 }
771 location_in_str = 0;
772 for (y = 0; y < params->height; y++) {
773 for (x = 0; x < params->width; x++) {
774 if (desc[(y * params->width) + x].shown) {
775#ifdef DEBUG_PRINTS
776 printf("%d(%d)", desc[(y * params->width) + x].value,
777 desc[(y * params->width) + x].clue);
778#endif
779 sprintf(desc_string + location_in_str, "%d",
780 desc[(y * params->width) + x].clue);
781 } else {
782#ifdef DEBUG_PRINTS
783 printf("%d( )", desc[(y * params->width) + x].value);
784#endif
785 sprintf(desc_string + location_in_str, " ");
786 }
787 location_in_str += 1;
788 }
789#ifdef DEBUG_PRINTS
790 printf("\n");
791#endif
792 }
793 location_in_str = 0;
794 space_count = 'a' - 1;
795 for (y = 0; y < params->height; y++) {
796 for (x = 0; x < params->width; x++) {
797 if (desc[(y * params->width) + x].shown) {
798 if (space_count >= 'a') {
799 sprintf(compressed_desc + location_in_str, "%c",
800 space_count);
801 location_in_str++;
802 space_count = 'a' - 1;
803 }
804 sprintf(compressed_desc + location_in_str, "%d",
805 desc[(y * params->width) + x].clue);
806 location_in_str++;
807 } else {
808 if (space_count <= 'z') {
809 space_count++;
810 } else {
811 sprintf(compressed_desc + location_in_str, "%c",
812 space_count);
813 location_in_str++;
814 space_count = 'a' - 1;
815 }
816 }
817 }
818 }
819 if (space_count >= 'a') {
820 sprintf(compressed_desc + location_in_str, "%c", space_count);
821 location_in_str++;
822 }
823 compressed_desc[location_in_str] = '\0';
824#ifdef DEBUG_PRINTS
825 printf("compressed_desc: %s\n", compressed_desc);
826#endif
827 return compressed_desc;
828}
829
830static const char *validate_desc(const game_params *params,
831 const char *desc)
832{
833 int size_dest = params->height * params->width;
834 int length;
835 length = 0;
836
837 while (*desc != '\0') {
838 if (*desc >= 'a' && *desc <= 'z') {
839 length += *desc - 'a';
840 } else if (*desc < '0' || *desc > '9')
841 return "Invalid character in game description";
842 length++;
843 desc++;
844 }
845
846 if (length != size_dest) {
847 return "Desc size mismatch";
848 }
849 return NULL;
850}
851
852static game_state *new_game(midend *me, const game_params *params,
853 const char *desc)
854{
855 game_state *state = snew(game_state);
856 char *curr_desc = dupstr(desc);
857 char *desc_base = curr_desc;
858 int dest_loc;
859 int spaces, total_spaces;
860
861 state->cheating = false;
862 state->not_completed_clues = 0;
863 dest_loc = 0;
864 state->height = params->height;
865 state->width = params->width;
866 state->cells_contents = snewn(params->height * params->width, char);
867 memset(state->cells_contents, 0, params->height * params->width);
868 state->board = snew(board_state);
869 state->board->references = 1;
870 state->board->actual_board =
871 snewn(params->height * params->width, struct board_cell);
872
873 while (*curr_desc != '\0') {
874 if (*curr_desc >= '0' && *curr_desc <= '9') {
875 state->board->actual_board[dest_loc].shown = true;
876 state->not_completed_clues++;
877 state->board->actual_board[dest_loc].clue = *curr_desc - '0';
878 } else {
879 if (*curr_desc != ' ') {
880 total_spaces = *curr_desc - 'a' + 1;
881 } else {
882 total_spaces = 1;
883 }
884 spaces = 0;
885 while (spaces < total_spaces) {
886 state->board->actual_board[dest_loc].shown = false;
887 state->board->actual_board[dest_loc].clue = -1;
888 spaces++;
889 if (spaces < total_spaces) {
890 dest_loc++;
891 }
892 }
893 }
894 curr_desc++;
895 dest_loc++;
896 }
897
898 sfree(desc_base);
899 return state;
900}
901
902static game_state *dup_game(const game_state *state)
903{
904 game_state *ret = snew(game_state);
905
906 ret->cheating = state->cheating;
907 ret->not_completed_clues = state->not_completed_clues;
908 ret->width = state->width;
909 ret->height = state->height;
910 ret->cells_contents = snewn(state->height * state->width, char);
911 memcpy(ret->cells_contents, state->cells_contents,
912 state->height * state->width);
913 ret->board = state->board;
914 ret->board->references++;
915
916 return ret;
917}
918
919static void free_game(game_state *state)
920{
921 sfree(state->cells_contents);
922 state->cells_contents = NULL;
923 if (state->board->references <= 1) {
924 sfree(state->board->actual_board);
925 sfree(state->board);
926 state->board = NULL;
927 } else {
928 state->board->references--;
929 }
930 sfree(state);
931}
932
933static char *solve_game(const game_state *state,
934 const game_state *currstate, const char *aux,
935 const char **error)
936{
937 struct solution_cell *sol = NULL;
938 game_params param;
939 bool solved;
940 char *ret = NULL;
941 unsigned int curr_ret;
942 int i, bits, ret_loc = 1;
943 int size = state->width * state->height;
944
945 param.width = state->width;
946 param.height = state->height;
947 solved = solve_game_actual(&param, state->board->actual_board, &sol);
948 if (!solved) {
949 *error = dupstr("Could not solve this board");
950 sfree(sol);
951 return NULL;
952 }
953
954 ret = snewn((size / 4) + 3, char);
955
956 ret[0] = 's';
957 i = 0;
958 while (i < size) {
959 curr_ret = 0;
960 bits = 0;
961 while (bits < 8 && i < size) {
962 curr_ret <<= 1;
963 curr_ret |= sol[i].cell == STATE_MARKED;
964 i++;
965 bits++;
966 }
967 curr_ret <<= 8 - bits;
968 sprintf(ret + ret_loc, "%02x", curr_ret);
969 ret_loc += 2;
970 }
971
972 sfree(sol);
973 return ret;
974}
975
976static bool game_can_format_as_text_now(const game_params *params)
977{
978 return true;
979}
980
981static char *game_text_format(const game_state *state)
982{
983 char *desc_string =
984 snewn((state->height * state->width) * 3 + 1, char);
985 int location_in_str = 0, x, y;
986 for (y = 0; y < state->height; y++) {
987 for (x = 0; x < state->width; x++) {
988 if (state->board->actual_board[(y * state->width) + x].shown) {
989 sprintf(desc_string + location_in_str, "|%d|",
990 state->board->actual_board[(y * state->width) +
991 x].clue);
992 } else {
993 sprintf(desc_string + location_in_str, "| |");
994 }
995 location_in_str += 3;
996 }
997 sprintf(desc_string + location_in_str, "\n");
998 location_in_str += 1;
999 }
1000 return desc_string;
1001}
1002
1003static game_ui *new_ui(const game_state *state)
1004{
1005 game_ui *ui = snew(game_ui);
1006 ui->last_x = -1;
1007 ui->last_y = -1;
1008 ui->last_state = 0;
1009 ui->solved = false;
1010 ui->cur_x = ui->cur_y = 0;
1011 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1012 return ui;
1013}
1014
1015static void free_ui(game_ui *ui)
1016{
1017 sfree(ui);
1018}
1019
1020static void game_changed_state(game_ui *ui, const game_state *oldstate,
1021 const game_state *newstate)
1022{
1023}
1024
1025static const char *current_key_label(const game_ui *ui,
1026 const game_state *state, int button)
1027{
1028 char *cell;
1029
1030 if (IS_CURSOR_SELECT(button)) {
1031 if (!ui->cur_visible || state->not_completed_clues == 0) return "";
1032 cell = get_coords(state, state->cells_contents, ui->cur_x, ui->cur_y);
1033 switch (*cell & STATE_OK_NUM) {
1034 case STATE_UNMARKED:
1035 return button == CURSOR_SELECT ? "Black" : "White";
1036 case STATE_MARKED:
1037 return button == CURSOR_SELECT ? "White" : "Empty";
1038 case STATE_BLANK:
1039 return button == CURSOR_SELECT ? "Empty" : "Black";
1040 }
1041 }
1042 return "";
1043}
1044
1045static char *interpret_move(const game_state *state, game_ui *ui,
1046 const game_drawstate *ds, int x, int y,
1047 int button)
1048{
1049 int srcX = ui->last_x, srcY = ui->last_y;
1050 int offsetX, offsetY, gameX, gameY, i;
1051 int dirX, dirY, diff;
1052 char move_type;
1053 char move_desc[80];
1054 char *ret = NULL;
1055 const char *cell_state;
1056 bool changed = false;
1057 if (state->not_completed_clues == 0 && !IS_CURSOR_MOVE(button)) {
1058 return NULL;
1059 }
1060 offsetX = x - (ds->tilesize / 2);
1061 offsetY = y - (ds->tilesize / 2);
1062 gameX = offsetX / ds->tilesize;
1063 gameY = offsetY / ds->tilesize;
1064 if ((IS_MOUSE_DOWN(button) || IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button))
1065 && ((offsetX < 0) || (offsetY < 0)))
1066 return NULL;
1067 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1068 cell_state =
1069 get_coords(state, state->cells_contents, gameX, gameY);
1070 if (cell_state) {
1071 ui->last_state = *cell_state & (STATE_BLANK | STATE_MARKED);
1072 ui->last_state =
1073 (ui->last_state +
1074 ((button ==
1075 RIGHT_BUTTON) ? 2 : 1)) % (STATE_BLANK | STATE_MARKED);
1076 }
1077 if (button == RIGHT_BUTTON) {
1078 /* Right button toggles twice */
1079 move_type = 'T';
1080 } else {
1081 move_type = 't';
1082 }
1083 if (gameX >= 0 && gameY >= 0 && gameX < state->width &&
1084 gameY < state->height) {
1085 sprintf(move_desc, "%c%d,%d", move_type, gameX, gameY);
1086 ui->last_x = gameX;
1087 ui->last_y = gameY;
1088 ret = dupstr(move_desc);
1089 } else {
1090 ui->last_x = -1;
1091 ui->last_y = -1;
1092 }
1093 changed = true;
1094 ui->cur_visible = false;
1095 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
1096 move_type = 'd';
1097 /* allowing only drags in straight lines */
1098 if (gameX >= 0 && gameY >= 0 && gameX < state->width &&
1099 gameY < state->height && ui->last_x >= 0 && ui->last_y >= 0 &&
1100 (gameY == ui->last_y || gameX == ui->last_x)) {
1101 sprintf(move_desc, "%c%d,%d,%d,%d,%d", move_type, gameX, gameY,
1102 ui->last_x, ui->last_y, ui->last_state);
1103 if (srcX == gameX && srcY != gameY) {
1104 dirX = 0;
1105 diff = srcY - gameY;
1106 if (diff < 0) {
1107 dirY = -1;
1108 diff *= -1;
1109 } else {
1110 dirY = 1;
1111 }
1112 } else {
1113 diff = srcX - gameX;
1114 dirY = 0;
1115 if (diff < 0) {
1116 dirX = -1;
1117 diff *= -1;
1118 } else {
1119 dirX = 1;
1120 }
1121 }
1122 for (i = 0; i < diff; i++) {
1123 cell_state = get_coords(state, state->cells_contents,
1124 gameX + (dirX * i),
1125 gameY + (dirY * i));
1126 if (cell_state && (*cell_state & STATE_OK_NUM) == 0
1127 && ui->last_state > 0) {
1128 changed = true;
1129 break;
1130 }
1131 }
1132 ui->last_x = gameX;
1133 ui->last_y = gameY;
1134 if (changed) {
1135 ret = dupstr(move_desc);
1136 }
1137 } else {
1138 ui->last_x = -1;
1139 ui->last_y = -1;
1140 }
1141 ui->cur_visible = false;
1142 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
1143 move_type = 'e';
1144 if (gameX >= 0 && gameY >= 0 && gameX < state->width &&
1145 gameY < state->height && ui->last_x >= 0 && ui->last_y >= 0 &&
1146 (gameY == ui->last_y || gameX == ui->last_x)) {
1147 sprintf(move_desc, "%c%d,%d,%d,%d,%d", move_type, gameX, gameY,
1148 ui->last_x, ui->last_y, ui->last_state);
1149 if (srcX == gameX && srcY != gameY) {
1150 dirX = 0;
1151 diff = srcY - gameY;
1152 if (diff < 0) {
1153 dirY = -1;
1154 diff *= -1;
1155 } else {
1156 dirY = 1;
1157 }
1158 } else {
1159 diff = srcX - gameX;
1160 dirY = 0;
1161 if (diff < 0) {
1162 dirX = -1;
1163 diff *= -1;
1164 } else {
1165 dirX = 1;
1166 }
1167 }
1168 for (i = 0; i < diff; i++) {
1169 cell_state = get_coords(state, state->cells_contents,
1170 gameX + (dirX * i),
1171 gameY + (dirY * i));
1172 if (cell_state && (*cell_state & STATE_OK_NUM) == 0
1173 && ui->last_state > 0) {
1174 changed = true;
1175 break;
1176 }
1177 }
1178 if (changed) {
1179 ret = dupstr(move_desc);
1180 }
1181 } else {
1182 ui->last_x = -1;
1183 ui->last_y = -1;
1184 }
1185 ui->cur_visible = false;
1186 } else if (IS_CURSOR_MOVE(button)) {
1187 return move_cursor(button, &ui->cur_x, &ui->cur_y, state->width,
1188 state->height, false, &ui->cur_visible);
1189 } else if (IS_CURSOR_SELECT(button)) {
1190 if (!ui->cur_visible) {
1191 ui->cur_x = 0;
1192 ui->cur_y = 0;
1193 ui->cur_visible = true;
1194 return MOVE_UI_UPDATE;
1195 }
1196
1197 if (button == CURSOR_SELECT2) {
1198 sprintf(move_desc, "T%d,%d", ui->cur_x, ui->cur_y);
1199 ret = dupstr(move_desc);
1200 } else {
1201 /* Otherwise, treat as LEFT_BUTTON, for a single square. */
1202 sprintf(move_desc, "t%d,%d", ui->cur_x, ui->cur_y);
1203 ret = dupstr(move_desc);
1204 }
1205 }
1206 return ret;
1207}
1208
1209static void update_board_state_around(game_state *state, int x, int y)
1210{
1211 int i, j;
1212 struct board_cell *curr;
1213 char *curr_state;
1214 int total;
1215 int blank;
1216 int marked;
1217
1218 for (i = -1; i < 2; i++) {
1219 for (j = -1; j < 2; j++) {
1220 curr =
1221 get_coords(state, state->board->actual_board, x + i,
1222 y + j);
1223 if (curr && curr->shown) {
1224 curr_state =
1225 get_coords(state, state->cells_contents, x + i, y + j);
1226 count_around_state(state, x + i, y + j, &marked, &blank,
1227 &total);
1228 if (curr->clue == marked && (total - marked - blank) == 0) {
1229 *curr_state &= STATE_MARKED | STATE_BLANK;
1230 *curr_state |= STATE_SOLVED;
1231 } else if (curr->clue < marked
1232 || curr->clue > (total - blank)) {
1233 *curr_state &= STATE_MARKED | STATE_BLANK;
1234 *curr_state |= STATE_ERROR;
1235 } else {
1236 *curr_state &= STATE_MARKED | STATE_BLANK;
1237 }
1238 }
1239 }
1240 }
1241}
1242
1243static game_state *execute_move(const game_state *state, const char *move)
1244{
1245 game_state *new_state = dup_game(state);
1246 int i = 0, x = -1, y = -1, clues_left = 0;
1247 int srcX = -1, srcY = -1, size = state->height * state->width;
1248 const char *p;
1249 char *cell, sol_char;
1250 int steps = 1, bits, sol_location, dirX, dirY, diff,
1251 last_state = STATE_UNMARKED;
1252 unsigned int sol_value;
1253 struct board_cell *curr_cell;
1254 char move_type;
1255 int nparams = 0, move_params[5];
1256
1257 p = move;
1258 move_type = *p++;
1259 switch (move_type) {
1260 case 't':
1261 case 'T':
1262 nparams = 2;
1263 break;
1264 case 'd':
1265 case 'e':
1266 nparams = 5;
1267 break;
1268 }
1269
1270 for (i = 0; i < nparams; i++) {
1271 move_params[i] = atoi(p);
1272 while (*p && isdigit((unsigned char)*p)) p++;
1273 if (i+1 < nparams) {
1274 if (*p != ',') {
1275 free_game(new_state);
1276 return NULL;
1277 }
1278 p++;
1279 }
1280 }
1281
1282 if (move_type == 't' || move_type == 'T') {
1283 if (move_type == 'T') {
1284 steps++;
1285 }
1286 x = move_params[0];
1287 y = move_params[1];
1288 if (x == -1 || y == -1) {
1289 return new_state;
1290 }
1291 cell = get_coords(new_state, new_state->cells_contents, x, y);
1292 if (cell == NULL) {
1293 free_game(new_state);
1294 return NULL;
1295 }
1296 if (*cell >= STATE_OK_NUM) {
1297 *cell &= STATE_OK_NUM;
1298 }
1299 *cell = (*cell + steps) % STATE_OK_NUM;
1300 update_board_state_around(new_state, x, y);
1301 } else if (move_type == 's') {
1302 new_state->not_completed_clues = 0;
1303 new_state->cheating = true;
1304 sol_location = 0;
1305 bits = 0;
1306 i = 1;
1307 while (i < strlen(move)) {
1308 sol_value = 0;
1309 while (bits < 8) {
1310 sol_value <<= 4;
1311 sol_char = move[i];
1312 if (sol_char >= '0' && sol_char <= '9') {
1313 sol_value |= sol_char - '0';
1314 } else {
1315 sol_value |= (sol_char - 'a') + 10;
1316 }
1317 bits += 4;
1318 i++;
1319 }
1320 while (bits > 0 && sol_location < size) {
1321 if (sol_value & 0x80) {
1322 new_state->cells_contents[sol_location] =
1323 STATE_MARKED_SOLVED;
1324 } else {
1325 new_state->cells_contents[sol_location] =
1326 STATE_BLANK_SOLVED;
1327 }
1328 sol_value <<= 1;
1329 bits--;
1330 sol_location++;
1331 }
1332 }
1333 return new_state;
1334 } else if (move_type == 'd' || move_type == 'e') {
1335 x = move_params[0];
1336 y = move_params[1];
1337 srcX = move_params[2];
1338 srcY = move_params[3];
1339 last_state = move_params[4];
1340 if (srcX == x && srcY != y) {
1341 dirX = 0;
1342 diff = srcY - y;
1343 if (diff < 0) {
1344 dirY = -1;
1345 diff *= -1;
1346 } else {
1347 dirY = 1;
1348 }
1349 } else {
1350 diff = srcX - x;
1351 dirY = 0;
1352 if (diff < 0) {
1353 dirX = -1;
1354 diff *= -1;
1355 } else {
1356 dirX = 1;
1357 }
1358 }
1359 for (i = 0; i < diff; i++) {
1360 cell = get_coords(new_state, new_state->cells_contents,
1361 x + (dirX * i), y + (dirY * i));
1362 if (cell == NULL) {
1363 free_game(new_state);
1364 return NULL;
1365 }
1366 if ((*cell & STATE_OK_NUM) == 0) {
1367 *cell = last_state;
1368 update_board_state_around(new_state, x + (dirX * i),
1369 y + (dirY * i));
1370 }
1371 }
1372 }
1373 for (y = 0; y < state->height; y++) {
1374 for (x = 0; x < state->width; x++) {
1375 cell = get_coords(new_state, new_state->cells_contents, x, y);
1376 curr_cell = get_coords(new_state, new_state->board->actual_board,
1377 x, y);
1378 if (curr_cell->shown && ((*cell & STATE_SOLVED) == 0)) {
1379 clues_left++;
1380 }
1381 }
1382 }
1383 new_state->not_completed_clues = clues_left;
1384 return new_state;
1385}
1386
1387/* ----------------------------------------------------------------------
1388 * Drawing routines.
1389 */
1390
1391static void game_compute_size(const game_params *params, int tilesize,
1392 const game_ui *ui, int *x, int *y)
1393{
1394 *x = (params->width + 1) * tilesize;
1395 *y = (params->height + 1) * tilesize;
1396}
1397
1398static void game_set_size(drawing *dr, game_drawstate *ds,
1399 const game_params *params, int tilesize)
1400{
1401 ds->tilesize = tilesize;
1402}
1403
1404#define COLOUR(ret, i, r, g, b) \
1405 ((ret[3 * (i) + 0] = (r)), (ret[3 * (i) + 1] = (g)), (ret[3 * (i) + 2] = (b)))
1406
1407static float *game_colours(frontend *fe, int *ncolours)
1408{
1409 float *ret = snewn(3 * NCOLOURS, float);
1410
1411 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1412 COLOUR(ret, COL_GRID, 0.0F, 102 / 255.0F, 99 / 255.0F);
1413 COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F);
1414 COLOUR(ret, COL_BLANK, 236 / 255.0F, 236 / 255.0F, 236 / 255.0F);
1415 COLOUR(ret, COL_MARKED, 20 / 255.0F, 20 / 255.0F, 20 / 255.0F);
1416 COLOUR(ret, COL_UNMARKED, 148 / 255.0F, 196 / 255.0F, 190 / 255.0F);
1417 COLOUR(ret, COL_TEXT_SOLVED, 100 / 255.0F, 100 / 255.0F, 100 / 255.0F);
1418 COLOUR(ret, COL_CURSOR, 255 / 255.0F, 200 / 255.0F, 200 / 255.0F);
1419
1420 *ncolours = NCOLOURS;
1421 return ret;
1422}
1423
1424/* Extra flags in game_drawstate entries, not in main game state */
1425#define DRAWFLAG_CURSOR 0x100
1426#define DRAWFLAG_CURSOR_U 0x200
1427#define DRAWFLAG_CURSOR_L 0x400
1428#define DRAWFLAG_CURSOR_UL 0x800
1429#define DRAWFLAG_MARGIN_R 0x1000
1430#define DRAWFLAG_MARGIN_D 0x2000
1431
1432static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1433{
1434 struct game_drawstate *ds = snew(game_drawstate);
1435 int i;
1436
1437 ds->tilesize = 0;
1438 ds->state = NULL;
1439 ds->state = snewn((state->width + 1) * (state->height + 1), int);
1440 for (i = 0; i < (state->width + 1) * (state->height + 1); i++)
1441 ds->state[i] = -1;
1442
1443 return ds;
1444}
1445
1446static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1447{
1448 sfree(ds->state);
1449 sfree(ds);
1450}
1451
1452static void draw_cell(drawing *dr, int cell, int ts, signed char clue_val,
1453 int x, int y)
1454{
1455 int startX = ((x * ts) + ts / 2) - 1, startY = ((y * ts) + ts / 2) - 1;
1456 int color, text_color = COL_TEXT_DARK;
1457
1458 clip(dr, startX - 1, startY - 1, ts, ts);
1459 if (!(cell & DRAWFLAG_MARGIN_R))
1460 draw_rect(dr, startX - 1, startY - 1, ts, 1,
1461 (cell & (DRAWFLAG_CURSOR | DRAWFLAG_CURSOR_U) ?
1462 COL_CURSOR : COL_GRID));
1463 if (!(cell & DRAWFLAG_MARGIN_D))
1464 draw_rect(dr, startX - 1, startY - 1, 1, ts,
1465 (cell & (DRAWFLAG_CURSOR | DRAWFLAG_CURSOR_L) ?
1466 COL_CURSOR : COL_GRID));
1467 if (cell & DRAWFLAG_CURSOR_UL)
1468 draw_rect(dr, startX - 1, startY - 1, 1, 1, COL_CURSOR);
1469
1470 if (!(cell & (DRAWFLAG_MARGIN_R | DRAWFLAG_MARGIN_D))) {
1471 if (cell & STATE_MARKED) {
1472 color = COL_MARKED;
1473 text_color = COL_TEXT_LIGHT;
1474 } else if (cell & STATE_BLANK) {
1475 text_color = COL_TEXT_DARK;
1476 color = COL_BLANK;
1477 } else {
1478 text_color = COL_TEXT_DARK;
1479 color = COL_UNMARKED;
1480 }
1481 if (cell & STATE_ERROR) {
1482 text_color = COL_ERROR;
1483 } else if (cell & STATE_SOLVED) {
1484 text_color = COL_TEXT_SOLVED;
1485 }
1486
1487 draw_rect(dr, startX, startY, ts - 1, ts - 1, color);
1488 if (clue_val >= 0) {
1489 char clue[80];
1490 sprintf(clue, "%d", clue_val);
1491 draw_text(dr, startX + ts / 2, startY + ts / 2, 1, ts * 3 / 5,
1492 ALIGN_VCENTRE | ALIGN_HCENTRE, text_color, clue);
1493 }
1494 }
1495
1496 unclip(dr);
1497 draw_update(dr, startX - 1, startY - 1, ts, ts);
1498}
1499
1500static void game_redraw(drawing *dr, game_drawstate *ds,
1501 const game_state *oldstate,
1502 const game_state *state, int dir,
1503 const game_ui *ui, float animtime,
1504 float flashtime)
1505{
1506 int x, y;
1507 char status[80];
1508 signed char clue_val;
1509 bool flashing = (flashtime > 0 && (flashtime <= FLASH_TIME / 3 ||
1510 flashtime > 2*FLASH_TIME / 3));
1511
1512 for (y = 0; y <= state->height; y++) {
1513 for (x = 0; x <= state->width; x++) {
1514 bool inbounds = x < state->width && y < state->height;
1515 int cell = (inbounds ?
1516 state->cells_contents[(y * state->width) + x] : 0);
1517 if (x == state->width)
1518 cell |= DRAWFLAG_MARGIN_R;
1519 if (y == state->height)
1520 cell |= DRAWFLAG_MARGIN_D;
1521 if (flashing)
1522 cell ^= (STATE_BLANK | STATE_MARKED);
1523 if (ui->cur_visible) {
1524 if (ui->cur_x == x && ui->cur_y == y)
1525 cell |= DRAWFLAG_CURSOR;
1526 if (ui->cur_x == x-1 && ui->cur_y == y)
1527 cell |= DRAWFLAG_CURSOR_L;
1528 if (ui->cur_x == x && ui->cur_y == y-1)
1529 cell |= DRAWFLAG_CURSOR_U;
1530 if (ui->cur_x == x-1 && ui->cur_y == y-1)
1531 cell |= DRAWFLAG_CURSOR_UL;
1532 }
1533
1534 if (inbounds &&
1535 state->board->actual_board[(y * state->width) + x].shown) {
1536 clue_val = state->board->actual_board[
1537 (y * state->width) + x].clue;
1538 } else {
1539 clue_val = -1;
1540 }
1541
1542 if (ds->state[(y * (state->width+1)) + x] != cell) {
1543 draw_cell(dr, cell, ds->tilesize, clue_val, x, y);
1544 ds->state[(y * (state->width+1)) + x] = cell;
1545 }
1546 }
1547 }
1548 sprintf(status, "Clues left: %d", state->not_completed_clues);
1549 if (state->not_completed_clues == 0 && !state->cheating) {
1550 sprintf(status, "COMPLETED!");
1551 } else if (state->not_completed_clues == 0 && state->cheating) {
1552 sprintf(status, "Auto solved");
1553 }
1554 status_bar(dr, status);
1555}
1556
1557static float game_anim_length(const game_state *oldstate,
1558 const game_state *newstate, int dir,
1559 game_ui *ui)
1560{
1561 return 0.0F;
1562}
1563
1564static float game_flash_length(const game_state *oldstate,
1565 const game_state *newstate, int dir,
1566 game_ui *ui)
1567{
1568 if (!oldstate->cheating && oldstate->not_completed_clues > 0 &&
1569 newstate->not_completed_clues == 0) {
1570 return FLASH_TIME;
1571 }
1572 return 0.0F;
1573}
1574
1575static int game_status(const game_state *state)
1576{
1577 if (state->not_completed_clues == 0)
1578 return +1;
1579 return 0;
1580}
1581
1582#ifdef COMBINED
1583#define thegame mosaic
1584#endif
1585
1586const struct game thegame = {
1587 "Mosaic", "games.mosaic", "mosaic",
1588 default_params,
1589 game_fetch_preset, NULL,
1590 decode_params,
1591 encode_params,
1592 free_params,
1593 dup_params,
1594 true, game_configure, custom_params,
1595 validate_params,
1596 new_game_desc,
1597 validate_desc,
1598 new_game,
1599 dup_game,
1600 free_game,
1601 true, solve_game,
1602 true, game_can_format_as_text_now, game_text_format,
1603 NULL, NULL, /* get_prefs, set_prefs */
1604 new_ui,
1605 free_ui,
1606 NULL, /* encode_ui */
1607 NULL, /* decode_ui */
1608 NULL, /* game_request_keys */
1609 game_changed_state,
1610 current_key_label,
1611 interpret_move,
1612 execute_move,
1613 DEFAULT_TILE_SIZE, game_compute_size, game_set_size,
1614 game_colours,
1615 game_new_drawstate,
1616 game_free_drawstate,
1617 game_redraw,
1618 game_anim_length,
1619 game_flash_length,
1620 game_get_cursor_location,
1621 game_status,
1622 false, false, NULL, NULL, /* print_size, print */
1623 true, /* wants_statusbar */
1624 false, NULL, /* timing_state */
1625 0, /* flags */
1626};
diff --git a/apps/plugins/puzzles/src/nestedvm.c b/apps/plugins/puzzles/src/nestedvm.c
deleted file mode 100644
index 947abe0fae..0000000000
--- a/apps/plugins/puzzles/src/nestedvm.c
+++ /dev/null
@@ -1,486 +0,0 @@
1/*
2 * nestedvm.c: NestedVM front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <stdlib.h>
8#include <time.h>
9#include <stdarg.h>
10#include <string.h>
11#include <errno.h>
12
13#include <sys/time.h>
14
15#include "puzzles.h"
16
17extern void _pause();
18extern int _call_java(int cmd, int arg1, int arg2, int arg3);
19
20void fatal(const char *fmt, ...)
21{
22 va_list ap;
23 fprintf(stderr, "fatal error: ");
24 va_start(ap, fmt);
25 vfprintf(stderr, fmt, ap);
26 va_end(ap);
27 fprintf(stderr, "\n");
28 exit(1);
29}
30
31struct frontend {
32 // TODO kill unneeded members!
33 midend *me;
34 bool timer_active;
35 struct timeval last_time;
36 config_item *cfg;
37 int cfg_which;
38 bool cfgret;
39 int ox, oy, w, h;
40};
41
42static frontend *_fe;
43
44void get_random_seed(void **randseed, int *randseedsize)
45{
46 struct timeval *tvp = snew(struct timeval);
47 gettimeofday(tvp, NULL);
48 *randseed = (void *)tvp;
49 *randseedsize = sizeof(struct timeval);
50}
51
52void frontend_default_colour(frontend *fe, float *output)
53{
54 output[0] = output[1]= output[2] = 0.8f;
55}
56
57void nestedvm_status_bar(void *handle, const char *text)
58{
59 _call_java(4,0,(int)text,0);
60}
61
62void nestedvm_start_draw(void *handle)
63{
64 frontend *fe = (frontend *)handle;
65 _call_java(5, 0, fe->w, fe->h);
66 _call_java(4, 1, fe->ox, fe->oy);
67}
68
69void nestedvm_clip(void *handle, int x, int y, int w, int h)
70{
71 frontend *fe = (frontend *)handle;
72 _call_java(5, w, h, 0);
73 _call_java(4, 3, x + fe->ox, y + fe->oy);
74}
75
76void nestedvm_unclip(void *handle)
77{
78 frontend *fe = (frontend *)handle;
79 _call_java(4, 4, fe->ox, fe->oy);
80}
81
82void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
83 int align, int colour, const char *text)
84{
85 frontend *fe = (frontend *)handle;
86 _call_java(5, x + fe->ox, y + fe->oy,
87 (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
88 _call_java(7, fontsize, colour, (int)text);
89}
90
91void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour)
92{
93 frontend *fe = (frontend *)handle;
94 _call_java(5, w, h, colour);
95 _call_java(4, 5, x + fe->ox, y + fe->oy);
96}
97
98void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2,
99 int colour)
100{
101 frontend *fe = (frontend *)handle;
102 _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
103 _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
104}
105
106void nestedvm_draw_poly(void *handle, int *coords, int npoints,
107 int fillcolour, int outlinecolour)
108{
109 frontend *fe = (frontend *)handle;
110 int i;
111 _call_java(4, 7, npoints, 0);
112 for (i = 0; i < npoints; i++) {
113 _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy);
114 }
115 _call_java(4, 8, outlinecolour, fillcolour);
116}
117
118void nestedvm_draw_circle(void *handle, int cx, int cy, int radius,
119 int fillcolour, int outlinecolour)
120{
121 frontend *fe = (frontend *)handle;
122 _call_java(5, cx+fe->ox, cy+fe->oy, radius);
123 _call_java(4, 9, outlinecolour, fillcolour);
124}
125
126struct blitter {
127 int handle, w, h, x, y;
128};
129
130blitter *nestedvm_blitter_new(void *handle, int w, int h)
131{
132 blitter *bl = snew(blitter);
133 bl->handle = -1;
134 bl->w = w;
135 bl->h = h;
136 return bl;
137}
138
139void nestedvm_blitter_free(void *handle, blitter *bl)
140{
141 if (bl->handle != -1)
142 _call_java(4, 11, bl->handle, 0);
143 sfree(bl);
144}
145
146void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
147{
148 frontend *fe = (frontend *)handle;
149 if (bl->handle == -1)
150 bl->handle = _call_java(4,10,bl->w, bl->h);
151 bl->x = x;
152 bl->y = y;
153 _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
154}
155
156void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
157{
158 frontend *fe = (frontend *)handle;
159 assert(bl->handle != -1);
160 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
161 x = bl->x;
162 y = bl->y;
163 }
164 _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
165}
166
167void nestedvm_end_draw(void *handle)
168{
169 _call_java(4,2,0,0);
170}
171
172char *nestedvm_text_fallback(void *handle, const char *const *strings,
173 int nstrings)
174{
175 /*
176 * We assume Java can cope with any UTF-8 likely to be emitted
177 * by a puzzle.
178 */
179 return dupstr(strings[0]);
180}
181
182const struct drawing_api nestedvm_drawing = {
183 nestedvm_draw_text,
184 nestedvm_draw_rect,
185 nestedvm_draw_line,
186 nestedvm_draw_poly,
187 nestedvm_draw_circle,
188 NULL, // draw_update,
189 nestedvm_clip,
190 nestedvm_unclip,
191 nestedvm_start_draw,
192 nestedvm_end_draw,
193 nestedvm_status_bar,
194 nestedvm_blitter_new,
195 nestedvm_blitter_free,
196 nestedvm_blitter_save,
197 nestedvm_blitter_load,
198 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
199 NULL, NULL, /* line_width, line_dotted */
200 nestedvm_text_fallback,
201};
202
203int jcallback_key_event(int x, int y, int keyval)
204{
205 frontend *fe = (frontend *)_fe;
206 if (fe->ox == -1)
207 return 1;
208 if (keyval >= 0 &&
209 !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval))
210 return 42;
211 return 1;
212}
213
214int jcallback_resize(int width, int height)
215{
216 frontend *fe = (frontend *)_fe;
217 int x, y;
218 x = width;
219 y = height;
220 midend_size(fe->me, &x, &y, true);
221 fe->ox = (width - x) / 2;
222 fe->oy = (height - y) / 2;
223 fe->w = x;
224 fe->h = y;
225 midend_force_redraw(fe->me);
226 return 0;
227}
228
229int jcallback_timer_func()
230{
231 frontend *fe = (frontend *)_fe;
232 if (fe->timer_active) {
233 struct timeval now;
234 float elapsed;
235 gettimeofday(&now, NULL);
236 elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
237 (now.tv_sec - fe->last_time.tv_sec));
238 midend_timer(fe->me, elapsed); /* may clear timer_active */
239 fe->last_time = now;
240 }
241 return fe->timer_active;
242}
243
244void deactivate_timer(frontend *fe)
245{
246 if (fe->timer_active)
247 _call_java(4, 13, 0, 0);
248 fe->timer_active = false;
249}
250
251void activate_timer(frontend *fe)
252{
253 if (!fe->timer_active) {
254 _call_java(4, 12, 0, 0);
255 gettimeofday(&fe->last_time, NULL);
256 }
257 fe->timer_active = true;
258}
259
260void jcallback_config_ok()
261{
262 frontend *fe = (frontend *)_fe;
263 const char *err;
264
265 err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
266
267 if (err)
268 _call_java(2, (int) "Error", (int)err, 1);
269 else {
270 fe->cfgret = true;
271 }
272}
273
274void jcallback_config_set_string(int item_ptr, int char_ptr) {
275 config_item *i = (config_item *)item_ptr;
276 char* newval = (char*) char_ptr;
277 assert(i->type == C_STRING);
278 sfree(i->u.string.sval);
279 i->u.string.sval = dupstr(newval);
280 free(newval);
281}
282
283void jcallback_config_set_boolean(int item_ptr, int selected) {
284 config_item *i = (config_item *)item_ptr;
285 assert(i->type == C_BOOLEAN);
286 i->u.boolean.bval = selected != 0 ? true : false;
287}
288
289void jcallback_config_set_choice(int item_ptr, int selected) {
290 config_item *i = (config_item *)item_ptr;
291 assert(i->type == C_CHOICES);
292 i->u.choices.selected = selected;
293}
294
295static bool get_config(frontend *fe, int which)
296{
297 char *title;
298 config_item *i;
299 fe->cfg = midend_get_config(fe->me, which, &title);
300 fe->cfg_which = which;
301 fe->cfgret = false;
302 _call_java(10, (int)title, 0, 0);
303 for (i = fe->cfg; i->type != C_END; i++) {
304 _call_java(5, (int)i, i->type, (int)i->name);
305 switch (i->type) {
306 case C_STRING:
307 _call_java(11, (int)i->u.string.sval, 0, 0);
308 break;
309 case C_BOOLEAN:
310 _call_java(11, 0, i->u.boolean.bval, 0);
311 break;
312 case C_CHOICES:
313 _call_java(11, (int)i->u.choices.choicenames,
314 i->u.choices.selected, 0);
315 break;
316 }
317 }
318 _call_java(12,0,0,0);
319 free_cfg(fe->cfg);
320 return fe->cfgret;
321}
322
323int jcallback_newgame_event(void)
324{
325 frontend *fe = (frontend *)_fe;
326 if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME))
327 return 42;
328 return 0;
329}
330
331int jcallback_undo_event(void)
332{
333 frontend *fe = (frontend *)_fe;
334 if (!midend_process_key(fe->me, 0, 0, UI_UNDO))
335 return 42;
336 return 0;
337}
338
339int jcallback_redo_event(void)
340{
341 frontend *fe = (frontend *)_fe;
342 if (!midend_process_key(fe->me, 0, 0, UI_REDO))
343 return 42;
344 return 0;
345}
346
347int jcallback_quit_event(void)
348{
349 frontend *fe = (frontend *)_fe;
350 if (!midend_process_key(fe->me, 0, 0, UI_QUIT))
351 return 42;
352 return 0;
353}
354
355static void resize_fe(frontend *fe)
356{
357 int x, y;
358
359 x = INT_MAX;
360 y = INT_MAX;
361 midend_size(fe->me, &x, &y, false);
362 _call_java(3, x, y, 0);
363}
364
365int jcallback_preset_event(int ptr_game_params)
366{
367 frontend *fe = (frontend *)_fe;
368 game_params *params =
369 (game_params *)ptr_game_params;
370
371 midend_set_params(fe->me, params);
372 midend_new_game(fe->me);
373 resize_fe(fe);
374 _call_java(13, midend_which_preset(fe->me), 0, 0);
375 return 0;
376}
377
378int jcallback_solve_event()
379{
380 frontend *fe = (frontend *)_fe;
381 const char *msg;
382
383 msg = midend_solve(fe->me);
384
385 if (msg)
386 _call_java(2, (int) "Error", (int)msg, 1);
387 return 0;
388}
389
390int jcallback_restart_event()
391{
392 frontend *fe = (frontend *)_fe;
393
394 midend_restart_game(fe->me);
395 return 0;
396}
397
398int jcallback_config_event(int which)
399{
400 frontend *fe = (frontend *)_fe;
401 _call_java(13, midend_which_preset(fe->me), 0, 0);
402 if (!get_config(fe, which))
403 return 0;
404 midend_new_game(fe->me);
405 resize_fe(fe);
406 _call_java(13, midend_which_preset(fe->me), 0, 0);
407 return 0;
408}
409
410int jcallback_about_event()
411{
412 char titlebuf[256];
413 char textbuf[1024];
414
415 sprintf(titlebuf, "About %.200s", thegame.name);
416 sprintf(textbuf,
417 "%.200s\n\n"
418 "from Simon Tatham's Portable Puzzle Collection\n\n"
419 "%.500s", thegame.name, ver);
420 _call_java(2, (int)&titlebuf, (int)&textbuf, 0);
421 return 0;
422}
423
424void preset_menu_populate(struct preset_menu *menu, int menuid)
425{
426 int i;
427
428 for (i = 0; i < menu->n_entries; i++) {
429 struct preset_menu_entry *entry = &menu->entries[i];
430 if (entry->params) {
431 _call_java(5, (int)entry->params, 0, 0);
432 _call_java(1, (int)entry->title, menuid, entry->id);
433 } else {
434 _call_java(5, 0, 0, 0);
435 _call_java(1, (int)entry->title, menuid, entry->id);
436 preset_menu_populate(entry->submenu, entry->id);
437 }
438 }
439}
440
441int main(int argc, char **argv)
442{
443 int i, n;
444 float* colours;
445
446 _fe = snew(frontend);
447 _fe->timer_active = false;
448 _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
449 if (argc > 1)
450 midend_game_id(_fe->me, argv[1]); /* ignore failure */
451 midend_new_game(_fe->me);
452
453 {
454 struct preset_menu *menu;
455 int nids, topmenu;
456 menu = midend_get_presets(_fe->me, &nids);
457 topmenu = _call_java(1, 0, nids, 0);
458 preset_menu_populate(menu, topmenu);
459 }
460
461 colours = midend_colours(_fe->me, &n);
462 _fe->ox = -1;
463
464 _call_java(0, (int)thegame.name,
465 (thegame.can_configure ? 1 : 0) |
466 (midend_wants_statusbar(_fe->me) ? 2 : 0) |
467 (thegame.can_solve ? 4 : 0), n);
468 for (i = 0; i < n; i++) {
469 _call_java(1024+ i,
470 (int)(colours[i*3] * 0xFF),
471 (int)(colours[i*3+1] * 0xFF),
472 (int)(colours[i*3+2] * 0xFF));
473 }
474 resize_fe(_fe);
475
476 _call_java(13, midend_which_preset(_fe->me), 0, 0);
477
478 // Now pause the vm. The VM will be call()ed when
479 // an input event occurs.
480 _pause();
481
482 // shut down when the VM is resumed.
483 deactivate_timer(_fe);
484 midend_free(_fe->me);
485 return 0;
486}
diff --git a/apps/plugins/puzzles/src/net.R b/apps/plugins/puzzles/src/net.R
deleted file mode 100644
index 8e98216e03..0000000000
--- a/apps/plugins/puzzles/src/net.R
+++ /dev/null
@@ -1,23 +0,0 @@
1# -*- makefile -*-
2
3NET_EXTRA = tree234 dsf findloop
4
5net : [X] GTK COMMON net NET_EXTRA net-icon|no-icon
6
7# The Windows Net shouldn't be called `net.exe' since Windows
8# already has a reasonably important utility program by that name!
9netgame : [G] WINDOWS COMMON net NET_EXTRA net.res|noicon.res
10
11ALL += net[COMBINED] NET_EXTRA
12
13!begin am gtk
14GAMES += net
15!end
16
17!begin >list.c
18 A(net) \
19!end
20
21!begin >gamedesc.txt
22net:netgame.exe:Net:Network jigsaw puzzle:Rotate each tile to reassemble the network.
23!end
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c
index d3032b6fe2..3200253ef9 100644
--- a/apps/plugins/puzzles/src/net.c
+++ b/apps/plugins/puzzles/src/net.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13#include "tree234.h" 18#include "tree234.h"
@@ -249,10 +254,7 @@ static char *encode_params(const game_params *params, bool full)
249 if (params->wrapping) 254 if (params->wrapping)
250 ret[len++] = 'w'; 255 ret[len++] = 'w';
251 if (full && params->barrier_probability) 256 if (full && params->barrier_probability)
252 { 257 len += sprintf(ret+len, "b%g", params->barrier_probability);
253 len += sprintf(ret+len, "b");
254 len += ftoa(ret + len, params->barrier_probability);
255 }
256 if (full && !params->unique) 258 if (full && !params->unique)
257 ret[len++] = 'a'; 259 ret[len++] = 'a';
258 assert(len < lenof(ret)); 260 assert(len < lenof(ret));
@@ -284,7 +286,7 @@ static config_item *game_configure(const game_params *params)
284 286
285 ret[3].name = "Barrier probability"; 287 ret[3].name = "Barrier probability";
286 ret[3].type = C_STRING; 288 ret[3].type = C_STRING;
287 ftoa(buf, params->barrier_probability); 289 sprintf(buf, "%g", params->barrier_probability);
288 ret[3].u.string.sval = dupstr(buf); 290 ret[3].u.string.sval = dupstr(buf);
289 291
290 ret[4].name = "Ensure unique solution"; 292 ret[4].name = "Ensure unique solution";
@@ -316,6 +318,8 @@ static const char *validate_params(const game_params *params, bool full)
316 return "Width and height must both be greater than zero"; 318 return "Width and height must both be greater than zero";
317 if (params->width <= 1 && params->height <= 1) 319 if (params->width <= 1 && params->height <= 1)
318 return "At least one of width and height must be greater than one"; 320 return "At least one of width and height must be greater than one";
321 if (params->width > INT_MAX / params->height)
322 return "Width times height must not be unreasonably large";
319 if (params->barrier_probability < 0) 323 if (params->barrier_probability < 0)
320 return "Barrier probability may not be negative"; 324 return "Barrier probability may not be negative";
321 if (params->barrier_probability > 1) 325 if (params->barrier_probability > 1)
@@ -458,7 +462,7 @@ static int net_solver(int w, int h, unsigned char *tiles,
458 unsigned char *tilestate; 462 unsigned char *tilestate;
459 unsigned char *edgestate; 463 unsigned char *edgestate;
460 int *deadends; 464 int *deadends;
461 int *equivalence; 465 DSF *equivalence;
462 struct todo *todo; 466 struct todo *todo;
463 int i, j, x, y; 467 int i, j, x, y;
464 int area; 468 int area;
@@ -543,7 +547,7 @@ static int net_solver(int w, int h, unsigned char *tiles,
543 * classes) by finding the representative of each tile and 547 * classes) by finding the representative of each tile and
544 * setting equivalence[one]=the_other. 548 * setting equivalence[one]=the_other.
545 */ 549 */
546 equivalence = snew_dsf(w * h); 550 equivalence = dsf_new(w * h);
547 551
548 /* 552 /*
549 * On a non-wrapping grid, we instantly know that all the edges 553 * On a non-wrapping grid, we instantly know that all the edges
@@ -828,7 +832,7 @@ static int net_solver(int w, int h, unsigned char *tiles,
828 sfree(tilestate); 832 sfree(tilestate);
829 sfree(edgestate); 833 sfree(edgestate);
830 sfree(deadends); 834 sfree(deadends);
831 sfree(equivalence); 835 dsf_free(equivalence);
832 836
833 return j; 837 return j;
834} 838}
@@ -1131,7 +1135,8 @@ static void perturb(int w, int h, unsigned char *tiles, bool wrapping,
1131 1135
1132static int *compute_loops_inner(int w, int h, bool wrapping, 1136static int *compute_loops_inner(int w, int h, bool wrapping,
1133 const unsigned char *tiles, 1137 const unsigned char *tiles,
1134 const unsigned char *barriers); 1138 const unsigned char *barriers,
1139 bool include_unlocked_squares);
1135 1140
1136static char *new_game_desc(const game_params *params, random_state *rs, 1141static char *new_game_desc(const game_params *params, random_state *rs,
1137 char **aux, bool interactive) 1142 char **aux, bool interactive)
@@ -1460,7 +1465,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1460 */ 1465 */
1461 prev_loopsquares = w*h+1; 1466 prev_loopsquares = w*h+1;
1462 while (1) { 1467 while (1) {
1463 loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL); 1468 loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL,
1469 true);
1464 this_loopsquares = 0; 1470 this_loopsquares = 0;
1465 for (i = 0; i < w*h; i++) { 1471 for (i = 0; i < w*h; i++) {
1466 if (loops[i]) { 1472 if (loops[i]) {
@@ -1848,16 +1854,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1848 return ret; 1854 return ret;
1849} 1855}
1850 1856
1851static bool game_can_format_as_text_now(const game_params *params)
1852{
1853 return true;
1854}
1855
1856static char *game_text_format(const game_state *state)
1857{
1858 return NULL;
1859}
1860
1861/* ---------------------------------------------------------------------- 1857/* ----------------------------------------------------------------------
1862 * Utility routine. 1858 * Utility routine.
1863 */ 1859 */
@@ -1878,6 +1874,8 @@ static unsigned char *compute_active(const game_state *state, int cx, int cy)
1878 active = snewn(state->width * state->height, unsigned char); 1874 active = snewn(state->width * state->height, unsigned char);
1879 memset(active, 0, state->width * state->height); 1875 memset(active, 0, state->width * state->height);
1880 1876
1877 assert(0 <= cx && cx < state->width);
1878 assert(0 <= cy && cy < state->height);
1881 /* 1879 /*
1882 * We only store (x,y) pairs in todo, but it's easier to reuse 1880 * We only store (x,y) pairs in todo, but it's easier to reuse
1883 * xyd_cmp and just store direction 0 every time. 1881 * xyd_cmp and just store direction 0 every time.
@@ -1923,6 +1921,7 @@ struct net_neighbour_ctx {
1923 int w, h; 1921 int w, h;
1924 const unsigned char *tiles, *barriers; 1922 const unsigned char *tiles, *barriers;
1925 int i, n, neighbours[4]; 1923 int i, n, neighbours[4];
1924 bool include_unlocked_squares;
1926}; 1925};
1927static int net_neighbour(int vertex, void *vctx) 1926static int net_neighbour(int vertex, void *vctx)
1928{ 1927{
@@ -1943,6 +1942,9 @@ static int net_neighbour(int vertex, void *vctx)
1943 continue; 1942 continue;
1944 OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h); 1943 OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h);
1945 v1 = y1 * ctx->w + x1; 1944 v1 = y1 * ctx->w + x1;
1945 if (!ctx->include_unlocked_squares &&
1946 !(tile & ctx->tiles[v1] & LOCKED))
1947 continue;
1946 if (ctx->tiles[v1] & F(dir)) 1948 if (ctx->tiles[v1] & F(dir))
1947 ctx->neighbours[ctx->n++] = v1; 1949 ctx->neighbours[ctx->n++] = v1;
1948 } 1950 }
@@ -1956,32 +1958,39 @@ static int net_neighbour(int vertex, void *vctx)
1956 1958
1957static int *compute_loops_inner(int w, int h, bool wrapping, 1959static int *compute_loops_inner(int w, int h, bool wrapping,
1958 const unsigned char *tiles, 1960 const unsigned char *tiles,
1959 const unsigned char *barriers) 1961 const unsigned char *barriers,
1962 bool include_unlocked_squares)
1960{ 1963{
1961 struct net_neighbour_ctx ctx; 1964 struct net_neighbour_ctx ctx;
1962 struct findloopstate *fls; 1965 struct findloopstate *fls;
1963 int *loops; 1966 int *loops;
1964 int x, y; 1967 int x, y, v;
1965 1968
1966 fls = findloop_new_state(w*h); 1969 fls = findloop_new_state(w*h);
1967 ctx.w = w; 1970 ctx.w = w;
1968 ctx.h = h; 1971 ctx.h = h;
1969 ctx.tiles = tiles; 1972 ctx.tiles = tiles;
1970 ctx.barriers = barriers; 1973 ctx.barriers = barriers;
1974 ctx.include_unlocked_squares = include_unlocked_squares;
1971 findloop_run(fls, w*h, net_neighbour, &ctx); 1975 findloop_run(fls, w*h, net_neighbour, &ctx);
1972 1976
1973 loops = snewn(w*h, int); 1977 loops = snewn(w*h, int);
1974 1978
1975 for (y = 0; y < h; y++) { 1979 for (y = 0; y < h; y++) {
1976 for (x = 0; x < w; x++) { 1980 for (x = 0; x < w; x++) {
1977 int x1, y1, dir; 1981 int x1, y1, v1, dir;
1978 int flags = 0; 1982 int flags = 0;
1979 1983
1984 v = y * w + x;
1980 for (dir = 1; dir < 0x10; dir <<= 1) { 1985 for (dir = 1; dir < 0x10; dir <<= 1) {
1981 if ((tiles[y*w+x] & dir) && 1986 if ((tiles[v] & dir) &&
1982 !(barriers && (barriers[y*w+x] & dir))) { 1987 !(barriers && (barriers[y*w+x] & dir))) {
1983 OFFSETWH(x1, y1, x, y, dir, w, h); 1988 OFFSETWH(x1, y1, x, y, dir, w, h);
1984 if ((tiles[y1*w+x1] & F(dir)) && 1989 v1 = y1 * w + x1;
1990 if (!include_unlocked_squares &&
1991 !(tiles[v] & tiles[v1] & LOCKED))
1992 continue;
1993 if ((tiles[v1] & F(dir)) &&
1985 findloop_is_loop_edge(fls, y*w+x, y1*w+x1)) 1994 findloop_is_loop_edge(fls, y*w+x, y1*w+x1))
1986 flags |= ERR(dir); 1995 flags |= ERR(dir);
1987 } 1996 }
@@ -1994,10 +2003,12 @@ static int *compute_loops_inner(int w, int h, bool wrapping,
1994 return loops; 2003 return loops;
1995} 2004}
1996 2005
1997static int *compute_loops(const game_state *state) 2006static int *compute_loops(const game_state *state,
2007 bool include_unlocked_squares)
1998{ 2008{
1999 return compute_loops_inner(state->width, state->height, state->wrapping, 2009 return compute_loops_inner(state->width, state->height, state->wrapping,
2000 state->tiles, state->imm->barriers); 2010 state->tiles, state->imm->barriers,
2011 include_unlocked_squares);
2001} 2012}
2002 2013
2003struct game_ui { 2014struct game_ui {
@@ -2010,6 +2021,8 @@ struct game_ui {
2010 int dragtilex, dragtiley, dragstartx, dragstarty; 2021 int dragtilex, dragtiley, dragstartx, dragstarty;
2011 bool dragged; 2022 bool dragged;
2012#endif 2023#endif
2024
2025 bool unlocked_loops;
2013}; 2026};
2014 2027
2015static game_ui *new_ui(const game_state *state) 2028static game_ui *new_ui(const game_state *state)
@@ -2017,20 +2030,31 @@ static game_ui *new_ui(const game_state *state)
2017 void *seed; 2030 void *seed;
2018 int seedsize; 2031 int seedsize;
2019 game_ui *ui = snew(game_ui); 2032 game_ui *ui = snew(game_ui);
2020 ui->org_x = ui->org_y = 0; 2033
2021 ui->cur_x = ui->cx = state->width / 2; 2034 ui->unlocked_loops = true;
2022 ui->cur_y = ui->cy = state->height / 2; 2035
2023 ui->cur_visible = false; 2036 if (state) {
2024 get_random_seed(&seed, &seedsize); 2037 ui->org_x = ui->org_y = 0;
2025 ui->rs = random_new(seed, seedsize); 2038 ui->cur_x = ui->cx = state->width / 2;
2026 sfree(seed); 2039 ui->cur_y = ui->cy = state->height / 2;
2040 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2041 get_random_seed(&seed, &seedsize);
2042 ui->rs = random_new(seed, seedsize);
2043 sfree(seed);
2044#ifdef USE_DRAGGING
2045 ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1;
2046#endif
2047 } else {
2048 ui->rs = NULL;
2049 }
2027 2050
2028 return ui; 2051 return ui;
2029} 2052}
2030 2053
2031static void free_ui(game_ui *ui) 2054static void free_ui(game_ui *ui)
2032{ 2055{
2033 random_free(ui->rs); 2056 if (ui->rs)
2057 random_free(ui->rs);
2034 sfree(ui); 2058 sfree(ui);
2035} 2059}
2036 2060
@@ -2045,10 +2069,45 @@ static char *encode_ui(const game_ui *ui)
2045 return dupstr(buf); 2069 return dupstr(buf);
2046} 2070}
2047 2071
2048static void decode_ui(game_ui *ui, const char *encoding) 2072static void decode_ui(game_ui *ui, const char *encoding,
2073 const game_state *state)
2049{ 2074{
2050 sscanf(encoding, "O%d,%d;C%d,%d", 2075 int org_x, org_y, cx, cy;
2051 &ui->org_x, &ui->org_y, &ui->cx, &ui->cy); 2076
2077 if (sscanf(encoding, "O%d,%d;C%d,%d", &org_x, &org_y, &cx, &cy) == 4) {
2078 if (0 <= org_x && org_x < state->width &&
2079 0 <= org_y && org_y < state->height) {
2080 ui->org_x = org_x;
2081 ui->org_y = org_y;
2082 }
2083 if (0 <= cx && cx < state->width &&
2084 0 <= cy && cy < state->height) {
2085 ui->cx = cx;
2086 ui->cy = cy;
2087 }
2088 }
2089}
2090
2091static config_item *get_prefs(game_ui *ui)
2092{
2093 config_item *ret;
2094
2095 ret = snewn(2, config_item);
2096
2097 ret[0].name = "Highlight loops involving unlocked squares";
2098 ret[0].kw = "unlocked-loops";
2099 ret[0].type = C_BOOLEAN;
2100 ret[0].u.boolean.bval = ui->unlocked_loops;
2101
2102 ret[1].name = NULL;
2103 ret[1].type = C_END;
2104
2105 return ret;
2106}
2107
2108static void set_prefs(game_ui *ui, const config_item *cfg)
2109{
2110 ui->unlocked_loops = cfg[0].u.boolean.bval;
2052} 2111}
2053 2112
2054static void game_changed_state(game_ui *ui, const game_state *oldstate, 2113static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -2056,8 +2115,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2056{ 2115{
2057} 2116}
2058 2117
2118static const char *current_key_label(const game_ui *ui,
2119 const game_state *state, int button)
2120{
2121 if (tile(state, ui->cur_x, ui->cur_y) & LOCKED) {
2122 if (button == CURSOR_SELECT2) return "Unlock";
2123 } else {
2124 if (button == CURSOR_SELECT) return "Rotate";
2125 if (button == CURSOR_SELECT2) return "Lock";
2126 }
2127 return "";
2128}
2129
2059struct game_drawstate { 2130struct game_drawstate {
2060 bool started;
2061 int width, height; 2131 int width, height;
2062 int tilesize; 2132 int tilesize;
2063 unsigned long *visible, *to_draw; 2133 unsigned long *visible, *to_draw;
@@ -2078,7 +2148,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2078 MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR 2148 MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR
2079 } action; 2149 } action;
2080 2150
2081 button &= ~MOD_MASK; 2151 button = STRIP_BUTTON_MODIFIERS(button);
2082 nullret = NULL; 2152 nullret = NULL;
2083 action = NONE; 2153 action = NONE;
2084 2154
@@ -2094,7 +2164,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2094 2164
2095 if (ui->cur_visible) { 2165 if (ui->cur_visible) {
2096 ui->cur_visible = false; 2166 ui->cur_visible = false;
2097 nullret = UI_UPDATE; 2167 nullret = MOVE_UI_UPDATE;
2098 } 2168 }
2099 2169
2100 /* 2170 /*
@@ -2102,12 +2172,22 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2102 */ 2172 */
2103 x -= WINDOW_OFFSET + LINE_THICK; 2173 x -= WINDOW_OFFSET + LINE_THICK;
2104 y -= WINDOW_OFFSET + LINE_THICK; 2174 y -= WINDOW_OFFSET + LINE_THICK;
2105 if (x < 0 || y < 0)
2106 return nullret;
2107 tx = x / TILE_SIZE; 2175 tx = x / TILE_SIZE;
2108 ty = y / TILE_SIZE; 2176 ty = y / TILE_SIZE;
2109 if (tx >= state->width || ty >= state->height) 2177 if (x < 0 || y < 0 || tx >= state->width || ty >= state->height) {
2178#ifdef USE_DRAGGING
2179 if (IS_MOUSE_DOWN(button)) {
2180 ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1;
2181 return nullret;
2182 }
2183 /*
2184 * else: Despite the mouse moving off the grid, let drags and releases
2185 * continue to manipulate the tile they started from.
2186 */
2187#else
2110 return nullret; 2188 return nullret;
2189#endif
2190 }
2111 /* Transform from physical to game coords */ 2191 /* Transform from physical to game coords */
2112 tx = (tx + ui->org_x) % state->width; 2192 tx = (tx + ui->org_x) % state->width;
2113 ty = (ty + ui->org_y) % state->height; 2193 ty = (ty + ui->org_y) % state->height;
@@ -2145,6 +2225,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2145 || button == RIGHT_DRAG 2225 || button == RIGHT_DRAG
2146#endif 2226#endif
2147 ) { 2227 ) {
2228 if (ui->dragtilex < 0)
2229 return nullret;
2230
2148 /* 2231 /*
2149 * Find the new drag point and see if it necessitates a 2232 * Find the new drag point and see if it necessitates a
2150 * rotation. 2233 * rotation.
@@ -2198,7 +2281,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2198 || button == RIGHT_RELEASE 2281 || button == RIGHT_RELEASE
2199#endif 2282#endif
2200 ) { 2283 ) {
2201 if (!ui->dragged) { 2284 if (!ui->dragged && ui->dragtilex >= 0) {
2202 /* 2285 /*
2203 * There was a click but no perceptible drag: 2286 * There was a click but no perceptible drag:
2204 * revert to single-click behaviour. 2287 * revert to single-click behaviour.
@@ -2334,7 +2417,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2334 OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); 2417 OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state);
2335 ui->cur_visible = true; 2418 ui->cur_visible = true;
2336 } 2419 }
2337 return UI_UPDATE; 2420 return MOVE_UI_UPDATE;
2338 } else { 2421 } else {
2339 return NULL; 2422 return NULL;
2340 } 2423 }
@@ -2444,7 +2527,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2444 game_drawstate *ds = snew(game_drawstate); 2527 game_drawstate *ds = snew(game_drawstate);
2445 int i, ncells; 2528 int i, ncells;
2446 2529
2447 ds->started = false;
2448 ds->width = state->width; 2530 ds->width = state->width;
2449 ds->height = state->height; 2531 ds->height = state->height;
2450 ncells = (state->width+2) * (state->height+2); 2532 ncells = (state->width+2) * (state->height+2);
@@ -2464,11 +2546,12 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2464static void game_free_drawstate(drawing *dr, game_drawstate *ds) 2546static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2465{ 2547{
2466 sfree(ds->visible); 2548 sfree(ds->visible);
2549 sfree(ds->to_draw);
2467 sfree(ds); 2550 sfree(ds);
2468} 2551}
2469 2552
2470static void game_compute_size(const game_params *params, int tilesize, 2553static void game_compute_size(const game_params *params, int tilesize,
2471 int *x, int *y) 2554 const game_ui *ui, int *x, int *y)
2472{ 2555{
2473 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2556 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2474 struct { int tilesize; } ads, *ds = &ads; 2557 struct { int tilesize; } ads, *ds = &ads;
@@ -2605,8 +2688,8 @@ static void draw_wires(drawing *dr, int cx, int cy, int radius,
2605 2688
2606 for (i = 0; i < npoints; i++) { 2689 for (i = 0; i < npoints; i++) {
2607 rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]); 2690 rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]);
2608 points[2*i] = 0.5 + xf; 2691 points[2*i] = 0.5F + xf;
2609 points[2*i+1] = 0.5 + yf; 2692 points[2*i+1] = 0.5F + yf;
2610 } 2693 }
2611 2694
2612 draw_polygon(dr, points, npoints, colour, colour); 2695 draw_polygon(dr, points, npoints, colour, colour);
@@ -2746,8 +2829,8 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y,
2746 * rotated by an arbitrary angle about that centre point. 2829 * rotated by an arbitrary angle about that centre point.
2747 */ 2830 */
2748 if (tile & TILE_ROTATING) { 2831 if (tile & TILE_ROTATING) {
2749 matrix[0] = (float)cos(angle * PI / 180.0); 2832 matrix[0] = (float)cos(angle * (float)PI / 180.0F);
2750 matrix[2] = (float)sin(angle * PI / 180.0); 2833 matrix[2] = (float)sin(angle * (float)PI / 180.0F);
2751 } else { 2834 } else {
2752 matrix[0] = 1.0F; 2835 matrix[0] = 1.0F;
2753 matrix[2] = 0.0F; 2836 matrix[2] = 0.0F;
@@ -2786,8 +2869,8 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y,
2786 float x, y; 2869 float x, y;
2787 rotated_coords(&x, &y, matrix, cx, cy, 2870 rotated_coords(&x, &y, matrix, cx, cy,
2788 boxr * points[i], boxr * points[i+1]); 2871 boxr * points[i], boxr * points[i+1]);
2789 points[i] = x + 0.5; 2872 points[i] = x + 0.5F;
2790 points[i+1] = y + 0.5; 2873 points[i+1] = y + 0.5F;
2791 } 2874 }
2792 2875
2793 draw_polygon(dr, points, 4, col, COL_WIRE); 2876 draw_polygon(dr, points, 4, col, COL_WIRE);
@@ -2841,23 +2924,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2841 int *loops; 2924 int *loops;
2842 float angle = 0.0; 2925 float angle = 0.0;
2843 2926
2844 /*
2845 * Clear the screen on our first call.
2846 */
2847 if (!ds->started) {
2848 int w, h;
2849 game_params params;
2850
2851 ds->started = true;
2852
2853 params.width = ds->width;
2854 params.height = ds->height;
2855 game_compute_size(&params, TILE_SIZE, &w, &h);
2856
2857 draw_rect(dr, 0, 0, w, h, COL_BACKGROUND);
2858 draw_update(dr, 0, 0, w, h);
2859 }
2860
2861 tx = ty = -1; 2927 tx = ty = -1;
2862 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : 2928 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
2863 state->last_rotate_dir; 2929 state->last_rotate_dir;
@@ -2888,7 +2954,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2888 * of barriers. 2954 * of barriers.
2889 */ 2955 */
2890 active = compute_active(state, ui->cx, ui->cy); 2956 active = compute_active(state, ui->cx, ui->cy);
2891 loops = compute_loops(state); 2957 loops = compute_loops(state, ui->unlocked_loops);
2892 2958
2893 for (dy = -1; dy < ds->height+1; dy++) { 2959 for (dy = -1; dy < ds->height+1; dy++) {
2894 for (dx = -1; dx < ds->width+1; dx++) { 2960 for (dx = -1; dx < ds->width+1; dx++) {
@@ -3109,19 +3175,15 @@ static int game_status(const game_state *state)
3109 return state->completed ? +1 : 0; 3175 return state->completed ? +1 : 0;
3110} 3176}
3111 3177
3112static bool game_timing_state(const game_state *state, game_ui *ui) 3178static void game_print_size(const game_params *params, const game_ui *ui,
3113{ 3179 float *x, float *y)
3114 return true;
3115}
3116
3117static void game_print_size(const game_params *params, float *x, float *y)
3118{ 3180{
3119 int pw, ph; 3181 int pw, ph;
3120 3182
3121 /* 3183 /*
3122 * I'll use 8mm squares by default. 3184 * I'll use 8mm squares by default.
3123 */ 3185 */
3124 game_compute_size(params, 800, &pw, &ph); 3186 game_compute_size(params, 800, ui, &pw, &ph);
3125 *x = pw / 100.0F; 3187 *x = pw / 100.0F;
3126 *y = ph / 100.0F; 3188 *y = ph / 100.0F;
3127} 3189}
@@ -3172,7 +3234,8 @@ static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
3172 } 3234 }
3173} 3235}
3174 3236
3175static void game_print(drawing *dr, const game_state *state, int tilesize) 3237static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3238 int tilesize)
3176{ 3239{
3177 int w = state->width, h = state->height; 3240 int w = state->width, h = state->height;
3178 int ink = print_mono_colour(dr, 0); 3241 int ink = print_mono_colour(dr, 0);
@@ -3269,13 +3332,15 @@ const struct game thegame = {
3269 dup_game, 3332 dup_game,
3270 free_game, 3333 free_game,
3271 true, solve_game, 3334 true, solve_game,
3272 false, game_can_format_as_text_now, game_text_format, 3335 false, NULL, NULL, /* can_format_as_text_now, text_format */
3336 get_prefs, set_prefs,
3273 new_ui, 3337 new_ui,
3274 free_ui, 3338 free_ui,
3275 encode_ui, 3339 encode_ui,
3276 decode_ui, 3340 decode_ui,
3277 NULL, /* game_request_keys */ 3341 NULL, /* game_request_keys */
3278 game_changed_state, 3342 game_changed_state,
3343 current_key_label,
3279 interpret_move, 3344 interpret_move,
3280 execute_move, 3345 execute_move,
3281 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3346 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3289,6 +3354,6 @@ const struct game thegame = {
3289 game_status, 3354 game_status,
3290 true, false, game_print_size, game_print, 3355 true, false, game_print_size, game_print,
3291 true, /* wants_statusbar */ 3356 true, /* wants_statusbar */
3292 false, game_timing_state, 3357 false, NULL, /* timing_state */
3293 0, /* flags */ 3358 0, /* flags */
3294}; 3359};
diff --git a/apps/plugins/puzzles/src/netslide.R b/apps/plugins/puzzles/src/netslide.R
deleted file mode 100644
index ecfe7c3df8..0000000000
--- a/apps/plugins/puzzles/src/netslide.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3NETSLIDE_EXTRA = tree234
4
5netslide : [X] GTK COMMON netslide NETSLIDE_EXTRA netslide-icon|no-icon
6
7netslide : [G] WINDOWS COMMON netslide NETSLIDE_EXTRA netslide.res|noicon.res
8
9ALL += netslide[COMBINED] NETSLIDE_EXTRA
10
11!begin am gtk
12GAMES += netslide
13!end
14
15!begin >list.c
16 A(netslide) \
17!end
18
19!begin >gamedesc.txt
20netslide:netslide.exe:Netslide:Toroidal sliding network puzzle:Slide a row at a time to reassemble the network.
21!end
diff --git a/apps/plugins/puzzles/src/netslide.c b/apps/plugins/puzzles/src/netslide.c
index 14af2a689d..4e6e82b39f 100644
--- a/apps/plugins/puzzles/src/netslide.c
+++ b/apps/plugins/puzzles/src/netslide.c
@@ -8,7 +8,12 @@
8#include <string.h> 8#include <string.h>
9#include <assert.h> 9#include <assert.h>
10#include <ctype.h> 10#include <ctype.h>
11#include <math.h> 11#include <limits.h>
12#ifdef NO_TGMATH_H
13# include <math.h>
14#else
15# include <tgmath.h>
16#endif
12 17
13#include "puzzles.h" 18#include "puzzles.h"
14#include "tree234.h" 19#include "tree234.h"
@@ -242,10 +247,7 @@ static char *encode_params(const game_params *params, bool full)
242 if (params->wrapping) 247 if (params->wrapping)
243 ret[len++] = 'w'; 248 ret[len++] = 'w';
244 if (full && params->barrier_probability) 249 if (full && params->barrier_probability)
245 { 250 len += sprintf(ret+len, "b%g", params->barrier_probability);
246 len += sprintf(ret+len, "b");
247 len += ftoa(ret + len, params->barrier_probability);
248 }
249 /* Shuffle limit is part of the limited parameters, because we have to 251 /* Shuffle limit is part of the limited parameters, because we have to
250 * provide the target move count. */ 252 * provide the target move count. */
251 if (params->movetarget) 253 if (params->movetarget)
@@ -279,7 +281,7 @@ static config_item *game_configure(const game_params *params)
279 281
280 ret[3].name = "Barrier probability"; 282 ret[3].name = "Barrier probability";
281 ret[3].type = C_STRING; 283 ret[3].type = C_STRING;
282 ftoa(buf, params->barrier_probability); 284 sprintf(buf, "%g", params->barrier_probability);
283 ret[3].u.string.sval = dupstr(buf); 285 ret[3].u.string.sval = dupstr(buf);
284 286
285 ret[4].name = "Number of shuffling moves"; 287 ret[4].name = "Number of shuffling moves";
@@ -310,6 +312,8 @@ static const char *validate_params(const game_params *params, bool full)
310{ 312{
311 if (params->width <= 1 || params->height <= 1) 313 if (params->width <= 1 || params->height <= 1)
312 return "Width and height must both be greater than one"; 314 return "Width and height must both be greater than one";
315 if (params->width > INT_MAX / params->height)
316 return "Width times height must not be unreasonably large";
313 if (params->barrier_probability < 0) 317 if (params->barrier_probability < 0)
314 return "Barrier probability may not be negative"; 318 return "Barrier probability may not be negative";
315 if (params->barrier_probability > 1) 319 if (params->barrier_probability > 1)
@@ -895,16 +899,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
895 return dupstr(aux); 899 return dupstr(aux);
896} 900}
897 901
898static bool game_can_format_as_text_now(const game_params *params)
899{
900 return true;
901}
902
903static char *game_text_format(const game_state *state)
904{
905 return NULL;
906}
907
908/* ---------------------------------------------------------------------- 902/* ----------------------------------------------------------------------
909 * Utility routine. 903 * Utility routine.
910 */ 904 */
@@ -981,7 +975,7 @@ static game_ui *new_ui(const game_state *state)
981 game_ui *ui = snew(game_ui); 975 game_ui *ui = snew(game_ui);
982 ui->cur_x = 0; 976 ui->cur_x = 0;
983 ui->cur_y = -1; 977 ui->cur_y = -1;
984 ui->cur_visible = false; 978 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
985 979
986 return ui; 980 return ui;
987} 981}
@@ -991,15 +985,6 @@ static void free_ui(game_ui *ui)
991 sfree(ui); 985 sfree(ui);
992} 986}
993 987
994static char *encode_ui(const game_ui *ui)
995{
996 return NULL;
997}
998
999static void decode_ui(game_ui *ui, const char *encoding)
1000{
1001}
1002
1003/* ---------------------------------------------------------------------- 988/* ----------------------------------------------------------------------
1004 * Process a move. 989 * Process a move.
1005 */ 990 */
@@ -1009,7 +994,9 @@ static void slide_row_int(int w, int h, unsigned char *tiles, int dir, int row)
1009 int x = dir > 0 ? -1 : w; 994 int x = dir > 0 ? -1 : w;
1010 int tx = x + dir; 995 int tx = x + dir;
1011 int n = w - 1; 996 int n = w - 1;
1012 unsigned char endtile = tiles[row * w + tx]; 997 unsigned char endtile;
998 assert(0 <= tx && tx < w);
999 endtile = tiles[row * w + tx];
1013 do { 1000 do {
1014 x = tx; 1001 x = tx;
1015 tx = (x + dir + w) % w; 1002 tx = (x + dir + w) % w;
@@ -1023,7 +1010,9 @@ static void slide_col_int(int w, int h, unsigned char *tiles, int dir, int col)
1023 int y = dir > 0 ? -1 : h; 1010 int y = dir > 0 ? -1 : h;
1024 int ty = y + dir; 1011 int ty = y + dir;
1025 int n = h - 1; 1012 int n = h - 1;
1026 unsigned char endtile = tiles[ty * w + col]; 1013 unsigned char endtile;
1014 assert(0 <= ty && ty < h);
1015 endtile = tiles[ty * w + col];
1027 do { 1016 do {
1028 y = ty; 1017 y = ty;
1029 ty = (y + dir + h) % h; 1018 ty = (y + dir + h) % h;
@@ -1055,6 +1044,14 @@ struct game_drawstate {
1055 int cur_x, cur_y; 1044 int cur_x, cur_y;
1056}; 1045};
1057 1046
1047static const char *current_key_label(const game_ui *ui,
1048 const game_state *state, int button)
1049{
1050 if (IS_CURSOR_SELECT(button) && ui->cur_visible)
1051 return "Slide";
1052 return "";
1053}
1054
1058static char *interpret_move(const game_state *state, game_ui *ui, 1055static char *interpret_move(const game_state *state, game_ui *ui,
1059 const game_drawstate *ds, 1056 const game_drawstate *ds,
1060 int x, int y, int button) 1057 int x, int y, int button)
@@ -1063,7 +1060,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1063 int dx, dy; 1060 int dx, dy;
1064 char buf[80]; 1061 char buf[80];
1065 1062
1066 button &= ~MOD_MASK; 1063 button = STRIP_BUTTON_MODIFIERS(button);
1067 1064
1068 if (IS_CURSOR_MOVE(button)) { 1065 if (IS_CURSOR_MOVE(button)) {
1069 int cpos, diff = 0; 1066 int cpos, diff = 0;
@@ -1078,7 +1075,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1078 } 1075 }
1079 1076
1080 ui->cur_visible = true; 1077 ui->cur_visible = true;
1081 return UI_UPDATE; 1078 return MOVE_UI_UPDATE;
1082 } 1079 }
1083 1080
1084 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1081 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
@@ -1092,7 +1089,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1092 } else { 1089 } else {
1093 /* 'click' when cursor is invisible just makes cursor visible. */ 1090 /* 'click' when cursor is invisible just makes cursor visible. */
1094 ui->cur_visible = true; 1091 ui->cur_visible = true;
1095 return UI_UPDATE; 1092 return MOVE_UI_UPDATE;
1096 } 1093 }
1097 } else 1094 } else
1098 return NULL; 1095 return NULL;
@@ -1136,7 +1133,9 @@ static game_state *execute_move(const game_state *from, const char *move)
1136 1133
1137 if ((move[0] == 'C' || move[0] == 'R') && 1134 if ((move[0] == 'C' || move[0] == 'R') &&
1138 sscanf(move+1, "%d,%d", &c, &d) == 2 && 1135 sscanf(move+1, "%d,%d", &c, &d) == 2 &&
1139 c >= 0 && c < (move[0] == 'C' ? from->width : from->height)) { 1136 c >= 0 && c < (move[0] == 'C' ? from->width : from->height) &&
1137 d <= (move[0] == 'C' ? from->height : from->width) &&
1138 d >= -(move[0] == 'C' ? from->height : from->width) && d != 0) {
1140 col = (move[0] == 'C'); 1139 col = (move[0] == 'C');
1141 } else if (move[0] == 'S' && 1140 } else if (move[0] == 'S' &&
1142 strlen(move) == from->width * from->height + 1) { 1141 strlen(move) == from->width * from->height + 1) {
@@ -1226,7 +1225,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1226} 1225}
1227 1226
1228static void game_compute_size(const game_params *params, int tilesize, 1227static void game_compute_size(const game_params *params, int tilesize,
1229 int *x, int *y) 1228 const game_ui *ui, int *x, int *y)
1230{ 1229{
1231 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1230 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1232 struct { int tilesize; } ads, *ds = &ads; 1231 struct { int tilesize; } ads, *ds = &ads;
@@ -1490,7 +1489,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1490 vx = (dy ? 1 : 0); 1489 vx = (dy ? 1 : 0);
1491 vy = (dx ? 1 : 0); 1490 vy = (dx ? 1 : 0);
1492 1491
1493 if (xshift == 0.0 && yshift == 0.0 && (tile & dir)) { 1492 if (xshift == 0.0F && yshift == 0.0F && (tile & dir)) {
1494 /* 1493 /*
1495 * If we are fully connected to the other tile, we must 1494 * If we are fully connected to the other tile, we must
1496 * draw right across the tile border. (We can use our 1495 * draw right across the tile border. (We can use our
@@ -1594,22 +1593,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1594 int cur_x = -1, cur_y = -1; 1593 int cur_x = -1, cur_y = -1;
1595 1594
1596 /* 1595 /*
1597 * Clear the screen and draw the exterior barrier lines if this 1596 * Draw the exterior barrier lines if this is our first call.
1598 * is our first call.
1599 */ 1597 */
1600 if (!ds->started) { 1598 if (!ds->started) {
1601 int phase; 1599 int phase;
1602 1600
1603 ds->started = true; 1601 ds->started = true;
1604 1602
1605 draw_rect(dr, 0, 0,
1606 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
1607 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
1608 COL_BACKGROUND);
1609 draw_update(dr, 0, 0,
1610 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
1611 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
1612
1613 for (phase = 0; phase < 2; phase++) { 1603 for (phase = 0; phase < 2; phase++) {
1614 1604
1615 for (x = 0; x < ds->width; x++) { 1605 for (x = 0; x < ds->width; x++) {
@@ -1703,7 +1693,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1703 /* 1693 /*
1704 * Draw any tile which differs from the way it was last drawn. 1694 * Draw any tile which differs from the way it was last drawn.
1705 */ 1695 */
1706 if (xshift != 0.0 || yshift != 0.0) { 1696 if (xshift != 0.0F || yshift != 0.0F) {
1707 active = compute_active(state, 1697 active = compute_active(state,
1708 state->last_move_row, state->last_move_col); 1698 state->last_move_row, state->last_move_col);
1709 } else { 1699 } else {
@@ -1849,19 +1839,6 @@ static int game_status(const game_state *state)
1849 return state->completed ? +1 : 0; 1839 return state->completed ? +1 : 0;
1850} 1840}
1851 1841
1852static bool game_timing_state(const game_state *state, game_ui *ui)
1853{
1854 return false;
1855}
1856
1857static void game_print_size(const game_params *params, float *x, float *y)
1858{
1859}
1860
1861static void game_print(drawing *dr, const game_state *state, int tilesize)
1862{
1863}
1864
1865#ifdef COMBINED 1842#ifdef COMBINED
1866#define thegame netslide 1843#define thegame netslide
1867#endif 1844#endif
@@ -1882,13 +1859,15 @@ const struct game thegame = {
1882 dup_game, 1859 dup_game,
1883 free_game, 1860 free_game,
1884 true, solve_game, 1861 true, solve_game,
1885 false, game_can_format_as_text_now, game_text_format, 1862 false, NULL, NULL, /* can_format_as_text_now, text_format */
1863 NULL, NULL, /* get_prefs, set_prefs */
1886 new_ui, 1864 new_ui,
1887 free_ui, 1865 free_ui,
1888 encode_ui, 1866 NULL, /* encode_ui */
1889 decode_ui, 1867 NULL, /* decode_ui */
1890 NULL, /* game_request_keys */ 1868 NULL, /* game_request_keys */
1891 game_changed_state, 1869 game_changed_state,
1870 current_key_label,
1892 interpret_move, 1871 interpret_move,
1893 execute_move, 1872 execute_move,
1894 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1873 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1900,9 +1879,9 @@ const struct game thegame = {
1900 game_flash_length, 1879 game_flash_length,
1901 game_get_cursor_location, 1880 game_get_cursor_location,
1902 game_status, 1881 game_status,
1903 false, false, game_print_size, game_print, 1882 false, false, NULL, NULL, /* print_size, print */
1904 true, /* wants_statusbar */ 1883 true, /* wants_statusbar */
1905 false, game_timing_state, 1884 false, NULL, /* timing_state */
1906 0, /* flags */ 1885 0, /* flags */
1907}; 1886};
1908 1887
diff --git a/apps/plugins/puzzles/src/no-icon.c b/apps/plugins/puzzles/src/no-icon.c
deleted file mode 100644
index 114b2c57c7..0000000000
--- a/apps/plugins/puzzles/src/no-icon.c
+++ /dev/null
@@ -1,8 +0,0 @@
1
2/*
3 * Dummy source file which replaces the files generated in the
4 * `icons' subdirectory, when they're absent.
5 */
6
7const char *const *const xpm_icons[] = { 0 };
8const int n_xpm_icons = 0;
diff --git a/apps/plugins/puzzles/src/nullfe.c b/apps/plugins/puzzles/src/nullfe.c
deleted file mode 100644
index 7cba4e0c23..0000000000
--- a/apps/plugins/puzzles/src/nullfe.c
+++ /dev/null
@@ -1,75 +0,0 @@
1/*
2 * nullfe.c: Null front-end code containing a bunch of boring stub
3 * functions. Used to ensure successful linking when building the
4 * various stand-alone solver binaries.
5 */
6
7#include <stdarg.h>
8
9#include "puzzles.h"
10
11void frontend_default_colour(frontend *fe, float *output) {}
12void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
13 int align, int colour, const char *text) {}
14void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) {}
15void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) {}
16void draw_thick_line(drawing *dr, float thickness,
17 float x1, float y1, float x2, float y2, int colour) {}
18void draw_polygon(drawing *dr, int *coords, int npoints,
19 int fillcolour, int outlinecolour) {}
20void draw_circle(drawing *dr, int cx, int cy, int radius,
21 int fillcolour, int outlinecolour) {}
22char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
23{ return dupstr(strings[0]); }
24void clip(drawing *dr, int x, int y, int w, int h) {}
25void unclip(drawing *dr) {}
26void start_draw(drawing *dr) {}
27void draw_update(drawing *dr, int x, int y, int w, int h) {}
28void end_draw(drawing *dr) {}
29blitter *blitter_new(drawing *dr, int w, int h) {return NULL;}
30void blitter_free(drawing *dr, blitter *bl) {}
31void blitter_save(drawing *dr, blitter *bl, int x, int y) {}
32void blitter_load(drawing *dr, blitter *bl, int x, int y) {}
33int print_mono_colour(drawing *dr, int grey) { return 0; }
34int print_grey_colour(drawing *dr, float grey) { return 0; }
35int print_hatched_colour(drawing *dr, int hatch) { return 0; }
36int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey)
37{ return 0; }
38int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey)
39{ return 0; }
40int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch)
41{ return 0; }
42void print_line_width(drawing *dr, int width) {}
43void print_line_dotted(drawing *dr, bool dotted) {}
44void midend_supersede_game_desc(midend *me, const char *desc,
45 const char *privdesc) {}
46void status_bar(drawing *dr, const char *text) {}
47struct preset_menu *preset_menu_new(void) {return NULL;}
48struct preset_menu *preset_menu_add_submenu(struct preset_menu *parent,
49 char *title) {return NULL;}
50void preset_menu_add_preset(struct preset_menu *parent,
51 char *title, game_params *params) {}
52
53void fatal(const char *fmt, ...)
54{
55 va_list ap;
56
57 fprintf(stderr, "fatal error: ");
58
59 va_start(ap, fmt);
60 vfprintf(stderr, fmt, ap);
61 va_end(ap);
62
63 fprintf(stderr, "\n");
64 exit(1);
65}
66
67#ifdef DEBUGGING
68void debug_printf(const char *fmt, ...)
69{
70 va_list ap;
71 va_start(ap, fmt);
72 vfprintf(stdout, fmt, ap);
73 va_end(ap);
74}
75#endif
diff --git a/apps/plugins/puzzles/src/nullgame.R b/apps/plugins/puzzles/src/nullgame.R
deleted file mode 100644
index 41bdb85d57..0000000000
--- a/apps/plugins/puzzles/src/nullgame.R
+++ /dev/null
@@ -1,12 +0,0 @@
1# -*- makefile -*-
2
3# The `nullgame' source file is a largely blank one, which contains
4# all the correct function definitions to compile and link, but
5# which defines the null game in which nothing is ever drawn and
6# there are no valid moves. Its main purpose is to act as a
7# template for writing new game definition source files. I include
8# it in the Makefile because it will be worse than useless if it
9# ever fails to compile, so it's important that it should actually
10# be built on a regular basis.
11nullgame : [X] GTK COMMON nullgame nullgame-icon|no-icon
12nullgame : [G] WINDOWS COMMON nullgame nullgame.res|noicon.res
diff --git a/apps/plugins/puzzles/src/nullgame.c b/apps/plugins/puzzles/src/nullgame.c
deleted file mode 100644
index d923bc8710..0000000000
--- a/apps/plugins/puzzles/src/nullgame.c
+++ /dev/null
@@ -1,313 +0,0 @@
1/*
2 * nullgame.c [FIXME]: Template defining the null game (in which no
3 * moves are permitted and nothing is ever drawn). This file exists
4 * solely as a basis for constructing new game definitions - it
5 * helps to have something which will compile from the word go and
6 * merely doesn't _do_ very much yet.
7 *
8 * Parts labelled FIXME actually want _removing_ (e.g. the dummy
9 * field in each of the required data structures, and this entire
10 * comment itself) when converting this source file into one
11 * describing a real game.
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <assert.h>
18#include <ctype.h>
19#include <math.h>
20
21#include "puzzles.h"
22
23enum {
24 COL_BACKGROUND,
25 NCOLOURS
26};
27
28struct game_params {
29 int FIXME;
30};
31
32struct game_state {
33 int FIXME;
34};
35
36static game_params *default_params(void)
37{
38 game_params *ret = snew(game_params);
39
40 ret->FIXME = 0;
41
42 return ret;
43}
44
45static bool game_fetch_preset(int i, char **name, game_params **params)
46{
47 return false;
48}
49
50static void free_params(game_params *params)
51{
52 sfree(params);
53}
54
55static game_params *dup_params(const game_params *params)
56{
57 game_params *ret = snew(game_params);
58 *ret = *params; /* structure copy */
59 return ret;
60}
61
62static void decode_params(game_params *params, char const *string)
63{
64}
65
66static char *encode_params(const game_params *params, bool full)
67{
68 return dupstr("FIXME");
69}
70
71static config_item *game_configure(const game_params *params)
72{
73 return NULL;
74}
75
76static game_params *custom_params(const config_item *cfg)
77{
78 return NULL;
79}
80
81static const char *validate_params(const game_params *params, bool full)
82{
83 return NULL;
84}
85
86static char *new_game_desc(const game_params *params, random_state *rs,
87 char **aux, bool interactive)
88{
89 return dupstr("FIXME");
90}
91
92static const char *validate_desc(const game_params *params, const char *desc)
93{
94 return NULL;
95}
96
97static game_state *new_game(midend *me, const game_params *params,
98 const char *desc)
99{
100 game_state *state = snew(game_state);
101
102 state->FIXME = 0;
103
104 return state;
105}
106
107static game_state *dup_game(const game_state *state)
108{
109 game_state *ret = snew(game_state);
110
111 ret->FIXME = state->FIXME;
112
113 return ret;
114}
115
116static void free_game(game_state *state)
117{
118 sfree(state);
119}
120
121static char *solve_game(const game_state *state, const game_state *currstate,
122 const char *aux, const char **error)
123{
124 return NULL;
125}
126
127static bool game_can_format_as_text_now(const game_params *params)
128{
129 return true;
130}
131
132static char *game_text_format(const game_state *state)
133{
134 return NULL;
135}
136
137static game_ui *new_ui(const game_state *state)
138{
139 return NULL;
140}
141
142static void free_ui(game_ui *ui)
143{
144}
145
146static char *encode_ui(const game_ui *ui)
147{
148 return NULL;
149}
150
151static void decode_ui(game_ui *ui, const char *encoding)
152{
153}
154
155static void game_changed_state(game_ui *ui, const game_state *oldstate,
156 const game_state *newstate)
157{
158}
159
160struct game_drawstate {
161 int tilesize;
162 int FIXME;
163};
164
165static char *interpret_move(const game_state *state, game_ui *ui,
166 const game_drawstate *ds,
167 int x, int y, int button)
168{
169 return NULL;
170}
171
172static game_state *execute_move(const game_state *state, const char *move)
173{
174 return NULL;
175}
176
177/* ----------------------------------------------------------------------
178 * Drawing routines.
179 */
180
181static void game_compute_size(const game_params *params, int tilesize,
182 int *x, int *y)
183{
184 *x = *y = 10 * tilesize; /* FIXME */
185}
186
187static void game_set_size(drawing *dr, game_drawstate *ds,
188 const game_params *params, int tilesize)
189{
190 ds->tilesize = tilesize;
191}
192
193static float *game_colours(frontend *fe, int *ncolours)
194{
195 float *ret = snewn(3 * NCOLOURS, float);
196
197 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
198
199 *ncolours = NCOLOURS;
200 return ret;
201}
202
203static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
204{
205 struct game_drawstate *ds = snew(struct game_drawstate);
206
207 ds->tilesize = 0;
208 ds->FIXME = 0;
209
210 return ds;
211}
212
213static void game_free_drawstate(drawing *dr, game_drawstate *ds)
214{
215 sfree(ds);
216}
217
218static void game_redraw(drawing *dr, game_drawstate *ds,
219 const game_state *oldstate, const game_state *state,
220 int dir, const game_ui *ui,
221 float animtime, float flashtime)
222{
223 /*
224 * The initial contents of the window are not guaranteed and
225 * can vary with front ends. To be on the safe side, all games
226 * should start by drawing a big background-colour rectangle
227 * covering the whole window.
228 */
229 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
230 draw_update(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize);
231}
232
233static float game_anim_length(const game_state *oldstate,
234 const game_state *newstate, int dir, game_ui *ui)
235{
236 return 0.0F;
237}
238
239static float game_flash_length(const game_state *oldstate,
240 const game_state *newstate, int dir, game_ui *ui)
241{
242 return 0.0F;
243}
244
245static void game_get_cursor_location(const game_ui *ui,
246 const game_drawstate *ds,
247 const game_state *state,
248 const game_params *params,
249 int *x, int *y, int *w, int *h)
250{
251}
252
253static int game_status(const game_state *state)
254{
255 return 0;
256}
257
258static bool game_timing_state(const game_state *state, game_ui *ui)
259{
260 return true;
261}
262
263static void game_print_size(const game_params *params, float *x, float *y)
264{
265}
266
267static void game_print(drawing *dr, const game_state *state, int tilesize)
268{
269}
270
271#ifdef COMBINED
272#define thegame nullgame
273#endif
274
275const struct game thegame = {
276 "Null Game", NULL, NULL,
277 default_params,
278 game_fetch_preset, NULL,
279 decode_params,
280 encode_params,
281 free_params,
282 dup_params,
283 false, game_configure, custom_params,
284 validate_params,
285 new_game_desc,
286 validate_desc,
287 new_game,
288 dup_game,
289 free_game,
290 false, solve_game,
291 false, game_can_format_as_text_now, game_text_format,
292 new_ui,
293 free_ui,
294 encode_ui,
295 decode_ui,
296 NULL, /* game_request_keys */
297 game_changed_state,
298 interpret_move,
299 execute_move,
300 20 /* FIXME */, game_compute_size, game_set_size,
301 game_colours,
302 game_new_drawstate,
303 game_free_drawstate,
304 game_redraw,
305 game_anim_length,
306 game_flash_length,
307 game_get_cursor_location,
308 game_status,
309 false, false, game_print_size, game_print,
310 false, /* wants_statusbar */
311 false, game_timing_state,
312 0, /* flags */
313};
diff --git a/apps/plugins/puzzles/src/obfusc.c b/apps/plugins/puzzles/src/obfusc.c
deleted file mode 100644
index c62189c3ad..0000000000
--- a/apps/plugins/puzzles/src/obfusc.c
+++ /dev/null
@@ -1,130 +0,0 @@
1/*
2 * Stand-alone tool to access the Puzzles obfuscation algorithm.
3 *
4 * To deobfuscate, use "obfusc -d":
5 *
6 * obfusc -d reads binary data from stdin, writes to stdout
7 * obfusc -d <hex string> works on the given hex string instead of stdin
8 * obfusc -d -h writes a hex string instead of binary to stdout
9 *
10 * To obfuscate, "obfusc -e":
11 *
12 * obfusc -e reads binary from stdin, writes hex to stdout
13 * obfusc -e <hex string> works on the given hex string instead of stdin
14 * obfusc -e -b writes binary instead of text to stdout
15 *
16 * The default output format is hex for -e and binary for -d
17 * because that's the way obfuscation is generally used in
18 * Puzzles. Either of -b and -h can always be specified to set it
19 * explicitly.
20 *
21 * Data read from standard input is assumed always to be binary;
22 * data provided on the command line is taken to be hex.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdarg.h>
28#include <string.h>
29#include <errno.h>
30
31#include "puzzles.h"
32
33int main(int argc, char **argv)
34{
35 enum { BINARY, DEFAULT, HEX } outputmode = DEFAULT;
36 char *inhex = NULL;
37 unsigned char *data;
38 int datalen;
39 enum { UNKNOWN, DECODE, ENCODE } mode = UNKNOWN;
40 bool doing_opts = true;
41
42 while (--argc > 0) {
43 char *p = *++argv;
44
45 if (doing_opts && *p == '-') {
46 if (!strcmp(p, "--")) {
47 doing_opts = 0;
48 continue;
49 }
50 p++;
51 while (*p) {
52 switch (*p) {
53 case 'e':
54 mode = ENCODE;
55 break;
56 case 'd':
57 mode = DECODE;
58 break;
59 case 'b':
60 outputmode = BINARY;
61 break;
62 case 'h':
63 outputmode = HEX;
64 break;
65 default:
66 fprintf(stderr, "obfusc: unrecognised option '-%c'\n",
67 *p);
68 return 1;
69 }
70 p++;
71 }
72 } else {
73 if (!inhex) {
74 inhex = p;
75 } else {
76 fprintf(stderr, "obfusc: expected at most one argument\n");
77 return 1;
78 }
79 }
80 }
81
82 if (mode == UNKNOWN) {
83 fprintf(stderr, "usage: obfusc < -e | -d > [ -b | -h ] [hex data]\n");
84 return 0;
85 }
86
87 if (outputmode == DEFAULT)
88 outputmode = (mode == DECODE ? BINARY : HEX);
89
90 if (inhex) {
91 datalen = strlen(inhex) / 2;
92 data = hex2bin(inhex, datalen);
93 } else {
94 int datasize = 4096;
95 datalen = 0;
96 data = snewn(datasize, unsigned char);
97 while (1) {
98 int ret = fread(data + datalen, 1, datasize - datalen, stdin);
99 if (ret < 0) {
100 fprintf(stderr, "obfusc: read: %s\n", strerror(errno));
101 return 1;
102 } else if (ret == 0) {
103 break;
104 } else {
105 datalen += ret;
106 if (datasize - datalen < 4096) {
107 datasize = datalen * 5 / 4 + 4096;
108 data = sresize(data, datasize, unsigned char);
109 }
110 }
111 }
112 }
113
114 obfuscate_bitmap(data, datalen * 8, mode == DECODE);
115
116 if (outputmode == BINARY) {
117 int ret = fwrite(data, 1, datalen, stdout);
118 if (ret < 0) {
119 fprintf(stderr, "obfusc: write: %s\n", strerror(errno));
120 return 1;
121 }
122 } else {
123 int i;
124 for (i = 0; i < datalen; i++)
125 printf("%02x", data[i]);
126 printf("\n");
127 }
128
129 return 0;
130}
diff --git a/apps/plugins/puzzles/src/osx-help.but b/apps/plugins/puzzles/src/osx-help.but
deleted file mode 100644
index fa45996aee..0000000000
--- a/apps/plugins/puzzles/src/osx-help.but
+++ /dev/null
@@ -1,14 +0,0 @@
1\# Additional Halibut fragment to set up the HTML output
2\# appropriately for MacOS online help.
3
4\cfg{html-head-end}{
5<style type="text/css">
6body \{ font-family: "Lucida Grande", Helvetica, Arial; font-size: 9pt \}
7h1 \{ font-size: 12pt \}
8h2 \{ font-size: 10pt \}
9h3 \{ font-size: 9pt \}
10h4 \{ font-size: 9pt \}
11h5 \{ font-size: 9pt \}
12h6 \{ font-size: 9pt \}
13</style>
14}
diff --git a/apps/plugins/puzzles/src/palisade.R b/apps/plugins/puzzles/src/palisade.R
deleted file mode 100644
index de4bd83126..0000000000
--- a/apps/plugins/puzzles/src/palisade.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3PALISADE_EXTRA = divvy dsf
4
5palisade : [X] GTK COMMON palisade PALISADE_EXTRA palisade-icon|no-icon
6
7palisade : [G] WINDOWS COMMON palisade PALISADE_EXTRA palisade.res|noicon.res
8
9ALL += palisade[COMBINED] PALISADE_EXTRA
10
11!begin am gtk
12GAMES += palisade
13!end
14
15!begin >list.c
16 A(palisade) \
17!end
18
19!begin >gamedesc.txt
20palisade:palisade.exe:Palisade:Grid-division puzzle:Divide the grid into equal-sized areas in accordance with the clues.
21!end
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
index e941661a0e..ecbbbb4d6f 100644
--- a/apps/plugins/puzzles/src/palisade.c
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -17,6 +17,7 @@
17 17
18#include <assert.h> 18#include <assert.h>
19#include <ctype.h> 19#include <ctype.h>
20#include <limits.h>
20#include <stdarg.h> 21#include <stdarg.h>
21#include <stdio.h> 22#include <stdio.h>
22#include <stdlib.h> 23#include <stdlib.h>
@@ -46,7 +47,7 @@ struct game_params {
46 int w, h, k; 47 int w, h, k;
47}; 48};
48 49
49typedef char clue; 50typedef signed char clue;
50typedef unsigned char borderflag; 51typedef unsigned char borderflag;
51 52
52typedef struct shared_state { 53typedef struct shared_state {
@@ -156,13 +157,15 @@ static game_params *custom_params(const config_item *cfg)
156 157
157static const char *validate_params(const game_params *params, bool full) 158static const char *validate_params(const game_params *params, bool full)
158{ 159{
159 int w = params->w, h = params->h, k = params->k, wh = w * h; 160 int w = params->w, h = params->h, k = params->k, wh;
160 161
161 if (k < 1) return "Region size must be at least one"; 162 if (k < 1) return "Region size must be at least one";
162 if (w < 1) return "Width must be at least one"; 163 if (w < 1) return "Width must be at least one";
163 if (h < 1) return "Height must be at least one"; 164 if (h < 1) return "Height must be at least one";
165 if (w > INT_MAX / h)
166 return "Width times height must not be unreasonably large";
167 wh = w * h;
164 if (wh % k) return "Region size must divide grid area"; 168 if (wh % k) return "Region size must divide grid area";
165
166 if (!full) return NULL; /* succeed partial validation */ 169 if (!full) return NULL; /* succeed partial validation */
167 170
168 /* MAYBE FIXME: we (just?) don't have the UI for winning these. */ 171 /* MAYBE FIXME: we (just?) don't have the UI for winning these. */
@@ -183,7 +186,7 @@ typedef struct solver_ctx {
183 const game_params *params; /* also in shared_state */ 186 const game_params *params; /* also in shared_state */
184 clue *clues; /* also in shared_state */ 187 clue *clues; /* also in shared_state */
185 borderflag *borders; /* also in game_state */ 188 borderflag *borders; /* also in game_state */
186 int *dsf; /* particular to the solver */ 189 DSF *dsf; /* particular to the solver */
187} solver_ctx; 190} solver_ctx;
188 191
189/* Deductions: 192/* Deductions:
@@ -270,7 +273,7 @@ static void connect(solver_ctx *ctx, int i, int j)
270static bool connected(solver_ctx *ctx, int i, int j, int dir) 273static bool connected(solver_ctx *ctx, int i, int j, int dir)
271{ 274{
272 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir]; 275 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir];
273 return dsf_canonify(ctx->dsf, i) == dsf_canonify(ctx->dsf, j); 276 return dsf_equivalent(ctx->dsf, i, j);
274} 277}
275 278
276static void disconnect(solver_ctx *ctx, int i, int j, int dir) 279static void disconnect(solver_ctx *ctx, int i, int j, int dir)
@@ -502,19 +505,20 @@ static bool solver_equivalent_edges(solver_ctx *ctx)
502 return changed; 505 return changed;
503} 506}
504 507
505#define UNVISITED 6
506
507/* build connected components in `dsf', along the lines of `borders'. */ 508/* build connected components in `dsf', along the lines of `borders'. */
508static void dfs_dsf(int i, int w, borderflag *border, int *dsf, bool black) 509static void build_dsf(int w, int h, borderflag *border, DSF *dsf, bool black)
509{ 510{
510 int dir; 511 int x, y;
511 for (dir = 0; dir < 4; ++dir) { 512
512 int ii = i + dx[dir] + w*dy[dir], bdir = BORDER(dir); 513 for (y = 0; y < h; y++) {
513 if (black ? (border[i] & bdir) : !(border[i] & DISABLED(bdir))) 514 for (x = 0; x < w; x++) {
514 continue; 515 if (x+1 < w && (black ? !(border[y*w+x] & BORDER_R) :
515 if (dsf[ii] != UNVISITED) continue; 516 (border[y*w+x] & DISABLED(BORDER_R))))
516 dsf_merge(dsf, i, ii); 517 dsf_merge(dsf, y*w+x, y*w+(x+1));
517 dfs_dsf(ii, w, border, dsf, black); 518 if (y+1 < h && (black ? !(border[y*w+x] & BORDER_D) :
519 (border[y*w+x] & DISABLED(BORDER_D))))
520 dsf_merge(dsf, y*w+x, (y+1)*w+x);
521 }
518 } 522 }
519} 523}
520 524
@@ -523,9 +527,9 @@ static bool is_solved(const game_params *params, clue *clues,
523{ 527{
524 int w = params->w, h = params->h, wh = w*h, k = params->k; 528 int w = params->w, h = params->h, wh = w*h, k = params->k;
525 int i, x, y; 529 int i, x, y;
526 int *dsf = snew_dsf(wh); 530 DSF *dsf = dsf_new(wh);
527 531
528 assert (dsf[0] == UNVISITED); /* check: UNVISITED and dsf.c match up */ 532 build_dsf(w, h, border, dsf, true);
529 533
530 /* 534 /*
531 * A game is solved if: 535 * A game is solved if:
@@ -536,7 +540,6 @@ static bool is_solved(const game_params *params, clue *clues,
536 * - the borders also satisfy the clue set 540 * - the borders also satisfy the clue set
537 */ 541 */
538 for (i = 0; i < wh; ++i) { 542 for (i = 0; i < wh; ++i) {
539 if (dsf[i] == UNVISITED) dfs_dsf(i, params->w, border, dsf, true);
540 if (dsf_size(dsf, i) != k) goto error; 543 if (dsf_size(dsf, i) != k) goto error;
541 if (clues[i] == EMPTY) continue; 544 if (clues[i] == EMPTY) continue;
542 if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error; 545 if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error;
@@ -554,19 +557,19 @@ static bool is_solved(const game_params *params, clue *clues,
554 for (y = 0; y < h; y++) { 557 for (y = 0; y < h; y++) {
555 for (x = 0; x < w; x++) { 558 for (x = 0; x < w; x++) {
556 if (x+1 < w && (border[y*w+x] & BORDER_R) && 559 if (x+1 < w && (border[y*w+x] & BORDER_R) &&
557 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, y*w+(x+1))) 560 dsf_equivalent(dsf, y*w+x, y*w+(x+1)))
558 goto error; 561 goto error;
559 if (y+1 < h && (border[y*w+x] & BORDER_D) && 562 if (y+1 < h && (border[y*w+x] & BORDER_D) &&
560 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, (y+1)*w+x)) 563 dsf_equivalent(dsf, y*w+x, (y+1)*w+x))
561 goto error; 564 goto error;
562 } 565 }
563 } 566 }
564 567
565 sfree(dsf); 568 dsf_free(dsf);
566 return true; 569 return true;
567 570
568error: 571error:
569 sfree(dsf); 572 dsf_free(dsf);
570 return false; 573 return false;
571} 574}
572 575
@@ -579,7 +582,7 @@ static bool solver(const game_params *params, clue *clues, borderflag *borders)
579 ctx.params = params; 582 ctx.params = params;
580 ctx.clues = clues; 583 ctx.clues = clues;
581 ctx.borders = borders; 584 ctx.borders = borders;
582 ctx.dsf = snew_dsf(wh); 585 ctx.dsf = dsf_new(wh);
583 586
584 solver_connected_clues_versus_region_size(&ctx); /* idempotent */ 587 solver_connected_clues_versus_region_size(&ctx); /* idempotent */
585 do { 588 do {
@@ -591,7 +594,7 @@ static bool solver(const game_params *params, clue *clues, borderflag *borders)
591 changed |= solver_equivalent_edges(&ctx); 594 changed |= solver_equivalent_edges(&ctx);
592 } while (changed); 595 } while (changed);
593 596
594 sfree(ctx.dsf); 597 dsf_free(ctx.dsf);
595 598
596 return is_solved(params, clues, borders); 599 return is_solved(params, clues, borders);
597} 600}
@@ -622,15 +625,14 @@ static char *new_game_desc(const game_params *params, random_state *rs,
622{ 625{
623 int w = params->w, h = params->h, wh = w*h, k = params->k; 626 int w = params->w, h = params->h, wh = w*h, k = params->k;
624 627
625 clue *numbers = snewn(wh + 1, clue), *p; 628 clue *numbers = snewn(wh + 1, clue);
626 borderflag *rim = snewn(wh, borderflag); 629 borderflag *rim = snewn(wh, borderflag);
627 borderflag *scratch_borders = snewn(wh, borderflag); 630 borderflag *scratch_borders = snewn(wh, borderflag);
628 631
629 char *soln = snewa(*aux, wh + 2); 632 char *soln = snewa(*aux, wh + 2);
630 int *shuf = snewn(wh, int); 633 int *shuf = snewn(wh, int);
631 int *dsf = NULL, i, r, c; 634 DSF *dsf = NULL;
632 635 int i, r, c;
633 int attempts = 0;
634 636
635 for (i = 0; i < wh; ++i) shuf[i] = i; 637 for (i = 0; i < wh; ++i) shuf[i] = i;
636 xshuffle(shuf, wh, rs); 638 xshuffle(shuf, wh, rs);
@@ -642,10 +644,9 @@ static char *new_game_desc(const game_params *params, random_state *rs,
642 soln[wh] = '\0'; 644 soln[wh] = '\0';
643 645
644 do { 646 do {
645 ++attempts;
646 setmem(soln, '@', wh); 647 setmem(soln, '@', wh);
647 648
648 sfree(dsf); 649 dsf_free(dsf);
649 dsf = divvy_rectangle(w, h, k, rs); 650 dsf = divvy_rectangle(w, h, k, rs);
650 651
651 for (r = 0; r < h; ++r) 652 for (r = 0; r < h; ++r)
@@ -655,7 +656,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
655 for (dir = 0; dir < 4; ++dir) { 656 for (dir = 0; dir < 4; ++dir) {
656 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc; 657 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc;
657 if (OUT_OF_BOUNDS(cc, rr, w, h) || 658 if (OUT_OF_BOUNDS(cc, rr, w, h) ||
658 dsf_canonify(dsf, i) != dsf_canonify(dsf, ii)) { 659 !dsf_equivalent(dsf, i, ii)) {
659 ++numbers[i]; 660 ++numbers[i];
660 soln[i] |= BORDER(dir); 661 soln[i] |= BORDER(dir);
661 } 662 }
@@ -680,9 +681,10 @@ static char *new_game_desc(const game_params *params, random_state *rs,
680 sfree(scratch_borders); 681 sfree(scratch_borders);
681 sfree(rim); 682 sfree(rim);
682 sfree(shuf); 683 sfree(shuf);
683 sfree(dsf); 684 dsf_free(dsf);
685
686 char *output = snewn(wh + 1, char), *p = output;
684 687
685 p = numbers;
686 r = 0; 688 r = 0;
687 for (i = 0; i < wh; ++i) { 689 for (i = 0; i < wh; ++i) {
688 if (numbers[i] != EMPTY) { 690 if (numbers[i] != EMPTY) {
@@ -699,7 +701,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
699 } 701 }
700 *p++ = '\0'; 702 *p++ = '\0';
701 703
702 return sresize(numbers, p - numbers, clue); 704 sfree(numbers);
705 return sresize(output, p - output, char);
703} 706}
704 707
705static const char *validate_desc(const game_params *params, const char *desc) 708static const char *validate_desc(const game_params *params, const char *desc)
@@ -865,32 +868,51 @@ static char *game_text_format(const game_state *state)
865} 868}
866 869
867struct game_ui { 870struct game_ui {
871 /* These are half-grid coordinates - (0,0) is the top left corner
872 * of the top left square; (1,1) is the center of the top left
873 * grid square. */
868 int x, y; 874 int x, y;
869 bool show; 875 bool show;
870 bool fake_ctrl, fake_shift; 876
877 bool legacy_cursor;
871}; 878};
872 879
873static game_ui *new_ui(const game_state *state) 880static game_ui *new_ui(const game_state *state)
874{ 881{
875 game_ui *ui = snew(game_ui); 882 game_ui *ui = snew(game_ui);
876 ui->x = ui->y = 0; 883 ui->x = ui->y = 1;
877 ui->show = ui->fake_ctrl = ui->fake_shift = false; 884 ui->show = getenv_bool("PUZZLES_SHOW_CURSOR", false);
885 ui->legacy_cursor = false;
878 return ui; 886 return ui;
879} 887}
880 888
881static void free_ui(game_ui *ui) 889static config_item *get_prefs(game_ui *ui)
882{ 890{
883 sfree(ui); 891 config_item *cfg;
892
893 cfg = snewn(2, config_item);
894
895 cfg[0].name = "Cursor mode";
896 cfg[0].kw = "cursor-mode";
897 cfg[0].type = C_CHOICES;
898 cfg[0].u.choices.choicenames = ":Half-grid:Full-grid";
899 cfg[0].u.choices.choicekws = ":half:full";
900 cfg[0].u.choices.selected = ui->legacy_cursor;
901
902 cfg[1].name = NULL;
903 cfg[1].type = C_END;
904
905 return cfg;
884} 906}
885 907
886static char *encode_ui(const game_ui *ui) 908static void set_prefs(game_ui *ui, const config_item *cfg)
887{ 909{
888 return NULL; 910 ui->legacy_cursor = cfg[0].u.choices.selected;
889} 911}
890 912
891static void decode_ui(game_ui *ui, const char *encoding) 913static void free_ui(game_ui *ui)
892{ 914{
893 assert (encoding == NULL); 915 sfree(ui);
894} 916}
895 917
896static void game_changed_state(game_ui *ui, const game_state *oldstate, 918static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -898,7 +920,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
898{ 920{
899} 921}
900 922
901typedef unsigned short dsflags; 923typedef int dsflags;
902 924
903struct game_drawstate { 925struct game_drawstate {
904 int tilesize; 926 int tilesize;
@@ -907,7 +929,7 @@ struct game_drawstate {
907 929
908#define TILESIZE (ds->tilesize) 930#define TILESIZE (ds->tilesize)
909#define MARGIN (ds->tilesize / 2) 931#define MARGIN (ds->tilesize / 2)
910#define WIDTH (1 + (TILESIZE >= 16) + (TILESIZE >= 32) + (TILESIZE >= 64)) 932#define WIDTH (3*TILESIZE/32 > 1 ? 3*TILESIZE/32 : 1)
911#define CENTER ((ds->tilesize / 2) + WIDTH/2) 933#define CENTER ((ds->tilesize / 2) + WIDTH/2)
912 934
913#define FROMCOORD(x) (((x) - MARGIN) / TILESIZE) 935#define FROMCOORD(x) (((x) - MARGIN) / TILESIZE)
@@ -918,12 +940,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
918 const game_drawstate *ds, int x, int y, int button) 940 const game_drawstate *ds, int x, int y, int button)
919{ 941{
920 int w = state->shared->params.w, h = state->shared->params.h; 942 int w = state->shared->params.w, h = state->shared->params.h;
921 bool control = (button & MOD_CTRL) | ui->fake_ctrl, shift = (button & MOD_SHFT) | ui->fake_shift; 943 bool control = button & MOD_CTRL, shift = button & MOD_SHFT;
922
923 /* reset */
924 ui->fake_ctrl = ui->fake_shift = false;
925 944
926 button &= ~MOD_MASK; 945 button = STRIP_BUTTON_MODIFIERS(button);
927 946
928 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 947 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
929 int gx = FROMCOORD(x), gy = FROMCOORD(y), possible = BORDER_MASK; 948 int gx = FROMCOORD(x), gy = FROMCOORD(y), possible = BORDER_MASK;
@@ -932,9 +951,6 @@ static char *interpret_move(const game_state *state, game_ui *ui,
932 951
933 if (OUT_OF_BOUNDS(gx, gy, w, h)) return NULL; 952 if (OUT_OF_BOUNDS(gx, gy, w, h)) return NULL;
934 953
935 ui->x = gx;
936 ui->y = gy;
937
938 /* find edge closest to click point */ 954 /* find edge closest to click point */
939 possible &=~ (2*px < TILESIZE ? BORDER_R : BORDER_L); 955 possible &=~ (2*px < TILESIZE ? BORDER_R : BORDER_L);
940 possible &=~ (2*py < TILESIZE ? BORDER_D : BORDER_U); 956 possible &=~ (2*py < TILESIZE ? BORDER_D : BORDER_U);
@@ -945,6 +961,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
945 for (dir = 0; dir < 4 && BORDER(dir) != possible; ++dir); 961 for (dir = 0; dir < 4 && BORDER(dir) != possible; ++dir);
946 if (dir == 4) return NULL; /* there's not exactly one such edge */ 962 if (dir == 4) return NULL; /* there's not exactly one such edge */
947 963
964 ui->x = min(max(2*gx + 1 + dx[dir], 1), 2*w-1);
965 ui->y = min(max(2*gy + 1 + dy[dir], 1), 2*h-1);
966
948 hx = gx + dx[dir]; 967 hx = gx + dx[dir];
949 hy = gy + dy[dir]; 968 hy = gy + dy[dir];
950 969
@@ -974,17 +993,17 @@ static char *interpret_move(const game_state *state, game_ui *ui,
974 } 993 }
975 994
976 if (IS_CURSOR_MOVE(button)) { 995 if (IS_CURSOR_MOVE(button)) {
977 ui->show = true; 996 if(ui->legacy_cursor && (control || shift)) {
978 if (control || shift) {
979 borderflag flag = 0, newflag; 997 borderflag flag = 0, newflag;
980 int dir, i = ui->y * w + ui->x; 998 int dir, i = (ui->y/2) * w + (ui->x/2);
981 x = ui->x; 999 ui->show = true;
982 y = ui->y; 1000 x = ui->x/2;
983 move_cursor(button, &x, &y, w, h, false); 1001 y = ui->y/2;
1002 move_cursor(button, &x, &y, w, h, false, NULL);
984 if (OUT_OF_BOUNDS(x, y, w, h)) return NULL; 1003 if (OUT_OF_BOUNDS(x, y, w, h)) return NULL;
985 1004
986 for (dir = 0; dir < 4; ++dir) 1005 for (dir = 0; dir < 4; ++dir)
987 if (dx[dir] == x - ui->x && dy[dir] == y - ui->y) break; 1006 if (dx[dir] == x - ui->x/2 && dy[dir] == y - ui->y/2) break;
988 if (dir == 4) return NULL; /* how the ... ?! */ 1007 if (dir == 4) return NULL; /* how the ... ?! */
989 1008
990 if (control) flag |= BORDER(dir); 1009 if (control) flag |= BORDER(dir);
@@ -998,21 +1017,67 @@ static char *interpret_move(const game_state *state, game_ui *ui,
998 if (control) newflag |= BORDER(FLIP(dir)); 1017 if (control) newflag |= BORDER(FLIP(dir));
999 if (shift) newflag |= DISABLED(BORDER(FLIP(dir))); 1018 if (shift) newflag |= DISABLED(BORDER(FLIP(dir)));
1000 return string(80, "F%d,%d,%dF%d,%d,%d", 1019 return string(80, "F%d,%d,%dF%d,%d,%d",
1001 ui->x, ui->y, flag, x, y, newflag); 1020 ui->x/2, ui->y/2, flag, x, y, newflag);
1002 } else { 1021 } else {
1003 move_cursor(button, &ui->x, &ui->y, w, h, false); 1022 /* TODO: Refactor this and other half-grid cursor games
1004 return UI_UPDATE; 1023 * (Tracks, etc.) */
1024 int dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0);
1025 int dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
1026
1027 if(ui->legacy_cursor) {
1028 dx *= 2; dy *= 2;
1029
1030 ui->x |= 1;
1031 ui->y |= 1;
1032 }
1033
1034 if (!ui->show) {
1035 ui->show = true;
1036 }
1037
1038 ui->x = min(max(ui->x + dx, 1), 2*w-1);
1039 ui->y = min(max(ui->y + dy, 1), 2*h-1);
1040
1041 return MOVE_UI_UPDATE;
1042 }
1043 } else if (IS_CURSOR_SELECT(button)) {
1044 int px = ui->x % 2, py = ui->y % 2;
1045 int gx = ui->x / 2, gy = ui->y / 2;
1046 int dir = (px == 0) ? 3 : 0; /* left = 3; up = 0 */
1047 int hx = gx + dx[dir];
1048 int hy = gy + dy[dir];
1049
1050 int i = gy * w + gx;
1051
1052 if(!ui->show) {
1053 ui->show = true;
1054 return MOVE_UI_UPDATE;
1055 }
1056
1057 /* clicks on square corners and centers do nothing */
1058 if (px == py)
1059 return MOVE_NO_EFFECT;
1060
1061 /* TODO: Refactor this and the mouse click handling code
1062 * above. */
1063 switch ((button == CURSOR_SELECT2) |
1064 ((state->borders[i] & BORDER(dir)) >> dir << 1) |
1065 ((state->borders[i] & DISABLED(BORDER(dir))) >> dir >> 2)) {
1066
1067 case MAYBE_LEFT:
1068 case ON_LEFT:
1069 case ON_RIGHT:
1070 return string(80, "F%d,%d,%dF%d,%d,%d",
1071 gx, gy, BORDER(dir),
1072 hx, hy, BORDER(FLIP(dir)));
1073
1074 case MAYBE_RIGHT:
1075 case OFF_LEFT:
1076 case OFF_RIGHT:
1077 return string(80, "F%d,%d,%dF%d,%d,%d",
1078 gx, gy, DISABLED(BORDER(dir)),
1079 hx, hy, DISABLED(BORDER(FLIP(dir))));
1005 } 1080 }
1006 }
1007 else if(IS_CURSOR_SELECT(button)) {
1008 /* CURSOR_SELECT or CURSOR_SELECT2 tells us to toggle whether
1009 * the button press should be interpreted as having CTRL or
1010 * shift pressed along with it, respectively. */
1011 ui->show = true;
1012 if(button == CURSOR_SELECT2)
1013 ui->fake_shift = !ui->fake_shift;
1014 else
1015 ui->fake_ctrl = !ui->fake_ctrl;
1016 } 1081 }
1017 1082
1018 return NULL; 1083 return NULL;
@@ -1022,15 +1087,14 @@ static game_state *execute_move(const game_state *state, const char *move)
1022{ 1087{
1023 int w = state->shared->params.w, h = state->shared->params.h, wh = w * h; 1088 int w = state->shared->params.w, h = state->shared->params.h, wh = w * h;
1024 game_state *ret = dup_game(state); 1089 game_state *ret = dup_game(state);
1025 int nchars, x, y, flag; 1090 int nchars, x, y, flag, i;
1026 1091
1027 if (*move == 'S') { 1092 if (*move == 'S') {
1028 int i;
1029 ++move; 1093 ++move;
1030 for (i = 0; i < wh && move[i]; ++i) 1094 for (i = 0; i < wh && move[i]; ++i)
1031 ret->borders[i] = 1095 ret->borders[i] =
1032 (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK); 1096 (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK);
1033 if (i < wh || move[i]) return NULL; /* leaks `ret', then we die */ 1097 if (i < wh || move[i]) goto badmove;
1034 ret->cheated = ret->completed = true; 1098 ret->cheated = ret->completed = true;
1035 return ret; 1099 return ret;
1036 } 1100 }
@@ -1038,22 +1102,31 @@ static game_state *execute_move(const game_state *state, const char *move)
1038 while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 && 1102 while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 &&
1039 !OUT_OF_BOUNDS(x, y, w, h)) { 1103 !OUT_OF_BOUNDS(x, y, w, h)) {
1040 move += nchars; 1104 move += nchars;
1105 for (i = 0; i < 4; i++)
1106 if ((flag & BORDER(i)) &&
1107 OUT_OF_BOUNDS(x+dx[i], y+dy[i], w, h))
1108 /* No toggling the borders of the grid! */
1109 goto badmove;
1041 ret->borders[y*w + x] ^= flag; 1110 ret->borders[y*w + x] ^= flag;
1042 } 1111 }
1043 1112
1044 if (*move) return NULL; /* leaks `ret', then we die */ 1113 if (*move) goto badmove;
1045 1114
1046 if (!ret->completed) 1115 if (!ret->completed)
1047 ret->completed = is_solved(&ret->shared->params, ret->shared->clues, 1116 ret->completed = is_solved(&ret->shared->params, ret->shared->clues,
1048 ret->borders); 1117 ret->borders);
1049 1118
1050 return ret; 1119 return ret;
1120
1121 badmove:
1122 free_game(ret);
1123 return NULL;
1051} 1124}
1052 1125
1053/* --- Drawing routines --------------------------------------------- */ 1126/* --- Drawing routines --------------------------------------------- */
1054 1127
1055static void game_compute_size(const game_params *params, int tilesize, 1128static void game_compute_size(const game_params *params, int tilesize,
1056 int *x, int *y) 1129 const game_ui *ui, int *x, int *y)
1057{ 1130{
1058 *x = (params->w + 1) * tilesize; 1131 *x = (params->w + 1) * tilesize;
1059 *y = (params->h + 1) * tilesize; 1132 *y = (params->h + 1) * tilesize;
@@ -1113,7 +1186,7 @@ static float *game_colours(frontend *fe, int *ncolours)
1113#define F_ERROR_L BORDER_ERROR(BORDER_L) /* BIT(11) */ 1186#define F_ERROR_L BORDER_ERROR(BORDER_L) /* BIT(11) */
1114#define F_ERROR_CLUE BIT(12) 1187#define F_ERROR_CLUE BIT(12)
1115#define F_FLASH BIT(13) 1188#define F_FLASH BIT(13)
1116#define F_CURSOR BIT(14) 1189#define CONTAINS_CURSOR(x) ((x) << 14)
1117 1190
1118static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 1191static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1119{ 1192{
@@ -1147,9 +1220,6 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int r, int c,
1147 draw_rect(dr, x + WIDTH, y + WIDTH, TILESIZE - WIDTH, TILESIZE - WIDTH, 1220 draw_rect(dr, x + WIDTH, y + WIDTH, TILESIZE - WIDTH, TILESIZE - WIDTH,
1148 (flags & F_FLASH ? COL_FLASH : COL_BACKGROUND)); 1221 (flags & F_FLASH ? COL_FLASH : COL_BACKGROUND));
1149 1222
1150 if (flags & F_CURSOR)
1151 draw_rect_corners(dr, x + CENTER, y + CENTER, TILESIZE / 3, COL_GRID);
1152
1153 if (clue != EMPTY) { 1223 if (clue != EMPTY) {
1154 char buf[2]; 1224 char buf[2];
1155 buf[0] = '0' + clue; 1225 buf[0] = '0' + clue;
@@ -1173,6 +1243,47 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int r, int c,
1173 draw_update(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH); 1243 draw_update(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH);
1174} 1244}
1175 1245
1246static void draw_cursor(drawing *dr, game_drawstate *ds,
1247 int cur_x, int cur_y, bool legacy_cursor)
1248{
1249 int off_x = cur_x % 2, off_y = cur_y % 2;
1250
1251 /* Figure out the tile coordinates corresponding to these cursor
1252 * coordinates. */
1253 int x = MARGIN + TILESIZE * (cur_x / 2), y = MARGIN + TILESIZE * (cur_y / 2);
1254
1255 /* off_x and off_y are either 0 or 1. The possible cases are
1256 * therefore:
1257 *
1258 * (0, 0): the cursor is in the top left corner of the tile.
1259 * (0, 1): the cursor is on the left border of the tile.
1260 * (1, 0): the cursor is on the top border of the tile.
1261 * (1, 1): the cursor is in the center of the tile.
1262 */
1263 enum { TOP_LEFT_CORNER, LEFT_BORDER, TOP_BORDER, TILE_CENTER } cur_type = (off_x << 1) + off_y;
1264
1265 int center_x = x + ((off_x == 0) ? WIDTH/2 : CENTER),
1266 center_y = y + ((off_y == 0) ? WIDTH/2 : CENTER);
1267
1268 struct { int w, h; } cursor_dimensions[] = {
1269 { TILESIZE / 3, TILESIZE / 3 }, /* top left corner */
1270 { TILESIZE / 3, 2 * TILESIZE / 3}, /* left border */
1271 { 2 * TILESIZE / 3, TILESIZE / 3}, /* top border */
1272 { 2 * TILESIZE / 3, 2 * TILESIZE / 3 } /* center */
1273 }, *dims = cursor_dimensions + cur_type;
1274
1275 if(legacy_cursor && cur_type == TILE_CENTER)
1276 draw_rect_corners(dr, center_x, center_y, TILESIZE / 3, COL_GRID);
1277 else
1278 draw_rect_outline(dr,
1279 center_x - dims->w / 2, center_y - dims->h / 2,
1280 dims->w, dims->h, COL_GRID);
1281
1282 draw_update(dr,
1283 center_x - dims->w / 2, center_y - dims->h / 2,
1284 dims->w, dims->h);
1285}
1286
1176#define FLASH_TIME 0.7F 1287#define FLASH_TIME 0.7F
1177 1288
1178static void game_redraw(drawing *dr, game_drawstate *ds, 1289static void game_redraw(drawing *dr, game_drawstate *ds,
@@ -1181,14 +1292,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1181 float animtime, float flashtime) 1292 float animtime, float flashtime)
1182{ 1293{
1183 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h; 1294 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
1184 int r, c, i, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; 1295 int r, c, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1185 int *black_border_dsf = snew_dsf(wh), *yellow_border_dsf = snew_dsf(wh); 1296 DSF *black_border_dsf = dsf_new(wh), *yellow_border_dsf = dsf_new(wh);
1186 int k = state->shared->params.k; 1297 int k = state->shared->params.k;
1187 1298
1188 if (!ds->grid) { 1299 if (!ds->grid) {
1189 char buf[40]; 1300 char buf[40];
1190 int bgw = (w+1) * ds->tilesize, bgh = (h+1) * ds->tilesize; 1301 int bgw = (w+1) * ds->tilesize, bgh = (h+1) * ds->tilesize;
1191 draw_rect(dr, 0, 0, bgw, bgh, COL_BACKGROUND);
1192 1302
1193 for (r = 0; r <= h; ++r) 1303 for (r = 0; r <= h; ++r)
1194 for (c = 0; c <= w; ++c) 1304 for (c = 0; c <= w; ++c)
@@ -1203,12 +1313,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1203 status_bar(dr, buf); 1313 status_bar(dr, buf);
1204 } 1314 }
1205 1315
1206 for (i = 0; i < wh; ++i) { 1316 build_dsf(w, h, state->borders, black_border_dsf, true);
1207 if (black_border_dsf[i] == UNVISITED) 1317 build_dsf(w, h, state->borders, yellow_border_dsf, false);
1208 dfs_dsf(i, w, state->borders, black_border_dsf, true);
1209 if (yellow_border_dsf[i] == UNVISITED)
1210 dfs_dsf(i, w, state->borders, yellow_border_dsf, false);
1211 }
1212 1318
1213 for (r = 0; r < h; ++r) 1319 for (r = 0; r < h; ++r)
1214 for (c = 0; c < w; ++c) { 1320 for (c = 0; c < w; ++c) {
@@ -1223,8 +1329,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1223 if (clue != EMPTY && (on > clue || clue > 4 - off)) 1329 if (clue != EMPTY && (on > clue || clue > 4 - off))
1224 flags |= F_ERROR_CLUE; 1330 flags |= F_ERROR_CLUE;
1225 1331
1226 if (ui->show && ui->x == c && ui->y == r) 1332 if (ui->show) {
1227 flags |= F_CURSOR; 1333 int u, v;
1334 for(u = 0; u < 3; u++)
1335 for(v = 0; v < 3; v++)
1336 if(ui->x == 2*c+u && ui->y == 2*r+v)
1337 flags |= CONTAINS_CURSOR(BIT(3*u+v));
1338 }
1228 1339
1229 /* border errors */ 1340 /* border errors */
1230 for (dir = 0; dir < 4; ++dir) { 1341 for (dir = 0; dir < 4; ++dir) {
@@ -1268,8 +1379,11 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1268 draw_tile(dr, ds, r, c, ds->grid[i], clue); 1379 draw_tile(dr, ds, r, c, ds->grid[i], clue);
1269 } 1380 }
1270 1381
1271 sfree(black_border_dsf); 1382 if (ui->show)
1272 sfree(yellow_border_dsf); 1383 draw_cursor(dr, ds, ui->x, ui->y, ui->legacy_cursor);
1384
1385 dsf_free(black_border_dsf);
1386 dsf_free(yellow_border_dsf);
1273} 1387}
1274 1388
1275static float game_anim_length(const game_state *oldstate, 1389static float game_anim_length(const game_state *oldstate,
@@ -1306,17 +1420,12 @@ static int game_status(const game_state *state)
1306 return state->completed ? +1 : 0; 1420 return state->completed ? +1 : 0;
1307} 1421}
1308 1422
1309static bool game_timing_state(const game_state *state, game_ui *ui) 1423static void game_print_size(const game_params *params, const game_ui *ui,
1310{ 1424 float *x, float *y)
1311 assert (!"this shouldn't get called");
1312 return false; /* placate optimiser */
1313}
1314
1315static void game_print_size(const game_params *params, float *x, float *y)
1316{ 1425{
1317 int pw, ph; 1426 int pw, ph;
1318 1427
1319 game_compute_size(params, 700, &pw, &ph); /* 7mm, like loopy */ 1428 game_compute_size(params, 700, ui, &pw, &ph); /* 7mm, like loopy */
1320 1429
1321 *x = pw / 100.0F; 1430 *x = pw / 100.0F;
1322 *y = ph / 100.0F; 1431 *y = ph / 100.0F;
@@ -1335,7 +1444,8 @@ static void print_line(drawing *dr, int x1, int y1, int x2, int y2,
1335 } else draw_line(dr, x1, y1, x2, y2, colour); 1444 } else draw_line(dr, x1, y1, x2, y2, colour);
1336} 1445}
1337 1446
1338static void game_print(drawing *dr, const game_state *state, int tilesize) 1447static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1448 int tilesize)
1339{ 1449{
1340 int w = state->shared->params.w, h = state->shared->params.h; 1450 int w = state->shared->params.w, h = state->shared->params.h;
1341 int ink = print_mono_colour(dr, 0); 1451 int ink = print_mono_colour(dr, 0);
@@ -1399,12 +1509,14 @@ const struct game thegame = {
1399 free_game, 1509 free_game,
1400 true, solve_game, 1510 true, solve_game,
1401 true, game_can_format_as_text_now, game_text_format, 1511 true, game_can_format_as_text_now, game_text_format,
1512 get_prefs, set_prefs, /* get_prefs, set_prefs */
1402 new_ui, 1513 new_ui,
1403 free_ui, 1514 free_ui,
1404 encode_ui, 1515 NULL, /* encode_ui */
1405 decode_ui, 1516 NULL, /* decode_ui */
1406 NULL, /* game_request_keys */ 1517 NULL, /* game_request_keys */
1407 game_changed_state, 1518 game_changed_state,
1519 NULL, /* current_key_label */
1408 interpret_move, 1520 interpret_move,
1409 execute_move, 1521 execute_move,
1410 48, game_compute_size, game_set_size, 1522 48, game_compute_size, game_set_size,
@@ -1418,6 +1530,6 @@ const struct game thegame = {
1418 game_status, 1530 game_status,
1419 true, false, game_print_size, game_print, 1531 true, false, game_print_size, game_print,
1420 true, /* wants_statusbar */ 1532 true, /* wants_statusbar */
1421 false, game_timing_state, 1533 false, NULL, /* timing_state */
1422 0, /* flags */ 1534 0, /* flags */
1423}; 1535};
diff --git a/apps/plugins/puzzles/src/pattern.R b/apps/plugins/puzzles/src/pattern.R
deleted file mode 100644
index d6695715f9..0000000000
--- a/apps/plugins/puzzles/src/pattern.R
+++ /dev/null
@@ -1,25 +0,0 @@
1# -*- makefile -*-
2
3pattern : [X] GTK COMMON pattern pattern-icon|no-icon
4
5pattern : [G] WINDOWS COMMON pattern pattern.res|noicon.res
6
7patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE
8patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
9
10patternpicture : [U] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
11patternpicture : [C] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
12
13ALL += pattern[COMBINED]
14
15!begin am gtk
16GAMES += pattern
17!end
18
19!begin >list.c
20 A(pattern) \
21!end
22
23!begin >gamedesc.txt
24pattern:pattern.exe:Pattern:Pattern puzzle:Fill in the pattern in the grid, given only the lengths of runs of black squares.
25!end
diff --git a/apps/plugins/puzzles/src/pattern.c b/apps/plugins/puzzles/src/pattern.c
index df720b7d82..b370a3dc2f 100644
--- a/apps/plugins/puzzles/src/pattern.c
+++ b/apps/plugins/puzzles/src/pattern.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13 18
@@ -53,6 +58,7 @@ typedef struct game_state_common {
53 int *rowdata, *rowlen; 58 int *rowdata, *rowlen;
54 bool *immutable; 59 bool *immutable;
55 int refcount; 60 int refcount;
61 enum { FS_SMALL, FS_LARGE } fontsize;
56} game_state_common; 62} game_state_common;
57 63
58struct game_state { 64struct game_state {
@@ -176,7 +182,10 @@ static const char *validate_params(const game_params *params, bool full)
176{ 182{
177 if (params->w <= 0 || params->h <= 0) 183 if (params->w <= 0 || params->h <= 0)
178 return "Width and height must both be greater than zero"; 184 return "Width and height must both be greater than zero";
179 if (params->w * params->w < 2) 185 if (params->w > INT_MAX - 1 || params->h > INT_MAX - 1 ||
186 params->w > INT_MAX / params->h)
187 return "Puzzle must not be unreasonably large";
188 if (params->w * params->h < 2)
180 return "Grid must contain at least two squares"; 189 return "Grid must contain at least two squares";
181 return NULL; 190 return NULL;
182} 191}
@@ -360,7 +369,7 @@ static int compute_rowdata(int *ret, unsigned char *start, int len, int step)
360#define STILL_UNKNOWN 3 369#define STILL_UNKNOWN 3
361 370
362#ifdef STANDALONE_SOLVER 371#ifdef STANDALONE_SOLVER
363bool verbose = false; 372static bool verbose = false;
364#endif 373#endif
365 374
366static bool do_recurse(unsigned char *known, unsigned char *deduced, 375static bool do_recurse(unsigned char *known, unsigned char *deduced,
@@ -441,6 +450,8 @@ static bool do_row(unsigned char *known, unsigned char *deduced,
441 int rowlen, i, freespace; 450 int rowlen, i, freespace;
442 bool done_any; 451 bool done_any;
443 452
453 assert(len >= 0); /* avoid compile warnings about the memsets below */
454
444 freespace = len+1; 455 freespace = len+1;
445 for (rowlen = 0; data[rowlen]; rowlen++) { 456 for (rowlen = 0; data[rowlen]; rowlen++) {
446 minpos_done[rowlen] = minpos_ok[rowlen] = len - 1; 457 minpos_done[rowlen] = minpos_ok[rowlen] = len - 1;
@@ -654,7 +665,7 @@ static bool solve_puzzle(const game_state *state, unsigned char *grid,
654#ifndef STANDALONE_PICTURE_GENERATOR 665#ifndef STANDALONE_PICTURE_GENERATOR
655static unsigned char *generate_soluble(random_state *rs, int w, int h) 666static unsigned char *generate_soluble(random_state *rs, int w, int h)
656{ 667{
657 int i, j, ntries, max; 668 int i, j, max;
658 bool ok; 669 bool ok;
659 unsigned char *grid, *matrix, *workspace; 670 unsigned char *grid, *matrix, *workspace;
660 unsigned int *changed_h, *changed_w; 671 unsigned int *changed_h, *changed_w;
@@ -670,11 +681,7 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
670 changed_w = snewn(max+1, unsigned int); 681 changed_w = snewn(max+1, unsigned int);
671 rowdata = snewn(max+1, int); 682 rowdata = snewn(max+1, int);
672 683
673 ntries = 0;
674
675 do { 684 do {
676 ntries++;
677
678 generate(rs, w, h, grid); 685 generate(rs, w, h, grid);
679 686
680 /* 687 /*
@@ -719,7 +726,7 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
719#endif 726#endif
720 727
721#ifdef STANDALONE_PICTURE_GENERATOR 728#ifdef STANDALONE_PICTURE_GENERATOR
722unsigned char *picture; 729static unsigned char *picture;
723#endif 730#endif
724 731
725static char *new_game_desc(const game_params *params, random_state *rs, 732static char *new_game_desc(const game_params *params, random_state *rs,
@@ -908,6 +915,10 @@ static const char *validate_desc(const game_params *params, const char *desc)
908 p = desc; 915 p = desc;
909 while (*desc && isdigit((unsigned char)*desc)) desc++; 916 while (*desc && isdigit((unsigned char)*desc)) desc++;
910 n = atoi(p); 917 n = atoi(p);
918 if (n <= 0)
919 return "all clues must be positive";
920 if (n > INT_MAX - 1)
921 return "at least one clue is grossly excessive";
911 rowspace -= n+1; 922 rowspace -= n+1;
912 923
913 if (rowspace < 0) { 924 if (rowspace < 0) {
@@ -965,7 +976,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
965static game_state *new_game(midend *me, const game_params *params, 976static game_state *new_game(midend *me, const game_params *params,
966 const char *desc) 977 const char *desc)
967{ 978{
968 int i; 979 int i, j;
969 const char *p; 980 const char *p;
970 game_state *state = snew(game_state); 981 game_state *state = snew(game_state);
971 982
@@ -1003,6 +1014,26 @@ static game_state *new_game(midend *me, const game_params *params,
1003 } 1014 }
1004 } 1015 }
1005 1016
1017 /*
1018 * Choose a font size based on the clues. If any column clue is
1019 * more than one digit, switch to the smaller size.
1020 */
1021 state->common->fontsize = FS_LARGE;
1022 for (i = 0; i < params->w; i++)
1023 for (j = 0; j < state->common->rowlen[i]; j++)
1024 if (state->common->rowdata[state->common->rowsize * i + j] >= 10)
1025 state->common->fontsize = FS_SMALL;
1026 /*
1027 * We might also need to use the small font if there are lots of
1028 * row clues. We assume that all clues are one digit and that a
1029 * single-digit clue takes up 1.5 tiles, of which the clue is 0.5
1030 * tiles and the space is 1.0 tiles.
1031 */
1032 for (i = params->w; i < params->w + params->h; i++)
1033 if ((state->common->rowlen[i] * 3 - 2) >
1034 TLBORDER(state->common->w) * 2)
1035 state->common->fontsize = FS_SMALL;
1036
1006 if (desc[-1] == ',') { 1037 if (desc[-1] == ',') {
1007 /* 1038 /*
1008 * Optional extra piece of game description which fills in 1039 * Optional extra piece of game description which fills in
@@ -1217,7 +1248,7 @@ static game_ui *new_ui(const game_state *state)
1217 ret = snew(game_ui); 1248 ret = snew(game_ui);
1218 ret->dragging = false; 1249 ret->dragging = false;
1219 ret->cur_x = ret->cur_y = 0; 1250 ret->cur_x = ret->cur_y = 0;
1220 ret->cur_visible = false; 1251 ret->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1221 1252
1222 return ret; 1253 return ret;
1223} 1254}
@@ -1227,18 +1258,26 @@ static void free_ui(game_ui *ui)
1227 sfree(ui); 1258 sfree(ui);
1228} 1259}
1229 1260
1230static char *encode_ui(const game_ui *ui) 1261static void game_changed_state(game_ui *ui, const game_state *oldstate,
1231{ 1262 const game_state *newstate)
1232 return NULL;
1233}
1234
1235static void decode_ui(game_ui *ui, const char *encoding)
1236{ 1263{
1237} 1264}
1238 1265
1239static void game_changed_state(game_ui *ui, const game_state *oldstate, 1266static const char *current_key_label(const game_ui *ui,
1240 const game_state *newstate) 1267 const game_state *state, int button)
1241{ 1268{
1269 if (IS_CURSOR_SELECT(button)) {
1270 if (!ui->cur_visible) return "";
1271 switch (state->grid[ui->cur_y * state->common->w + ui->cur_x]) {
1272 case GRID_UNKNOWN:
1273 return button == CURSOR_SELECT ? "Black" : "White";
1274 case GRID_FULL:
1275 return button == CURSOR_SELECT ? "White" : "Grey";
1276 case GRID_EMPTY:
1277 return button == CURSOR_SELECT ? "Grey" : "Black";
1278 }
1279 }
1280 return "";
1242} 1281}
1243 1282
1244struct game_drawstate { 1283struct game_drawstate {
@@ -1247,6 +1286,7 @@ struct game_drawstate {
1247 int tilesize; 1286 int tilesize;
1248 unsigned char *visible, *numcolours; 1287 unsigned char *visible, *numcolours;
1249 int cur_x, cur_y; 1288 int cur_x, cur_y;
1289 char *strbuf; /* Used for formatting clues. */
1250}; 1290};
1251 1291
1252static char *interpret_move(const game_state *state, game_ui *ui, 1292static char *interpret_move(const game_state *state, game_ui *ui,
@@ -1254,7 +1294,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1254 int x, int y, int button) 1294 int x, int y, int button)
1255{ 1295{
1256 bool control = button & MOD_CTRL, shift = button & MOD_SHFT; 1296 bool control = button & MOD_CTRL, shift = button & MOD_SHFT;
1257 button &= ~MOD_MASK; 1297 button = STRIP_BUTTON_MODIFIERS(button);
1258 1298
1259 x = FROMCOORD(state->common->w, x); 1299 x = FROMCOORD(state->common->w, x);
1260 y = FROMCOORD(state->common->h, y); 1300 y = FROMCOORD(state->common->h, y);
@@ -1294,7 +1334,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1294 ui->drag_start_y = ui->drag_end_y = y; 1334 ui->drag_start_y = ui->drag_end_y = y;
1295 ui->cur_visible = false; 1335 ui->cur_visible = false;
1296 1336
1297 return UI_UPDATE; 1337 return MOVE_UI_UPDATE;
1298 } 1338 }
1299 1339
1300 if (ui->dragging && button == ui->drag) { 1340 if (ui->dragging && button == ui->drag) {
@@ -1323,7 +1363,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1323 ui->drag_end_x = x; 1363 ui->drag_end_x = x;
1324 ui->drag_end_y = y; 1364 ui->drag_end_y = y;
1325 1365
1326 return UI_UPDATE; 1366 return MOVE_UI_UPDATE;
1327 } 1367 }
1328 1368
1329 if (ui->dragging && button == ui->release) { 1369 if (ui->dragging && button == ui->release) {
@@ -1351,20 +1391,21 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1351 x1, y1, x2-x1+1, y2-y1+1); 1391 x1, y1, x2-x1+1, y2-y1+1);
1352 return dupstr(buf); 1392 return dupstr(buf);
1353 } else 1393 } else
1354 return UI_UPDATE; 1394 return MOVE_UI_UPDATE;
1355 } 1395 }
1356 1396
1357 if (IS_CURSOR_MOVE(button)) { 1397 if (IS_CURSOR_MOVE(button)) {
1358 int x = ui->cur_x, y = ui->cur_y, newstate; 1398 int x = ui->cur_x, y = ui->cur_y, newstate;
1359 char buf[80]; 1399 char buf[80], *ret;
1360 move_cursor(button, &ui->cur_x, &ui->cur_y, state->common->w, state->common->h, false); 1400 ret = move_cursor(button, &ui->cur_x, &ui->cur_y,
1361 ui->cur_visible = true; 1401 state->common->w, state->common->h, false,
1362 if (!control && !shift) return UI_UPDATE; 1402 &ui->cur_visible);
1403 if (!control && !shift) return ret;
1363 1404
1364 newstate = control ? shift ? GRID_UNKNOWN : GRID_FULL : GRID_EMPTY; 1405 newstate = control ? shift ? GRID_UNKNOWN : GRID_FULL : GRID_EMPTY;
1365 if (state->grid[y * state->common->w + x] == newstate && 1406 if (state->grid[y * state->common->w + x] == newstate &&
1366 state->grid[ui->cur_y * state->common->w + ui->cur_x] == newstate) 1407 state->grid[ui->cur_y * state->common->w + ui->cur_x] == newstate)
1367 return UI_UPDATE; 1408 return ret;
1368 1409
1369 sprintf(buf, "%c%d,%d,%d,%d", control ? shift ? 'U' : 'F' : 'E', 1410 sprintf(buf, "%c%d,%d,%d,%d", control ? shift ? 'U' : 'F' : 'E',
1370 min(x, ui->cur_x), min(y, ui->cur_y), 1411 min(x, ui->cur_x), min(y, ui->cur_y),
@@ -1379,7 +1420,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1379 1420
1380 if (!ui->cur_visible) { 1421 if (!ui->cur_visible) {
1381 ui->cur_visible = true; 1422 ui->cur_visible = true;
1382 return UI_UPDATE; 1423 return MOVE_UI_UPDATE;
1383 } 1424 }
1384 1425
1385 if (button == CURSOR_SELECT2) 1426 if (button == CURSOR_SELECT2)
@@ -1637,7 +1678,7 @@ static bool check_errors(const game_state *state, int i)
1637 */ 1678 */
1638 1679
1639static void game_compute_size(const game_params *params, int tilesize, 1680static void game_compute_size(const game_params *params, int tilesize,
1640 int *x, int *y) 1681 const game_ui *ui, int *x, int *y)
1641{ 1682{
1642 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1683 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1643 struct { int tilesize; } ads, *ds = &ads; 1684 struct { int tilesize; } ads, *ds = &ads;
@@ -1692,6 +1733,8 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1692 ds->numcolours = snewn(ds->w + ds->h, unsigned char); 1733 ds->numcolours = snewn(ds->w + ds->h, unsigned char);
1693 memset(ds->numcolours, 255, ds->w + ds->h); 1734 memset(ds->numcolours, 255, ds->w + ds->h);
1694 ds->cur_x = ds->cur_y = 0; 1735 ds->cur_x = ds->cur_y = 0;
1736 ds->strbuf = snewn(state->common->rowsize *
1737 MAX_DIGITS(*state->common->rowdata) + 1, char);
1695 1738
1696 return ds; 1739 return ds;
1697} 1740}
@@ -1699,6 +1742,8 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1699static void game_free_drawstate(drawing *dr, game_drawstate *ds) 1742static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1700{ 1743{
1701 sfree(ds->visible); 1744 sfree(ds->visible);
1745 sfree(ds->numcolours);
1746 sfree(ds->strbuf);
1702 sfree(ds); 1747 sfree(ds);
1703} 1748}
1704 1749
@@ -1743,19 +1788,43 @@ static void draw_numbers(
1743 int *rowdata = state->common->rowdata + state->common->rowsize * i; 1788 int *rowdata = state->common->rowdata + state->common->rowsize * i;
1744 int nfit; 1789 int nfit;
1745 int j; 1790 int j;
1791 int rx, ry, rw, rh;
1792 int fontsize;
1746 1793
1747 if (erase) { 1794 if (i < state->common->w) {
1748 if (i < state->common->w) { 1795 rx = TOCOORD(state->common->w, i);
1749 draw_rect(dr, TOCOORD(state->common->w, i), 0, 1796 ry = 0;
1750 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE, 1797 rw = TILE_SIZE;
1751 COL_BACKGROUND); 1798 rh = BORDER + TLBORDER(state->common->h) * TILE_SIZE;
1752 } else { 1799 } else {
1753 draw_rect(dr, 0, TOCOORD(state->common->h, i - state->common->w), 1800 rx = 0;
1754 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE, 1801 ry = TOCOORD(state->common->h, i - state->common->w);
1755 COL_BACKGROUND); 1802 rw = BORDER + TLBORDER(state->common->w) * TILE_SIZE;
1756 } 1803 rh = TILE_SIZE;
1757 } 1804 }
1758 1805
1806 clip(dr, rx, ry, rw, rh);
1807 if (erase)
1808 draw_rect(dr, rx, ry, rw, rh, COL_BACKGROUND);
1809
1810 /*
1811 * Choose a font size that's suitable for the lengths of clue.
1812 * Only column clues are interesting because row clues can be
1813 * spaced out independent of the tile size. For column clues, we
1814 * want to go as large as practical while leaving decent space
1815 * between horizintally adjacent clues. We currently distinguish
1816 * two cases: FS_LARGE is when all column clues are single digits,
1817 * and FS_SMALL in all other cases.
1818 *
1819 * If we assume that a digit is about 0.6em wide, and we want
1820 * about that space between clues, then FS_LARGE should be
1821 * TILESIZE/1.2. If we also assume that clues are at most two
1822 * digits long then the case where adjacent clues are two digits
1823 * long requries FS_SMALL to be TILESIZE/1.8.
1824 */
1825 fontsize = (TILE_SIZE + 0.5F) /
1826 (state->common->fontsize == FS_LARGE ? 1.2F : 1.8F);
1827
1759 /* 1828 /*
1760 * Normally I space the numbers out by the same distance as the 1829 * Normally I space the numbers out by the same distance as the
1761 * tile size. However, if there are more numbers than available 1830 * tile size. However, if there are more numbers than available
@@ -1768,32 +1837,38 @@ static void draw_numbers(
1768 nfit = max(rowlen, nfit) - 1; 1837 nfit = max(rowlen, nfit) - 1;
1769 assert(nfit > 0); 1838 assert(nfit > 0);
1770 1839
1771 for (j = 0; j < rowlen; j++) { 1840 if (i < state->common->w) {
1772 int x, y; 1841 for (j = 0; j < rowlen; j++) {
1773 char str[80]; 1842 int x, y;
1843 char str[MAX_DIGITS(*rowdata) + 1];
1774 1844
1775 if (i < state->common->w) { 1845 x = rx;
1776 x = TOCOORD(state->common->w, i);
1777 y = BORDER + TILE_SIZE * (TLBORDER(state->common->h)-1); 1846 y = BORDER + TILE_SIZE * (TLBORDER(state->common->h)-1);
1778 y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->h)-1) / nfit; 1847 y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->h)-1) / nfit;
1779 } else { 1848 sprintf(str, "%d", rowdata[j]);
1780 y = TOCOORD(state->common->h, i - state->common->w); 1849 draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE,
1781 x = BORDER + TILE_SIZE * (TLBORDER(state->common->w)-1); 1850 fontsize, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str);
1782 x -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->w)-1) / nfit;
1783 } 1851 }
1784
1785 sprintf(str, "%d", rowdata[j]);
1786 draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE,
1787 TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str);
1788 }
1789
1790 if (i < state->common->w) {
1791 draw_update(dr, TOCOORD(state->common->w, i), 0,
1792 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE);
1793 } else { 1852 } else {
1794 draw_update(dr, 0, TOCOORD(state->common->h, i - state->common->w), 1853 int x, y;
1795 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE); 1854 size_t off = 0;
1855 const char *spaces = " ";
1856
1857 assert(rowlen <= state->common->rowsize);
1858 *ds->strbuf = '\0';
1859 /* Squish up a bit if there are lots of clues. */
1860 if (rowlen > TLBORDER(state->common->w)) spaces++;
1861 for (j = 0; j < rowlen; j++)
1862 off += sprintf(ds->strbuf + off, "%s%d",
1863 j ? spaces : "", rowdata[j]);
1864 y = ry;
1865 x = BORDER + TILE_SIZE * (TLBORDER(state->common->w)-1);
1866 draw_text(dr, x+TILE_SIZE, y+TILE_SIZE/2, FONT_VARIABLE,
1867 fontsize, ALIGN_HRIGHT | ALIGN_VCENTRE, colour, ds->strbuf);
1796 } 1868 }
1869
1870 unclip(dr);
1871 draw_update(dr, rx, ry, rw, rh);
1797} 1872}
1798 1873
1799static void game_redraw(drawing *dr, game_drawstate *ds, 1874static void game_redraw(drawing *dr, game_drawstate *ds,
@@ -1808,14 +1883,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1808 1883
1809 if (!ds->started) { 1884 if (!ds->started) {
1810 /* 1885 /*
1811 * The initial contents of the window are not guaranteed
1812 * and can vary with front ends. To be on the safe side,
1813 * all games should start by drawing a big background-
1814 * colour rectangle covering the whole window.
1815 */
1816 draw_rect(dr, 0, 0, SIZE(ds->w), SIZE(ds->h), COL_BACKGROUND);
1817
1818 /*
1819 * Draw the grid outline. 1886 * Draw the grid outline.
1820 */ 1887 */
1821 draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1, 1888 draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1,
@@ -1936,24 +2003,21 @@ static int game_status(const game_state *state)
1936 return state->completed ? +1 : 0; 2003 return state->completed ? +1 : 0;
1937} 2004}
1938 2005
1939static bool game_timing_state(const game_state *state, game_ui *ui) 2006static void game_print_size(const game_params *params, const game_ui *ui,
1940{ 2007 float *x, float *y)
1941 return true;
1942}
1943
1944static void game_print_size(const game_params *params, float *x, float *y)
1945{ 2008{
1946 int pw, ph; 2009 int pw, ph;
1947 2010
1948 /* 2011 /*
1949 * I'll use 5mm squares by default. 2012 * I'll use 5mm squares by default.
1950 */ 2013 */
1951 game_compute_size(params, 500, &pw, &ph); 2014 game_compute_size(params, 500, ui, &pw, &ph);
1952 *x = pw / 100.0F; 2015 *x = pw / 100.0F;
1953 *y = ph / 100.0F; 2016 *y = ph / 100.0F;
1954} 2017}
1955 2018
1956static void game_print(drawing *dr, const game_state *state, int tilesize) 2019static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2020 int tilesize)
1957{ 2021{
1958 int w = state->common->w, h = state->common->h; 2022 int w = state->common->w, h = state->common->h;
1959 int ink = print_mono_colour(dr, 0); 2023 int ink = print_mono_colour(dr, 0);
@@ -2027,12 +2091,14 @@ const struct game thegame = {
2027 free_game, 2091 free_game,
2028 true, solve_game, 2092 true, solve_game,
2029 true, game_can_format_as_text_now, game_text_format, 2093 true, game_can_format_as_text_now, game_text_format,
2094 NULL, NULL, /* get_prefs, set_prefs */
2030 new_ui, 2095 new_ui,
2031 free_ui, 2096 free_ui,
2032 encode_ui, 2097 NULL, /* encode_ui */
2033 decode_ui, 2098 NULL, /* decode_ui */
2034 NULL, /* game_request_keys */ 2099 NULL, /* game_request_keys */
2035 game_changed_state, 2100 game_changed_state,
2101 current_key_label,
2036 interpret_move, 2102 interpret_move,
2037 execute_move, 2103 execute_move,
2038 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2104 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2046,7 +2112,7 @@ const struct game thegame = {
2046 game_status, 2112 game_status,
2047 true, false, game_print_size, game_print, 2113 true, false, game_print_size, game_print,
2048 false, /* wants_statusbar */ 2114 false, /* wants_statusbar */
2049 false, game_timing_state, 2115 false, NULL, /* timing_state */
2050 REQUIRE_RBUTTON, /* flags */ 2116 REQUIRE_RBUTTON, /* flags */
2051}; 2117};
2052 2118
diff --git a/apps/plugins/puzzles/src/pearl.R b/apps/plugins/puzzles/src/pearl.R
deleted file mode 100644
index 79bc7325c7..0000000000
--- a/apps/plugins/puzzles/src/pearl.R
+++ /dev/null
@@ -1,23 +0,0 @@
1# -*- makefile -*-
2
3PEARL_EXTRA = dsf tree234 grid penrose loopgen tdq
4
5pearl : [X] GTK COMMON pearl PEARL_EXTRA pearl-icon|no-icon
6pearl : [G] WINDOWS COMMON pearl PEARL_EXTRA pearl.res?
7
8pearlbench : [U] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE m.lib
9pearlbench : [C] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE
10
11ALL += pearl[COMBINED] PEARL_EXTRA
12
13!begin am gtk
14GAMES += pearl
15!end
16
17!begin >list.c
18 A(pearl) \
19!end
20
21!begin >gamedesc.txt
22pearl:pearl.exe:Pearl:Loop-drawing puzzle:Draw a single closed loop, given clues about corner and straight squares.
23!end
diff --git a/apps/plugins/puzzles/src/pearl.c b/apps/plugins/puzzles/src/pearl.c
index 2657d45a67..95993172ca 100644
--- a/apps/plugins/puzzles/src/pearl.c
+++ b/apps/plugins/puzzles/src/pearl.c
@@ -35,7 +35,12 @@
35#include <string.h> 35#include <string.h>
36#include <assert.h> 36#include <assert.h>
37#include <ctype.h> 37#include <ctype.h>
38#include <math.h> 38#include <limits.h>
39#ifdef NO_TGMATH_H
40# include <math.h>
41#else
42# include <tgmath.h>
43#endif
39 44
40#include "puzzles.h" 45#include "puzzles.h"
41#include "grid.h" 46#include "grid.h"
@@ -272,8 +277,12 @@ static const char *validate_params(const game_params *params, bool full)
272{ 277{
273 if (params->w < 5) return "Width must be at least five"; 278 if (params->w < 5) return "Width must be at least five";
274 if (params->h < 5) return "Height must be at least five"; 279 if (params->h < 5) return "Height must be at least five";
280 if (params->w > INT_MAX / params->h)
281 return "Width times height must not be unreasonably large";
275 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT) 282 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT)
276 return "Unknown difficulty level"; 283 return "Unknown difficulty level";
284 if (params->difficulty >= DIFF_TRICKY && params->w + params->h < 11)
285 return "Width or height must be at least six for Tricky";
277 286
278 return NULL; 287 return NULL;
279} 288}
@@ -287,7 +296,8 @@ static int pearl_solve(int w, int h, char *clues, char *result,
287{ 296{
288 int W = 2*w+1, H = 2*h+1; 297 int W = 2*w+1, H = 2*h+1;
289 short *workspace; 298 short *workspace;
290 int *dsf, *dsfsize; 299 DSF *dsf;
300 int *dsfsize;
291 int x, y, b, d; 301 int x, y, b, d;
292 int ret = -1; 302 int ret = -1;
293 303
@@ -340,7 +350,7 @@ static int pearl_solve(int w, int h, char *clues, char *result,
340 * We maintain a dsf of connected squares, together with a 350 * We maintain a dsf of connected squares, together with a
341 * count of the size of each equivalence class. 351 * count of the size of each equivalence class.
342 */ 352 */
343 dsf = snewn(w*h, int); 353 dsf = dsf_new(w*h);
344 dsfsize = snewn(w*h, int); 354 dsfsize = snewn(w*h, int);
345 355
346 /* 356 /*
@@ -583,7 +593,7 @@ static int pearl_solve(int w, int h, char *clues, char *result,
583 { 593 {
584 int nonblanks, loopclass; 594 int nonblanks, loopclass;
585 595
586 dsf_init(dsf, w*h); 596 dsf_reinit(dsf);
587 for (x = 0; x < w*h; x++) 597 for (x = 0; x < w*h; x++)
588 dsfsize[x] = 1; 598 dsfsize[x] = 1;
589 599
@@ -850,10 +860,39 @@ cleanup:
850 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */ 860 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */
851 } 861 }
852 } 862 }
863
864 /*
865 * Ensure we haven't left the _data structure_ inconsistent,
866 * regardless of the consistency of the _puzzle_. In
867 * particular, we should never have marked one square as
868 * linked to its neighbour if the neighbour is not
869 * reciprocally linked back to the original square.
870 *
871 * This can happen if we get part way through solving an
872 * impossible puzzle and then give up trying to make further
873 * progress. So here we fix it up to avoid confusing the rest
874 * of the game.
875 */
876 for (y = 0; y < h; y++) {
877 for (x = 0; x < w; x++) {
878 for (d = 1; d <= 8; d += d) {
879 int nx = x + DX(d), ny = y + DY(d);
880 int rlink;
881 if (0 <= nx && nx < w && 0 <= ny && ny < h)
882 rlink = result[ny*w+nx] & F(d);
883 else
884 rlink = 0; /* off-board squares don't link back */
885
886 /* If other square doesn't link to us, don't link to it */
887 if (!rlink)
888 result[y*w+x] &= ~d;
889 }
890 }
891 }
853 } 892 }
854 893
855 sfree(dsfsize); 894 sfree(dsfsize);
856 sfree(dsf); 895 dsf_free(dsf);
857 sfree(workspace); 896 sfree(workspace);
858 assert(ret >= 0); 897 assert(ret >= 0);
859 return ret; 898 return ret;
@@ -941,9 +980,9 @@ static int pearl_loopgen_bias(void *vctx, char *board, int face)
941 * to reprocess the edges for this boundary. 980 * to reprocess the edges for this boundary.
942 */ 981 */
943 if (oldface == c || newface == c) { 982 if (oldface == c || newface == c) {
944 grid_face *f = &g->faces[face]; 983 grid_face *f = g->faces[face];
945 for (k = 0; k < f->order; k++) 984 for (k = 0; k < f->order; k++)
946 tdq_add(b->edges_todo, f->edges[k] - g->edges); 985 tdq_add(b->edges_todo, f->edges[k]->index);
947 } 986 }
948 } 987 }
949 } 988 }
@@ -959,15 +998,15 @@ static int pearl_loopgen_bias(void *vctx, char *board, int face)
959 * the vertextypes_todo list. 998 * the vertextypes_todo list.
960 */ 999 */
961 while ((j = tdq_remove(b->edges_todo)) >= 0) { 1000 while ((j = tdq_remove(b->edges_todo)) >= 0) {
962 grid_edge *e = &g->edges[j]; 1001 grid_edge *e = g->edges[j];
963 int fc1 = e->face1 ? board[e->face1 - g->faces] : FACE_BLACK; 1002 int fc1 = e->face1 ? board[e->face1->index] : FACE_BLACK;
964 int fc2 = e->face2 ? board[e->face2 - g->faces] : FACE_BLACK; 1003 int fc2 = e->face2 ? board[e->face2->index] : FACE_BLACK;
965 bool oldedge = b->edges[j]; 1004 bool oldedge = b->edges[j];
966 bool newedge = (fc1==c) ^ (fc2==c); 1005 bool newedge = (fc1==c) ^ (fc2==c);
967 if (oldedge != newedge) { 1006 if (oldedge != newedge) {
968 b->edges[j] = newedge; 1007 b->edges[j] = newedge;
969 tdq_add(b->vertextypes_todo, e->dot1 - g->dots); 1008 tdq_add(b->vertextypes_todo, e->dot1->index);
970 tdq_add(b->vertextypes_todo, e->dot2 - g->dots); 1009 tdq_add(b->vertextypes_todo, e->dot2->index);
971 } 1010 }
972 } 1011 }
973 1012
@@ -982,7 +1021,7 @@ static int pearl_loopgen_bias(void *vctx, char *board, int face)
982 * old neighbours. 1021 * old neighbours.
983 */ 1022 */
984 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) { 1023 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) {
985 grid_dot *d = &g->dots[j]; 1024 grid_dot *d = g->dots[j];
986 int neighbours[2], type = 0, n = 0; 1025 int neighbours[2], type = 0, n = 0;
987 1026
988 for (k = 0; k < d->order; k++) { 1027 for (k = 0; k < d->order; k++) {
@@ -990,10 +1029,10 @@ static int pearl_loopgen_bias(void *vctx, char *board, int face)
990 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1); 1029 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1);
991 /* dir == 0,1,2,3 for an edge going L,U,R,D */ 1030 /* dir == 0,1,2,3 for an edge going L,U,R,D */
992 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y); 1031 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y);
993 int ei = e - g->edges; 1032 int ei = e->index;
994 if (b->edges[ei]) { 1033 if (b->edges[ei]) {
995 type |= 1 << dir; 1034 type |= 1 << dir;
996 neighbours[n] = d2 - g->dots; 1035 neighbours[n] = d2->index;
997 n++; 1036 n++;
998 } 1037 }
999 } 1038 }
@@ -1048,9 +1087,8 @@ static int pearl_loopgen_bias(void *vctx, char *board, int face)
1048 return ctx->score; 1087 return ctx->score;
1049} 1088}
1050 1089
1051static void pearl_loopgen(int w, int h, char *lines, random_state *rs) 1090static void pearl_loopgen(int w, int h, char *lines, random_state *rs, grid *g)
1052{ 1091{
1053 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL);
1054 char *board = snewn(g->num_faces, char); 1092 char *board = snewn(g->num_faces, char);
1055 int i, s = g->tilesize; 1093 int i, s = g->tilesize;
1056 struct pearl_loopgen_bias_ctx biasctx; 1094 struct pearl_loopgen_bias_ctx biasctx;
@@ -1102,7 +1140,7 @@ static void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1102 } 1140 }
1103 1141
1104 for (i = 0; i < g->num_edges; i++) { 1142 for (i = 0; i < g->num_edges; i++) {
1105 grid_edge *e = g->edges + i; 1143 grid_edge *e = g->edges[i];
1106 enum face_colour c1 = FACE_COLOUR(e->face1); 1144 enum face_colour c1 = FACE_COLOUR(e->face1);
1107 enum face_colour c2 = FACE_COLOUR(e->face2); 1145 enum face_colour c2 = FACE_COLOUR(e->face2);
1108 assert(c1 != FACE_GREY); 1146 assert(c1 != FACE_GREY);
@@ -1130,7 +1168,6 @@ static void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1130 } 1168 }
1131 } 1169 }
1132 1170
1133 grid_free(g);
1134 sfree(board); 1171 sfree(board);
1135 1172
1136#if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS 1173#if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS
@@ -1153,11 +1190,11 @@ static void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1153} 1190}
1154 1191
1155static int new_clues(const game_params *params, random_state *rs, 1192static int new_clues(const game_params *params, random_state *rs,
1156 char *clues, char *grid) 1193 char *clues, char *grid_out)
1157{ 1194{
1158 int w = params->w, h = params->h, diff = params->difficulty; 1195 int w = params->w, h = params->h, diff = params->difficulty;
1159 int ngen = 0, x, y, d, ret, i; 1196 int ngen = 0, x, y, d, ret, i;
1160 1197 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL);
1161 1198
1162 /* 1199 /*
1163 * Difficulty exception: 5x5 Tricky is not generable (the 1200 * Difficulty exception: 5x5 Tricky is not generable (the
@@ -1168,13 +1205,13 @@ static int new_clues(const game_params *params, random_state *rs,
1168 1205
1169 while (1) { 1206 while (1) {
1170 ngen++; 1207 ngen++;
1171 pearl_loopgen(w, h, grid, rs); 1208 pearl_loopgen(w, h, grid_out, rs, g);
1172 1209
1173#ifdef GENERATION_DIAGNOSTICS 1210#ifdef GENERATION_DIAGNOSTICS
1174 printf("grid array:\n"); 1211 printf("grid array:\n");
1175 for (y = 0; y < h; y++) { 1212 for (y = 0; y < h; y++) {
1176 for (x = 0; x < w; x++) { 1213 for (x = 0; x < w; x++) {
1177 int type = grid[y*w+x]; 1214 int type = grid_out[y*w+x];
1178 char s[5], *p = s; 1215 char s[5], *p = s;
1179 if (type & L) *p++ = 'L'; 1216 if (type & L) *p++ = 'L';
1180 if (type & R) *p++ = 'R'; 1217 if (type & R) *p++ = 'R';
@@ -1193,7 +1230,7 @@ static int new_clues(const game_params *params, random_state *rs,
1193 */ 1230 */
1194 for (y = 0; y < h; y++) 1231 for (y = 0; y < h; y++)
1195 for (x = 0; x < w; x++) { 1232 for (x = 0; x < w; x++) {
1196 int type = grid[y*w+x]; 1233 int type = grid_out[y*w+x];
1197 1234
1198 clues[y*w+x] = NOCLUE; 1235 clues[y*w+x] = NOCLUE;
1199 1236
@@ -1207,7 +1244,7 @@ static int new_clues(const game_params *params, random_state *rs,
1207 for (d = 1; d <= 8; d += d) if (type & d) { 1244 for (d = 1; d <= 8; d += d) if (type & d) {
1208 int xx = x + DX(d), yy = y + DY(d); 1245 int xx = x + DX(d), yy = y + DY(d);
1209 assert(xx >= 0 && xx < w && yy >= 0 && yy < h); 1246 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1210 if ((bLU|bLD|bRU|bRD) & (1 << grid[yy*w+xx])) 1247 if ((bLU|bLD|bRU|bRD) & (1 << grid_out[yy*w+xx]))
1211 break; 1248 break;
1212 } 1249 }
1213 if (d <= 8) /* we found one */ 1250 if (d <= 8) /* we found one */
@@ -1221,7 +1258,7 @@ static int new_clues(const game_params *params, random_state *rs,
1221 for (d = 1; d <= 8; d += d) if (type & d) { 1258 for (d = 1; d <= 8; d += d) if (type & d) {
1222 int xx = x + DX(d), yy = y + DY(d); 1259 int xx = x + DX(d), yy = y + DY(d);
1223 assert(xx >= 0 && xx < w && yy >= 0 && yy < h); 1260 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1224 if (!((bLR|bUD) & (1 << grid[yy*w+xx]))) 1261 if (!((bLR|bUD) & (1 << grid_out[yy*w+xx])))
1225 break; 1262 break;
1226 } 1263 }
1227 if (d > 8) /* we didn't find a counterexample */ 1264 if (d > 8) /* we didn't find a counterexample */
@@ -1247,7 +1284,7 @@ static int new_clues(const game_params *params, random_state *rs,
1247 /* 1284 /*
1248 * See if we can solve the puzzle just like this. 1285 * See if we can solve the puzzle just like this.
1249 */ 1286 */
1250 ret = pearl_solve(w, h, clues, grid, diff, false); 1287 ret = pearl_solve(w, h, clues, grid_out, diff, false);
1251 assert(ret > 0); /* shouldn't be inconsistent! */ 1288 assert(ret > 0); /* shouldn't be inconsistent! */
1252 if (ret != 1) 1289 if (ret != 1)
1253 continue; /* go round and try again */ 1290 continue; /* go round and try again */
@@ -1256,7 +1293,7 @@ static int new_clues(const game_params *params, random_state *rs,
1256 * Check this puzzle isn't too easy. 1293 * Check this puzzle isn't too easy.
1257 */ 1294 */
1258 if (diff > DIFF_EASY) { 1295 if (diff > DIFF_EASY) {
1259 ret = pearl_solve(w, h, clues, grid, diff-1, false); 1296 ret = pearl_solve(w, h, clues, grid_out, diff-1, false);
1260 assert(ret > 0); 1297 assert(ret > 0);
1261 if (ret == 1) 1298 if (ret == 1)
1262 continue; /* too easy: try again */ 1299 continue; /* too easy: try again */
@@ -1323,7 +1360,7 @@ static int new_clues(const game_params *params, random_state *rs,
1323 clue = clues[y*w+x]; 1360 clue = clues[y*w+x];
1324 clues[y*w+x] = 0; /* try removing this clue */ 1361 clues[y*w+x] = 0; /* try removing this clue */
1325 1362
1326 ret = pearl_solve(w, h, clues, grid, diff, false); 1363 ret = pearl_solve(w, h, clues, grid_out, diff, false);
1327 assert(ret > 0); 1364 assert(ret > 0);
1328 if (ret != 1) 1365 if (ret != 1)
1329 clues[y*w+x] = clue; /* oops, put it back again */ 1366 clues[y*w+x] = clue; /* oops, put it back again */
@@ -1344,6 +1381,7 @@ static int new_clues(const game_params *params, random_state *rs,
1344 1381
1345 break; /* got it */ 1382 break; /* got it */
1346 } 1383 }
1384 grid_free(g);
1347 1385
1348 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h)); 1386 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
1349 1387
@@ -1491,29 +1529,35 @@ static char nbits[16] = { 0, 1, 1, 2,
1491 1529
1492#define ERROR_CLUE 16 1530#define ERROR_CLUE 16
1493 1531
1494static void dsf_update_completion(game_state *state, int ax, int ay, char dir, 1532/* Returns false if the state is invalid. */
1495 int *dsf) 1533static bool dsf_update_completion(game_state *state, int ax, int ay, char dir,
1534 DSF *dsf)
1496{ 1535{
1497 int w = state->shared->w /*, h = state->shared->h */; 1536 int w = state->shared->w /*, h = state->shared->h */;
1498 int ac = ay*w+ax, bx, by, bc; 1537 int ac = ay*w+ax, bx, by, bc;
1499 1538
1500 if (!(state->lines[ac] & dir)) return; /* no link */ 1539 if (!(state->lines[ac] & dir)) return true; /* no link */
1501 bx = ax + DX(dir); by = ay + DY(dir); 1540 bx = ax + DX(dir); by = ay + DY(dir);
1502 1541
1503 assert(INGRID(state, bx, by)); /* should not have a link off grid */ 1542 if (!INGRID(state, bx, by))
1543 return false; /* should not have a link off grid */
1504 1544
1505 bc = by*w+bx; 1545 bc = by*w+bx;
1506 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */ 1546 if (!(state->lines[bc] & F(dir)))
1507 if (!(state->lines[bc] & F(dir))) return; 1547 return false; /* should have reciprocal link */
1548 if (!(state->lines[bc] & F(dir))) return true;
1508 1549
1509 dsf_merge(dsf, ac, bc); 1550 dsf_merge(dsf, ac, bc);
1551 return true;
1510} 1552}
1511 1553
1512static void check_completion(game_state *state, bool mark) 1554/* Returns false if the state is invalid. */
1555static bool check_completion(game_state *state, bool mark)
1513{ 1556{
1514 int w = state->shared->w, h = state->shared->h, x, y, i, d; 1557 int w = state->shared->w, h = state->shared->h, x, y, i, d;
1515 bool had_error = false; 1558 bool had_error = false;
1516 int *dsf, *component_state; 1559 DSF *dsf;
1560 int *component_state;
1517 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize; 1561 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1518 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY }; 1562 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1519 1563
@@ -1532,13 +1576,16 @@ static void check_completion(game_state *state, bool mark)
1532 * same reasons, since Loopy and Pearl have basically the same 1576 * same reasons, since Loopy and Pearl have basically the same
1533 * form of expected solution. 1577 * form of expected solution.
1534 */ 1578 */
1535 dsf = snew_dsf(w*h); 1579 dsf = dsf_new(w*h);
1536 1580
1537 /* Build the dsf. */ 1581 /* Build the dsf. */
1538 for (x = 0; x < w; x++) { 1582 for (x = 0; x < w; x++) {
1539 for (y = 0; y < h; y++) { 1583 for (y = 0; y < h; y++) {
1540 dsf_update_completion(state, x, y, R, dsf); 1584 if (!dsf_update_completion(state, x, y, R, dsf) ||
1541 dsf_update_completion(state, x, y, D, dsf); 1585 !dsf_update_completion(state, x, y, D, dsf)) {
1586 dsf_free(dsf);
1587 return false;
1588 }
1542 } 1589 }
1543 } 1590 }
1544 1591
@@ -1619,7 +1666,7 @@ static void check_completion(game_state *state, bool mark)
1619 * part of a single loop, for which our counter variables 1666 * part of a single loop, for which our counter variables
1620 * nsilly,nloop,npath are enough. */ 1667 * nsilly,nloop,npath are enough. */
1621 sfree(component_state); 1668 sfree(component_state);
1622 sfree(dsf); 1669 dsf_free(dsf);
1623 1670
1624 /* 1671 /*
1625 * Check that no clues are contradicted. This code is similar to 1672 * Check that no clues are contradicted. This code is similar to
@@ -1693,6 +1740,7 @@ static void check_completion(game_state *state, bool mark)
1693 if (!had_error) 1740 if (!had_error)
1694 state->completed = true; 1741 state->completed = true;
1695 } 1742 }
1743 return true;
1696} 1744}
1697 1745
1698/* completion check: 1746/* completion check:
@@ -1810,18 +1858,53 @@ struct game_ui {
1810 1858
1811 int curx, cury; /* grid position of keyboard cursor */ 1859 int curx, cury; /* grid position of keyboard cursor */
1812 bool cursor_active; /* true iff cursor is shown */ 1860 bool cursor_active; /* true iff cursor is shown */
1861
1862 /*
1863 * User preference: general visual style of the GUI. GUI_MASYU is
1864 * how this puzzle is traditionally presented, with clue dots in
1865 * the middle of grid squares, and the solution loop connecting
1866 * square-centres. GUI_LOOPY shifts the grid by half a square in
1867 * each direction, so that the clue dots are at _vertices_ of the
1868 * grid and the solution loop follows the grid edges, which you
1869 * could argue is more logical.
1870 */
1871 enum { GUI_MASYU, GUI_LOOPY } gui_style;
1813}; 1872};
1814 1873
1874static void legacy_prefs_override(struct game_ui *ui_out)
1875{
1876 static bool initialised = false;
1877 static int gui_style = -1;
1878
1879 if (!initialised) {
1880 initialised = true;
1881
1882 switch (getenv_bool("PEARL_GUI_LOOPY", -1)) {
1883 case 0:
1884 gui_style = GUI_MASYU;
1885 break;
1886 case 1:
1887 gui_style = GUI_LOOPY;
1888 break;
1889 }
1890 }
1891
1892 if (gui_style != -1)
1893 ui_out->gui_style = gui_style;
1894}
1895
1815static game_ui *new_ui(const game_state *state) 1896static game_ui *new_ui(const game_state *state)
1816{ 1897{
1817 game_ui *ui = snew(game_ui); 1898 game_ui *ui = snew(game_ui);
1818 int sz = state->shared->sz;
1819 1899
1820 ui->ndragcoords = -1; 1900 ui->ndragcoords = -1;
1821 ui->dragcoords = snewn(sz, int); 1901 ui->dragcoords = state ? snewn(state->shared->sz, int) : NULL;
1822 ui->cursor_active = false; 1902 ui->cursor_active = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1823 ui->curx = ui->cury = 0; 1903 ui->curx = ui->cury = 0;
1824 1904
1905 ui->gui_style = GUI_MASYU;
1906 legacy_prefs_override(ui);
1907
1825 return ui; 1908 return ui;
1826} 1909}
1827 1910
@@ -1831,13 +1914,28 @@ static void free_ui(game_ui *ui)
1831 sfree(ui); 1914 sfree(ui);
1832} 1915}
1833 1916
1834static char *encode_ui(const game_ui *ui) 1917static config_item *get_prefs(game_ui *ui)
1835{ 1918{
1836 return NULL; 1919 config_item *ret;
1920
1921 ret = snewn(2, config_item);
1922
1923 ret[0].name = "Puzzle appearance";
1924 ret[0].kw = "appearance";
1925 ret[0].type = C_CHOICES;
1926 ret[0].u.choices.choicenames = ":Traditional:Loopy-style";
1927 ret[0].u.choices.choicekws = ":traditional:loopy";
1928 ret[0].u.choices.selected = ui->gui_style;
1929
1930 ret[1].name = NULL;
1931 ret[1].type = C_END;
1932
1933 return ret;
1837} 1934}
1838 1935
1839static void decode_ui(game_ui *ui, const char *encoding) 1936static void set_prefs(game_ui *ui, const config_item *cfg)
1840{ 1937{
1938 ui->gui_style = cfg[0].u.choices.selected;
1841} 1939}
1842 1940
1843static void game_changed_state(game_ui *ui, const game_state *oldstate, 1941static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1845,11 +1943,25 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1845{ 1943{
1846} 1944}
1847 1945
1946static const char *current_key_label(const game_ui *ui,
1947 const game_state *state, int button)
1948{
1949 if (IS_CURSOR_SELECT(button) && ui->cursor_active) {
1950 if (button == CURSOR_SELECT) {
1951 if (ui->ndragcoords == -1) return "Start";
1952 return "Stop";
1953 }
1954 if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0)
1955 return "Cancel";
1956 }
1957 return "";
1958}
1959
1848#define PREFERRED_TILE_SIZE 31 1960#define PREFERRED_TILE_SIZE 31
1849#define HALFSZ (ds->halfsz) 1961#define HALFSZ (ds->halfsz)
1850#define TILE_SIZE (ds->halfsz*2 + 1) 1962#define TILE_SIZE (ds->halfsz*2 + 1)
1851 1963
1852#define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2)) 1964#define BORDER ((ui->gui_style == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
1853 1965
1854#define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) 1966#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1855 1967
@@ -1865,22 +1977,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1865#define DS_FLASH (1 << 21) 1977#define DS_FLASH (1 << 21)
1866#define DS_CURSOR (1 << 22) 1978#define DS_CURSOR (1 << 22)
1867 1979
1868enum { GUI_MASYU, GUI_LOOPY };
1869
1870static int get_gui_style(void)
1871{
1872 static int gui_style = -1;
1873
1874 if (gui_style == -1) {
1875 char *env = getenv("PEARL_GUI_LOOPY");
1876 if (env && (env[0] == 'y' || env[0] == 'Y'))
1877 gui_style = GUI_LOOPY;
1878 else
1879 gui_style = GUI_MASYU;
1880 }
1881 return gui_style;
1882}
1883
1884struct game_drawstate { 1980struct game_drawstate {
1885 int halfsz; 1981 int halfsz;
1886 bool started; 1982 bool started;
@@ -1891,6 +1987,56 @@ struct game_drawstate {
1891 char *draglines; /* size w*h; lines flipped by current drag */ 1987 char *draglines; /* size w*h; lines flipped by current drag */
1892}; 1988};
1893 1989
1990/*
1991 * Routine shared between multiple callers to work out the intended
1992 * effect of a drag path on the grid.
1993 *
1994 * Call it in a loop, like this:
1995 *
1996 * bool clearing = true;
1997 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1998 * int sx, sy, dx, dy, dir, oldstate, newstate;
1999 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2000 * &dir, &oldstate, &newstate);
2001 *
2002 * [do whatever is needed to handle the fact that the drag
2003 * wants the edge from sx,sy to dx,dy (heading in direction
2004 * 'dir' at the sx,sy end) to be changed from state oldstate
2005 * to state newstate, each of which equals either 0 or dir]
2006 * }
2007 */
2008static void interpret_ui_drag(const game_state *state, const game_ui *ui,
2009 bool *clearing, int i, int *sx, int *sy,
2010 int *dx, int *dy, int *dir,
2011 int *oldstate, int *newstate)
2012{
2013 int w = state->shared->w;
2014 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
2015 *sy = sp/w;
2016 *sx = sp%w;
2017 *dy = dp/w;
2018 *dx = dp%w;
2019 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
2020 *oldstate = state->lines[sp] & *dir;
2021 if (*oldstate) {
2022 /*
2023 * The edge we've dragged over was previously
2024 * present. Set it to absent, unless we've already
2025 * stopped doing that.
2026 */
2027 *newstate = *clearing ? 0 : *dir;
2028 } else {
2029 /*
2030 * The edge we've dragged over was previously
2031 * absent. Set it to present, and cancel the
2032 * 'clearing' flag so that all subsequent edges in
2033 * the drag are set rather than cleared.
2034 */
2035 *newstate = *dir;
2036 *clearing = false;
2037 }
2038}
2039
1894static void update_ui_drag(const game_state *state, game_ui *ui, 2040static void update_ui_drag(const game_state *state, game_ui *ui,
1895 int gx, int gy) 2041 int gx, int gy)
1896{ 2042{
@@ -1919,13 +2065,42 @@ static void update_ui_drag(const game_state *state, game_ui *ui,
1919 * the drag path so far has the effect of truncating the path back 2065 * the drag path so far has the effect of truncating the path back
1920 * to that square, so a player can back out part of an uncommitted 2066 * to that square, so a player can back out part of an uncommitted
1921 * drag without having to let go of the mouse. 2067 * drag without having to let go of the mouse.
2068 *
2069 * An exception is that you're allowed to drag round in a loop
2070 * back to the very start of the drag, provided that doesn't
2071 * create a vertex of the wrong degree. This allows a player who's
2072 * after an extra challenge to draw the entire loop in a single
2073 * drag, without it cancelling itself just before release.
1922 */ 2074 */
1923 for (i = 0; i < ui->ndragcoords; i++) 2075 for (i = 1; i < ui->ndragcoords; i++)
1924 if (pos == ui->dragcoords[i]) { 2076 if (pos == ui->dragcoords[i]) {
1925 ui->ndragcoords = i+1; 2077 ui->ndragcoords = i+1;
1926 return; 2078 return;
1927 } 2079 }
1928 2080
2081 if (pos == ui->dragcoords[0]) {
2082 /* More complex check for a loop-shaped drag, which has to go
2083 * through interpret_ui_drag to decide on the final degree of
2084 * the start/end vertex. */
2085 ui->dragcoords[ui->ndragcoords] = pos;
2086 bool clearing = true;
2087 int lines = state->lines[pos] & (L|R|U|D);
2088 for (i = 0; i < ui->ndragcoords; i++) {
2089 int sx, sy, dx, dy, dir, oldstate, newstate;
2090 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2091 &dir, &oldstate, &newstate);
2092 if (sx == gx && sy == gy)
2093 lines ^= (oldstate ^ newstate);
2094 if (dx == gx && dy == gy)
2095 lines ^= (F(oldstate) ^ F(newstate));
2096 }
2097 if (NBITS(lines) > 2) {
2098 /* Bad vertex degree: fall back to the backtracking behaviour. */
2099 ui->ndragcoords = 1;
2100 return;
2101 }
2102 }
2103
1929 /* 2104 /*
1930 * Otherwise, dragging the mouse into a square that's a rook-move 2105 * Otherwise, dragging the mouse into a square that's a rook-move
1931 * away from the last one on the path extends the path. 2106 * away from the last one on the path extends the path.
@@ -1958,56 +2133,6 @@ static void update_ui_drag(const game_state *state, game_ui *ui,
1958 */ 2133 */
1959} 2134}
1960 2135
1961/*
1962 * Routine shared between interpret_move and game_redraw to work out
1963 * the intended effect of a drag path on the grid.
1964 *
1965 * Call it in a loop, like this:
1966 *
1967 * bool clearing = true;
1968 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1969 * int sx, sy, dx, dy, dir, oldstate, newstate;
1970 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1971 * &dir, &oldstate, &newstate);
1972 *
1973 * [do whatever is needed to handle the fact that the drag
1974 * wants the edge from sx,sy to dx,dy (heading in direction
1975 * 'dir' at the sx,sy end) to be changed from state oldstate
1976 * to state newstate, each of which equals either 0 or dir]
1977 * }
1978 */
1979static void interpret_ui_drag(const game_state *state, const game_ui *ui,
1980 bool *clearing, int i, int *sx, int *sy,
1981 int *dx, int *dy, int *dir,
1982 int *oldstate, int *newstate)
1983{
1984 int w = state->shared->w;
1985 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
1986 *sy = sp/w;
1987 *sx = sp%w;
1988 *dy = dp/w;
1989 *dx = dp%w;
1990 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
1991 *oldstate = state->lines[sp] & *dir;
1992 if (*oldstate) {
1993 /*
1994 * The edge we've dragged over was previously
1995 * present. Set it to absent, unless we've already
1996 * stopped doing that.
1997 */
1998 *newstate = *clearing ? 0 : *dir;
1999 } else {
2000 /*
2001 * The edge we've dragged over was previously
2002 * absent. Set it to present, and cancel the
2003 * 'clearing' flag so that all subsequent edges in
2004 * the drag are set rather than cleared.
2005 */
2006 *newstate = *dir;
2007 *clearing = false;
2008 }
2009}
2010
2011static char *mark_in_direction(const game_state *state, int x, int y, int dir, 2136static char *mark_in_direction(const game_state *state, int x, int y, int dir,
2012 bool primary, char *buf) 2137 bool primary, char *buf)
2013{ 2138{
@@ -2018,11 +2143,11 @@ static char *mark_in_direction(const game_state *state, int x, int y, int dir,
2018 2143
2019 char ch = primary ? 'F' : 'M', *other; 2144 char ch = primary ? 'F' : 'M', *other;
2020 2145
2021 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return UI_UPDATE; 2146 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return MOVE_UI_UPDATE;
2022 2147
2023 /* disallow laying a mark over a line, or vice versa. */ 2148 /* disallow laying a mark over a line, or vice versa. */
2024 other = primary ? state->marks : state->lines; 2149 other = primary ? state->marks : state->lines;
2025 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return UI_UPDATE; 2150 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return MOVE_UI_UPDATE;
2026 2151
2027 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2); 2152 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2);
2028 return dupstr(buf); 2153 return dupstr(buf);
@@ -2042,26 +2167,26 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2042 char tmpbuf[80]; 2167 char tmpbuf[80];
2043 2168
2044 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 2169 bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
2045 button &= ~MOD_MASK; 2170 button = STRIP_BUTTON_MODIFIERS(button);
2046 2171
2047 if (IS_MOUSE_DOWN(button)) { 2172 if (IS_MOUSE_DOWN(button)) {
2048 ui->cursor_active = false; 2173 ui->cursor_active = false;
2049 2174
2050 if (!INGRID(state, gx, gy)) { 2175 if (!INGRID(state, gx, gy)) {
2051 ui->ndragcoords = -1; 2176 ui->ndragcoords = -1;
2052 return NULL; 2177 return MOVE_UI_UPDATE;
2053 } 2178 }
2054 2179
2055 ui->clickx = x; ui->clicky = y; 2180 ui->clickx = x; ui->clicky = y;
2056 ui->dragcoords[0] = gy * w + gx; 2181 ui->dragcoords[0] = gy * w + gx;
2057 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */ 2182 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */
2058 2183
2059 return UI_UPDATE; 2184 return MOVE_UI_UPDATE;
2060 } 2185 }
2061 2186
2062 if (button == LEFT_DRAG && ui->ndragcoords >= 0) { 2187 if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
2063 update_ui_drag(state, ui, gx, gy); 2188 update_ui_drag(state, ui, gx, gy);
2064 return UI_UPDATE; 2189 return MOVE_UI_UPDATE;
2065 } 2190 }
2066 2191
2067 if (IS_MOUSE_RELEASE(button)) release = true; 2192 if (IS_MOUSE_RELEASE(button)) release = true;
@@ -2071,42 +2196,48 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2071 ui->cursor_active = true; 2196 ui->cursor_active = true;
2072 } else if (control || shift) { 2197 } else if (control || shift) {
2073 char *move; 2198 char *move;
2074 if (ui->ndragcoords > 0) return NULL; 2199 if (ui->ndragcoords > 0) return MOVE_NO_EFFECT;
2075 ui->ndragcoords = -1; 2200 ui->ndragcoords = -1;
2076 move = mark_in_direction(state, ui->curx, ui->cury, 2201 move = mark_in_direction(state, ui->curx, ui->cury,
2077 KEY_DIRECTION(button), control, tmpbuf); 2202 KEY_DIRECTION(button), control, tmpbuf);
2078 if (control && !shift && *move) 2203 if (control && !shift && *move)
2079 move_cursor(button, &ui->curx, &ui->cury, w, h, false); 2204 move_cursor(button, &ui->curx, &ui->cury, w, h, false, NULL);
2080 return move; 2205 return move;
2081 } else { 2206 } else {
2082 move_cursor(button, &ui->curx, &ui->cury, w, h, false); 2207 move_cursor(button, &ui->curx, &ui->cury, w, h, false, NULL);
2083 if (ui->ndragcoords >= 0) 2208 if (ui->ndragcoords >= 0)
2084 update_ui_drag(state, ui, ui->curx, ui->cury); 2209 update_ui_drag(state, ui, ui->curx, ui->cury);
2085 } 2210 }
2086 return UI_UPDATE; 2211 return MOVE_UI_UPDATE;
2087 } 2212 }
2088 2213
2089 if (IS_CURSOR_SELECT(button)) { 2214 if (IS_CURSOR_SELECT(button)) {
2090 if (!ui->cursor_active) { 2215 if (!ui->cursor_active) {
2091 ui->cursor_active = true; 2216 ui->cursor_active = true;
2092 return UI_UPDATE; 2217 return MOVE_UI_UPDATE;
2093 } else if (button == CURSOR_SELECT) { 2218 } else if (button == CURSOR_SELECT) {
2094 if (ui->ndragcoords == -1) { 2219 if (ui->ndragcoords == -1) {
2095 ui->ndragcoords = 0; 2220 ui->ndragcoords = 0;
2096 ui->dragcoords[0] = ui->cury * w + ui->curx; 2221 ui->dragcoords[0] = ui->cury * w + ui->curx;
2097 ui->clickx = CENTERED_COORD(ui->curx); 2222 ui->clickx = CENTERED_COORD(ui->curx);
2098 ui->clicky = CENTERED_COORD(ui->cury); 2223 ui->clicky = CENTERED_COORD(ui->cury);
2099 return UI_UPDATE; 2224 return MOVE_UI_UPDATE;
2100 } else release = true; 2225 } else release = true;
2101 } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) { 2226 } else if (button == CURSOR_SELECT2) {
2102 ui->ndragcoords = -1; 2227 if (ui->ndragcoords >= 0) {
2103 return UI_UPDATE; 2228 ui->ndragcoords = -1;
2104 } 2229 return MOVE_UI_UPDATE;
2230 }
2231 return MOVE_NO_EFFECT;
2232 }
2105 } 2233 }
2106 2234
2107 if (button == 27 || button == '\b') { 2235 if (button == 27 || button == '\b') {
2108 ui->ndragcoords = -1; 2236 if (ui->ndragcoords >= 0) {
2109 return UI_UPDATE; 2237 ui->ndragcoords = -1;
2238 return MOVE_UI_UPDATE;
2239 }
2240 return MOVE_NO_EFFECT;
2110 } 2241 }
2111 2242
2112 if (release) { 2243 if (release) {
@@ -2138,7 +2269,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2138 2269
2139 ui->ndragcoords = -1; 2270 ui->ndragcoords = -1;
2140 2271
2141 return buf ? buf : UI_UPDATE; 2272 return buf ? buf : MOVE_UI_UPDATE;
2142 } else if (ui->ndragcoords == 0) { 2273 } else if (ui->ndragcoords == 0) {
2143 /* Click (or tiny drag). Work out which edge we were 2274 /* Click (or tiny drag). Work out which edge we were
2144 * closest to. */ 2275 * closest to. */
@@ -2159,12 +2290,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2159 cx = CENTERED_COORD(gx); 2290 cx = CENTERED_COORD(gx);
2160 cy = CENTERED_COORD(gy); 2291 cy = CENTERED_COORD(gy);
2161 2292
2162 if (!INGRID(state, gx, gy)) return UI_UPDATE; 2293 if (!INGRID(state, gx, gy)) return MOVE_UI_UPDATE;
2163 2294
2164 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) { 2295 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
2165 /* TODO closer to centre of grid: process as a cell click not an edge click. */ 2296 /* TODO closer to centre of grid: process as a cell click not an edge click. */
2166 2297
2167 return UI_UPDATE; 2298 return MOVE_UI_UPDATE;
2168 } else { 2299 } else {
2169 int direction; 2300 int direction;
2170 if (abs(x-cx) < abs(y-cy)) { 2301 if (abs(x-cx) < abs(y-cy)) {
@@ -2183,7 +2314,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2183 if (button == 'H' || button == 'h') 2314 if (button == 'H' || button == 'h')
2184 return dupstr("H"); 2315 return dupstr("H");
2185 2316
2186 return NULL; 2317 return MOVE_UNUSED;
2187} 2318}
2188 2319
2189static game_state *execute_move(const game_state *state, const char *move) 2320static game_state *execute_move(const game_state *state, const char *move)
@@ -2246,7 +2377,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2246 goto badmove; 2377 goto badmove;
2247 } 2378 }
2248 2379
2249 check_completion(ret, true); 2380 if (!check_completion(ret, true)) goto badmove;
2250 2381
2251 return ret; 2382 return ret;
2252 2383
@@ -2262,7 +2393,7 @@ badmove:
2262#define FLASH_TIME 0.5F 2393#define FLASH_TIME 0.5F
2263 2394
2264static void game_compute_size(const game_params *params, int tilesize, 2395static void game_compute_size(const game_params *params, int tilesize,
2265 int *x, int *y) 2396 const game_ui *ui, int *x, int *y)
2266{ 2397{
2267 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2398 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2268 struct { int halfsz; } ads, *ds = &ads; 2399 struct { int halfsz; } ads, *ds = &ads;
@@ -2340,8 +2471,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2340} 2471}
2341 2472
2342static void draw_lines_specific(drawing *dr, game_drawstate *ds, 2473static void draw_lines_specific(drawing *dr, game_drawstate *ds,
2343 int x, int y, unsigned int lflags, 2474 const game_ui *ui, int x, int y,
2344 unsigned int shift, int c) 2475 unsigned int lflags, unsigned int shift, int c)
2345{ 2476{
2346 int ox = COORD(x), oy = COORD(y); 2477 int ox = COORD(x), oy = COORD(y);
2347 int t2 = HALFSZ, t16 = HALFSZ/4; 2478 int t2 = HALFSZ, t16 = HALFSZ/4;
@@ -2390,7 +2521,7 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2390 COL_CURSOR_BACKGROUND : COL_BACKGROUND); 2521 COL_CURSOR_BACKGROUND : COL_BACKGROUND);
2391 2522
2392 2523
2393 if (get_gui_style() == GUI_LOOPY) { 2524 if (ui->gui_style == GUI_LOOPY) {
2394 /* Draw small dot, underneath any lines. */ 2525 /* Draw small dot, underneath any lines. */
2395 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID); 2526 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
2396 } else { 2527 } else {
@@ -2417,7 +2548,7 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2417 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK); 2548 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
2418 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK); 2549 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
2419 } else { 2550 } else {
2420 if (get_gui_style() == GUI_LOOPY) { 2551 if (ui->gui_style == GUI_LOOPY) {
2421 /* draw grid lines connecting centre of cells */ 2552 /* draw grid lines connecting centre of cells */
2422 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID); 2553 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
2423 } 2554 }
@@ -2427,11 +2558,11 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2427 /* Draw each of the four directions, where laid (or error, or drag, etc.) 2558 /* Draw each of the four directions, where laid (or error, or drag, etc.)
2428 * Order is important here, specifically for the eventual colours of the 2559 * Order is important here, specifically for the eventual colours of the
2429 * exposed end caps. */ 2560 * exposed end caps. */
2430 draw_lines_specific(dr, ds, x, y, lflags, 0, 2561 draw_lines_specific(dr, ds, ui, x, y, lflags, 0,
2431 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK)); 2562 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
2432 draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR); 2563 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_ESHIFT, COL_ERROR);
2433 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF); 2564 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
2434 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON); 2565 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGON);
2435 2566
2436 /* Draw a clue, if present */ 2567 /* Draw a clue, if present */
2437 if (clue != NOCLUE) { 2568 if (clue != NOCLUE) {
@@ -2458,18 +2589,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2458 bool force = false; 2589 bool force = false;
2459 2590
2460 if (!ds->started) { 2591 if (!ds->started) {
2461 /* 2592 if (ui->gui_style == GUI_MASYU) {
2462 * The initial contents of the window are not guaranteed and
2463 * can vary with front ends. To be on the safe side, all games
2464 * should start by drawing a big background-colour rectangle
2465 * covering the whole window.
2466 */
2467 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2468 COL_BACKGROUND);
2469
2470 if (get_gui_style() == GUI_MASYU) {
2471 /* 2593 /*
2472 * Smaller black rectangle which is the main grid. 2594 * Black rectangle which is the main grid.
2473 */ 2595 */
2474 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, 2596 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2475 w*TILE_SIZE + 2*BORDER_WIDTH + 1, 2597 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
@@ -2560,47 +2682,64 @@ static int game_status(const game_state *state)
2560 return state->completed ? +1 : 0; 2682 return state->completed ? +1 : 0;
2561} 2683}
2562 2684
2563static bool game_timing_state(const game_state *state, game_ui *ui) 2685static void game_print_size(const game_params *params, const game_ui *ui,
2564{ 2686 float *x, float *y)
2565 return true;
2566}
2567
2568static void game_print_size(const game_params *params, float *x, float *y)
2569{ 2687{
2570 int pw, ph; 2688 int pw, ph;
2571 2689
2572 /* 2690 /*
2573 * I'll use 6mm squares by default. 2691 * I'll use 6mm squares by default.
2574 */ 2692 */
2575 game_compute_size(params, 600, &pw, &ph); 2693 game_compute_size(params, 600, ui, &pw, &ph);
2576 *x = pw / 100.0F; 2694 *x = pw / 100.0F;
2577 *y = ph / 100.0F; 2695 *y = ph / 100.0F;
2578} 2696}
2579 2697
2580static void game_print(drawing *dr, const game_state *state, int tilesize) 2698static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2699 int tilesize)
2581{ 2700{
2582 int w = state->shared->w, h = state->shared->h, x, y; 2701 int w = state->shared->w, h = state->shared->h, x, y;
2583 int black = print_mono_colour(dr, 0); 2702 int black = print_mono_colour(dr, 0);
2584 int white = print_mono_colour(dr, 1); 2703 int white = print_mono_colour(dr, 1);
2585 2704
2586 /* No GUI_LOOPY here: only use the familiar masyu style. */
2587
2588 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2705 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2589 game_drawstate *ds = game_new_drawstate(dr, state); 2706 game_drawstate *ds = game_new_drawstate(dr, state);
2590 game_set_size(dr, ds, NULL, tilesize); 2707 game_set_size(dr, ds, NULL, tilesize);
2591 2708
2592 /* Draw grid outlines (black). */ 2709 if (ui->gui_style == GUI_MASYU) {
2593 for (x = 0; x <= w; x++) 2710 /* Draw grid outlines (black). */
2594 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black); 2711 for (x = 0; x <= w; x++)
2595 for (y = 0; y <= h; y++) 2712 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
2596 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black); 2713 for (y = 0; y <= h; y++)
2714 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
2715 } else {
2716 /* Draw small dots, and dotted lines connecting them. For
2717 * added clarity, try to start and end the dotted lines a
2718 * little way away from the dots. */
2719 print_line_width(dr, TILE_SIZE / 40);
2720 print_line_dotted(dr, true);
2721 for (x = 0; x < w; x++) {
2722 for (y = 0; y < h; y++) {
2723 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2724 draw_circle(dr, cx, cy, tilesize/10, black, black);
2725 if (x+1 < w)
2726 draw_line(dr, cx+tilesize/5, cy,
2727 cx+tilesize-tilesize/5, cy, black);
2728 if (y+1 < h)
2729 draw_line(dr, cx, cy+tilesize/5,
2730 cx, cy+tilesize-tilesize/5, black);
2731 }
2732 }
2733 print_line_dotted(dr, false);
2734 }
2597 2735
2598 for (x = 0; x < w; x++) { 2736 for (x = 0; x < w; x++) {
2599 for (y = 0; y < h; y++) { 2737 for (y = 0; y < h; y++) {
2600 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ; 2738 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2601 int clue = state->shared->clues[y*w+x]; 2739 int clue = state->shared->clues[y*w+x];
2602 2740
2603 draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black); 2741 draw_lines_specific(dr, ds, ui, x, y,
2742 state->lines[y*w+x], 0, black);
2604 2743
2605 if (clue != NOCLUE) { 2744 if (clue != NOCLUE) {
2606 int c = (clue == CORNER) ? black : white; 2745 int c = (clue == CORNER) ? black : white;
@@ -2633,12 +2772,14 @@ const struct game thegame = {
2633 free_game, 2772 free_game,
2634 true, solve_game, 2773 true, solve_game,
2635 true, game_can_format_as_text_now, game_text_format, 2774 true, game_can_format_as_text_now, game_text_format,
2775 get_prefs, set_prefs,
2636 new_ui, 2776 new_ui,
2637 free_ui, 2777 free_ui,
2638 encode_ui, 2778 NULL, /* encode_ui */
2639 decode_ui, 2779 NULL, /* decode_ui */
2640 NULL, /* game_request_keys */ 2780 NULL, /* game_request_keys */
2641 game_changed_state, 2781 game_changed_state,
2782 current_key_label,
2642 interpret_move, 2783 interpret_move,
2643 execute_move, 2784 execute_move,
2644 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2785 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2652,7 +2793,7 @@ const struct game thegame = {
2652 game_status, 2793 game_status,
2653 true, false, game_print_size, game_print, 2794 true, false, game_print_size, game_print,
2654 false, /* wants_statusbar */ 2795 false, /* wants_statusbar */
2655 false, game_timing_state, 2796 false, NULL, /* timing_state */
2656 0, /* flags */ 2797 0, /* flags */
2657}; 2798};
2658 2799
@@ -2661,7 +2802,7 @@ const struct game thegame = {
2661#include <time.h> 2802#include <time.h>
2662#include <stdarg.h> 2803#include <stdarg.h>
2663 2804
2664const char *quis = NULL; 2805static const char *quis = NULL;
2665 2806
2666static void usage(FILE *out) { 2807static void usage(FILE *out) {
2667 fprintf(out, "usage: %s <params>\n", quis); 2808 fprintf(out, "usage: %s <params>\n", quis);
@@ -2720,7 +2861,7 @@ static void start_soak(game_params *p, random_state *rs, int nsecs)
2720 sfree(clues); 2861 sfree(clues);
2721} 2862}
2722 2863
2723int main(int argc, const char *argv[]) 2864int main(int argc, char *argv[])
2724{ 2865{
2725 game_params *p = NULL; 2866 game_params *p = NULL;
2726 random_state *rs = NULL; 2867 random_state *rs = NULL;
diff --git a/apps/plugins/puzzles/src/pegs.R b/apps/plugins/puzzles/src/pegs.R
deleted file mode 100644
index 1e79e99ca0..0000000000
--- a/apps/plugins/puzzles/src/pegs.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3PEGS_EXTRA = tree234
4
5pegs : [X] GTK COMMON pegs PEGS_EXTRA pegs-icon|no-icon
6
7pegs : [G] WINDOWS COMMON pegs PEGS_EXTRA pegs.res|noicon.res
8
9ALL += pegs[COMBINED] PEGS_EXTRA
10
11!begin am gtk
12GAMES += pegs
13!end
14
15!begin >list.c
16 A(pegs) \
17!end
18
19!begin >gamedesc.txt
20pegs:pegs.exe:Pegs:Peg solitaire puzzle:Jump pegs over each other to remove all but one.
21!end
diff --git a/apps/plugins/puzzles/src/pegs.c b/apps/plugins/puzzles/src/pegs.c
index ec12aa9552..c770bf3076 100644
--- a/apps/plugins/puzzles/src/pegs.c
+++ b/apps/plugins/puzzles/src/pegs.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13#include "tree234.h" 18#include "tree234.h"
@@ -70,7 +75,11 @@ static game_params *default_params(void)
70} 75}
71 76
72static const struct game_params pegs_presets[] = { 77static const struct game_params pegs_presets[] = {
78 {5, 7, TYPE_CROSS},
73 {7, 7, TYPE_CROSS}, 79 {7, 7, TYPE_CROSS},
80 {5, 9, TYPE_CROSS},
81 {7, 9, TYPE_CROSS},
82 {9, 9, TYPE_CROSS},
74 {7, 7, TYPE_OCTAGON}, 83 {7, 7, TYPE_OCTAGON},
75 {5, 5, TYPE_RANDOM}, 84 {5, 5, TYPE_RANDOM},
76 {7, 7, TYPE_RANDOM}, 85 {7, 7, TYPE_RANDOM},
@@ -89,7 +98,7 @@ static bool game_fetch_preset(int i, char **name, game_params **params)
89 *ret = pegs_presets[i]; 98 *ret = pegs_presets[i];
90 99
91 strcpy(str, pegs_titletypes[ret->type]); 100 strcpy(str, pegs_titletypes[ret->type]);
92 if (ret->type == TYPE_RANDOM) 101 if (ret->type == TYPE_CROSS || ret->type == TYPE_RANDOM)
93 sprintf(str + strlen(str), " %dx%d", ret->w, ret->h); 102 sprintf(str + strlen(str), " %dx%d", ret->w, ret->h);
94 103
95 *name = dupstr(str); 104 *name = dupstr(str);
@@ -182,14 +191,38 @@ static const char *validate_params(const game_params *params, bool full)
182{ 191{
183 if (full && (params->w <= 3 || params->h <= 3)) 192 if (full && (params->w <= 3 || params->h <= 3))
184 return "Width and height must both be greater than three"; 193 return "Width and height must both be greater than three";
194 if (params->w < 1 || params->h < 1)
195 return "Width and height must both be at least one";
196 if (params->w > INT_MAX / params->h)
197 return "Width times height must not be unreasonably large";
198
199 /*
200 * At http://www.gibell.net/pegsolitaire/GenCross/GenCrossBoards0.html
201 * George I. Bell asserts that various generalised cross-shaped
202 * boards are soluble starting (and finishing) with the centre
203 * hole. We permit the symmetric ones. Bell's notation for each
204 * soluble board is listed.
205 */
206 if (full && params->type == TYPE_CROSS) {
207 if (!((params->w == 9 && params->h == 5) || /* (3,1,3,1) */
208 (params->w == 5 && params->h == 9) || /* (1,3,1,3) */
209 (params->w == 9 && params->h == 9) || /* (3,3,3,3) */
210 (params->w == 7 && params->h == 5) || /* (2,1,2,1) */
211 (params->w == 5 && params->h == 7) || /* (1,2,1,2) */
212 (params->w == 9 && params->h == 7) || /* (3,2,3,2) */
213 (params->w == 7 && params->h == 9) || /* (2,3,2,3) */
214 (params->w == 7 && params->h == 7))) /* (2,2,2,2) */
215 return "This board type is only supported at "
216 "5x7, 5x9, 7x7, 7x9, and 9x9";
217 }
185 218
186 /* 219 /*
187 * It might be possible to implement generalisations of Cross 220 * It might be possible to implement generalisations of
188 * and Octagon, but only if I can find a proof that they're all 221 * Octagon, but only if I can find a proof that they're all
189 * soluble. For the moment, therefore, I'm going to disallow 222 * soluble. For the moment, therefore, I'm going to disallow
190 * them at any size other than the standard one. 223 * it at any size other than the standard one.
191 */ 224 */
192 if (full && (params->type == TYPE_CROSS || params->type == TYPE_OCTAGON)) { 225 if (full && params->type == TYPE_OCTAGON) {
193 if (params->w != 7 || params->h != 7) 226 if (params->w != 7 || params->h != 7)
194 return "This board type is only supported at 7x7"; 227 return "This board type is only supported at 7x7";
195 } 228 }
@@ -658,12 +691,23 @@ static char *new_game_desc(const game_params *params, random_state *rs,
658 691
659static const char *validate_desc(const game_params *params, const char *desc) 692static const char *validate_desc(const game_params *params, const char *desc)
660{ 693{
661 int len = params->w * params->h; 694 int len, i, npeg = 0, nhole = 0;
695
696 len = params->w * params->h;
662 697
663 if (len != strlen(desc)) 698 if (len != strlen(desc))
664 return "Game description is wrong length"; 699 return "Game description is wrong length";
665 if (len != strspn(desc, "PHO")) 700 if (len != strspn(desc, "PHO"))
666 return "Invalid character in game description"; 701 return "Invalid character in game description";
702 for (i = 0; i < len; i++) {
703 npeg += desc[i] == 'P';
704 nhole += desc[i] == 'H';
705 }
706 /* The minimal soluble game has two pegs and a hole: "3x1:PPH". */
707 if (npeg < 2)
708 return "Too few pegs in game description";
709 if (nhole < 1)
710 return "Too few holes in game description";
667 711
668 return NULL; 712 return NULL;
669} 713}
@@ -706,12 +750,6 @@ static void free_game(game_state *state)
706 sfree(state); 750 sfree(state);
707} 751}
708 752
709static char *solve_game(const game_state *state, const game_state *currstate,
710 const char *aux, const char **error)
711{
712 return NULL;
713}
714
715static bool game_can_format_as_text_now(const game_params *params) 753static bool game_can_format_as_text_now(const game_params *params)
716{ 754{
717 return true; 755 return true;
@@ -751,7 +789,7 @@ static game_ui *new_ui(const game_state *state)
751 789
752 ui->sx = ui->sy = ui->dx = ui->dy = 0; 790 ui->sx = ui->sy = ui->dx = ui->dy = 0;
753 ui->dragging = false; 791 ui->dragging = false;
754 ui->cur_visible = false; 792 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
755 ui->cur_jumping = false; 793 ui->cur_jumping = false;
756 794
757 /* make sure we start the cursor somewhere on the grid. */ 795 /* make sure we start the cursor somewhere on the grid. */
@@ -775,15 +813,6 @@ static void free_ui(game_ui *ui)
775 sfree(ui); 813 sfree(ui);
776} 814}
777 815
778static char *encode_ui(const game_ui *ui)
779{
780 return NULL;
781}
782
783static void decode_ui(game_ui *ui, const char *encoding)
784{
785}
786
787static void game_changed_state(game_ui *ui, const game_state *oldstate, 816static void game_changed_state(game_ui *ui, const game_state *oldstate,
788 const game_state *newstate) 817 const game_state *newstate)
789{ 818{
@@ -800,6 +829,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
800 ui->cur_jumping = false; 829 ui->cur_jumping = false;
801} 830}
802 831
832static const char *current_key_label(const game_ui *ui,
833 const game_state *state, int button)
834{
835 int w = state->w;
836
837 if (IS_CURSOR_SELECT(button)) {
838 if (!ui->cur_visible) return "";
839 if (ui->cur_jumping) return "Cancel";
840 if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) return "Select";
841 }
842 return "";
843}
844
803#define PREFERRED_TILE_SIZE 33 845#define PREFERRED_TILE_SIZE 33
804#define TILESIZE (ds->tilesize) 846#define TILESIZE (ds->tilesize)
805#define BORDER (TILESIZE / 2) 847#define BORDER (TILESIZE / 2)
@@ -845,16 +887,23 @@ static char *interpret_move(const game_state *state, game_ui *ui,
845 887
846 tx = FROMCOORD(x); 888 tx = FROMCOORD(x);
847 ty = FROMCOORD(y); 889 ty = FROMCOORD(y);
848 if (tx >= 0 && tx < w && ty >= 0 && ty < h && 890 if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
849 state->grid[ty*w+tx] == GRID_PEG) { 891 switch (state->grid[ty*w+tx]) {
850 ui->dragging = true; 892 case GRID_PEG:
851 ui->sx = tx; 893 ui->dragging = true;
852 ui->sy = ty; 894 ui->sx = tx;
853 ui->dx = x; 895 ui->sy = ty;
854 ui->dy = y; 896 ui->dx = x;
855 ui->cur_visible = false; 897 ui->dy = y;
856 ui->cur_jumping = false; 898 ui->cur_visible = false;
857 return UI_UPDATE; 899 ui->cur_jumping = false;
900 return MOVE_UI_UPDATE;
901 case GRID_HOLE:
902 return MOVE_NO_EFFECT;
903 case GRID_OBST:
904 default:
905 return MOVE_UNUSED;
906 }
858 } 907 }
859 } else if (button == LEFT_DRAG && ui->dragging) { 908 } else if (button == LEFT_DRAG && ui->dragging) {
860 /* 909 /*
@@ -862,7 +911,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
862 */ 911 */
863 ui->dx = x; 912 ui->dx = x;
864 ui->dy = y; 913 ui->dy = y;
865 return UI_UPDATE; 914 return MOVE_UI_UPDATE;
866 } else if (button == LEFT_RELEASE && ui->dragging) { 915 } else if (button == LEFT_RELEASE && ui->dragging) {
867 int tx, ty, dx, dy; 916 int tx, ty, dx, dy;
868 917
@@ -874,18 +923,18 @@ static char *interpret_move(const game_state *state, game_ui *ui,
874 tx = FROMCOORD(x); 923 tx = FROMCOORD(x);
875 ty = FROMCOORD(y); 924 ty = FROMCOORD(y);
876 if (tx < 0 || tx >= w || ty < 0 || ty >= h) 925 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
877 return UI_UPDATE; /* target out of range */ 926 return MOVE_UI_UPDATE; /* target out of range */
878 dx = tx - ui->sx; 927 dx = tx - ui->sx;
879 dy = ty - ui->sy; 928 dy = ty - ui->sy;
880 if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0) 929 if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0)
881 return UI_UPDATE; /* move length was wrong */ 930 return MOVE_UI_UPDATE; /* move length was wrong */
882 dx /= 2; 931 dx /= 2;
883 dy /= 2; 932 dy /= 2;
884 933
885 if (state->grid[ty*w+tx] != GRID_HOLE || 934 if (state->grid[ty*w+tx] != GRID_HOLE ||
886 state->grid[(ty-dy)*w+(tx-dx)] != GRID_PEG || 935 state->grid[(ty-dy)*w+(tx-dx)] != GRID_PEG ||
887 state->grid[ui->sy*w+ui->sx] != GRID_PEG) 936 state->grid[ui->sy*w+ui->sx] != GRID_PEG)
888 return UI_UPDATE; /* grid contents were invalid */ 937 return MOVE_UI_UPDATE; /* grid contents were invalid */
889 938
890 /* 939 /*
891 * We have a valid move. Encode it simply as source and 940 * We have a valid move. Encode it simply as source and
@@ -898,14 +947,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
898 /* Not jumping; move cursor as usual, making sure we don't 947 /* Not jumping; move cursor as usual, making sure we don't
899 * leave the gameboard (which may be an irregular shape) */ 948 * leave the gameboard (which may be an irregular shape) */
900 int cx = ui->cur_x, cy = ui->cur_y; 949 int cx = ui->cur_x, cy = ui->cur_y;
901 move_cursor(button, &cx, &cy, w, h, false); 950 move_cursor(button, &cx, &cy, w, h, false, NULL);
902 ui->cur_visible = true; 951 ui->cur_visible = true;
903 if (state->grid[cy*w+cx] == GRID_HOLE || 952 if (state->grid[cy*w+cx] == GRID_HOLE ||
904 state->grid[cy*w+cx] == GRID_PEG) { 953 state->grid[cy*w+cx] == GRID_PEG) {
905 ui->cur_x = cx; 954 ui->cur_x = cx;
906 ui->cur_y = cy; 955 ui->cur_y = cy;
907 } 956 }
908 return UI_UPDATE; 957 return MOVE_UI_UPDATE;
909 } else { 958 } else {
910 int dx, dy, mx, my, jx, jy; 959 int dx, dy, mx, my, jx, jy;
911 960
@@ -928,26 +977,26 @@ static char *interpret_move(const game_state *state, game_ui *ui,
928 ui->cur_x = jx; ui->cur_y = jy; 977 ui->cur_x = jx; ui->cur_y = jy;
929 return dupstr(buf); 978 return dupstr(buf);
930 } 979 }
931 return UI_UPDATE; 980 return MOVE_UI_UPDATE;
932 } 981 }
933 } else if (IS_CURSOR_SELECT(button)) { 982 } else if (IS_CURSOR_SELECT(button)) {
934 if (!ui->cur_visible) { 983 if (!ui->cur_visible) {
935 ui->cur_visible = true; 984 ui->cur_visible = true;
936 return UI_UPDATE; 985 return MOVE_UI_UPDATE;
937 } 986 }
938 if (ui->cur_jumping) { 987 if (ui->cur_jumping) {
939 ui->cur_jumping = false; 988 ui->cur_jumping = false;
940 return UI_UPDATE; 989 return MOVE_UI_UPDATE;
941 } 990 }
942 if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) { 991 if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) {
943 /* cursor is on peg: next arrow-move wil jump. */ 992 /* cursor is on peg: next arrow-move will jump. */
944 ui->cur_jumping = true; 993 ui->cur_jumping = true;
945 return UI_UPDATE; 994 return MOVE_UI_UPDATE;
946 } 995 }
947 return NULL; 996 return MOVE_NO_EFFECT;
948 } 997 }
949 998
950 return NULL; 999 return MOVE_UNUSED;
951} 1000}
952 1001
953static game_state *execute_move(const game_state *state, const char *move) 1002static game_state *execute_move(const game_state *state, const char *move)
@@ -1006,7 +1055,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1006 */ 1055 */
1007 1056
1008static void game_compute_size(const game_params *params, int tilesize, 1057static void game_compute_size(const game_params *params, int tilesize,
1009 int *x, int *y) 1058 const game_ui *ui, int *x, int *y)
1010{ 1059{
1011 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1060 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1012 struct { int tilesize; } ads, *ds = &ads; 1061 struct { int tilesize; } ads, *ds = &ads;
@@ -1135,10 +1184,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1135 } 1184 }
1136 1185
1137 if (!ds->started) { 1186 if (!ds->started) {
1138 draw_rect(dr, 0, 0,
1139 TILESIZE * state->w + 2 * BORDER,
1140 TILESIZE * state->h + 2 * BORDER, COL_BACKGROUND);
1141
1142 /* 1187 /*
1143 * Draw relief marks around all the squares that aren't 1188 * Draw relief marks around all the squares that aren't
1144 * GRID_OBST. 1189 * GRID_OBST.
@@ -1302,19 +1347,6 @@ static int game_status(const game_state *state)
1302 return state->completed ? +1 : 0; 1347 return state->completed ? +1 : 0;
1303} 1348}
1304 1349
1305static bool game_timing_state(const game_state *state, game_ui *ui)
1306{
1307 return true;
1308}
1309
1310static void game_print_size(const game_params *params, float *x, float *y)
1311{
1312}
1313
1314static void game_print(drawing *dr, const game_state *state, int tilesize)
1315{
1316}
1317
1318#ifdef COMBINED 1350#ifdef COMBINED
1319#define thegame pegs 1351#define thegame pegs
1320#endif 1352#endif
@@ -1334,14 +1366,16 @@ const struct game thegame = {
1334 new_game, 1366 new_game,
1335 dup_game, 1367 dup_game,
1336 free_game, 1368 free_game,
1337 false, solve_game, 1369 false, NULL, /* solve */
1338 true, game_can_format_as_text_now, game_text_format, 1370 true, game_can_format_as_text_now, game_text_format,
1371 NULL, NULL, /* get_prefs, set_prefs */
1339 new_ui, 1372 new_ui,
1340 free_ui, 1373 free_ui,
1341 encode_ui, 1374 NULL, /* encode_ui */
1342 decode_ui, 1375 NULL, /* decode_ui */
1343 NULL, /* game_request_keys */ 1376 NULL, /* game_request_keys */
1344 game_changed_state, 1377 game_changed_state,
1378 current_key_label,
1345 interpret_move, 1379 interpret_move,
1346 execute_move, 1380 execute_move,
1347 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1381 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1353,9 +1387,9 @@ const struct game thegame = {
1353 game_flash_length, 1387 game_flash_length,
1354 game_get_cursor_location, 1388 game_get_cursor_location,
1355 game_status, 1389 game_status,
1356 false, false, game_print_size, game_print, 1390 false, false, NULL, NULL, /* print_size, print */
1357 false, /* wants_statusbar */ 1391 false, /* wants_statusbar */
1358 false, game_timing_state, 1392 false, NULL, /* timing_state */
1359 0, /* flags */ 1393 0, /* flags */
1360}; 1394};
1361 1395
diff --git a/apps/plugins/puzzles/src/penrose-internal.h b/apps/plugins/puzzles/src/penrose-internal.h
new file mode 100644
index 0000000000..984cd35711
--- /dev/null
+++ b/apps/plugins/puzzles/src/penrose-internal.h
@@ -0,0 +1,289 @@
1#include "penrose.h"
2
3static inline unsigned num_subtriangles(char t)
4{
5 return (t == 'A' || t == 'B' || t == 'X' || t == 'Y') ? 3 : 2;
6}
7
8static inline unsigned sibling_edge(char t)
9{
10 switch (t) {
11 case 'A': case 'U': return 2;
12 case 'B': case 'V': return 1;
13 default: return 0;
14 }
15}
16
17/*
18 * Coordinate system for tracking Penrose-tile half-triangles.
19 * PenroseCoords simply stores an array of triangle types.
20 */
21typedef struct PenroseCoords {
22 char *c;
23 size_t nc, csize;
24} PenroseCoords;
25
26PenroseCoords *penrose_coords_new(void);
27void penrose_coords_free(PenroseCoords *pc);
28void penrose_coords_make_space(PenroseCoords *pc, size_t size);
29PenroseCoords *penrose_coords_copy(PenroseCoords *pc_in);
30
31/*
32 * Coordinate system for locating Penrose tiles in the plane.
33 *
34 * The 'Point' structure represents a single point by means of an
35 * integer linear combination of {1, t, t^2, t^3}, where t is the
36 * complex number exp(i pi/5) representing 1/10 of a turn about the
37 * origin.
38 *
39 * The 'PenroseTriangle' structure represents a half-tile triangle,
40 * giving both the locations of its vertices and its combinatorial
41 * coordinates. It also contains a linked-list pointer and a boolean
42 * flag, used during breadth-first search to generate all the tiles in
43 * an area and report them exactly once.
44 */
45typedef struct Point {
46 int coeffs[4];
47} Point;
48typedef struct PenroseTriangle PenroseTriangle;
49struct PenroseTriangle {
50 Point vertices[3];
51 PenroseCoords *pc;
52 PenroseTriangle *next; /* used in breadth-first search */
53 bool reported;
54};
55
56/* Fill in all the coordinates of a triangle starting from any single edge.
57 * Requires tri->pc to have been filled in, so that we know which shape of
58 * triangle we're placing. */
59void penrose_place(PenroseTriangle *tri, Point u, Point v, int index_of_u);
60
61/* Free a PenroseHalf and its contained coordinates, or a whole PenroseTile */
62void penrose_free(PenroseTriangle *tri);
63
64/*
65 * A Point is really a complex number, so we can add, subtract and
66 * multiply them.
67 */
68static inline Point point_add(Point a, Point b)
69{
70 Point r;
71 size_t i;
72 for (i = 0; i < 4; i++)
73 r.coeffs[i] = a.coeffs[i] + b.coeffs[i];
74 return r;
75}
76static inline Point point_sub(Point a, Point b)
77{
78 Point r;
79 size_t i;
80 for (i = 0; i < 4; i++)
81 r.coeffs[i] = a.coeffs[i] - b.coeffs[i];
82 return r;
83}
84static inline Point point_mul_by_t(Point x)
85{
86 Point r;
87 /* Multiply by t by using the identity t^4 - t^3 + t^2 - t + 1 = 0,
88 * so t^4 = t^3 - t^2 + t - 1 */
89 r.coeffs[0] = -x.coeffs[3];
90 r.coeffs[1] = x.coeffs[0] + x.coeffs[3];
91 r.coeffs[2] = x.coeffs[1] - x.coeffs[3];
92 r.coeffs[3] = x.coeffs[2] + x.coeffs[3];
93 return r;
94}
95static inline Point point_mul(Point a, Point b)
96{
97 size_t i, j;
98 Point r;
99
100 /* Initialise r to be a, scaled by b's t^3 term */
101 for (j = 0; j < 4; j++)
102 r.coeffs[j] = a.coeffs[j] * b.coeffs[3];
103
104 /* Now iterate r = t*r + (next coefficient down), by Horner's rule */
105 for (i = 3; i-- > 0 ;) {
106 r = point_mul_by_t(r);
107 for (j = 0; j < 4; j++)
108 r.coeffs[j] += a.coeffs[j] * b.coeffs[i];
109 }
110
111 return r;
112}
113static inline bool point_equal(Point a, Point b)
114{
115 size_t i;
116 for (i = 0; i < 4; i++)
117 if (a.coeffs[i] != b.coeffs[i])
118 return false;
119 return true;
120}
121
122/*
123 * Return the Point corresponding to a rotation of s steps around the
124 * origin, i.e. a rotation by 36*s degrees or s*pi/5 radians.
125 */
126static inline Point point_rot(int s)
127{
128 Point r = {{ 1, 0, 0, 0 }};
129 Point tpower = {{ 0, 1, 0, 0 }};
130
131 /* Reduce to a sensible range */
132 s = s % 10;
133 if (s < 0)
134 s += 10;
135
136 while (true) {
137 if (s & 1)
138 r = point_mul(r, tpower);
139 s >>= 1;
140 if (!s)
141 break;
142 tpower = point_mul(tpower, tpower);
143 }
144
145 return r;
146}
147
148/*
149 * PenroseContext is the shared context of a whole run of the
150 * algorithm. Its 'prototype' PenroseCoords object represents the
151 * coordinates of the starting triangle, and is extended as necessary;
152 * any other PenroseCoord that needs extending will copy the
153 * higher-order values from ctx->prototype as needed, so that once
154 * each choice has been made, it remains consistent.
155 *
156 * When we're inventing a random piece of tiling in the first place,
157 * we append to ctx->prototype by choosing a random (but legal)
158 * higher-level metatile for the current topmost one to turn out to be
159 * part of. When we're replaying a generation whose parameters are
160 * already stored, we don't have a random_state, and we make fixed
161 * decisions if not enough coordinates were provided, as in the
162 * corresponding hat.c system.
163 *
164 * For a normal (non-testing) caller, penrosectx_generate() is the
165 * main useful function. It breadth-first searches a whole area to
166 * generate all the triangles in it, starting from a (typically
167 * central) one with the coordinates of ctx->prototype. It takes two
168 * callback function: one that checks whether a triangle is within the
169 * bounds of the target area (and therefore the search should continue
170 * exploring its neighbours), and another that reports a full Penrose
171 * tile once both of its halves have been found and determined to be
172 * in bounds.
173 */
174typedef struct PenroseContext {
175 random_state *rs;
176 bool must_free_rs;
177 unsigned start_vertex; /* which vertex of 'prototype' is at the origin? */
178 int orientation; /* orientation to put in PenrosePatchParams */
179 PenroseCoords *prototype;
180} PenroseContext;
181
182void penrosectx_init_random(PenroseContext *ctx, random_state *rs, int which);
183void penrosectx_init_from_params(
184 PenroseContext *ctx, const struct PenrosePatchParams *ps);
185void penrosectx_cleanup(PenroseContext *ctx);
186PenroseCoords *penrosectx_initial_coords(PenroseContext *ctx);
187void penrosectx_extend_coords(PenroseContext *ctx, PenroseCoords *pc,
188 size_t n);
189void penrosectx_step(PenroseContext *ctx, PenroseCoords *pc,
190 unsigned edge, unsigned *outedge);
191void penrosectx_generate(
192 PenroseContext *ctx,
193 bool (*inbounds)(void *inboundsctx,
194 const PenroseTriangle *tri), void *inboundsctx,
195 void (*tile)(void *tilectx, const Point *vertices), void *tilectx);
196
197/* Subroutines that step around the tiling specified by a PenroseCtx,
198 * delivering both plane and combinatorial coordinates as they go */
199PenroseTriangle *penrose_initial(PenroseContext *ctx);
200PenroseTriangle *penrose_adjacent(PenroseContext *ctx,
201 const PenroseTriangle *src_spec,
202 unsigned src_edge, unsigned *dst_edge);
203
204/* For extracting the point coordinates */
205typedef struct Coord {
206 int c1, cr5; /* coefficients of 1 and sqrt(5) respectively */
207} Coord;
208
209static inline Coord point_x(Point p)
210{
211 Coord x = {
212 4 * p.coeffs[0] + p.coeffs[1] - p.coeffs[2] + p.coeffs[3],
213 p.coeffs[1] + p.coeffs[2] - p.coeffs[3],
214 };
215 return x;
216}
217
218static inline Coord point_y(Point p)
219{
220 Coord y = {
221 2 * p.coeffs[1] + p.coeffs[2] + p.coeffs[3],
222 p.coeffs[2] + p.coeffs[3],
223 };
224 return y;
225}
226
227static inline int coord_sign(Coord x)
228{
229 if (x.c1 == 0 && x.cr5 == 0)
230 return 0;
231 if (x.c1 >= 0 && x.cr5 >= 0)
232 return +1;
233 if (x.c1 <= 0 && x.cr5 <= 0)
234 return -1;
235
236 if (x.c1 * x.c1 > 5 * x.cr5 * x.cr5)
237 return x.c1 < 0 ? -1 : +1;
238 else
239 return x.cr5 < 0 ? -1 : +1;
240}
241
242static inline Coord coord_construct(int c1, int cr5)
243{
244 Coord c = { c1, cr5 };
245 return c;
246}
247
248static inline Coord coord_integer(int c1)
249{
250 return coord_construct(c1, 0);
251}
252
253static inline Coord coord_add(Coord a, Coord b)
254{
255 Coord sum;
256 sum.c1 = a.c1 + b.c1;
257 sum.cr5 = a.cr5 + b.cr5;
258 return sum;
259}
260
261static inline Coord coord_sub(Coord a, Coord b)
262{
263 Coord diff;
264 diff.c1 = a.c1 - b.c1;
265 diff.cr5 = a.cr5 - b.cr5;
266 return diff;
267}
268
269static inline Coord coord_mul(Coord a, Coord b)
270{
271 Coord prod;
272 prod.c1 = a.c1 * b.c1 + 5 * a.cr5 * b.cr5;
273 prod.cr5 = a.c1 * b.cr5 + a.cr5 * b.c1;
274 return prod;
275}
276
277static inline Coord coord_abs(Coord a)
278{
279 int sign = coord_sign(a);
280 Coord abs;
281 abs.c1 = a.c1 * sign;
282 abs.cr5 = a.cr5 * sign;
283 return abs;
284}
285
286static inline int coord_cmp(Coord a, Coord b)
287{
288 return coord_sign(coord_sub(a, b));
289}
diff --git a/apps/plugins/puzzles/src/penrose-legacy.c b/apps/plugins/puzzles/src/penrose-legacy.c
new file mode 100644
index 0000000000..709d68d7f5
--- /dev/null
+++ b/apps/plugins/puzzles/src/penrose-legacy.c
@@ -0,0 +1,506 @@
1/* penrose-legacy.c: legacy Penrose tile generator.
2 *
3 * Works by choosing a small patch from a recursively expanded large
4 * region of tiling, using one of the two algorithms described at
5 *
6 * https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/
7 *
8 * This method of generating Penrose tiling fragments is superseded by
9 * the completely different algorithm in penrose.c, using the other
10 * algorithm in that article (the 'combinatorial coordinates' one). We
11 * keep the legacy algorithm around only for interpreting Loopy game
12 * IDs generated by older versions of the code.
13 */
14
15#include <assert.h>
16#include <string.h>
17#ifdef NO_TGMATH_H
18# include <math.h>
19#else
20# include <tgmath.h>
21#endif
22#include <stdio.h>
23
24#include "puzzles.h" /* for malloc routines, and PI */
25#include "penrose-legacy.h"
26
27/* -------------------------------------------------------
28 * 36-degree basis vector arithmetic routines.
29 */
30
31/* Imagine drawing a
32 * ten-point 'clock face' like this:
33 *
34 * -E
35 * -D | A
36 * \ | /
37 * -C. \ | / ,B
38 * `-._\|/_,-'
39 * ,-' /|\ `-.
40 * -B' / | \ `C
41 * / | \
42 * -A | D
43 * E
44 *
45 * In case the ASCII art isn't clear, those are supposed to be ten
46 * vectors of length 1, all sticking out from the origin at equal
47 * angular spacing (hence 36 degrees). Our basis vectors are A,B,C,D (I
48 * choose them to be symmetric about the x-axis so that the final
49 * translation into 2d coordinates will also be symmetric, which I
50 * think will avoid minor rounding uglinesses), so our vector
51 * representation sets
52 *
53 * A = (1,0,0,0)
54 * B = (0,1,0,0)
55 * C = (0,0,1,0)
56 * D = (0,0,0,1)
57 *
58 * The fifth vector E looks at first glance as if it needs to be
59 * another basis vector, but in fact it doesn't, because it can be
60 * represented in terms of the other four. Imagine starting from the
61 * origin and following the path -A, +B, -C, +D: you'll find you've
62 * traced four sides of a pentagram, and ended up one E-vector away
63 * from the origin. So we have
64 *
65 * E = (-1,1,-1,1)
66 *
67 * This tells us that we can rotate any vector in this system by 36
68 * degrees: if we start with a*A + b*B + c*C + d*D, we want to end up
69 * with a*B + b*C + c*D + d*E, and we substitute our identity for E to
70 * turn that into a*B + b*C + c*D + d*(-A+B-C+D). In other words,
71 *
72 * rotate_one_notch_clockwise(a,b,c,d) = (-d, d+a, -d+b, d+c)
73 *
74 * and you can verify for yourself that applying that operation
75 * repeatedly starting with (1,0,0,0) cycles round ten vectors and
76 * comes back to where it started.
77 *
78 * The other operation that may be required is to construct vectors
79 * with lengths that are multiples of phi. That can be done by
80 * observing that the vector C-B is parallel to E and has length 1/phi,
81 * and the vector D-A is parallel to E and has length phi. So this
82 * tells us that given any vector, we can construct one which points in
83 * the same direction and is 1/phi or phi times its length, like this:
84 *
85 * divide_by_phi(vector) = rotate(vector, 2) - rotate(vector, 3)
86 * multiply_by_phi(vector) = rotate(vector, 1) - rotate(vector, 4)
87 *
88 * where rotate(vector, n) means applying the above
89 * rotate_one_notch_clockwise primitive n times. Expanding out the
90 * applications of rotate gives the following direct representation in
91 * terms of the vector coordinates:
92 *
93 * divide_by_phi(a,b,c,d) = (b-d, c+d-b, a+b-c, c-a)
94 * multiply_by_phi(a,b,c,d) = (a+b-d, c+d, a+b, c+d-a)
95 *
96 * and you can verify for yourself that those two operations are
97 * inverses of each other (as you'd hope!).
98 *
99 * Having done all of this, testing for equality between two vectors is
100 * a trivial matter of comparing the four integer coordinates. (Which
101 * it _wouldn't_ have been if we'd kept E as a fifth basis vector,
102 * because then (-1,1,-1,1,0) and (0,0,0,0,1) would have had to be
103 * considered identical. So leaving E out is vital.)
104 */
105
106struct vector { int a, b, c, d; };
107
108static vector v_origin(void)
109{
110 vector v;
111 v.a = v.b = v.c = v.d = 0;
112 return v;
113}
114
115/* We start with a unit vector of B: this means we can easily
116 * draw an isoceles triangle centred on the X axis. */
117#ifdef TEST_VECTORS
118
119static vector v_unit(void)
120{
121 vector v;
122
123 v.b = 1;
124 v.a = v.c = v.d = 0;
125 return v;
126}
127
128#endif
129
130#define COS54 0.5877852
131#define SIN54 0.8090169
132#define COS18 0.9510565
133#define SIN18 0.3090169
134
135/* These two are a bit rough-and-ready for now. Note that B/C are
136 * 18 degrees from the x-axis, and A/D are 54 degrees. */
137double penrose_legacy_vx(vector *vs, int i)
138{
139 return (vs[i].a + vs[i].d) * COS54 +
140 (vs[i].b + vs[i].c) * COS18;
141}
142
143double penrose_legacy_vy(vector *vs, int i)
144{
145 return (vs[i].a - vs[i].d) * SIN54 +
146 (vs[i].b - vs[i].c) * SIN18;
147
148}
149
150static vector v_trans(vector v, vector trans)
151{
152 v.a += trans.a;
153 v.b += trans.b;
154 v.c += trans.c;
155 v.d += trans.d;
156 return v;
157}
158
159static vector v_rotate_36(vector v)
160{
161 vector vv;
162 vv.a = -v.d;
163 vv.b = v.d + v.a;
164 vv.c = -v.d + v.b;
165 vv.d = v.d + v.c;
166 return vv;
167}
168
169static vector v_rotate(vector v, int ang)
170{
171 int i;
172
173 assert((ang % 36) == 0);
174 while (ang < 0) ang += 360;
175 ang = 360-ang;
176 for (i = 0; i < (ang/36); i++)
177 v = v_rotate_36(v);
178 return v;
179}
180
181#ifdef TEST_VECTORS
182
183static vector v_scale(vector v, int sc)
184{
185 v.a *= sc;
186 v.b *= sc;
187 v.c *= sc;
188 v.d *= sc;
189 return v;
190}
191
192#endif
193
194static vector v_growphi(vector v)
195{
196 vector vv;
197 vv.a = v.a + v.b - v.d;
198 vv.b = v.c + v.d;
199 vv.c = v.a + v.b;
200 vv.d = v.c + v.d - v.a;
201 return vv;
202}
203
204static vector v_shrinkphi(vector v)
205{
206 vector vv;
207 vv.a = v.b - v.d;
208 vv.b = v.c + v.d - v.b;
209 vv.c = v.a + v.b - v.c;
210 vv.d = v.c - v.a;
211 return vv;
212}
213
214#ifdef TEST_VECTORS
215
216static const char *v_debug(vector v)
217{
218 static char buf[255];
219 sprintf(buf,
220 "(%d,%d,%d,%d)[%2.2f,%2.2f]",
221 v.a, v.b, v.c, v.d, v_x(&v,0), v_y(&v,0));
222 return buf;
223}
224
225#endif
226
227/* -------------------------------------------------------
228 * Tiling routines.
229 */
230
231static vector xform_coord(vector vo, int shrink, vector vtrans, int ang)
232{
233 if (shrink < 0)
234 vo = v_shrinkphi(vo);
235 else if (shrink > 0)
236 vo = v_growphi(vo);
237
238 vo = v_rotate(vo, ang);
239 vo = v_trans(vo, vtrans);
240
241 return vo;
242}
243
244
245#define XFORM(n,o,s,a) vs[(n)] = xform_coord(v_edge, (s), vs[(o)], (a))
246
247static int penrose_p2_small(penrose_legacy_state *state, int depth, int flip,
248 vector v_orig, vector v_edge);
249
250static int penrose_p2_large(penrose_legacy_state *state, int depth, int flip,
251 vector v_orig, vector v_edge)
252{
253 vector vv_orig, vv_edge;
254
255#ifdef DEBUG_PENROSE
256 {
257 vector vs[3];
258 vs[0] = v_orig;
259 XFORM(1, 0, 0, 0);
260 XFORM(2, 0, 0, -36*flip);
261
262 state->new_tile(state, vs, 3, depth);
263 }
264#endif
265
266 if (flip > 0) {
267 vector vs[4];
268
269 vs[0] = v_orig;
270 XFORM(1, 0, 0, -36);
271 XFORM(2, 0, 0, 0);
272 XFORM(3, 0, 0, 36);
273
274 state->new_tile(state, vs, 4, depth);
275 }
276 if (depth >= state->max_depth) return 0;
277
278 vv_orig = v_trans(v_orig, v_rotate(v_edge, -36*flip));
279 vv_edge = v_rotate(v_edge, 108*flip);
280
281 penrose_p2_small(state, depth+1, flip,
282 v_orig, v_shrinkphi(v_edge));
283
284 penrose_p2_large(state, depth+1, flip,
285 vv_orig, v_shrinkphi(vv_edge));
286 penrose_p2_large(state, depth+1, -flip,
287 vv_orig, v_shrinkphi(vv_edge));
288
289 return 0;
290}
291
292static int penrose_p2_small(penrose_legacy_state *state, int depth, int flip,
293 vector v_orig, vector v_edge)
294{
295 vector vv_orig;
296
297#ifdef DEBUG_PENROSE
298 {
299 vector vs[3];
300 vs[0] = v_orig;
301 XFORM(1, 0, 0, 0);
302 XFORM(2, 0, -1, -36*flip);
303
304 state->new_tile(state, vs, 3, depth);
305 }
306#endif
307
308 if (flip > 0) {
309 vector vs[4];
310
311 vs[0] = v_orig;
312 XFORM(1, 0, 0, -72);
313 XFORM(2, 0, -1, -36);
314 XFORM(3, 0, 0, 0);
315
316 state->new_tile(state, vs, 4, depth);
317 }
318
319 if (depth >= state->max_depth) return 0;
320
321 vv_orig = v_trans(v_orig, v_edge);
322
323 penrose_p2_large(state, depth+1, -flip,
324 v_orig, v_shrinkphi(v_rotate(v_edge, -36*flip)));
325
326 penrose_p2_small(state, depth+1, flip,
327 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
328
329 return 0;
330}
331
332static int penrose_p3_small(penrose_legacy_state *state, int depth, int flip,
333 vector v_orig, vector v_edge);
334
335static int penrose_p3_large(penrose_legacy_state *state, int depth, int flip,
336 vector v_orig, vector v_edge)
337{
338 vector vv_orig;
339
340#ifdef DEBUG_PENROSE
341 {
342 vector vs[3];
343 vs[0] = v_orig;
344 XFORM(1, 0, 1, 0);
345 XFORM(2, 0, 0, -36*flip);
346
347 state->new_tile(state, vs, 3, depth);
348 }
349#endif
350
351 if (flip > 0) {
352 vector vs[4];
353
354 vs[0] = v_orig;
355 XFORM(1, 0, 0, -36);
356 XFORM(2, 0, 1, 0);
357 XFORM(3, 0, 0, 36);
358
359 state->new_tile(state, vs, 4, depth);
360 }
361 if (depth >= state->max_depth) return 0;
362
363 vv_orig = v_trans(v_orig, v_edge);
364
365 penrose_p3_large(state, depth+1, -flip,
366 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
367
368 penrose_p3_small(state, depth+1, flip,
369 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip)));
370
371 vv_orig = v_trans(v_orig, v_growphi(v_edge));
372
373 penrose_p3_large(state, depth+1, flip,
374 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
375
376
377 return 0;
378}
379
380static int penrose_p3_small(penrose_legacy_state *state, int depth, int flip,
381 vector v_orig, vector v_edge)
382{
383 vector vv_orig;
384
385#ifdef DEBUG_PENROSE
386 {
387 vector vs[3];
388 vs[0] = v_orig;
389 XFORM(1, 0, 0, 0);
390 XFORM(2, 0, 0, -36*flip);
391
392 state->new_tile(state, vs, 3, depth);
393 }
394#endif
395
396 if (flip > 0) {
397 vector vs[4];
398
399 vs[0] = v_orig;
400 XFORM(1, 0, 0, -36);
401 XFORM(3, 0, 0, 0);
402 XFORM(2, 3, 0, -36);
403
404 state->new_tile(state, vs, 4, depth);
405 }
406 if (depth >= state->max_depth) return 0;
407
408 /* NB these two are identical to the first two of p3_large. */
409 vv_orig = v_trans(v_orig, v_edge);
410
411 penrose_p3_large(state, depth+1, -flip,
412 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
413
414 penrose_p3_small(state, depth+1, flip,
415 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip)));
416
417 return 0;
418}
419
420/* -------------------------------------------------------
421 * Utility routines.
422 */
423
424double penrose_legacy_side_length(double start_size, int depth)
425{
426 return start_size / pow(PHI, depth);
427}
428
429/*
430 * It turns out that an acute isosceles triangle with sides in ratio 1:phi:phi
431 * has an incentre which is conveniently 2*phi^-2 of the way from the apex to
432 * the base. Why's that convenient? Because: if we situate the incentre of the
433 * triangle at the origin, then we can place the apex at phi^-2 * (B+C), and
434 * the other two vertices at apex-B and apex-C respectively. So that's an acute
435 * triangle with its long sides of unit length, covering a circle about the
436 * origin of radius 1-(2*phi^-2), which is conveniently enough phi^-3.
437 *
438 * (later mail: this is an overestimate by about 5%)
439 */
440
441int penrose_legacy(penrose_legacy_state *state, int which, int angle)
442{
443 vector vo = v_origin();
444 vector vb = v_origin();
445
446 vo.b = vo.c = -state->start_size;
447 vo = v_shrinkphi(v_shrinkphi(vo));
448
449 vb.b = state->start_size;
450
451 vo = v_rotate(vo, angle);
452 vb = v_rotate(vb, angle);
453
454 if (which == PENROSE_P2)
455 return penrose_p2_large(state, 0, 1, vo, vb);
456 else
457 return penrose_p3_small(state, 0, 1, vo, vb);
458}
459
460/*
461 * We're asked for a MxN grid, which just means a tiling fitting into roughly
462 * an MxN space in some kind of reasonable unit - say, the side length of the
463 * two-arrow edges of the tiles. By some reasoning in a previous email, that
464 * means we want to pick some subarea of a circle of radius 3.11*sqrt(M^2+N^2).
465 * To cover that circle, we need to subdivide a triangle large enough that it
466 * contains a circle of that radius.
467 *
468 * Hence: start with those three vectors marking triangle vertices, scale them
469 * all up by phi repeatedly until the radius of the inscribed circle gets
470 * bigger than the target, and then recurse into that triangle with the same
471 * recursion depth as the number of times you scaled up. That will give you
472 * tiles of unit side length, covering a circle big enough that if you randomly
473 * choose an orientation and coordinates within the circle, you'll be able to
474 * get any valid piece of Penrose tiling of size MxN.
475 */
476#define INCIRCLE_RADIUS 0.22426 /* phi^-3 less 5%: see above */
477
478void penrose_legacy_calculate_size(
479 int which, int tilesize, int w, int h,
480 double *required_radius, int *start_size, int *depth)
481{
482 double rradius, size;
483 int n = 0;
484
485 /*
486 * Fudge factor to scale P2 and P3 tilings differently. This
487 * doesn't seem to have much relevance to questions like the
488 * average number of tiles per unit area; it's just aesthetic.
489 */
490 if (which == PENROSE_P2)
491 tilesize = tilesize * 3 / 2;
492 else
493 tilesize = tilesize * 5 / 4;
494
495 rradius = tilesize * 3.11 * sqrt((double)(w*w + h*h));
496 size = tilesize;
497
498 while ((size * INCIRCLE_RADIUS) < rradius) {
499 n++;
500 size = size * PHI;
501 }
502
503 *start_size = (int)size;
504 *depth = n;
505 *required_radius = rradius;
506}
diff --git a/apps/plugins/puzzles/src/penrose-legacy.h b/apps/plugins/puzzles/src/penrose-legacy.h
new file mode 100644
index 0000000000..514fd8b592
--- /dev/null
+++ b/apps/plugins/puzzles/src/penrose-legacy.h
@@ -0,0 +1,63 @@
1/* penrose-legacy.h: legacy Penrose tiling functions.
2 *
3 * Provides an interface with which to generate Penrose tilings
4 * by recursive subdivision of an initial tile of choice (one of the
5 * four sets of two pairs kite/dart, or thin/thick rhombus).
6 *
7 * You supply a callback function and a context pointer, which is
8 * called with each tile in turn: you choose how many times to recurse.
9 *
10 * This method of generating Penrose tiling fragments is superseded by
11 * the completely different algorithm in penrose.c. We keep the legacy
12 * algorithm around only for interpreting Loopy game IDs generated by
13 * older versions of the code.
14 */
15
16#ifndef PUZZLES_PENROSE_LEGACY_H
17#define PUZZLES_PENROSE_LEGACY_H
18
19#ifndef PHI
20#define PHI 1.6180339887
21#endif
22
23typedef struct vector vector;
24
25double penrose_legacy_vx(vector *vs, int i);
26double penrose_legacy_vy(vector *vs, int i);
27
28typedef struct penrose_legacy_state penrose_legacy_state;
29
30/* Return non-zero to clip the tree here (i.e. not recurse
31 * below this tile).
32 *
33 * Parameters are state, vector array, npoints, depth.
34 * ctx is inside state.
35 */
36typedef int (*tile_callback)(penrose_legacy_state *, vector *, int, int);
37
38struct penrose_legacy_state {
39 int start_size; /* initial side length */
40 int max_depth; /* Recursion depth */
41
42 tile_callback new_tile;
43 void *ctx; /* for callback */
44};
45
46#ifndef PENROSE_ENUM_DEFINED
47#define PENROSE_ENUM_DEFINED
48enum { PENROSE_P2, PENROSE_P3 };
49#endif
50
51extern int penrose_legacy(penrose_legacy_state *state, int which, int angle);
52
53/* Returns the side-length of a penrose tile at recursion level
54 * gen, given a starting side length. */
55extern double penrose_legacy_side_length(double start_size, int depth);
56
57/* Calculate start size and recursion depth required to produce a
58 * width-by-height sized patch of penrose tiles with the given tilesize */
59extern void penrose_legacy_calculate_size(
60 int which, int tilesize, int w, int h,
61 double *required_radius, int *start_size, int *depth);
62
63#endif
diff --git a/apps/plugins/puzzles/src/penrose.c b/apps/plugins/puzzles/src/penrose.c
index ccde30d8b4..4d7dcc4347 100644
--- a/apps/plugins/puzzles/src/penrose.c
+++ b/apps/plugins/puzzles/src/penrose.c
@@ -1,629 +1,894 @@
1/* penrose.c 1/*
2 * 2 * Generate Penrose tilings via combinatorial coordinates.
3 * Penrose tile generator.
4 * 3 *
5 * Uses half-tile technique outlined on: 4 * For general explanation of the algorithm:
5 * https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/
6 * 6 *
7 * http://tartarus.org/simon/20110412-penrose/penrose.xhtml 7 * I use exactly the same indexing system here that's described in the
8 * article. For the P2 tiling, acute isosceles triangles (half-kites)
9 * are assigned letters A,B, and obtuse ones (half-darts) U,V; for P3,
10 * acute triangles (half of a thin rhomb) are C,D and obtuse ones
11 * (half a thick rhomb) are X,Y. Edges of all triangles are indexed
12 * anticlockwise around the triangle, with 0 being the base and 1,2
13 * being the two equal legs.
8 */ 14 */
9 15
10#include <assert.h> 16#include <assert.h>
17#include <stddef.h>
11#include <string.h> 18#include <string.h>
12#include <math.h>
13#include <stdio.h>
14 19
15#include "puzzles.h" /* for malloc routines, and PI */ 20#include "puzzles.h"
16#include "penrose.h" 21#include "penrose.h"
22#include "penrose-internal.h"
23#include "tree234.h"
17 24
18/* ------------------------------------------------------- 25bool penrose_valid_letter(char c, int which)
19 * 36-degree basis vector arithmetic routines. 26{
20 */ 27 switch (c) {
28 case 'A': case 'B': case 'U': case 'V':
29 return which == PENROSE_P2;
30 case 'C': case 'D': case 'X': case 'Y':
31 return which == PENROSE_P3;
32 default:
33 return false;
34 }
35}
21 36
22/* Imagine drawing a 37/*
23 * ten-point 'clock face' like this: 38 * Result of attempting a transition within the coordinate system.
24 * 39 * INTERNAL means we've moved to a different child of the same parent,
25 * -E 40 * so the 'internal' substructure gives the type of the new triangle
26 * -D | A 41 * and which edge of it we came in through; EXTERNAL means we've moved
27 * \ | / 42 * out of the parent entirely, and the 'external' substructure tells
28 * -C. \ | / ,B 43 * us which edge of the parent triangle we left by, and if it's
29 * `-._\|/_,-' 44 * divided in two, which end of that edge (-1 for the left end or +1
30 * ,-' /|\ `-. 45 * for the right end). If the parent edge is undivided, end == 0.
31 * -B' / | \ `C
32 * / | \
33 * -A | D
34 * E
35 *
36 * In case the ASCII art isn't clear, those are supposed to be ten
37 * vectors of length 1, all sticking out from the origin at equal
38 * angular spacing (hence 36 degrees). Our basis vectors are A,B,C,D (I
39 * choose them to be symmetric about the x-axis so that the final
40 * translation into 2d coordinates will also be symmetric, which I
41 * think will avoid minor rounding uglinesses), so our vector
42 * representation sets
43 *
44 * A = (1,0,0,0)
45 * B = (0,1,0,0)
46 * C = (0,0,1,0)
47 * D = (0,0,0,1)
48 *
49 * The fifth vector E looks at first glance as if it needs to be
50 * another basis vector, but in fact it doesn't, because it can be
51 * represented in terms of the other four. Imagine starting from the
52 * origin and following the path -A, +B, -C, +D: you'll find you've
53 * traced four sides of a pentagram, and ended up one E-vector away
54 * from the origin. So we have
55 *
56 * E = (-1,1,-1,1)
57 *
58 * This tells us that we can rotate any vector in this system by 36
59 * degrees: if we start with a*A + b*B + c*C + d*D, we want to end up
60 * with a*B + b*C + c*D + d*E, and we substitute our identity for E to
61 * turn that into a*B + b*C + c*D + d*(-A+B-C+D). In other words,
62 *
63 * rotate_one_notch_clockwise(a,b,c,d) = (-d, d+a, -d+b, d+c)
64 *
65 * and you can verify for yourself that applying that operation
66 * repeatedly starting with (1,0,0,0) cycles round ten vectors and
67 * comes back to where it started.
68 *
69 * The other operation that may be required is to construct vectors
70 * with lengths that are multiples of phi. That can be done by
71 * observing that the vector C-B is parallel to E and has length 1/phi,
72 * and the vector D-A is parallel to E and has length phi. So this
73 * tells us that given any vector, we can construct one which points in
74 * the same direction and is 1/phi or phi times its length, like this:
75 *
76 * divide_by_phi(vector) = rotate(vector, 2) - rotate(vector, 3)
77 * multiply_by_phi(vector) = rotate(vector, 1) - rotate(vector, 4)
78 *
79 * where rotate(vector, n) means applying the above
80 * rotate_one_notch_clockwise primitive n times. Expanding out the
81 * applications of rotate gives the following direct representation in
82 * terms of the vector coordinates:
83 *
84 * divide_by_phi(a,b,c,d) = (b-d, c+d-b, a+b-c, c-a)
85 * multiply_by_phi(a,b,c,d) = (a+b-d, c+d, a+b, c+d-a)
86 *
87 * and you can verify for yourself that those two operations are
88 * inverses of each other (as you'd hope!).
89 * 46 *
90 * Having done all of this, testing for equality between two vectors is 47 * The type FAIL _shouldn't_ ever come up! It occurs if you try to
91 * a trivial matter of comparing the four integer coordinates. (Which 48 * compute an incoming transition with an illegal value of 'end' (i.e.
92 * it _wouldn't_ have been if we'd kept E as a fifth basis vector, 49 * having the wrong idea of whether the edge is divided), or if you
93 * because then (-1,1,-1,1,0) and (0,0,0,0,1) would have had to be 50 * refer to a child triangle type that doesn't exist in that parent.
94 * considered identical. So leaving E out is vital.) 51 * If it ever happens in the production code then an assertion will
52 * fail. But it might be useful to other users of the same code.
95 */ 53 */
96 54typedef struct TransitionResult {
97struct vector { int a, b, c, d; }; 55 enum { INTERNAL, EXTERNAL, FAIL } type;
98 56 union {
99static vector v_origin(void) 57 struct {
58 char new_child;
59 unsigned char new_edge;
60 } internal;
61 struct {
62 unsigned char parent_edge;
63 signed char end;
64 } external;
65 } u;
66} TransitionResult;
67
68/* Construction function to make an INTERNAL-type TransitionResult */
69static inline TransitionResult internal(char new_child, unsigned new_edge)
100{ 70{
101 vector v; 71 TransitionResult tr;
102 v.a = v.b = v.c = v.d = 0; 72 tr.type = INTERNAL;
103 return v; 73 tr.u.internal.new_child = new_child;
74 tr.u.internal.new_edge = new_edge;
75 return tr;
104} 76}
105 77
106/* We start with a unit vector of B: this means we can easily 78/* Construction function to make an EXTERNAL-type TransitionResult */
107 * draw an isoceles triangle centred on the X axis. */ 79static inline TransitionResult external(unsigned parent_edge, int end)
108#ifdef TEST_VECTORS 80{
81 TransitionResult tr;
82 tr.type = EXTERNAL;
83 tr.u.external.parent_edge = parent_edge;
84 tr.u.external.end = end;
85 return tr;
86}
109 87
110static vector v_unit(void) 88/* Construction function to make a FAIL-type TransitionResult */
89static inline TransitionResult fail(void)
111{ 90{
112 vector v; 91 TransitionResult tr;
92 tr.type = FAIL;
93 return tr;
94}
113 95
114 v.b = 1; 96/*
115 v.a = v.c = v.d = 0; 97 * Compute a transition out of a triangle. Can return either INTERNAL
116 return v; 98 * or EXTERNAL types (or FAIL if it gets invalid data).
99 */
100static TransitionResult transition(char parent, char child, unsigned edge)
101{
102 switch (parent) {
103 case 'A':
104 switch (child) {
105 case 'A':
106 switch (edge) {
107 case 0: return external(2, -1);
108 case 1: return external(0, 0);
109 case 2: return internal('B', 1);
110 }
111 case 'B':
112 switch (edge) {
113 case 0: return internal('U', 1);
114 case 1: return internal('A', 2);
115 case 2: return external(1, +1);
116 }
117 case 'U':
118 switch (edge) {
119 case 0: return external(2, +1);
120 case 1: return internal('B', 0);
121 case 2: return external(1, -1);
122 }
123 default:
124 return fail();
125 }
126 case 'B':
127 switch (child) {
128 case 'A':
129 switch (edge) {
130 case 0: return internal('V', 2);
131 case 1: return external(2, -1);
132 case 2: return internal('B', 1);
133 }
134 case 'B':
135 switch (edge) {
136 case 0: return external(1, +1);
137 case 1: return internal('A', 2);
138 case 2: return external(0, 0);
139 }
140 case 'V':
141 switch (edge) {
142 case 0: return external(1, -1);
143 case 1: return external(2, +1);
144 case 2: return internal('A', 0);
145 }
146 default:
147 return fail();
148 }
149 case 'U':
150 switch (child) {
151 case 'B':
152 switch (edge) {
153 case 0: return internal('U', 1);
154 case 1: return external(2, 0);
155 case 2: return external(0, +1);
156 }
157 case 'U':
158 switch (edge) {
159 case 0: return external(1, 0);
160 case 1: return internal('B', 0);
161 case 2: return external(0, -1);
162 }
163 default:
164 return fail();
165 }
166 case 'V':
167 switch (child) {
168 case 'A':
169 switch (edge) {
170 case 0: return internal('V', 2);
171 case 1: return external(0, -1);
172 case 2: return external(1, 0);
173 }
174 case 'V':
175 switch (edge) {
176 case 0: return external(2, 0);
177 case 1: return external(0, +1);
178 case 2: return internal('A', 0);
179 }
180 default:
181 return fail();
182 }
183 case 'C':
184 switch (child) {
185 case 'C':
186 switch (edge) {
187 case 0: return external(1, +1);
188 case 1: return internal('Y', 1);
189 case 2: return external(0, 0);
190 }
191 case 'Y':
192 switch (edge) {
193 case 0: return external(2, 0);
194 case 1: return internal('C', 1);
195 case 2: return external(1, -1);
196 }
197 default:
198 return fail();
199 }
200 case 'D':
201 switch (child) {
202 case 'D':
203 switch (edge) {
204 case 0: return external(2, -1);
205 case 1: return external(0, 0);
206 case 2: return internal('X', 2);
207 }
208 case 'X':
209 switch (edge) {
210 case 0: return external(1, 0);
211 case 1: return external(2, +1);
212 case 2: return internal('D', 2);
213 }
214 default:
215 return fail();
216 }
217 case 'X':
218 switch (child) {
219 case 'C':
220 switch (edge) {
221 case 0: return external(2, +1);
222 case 1: return internal('Y', 1);
223 case 2: return internal('X', 1);
224 }
225 case 'X':
226 switch (edge) {
227 case 0: return external(1, 0);
228 case 1: return internal('C', 2);
229 case 2: return external(0, -1);
230 }
231 case 'Y':
232 switch (edge) {
233 case 0: return external(0, +1);
234 case 1: return internal('C', 1);
235 case 2: return external(2, -1);
236 }
237 default:
238 return fail();
239 }
240 case 'Y':
241 switch (child) {
242 case 'D':
243 switch (edge) {
244 case 0: return external(1, -1);
245 case 1: return internal('Y', 2);
246 case 2: return internal('X', 2);
247 }
248 case 'X':
249 switch (edge) {
250 case 0: return external(0, -1);
251 case 1: return external(1, +1);
252 case 2: return internal('D', 2);
253 }
254 case 'Y':
255 switch (edge) {
256 case 0: return external(2, 0);
257 case 1: return external(0, +1);
258 case 2: return internal('D', 1);
259 }
260 default:
261 return fail();
262 }
263 default:
264 return fail();
265 }
117} 266}
118 267
119#endif 268/*
269 * Compute a transition into a parent triangle, after the above
270 * function reported an EXTERNAL transition out of a neighbouring
271 * parent and we had to recurse. Because we're coming inwards, this
272 * should always return an INTERNAL TransitionResult (or FAIL if it
273 * gets invalid data).
274 */
275static TransitionResult transition_in(char parent, unsigned edge, int end)
276{
277 #define EDGEEND(edge, end) (3 * (edge) + 1 + (end))
278
279 switch (parent) {
280 case 'A':
281 switch (EDGEEND(edge, end)) {
282 case EDGEEND(0, 0): return internal('A', 1);
283 case EDGEEND(1, -1): return internal('B', 2);
284 case EDGEEND(1, +1): return internal('U', 2);
285 case EDGEEND(2, -1): return internal('U', 0);
286 case EDGEEND(2, +1): return internal('A', 0);
287 default:
288 return fail();
289 }
290 case 'B':
291 switch (EDGEEND(edge, end)) {
292 case EDGEEND(0, 0): return internal('B', 2);
293 case EDGEEND(1, -1): return internal('B', 0);
294 case EDGEEND(1, +1): return internal('V', 0);
295 case EDGEEND(2, -1): return internal('V', 1);
296 case EDGEEND(2, +1): return internal('A', 1);
297 default:
298 return fail();
299 }
300 case 'U':
301 switch (EDGEEND(edge, end)) {
302 case EDGEEND(0, -1): return internal('B', 2);
303 case EDGEEND(0, +1): return internal('U', 2);
304 case EDGEEND(1, 0): return internal('U', 0);
305 case EDGEEND(2, 0): return internal('B', 1);
306 default:
307 return fail();
308 }
309 case 'V':
310 switch (EDGEEND(edge, end)) {
311 case EDGEEND(0, -1): return internal('V', 1);
312 case EDGEEND(0, +1): return internal('A', 1);
313 case EDGEEND(1, 0): return internal('A', 2);
314 case EDGEEND(2, 0): return internal('V', 0);
315 default:
316 return fail();
317 }
318 case 'C':
319 switch (EDGEEND(edge, end)) {
320 case EDGEEND(0, 0): return internal('C', 2);
321 case EDGEEND(1, -1): return internal('C', 0);
322 case EDGEEND(1, +1): return internal('Y', 2);
323 case EDGEEND(2, 0): return internal('Y', 0);
324 default:
325 return fail();
326 }
327 case 'D':
328 switch (EDGEEND(edge, end)) {
329 case EDGEEND(0, 0): return internal('D', 1);
330 case EDGEEND(1, 0): return internal('X', 0);
331 case EDGEEND(2, -1): return internal('X', 1);
332 case EDGEEND(2, +1): return internal('D', 0);
333 default:
334 return fail();
335 }
336 case 'X':
337 switch (EDGEEND(edge, end)) {
338 case EDGEEND(0, -1): return internal('Y', 0);
339 case EDGEEND(0, +1): return internal('X', 2);
340 case EDGEEND(1, 0): return internal('X', 0);
341 case EDGEEND(2, -1): return internal('C', 0);
342 case EDGEEND(2, +1): return internal('Y', 2);
343 default:
344 return fail();
345 }
346 case 'Y':
347 switch (EDGEEND(edge, end)) {
348 case EDGEEND(0, +1): return internal('X', 0);
349 case EDGEEND(0, -1): return internal('Y', 1);
350 case EDGEEND(1, -1): return internal('X', 1);
351 case EDGEEND(1, +1): return internal('D', 0);
352 case EDGEEND(2, 0): return internal('Y', 0);
353 default:
354 return fail();
355 }
356 default:
357 return fail();
358 }
120 359
121#define COS54 0.5877852 360 #undef EDGEEND
122#define SIN54 0.8090169 361}
123#define COS18 0.9510565
124#define SIN18 0.3090169
125 362
126/* These two are a bit rough-and-ready for now. Note that B/C are 363PenroseCoords *penrose_coords_new(void)
127 * 18 degrees from the x-axis, and A/D are 54 degrees. */
128double v_x(vector *vs, int i)
129{ 364{
130 return (vs[i].a + vs[i].d) * COS54 + 365 PenroseCoords *pc = snew(PenroseCoords);
131 (vs[i].b + vs[i].c) * COS18; 366 pc->nc = pc->csize = 0;
367 pc->c = NULL;
368 return pc;
132} 369}
133 370
134double v_y(vector *vs, int i) 371void penrose_coords_free(PenroseCoords *pc)
135{ 372{
136 return (vs[i].a - vs[i].d) * SIN54 + 373 if (pc) {
137 (vs[i].b - vs[i].c) * SIN18; 374 sfree(pc->c);
138 375 sfree(pc);
376 }
139} 377}
140 378
141static vector v_trans(vector v, vector trans) 379void penrose_coords_make_space(PenroseCoords *pc, size_t size)
142{ 380{
143 v.a += trans.a; 381 if (pc->csize < size) {
144 v.b += trans.b; 382 pc->csize = pc->csize * 5 / 4 + 16;
145 v.c += trans.c; 383 if (pc->csize < size)
146 v.d += trans.d; 384 pc->csize = size;
147 return v; 385 pc->c = sresize(pc->c, pc->csize, char);
386 }
148} 387}
149 388
150static vector v_rotate_36(vector v) 389PenroseCoords *penrose_coords_copy(PenroseCoords *pc_in)
151{ 390{
152 vector vv; 391 PenroseCoords *pc_out = penrose_coords_new();
153 vv.a = -v.d; 392 penrose_coords_make_space(pc_out, pc_in->nc);
154 vv.b = v.d + v.a; 393 memcpy(pc_out->c, pc_in->c, pc_in->nc * sizeof(*pc_out->c));
155 vv.c = -v.d + v.b; 394 pc_out->nc = pc_in->nc;
156 vv.d = v.d + v.c; 395 return pc_out;
157 return vv;
158} 396}
159 397
160static vector v_rotate(vector v, int ang) 398/*
399 * The main recursive function for computing the next triangle's
400 * combinatorial coordinates.
401 */
402static void penrosectx_step_recurse(
403 PenroseContext *ctx, PenroseCoords *pc, size_t depth,
404 unsigned edge, unsigned *outedge)
161{ 405{
162 int i; 406 TransitionResult tr;
407
408 penrosectx_extend_coords(ctx, pc, depth+2);
409
410 /* Look up the transition out of the starting triangle */
411 tr = transition(pc->c[depth+1], pc->c[depth], edge);
412
413 /* If we've left the parent triangle, recurse to find out what new
414 * triangle we've landed in at the next size up, and then call
415 * transition_in to find out which child of that parent we're
416 * going to */
417 if (tr.type == EXTERNAL) {
418 unsigned parent_outedge;
419 penrosectx_step_recurse(
420 ctx, pc, depth+1, tr.u.external.parent_edge, &parent_outedge);
421 tr = transition_in(pc->c[depth+1], parent_outedge, tr.u.external.end);
422 }
163 423
164 assert((ang % 36) == 0); 424 /* Now we should definitely have ended up in a child of the
165 while (ang < 0) ang += 360; 425 * (perhaps rewritten) parent triangle */
166 ang = 360-ang; 426 assert(tr.type == INTERNAL);
167 for (i = 0; i < (ang/36); i++) 427 pc->c[depth] = tr.u.internal.new_child;
168 v = v_rotate_36(v); 428 *outedge = tr.u.internal.new_edge;
169 return v;
170} 429}
171 430
172#ifdef TEST_VECTORS 431void penrosectx_step(PenroseContext *ctx, PenroseCoords *pc,
173 432 unsigned edge, unsigned *outedge)
174static vector v_scale(vector v, int sc)
175{ 433{
176 v.a *= sc; 434 /* Allow outedge to be NULL harmlessly, just in case */
177 v.b *= sc; 435 unsigned dummy_outedge;
178 v.c *= sc; 436 if (!outedge)
179 v.d *= sc; 437 outedge = &dummy_outedge;
180 return v;
181}
182 438
183#endif 439 penrosectx_step_recurse(ctx, pc, 0, edge, outedge);
440}
184 441
185static vector v_growphi(vector v) 442static Point penrose_triangle_post_edge(char c, unsigned edge)
186{ 443{
187 vector vv; 444 static const Point acute_post_edge[3] = {
188 vv.a = v.a + v.b - v.d; 445 {{-1, 1, 0, 1}}, /* phi * t^3 */
189 vv.b = v.c + v.d; 446 {{-1, 1, -1, 1}}, /* t^4 */
190 vv.c = v.a + v.b; 447 {{-1, 1, 0, 0}}, /* 1/phi * t^3 */
191 vv.d = v.c + v.d - v.a; 448 };
192 return vv; 449 static const Point obtuse_post_edge[3] = {
450 {{0, -1, 1, 0}}, /* 1/phi * t^4 */
451 {{0, 0, 1, 0}}, /* t^2 */
452 {{-1, 0, 0, 1}}, /* phi * t^4 */
453 };
454
455 switch (c) {
456 case 'A': case 'B': case 'C': case 'D':
457 return acute_post_edge[edge];
458 default: /* case 'U': case 'V': case 'X': case 'Y': */
459 return obtuse_post_edge[edge];
460 }
193} 461}
194 462
195static vector v_shrinkphi(vector v) 463void penrose_place(PenroseTriangle *tri, Point u, Point v, int index_of_u)
196{ 464{
197 vector vv; 465 unsigned i;
198 vv.a = v.b - v.d; 466 Point here = u, delta = point_sub(v, u);
199 vv.b = v.c + v.d - v.b; 467
200 vv.c = v.a + v.b - v.c; 468 for (i = 0; i < 3; i++) {
201 vv.d = v.c - v.a; 469 unsigned edge = (index_of_u + i) % 3;
202 return vv; 470 tri->vertices[edge] = here;
471 here = point_add(here, delta);
472 delta = point_mul(delta, penrose_triangle_post_edge(
473 tri->pc->c[0], edge));
474 }
203} 475}
204 476
205#ifdef TEST_VECTORS 477void penrose_free(PenroseTriangle *tri)
206
207static const char *v_debug(vector v)
208{ 478{
209 static char buf[255]; 479 penrose_coords_free(tri->pc);
210 sprintf(buf, 480 sfree(tri);
211 "(%d,%d,%d,%d)[%2.2f,%2.2f]",
212 v.a, v.b, v.c, v.d, v_x(&v,0), v_y(&v,0));
213 return buf;
214} 481}
215 482
216#endif 483static bool penrose_relative_probability(char c)
217
218/* -------------------------------------------------------
219 * Tiling routines.
220 */
221
222static vector xform_coord(vector vo, int shrink, vector vtrans, int ang)
223{ 484{
224 if (shrink < 0) 485 /* Penrose tile probability ratios are always phi, so we can
225 vo = v_shrinkphi(vo); 486 * approximate that very well using two consecutive Fibonacci
226 else if (shrink > 0) 487 * numbers */
227 vo = v_growphi(vo); 488 switch (c) {
228 489 case 'A': case 'B': case 'X': case 'Y':
229 vo = v_rotate(vo, ang); 490 return 165580141;
230 vo = v_trans(vo, vtrans); 491 case 'C': case 'D': case 'U': case 'V':
231 492 return 102334155;
232 return vo; 493 default:
494 return 0;
495 }
233} 496}
234 497
235 498static char penrose_choose_random(const char *possibilities, random_state *rs)
236#define XFORM(n,o,s,a) vs[(n)] = xform_coord(v_edge, (s), vs[(o)], (a))
237
238static int penrose_p2_small(penrose_state *state, int depth, int flip,
239 vector v_orig, vector v_edge);
240
241static int penrose_p2_large(penrose_state *state, int depth, int flip,
242 vector v_orig, vector v_edge)
243{ 499{
244 vector vv_orig, vv_edge; 500 const char *p;
245 501 unsigned long value, limit = 0;
246#ifdef DEBUG_PENROSE 502
247 { 503 for (p = possibilities; *p; p++)
248 vector vs[3]; 504 limit += penrose_relative_probability(*p);
249 vs[0] = v_orig; 505 value = random_upto(rs, limit);
250 XFORM(1, 0, 0, 0); 506 for (p = possibilities; *p; p++) {
251 XFORM(2, 0, 0, -36*flip); 507 unsigned long curr = penrose_relative_probability(*p);
252 508 if (value < curr)
253 state->new_tile(state, vs, 3, depth); 509 return *p;
510 value -= curr;
254 } 511 }
255#endif 512 assert(false && "Probability overflow!");
256 513 return possibilities[0];
257 if (flip > 0) { 514}
258 vector vs[4];
259 515
260 vs[0] = v_orig; 516static const char *penrose_starting_tiles(int which)
261 XFORM(1, 0, 0, -36); 517{
262 XFORM(2, 0, 0, 0); 518 return which == PENROSE_P2 ? "ABUV" : "CDXY";
263 XFORM(3, 0, 0, 36); 519}
264 520
265 state->new_tile(state, vs, 4, depth); 521static const char *penrose_valid_parents(char tile)
522{
523 switch (tile) {
524 case 'A': return "ABV";
525 case 'B': return "ABU";
526 case 'U': return "AU";
527 case 'V': return "BV";
528 case 'C': return "CX";
529 case 'D': return "DY";
530 case 'X': return "DXY";
531 case 'Y': return "CXY";
532 default: return NULL;
266 } 533 }
267 if (depth >= state->max_depth) return 0; 534}
268
269 vv_orig = v_trans(v_orig, v_rotate(v_edge, -36*flip));
270 vv_edge = v_rotate(v_edge, 108*flip);
271
272 penrose_p2_small(state, depth+1, flip,
273 v_orig, v_shrinkphi(v_edge));
274 535
275 penrose_p2_large(state, depth+1, flip, 536void penrosectx_init_random(PenroseContext *ctx, random_state *rs, int which)
276 vv_orig, v_shrinkphi(vv_edge)); 537{
277 penrose_p2_large(state, depth+1, -flip, 538 ctx->rs = rs;
278 vv_orig, v_shrinkphi(vv_edge)); 539 ctx->must_free_rs = false;
540 ctx->prototype = penrose_coords_new();
541 penrose_coords_make_space(ctx->prototype, 1);
542 ctx->prototype->c[0] = penrose_choose_random(
543 penrose_starting_tiles(which), rs);
544 ctx->prototype->nc = 1;
545 ctx->start_vertex = random_upto(rs, 3);
546 ctx->orientation = random_upto(rs, 10);
547}
279 548
280 return 0; 549void penrosectx_init_from_params(
550 PenroseContext *ctx, const struct PenrosePatchParams *ps)
551{
552 size_t i;
553
554 ctx->rs = NULL;
555 ctx->must_free_rs = false;
556 ctx->prototype = penrose_coords_new();
557 penrose_coords_make_space(ctx->prototype, ps->ncoords);
558 for (i = 0; i < ps->ncoords; i++)
559 ctx->prototype->c[i] = ps->coords[i];
560 ctx->prototype->nc = ps->ncoords;
561 ctx->start_vertex = ps->start_vertex;
562 ctx->orientation = ps->orientation;
281} 563}
282 564
283static int penrose_p2_small(penrose_state *state, int depth, int flip, 565void penrosectx_cleanup(PenroseContext *ctx)
284 vector v_orig, vector v_edge)
285{ 566{
286 vector vv_orig; 567 if (ctx->must_free_rs)
568 random_free(ctx->rs);
569 penrose_coords_free(ctx->prototype);
570}
287 571
288#ifdef DEBUG_PENROSE 572PenroseCoords *penrosectx_initial_coords(PenroseContext *ctx)
289 { 573{
290 vector vs[3]; 574 return penrose_coords_copy(ctx->prototype);
291 vs[0] = v_orig; 575}
292 XFORM(1, 0, 0, 0);
293 XFORM(2, 0, -1, -36*flip);
294 576
295 state->new_tile(state, vs, 3, depth); 577void penrosectx_extend_coords(PenroseContext *ctx, PenroseCoords *pc,
578 size_t n)
579{
580 if (ctx->prototype->nc < n) {
581 penrose_coords_make_space(ctx->prototype, n);
582 while (ctx->prototype->nc < n) {
583 if (!ctx->rs) {
584 /*
585 * For safety, similarly to spectre.c, we respond to a
586 * lack of available random_state by making a
587 * deterministic one.
588 */
589 ctx->rs = random_new("dummy", 5);
590 ctx->must_free_rs = true;
591 }
592
593 ctx->prototype->c[ctx->prototype->nc] = penrose_choose_random(
594 penrose_valid_parents(ctx->prototype->c[ctx->prototype->nc-1]),
595 ctx->rs);
596 ctx->prototype->nc++;
597 }
296 } 598 }
297#endif
298
299 if (flip > 0) {
300 vector vs[4];
301
302 vs[0] = v_orig;
303 XFORM(1, 0, 0, -72);
304 XFORM(2, 0, -1, -36);
305 XFORM(3, 0, 0, 0);
306 599
307 state->new_tile(state, vs, 4, depth); 600 penrose_coords_make_space(pc, n);
601 while (pc->nc < n) {
602 pc->c[pc->nc] = ctx->prototype->c[pc->nc];
603 pc->nc++;
308 } 604 }
309
310 if (depth >= state->max_depth) return 0;
311
312 vv_orig = v_trans(v_orig, v_edge);
313
314 penrose_p2_large(state, depth+1, -flip,
315 v_orig, v_shrinkphi(v_rotate(v_edge, -36*flip)));
316
317 penrose_p2_small(state, depth+1, flip,
318 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
319
320 return 0;
321} 605}
322 606
323static int penrose_p3_small(penrose_state *state, int depth, int flip, 607static Point penrose_triangle_edge_0_length(char c)
324 vector v_orig, vector v_edge);
325
326static int penrose_p3_large(penrose_state *state, int depth, int flip,
327 vector v_orig, vector v_edge)
328{ 608{
329 vector vv_orig; 609 static const Point one = {{ 1, 0, 0, 0 }};
330 610 static const Point phi = {{ 1, 0, 1, -1 }};
331#ifdef DEBUG_PENROSE 611 static const Point invphi = {{ 0, 0, 1, -1 }};
332 { 612
333 vector vs[3]; 613 switch (c) {
334 vs[0] = v_orig; 614 /* P2 tiling: unit-length edges are the long edges, i.e. edges
335 XFORM(1, 0, 1, 0); 615 * 1,2 of AB and edge 0 of UV. So AB have edge 0 short. */
336 XFORM(2, 0, 0, -36*flip); 616 case 'A': case 'B':
337 617 return invphi;
338 state->new_tile(state, vs, 3, depth); 618 case 'U': case 'V':
619 return one;
620
621 /* P3 tiling: unit-length edges are edges 1,2 of everything,
622 * so CD have edge 0 short and XY have it long. */
623 case 'C': case 'D':
624 return invphi;
625 default: /* case 'X': case 'Y': */
626 return phi;
339 } 627 }
340#endif 628}
341 629
342 if (flip > 0) { 630PenroseTriangle *penrose_initial(PenroseContext *ctx)
343 vector vs[4]; 631{
632 char type = ctx->prototype->c[0];
633 Point origin = {{ 0, 0, 0, 0 }};
634 Point edge0 = penrose_triangle_edge_0_length(type);
635 Point negoffset;
636 size_t i;
637 PenroseTriangle *tri = snew(PenroseTriangle);
638
639 /* Orient the triangle by deciding what vector edge #0 should traverse */
640 edge0 = point_mul(edge0, point_rot(ctx->orientation));
641
642 /* Place the triangle at an arbitrary position, in that orientation */
643 tri->pc = penrose_coords_copy(ctx->prototype);
644 penrose_place(tri, origin, edge0, 0);
645
646 /* Now translate so that the appropriate vertex is at the origin */
647 negoffset = tri->vertices[ctx->start_vertex];
648 for (i = 0; i < 3; i++)
649 tri->vertices[i] = point_sub(tri->vertices[i], negoffset);
650
651 return tri;
652}
344 653
345 vs[0] = v_orig; 654PenroseTriangle *penrose_adjacent(PenroseContext *ctx,
346 XFORM(1, 0, 0, -36); 655 const PenroseTriangle *src_tri,
347 XFORM(2, 0, 1, 0); 656 unsigned src_edge, unsigned *dst_edge_out)
348 XFORM(3, 0, 0, 36); 657{
658 unsigned dst_edge;
659 PenroseTriangle *dst_tri = snew(PenroseTriangle);
660 dst_tri->pc = penrose_coords_copy(src_tri->pc);
661 penrosectx_step(ctx, dst_tri->pc, src_edge, &dst_edge);
662 penrose_place(dst_tri, src_tri->vertices[(src_edge+1) % 3],
663 src_tri->vertices[src_edge], dst_edge);
664 if (dst_edge_out)
665 *dst_edge_out = dst_edge;
666 return dst_tri;
667}
349 668
350 state->new_tile(state, vs, 4, depth); 669static int penrose_cmp(void *av, void *bv)
670{
671 PenroseTriangle *a = (PenroseTriangle *)av, *b = (PenroseTriangle *)bv;
672 size_t i, j;
673
674 /* We should only ever need to compare the first two vertices of
675 * any triangle, because those force the rest */
676 for (i = 0; i < 2; i++) {
677 for (j = 0; j < 4; j++) {
678 int ac = a->vertices[i].coeffs[j], bc = b->vertices[i].coeffs[j];
679 if (ac < bc)
680 return -1;
681 if (ac > bc)
682 return +1;
683 }
351 } 684 }
352 if (depth >= state->max_depth) return 0;
353
354 vv_orig = v_trans(v_orig, v_edge);
355
356 penrose_p3_large(state, depth+1, -flip,
357 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
358
359 penrose_p3_small(state, depth+1, flip,
360 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip)));
361
362 vv_orig = v_trans(v_orig, v_growphi(v_edge));
363
364 penrose_p3_large(state, depth+1, flip,
365 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
366
367 685
368 return 0; 686 return 0;
369} 687}
370 688
371static int penrose_p3_small(penrose_state *state, int depth, int flip, 689static unsigned penrose_sibling_edge_index(char c)
372 vector v_orig, vector v_edge)
373{ 690{
374 vector vv_orig; 691 switch (c) {
375 692 case 'A': case 'U': return 2;
376#ifdef DEBUG_PENROSE 693 case 'B': case 'V': return 1;
377 { 694 default: return 0;
378 vector vs[3];
379 vs[0] = v_orig;
380 XFORM(1, 0, 0, 0);
381 XFORM(2, 0, 0, -36*flip);
382
383 state->new_tile(state, vs, 3, depth);
384 } 695 }
385#endif 696}
386
387 if (flip > 0) {
388 vector vs[4];
389
390 vs[0] = v_orig;
391 XFORM(1, 0, 0, -36);
392 XFORM(3, 0, 0, 0);
393 XFORM(2, 3, 0, -36);
394 697
395 state->new_tile(state, vs, 4, depth); 698void penrosectx_generate(
396 } 699 PenroseContext *ctx,
397 if (depth >= state->max_depth) return 0; 700 bool (*inbounds)(void *inboundsctx,
701 const PenroseTriangle *tri), void *inboundsctx,
702 void (*tile)(void *tilectx, const Point *vertices), void *tilectx)
703{
704 tree234 *placed = newtree234(penrose_cmp);
705 PenroseTriangle *qhead = NULL, *qtail = NULL;
398 706
399 /* NB these two are identical to the first two of p3_large. */ 707 {
400 vv_orig = v_trans(v_orig, v_edge); 708 PenroseTriangle *tri = penrose_initial(ctx);
401 709
402 penrose_p3_large(state, depth+1, -flip, 710 add234(placed, tri);
403 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
404 711
405 penrose_p3_small(state, depth+1, flip, 712 tri->next = NULL;
406 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip))); 713 tri->reported = false;
407 714
408 return 0; 715 if (inbounds(inboundsctx, tri))
409} 716 qhead = qtail = tri;
717 }
410 718
411/* ------------------------------------------------------- 719 while (qhead) {
412 * Utility routines. 720 PenroseTriangle *tri = qhead;
413 */ 721 unsigned edge;
722 unsigned sibling_edge = penrose_sibling_edge_index(tri->pc->c[0]);
723
724 for (edge = 0; edge < 3; edge++) {
725 PenroseTriangle *new_tri, *found_tri;
726
727 new_tri = penrose_adjacent(ctx, tri, edge, NULL);
728
729 if (!inbounds(inboundsctx, new_tri)) {
730 penrose_free(new_tri);
731 continue;
732 }
733
734 found_tri = find234(placed, new_tri, NULL);
735 if (found_tri) {
736 if (edge == sibling_edge && !tri->reported &&
737 !found_tri->reported) {
738 /*
739 * found_tri and tri are opposite halves of the
740 * same tile; both are in the tree, and haven't
741 * yet been reported as a completed tile.
742 */
743 unsigned new_sibling_edge = penrose_sibling_edge_index(
744 found_tri->pc->c[0]);
745 Point tilevertices[4] = {
746 tri->vertices[(sibling_edge + 1) % 3],
747 tri->vertices[(sibling_edge + 2) % 3],
748 found_tri->vertices[(new_sibling_edge + 1) % 3],
749 found_tri->vertices[(new_sibling_edge + 2) % 3],
750 };
751 tile(tilectx, tilevertices);
752
753 tri->reported = true;
754 found_tri->reported = true;
755 }
756
757 penrose_free(new_tri);
758 continue;
759 }
760
761 add234(placed, new_tri);
762 qtail->next = new_tri;
763 qtail = new_tri;
764 new_tri->next = NULL;
765 new_tri->reported = false;
766 }
414 767
415double penrose_side_length(double start_size, int depth) 768 qhead = qhead->next;
416{ 769 }
417 return start_size / pow(PHI, depth);
418}
419 770
420void penrose_count_tiles(int depth, int *nlarge, int *nsmall) 771 {
421{ 772 PenroseTriangle *tri;
422 /* Steal sgt's fibonacci thingummy. */ 773 while ((tri = delpos234(placed, 0)) != NULL)
774 penrose_free(tri);
775 freetree234(placed);
776 }
423} 777}
424 778
425/* 779const char *penrose_tiling_params_invalid(
426 * It turns out that an acute isosceles triangle with sides in ratio 1:phi:phi 780 const struct PenrosePatchParams *params, int which)
427 * has an incentre which is conveniently 2*phi^-2 of the way from the apex to
428 * the base. Why's that convenient? Because: if we situate the incentre of the
429 * triangle at the origin, then we can place the apex at phi^-2 * (B+C), and
430 * the other two vertices at apex-B and apex-C respectively. So that's an acute
431 * triangle with its long sides of unit length, covering a circle about the
432 * origin of radius 1-(2*phi^-2), which is conveniently enough phi^-3.
433 *
434 * (later mail: this is an overestimate by about 5%)
435 */
436
437int penrose(penrose_state *state, int which, int angle)
438{ 781{
439 vector vo = v_origin(); 782 size_t i;
440 vector vb = v_origin();
441
442 vo.b = vo.c = -state->start_size;
443 vo = v_shrinkphi(v_shrinkphi(vo));
444
445 vb.b = state->start_size;
446 783
447 vo = v_rotate(vo, angle); 784 if (params->ncoords == 0)
448 vb = v_rotate(vb, angle); 785 return "expected at least one coordinate";
449 786
450 if (which == PENROSE_P2) 787 for (i = 0; i < params->ncoords; i++) {
451 return penrose_p2_large(state, 0, 1, vo, vb); 788 if (!penrose_valid_letter(params->coords[i], which))
452 else 789 return "invalid coordinate letter";
453 return penrose_p3_small(state, 0, 1, vo, vb); 790 if (i > 0 && !strchr(penrose_valid_parents(params->coords[i-1]),
454} 791 params->coords[i]))
455 792 return "invalid pair of consecutive coordinates";
456/*
457 * We're asked for a MxN grid, which just means a tiling fitting into roughly
458 * an MxN space in some kind of reasonable unit - say, the side length of the
459 * two-arrow edges of the tiles. By some reasoning in a previous email, that
460 * means we want to pick some subarea of a circle of radius 3.11*sqrt(M^2+N^2).
461 * To cover that circle, we need to subdivide a triangle large enough that it
462 * contains a circle of that radius.
463 *
464 * Hence: start with those three vectors marking triangle vertices, scale them
465 * all up by phi repeatedly until the radius of the inscribed circle gets
466 * bigger than the target, and then recurse into that triangle with the same
467 * recursion depth as the number of times you scaled up. That will give you
468 * tiles of unit side length, covering a circle big enough that if you randomly
469 * choose an orientation and coordinates within the circle, you'll be able to
470 * get any valid piece of Penrose tiling of size MxN.
471 */
472#define INCIRCLE_RADIUS 0.22426 /* phi^-3 less 5%: see above */
473
474void penrose_calculate_size(int which, int tilesize, int w, int h,
475 double *required_radius, int *start_size, int *depth)
476{
477 double rradius, size;
478 int n = 0;
479
480 /*
481 * Fudge factor to scale P2 and P3 tilings differently. This
482 * doesn't seem to have much relevance to questions like the
483 * average number of tiles per unit area; it's just aesthetic.
484 */
485 if (which == PENROSE_P2)
486 tilesize = tilesize * 3 / 2;
487 else
488 tilesize = tilesize * 5 / 4;
489
490 rradius = tilesize * 3.11 * sqrt((double)(w*w + h*h));
491 size = tilesize;
492
493 while ((size * INCIRCLE_RADIUS) < rradius) {
494 n++;
495 size = size * PHI;
496 } 793 }
497 794
498 *start_size = (int)size; 795 return NULL;
499 *depth = n;
500 *required_radius = rradius;
501} 796}
502 797
503/* ------------------------------------------------------- 798struct PenroseOutputCtx {
504 * Test code. 799 int xoff, yoff;
505 */ 800 Coord xmin, xmax, ymin, ymax;
506
507#ifdef TEST_PENROSE
508
509#include <stdio.h>
510#include <string.h>
511 801
512int show_recursion = 0; 802 penrose_tile_callback_fn external_cb;
513int ntiles, nfinal; 803 void *external_cbctx;
804};
514 805
515int test_cb(penrose_state *state, vector *vs, int n, int depth) 806static bool inbounds(void *vctx, const PenroseTriangle *tri)
516{ 807{
517 int i, xoff = 0, yoff = 0; 808 struct PenroseOutputCtx *octx = (struct PenroseOutputCtx *)vctx;
518 double l = penrose_side_length(state->start_size, depth); 809 size_t i;
519 double rball = l / 10.0;
520 const char *col;
521 810
522 ntiles++; 811 for (i = 0; i < 3; i++) {
523 if (state->max_depth == depth) { 812 Coord x = point_x(tri->vertices[i]);
524 col = n == 4 ? "black" : "green"; 813 Coord y = point_y(tri->vertices[i]);
525 nfinal++;
526 } else {
527 if (!show_recursion)
528 return 0;
529 col = n == 4 ? "red" : "blue";
530 }
531 if (n != 4) yoff = state->start_size;
532 814
533 printf("<polygon points=\""); 815 if (coord_cmp(x, octx->xmin) < 0 || coord_cmp(x, octx->xmax) > 0 ||
534 for (i = 0; i < n; i++) { 816 coord_cmp(y, octx->ymin) < 0 || coord_cmp(y, octx->ymax) > 0)
535 printf("%s%f,%f", (i == 0) ? "" : " ", 817 return false;
536 v_x(vs, i) + xoff, v_y(vs, i) + yoff);
537 } 818 }
538 printf("\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", col, col);
539 printf("<ellipse cx=\"%f\" cy=\"%f\" rx=\"%f\" ry=\"%f\" fill=\"%s\" />",
540 v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col);
541 819
542 return 0; 820 return true;
543} 821}
544 822
545void usage_exit(void) 823static void null_output_tile(void *vctx, const Point *vertices)
546{ 824{
547 fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n");
548 exit(1);
549} 825}
550 826
551int main(int argc, char *argv[]) 827static void really_output_tile(void *vctx, const Point *vertices)
552{ 828{
553 penrose_state ps; 829 struct PenroseOutputCtx *octx = (struct PenroseOutputCtx *)vctx;
554 int which = 0; 830 size_t i;
555 831 int coords[16];
556 while (--argc > 0) { 832
557 char *p = *++argv; 833 for (i = 0; i < 4; i++) {
558 if (!strcmp(p, "-h") || !strcmp(p, "--help")) { 834 Coord x = point_x(vertices[i]);
559 usage_exit(); 835 Coord y = point_y(vertices[i]);
560 } else if (!strcmp(p, "--recursion")) { 836
561 show_recursion = 1; 837 coords[4*i + 0] = x.c1 + octx->xoff;
562 } else if (*p == '-') { 838 coords[4*i + 1] = x.cr5;
563 fprintf(stderr, "Unrecognised option '%s'\n", p); 839 coords[4*i + 2] = y.c1 + octx->yoff;
564 exit(1); 840 coords[4*i + 3] = y.cr5;
565 } else {
566 break;
567 }
568 } 841 }
569 842
570 if (argc < 3) usage_exit(); 843 octx->external_cb(octx->external_cbctx, coords);
571 844}
572 if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2;
573 else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3;
574 else usage_exit();
575
576 ps.start_size = atoi(argv[1]);
577 ps.max_depth = atoi(argv[2]);
578 ps.new_tile = test_cb;
579
580 ntiles = nfinal = 0;
581
582 printf("\
583<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
584<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\
585\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\
586\n\
587<svg xmlns=\"http://www.w3.org/2000/svg\"\n\
588xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
589
590 printf("<g>\n");
591 penrose(&ps, which);
592 printf("</g>\n");
593 845
594 printf("<!-- %d tiles and %d leaf tiles total -->\n", 846static void penrose_set_bounds(struct PenroseOutputCtx *octx, int w, int h)
595 ntiles, nfinal); 847{
848 octx->xoff = w/2;
849 octx->yoff = h/2;
850 octx->xmin.c1 = -octx->xoff;
851 octx->xmax.c1 = -octx->xoff + w;
852 octx->ymin.c1 = octx->yoff - h;
853 octx->ymax.c1 = octx->yoff;
854 octx->xmin.cr5 = 0;
855 octx->xmax.cr5 = 0;
856 octx->ymin.cr5 = 0;
857 octx->ymax.cr5 = 0;
858}
596 859
597 printf("</svg>"); 860void penrose_tiling_randomise(struct PenrosePatchParams *params, int which,
861 int w, int h, random_state *rs)
862{
863 PenroseContext ctx[1];
864 struct PenroseOutputCtx octx[1];
598 865
599 return 0; 866 penrose_set_bounds(octx, w, h);
600}
601 867
602#endif 868 penrosectx_init_random(ctx, rs, which);
869 penrosectx_generate(ctx, inbounds, octx, null_output_tile, NULL);
603 870
604#ifdef TEST_VECTORS 871 params->orientation = ctx->orientation;
872 params->start_vertex = ctx->start_vertex;
873 params->ncoords = ctx->prototype->nc;
874 params->coords = snewn(params->ncoords, char);
875 memcpy(params->coords, ctx->prototype->c, params->ncoords);
605 876
606static void dbgv(const char *msg, vector v) 877 penrosectx_cleanup(ctx);
607{
608 printf("%s: %s\n", msg, v_debug(v));
609} 878}
610 879
611int main(int argc, const char *argv[]) 880void penrose_tiling_generate(
881 const struct PenrosePatchParams *params, int w, int h,
882 penrose_tile_callback_fn cb, void *cbctx)
612{ 883{
613 vector v = v_unit(); 884 PenroseContext ctx[1];
885 struct PenroseOutputCtx octx[1];
614 886
615 dbgv("unit vector", v); 887 penrose_set_bounds(octx, w, h);
616 v = v_rotate(v, 36); 888 octx->external_cb = cb;
617 dbgv("rotated 36", v); 889 octx->external_cbctx = cbctx;
618 v = v_scale(v, 2);
619 dbgv("scaled x2", v);
620 v = v_shrinkphi(v);
621 dbgv("shrunk phi", v);
622 v = v_rotate(v, -36);
623 dbgv("rotated -36", v);
624 890
625 return 0; 891 penrosectx_init_from_params(ctx, params);
892 penrosectx_generate(ctx, inbounds, octx, really_output_tile, octx);
893 penrosectx_cleanup(ctx);
626} 894}
627
628#endif
629/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/penrose.h b/apps/plugins/puzzles/src/penrose.h
index ba5ae16f2c..e6e927d618 100644
--- a/apps/plugins/puzzles/src/penrose.h
+++ b/apps/plugins/puzzles/src/penrose.h
@@ -1,59 +1,86 @@
1/* penrose.h 1#ifndef PUZZLES_PENROSE_H
2 * 2#define PUZZLES_PENROSE_H
3 * Penrose tiling functions.
4 *
5 * Provides an interface with which to generate Penrose tilings
6 * by recursive subdivision of an initial tile of choice (one of the
7 * four sets of two pairs kite/dart, or thin/thick rhombus).
8 *
9 * You supply a callback function and a context pointer, which is
10 * called with each tile in turn: you choose how many times to recurse.
11 */
12 3
13#ifndef _PENROSE_H 4struct PenrosePatchParams {
14#define _PENROSE_H 5 /*
6 * A patch of Penrose tiling is identified by giving
7 *
8 * - the coordinates of the starting triangle, using a
9 * combinatorial coordinate system
10 *
11 * - which vertex of that triangle is at the centre point of the
12 * tiling
13 *
14 * - the orientation of the triangle's base edge, as a number
15 * from 0 to 9, measured in tenths of a turn
16 *
17 * Coordinates are a sequence of letters. For a P2 tiling all
18 * letters are from the set {A,B,U,V}; for P3, {C,D,X,Y}.
19 */
20 unsigned start_vertex;
21 int orientation;
22 size_t ncoords;
23 char *coords;
24};
15 25
16#ifndef PHI 26#ifndef PENROSE_ENUM_DEFINED
17#define PHI 1.6180339887 27#define PENROSE_ENUM_DEFINED
28enum { PENROSE_P2, PENROSE_P3 };
18#endif 29#endif
19 30
20typedef struct vector vector; 31bool penrose_valid_letter(char c, int which);
21
22double v_x(vector *vs, int i);
23double v_y(vector *vs, int i);
24 32
25typedef struct penrose_state penrose_state; 33/*
26 34 * Fill in PenrosePatchParams with a randomly selected set of
27/* Return non-zero to clip the tree here (i.e. not recurse 35 * coordinates, in enough detail to generate a patch of tiling filling
28 * below this tile). 36 * a w x h area.
37 *
38 * Units of measurement: the tiling will be oriented such that
39 * horizontal tile edges are possible (and therefore vertical ones are
40 * not). Therefore, all x-coordinates will be rational linear
41 * combinations of 1 and sqrt(5), and all y-coordinates will be
42 * sin(pi/5) times such a rational linear combination. By scaling up
43 * appropriately we can turn those rational combinations into
44 * _integer_ combinations, so we do. Therefore, w is measured in units
45 * of 1/4, and h is measured in units of sin(pi/5)/2, on a scale where
46 * a length of 1 corresponds to the legs of the acute isosceles
47 * triangles in the tiling (hence, the long edges of P2 kites and
48 * darts, or all edges of P3 rhombs).
29 * 49 *
30 * Parameters are state, vector array, npoints, depth. 50 * (In case it's useful, the y scale factor sin(pi/5)/2 is an
31 * ctx is inside state. 51 * algebraic number. Its minimal polynomial is 256x^4 - 80x^2 + 5.)
52 *
53 * The 'coords' field of the structure will be filled in with a new
54 * dynamically allocated array. Any previous pointer in that field
55 * will be overwritten.
32 */ 56 */
33typedef int (*tile_callback)(penrose_state *, vector *, int, int); 57void penrose_tiling_randomise(struct PenrosePatchParams *params, int which,
34 58 int w, int h, random_state *rs);
35struct penrose_state {
36 int start_size; /* initial side length */
37 int max_depth; /* Recursion depth */
38
39 tile_callback new_tile;
40 void *ctx; /* for callback */
41};
42 59
43enum { PENROSE_P2, PENROSE_P3 }; 60/*
44 61 * Validate a PenrosePatchParams to ensure it contains no illegal
45extern int penrose(penrose_state *state, int which, int angle); 62 * coordinates. Returns NULL if it's acceptable, or an error string if
63 * not.
64 */
65const char *penrose_tiling_params_invalid(
66 const struct PenrosePatchParams *params, int which);
46 67
47/* Returns the side-length of a penrose tile at recursion level 68/*
48 * gen, given a starting side length. */ 69 * Generate the actual set of Penrose tiles from a PenrosePatchParams,
49extern double penrose_side_length(double start_size, int depth); 70 * passing each one to a callback. The callback receives the vertices
71 * of each point, in the form of an array of 4*4 integers. Each vertex
72 * is represented by four consecutive integers in this array, with the
73 * first two giving the x coordinate and the last two the y
74 * coordinate. Each pair of integers a,b represent a single coordinate
75 * whose value is a + b*sqrt(5). The units of measurement for x and y
76 * are as described above.
77 */
78typedef void (*penrose_tile_callback_fn)(void *ctx, const int *coords);
50 79
51/* Returns the count of each type of tile at a given recursion depth. */ 80#define PENROSE_NVERTICES 4
52extern void penrose_count_tiles(int gen, int *nlarge, int *nsmall);
53 81
54/* Calculate start size and recursion depth required to produce a 82void penrose_tiling_generate(
55 * width-by-height sized patch of penrose tiles with the given tilesize */ 83 const struct PenrosePatchParams *params, int w, int h,
56extern void penrose_calculate_size(int which, int tilesize, int w, int h, 84 penrose_tile_callback_fn cb, void *cbctx);
57 double *required_radius, int *start_size, int *depth);
58 85
59#endif 86#endif
diff --git a/apps/plugins/puzzles/src/printing.c b/apps/plugins/puzzles/src/printing.c
index d1a3badaaa..0a911949a7 100644
--- a/apps/plugins/puzzles/src/printing.c
+++ b/apps/plugins/puzzles/src/printing.c
@@ -10,6 +10,7 @@
10struct puzzle { 10struct puzzle {
11 const game *game; 11 const game *game;
12 game_params *par; 12 game_params *par;
13 game_ui *ui;
13 game_state *st; 14 game_state *st;
14 game_state *st2; 15 game_state *st2;
15}; 16};
@@ -56,6 +57,7 @@ void document_free(document *doc)
56 57
57 for (i = 0; i < doc->npuzzles; i++) { 58 for (i = 0; i < doc->npuzzles; i++) {
58 doc->puzzles[i].game->free_params(doc->puzzles[i].par); 59 doc->puzzles[i].game->free_params(doc->puzzles[i].par);
60 doc->puzzles[i].game->free_ui(doc->puzzles[i].ui);
59 doc->puzzles[i].game->free_game(doc->puzzles[i].st); 61 doc->puzzles[i].game->free_game(doc->puzzles[i].st);
60 if (doc->puzzles[i].st2) 62 if (doc->puzzles[i].st2)
61 doc->puzzles[i].game->free_game(doc->puzzles[i].st2); 63 doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
@@ -75,7 +77,7 @@ void document_free(document *doc)
75 * another sheet (typically the solution to the first game_state). 77 * another sheet (typically the solution to the first game_state).
76 */ 78 */
77void document_add_puzzle(document *doc, const game *game, game_params *par, 79void document_add_puzzle(document *doc, const game *game, game_params *par,
78 game_state *st, game_state *st2) 80 game_ui *ui, game_state *st, game_state *st2)
79{ 81{
80 if (doc->npuzzles >= doc->puzzlesize) { 82 if (doc->npuzzles >= doc->puzzlesize) {
81 doc->puzzlesize += 32; 83 doc->puzzlesize += 32;
@@ -83,6 +85,7 @@ void document_add_puzzle(document *doc, const game *game, game_params *par,
83 } 85 }
84 doc->puzzles[doc->npuzzles].game = game; 86 doc->puzzles[doc->npuzzles].game = game;
85 doc->puzzles[doc->npuzzles].par = par; 87 doc->puzzles[doc->npuzzles].par = par;
88 doc->puzzles[doc->npuzzles].ui = ui;
86 doc->puzzles[doc->npuzzles].st = st; 89 doc->puzzles[doc->npuzzles].st = st;
87 doc->puzzles[doc->npuzzles].st2 = st2; 90 doc->puzzles[doc->npuzzles].st2 = st2;
88 doc->npuzzles++; 91 doc->npuzzles++;
@@ -96,7 +99,11 @@ static void get_puzzle_size(const document *doc, struct puzzle *pz,
96 float ww, hh, ourscale; 99 float ww, hh, ourscale;
97 100
98 /* Get the preferred size of the game, in mm. */ 101 /* Get the preferred size of the game, in mm. */
99 pz->game->print_size(pz->par, &ww, &hh); 102 {
103 game_ui *ui = pz->game->new_ui(pz->st);
104 pz->game->print_size(pz->par, ui, &ww, &hh);
105 pz->game->free_ui(ui);
106 }
100 107
101 /* Adjust for user-supplied scale factor. */ 108 /* Adjust for user-supplied scale factor. */
102 ourscale = doc->userscale; 109 ourscale = doc->userscale;
@@ -270,9 +277,9 @@ void document_print_page(const document *doc, drawing *dr, int page_nr)
270 * permit each game to choose its own?) 277 * permit each game to choose its own?)
271 */ 278 */
272 tilesize = 512; 279 tilesize = 512;
273 pz->game->compute_size(pz->par, tilesize, &pixw, &pixh); 280 pz->game->compute_size(pz->par, tilesize, pz->ui, &pixw, &pixh);
274 print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale); 281 print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
275 pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize); 282 pz->game->print(dr, pass == 0 ? pz->st : pz->st2, pz->ui, tilesize);
276 print_end_puzzle(dr); 283 print_end_puzzle(dr);
277 } 284 }
278 285
diff --git a/apps/plugins/puzzles/src/ps.c b/apps/plugins/puzzles/src/ps.c
deleted file mode 100644
index ab8a1589f4..0000000000
--- a/apps/plugins/puzzles/src/ps.c
+++ /dev/null
@@ -1,432 +0,0 @@
1/*
2 * ps.c: PostScript printing functions.
3 */
4
5#include <stdio.h>
6#include <stdarg.h>
7#include <string.h>
8#include <assert.h>
9
10#include "puzzles.h"
11
12struct psdata {
13 FILE *fp;
14 bool colour;
15 int ytop;
16 bool clipped;
17 float hatchthick, hatchspace;
18 int gamewidth, gameheight;
19 drawing *drawing;
20};
21
22static void ps_printf(psdata *ps, const char *fmt, ...)
23{
24 va_list ap;
25
26 va_start(ap, fmt);
27 vfprintf(ps->fp, fmt, ap);
28 va_end(ap);
29}
30
31static void ps_fill(psdata *ps, int colour)
32{
33 int hatch;
34 float r, g, b;
35
36 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
37
38 if (hatch < 0) {
39 if (ps->colour)
40 ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b);
41 else
42 ps_printf(ps, "%g setgray fill\n", r);
43 } else {
44 /* Clip to the region. */
45 ps_printf(ps, "gsave clip\n");
46 /* Hatch the entire game printing area. */
47 ps_printf(ps, "newpath\n");
48 if (hatch == HATCH_VERT || hatch == HATCH_PLUS)
49 ps_printf(ps, "0 %g %d {\n"
50 " 0 moveto 0 %d rlineto\n"
51 "} for\n", ps->hatchspace, ps->gamewidth,
52 ps->gameheight);
53 if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS)
54 ps_printf(ps, "0 %g %d {\n"
55 " 0 exch moveto %d 0 rlineto\n"
56 "} for\n", ps->hatchspace, ps->gameheight,
57 ps->gamewidth);
58 if (hatch == HATCH_SLASH || hatch == HATCH_X)
59 ps_printf(ps, "%d %g %d {\n"
60 " 0 moveto %d dup rlineto\n"
61 "} for\n", -ps->gameheight, ps->hatchspace * ROOT2,
62 ps->gamewidth, max(ps->gamewidth, ps->gameheight));
63 if (hatch == HATCH_BACKSLASH || hatch == HATCH_X)
64 ps_printf(ps, "0 %g %d {\n"
65 " 0 moveto %d neg dup neg rlineto\n"
66 "} for\n", ps->hatchspace * ROOT2,
67 ps->gamewidth+ps->gameheight,
68 max(ps->gamewidth, ps->gameheight));
69 ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n",
70 ps->hatchthick);
71 }
72}
73
74static void ps_setcolour_internal(psdata *ps, int colour, const char *suffix)
75{
76 int hatch;
77 float r, g, b;
78
79 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
80
81 /*
82 * Stroking in hatched colours is not permitted.
83 */
84 assert(hatch < 0);
85
86 if (ps->colour)
87 ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix);
88 else
89 ps_printf(ps, "%g setgray%s\n", r, suffix);
90}
91
92static void ps_setcolour(psdata *ps, int colour)
93{
94 ps_setcolour_internal(ps, colour, "");
95}
96
97static void ps_stroke(psdata *ps, int colour)
98{
99 ps_setcolour_internal(ps, colour, " stroke");
100}
101
102static void ps_draw_text(void *handle, int x, int y, int fonttype,
103 int fontsize, int align, int colour,
104 const char *text)
105{
106 psdata *ps = (psdata *)handle;
107
108 y = ps->ytop - y;
109 ps_setcolour(ps, colour);
110 ps_printf(ps, "/%s findfont %d scalefont setfont\n",
111 fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1",
112 fontsize);
113 if (align & ALIGN_VCENTRE) {
114 ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
115 " pathbbox\n"
116 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
117 y, x);
118 } else {
119 ps_printf(ps, "%d %d moveto\n", x, y);
120 }
121 ps_printf(ps, "(");
122 while (*text) {
123 if (*text == '\\' || *text == '(' || *text == ')')
124 ps_printf(ps, "\\");
125 ps_printf(ps, "%c", *text);
126 text++;
127 }
128 ps_printf(ps, ") ");
129 if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT))
130 ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n",
131 (align & ALIGN_HCENTRE) ? "2 div " : "");
132 else
133 ps_printf(ps, "show\n");
134}
135
136static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
137{
138 psdata *ps = (psdata *)handle;
139
140 y = ps->ytop - y;
141 /*
142 * Offset by half a pixel for the exactness requirement.
143 */
144 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
145 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
146 ps_fill(ps, colour);
147}
148
149static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
150 int colour)
151{
152 psdata *ps = (psdata *)handle;
153
154 y1 = ps->ytop - y1;
155 y2 = ps->ytop - y2;
156 ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2);
157 ps_stroke(ps, colour);
158}
159
160static void ps_draw_polygon(void *handle, int *coords, int npoints,
161 int fillcolour, int outlinecolour)
162{
163 psdata *ps = (psdata *)handle;
164
165 int i;
166
167 ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]);
168
169 for (i = 1; i < npoints; i++)
170 ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]);
171
172 ps_printf(ps, "closepath\n");
173
174 if (fillcolour >= 0) {
175 ps_printf(ps, "gsave\n");
176 ps_fill(ps, fillcolour);
177 ps_printf(ps, "grestore\n");
178 }
179 ps_stroke(ps, outlinecolour);
180}
181
182static void ps_draw_circle(void *handle, int cx, int cy, int radius,
183 int fillcolour, int outlinecolour)
184{
185 psdata *ps = (psdata *)handle;
186
187 cy = ps->ytop - cy;
188
189 ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius);
190
191 if (fillcolour >= 0) {
192 ps_printf(ps, "gsave\n");
193 ps_fill(ps, fillcolour);
194 ps_printf(ps, "grestore\n");
195 }
196 ps_stroke(ps, outlinecolour);
197}
198
199static void ps_unclip(void *handle)
200{
201 psdata *ps = (psdata *)handle;
202
203 assert(ps->clipped);
204 ps_printf(ps, "grestore\n");
205 ps->clipped = false;
206}
207
208static void ps_clip(void *handle, int x, int y, int w, int h)
209{
210 psdata *ps = (psdata *)handle;
211
212 if (ps->clipped)
213 ps_unclip(ps);
214
215 y = ps->ytop - y;
216 /*
217 * Offset by half a pixel for the exactness requirement.
218 */
219 ps_printf(ps, "gsave\n");
220 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
221 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
222 ps_printf(ps, "clip\n");
223 ps->clipped = true;
224}
225
226static void ps_line_width(void *handle, float width)
227{
228 psdata *ps = (psdata *)handle;
229
230 ps_printf(ps, "%g setlinewidth\n", width);
231}
232
233static void ps_line_dotted(void *handle, bool dotted)
234{
235 psdata *ps = (psdata *)handle;
236
237 if (dotted) {
238 ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n");
239 } else {
240 ps_printf(ps, "[ ] 0 setdash\n");
241 }
242}
243
244static char *ps_text_fallback(void *handle, const char *const *strings,
245 int nstrings)
246{
247 /*
248 * We can handle anything in ISO 8859-1, and we'll manually
249 * translate it out of UTF-8 for the purpose.
250 */
251 int i, maxlen;
252 char *ret;
253
254 maxlen = 0;
255 for (i = 0; i < nstrings; i++) {
256 int len = strlen(strings[i]);
257 if (maxlen < len) maxlen = len;
258 }
259
260 ret = snewn(maxlen + 1, char);
261
262 for (i = 0; i < nstrings; i++) {
263 const char *p = strings[i];
264 char *q = ret;
265
266 while (*p) {
267 int c = (unsigned char)*p++;
268 if (c < 0x80) {
269 *q++ = c; /* ASCII */
270 } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) {
271 *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */
272 } else {
273 break;
274 }
275 }
276
277 if (!*p) {
278 *q = '\0';
279 return ret;
280 }
281 }
282
283 assert(!"Should never reach here");
284 return NULL;
285}
286
287static void ps_begin_doc(void *handle, int pages)
288{
289 psdata *ps = (psdata *)handle;
290
291 fputs("%!PS-Adobe-3.0\n", ps->fp);
292 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
293 fputs("%%DocumentData: Clean7Bit\n", ps->fp);
294 fputs("%%LanguageLevel: 1\n", ps->fp);
295 fprintf(ps->fp, "%%%%Pages: %d\n", pages);
296 fputs("%%DocumentNeededResources:\n", ps->fp);
297 fputs("%%+ font Helvetica\n", ps->fp);
298 fputs("%%+ font Courier\n", ps->fp);
299 fputs("%%EndComments\n", ps->fp);
300 fputs("%%BeginSetup\n", ps->fp);
301 fputs("%%IncludeResource: font Helvetica\n", ps->fp);
302 fputs("%%IncludeResource: font Courier\n", ps->fp);
303 fputs("%%EndSetup\n", ps->fp);
304 fputs("%%BeginProlog\n", ps->fp);
305 /*
306 * Re-encode Helvetica and Courier into ISO-8859-1, which gives
307 * us times and divide signs - and also (according to the
308 * Language Reference Manual) a bonus in that the ASCII '-' code
309 * point now points to a minus sign instead of a hyphen.
310 */
311 fputs("/Helvetica findfont " /* get the font dictionary */
312 "dup maxlength dict dup begin " /* create and open a new dict */
313 "exch " /* move the original font to top of stack */
314 "{1 index /FID ne {def} {pop pop} ifelse} forall "
315 /* copy everything except FID */
316 "/Encoding ISOLatin1Encoding def "
317 /* set the thing we actually wanted to change */
318 "/FontName /Helvetica-L1 def " /* set a new font name */
319 "FontName end exch definefont" /* and define the font */
320 "\n", ps->fp);
321 fputs("/Courier findfont " /* get the font dictionary */
322 "dup maxlength dict dup begin " /* create and open a new dict */
323 "exch " /* move the original font to top of stack */
324 "{1 index /FID ne {def} {pop pop} ifelse} forall "
325 /* copy everything except FID */
326 "/Encoding ISOLatin1Encoding def "
327 /* set the thing we actually wanted to change */
328 "/FontName /Courier-L1 def " /* set a new font name */
329 "FontName end exch definefont" /* and define the font */
330 "\n", ps->fp);
331 fputs("%%EndProlog\n", ps->fp);
332}
333
334static void ps_begin_page(void *handle, int number)
335{
336 psdata *ps = (psdata *)handle;
337
338 fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
339 number, number, 72.0 / 25.4);
340}
341
342static void ps_begin_puzzle(void *handle, float xm, float xc,
343 float ym, float yc, int pw, int ph, float wmm)
344{
345 psdata *ps = (psdata *)handle;
346
347 fprintf(ps->fp, "gsave\n"
348 "clippath flattenpath pathbbox pop pop translate\n"
349 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
350 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
351 "%g dup scale\n"
352 "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph);
353 ps->ytop = ph;
354 ps->clipped = false;
355 ps->gamewidth = pw;
356 ps->gameheight = ph;
357 ps->hatchthick = 0.2 * pw / wmm;
358 ps->hatchspace = 1.0 * pw / wmm;
359}
360
361static void ps_end_puzzle(void *handle)
362{
363 psdata *ps = (psdata *)handle;
364
365 fputs("grestore\n", ps->fp);
366}
367
368static void ps_end_page(void *handle, int number)
369{
370 psdata *ps = (psdata *)handle;
371
372 fputs("restore grestore showpage\n", ps->fp);
373}
374
375static void ps_end_doc(void *handle)
376{
377 psdata *ps = (psdata *)handle;
378
379 fputs("%%EOF\n", ps->fp);
380}
381
382static const struct drawing_api ps_drawing = {
383 ps_draw_text,
384 ps_draw_rect,
385 ps_draw_line,
386 ps_draw_polygon,
387 ps_draw_circle,
388 NULL /* draw_update */,
389 ps_clip,
390 ps_unclip,
391 NULL /* start_draw */,
392 NULL /* end_draw */,
393 NULL /* status_bar */,
394 NULL /* blitter_new */,
395 NULL /* blitter_free */,
396 NULL /* blitter_save */,
397 NULL /* blitter_load */,
398 ps_begin_doc,
399 ps_begin_page,
400 ps_begin_puzzle,
401 ps_end_puzzle,
402 ps_end_page,
403 ps_end_doc,
404 ps_line_width,
405 ps_line_dotted,
406 ps_text_fallback,
407};
408
409psdata *ps_init(FILE *outfile, bool colour)
410{
411 psdata *ps = snew(psdata);
412
413 ps->fp = outfile;
414 ps->colour = colour;
415 ps->ytop = 0;
416 ps->clipped = false;
417 ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0;
418 ps->drawing = drawing_new(&ps_drawing, NULL, ps);
419
420 return ps;
421}
422
423void ps_free(psdata *ps)
424{
425 drawing_free(ps->drawing);
426 sfree(ps);
427}
428
429drawing *ps_drawing_api(psdata *ps)
430{
431 return ps->drawing;
432}
diff --git a/apps/plugins/puzzles/src/puzzles.but b/apps/plugins/puzzles/src/puzzles.but
index ee519b8aa1..0eb3511cc0 100644
--- a/apps/plugins/puzzles/src/puzzles.but
+++ b/apps/plugins/puzzles/src/puzzles.but
@@ -36,7 +36,7 @@
36 36
37This is a collection of small one-player puzzle games. 37This is a collection of small one-player puzzle games.
38 38
39\copyright This manual is copyright 2004-2014 Simon Tatham. All rights 39\copyright This manual is copyright 2004-2024 Simon Tatham. All rights
40reserved. You may distribute this documentation under the MIT licence. 40reserved. You may distribute this documentation under the MIT licence.
41See \k{licence} for the licence text in full. 41See \k{licence} for the licence text in full.
42 42
@@ -130,12 +130,12 @@ current puzzle. (Only for puzzles which make sense to print, of
130course \dash it's hard to think of a sensible printable representation 130course \dash it's hard to think of a sensible printable representation
131of Fifteen!) 131of Fifteen!)
132 132
133\dt \ii\e{Undo} (\q{U}, Ctrl+\q{Z}, Ctrl+\q{_}) 133\dt \ii\e{Undo} (\q{U}, Ctrl+\q{Z}, Ctrl+\q{_}, \q{*})
134 134
135\dd Undoes a single move. (You can undo moves back to the start of the 135\dd Undoes a single move. (You can undo moves back to the start of the
136session.) 136session.)
137 137
138\dt \ii\e{Redo} (\q{R}, Ctrl+\q{R}) 138\dt \ii\e{Redo} (\q{R}, Ctrl+\q{R}, \q{#})
139 139
140\dd Redoes a previously undone move. 140\dd Redoes a previously undone move.
141 141
@@ -177,6 +177,22 @@ solving it yourself after seeing the answer, you can just press Undo.
177 177
178\dd Closes the application entirely. 178\dd Closes the application entirely.
179 179
180\dt \i\e{Preferences}
181
182\dd Where supported, brings up a dialog allowing you to configure
183personal preferences about a particular game. Some of these
184preferences will be specific to a particular game; others will be
185common to all games.
186
187\lcont{
188
189One option common to all games allows you to turn off the one-key
190shortcuts like \q{N} for new game or \q{Q} for quit, so that there's
191less chance of hitting them by accident. You can still access the same
192shortcuts with the Ctrl key.
193
194}
195
180\H{common-id} Specifying games with the \ii{game ID} 196\H{common-id} Specifying games with the \ii{game ID}
181 197
182There are two ways to save a game specification out of a puzzle and 198There are two ways to save a game specification out of a puzzle and
@@ -348,6 +364,11 @@ in which case its behaviour is slightly different; see below.
348 364
349} 365}
350 366
367\dt \cw{--delete-prefs}
368
369\dd This option causes the puzzle to delete the configuration file in
370which its user preferences were stored, if there is one.
371
351\dt \I{printing, on Unix}\cw{--print }\e{w}\cw{x}\e{h} 372\dt \I{printing, on Unix}\cw{--print }\e{w}\cw{x}\e{h}
352 373
353\dd If this option is specified, instead of a puzzle being displayed, 374\dd If this option is specified, instead of a puzzle being displayed,
@@ -535,6 +556,16 @@ feature and risk having ambiguous puzzles. (Also, finding \e{all}
535the possible solutions can be an additional challenge for an 556the possible solutions can be an additional challenge for an
536advanced player.) 557advanced player.)
537 558
559\H{net-prefs} \I{preferences, for Net}Net user preferences
560
561On platforms that support user preferences, the \q{Preferences} option
562on the \q{Game} menu will let you configure when loops are highlighted
563as errors. By default, they're always highlighted; by changing this
564option, you can ask for a loop to be highlighted only if every tile
565forming part of the loop is locked. This avoids the loop highlighting
566acting as a spoiler for available deductions about squares you haven't
567even looked at yet.
568
538 569
539\C{cube} \i{Cube} 570\C{cube} \i{Cube}
540 571
@@ -601,7 +632,8 @@ respectively.
601\cfg{winhelp-topic}{games.fifteen} 632\cfg{winhelp-topic}{games.fifteen}
602 633
603The old ones are the best: this is the good old \q{\i{15-puzzle}} 634The old ones are the best: this is the good old \q{\i{15-puzzle}}
604with sliding tiles. You have a 4\by\.4 square grid; 15 squares 635with sliding tiles, which dates from the 1870s.
636You have a 4\by\.4 square grid; 15 squares
605contain numbered tiles, and the sixteenth is empty. Your move is to 637contain numbered tiles, and the sixteenth is empty. Your move is to
606choose a tile next to the empty space, and slide it into the space. 638choose a tile next to the empty space, and slide it into the space.
607The aim is to end up with the tiles in numerical order, with the 639The aim is to end up with the tiles in numerical order, with the
@@ -620,8 +652,9 @@ A left-click with the mouse in the row or column containing the empty
620space will move as many tiles as necessary to move the space to the 652space will move as many tiles as necessary to move the space to the
621mouse pointer. 653mouse pointer.
622 654
623The arrow keys will move a tile adjacent to the space in the direction 655By default, the arrow keys will move a tile adjacent to the space in
624indicated (moving the space in the \e{opposite} direction). 656the direction indicated (moving the space in the \e{opposite}
657direction).
625 658
626Pressing \q{h} will make a suggested move. Pressing \q{h} enough 659Pressing \q{h} will make a suggested move. Pressing \q{h} enough
627times will solve the game, but it may scramble your progress while 660times will solve the game, but it may scramble your progress while
@@ -635,6 +668,18 @@ The only options available from the \q{Custom...} option on the \q{Type}
635menu are \e{Width} and \e{Height}, which are self-explanatory. (Once 668menu are \e{Width} and \e{Height}, which are self-explanatory. (Once
636you've changed these, it's not a \q{15-puzzle} any more, of course!) 669you've changed these, it's not a \q{15-puzzle} any more, of course!)
637 670
671\H{fifteen-prefs} \I{preferences, for Fifteen}Fifteen user preferences
672
673On platforms that support user preferences, the \q{Preferences} option
674on the \q{Game} menu will let you configure the sense of the arrow
675keys. With the default setting, \q{Move the tile}, the arrow key you
676press indicates the direction that you want a tile to move, so that
677(for example) if you want to move the tile left of the gap rightwards
678into the gap, you'd press Right. With the opposite setting, \q{Move
679the gap}, the behaviour of the arrow keys is reversed, and you would
680press Left to move the tile left of the gap into the gap, so that the
681\e{gap} ends up one square left of where it was.
682
638 683
639\C{sixteen} \i{Sixteen} 684\C{sixteen} \i{Sixteen}
640 685
@@ -785,8 +830,7 @@ quite as good as hand-crafted puzzles would be, but on the plus side
785you get an inexhaustible supply of puzzles tailored to your own 830you get an inexhaustible supply of puzzles tailored to your own
786specification. 831specification.
787 832
788\B{nikoli-rect} \W{http://www.nikoli.co.jp/en/puzzles/shikaku.html}\cw{http://www.nikoli.co.jp/en/puzzles/shikaku.html} 833\B{nikoli-rect} \W{https://www.nikoli.co.jp/en/puzzles/shikaku/}\cw{https://www.nikoli.co.jp/en/puzzles/shikaku/}
789(beware of Flash)
790 834
791\B{puzzle-palace-rect} \W{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}\cw{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en} 835\B{puzzle-palace-rect} \W{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}\cw{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}
792 836
@@ -880,10 +924,10 @@ Netslide was contributed to this collection by Richard Boulton.
880\cfg{winhelp-topic}{games.pattern} 924\cfg{winhelp-topic}{games.pattern}
881 925
882You have a grid of squares, which must all be filled in either black 926You have a grid of squares, which must all be filled in either black
883or white. Beside each row of the grid are listed the lengths of the 927or white. Beside each row of the grid are listed, in order, the
884runs of black squares on that row; above each column are listed the 928lengths of the runs of black squares on that row; above each column
885lengths of the runs of black squares in that column. Your aim is to 929are listed, in order, the lengths of the runs of black squares in that
886fill in the entire grid black or white. 930column. Your aim is to fill in the entire grid black or white.
887 931
888I first saw this puzzle form around 1995, under the name 932I first saw this puzzle form around 1995, under the name
889\q{\i{nonograms}}. I've seen it in various places since then, under 933\q{\i{nonograms}}. I've seen it in various places since then, under
@@ -977,8 +1021,7 @@ of the modern form of the puzzle, and it was first published in
977of the history of the puzzle can be found on Wikipedia 1021of the history of the puzzle can be found on Wikipedia
978\k{wikipedia-solo}. 1022\k{wikipedia-solo}.
979 1023
980\B{nikoli-solo} \W{http://www.nikoli.co.jp/en/puzzles/sudoku.html}\cw{http://www.nikoli.co.jp/en/puzzles/sudoku.html} 1024\B{nikoli-solo} \W{https://www.nikoli.co.jp/en/puzzles/sudoku/}\cw{https://www.nikoli.co.jp/en/puzzles/sudoku/}
981(beware of Flash)
982 1025
983\B{wikipedia-solo} \W{http://en.wikipedia.org/wiki/Sudoku}\cw{http://en.wikipedia.org/wiki/Sudoku} 1026\B{wikipedia-solo} \W{http://en.wikipedia.org/wiki/Sudoku}\cw{http://en.wikipedia.org/wiki/Sudoku}
984 1027
@@ -1337,10 +1380,15 @@ after marking.
1337 1380
1338Alternatively, with the keyboard, the up and down cursor keys can be 1381Alternatively, with the keyboard, the up and down cursor keys can be
1339used to select a peg colour, the left and right keys to select a 1382used to select a peg colour, the left and right keys to select a
1340peg position, and the space bar or Enter key to place a peg of the 1383peg position, and the Enter key to place a peg of the
1341selected colour in the chosen position. \q{D} or Backspace removes a 1384selected colour in the chosen position. \q{D} or Backspace removes a
1342peg, and Space adds a hold marker. 1385peg, and Space adds a hold marker.
1343 1386
1387The number keys can also be used to insert pegs: \q{1} inserts the
1388top-most colour, \q{2} the second one, and so forth. These also
1389move the peg cursor to the right. Pressing \q{L} will label the
1390pegs with their numbers.
1391
1344Pressing \q{h} or \q{?} will fill the current guess with a suggested 1392Pressing \q{h} or \q{?} will fill the current guess with a suggested
1345guess. Using this is not recommended for 10 or more pegs as it is 1393guess. Using this is not recommended for 10 or more pegs as it is
1346slow. 1394slow.
@@ -1395,6 +1443,13 @@ that, use one extra colour.
1395this increases the search space (making things harder), and is turned on by 1443this increases the search space (making things harder), and is turned on by
1396default. 1444default.
1397 1445
1446\H{guess-prefs} \I{preferences, for Guess}Guess user preferences
1447
1448On platforms that support user preferences, the \q{Preferences} option
1449on the \q{Game} menu will let you configure whether pegs are labelled
1450with their numbers. Unlike the \q{L} key, this will persist between
1451games.
1452
1398 1453
1399\C{pegs} \i{Pegs} 1454\C{pegs} \i{Pegs}
1400 1455
@@ -1442,8 +1497,9 @@ These parameters are available from the \q{Custom...} option on the
1442 1497
1443\dd Controls whether you are given a board of a standard shape or a 1498\dd Controls whether you are given a board of a standard shape or a
1444randomly generated shape. The two standard shapes currently 1499randomly generated shape. The two standard shapes currently
1445supported are \q{Cross} and \q{Octagon} (also commonly known as the 1500supported are \q{Cross} (in various sizes) and \q{Octagon}.
1446English and European traditional board layouts respectively). 1501The 7\by\.7 Cross is the traditional English board layout.
1502The Octagon is the traditional French one.
1447Selecting \q{Random} will give you a different board shape every 1503Selecting \q{Random} will give you a different board shape every
1448time (but always one that is known to have a solution). 1504time (but always one that is known to have a solution).
1449 1505
@@ -1530,6 +1586,11 @@ I originally saw this in the form of a Flash game called \i{Planarity}
1530To move a point, click on it with the left mouse button and drag it 1586To move a point, click on it with the left mouse button and drag it
1531into a new position. 1587into a new position.
1532 1588
1589The cursor keys may also be used to navigate amongst the
1590points. Pressing the Enter key will toggle dragging the
1591currently-highlighted point. Pressing Tab or Space will cycle through
1592all the points.
1593
1533(All the actions described in \k{common-actions} are also available.) 1594(All the actions described in \k{common-actions} are also available.)
1534 1595
1535\H{untangle-parameters} \I{parameters, for Untangle}Untangle parameters 1596\H{untangle-parameters} \I{parameters, for Untangle}Untangle parameters
@@ -1714,7 +1775,7 @@ grid because that would immediately cause a loop.)
1714Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}. 1775Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}.
1715 1776
1716\B{nikoli-slant} 1777\B{nikoli-slant}
1717\W{http://www.nikoli.co.jp/ja/puzzles/gokigen_naname}\cw{http://www.nikoli.co.jp/ja/puzzles/gokigen_naname} 1778\W{https://www.nikoli.co.jp/ja/puzzles/gokigen_naname/}\cw{https://www.nikoli.co.jp/ja/puzzles/gokigen_naname/}
1718(in Japanese) 1779(in Japanese)
1719 1780
1720\H{slant-controls} \i{Slant controls} 1781\H{slant-controls} \i{Slant controls}
@@ -1763,6 +1824,12 @@ don't yet know what that direction is, and this might enable you to
1763deduce something about still other squares.) Even at Hard level, 1824deduce something about still other squares.) Even at Hard level,
1764guesswork and backtracking should never be necessary. 1825guesswork and backtracking should never be necessary.
1765 1826
1827\H{slant-prefs} \I{preferences, for Slant}Slant user preferences
1828
1829On platforms that support user preferences, the \q{Preferences} option
1830on the \q{Game} menu will let you configure which way round the mouse
1831buttons work.
1832
1766 1833
1767\C{lightup} \i{Light Up} 1834\C{lightup} \i{Light Up}
1768 1835
@@ -1792,8 +1859,7 @@ Credit for this puzzle goes to \i{Nikoli} \k{nikoli-lightup}.
1792Light Up was contributed to this collection by James Harvey. 1859Light Up was contributed to this collection by James Harvey.
1793 1860
1794\B{nikoli-lightup} 1861\B{nikoli-lightup}
1795\W{http://www.nikoli.co.jp/en/puzzles/akari.html}\cw{http://www.nikoli.co.jp/en/puzzles/akari.html} 1862\W{https://www.nikoli.co.jp/en/puzzles/akari/}\cw{https://www.nikoli.co.jp/en/puzzles/akari/}
1796(beware of Flash)
1797 1863
1798\H{lightup-controls} \i{Light Up controls} 1864\H{lightup-controls} \i{Light Up controls}
1799 1865
@@ -1847,6 +1913,12 @@ noticeably.)
1847backtracking or guessing, \q{Hard} means that some guesses will 1913backtracking or guessing, \q{Hard} means that some guesses will
1848probably be necessary. 1914probably be necessary.
1849 1915
1916\H{lightup-prefs} \I{preferences, for Light Up}Light Up user preferences
1917
1918On platforms that support user preferences, the \q{Preferences} option
1919on the \q{Game} menu will let you configure whether \q{this is not a
1920light} marks are shown when the square is also lit.
1921
1850 1922
1851\C{map} \i{Map} 1923\C{map} \i{Map}
1852 1924
@@ -1856,7 +1928,7 @@ You are given a map consisting of a number of regions. Your task is
1856to colour each region with one of four colours, in such a way that 1928to colour each region with one of four colours, in such a way that
1857no two regions sharing a boundary have the same colour. You are 1929no two regions sharing a boundary have the same colour. You are
1858provided with some regions already coloured, sufficient to make the 1930provided with some regions already coloured, sufficient to make the
1859remainder of the solution unique. 1931remainder of the solution unique, and these cannot be changed.
1860 1932
1861Only regions which share a length of border are required to be 1933Only regions which share a length of border are required to be
1862different colours. Two regions which meet at only one \e{point} 1934different colours. Two regions which meet at only one \e{point}
@@ -1864,9 +1936,9 @@ different colours. Two regions which meet at only one \e{point}
1864 1936
1865I believe this puzzle is original; I've never seen an implementation 1937I believe this puzzle is original; I've never seen an implementation
1866of it anywhere else. The concept of a \i{four-colouring} puzzle was 1938of it anywhere else. The concept of a \i{four-colouring} puzzle was
1867suggested by Owen Dunn; credit must also go to Nikoli and to Verity 1939suggested by Alexandra Lanes; credit must also go to Nikoli and to Verity
1868Allan for inspiring the train of thought that led to me realising 1940Allan for inspiring the train of thought that led to me realising
1869Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor 1941Alex's suggestion was a viable puzzle. Thanks also to Gareth Taylor
1870for many detailed suggestions. 1942for many detailed suggestions.
1871 1943
1872\H{map-controls} \i{Map controls} 1944\H{map-controls} \i{Map controls}
@@ -1940,6 +2012,12 @@ Unreasonable puzzles may require guessing and backtracking.
1940 2012
1941} 2013}
1942 2014
2015\H{map-prefs} \I{preferences, for Map}Map user preferences
2016
2017On platforms that support user preferences, the \q{Preferences} option
2018on the \q{Game} menu will let you configure the style of the victory
2019flash and also whether the regions start out labelled with numbers.
2020
1943 2021
1944\C{loopy} \i{Loopy} 2022\C{loopy} \i{Loopy}
1945 2023
@@ -1967,8 +2045,7 @@ and subsequently enhanced to handle various types of non-square grid
1967by Lambros Lambrou. 2045by Lambros Lambrou.
1968 2046
1969\B{nikoli-loopy} 2047\B{nikoli-loopy}
1970\W{http://www.nikoli.co.jp/en/puzzles/slitherlink.html}\cw{http://www.nikoli.co.jp/en/puzzles/slitherlink.html} 2048\W{https://www.nikoli.co.jp/en/puzzles/slitherlink/}\cw{https://www.nikoli.co.jp/en/puzzles/slitherlink/}
1971(beware of Flash)
1972 2049
1973\H{loopy-controls} \i{Loopy controls} 2050\H{loopy-controls} \i{Loopy controls}
1974 2051
@@ -2013,6 +2090,34 @@ same; this makes them the least confusing to play.
2013\#{FIXME: what distinguishes Easy, Medium, and Hard? In particular, 2090\#{FIXME: what distinguishes Easy, Medium, and Hard? In particular,
2014when are backtracking/guesswork required, if ever?} 2091when are backtracking/guesswork required, if ever?}
2015 2092
2093\H{loopy-prefs} \I{preferences, for Loopy}Loopy user preferences
2094
2095On platforms that support user preferences, the \q{Preferences} option
2096on the \q{Game} menu will let you configure the following things:
2097
2098\q{Draw excluded grid lines faintly}. This is on by default: when a
2099line of the grid has been explicitly excluded from the solution by
2100right-clicking it, the line is still drawn, just in a faint grey
2101colour. If you turn this option off, excluded lines are not drawn at
2102all.
2103
2104\q{Auto-follow unique paths of edges}. This is off by default. When
2105it's on, clicking to change the status of a single grid line will
2106potentially propagate the change along multiple lines, if one or both
2107ends of the line you clicked connect to only one other line. (The idea
2108is that if two lines meet at a vertex and no other lines do at all,
2109then those lines are either both part of the loop or neither, so
2110there's no reason you should have to click separately to toggle each
2111one.)
2112
2113In the mode \q{Based on grid only}, the effects of a click will only
2114propagate across vertices that have degree 2 in the underlying grid.
2115For example, in the square grid, the effect will \e{only} occur at the
2116four grid corners.
2117
2118In the mode \q{Based on grid and game state}, the propagation will
2119also take account of edges you've already excluded from the solution,
2120so that it will do even more work for you.
2016 2121
2017\C{inertia} \i{Inertia} 2122\C{inertia} \i{Inertia}
2018 2123
@@ -2171,8 +2276,7 @@ Credit for this puzzle goes to \i{Nikoli} \k{nikoli-bridges}.
2171Bridges was contributed to this collection by James Harvey. 2276Bridges was contributed to this collection by James Harvey.
2172 2277
2173\B{nikoli-bridges} 2278\B{nikoli-bridges}
2174\W{http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html}\cw{http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html} 2279\W{https://www.nikoli.co.jp/en/puzzles/Hashiwokakero/}\cw{https://www.nikoli.co.jp/en/puzzles/Hashiwokakero/}
2175(beware of Flash)
2176 2280
2177\H{bridges-controls} \i{Bridges controls} 2281\H{bridges-controls} \i{Bridges controls}
2178 2282
@@ -2217,6 +2321,9 @@ By pressing a number key, you can jump to the nearest island with that
2217number. Letters \q{a}, ..., \q{f} count as 10, ..., 15 and \q{0} as 2321number. Letters \q{a}, ..., \q{f} count as 10, ..., 15 and \q{0} as
221816. 232216.
2219 2323
2324The \q{G} key will draw a grey line between each pair of islands that
2325could be connected with a bridge or non-bridge but are currently not.
2326
2220Violations of the puzzle rules will be marked in red: 2327Violations of the puzzle rules will be marked in red:
2221 2328
2222\b An island with too many bridges will be highlighted in red. 2329\b An island with too many bridges will be highlighted in red.
@@ -2288,6 +2395,13 @@ tightly-packed islands.
2288 2395
2289} 2396}
2290 2397
2398\H{bridges-prefs} \I{preferences, for Bridges}Bridges user preferences
2399
2400On platforms that support user preferences, the \q{Preferences} option
2401on the \q{Game} menu will let you configure whether possible bridge
2402locations are shown. Unlike the \q{G} key, this will persist between
2403games.
2404
2291 2405
2292\C{unequal} \i{Unequal} 2406\C{unequal} \i{Unequal}
2293 2407
@@ -2416,7 +2530,7 @@ English as \q{Spiral Galaxies}.
2416 2530
2417Galaxies was contributed to this collection by James Harvey. 2531Galaxies was contributed to this collection by James Harvey.
2418 2532
2419\B{nikoli-galaxies} \W{http://www.nikoli.co.jp/en/puzzles/astronomical_show.html}\cw{http://www.nikoli.co.jp/en/puzzles/astronomical_show.html} 2533\B{nikoli-galaxies} \W{https://www.nikoli.co.jp/en/puzzles/tentai_show/}\cw{https://www.nikoli.co.jp/en/puzzles/tentai_show/}
2420 2534
2421\H{galaxies-controls} \i{Galaxies controls} 2535\H{galaxies-controls} \i{Galaxies controls}
2422 2536
@@ -2489,7 +2603,7 @@ Credit for this puzzle goes to \i{Nikoli} \k{nikoli-fillomino}.
2489Filling was contributed to this collection by Jonas K\u00F6{oe}lker. 2603Filling was contributed to this collection by Jonas K\u00F6{oe}lker.
2490 2604
2491\B{nikoli-fillomino} 2605\B{nikoli-fillomino}
2492\W{http://www.nikoli.co.jp/en/puzzles/fillomino.html}\cw{http://www.nikoli.co.jp/en/puzzles/fillomino.html} 2606\W{https://www.nikoli.co.jp/en/puzzles/fillomino/}\cw{https://www.nikoli.co.jp/en/puzzles/fillomino/}
2493 2607
2494\H{filling-controls} \I{controls, for Filling}Filling controls 2608\H{filling-controls} \I{controls, for Filling}Filling controls
2495 2609
@@ -2718,6 +2832,14 @@ level, some backtracking will be required, but the solution should
2718still be unique. The remaining levels require increasingly complex 2832still be unique. The remaining levels require increasingly complex
2719reasoning to avoid having to backtrack. 2833reasoning to avoid having to backtrack.
2720 2834
2835\H{towers-prefs} \I{preferences, for Towers}Towers user preferences
2836
2837On platforms that support user preferences, the \q{Preferences} option
2838on the \q{Game} menu will let you configure the style of the game
2839display. If you don't like the three-dimensional mode, selecting
2840\q{2D} will switch to a simpler display style in which towers are
2841shown by just writing their height in the square.
2842
2721 2843
2722\C{singles} \i{Singles} 2844\C{singles} \i{Singles}
2723 2845
@@ -2741,8 +2863,7 @@ Credit for this puzzle goes to \i{Nikoli} \k{nikoli-hitori} who call it
2741Singles was contributed to this collection by James Harvey. 2863Singles was contributed to this collection by James Harvey.
2742 2864
2743\B{nikoli-hitori} 2865\B{nikoli-hitori}
2744\W{http://www.nikoli.com/en/puzzles/hitori.html}\cw{http://www.nikoli.com/en/puzzles/hitori.html} 2866\W{https://www.nikoli.co.jp/en/puzzles/hitori/}\cw{https://www.nikoli.co.jp/en/puzzles/hitori/}
2745(beware of Flash)
2746 2867
2747\H{singles-controls} \i{Singles controls} 2868\H{singles-controls} \i{Singles controls}
2748 2869
@@ -2750,7 +2871,9 @@ Singles was contributed to this collection by James Harvey.
2750 2871
2751Left-clicking on an empty square will colour it black; left-clicking again 2872Left-clicking on an empty square will colour it black; left-clicking again
2752will restore the number. Right-clicking will add a circle (useful for 2873will restore the number. Right-clicking will add a circle (useful for
2753indicating that a cell is definitely not black). 2874indicating that a cell is definitely not black). Clicking outside the
2875grid will toggle whether black squares completely hide the numbers on
2876them, or display them in dark grey.
2754 2877
2755You can also use the cursor keys to move around the grid. Pressing the 2878You can also use the cursor keys to move around the grid. Pressing the
2756return or space keys will turn a square black or add a circle respectively, 2879return or space keys will turn a square black or add a circle respectively,
@@ -2771,6 +2894,12 @@ These parameters are available from the \q{Custom...} option on the
2771 2894
2772\dd Controls the difficulty of the generated puzzle. 2895\dd Controls the difficulty of the generated puzzle.
2773 2896
2897\H{Singles-prefs} \I{preferences, for Singles}Singles user preferences
2898
2899On platforms that support user preferences, the \q{Preferences} option
2900on the \q{Game} menu will let you configure whether numbers on black
2901squares are visible. Unlike clicking outside the grid, this will
2902persist between games.
2774 2903
2775\C{magnets} \i{Magnets} 2904\C{magnets} \i{Magnets}
2776 2905
@@ -2925,6 +3054,13 @@ These parameters are available from the \q{Custom...} option on the
2925(the start at the top left, and the end at the bottom right). If false the start 3054(the start at the top left, and the end at the bottom right). If false the start
2926and end squares are placed randomly (although always both shown). 3055and end squares are placed randomly (although always both shown).
2927 3056
3057\H{signpost-prefs} \I{preferences, for Signpost}Signpost user preferences
3058
3059On platforms that support user preferences, the \q{Preferences} option
3060on the \q{Game} menu will let you configure the style of the victory
3061effect.
3062
3063
2928\C{range} \i{Range} 3064\C{range} \i{Range}
2929 3065
2930\cfg{winhelp-topic}{games.range} 3066\cfg{winhelp-topic}{games.range}
@@ -2959,7 +3095,7 @@ it \q{Kurodoko}, \q{Kuromasu} or \q{Where is Black Cells}.
2959Range was contributed to this collection by Jonas K\u00F6{oe}lker. 3095Range was contributed to this collection by Jonas K\u00F6{oe}lker.
2960 3096
2961\B{nikoli-range} 3097\B{nikoli-range}
2962\W{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html}\cw{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html} 3098\W{https://www.nikoli.co.jp/en/puzzles/kurodoko/}\cw{https://www.nikoli.co.jp/en/puzzles/kurodoko/}
2963 3099
2964\H{range-controls} \I{controls, for Range}Range controls 3100\H{range-controls} \I{controls, for Range}Range controls
2965 3101
@@ -2987,6 +3123,13 @@ These parameters are available from the \q{Custom...} option on the
2987 3123
2988\dd Size of grid in squares. 3124\dd Size of grid in squares.
2989 3125
3126\H{range-prefs} \I{preferences, for Range}Range user preferences
3127
3128On platforms that support user preferences, the \q{Preferences} option
3129on the \q{Game} menu will let you configure which way round the mouse
3130buttons work.
3131
3132
2990\C{pearl} \i{Pearl} 3133\C{pearl} \i{Pearl}
2991 3134
2992\cfg{winhelp-topic}{games.pearl} 3135\cfg{winhelp-topic}{games.pearl}
@@ -3019,8 +3162,7 @@ Credit for this puzzle goes to \i{Nikoli}, who call it \q{Masyu}.
3019Thanks to James Harvey for assistance with the implementation. 3162Thanks to James Harvey for assistance with the implementation.
3020 3163
3021\B{nikoli-pearl} 3164\B{nikoli-pearl}
3022\W{http://www.nikoli.co.jp/en/puzzles/masyu.html}\cw{http://www.nikoli.co.jp/en/puzzles/masyu.html} 3165\W{https://www.nikoli.co.jp/en/puzzles/masyu/}\cw{https://www.nikoli.co.jp/en/puzzles/masyu/}
3023(beware of Flash)
3024 3166
3025\H{pearl-controls} \I{controls, for Pearl}Pearl controls 3167\H{pearl-controls} \I{controls, for Pearl}Pearl controls
3026 3168
@@ -3054,6 +3196,41 @@ right click, respectively, on the edge in the direction of the key.
3054These parameters are available from the \q{Custom...} option on the 3196These parameters are available from the \q{Custom...} option on the
3055\q{Type} menu. 3197\q{Type} menu.
3056 3198
3199\dt \e{Width}, \e{Height}
3200
3201\dd Size of grid in squares.
3202
3203\dt \e{Difficulty}
3204
3205\dd Controls the difficulty of the generated puzzle.
3206
3207\dt \e{Allow unsoluble}
3208
3209\dd If this is set, then the game will be generated in the simplest
3210way: every clue square that can possibly be provided will be shown,
3211and the generator will not check whether the puzzle can be uniquely
3212solved.
3213
3214\lcont{
3215
3216This speeds up game generation, and allows much larger grids to be
3217played. At least one possible solution will still always exist, but
3218there's no guarantee that it will be unique, or that it will be
3219possible to deduce it step by step.
3220
3221}
3222
3223\H{pearl-prefs} \I{preferences, for Pearl}Pearl user preferences
3224
3225On platforms that support user preferences, the \q{Preferences} option
3226on the \q{Game} menu will let you configure the style of the game
3227display. \q{Traditional} is the default mode, in which the loop runs
3228between centres of grid squares, and each clue occupies a square.
3229\q{Loopy-style} is an alternative mode that looks more like Loopy
3230(\k{loopy}), in which the loop runs between grid \e{vertices}, and the
3231clues also occupy vertices.
3232
3233
3057\C{undead} \i{Undead} 3234\C{undead} \i{Undead}
3058 3235
3059\cfg{winhelp-topic}{games.undead} 3236\cfg{winhelp-topic}{games.undead}
@@ -3088,12 +3265,13 @@ Undead was contributed to this collection by Steffen Bauer.
3088Undead has a similar control system to Solo, Unequal and Keen. 3265Undead has a similar control system to Solo, Unequal and Keen.
3089 3266
3090To play Undead, click the mouse in any empty square and then type a 3267To play Undead, click the mouse in any empty square and then type a
3091letter on the keyboard indicating the type of monster: \q{G} for a 3268letter or number on the keyboard indicating the type of monster:
3092ghost, \q{V} for a vampire, or \q{Z} for a zombie. If you make a 3269\q{G} or \q{1} for a ghost, \q{V} or \q{2} for a vampire,
3270or \q{Z} or \q{3} for a zombie. If you make a
3093mistake, click the mouse in the incorrect square and press Space to 3271mistake, click the mouse in the incorrect square and press Space to
3094clear it again (or use the Undo feature). 3272clear it again (or use the Undo feature).
3095 3273
3096If you \e{right}-click in a square and then type a letter, the 3274If you \e{right}-click in a square and then type a letter or number, the
3097corresponding monster will be shown in reduced size in that square, as 3275corresponding monster will be shown in reduced size in that square, as
3098a \q{pencil mark}. You can have pencil marks for multiple monsters in 3276a \q{pencil mark}. You can have pencil marks for multiple monsters in
3099the same square. A square containing a full-size monster cannot also 3277the same square. A square containing a full-size monster cannot also
@@ -3106,7 +3284,7 @@ monster, or you can use them as lists of the possible monster in a
3106given square, or anything else you feel like. 3284given square, or anything else you feel like.
3107 3285
3108To erase a single pencil mark, right-click in the square and type 3286To erase a single pencil mark, right-click in the square and type
3109the same letter again. 3287the same letter or number again.
3110 3288
3111All pencil marks in a square are erased when you left-click and type a 3289All pencil marks in a square are erased when you left-click and type a
3112monster letter, or when you left-click and press Space. Right-clicking 3290monster letter, or when you left-click and press Space. Right-clicking
@@ -3114,7 +3292,7 @@ and pressing space will also erase pencil marks.
3114 3292
3115As for Solo, the cursor keys can be used in conjunction with the letter 3293As for Solo, the cursor keys can be used in conjunction with the letter
3116keys to place monsters or pencil marks. Use the cursor keys to move a 3294keys to place monsters or pencil marks. Use the cursor keys to move a
3117highlight around the grid, and type a monster letter to enter it in 3295highlight around the grid, and type a monster letter or number to enter it in
3118the highlighted square. Pressing return toggles the highlight into a 3296the highlighted square. Pressing return toggles the highlight into a
3119mode in which you can enter or remove pencil marks. 3297mode in which you can enter or remove pencil marks.
3120 3298
@@ -3140,6 +3318,13 @@ These parameters are available from the \q{Custom...} option on the
3140 3318
3141\dd Controls the difficulty of the generated puzzle. 3319\dd Controls the difficulty of the generated puzzle.
3142 3320
3321\H{undead-prefs} \I{preferences, for Undead}Undead user preferences
3322
3323On platforms that support user preferences, the \q{Preferences} option
3324on the \q{Game} menu will let you configure whether Undead uses letters
3325or pictures to represent monsters.
3326
3327
3143\C{unruly} \i{Unruly} 3328\C{unruly} \i{Unruly}
3144 3329
3145\cfg{winhelp-topic}{games.unruly} 3330\cfg{winhelp-topic}{games.unruly}
@@ -3329,15 +3514,20 @@ Credit for this puzzle goes to \i{Nikoli}, who call it \q{Five Cells}.
3329Palisade was contributed to this collection by Jonas K\u00F6{oe}lker. 3514Palisade was contributed to this collection by Jonas K\u00F6{oe}lker.
3330 3515
3331\B{nikoli-palisade} 3516\B{nikoli-palisade}
3332\W{http://nikoli.co.jp/en/puzzles/five_cells.html}\cw{http://nikoli.co.jp/en/puzzles/five_cells.html} 3517\W{https://www.nikoli.co.jp/en/puzzles/five_cells/}\cw{https://www.nikoli.co.jp/en/puzzles/five_cells/}
3333 3518
3334\H{palisade-controls} \I{controls, for Palisade}Palisade controls 3519\H{palisade-controls} \I{controls, for Palisade}Palisade controls
3335 3520
3336Left-click to place an edge. Right-click to indicate \q{no edge}. 3521Left-click to place an edge. Right-click to indicate \q{no edge}.
3337Alternatively, the arrow keys will move a keyboard cursor. Holding 3522
3338Control while pressing an arrow key will place an edge. Press 3523Alternatively, the arrow keys will move a keyboard cursor. Depending
3339Shift-arrowkey to switch off an edge. Repeat an action to perform 3524on the \q{Cursor mode} preference (see \k{palisade-prefs}), the cursor
3340its inverse. 3525will either navigate among the grid squares, or along their
3526borders. In \q{Full-grid} mode, hold Control while pressing an arrow
3527key to place an edge, and press Shift-arrowkey to switch off an
3528edge. In \q{Half-grid} mode, press Enter to place an edge, and Space
3529to switch off an edge. In either mode, you can repeat an action to
3530perform its inverse.
3341 3531
3342(All the actions described in \k{common-actions} are also available.) 3532(All the actions described in \k{common-actions} are also available.)
3343 3533
@@ -3354,14 +3544,71 @@ These parameters are available from the \q{Custom...} option on the
3354 3544
3355\dd The size of the regions into which the grid must be subdivided. 3545\dd The size of the regions into which the grid must be subdivided.
3356 3546
3547\H{palisade-prefs} \I{preferences, for Palisade}Palisade user preferences
3548
3549On platforms that support user preferences, the \q{Preferences} option
3550on the \q{Game} menu will let you configure the behavior of the cursor
3551keys to either navigate among full grid squares, or along the borders
3552of the grid squares.
3553
3554\C{mosaic} \i{Mosaic}
3555
3556\cfg{winhelp-topic}{games.mosaic}
3557
3558You are given a grid of squares, which you must colour either black or
3559white.
3560
3561Some squares contain clue numbers. Each clue tells you the number of
3562black squares in the 3\times\.3 region surrounding the clue \dash
3563\e{including} the clue square itself.
3564
3565This game is variously known in other locations as: ArtMosaico, Count
3566and Darken, Cuenta Y Sombrea, Fill-a-Pix, Fill-In, Komsu Karala,
3567Magipic, Majipiku, Mosaico, Mosaik, Mozaiek, Nampre Puzzle,
3568Nurie-Puzzle, Oekaki-Pix, Voisimage.
3569
3570Mosaic was contributed to this collection by Didi Kohen. Colour design
3571by Michal Shomer. The implementation is loosely based on
3572\W{https://github.com/mordechaim/Mosaic}\cw{github.com/mordechaim/Mosaic}.
3573
3574\H{mosaic-controls} \I{controls, for Mosaic}Mosaic controls
3575
3576To play Mosaic, click the mouse in a square to change its colour.
3577Left-clicking an empty square will turn it black, and right-clicking
3578will turn it white. Keep clicking the same button to cycle through the
3579three possible states for the square.
3580
3581If you hold down the mouse button and drag, you can colour multiple
3582cells in a single action.
3583
3584You can also use the cursor keys to move around the grid. Pressing the
3585return or space keys will turn an empty square black or white
3586respectively (and then cycle the colours in the same way as the mouse
3587buttons), and pressing Backspace will reset a square to empty.
3588
3589\H{Mosaic-parameters} \I{parameters, for Mosaic}Mosaic parameters
3590
3591These parameters are available from the \q{Custom...} option on the
3592\q{Type} menu.
3593
3594\dt \e{Width}, \e{Height}
3595
3596\dd Size of grid in squares.
3597
3598\dt \e{Aggressive generation}
3599
3600\dd With this option set, the game generator will try harder to
3601eliminate unnecessary clues on the board. This slows down generation,
3602so it's not recommended for boards larger than, say, 30\times\.30.
3603
3357\A{licence} \I{MIT licence}\ii{Licence} 3604\A{licence} \I{MIT licence}\ii{Licence}
3358 3605
3359This software is \i{copyright} 2004-2014 Simon Tatham. 3606This software is \i{copyright} 2004-2024 Simon Tatham.
3360 3607
3361Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas 3608Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
3362K\u00F6{oe}lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, 3609K\u00F6{oe}lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou,
3363Bernd Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens, Michael 3610Bernd Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens, Michael
3364Quevillon and Asher Gordon. 3611Quevillon, Asher Gordon, Didi Kohen, and Ben Harris.
3365 3612
3366Permission is hereby granted, free of charge, to any person 3613Permission is hereby granted, free of charge, to any person
3367obtaining a copy of this software and associated documentation files 3614obtaining a copy of this software and associated documentation files
diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h
index 29b00ddaf0..44a056d78f 100644
--- a/apps/plugins/puzzles/src/puzzles.h
+++ b/apps/plugins/puzzles/src/puzzles.h
@@ -18,6 +18,9 @@
18#define STR_INT(x) #x 18#define STR_INT(x) #x
19#define STR(x) STR_INT(x) 19#define STR(x) STR_INT(x)
20 20
21/* An upper bound on the length of sprintf'ed integers (signed or unsigned). */
22#define MAX_DIGITS(x) (sizeof(x) * CHAR_BIT / 3 + 2)
23
21/* NB not perfect because they evaluate arguments multiple times. */ 24/* NB not perfect because they evaluate arguments multiple times. */
22#ifndef max 25#ifndef max
23#define max(x,y) ( (x)>(y) ? (x) : (y) ) 26#define max(x,y) ( (x)>(y) ? (x) : (y) )
@@ -69,6 +72,7 @@ enum {
69 (m) == CURSOR_RIGHT || (m) == CURSOR_LEFT ) 72 (m) == CURSOR_RIGHT || (m) == CURSOR_LEFT )
70#define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2) 73#define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2)
71#define IS_UI_FAKE_KEY(m) ( (m) > UI_LOWER_BOUND && (m) < UI_UPPER_BOUND ) 74#define IS_UI_FAKE_KEY(m) ( (m) > UI_LOWER_BOUND && (m) < UI_UPPER_BOUND )
75#define STRIP_BUTTON_MODIFIERS(m) ( (unsigned)(m) & ~MOD_MASK )
72 76
73/* 77/*
74 * Flags in the back end's `flags' word. 78 * Flags in the back end's `flags' word.
@@ -83,14 +87,6 @@ enum {
83#define REQUIRE_NUMPAD ( 1 << 11 ) 87#define REQUIRE_NUMPAD ( 1 << 11 )
84/* end of `flags' word definitions */ 88/* end of `flags' word definitions */
85 89
86#ifdef _WIN32_WCE
87 /* Pocket PC devices have small, portrait screen that requires more vivid colours */
88 #define SMALL_SCREEN
89 #define PORTRAIT_SCREEN
90 #define VIVID_COLOURS
91 #define STYLUS_BASED
92#endif
93
94#define IGNOREARG(x) ( (x) = (x) ) 90#define IGNOREARG(x) ( (x) = (x) )
95 91
96typedef struct frontend frontend; 92typedef struct frontend frontend;
@@ -132,8 +128,13 @@ typedef struct psdata psdata;
132 */ 128 */
133enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END }; 129enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END };
134struct config_item { 130struct config_item {
135 /* Not dynamically allocated */ 131 /* Not dynamically allocated: the GUI display name for the option */
136 const char *name; 132 const char *name;
133 /* Not dynamically allocated: the keyword identifier for the
134 * option. Only examined in the case where this structure is being
135 * used for options that appear in config files, i.e. the
136 * get_prefs method fills this in but configure does not. */
137 const char *kw;
137 /* Value from the above C_* enum */ 138 /* Value from the above C_* enum */
138 int type; 139 int type;
139 union { 140 union {
@@ -151,6 +152,13 @@ struct config_item {
151 */ 152 */
152 const char *choicenames; 153 const char *choicenames;
153 /* 154 /*
155 * choicekws is non-NULL, not dynamically allocated, and
156 * contains a parallel list of keyword strings used to
157 * represent the enumeration in config files. As with 'kw'
158 * above, this is only expected to be set by get_prefs.
159 */
160 const char *choicekws;
161 /*
154 * Indicates the chosen index from the options in 162 * Indicates the chosen index from the options in
155 * choicenames. In the above example, 0==Foo, 1==Bar and 163 * choicenames. In the above example, 0==Foo, 1==Bar and
156 * 2==Baz. 164 * 2==Baz.
@@ -256,8 +264,10 @@ void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
256 int align, int colour, const char *text); 264 int align, int colour, const char *text);
257void draw_rect(drawing *dr, int x, int y, int w, int h, int colour); 265void draw_rect(drawing *dr, int x, int y, int w, int h, int colour);
258void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour); 266void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour);
259void draw_polygon(drawing *dr, int *coords, int npoints, 267void draw_polygon(drawing *dr, const int *coords, int npoints,
260 int fillcolour, int outlinecolour); 268 int fillcolour, int outlinecolour);
269void draw_polygon_fallback(drawing *dr, const int *coords, int npoints,
270 int fillcolour, int outlinecolour);
261void draw_circle(drawing *dr, int cx, int cy, int radius, 271void draw_circle(drawing *dr, int cx, int cy, int radius,
262 int fillcolour, int outlinecolour); 272 int fillcolour, int outlinecolour);
263void draw_thick_line(drawing *dr, float thickness, 273void draw_thick_line(drawing *dr, float thickness,
@@ -307,13 +317,16 @@ void midend_free(midend *me);
307const game *midend_which_game(midend *me); 317const game *midend_which_game(midend *me);
308void midend_set_params(midend *me, game_params *params); 318void midend_set_params(midend *me, game_params *params);
309game_params *midend_get_params(midend *me); 319game_params *midend_get_params(midend *me);
310void midend_size(midend *me, int *x, int *y, bool user_size); 320void midend_size(midend *me, int *x, int *y, bool user_size,
321 double device_pixel_ratio);
311void midend_reset_tilesize(midend *me); 322void midend_reset_tilesize(midend *me);
312void midend_new_game(midend *me); 323void midend_new_game(midend *me);
313void midend_restart_game(midend *me); 324void midend_restart_game(midend *me);
314void midend_stop_anim(midend *me); 325void midend_stop_anim(midend *me);
315bool midend_process_key(midend *me, int x, int y, int button); 326enum { PKR_QUIT = 0, PKR_SOME_EFFECT, PKR_NO_EFFECT, PKR_UNUSED };
327int midend_process_key(midend *me, int x, int y, int button);
316key_label *midend_request_keys(midend *me, int *nkeys); 328key_label *midend_request_keys(midend *me, int *nkeys);
329const char *midend_current_key_label(midend *me, int button);
317void midend_force_redraw(midend *me); 330void midend_force_redraw(midend *me);
318void midend_redraw(midend *me); 331void midend_redraw(midend *me);
319float *midend_colours(midend *me, int *ncolours); 332float *midend_colours(midend *me, int *ncolours);
@@ -322,7 +335,7 @@ void midend_timer(midend *me, float tplus);
322struct preset_menu *midend_get_presets(midend *me, int *id_limit); 335struct preset_menu *midend_get_presets(midend *me, int *id_limit);
323int midend_which_preset(midend *me); 336int midend_which_preset(midend *me);
324bool midend_wants_statusbar(midend *me); 337bool midend_wants_statusbar(midend *me);
325enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_FRONTEND_SPECIFIC }; 338enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_PREFS, CFG_FRONTEND_SPECIFIC };
326config_item *midend_get_config(midend *me, int which, char **wintitle); 339config_item *midend_get_config(midend *me, int which, char **wintitle);
327const char *midend_set_config(midend *me, int which, config_item *cfg); 340const char *midend_set_config(midend *me, int which, config_item *cfg);
328const char *midend_game_id(midend *me, const char *id); 341const char *midend_game_id(midend *me, const char *id);
@@ -343,6 +356,11 @@ void midend_serialise(midend *me,
343const char *midend_deserialise(midend *me, 356const char *midend_deserialise(midend *me,
344 bool (*read)(void *ctx, void *buf, int len), 357 bool (*read)(void *ctx, void *buf, int len),
345 void *rctx); 358 void *rctx);
359const char *midend_load_prefs(
360 midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx);
361void midend_save_prefs(midend *me,
362 void (*write)(void *ctx, const void *buf, int len),
363 void *wctx);
346const char *identify_game(char **name, 364const char *identify_game(char **name,
347 bool (*read)(void *ctx, void *buf, int len), 365 bool (*read)(void *ctx, void *buf, int len),
348 void *rctx); 366 void *rctx);
@@ -374,12 +392,22 @@ void free_cfg(config_item *cfg);
374void free_keys(key_label *keys, int nkeys); 392void free_keys(key_label *keys, int nkeys);
375void obfuscate_bitmap(unsigned char *bmp, int bits, bool decode); 393void obfuscate_bitmap(unsigned char *bmp, int bits, bool decode);
376char *fgetline(FILE *fp); 394char *fgetline(FILE *fp);
395char *make_prefs_path(const char *dir, const char *sep,
396 const game *game, const char *suffix);
397int n_times_root_k(int n, int k);
377 398
378/* allocates output each time. len is always in bytes of binary data. 399/* allocates output each time. len is always in bytes of binary data.
379 * May assert (or just go wrong) if lengths are unchecked. */ 400 * May assert (or just go wrong) if lengths are unchecked. */
380char *bin2hex(const unsigned char *in, int inlen); 401char *bin2hex(const unsigned char *in, int inlen);
381unsigned char *hex2bin(const char *in, int outlen); 402unsigned char *hex2bin(const char *in, int outlen);
382 403
404/* Returns 0 or 1 if the environment variable is set, or dflt if not.
405 * dflt may be a third value if it needs to be. */
406int getenv_bool(const char *name, int dflt);
407
408/* Mixes two colours in specified proportions. */
409void colour_mix(const float src1[3], const float src2[3], float p,
410 float dst[3]);
383/* Sets (and possibly dims) background from frontend default colour, 411/* Sets (and possibly dims) background from frontend default colour,
384 * and auto-generates highlight and lowlight colours too. */ 412 * and auto-generates highlight and lowlight colours too. */
385void game_mkhighlight(frontend *fe, float *ret, 413void game_mkhighlight(frontend *fe, float *ret,
@@ -392,14 +420,15 @@ void game_mkhighlight_specific(frontend *fe, float *ret,
392/* Randomly shuffles an array of items. */ 420/* Randomly shuffles an array of items. */
393void shuffle(void *array, int nelts, int eltsize, random_state *rs); 421void shuffle(void *array, int nelts, int eltsize, random_state *rs);
394 422
395/* Draw a rectangle outline, using the drawing API's draw_line. */ 423/* Draw a rectangle outline, using the drawing API's draw_polygon. */
396void draw_rect_outline(drawing *dr, int x, int y, int w, int h, 424void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
397 int colour); 425 int colour);
398 426
399/* Draw a set of rectangle corners (e.g. for a cursor display). */ 427/* Draw a set of rectangle corners (e.g. for a cursor display). */
400void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col); 428void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col);
401 429
402void move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap); 430char *move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap,
431 bool *visible);
403 432
404/* Used in netslide.c and sixteen.c for cursor movement around edge. */ 433/* Used in netslide.c and sixteen.c for cursor movement around edge. */
405int c2pos(int w, int h, int cx, int cy); 434int c2pos(int w, int h, int cx, int cy);
@@ -416,37 +445,55 @@ void draw_text_outline(drawing *dr, int x, int y, int fonttype,
416 * less than buffer size. */ 445 * less than buffer size. */
417void copy_left_justified(char *buf, size_t sz, const char *str); 446void copy_left_justified(char *buf, size_t sz, const char *str);
418 447
419/* An ugly, but working float-to-string implementation for platforms
420 * that don't have one */
421int ftoa(char *buf, float f);
422
423/* Returns a generic label based on the value of `button.' To be used 448/* Returns a generic label based on the value of `button.' To be used
424 whenever a `label' field returned by the request_keys() game 449 whenever a `label' field returned by the request_keys() game
425 function is NULL. Dynamically allocated, to be freed by caller. */ 450 function is NULL. Dynamically allocated, to be freed by caller. */
426char *button2label(int button); 451char *button2label(int button);
427 452
453/* Swap two regions of memory. The two regions must not
454 * overlap. (Note: the natural name for this might be "memswap", but
455 * the mem* namespace is reserved for future expansion by the C99
456 * standard per clause 7.26.11.1.) */
457void swap_regions(void *av, void *bv, size_t size);
458
459/* comparator for sorting ints with qsort() */
460int compare_integers(const void *av, const void *bv);
461
428/* 462/*
429 * dsf.c 463 * dsf.c
430 */ 464 */
431int *snew_dsf(int size); 465typedef struct DSF DSF;
432 466DSF *dsf_new(int size);
433void print_dsf(int *dsf, int size); 467void dsf_free(DSF *dsf);
434 468
435/* Return the canonical element of the equivalence class containing element 469void dsf_copy(DSF *to, DSF *from);
436 * val. If 'inverse' is non-NULL, this function will put into it a flag 470
437 * indicating whether the canonical element is inverse to val. */ 471/* Basic dsf operations, return the canonical element of a class,
438int edsf_canonify(int *dsf, int val, bool *inverse); 472 * check if two elements are in the same class, and return the size of
439int dsf_canonify(int *dsf, int val); 473 * a class. These work on all types of dsf. */
440int dsf_size(int *dsf, int val); 474int dsf_canonify(DSF *dsf, int n);
441 475bool dsf_equivalent(DSF *dsf, int n1, int n2);
442/* Allow the caller to specify that two elements should be in the same 476int dsf_size(DSF *dsf, int n);
443 * equivalence class. If 'inverse' is true, the elements are actually opposite 477
444 * to one another in some sense. This function will fail an assertion if the 478/* Merge two elements and their classes. Not legal on a flip dsf. */
445 * caller gives it self-contradictory data, ie if two elements are claimed to 479void dsf_merge(DSF *dsf, int n1, int n2);
446 * be both opposite and non-opposite. */ 480
447void edsf_merge(int *dsf, int v1, int v2, bool inverse); 481/* Special dsf that tracks the minimal element of every equivalence
448void dsf_merge(int *dsf, int v1, int v2); 482 * class, and a function to query it. */
449void dsf_init(int *dsf, int len); 483DSF *dsf_new_min(int size);
484int dsf_minimal(DSF *dsf, int n);
485
486/* Special dsf that tracks whether pairs of elements in the same class
487 * have flipped sense relative to each other. Merge function takes an
488 * argument saying whether n1 and n2 are opposite to each other;
489 * canonify function will report whether n is opposite to the returned
490 * element. */
491DSF *dsf_new_flip(int size);
492void dsf_merge_flip(DSF *dsf, int n1, int n2, bool flip);
493int dsf_canonify_flip(DSF *dsf, int n, bool *flip);
494
495/* Reinitialise a dsf to the starting 'all elements distinct' state. */
496void dsf_reinit(DSF *dsf);
450 497
451/* 498/*
452 * tdq.c 499 * tdq.c
@@ -507,7 +554,7 @@ void random_free(random_state *state);
507char *random_state_encode(random_state *state); 554char *random_state_encode(random_state *state);
508random_state *random_state_decode(const char *input); 555random_state *random_state_decode(const char *input);
509/* random.c also exports SHA, which occasionally comes in useful. */ 556/* random.c also exports SHA, which occasionally comes in useful. */
510#if __STDC_VERSION__ >= 199901L 557#if HAVE_STDINT_H
511#include <stdint.h> 558#include <stdint.h>
512typedef uint32_t uint32; 559typedef uint32_t uint32;
513#elif UINT_MAX >= 4294967295L 560#elif UINT_MAX >= 4294967295L
@@ -532,7 +579,7 @@ void SHA_Simple(const void *p, int len, unsigned char *output);
532document *document_new(int pw, int ph, float userscale); 579document *document_new(int pw, int ph, float userscale);
533void document_free(document *doc); 580void document_free(document *doc);
534void document_add_puzzle(document *doc, const game *game, game_params *par, 581void document_add_puzzle(document *doc, const game *game, game_params *par,
535 game_state *st, game_state *st2); 582 game_ui *ui, game_state *st, game_state *st2);
536int document_npages(const document *doc); 583int document_npages(const document *doc);
537void document_begin(const document *doc, drawing *dr); 584void document_begin(const document *doc, drawing *dr);
538void document_end(const document *doc, drawing *dr); 585void document_end(const document *doc, drawing *dr);
@@ -564,7 +611,9 @@ void free_combi(combi_ctx *combi);
564 * divvy.c 611 * divvy.c
565 */ 612 */
566/* divides w*h rectangle into pieces of size k. Returns w*h dsf. */ 613/* divides w*h rectangle into pieces of size k. Returns w*h dsf. */
567int *divvy_rectangle(int w, int h, int k, random_state *rs); 614DSF *divvy_rectangle(int w, int h, int k, random_state *rs);
615/* Same, but only tries once, and may fail. (Exposed for test program.) */
616DSF *divvy_rectangle_attempt(int w, int h, int k, random_state *rs);
568 617
569/* 618/*
570 * findloop.c 619 * findloop.c
@@ -661,19 +710,24 @@ struct game {
661 bool can_format_as_text_ever; 710 bool can_format_as_text_ever;
662 bool (*can_format_as_text_now)(const game_params *params); 711 bool (*can_format_as_text_now)(const game_params *params);
663 char *(*text_format)(const game_state *state); 712 char *(*text_format)(const game_state *state);
713 config_item *(*get_prefs)(game_ui *ui);
714 void (*set_prefs)(game_ui *ui, const config_item *cfg);
664 game_ui *(*new_ui)(const game_state *state); 715 game_ui *(*new_ui)(const game_state *state);
665 void (*free_ui)(game_ui *ui); 716 void (*free_ui)(game_ui *ui);
666 char *(*encode_ui)(const game_ui *ui); 717 char *(*encode_ui)(const game_ui *ui);
667 void (*decode_ui)(game_ui *ui, const char *encoding); 718 void (*decode_ui)(game_ui *ui, const char *encoding,
719 const game_state *state);
668 key_label *(*request_keys)(const game_params *params, int *nkeys); 720 key_label *(*request_keys)(const game_params *params, int *nkeys);
669 void (*changed_state)(game_ui *ui, const game_state *oldstate, 721 void (*changed_state)(game_ui *ui, const game_state *oldstate,
670 const game_state *newstate); 722 const game_state *newstate);
723 const char *(*current_key_label)(const game_ui *ui,
724 const game_state *state, int button);
671 char *(*interpret_move)(const game_state *state, game_ui *ui, 725 char *(*interpret_move)(const game_state *state, game_ui *ui,
672 const game_drawstate *ds, int x, int y, int button); 726 const game_drawstate *ds, int x, int y, int button);
673 game_state *(*execute_move)(const game_state *state, const char *move); 727 game_state *(*execute_move)(const game_state *state, const char *move);
674 int preferred_tilesize; 728 int preferred_tilesize;
675 void (*compute_size)(const game_params *params, int tilesize, 729 void (*compute_size)(const game_params *params, int tilesize,
676 int *x, int *y); 730 const game_ui *ui, int *x, int *y);
677 void (*set_size)(drawing *dr, game_drawstate *ds, 731 void (*set_size)(drawing *dr, game_drawstate *ds,
678 const game_params *params, int tilesize); 732 const game_params *params, int tilesize);
679 float *(*colours)(frontend *fe, int *ncolours); 733 float *(*colours)(frontend *fe, int *ncolours);
@@ -693,51 +747,86 @@ struct game {
693 int *x, int *y, int *w, int *h); 747 int *x, int *y, int *w, int *h);
694 int (*status)(const game_state *state); 748 int (*status)(const game_state *state);
695 bool can_print, can_print_in_colour; 749 bool can_print, can_print_in_colour;
696 void (*print_size)(const game_params *params, float *x, float *y); 750 void (*print_size)(const game_params *params, const game_ui *ui,
697 void (*print)(drawing *dr, const game_state *state, int tilesize); 751 float *x, float *y);
752 void (*print)(drawing *dr, const game_state *state, const game_ui *ui,
753 int tilesize);
698 bool wants_statusbar; 754 bool wants_statusbar;
699 bool is_timed; 755 bool is_timed;
700 bool (*timing_state)(const game_state *state, game_ui *ui); 756 bool (*timing_state)(const game_state *state, game_ui *ui);
701 int flags; 757 int flags;
702}; 758};
703 759
760#define GET_HANDLE_AS_TYPE(dr, type) ((type*)((dr)->handle))
761
762struct drawing {
763 const drawing_api *api;
764 void *handle;
765};
766
704/* 767/*
705 * Data structure containing the drawing API implemented by the 768 * Data structure containing the drawing API implemented by the
706 * front end and also by cross-platform printing modules such as 769 * front end and also by cross-platform printing modules such as
707 * PostScript. 770 * PostScript.
708 */ 771 */
709struct drawing_api { 772struct drawing_api {
710 void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize, 773 /*
774 * API version. Increment this when there is a breaking change to
775 * this API which requires front ends to change.
776 *
777 * There is expliclty not a public LATEST_API_VERSION define, so
778 * that front end authors will need to manually intervene when the
779 * version number changes. Naturally, this should be done
780 * sparingly.
781 *
782 * If a function is ever added to this API, please move this field
783 * to the _end_ of the structure, so that changes thereafter will
784 * shift the position of the version and lead to a compilation
785 * error if old implementations are not updated. Then remove this
786 * comment.
787 *
788 * The latest version number is 1.
789 *
790 * Change log:
791 *
792 * Version 1 (2024-08-14): Introduction of version number, in
793 * conjunction with changing every API function to take `drawing
794 * *` instead of `void *`. See commit f379130 for the detailed
795 * rationale behind this change.
796 */
797 int version;
798
799 void (*draw_text)(drawing *dr, int x, int y, int fonttype, int fontsize,
711 int align, int colour, const char *text); 800 int align, int colour, const char *text);
712 void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour); 801 void (*draw_rect)(drawing *dr, int x, int y, int w, int h, int colour);
713 void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, 802 void (*draw_line)(drawing *dr, int x1, int y1, int x2, int y2,
714 int colour); 803 int colour);
715 void (*draw_polygon)(void *handle, int *coords, int npoints, 804 void (*draw_polygon)(drawing *dr, const int *coords, int npoints,
716 int fillcolour, int outlinecolour); 805 int fillcolour, int outlinecolour);
717 void (*draw_circle)(void *handle, int cx, int cy, int radius, 806 void (*draw_circle)(drawing *dr, int cx, int cy, int radius,
718 int fillcolour, int outlinecolour); 807 int fillcolour, int outlinecolour);
719 void (*draw_update)(void *handle, int x, int y, int w, int h); 808 void (*draw_update)(drawing *dr, int x, int y, int w, int h);
720 void (*clip)(void *handle, int x, int y, int w, int h); 809 void (*clip)(drawing *dr, int x, int y, int w, int h);
721 void (*unclip)(void *handle); 810 void (*unclip)(drawing *dr);
722 void (*start_draw)(void *handle); 811 void (*start_draw)(drawing *dr);
723 void (*end_draw)(void *handle); 812 void (*end_draw)(drawing *dr);
724 void (*status_bar)(void *handle, const char *text); 813 void (*status_bar)(drawing *dr, const char *text);
725 blitter *(*blitter_new)(void *handle, int w, int h); 814 blitter *(*blitter_new)(drawing *dr, int w, int h);
726 void (*blitter_free)(void *handle, blitter *bl); 815 void (*blitter_free)(drawing *dr, blitter *bl);
727 void (*blitter_save)(void *handle, blitter *bl, int x, int y); 816 void (*blitter_save)(drawing *dr, blitter *bl, int x, int y);
728 void (*blitter_load)(void *handle, blitter *bl, int x, int y); 817 void (*blitter_load)(drawing *dr, blitter *bl, int x, int y);
729 void (*begin_doc)(void *handle, int pages); 818 void (*begin_doc)(drawing *dr, int pages);
730 void (*begin_page)(void *handle, int number); 819 void (*begin_page)(drawing *dr, int number);
731 void (*begin_puzzle)(void *handle, float xm, float xc, 820 void (*begin_puzzle)(drawing *dr, float xm, float xc,
732 float ym, float yc, int pw, int ph, float wmm); 821 float ym, float yc, int pw, int ph, float wmm);
733 void (*end_puzzle)(void *handle); 822 void (*end_puzzle)(drawing *dr);
734 void (*end_page)(void *handle, int number); 823 void (*end_page)(drawing *dr, int number);
735 void (*end_doc)(void *handle); 824 void (*end_doc)(drawing *dr);
736 void (*line_width)(void *handle, float width); 825 void (*line_width)(drawing *dr, float width);
737 void (*line_dotted)(void *handle, bool dotted); 826 void (*line_dotted)(drawing *dr, bool dotted);
738 char *(*text_fallback)(void *handle, const char *const *strings, 827 char *(*text_fallback)(drawing *dr, const char *const *strings,
739 int nstrings); 828 int nstrings);
740 void (*draw_thick_line)(void *handle, float thickness, 829 void (*draw_thick_line)(drawing *dr, float thickness,
741 float x1, float y1, float x2, float y2, 830 float x1, float y1, float x2, float y2,
742 int colour); 831 int colour);
743}; 832};
@@ -750,17 +839,29 @@ struct drawing_api {
750#ifdef COMBINED 839#ifdef COMBINED
751extern const game *gamelist[]; 840extern const game *gamelist[];
752extern const int gamecount; 841extern const int gamecount;
842/* Also pre-declare every individual 'struct game' we expect */
843#define GAME(x) extern const game x;
844#include "generated-games.h"
845#undef GAME
753#else 846#else
754extern const game thegame; 847extern const game thegame;
755#endif 848#endif
756 849
757/* 850/*
758 * Special string value to return from interpret_move in the case 851 * Special string values to return from interpret_move.
759 * where the game UI has been updated but no actual move is being 852 *
760 * appended to the undo chain. Must be declared as a non-const char, 853 * MOVE_UI_UPDATE is for the case where the game UI has been updated
761 * but should never actually be modified by anyone. 854 * but no actual move is being appended to the undo chain.
855 *
856 * MOVE_NO_EFFECT is for when the key was understood by the puzzle,
857 * but it happens that there isn't effect, not even a UI change.
858 *
859 * MOVE_UNUSED is for keys that the puzzle has no use for at all.
860 *
861 * Each must be declared as a non-const char, but should never
862 * actually be modified by anyone.
762 */ 863 */
763extern char UI_UPDATE[]; 864extern char MOVE_UI_UPDATE[], MOVE_NO_EFFECT[], MOVE_UNUSED[];
764 865
765/* A little bit of help to lazy developers */ 866/* A little bit of help to lazy developers */
766#define DEFAULT_STATUSBAR_TEXT "Use status_bar() to fill this in." 867#define DEFAULT_STATUSBAR_TEXT "Use status_bar() to fill this in."
diff --git a/apps/plugins/puzzles/src/random.c b/apps/plugins/puzzles/src/random.c
index fb54560c95..bd11f16601 100644
--- a/apps/plugins/puzzles/src/random.c
+++ b/apps/plugins/puzzles/src/random.c
@@ -109,7 +109,7 @@ void SHA_Init(SHA_State * s)
109 109
110void SHA_Bytes(SHA_State * s, const void *p, int len) 110void SHA_Bytes(SHA_State * s, const void *p, int len)
111{ 111{
112 unsigned char *q = (unsigned char *) p; 112 const unsigned char *q = (const unsigned char *) p;
113 uint32 wordblock[16]; 113 uint32 wordblock[16];
114 uint32 lenw = len; 114 uint32 lenw = len;
115 int i; 115 int i;
@@ -254,12 +254,12 @@ unsigned long random_bits(random_state *state, int bits)
254 } 254 }
255 255
256 /* 256 /*
257 * `(1 << bits) - 1' is not good enough, since if bits==32 on a 257 * `(1UL << bits) - 1' is not good enough, since if bits==32 on a
258 * 32-bit machine, behaviour is undefined and Intel has a nasty 258 * 32-bit machine, behaviour is undefined and Intel has a nasty
259 * habit of shifting left by zero instead. We'll shift by 259 * habit of shifting left by zero instead. We'll shift by
260 * bits-1 and then separately shift by one. 260 * bits-1 and then separately shift by one.
261 */ 261 */
262 ret &= (1 << (bits-1)) * 2 - 1; 262 ret &= (1UL << (bits-1)) * 2 - 1;
263 return ret; 263 return ret;
264} 264}
265 265
diff --git a/apps/plugins/puzzles/src/range.R b/apps/plugins/puzzles/src/range.R
deleted file mode 100644
index f1256efd1e..0000000000
--- a/apps/plugins/puzzles/src/range.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3RANGE_EXTRA = dsf
4
5range : [X] GTK COMMON range RANGE_EXTRA range-icon|no-icon
6
7range : [G] WINDOWS COMMON range RANGE_EXTRA range.res|noicon.res
8
9ALL += range[COMBINED] RANGE_EXTRA
10
11!begin am gtk
12GAMES += range
13!end
14
15!begin >list.c
16 A(range) \
17!end
18
19!begin >gamedesc.txt
20range:range.exe:Range:Visible-distance puzzle:Place black squares to limit the visible distance from each numbered cell.
21!end
diff --git a/apps/plugins/puzzles/src/range.c b/apps/plugins/puzzles/src/range.c
index fc0a5405f1..bc29f7604b 100644
--- a/apps/plugins/puzzles/src/range.c
+++ b/apps/plugins/puzzles/src/range.c
@@ -58,7 +58,11 @@
58#include <string.h> 58#include <string.h>
59#include <assert.h> 59#include <assert.h>
60#include <ctype.h> 60#include <ctype.h>
61#include <math.h> 61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
62 66
63#include "puzzles.h" 67#include "puzzles.h"
64 68
@@ -911,8 +915,8 @@ static const char *validate_params(const game_params *params, bool full)
911 int const w = params->w, h = params->h; 915 int const w = params->w, h = params->h;
912 if (w < 1) return "Error: width is less than 1"; 916 if (w < 1) return "Error: width is less than 1";
913 if (h < 1) return "Error: height is less than 1"; 917 if (h < 1) return "Error: height is less than 1";
918 if (w > SCHAR_MAX - (h - 1)) return "Error: w + h is too big";
914 if (w * h < 1) return "Error: size is less than 1"; 919 if (w * h < 1) return "Error: size is less than 1";
915 if (w + h - 1 > SCHAR_MAX) return "Error: w + h is too big";
916 /* I might be unable to store clues in my puzzle_size *grid; */ 920 /* I might be unable to store clues in my puzzle_size *grid; */
917 if (full) { 921 if (full) {
918 if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles"; 922 if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles";
@@ -1221,28 +1225,101 @@ static char *game_text_format(const game_state *state)
1221struct game_ui { 1225struct game_ui {
1222 puzzle_size r, c; /* cursor position */ 1226 puzzle_size r, c; /* cursor position */
1223 bool cursor_show; 1227 bool cursor_show;
1228
1229 /*
1230 * User preference option to swap the left and right mouse
1231 * buttons.
1232 *
1233 * The original puzzle submitter thought it would be more useful
1234 * to have the left button turn an empty square into a dotted one,
1235 * on the grounds that that was what you did most often; I (SGT)
1236 * felt instinctively that the left button ought to place black
1237 * squares and the right button place dots, on the grounds that
1238 * that was consistent with many other puzzles in which the left
1239 * button fills in the data used by the solution checker while the
1240 * right button places pencil marks for the user's convenience.
1241 *
1242 * My first beta-player wasn't sure either, so I thought I'd
1243 * pre-emptively put in a 'configuration' mechanism just in case.
1244 */
1245 bool swap_buttons;
1224}; 1246};
1225 1247
1248static void legacy_prefs_override(struct game_ui *ui_out)
1249{
1250 static int initialised = false;
1251 static int swap_buttons = -1;
1252
1253 if (!initialised) {
1254 initialised = true;
1255 swap_buttons = getenv_bool("RANGE_SWAP_BUTTONS", -1);
1256 }
1257
1258 if (swap_buttons != -1)
1259 ui_out->swap_buttons = swap_buttons;
1260}
1261
1226static game_ui *new_ui(const game_state *state) 1262static game_ui *new_ui(const game_state *state)
1227{ 1263{
1228 struct game_ui *ui = snew(game_ui); 1264 struct game_ui *ui = snew(game_ui);
1229 ui->r = ui->c = 0; 1265 ui->r = ui->c = 0;
1230 ui->cursor_show = false; 1266 ui->cursor_show = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1267
1268 ui->swap_buttons = false;
1269 legacy_prefs_override(ui);
1270
1231 return ui; 1271 return ui;
1232} 1272}
1233 1273
1234static void free_ui(game_ui *ui) 1274static config_item *get_prefs(game_ui *ui)
1235{ 1275{
1236 sfree(ui); 1276 config_item *ret;
1277
1278 ret = snewn(2, config_item);
1279
1280 ret[0].name = "Mouse button order";
1281 ret[0].kw = "left-mouse-button";
1282 ret[0].type = C_CHOICES;
1283 ret[0].u.choices.choicenames =
1284 ":Left to fill, right to dot:Left to dot, right to fill";
1285 ret[0].u.choices.choicekws = ":fill:dot";
1286 ret[0].u.choices.selected = ui->swap_buttons;
1287
1288 ret[1].name = NULL;
1289 ret[1].type = C_END;
1290
1291 return ret;
1237} 1292}
1238 1293
1239static char *encode_ui(const game_ui *ui) 1294static void set_prefs(game_ui *ui, const config_item *cfg)
1240{ 1295{
1241 return NULL; 1296 ui->swap_buttons = cfg[0].u.choices.selected;
1297}
1298
1299static void free_ui(game_ui *ui)
1300{
1301 sfree(ui);
1242} 1302}
1243 1303
1244static void decode_ui(game_ui *ui, const char *encoding) 1304static const char *current_key_label(const game_ui *ui,
1305 const game_state *state, int button)
1245{ 1306{
1307 int cell;
1308
1309 if (IS_CURSOR_SELECT(button)) {
1310 cell = state->grid[idx(ui->r, ui->c, state->params.w)];
1311 if (!ui->cursor_show || cell > 0) return "";
1312 switch (cell) {
1313 case EMPTY:
1314 return button == CURSOR_SELECT ? "Fill" : "Dot";
1315 case WHITE:
1316 return button == CURSOR_SELECT ? "Empty" : "Fill";
1317 case BLACK:
1318 return button == CURSOR_SELECT ? "Dot" : "Empty";
1319 }
1320 }
1321 return "";
1322
1246} 1323}
1247 1324
1248typedef struct drawcell { 1325typedef struct drawcell {
@@ -1253,7 +1330,6 @@ typedef struct drawcell {
1253struct game_drawstate { 1330struct game_drawstate {
1254 int tilesize; 1331 int tilesize;
1255 drawcell *grid; 1332 drawcell *grid;
1256 bool started;
1257}; 1333};
1258 1334
1259#define TILESIZE (ds->tilesize) 1335#define TILESIZE (ds->tilesize)
@@ -1283,38 +1359,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1283 } 1359 }
1284 1360
1285 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1361 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1286 /* 1362 if (ui->swap_buttons) {
1287 * Utterly awful hack, exactly analogous to the one in Slant, 1363 if (button == LEFT_BUTTON)
1288 * to configure the left and right mouse buttons the opposite 1364 button = RIGHT_BUTTON;
1289 * way round. 1365 else
1290 * 1366 button = LEFT_BUTTON;
1291 * The original puzzle submitter thought it would be more 1367 }
1292 * useful to have the left button turn an empty square into a
1293 * dotted one, on the grounds that that was what you did most
1294 * often; I (SGT) felt instinctively that the left button
1295 * ought to place black squares and the right button place
1296 * dots, on the grounds that that was consistent with many
1297 * other puzzles in which the left button fills in the data
1298 * used by the solution checker while the right button places
1299 * pencil marks for the user's convenience.
1300 *
1301 * My first beta-player wasn't sure either, so I thought I'd
1302 * pre-emptively put in a 'configuration' mechanism just in
1303 * case.
1304 */
1305 {
1306 static int swap_buttons = -1;
1307 if (swap_buttons < 0) {
1308 char *env = getenv("RANGE_SWAP_BUTTONS");
1309 swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
1310 }
1311 if (swap_buttons) {
1312 if (button == LEFT_BUTTON)
1313 button = RIGHT_BUTTON;
1314 else
1315 button = LEFT_BUTTON;
1316 }
1317 }
1318 } 1368 }
1319 1369
1320 switch (button) { 1370 switch (button) {
@@ -1355,14 +1405,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1355 else if (do_post) 1405 else if (do_post)
1356 return nfmtstr(40, "W,%d,%d", ui->r, ui->c); 1406 return nfmtstr(40, "W,%d,%d", ui->r, ui->c);
1357 else 1407 else
1358 return UI_UPDATE; 1408 return MOVE_UI_UPDATE;
1359 1409
1360 } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) { 1410 } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) {
1361 ui->r += dr[i]; 1411 ui->r += dr[i];
1362 ui->c += dc[i]; 1412 ui->c += dc[i];
1363 } 1413 }
1364 } else ui->cursor_show = true; 1414 } else ui->cursor_show = true;
1365 return UI_UPDATE; 1415 return MOVE_UI_UPDATE;
1366 } 1416 }
1367 1417
1368 if (action == hint) { 1418 if (action == hint) {
@@ -1408,7 +1458,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1408static bool find_errors(const game_state *state, bool *report) 1458static bool find_errors(const game_state *state, bool *report)
1409{ 1459{
1410 int const w = state->params.w, h = state->params.h, n = w * h; 1460 int const w = state->params.w, h = state->params.h, n = w * h;
1411 int *dsf; 1461 DSF *dsf;
1412 1462
1413 int r, c, i; 1463 int r, c, i;
1414 1464
@@ -1463,7 +1513,7 @@ static bool find_errors(const game_state *state, bool *report)
1463 /* 1513 /*
1464 * Check that all the white cells form a single connected component. 1514 * Check that all the white cells form a single connected component.
1465 */ 1515 */
1466 dsf = snew_dsf(n); 1516 dsf = dsf_new(n);
1467 for (r = 0; r < h-1; ++r) 1517 for (r = 0; r < h-1; ++r)
1468 for (c = 0; c < w; ++c) 1518 for (c = 0; c < w; ++c)
1469 if (state->grid[r*w+c] != BLACK && 1519 if (state->grid[r*w+c] != BLACK &&
@@ -1474,11 +1524,12 @@ static bool find_errors(const game_state *state, bool *report)
1474 if (state->grid[r*w+c] != BLACK && 1524 if (state->grid[r*w+c] != BLACK &&
1475 state->grid[r*w+(c+1)] != BLACK) 1525 state->grid[r*w+(c+1)] != BLACK)
1476 dsf_merge(dsf, r*w+c, r*w+(c+1)); 1526 dsf_merge(dsf, r*w+c, r*w+(c+1));
1477 if (nblack + dsf_size(dsf, any_white_cell) < n) { 1527 if (any_white_cell != -1 &&
1528 nblack + dsf_size(dsf, any_white_cell) < n) {
1478 int biggest, canonical; 1529 int biggest, canonical;
1479 1530
1480 if (!report) { 1531 if (!report) {
1481 sfree(dsf); 1532 dsf_free(dsf);
1482 goto found_error; 1533 goto found_error;
1483 } 1534 }
1484 1535
@@ -1503,7 +1554,7 @@ static bool find_errors(const game_state *state, bool *report)
1503 if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical) 1554 if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical)
1504 report[i] = true; 1555 report[i] = true;
1505 } 1556 }
1506 sfree(dsf); 1557 dsf_free(dsf);
1507 1558
1508 free_game(dup); 1559 free_game(dup);
1509 return false; /* if report != NULL, this is ignored */ 1560 return false; /* if report != NULL, this is ignored */
@@ -1604,13 +1655,12 @@ enum {
1604 COL_USER = COL_GRID, 1655 COL_USER = COL_GRID,
1605 COL_ERROR, 1656 COL_ERROR,
1606 COL_LOWLIGHT, 1657 COL_LOWLIGHT,
1607 COL_HIGHLIGHT = COL_ERROR, /* mkhighlight needs it, I don't */
1608 COL_CURSOR = COL_LOWLIGHT, 1658 COL_CURSOR = COL_LOWLIGHT,
1609 NCOLOURS 1659 NCOLOURS
1610}; 1660};
1611 1661
1612static void game_compute_size(const game_params *params, int tilesize, 1662static void game_compute_size(const game_params *params, int tilesize,
1613 int *x, int *y) 1663 const game_ui *ui, int *x, int *y)
1614{ 1664{
1615 *x = (1 + params->w) * tilesize; 1665 *x = (1 + params->w) * tilesize;
1616 *y = (1 + params->h) * tilesize; 1666 *y = (1 + params->h) * tilesize;
@@ -1629,7 +1679,7 @@ static float *game_colours(frontend *fe, int *ncolours)
1629{ 1679{
1630 float *ret = snewn(3 * NCOLOURS, float); 1680 float *ret = snewn(3 * NCOLOURS, float);
1631 1681
1632 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 1682 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT);
1633 COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F); 1683 COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F);
1634 COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F); 1684 COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F);
1635 1685
@@ -1655,7 +1705,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1655 int i; 1705 int i;
1656 1706
1657 ds->tilesize = 0; 1707 ds->tilesize = 0;
1658 ds->started = false;
1659 1708
1660 ds->grid = snewn(n, drawcell); 1709 ds->grid = snewn(n, drawcell);
1661 for (i = 0; i < n; ++i) 1710 for (i = 0; i < n; ++i)
@@ -1690,7 +1739,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1690 float animtime, float flashtime) 1739 float animtime, float flashtime)
1691{ 1740{
1692 int const w = state->params.w, h = state->params.h, n = w * h; 1741 int const w = state->params.w, h = state->params.h, n = w * h;
1693 int const wpx = (w+1) * ds->tilesize, hpx = (h+1) * ds->tilesize;
1694 int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; 1742 int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1695 1743
1696 int r, c, i; 1744 int r, c, i;
@@ -1701,12 +1749,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1701 1749
1702 assert (oldstate == NULL); /* only happens if animating moves */ 1750 assert (oldstate == NULL); /* only happens if animating moves */
1703 1751
1704 if (!ds->started) {
1705 ds->started = true;
1706 draw_rect(dr, 0, 0, wpx, hpx, COL_BACKGROUND);
1707 draw_update(dr, 0, 0, wpx, hpx);
1708 }
1709
1710 for (i = r = 0; r < h; ++r) { 1752 for (i = r = 0; r < h; ++r) {
1711 for (c = 0; c < w; ++c, ++i) { 1753 for (c = 0; c < w; ++c, ++i) {
1712 drawcell cell = makecell(state->grid[i], errors[i], false, flash); 1754 drawcell cell = makecell(state->grid[i], errors[i], false, flash);
@@ -1757,25 +1799,21 @@ static void draw_cell(drawing *draw, game_drawstate *ds, int r, int c,
1757 draw_update(draw, x, y, ts + 1, ts + 1); 1799 draw_update(draw, x, y, ts + 1, ts + 1);
1758} 1800}
1759 1801
1760static bool game_timing_state(const game_state *state, game_ui *ui)
1761{
1762 puts("warning: game_timing_state was called (this shouldn't happen)");
1763 return false; /* the (non-existing) timer should not be running */
1764}
1765
1766/* ---------------------------------------------------------------------- 1802/* ----------------------------------------------------------------------
1767 * User interface: print 1803 * User interface: print
1768 */ 1804 */
1769 1805
1770static void game_print_size(const game_params *params, float *x, float *y) 1806static void game_print_size(const game_params *params, const game_ui *ui,
1807 float *x, float *y)
1771{ 1808{
1772 int print_width, print_height; 1809 int print_width, print_height;
1773 game_compute_size(params, 800, &print_width, &print_height); 1810 game_compute_size(params, 800, ui, &print_width, &print_height);
1774 *x = print_width / 100.0F; 1811 *x = print_width / 100.0F;
1775 *y = print_height / 100.0F; 1812 *y = print_height / 100.0F;
1776} 1813}
1777 1814
1778static void game_print(drawing *dr, const game_state *state, int tilesize) 1815static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1816 int tilesize)
1779{ 1817{
1780 int const w = state->params.w, h = state->params.h; 1818 int const w = state->params.w, h = state->params.h;
1781 game_drawstate ds_obj, *ds = &ds_obj; 1819 game_drawstate ds_obj, *ds = &ds_obj;
@@ -1821,12 +1859,14 @@ struct game const thegame = {
1821 free_game, 1859 free_game,
1822 true, solve_game, 1860 true, solve_game,
1823 true, game_can_format_as_text_now, game_text_format, 1861 true, game_can_format_as_text_now, game_text_format,
1862 get_prefs, set_prefs,
1824 new_ui, 1863 new_ui,
1825 free_ui, 1864 free_ui,
1826 encode_ui, 1865 NULL, /* encode_ui */
1827 decode_ui, 1866 NULL, /* decode_ui */
1828 NULL, /* game_request_keys */ 1867 NULL, /* game_request_keys */
1829 game_changed_state, 1868 game_changed_state,
1869 current_key_label,
1830 interpret_move, 1870 interpret_move,
1831 execute_move, 1871 execute_move,
1832 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1872 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1840,6 +1880,6 @@ struct game const thegame = {
1840 game_status, 1880 game_status,
1841 true, false, game_print_size, game_print, 1881 true, false, game_print_size, game_print,
1842 false, /* wants_statusbar */ 1882 false, /* wants_statusbar */
1843 false, game_timing_state, 1883 false, NULL, /* timing_state */
1844 0, /* flags */ 1884 0, /* flags */
1845}; 1885};
diff --git a/apps/plugins/puzzles/src/rect.R b/apps/plugins/puzzles/src/rect.R
deleted file mode 100644
index 1448c0fa63..0000000000
--- a/apps/plugins/puzzles/src/rect.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3rect : [X] GTK COMMON rect rect-icon|no-icon
4
5rect : [G] WINDOWS COMMON rect rect.res|noicon.res
6
7ALL += rect[COMBINED]
8
9!begin am gtk
10GAMES += rect
11!end
12
13!begin >list.c
14 A(rect) \
15!end
16
17!begin >gamedesc.txt
18rect:rect.exe:Rectangles:Rectangles puzzle:Divide the grid into rectangles with areas equal to the numbers.
19!end
diff --git a/apps/plugins/puzzles/src/rect.c b/apps/plugins/puzzles/src/rect.c
index b13de75fd4..c3ee1ab478 100644
--- a/apps/plugins/puzzles/src/rect.c
+++ b/apps/plugins/puzzles/src/rect.c
@@ -26,7 +26,11 @@
26#include <string.h> 26#include <string.h>
27#include <assert.h> 27#include <assert.h>
28#include <ctype.h> 28#include <ctype.h>
29#include <math.h> 29#ifdef NO_TGMATH_H
30# include <math.h>
31#else
32# include <tgmath.h>
33#endif
30 34
31#include "puzzles.h" 35#include "puzzles.h"
32 36
@@ -163,10 +167,7 @@ static char *encode_params(const game_params *params, bool full)
163 167
164 sprintf(data, "%dx%d", params->w, params->h); 168 sprintf(data, "%dx%d", params->w, params->h);
165 if (full && params->expandfactor) 169 if (full && params->expandfactor)
166 { 170 sprintf(data + strlen(data), "e%g", params->expandfactor);
167 sprintf(data + strlen(data), "e");
168 ftoa(data + strlen(data), params->expandfactor);
169 }
170 if (full && !params->unique) 171 if (full && !params->unique)
171 strcat(data, "a"); 172 strcat(data, "a");
172 173
@@ -192,7 +193,7 @@ static config_item *game_configure(const game_params *params)
192 193
193 ret[2].name = "Expansion factor"; 194 ret[2].name = "Expansion factor";
194 ret[2].type = C_STRING; 195 ret[2].type = C_STRING;
195 ftoa(buf, params->expandfactor); 196 sprintf(buf, "%g", params->expandfactor);
196 ret[2].u.string.sval = dupstr(buf); 197 ret[2].u.string.sval = dupstr(buf);
197 198
198 ret[3].name = "Ensure unique solution"; 199 ret[3].name = "Ensure unique solution";
@@ -221,6 +222,8 @@ static const char *validate_params(const game_params *params, bool full)
221{ 222{
222 if (params->w <= 0 || params->h <= 0) 223 if (params->w <= 0 || params->h <= 0)
223 return "Width and height must both be greater than zero"; 224 return "Width and height must both be greater than zero";
225 if (params->w > INT_MAX / params->h)
226 return "Width times height must not be unreasonably large";
224 if (params->w*params->h < 2) 227 if (params->w*params->h < 2)
225 return "Grid area must be greater than one"; 228 return "Grid area must be greater than one";
226 if (params->expandfactor < 0.0F) 229 if (params->expandfactor < 0.0F)
@@ -2207,7 +2210,7 @@ static game_ui *new_ui(const game_state *state)
2207 reset_ui(ui); 2210 reset_ui(ui);
2208 ui->erasing = false; 2211 ui->erasing = false;
2209 ui->cur_x = ui->cur_y = 0; 2212 ui->cur_x = ui->cur_y = 0;
2210 ui->cur_visible = false; 2213 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2211 ui->cur_dragging = false; 2214 ui->cur_dragging = false;
2212 return ui; 2215 return ui;
2213} 2216}
@@ -2217,15 +2220,6 @@ static void free_ui(game_ui *ui)
2217 sfree(ui); 2220 sfree(ui);
2218} 2221}
2219 2222
2220static char *encode_ui(const game_ui *ui)
2221{
2222 return NULL;
2223}
2224
2225static void decode_ui(game_ui *ui, const char *encoding)
2226{
2227}
2228
2229static void coord_round(float x, float y, int *xr, int *yr) 2223static void coord_round(float x, float y, int *xr, int *yr)
2230{ 2224{
2231 float xs, ys, xv, yv, dx, dy, dist; 2225 float xs, ys, xv, yv, dx, dy, dist;
@@ -2377,6 +2371,21 @@ struct game_drawstate {
2377 unsigned long *visible; 2371 unsigned long *visible;
2378}; 2372};
2379 2373
2374static const char *current_key_label(const game_ui *ui,
2375 const game_state *state, int button)
2376{
2377 if (IS_CURSOR_SELECT(button) && ui->cur_visible &&
2378 !(ui->drag_start_x >= 0 && !ui->cur_dragging)) {
2379 if (ui->cur_dragging) {
2380 if (!ui->dragged) return "Cancel";
2381 if ((button == CURSOR_SELECT2) == ui->erasing) return "Done";
2382 return "Cancel";
2383 }
2384 return button == CURSOR_SELECT ? "Mark" : "Erase";
2385 }
2386 return "";
2387}
2388
2380static char *interpret_move(const game_state *from, game_ui *ui, 2389static char *interpret_move(const game_state *from, game_ui *ui,
2381 const game_drawstate *ds, 2390 const game_drawstate *ds,
2382 int x, int y, int button) 2391 int x, int y, int button)
@@ -2385,7 +2394,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2385 bool startdrag = false, enddrag = false, active = false, erasing = false; 2394 bool startdrag = false, enddrag = false, active = false, erasing = false;
2386 char buf[80], *ret; 2395 char buf[80], *ret;
2387 2396
2388 button &= ~MOD_MASK; 2397 button = STRIP_BUTTON_MODIFIERS(button);
2389 2398
2390 coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc); 2399 coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc);
2391 2400
@@ -2406,10 +2415,11 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2406 enddrag = true; 2415 enddrag = true;
2407 erasing = (button == RIGHT_RELEASE); 2416 erasing = (button == RIGHT_RELEASE);
2408 } else if (IS_CURSOR_MOVE(button)) { 2417 } else if (IS_CURSOR_MOVE(button)) {
2409 move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, false); 2418 char *ret;
2410 ui->cur_visible = true; 2419 ret = move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h,
2420 false, &ui->cur_visible);
2411 active = true; 2421 active = true;
2412 if (!ui->cur_dragging) return UI_UPDATE; 2422 if (!ui->cur_dragging || ret != MOVE_UI_UPDATE) return ret;
2413 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); 2423 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc);
2414 } else if (IS_CURSOR_SELECT(button)) { 2424 } else if (IS_CURSOR_SELECT(button)) {
2415 if (ui->drag_start_x >= 0 && !ui->cur_dragging) { 2425 if (ui->drag_start_x >= 0 && !ui->cur_dragging) {
@@ -2422,7 +2432,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2422 if (!ui->cur_visible) { 2432 if (!ui->cur_visible) {
2423 assert(!ui->cur_dragging); 2433 assert(!ui->cur_dragging);
2424 ui->cur_visible = true; 2434 ui->cur_visible = true;
2425 return UI_UPDATE; 2435 return MOVE_UI_UPDATE;
2426 } 2436 }
2427 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); 2437 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc);
2428 erasing = (button == CURSOR_SELECT2); 2438 erasing = (button == CURSOR_SELECT2);
@@ -2443,7 +2453,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2443 reset_ui(ui); /* cancel keyboard dragging */ 2453 reset_ui(ui); /* cancel keyboard dragging */
2444 ui->cur_dragging = false; 2454 ui->cur_dragging = false;
2445 } 2455 }
2446 return UI_UPDATE; 2456 return MOVE_UI_UPDATE;
2447 } else if (button != LEFT_DRAG && button != RIGHT_DRAG) { 2457 } else if (button != LEFT_DRAG && button != RIGHT_DRAG) {
2448 return NULL; 2458 return NULL;
2449 } 2459 }
@@ -2527,7 +2537,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2527 if (ret) 2537 if (ret)
2528 return ret; /* a move has been made */ 2538 return ret; /* a move has been made */
2529 else if (active) 2539 else if (active)
2530 return UI_UPDATE; 2540 return MOVE_UI_UPDATE;
2531 else 2541 else
2532 return NULL; 2542 return NULL;
2533} 2543}
@@ -2621,7 +2631,7 @@ static game_state *execute_move(const game_state *from, const char *move)
2621#define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) ) 2631#define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) )
2622 2632
2623static void game_compute_size(const game_params *params, int tilesize, 2633static void game_compute_size(const game_params *params, int tilesize,
2624 int *x, int *y) 2634 const game_ui *ui, int *x, int *y)
2625{ 2635{
2626 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2636 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2627 struct { int tilesize; } ads, *ds = &ads; 2637 struct { int tilesize; } ads, *ds = &ads;
@@ -2796,9 +2806,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2796 } 2806 }
2797 2807
2798 if (!ds->started) { 2808 if (!ds->started) {
2799 draw_rect(dr, 0, 0,
2800 state->w * TILE_SIZE + 2*BORDER + 1,
2801 state->h * TILE_SIZE + 2*BORDER + 1, COL_BACKGROUND);
2802 draw_rect(dr, COORD(0)-1, COORD(0)-1, 2809 draw_rect(dr, COORD(0)-1, COORD(0)-1,
2803 ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE); 2810 ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE);
2804 ds->started = true; 2811 ds->started = true;
@@ -2901,24 +2908,21 @@ static int game_status(const game_state *state)
2901 return state->completed ? +1 : 0; 2908 return state->completed ? +1 : 0;
2902} 2909}
2903 2910
2904static bool game_timing_state(const game_state *state, game_ui *ui) 2911static void game_print_size(const game_params *params, const game_ui *ui,
2905{ 2912 float *x, float *y)
2906 return true;
2907}
2908
2909static void game_print_size(const game_params *params, float *x, float *y)
2910{ 2913{
2911 int pw, ph; 2914 int pw, ph;
2912 2915
2913 /* 2916 /*
2914 * I'll use 5mm squares by default. 2917 * I'll use 5mm squares by default.
2915 */ 2918 */
2916 game_compute_size(params, 500, &pw, &ph); 2919 game_compute_size(params, 500, ui, &pw, &ph);
2917 *x = pw / 100.0F; 2920 *x = pw / 100.0F;
2918 *y = ph / 100.0F; 2921 *y = ph / 100.0F;
2919} 2922}
2920 2923
2921static void game_print(drawing *dr, const game_state *state, int tilesize) 2924static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2925 int tilesize)
2922{ 2926{
2923 int w = state->w, h = state->h; 2927 int w = state->w, h = state->h;
2924 int ink = print_mono_colour(dr, 0); 2928 int ink = print_mono_colour(dr, 0);
@@ -2992,12 +2996,14 @@ const struct game thegame = {
2992 free_game, 2996 free_game,
2993 true, solve_game, 2997 true, solve_game,
2994 true, game_can_format_as_text_now, game_text_format, 2998 true, game_can_format_as_text_now, game_text_format,
2999 NULL, NULL, /* get_prefs, set_prefs */
2995 new_ui, 3000 new_ui,
2996 free_ui, 3001 free_ui,
2997 encode_ui, 3002 NULL, /* encode_ui */
2998 decode_ui, 3003 NULL, /* decode_ui */
2999 NULL, /* game_request_keys */ 3004 NULL, /* game_request_keys */
3000 game_changed_state, 3005 game_changed_state,
3006 current_key_label,
3001 interpret_move, 3007 interpret_move,
3002 execute_move, 3008 execute_move,
3003 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3009 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3011,7 +3017,7 @@ const struct game thegame = {
3011 game_status, 3017 game_status,
3012 true, false, game_print_size, game_print, 3018 true, false, game_print_size, game_print,
3013 true, /* wants_statusbar */ 3019 true, /* wants_statusbar */
3014 false, game_timing_state, 3020 false, NULL, /* timing_state */
3015 0, /* flags */ 3021 0, /* flags */
3016}; 3022};
3017 3023
diff --git a/apps/plugins/puzzles/src/resource.h b/apps/plugins/puzzles/src/resource.h
deleted file mode 100644
index f0bfa16d6d..0000000000
--- a/apps/plugins/puzzles/src/resource.h
+++ /dev/null
@@ -1,20 +0,0 @@
1
2#define IDR_MENUBAR1 101
3
4#define ID_GAME 40005
5#define ID_TYPE 40006
6
7#define IDS_CAP_GAME 40105
8#define IDS_CAP_TYPE 40106
9
10#define IDD_ABOUT 2000
11#define IDC_ABOUT_CAPTION 2001
12#define IDC_ABOUT_LINE 2002
13#define IDC_ABOUT_GAME 2003
14#define IDC_ABOUT_VERSION 2004
15
16#define IDD_CONFIG 2100
17#define IDC_CONFIG_CAPTION 2101
18#define IDC_CONFIG_LINE 2102
19
20#define IDR_PADTOOLBAR 4000
diff --git a/apps/plugins/puzzles/src/samegame.R b/apps/plugins/puzzles/src/samegame.R
deleted file mode 100644
index cc0d350041..0000000000
--- a/apps/plugins/puzzles/src/samegame.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3samegame : [X] GTK COMMON samegame samegame-icon|no-icon
4
5samegame : [G] WINDOWS COMMON samegame samegame.res|noicon.res
6
7ALL += samegame[COMBINED]
8
9!begin am gtk
10GAMES += samegame
11!end
12
13!begin >list.c
14 A(samegame) \
15!end
16
17!begin >gamedesc.txt
18samegame:samegame.exe:Same Game:Block-clearing puzzle:Clear the grid by removing touching groups of the same colour squares.
19!end
diff --git a/apps/plugins/puzzles/src/samegame.c b/apps/plugins/puzzles/src/samegame.c
index 615c60e0a5..30550e1f1c 100644
--- a/apps/plugins/puzzles/src/samegame.c
+++ b/apps/plugins/puzzles/src/samegame.c
@@ -67,7 +67,12 @@
67#include <string.h> 67#include <string.h>
68#include <assert.h> 68#include <assert.h>
69#include <ctype.h> 69#include <ctype.h>
70#include <math.h> 70#include <limits.h>
71#ifdef NO_TGMATH_H
72# include <math.h>
73#else
74# include <tgmath.h>
75#endif
71 76
72#include "puzzles.h" 77#include "puzzles.h"
73 78
@@ -285,6 +290,8 @@ static const char *validate_params(const game_params *params, bool full)
285{ 290{
286 if (params->w < 1 || params->h < 1) 291 if (params->w < 1 || params->h < 1)
287 return "Width and height must both be positive"; 292 return "Width and height must both be positive";
293 if (params->w > INT_MAX / params->h)
294 return "Width times height must not be unreasonably large";
288 295
289 if (params->ncols > 9) 296 if (params->ncols > 9)
290 return "Maximum of 9 colours"; 297 return "Maximum of 9 colours";
@@ -858,6 +865,8 @@ static void gen_grid(int w, int h, int nc, int *grid, random_state *rs)
858 865
859#if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES 866#if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES
860 printf("%d failures\n", failures); 867 printf("%d failures\n", failures);
868#else
869 (void)failures;
861#endif 870#endif
862#ifdef GENERATION_DIAGNOSTICS 871#ifdef GENERATION_DIAGNOSTICS
863 { 872 {
@@ -1013,12 +1022,6 @@ static void free_game(game_state *state)
1013 sfree(state); 1022 sfree(state);
1014} 1023}
1015 1024
1016static char *solve_game(const game_state *state, const game_state *currstate,
1017 const char *aux, const char **error)
1018{
1019 return NULL;
1020}
1021
1022static bool game_can_format_as_text_now(const game_params *params) 1025static bool game_can_format_as_text_now(const game_params *params)
1023{ 1026{
1024 return true; 1027 return true;
@@ -1065,7 +1068,7 @@ static game_ui *new_ui(const game_state *state)
1065 ui->nselected = 0; 1068 ui->nselected = 0;
1066 1069
1067 ui->xsel = ui->ysel = 0; 1070 ui->xsel = ui->ysel = 0;
1068 ui->displaysel = false; 1071 ui->displaysel = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1069 1072
1070 return ui; 1073 return ui;
1071} 1074}
@@ -1076,15 +1079,6 @@ static void free_ui(game_ui *ui)
1076 sfree(ui); 1079 sfree(ui);
1077} 1080}
1078 1081
1079static char *encode_ui(const game_ui *ui)
1080{
1081 return NULL;
1082}
1083
1084static void decode_ui(game_ui *ui, const char *encoding)
1085{
1086}
1087
1088static void sel_clear(game_ui *ui, const game_state *state) 1082static void sel_clear(game_ui *ui, const game_state *state)
1089{ 1083{
1090 int i; 1084 int i;
@@ -1099,14 +1093,25 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1099 const game_state *newstate) 1093 const game_state *newstate)
1100{ 1094{
1101 sel_clear(ui, newstate); 1095 sel_clear(ui, newstate);
1096}
1102 1097
1103 /* 1098static const char *current_key_label(const game_ui *ui,
1104 * If the game state has just changed into an unplayable one 1099 const game_state *state, int button)
1105 * (either completed or impossible), we vanish the keyboard- 1100{
1106 * control cursor. 1101 if (IS_CURSOR_SELECT(button)) {
1107 */ 1102 int x = ui->xsel, y = ui->ysel, c = COL(state,x,y);
1108 if (newstate->complete || newstate->impossible) 1103 if (c == 0) return "";
1109 ui->displaysel = false; 1104 if (ISSEL(ui, x, y))
1105 return button == CURSOR_SELECT2 ? "Unselect" : "Remove";
1106 if ((x > 0 && COL(state,x-1,y) == c) ||
1107 (x+1 < state->params.w && COL(state,x+1,y) == c) ||
1108 (y > 0 && COL(state,x,y-1) == c) ||
1109 (y+1 < state->params.h && COL(state,x,y+1) == c))
1110 return "Select";
1111 /* Cursor is over a lone square, so we can't select it. */
1112 if (ui->nselected) return "Unselect";
1113 }
1114 return "";
1110} 1115}
1111 1116
1112static char *sel_movedesc(game_ui *ui, const game_state *state) 1117static char *sel_movedesc(game_ui *ui, const game_state *state)
@@ -1274,30 +1279,25 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1274 int x, int y, int button) 1279 int x, int y, int button)
1275{ 1280{
1276 int tx, ty; 1281 int tx, ty;
1277 char *ret = UI_UPDATE; 1282 char *ret = MOVE_UI_UPDATE;
1278
1279 ui->displaysel = false;
1280 1283
1281 if (button == RIGHT_BUTTON || button == LEFT_BUTTON) { 1284 if (button == RIGHT_BUTTON || button == LEFT_BUTTON) {
1285 ui->displaysel = false;
1282 tx = FROMCOORD(x); ty= FROMCOORD(y); 1286 tx = FROMCOORD(x); ty= FROMCOORD(y);
1283 } else if (IS_CURSOR_MOVE(button)) { 1287 } else if (IS_CURSOR_MOVE(button)) {
1284 int dx = 0, dy = 0; 1288 return move_cursor(button, &ui->xsel, &ui->ysel,
1285 ui->displaysel = true; 1289 state->params.w, state->params.h,
1286 dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0); 1290 true, &ui->displaysel);
1287 dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
1288 ui->xsel = (ui->xsel + state->params.w + dx) % state->params.w;
1289 ui->ysel = (ui->ysel + state->params.h + dy) % state->params.h;
1290 return ret;
1291 } else if (IS_CURSOR_SELECT(button)) { 1291 } else if (IS_CURSOR_SELECT(button)) {
1292 ui->displaysel = true; 1292 ui->displaysel = true;
1293 tx = ui->xsel; 1293 tx = ui->xsel;
1294 ty = ui->ysel; 1294 ty = ui->ysel;
1295 } else 1295 } else
1296 return NULL; 1296 return MOVE_UNUSED;
1297 1297
1298 if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h) 1298 if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h)
1299 return NULL; 1299 return MOVE_UNUSED;
1300 if (COL(state, tx, ty) == 0) return NULL; 1300 if (COL(state, tx, ty) == 0) return MOVE_NO_EFFECT;
1301 1301
1302 if (ISSEL(ui,tx,ty)) { 1302 if (ISSEL(ui,tx,ty)) {
1303 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) 1303 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2)
@@ -1324,6 +1324,10 @@ static game_state *execute_move(const game_state *from, const char *move)
1324 move++; 1324 move++;
1325 1325
1326 while (*move) { 1326 while (*move) {
1327 if (!isdigit((unsigned char)*move)) {
1328 free_game(ret);
1329 return NULL;
1330 }
1327 i = atoi(move); 1331 i = atoi(move);
1328 if (i < 0 || i >= ret->n) { 1332 if (i < 0 || i >= ret->n) {
1329 free_game(ret); 1333 free_game(ret);
@@ -1353,12 +1357,12 @@ static game_state *execute_move(const game_state *from, const char *move)
1353static void game_set_size(drawing *dr, game_drawstate *ds, 1357static void game_set_size(drawing *dr, game_drawstate *ds,
1354 const game_params *params, int tilesize) 1358 const game_params *params, int tilesize)
1355{ 1359{
1356 ds->tilegap = 2; 1360 ds->tilegap = (tilesize + 8) / 16;
1357 ds->tileinner = tilesize - ds->tilegap; 1361 ds->tileinner = tilesize - ds->tilegap;
1358} 1362}
1359 1363
1360static void game_compute_size(const game_params *params, int tilesize, 1364static void game_compute_size(const game_params *params, int tilesize,
1361 int *x, int *y) 1365 const game_ui *ui, int *x, int *y)
1362{ 1366{
1363 /* Ick: fake up tile size variables for macro expansion purposes */ 1367 /* Ick: fake up tile size variables for macro expansion purposes */
1364 game_drawstate ads, *ds = &ads; 1368 game_drawstate ads, *ds = &ads;
@@ -1372,7 +1376,7 @@ static float *game_colours(frontend *fe, int *ncolours)
1372{ 1376{
1373 float *ret = snewn(3 * NCOLOURS, float); 1377 float *ret = snewn(3 * NCOLOURS, float);
1374 1378
1375 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 1379 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1376 1380
1377 ret[COL_1 * 3 + 0] = 0.0F; 1381 ret[COL_1 * 3 + 0] = 0.0F;
1378 ret[COL_1 * 3 + 1] = 0.0F; 1382 ret[COL_1 * 3 + 1] = 0.0F;
@@ -1386,8 +1390,8 @@ static float *game_colours(frontend *fe, int *ncolours)
1386 ret[COL_3 * 3 + 1] = 0.0F; 1390 ret[COL_3 * 3 + 1] = 0.0F;
1387 ret[COL_3 * 3 + 2] = 0.0F; 1391 ret[COL_3 * 3 + 2] = 0.0F;
1388 1392
1389 ret[COL_4 * 3 + 0] = 1.0F; 1393 ret[COL_4 * 3 + 0] = 0.7F;
1390 ret[COL_4 * 3 + 1] = 1.0F; 1394 ret[COL_4 * 3 + 1] = 0.7F;
1391 ret[COL_4 * 3 + 2] = 0.0F; 1395 ret[COL_4 * 3 + 2] = 0.0F;
1392 1396
1393 ret[COL_5 * 3 + 0] = 1.0F; 1397 ret[COL_5 * 3 + 0] = 1.0F;
@@ -1395,16 +1399,16 @@ static float *game_colours(frontend *fe, int *ncolours)
1395 ret[COL_5 * 3 + 2] = 1.0F; 1399 ret[COL_5 * 3 + 2] = 1.0F;
1396 1400
1397 ret[COL_6 * 3 + 0] = 0.0F; 1401 ret[COL_6 * 3 + 0] = 0.0F;
1398 ret[COL_6 * 3 + 1] = 1.0F; 1402 ret[COL_6 * 3 + 1] = 0.8F;
1399 ret[COL_6 * 3 + 2] = 1.0F; 1403 ret[COL_6 * 3 + 2] = 0.8F;
1400 1404
1401 ret[COL_7 * 3 + 0] = 0.5F; 1405 ret[COL_7 * 3 + 0] = 0.5F;
1402 ret[COL_7 * 3 + 1] = 0.5F; 1406 ret[COL_7 * 3 + 1] = 0.5F;
1403 ret[COL_7 * 3 + 2] = 1.0F; 1407 ret[COL_7 * 3 + 2] = 1.0F;
1404 1408
1405 ret[COL_8 * 3 + 0] = 0.5F; 1409 ret[COL_8 * 3 + 0] = 0.2F;
1406 ret[COL_8 * 3 + 1] = 1.0F; 1410 ret[COL_8 * 3 + 1] = 0.8F;
1407 ret[COL_8 * 3 + 2] = 0.5F; 1411 ret[COL_8 * 3 + 2] = 0.2F;
1408 1412
1409 ret[COL_9 * 3 + 0] = 1.0F; 1413 ret[COL_9 * 3 + 0] = 1.0F;
1410 ret[COL_9 * 3 + 1] = 0.5F; 1414 ret[COL_9 * 3 + 1] = 0.5F;
@@ -1418,14 +1422,6 @@ static float *game_colours(frontend *fe, int *ncolours)
1418 ret[COL_SEL * 3 + 1] = 1.0F; 1422 ret[COL_SEL * 3 + 1] = 1.0F;
1419 ret[COL_SEL * 3 + 2] = 1.0F; 1423 ret[COL_SEL * 3 + 2] = 1.0F;
1420 1424
1421 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
1422 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
1423 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
1424
1425 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
1426 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
1427 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
1428
1429 *ncolours = NCOLOURS; 1425 *ncolours = NCOLOURS;
1430 return ret; 1426 return ret;
1431} 1427}
@@ -1462,6 +1458,7 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
1462 int tile, int bgcolour) 1458 int tile, int bgcolour)
1463{ 1459{
1464 int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK; 1460 int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK;
1461 int tile_w, tile_h, outer_w, outer_h;
1465 1462
1466 if (col) { 1463 if (col) {
1467 if (tile & TILE_IMPOSSIBLE) { 1464 if (tile & TILE_IMPOSSIBLE) {
@@ -1474,19 +1471,25 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
1474 outer = inner = col; 1471 outer = inner = col;
1475 } 1472 }
1476 } 1473 }
1477 draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer); 1474 tile_w = dright ? TILE_SIZE : TILE_INNER;
1478 draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4, 1475 tile_h = dbelow ? TILE_SIZE : TILE_INNER;
1479 TILE_INNER/2, TILE_INNER/2, inner); 1476 outer_w = (tile & TILE_JOINRIGHT) ? tile_w : TILE_INNER;
1480 1477 outer_h = (tile & TILE_JOINDOWN) ? tile_h : TILE_INNER;
1481 if (dright) 1478 /* Draw the background if any of it will be visible. */
1482 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER, 1479 if (outer_w != tile_w || outer_h != tile_h || outer == bgcolour)
1483 (tile & TILE_JOINRIGHT) ? outer : bgcolour); 1480 draw_rect(dr, COORD(x), COORD(y), tile_w, tile_h, bgcolour);
1484 if (dbelow) 1481 /* Draw the piece. */
1485 draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP, 1482 if (outer != bgcolour)
1486 (tile & TILE_JOINDOWN) ? outer : bgcolour); 1483 draw_rect(dr, COORD(x), COORD(y), outer_w, outer_h, outer);
1487 if (dright && dbelow) 1484 if (inner != outer)
1488 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP, 1485 draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
1489 (tile & TILE_JOINDIAG) ? outer : bgcolour); 1486 TILE_INNER/2, TILE_INNER/2, inner);
1487 /* Reset bottom-right corner if necessary. */
1488 if ((tile & (TILE_JOINRIGHT | TILE_JOINDOWN | TILE_JOINDIAG)) ==
1489 (TILE_JOINRIGHT | TILE_JOINDOWN) && outer != bgcolour &&
1490 TILE_GAP != 0)
1491 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER,
1492 TILE_GAP, TILE_GAP, bgcolour);
1490 1493
1491 if (tile & TILE_HASSEL) { 1494 if (tile & TILE_HASSEL) {
1492 int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5; 1495 int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5;
@@ -1512,13 +1515,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1512 if (!ds->started) { 1515 if (!ds->started) {
1513 int coords[10]; 1516 int coords[10];
1514 1517
1515 draw_rect(dr, 0, 0,
1516 TILE_SIZE * state->params.w + 2 * BORDER,
1517 TILE_SIZE * state->params.h + 2 * BORDER, COL_BACKGROUND);
1518 draw_update(dr, 0, 0,
1519 TILE_SIZE * state->params.w + 2 * BORDER,
1520 TILE_SIZE * state->params.h + 2 * BORDER);
1521
1522 /* 1518 /*
1523 * Recessed area containing the whole puzzle. 1519 * Recessed area containing the whole puzzle.
1524 */ 1520 */
@@ -1541,7 +1537,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1541 ds->started = true; 1537 ds->started = true;
1542 } 1538 }
1543 1539
1544 if (flashtime > 0.0) { 1540 if (flashtime > 0.0F) {
1545 int frame = (int)(flashtime / FLASH_FRAME); 1541 int frame = (int)(flashtime / FLASH_FRAME);
1546 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); 1542 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
1547 } else 1543 } else
@@ -1564,8 +1560,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1564 if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) && 1560 if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) &&
1565 COL(state,x+1,y+1) == col) 1561 COL(state,x+1,y+1) == col)
1566 tile |= TILE_JOINDIAG; 1562 tile |= TILE_JOINDIAG;
1567 1563 /*
1568 if (ui->displaysel && ui->xsel == x && ui->ysel == y) 1564 * If the game state is an unplayable one (either
1565 * completed or impossible), we hide the keyboard-control
1566 * cursor.
1567 */
1568 if (ui->displaysel && ui->xsel == x && ui->ysel == y &&
1569 !(state->complete || state->impossible))
1569 tile |= TILE_HASSEL; 1570 tile |= TILE_HASSEL;
1570 1571
1571 /* For now we're never expecting oldstate at all (because we have 1572 /* For now we're never expecting oldstate at all (because we have
@@ -1637,19 +1638,6 @@ static int game_status(const game_state *state)
1637 return state->complete ? +1 : 0; 1638 return state->complete ? +1 : 0;
1638} 1639}
1639 1640
1640static bool game_timing_state(const game_state *state, game_ui *ui)
1641{
1642 return true;
1643}
1644
1645static void game_print_size(const game_params *params, float *x, float *y)
1646{
1647}
1648
1649static void game_print(drawing *dr, const game_state *state, int tilesize)
1650{
1651}
1652
1653#ifdef COMBINED 1641#ifdef COMBINED
1654#define thegame samegame 1642#define thegame samegame
1655#endif 1643#endif
@@ -1669,14 +1657,16 @@ const struct game thegame = {
1669 new_game, 1657 new_game,
1670 dup_game, 1658 dup_game,
1671 free_game, 1659 free_game,
1672 false, solve_game, 1660 false, NULL, /* solve */
1673 true, game_can_format_as_text_now, game_text_format, 1661 true, game_can_format_as_text_now, game_text_format,
1662 NULL, NULL, /* get_prefs, set_prefs */
1674 new_ui, 1663 new_ui,
1675 free_ui, 1664 free_ui,
1676 encode_ui, 1665 NULL, /* encode_ui */
1677 decode_ui, 1666 NULL, /* decode_ui */
1678 NULL, /* game_request_keys */ 1667 NULL, /* game_request_keys */
1679 game_changed_state, 1668 game_changed_state,
1669 current_key_label,
1680 interpret_move, 1670 interpret_move,
1681 execute_move, 1671 execute_move,
1682 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1672 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1688,8 +1678,8 @@ const struct game thegame = {
1688 game_flash_length, 1678 game_flash_length,
1689 game_get_cursor_location, 1679 game_get_cursor_location,
1690 game_status, 1680 game_status,
1691 false, false, game_print_size, game_print, 1681 false, false, NULL, NULL, /* print_size, print */
1692 true, /* wants_statusbar */ 1682 true, /* wants_statusbar */
1693 false, game_timing_state, 1683 false, NULL, /* timing_state */
1694 0, /* flags */ 1684 0, /* flags */
1695}; 1685};
diff --git a/apps/plugins/puzzles/src/signpost.R b/apps/plugins/puzzles/src/signpost.R
deleted file mode 100644
index 09ea367d57..0000000000
--- a/apps/plugins/puzzles/src/signpost.R
+++ /dev/null
@@ -1,23 +0,0 @@
1# -*- makefile -*-
2
3SIGNPOST_EXTRA = dsf
4
5signpost : [X] GTK COMMON signpost SIGNPOST_EXTRA signpost-icon|no-icon
6signpost : [G] WINDOWS COMMON signpost SIGNPOST_EXTRA signpost.res|noicon.res
7
8signpostsolver : [U] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE m.lib
9signpostsolver : [C] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE
10
11ALL += signpost[COMBINED] SIGNPOST_EXTRA
12
13!begin am gtk
14GAMES += signpost
15!end
16
17!begin >list.c
18 A(signpost) \
19!end
20
21!begin >gamedesc.txt
22signpost:signpost.exe:Signpost:Square-connecting puzzle:Connect the squares into a path following the arrows.
23!end
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c
index 9aee255ebe..9aed67bb4a 100644
--- a/apps/plugins/puzzles/src/signpost.c
+++ b/apps/plugins/puzzles/src/signpost.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13 18
@@ -57,7 +62,7 @@ struct game_state {
57 int *nums; /* numbers, size n */ 62 int *nums; /* numbers, size n */
58 unsigned int *flags; /* flags, size n */ 63 unsigned int *flags; /* flags, size n */
59 int *next, *prev; /* links to other cell indexes, size n (-1 absent) */ 64 int *next, *prev; /* links to other cell indexes, size n (-1 absent) */
60 int *dsf; /* connects regions with a dsf. */ 65 DSF *dsf; /* connects regions with a dsf. */
61 int *numsi; /* for each number, which index is it in? (-1 absent) */ 66 int *numsi; /* for each number, which index is it in? (-1 absent) */
62}; 67};
63 68
@@ -168,7 +173,7 @@ static bool isvalidmove(const game_state *state, bool clever,
168 173
169 /* can't create a new connection between cells in the same region 174 /* can't create a new connection between cells in the same region
170 * as that would create a loop. */ 175 * as that would create a loop. */
171 if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to)) 176 if (dsf_equivalent(state->dsf, from, to))
172 return false; 177 return false;
173 178
174 /* if both cells are actual numbers, can't drag if we're not 179 /* if both cells are actual numbers, can't drag if we're not
@@ -277,7 +282,7 @@ static void strip_nums(game_state *state) {
277 memset(state->next, -1, state->n*sizeof(int)); 282 memset(state->next, -1, state->n*sizeof(int));
278 memset(state->prev, -1, state->n*sizeof(int)); 283 memset(state->prev, -1, state->n*sizeof(int));
279 memset(state->numsi, -1, (state->n+1)*sizeof(int)); 284 memset(state->numsi, -1, (state->n+1)*sizeof(int));
280 dsf_init(state->dsf, state->n); 285 dsf_reinit(state->dsf);
281} 286}
282 287
283static bool check_nums(game_state *orig, game_state *copy, bool only_immutable) 288static bool check_nums(game_state *orig, game_state *copy, bool only_immutable)
@@ -421,6 +426,8 @@ static const char *validate_params(const game_params *params, bool full)
421{ 426{
422 if (params->w < 1) return "Width must be at least one"; 427 if (params->w < 1) return "Width must be at least one";
423 if (params->h < 1) return "Height must be at least one"; 428 if (params->h < 1) return "Height must be at least one";
429 if (params->w > INT_MAX / params->h)
430 return "Width times height must not be unreasonably large";
424 if (full && params->w == 1 && params->h == 1) 431 if (full && params->w == 1 && params->h == 1)
425 /* The UI doesn't let us move these from unsolved to solved, 432 /* The UI doesn't let us move these from unsolved to solved,
426 * so we disallow generating (but not playing) them. */ 433 * so we disallow generating (but not playing) them. */
@@ -454,7 +461,7 @@ static game_state *blank_game(int w, int h)
454 state->flags = snewn(state->n, unsigned int); 461 state->flags = snewn(state->n, unsigned int);
455 state->next = snewn(state->n, int); 462 state->next = snewn(state->n, int);
456 state->prev = snewn(state->n, int); 463 state->prev = snewn(state->n, int);
457 state->dsf = snew_dsf(state->n); 464 state->dsf = dsf_new(state->n);
458 state->numsi = snewn(state->n+1, int); 465 state->numsi = snewn(state->n+1, int);
459 466
460 blank_game_into(state); 467 blank_game_into(state);
@@ -475,7 +482,7 @@ static void dup_game_to(game_state *to, const game_state *from)
475 memcpy(to->next, from->next, to->n*sizeof(int)); 482 memcpy(to->next, from->next, to->n*sizeof(int));
476 memcpy(to->prev, from->prev, to->n*sizeof(int)); 483 memcpy(to->prev, from->prev, to->n*sizeof(int));
477 484
478 memcpy(to->dsf, from->dsf, to->n*sizeof(int)); 485 dsf_copy(to->dsf, from->dsf);
479 memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); 486 memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int));
480} 487}
481 488
@@ -493,7 +500,7 @@ static void free_game(game_state *state)
493 sfree(state->flags); 500 sfree(state->flags);
494 sfree(state->next); 501 sfree(state->next);
495 sfree(state->prev); 502 sfree(state->prev);
496 sfree(state->dsf); 503 dsf_free(state->dsf);
497 sfree(state->numsi); 504 sfree(state->numsi);
498 sfree(state); 505 sfree(state);
499} 506}
@@ -1011,7 +1018,7 @@ static void connect_numbers(game_state *state)
1011{ 1018{
1012 int i, di, dni; 1019 int i, di, dni;
1013 1020
1014 dsf_init(state->dsf, state->n); 1021 dsf_reinit(state->dsf);
1015 for (i = 0; i < state->n; i++) { 1022 for (i = 0; i < state->n; i++) {
1016 if (state->next[i] != -1) { 1023 if (state->next[i] != -1) {
1017 assert(state->prev[state->next[i]] == i); 1024 assert(state->prev[state->next[i]] == i);
@@ -1028,8 +1035,8 @@ static void connect_numbers(game_state *state)
1028 1035
1029static int compare_heads(const void *a, const void *b) 1036static int compare_heads(const void *a, const void *b)
1030{ 1037{
1031 struct head_meta *ha = (struct head_meta *)a; 1038 const struct head_meta *ha = (const struct head_meta *)a;
1032 struct head_meta *hb = (struct head_meta *)b; 1039 const struct head_meta *hb = (const struct head_meta *)b;
1033 1040
1034 /* Heads with preferred colours first... */ 1041 /* Heads with preferred colours first... */
1035 if (ha->preference && !hb->preference) return -1; 1042 if (ha->preference && !hb->preference) return -1;
@@ -1380,8 +1387,32 @@ struct game_ui {
1380 bool dragging, drag_is_from; 1387 bool dragging, drag_is_from;
1381 int sx, sy; /* grid coords of start cell */ 1388 int sx, sy; /* grid coords of start cell */
1382 int dx, dy; /* pixel coords of drag posn */ 1389 int dx, dy; /* pixel coords of drag posn */
1390
1391 /*
1392 * Trivial and foolish configurable option done on purest whim.
1393 * With this option enabled, the victory flash is done by rotating
1394 * each square in the opposite direction from its immediate
1395 * neighbours, so that they behave like a field of interlocking
1396 * gears. With it disabled, they all rotate in the same direction.
1397 * Choose for yourself which is more brain-twisting :-)
1398 */
1399 bool gear_mode;
1383}; 1400};
1384 1401
1402static void legacy_prefs_override(struct game_ui *ui_out)
1403{
1404 static bool initialised = false;
1405 static int gear_mode = -1;
1406
1407 if (!initialised) {
1408 initialised = true;
1409 gear_mode = getenv_bool("SIGNPOST_GEARS", -1);
1410 }
1411
1412 if (gear_mode != -1)
1413 ui_out->gear_mode = gear_mode;
1414}
1415
1385static game_ui *new_ui(const game_state *state) 1416static game_ui *new_ui(const game_state *state)
1386{ 1417{
1387 game_ui *ui = snew(game_ui); 1418 game_ui *ui = snew(game_ui);
@@ -1390,11 +1421,14 @@ static game_ui *new_ui(const game_state *state)
1390 * copy to clone, there's code that needs fixing in game_redraw too. */ 1421 * copy to clone, there's code that needs fixing in game_redraw too. */
1391 1422
1392 ui->cx = ui->cy = 0; 1423 ui->cx = ui->cy = 0;
1393 ui->cshow = false; 1424 ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1394 1425
1395 ui->dragging = false; 1426 ui->dragging = false;
1396 ui->sx = ui->sy = ui->dx = ui->dy = 0; 1427 ui->sx = ui->sy = ui->dx = ui->dy = 0;
1397 1428
1429 ui->gear_mode = false;
1430 legacy_prefs_override(ui);
1431
1398 return ui; 1432 return ui;
1399} 1433}
1400 1434
@@ -1403,13 +1437,28 @@ static void free_ui(game_ui *ui)
1403 sfree(ui); 1437 sfree(ui);
1404} 1438}
1405 1439
1406static char *encode_ui(const game_ui *ui) 1440static config_item *get_prefs(game_ui *ui)
1407{ 1441{
1408 return NULL; 1442 config_item *ret;
1443
1444 ret = snewn(2, config_item);
1445
1446 ret[0].name = "Victory rotation effect";
1447 ret[0].kw = "flash-type";
1448 ret[0].type = C_CHOICES;
1449 ret[0].u.choices.choicenames = ":Unidirectional:Meshing gears";
1450 ret[0].u.choices.choicekws = ":unidirectional:gears";
1451 ret[0].u.choices.selected = ui->gear_mode;
1452
1453 ret[1].name = NULL;
1454 ret[1].type = C_END;
1455
1456 return ret;
1409} 1457}
1410 1458
1411static void decode_ui(game_ui *ui, const char *encoding) 1459static void set_prefs(game_ui *ui, const config_item *cfg)
1412{ 1460{
1461 ui->gear_mode = cfg[0].u.choices.selected;
1413} 1462}
1414 1463
1415static void game_changed_state(game_ui *ui, const game_state *oldstate, 1464static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1421,6 +1470,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1421 } 1470 }
1422} 1471}
1423 1472
1473static const char *current_key_label(const game_ui *ui,
1474 const game_state *state, int button)
1475{
1476 if (IS_CURSOR_SELECT(button) && ui->cshow) {
1477 if (ui->dragging) {
1478 if (ui->drag_is_from) {
1479 if (isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy))
1480 return "To here";
1481 } else {
1482 if (isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy))
1483 return "From here";
1484 }
1485 return "Cancel";
1486 } else {
1487 return button == CURSOR_SELECT ? "From here" : "To here";
1488 }
1489 }
1490 return "";
1491}
1492
1424struct game_drawstate { 1493struct game_drawstate {
1425 int tilesize; 1494 int tilesize;
1426 bool started, solved; 1495 bool started, solved;
@@ -1442,26 +1511,27 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1442 char buf[80]; 1511 char buf[80];
1443 1512
1444 if (IS_CURSOR_MOVE(button)) { 1513 if (IS_CURSOR_MOVE(button)) {
1445 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false); 1514 char *ret;
1446 ui->cshow = true; 1515 ret = move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false,
1516 &ui->cshow);
1447 if (ui->dragging) { 1517 if (ui->dragging) {
1448 ui->dx = COORD(ui->cx) + TILE_SIZE/2; 1518 ui->dx = COORD(ui->cx) + TILE_SIZE/2;
1449 ui->dy = COORD(ui->cy) + TILE_SIZE/2; 1519 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1450 } 1520 }
1451 return UI_UPDATE; 1521 return ret;
1452 } else if (IS_CURSOR_SELECT(button)) { 1522 } else if (IS_CURSOR_SELECT(button)) {
1453 if (!ui->cshow) 1523 if (!ui->cshow)
1454 ui->cshow = true; 1524 ui->cshow = true;
1455 else if (ui->dragging) { 1525 else if (ui->dragging) {
1456 ui->dragging = false; 1526 ui->dragging = false;
1457 if (ui->sx == ui->cx && ui->sy == ui->cy) return UI_UPDATE; 1527 if (ui->sx == ui->cx && ui->sy == ui->cy) return MOVE_UI_UPDATE;
1458 if (ui->drag_is_from) { 1528 if (ui->drag_is_from) {
1459 if (!isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) 1529 if (!isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy))
1460 return UI_UPDATE; 1530 return MOVE_UI_UPDATE;
1461 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy); 1531 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy);
1462 } else { 1532 } else {
1463 if (!isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) 1533 if (!isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy))
1464 return UI_UPDATE; 1534 return MOVE_UI_UPDATE;
1465 sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy); 1535 sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy);
1466 } 1536 }
1467 return dupstr(buf); 1537 return dupstr(buf);
@@ -1473,7 +1543,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1473 ui->dy = COORD(ui->cy) + TILE_SIZE/2; 1543 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1474 ui->drag_is_from = (button == CURSOR_SELECT); 1544 ui->drag_is_from = (button == CURSOR_SELECT);
1475 } 1545 }
1476 return UI_UPDATE; 1546 return MOVE_UI_UPDATE;
1477 } 1547 }
1478 if (IS_MOUSE_DOWN(button)) { 1548 if (IS_MOUSE_DOWN(button)) {
1479 if (ui->cshow) { 1549 if (ui->cshow) {
@@ -1502,19 +1572,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1502 ui->dx = mx; 1572 ui->dx = mx;
1503 ui->dy = my; 1573 ui->dy = my;
1504 ui->cshow = false; 1574 ui->cshow = false;
1505 return UI_UPDATE; 1575 return MOVE_UI_UPDATE;
1506 } else if (IS_MOUSE_DRAG(button) && ui->dragging) { 1576 } else if (IS_MOUSE_DRAG(button) && ui->dragging) {
1507 ui->dx = mx; 1577 ui->dx = mx;
1508 ui->dy = my; 1578 ui->dy = my;
1509 return UI_UPDATE; 1579 return MOVE_UI_UPDATE;
1510 } else if (IS_MOUSE_RELEASE(button) && ui->dragging) { 1580 } else if (IS_MOUSE_RELEASE(button) && ui->dragging) {
1511 ui->dragging = false; 1581 ui->dragging = false;
1512 if (ui->sx == x && ui->sy == y) return UI_UPDATE; /* single click */ 1582 if (ui->sx == x && ui->sy == y) return MOVE_UI_UPDATE; /* single click */
1513 1583
1514 if (!INGRID(state, x, y)) { 1584 if (!INGRID(state, x, y)) {
1515 int si = ui->sy*w+ui->sx; 1585 int si = ui->sy*w+ui->sx;
1516 if (state->prev[si] == -1 && state->next[si] == -1) 1586 if (state->prev[si] == -1 && state->next[si] == -1)
1517 return UI_UPDATE; 1587 return MOVE_UI_UPDATE;
1518 sprintf(buf, "%c%d,%d", 1588 sprintf(buf, "%c%d,%d",
1519 (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy); 1589 (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy);
1520 return dupstr(buf); 1590 return dupstr(buf);
@@ -1522,11 +1592,11 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1522 1592
1523 if (ui->drag_is_from) { 1593 if (ui->drag_is_from) {
1524 if (!isvalidmove(state, false, ui->sx, ui->sy, x, y)) 1594 if (!isvalidmove(state, false, ui->sx, ui->sy, x, y))
1525 return UI_UPDATE; 1595 return MOVE_UI_UPDATE;
1526 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y); 1596 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y);
1527 } else { 1597 } else {
1528 if (!isvalidmove(state, false, x, y, ui->sx, ui->sy)) 1598 if (!isvalidmove(state, false, x, y, ui->sx, ui->sy))
1529 return UI_UPDATE; 1599 return MOVE_UI_UPDATE;
1530 sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy); 1600 sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy);
1531 } 1601 }
1532 return dupstr(buf); 1602 return dupstr(buf);
@@ -1535,7 +1605,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1535 else if ((button == 'x' || button == 'X') && ui->cshow) { 1605 else if ((button == 'x' || button == 'X') && ui->cshow) {
1536 int si = ui->cy*w + ui->cx; 1606 int si = ui->cy*w + ui->cx;
1537 if (state->prev[si] == -1 && state->next[si] == -1) 1607 if (state->prev[si] == -1 && state->next[si] == -1)
1538 return UI_UPDATE; 1608 return MOVE_UI_UPDATE;
1539 sprintf(buf, "%c%d,%d", 1609 sprintf(buf, "%c%d,%d",
1540 (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy); 1610 (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy);
1541 return dupstr(buf); 1611 return dupstr(buf);
@@ -1641,7 +1711,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1641 */ 1711 */
1642 1712
1643static void game_compute_size(const game_params *params, int tilesize, 1713static void game_compute_size(const game_params *params, int tilesize,
1644 int *x, int *y) 1714 const game_ui *ui, int *x, int *y)
1645{ 1715{
1646 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1716 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1647 struct { int tilesize, order; } ads, *ds = &ads; 1717 struct { int tilesize, order; } ads, *ds = &ads;
@@ -1825,9 +1895,8 @@ static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints,
1825 coords = snewn(npoints * 2 * 2, int); 1895 coords = snewn(npoints * 2 * 2, int);
1826 1896
1827 for (n = 0; n < npoints * 2; n++) { 1897 for (n = 0; n < npoints * 2; n++) {
1828 /* hack to accomodate rockbox's concave polygon drawing */ 1898 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset;
1829 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset - PI / npoints; 1899 r = (n % 2) ? (double)rad/2.0 : (double)rad;
1830 r = (n % 2 == 0) ? (double)rad/2.0 : (double)rad;
1831 1900
1832 /* We're rotating the point at (0, -r) by a degrees */ 1901 /* We're rotating the point at (0, -r) by a degrees */
1833 coords[2*n+0] = cx + (int)( r * sin(a)); 1902 coords[2*n+0] = cx + (int)( r * sin(a));
@@ -2082,7 +2151,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2082 if (!ds->started) { 2151 if (!ds->started) {
2083 int aw = TILE_SIZE * state->w; 2152 int aw = TILE_SIZE * state->w;
2084 int ah = TILE_SIZE * state->h; 2153 int ah = TILE_SIZE * state->h;
2085 draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND);
2086 draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID); 2154 draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID);
2087 draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER); 2155 draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER);
2088 } 2156 }
@@ -2127,28 +2195,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2127 if (state->nums[i] != ds->nums[i] || 2195 if (state->nums[i] != ds->nums[i] ||
2128 f != ds->f[i] || dirp != ds->dirp[i] || 2196 f != ds->f[i] || dirp != ds->dirp[i] ||
2129 force || !ds->started) { 2197 force || !ds->started) {
2130 int sign; 2198 int sign = (ui->gear_mode ? 1 - 2 * ((x ^ y) & 1) : 1);
2131 {
2132 /*
2133 * Trivial and foolish configurable option done on
2134 * purest whim. With this option enabled, the
2135 * victory flash is done by rotating each square
2136 * in the opposite direction from its immediate
2137 * neighbours, so that they behave like a field of
2138 * interlocking gears. With it disabled, they all
2139 * rotate in the same direction. Choose for
2140 * yourself which is more brain-twisting :-)
2141 */
2142 static int gear_mode = -1;
2143 if (gear_mode < 0) {
2144 char *env = getenv("SIGNPOST_GEARS");
2145 gear_mode = (env && (env[0] == 'y' || env[0] == 'Y'));
2146 }
2147 if (gear_mode)
2148 sign = 1 - 2 * ((x ^ y) & 1);
2149 else
2150 sign = 1;
2151 }
2152 tile_redraw(dr, ds, 2199 tile_redraw(dr, ds,
2153 BORDER + x * TILE_SIZE, 2200 BORDER + x * TILE_SIZE,
2154 BORDER + y * TILE_SIZE, 2201 BORDER + y * TILE_SIZE,
@@ -2206,21 +2253,18 @@ static int game_status(const game_state *state)
2206 return state->completed ? +1 : 0; 2253 return state->completed ? +1 : 0;
2207} 2254}
2208 2255
2209static bool game_timing_state(const game_state *state, game_ui *ui) 2256static void game_print_size(const game_params *params, const game_ui *ui,
2210{ 2257 float *x, float *y)
2211 return true;
2212}
2213
2214static void game_print_size(const game_params *params, float *x, float *y)
2215{ 2258{
2216 int pw, ph; 2259 int pw, ph;
2217 2260
2218 game_compute_size(params, 1300, &pw, &ph); 2261 game_compute_size(params, 1300, ui, &pw, &ph);
2219 *x = pw / 100.0F; 2262 *x = pw / 100.0F;
2220 *y = ph / 100.0F; 2263 *y = ph / 100.0F;
2221} 2264}
2222 2265
2223static void game_print(drawing *dr, const game_state *state, int tilesize) 2266static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2267 int tilesize)
2224{ 2268{
2225 int ink = print_mono_colour(dr, 0); 2269 int ink = print_mono_colour(dr, 0);
2226 int x, y; 2270 int x, y;
@@ -2273,12 +2317,14 @@ const struct game thegame = {
2273 free_game, 2317 free_game,
2274 true, solve_game, 2318 true, solve_game,
2275 true, game_can_format_as_text_now, game_text_format, 2319 true, game_can_format_as_text_now, game_text_format,
2320 get_prefs, set_prefs,
2276 new_ui, 2321 new_ui,
2277 free_ui, 2322 free_ui,
2278 encode_ui, 2323 NULL, /* encode_ui */
2279 decode_ui, 2324 NULL, /* decode_ui */
2280 NULL, /* game_request_keys */ 2325 NULL, /* game_request_keys */
2281 game_changed_state, 2326 game_changed_state,
2327 current_key_label,
2282 interpret_move, 2328 interpret_move,
2283 execute_move, 2329 execute_move,
2284 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2330 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2292,7 +2338,7 @@ const struct game thegame = {
2292 game_status, 2338 game_status,
2293 true, false, game_print_size, game_print, 2339 true, false, game_print_size, game_print,
2294 false, /* wants_statusbar */ 2340 false, /* wants_statusbar */
2295 false, game_timing_state, 2341 false, NULL, /* timing_state */
2296 REQUIRE_RBUTTON, /* flags */ 2342 REQUIRE_RBUTTON, /* flags */
2297}; 2343};
2298 2344
@@ -2301,10 +2347,9 @@ const struct game thegame = {
2301#include <time.h> 2347#include <time.h>
2302#include <stdarg.h> 2348#include <stdarg.h>
2303 2349
2304const char *quis = NULL; 2350static const char *quis = NULL;
2305int verbose = 0;
2306 2351
2307void usage(FILE *out) { 2352static void usage(FILE *out) {
2308 fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis); 2353 fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis);
2309} 2354}
2310 2355
@@ -2405,7 +2450,7 @@ static void process_desc(char *id)
2405 thegame.free_params(p); 2450 thegame.free_params(p);
2406} 2451}
2407 2452
2408int main(int argc, const char *argv[]) 2453int main(int argc, char *argv[])
2409{ 2454{
2410 char *id = NULL, *desc, *aux = NULL; 2455 char *id = NULL, *desc, *aux = NULL;
2411 const char *err; 2456 const char *err;
diff --git a/apps/plugins/puzzles/src/singles.R b/apps/plugins/puzzles/src/singles.R
deleted file mode 100644
index a67aed2fbc..0000000000
--- a/apps/plugins/puzzles/src/singles.R
+++ /dev/null
@@ -1,23 +0,0 @@
1# -*- makefile -*-
2
3SINGLES_EXTRA = dsf LATIN
4
5singles : [X] GTK COMMON singles SINGLES_EXTRA singles-icon|no-icon
6singles : [G] WINDOWS COMMON singles SINGLES_EXTRA singles.res|noicon.res
7
8ALL += singles[COMBINED] SINGLES_EXTRA
9
10singlessolver : [U] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
11singlessolver : [C] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
12
13!begin am gtk
14GAMES += singles
15!end
16
17!begin >list.c
18 A(singles) \
19!end
20
21!begin >gamedesc.txt
22singles:singles.exe:Singles:Number-removing puzzle:Black out the right set of duplicate numbers.
23!end
diff --git a/apps/plugins/puzzles/src/singles.c b/apps/plugins/puzzles/src/singles.c
index 202ce08b20..07db8361ae 100644
--- a/apps/plugins/puzzles/src/singles.c
+++ b/apps/plugins/puzzles/src/singles.c
@@ -58,13 +58,17 @@
58#include <string.h> 58#include <string.h>
59#include <assert.h> 59#include <assert.h>
60#include <ctype.h> 60#include <ctype.h>
61#include <math.h> 61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
62 66
63#include "puzzles.h" 67#include "puzzles.h"
64#include "latin.h" 68#include "latin.h"
65 69
66#ifdef STANDALONE_SOLVER 70#ifdef STANDALONE_SOLVER
67bool verbose = false; 71static bool verbose = false;
68#endif 72#endif
69 73
70#define PREFERRED_TILE_SIZE 32 74#define PREFERRED_TILE_SIZE 32
@@ -82,7 +86,7 @@ bool verbose = false;
82#define FLASH_TIME 0.7F 86#define FLASH_TIME 0.7F
83 87
84enum { 88enum {
85 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, 89 COL_BACKGROUND, COL_UNUSED1, COL_LOWLIGHT,
86 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID, 90 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID,
87 COL_CURSOR, COL_ERROR, 91 COL_CURSOR, COL_ERROR,
88 NCOLOURS 92 NCOLOURS
@@ -254,7 +258,7 @@ static game_params *custom_params(const config_item *cfg)
254static const char *validate_params(const game_params *params, bool full) 258static const char *validate_params(const game_params *params, bool full)
255{ 259{
256 if (params->w < 2 || params->h < 2) 260 if (params->w < 2 || params->h < 2)
257 return "Width and neight must be at least two"; 261 return "Width and height must be at least two";
258 if (params->w > 10+26+26 || params->h > 10+26+26) 262 if (params->w > 10+26+26 || params->h > 10+26+26)
259 return "Puzzle is too large"; 263 return "Puzzle is too large";
260 if (full) { 264 if (full) {
@@ -417,7 +421,7 @@ static void debug_state(const char *desc, game_state *state) {
417 sfree(dbg); 421 sfree(dbg);
418} 422}
419 423
420static void connect_if_same(game_state *state, int *dsf, int i1, int i2) 424static void connect_if_same(game_state *state, DSF *dsf, int i1, int i2)
421{ 425{
422 int c1, c2; 426 int c1, c2;
423 427
@@ -429,13 +433,13 @@ static void connect_if_same(game_state *state, int *dsf, int i1, int i2)
429 dsf_merge(dsf, c1, c2); 433 dsf_merge(dsf, c1, c2);
430} 434}
431 435
432static void connect_dsf(game_state *state, int *dsf) 436static void connect_dsf(game_state *state, DSF *dsf)
433{ 437{
434 int x, y, i; 438 int x, y, i;
435 439
436 /* Construct a dsf array for connected blocks; connections 440 /* Construct a dsf array for connected blocks; connections
437 * tracked to right and down. */ 441 * tracked to right and down. */
438 dsf_init(dsf, state->n); 442 dsf_reinit(dsf);
439 for (x = 0; x < state->w; x++) { 443 for (x = 0; x < state->w; x++) {
440 for (y = 0; y < state->h; y++) { 444 for (y = 0; y < state->h; y++) {
441 i = y*state->w + x; 445 i = y*state->w + x;
@@ -494,7 +498,7 @@ static int check_rowcol(game_state *state, int starti, int di, int sz, unsigned
494 498
495static bool check_complete(game_state *state, unsigned flags) 499static bool check_complete(game_state *state, unsigned flags)
496{ 500{
497 int *dsf = snewn(state->n, int); 501 DSF *dsf = dsf_new(state->n);
498 int x, y, i, error = 0, nwhite, w = state->w, h = state->h; 502 int x, y, i, error = 0, nwhite, w = state->w, h = state->h;
499 503
500 if (flags & CC_MARK_ERRORS) { 504 if (flags & CC_MARK_ERRORS) {
@@ -543,7 +547,7 @@ static bool check_complete(game_state *state, unsigned flags)
543 int size = dsf_size(dsf, i); 547 int size = dsf_size(dsf, i);
544 if (largest < size) { 548 if (largest < size) {
545 largest = size; 549 largest = size;
546 canonical = i; 550 canonical = dsf_canonify(dsf, i);
547 } 551 }
548 } 552 }
549 553
@@ -558,7 +562,7 @@ static bool check_complete(game_state *state, unsigned flags)
558 } 562 }
559 } 563 }
560 564
561 sfree(dsf); 565 dsf_free(dsf);
562 return !(error > 0); 566 return !(error > 0);
563} 567}
564 568
@@ -1304,9 +1308,10 @@ found:
1304 return j; 1308 return j;
1305} 1309}
1306 1310
1307static char *new_game_desc(const game_params *params, random_state *rs, 1311static char *new_game_desc(const game_params *params_orig, random_state *rs,
1308 char **aux, bool interactive) 1312 char **aux, bool interactive)
1309{ 1313{
1314 game_params *params = dup_params(params_orig);
1310 game_state *state = blank_game(params->w, params->h); 1315 game_state *state = blank_game(params->w, params->h);
1311 game_state *tosolve = blank_game(params->w, params->h); 1316 game_state *tosolve = blank_game(params->w, params->h);
1312 int i, j, *scratch, *rownums, *colnums, x, y, ntries; 1317 int i, j, *scratch, *rownums, *colnums, x, y, ntries;
@@ -1315,6 +1320,12 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1315 digit *latin; 1320 digit *latin;
1316 struct solver_state *ss = solver_state_new(state); 1321 struct solver_state *ss = solver_state_new(state);
1317 1322
1323 /* Downgrade difficulty to Easy for puzzles so tiny that they aren't
1324 * possible to generate at Tricky. These are 2x2, 2x3 and 3x3, i.e.
1325 * any puzzle that doesn't have one dimension at least 4. */
1326 if ((w < 4 || h < 4) && params->diff > DIFF_EASY)
1327 params->diff = DIFF_EASY;
1328
1318 scratch = snewn(state->n, int); 1329 scratch = snewn(state->n, int);
1319 rownums = snewn(h*o, int); 1330 rownums = snewn(h*o, int);
1320 colnums = snewn(w*o, int); 1331 colnums = snewn(w*o, int);
@@ -1408,6 +1419,7 @@ randomise:
1408 1419
1409 free_game(tosolve); 1420 free_game(tosolve);
1410 free_game(state); 1421 free_game(state);
1422 free_params(params);
1411 solver_state_free(ss); 1423 solver_state_free(ss);
1412 sfree(scratch); 1424 sfree(scratch);
1413 sfree(rownums); 1425 sfree(rownums);
@@ -1446,24 +1458,37 @@ static game_ui *new_ui(const game_state *state)
1446 game_ui *ui = snew(game_ui); 1458 game_ui *ui = snew(game_ui);
1447 1459
1448 ui->cx = ui->cy = 0; 1460 ui->cx = ui->cy = 0;
1449 ui->cshow = false; 1461 ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1450 ui->show_black_nums = false; 1462 ui->show_black_nums = false;
1451 1463
1452 return ui; 1464 return ui;
1453} 1465}
1454 1466
1455static void free_ui(game_ui *ui) 1467static config_item *get_prefs(game_ui *ui)
1456{ 1468{
1457 sfree(ui); 1469 config_item *ret;
1470
1471 ret = snewn(2, config_item);
1472
1473 ret[0].name = "Show numbers on black squares";
1474 ret[0].kw = "show-black-nums";
1475 ret[0].type = C_BOOLEAN;
1476 ret[0].u.boolean.bval = ui->show_black_nums;
1477
1478 ret[1].name = NULL;
1479 ret[1].type = C_END;
1480
1481 return ret;
1458} 1482}
1459 1483
1460static char *encode_ui(const game_ui *ui) 1484static void set_prefs(game_ui *ui, const config_item *cfg)
1461{ 1485{
1462 return NULL; 1486 ui->show_black_nums = cfg[0].u.boolean.bval;
1463} 1487}
1464 1488
1465static void decode_ui(game_ui *ui, const char *encoding) 1489static void free_ui(game_ui *ui)
1466{ 1490{
1491 sfree(ui);
1467} 1492}
1468 1493
1469static void game_changed_state(game_ui *ui, const game_state *oldstate, 1494static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1473,6 +1498,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1473 ui->cshow = false; 1498 ui->cshow = false;
1474} 1499}
1475 1500
1501static const char *current_key_label(const game_ui *ui,
1502 const game_state *state, int button)
1503{
1504 if (IS_CURSOR_SELECT(button) && ui->cshow) {
1505 unsigned int f = state->flags[ui->cy * state->w + ui->cx];
1506 if (f & F_BLACK) return "Restore";
1507 if (f & F_CIRCLE) return "Remove";
1508 return button == CURSOR_SELECT ? "Black" : "Circle";
1509 }
1510 return "";
1511}
1512
1476#define DS_BLACK 0x1 1513#define DS_BLACK 0x1
1477#define DS_CIRCLE 0x2 1514#define DS_CIRCLE 0x2
1478#define DS_CURSOR 0x4 1515#define DS_CURSOR 0x4
@@ -1497,11 +1534,10 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1497 int i, x = FROMCOORD(mx), y = FROMCOORD(my); 1534 int i, x = FROMCOORD(mx), y = FROMCOORD(my);
1498 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE; 1535 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE;
1499 1536
1500 if (IS_CURSOR_MOVE(button)) { 1537 if (IS_CURSOR_MOVE(button))
1501 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, true); 1538 return move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, true,
1502 ui->cshow = true; 1539 &ui->cshow);
1503 action = UI; 1540 else if (IS_CURSOR_SELECT(button)) {
1504 } else if (IS_CURSOR_SELECT(button)) {
1505 x = ui->cx; y = ui->cy; 1541 x = ui->cx; y = ui->cy;
1506 if (!ui->cshow) { 1542 if (!ui->cshow) {
1507 action = UI; 1543 action = UI;
@@ -1519,14 +1555,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1519 } 1555 }
1520 if (!INGRID(state, x, y)) { 1556 if (!INGRID(state, x, y)) {
1521 ui->show_black_nums = !ui->show_black_nums; 1557 ui->show_black_nums = !ui->show_black_nums;
1522 action = UI; /* this wants to be a per-game option. */ 1558 action = UI;
1523 } else if (button == LEFT_BUTTON) { 1559 } else if (button == LEFT_BUTTON) {
1524 action = TOGGLE_BLACK; 1560 action = TOGGLE_BLACK;
1525 } else if (button == RIGHT_BUTTON) { 1561 } else if (button == RIGHT_BUTTON) {
1526 action = TOGGLE_CIRCLE; 1562 action = TOGGLE_CIRCLE;
1527 } 1563 }
1528 } 1564 }
1529 if (action == UI) return UI_UPDATE; 1565 if (action == UI) return MOVE_UI_UPDATE;
1530 1566
1531 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) { 1567 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) {
1532 i = y * state->w + x; 1568 i = y * state->w + x;
@@ -1587,7 +1623,7 @@ badmove:
1587 */ 1623 */
1588 1624
1589static void game_compute_size(const game_params *params, int tilesize, 1625static void game_compute_size(const game_params *params, int tilesize,
1590 int *x, int *y) 1626 const game_ui *ui, int *x, int *y)
1591{ 1627{
1592 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1628 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1593 struct { int tilesize; } ads, *ds = &ads; 1629 struct { int tilesize; } ads, *ds = &ads;
@@ -1608,12 +1644,13 @@ static float *game_colours(frontend *fe, int *ncolours)
1608 float *ret = snewn(3 * NCOLOURS, float); 1644 float *ret = snewn(3 * NCOLOURS, float);
1609 int i; 1645 int i;
1610 1646
1611 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 1647 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT);
1612 for (i = 0; i < 3; i++) { 1648 for (i = 0; i < 3; i++) {
1613 ret[COL_BLACK * 3 + i] = 0.0F; 1649 ret[COL_BLACK * 3 + i] = 0.0F;
1614 ret[COL_BLACKNUM * 3 + i] = 0.4F; 1650 ret[COL_BLACKNUM * 3 + i] = 0.4F;
1615 ret[COL_WHITE * 3 + i] = 1.0F; 1651 ret[COL_WHITE * 3 + i] = 1.0F;
1616 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i]; 1652 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
1653 ret[COL_UNUSED1 * 3 + i] = 0.0F; /* To placate an assertion. */
1617 } 1654 }
1618 ret[COL_CURSOR * 3 + 0] = 0.2F; 1655 ret[COL_CURSOR * 3 + 0] = 0.2F;
1619 ret[COL_CURSOR * 3 + 1] = 0.8F; 1656 ret[COL_CURSOR * 3 + 1] = 0.8F;
@@ -1708,7 +1745,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1708 if (!ds->started) { 1745 if (!ds->started) {
1709 int wsz = TILE_SIZE * state->w + 2 * BORDER; 1746 int wsz = TILE_SIZE * state->w + 2 * BORDER;
1710 int hsz = TILE_SIZE * state->h + 2 * BORDER; 1747 int hsz = TILE_SIZE * state->h + 2 * BORDER;
1711 draw_rect(dr, 0, 0, wsz, hsz, COL_BACKGROUND);
1712 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, 1748 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
1713 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2, 1749 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2,
1714 COL_GRID); 1750 COL_GRID);
@@ -1776,22 +1812,19 @@ static int game_status(const game_state *state)
1776 return state->completed ? +1 : 0; 1812 return state->completed ? +1 : 0;
1777} 1813}
1778 1814
1779static bool game_timing_state(const game_state *state, game_ui *ui) 1815static void game_print_size(const game_params *params, const game_ui *ui,
1780{ 1816 float *x, float *y)
1781 return true;
1782}
1783
1784static void game_print_size(const game_params *params, float *x, float *y)
1785{ 1817{
1786 int pw, ph; 1818 int pw, ph;
1787 1819
1788 /* 8mm squares by default. */ 1820 /* 8mm squares by default. */
1789 game_compute_size(params, 800, &pw, &ph); 1821 game_compute_size(params, 800, ui, &pw, &ph);
1790 *x = pw / 100.0F; 1822 *x = pw / 100.0F;
1791 *y = ph / 100.0F; 1823 *y = ph / 100.0F;
1792} 1824}
1793 1825
1794static void game_print(drawing *dr, const game_state *state, int tilesize) 1826static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1827 int tilesize)
1795{ 1828{
1796 int ink = print_mono_colour(dr, 0); 1829 int ink = print_mono_colour(dr, 0);
1797 int paper = print_mono_colour(dr, 1); 1830 int paper = print_mono_colour(dr, 1);
@@ -1848,12 +1881,14 @@ const struct game thegame = {
1848 free_game, 1881 free_game,
1849 true, solve_game, 1882 true, solve_game,
1850 true, game_can_format_as_text_now, game_text_format, 1883 true, game_can_format_as_text_now, game_text_format,
1884 get_prefs, set_prefs,
1851 new_ui, 1885 new_ui,
1852 free_ui, 1886 free_ui,
1853 encode_ui, 1887 NULL, /* encode_ui */
1854 decode_ui, 1888 NULL, /* decode_ui */
1855 NULL, /* game_request_keys */ 1889 NULL, /* game_request_keys */
1856 game_changed_state, 1890 game_changed_state,
1891 current_key_label,
1857 interpret_move, 1892 interpret_move,
1858 execute_move, 1893 execute_move,
1859 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1894 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1867,7 +1902,7 @@ const struct game thegame = {
1867 game_status, 1902 game_status,
1868 true, false, game_print_size, game_print, 1903 true, false, game_print_size, game_print,
1869 false, /* wants_statusbar */ 1904 false, /* wants_statusbar */
1870 false, game_timing_state, 1905 false, NULL, /* timing_state */
1871 REQUIRE_RBUTTON, /* flags */ 1906 REQUIRE_RBUTTON, /* flags */
1872}; 1907};
1873 1908
diff --git a/apps/plugins/puzzles/src/sixteen.R b/apps/plugins/puzzles/src/sixteen.R
deleted file mode 100644
index c63a27cef8..0000000000
--- a/apps/plugins/puzzles/src/sixteen.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3sixteen : [X] GTK COMMON sixteen sixteen-icon|no-icon
4
5sixteen : [G] WINDOWS COMMON sixteen sixteen.res|noicon.res
6
7ALL += sixteen[COMBINED]
8
9!begin am gtk
10GAMES += sixteen
11!end
12
13!begin >list.c
14 A(sixteen) \
15!end
16
17!begin >gamedesc.txt
18sixteen:sixteen.exe:Sixteen:Toroidal sliding block puzzle:Slide a row at a time to arrange the tiles into order.
19!end
diff --git a/apps/plugins/puzzles/src/sixteen.c b/apps/plugins/puzzles/src/sixteen.c
index 0b02038c43..38f6711a49 100644
--- a/apps/plugins/puzzles/src/sixteen.c
+++ b/apps/plugins/puzzles/src/sixteen.c
@@ -9,7 +9,12 @@
9#include <string.h> 9#include <string.h>
10#include <assert.h> 10#include <assert.h>
11#include <ctype.h> 11#include <ctype.h>
12#include <math.h> 12#include <limits.h>
13#ifdef NO_TGMATH_H
14# include <math.h>
15#else
16# include <tgmath.h>
17#endif
13 18
14#include "puzzles.h" 19#include "puzzles.h"
15 20
@@ -173,6 +178,8 @@ static const char *validate_params(const game_params *params, bool full)
173{ 178{
174 if (params->w < 2 || params->h < 2) 179 if (params->w < 2 || params->h < 2)
175 return "Width and height must both be at least two"; 180 return "Width and height must both be at least two";
181 if (params->w > INT_MAX / params->h)
182 return "Width times height must not be unreasonably large";
176 if (params->movetarget < 0) 183 if (params->movetarget < 0)
177 return "Number of shuffling moves may not be negative"; 184 return "Number of shuffling moves may not be negative";
178 return NULL; 185 return NULL;
@@ -567,7 +574,7 @@ static game_ui *new_ui(const game_state *state)
567 game_ui *ui = snew(game_ui); 574 game_ui *ui = snew(game_ui);
568 ui->cur_x = 0; 575 ui->cur_x = 0;
569 ui->cur_y = 0; 576 ui->cur_y = 0;
570 ui->cur_visible = false; 577 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
571 ui->cur_mode = unlocked; 578 ui->cur_mode = unlocked;
572 579
573 return ui; 580 return ui;
@@ -578,18 +585,24 @@ static void free_ui(game_ui *ui)
578 sfree(ui); 585 sfree(ui);
579} 586}
580 587
581static char *encode_ui(const game_ui *ui) 588static void game_changed_state(game_ui *ui, const game_state *oldstate,
582{ 589 const game_state *newstate)
583 return NULL;
584}
585
586static void decode_ui(game_ui *ui, const char *encoding)
587{ 590{
588} 591}
589 592
590static void game_changed_state(game_ui *ui, const game_state *oldstate, 593static const char *current_key_label(const game_ui *ui,
591 const game_state *newstate) 594 const game_state *state, int button)
592{ 595{
596 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
597 if (ui->cur_x == -1 || ui->cur_x == state->w ||
598 ui->cur_y == -1 || ui->cur_y == state->h)
599 return button == CURSOR_SELECT2 ? "Back" : "Slide";
600 if (button == CURSOR_SELECT)
601 return ui->cur_mode == lock_tile ? "Unlock" : "Lock tile";
602 if (button == CURSOR_SELECT2)
603 return ui->cur_mode == lock_position ? "Unlock" : "Lock pos";
604 }
605 return "";
593} 606}
594 607
595struct game_drawstate { 608struct game_drawstate {
@@ -609,12 +622,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
609 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 622 bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
610 int pad = button & MOD_NUM_KEYPAD; 623 int pad = button & MOD_NUM_KEYPAD;
611 624
612 button &= ~MOD_MASK; 625 button = STRIP_BUTTON_MODIFIERS(button);
613 626
614 if (IS_CURSOR_MOVE(button) || pad) { 627 if (IS_CURSOR_MOVE(button) || pad) {
615 if (!ui->cur_visible) { 628 if (!ui->cur_visible) {
616 ui->cur_visible = true; 629 ui->cur_visible = true;
617 return UI_UPDATE; 630 return MOVE_UI_UPDATE;
618 } 631 }
619 632
620 if (control || shift || ui->cur_mode) { 633 if (control || shift || ui->cur_mode) {
@@ -622,9 +635,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
622 if (x < 0 || x >= state->w || y < 0 || y >= state->h) 635 if (x < 0 || x >= state->w || y < 0 || y >= state->h)
623 return NULL; 636 return NULL;
624 move_cursor(button | pad, &x, &y, 637 move_cursor(button | pad, &x, &y,
625 state->w, state->h, false); 638 state->w, state->h, false, NULL);
626 move_cursor(button | pad, &xwrap, &ywrap, 639 move_cursor(button | pad, &xwrap, &ywrap,
627 state->w, state->h, true); 640 state->w, state->h, true, NULL);
628 641
629 if (x != xwrap) { 642 if (x != xwrap) {
630 sprintf(buf, "R%d,%c1", y, x ? '+' : '-'); 643 sprintf(buf, "R%d,%c1", y, x ? '+' : '-');
@@ -645,7 +658,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
645 int x = ui->cur_x + 1, y = ui->cur_y + 1; 658 int x = ui->cur_x + 1, y = ui->cur_y + 1;
646 659
647 move_cursor(button | pad, &x, &y, 660 move_cursor(button | pad, &x, &y,
648 state->w + 2, state->h + 2, false); 661 state->w + 2, state->h + 2, false, NULL);
649 662
650 if (x == 0 && y == 0) { 663 if (x == 0 && y == 0) {
651 int t = ui->cur_x; 664 int t = ui->cur_x;
@@ -669,7 +682,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
669 } 682 }
670 683
671 ui->cur_visible = true; 684 ui->cur_visible = true;
672 return UI_UPDATE; 685 return MOVE_UI_UPDATE;
673 } 686 }
674 } 687 }
675 688
@@ -687,11 +700,11 @@ static char *interpret_move(const game_state *state, game_ui *ui,
687 const enum cursor_mode m = (button == CURSOR_SELECT2 ? 700 const enum cursor_mode m = (button == CURSOR_SELECT2 ?
688 lock_position : lock_tile); 701 lock_position : lock_tile);
689 ui->cur_mode = (ui->cur_mode == m ? unlocked : m); 702 ui->cur_mode = (ui->cur_mode == m ? unlocked : m);
690 return UI_UPDATE; 703 return MOVE_UI_UPDATE;
691 } 704 }
692 } else { 705 } else {
693 ui->cur_visible = true; 706 ui->cur_visible = true;
694 return UI_UPDATE; 707 return MOVE_UI_UPDATE;
695 } 708 }
696 } else { 709 } else {
697 return NULL; 710 return NULL;
@@ -706,7 +719,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
706 else if (cy == state->h && cx >= 0 && cx < state->w) 719 else if (cy == state->h && cx >= 0 && cx < state->w)
707 dy = +1, dx = 0; 720 dy = +1, dx = 0;
708 else 721 else
709 return UI_UPDATE; /* invalid click location */ 722 return MOVE_UI_UPDATE; /* invalid click location */
710 723
711 /* reverse direction if right hand button is pressed */ 724 /* reverse direction if right hand button is pressed */
712 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) { 725 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) {
@@ -748,11 +761,11 @@ static game_state *execute_move(const game_state *from, const char *move)
748 } 761 }
749 762
750 if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 && 763 if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 &&
751 cy >= 0 && cy < from->h) { 764 cy >= 0 && cy < from->h && -from->h <= dx && dx <= from->w ) {
752 cx = dy = 0; 765 cx = dy = 0;
753 n = from->w; 766 n = from->w;
754 } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 && 767 } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 &&
755 cx >= 0 && cx < from->w) { 768 cx >= 0 && cx < from->w && -from->h <= dy && dy <= from->h) {
756 cy = dx = 0; 769 cy = dx = 0;
757 n = from->h; 770 n = from->h;
758 } else 771 } else
@@ -790,7 +803,7 @@ static game_state *execute_move(const game_state *from, const char *move)
790 */ 803 */
791 804
792static void game_compute_size(const game_params *params, int tilesize, 805static void game_compute_size(const game_params *params, int tilesize,
793 int *x, int *y) 806 const game_ui *ui, int *x, int *y)
794{ 807{
795 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 808 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
796 struct { int tilesize; } ads, *ds = &ads; 809 struct { int tilesize; } ads, *ds = &ads;
@@ -937,13 +950,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
937 if (!ds->started) { 950 if (!ds->started) {
938 int coords[10]; 951 int coords[10];
939 952
940 draw_rect(dr, 0, 0,
941 TILE_SIZE * state->w + 2 * BORDER,
942 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
943 draw_update(dr, 0, 0,
944 TILE_SIZE * state->w + 2 * BORDER,
945 TILE_SIZE * state->h + 2 * BORDER);
946
947 /* 953 /*
948 * Recessed area containing the whole puzzle. 954 * Recessed area containing the whole puzzle.
949 */ 955 */
@@ -1165,19 +1171,6 @@ static int game_status(const game_state *state)
1165 return state->completed ? +1 : 0; 1171 return state->completed ? +1 : 0;
1166} 1172}
1167 1173
1168static bool game_timing_state(const game_state *state, game_ui *ui)
1169{
1170 return true;
1171}
1172
1173static void game_print_size(const game_params *params, float *x, float *y)
1174{
1175}
1176
1177static void game_print(drawing *dr, const game_state *state, int tilesize)
1178{
1179}
1180
1181#ifdef COMBINED 1174#ifdef COMBINED
1182#define thegame sixteen 1175#define thegame sixteen
1183#endif 1176#endif
@@ -1199,12 +1192,14 @@ const struct game thegame = {
1199 free_game, 1192 free_game,
1200 true, solve_game, 1193 true, solve_game,
1201 true, game_can_format_as_text_now, game_text_format, 1194 true, game_can_format_as_text_now, game_text_format,
1195 NULL, NULL, /* get_prefs, set_prefs */
1202 new_ui, 1196 new_ui,
1203 free_ui, 1197 free_ui,
1204 encode_ui, 1198 NULL, /* encode_ui */
1205 decode_ui, 1199 NULL, /* decode_ui */
1206 NULL, /* game_request_keys */ 1200 NULL, /* game_request_keys */
1207 game_changed_state, 1201 game_changed_state,
1202 current_key_label,
1208 interpret_move, 1203 interpret_move,
1209 execute_move, 1204 execute_move,
1210 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1205 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1216,9 +1211,9 @@ const struct game thegame = {
1216 game_flash_length, 1211 game_flash_length,
1217 game_get_cursor_location, 1212 game_get_cursor_location,
1218 game_status, 1213 game_status,
1219 false, false, game_print_size, game_print, 1214 false, false, NULL, NULL, /* print_size, print */
1220 true, /* wants_statusbar */ 1215 true, /* wants_statusbar */
1221 false, game_timing_state, 1216 false, NULL, /* timing_state */
1222 0, /* flags */ 1217 0, /* flags */
1223}; 1218};
1224 1219
diff --git a/apps/plugins/puzzles/src/slant.R b/apps/plugins/puzzles/src/slant.R
deleted file mode 100644
index ff0d21f1eb..0000000000
--- a/apps/plugins/puzzles/src/slant.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3SLANT_EXTRA = dsf findloop
4
5slant : [X] GTK COMMON slant SLANT_EXTRA slant-icon|no-icon
6
7slant : [G] WINDOWS COMMON slant SLANT_EXTRA slant.res|noicon.res
8
9slantsolver : [U] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
10slantsolver : [C] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
11
12ALL += slant[COMBINED] SLANT_EXTRA
13
14!begin am gtk
15GAMES += slant
16!end
17
18!begin >list.c
19 A(slant) \
20!end
21
22!begin >gamedesc.txt
23slant:slant.exe:Slant:Maze-drawing puzzle:Draw a maze of slanting lines that matches the clues.
24!end
diff --git a/apps/plugins/puzzles/src/slant.c b/apps/plugins/puzzles/src/slant.c
index ed290fe57d..bd04b786e6 100644
--- a/apps/plugins/puzzles/src/slant.c
+++ b/apps/plugins/puzzles/src/slant.c
@@ -28,7 +28,11 @@
28#include <string.h> 28#include <string.h>
29#include <assert.h> 29#include <assert.h>
30#include <ctype.h> 30#include <ctype.h>
31#include <math.h> 31#ifdef NO_TGMATH_H
32# include <math.h>
33#else
34# include <tgmath.h>
35#endif
32 36
33#include "puzzles.h" 37#include "puzzles.h"
34 38
@@ -51,7 +55,7 @@ enum {
51 */ 55 */
52#if defined STANDALONE_SOLVER 56#if defined STANDALONE_SOLVER
53#define SOLVER_DIAGNOSTICS 57#define SOLVER_DIAGNOSTICS
54bool verbose = false; 58static bool verbose = false;
55#elif defined SOLVER_DIAGNOSTICS 59#elif defined SOLVER_DIAGNOSTICS
56#define verbose true 60#define verbose true
57#endif 61#endif
@@ -226,6 +230,8 @@ static const char *validate_params(const game_params *params, bool full)
226 230
227 if (params->w < 2 || params->h < 2) 231 if (params->w < 2 || params->h < 2)
228 return "Width and height must both be at least two"; 232 return "Width and height must both be at least two";
233 if (params->w > INT_MAX / params->h)
234 return "Width times height must not be unreasonably large";
229 235
230 return NULL; 236 return NULL;
231} 237}
@@ -238,7 +244,7 @@ struct solver_scratch {
238 * Disjoint set forest which tracks the connected sets of 244 * Disjoint set forest which tracks the connected sets of
239 * points. 245 * points.
240 */ 246 */
241 int *connected; 247 DSF *connected;
242 248
243 /* 249 /*
244 * Counts the number of possible exits from each connected set 250 * Counts the number of possible exits from each connected set
@@ -259,7 +265,7 @@ struct solver_scratch {
259 * Another disjoint set forest. This one tracks _squares_ which 265 * Another disjoint set forest. This one tracks _squares_ which
260 * are known to slant in the same direction. 266 * are known to slant in the same direction.
261 */ 267 */
262 int *equiv; 268 DSF *equiv;
263 269
264 /* 270 /*
265 * Stores slash values which we know for an equivalence class. 271 * Stores slash values which we know for an equivalence class.
@@ -306,10 +312,10 @@ static struct solver_scratch *new_scratch(int w, int h)
306{ 312{
307 int W = w+1, H = h+1; 313 int W = w+1, H = h+1;
308 struct solver_scratch *ret = snew(struct solver_scratch); 314 struct solver_scratch *ret = snew(struct solver_scratch);
309 ret->connected = snewn(W*H, int); 315 ret->connected = dsf_new(W*H);
310 ret->exits = snewn(W*H, int); 316 ret->exits = snewn(W*H, int);
311 ret->border = snewn(W*H, bool); 317 ret->border = snewn(W*H, bool);
312 ret->equiv = snewn(w*h, int); 318 ret->equiv = dsf_new(w*h);
313 ret->slashval = snewn(w*h, signed char); 319 ret->slashval = snewn(w*h, signed char);
314 ret->vbitmap = snewn(w*h, unsigned char); 320 ret->vbitmap = snewn(w*h, unsigned char);
315 return ret; 321 return ret;
@@ -319,10 +325,10 @@ static void free_scratch(struct solver_scratch *sc)
319{ 325{
320 sfree(sc->vbitmap); 326 sfree(sc->vbitmap);
321 sfree(sc->slashval); 327 sfree(sc->slashval);
322 sfree(sc->equiv); 328 dsf_free(sc->equiv);
323 sfree(sc->border); 329 sfree(sc->border);
324 sfree(sc->exits); 330 sfree(sc->exits);
325 sfree(sc->connected); 331 dsf_free(sc->connected);
326 sfree(sc); 332 sfree(sc);
327} 333}
328 334
@@ -330,7 +336,7 @@ static void free_scratch(struct solver_scratch *sc)
330 * Wrapper on dsf_merge() which updates the `exits' and `border' 336 * Wrapper on dsf_merge() which updates the `exits' and `border'
331 * arrays. 337 * arrays.
332 */ 338 */
333static void merge_vertices(int *connected, 339static void merge_vertices(DSF *connected,
334 struct solver_scratch *sc, int i, int j) 340 struct solver_scratch *sc, int i, int j)
335{ 341{
336 int exits = -1; 342 int exits = -1;
@@ -376,7 +382,7 @@ static void decr_exits(struct solver_scratch *sc, int i)
376 382
377static void fill_square(int w, int h, int x, int y, int v, 383static void fill_square(int w, int h, int x, int y, int v,
378 signed char *soln, 384 signed char *soln,
379 int *connected, struct solver_scratch *sc) 385 DSF *connected, struct solver_scratch *sc)
380{ 386{
381 int W = w+1 /*, H = h+1 */; 387 int W = w+1 /*, H = h+1 */;
382 388
@@ -466,13 +472,13 @@ static int slant_solve(int w, int h, const signed char *clues,
466 * Establish a disjoint set forest for tracking connectedness 472 * Establish a disjoint set forest for tracking connectedness
467 * between grid points. 473 * between grid points.
468 */ 474 */
469 dsf_init(sc->connected, W*H); 475 dsf_reinit(sc->connected);
470 476
471 /* 477 /*
472 * Establish a disjoint set forest for tracking which squares 478 * Establish a disjoint set forest for tracking which squares
473 * are known to slant in the same direction. 479 * are known to slant in the same direction.
474 */ 480 */
475 dsf_init(sc->equiv, w*h); 481 dsf_reinit(sc->equiv);
476 482
477 /* 483 /*
478 * Clear the slashval array. 484 * Clear the slashval array.
@@ -991,7 +997,8 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
991{ 997{
992 int W = w+1, H = h+1; 998 int W = w+1, H = h+1;
993 int x, y, i; 999 int x, y, i;
994 int *connected, *indices; 1000 DSF *connected;
1001 int *indices;
995 1002
996 /* 1003 /*
997 * Clear the output. 1004 * Clear the output.
@@ -1002,7 +1009,7 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
1002 * Establish a disjoint set forest for tracking connectedness 1009 * Establish a disjoint set forest for tracking connectedness
1003 * between grid points. 1010 * between grid points.
1004 */ 1011 */
1005 connected = snew_dsf(W*H); 1012 connected = dsf_new(W*H);
1006 1013
1007 /* 1014 /*
1008 * Prepare a list of the squares in the grid, and fill them in 1015 * Prepare a list of the squares in the grid, and fill them in
@@ -1058,7 +1065,7 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
1058 } 1065 }
1059 1066
1060 sfree(indices); 1067 sfree(indices);
1061 sfree(connected); 1068 dsf_free(connected);
1062} 1069}
1063 1070
1064static char *new_game_desc(const game_params *params, random_state *rs, 1071static char *new_game_desc(const game_params *params, random_state *rs,
@@ -1574,28 +1581,69 @@ static char *game_text_format(const game_state *state)
1574struct game_ui { 1581struct game_ui {
1575 int cur_x, cur_y; 1582 int cur_x, cur_y;
1576 bool cur_visible; 1583 bool cur_visible;
1584
1585 /*
1586 * User preference option to swap the left and right mouse
1587 * buttons. There isn't a completely obvious mapping of left and
1588 * right buttons to the two directions of slash, and at least one
1589 * player turned out not to have the same intuition as me.
1590 */
1591 bool swap_buttons;
1577}; 1592};
1578 1593
1594static void legacy_prefs_override(struct game_ui *ui_out)
1595{
1596 static bool initialised = false;
1597 static int swap_buttons = -1;
1598
1599 if (!initialised) {
1600 initialised = true;
1601 swap_buttons = getenv_bool("SLANT_SWAP_BUTTONS", -1);
1602 }
1603
1604 if (swap_buttons != -1)
1605 ui_out->swap_buttons = swap_buttons;
1606}
1607
1579static game_ui *new_ui(const game_state *state) 1608static game_ui *new_ui(const game_state *state)
1580{ 1609{
1581 game_ui *ui = snew(game_ui); 1610 game_ui *ui = snew(game_ui);
1582 ui->cur_x = ui->cur_y = 0; 1611 ui->cur_x = ui->cur_y = 0;
1583 ui->cur_visible = false; 1612 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1613
1614 ui->swap_buttons = false;
1615 legacy_prefs_override(ui);
1616
1584 return ui; 1617 return ui;
1585} 1618}
1586 1619
1587static void free_ui(game_ui *ui) 1620static config_item *get_prefs(game_ui *ui)
1588{ 1621{
1589 sfree(ui); 1622 config_item *ret;
1623
1624 ret = snewn(2, config_item);
1625
1626 ret[0].name = "Mouse button order";
1627 ret[0].kw = "left-button";
1628 ret[0].type = C_CHOICES;
1629 ret[0].u.choices.choicenames = ":Left \\, right /:Left /, right \\";
1630 ret[0].u.choices.choicekws = ":\\:/";
1631 ret[0].u.choices.selected = ui->swap_buttons;
1632
1633 ret[1].name = NULL;
1634 ret[1].type = C_END;
1635
1636 return ret;
1590} 1637}
1591 1638
1592static char *encode_ui(const game_ui *ui) 1639static void set_prefs(game_ui *ui, const config_item *cfg)
1593{ 1640{
1594 return NULL; 1641 ui->swap_buttons = cfg[0].u.choices.selected;
1595} 1642}
1596 1643
1597static void decode_ui(game_ui *ui, const char *encoding) 1644static void free_ui(game_ui *ui)
1598{ 1645{
1646 sfree(ui);
1599} 1647}
1600 1648
1601static void game_changed_state(game_ui *ui, const game_state *oldstate, 1649static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1603,6 +1651,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1603{ 1651{
1604} 1652}
1605 1653
1654static const char *current_key_label(const game_ui *ui,
1655 const game_state *state, int button)
1656{
1657 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
1658 switch (state->soln[ui->cur_y*state->p.w+ui->cur_x]) {
1659 case 0:
1660 return button == CURSOR_SELECT ? "\\" : "/";
1661 case -1:
1662 return button == CURSOR_SELECT ? "/" : "Blank";
1663 case +1:
1664 return button == CURSOR_SELECT ? "Blank" : "\\";
1665 }
1666 }
1667 return "";
1668}
1669
1606#define PREFERRED_TILESIZE 32 1670#define PREFERRED_TILESIZE 32
1607#define TILESIZE (ds->tilesize) 1671#define TILESIZE (ds->tilesize)
1608#define BORDER TILESIZE 1672#define BORDER TILESIZE
@@ -1638,7 +1702,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1638 1702
1639struct game_drawstate { 1703struct game_drawstate {
1640 int tilesize; 1704 int tilesize;
1641 bool started;
1642 long *grid; 1705 long *grid;
1643 long *todraw; 1706 long *todraw;
1644}; 1707};
@@ -1653,51 +1716,34 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1653 enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE; 1716 enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE;
1654 1717
1655 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1718 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1656 /* 1719 if (ui->swap_buttons) {
1657 * This is an utterly awful hack which I should really sort out 1720 if (button == LEFT_BUTTON)
1658 * by means of a proper configuration mechanism. One Slant 1721 button = RIGHT_BUTTON;
1659 * player has observed that they prefer the mouse buttons to 1722 else
1660 * function exactly the opposite way round, so here's a 1723 button = LEFT_BUTTON;
1661 * mechanism for environment-based configuration. I cache the 1724 }
1662 * result in a global variable - yuck! - to avoid repeated
1663 * lookups.
1664 */
1665 {
1666 static int swap_buttons = -1;
1667 if (swap_buttons < 0) {
1668 char *env = getenv("SLANT_SWAP_BUTTONS");
1669 swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
1670 }
1671 if (swap_buttons) {
1672 if (button == LEFT_BUTTON)
1673 button = RIGHT_BUTTON;
1674 else
1675 button = LEFT_BUTTON;
1676 }
1677 }
1678 action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE; 1725 action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE;
1679 1726
1680 x = FROMCOORD(x); 1727 x = FROMCOORD(x);
1681 y = FROMCOORD(y); 1728 y = FROMCOORD(y);
1682 if (x < 0 || y < 0 || x >= w || y >= h) 1729 if (x < 0 || y < 0 || x >= w || y >= h)
1683 return NULL; 1730 return MOVE_UNUSED;
1684 ui->cur_visible = false; 1731 ui->cur_visible = false;
1685 } else if (IS_CURSOR_SELECT(button)) { 1732 } else if (IS_CURSOR_SELECT(button)) {
1686 if (!ui->cur_visible) { 1733 if (!ui->cur_visible) {
1687 ui->cur_visible = true; 1734 ui->cur_visible = true;
1688 return UI_UPDATE; 1735 return MOVE_UI_UPDATE;
1689 } 1736 }
1690 x = ui->cur_x; 1737 x = ui->cur_x;
1691 y = ui->cur_y; 1738 y = ui->cur_y;
1692 1739
1693 action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE; 1740 action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE;
1694 } else if (IS_CURSOR_MOVE(button)) { 1741 } else if (IS_CURSOR_MOVE(button)) {
1695 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false); 1742 return move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false, &ui->cur_visible);
1696 ui->cur_visible = true;
1697 return UI_UPDATE;
1698 } else if (button == '\\' || button == '\b' || button == '/') { 1743 } else if (button == '\\' || button == '\b' || button == '/') {
1699 int x = ui->cur_x, y = ui->cur_y; 1744 int x = ui->cur_x, y = ui->cur_y;
1700 if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1]) return NULL; 1745 if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1])
1746 return MOVE_NO_EFFECT;
1701 sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y); 1747 sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y);
1702 return dupstr(buf); 1748 return dupstr(buf);
1703 } 1749 }
@@ -1723,7 +1769,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1723 return dupstr(buf); 1769 return dupstr(buf);
1724 } 1770 }
1725 1771
1726 return NULL; 1772 return MOVE_UNUSED;
1727} 1773}
1728 1774
1729static game_state *execute_move(const game_state *state, const char *move) 1775static game_state *execute_move(const game_state *state, const char *move)
@@ -1774,7 +1820,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1774 */ 1820 */
1775 1821
1776static void game_compute_size(const game_params *params, int tilesize, 1822static void game_compute_size(const game_params *params, int tilesize,
1777 int *x, int *y) 1823 const game_ui *ui, int *x, int *y)
1778{ 1824{
1779 /* fool the macros */ 1825 /* fool the macros */
1780 struct dummy { int tilesize; } dummy, *ds = &dummy; 1826 struct dummy { int tilesize; } dummy, *ds = &dummy;
@@ -1832,7 +1878,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1832 struct game_drawstate *ds = snew(struct game_drawstate); 1878 struct game_drawstate *ds = snew(struct game_drawstate);
1833 1879
1834 ds->tilesize = 0; 1880 ds->tilesize = 0;
1835 ds->started = false;
1836 ds->grid = snewn((w+2)*(h+2), long); 1881 ds->grid = snewn((w+2)*(h+2), long);
1837 ds->todraw = snewn((w+2)*(h+2), long); 1882 ds->todraw = snewn((w+2)*(h+2), long);
1838 for (i = 0; i < (w+2)*(h+2); i++) 1883 for (i = 0; i < (w+2)*(h+2); i++)
@@ -1972,14 +2017,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1972 else 2017 else
1973 flashing = false; 2018 flashing = false;
1974 2019
1975 if (!ds->started) {
1976 int ww, wh;
1977 game_compute_size(&state->p, TILESIZE, &ww, &wh);
1978 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
1979 draw_update(dr, 0, 0, ww, wh);
1980 ds->started = true;
1981 }
1982
1983 /* 2020 /*
1984 * Loop over the grid and work out where all the slashes are. 2021 * Loop over the grid and work out where all the slashes are.
1985 * We need to do this because a slash in one square affects the 2022 * We need to do this because a slash in one square affects the
@@ -2082,24 +2119,21 @@ static int game_status(const game_state *state)
2082 return state->completed ? +1 : 0; 2119 return state->completed ? +1 : 0;
2083} 2120}
2084 2121
2085static bool game_timing_state(const game_state *state, game_ui *ui) 2122static void game_print_size(const game_params *params, const game_ui *ui,
2086{ 2123 float *x, float *y)
2087 return true;
2088}
2089
2090static void game_print_size(const game_params *params, float *x, float *y)
2091{ 2124{
2092 int pw, ph; 2125 int pw, ph;
2093 2126
2094 /* 2127 /*
2095 * I'll use 6mm squares by default. 2128 * I'll use 6mm squares by default.
2096 */ 2129 */
2097 game_compute_size(params, 600, &pw, &ph); 2130 game_compute_size(params, 600, ui, &pw, &ph);
2098 *x = pw / 100.0F; 2131 *x = pw / 100.0F;
2099 *y = ph / 100.0F; 2132 *y = ph / 100.0F;
2100} 2133}
2101 2134
2102static void game_print(drawing *dr, const game_state *state, int tilesize) 2135static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2136 int tilesize)
2103{ 2137{
2104 int w = state->p.w, h = state->p.h, W = w+1; 2138 int w = state->p.w, h = state->p.h, W = w+1;
2105 int ink = print_mono_colour(dr, 0); 2139 int ink = print_mono_colour(dr, 0);
@@ -2179,12 +2213,14 @@ const struct game thegame = {
2179 free_game, 2213 free_game,
2180 true, solve_game, 2214 true, solve_game,
2181 true, game_can_format_as_text_now, game_text_format, 2215 true, game_can_format_as_text_now, game_text_format,
2216 get_prefs, set_prefs,
2182 new_ui, 2217 new_ui,
2183 free_ui, 2218 free_ui,
2184 encode_ui, 2219 NULL, /* encode_ui */
2185 decode_ui, 2220 NULL, /* decode_ui */
2186 NULL, /* game_request_keys */ 2221 NULL, /* game_request_keys */
2187 game_changed_state, 2222 game_changed_state,
2223 current_key_label,
2188 interpret_move, 2224 interpret_move,
2189 execute_move, 2225 execute_move,
2190 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2226 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2198,7 +2234,7 @@ const struct game thegame = {
2198 game_status, 2234 game_status,
2199 true, false, game_print_size, game_print, 2235 true, false, game_print_size, game_print,
2200 false, /* wants_statusbar */ 2236 false, /* wants_statusbar */
2201 false, game_timing_state, 2237 false, NULL, /* timing_state */
2202 0, /* flags */ 2238 0, /* flags */
2203}; 2239};
2204 2240
diff --git a/apps/plugins/puzzles/src/solo.R b/apps/plugins/puzzles/src/solo.R
deleted file mode 100644
index 081a76147e..0000000000
--- a/apps/plugins/puzzles/src/solo.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3SOLO_EXTRA = divvy dsf
4
5solo : [X] GTK COMMON solo SOLO_EXTRA solo-icon|no-icon
6
7solo : [G] WINDOWS COMMON solo SOLO_EXTRA solo.res|noicon.res
8
9solosolver : [U] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
10solosolver : [C] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
11
12ALL += solo[COMBINED] SOLO_EXTRA
13
14!begin am gtk
15GAMES += solo
16!end
17
18!begin >list.c
19 A(solo) \
20!end
21
22!begin >gamedesc.txt
23solo:solo.exe:Solo:Number placement puzzle:Fill in the grid so that each row, column and square block contains one of every digit.
24!end
diff --git a/apps/plugins/puzzles/src/solo.c b/apps/plugins/puzzles/src/solo.c
index 49753f41dc..2445501f57 100644
--- a/apps/plugins/puzzles/src/solo.c
+++ b/apps/plugins/puzzles/src/solo.c
@@ -20,7 +20,7 @@
20 * + while I'm revamping this area, filling in the _last_ 20 * + while I'm revamping this area, filling in the _last_
21 * number in a nearly-full row or column should certainly be 21 * number in a nearly-full row or column should certainly be
22 * permitted even at the lowest difficulty level. 22 * permitted even at the lowest difficulty level.
23 * + also Owen noticed that `Basic' grids requiring numeric 23 * + also Alex noticed that `Basic' grids requiring numeric
24 * elimination are actually very hard, so I wonder if a 24 * elimination are actually very hard, so I wonder if a
25 * difficulty gradation between that and positional- 25 * difficulty gradation between that and positional-
26 * elimination-only might be in order 26 * elimination-only might be in order
@@ -87,11 +87,15 @@
87#include <string.h> 87#include <string.h>
88#include <assert.h> 88#include <assert.h>
89#include <ctype.h> 89#include <ctype.h>
90#include <math.h> 90#ifdef NO_TGMATH_H
91# include <math.h>
92#else
93# include <tgmath.h>
94#endif
91 95
92#ifdef STANDALONE_SOLVER 96#ifdef STANDALONE_SOLVER
93#include <stdarg.h> 97#include <stdarg.h>
94int solver_show_working, solver_recurse_depth; 98static int solver_show_working, solver_recurse_depth;
95#endif 99#endif
96 100
97#include "puzzles.h" 101#include "puzzles.h"
@@ -2638,6 +2642,7 @@ static void solver(int cr, struct block_structure *blocks,
2638 sfree(usage->row); 2642 sfree(usage->row);
2639 sfree(usage->col); 2643 sfree(usage->col);
2640 sfree(usage->blk); 2644 sfree(usage->blk);
2645 sfree(usage->diag);
2641 if (usage->kblocks) { 2646 if (usage->kblocks) {
2642 free_block_structure(usage->kblocks); 2647 free_block_structure(usage->kblocks);
2643 free_block_structure(usage->extra_cages); 2648 free_block_structure(usage->extra_cages);
@@ -2969,6 +2974,7 @@ static bool gridgen(int cr, struct block_structure *blocks,
2969 sfree(usage->blk); 2974 sfree(usage->blk);
2970 sfree(usage->col); 2975 sfree(usage->col);
2971 sfree(usage->row); 2976 sfree(usage->row);
2977 sfree(usage->diag);
2972 sfree(usage); 2978 sfree(usage);
2973 2979
2974 return ret; 2980 return ret;
@@ -3222,7 +3228,7 @@ static char *encode_solve_move(int cr, digit *grid)
3222 return ret; 3228 return ret;
3223} 3229}
3224 3230
3225static void dsf_to_blocks(int *dsf, struct block_structure *blocks, 3231static void dsf_to_blocks(DSF *dsf, struct block_structure *blocks,
3226 int min_expected, int max_expected) 3232 int min_expected, int max_expected)
3227{ 3233{
3228 int cr = blocks->c * blocks->r, area = cr * cr; 3234 int cr = blocks->c * blocks->r, area = cr * cr;
@@ -3654,10 +3660,11 @@ static char *new_game_desc(const game_params *params, random_state *rs,
3654 * the puzzle size: all 2x2 puzzles appear to be Trivial 3660 * the puzzle size: all 2x2 puzzles appear to be Trivial
3655 * (DIFF_BLOCK) so we cannot hold out for even a Basic 3661 * (DIFF_BLOCK) so we cannot hold out for even a Basic
3656 * (DIFF_SIMPLE) one. 3662 * (DIFF_SIMPLE) one.
3663 * Jigsaw puzzles of size 2 and 3 are also all trivial.
3657 */ 3664 */
3658 dlev.maxdiff = params->diff; 3665 dlev.maxdiff = params->diff;
3659 dlev.maxkdiff = params->kdiff; 3666 dlev.maxkdiff = params->kdiff;
3660 if (c == 2 && r == 2) 3667 if ((c == 2 && r == 2) || (r == 1 && c < 4))
3661 dlev.maxdiff = DIFF_BLOCK; 3668 dlev.maxdiff = DIFF_BLOCK;
3662 3669
3663 grid = snewn(area, digit); 3670 grid = snewn(area, digit);
@@ -3684,11 +3691,11 @@ static char *new_game_desc(const game_params *params, random_state *rs,
3684 * constructing the block structure. 3691 * constructing the block structure.
3685 */ 3692 */
3686 if (r == 1) { /* jigsaw mode */ 3693 if (r == 1) { /* jigsaw mode */
3687 int *dsf = divvy_rectangle(cr, cr, cr, rs); 3694 DSF *dsf = divvy_rectangle(cr, cr, cr, rs);
3688 3695
3689 dsf_to_blocks (dsf, blocks, cr, cr); 3696 dsf_to_blocks (dsf, blocks, cr, cr);
3690 3697
3691 sfree(dsf); 3698 dsf_free(dsf);
3692 } else { /* basic Sudoku mode */ 3699 } else { /* basic Sudoku mode */
3693 for (y = 0; y < cr; y++) 3700 for (y = 0; y < cr; y++)
3694 for (x = 0; x < cr; x++) 3701 for (x = 0; x < cr; x++)
@@ -3903,14 +3910,14 @@ static const char *spec_to_grid(const char *desc, digit *grid, int area)
3903 * end of the block spec, and return an error string or NULL if everything 3910 * end of the block spec, and return an error string or NULL if everything
3904 * is OK. The DSF is stored in *PDSF. 3911 * is OK. The DSF is stored in *PDSF.
3905 */ 3912 */
3906static const char *spec_to_dsf(const char **pdesc, int **pdsf, 3913static const char *spec_to_dsf(const char **pdesc, DSF **pdsf,
3907 int cr, int area) 3914 int cr, int area)
3908{ 3915{
3909 const char *desc = *pdesc; 3916 const char *desc = *pdesc;
3910 int pos = 0; 3917 int pos = 0;
3911 int *dsf; 3918 DSF *dsf;
3912 3919
3913 *pdsf = dsf = snew_dsf(area); 3920 *pdsf = dsf = dsf_new(area);
3914 3921
3915 while (*desc && *desc != ',') { 3922 while (*desc && *desc != ',') {
3916 int c; 3923 int c;
@@ -3921,7 +3928,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf,
3921 else if (*desc >= 'a' && *desc <= 'z') 3928 else if (*desc >= 'a' && *desc <= 'z')
3922 c = *desc - 'a' + 1; 3929 c = *desc - 'a' + 1;
3923 else { 3930 else {
3924 sfree(dsf); 3931 dsf_free(dsf);
3925 return "Invalid character in game description"; 3932 return "Invalid character in game description";
3926 } 3933 }
3927 desc++; 3934 desc++;
@@ -3936,7 +3943,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf,
3936 * side of it. 3943 * side of it.
3937 */ 3944 */
3938 if (pos >= 2*cr*(cr-1)) { 3945 if (pos >= 2*cr*(cr-1)) {
3939 sfree(dsf); 3946 dsf_free(dsf);
3940 return "Too much data in block structure specification"; 3947 return "Too much data in block structure specification";
3941 } 3948 }
3942 3949
@@ -3966,7 +3973,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf,
3966 * edge at the end. 3973 * edge at the end.
3967 */ 3974 */
3968 if (pos != 2*cr*(cr-1)+1) { 3975 if (pos != 2*cr*(cr-1)+1) {
3969 sfree(dsf); 3976 dsf_free(dsf);
3970 return "Not enough data in block structure specification"; 3977 return "Not enough data in block structure specification";
3971 } 3978 }
3972 3979
@@ -4008,7 +4015,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area,
4008 int min_nr_squares, int max_nr_squares) 4015 int min_nr_squares, int max_nr_squares)
4009{ 4016{
4010 const char *err; 4017 const char *err;
4011 int *dsf; 4018 DSF *dsf;
4012 4019
4013 err = spec_to_dsf(pdesc, &dsf, cr, area); 4020 err = spec_to_dsf(pdesc, &dsf, cr, area);
4014 if (err) { 4021 if (err) {
@@ -4037,7 +4044,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area,
4037 if (canons[c] == j) { 4044 if (canons[c] == j) {
4038 counts[c]++; 4045 counts[c]++;
4039 if (counts[c] > max_nr_squares) { 4046 if (counts[c] > max_nr_squares) {
4040 sfree(dsf); 4047 dsf_free(dsf);
4041 sfree(canons); 4048 sfree(canons);
4042 sfree(counts); 4049 sfree(counts);
4043 return "A jigsaw block is too big"; 4050 return "A jigsaw block is too big";
@@ -4047,7 +4054,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area,
4047 4054
4048 if (c == ncanons) { 4055 if (c == ncanons) {
4049 if (ncanons >= max_nr_blocks) { 4056 if (ncanons >= max_nr_blocks) {
4050 sfree(dsf); 4057 dsf_free(dsf);
4051 sfree(canons); 4058 sfree(canons);
4052 sfree(counts); 4059 sfree(counts);
4053 return "Too many distinct jigsaw blocks"; 4060 return "Too many distinct jigsaw blocks";
@@ -4059,14 +4066,14 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area,
4059 } 4066 }
4060 4067
4061 if (ncanons < min_nr_blocks) { 4068 if (ncanons < min_nr_blocks) {
4062 sfree(dsf); 4069 dsf_free(dsf);
4063 sfree(canons); 4070 sfree(canons);
4064 sfree(counts); 4071 sfree(counts);
4065 return "Not enough distinct jigsaw blocks"; 4072 return "Not enough distinct jigsaw blocks";
4066 } 4073 }
4067 for (c = 0; c < ncanons; c++) { 4074 for (c = 0; c < ncanons; c++) {
4068 if (counts[c] < min_nr_squares) { 4075 if (counts[c] < min_nr_squares) {
4069 sfree(dsf); 4076 dsf_free(dsf);
4070 sfree(canons); 4077 sfree(canons);
4071 sfree(counts); 4078 sfree(counts);
4072 return "A jigsaw block is too small"; 4079 return "A jigsaw block is too small";
@@ -4076,7 +4083,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area,
4076 sfree(counts); 4083 sfree(counts);
4077 } 4084 }
4078 4085
4079 sfree(dsf); 4086 dsf_free(dsf);
4080 return NULL; 4087 return NULL;
4081} 4088}
4082 4089
@@ -4161,13 +4168,13 @@ static game_state *new_game(midend *me, const game_params *params,
4161 4168
4162 if (r == 1) { 4169 if (r == 1) {
4163 const char *err; 4170 const char *err;
4164 int *dsf; 4171 DSF *dsf;
4165 assert(*desc == ','); 4172 assert(*desc == ',');
4166 desc++; 4173 desc++;
4167 err = spec_to_dsf(&desc, &dsf, cr, area); 4174 err = spec_to_dsf(&desc, &dsf, cr, area);
4168 assert(err == NULL); 4175 assert(err == NULL);
4169 dsf_to_blocks(dsf, state->blocks, cr, cr); 4176 dsf_to_blocks(dsf, state->blocks, cr, cr);
4170 sfree(dsf); 4177 dsf_free(dsf);
4171 } else { 4178 } else {
4172 int x, y; 4179 int x, y;
4173 4180
@@ -4179,13 +4186,13 @@ static game_state *new_game(midend *me, const game_params *params,
4179 4186
4180 if (params->killer) { 4187 if (params->killer) {
4181 const char *err; 4188 const char *err;
4182 int *dsf; 4189 DSF *dsf;
4183 assert(*desc == ','); 4190 assert(*desc == ',');
4184 desc++; 4191 desc++;
4185 err = spec_to_dsf(&desc, &dsf, cr, area); 4192 err = spec_to_dsf(&desc, &dsf, cr, area);
4186 assert(err == NULL); 4193 assert(err == NULL);
4187 dsf_to_blocks(dsf, state->kblocks, cr, area); 4194 dsf_to_blocks(dsf, state->kblocks, cr, area);
4188 sfree(dsf); 4195 dsf_free(dsf);
4189 make_blocks_from_whichblock(state->kblocks); 4196 make_blocks_from_whichblock(state->kblocks);
4190 4197
4191 assert(*desc == ','); 4198 assert(*desc == ',');
@@ -4550,6 +4557,17 @@ struct game_ui {
4550 * allowed on immutable squares. 4557 * allowed on immutable squares.
4551 */ 4558 */
4552 bool hcursor; 4559 bool hcursor;
4560
4561 /*
4562 * User preference option: if the user right-clicks in a square
4563 * and presses a number or letter key to add/remove a pencil mark,
4564 * do we hide the mouse highlight again afterwards?
4565 *
4566 * Historically our answer was yes. The Android port prefers no.
4567 * There are advantages both ways, depending how much you dislike
4568 * the highlight cluttering your view. So it's a preference.
4569 */
4570 bool pencil_keep_highlight;
4553}; 4571};
4554 4572
4555static game_ui *new_ui(const game_state *state) 4573static game_ui *new_ui(const game_state *state)
@@ -4558,8 +4576,9 @@ static game_ui *new_ui(const game_state *state)
4558 4576
4559 ui->hx = ui->hy = 0; 4577 ui->hx = ui->hy = 0;
4560 ui->hpencil = false; 4578 ui->hpencil = false;
4561 ui->hshow = false; 4579 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
4562 ui->hcursor = false; 4580
4581 ui->pencil_keep_highlight = false;
4563 4582
4564 return ui; 4583 return ui;
4565} 4584}
@@ -4569,13 +4588,26 @@ static void free_ui(game_ui *ui)
4569 sfree(ui); 4588 sfree(ui);
4570} 4589}
4571 4590
4572static char *encode_ui(const game_ui *ui) 4591static config_item *get_prefs(game_ui *ui)
4573{ 4592{
4574 return NULL; 4593 config_item *ret;
4594
4595 ret = snewn(2, config_item);
4596
4597 ret[0].name = "Keep mouse highlight after changing a pencil mark";
4598 ret[0].kw = "pencil-keep-highlight";
4599 ret[0].type = C_BOOLEAN;
4600 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
4601
4602 ret[1].name = NULL;
4603 ret[1].type = C_END;
4604
4605 return ret;
4575} 4606}
4576 4607
4577static void decode_ui(game_ui *ui, const char *encoding) 4608static void set_prefs(game_ui *ui, const config_item *cfg)
4578{ 4609{
4610 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
4579} 4611}
4580 4612
4581static void game_changed_state(game_ui *ui, const game_state *oldstate, 4613static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -4594,6 +4626,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
4594 } 4626 }
4595} 4627}
4596 4628
4629static const char *current_key_label(const game_ui *ui,
4630 const game_state *state, int button)
4631{
4632 if (ui->hshow && (button == CURSOR_SELECT))
4633 return ui->hpencil ? "Ink" : "Pencil";
4634 return "";
4635}
4636
4597struct game_drawstate { 4637struct game_drawstate {
4598 bool started, xtype; 4638 bool started, xtype;
4599 int cr; 4639 int cr;
@@ -4613,7 +4653,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
4613 int tx, ty; 4653 int tx, ty;
4614 char buf[80]; 4654 char buf[80];
4615 4655
4616 button &= ~MOD_MASK; 4656 button = STRIP_BUTTON_MODIFIERS(button);
4617 4657
4618 tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; 4658 tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
4619 ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; 4659 ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
@@ -4632,7 +4672,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
4632 ui->hpencil = false; 4672 ui->hpencil = false;
4633 } 4673 }
4634 ui->hcursor = false; 4674 ui->hcursor = false;
4635 return UI_UPDATE; 4675 return MOVE_UI_UPDATE;
4636 } 4676 }
4637 if (button == RIGHT_BUTTON) { 4677 if (button == RIGHT_BUTTON) {
4638 /* 4678 /*
@@ -4652,20 +4692,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
4652 ui->hshow = false; 4692 ui->hshow = false;
4653 } 4693 }
4654 ui->hcursor = false; 4694 ui->hcursor = false;
4655 return UI_UPDATE; 4695 return MOVE_UI_UPDATE;
4656 } 4696 }
4657 } 4697 }
4658 if (IS_CURSOR_MOVE(button)) { 4698 if (IS_CURSOR_MOVE(button)) {
4659 move_cursor(button, &ui->hx, &ui->hy, cr, cr, false);
4660 ui->hshow = true;
4661 ui->hcursor = true; 4699 ui->hcursor = true;
4662 return UI_UPDATE; 4700 return move_cursor(button, &ui->hx, &ui->hy, cr, cr, false,
4701 &ui->hshow);
4663 } 4702 }
4664 if (ui->hshow && 4703 if (ui->hshow &&
4665 (button == CURSOR_SELECT)) { 4704 (button == CURSOR_SELECT)) {
4666 ui->hpencil = !ui->hpencil; 4705 ui->hpencil = !ui->hpencil;
4667 ui->hcursor = true; 4706 ui->hcursor = true;
4668 return UI_UPDATE; 4707 return MOVE_UI_UPDATE;
4669 } 4708 }
4670 4709
4671 if (ui->hshow && 4710 if (ui->hshow &&
@@ -4695,10 +4734,37 @@ static char *interpret_move(const game_state *state, game_ui *ui,
4695 if (ui->hpencil && state->grid[ui->hy*cr+ui->hx]) 4734 if (ui->hpencil && state->grid[ui->hy*cr+ui->hx])
4696 return NULL; 4735 return NULL;
4697 4736
4737 /*
4738 * If you ask to fill a square with what it already contains,
4739 * or blank it when it's already empty, that has no effect...
4740 */
4741 if ((!ui->hpencil || n == 0) && state->grid[ui->hy*cr+ui->hx] == n) {
4742 bool anypencil = false;
4743 int i;
4744 for (i = 0; i < cr; i++)
4745 anypencil = anypencil ||
4746 state->pencil[(ui->hy*cr+ui->hx) * cr + i];
4747 if (!anypencil) {
4748 /* ... expect to remove the cursor in mouse mode. */
4749 if (!ui->hcursor) {
4750 ui->hshow = false;
4751 return MOVE_UI_UPDATE;
4752 }
4753 return NULL;
4754 }
4755 }
4756
4698 sprintf(buf, "%c%d,%d,%d", 4757 sprintf(buf, "%c%d,%d,%d",
4699 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 4758 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
4700 4759
4701 if (!ui->hcursor) ui->hshow = false; 4760 /*
4761 * Hide the highlight after a keypress, if it was mouse-
4762 * generated. Also, don't hide it if this move has changed
4763 * pencil marks and the user preference says not to hide the
4764 * highlight in that situation.
4765 */
4766 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
4767 ui->hshow = false;
4702 4768
4703 return dupstr(buf); 4769 return dupstr(buf);
4704 } 4770 }
@@ -4787,7 +4853,7 @@ static game_state *execute_move(const game_state *from, const char *move)
4787#define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) ) 4853#define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) )
4788 4854
4789static void game_compute_size(const game_params *params, int tilesize, 4855static void game_compute_size(const game_params *params, int tilesize,
4790 int *x, int *y) 4856 const game_ui *ui, int *x, int *y)
4791{ 4857{
4792 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 4858 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
4793 struct { int tilesize; } ads, *ds = &ads; 4859 struct { int tilesize; } ads, *ds = &ads;
@@ -4920,6 +4986,18 @@ static void draw_number(drawing *dr, game_drawstate *ds,
4920 (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS : 4986 (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS :
4921 COL_BACKGROUND)); 4987 COL_BACKGROUND));
4922 4988
4989 /* pencil-mode highlight */
4990 if ((hl & 15) == 2) {
4991 int coords[6];
4992 coords[0] = cx;
4993 coords[1] = cy;
4994 coords[2] = cx+cw/2;
4995 coords[3] = cy;
4996 coords[4] = cx;
4997 coords[5] = cy+ch/2;
4998 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
4999 }
5000
4923 /* 5001 /*
4924 * Draw the corners of thick lines in corner-adjacent squares, 5002 * Draw the corners of thick lines in corner-adjacent squares,
4925 * which jut into this square by one pixel. 5003 * which jut into this square by one pixel.
@@ -4933,18 +5011,6 @@ static void draw_number(drawing *dr, game_drawstate *ds,
4933 if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1]) 5011 if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1])
4934 draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 5012 draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
4935 5013
4936 /* pencil-mode highlight */
4937 if ((hl & 15) == 2) {
4938 int coords[6];
4939 coords[0] = cx;
4940 coords[1] = cy;
4941 coords[2] = cx+cw/2;
4942 coords[3] = cy;
4943 coords[4] = cx;
4944 coords[5] = cy+ch/2;
4945 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
4946 }
4947
4948 if (state->kblocks) { 5014 if (state->kblocks) {
4949 int t = GRIDEXTRA * 3; 5015 int t = GRIDEXTRA * 3;
4950 int kcx, kcy, kcw, kch; 5016 int kcx, kcy, kcw, kch;
@@ -5104,7 +5170,7 @@ static void draw_number(drawing *dr, game_drawstate *ds,
5104 fw = (pr - pl) / (float)pw; 5170 fw = (pr - pl) / (float)pw;
5105 fh = (pb - pt) / (float)ph; 5171 fh = (pb - pt) / (float)ph;
5106 fs = min(fw, fh); 5172 fs = min(fw, fh);
5107 if (fs > bestsize) { 5173 if (fs >= bestsize) {
5108 bestsize = fs; 5174 bestsize = fs;
5109 pbest = pw; 5175 pbest = pw;
5110 } 5176 }
@@ -5175,14 +5241,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
5175 5241
5176 if (!ds->started) { 5242 if (!ds->started) {
5177 /* 5243 /*
5178 * The initial contents of the window are not guaranteed
5179 * and can vary with front ends. To be on the safe side,
5180 * all games should start by drawing a big
5181 * background-colour rectangle covering the whole window.
5182 */
5183 draw_rect(dr, 0, 0, SIZE(cr), SIZE(cr), COL_BACKGROUND);
5184
5185 /*
5186 * Draw the grid. We draw it as a big thick rectangle of 5244 * Draw the grid. We draw it as a big thick rectangle of
5187 * COL_GRID initially; individual calls to draw_number() 5245 * COL_GRID initially; individual calls to draw_number()
5188 * will poke the right-shaped holes in it. 5246 * will poke the right-shaped holes in it.
@@ -5315,14 +5373,8 @@ static int game_status(const game_state *state)
5315 return state->completed ? +1 : 0; 5373 return state->completed ? +1 : 0;
5316} 5374}
5317 5375
5318static bool game_timing_state(const game_state *state, game_ui *ui) 5376static void game_print_size(const game_params *params, const game_ui *ui,
5319{ 5377 float *x, float *y)
5320 if (state->completed)
5321 return false;
5322 return true;
5323}
5324
5325static void game_print_size(const game_params *params, float *x, float *y)
5326{ 5378{
5327 int pw, ph; 5379 int pw, ph;
5328 5380
@@ -5331,7 +5383,7 @@ static void game_print_size(const game_params *params, float *x, float *y)
5331 * for this game, because players will want to jot down no end 5383 * for this game, because players will want to jot down no end
5332 * of pencil marks in the squares. 5384 * of pencil marks in the squares.
5333 */ 5385 */
5334 game_compute_size(params, 900, &pw, &ph); 5386 game_compute_size(params, 900, ui, &pw, &ph);
5335 *x = pw / 100.0F; 5387 *x = pw / 100.0F;
5336 *y = ph / 100.0F; 5388 *y = ph / 100.0F;
5337} 5389}
@@ -5505,7 +5557,8 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
5505 sfree(coords); 5557 sfree(coords);
5506} 5558}
5507 5559
5508static void game_print(drawing *dr, const game_state *state, int tilesize) 5560static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
5561 int tilesize)
5509{ 5562{
5510 int cr = state->cr; 5563 int cr = state->cr;
5511 int ink = print_mono_colour(dr, 0); 5564 int ink = print_mono_colour(dr, 0);
@@ -5620,12 +5673,14 @@ const struct game thegame = {
5620 free_game, 5673 free_game,
5621 true, solve_game, 5674 true, solve_game,
5622 true, game_can_format_as_text_now, game_text_format, 5675 true, game_can_format_as_text_now, game_text_format,
5676 get_prefs, set_prefs,
5623 new_ui, 5677 new_ui,
5624 free_ui, 5678 free_ui,
5625 encode_ui, 5679 NULL, /* encode_ui */
5626 decode_ui, 5680 NULL, /* decode_ui */
5627 game_request_keys, 5681 game_request_keys,
5628 game_changed_state, 5682 game_changed_state,
5683 current_key_label,
5629 interpret_move, 5684 interpret_move,
5630 execute_move, 5685 execute_move,
5631 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 5686 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -5639,7 +5694,7 @@ const struct game thegame = {
5639 game_status, 5694 game_status,
5640 true, false, game_print_size, game_print, 5695 true, false, game_print_size, game_print,
5641 false, /* wants_statusbar */ 5696 false, /* wants_statusbar */
5642 false, game_timing_state, 5697 false, NULL, /* timing_state */
5643 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 5698 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
5644}; 5699};
5645 5700
diff --git a/apps/plugins/puzzles/src/sort.c b/apps/plugins/puzzles/src/sort.c
index d1897b6fdf..e82f4ec6dc 100644
--- a/apps/plugins/puzzles/src/sort.c
+++ b/apps/plugins/puzzles/src/sort.c
@@ -9,26 +9,8 @@
9 9
10#include "puzzles.h" 10#include "puzzles.h"
11 11
12static void memswap(void *av, void *bv, size_t size)
13{
14 char t[4096];
15 char *a = (char *)av, *b = (char *)bv;
16
17 while (size > 0) {
18 size_t thissize = size < sizeof(t) ? size : sizeof(t);
19
20 memcpy(t, a, thissize);
21 memcpy(a, b, thissize);
22 memcpy(b, t, thissize);
23
24 size -= thissize;
25 a += thissize;
26 b += thissize;
27 }
28}
29
30#define PTR(i) ((char *)array + size * (i)) 12#define PTR(i) ((char *)array + size * (i))
31#define SWAP(i,j) memswap(PTR(i), PTR(j), size) 13#define SWAP(i,j) swap_regions(PTR(i), PTR(j), size)
32#define CMP(i,j) cmp(PTR(i), PTR(j), ctx) 14#define CMP(i,j) cmp(PTR(i), PTR(j), ctx)
33 15
34#define LCHILD(i) (2*(i)+1) 16#define LCHILD(i) (2*(i)+1)
@@ -87,74 +69,3 @@ void arraysort_fn(void *array, size_t nmemb, size_t size,
87 downheap(array, i, size, cmp, ctx, 0); 69 downheap(array, i, size, cmp, ctx, 0);
88 } 70 }
89} 71}
90
91#ifdef SORT_TEST
92
93#include <stdlib.h>
94#include <time.h>
95
96int testcmp(const void *av, const void *bv, void *ctx)
97{
98 int a = *(const int *)av, b = *(const int *)bv;
99 const int *keys = (const int *)ctx;
100 return keys[a] < keys[b] ? -1 : keys[a] > keys[b] ? +1 : 0;
101}
102
103int resetcmp(const void *av, const void *bv)
104{
105 int a = *(const int *)av, b = *(const int *)bv;
106 return a < b ? -1 : a > b ? +1 : 0;
107}
108
109int main(int argc, char **argv)
110{
111 typedef int Array[3723];
112 Array data, keys;
113 int iteration;
114 unsigned seed;
115
116 seed = (argc > 1 ? strtoul(argv[1], NULL, 0) : time(NULL));
117 printf("Random seed = %u\n", seed);
118 srand(seed);
119
120 for (iteration = 0; iteration < 10000; iteration++) {
121 int j;
122 const char *fail = NULL;
123
124 for (j = 0; j < lenof(data); j++) {
125 data[j] = j;
126 keys[j] = rand();
127 }
128
129 arraysort(data, lenof(data), testcmp, keys);
130
131 for (j = 1; j < lenof(data); j++) {
132 if (keys[data[j]] < keys[data[j-1]])
133 fail = "output misordered";
134 }
135 if (!fail) {
136 Array reset;
137 memcpy(reset, data, sizeof(data));
138 qsort(reset, lenof(reset), sizeof(*reset), resetcmp);
139 for (j = 0; j < lenof(reset); j++)
140 if (reset[j] != j)
141 fail = "output not permuted";
142 }
143
144 if (fail) {
145 printf("Failed at iteration %d: %s\n", iteration, fail);
146 printf("Key values:\n");
147 for (j = 0; j < lenof(keys); j++)
148 printf(" [%2d] %10d\n", j, keys[j]);
149 printf("Output sorted order:\n");
150 for (j = 0; j < lenof(data); j++)
151 printf(" [%2d] %10d\n", data[j], keys[data[j]]);
152 return 1;
153 }
154 }
155
156 printf("OK\n");
157 return 0;
158}
159
160#endif /* SORT_TEST */
diff --git a/apps/plugins/puzzles/src/spectre-internal.h b/apps/plugins/puzzles/src/spectre-internal.h
new file mode 100644
index 0000000000..e43d30bfef
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre-internal.h
@@ -0,0 +1,327 @@
1#include "spectre.h"
2
3/*
4 * List macro of the names for hexagon types, which will be reused all
5 * over the place.
6 *
7 * (I have to call the parameter to this list macro something other
8 * than X, because here, X is also one of the macro arguments!)
9 */
10#define HEX_LETTERS(Z) Z(G) Z(D) Z(J) Z(L) Z(X) Z(P) Z(S) Z(F) Z(Y)
11
12typedef enum Hex {
13 #define HEX_ENUM_DECL(x) HEX_##x,
14 HEX_LETTERS(HEX_ENUM_DECL)
15 #undef HEX_ENUM_DECL
16} Hex;
17
18static inline unsigned num_subhexes(Hex h)
19{
20 return h == HEX_G ? 7 : 8;
21}
22
23static inline unsigned num_spectres(Hex h)
24{
25 return h == HEX_G ? 2 : 1;
26}
27
28/*
29 * Data types used in the lookup tables.
30 */
31struct MapEntry {
32 bool internal;
33 unsigned char hi, lo;
34};
35struct MapEdge {
36 unsigned char startindex, len;
37};
38struct Possibility {
39 unsigned char hi, lo;
40 unsigned long prob;
41};
42
43/*
44 * Coordinate system for tracking Spectres and their hexagonal
45 * metatiles.
46 *
47 * SpectreCoords will store the index of a single Spectre within a
48 * smallest-size hexagon, plus an array of HexCoord each indexing a
49 * hexagon within the expansion of a larger hexagon.
50 *
51 * The last coordinate stored, sc->c[sc->nc-1], will have a hex type
52 * but no index (represented by index==-1). This means "we haven't
53 * decided yet what this level of metatile needs to be". If we need to
54 * refer to this level during the hatctx_step algorithm, we make it up
55 * at random, based on a table of what metatiles each type can
56 * possibly be part of, at what index.
57 */
58typedef struct HexCoord {
59 int index; /* index within that tile, or -1 if not yet known */
60 Hex type; /* type of this hexagon */
61} HexCoord;
62
63typedef struct SpectreCoords {
64 int index; /* index of Spectre within the order-0 hexagon */
65 HexCoord *c;
66 size_t nc, csize;
67
68 /* Used by spectre-test to four-colour output tilings, and
69 * maintained unconditionally because it's easier than making it
70 * conditional */
71 unsigned char hex_colour, prev_hex_colour, incoming_hex_edge;
72} SpectreCoords;
73
74SpectreCoords *spectre_coords_new(void);
75void spectre_coords_free(SpectreCoords *hc);
76void spectre_coords_make_space(SpectreCoords *hc, size_t size);
77SpectreCoords *spectre_coords_copy(SpectreCoords *hc_in);
78
79/*
80 * Coordinate system for locating Spectres in the plane.
81 *
82 * The 'Point' structure represents a single point by means of an
83 * integer linear combination of {1, d, d^2, d^3}, where d is the
84 * complex number exp(i pi/6) representing 1/12 of a turn about the
85 * origin.
86 *
87 * The 'Spectre' structure represents an entire Spectre in a tiling,
88 * giving both the locations of all of its vertices and its
89 * combinatorial coordinates. It also contains a linked-list pointer,
90 * used during breadth-first search to generate all the Spectres in an
91 * area.
92 */
93typedef struct Point {
94 int coeffs[4];
95} Point;
96typedef struct Spectre Spectre;
97struct Spectre {
98 Point vertices[14];
99 SpectreCoords *sc;
100 Spectre *next; /* used in breadth-first search */
101};
102
103/* Fill in all the coordinates of a Spectre starting from any single edge */
104void spectre_place(Spectre *spec, Point u, Point v, int index_of_u);
105
106/* Free a Spectre and its contained coordinates */
107void spectre_free(Spectre *spec);
108
109/*
110 * A Point is really a complex number, so we can add, subtract and
111 * multiply them.
112 */
113static inline Point point_add(Point a, Point b)
114{
115 Point r;
116 size_t i;
117 for (i = 0; i < 4; i++)
118 r.coeffs[i] = a.coeffs[i] + b.coeffs[i];
119 return r;
120}
121static inline Point point_sub(Point a, Point b)
122{
123 Point r;
124 size_t i;
125 for (i = 0; i < 4; i++)
126 r.coeffs[i] = a.coeffs[i] - b.coeffs[i];
127 return r;
128}
129static inline Point point_mul_by_d(Point x)
130{
131 Point r;
132 /* Multiply by d by using the identity d^4 - d^2 + 1 = 0, so d^4 = d^2+1 */
133 r.coeffs[0] = -x.coeffs[3];
134 r.coeffs[1] = x.coeffs[0];
135 r.coeffs[2] = x.coeffs[1] + x.coeffs[3];
136 r.coeffs[3] = x.coeffs[2];
137 return r;
138}
139static inline Point point_mul(Point a, Point b)
140{
141 size_t i, j;
142 Point r;
143
144 /* Initialise r to be a, scaled by b's d^3 term */
145 for (j = 0; j < 4; j++)
146 r.coeffs[j] = a.coeffs[j] * b.coeffs[3];
147
148 /* Now iterate r = d*r + (next coefficient down), by Horner's rule */
149 for (i = 3; i-- > 0 ;) {
150 r = point_mul_by_d(r);
151 for (j = 0; j < 4; j++)
152 r.coeffs[j] += a.coeffs[j] * b.coeffs[i];
153 }
154
155 return r;
156}
157static inline bool point_equal(Point a, Point b)
158{
159 size_t i;
160 for (i = 0; i < 4; i++)
161 if (a.coeffs[i] != b.coeffs[i])
162 return false;
163 return true;
164}
165
166/*
167 * Return the Point corresponding to a rotation of s steps around the
168 * origin, i.e. a rotation by 30*s degrees or s*pi/6 radians.
169 */
170static inline Point point_rot(int s)
171{
172 Point r = {{ 1, 0, 0, 0 }};
173 Point dpower = {{ 0, 1, 0, 0 }};
174
175 /* Reduce to a sensible range */
176 s = s % 12;
177 if (s < 0)
178 s += 12;
179
180 while (true) {
181 if (s & 1)
182 r = point_mul(r, dpower);
183 s >>= 1;
184 if (!s)
185 break;
186 dpower = point_mul(dpower, dpower);
187 }
188
189 return r;
190}
191
192/*
193 * SpectreContext is the shared context of a whole run of the
194 * algorithm. Its 'prototype' SpectreCoords object represents the
195 * coordinates of the starting Spectre, and is extended as necessary;
196 * any other SpectreCoord that needs extending will copy the
197 * higher-order values from ctx->prototype as needed, so that once
198 * each choice has been made, it remains consistent.
199 *
200 * When we're inventing a random piece of tiling in the first place,
201 * we append to ctx->prototype by choosing a random (but legal)
202 * higher-level metatile for the current topmost one to turn out to be
203 * part of. When we're replaying a generation whose parameters are
204 * already stored, we don't have a random_state, and we make fixed
205 * decisions if not enough coordinates were provided, as in the
206 * corresponding hat.c system.
207 *
208 * For a normal (non-testing) caller, spectrectx_generate() is the
209 * main useful function. It breadth-first searches a whole area to
210 * generate all the Spectres in it, starting from a (typically
211 * central) one with the coordinates of ctx->prototype. The callback
212 * function processes each Spectre as it's generated, and returns true
213 * or false to indicate whether that Spectre is within the bounds of
214 * the target area (and therefore the search should continue exploring
215 * its neighbours).
216 */
217typedef struct SpectreContext {
218 random_state *rs;
219 bool must_free_rs;
220 Point start_vertices[2]; /* vertices 0,1 of the starting Spectre */
221 int orientation; /* orientation to put in SpectrePatchParams */
222 SpectreCoords *prototype;
223} SpectreContext;
224
225void spectrectx_init_random(SpectreContext *ctx, random_state *rs);
226void spectrectx_init_from_params(
227 SpectreContext *ctx, const struct SpectrePatchParams *ps);
228void spectrectx_cleanup(SpectreContext *ctx);
229SpectreCoords *spectrectx_initial_coords(SpectreContext *ctx);
230void spectrectx_extend_coords(SpectreContext *ctx, SpectreCoords *hc,
231 size_t n);
232void spectrectx_step(SpectreContext *ctx, SpectreCoords *sc,
233 unsigned edge, unsigned *outedge);
234void spectrectx_generate(SpectreContext *ctx,
235 bool (*callback)(void *cbctx, const Spectre *spec),
236 void *cbctx);
237
238/* For spectre-test to directly generate a tiling of hexes */
239void spectrectx_step_hex(SpectreContext *ctx, SpectreCoords *sc,
240 size_t depth, unsigned edge, unsigned *outedge);
241
242/* Subroutines that step around the tiling specified by a SpectreCtx,
243 * delivering both plane and combinatorial coordinates as they go */
244Spectre *spectre_initial(SpectreContext *ctx);
245Spectre *spectre_adjacent(SpectreContext *ctx, const Spectre *src_spec,
246 unsigned src_edge, unsigned *dst_edge);
247
248/* For extracting the point coordinates */
249typedef struct Coord {
250 int c1, cr3; /* coefficients of 1 and sqrt(3) respectively */
251} Coord;
252
253static inline Coord point_x(Point p)
254{
255 Coord x = { 2 * p.coeffs[0] + p.coeffs[2], p.coeffs[1] };
256 return x;
257}
258
259static inline Coord point_y(Point p)
260{
261 Coord y = { 2 * p.coeffs[3] + p.coeffs[1], p.coeffs[2] };
262 return y;
263}
264
265static inline int coord_sign(Coord x)
266{
267 if (x.c1 == 0 && x.cr3 == 0)
268 return 0;
269 if (x.c1 >= 0 && x.cr3 >= 0)
270 return +1;
271 if (x.c1 <= 0 && x.cr3 <= 0)
272 return -1;
273
274 if (x.c1 * x.c1 > 3 * x.cr3 * x.cr3)
275 return x.c1 < 0 ? -1 : +1;
276 else
277 return x.cr3 < 0 ? -1 : +1;
278}
279
280static inline Coord coord_construct(int c1, int cr3)
281{
282 Coord c = { c1, cr3 };
283 return c;
284}
285
286static inline Coord coord_integer(int c1)
287{
288 return coord_construct(c1, 0);
289}
290
291static inline Coord coord_add(Coord a, Coord b)
292{
293 Coord sum;
294 sum.c1 = a.c1 + b.c1;
295 sum.cr3 = a.cr3 + b.cr3;
296 return sum;
297}
298
299static inline Coord coord_sub(Coord a, Coord b)
300{
301 Coord diff;
302 diff.c1 = a.c1 - b.c1;
303 diff.cr3 = a.cr3 - b.cr3;
304 return diff;
305}
306
307static inline Coord coord_mul(Coord a, Coord b)
308{
309 Coord prod;
310 prod.c1 = a.c1 * b.c1 + 3 * a.cr3 * b.cr3;
311 prod.cr3 = a.c1 * b.cr3 + a.cr3 * b.c1;
312 return prod;
313}
314
315static inline Coord coord_abs(Coord a)
316{
317 int sign = coord_sign(a);
318 Coord abs;
319 abs.c1 = a.c1 * sign;
320 abs.cr3 = a.cr3 * sign;
321 return abs;
322}
323
324static inline int coord_cmp(Coord a, Coord b)
325{
326 return coord_sign(coord_sub(a, b));
327}
diff --git a/apps/plugins/puzzles/src/spectre-tables-auto.h b/apps/plugins/puzzles/src/spectre-tables-auto.h
new file mode 100644
index 0000000000..a41c4e1d18
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre-tables-auto.h
@@ -0,0 +1,1220 @@
1/*
2 * Autogenerated transition tables for the Spectre tiling.
3 * Generated by auxiliary/spectre-gen.c.
4 */
5
6static const struct MapEntry hexmap_G[] = {
7 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
8 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
9 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
10 { false, 2, 0 }, /* edge 3 of hex 0 (F) */
11 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
12 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
13 { true, 0, 2 }, /* edge 0 of hex 1 (X) */
14 { true, 3, 4 }, /* edge 1 of hex 1 (X) */
15 { false, 3, 1 }, /* edge 2 of hex 1 (X) */
16 { false, 3, 0 }, /* edge 3 of hex 1 (X) */
17 { false, 2, 2 }, /* edge 4 of hex 1 (X) */
18 { false, 2, 1 }, /* edge 5 of hex 1 (X) */
19 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
20 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
21 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
22 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
23 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
24 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
25 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
26 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
27 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
28 { false, 3, 2 }, /* edge 3 of hex 3 (S) */
29 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
30 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
31 { true, 5, 2 }, /* edge 0 of hex 4 (P) */
32 { true, 2, 2 }, /* edge 1 of hex 4 (P) */
33 { false, 0, 0 }, /* edge 2 of hex 4 (P) */
34 { false, 5, 1 }, /* edge 3 of hex 4 (P) */
35 { false, 5, 0 }, /* edge 4 of hex 4 (P) */
36 { false, 4, 4 }, /* edge 5 of hex 4 (P) */
37 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
38 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
39 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
40 { false, 4, 3 }, /* edge 3 of hex 5 (D) */
41 { false, 4, 2 }, /* edge 4 of hex 5 (D) */
42 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
43 { true, 3, 2 }, /* edge 0 of hex 6 (J) */
44 { true, 5, 5 }, /* edge 1 of hex 6 (J) */
45 { false, 4, 1 }, /* edge 2 of hex 6 (J) */
46 { false, 4, 0 }, /* edge 3 of hex 6 (J) */
47 { false, 3, 4 }, /* edge 4 of hex 6 (J) */
48 { false, 3, 3 }, /* edge 5 of hex 6 (J) */
49};
50static const struct MapEdge hexedges_G[] = {
51 { 0, 2 },
52 { 2, 3 },
53 { 5, 3 },
54 { 8, 5 },
55 { 13, 5 },
56 { 18, 2 },
57};
58static const struct MapEntry hexin_G[] = {
59 { true, 4, 2 }, /* subedge 0 of edge 0 */
60 { true, 2, 1 }, /* subedge 1 of edge 0 */
61 { true, 2, 0 }, /* subedge 0 of edge 1 */
62 { true, 0, 5 }, /* subedge 1 of edge 1 */
63 { true, 0, 4 }, /* subedge 2 of edge 1 */
64 { true, 0, 3 }, /* subedge 0 of edge 2 */
65 { true, 1, 5 }, /* subedge 1 of edge 2 */
66 { true, 1, 4 }, /* subedge 2 of edge 2 */
67 { true, 1, 3 }, /* subedge 0 of edge 3 */
68 { true, 1, 2 }, /* subedge 1 of edge 3 */
69 { true, 3, 3 }, /* subedge 2 of edge 3 */
70 { true, 6, 5 }, /* subedge 3 of edge 3 */
71 { true, 6, 4 }, /* subedge 4 of edge 3 */
72 { true, 6, 3 }, /* subedge 0 of edge 4 */
73 { true, 6, 2 }, /* subedge 1 of edge 4 */
74 { true, 5, 4 }, /* subedge 2 of edge 4 */
75 { true, 5, 3 }, /* subedge 3 of edge 4 */
76 { true, 4, 5 }, /* subedge 4 of edge 4 */
77 { true, 4, 4 }, /* subedge 0 of edge 5 */
78 { true, 4, 3 }, /* subedge 1 of edge 5 */
79};
80static const struct MapEntry hexmap_D[] = {
81 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
82 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
83 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
84 { false, 1, 3 }, /* edge 3 of hex 0 (F) */
85 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
86 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
87 { true, 0, 2 }, /* edge 0 of hex 1 (P) */
88 { true, 3, 4 }, /* edge 1 of hex 1 (P) */
89 { false, 3, 0 }, /* edge 2 of hex 1 (P) */
90 { false, 2, 1 }, /* edge 3 of hex 1 (P) */
91 { false, 2, 0 }, /* edge 4 of hex 1 (P) */
92 { false, 1, 4 }, /* edge 5 of hex 1 (P) */
93 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
94 { false, 0, 2 }, /* edge 1 of hex 2 (G) */
95 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
96 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
97 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
98 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
99 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
100 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
101 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
102 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
103 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
104 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
105 { true, 5, 2 }, /* edge 0 of hex 4 (X) */
106 { true, 2, 2 }, /* edge 1 of hex 4 (X) */
107 { false, 0, 1 }, /* edge 2 of hex 4 (X) */
108 { false, 0, 0 }, /* edge 3 of hex 4 (X) */
109 { false, 5, 4 }, /* edge 4 of hex 4 (X) */
110 { false, 5, 3 }, /* edge 5 of hex 4 (X) */
111 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
112 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
113 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
114 { false, 5, 2 }, /* edge 3 of hex 5 (D) */
115 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
116 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
117 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
118 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
119 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
120 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
121 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
122 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
123 { true, 6, 2 }, /* edge 0 of hex 7 (X) */
124 { true, 5, 4 }, /* edge 1 of hex 7 (X) */
125 { false, 5, 1 }, /* edge 2 of hex 7 (X) */
126 { false, 5, 0 }, /* edge 3 of hex 7 (X) */
127 { false, 4, 2 }, /* edge 4 of hex 7 (X) */
128 { false, 4, 1 }, /* edge 5 of hex 7 (X) */
129};
130static const struct MapEdge hexedges_D[] = {
131 { 0, 3 },
132 { 3, 5 },
133 { 8, 2 },
134 { 10, 4 },
135 { 14, 3 },
136 { 17, 5 },
137};
138static const struct MapEntry hexin_D[] = {
139 { true, 4, 3 }, /* subedge 0 of edge 0 */
140 { true, 4, 2 }, /* subedge 1 of edge 0 */
141 { true, 2, 1 }, /* subedge 2 of edge 0 */
142 { true, 2, 0 }, /* subedge 0 of edge 1 */
143 { true, 0, 5 }, /* subedge 1 of edge 1 */
144 { true, 0, 4 }, /* subedge 2 of edge 1 */
145 { true, 0, 3 }, /* subedge 3 of edge 1 */
146 { true, 1, 5 }, /* subedge 4 of edge 1 */
147 { true, 1, 4 }, /* subedge 0 of edge 2 */
148 { true, 1, 3 }, /* subedge 1 of edge 2 */
149 { true, 1, 2 }, /* subedge 0 of edge 3 */
150 { true, 3, 3 }, /* subedge 1 of edge 3 */
151 { true, 6, 5 }, /* subedge 2 of edge 3 */
152 { true, 6, 4 }, /* subedge 3 of edge 3 */
153 { true, 6, 3 }, /* subedge 0 of edge 4 */
154 { true, 7, 5 }, /* subedge 1 of edge 4 */
155 { true, 7, 4 }, /* subedge 2 of edge 4 */
156 { true, 7, 3 }, /* subedge 0 of edge 5 */
157 { true, 7, 2 }, /* subedge 1 of edge 5 */
158 { true, 5, 3 }, /* subedge 2 of edge 5 */
159 { true, 4, 5 }, /* subedge 3 of edge 5 */
160 { true, 4, 4 }, /* subedge 4 of edge 5 */
161};
162static const struct MapEntry hexmap_J[] = {
163 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
164 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
165 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
166 { false, 1, 3 }, /* edge 3 of hex 0 (F) */
167 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
168 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
169 { true, 0, 2 }, /* edge 0 of hex 1 (P) */
170 { true, 3, 4 }, /* edge 1 of hex 1 (P) */
171 { false, 3, 0 }, /* edge 2 of hex 1 (P) */
172 { false, 2, 1 }, /* edge 3 of hex 1 (P) */
173 { false, 2, 0 }, /* edge 4 of hex 1 (P) */
174 { false, 1, 4 }, /* edge 5 of hex 1 (P) */
175 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
176 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
177 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
178 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
179 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
180 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
181 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
182 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
183 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
184 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
185 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
186 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
187 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
188 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
189 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
190 { false, 5, 4 }, /* edge 3 of hex 4 (Y) */
191 { false, 5, 3 }, /* edge 4 of hex 4 (Y) */
192 { false, 5, 2 }, /* edge 5 of hex 4 (Y) */
193 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
194 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
195 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
196 { false, 5, 1 }, /* edge 3 of hex 5 (D) */
197 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
198 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
199 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
200 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
201 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
202 { false, 3, 4 }, /* edge 3 of hex 6 (F) */
203 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
204 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
205 { true, 6, 2 }, /* edge 0 of hex 7 (P) */
206 { true, 5, 4 }, /* edge 1 of hex 7 (P) */
207 { false, 5, 0 }, /* edge 2 of hex 7 (P) */
208 { false, 4, 1 }, /* edge 3 of hex 7 (P) */
209 { false, 4, 0 }, /* edge 4 of hex 7 (P) */
210 { false, 3, 5 }, /* edge 5 of hex 7 (P) */
211};
212static const struct MapEdge hexedges_J[] = {
213 { 0, 2 },
214 { 2, 5 },
215 { 7, 2 },
216 { 9, 6 },
217 { 15, 2 },
218 { 17, 5 },
219};
220static const struct MapEntry hexin_J[] = {
221 { true, 4, 2 }, /* subedge 0 of edge 0 */
222 { true, 2, 1 }, /* subedge 1 of edge 0 */
223 { true, 2, 0 }, /* subedge 0 of edge 1 */
224 { true, 0, 5 }, /* subedge 1 of edge 1 */
225 { true, 0, 4 }, /* subedge 2 of edge 1 */
226 { true, 0, 3 }, /* subedge 3 of edge 1 */
227 { true, 1, 5 }, /* subedge 4 of edge 1 */
228 { true, 1, 4 }, /* subedge 0 of edge 2 */
229 { true, 1, 3 }, /* subedge 1 of edge 2 */
230 { true, 1, 2 }, /* subedge 0 of edge 3 */
231 { true, 3, 3 }, /* subedge 1 of edge 3 */
232 { true, 6, 5 }, /* subedge 2 of edge 3 */
233 { true, 6, 4 }, /* subedge 3 of edge 3 */
234 { true, 6, 3 }, /* subedge 4 of edge 3 */
235 { true, 7, 5 }, /* subedge 5 of edge 3 */
236 { true, 7, 4 }, /* subedge 0 of edge 4 */
237 { true, 7, 3 }, /* subedge 1 of edge 4 */
238 { true, 7, 2 }, /* subedge 0 of edge 5 */
239 { true, 5, 3 }, /* subedge 1 of edge 5 */
240 { true, 4, 5 }, /* subedge 2 of edge 5 */
241 { true, 4, 4 }, /* subedge 3 of edge 5 */
242 { true, 4, 3 }, /* subedge 4 of edge 5 */
243};
244static const struct MapEntry hexmap_L[] = {
245 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
246 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
247 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
248 { false, 1, 3 }, /* edge 3 of hex 0 (F) */
249 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
250 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
251 { true, 0, 2 }, /* edge 0 of hex 1 (P) */
252 { true, 3, 4 }, /* edge 1 of hex 1 (P) */
253 { false, 3, 0 }, /* edge 2 of hex 1 (P) */
254 { false, 2, 1 }, /* edge 3 of hex 1 (P) */
255 { false, 2, 0 }, /* edge 4 of hex 1 (P) */
256 { false, 1, 4 }, /* edge 5 of hex 1 (P) */
257 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
258 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
259 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
260 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
261 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
262 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
263 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
264 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
265 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
266 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
267 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
268 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
269 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
270 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
271 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
272 { false, 5, 5 }, /* edge 3 of hex 4 (Y) */
273 { false, 5, 4 }, /* edge 4 of hex 4 (Y) */
274 { false, 5, 3 }, /* edge 5 of hex 4 (Y) */
275 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
276 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
277 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
278 { false, 5, 2 }, /* edge 3 of hex 5 (D) */
279 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
280 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
281 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
282 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
283 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
284 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
285 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
286 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
287 { true, 6, 2 }, /* edge 0 of hex 7 (X) */
288 { true, 5, 4 }, /* edge 1 of hex 7 (X) */
289 { false, 5, 1 }, /* edge 2 of hex 7 (X) */
290 { false, 5, 0 }, /* edge 3 of hex 7 (X) */
291 { false, 4, 2 }, /* edge 4 of hex 7 (X) */
292 { false, 4, 1 }, /* edge 5 of hex 7 (X) */
293};
294static const struct MapEdge hexedges_L[] = {
295 { 0, 2 },
296 { 2, 5 },
297 { 7, 2 },
298 { 9, 4 },
299 { 13, 3 },
300 { 16, 6 },
301};
302static const struct MapEntry hexin_L[] = {
303 { true, 4, 2 }, /* subedge 0 of edge 0 */
304 { true, 2, 1 }, /* subedge 1 of edge 0 */
305 { true, 2, 0 }, /* subedge 0 of edge 1 */
306 { true, 0, 5 }, /* subedge 1 of edge 1 */
307 { true, 0, 4 }, /* subedge 2 of edge 1 */
308 { true, 0, 3 }, /* subedge 3 of edge 1 */
309 { true, 1, 5 }, /* subedge 4 of edge 1 */
310 { true, 1, 4 }, /* subedge 0 of edge 2 */
311 { true, 1, 3 }, /* subedge 1 of edge 2 */
312 { true, 1, 2 }, /* subedge 0 of edge 3 */
313 { true, 3, 3 }, /* subedge 1 of edge 3 */
314 { true, 6, 5 }, /* subedge 2 of edge 3 */
315 { true, 6, 4 }, /* subedge 3 of edge 3 */
316 { true, 6, 3 }, /* subedge 0 of edge 4 */
317 { true, 7, 5 }, /* subedge 1 of edge 4 */
318 { true, 7, 4 }, /* subedge 2 of edge 4 */
319 { true, 7, 3 }, /* subedge 0 of edge 5 */
320 { true, 7, 2 }, /* subedge 1 of edge 5 */
321 { true, 5, 3 }, /* subedge 2 of edge 5 */
322 { true, 4, 5 }, /* subedge 3 of edge 5 */
323 { true, 4, 4 }, /* subedge 4 of edge 5 */
324 { true, 4, 3 }, /* subedge 5 of edge 5 */
325};
326static const struct MapEntry hexmap_X[] = {
327 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
328 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
329 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
330 { false, 2, 0 }, /* edge 3 of hex 0 (F) */
331 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
332 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
333 { true, 0, 2 }, /* edge 0 of hex 1 (Y) */
334 { true, 3, 4 }, /* edge 1 of hex 1 (Y) */
335 { false, 3, 0 }, /* edge 2 of hex 1 (Y) */
336 { false, 2, 3 }, /* edge 3 of hex 1 (Y) */
337 { false, 2, 2 }, /* edge 4 of hex 1 (Y) */
338 { false, 2, 1 }, /* edge 5 of hex 1 (Y) */
339 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
340 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
341 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
342 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
343 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
344 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
345 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
346 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
347 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
348 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
349 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
350 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
351 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
352 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
353 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
354 { false, 5, 4 }, /* edge 3 of hex 4 (Y) */
355 { false, 5, 3 }, /* edge 4 of hex 4 (Y) */
356 { false, 5, 2 }, /* edge 5 of hex 4 (Y) */
357 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
358 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
359 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
360 { false, 5, 1 }, /* edge 3 of hex 5 (D) */
361 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
362 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
363 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
364 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
365 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
366 { false, 3, 4 }, /* edge 3 of hex 6 (F) */
367 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
368 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
369 { true, 6, 2 }, /* edge 0 of hex 7 (P) */
370 { true, 5, 4 }, /* edge 1 of hex 7 (P) */
371 { false, 5, 0 }, /* edge 2 of hex 7 (P) */
372 { false, 4, 1 }, /* edge 3 of hex 7 (P) */
373 { false, 4, 0 }, /* edge 4 of hex 7 (P) */
374 { false, 3, 5 }, /* edge 5 of hex 7 (P) */
375};
376static const struct MapEdge hexedges_X[] = {
377 { 0, 2 },
378 { 2, 3 },
379 { 5, 4 },
380 { 9, 6 },
381 { 15, 2 },
382 { 17, 5 },
383};
384static const struct MapEntry hexin_X[] = {
385 { true, 4, 2 }, /* subedge 0 of edge 0 */
386 { true, 2, 1 }, /* subedge 1 of edge 0 */
387 { true, 2, 0 }, /* subedge 0 of edge 1 */
388 { true, 0, 5 }, /* subedge 1 of edge 1 */
389 { true, 0, 4 }, /* subedge 2 of edge 1 */
390 { true, 0, 3 }, /* subedge 0 of edge 2 */
391 { true, 1, 5 }, /* subedge 1 of edge 2 */
392 { true, 1, 4 }, /* subedge 2 of edge 2 */
393 { true, 1, 3 }, /* subedge 3 of edge 2 */
394 { true, 1, 2 }, /* subedge 0 of edge 3 */
395 { true, 3, 3 }, /* subedge 1 of edge 3 */
396 { true, 6, 5 }, /* subedge 2 of edge 3 */
397 { true, 6, 4 }, /* subedge 3 of edge 3 */
398 { true, 6, 3 }, /* subedge 4 of edge 3 */
399 { true, 7, 5 }, /* subedge 5 of edge 3 */
400 { true, 7, 4 }, /* subedge 0 of edge 4 */
401 { true, 7, 3 }, /* subedge 1 of edge 4 */
402 { true, 7, 2 }, /* subedge 0 of edge 5 */
403 { true, 5, 3 }, /* subedge 1 of edge 5 */
404 { true, 4, 5 }, /* subedge 2 of edge 5 */
405 { true, 4, 4 }, /* subedge 3 of edge 5 */
406 { true, 4, 3 }, /* subedge 4 of edge 5 */
407};
408static const struct MapEntry hexmap_P[] = {
409 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
410 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
411 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
412 { false, 2, 0 }, /* edge 3 of hex 0 (F) */
413 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
414 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
415 { true, 0, 2 }, /* edge 0 of hex 1 (Y) */
416 { true, 3, 4 }, /* edge 1 of hex 1 (Y) */
417 { false, 3, 0 }, /* edge 2 of hex 1 (Y) */
418 { false, 2, 3 }, /* edge 3 of hex 1 (Y) */
419 { false, 2, 2 }, /* edge 4 of hex 1 (Y) */
420 { false, 2, 1 }, /* edge 5 of hex 1 (Y) */
421 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
422 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
423 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
424 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
425 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
426 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
427 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
428 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
429 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
430 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
431 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
432 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
433 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
434 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
435 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
436 { false, 5, 5 }, /* edge 3 of hex 4 (Y) */
437 { false, 5, 4 }, /* edge 4 of hex 4 (Y) */
438 { false, 5, 3 }, /* edge 5 of hex 4 (Y) */
439 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
440 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
441 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
442 { false, 5, 2 }, /* edge 3 of hex 5 (D) */
443 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
444 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
445 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
446 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
447 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
448 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
449 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
450 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
451 { true, 6, 2 }, /* edge 0 of hex 7 (X) */
452 { true, 5, 4 }, /* edge 1 of hex 7 (X) */
453 { false, 5, 1 }, /* edge 2 of hex 7 (X) */
454 { false, 5, 0 }, /* edge 3 of hex 7 (X) */
455 { false, 4, 2 }, /* edge 4 of hex 7 (X) */
456 { false, 4, 1 }, /* edge 5 of hex 7 (X) */
457};
458static const struct MapEdge hexedges_P[] = {
459 { 0, 2 },
460 { 2, 3 },
461 { 5, 4 },
462 { 9, 4 },
463 { 13, 3 },
464 { 16, 6 },
465};
466static const struct MapEntry hexin_P[] = {
467 { true, 4, 2 }, /* subedge 0 of edge 0 */
468 { true, 2, 1 }, /* subedge 1 of edge 0 */
469 { true, 2, 0 }, /* subedge 0 of edge 1 */
470 { true, 0, 5 }, /* subedge 1 of edge 1 */
471 { true, 0, 4 }, /* subedge 2 of edge 1 */
472 { true, 0, 3 }, /* subedge 0 of edge 2 */
473 { true, 1, 5 }, /* subedge 1 of edge 2 */
474 { true, 1, 4 }, /* subedge 2 of edge 2 */
475 { true, 1, 3 }, /* subedge 3 of edge 2 */
476 { true, 1, 2 }, /* subedge 0 of edge 3 */
477 { true, 3, 3 }, /* subedge 1 of edge 3 */
478 { true, 6, 5 }, /* subedge 2 of edge 3 */
479 { true, 6, 4 }, /* subedge 3 of edge 3 */
480 { true, 6, 3 }, /* subedge 0 of edge 4 */
481 { true, 7, 5 }, /* subedge 1 of edge 4 */
482 { true, 7, 4 }, /* subedge 2 of edge 4 */
483 { true, 7, 3 }, /* subedge 0 of edge 5 */
484 { true, 7, 2 }, /* subedge 1 of edge 5 */
485 { true, 5, 3 }, /* subedge 2 of edge 5 */
486 { true, 4, 5 }, /* subedge 3 of edge 5 */
487 { true, 4, 4 }, /* subedge 4 of edge 5 */
488 { true, 4, 3 }, /* subedge 5 of edge 5 */
489};
490static const struct MapEntry hexmap_S[] = {
491 { true, 2, 5 }, /* edge 0 of hex 0 (L) */
492 { true, 3, 5 }, /* edge 1 of hex 0 (L) */
493 { true, 1, 0 }, /* edge 2 of hex 0 (L) */
494 { false, 1, 1 }, /* edge 3 of hex 0 (L) */
495 { false, 1, 0 }, /* edge 4 of hex 0 (L) */
496 { false, 0, 4 }, /* edge 5 of hex 0 (L) */
497 { true, 0, 2 }, /* edge 0 of hex 1 (P) */
498 { true, 3, 4 }, /* edge 1 of hex 1 (P) */
499 { false, 3, 0 }, /* edge 2 of hex 1 (P) */
500 { false, 2, 1 }, /* edge 3 of hex 1 (P) */
501 { false, 2, 0 }, /* edge 4 of hex 1 (P) */
502 { false, 1, 2 }, /* edge 5 of hex 1 (P) */
503 { false, 0, 3 }, /* edge 0 of hex 2 (G) */
504 { false, 0, 2 }, /* edge 1 of hex 2 (G) */
505 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
506 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
507 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
508 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
509 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
510 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
511 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
512 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
513 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
514 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
515 { true, 5, 2 }, /* edge 0 of hex 4 (X) */
516 { true, 2, 2 }, /* edge 1 of hex 4 (X) */
517 { false, 0, 1 }, /* edge 2 of hex 4 (X) */
518 { false, 0, 0 }, /* edge 3 of hex 4 (X) */
519 { false, 5, 4 }, /* edge 4 of hex 4 (X) */
520 { false, 5, 3 }, /* edge 5 of hex 4 (X) */
521 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
522 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
523 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
524 { false, 5, 2 }, /* edge 3 of hex 5 (D) */
525 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
526 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
527 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
528 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
529 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
530 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
531 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
532 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
533 { true, 6, 2 }, /* edge 0 of hex 7 (X) */
534 { true, 5, 4 }, /* edge 1 of hex 7 (X) */
535 { false, 5, 1 }, /* edge 2 of hex 7 (X) */
536 { false, 5, 0 }, /* edge 3 of hex 7 (X) */
537 { false, 4, 2 }, /* edge 4 of hex 7 (X) */
538 { false, 4, 1 }, /* edge 5 of hex 7 (X) */
539};
540static const struct MapEdge hexedges_S[] = {
541 { 0, 5 },
542 { 5, 3 },
543 { 8, 2 },
544 { 10, 4 },
545 { 14, 3 },
546 { 17, 5 },
547};
548static const struct MapEntry hexin_S[] = {
549 { true, 4, 3 }, /* subedge 0 of edge 0 */
550 { true, 4, 2 }, /* subedge 1 of edge 0 */
551 { true, 2, 1 }, /* subedge 2 of edge 0 */
552 { true, 2, 0 }, /* subedge 3 of edge 0 */
553 { true, 0, 5 }, /* subedge 4 of edge 0 */
554 { true, 0, 4 }, /* subedge 0 of edge 1 */
555 { true, 0, 3 }, /* subedge 1 of edge 1 */
556 { true, 1, 5 }, /* subedge 2 of edge 1 */
557 { true, 1, 4 }, /* subedge 0 of edge 2 */
558 { true, 1, 3 }, /* subedge 1 of edge 2 */
559 { true, 1, 2 }, /* subedge 0 of edge 3 */
560 { true, 3, 3 }, /* subedge 1 of edge 3 */
561 { true, 6, 5 }, /* subedge 2 of edge 3 */
562 { true, 6, 4 }, /* subedge 3 of edge 3 */
563 { true, 6, 3 }, /* subedge 0 of edge 4 */
564 { true, 7, 5 }, /* subedge 1 of edge 4 */
565 { true, 7, 4 }, /* subedge 2 of edge 4 */
566 { true, 7, 3 }, /* subedge 0 of edge 5 */
567 { true, 7, 2 }, /* subedge 1 of edge 5 */
568 { true, 5, 3 }, /* subedge 2 of edge 5 */
569 { true, 4, 5 }, /* subedge 3 of edge 5 */
570 { true, 4, 4 }, /* subedge 4 of edge 5 */
571};
572static const struct MapEntry hexmap_F[] = {
573 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
574 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
575 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
576 { false, 1, 3 }, /* edge 3 of hex 0 (F) */
577 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
578 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
579 { true, 0, 2 }, /* edge 0 of hex 1 (P) */
580 { true, 3, 4 }, /* edge 1 of hex 1 (P) */
581 { false, 3, 0 }, /* edge 2 of hex 1 (P) */
582 { false, 2, 1 }, /* edge 3 of hex 1 (P) */
583 { false, 2, 0 }, /* edge 4 of hex 1 (P) */
584 { false, 1, 4 }, /* edge 5 of hex 1 (P) */
585 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
586 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
587 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
588 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
589 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
590 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
591 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
592 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
593 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
594 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
595 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
596 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
597 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
598 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
599 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
600 { false, 5, 4 }, /* edge 3 of hex 4 (Y) */
601 { false, 5, 3 }, /* edge 4 of hex 4 (Y) */
602 { false, 5, 2 }, /* edge 5 of hex 4 (Y) */
603 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
604 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
605 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
606 { false, 5, 1 }, /* edge 3 of hex 5 (D) */
607 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
608 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
609 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
610 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
611 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
612 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
613 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
614 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
615 { true, 6, 2 }, /* edge 0 of hex 7 (Y) */
616 { true, 5, 4 }, /* edge 1 of hex 7 (Y) */
617 { false, 5, 0 }, /* edge 2 of hex 7 (Y) */
618 { false, 4, 3 }, /* edge 3 of hex 7 (Y) */
619 { false, 4, 2 }, /* edge 4 of hex 7 (Y) */
620 { false, 4, 1 }, /* edge 5 of hex 7 (Y) */
621};
622static const struct MapEdge hexedges_F[] = {
623 { 0, 2 },
624 { 2, 5 },
625 { 7, 2 },
626 { 9, 4 },
627 { 13, 4 },
628 { 17, 5 },
629};
630static const struct MapEntry hexin_F[] = {
631 { true, 4, 2 }, /* subedge 0 of edge 0 */
632 { true, 2, 1 }, /* subedge 1 of edge 0 */
633 { true, 2, 0 }, /* subedge 0 of edge 1 */
634 { true, 0, 5 }, /* subedge 1 of edge 1 */
635 { true, 0, 4 }, /* subedge 2 of edge 1 */
636 { true, 0, 3 }, /* subedge 3 of edge 1 */
637 { true, 1, 5 }, /* subedge 4 of edge 1 */
638 { true, 1, 4 }, /* subedge 0 of edge 2 */
639 { true, 1, 3 }, /* subedge 1 of edge 2 */
640 { true, 1, 2 }, /* subedge 0 of edge 3 */
641 { true, 3, 3 }, /* subedge 1 of edge 3 */
642 { true, 6, 5 }, /* subedge 2 of edge 3 */
643 { true, 6, 4 }, /* subedge 3 of edge 3 */
644 { true, 6, 3 }, /* subedge 0 of edge 4 */
645 { true, 7, 5 }, /* subedge 1 of edge 4 */
646 { true, 7, 4 }, /* subedge 2 of edge 4 */
647 { true, 7, 3 }, /* subedge 3 of edge 4 */
648 { true, 7, 2 }, /* subedge 0 of edge 5 */
649 { true, 5, 3 }, /* subedge 1 of edge 5 */
650 { true, 4, 5 }, /* subedge 2 of edge 5 */
651 { true, 4, 4 }, /* subedge 3 of edge 5 */
652 { true, 4, 3 }, /* subedge 4 of edge 5 */
653};
654static const struct MapEntry hexmap_Y[] = {
655 { true, 2, 5 }, /* edge 0 of hex 0 (F) */
656 { true, 3, 5 }, /* edge 1 of hex 0 (F) */
657 { true, 1, 0 }, /* edge 2 of hex 0 (F) */
658 { false, 2, 0 }, /* edge 3 of hex 0 (F) */
659 { false, 1, 2 }, /* edge 4 of hex 0 (F) */
660 { false, 1, 1 }, /* edge 5 of hex 0 (F) */
661 { true, 0, 2 }, /* edge 0 of hex 1 (Y) */
662 { true, 3, 4 }, /* edge 1 of hex 1 (Y) */
663 { false, 3, 0 }, /* edge 2 of hex 1 (Y) */
664 { false, 2, 3 }, /* edge 3 of hex 1 (Y) */
665 { false, 2, 2 }, /* edge 4 of hex 1 (Y) */
666 { false, 2, 1 }, /* edge 5 of hex 1 (Y) */
667 { false, 1, 0 }, /* edge 0 of hex 2 (G) */
668 { false, 0, 1 }, /* edge 1 of hex 2 (G) */
669 { true, 4, 1 }, /* edge 2 of hex 2 (G) */
670 { true, 5, 1 }, /* edge 3 of hex 2 (G) */
671 { true, 3, 0 }, /* edge 4 of hex 2 (G) */
672 { true, 0, 0 }, /* edge 5 of hex 2 (G) */
673 { true, 2, 4 }, /* edge 0 of hex 3 (S) */
674 { true, 5, 0 }, /* edge 1 of hex 3 (S) */
675 { true, 6, 0 }, /* edge 2 of hex 3 (S) */
676 { false, 3, 1 }, /* edge 3 of hex 3 (S) */
677 { true, 1, 1 }, /* edge 4 of hex 3 (S) */
678 { true, 0, 1 }, /* edge 5 of hex 3 (S) */
679 { true, 5, 2 }, /* edge 0 of hex 4 (Y) */
680 { true, 2, 2 }, /* edge 1 of hex 4 (Y) */
681 { false, 0, 0 }, /* edge 2 of hex 4 (Y) */
682 { false, 5, 4 }, /* edge 3 of hex 4 (Y) */
683 { false, 5, 3 }, /* edge 4 of hex 4 (Y) */
684 { false, 5, 2 }, /* edge 5 of hex 4 (Y) */
685 { true, 3, 1 }, /* edge 0 of hex 5 (D) */
686 { true, 2, 3 }, /* edge 1 of hex 5 (D) */
687 { true, 4, 0 }, /* edge 2 of hex 5 (D) */
688 { false, 5, 1 }, /* edge 3 of hex 5 (D) */
689 { true, 7, 1 }, /* edge 4 of hex 5 (D) */
690 { true, 6, 1 }, /* edge 5 of hex 5 (D) */
691 { true, 3, 2 }, /* edge 0 of hex 6 (F) */
692 { true, 5, 5 }, /* edge 1 of hex 6 (F) */
693 { true, 7, 0 }, /* edge 2 of hex 6 (F) */
694 { false, 4, 0 }, /* edge 3 of hex 6 (F) */
695 { false, 3, 3 }, /* edge 4 of hex 6 (F) */
696 { false, 3, 2 }, /* edge 5 of hex 6 (F) */
697 { true, 6, 2 }, /* edge 0 of hex 7 (Y) */
698 { true, 5, 4 }, /* edge 1 of hex 7 (Y) */
699 { false, 5, 0 }, /* edge 2 of hex 7 (Y) */
700 { false, 4, 3 }, /* edge 3 of hex 7 (Y) */
701 { false, 4, 2 }, /* edge 4 of hex 7 (Y) */
702 { false, 4, 1 }, /* edge 5 of hex 7 (Y) */
703};
704static const struct MapEdge hexedges_Y[] = {
705 { 0, 2 },
706 { 2, 3 },
707 { 5, 4 },
708 { 9, 4 },
709 { 13, 4 },
710 { 17, 5 },
711};
712static const struct MapEntry hexin_Y[] = {
713 { true, 4, 2 }, /* subedge 0 of edge 0 */
714 { true, 2, 1 }, /* subedge 1 of edge 0 */
715 { true, 2, 0 }, /* subedge 0 of edge 1 */
716 { true, 0, 5 }, /* subedge 1 of edge 1 */
717 { true, 0, 4 }, /* subedge 2 of edge 1 */
718 { true, 0, 3 }, /* subedge 0 of edge 2 */
719 { true, 1, 5 }, /* subedge 1 of edge 2 */
720 { true, 1, 4 }, /* subedge 2 of edge 2 */
721 { true, 1, 3 }, /* subedge 3 of edge 2 */
722 { true, 1, 2 }, /* subedge 0 of edge 3 */
723 { true, 3, 3 }, /* subedge 1 of edge 3 */
724 { true, 6, 5 }, /* subedge 2 of edge 3 */
725 { true, 6, 4 }, /* subedge 3 of edge 3 */
726 { true, 6, 3 }, /* subedge 0 of edge 4 */
727 { true, 7, 5 }, /* subedge 1 of edge 4 */
728 { true, 7, 4 }, /* subedge 2 of edge 4 */
729 { true, 7, 3 }, /* subedge 3 of edge 4 */
730 { true, 7, 2 }, /* subedge 0 of edge 5 */
731 { true, 5, 3 }, /* subedge 1 of edge 5 */
732 { true, 4, 5 }, /* subedge 2 of edge 5 */
733 { true, 4, 4 }, /* subedge 3 of edge 5 */
734 { true, 4, 3 }, /* subedge 4 of edge 5 */
735};
736static const struct MapEntry specmap_G[] = {
737 { false, 2, 2 }, /* edge 0 of Spectre 0 */
738 { false, 2, 1 }, /* edge 1 of Spectre 0 */
739 { false, 2, 0 }, /* edge 2 of Spectre 0 */
740 { false, 1, 2 }, /* edge 3 of Spectre 0 */
741 { false, 1, 1 }, /* edge 4 of Spectre 0 */
742 { false, 1, 0 }, /* edge 5 of Spectre 0 */
743 { false, 0, 2 }, /* edge 6 of Spectre 0 */
744 { false, 0, 1 }, /* edge 7 of Spectre 0 */
745 { false, 0, 0 }, /* edge 8 of Spectre 0 */
746 { false, 5, 2 }, /* edge 9 of Spectre 0 */
747 { true, 1, 7 }, /* edge 10 of Spectre 0 */
748 { true, 1, 6 }, /* edge 11 of Spectre 0 */
749 { true, 1, 5 }, /* edge 12 of Spectre 0 */
750 { true, 1, 4 }, /* edge 13 of Spectre 0 */
751 { false, 4, 1 }, /* edge 0 of Spectre 1 */
752 { false, 4, 0 }, /* edge 1 of Spectre 1 */
753 { false, 3, 1 }, /* edge 2 of Spectre 1 */
754 { false, 3, 0 }, /* edge 3 of Spectre 1 */
755 { true, 0, 13 }, /* edge 4 of Spectre 1 */
756 { true, 0, 12 }, /* edge 5 of Spectre 1 */
757 { true, 0, 11 }, /* edge 6 of Spectre 1 */
758 { true, 0, 10 }, /* edge 7 of Spectre 1 */
759 { false, 5, 1 }, /* edge 8 of Spectre 1 */
760 { false, 5, 0 }, /* edge 9 of Spectre 1 */
761 { false, 4, 5 }, /* edge 10 of Spectre 1 */
762 { false, 4, 4 }, /* edge 11 of Spectre 1 */
763 { false, 4, 3 }, /* edge 12 of Spectre 1 */
764 { false, 4, 2 }, /* edge 13 of Spectre 1 */
765};
766static const struct MapEdge specedges_G[] = {
767 { 0, 3 },
768 { 3, 3 },
769 { 6, 3 },
770 { 9, 2 },
771 { 11, 6 },
772 { 17, 3 },
773};
774static const struct MapEntry specin_G[] = {
775 { true, 0, 8 }, /* subedge 0 of edge 0 */
776 { true, 0, 7 }, /* subedge 1 of edge 0 */
777 { true, 0, 6 }, /* subedge 2 of edge 0 */
778 { true, 0, 5 }, /* subedge 0 of edge 1 */
779 { true, 0, 4 }, /* subedge 1 of edge 1 */
780 { true, 0, 3 }, /* subedge 2 of edge 1 */
781 { true, 0, 2 }, /* subedge 0 of edge 2 */
782 { true, 0, 1 }, /* subedge 1 of edge 2 */
783 { true, 0, 0 }, /* subedge 2 of edge 2 */
784 { true, 1, 3 }, /* subedge 0 of edge 3 */
785 { true, 1, 2 }, /* subedge 1 of edge 3 */
786 { true, 1, 1 }, /* subedge 0 of edge 4 */
787 { true, 1, 0 }, /* subedge 1 of edge 4 */
788 { true, 1, 13 }, /* subedge 2 of edge 4 */
789 { true, 1, 12 }, /* subedge 3 of edge 4 */
790 { true, 1, 11 }, /* subedge 4 of edge 4 */
791 { true, 1, 10 }, /* subedge 5 of edge 4 */
792 { true, 1, 9 }, /* subedge 0 of edge 5 */
793 { true, 1, 8 }, /* subedge 1 of edge 5 */
794 { true, 0, 9 }, /* subedge 2 of edge 5 */
795};
796static const struct MapEntry specmap_D[] = {
797 { false, 3, 0 }, /* edge 0 of Spectre 0 */
798 { false, 2, 2 }, /* edge 1 of Spectre 0 */
799 { false, 2, 1 }, /* edge 2 of Spectre 0 */
800 { false, 2, 0 }, /* edge 3 of Spectre 0 */
801 { false, 1, 1 }, /* edge 4 of Spectre 0 */
802 { false, 1, 0 }, /* edge 5 of Spectre 0 */
803 { false, 0, 1 }, /* edge 6 of Spectre 0 */
804 { false, 0, 0 }, /* edge 7 of Spectre 0 */
805 { false, 5, 1 }, /* edge 8 of Spectre 0 */
806 { false, 5, 0 }, /* edge 9 of Spectre 0 */
807 { false, 4, 2 }, /* edge 10 of Spectre 0 */
808 { false, 4, 1 }, /* edge 11 of Spectre 0 */
809 { false, 4, 0 }, /* edge 12 of Spectre 0 */
810 { false, 3, 1 }, /* edge 13 of Spectre 0 */
811};
812static const struct MapEdge specedges_D[] = {
813 { 0, 2 },
814 { 2, 2 },
815 { 4, 3 },
816 { 7, 2 },
817 { 9, 3 },
818 { 12, 2 },
819};
820static const struct MapEntry specin_D[] = {
821 { true, 0, 7 }, /* subedge 0 of edge 0 */
822 { true, 0, 6 }, /* subedge 1 of edge 0 */
823 { true, 0, 5 }, /* subedge 0 of edge 1 */
824 { true, 0, 4 }, /* subedge 1 of edge 1 */
825 { true, 0, 3 }, /* subedge 0 of edge 2 */
826 { true, 0, 2 }, /* subedge 1 of edge 2 */
827 { true, 0, 1 }, /* subedge 2 of edge 2 */
828 { true, 0, 0 }, /* subedge 0 of edge 3 */
829 { true, 0, 13 }, /* subedge 1 of edge 3 */
830 { true, 0, 12 }, /* subedge 0 of edge 4 */
831 { true, 0, 11 }, /* subedge 1 of edge 4 */
832 { true, 0, 10 }, /* subedge 2 of edge 4 */
833 { true, 0, 9 }, /* subedge 0 of edge 5 */
834 { true, 0, 8 }, /* subedge 1 of edge 5 */
835};
836static const struct MapEntry specmap_J[] = {
837 { false, 3, 0 }, /* edge 0 of Spectre 0 */
838 { false, 2, 2 }, /* edge 1 of Spectre 0 */
839 { false, 2, 1 }, /* edge 2 of Spectre 0 */
840 { false, 2, 0 }, /* edge 3 of Spectre 0 */
841 { false, 1, 1 }, /* edge 4 of Spectre 0 */
842 { false, 1, 0 }, /* edge 5 of Spectre 0 */
843 { false, 0, 2 }, /* edge 6 of Spectre 0 */
844 { false, 0, 1 }, /* edge 7 of Spectre 0 */
845 { false, 0, 0 }, /* edge 8 of Spectre 0 */
846 { false, 5, 1 }, /* edge 9 of Spectre 0 */
847 { false, 5, 0 }, /* edge 10 of Spectre 0 */
848 { false, 4, 2 }, /* edge 11 of Spectre 0 */
849 { false, 4, 1 }, /* edge 12 of Spectre 0 */
850 { false, 4, 0 }, /* edge 13 of Spectre 0 */
851};
852static const struct MapEdge specedges_J[] = {
853 { 0, 3 },
854 { 3, 2 },
855 { 5, 3 },
856 { 8, 1 },
857 { 9, 3 },
858 { 12, 2 },
859};
860static const struct MapEntry specin_J[] = {
861 { true, 0, 8 }, /* subedge 0 of edge 0 */
862 { true, 0, 7 }, /* subedge 1 of edge 0 */
863 { true, 0, 6 }, /* subedge 2 of edge 0 */
864 { true, 0, 5 }, /* subedge 0 of edge 1 */
865 { true, 0, 4 }, /* subedge 1 of edge 1 */
866 { true, 0, 3 }, /* subedge 0 of edge 2 */
867 { true, 0, 2 }, /* subedge 1 of edge 2 */
868 { true, 0, 1 }, /* subedge 2 of edge 2 */
869 { true, 0, 0 }, /* subedge 0 of edge 3 */
870 { true, 0, 13 }, /* subedge 0 of edge 4 */
871 { true, 0, 12 }, /* subedge 1 of edge 4 */
872 { true, 0, 11 }, /* subedge 2 of edge 4 */
873 { true, 0, 10 }, /* subedge 0 of edge 5 */
874 { true, 0, 9 }, /* subedge 1 of edge 5 */
875};
876static const struct MapEntry specmap_L[] = {
877 { false, 3, 0 }, /* edge 0 of Spectre 0 */
878 { false, 2, 2 }, /* edge 1 of Spectre 0 */
879 { false, 2, 1 }, /* edge 2 of Spectre 0 */
880 { false, 2, 0 }, /* edge 3 of Spectre 0 */
881 { false, 1, 1 }, /* edge 4 of Spectre 0 */
882 { false, 1, 0 }, /* edge 5 of Spectre 0 */
883 { false, 0, 2 }, /* edge 6 of Spectre 0 */
884 { false, 0, 1 }, /* edge 7 of Spectre 0 */
885 { false, 0, 0 }, /* edge 8 of Spectre 0 */
886 { false, 5, 0 }, /* edge 9 of Spectre 0 */
887 { false, 4, 2 }, /* edge 10 of Spectre 0 */
888 { false, 4, 1 }, /* edge 11 of Spectre 0 */
889 { false, 4, 0 }, /* edge 12 of Spectre 0 */
890 { false, 3, 1 }, /* edge 13 of Spectre 0 */
891};
892static const struct MapEdge specedges_L[] = {
893 { 0, 3 },
894 { 3, 2 },
895 { 5, 3 },
896 { 8, 2 },
897 { 10, 3 },
898 { 13, 1 },
899};
900static const struct MapEntry specin_L[] = {
901 { true, 0, 8 }, /* subedge 0 of edge 0 */
902 { true, 0, 7 }, /* subedge 1 of edge 0 */
903 { true, 0, 6 }, /* subedge 2 of edge 0 */
904 { true, 0, 5 }, /* subedge 0 of edge 1 */
905 { true, 0, 4 }, /* subedge 1 of edge 1 */
906 { true, 0, 3 }, /* subedge 0 of edge 2 */
907 { true, 0, 2 }, /* subedge 1 of edge 2 */
908 { true, 0, 1 }, /* subedge 2 of edge 2 */
909 { true, 0, 0 }, /* subedge 0 of edge 3 */
910 { true, 0, 13 }, /* subedge 1 of edge 3 */
911 { true, 0, 12 }, /* subedge 0 of edge 4 */
912 { true, 0, 11 }, /* subedge 1 of edge 4 */
913 { true, 0, 10 }, /* subedge 2 of edge 4 */
914 { true, 0, 9 }, /* subedge 0 of edge 5 */
915};
916static const struct MapEntry specmap_X[] = {
917 { false, 3, 0 }, /* edge 0 of Spectre 0 */
918 { false, 2, 1 }, /* edge 1 of Spectre 0 */
919 { false, 2, 0 }, /* edge 2 of Spectre 0 */
920 { false, 1, 2 }, /* edge 3 of Spectre 0 */
921 { false, 1, 1 }, /* edge 4 of Spectre 0 */
922 { false, 1, 0 }, /* edge 5 of Spectre 0 */
923 { false, 0, 2 }, /* edge 6 of Spectre 0 */
924 { false, 0, 1 }, /* edge 7 of Spectre 0 */
925 { false, 0, 0 }, /* edge 8 of Spectre 0 */
926 { false, 5, 1 }, /* edge 9 of Spectre 0 */
927 { false, 5, 0 }, /* edge 10 of Spectre 0 */
928 { false, 4, 2 }, /* edge 11 of Spectre 0 */
929 { false, 4, 1 }, /* edge 12 of Spectre 0 */
930 { false, 4, 0 }, /* edge 13 of Spectre 0 */
931};
932static const struct MapEdge specedges_X[] = {
933 { 0, 3 },
934 { 3, 3 },
935 { 6, 2 },
936 { 8, 1 },
937 { 9, 3 },
938 { 12, 2 },
939};
940static const struct MapEntry specin_X[] = {
941 { true, 0, 8 }, /* subedge 0 of edge 0 */
942 { true, 0, 7 }, /* subedge 1 of edge 0 */
943 { true, 0, 6 }, /* subedge 2 of edge 0 */
944 { true, 0, 5 }, /* subedge 0 of edge 1 */
945 { true, 0, 4 }, /* subedge 1 of edge 1 */
946 { true, 0, 3 }, /* subedge 2 of edge 1 */
947 { true, 0, 2 }, /* subedge 0 of edge 2 */
948 { true, 0, 1 }, /* subedge 1 of edge 2 */
949 { true, 0, 0 }, /* subedge 0 of edge 3 */
950 { true, 0, 13 }, /* subedge 0 of edge 4 */
951 { true, 0, 12 }, /* subedge 1 of edge 4 */
952 { true, 0, 11 }, /* subedge 2 of edge 4 */
953 { true, 0, 10 }, /* subedge 0 of edge 5 */
954 { true, 0, 9 }, /* subedge 1 of edge 5 */
955};
956static const struct MapEntry specmap_P[] = {
957 { false, 3, 0 }, /* edge 0 of Spectre 0 */
958 { false, 2, 1 }, /* edge 1 of Spectre 0 */
959 { false, 2, 0 }, /* edge 2 of Spectre 0 */
960 { false, 1, 2 }, /* edge 3 of Spectre 0 */
961 { false, 1, 1 }, /* edge 4 of Spectre 0 */
962 { false, 1, 0 }, /* edge 5 of Spectre 0 */
963 { false, 0, 2 }, /* edge 6 of Spectre 0 */
964 { false, 0, 1 }, /* edge 7 of Spectre 0 */
965 { false, 0, 0 }, /* edge 8 of Spectre 0 */
966 { false, 5, 0 }, /* edge 9 of Spectre 0 */
967 { false, 4, 2 }, /* edge 10 of Spectre 0 */
968 { false, 4, 1 }, /* edge 11 of Spectre 0 */
969 { false, 4, 0 }, /* edge 12 of Spectre 0 */
970 { false, 3, 1 }, /* edge 13 of Spectre 0 */
971};
972static const struct MapEdge specedges_P[] = {
973 { 0, 3 },
974 { 3, 3 },
975 { 6, 2 },
976 { 8, 2 },
977 { 10, 3 },
978 { 13, 1 },
979};
980static const struct MapEntry specin_P[] = {
981 { true, 0, 8 }, /* subedge 0 of edge 0 */
982 { true, 0, 7 }, /* subedge 1 of edge 0 */
983 { true, 0, 6 }, /* subedge 2 of edge 0 */
984 { true, 0, 5 }, /* subedge 0 of edge 1 */
985 { true, 0, 4 }, /* subedge 1 of edge 1 */
986 { true, 0, 3 }, /* subedge 2 of edge 1 */
987 { true, 0, 2 }, /* subedge 0 of edge 2 */
988 { true, 0, 1 }, /* subedge 1 of edge 2 */
989 { true, 0, 0 }, /* subedge 0 of edge 3 */
990 { true, 0, 13 }, /* subedge 1 of edge 3 */
991 { true, 0, 12 }, /* subedge 0 of edge 4 */
992 { true, 0, 11 }, /* subedge 1 of edge 4 */
993 { true, 0, 10 }, /* subedge 2 of edge 4 */
994 { true, 0, 9 }, /* subedge 0 of edge 5 */
995};
996static const struct MapEntry specmap_S[] = {
997 { false, 3, 0 }, /* edge 0 of Spectre 0 */
998 { false, 2, 2 }, /* edge 1 of Spectre 0 */
999 { false, 2, 1 }, /* edge 2 of Spectre 0 */
1000 { false, 2, 0 }, /* edge 3 of Spectre 0 */
1001 { false, 0, 3 }, /* edge 4 of Spectre 0 */
1002 { false, 0, 2 }, /* edge 5 of Spectre 0 */
1003 { false, 0, 1 }, /* edge 6 of Spectre 0 */
1004 { false, 0, 0 }, /* edge 7 of Spectre 0 */
1005 { false, 5, 1 }, /* edge 8 of Spectre 0 */
1006 { false, 5, 0 }, /* edge 9 of Spectre 0 */
1007 { false, 4, 2 }, /* edge 10 of Spectre 0 */
1008 { false, 4, 1 }, /* edge 11 of Spectre 0 */
1009 { false, 4, 0 }, /* edge 12 of Spectre 0 */
1010 { false, 3, 1 }, /* edge 13 of Spectre 0 */
1011};
1012static const struct MapEdge specedges_S[] = {
1013 { 0, 6 },
1014 { 6, 2 },
1015 { 8, 3 },
1016 { 11, 2 },
1017 { 13, 3 },
1018 { 16, 2 },
1019};
1020static const struct MapEntry specin_S[] = {
1021 { true, 0, 7 }, /* subedge 0 of edge 0 */
1022 { true, 0, 6 }, /* subedge 1 of edge 0 */
1023 { true, 0, 5 }, /* subedge 2 of edge 0 */
1024 { true, 0, 4 }, /* subedge 3 of edge 0 */
1025 { false, 1, 1 }, /* subedge 4 of edge 0 */
1026 { false, 1, 0 }, /* subedge 5 of edge 0 */
1027 { false, 0, 5 }, /* subedge 0 of edge 1 */
1028 { false, 0, 4 }, /* subedge 1 of edge 1 */
1029 { true, 0, 3 }, /* subedge 0 of edge 2 */
1030 { true, 0, 2 }, /* subedge 1 of edge 2 */
1031 { true, 0, 1 }, /* subedge 2 of edge 2 */
1032 { true, 0, 0 }, /* subedge 0 of edge 3 */
1033 { true, 0, 13 }, /* subedge 1 of edge 3 */
1034 { true, 0, 12 }, /* subedge 0 of edge 4 */
1035 { true, 0, 11 }, /* subedge 1 of edge 4 */
1036 { true, 0, 10 }, /* subedge 2 of edge 4 */
1037 { true, 0, 9 }, /* subedge 0 of edge 5 */
1038 { true, 0, 8 }, /* subedge 1 of edge 5 */
1039};
1040static const struct MapEntry specmap_F[] = {
1041 { false, 3, 0 }, /* edge 0 of Spectre 0 */
1042 { false, 2, 2 }, /* edge 1 of Spectre 0 */
1043 { false, 2, 1 }, /* edge 2 of Spectre 0 */
1044 { false, 2, 0 }, /* edge 3 of Spectre 0 */
1045 { false, 1, 1 }, /* edge 4 of Spectre 0 */
1046 { false, 1, 0 }, /* edge 5 of Spectre 0 */
1047 { false, 0, 2 }, /* edge 6 of Spectre 0 */
1048 { false, 0, 1 }, /* edge 7 of Spectre 0 */
1049 { false, 0, 0 }, /* edge 8 of Spectre 0 */
1050 { false, 5, 1 }, /* edge 9 of Spectre 0 */
1051 { false, 5, 0 }, /* edge 10 of Spectre 0 */
1052 { false, 4, 1 }, /* edge 11 of Spectre 0 */
1053 { false, 4, 0 }, /* edge 12 of Spectre 0 */
1054 { false, 3, 1 }, /* edge 13 of Spectre 0 */
1055};
1056static const struct MapEdge specedges_F[] = {
1057 { 0, 3 },
1058 { 3, 2 },
1059 { 5, 3 },
1060 { 8, 2 },
1061 { 10, 2 },
1062 { 12, 2 },
1063};
1064static const struct MapEntry specin_F[] = {
1065 { true, 0, 8 }, /* subedge 0 of edge 0 */
1066 { true, 0, 7 }, /* subedge 1 of edge 0 */
1067 { true, 0, 6 }, /* subedge 2 of edge 0 */
1068 { true, 0, 5 }, /* subedge 0 of edge 1 */
1069 { true, 0, 4 }, /* subedge 1 of edge 1 */
1070 { true, 0, 3 }, /* subedge 0 of edge 2 */
1071 { true, 0, 2 }, /* subedge 1 of edge 2 */
1072 { true, 0, 1 }, /* subedge 2 of edge 2 */
1073 { true, 0, 0 }, /* subedge 0 of edge 3 */
1074 { true, 0, 13 }, /* subedge 1 of edge 3 */
1075 { true, 0, 12 }, /* subedge 0 of edge 4 */
1076 { true, 0, 11 }, /* subedge 1 of edge 4 */
1077 { true, 0, 10 }, /* subedge 0 of edge 5 */
1078 { true, 0, 9 }, /* subedge 1 of edge 5 */
1079};
1080static const struct MapEntry specmap_Y[] = {
1081 { false, 3, 0 }, /* edge 0 of Spectre 0 */
1082 { false, 2, 1 }, /* edge 1 of Spectre 0 */
1083 { false, 2, 0 }, /* edge 2 of Spectre 0 */
1084 { false, 1, 2 }, /* edge 3 of Spectre 0 */
1085 { false, 1, 1 }, /* edge 4 of Spectre 0 */
1086 { false, 1, 0 }, /* edge 5 of Spectre 0 */
1087 { false, 0, 2 }, /* edge 6 of Spectre 0 */
1088 { false, 0, 1 }, /* edge 7 of Spectre 0 */
1089 { false, 0, 0 }, /* edge 8 of Spectre 0 */
1090 { false, 5, 1 }, /* edge 9 of Spectre 0 */
1091 { false, 5, 0 }, /* edge 10 of Spectre 0 */
1092 { false, 4, 1 }, /* edge 11 of Spectre 0 */
1093 { false, 4, 0 }, /* edge 12 of Spectre 0 */
1094 { false, 3, 1 }, /* edge 13 of Spectre 0 */
1095};
1096static const struct MapEdge specedges_Y[] = {
1097 { 0, 3 },
1098 { 3, 3 },
1099 { 6, 2 },
1100 { 8, 2 },
1101 { 10, 2 },
1102 { 12, 2 },
1103};
1104static const struct MapEntry specin_Y[] = {
1105 { true, 0, 8 }, /* subedge 0 of edge 0 */
1106 { true, 0, 7 }, /* subedge 1 of edge 0 */
1107 { true, 0, 6 }, /* subedge 2 of edge 0 */
1108 { true, 0, 5 }, /* subedge 0 of edge 1 */
1109 { true, 0, 4 }, /* subedge 1 of edge 1 */
1110 { true, 0, 3 }, /* subedge 2 of edge 1 */
1111 { true, 0, 2 }, /* subedge 0 of edge 2 */
1112 { true, 0, 1 }, /* subedge 1 of edge 2 */
1113 { true, 0, 0 }, /* subedge 0 of edge 3 */
1114 { true, 0, 13 }, /* subedge 1 of edge 3 */
1115 { true, 0, 12 }, /* subedge 0 of edge 4 */
1116 { true, 0, 11 }, /* subedge 1 of edge 4 */
1117 { true, 0, 10 }, /* subedge 0 of edge 5 */
1118 { true, 0, 9 }, /* subedge 1 of edge 5 */
1119};
1120static const struct Possibility poss_G[] = {
1121 { HEX_G, 2, PROB_G },
1122 { HEX_D, 2, PROB_D },
1123 { HEX_J, 2, PROB_J },
1124 { HEX_L, 2, PROB_L },
1125 { HEX_X, 2, PROB_X },
1126 { HEX_P, 2, PROB_P },
1127 { HEX_S, 2, PROB_S },
1128 { HEX_F, 2, PROB_F },
1129 { HEX_Y, 2, PROB_Y },
1130};
1131static const struct Possibility poss_D[] = {
1132 { HEX_G, 5, PROB_G },
1133 { HEX_D, 5, PROB_D },
1134 { HEX_J, 5, PROB_J },
1135 { HEX_L, 5, PROB_L },
1136 { HEX_X, 5, PROB_X },
1137 { HEX_P, 5, PROB_P },
1138 { HEX_S, 5, PROB_S },
1139 { HEX_F, 5, PROB_F },
1140 { HEX_Y, 5, PROB_Y },
1141};
1142static const struct Possibility poss_J[] = {
1143 { HEX_G, 6, PROB_G },
1144};
1145static const struct Possibility poss_L[] = {
1146 { HEX_S, 0, PROB_S },
1147};
1148static const struct Possibility poss_X[] = {
1149 { HEX_G, 1, PROB_G },
1150 { HEX_D, 4, PROB_D },
1151 { HEX_D, 7, PROB_D },
1152 { HEX_L, 7, PROB_L },
1153 { HEX_P, 7, PROB_P },
1154 { HEX_S, 4, PROB_S },
1155 { HEX_S, 7, PROB_S },
1156};
1157static const struct Possibility poss_P[] = {
1158 { HEX_G, 4, PROB_G },
1159 { HEX_D, 1, PROB_D },
1160 { HEX_J, 1, PROB_J },
1161 { HEX_J, 7, PROB_J },
1162 { HEX_L, 1, PROB_L },
1163 { HEX_X, 7, PROB_X },
1164 { HEX_S, 1, PROB_S },
1165 { HEX_F, 1, PROB_F },
1166};
1167static const struct Possibility poss_S[] = {
1168 { HEX_G, 3, PROB_G },
1169 { HEX_D, 3, PROB_D },
1170 { HEX_J, 3, PROB_J },
1171 { HEX_L, 3, PROB_L },
1172 { HEX_X, 3, PROB_X },
1173 { HEX_P, 3, PROB_P },
1174 { HEX_S, 3, PROB_S },
1175 { HEX_F, 3, PROB_F },
1176 { HEX_Y, 3, PROB_Y },
1177};
1178static const struct Possibility poss_F[] = {
1179 { HEX_G, 0, PROB_G },
1180 { HEX_D, 0, PROB_D },
1181 { HEX_D, 6, PROB_D },
1182 { HEX_J, 0, PROB_J },
1183 { HEX_J, 6, PROB_J },
1184 { HEX_L, 0, PROB_L },
1185 { HEX_L, 6, PROB_L },
1186 { HEX_X, 0, PROB_X },
1187 { HEX_X, 6, PROB_X },
1188 { HEX_P, 0, PROB_P },
1189 { HEX_P, 6, PROB_P },
1190 { HEX_S, 6, PROB_S },
1191 { HEX_F, 0, PROB_F },
1192 { HEX_F, 6, PROB_F },
1193 { HEX_Y, 0, PROB_Y },
1194 { HEX_Y, 6, PROB_Y },
1195};
1196static const struct Possibility poss_Y[] = {
1197 { HEX_J, 4, PROB_J },
1198 { HEX_L, 4, PROB_L },
1199 { HEX_X, 1, PROB_X },
1200 { HEX_X, 4, PROB_X },
1201 { HEX_P, 1, PROB_P },
1202 { HEX_P, 4, PROB_P },
1203 { HEX_F, 4, PROB_F },
1204 { HEX_F, 7, PROB_F },
1205 { HEX_Y, 1, PROB_Y },
1206 { HEX_Y, 4, PROB_Y },
1207 { HEX_Y, 7, PROB_Y },
1208};
1209static const struct Possibility poss_spectre[] = {
1210 { HEX_G, 0, PROB_G },
1211 { HEX_G, 1, PROB_G },
1212 { HEX_D, 0, PROB_D },
1213 { HEX_J, 0, PROB_J },
1214 { HEX_L, 0, PROB_L },
1215 { HEX_X, 0, PROB_X },
1216 { HEX_P, 0, PROB_P },
1217 { HEX_S, 0, PROB_S },
1218 { HEX_F, 0, PROB_F },
1219 { HEX_Y, 0, PROB_Y },
1220};
diff --git a/apps/plugins/puzzles/src/spectre-tables-manual.h b/apps/plugins/puzzles/src/spectre-tables-manual.h
new file mode 100644
index 0000000000..d57e6597dd
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre-tables-manual.h
@@ -0,0 +1,160 @@
1/*
2 * Handwritten data tables for the Spectre tiling.
3 *
4 * This file is used by both the final tiling generator in spectre.c,
5 * and by spectre-gen.c which auto-generates further tables to go with
6 * these.
7 */
8
9/*
10 * We generate the Spectre tiling based on the substitution system of
11 * 9 types of marked hexagon shown in the paper.
12 *
13 * The substitution expands each hexagon into 8 others, except for the
14 * G hex which expands to only seven. The layout, numbered with the
15 * indices we use in the arrays here, is as follows:
16 *
17 * 0 1
18 * 2 3
19 * 4 5 6
20 * 7
21 *
22 * That is: the hexes are oriented with a pair of vertical edges.
23 * Hexes 0 and 1 are horizontally adjacent; 2 and 3 are adjacent on
24 * the next row, with 3 nestling between 0 and 1; 4,5,6 are on the
25 * third row with 5 between 2 and 3; and 7 is by itself on a fourth
26 * row, between 5 and 6. In the expansion of the G hex, #7 is the
27 * missing one, so its indices are still consecutive from 0.
28 *
29 * These arrays list the type of each child hex. The hexes also have
30 * orientations, but those aren't listed here, because only
31 * spectre-gen needs to know them - by the time it's finished
32 * autogenerating transition tables, the orientations are baked into
33 * those and don't need to be consulted separately.
34 */
35
36static const Hex subhexes_G[] = {
37 HEX_F,
38 HEX_X,
39 HEX_G,
40 HEX_S,
41 HEX_P,
42 HEX_D,
43 HEX_J,
44 /* hex #7 is not present for this tile */
45};
46static const Hex subhexes_D[] = {
47 HEX_F,
48 HEX_P,
49 HEX_G,
50 HEX_S,
51 HEX_X,
52 HEX_D,
53 HEX_F,
54 HEX_X,
55};
56static const Hex subhexes_J[] = {
57 HEX_F,
58 HEX_P,
59 HEX_G,
60 HEX_S,
61 HEX_Y,
62 HEX_D,
63 HEX_F,
64 HEX_P,
65};
66static const Hex subhexes_L[] = {
67 HEX_F,
68 HEX_P,
69 HEX_G,
70 HEX_S,
71 HEX_Y,
72 HEX_D,
73 HEX_F,
74 HEX_X,
75};
76static const Hex subhexes_X[] = {
77 HEX_F,
78 HEX_Y,
79 HEX_G,
80 HEX_S,
81 HEX_Y,
82 HEX_D,
83 HEX_F,
84 HEX_P,
85};
86static const Hex subhexes_P[] = {
87 HEX_F,
88 HEX_Y,
89 HEX_G,
90 HEX_S,
91 HEX_Y,
92 HEX_D,
93 HEX_F,
94 HEX_X,
95};
96static const Hex subhexes_S[] = {
97 HEX_L,
98 HEX_P,
99 HEX_G,
100 HEX_S,
101 HEX_X,
102 HEX_D,
103 HEX_F,
104 HEX_X,
105};
106static const Hex subhexes_F[] = {
107 HEX_F,
108 HEX_P,
109 HEX_G,
110 HEX_S,
111 HEX_Y,
112 HEX_D,
113 HEX_F,
114 HEX_Y,
115};
116static const Hex subhexes_Y[] = {
117 HEX_F,
118 HEX_Y,
119 HEX_G,
120 HEX_S,
121 HEX_Y,
122 HEX_D,
123 HEX_F,
124 HEX_Y,
125};
126
127/*
128 * Shape of the Spectre itself.
129 *
130 * Vertex 0 is taken to be at the top of the Spectre's "head"; vertex
131 * 1 is the adjacent vertex, in the direction of the shorter edge of
132 * its "cloak".
133 *
134 * This array indicates how far to turn at each vertex, in 1/12 turns.
135 * All edges are the same length (counting the double-edge as two
136 * edges, which we do).
137 */
138static const int spectre_angles[14] = {
139 -3, -2, 3, -2, -3, 2, -3, 2, -3, -2, 0, -2, 3, -2,
140};
141
142/*
143 * The relative probabilities of the nine hex types, in the limit, as
144 * the expansion process is iterated more and more times. Used to
145 * choose the initial hex coordinates as if the segment of tiling came
146 * from the limiting distribution across the whole plane.
147 *
148 * This is obtained by finding the matrix that says how many hexes of
149 * each type are expanded from each starting hex, and taking the
150 * eigenvector that goes with its limiting eigenvalue.
151 */
152#define PROB_G 10000000 /* 1 */
153#define PROB_D 10000000 /* 1 */
154#define PROB_J 1270167 /* 4 - sqrt(15) */
155#define PROB_L 1270167 /* 4 - sqrt(15) */
156#define PROB_X 7459667 /* 2 sqrt(15) - 7 */
157#define PROB_P 7459667 /* 2 sqrt(15) - 7 */
158#define PROB_S 10000000 /* 1 */
159#define PROB_F 17459667 /* 2 sqrt(15) - 6 */
160#define PROB_Y 13810500 /* 13 - 3 sqrt(15) */
diff --git a/apps/plugins/puzzles/src/spectre.c b/apps/plugins/puzzles/src/spectre.c
new file mode 100644
index 0000000000..ba72304ff2
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre.c
@@ -0,0 +1,599 @@
1/*
2 * Code to generate patches of the aperiodic 'spectre' tiling
3 * discovered in 2023.
4 *
5 * Resources about the tiling from its discoverers:
6 * https://cs.uwaterloo.ca/~csk/spectre/
7 * https://arxiv.org/abs/2305.17743
8 *
9 * Writeup of the generation algorithm:
10 * https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-spectre/
11 */
12
13#include <assert.h>
14#include <string.h>
15
16#include "puzzles.h"
17#include "tree234.h"
18
19#include "spectre-internal.h"
20
21#include "spectre-tables-manual.h"
22#include "spectre-tables-auto.h"
23
24static const char *const letters =
25 #define STRINGIFY(x) #x
26 HEX_LETTERS(STRINGIFY)
27 #undef STRINGIFY
28 ;
29
30bool spectre_valid_hex_letter(char letter)
31{
32 return strchr(letters, letter) != NULL;
33}
34
35static Hex hex_from_letter(char letter)
36{
37 char buf[2];
38 buf[0] = letter;
39 buf[1] = '\0';
40 return strcspn(letters, buf);
41}
42
43static Hex hex_to_letter(unsigned char letter)
44{
45 return letters[letter];
46}
47
48struct HexData {
49 const struct MapEntry *hexmap, *hexin, *specmap, *specin;
50 const struct MapEdge *hexedges, *specedges;
51 const Hex *subhexes;
52 const struct Possibility *poss;
53 size_t nposs;
54};
55
56static const struct HexData hexdata[] = {
57 #define HEXDATA_ENTRY(x) { hexmap_##x, hexin_##x, specmap_##x, \
58 specin_##x, hexedges_##x, specedges_##x, subhexes_##x, \
59 poss_##x, lenof(poss_##x) },
60 HEX_LETTERS(HEXDATA_ENTRY)
61 #undef HEXDATA_ENTRY
62};
63
64static const struct Possibility *choose_poss(
65 random_state *rs, const struct Possibility *poss, size_t nposs)
66{
67 /*
68 * If we needed to do this _efficiently_, we'd rewrite all those
69 * tables above as cumulative frequency tables and use binary
70 * search. But this happens about log n times in a grid of area n,
71 * so it hardly matters, and it's easier to keep the tables
72 * legible.
73 */
74 unsigned long limit = 0, value;
75 size_t i;
76
77 for (i = 0; i < nposs; i++)
78 limit += poss[i].prob;
79
80 value = random_upto(rs, limit);
81
82 for (i = 0; i+1 < nposs; i++) {
83 if (value < poss[i].prob)
84 return &poss[i];
85 value -= poss[i].prob;
86 }
87
88 assert(i == nposs - 1);
89 assert(value < poss[i].prob);
90 return &poss[i];
91}
92
93SpectreCoords *spectre_coords_new(void)
94{
95 SpectreCoords *sc = snew(SpectreCoords);
96 sc->nc = sc->csize = 0;
97 sc->c = NULL;
98 return sc;
99}
100
101void spectre_coords_free(SpectreCoords *sc)
102{
103 if (sc) {
104 sfree(sc->c);
105 sfree(sc);
106 }
107}
108
109void spectre_coords_make_space(SpectreCoords *sc, size_t size)
110{
111 if (sc->csize < size) {
112 sc->csize = sc->csize * 5 / 4 + 16;
113 if (sc->csize < size)
114 sc->csize = size;
115 sc->c = sresize(sc->c, sc->csize, HexCoord);
116 }
117}
118
119SpectreCoords *spectre_coords_copy(SpectreCoords *sc_in)
120{
121 SpectreCoords *sc_out = spectre_coords_new();
122 spectre_coords_make_space(sc_out, sc_in->nc);
123 memcpy(sc_out->c, sc_in->c, sc_in->nc * sizeof(*sc_out->c));
124 sc_out->nc = sc_in->nc;
125 sc_out->index = sc_in->index;
126 sc_out->hex_colour = sc_in->hex_colour;
127 sc_out->prev_hex_colour = sc_in->prev_hex_colour;
128 sc_out->incoming_hex_edge = sc_in->incoming_hex_edge;
129 return sc_out;
130}
131
132void spectre_place(Spectre *spec, Point u, Point v, int index_of_u)
133{
134 size_t i;
135 Point disp;
136
137 /* Vector from u to v */
138 disp = point_sub(v, u);
139
140 for (i = 0; i < 14; i++) {
141 spec->vertices[(i + index_of_u) % 14] = u;
142 u = point_add(u, disp);
143 disp = point_mul(disp, point_rot(
144 spectre_angles[(i + 1 + index_of_u) % 14]));
145 }
146}
147
148Spectre *spectre_initial(SpectreContext *ctx)
149{
150 Spectre *spec = snew(Spectre);
151 spectre_place(spec, ctx->start_vertices[0], ctx->start_vertices[1], 0);
152 spec->sc = spectre_coords_copy(ctx->prototype);
153 return spec;
154}
155
156Spectre *spectre_adjacent(SpectreContext *ctx, const Spectre *src_spec,
157 unsigned src_edge, unsigned *dst_edge_out)
158{
159 unsigned dst_edge;
160 Spectre *dst_spec = snew(Spectre);
161 dst_spec->sc = spectre_coords_copy(src_spec->sc);
162 spectrectx_step(ctx, dst_spec->sc, src_edge, &dst_edge);
163 spectre_place(dst_spec, src_spec->vertices[(src_edge+1) % 14],
164 src_spec->vertices[src_edge], dst_edge);
165 if (dst_edge_out)
166 *dst_edge_out = dst_edge;
167 return dst_spec;
168}
169
170static int spectre_cmp(void *av, void *bv)
171{
172 Spectre *a = (Spectre *)av, *b = (Spectre *)bv;
173 size_t i, j;
174
175 /* We should only ever need to compare the first two vertices of
176 * any Spectre, because those force the rest */
177 for (i = 0; i < 2; i++) {
178 for (j = 0; j < 4; j++) {
179 int ac = a->vertices[i].coeffs[j], bc = b->vertices[i].coeffs[j];
180 if (ac < bc)
181 return -1;
182 if (ac > bc)
183 return +1;
184 }
185 }
186
187 return 0;
188}
189
190void spectre_free(Spectre *spec)
191{
192 spectre_coords_free(spec->sc);
193 sfree(spec);
194}
195
196static void spectrectx_start_vertices(SpectreContext *ctx, int orientation)
197{
198 Point minus_sqrt3 = point_add(point_rot(5), point_rot(-5));
199 Point basicedge = point_mul(point_add(point_rot(0), point_rot(-3)),
200 point_rot(orientation));
201 Point diagonal = point_add(basicedge, point_mul(basicedge, point_rot(-3)));
202 ctx->start_vertices[0] = point_mul(diagonal, minus_sqrt3);
203 ctx->start_vertices[1] = point_add(ctx->start_vertices[0], basicedge);
204 ctx->orientation = orientation;
205}
206
207void spectrectx_init_random(SpectreContext *ctx, random_state *rs)
208{
209 const struct Possibility *poss;
210
211 ctx->rs = rs;
212 ctx->must_free_rs = false;
213 ctx->prototype = spectre_coords_new();
214 spectre_coords_make_space(ctx->prototype, 1);
215 poss = choose_poss(rs, poss_spectre, lenof(poss_spectre));
216 ctx->prototype->index = poss->lo;
217 ctx->prototype->c[0].type = poss->hi;
218 ctx->prototype->c[0].index = -1;
219 ctx->prototype->nc = 1;
220
221 /*
222 * Choose a random orientation for the starting Spectre.
223 *
224 * The obvious thing is to choose the orientation out of all 12
225 * possibilities. But we do it a more complicated way.
226 *
227 * The Spectres in a tiling can be partitioned into two
228 * equivalence classes under the relation 'orientation differs by
229 * a multiple of 1/6 turn'. One class is much more common than the
230 * other class: the 'odd'-orientation Spectres occur rarely (very
231 * like the rare reflected hats in the hats tiling).
232 *
233 * I think it's nicer to arrange that there's a consistent
234 * orientation for the _common_ class of Spectres, so that there
235 * will always be plenty of them in the 'canonical' orientation
236 * with the head upwards. So if the starting Spectre is in the
237 * even class, we pick an even orientation for it, and if it's in
238 * the odd class, we pick an odd orientation.
239 *
240 * An odd-class Spectre is easy to identify from SpectreCoords.
241 * They're precisely the ones expanded from a G hex with index 1,
242 * which means they're the ones that have index 1 _at all_.
243 */
244 spectrectx_start_vertices(ctx, random_upto(rs, 6) * 2 +
245 ctx->prototype->index);
246
247 /* Initialiise the colouring fields deterministically but unhelpfully.
248 * spectre-test will set these up properly if it wants to */
249 ctx->prototype->hex_colour = 0;
250 ctx->prototype->prev_hex_colour = 0;
251 ctx->prototype->incoming_hex_edge = 0;
252}
253
254void spectrectx_init_from_params(
255 SpectreContext *ctx, const struct SpectrePatchParams *ps)
256{
257 size_t i;
258
259 ctx->rs = NULL;
260 ctx->must_free_rs = false;
261 ctx->prototype = spectre_coords_new();
262 spectre_coords_make_space(ctx->prototype, ps->ncoords);
263
264 ctx->prototype->index = ps->coords[0];
265 for (i = 1; i < ps->ncoords; i++)
266 ctx->prototype->c[i-1].index = ps->coords[i];
267 ctx->prototype->c[ps->ncoords-1].index = -1;
268 ctx->prototype->nc = ps->ncoords;
269
270 ctx->prototype->c[ps->ncoords-1].type = hex_from_letter(ps->final_hex);
271 for (i = ps->ncoords - 1; i-- > 0 ;) {
272 const struct HexData *h = &hexdata[ctx->prototype->c[i+1].type];
273 ctx->prototype->c[i].type = h->subhexes[ctx->prototype->c[i].index];
274 }
275
276 spectrectx_start_vertices(ctx, ps->orientation);
277
278 ctx->prototype->hex_colour = 0;
279 ctx->prototype->prev_hex_colour = 0;
280 ctx->prototype->incoming_hex_edge = 0;
281}
282
283void spectrectx_cleanup(SpectreContext *ctx)
284{
285 if (ctx->must_free_rs)
286 random_free(ctx->rs);
287 spectre_coords_free(ctx->prototype);
288}
289
290SpectreCoords *spectrectx_initial_coords(SpectreContext *ctx)
291{
292 return spectre_coords_copy(ctx->prototype);
293}
294
295/*
296 * Extend sc until it has at least n coordinates in, by copying from
297 * ctx->prototype if needed, and extending ctx->prototype if needed in
298 * order to do that.
299 */
300void spectrectx_extend_coords(SpectreContext *ctx, SpectreCoords *sc, size_t n)
301{
302 if (ctx->prototype->nc < n) {
303 spectre_coords_make_space(ctx->prototype, n);
304 while (ctx->prototype->nc < n) {
305 const struct HexData *h = &hexdata[
306 ctx->prototype->c[ctx->prototype->nc-1].type];
307 const struct Possibility *poss;
308
309 if (!ctx->rs) {
310 /*
311 * If there's no random_state available, it must be
312 * because we were given an explicit coordinate string
313 * and ran off the end of it.
314 *
315 * The obvious thing to do here would be to make up an
316 * answer non-randomly. But in fact there's a danger
317 * that this leads to endless recursion within a
318 * single coordinate step, if the hex edge we were
319 * trying to traverse turns into another copy of
320 * itself at the higher level. That happened in early
321 * testing before I put the random_state in at all.
322 *
323 * To avoid that risk, in this situation - which
324 * _shouldn't_ come up at all in sensibly play - we
325 * make up a random_state, and free it when the
326 * context goes away.
327 */
328 ctx->rs = random_new("dummy", 5);
329 ctx->must_free_rs = true;
330 }
331
332 poss = choose_poss(ctx->rs, h->poss, h->nposs);
333 ctx->prototype->c[ctx->prototype->nc-1].index = poss->lo;
334 ctx->prototype->c[ctx->prototype->nc].type = poss->hi;
335 ctx->prototype->c[ctx->prototype->nc].index = -1;
336 ctx->prototype->nc++;
337 }
338 }
339
340 spectre_coords_make_space(sc, n);
341 while (sc->nc < n) {
342 assert(sc->c[sc->nc - 1].index == -1);
343 assert(sc->c[sc->nc - 1].type == ctx->prototype->c[sc->nc - 1].type);
344 sc->c[sc->nc - 1].index = ctx->prototype->c[sc->nc - 1].index;
345 sc->c[sc->nc].index = -1;
346 sc->c[sc->nc].type = ctx->prototype->c[sc->nc].type;
347 sc->nc++;
348 }
349}
350
351void spectrectx_step_hex(SpectreContext *ctx, SpectreCoords *sc,
352 size_t depth, unsigned edge, unsigned *outedge)
353{
354 const struct HexData *h;
355 const struct MapEntry *m;
356
357 spectrectx_extend_coords(ctx, sc, depth+2);
358
359 assert(0 <= sc->c[depth].index);
360 assert(sc->c[depth].index < num_subhexes(sc->c[depth].type));
361 assert(0 <= edge);
362 assert(edge < 6);
363
364 h = &hexdata[sc->c[depth+1].type];
365 m = &h->hexmap[6 * sc->c[depth].index + edge];
366 if (!m->internal) {
367 unsigned recedge;
368 const struct MapEdge *me;
369 spectrectx_step_hex(ctx, sc, depth+1, m->hi, &recedge);
370 assert(recedge < 6);
371 h = &hexdata[sc->c[depth+1].type];
372 me = &h->hexedges[recedge];
373 assert(m->lo < me->len);
374 m = &h->hexin[me->startindex + me->len - 1 - m->lo];
375 assert(m->internal);
376 }
377 sc->c[depth].index = m->hi;
378 sc->c[depth].type = h->subhexes[sc->c[depth].index];
379 *outedge = m->lo;
380
381 if (depth == 0) {
382 /*
383 * Update the colouring fields to track the colour of the new
384 * hexagon.
385 */
386 unsigned char new_hex_colour;
387
388 if (!((edge ^ sc->incoming_hex_edge) & 1)) {
389 /* We're going out via the same parity of edge we came in
390 * on, so the new hex colour is the same as the previous
391 * one. */
392 new_hex_colour = sc->prev_hex_colour;
393 } else {
394 /* We're going out via the opposite parity of edge, so the
395 * new colour is the one of {0,1,2} that is neither this
396 * _nor_ the previous colour. */
397 new_hex_colour = 0+1+2 - sc->hex_colour - sc->prev_hex_colour;
398 }
399
400 sc->prev_hex_colour = sc->hex_colour;
401 sc->hex_colour = new_hex_colour;
402 sc->incoming_hex_edge = m->lo;
403 }
404}
405
406void spectrectx_step(SpectreContext *ctx, SpectreCoords *sc,
407 unsigned edge, unsigned *outedge)
408{
409 const struct HexData *h;
410 const struct MapEntry *m;
411
412 assert(0 <= sc->index);
413 assert(sc->index < num_spectres(sc->c[0].type));
414 assert(0 <= edge);
415 assert(edge < 14);
416
417 h = &hexdata[sc->c[0].type];
418 m = &h->specmap[14 * sc->index + edge];
419
420 while (!m->internal) {
421 unsigned recedge;
422 const struct MapEdge *me;
423 spectrectx_step_hex(ctx, sc, 0, m->hi, &recedge);
424 assert(recedge < 6);
425 h = &hexdata[sc->c[0].type];
426 me = &h->specedges[recedge];
427 assert(m->lo < me->len);
428 m = &h->specin[me->startindex + me->len - 1 - m->lo];
429 }
430 sc->index = m->hi;
431 *outedge = m->lo;
432}
433
434void spectrectx_generate(SpectreContext *ctx,
435 bool (*callback)(void *cbctx, const Spectre *spec),
436 void *cbctx)
437{
438 tree234 *placed = newtree234(spectre_cmp);
439 Spectre *qhead = NULL, *qtail = NULL;
440
441 {
442 Spectre *spec = spectre_initial(ctx);
443
444 add234(placed, spec);
445
446 spec->next = NULL;
447
448 if (callback(cbctx, spec))
449 qhead = qtail = spec;
450 }
451
452 while (qhead) {
453 unsigned edge;
454 Spectre *spec = qhead;
455
456 for (edge = 0; edge < 14; edge++) {
457 Spectre *new_spec;
458
459 new_spec = spectre_adjacent(ctx, spec, edge, NULL);
460
461 if (find234(placed, new_spec, NULL)) {
462 spectre_free(new_spec);
463 continue;
464 }
465
466 if (!callback(cbctx, new_spec)) {
467 spectre_free(new_spec);
468 continue;
469 }
470
471 add234(placed, new_spec);
472 qtail->next = new_spec;
473 qtail = new_spec;
474 new_spec->next = NULL;
475 }
476
477 qhead = qhead->next;
478 }
479
480 {
481 Spectre *spec;
482 while ((spec = delpos234(placed, 0)) != NULL)
483 spectre_free(spec);
484 freetree234(placed);
485 }
486}
487
488const char *spectre_tiling_params_invalid(
489 const struct SpectrePatchParams *params)
490{
491 size_t i;
492 Hex h;
493
494 if (params->ncoords == 0)
495 return "expected at least one numeric coordinate";
496 if (!spectre_valid_hex_letter(params->final_hex))
497 return "invalid final hexagon type";
498
499 h = hex_from_letter(params->final_hex);
500 for (i = params->ncoords; i-- > 0 ;) {
501 unsigned limit = (i == 0) ? num_spectres(h) : num_subhexes(h);
502 if (params->coords[i] >= limit)
503 return "coordinate out of range";
504
505 if (i > 0)
506 h = hexdata[h].subhexes[params->coords[i]];
507 }
508
509 return NULL;
510}
511
512struct SpectreCallbackContext {
513 int xoff, yoff;
514 Coord xmin, xmax, ymin, ymax;
515
516 spectre_tile_callback_fn external_cb;
517 void *external_cbctx;
518};
519
520static bool spectre_internal_callback(void *vctx, const Spectre *spec)
521{
522 struct SpectreCallbackContext *ctx = (struct SpectreCallbackContext *)vctx;
523 size_t i;
524 int output_coords[4*14];
525
526 for (i = 0; i < 14; i++) {
527 Point p = spec->vertices[i];
528 Coord x = point_x(p), y = point_y(p);
529 if (coord_cmp(x, ctx->xmin) < 0 || coord_cmp(x, ctx->xmax) > 0 ||
530 coord_cmp(y, ctx->ymin) < 0 || coord_cmp(y, ctx->ymax) > 0)
531 return false;
532
533 output_coords[4*i + 0] = ctx->xoff + x.c1;
534 output_coords[4*i + 1] = x.cr3;
535 output_coords[4*i + 2] = ctx->yoff - y.c1;
536 output_coords[4*i + 3] = -y.cr3;
537 }
538
539 if (ctx->external_cb)
540 ctx->external_cb(ctx->external_cbctx, output_coords);
541
542 return true;
543}
544
545static void spectre_set_bounds(struct SpectreCallbackContext *cbctx,
546 int w, int h)
547{
548 cbctx->xoff = w/2;
549 cbctx->yoff = h/2;
550 cbctx->xmin.c1 = -cbctx->xoff;
551 cbctx->xmax.c1 = -cbctx->xoff + w;
552 cbctx->ymin.c1 = cbctx->yoff - h;
553 cbctx->ymax.c1 = cbctx->yoff;
554 cbctx->xmin.cr3 = 0;
555 cbctx->xmax.cr3 = 0;
556 cbctx->ymin.cr3 = 0;
557 cbctx->ymax.cr3 = 0;
558}
559
560void spectre_tiling_randomise(struct SpectrePatchParams *ps, int w, int h,
561 random_state *rs)
562{
563 SpectreContext ctx[1];
564 struct SpectreCallbackContext cbctx[1];
565 size_t i;
566
567 spectre_set_bounds(cbctx, w, h);
568 cbctx->external_cb = NULL;
569 cbctx->external_cbctx = NULL;
570
571 spectrectx_init_random(ctx, rs);
572 spectrectx_generate(ctx, spectre_internal_callback, cbctx);
573
574 ps->orientation = ctx->orientation;
575 ps->ncoords = ctx->prototype->nc;
576 ps->coords = snewn(ps->ncoords, unsigned char);
577 ps->coords[0] = ctx->prototype->index;
578 for (i = 1; i < ps->ncoords; i++)
579 ps->coords[i] = ctx->prototype->c[i-1].index;
580 ps->final_hex = hex_to_letter(ctx->prototype->c[ps->ncoords-1].type);
581
582 spectrectx_cleanup(ctx);
583}
584
585void spectre_tiling_generate(
586 const struct SpectrePatchParams *params, int w, int h,
587 spectre_tile_callback_fn external_cb, void *external_cbctx)
588{
589 SpectreContext ctx[1];
590 struct SpectreCallbackContext cbctx[1];
591
592 spectre_set_bounds(cbctx, w, h);
593 cbctx->external_cb = external_cb;
594 cbctx->external_cbctx = external_cbctx;
595
596 spectrectx_init_from_params(ctx, params);
597 spectrectx_generate(ctx, spectre_internal_callback, cbctx);
598 spectrectx_cleanup(ctx);
599}
diff --git a/apps/plugins/puzzles/src/spectre.h b/apps/plugins/puzzles/src/spectre.h
new file mode 100644
index 0000000000..97d7595e3a
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre.h
@@ -0,0 +1,72 @@
1#ifndef PUZZLES_SPECTRE_H
2#define PUZZLES_SPECTRE_H
3
4struct SpectrePatchParams {
5 /*
6 * A patch of Spectre tiling is identified by giving
7 *
8 * - the coordinates of the central Spectre, using a
9 * combinatorial coordinate system similar to the Hat tiling in
10 * hat.h
11 *
12 * - the orientation of that Spectre, as a number from 0 to 11 (a
13 * multiple of 30 degrees), with 0 representing the 'head' of
14 * the Spectre facing upwards, and numbers counting
15 * anticlockwise.
16 *
17 * Coordinates are a sequence of small non-negative integers. The
18 * valid range for each coordinate depends on the next coordinate,
19 * or on final_hex if it's the last one in the list. The largest
20 * valid range is {0,...,7}.
21 *
22 * 'final_hex' is one of the letters GDJLXPSFY.
23 * spectre_valid_hex_letter() will check that.
24 */
25 int orientation;
26 size_t ncoords;
27 unsigned char *coords;
28 char final_hex;
29};
30
31bool spectre_valid_hex_letter(char c);
32
33/*
34 * Fill in SpectrePatchParams with a randomly selected set of
35 * coordinates, in enough detail to generate a patch of tiling filling
36 * a w x h area. The unit of measurement is 1/(2*sqrt(2)) of a Spectre
37 * edge, i.e. such that a single Spectre edge at 45 degrees would
38 * correspond to the vector (2,2).
39 *
40 * The 'coords' field of the structure will be filled in with a new
41 * dynamically allocated array. Any previous pointer in that field
42 * will be overwritten.
43 */
44void spectre_tiling_randomise(struct SpectrePatchParams *params, int w, int h,
45 random_state *rs);
46
47/*
48 * Validate a SpectrePatchParams to ensure it contains no illegal
49 * coordinates. Returns NULL if it's acceptable, or an error string if
50 * not.
51 */
52const char *spectre_tiling_params_invalid(
53 const struct SpectrePatchParams *params);
54
55/*
56 * Generate the actual set of Spectre tiles from a SpectrePatchParams,
57 * passing each one to a callback. The callback receives the vertices
58 * of each point, in the form of an array of 4*14 integers. Each
59 * vertex is represented by four consecutive integers in this array,
60 * with the first two giving the x coordinate and the last two the y
61 * coordinate. Each pair of integers a,b represent a single coordinate
62 * whose value is a + b*sqrt(3). The unit of measurement is as above.
63 */
64typedef void (*spectre_tile_callback_fn)(void *ctx, const int *coords);
65
66#define SPECTRE_NVERTICES 14
67
68void spectre_tiling_generate(
69 const struct SpectrePatchParams *params, int w, int h,
70 spectre_tile_callback_fn cb, void *cbctx);
71
72#endif
diff --git a/apps/plugins/puzzles/src/tents.R b/apps/plugins/puzzles/src/tents.R
deleted file mode 100644
index 925661c761..0000000000
--- a/apps/plugins/puzzles/src/tents.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3TENTS_EXTRA = matching dsf
4
5tents : [X] GTK COMMON tents TENTS_EXTRA tents-icon|no-icon
6
7tents : [G] WINDOWS COMMON tents TENTS_EXTRA tents.res|noicon.res
8
9ALL += tents[COMBINED] TENTS_EXTRA
10
11tentssolver : [U] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
12tentssolver : [C] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
13
14!begin am gtk
15GAMES += tents
16!end
17
18!begin >list.c
19 A(tents) \
20!end
21
22!begin >gamedesc.txt
23tents:tents.exe:Tents:Tent-placing puzzle:Place a tent next to each tree.
24!end
diff --git a/apps/plugins/puzzles/src/tents.c b/apps/plugins/puzzles/src/tents.c
index ee06172baf..0d40959d26 100644
--- a/apps/plugins/puzzles/src/tents.c
+++ b/apps/plugins/puzzles/src/tents.c
@@ -32,7 +32,12 @@
32#include <string.h> 32#include <string.h>
33#include <assert.h> 33#include <assert.h>
34#include <ctype.h> 34#include <ctype.h>
35#include <math.h> 35#include <limits.h>
36#ifdef NO_TGMATH_H
37# include <math.h>
38#else
39# include <tgmath.h>
40#endif
36 41
37#include "puzzles.h" 42#include "puzzles.h"
38#include "matching.h" 43#include "matching.h"
@@ -229,7 +234,7 @@
229 */ 234 */
230#if defined STANDALONE_SOLVER 235#if defined STANDALONE_SOLVER
231#define SOLVER_DIAGNOSTICS 236#define SOLVER_DIAGNOSTICS
232bool verbose = false; 237static bool verbose = false;
233#elif defined SOLVER_DIAGNOSTICS 238#elif defined SOLVER_DIAGNOSTICS
234#define verbose true 239#define verbose true
235#endif 240#endif
@@ -408,6 +413,8 @@ static const char *validate_params(const game_params *params, bool full)
408 */ 413 */
409 if (params->w < 4 || params->h < 4) 414 if (params->w < 4 || params->h < 4)
410 return "Width and height must both be at least four"; 415 return "Width and height must both be at least four";
416 if (params->w > (INT_MAX - 1) / params->h)
417 return "Width times height must not be unreasonably large";
411 return NULL; 418 return NULL;
412} 419}
413 420
@@ -998,7 +1005,7 @@ static char *new_game_desc(const game_params *params_in, random_state *rs,
998 int dy, dx; 1005 int dy, dx;
999 bool ok = true; 1006 bool ok = true;
1000 1007
1001 which = i + random_upto(rs, j); 1008 which = i + random_upto(rs, w*h - i);
1002 tmp = order[which]; 1009 tmp = order[which];
1003 order[which] = order[i]; 1010 order[which] = order[i];
1004 order[i] = tmp; 1011 order[i] = tmp;
@@ -1188,6 +1195,21 @@ static char *new_game_desc(const game_params *params_in, random_state *rs,
1188 return ret; 1195 return ret;
1189} 1196}
1190 1197
1198/*
1199 * Grid description format:
1200 *
1201 * _ = tree
1202 * a = 1 BLANK then TREE
1203 * ...
1204 * y = 25 BLANKs then TREE
1205 * z = 25 BLANKs
1206 * ! = set previous square to TENT
1207 * - = set previous square to NONTENT
1208 *
1209 * Last character must be one that would insert a tree as the first
1210 * square after the grid.
1211 */
1212
1191static const char *validate_desc(const game_params *params, const char *desc) 1213static const char *validate_desc(const game_params *params, const char *desc)
1192{ 1214{
1193 int w = params->w, h = params->h; 1215 int w = params->w, h = params->h;
@@ -1201,9 +1223,10 @@ static const char *validate_desc(const game_params *params, const char *desc)
1201 area += *desc - 'a' + 2; 1223 area += *desc - 'a' + 2;
1202 else if (*desc == 'z') 1224 else if (*desc == 'z')
1203 area += 25; 1225 area += 25;
1204 else if (*desc == '!' || *desc == '-') 1226 else if (*desc == '!' || *desc == '-') {
1205 /* do nothing */; 1227 if (area == 0 || area > w * h)
1206 else 1228 return "Tent or non-tent placed off the grid";
1229 } else
1207 return "Invalid character in grid specification"; 1230 return "Invalid character in grid specification";
1208 1231
1209 desc++; 1232 desc++;
@@ -1436,7 +1459,7 @@ static game_ui *new_ui(const game_state *state)
1436 ui->drag_button = -1; 1459 ui->drag_button = -1;
1437 ui->drag_ok = false; 1460 ui->drag_ok = false;
1438 ui->cx = ui->cy = 0; 1461 ui->cx = ui->cy = 0;
1439 ui->cdisp = false; 1462 ui->cdisp = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1440 return ui; 1463 return ui;
1441} 1464}
1442 1465
@@ -1445,18 +1468,25 @@ static void free_ui(game_ui *ui)
1445 sfree(ui); 1468 sfree(ui);
1446} 1469}
1447 1470
1448static char *encode_ui(const game_ui *ui) 1471static void game_changed_state(game_ui *ui, const game_state *oldstate,
1449{ 1472 const game_state *newstate)
1450 return NULL;
1451}
1452
1453static void decode_ui(game_ui *ui, const char *encoding)
1454{ 1473{
1455} 1474}
1456 1475
1457static void game_changed_state(game_ui *ui, const game_state *oldstate, 1476static const char *current_key_label(const game_ui *ui,
1458 const game_state *newstate) 1477 const game_state *state, int button)
1459{ 1478{
1479 int w = state->p.w;
1480 int v = state->grid[ui->cy*w+ui->cx];
1481
1482 if (IS_CURSOR_SELECT(button) && ui->cdisp) {
1483 switch (v) {
1484 case BLANK:
1485 return button == CURSOR_SELECT ? "Tent" : "Green";
1486 case TENT: case NONTENT: return "Clear";
1487 }
1488 }
1489 return "";
1460} 1490}
1461 1491
1462struct game_drawstate { 1492struct game_drawstate {
@@ -1546,7 +1576,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1546 char tmpbuf[80]; 1576 char tmpbuf[80];
1547 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 1577 bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
1548 1578
1549 button &= ~MOD_MASK; 1579 button = STRIP_BUTTON_MODIFIERS(button);
1550 1580
1551 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1581 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1552 x = FROMCOORD(x); 1582 x = FROMCOORD(x);
@@ -1559,7 +1589,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1559 ui->dsy = ui->dey = y; 1589 ui->dsy = ui->dey = y;
1560 ui->drag_ok = true; 1590 ui->drag_ok = true;
1561 ui->cdisp = false; 1591 ui->cdisp = false;
1562 return UI_UPDATE; 1592 return MOVE_UI_UPDATE;
1563 } 1593 }
1564 1594
1565 if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) && 1595 if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) &&
@@ -1591,14 +1621,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1591 } 1621 }
1592 1622
1593 if (IS_MOUSE_DRAG(button)) 1623 if (IS_MOUSE_DRAG(button))
1594 return UI_UPDATE; 1624 return MOVE_UI_UPDATE;
1595 1625
1596 /* 1626 /*
1597 * The drag has been released. Enact it. 1627 * The drag has been released. Enact it.
1598 */ 1628 */
1599 if (!ui->drag_ok) { 1629 if (!ui->drag_ok) {
1600 ui->drag_button = -1; 1630 ui->drag_button = -1;
1601 return UI_UPDATE; /* drag was just cancelled */ 1631 return MOVE_UI_UPDATE; /* drag was just cancelled */
1602 } 1632 }
1603 1633
1604 xmin = min(ui->dsx, ui->dex); 1634 xmin = min(ui->dsx, ui->dex);
@@ -1636,7 +1666,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1636 1666
1637 if (buflen == 0) { 1667 if (buflen == 0) {
1638 sfree(buf); 1668 sfree(buf);
1639 return UI_UPDATE; /* drag was terminated */ 1669 return MOVE_UI_UPDATE; /* drag was terminated */
1640 } else { 1670 } else {
1641 buf[buflen] = '\0'; 1671 buf[buflen] = '\0';
1642 return buf; 1672 return buf;
@@ -1644,11 +1674,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1644 } 1674 }
1645 1675
1646 if (IS_CURSOR_MOVE(button)) { 1676 if (IS_CURSOR_MOVE(button)) {
1647 ui->cdisp = true; 1677 char *ret;
1648 if (shift || control) { 1678 if (shift || control) {
1649 int len = 0, i, indices[2]; 1679 int len = 0, i, indices[2];
1650 indices[0] = ui->cx + w * ui->cy; 1680 indices[0] = ui->cx + w * ui->cy;
1651 move_cursor(button, &ui->cx, &ui->cy, w, h, false); 1681 ret = move_cursor(button, &ui->cx, &ui->cy, w, h, false,
1682 &ui->cdisp);
1652 indices[1] = ui->cx + w * ui->cy; 1683 indices[1] = ui->cx + w * ui->cy;
1653 1684
1654 /* NONTENTify all unique traversed eligible squares */ 1685 /* NONTENTify all unique traversed eligible squares */
@@ -1663,8 +1694,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1663 tmpbuf[len] = '\0'; 1694 tmpbuf[len] = '\0';
1664 if (len) return dupstr(tmpbuf); 1695 if (len) return dupstr(tmpbuf);
1665 } else 1696 } else
1666 move_cursor(button, &ui->cx, &ui->cy, w, h, false); 1697 ret = move_cursor(button, &ui->cx, &ui->cy, w, h, false,
1667 return UI_UPDATE; 1698 &ui->cdisp);
1699 return ret;
1668 } 1700 }
1669 if (ui->cdisp) { 1701 if (ui->cdisp) {
1670 char rep = 0; 1702 char rep = 0;
@@ -1691,7 +1723,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1691 } 1723 }
1692 } else if (IS_CURSOR_SELECT(button)) { 1724 } else if (IS_CURSOR_SELECT(button)) {
1693 ui->cdisp = true; 1725 ui->cdisp = true;
1694 return UI_UPDATE; 1726 return MOVE_UI_UPDATE;
1695 } 1727 }
1696 1728
1697 return NULL; 1729 return NULL;
@@ -1875,7 +1907,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1875 */ 1907 */
1876 1908
1877static void game_compute_size(const game_params *params, int tilesize, 1909static void game_compute_size(const game_params *params, int tilesize,
1878 int *x, int *y) 1910 const game_ui *ui, int *x, int *y)
1879{ 1911{
1880 /* fool the macros */ 1912 /* fool the macros */
1881 struct dummy { int tilesize; } dummy, *ds = &dummy; 1913 struct dummy { int tilesize; } dummy, *ds = &dummy;
@@ -1976,7 +2008,8 @@ static int *find_errors(const game_state *state, char *grid)
1976{ 2008{
1977 int w = state->p.w, h = state->p.h; 2009 int w = state->p.w, h = state->p.h;
1978 int *ret = snewn(w*h + w + h, int); 2010 int *ret = snewn(w*h + w + h, int);
1979 int *tmp = snewn(w*h*2, int), *dsf = tmp + w*h; 2011 int *tmp = snewn(w*h, int);
2012 DSF *dsf;
1980 int x, y; 2013 int x, y;
1981 2014
1982 /* 2015 /*
@@ -2193,7 +2226,7 @@ static int *find_errors(const game_state *state, char *grid)
2193 * all the tents in any component which has a smaller tree 2226 * all the tents in any component which has a smaller tree
2194 * count. 2227 * count.
2195 */ 2228 */
2196 dsf_init(dsf, w*h); 2229 dsf = dsf_new(w*h);
2197 /* Construct the equivalence classes. */ 2230 /* Construct the equivalence classes. */
2198 for (y = 0; y < h; y++) { 2231 for (y = 0; y < h; y++) {
2199 for (x = 0; x < w-1; x++) { 2232 for (x = 0; x < w-1; x++) {
@@ -2236,7 +2269,7 @@ static int *find_errors(const game_state *state, char *grid)
2236 * start of the game, before the user had done anything wrong!) 2269 * start of the game, before the user had done anything wrong!)
2237 */ 2270 */
2238#define TENT(x) ((x)==TENT || (x)==BLANK) 2271#define TENT(x) ((x)==TENT || (x)==BLANK)
2239 dsf_init(dsf, w*h); 2272 dsf_reinit(dsf);
2240 /* Construct the equivalence classes. */ 2273 /* Construct the equivalence classes. */
2241 for (y = 0; y < h; y++) { 2274 for (y = 0; y < h; y++) {
2242 for (x = 0; x < w-1; x++) { 2275 for (x = 0; x < w-1; x++) {
@@ -2272,6 +2305,7 @@ static int *find_errors(const game_state *state, char *grid)
2272#undef TENT 2305#undef TENT
2273 2306
2274 sfree(tmp); 2307 sfree(tmp);
2308 dsf_free(dsf);
2275 return ret; 2309 return ret;
2276} 2310}
2277 2311
@@ -2411,14 +2445,6 @@ static void int_redraw(drawing *dr, game_drawstate *ds,
2411 } 2445 }
2412 2446
2413 if (printing || !ds->started) { 2447 if (printing || !ds->started) {
2414 if (!printing) {
2415 int ww, wh;
2416 game_compute_size(&state->p, TILESIZE, &ww, &wh);
2417 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
2418 draw_update(dr, 0, 0, ww, wh);
2419 ds->started = true;
2420 }
2421
2422 if (printing) 2448 if (printing)
2423 print_line_width(dr, TILESIZE/64); 2449 print_line_width(dr, TILESIZE/64);
2424 2450
@@ -2572,24 +2598,21 @@ static int game_status(const game_state *state)
2572 return state->completed ? +1 : 0; 2598 return state->completed ? +1 : 0;
2573} 2599}
2574 2600
2575static bool game_timing_state(const game_state *state, game_ui *ui) 2601static void game_print_size(const game_params *params, const game_ui *ui,
2576{ 2602 float *x, float *y)
2577 return true;
2578}
2579
2580static void game_print_size(const game_params *params, float *x, float *y)
2581{ 2603{
2582 int pw, ph; 2604 int pw, ph;
2583 2605
2584 /* 2606 /*
2585 * I'll use 6mm squares by default. 2607 * I'll use 6mm squares by default.
2586 */ 2608 */
2587 game_compute_size(params, 600, &pw, &ph); 2609 game_compute_size(params, 600, ui, &pw, &ph);
2588 *x = pw / 100.0F; 2610 *x = pw / 100.0F;
2589 *y = ph / 100.0F; 2611 *y = ph / 100.0F;
2590} 2612}
2591 2613
2592static void game_print(drawing *dr, const game_state *state, int tilesize) 2614static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2615 int tilesize)
2593{ 2616{
2594 int c; 2617 int c;
2595 2618
@@ -2628,12 +2651,14 @@ const struct game thegame = {
2628 free_game, 2651 free_game,
2629 true, solve_game, 2652 true, solve_game,
2630 true, game_can_format_as_text_now, game_text_format, 2653 true, game_can_format_as_text_now, game_text_format,
2654 NULL, NULL, /* get_prefs, set_prefs */
2631 new_ui, 2655 new_ui,
2632 free_ui, 2656 free_ui,
2633 encode_ui, 2657 NULL, /* encode_ui */
2634 decode_ui, 2658 NULL, /* decode_ui */
2635 NULL, /* game_request_keys */ 2659 NULL, /* game_request_keys */
2636 game_changed_state, 2660 game_changed_state,
2661 current_key_label,
2637 interpret_move, 2662 interpret_move,
2638 execute_move, 2663 execute_move,
2639 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2664 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2647,7 +2672,7 @@ const struct game thegame = {
2647 game_status, 2672 game_status,
2648 true, false, game_print_size, game_print, 2673 true, false, game_print_size, game_print,
2649 false, /* wants_statusbar */ 2674 false, /* wants_statusbar */
2650 false, game_timing_state, 2675 false, NULL, /* timing_state */
2651 REQUIRE_RBUTTON, /* flags */ 2676 REQUIRE_RBUTTON, /* flags */
2652}; 2677};
2653 2678
diff --git a/apps/plugins/puzzles/src/towers.R b/apps/plugins/puzzles/src/towers.R
deleted file mode 100644
index c2bb78a2dc..0000000000
--- a/apps/plugins/puzzles/src/towers.R
+++ /dev/null
@@ -1,25 +0,0 @@
1# -*- makefile -*-
2
3TOWERS_EXTRA = LATIN
4TOWERS_EXTRA_SOLVER = LATIN_SOLVER
5
6towers : [X] GTK COMMON towers TOWERS_EXTRA towers-icon|no-icon
7
8towers : [G] WINDOWS COMMON towers TOWERS_EXTRA towers.res|noicon.res
9
10towerssolver : [U] towers[STANDALONE_SOLVER] TOWERS_EXTRA_SOLVER STANDALONE
11towerssolver : [C] towers[STANDALONE_SOLVER] TOWERS_EXTRA_SOLVER STANDALONE
12
13ALL += towers[COMBINED] TOWERS_EXTRA
14
15!begin am gtk
16GAMES += towers
17!end
18
19!begin >list.c
20 A(towers) \
21!end
22
23!begin >gamedesc.txt
24towers:towers.exe:Towers:Tower-placing Latin square puzzle:Complete the latin square of towers in accordance with the clues.
25!end
diff --git a/apps/plugins/puzzles/src/towers.c b/apps/plugins/puzzles/src/towers.c
index 13c652b819..4dd9484ad9 100644
--- a/apps/plugins/puzzles/src/towers.c
+++ b/apps/plugins/puzzles/src/towers.c
@@ -23,7 +23,11 @@
23#include <string.h> 23#include <string.h>
24#include <assert.h> 24#include <assert.h>
25#include <ctype.h> 25#include <ctype.h>
26#include <math.h> 26#ifdef NO_TGMATH_H
27# include <math.h>
28#else
29# include <tgmath.h>
30#endif
27 31
28#include "puzzles.h" 32#include "puzzles.h"
29#include "latin.h" 33#include "latin.h"
@@ -385,12 +389,12 @@ static int solver_easy(struct latin_solver *solver, void *vctx)
385 return ret; 389 return ret;
386 390
387#ifdef STANDALONE_SOLVER 391#ifdef STANDALONE_SOLVER
388 if (solver_show_working) 392 if (solver_show_working)
389 sprintf(prefix, "%*slower bounds for clue %s %d:\n", 393 sprintf(prefix, "%*slower bounds for clue %s %d:\n",
390 solver_recurse_depth*4, "", 394 solver_recurse_depth*4, "",
391 cluepos[c/w], c%w+1); 395 cluepos[c/w], c%w+1);
392 else 396 else
393 prefix[0] = '\0'; /* placate optimiser */ 397 prefix[0] = '\0'; /* placate optimiser */
394#endif 398#endif
395 399
396 i = 0; 400 i = 0;
@@ -893,6 +897,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
893 return "Too much data to fit in grid"; 897 return "Too much data to fit in grid";
894 } 898 }
895 899
900 if (*p) return "Rubbish at end of game description";
896 return NULL; 901 return NULL;
897} 902}
898 903
@@ -1154,16 +1159,59 @@ struct game_ui {
1154 * allowed on immutable squares. 1159 * allowed on immutable squares.
1155 */ 1160 */
1156 bool hcursor; 1161 bool hcursor;
1162
1163 /*
1164 * User preference option which can be set to FALSE to disable the
1165 * 3D graphical style, and instead just display the puzzle as if
1166 * it was a Sudoku variant, i.e. each square just has a digit in
1167 * it.
1168 *
1169 * I was initially a bit uncertain about whether the 3D style
1170 * would be the right thing, on the basis that it uses up space in
1171 * the cells and makes it hard to use many pencil marks. Actually
1172 * nobody seems to have complained, but having put in the option
1173 * while I was still being uncertain, it seems silly not to leave
1174 * it in just in case.
1175 */
1176 int three_d;
1177
1178 /*
1179 * User preference option: if the user right-clicks in a square
1180 * and presses a number key to add/remove a pencil mark, do we
1181 * hide the mouse highlight again afterwards?
1182 *
1183 * Historically our answer was yes. The Android port prefers no.
1184 * There are advantages both ways, depending how much you dislike
1185 * the highlight cluttering your view. So it's a preference.
1186 */
1187 bool pencil_keep_highlight;
1157}; 1188};
1158 1189
1190static void legacy_prefs_override(struct game_ui *ui_out)
1191{
1192 static bool initialised = false;
1193 static int three_d = -1;
1194
1195 if (!initialised) {
1196 initialised = true;
1197 three_d = getenv_bool("TOWERS_2D", -1);
1198 }
1199
1200 if (three_d != -1)
1201 ui_out->three_d = three_d;
1202}
1203
1159static game_ui *new_ui(const game_state *state) 1204static game_ui *new_ui(const game_state *state)
1160{ 1205{
1161 game_ui *ui = snew(game_ui); 1206 game_ui *ui = snew(game_ui);
1162 1207
1163 ui->hx = ui->hy = 0; 1208 ui->hx = ui->hy = 0;
1164 ui->hpencil = false; 1209 ui->hpencil = false;
1165 ui->hshow = false; 1210 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1166 ui->hcursor = false; 1211
1212 ui->three_d = true;
1213 ui->pencil_keep_highlight = false;
1214 legacy_prefs_override(ui);
1167 1215
1168 return ui; 1216 return ui;
1169} 1217}
@@ -1173,13 +1221,34 @@ static void free_ui(game_ui *ui)
1173 sfree(ui); 1221 sfree(ui);
1174} 1222}
1175 1223
1176static char *encode_ui(const game_ui *ui) 1224static config_item *get_prefs(game_ui *ui)
1177{ 1225{
1178 return NULL; 1226 config_item *ret;
1227
1228 ret = snewn(3, config_item);
1229
1230 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1231 ret[0].kw = "pencil-keep-highlight";
1232 ret[0].type = C_BOOLEAN;
1233 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1234
1235 ret[1].name = "Puzzle appearance";
1236 ret[1].kw = "appearance";
1237 ret[1].type = C_CHOICES;
1238 ret[1].u.choices.choicenames = ":2D:3D";
1239 ret[1].u.choices.choicekws = ":2d:3d";
1240 ret[1].u.choices.selected = ui->three_d;
1241
1242 ret[2].name = NULL;
1243 ret[2].type = C_END;
1244
1245 return ret;
1179} 1246}
1180 1247
1181static void decode_ui(game_ui *ui, const char *encoding) 1248static void set_prefs(game_ui *ui, const config_item *cfg)
1182{ 1249{
1250 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1251 ui->three_d = cfg[1].u.choices.selected;
1183} 1252}
1184 1253
1185static void game_changed_state(game_ui *ui, const game_state *oldstate, 1254static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1198,6 +1267,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1198 } 1267 }
1199} 1268}
1200 1269
1270static const char *current_key_label(const game_ui *ui,
1271 const game_state *state, int button)
1272{
1273 if (ui->hshow && (button == CURSOR_SELECT))
1274 return ui->hpencil ? "Ink" : "Pencil";
1275 return "";
1276}
1277
1201#define PREFERRED_TILESIZE 48 1278#define PREFERRED_TILESIZE 48
1202#define TILESIZE (ds->tilesize) 1279#define TILESIZE (ds->tilesize)
1203#define BORDER (TILESIZE * 9 / 8) 1280#define BORDER (TILESIZE * 9 / 8)
@@ -1221,8 +1298,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1221 1298
1222struct game_drawstate { 1299struct game_drawstate {
1223 int tilesize; 1300 int tilesize;
1224 bool three_d; /* default 3D graphics are user-disableable */
1225 bool started;
1226 long *tiles; /* (w+2)*(w+2) temp space */ 1301 long *tiles; /* (w+2)*(w+2) temp space */
1227 long *drawn; /* (w+2)*(w+2)*4: current drawn data */ 1302 long *drawn; /* (w+2)*(w+2)*4: current drawn data */
1228 bool *errtmp; 1303 bool *errtmp;
@@ -1349,12 +1424,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1349 int tx, ty; 1424 int tx, ty;
1350 char buf[80]; 1425 char buf[80];
1351 1426
1352 button &= ~MOD_MASK; 1427 button = STRIP_BUTTON_MODIFIERS(button);
1353 1428
1354 tx = FROMCOORD(x); 1429 tx = FROMCOORD(x);
1355 ty = FROMCOORD(y); 1430 ty = FROMCOORD(y);
1356 1431
1357 if (ds->three_d) { 1432 if (ui->three_d) {
1358 /* 1433 /*
1359 * In 3D mode, just locating the mouse click in the natural 1434 * In 3D mode, just locating the mouse click in the natural
1360 * square grid may not be sufficient to tell which tower the 1435 * square grid may not be sufficient to tell which tower the
@@ -1401,7 +1476,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1401 ui->hpencil = false; 1476 ui->hpencil = false;
1402 } 1477 }
1403 ui->hcursor = false; 1478 ui->hcursor = false;
1404 return UI_UPDATE; 1479 return MOVE_UI_UPDATE;
1405 } 1480 }
1406 if (button == RIGHT_BUTTON) { 1481 if (button == RIGHT_BUTTON) {
1407 /* 1482 /*
@@ -1421,7 +1496,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1421 ui->hshow = false; 1496 ui->hshow = false;
1422 } 1497 }
1423 ui->hcursor = false; 1498 ui->hcursor = false;
1424 return UI_UPDATE; 1499 return MOVE_UI_UPDATE;
1425 } 1500 }
1426 } else if (button == LEFT_BUTTON) { 1501 } else if (button == LEFT_BUTTON) {
1427 if (is_clue(state, tx, ty)) { 1502 if (is_clue(state, tx, ty)) {
@@ -1444,16 +1519,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1444 } 1519 }
1445 return NULL; 1520 return NULL;
1446 } 1521 }
1447 move_cursor(button, &ui->hx, &ui->hy, w, w, false);
1448 ui->hshow = true;
1449 ui->hcursor = true; 1522 ui->hcursor = true;
1450 return UI_UPDATE; 1523 return move_cursor(button, &ui->hx, &ui->hy, w, w, false, &ui->hshow);
1451 } 1524 }
1452 if (ui->hshow && 1525 if (ui->hshow &&
1453 (button == CURSOR_SELECT)) { 1526 (button == CURSOR_SELECT)) {
1454 ui->hpencil = !ui->hpencil; 1527 ui->hpencil = !ui->hpencil;
1455 ui->hcursor = true; 1528 ui->hcursor = true;
1456 return UI_UPDATE; 1529 return MOVE_UI_UPDATE;
1457 } 1530 }
1458 1531
1459 if (ui->hshow && 1532 if (ui->hshow &&
@@ -1476,10 +1549,31 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1476 if (state->clues->immutable[ui->hy*w+ui->hx]) 1549 if (state->clues->immutable[ui->hy*w+ui->hx])
1477 return NULL; 1550 return NULL;
1478 1551
1552 /*
1553 * If you ask to fill a square with what it already contains,
1554 * or blank it when it's already empty, that has no effect...
1555 */
1556 if ((!ui->hpencil || n == 0) && state->grid[ui->hy*w+ui->hx] == n &&
1557 state->pencil[ui->hy*w+ui->hx] == 0) {
1558 /* ... expect to remove the cursor in mouse mode. */
1559 if (!ui->hcursor) {
1560 ui->hshow = false;
1561 return MOVE_UI_UPDATE;
1562 }
1563 return NULL;
1564 }
1565
1479 sprintf(buf, "%c%d,%d,%d", 1566 sprintf(buf, "%c%d,%d,%d",
1480 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 1567 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1481 1568
1482 if (!ui->hcursor) ui->hshow = false; 1569 /*
1570 * Hide the highlight after a keypress, if it was mouse-
1571 * generated. Also, don't hide it if this move has changed
1572 * pencil marks and the user preference says not to hide the
1573 * highlight in that situation.
1574 */
1575 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1576 ui->hshow = false;
1483 1577
1484 return dupstr(buf); 1578 return dupstr(buf);
1485 } 1579 }
@@ -1558,7 +1652,7 @@ static game_state *execute_move(const game_state *from, const char *move)
1558#define SIZE(w) ((w) * TILESIZE + 2*BORDER) 1652#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
1559 1653
1560static void game_compute_size(const game_params *params, int tilesize, 1654static void game_compute_size(const game_params *params, int tilesize,
1561 int *x, int *y) 1655 const game_ui *ui, int *x, int *y)
1562{ 1656{
1563 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1657 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1564 struct { int tilesize; } ads, *ds = &ads; 1658 struct { int tilesize; } ads, *ds = &ads;
@@ -1614,8 +1708,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1614 int i; 1708 int i;
1615 1709
1616 ds->tilesize = 0; 1710 ds->tilesize = 0;
1617 ds->three_d = !getenv("TOWERS_2D");
1618 ds->started = false;
1619 ds->tiles = snewn((w+2)*(w+2), long); 1711 ds->tiles = snewn((w+2)*(w+2), long);
1620 ds->drawn = snewn((w+2)*(w+2)*4, long); 1712 ds->drawn = snewn((w+2)*(w+2)*4, long);
1621 for (i = 0; i < (w+2)*(w+2)*4; i++) 1713 for (i = 0; i < (w+2)*(w+2)*4; i++)
@@ -1633,8 +1725,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1633 sfree(ds); 1725 sfree(ds);
1634} 1726}
1635 1727
1636static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues, 1728static void draw_tile(drawing *dr, game_drawstate *ds, const game_ui *ui,
1637 int x, int y, long tile) 1729 struct clues *clues, int x, int y, long tile)
1638{ 1730{
1639 int w = clues->w /* , a = w*w */; 1731 int w = clues->w /* , a = w*w */;
1640 int tx, ty, bg; 1732 int tx, ty, bg;
@@ -1646,7 +1738,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1646 bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND; 1738 bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
1647 1739
1648 /* draw tower */ 1740 /* draw tower */
1649 if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) { 1741 if (ui->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
1650 int coords[8]; 1742 int coords[8];
1651 int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w); 1743 int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
1652 int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w); 1744 int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
@@ -1747,10 +1839,10 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1747 * to put the pencil marks. 1839 * to put the pencil marks.
1748 */ 1840 */
1749 /* Start with the whole square, minus space for impinging towers */ 1841 /* Start with the whole square, minus space for impinging towers */
1750 pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0); 1842 pl = tx + (ui->three_d ? X_3D_DISP(w,w) : 0);
1751 pr = tx + TILESIZE; 1843 pr = tx + TILESIZE;
1752 pt = ty; 1844 pt = ty;
1753 pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0); 1845 pb = ty + TILESIZE - (ui->three_d ? Y_3D_DISP(w,w) : 0);
1754 1846
1755 /* 1847 /*
1756 * We arrange our pencil marks in a grid layout, with 1848 * We arrange our pencil marks in a grid layout, with
@@ -1821,20 +1913,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1821 int w = state->par.w /*, a = w*w */; 1913 int w = state->par.w /*, a = w*w */;
1822 int i, x, y; 1914 int i, x, y;
1823 1915
1824 if (!ds->started) {
1825 /*
1826 * The initial contents of the window are not guaranteed and
1827 * can vary with front ends. To be on the safe side, all
1828 * games should start by drawing a big background-colour
1829 * rectangle covering the whole window.
1830 */
1831 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
1832
1833 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
1834
1835 ds->started = true;
1836 }
1837
1838 check_errors(state, ds->errtmp); 1916 check_errors(state, ds->errtmp);
1839 1917
1840 /* 1918 /*
@@ -1900,13 +1978,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1900 ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) { 1978 ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
1901 clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE); 1979 clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
1902 1980
1903 draw_tile(dr, ds, state->clues, x-1, y-1, tr); 1981 draw_tile(dr, ds, ui, state->clues, x-1, y-1, tr);
1904 if (x > 0) 1982 if (x > 0)
1905 draw_tile(dr, ds, state->clues, x-2, y-1, tl); 1983 draw_tile(dr, ds, ui, state->clues, x-2, y-1, tl);
1906 if (y <= w) 1984 if (y <= w)
1907 draw_tile(dr, ds, state->clues, x-1, y, br); 1985 draw_tile(dr, ds, ui, state->clues, x-1, y, br);
1908 if (x > 0 && y <= w) 1986 if (x > 0 && y <= w)
1909 draw_tile(dr, ds, state->clues, x-2, y, bl); 1987 draw_tile(dr, ds, ui, state->clues, x-2, y, bl);
1910 1988
1911 unclip(dr); 1989 unclip(dr);
1912 draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE); 1990 draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
@@ -1953,26 +2031,21 @@ static int game_status(const game_state *state)
1953 return state->completed ? +1 : 0; 2031 return state->completed ? +1 : 0;
1954} 2032}
1955 2033
1956static bool game_timing_state(const game_state *state, game_ui *ui) 2034static void game_print_size(const game_params *params, const game_ui *ui,
1957{ 2035 float *x, float *y)
1958 if (state->completed)
1959 return false;
1960 return true;
1961}
1962
1963static void game_print_size(const game_params *params, float *x, float *y)
1964{ 2036{
1965 int pw, ph; 2037 int pw, ph;
1966 2038
1967 /* 2039 /*
1968 * We use 9mm squares by default, like Solo. 2040 * We use 9mm squares by default, like Solo.
1969 */ 2041 */
1970 game_compute_size(params, 900, &pw, &ph); 2042 game_compute_size(params, 900, ui, &pw, &ph);
1971 *x = pw / 100.0F; 2043 *x = pw / 100.0F;
1972 *y = ph / 100.0F; 2044 *y = ph / 100.0F;
1973} 2045}
1974 2046
1975static void game_print(drawing *dr, const game_state *state, int tilesize) 2047static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2048 int tilesize)
1976{ 2049{
1977 int w = state->par.w; 2050 int w = state->par.w;
1978 int ink = print_mono_colour(dr, 0); 2051 int ink = print_mono_colour(dr, 0);
@@ -2058,12 +2131,14 @@ const struct game thegame = {
2058 free_game, 2131 free_game,
2059 true, solve_game, 2132 true, solve_game,
2060 true, game_can_format_as_text_now, game_text_format, 2133 true, game_can_format_as_text_now, game_text_format,
2134 get_prefs, set_prefs,
2061 new_ui, 2135 new_ui,
2062 free_ui, 2136 free_ui,
2063 encode_ui, 2137 NULL, /* encode_ui */
2064 decode_ui, 2138 NULL, /* decode_ui */
2065 game_request_keys, 2139 game_request_keys,
2066 game_changed_state, 2140 game_changed_state,
2141 current_key_label,
2067 interpret_move, 2142 interpret_move,
2068 execute_move, 2143 execute_move,
2069 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2144 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2077,7 +2152,7 @@ const struct game thegame = {
2077 game_status, 2152 game_status,
2078 true, false, game_print_size, game_print, 2153 true, false, game_print_size, game_print,
2079 false, /* wants_statusbar */ 2154 false, /* wants_statusbar */
2080 false, game_timing_state, 2155 false, NULL, /* timing_state */
2081 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2156 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2082}; 2157};
2083 2158
diff --git a/apps/plugins/puzzles/src/tracks.R b/apps/plugins/puzzles/src/tracks.R
deleted file mode 100644
index 8b0ac97e0f..0000000000
--- a/apps/plugins/puzzles/src/tracks.R
+++ /dev/null
@@ -1,24 +0,0 @@
1# -*- makefile -*-
2
3TRACKS_EXTRA = dsf findloop
4
5tracks : [X] GTK COMMON tracks TRACKS_EXTRA tracks-icon|no-icon
6
7tracks : [G] WINDOWS COMMON tracks TRACKS_EXTRA tracks.res|noicon.res
8
9ALL += tracks[COMBINED] TRACKS_EXTRA
10
11trackssolver : [U] tracks[STANDALONE_SOLVER] TRACKS_EXTRA STANDALONE
12trackssolver : [C] tracks[STANDALONE_SOLVER] TRACKS_EXTRA STANDALONE
13
14!begin am gtk
15GAMES += tracks
16!end
17
18!begin >list.c
19 A(tracks) \
20!end
21
22!begin >gamedesc.txt
23tracks:tracks.exe:Tracks:Path-finding railway track puzzle:Fill in the railway track according to the clues.
24!end
diff --git a/apps/plugins/puzzles/src/tracks.c b/apps/plugins/puzzles/src/tracks.c
index 3cf5df70a1..a2d4560907 100644
--- a/apps/plugins/puzzles/src/tracks.c
+++ b/apps/plugins/puzzles/src/tracks.c
@@ -20,7 +20,12 @@
20#include <string.h> 20#include <string.h>
21#include <assert.h> 21#include <assert.h>
22#include <ctype.h> 22#include <ctype.h>
23#include <math.h> 23#include <limits.h>
24#ifdef NO_TGMATH_H
25# include <math.h>
26#else
27# include <tgmath.h>
28#endif
24 29
25#include "puzzles.h" 30#include "puzzles.h"
26 31
@@ -196,6 +201,8 @@ static const char *validate_params(const game_params *params, bool full)
196 */ 201 */
197 if (params->w < 4 || params->h < 4) 202 if (params->w < 4 || params->h < 4)
198 return "Width and height must both be at least four"; 203 return "Width and height must both be at least four";
204 if (params->w > INT_MAX / params->h)
205 return "Width times height must not be unreasonably large";
199 return NULL; 206 return NULL;
200} 207}
201 208
@@ -242,6 +249,9 @@ static const int nbits[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
242#define S_CLUE 8 249#define S_CLUE 8
243#define S_MARK 16 250#define S_MARK 16
244 251
252#define S_FLASH_SHIFT 8 /* Position of tile in solved track */
253#define S_FLASH_WIDTH 8 /* Width of above sub-field */
254#define S_FLASH_MASK ((1 << S_FLASH_WIDTH) - 1)
245#define S_TRACK_SHIFT 16 /* U/D/L/R flags for edge track indicators */ 255#define S_TRACK_SHIFT 16 /* U/D/L/R flags for edge track indicators */
246#define S_NOTRACK_SHIFT 20 /* U/D/L/R flags for edge no-track indicators */ 256#define S_NOTRACK_SHIFT 20 /* U/D/L/R flags for edge no-track indicators */
247 257
@@ -904,7 +914,7 @@ static game_state *new_game(midend *me, const game_params *params, const char *d
904} 914}
905 915
906struct solver_scratch { 916struct solver_scratch {
907 int *dsf; 917 DSF *dsf;
908}; 918};
909 919
910static int solve_set_sflag(game_state *state, int x, int y, 920static int solve_set_sflag(game_state *state, int x, int y,
@@ -919,8 +929,8 @@ static int solve_set_sflag(game_state *state, int x, int y,
919 if (state->sflags[i] & (f == S_TRACK ? S_NOTRACK : S_TRACK)) { 929 if (state->sflags[i] & (f == S_TRACK ? S_NOTRACK : S_TRACK)) {
920 solverdebug(("opposite flag already set there, marking IMPOSSIBLE")); 930 solverdebug(("opposite flag already set there, marking IMPOSSIBLE"));
921 state->impossible = true; 931 state->impossible = true;
922 } 932 } else
923 state->sflags[i] |= f; 933 state->sflags[i] |= f;
924 return 1; 934 return 1;
925} 935}
926 936
@@ -937,8 +947,8 @@ static int solve_set_eflag(game_state *state, int x, int y, int d,
937 if (sf & (f == E_TRACK ? E_NOTRACK : E_TRACK)) { 947 if (sf & (f == E_TRACK ? E_NOTRACK : E_TRACK)) {
938 solverdebug(("opposite flag already set there, marking IMPOSSIBLE")); 948 solverdebug(("opposite flag already set there, marking IMPOSSIBLE"));
939 state->impossible = true; 949 state->impossible = true;
940 } 950 } else
941 S_E_SET(state, x, y, d, f); 951 S_E_SET(state, x, y, d, f);
942 return 1; 952 return 1;
943} 953}
944 954
@@ -1306,7 +1316,7 @@ static int solve_check_neighbours(game_state *state, bool both_ways)
1306} 1316}
1307 1317
1308static int solve_check_loop_sub(game_state *state, int x, int y, int dir, 1318static int solve_check_loop_sub(game_state *state, int x, int y, int dir,
1309 int *dsf, int startc, int endc) 1319 DSF *dsf, int startc, int endc)
1310{ 1320{
1311 int w = state->p.w, h = state->p.h, i = y*w+x, j, k; 1321 int w = state->p.w, h = state->p.h, i = y*w+x, j, k;
1312 bool satisfied = true; 1322 bool satisfied = true;
@@ -1358,13 +1368,13 @@ static int solve_check_loop_sub(game_state *state, int x, int y, int dir,
1358static int solve_check_loop(game_state *state) 1368static int solve_check_loop(game_state *state)
1359{ 1369{
1360 int w = state->p.w, h = state->p.h, x, y, i, j, did = 0; 1370 int w = state->p.w, h = state->p.h, x, y, i, j, did = 0;
1361 int *dsf, startc, endc; 1371 DSF *dsf;
1372 int startc, endc;
1362 1373
1363 /* TODO eventually we should pull this out into a solver struct and keep it 1374 /* TODO eventually we should pull this out into a solver struct and keep it
1364 updated as we connect squares. For now we recreate it every time we try 1375 updated as we connect squares. For now we recreate it every time we try
1365 this particular solver step. */ 1376 this particular solver step. */
1366 dsf = snewn(w*h, int); 1377 dsf = dsf_new(w*h);
1367 dsf_init(dsf, w*h);
1368 1378
1369 /* Work out the connectedness of the current loop set. */ 1379 /* Work out the connectedness of the current loop set. */
1370 for (x = 0; x < w; x++) { 1380 for (x = 0; x < w; x++) {
@@ -1402,7 +1412,7 @@ static int solve_check_loop(game_state *state)
1402 } 1412 }
1403 } 1413 }
1404 1414
1405 sfree(dsf); 1415 dsf_free(dsf);
1406 1416
1407 return did; 1417 return did;
1408} 1418}
@@ -1455,8 +1465,8 @@ static int solve_bridge_sub(game_state *state, int x, int y, int d,
1455 assert(d == D || d == R); 1465 assert(d == D || d == R);
1456 1466
1457 if (!sc->dsf) 1467 if (!sc->dsf)
1458 sc->dsf = snew_dsf(wh); 1468 sc->dsf = dsf_new(wh);
1459 dsf_init(sc->dsf, wh); 1469 dsf_reinit(sc->dsf);
1460 1470
1461 for (xi = 0; xi < w; xi++) { 1471 for (xi = 0; xi < w; xi++) {
1462 for (yi = 0; yi < h; yi++) { 1472 for (yi = 0; yi < h; yi++) {
@@ -1595,7 +1605,7 @@ static int tracks_solve(game_state *state, int diff, int *max_diff_out)
1595 break; 1605 break;
1596 } 1606 }
1597 1607
1598 sfree(sc->dsf); 1608 dsf_free(sc->dsf);
1599 1609
1600 if (max_diff_out) 1610 if (max_diff_out)
1601 *max_diff_out = max_diff; 1611 *max_diff_out = max_diff;
@@ -1775,7 +1785,7 @@ static void debug_state(game_state *state, const char *what) {
1775} 1785}
1776 1786
1777static void dsf_update_completion(game_state *state, int ax, int ay, 1787static void dsf_update_completion(game_state *state, int ax, int ay,
1778 char dir, int *dsf) 1788 char dir, DSF *dsf)
1779{ 1789{
1780 int w = state->p.w, ai = ay*w+ax, bx, by, bi; 1790 int w = state->p.w, ai = ay*w+ax, bx, by, bi;
1781 1791
@@ -1820,12 +1830,37 @@ static int tracks_neighbour(int vertex, void *vctx)
1820 return -1; 1830 return -1;
1821} 1831}
1822 1832
1833/*
1834 * The completion flash moves along the track, so we want to label
1835 * each tile with how far along the track it is. This is represented
1836 * as an eight-bit field, which is more than enough when the
1837 * completion flash is only 0.5 s long.
1838 */
1839static void set_flash_data(game_state *state)
1840{
1841 int ntrack = 0, x, y, n, d;
1842 const int w = state->p.w;
1843
1844 for (x = 0; x < w; x++)
1845 ntrack += state->numbers->numbers[x];
1846 n = 0; x = 0; y = state->numbers->row_s; d = R;
1847 do {
1848 state->sflags[y*w + x] &= ~(S_FLASH_MASK << S_FLASH_SHIFT);
1849 state->sflags[y*w + x] |=
1850 n++ * (S_FLASH_MASK / (ntrack - 1)) << S_FLASH_SHIFT;
1851 d = F(d); /* Find the direction we just arrived from. */
1852 d = S_E_DIRS(state, x, y, E_TRACK) & ~d; /* Other track from here. */
1853 x += DX(d); y += DY(d); /* Move to the next tile. */
1854 } while (INGRID(state, x, y));
1855}
1856
1823static bool check_completion(game_state *state, bool mark) 1857static bool check_completion(game_state *state, bool mark)
1824{ 1858{
1825 int w = state->p.w, h = state->p.h, x, y, i, target; 1859 int w = state->p.w, h = state->p.h, x, y, i, target;
1826 bool ret = true; 1860 bool ret = true, pathret;
1827 int ntrack, nnotrack, ntrackcomplete; 1861 int ntrack, nnotrack, ntrackcomplete;
1828 int *dsf, pathclass; 1862 DSF *dsf;
1863 int pathclass;
1829 struct findloopstate *fls; 1864 struct findloopstate *fls;
1830 struct tracks_neighbour_ctx ctx; 1865 struct tracks_neighbour_ctx ctx;
1831 1866
@@ -1844,60 +1879,7 @@ static bool check_completion(game_state *state, bool mark)
1844 } 1879 }
1845 } 1880 }
1846 1881
1847 /* A cell is 'complete', for the purposes of marking the game as 1882 dsf = dsf_new(w*h);
1848 * finished, if it has two edges marked as TRACK. But it only has
1849 * to have one edge marked as TRACK, or be filled in as trackful
1850 * without any specific edges known, to count towards checking
1851 * row/column clue errors. */
1852 for (x = 0; x < w; x++) {
1853 target = state->numbers->numbers[x];
1854 ntrack = nnotrack = ntrackcomplete = 0;
1855 for (y = 0; y < h; y++) {
1856 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1857 state->sflags[y*w+x] & S_TRACK)
1858 ntrack++;
1859 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1860 ntrackcomplete++;
1861 if (state->sflags[y*w+x] & S_NOTRACK)
1862 nnotrack++;
1863 }
1864 if (mark) {
1865 if (ntrack > target || nnotrack > (h-target)) {
1866 debug(("col %d error: target %d, track %d, notrack %d",
1867 x, target, ntrack, nnotrack));
1868 state->num_errors[x] = 1;
1869 ret = false;
1870 }
1871 }
1872 if (ntrackcomplete != target)
1873 ret = false;
1874 }
1875 for (y = 0; y < h; y++) {
1876 target = state->numbers->numbers[w+y];
1877 ntrack = nnotrack = ntrackcomplete = 0;
1878 for (x = 0; x < w; x++) {
1879 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1880 state->sflags[y*w+x] & S_TRACK)
1881 ntrack++;
1882 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1883 ntrackcomplete++;
1884 if (state->sflags[y*w+x] & S_NOTRACK)
1885 nnotrack++;
1886 }
1887 if (mark) {
1888 if (ntrack > target || nnotrack > (w-target)) {
1889 debug(("row %d error: target %d, track %d, notrack %d",
1890 y, target, ntrack, nnotrack));
1891 state->num_errors[w+y] = 1;
1892 ret = false;
1893 }
1894 }
1895 if (ntrackcomplete != target)
1896 ret = false;
1897 }
1898
1899 dsf = snewn(w*h, int);
1900 dsf_init(dsf, w*h);
1901 1883
1902 for (x = 0; x < w; x++) { 1884 for (x = 0; x < w; x++) {
1903 for (y = 0; y < h; y++) { 1885 for (y = 0; y < h; y++) {
@@ -1949,9 +1931,75 @@ static bool check_completion(game_state *state, bool mark)
1949 } 1931 }
1950 } 1932 }
1951 1933
1952 if (mark) 1934 /*
1935 * A cell is 'complete', for the purposes of marking the game as
1936 * finished, if it has two edges marked as TRACK. But it only has
1937 * to have one edge marked as TRACK, or be filled in as trackful
1938 * without any specific edges known, to count towards checking
1939 * row/column clue errors.
1940 *
1941 * This changes if we haven't found any other errors by this
1942 * point, so the player has constructed a route from A to B. In
1943 * that case, we highlight any row/column where the actually laid
1944 * tracks don't match the clue.
1945 */
1946 pathret = ret; /* Do we have a plausible solution so far? */
1947 for (x = 0; x < w; x++) {
1948 target = state->numbers->numbers[x];
1949 ntrack = nnotrack = ntrackcomplete = 0;
1950 for (y = 0; y < h; y++) {
1951 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1952 state->sflags[y*w+x] & S_TRACK)
1953 ntrack++;
1954 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1955 ntrackcomplete++;
1956 if (state->sflags[y*w+x] & S_NOTRACK)
1957 nnotrack++;
1958 }
1959 if (mark) {
1960 if (ntrack > target || nnotrack > (h-target) ||
1961 (pathret && ntrackcomplete != target)) {
1962 debug(("col %d error: target %d, track %d, notrack %d, "
1963 "pathret %d, trackcomplete %d",
1964 x, target, ntrack, nnotrack, pathret, ntrackcomplete));
1965 state->num_errors[x] = 1;
1966 ret = false;
1967 }
1968 }
1969 if (ntrackcomplete != target)
1970 ret = false;
1971 }
1972 for (y = 0; y < h; y++) {
1973 target = state->numbers->numbers[w+y];
1974 ntrack = nnotrack = ntrackcomplete = 0;
1975 for (x = 0; x < w; x++) {
1976 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1977 state->sflags[y*w+x] & S_TRACK)
1978 ntrack++;
1979 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1980 ntrackcomplete++;
1981 if (state->sflags[y*w+x] & S_NOTRACK)
1982 nnotrack++;
1983 }
1984 if (mark) {
1985 if (ntrack > target || nnotrack > (w-target) ||
1986 (pathret && ntrackcomplete != target)) {
1987 debug(("row %d error: target %d, track %d, notrack %d, "
1988 "pathret %d, trackcomplete %d",
1989 y, target, ntrack, nnotrack, pathret, ntrackcomplete));
1990 state->num_errors[w+y] = 1;
1991 ret = false;
1992 }
1993 }
1994 if (ntrackcomplete != target)
1995 ret = false;
1996 }
1997
1998 if (mark) {
1953 state->completed = ret; 1999 state->completed = ret;
1954 sfree(dsf); 2000 if (ret) set_flash_data(state);
2001 }
2002 dsf_free(dsf);
1955 return ret; 2003 return ret;
1956} 2004}
1957 2005
@@ -1974,7 +2022,7 @@ static game_ui *new_ui(const game_state *state)
1974 ui->notrack = false; 2022 ui->notrack = false;
1975 ui->dragging = false; 2023 ui->dragging = false;
1976 ui->drag_sx = ui->drag_sy = ui->drag_ex = ui->drag_ey = -1; 2024 ui->drag_sx = ui->drag_sy = ui->drag_ex = ui->drag_ey = -1;
1977 ui->cursor_active = false; 2025 ui->cursor_active = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1978 ui->curx = ui->cury = 1; 2026 ui->curx = ui->cury = 1;
1979 2027
1980 return ui; 2028 return ui;
@@ -1985,27 +2033,19 @@ static void free_ui(game_ui *ui)
1985 sfree(ui); 2033 sfree(ui);
1986} 2034}
1987 2035
1988static char *encode_ui(const game_ui *ui)
1989{
1990 return NULL;
1991}
1992
1993static void decode_ui(game_ui *ui, const char *encoding)
1994{
1995}
1996
1997static void game_changed_state(game_ui *ui, const game_state *oldstate, 2036static void game_changed_state(game_ui *ui, const game_state *oldstate,
1998 const game_state *newstate) 2037 const game_state *newstate)
1999{ 2038{
2000} 2039}
2001 2040
2002#define PREFERRED_TILE_SIZE 30 2041#define PREFERRED_TILE_SIZE 33
2003#define HALFSZ (ds->sz6*3) 2042#define HALFSZ (ds->sz6*3)
2004#define THIRDSZ (ds->sz6*2) 2043#define THIRDSZ (ds->sz6*2)
2005#define TILE_SIZE (ds->sz6*6) 2044#define TILE_SIZE (ds->sz6*6)
2006 2045
2007#define BORDER (TILE_SIZE/8) 2046#define MAX_BORDER (TILE_SIZE/8)
2008#define LINE_THICK (TILE_SIZE/16) 2047#define LINE_THICK (TILE_SIZE/16)
2048#define BORDER (ds->border)
2009#define GRID_LINE_TL (ds->grid_line_tl) 2049#define GRID_LINE_TL (ds->grid_line_tl)
2010#define GRID_LINE_BR (ds->grid_line_br) 2050#define GRID_LINE_BR (ds->grid_line_br)
2011#define GRID_LINE_ALL (ds->grid_line_all) 2051#define GRID_LINE_ALL (ds->grid_line_all)
@@ -2028,7 +2068,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2028#define DS_CSHIFT 20 /* R/U/L/D shift, for cursor-on-edge */ 2068#define DS_CSHIFT 20 /* R/U/L/D shift, for cursor-on-edge */
2029 2069
2030struct game_drawstate { 2070struct game_drawstate {
2031 int sz6, grid_line_all, grid_line_tl, grid_line_br; 2071 int sz6, border, grid_line_all, grid_line_tl, grid_line_br;
2032 bool started; 2072 bool started;
2033 2073
2034 int w, h, sz; 2074 int w, h, sz;
@@ -2120,6 +2160,35 @@ static bool ui_can_flip_square(const game_state *state, int x, int y, bool notra
2120 return true; 2160 return true;
2121} 2161}
2122 2162
2163static const char *current_key_label(const game_ui *ui,
2164 const game_state *state, int button)
2165{
2166 if (IS_CURSOR_SELECT(button) && ui->cursor_active) {
2167 int gx = ui->curx / 2, gy = ui->cury / 2;
2168 int w = state->p.w;
2169 int direction =
2170 ((ui->curx % 2) == 0) ? L : ((ui->cury % 2) == 0) ? U : 0;
2171 if (direction &&
2172 ui_can_flip_edge(state, gx, gy, direction,
2173 button == CURSOR_SELECT2)) {
2174 unsigned ef = S_E_FLAGS(state, gx, gy, direction);
2175 switch (button) {
2176 case CURSOR_SELECT: return (ef & E_TRACK) ? "Clear" : "Track";
2177 case CURSOR_SELECT2: return (ef & E_NOTRACK) ? "Clear" : "X";
2178 }
2179 }
2180 if (!direction &&
2181 ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2)) {
2182 unsigned sf = state->sflags[gy*w+gx];
2183 switch (button) {
2184 case CURSOR_SELECT: return (sf & S_TRACK) ? "Clear" : "Track";
2185 case CURSOR_SELECT2: return (sf & S_NOTRACK) ? "Clear" : "X";
2186 }
2187 }
2188 }
2189 return "";
2190}
2191
2123static char *edge_flip_str(const game_state *state, int x, int y, int dir, bool notrack, char *buf) { 2192static char *edge_flip_str(const game_state *state, int x, int y, int dir, bool notrack, char *buf) {
2124 unsigned ef = S_E_FLAGS(state, x, y, dir); 2193 unsigned ef = S_E_FLAGS(state, x, y, dir);
2125 char c; 2194 char c;
@@ -2193,6 +2262,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2193 2262
2194 if (!INGRID(state, gx, gy)) { 2263 if (!INGRID(state, gx, gy)) {
2195 /* can't drag from off grid */ 2264 /* can't drag from off grid */
2265 ui->drag_sx = ui->drag_sy = -1;
2196 return NULL; 2266 return NULL;
2197 } 2267 }
2198 2268
@@ -2209,13 +2279,13 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2209 ui->drag_sx = ui->drag_ex = gx; 2279 ui->drag_sx = ui->drag_ex = gx;
2210 ui->drag_sy = ui->drag_ey = gy; 2280 ui->drag_sy = ui->drag_ey = gy;
2211 2281
2212 return UI_UPDATE; 2282 return MOVE_UI_UPDATE;
2213 } 2283 }
2214 2284
2215 if (IS_MOUSE_DRAG(button)) { 2285 if (IS_MOUSE_DRAG(button)) {
2216 ui->cursor_active = false; 2286 ui->cursor_active = false;
2217 update_ui_drag(state, ui, gx, gy); 2287 update_ui_drag(state, ui, gx, gy);
2218 return UI_UPDATE; 2288 return MOVE_UI_UPDATE;
2219 } 2289 }
2220 2290
2221 if (IS_MOUSE_RELEASE(button)) { 2291 if (IS_MOUSE_RELEASE(button)) {
@@ -2252,12 +2322,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2252 cy = CENTERED_COORD(gy); 2322 cy = CENTERED_COORD(gy);
2253 2323
2254 if (!INGRID(state, gx, gy) || FROMCOORD(x) != gx || FROMCOORD(y) != gy) 2324 if (!INGRID(state, gx, gy) || FROMCOORD(x) != gx || FROMCOORD(y) != gy)
2255 return UI_UPDATE; 2325 return MOVE_UI_UPDATE;
2256 2326
2257 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) { 2327 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
2258 if (ui_can_flip_square(state, gx, gy, button == RIGHT_RELEASE)) 2328 if (ui_can_flip_square(state, gx, gy, button == RIGHT_RELEASE))
2259 return square_flip_str(state, gx, gy, button == RIGHT_RELEASE, tmpbuf); 2329 return square_flip_str(state, gx, gy, button == RIGHT_RELEASE, tmpbuf);
2260 return UI_UPDATE; 2330 return MOVE_UI_UPDATE;
2261 } else { 2331 } else {
2262 if (abs(x-cx) < abs(y-cy)) { 2332 if (abs(x-cx) < abs(y-cy)) {
2263 /* Closest to top/bottom edge. */ 2333 /* Closest to top/bottom edge. */
@@ -2271,7 +2341,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2271 return edge_flip_str(state, gx, gy, direction, 2341 return edge_flip_str(state, gx, gy, direction,
2272 button == RIGHT_RELEASE, tmpbuf); 2342 button == RIGHT_RELEASE, tmpbuf);
2273 else 2343 else
2274 return UI_UPDATE; 2344 return MOVE_UI_UPDATE;
2275 } 2345 }
2276 } 2346 }
2277 } 2347 }
@@ -2284,7 +2354,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2284 2354
2285 if (!ui->cursor_active) { 2355 if (!ui->cursor_active) {
2286 ui->cursor_active = true; 2356 ui->cursor_active = true;
2287 return UI_UPDATE; 2357 return MOVE_UI_UPDATE;
2288 } 2358 }
2289 2359
2290 ui->curx = ui->curx + dx; 2360 ui->curx = ui->curx + dx;
@@ -2295,17 +2365,17 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2295 } 2365 }
2296 ui->curx = min(max(ui->curx, 1), 2*w-1); 2366 ui->curx = min(max(ui->curx, 1), 2*w-1);
2297 ui->cury = min(max(ui->cury, 1), 2*h-1); 2367 ui->cury = min(max(ui->cury, 1), 2*h-1);
2298 return UI_UPDATE; 2368 return MOVE_UI_UPDATE;
2299 } 2369 }
2300 2370
2301 if (IS_CURSOR_SELECT(button)) { 2371 if (IS_CURSOR_SELECT(button)) {
2302 if (!ui->cursor_active) { 2372 if (!ui->cursor_active) {
2303 ui->cursor_active = true; 2373 ui->cursor_active = true;
2304 return UI_UPDATE; 2374 return MOVE_UI_UPDATE;
2305 } 2375 }
2306 /* click on square corner does nothing (shouldn't get here) */ 2376 /* click on square corner does nothing (shouldn't get here) */
2307 if ((ui->curx % 2) == 0 && (ui->cury % 2 == 0)) 2377 if ((ui->curx % 2) == 0 && (ui->cury % 2 == 0))
2308 return UI_UPDATE; 2378 return MOVE_UI_UPDATE;
2309 2379
2310 gx = ui->curx / 2; 2380 gx = ui->curx / 2;
2311 gy = ui->cury / 2; 2381 gy = ui->cury / 2;
@@ -2317,7 +2387,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2317 else if (!direction && 2387 else if (!direction &&
2318 ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2)) 2388 ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2))
2319 return square_flip_str(state, gx, gy, button == CURSOR_SELECT2, tmpbuf); 2389 return square_flip_str(state, gx, gy, button == CURSOR_SELECT2, tmpbuf);
2320 return UI_UPDATE; 2390 return MOVE_UI_UPDATE;
2321 } 2391 }
2322 2392
2323#if 0 2393#if 0
@@ -2334,6 +2404,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2334 int w = state->p.w, x, y, n, i; 2404 int w = state->p.w, x, y, n, i;
2335 char c, d; 2405 char c, d;
2336 unsigned f; 2406 unsigned f;
2407 bool move_is_solve = false;
2337 game_state *ret = dup_game(state); 2408 game_state *ret = dup_game(state);
2338 2409
2339 /* this is breaking the bank on GTK, which vsprintf's into a fixed-size buffer 2410 /* this is breaking the bank on GTK, which vsprintf's into a fixed-size buffer
@@ -2344,6 +2415,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2344 c = *move; 2415 c = *move;
2345 if (c == 'S') { 2416 if (c == 'S') {
2346 ret->used_solve = true; 2417 ret->used_solve = true;
2418 move_is_solve = true;
2347 move++; 2419 move++;
2348 } else if (c == 'T' || c == 't' || c == 'N' || c == 'n') { 2420 } else if (c == 'T' || c == 't' || c == 'N' || c == 'n') {
2349 /* set track, clear track; set notrack, clear notrack */ 2421 /* set track, clear track; set notrack, clear notrack */
@@ -2355,6 +2427,9 @@ static game_state *execute_move(const game_state *state, const char *move)
2355 f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK; 2427 f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK;
2356 2428
2357 if (d == 'S') { 2429 if (d == 'S') {
2430 if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK) &&
2431 !move_is_solve)
2432 goto badmove;
2358 if (c == 'T' || c == 'N') 2433 if (c == 'T' || c == 'N')
2359 ret->sflags[y*w+x] |= f; 2434 ret->sflags[y*w+x] |= f;
2360 else 2435 else
@@ -2364,6 +2439,9 @@ static game_state *execute_move(const game_state *state, const char *move)
2364 unsigned df = 1<<i; 2439 unsigned df = 1<<i;
2365 2440
2366 if (MOVECHAR(df) == d) { 2441 if (MOVECHAR(df) == d) {
2442 if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK) &&
2443 !move_is_solve)
2444 goto badmove;
2367 if (c == 'T' || c == 'N') 2445 if (c == 'T' || c == 'N')
2368 S_E_SET(ret, x, y, df, f); 2446 S_E_SET(ret, x, y, df, f);
2369 else 2447 else
@@ -2401,13 +2479,21 @@ static game_state *execute_move(const game_state *state, const char *move)
2401#define FLASH_TIME 0.5F 2479#define FLASH_TIME 0.5F
2402 2480
2403static void game_compute_size(const game_params *params, int tilesize, 2481static void game_compute_size(const game_params *params, int tilesize,
2404 int *x, int *y) 2482 const game_ui *ui, int *x, int *y)
2405{ 2483{
2406 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2484 /* Ick: fake up `ds->sz6' and `ds->border` for macro expansion purposes */
2407 struct { 2485 struct {
2408 int sz6; 2486 int sz6, border;
2409 } ads, *ds = &ads; 2487 } ads, *ds = &ads;
2410 ads.sz6 = tilesize/6; 2488 ads.sz6 = tilesize/6;
2489 ads.border = MAX_BORDER;
2490 /*
2491 * Allow a reduced border at small tile sizes because the steps
2492 * are quite large and it's better to have a thin border than
2493 * to go down to a smaller tile size.
2494 */
2495 if (ads.border <= 5)
2496 ads.border = min(tilesize % 6, MAX_BORDER);
2411 *x = (params->w+2) * TILE_SIZE + 2 * BORDER; 2497 *x = (params->w+2) * TILE_SIZE + 2 * BORDER;
2412 *y = (params->h+2) * TILE_SIZE + 2 * BORDER; 2498 *y = (params->h+2) * TILE_SIZE + 2 * BORDER;
2413} 2499}
@@ -2416,14 +2502,16 @@ static void game_set_size(drawing *dr, game_drawstate *ds,
2416 const game_params *params, int tilesize) 2502 const game_params *params, int tilesize)
2417{ 2503{
2418 ds->sz6 = tilesize/6; 2504 ds->sz6 = tilesize/6;
2505 ds->border = MAX_BORDER;
2506 if (ds->border <= 5)
2507 ds->border = min(tilesize % 6, MAX_BORDER);
2419 ds->grid_line_all = max(LINE_THICK, 1); 2508 ds->grid_line_all = max(LINE_THICK, 1);
2420 ds->grid_line_br = ds->grid_line_all / 2; 2509 ds->grid_line_br = ds->grid_line_all / 2;
2421 ds->grid_line_tl = ds->grid_line_all - ds->grid_line_br; 2510 ds->grid_line_tl = ds->grid_line_all - ds->grid_line_br;
2422} 2511}
2423 2512
2424enum { 2513enum {
2425 COL_BACKGROUND, COL_LOWLIGHT, COL_HIGHLIGHT, 2514 COL_BACKGROUND, COL_TRACK_BACKGROUND,
2426 COL_TRACK_BACKGROUND = COL_LOWLIGHT,
2427 COL_GRID, COL_CLUE, COL_CURSOR, 2515 COL_GRID, COL_CLUE, COL_CURSOR,
2428 COL_TRACK, COL_TRACK_CLUE, COL_SLEEPER, 2516 COL_TRACK, COL_TRACK_CLUE, COL_SLEEPER,
2429 COL_DRAGON, COL_DRAGOFF, 2517 COL_DRAGON, COL_DRAGOFF,
@@ -2436,14 +2524,15 @@ static float *game_colours(frontend *fe, int *ncolours)
2436 float *ret = snewn(3 * NCOLOURS, float); 2524 float *ret = snewn(3 * NCOLOURS, float);
2437 int i; 2525 int i;
2438 2526
2439 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 2527 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_TRACK_BACKGROUND);
2528 colour_mix(&ret[COL_BACKGROUND*3], &ret[COL_TRACK_BACKGROUND*3], 0.5F,
2529 &ret[COL_GRID*3]);
2440 2530
2441 for (i = 0; i < 3; i++) { 2531 for (i = 0; i < 3; i++) {
2442 ret[COL_TRACK_CLUE * 3 + i] = 0.0F; 2532 ret[COL_TRACK_CLUE * 3 + i] = 0.0F;
2443 ret[COL_TRACK * 3 + i] = 0.5F; 2533 ret[COL_TRACK * 3 + i] = 0.5F;
2444 ret[COL_CLUE * 3 + i] = 0.0F; 2534 ret[COL_CLUE * 3 + i] = 0.0F;
2445 ret[COL_GRID * 3 + i] = 0.75F; 2535 ret[COL_CURSOR * 3 + i] = 0.3F;
2446 ret[COL_CURSOR * 3 + i] = 0.6F;
2447 ret[COL_ERROR_BACKGROUND * 3 + i] = 1.0F; 2536 ret[COL_ERROR_BACKGROUND * 3 + i] = 1.0F;
2448 } 2537 }
2449 2538
@@ -2535,7 +2624,8 @@ static void draw_thick_circle_outline(drawing *dr, float thickness,
2535 x2 = cx + r*(float)cos(th2); 2624 x2 = cx + r*(float)cos(th2);
2536 y1 = cy + r*(float)sin(th); 2625 y1 = cy + r*(float)sin(th);
2537 y2 = cy + r*(float)sin(th2); 2626 y2 = cy + r*(float)sin(th2);
2538 debug(("circ outline: x=%.2f -> %.2f, thick=%.2f", x1, x2, thickness)); 2627 debug(("circ outline: x=%.2f -> %.2f, thick=%.2f\n",
2628 x1, x2, thickness));
2539 draw_thick_line(dr, thickness, x1, y1, x2, y2, colour); 2629 draw_thick_line(dr, thickness, x1, y1, x2, y2, colour);
2540 } 2630 }
2541} 2631}
@@ -2665,7 +2755,7 @@ static void draw_square(drawing *dr, game_drawstate *ds,
2665 curx = ox + TILE_SIZE - off; curw = 2*off + 1; 2755 curx = ox + TILE_SIZE - off; curw = 2*off + 1;
2666 } 2756 }
2667 2757
2668 draw_rect_outline(dr, curx, cury, curw, curh, COL_GRID); 2758 draw_rect_outline(dr, curx, cury, curw, curh, COL_CURSOR);
2669 } 2759 }
2670 2760
2671 /* Draw tracks themselves */ 2761 /* Draw tracks themselves */
@@ -2777,20 +2867,11 @@ static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldst
2777 const game_state *state, int dir, const game_ui *ui, 2867 const game_state *state, int dir, const game_ui *ui,
2778 float animtime, float flashtime) 2868 float animtime, float flashtime)
2779{ 2869{
2780 int i, x, y, flashing = 0, w = ds->w, h = ds->h; 2870 int i, x, y, flashing, w = ds->w, h = ds->h;
2781 bool force = false; 2871 bool force = false;
2782 game_state *drag_state = NULL; 2872 game_state *drag_state = NULL;
2783 2873
2784 if (!ds->started) { 2874 if (!ds->started) {
2785 /*
2786 * The initial contents of the window are not guaranteed and
2787 * can vary with front ends. To be on the safe side, all games
2788 * should start by drawing a big background-colour rectangle
2789 * covering the whole window.
2790 */
2791 draw_rect(dr, 0, 0, (w+2)*TILE_SIZE + 2*BORDER, (h+2)*TILE_SIZE + 2*BORDER,
2792 COL_BACKGROUND);
2793
2794 draw_loop_ends(dr, ds, state, COL_CLUE); 2875 draw_loop_ends(dr, ds, state, COL_CLUE);
2795 2876
2796 draw_rect(dr, COORD(0) - GRID_LINE_BR, COORD(0) - GRID_LINE_BR, 2877 draw_rect(dr, COORD(0) - GRID_LINE_BR, COORD(0) - GRID_LINE_BR,
@@ -2812,11 +2893,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldst
2812 } 2893 }
2813 } 2894 }
2814 2895
2815 if (flashtime > 0 &&
2816 (flashtime <= FLASH_TIME/3 ||
2817 flashtime >= FLASH_TIME*2/3))
2818 flashing = DS_FLASH;
2819
2820 if (ui->dragging) 2896 if (ui->dragging)
2821 drag_state = copy_and_apply_drag(state, ui); 2897 drag_state = copy_and_apply_drag(state, ui);
2822 2898
@@ -2824,6 +2900,16 @@ static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldst
2824 for (y = 0; y < h; y++) { 2900 for (y = 0; y < h; y++) {
2825 unsigned int f, f_d; 2901 unsigned int f, f_d;
2826 2902
2903 flashing = 0;
2904 if (flashtime > 0) {
2905 float flashpos =
2906 (state->sflags[y*w+x] >> S_FLASH_SHIFT & S_FLASH_MASK) /
2907 (float)S_FLASH_MASK;
2908 if (flashtime > FLASH_TIME / 2 * flashpos &&
2909 flashtime <= FLASH_TIME / 2 * (flashpos + 1.0F))
2910 flashing = DS_FLASH;
2911 }
2912
2827 f = s2d_flags(state, x, y, ui) | flashing; 2913 f = s2d_flags(state, x, y, ui) | flashing;
2828 f_d = drag_state ? s2d_flags(drag_state, x, y, ui) : f; 2914 f_d = drag_state ? s2d_flags(drag_state, x, y, ui) : f;
2829 2915
@@ -2890,22 +2976,19 @@ static int game_status(const game_state *state)
2890 return state->completed ? +1 : 0; 2976 return state->completed ? +1 : 0;
2891} 2977}
2892 2978
2893static bool game_timing_state(const game_state *state, game_ui *ui) 2979static void game_print_size(const game_params *params, const game_ui *ui,
2894{ 2980 float *x, float *y)
2895 return true;
2896}
2897
2898static void game_print_size(const game_params *params, float *x, float *y)
2899{ 2981{
2900 int pw, ph; 2982 int pw, ph;
2901 2983
2902 /* The Times uses 7mm squares */ 2984 /* The Times uses 7mm squares */
2903 game_compute_size(params, 700, &pw, &ph); 2985 game_compute_size(params, 700, ui, &pw, &ph);
2904 *x = pw / 100.0F; 2986 *x = pw / 100.0F;
2905 *y = ph / 100.0F; 2987 *y = ph / 100.0F;
2906} 2988}
2907 2989
2908static void game_print(drawing *dr, const game_state *state, int tilesize) 2990static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2991 int tilesize)
2909{ 2992{
2910 int w = state->p.w, h = state->p.h; 2993 int w = state->p.w, h = state->p.h;
2911 int black = print_mono_colour(dr, 0), grey = print_grey_colour(dr, 0.5F); 2994 int black = print_mono_colour(dr, 0), grey = print_grey_colour(dr, 0.5F);
@@ -2948,7 +3031,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
2948#endif 3031#endif
2949 3032
2950const struct game thegame = { 3033const struct game thegame = {
2951 "Tracks", "games.tracks", "tracks", 3034 "Train Tracks", "games.tracks", "tracks",
2952 default_params, 3035 default_params,
2953 game_fetch_preset, NULL, 3036 game_fetch_preset, NULL,
2954 decode_params, 3037 decode_params,
@@ -2964,12 +3047,14 @@ const struct game thegame = {
2964 free_game, 3047 free_game,
2965 true, solve_game, 3048 true, solve_game,
2966 true, game_can_format_as_text_now, game_text_format, 3049 true, game_can_format_as_text_now, game_text_format,
3050 NULL, NULL, /* get_prefs, set_prefs */
2967 new_ui, 3051 new_ui,
2968 free_ui, 3052 free_ui,
2969 encode_ui, 3053 NULL, /* encode_ui */
2970 decode_ui, 3054 NULL, /* decode_ui */
2971 NULL, /* game_request_keys */ 3055 NULL, /* game_request_keys */
2972 game_changed_state, 3056 game_changed_state,
3057 current_key_label,
2973 interpret_move, 3058 interpret_move,
2974 execute_move, 3059 execute_move,
2975 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3060 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2983,7 +3068,7 @@ const struct game thegame = {
2983 game_status, 3068 game_status,
2984 true, false, game_print_size, game_print, 3069 true, false, game_print_size, game_print,
2985 false, /* wants_statusbar */ 3070 false, /* wants_statusbar */
2986 false, game_timing_state, 3071 false, NULL, /* timing_state */
2987 0, /* flags */ 3072 0, /* flags */
2988}; 3073};
2989 3074
diff --git a/apps/plugins/puzzles/src/tree234.c b/apps/plugins/puzzles/src/tree234.c
index ad8eb047cd..ef2c3ffaf7 100644
--- a/apps/plugins/puzzles/src/tree234.c
+++ b/apps/plugins/puzzles/src/tree234.c
@@ -29,33 +29,25 @@
29#include <stdlib.h> 29#include <stdlib.h>
30#include <assert.h> 30#include <assert.h>
31 31
32#define TREE234_INTERNALS
32#include "tree234.h" 33#include "tree234.h"
33 34
34#include "puzzles.h" /* for smalloc/sfree */ 35#include "puzzles.h" /* for smalloc/sfree */
35 36
36#ifdef TEST 37#ifdef DEBUG_TREE234
37#define LOG(x) (printf x) 38#include <stdarg.h>
38#define smalloc malloc 39static void logprintf(const char *fmt, ...)
39#define srealloc realloc 40{
40#define sfree free 41 va_list ap;
42 va_start(ap, fmt);
43 vprintf(fmt, ap);
44 va_end(ap);
45}
46#define LOG(x) (logprintf x)
41#else 47#else
42#define LOG(x) 48#define LOG(x)
43#endif 49#endif
44 50
45typedef struct node234_Tag node234;
46
47struct tree234_Tag {
48 node234 *root;
49 cmpfn234 cmp;
50};
51
52struct node234_Tag {
53 node234 *parent;
54 node234 *kids[4];
55 int counts[4];
56 void *elems[3];
57};
58
59/* 51/*
60 * Create a 2-3-4 tree. 52 * Create a 2-3-4 tree.
61 */ 53 */
@@ -327,7 +319,7 @@ static void *add234_internal(tree234 *t, void *e, int index) {
327 } 319 }
328 320
329 n = t->root; 321 n = t->root;
330 while (n) { 322 do {
331 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", 323 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
332 n, 324 n,
333 n->kids[0], n->counts[0], n->elems[0], 325 n->kids[0], n->counts[0], n->elems[0],
@@ -380,7 +372,7 @@ static void *add234_internal(tree234 *t, void *e, int index) {
380 if (!n->kids[ki]) 372 if (!n->kids[ki])
381 break; 373 break;
382 n = n->kids[ki]; 374 n = n->kids[ki];
383 } 375 } while (n);
384 376
385 add234_insert(NULL, e, NULL, &t->root, n, ki); 377 add234_insert(NULL, e, NULL, &t->root, n, ki);
386 378
@@ -1104,7 +1096,7 @@ static node234 *join234_internal(node234 *left, void *sep,
1104 1096
1105 return root; 1097 return root;
1106} 1098}
1107static int height234(tree234 *t) { 1099int height234(tree234 *t) {
1108 int level = 0; 1100 int level = 0;
1109 node234 *n = t->root; 1101 node234 *n = t->root;
1110 while (n) { 1102 while (n) {
@@ -1452,749 +1444,3 @@ tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate) {
1452 1444
1453 return t2; 1445 return t2;
1454} 1446}
1455
1456#ifdef TEST
1457
1458/*
1459 * Test code for the 2-3-4 tree. This code maintains an alternative
1460 * representation of the data in the tree, in an array (using the
1461 * obvious and slow insert and delete functions). After each tree
1462 * operation, the verify() function is called, which ensures all
1463 * the tree properties are preserved:
1464 * - node->child->parent always equals node
1465 * - tree->root->parent always equals NULL
1466 * - number of kids == 0 or number of elements + 1;
1467 * - tree has the same depth everywhere
1468 * - every node has at least one element
1469 * - subtree element counts are accurate
1470 * - any NULL kid pointer is accompanied by a zero count
1471 * - in a sorted tree: ordering property between elements of a
1472 * node and elements of its children is preserved
1473 * and also ensures the list represented by the tree is the same
1474 * list it should be. (This last check also doubly verifies the
1475 * ordering properties, because the `same list it should be' is by
1476 * definition correctly ordered. It also ensures all nodes are
1477 * distinct, because the enum functions would get caught in a loop
1478 * if not.)
1479 */
1480
1481#include <string.h>
1482#include <stdarg.h>
1483
1484#define srealloc realloc
1485
1486/*
1487 * Error reporting function.
1488 */
1489void error(char *fmt, ...) {
1490 va_list ap;
1491 printf("ERROR: ");
1492 va_start(ap, fmt);
1493 vfprintf(stdout, fmt, ap);
1494 va_end(ap);
1495 printf("\n");
1496}
1497
1498/* The array representation of the data. */
1499void **array;
1500int arraylen, arraysize;
1501cmpfn234 cmp;
1502
1503/* The tree representation of the same data. */
1504tree234 *tree;
1505
1506/*
1507 * Routines to provide a diagnostic printout of a tree. Currently
1508 * relies on every element in the tree being a one-character string
1509 * :-)
1510 */
1511typedef struct {
1512 char **levels;
1513} dispctx;
1514
1515int dispnode(node234 *n, int level, dispctx *ctx) {
1516 if (level == 0) {
1517 int xpos = strlen(ctx->levels[0]);
1518 int len;
1519
1520 if (n->elems[2])
1521 len = sprintf(ctx->levels[0]+xpos, " %s%s%s",
1522 n->elems[0], n->elems[1], n->elems[2]);
1523 else if (n->elems[1])
1524 len = sprintf(ctx->levels[0]+xpos, " %s%s",
1525 n->elems[0], n->elems[1]);
1526 else
1527 len = sprintf(ctx->levels[0]+xpos, " %s",
1528 n->elems[0]);
1529 return xpos + 1 + (len-1) / 2;
1530 } else {
1531 int xpos[4], nkids;
1532 int nodelen, mypos, myleft, x, i;
1533
1534 xpos[0] = dispnode(n->kids[0], level-3, ctx);
1535 xpos[1] = dispnode(n->kids[1], level-3, ctx);
1536 nkids = 2;
1537 if (n->kids[2]) {
1538 xpos[2] = dispnode(n->kids[2], level-3, ctx);
1539 nkids = 3;
1540 }
1541 if (n->kids[3]) {
1542 xpos[3] = dispnode(n->kids[3], level-3, ctx);
1543 nkids = 4;
1544 }
1545
1546 if (nkids == 4)
1547 mypos = (xpos[1] + xpos[2]) / 2;
1548 else if (nkids == 3)
1549 mypos = xpos[1];
1550 else
1551 mypos = (xpos[0] + xpos[1]) / 2;
1552 nodelen = nkids * 2 - 1;
1553 myleft = mypos - ((nodelen-1)/2);
1554 assert(myleft >= xpos[0]);
1555 assert(myleft + nodelen-1 <= xpos[nkids-1]);
1556
1557 x = strlen(ctx->levels[level]);
1558 while (x <= xpos[0] && x < myleft)
1559 ctx->levels[level][x++] = ' ';
1560 while (x < myleft)
1561 ctx->levels[level][x++] = '_';
1562 if (nkids==4)
1563 x += sprintf(ctx->levels[level]+x, ".%s.%s.%s.",
1564 n->elems[0], n->elems[1], n->elems[2]);
1565 else if (nkids==3)
1566 x += sprintf(ctx->levels[level]+x, ".%s.%s.",
1567 n->elems[0], n->elems[1]);
1568 else
1569 x += sprintf(ctx->levels[level]+x, ".%s.",
1570 n->elems[0]);
1571 while (x < xpos[nkids-1])
1572 ctx->levels[level][x++] = '_';
1573 ctx->levels[level][x] = '\0';
1574
1575 x = strlen(ctx->levels[level-1]);
1576 for (i = 0; i < nkids; i++) {
1577 int rpos, pos;
1578 rpos = xpos[i];
1579 if (i > 0 && i < nkids-1)
1580 pos = myleft + 2*i;
1581 else
1582 pos = rpos;
1583 if (rpos < pos)
1584 rpos++;
1585 while (x < pos && x < rpos)
1586 ctx->levels[level-1][x++] = ' ';
1587 if (x == pos)
1588 ctx->levels[level-1][x++] = '|';
1589 while (x < pos || x < rpos)
1590 ctx->levels[level-1][x++] = '_';
1591 if (x == pos)
1592 ctx->levels[level-1][x++] = '|';
1593 }
1594 ctx->levels[level-1][x] = '\0';
1595
1596 x = strlen(ctx->levels[level-2]);
1597 for (i = 0; i < nkids; i++) {
1598 int rpos = xpos[i];
1599
1600 while (x < rpos)
1601 ctx->levels[level-2][x++] = ' ';
1602 ctx->levels[level-2][x++] = '|';
1603 }
1604 ctx->levels[level-2][x] = '\0';
1605
1606 return mypos;
1607 }
1608}
1609
1610void disptree(tree234 *t) {
1611 dispctx ctx;
1612 char *leveldata;
1613 int width = count234(t);
1614 int ht = height234(t) * 3 - 2;
1615 int i;
1616
1617 if (!t->root) {
1618 printf("[empty tree]\n");
1619 }
1620
1621 leveldata = smalloc(ht * (width+2));
1622 ctx.levels = smalloc(ht * sizeof(char *));
1623 for (i = 0; i < ht; i++) {
1624 ctx.levels[i] = leveldata + i * (width+2);
1625 ctx.levels[i][0] = '\0';
1626 }
1627
1628 (void) dispnode(t->root, ht-1, &ctx);
1629
1630 for (i = ht; i-- ;)
1631 printf("%s\n", ctx.levels[i]);
1632
1633 sfree(ctx.levels);
1634 sfree(leveldata);
1635}
1636
1637typedef struct {
1638 int treedepth;
1639 int elemcount;
1640} chkctx;
1641
1642int chknode(chkctx *ctx, int level, node234 *node,
1643 void *lowbound, void *highbound) {
1644 int nkids, nelems;
1645 int i;
1646 int count;
1647
1648 /* Count the non-NULL kids. */
1649 for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);
1650 /* Ensure no kids beyond the first NULL are non-NULL. */
1651 for (i = nkids; i < 4; i++)
1652 if (node->kids[i]) {
1653 error("node %p: nkids=%d but kids[%d] non-NULL",
1654 node, nkids, i);
1655 } else if (node->counts[i]) {
1656 error("node %p: kids[%d] NULL but count[%d]=%d nonzero",
1657 node, i, i, node->counts[i]);
1658 }
1659
1660 /* Count the non-NULL elements. */
1661 for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);
1662 /* Ensure no elements beyond the first NULL are non-NULL. */
1663 for (i = nelems; i < 3; i++)
1664 if (node->elems[i]) {
1665 error("node %p: nelems=%d but elems[%d] non-NULL",
1666 node, nelems, i);
1667 }
1668
1669 if (nkids == 0) {
1670 /*
1671 * If nkids==0, this is a leaf node; verify that the tree
1672 * depth is the same everywhere.
1673 */
1674 if (ctx->treedepth < 0)
1675 ctx->treedepth = level; /* we didn't know the depth yet */
1676 else if (ctx->treedepth != level)
1677 error("node %p: leaf at depth %d, previously seen depth %d",
1678 node, level, ctx->treedepth);
1679 } else {
1680 /*
1681 * If nkids != 0, then it should be nelems+1, unless nelems
1682 * is 0 in which case nkids should also be 0 (and so we
1683 * shouldn't be in this condition at all).
1684 */
1685 int shouldkids = (nelems ? nelems+1 : 0);
1686 if (nkids != shouldkids) {
1687 error("node %p: %d elems should mean %d kids but has %d",
1688 node, nelems, shouldkids, nkids);
1689 }
1690 }
1691
1692 /*
1693 * nelems should be at least 1.
1694 */
1695 if (nelems == 0) {
1696 error("node %p: no elems", node, nkids);
1697 }
1698
1699 /*
1700 * Add nelems to the running element count of the whole tree.
1701 */
1702 ctx->elemcount += nelems;
1703
1704 /*
1705 * Check ordering property: all elements should be strictly >
1706 * lowbound, strictly < highbound, and strictly < each other in
1707 * sequence. (lowbound and highbound are NULL at edges of tree
1708 * - both NULL at root node - and NULL is considered to be <
1709 * everything and > everything. IYSWIM.)
1710 */
1711 if (cmp) {
1712 for (i = -1; i < nelems; i++) {
1713 void *lower = (i == -1 ? lowbound : node->elems[i]);
1714 void *higher = (i+1 == nelems ? highbound : node->elems[i+1]);
1715 if (lower && higher && cmp(lower, higher) >= 0) {
1716 error("node %p: kid comparison [%d=%s,%d=%s] failed",
1717 node, i, lower, i+1, higher);
1718 }
1719 }
1720 }
1721
1722 /*
1723 * Check parent pointers: all non-NULL kids should have a
1724 * parent pointer coming back to this node.
1725 */
1726 for (i = 0; i < nkids; i++)
1727 if (node->kids[i]->parent != node) {
1728 error("node %p kid %d: parent ptr is %p not %p",
1729 node, i, node->kids[i]->parent, node);
1730 }
1731
1732
1733 /*
1734 * Now (finally!) recurse into subtrees.
1735 */
1736 count = nelems;
1737
1738 for (i = 0; i < nkids; i++) {
1739 void *lower = (i == 0 ? lowbound : node->elems[i-1]);
1740 void *higher = (i >= nelems ? highbound : node->elems[i]);
1741 int subcount = chknode(ctx, level+1, node->kids[i], lower, higher);
1742 if (node->counts[i] != subcount) {
1743 error("node %p kid %d: count says %d, subtree really has %d",
1744 node, i, node->counts[i], subcount);
1745 }
1746 count += subcount;
1747 }
1748
1749 return count;
1750}
1751
1752void verifytree(tree234 *tree, void **array, int arraylen) {
1753 chkctx ctx;
1754 int i;
1755 void *p;
1756
1757 ctx.treedepth = -1; /* depth unknown yet */
1758 ctx.elemcount = 0; /* no elements seen yet */
1759 /*
1760 * Verify validity of tree properties.
1761 */
1762 if (tree->root) {
1763 if (tree->root->parent != NULL)
1764 error("root->parent is %p should be null", tree->root->parent);
1765 chknode(&ctx, 0, tree->root, NULL, NULL);
1766 }
1767 printf("tree depth: %d\n", ctx.treedepth);
1768 /*
1769 * Enumerate the tree and ensure it matches up to the array.
1770 */
1771 for (i = 0; NULL != (p = index234(tree, i)); i++) {
1772 if (i >= arraylen)
1773 error("tree contains more than %d elements", arraylen);
1774 if (array[i] != p)
1775 error("enum at position %d: array says %s, tree says %s",
1776 i, array[i], p);
1777 }
1778 if (ctx.elemcount != i) {
1779 error("tree really contains %d elements, enum gave %d",
1780 ctx.elemcount, i);
1781 }
1782 if (i < arraylen) {
1783 error("enum gave only %d elements, array has %d", i, arraylen);
1784 }
1785 i = count234(tree);
1786 if (ctx.elemcount != i) {
1787 error("tree really contains %d elements, count234 gave %d",
1788 ctx.elemcount, i);
1789 }
1790}
1791void verify(void) { verifytree(tree, array, arraylen); }
1792
1793void internal_addtest(void *elem, int index, void *realret) {
1794 int i, j;
1795 void *retval;
1796
1797 if (arraysize < arraylen+1) {
1798 arraysize = arraylen+1+256;
1799 array = (array == NULL ? smalloc(arraysize*sizeof(*array)) :
1800 srealloc(array, arraysize*sizeof(*array)));
1801 }
1802
1803 i = index;
1804 /* now i points to the first element >= elem */
1805 retval = elem; /* expect elem returned (success) */
1806 for (j = arraylen; j > i; j--)
1807 array[j] = array[j-1];
1808 array[i] = elem; /* add elem to array */
1809 arraylen++;
1810
1811 if (realret != retval) {
1812 error("add: retval was %p expected %p", realret, retval);
1813 }
1814
1815 verify();
1816}
1817
1818void addtest(void *elem) {
1819 int i;
1820 void *realret;
1821
1822 realret = add234(tree, elem);
1823
1824 i = 0;
1825 while (i < arraylen && cmp(elem, array[i]) > 0)
1826 i++;
1827 if (i < arraylen && !cmp(elem, array[i])) {
1828 void *retval = array[i]; /* expect that returned not elem */
1829 if (realret != retval) {
1830 error("add: retval was %p expected %p", realret, retval);
1831 }
1832 } else
1833 internal_addtest(elem, i, realret);
1834}
1835
1836void addpostest(void *elem, int i) {
1837 void *realret;
1838
1839 realret = addpos234(tree, elem, i);
1840
1841 internal_addtest(elem, i, realret);
1842}
1843
1844void delpostest(int i) {
1845 int index = i;
1846 void *elem = array[i], *ret;
1847
1848 /* i points to the right element */
1849 while (i < arraylen-1) {
1850 array[i] = array[i+1];
1851 i++;
1852 }
1853 arraylen--; /* delete elem from array */
1854
1855 if (tree->cmp)
1856 ret = del234(tree, elem);
1857 else
1858 ret = delpos234(tree, index);
1859
1860 if (ret != elem) {
1861 error("del returned %p, expected %p", ret, elem);
1862 }
1863
1864 verify();
1865}
1866
1867void deltest(void *elem) {
1868 int i;
1869
1870 i = 0;
1871 while (i < arraylen && cmp(elem, array[i]) > 0)
1872 i++;
1873 if (i >= arraylen || cmp(elem, array[i]) != 0)
1874 return; /* don't do it! */
1875 delpostest(i);
1876}
1877
1878/* A sample data set and test utility. Designed for pseudo-randomness,
1879 * and yet repeatability. */
1880
1881/*
1882 * This random number generator uses the `portable implementation'
1883 * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;
1884 * change it if not.
1885 */
1886int randomnumber(unsigned *seed) {
1887 *seed *= 1103515245;
1888 *seed += 12345;
1889 return ((*seed) / 65536) % 32768;
1890}
1891
1892int mycmp(void *av, void *bv) {
1893 char const *a = (char const *)av;
1894 char const *b = (char const *)bv;
1895 return strcmp(a, b);
1896}
1897
1898char *strings[] = {
1899 "0", "2", "3", "I", "K", "d", "H", "J", "Q", "N", "n", "q", "j", "i",
1900 "7", "G", "F", "D", "b", "x", "g", "B", "e", "v", "V", "T", "f", "E",
1901 "S", "8", "A", "k", "X", "p", "C", "R", "a", "o", "r", "O", "Z", "u",
1902 "6", "1", "w", "L", "P", "M", "c", "U", "h", "9", "t", "5", "W", "Y",
1903 "m", "s", "l", "4",
1904#if 0
1905 "a", "ab", "absque", "coram", "de",
1906 "palam", "clam", "cum", "ex", "e",
1907 "sine", "tenus", "pro", "prae",
1908 "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",
1909 "penguin", "blancmange", "pangolin", "whale", "hedgehog",
1910 "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",
1911 "murfl", "spoo", "breen", "flarn", "octothorpe",
1912 "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",
1913 "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",
1914 "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",
1915 "wand", "ring", "amulet"
1916#endif
1917};
1918
1919#define NSTR lenof(strings)
1920
1921void findtest(void) {
1922 static const int rels[] = {
1923 REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT
1924 };
1925 static const char *const relnames[] = {
1926 "EQ", "GE", "LE", "LT", "GT"
1927 };
1928 int i, j, rel, index;
1929 char *p, *ret, *realret, *realret2;
1930 int lo, hi, mid, c;
1931
1932 for (i = 0; i < (int)NSTR; i++) {
1933 p = strings[i];
1934 for (j = 0; j < (int)(sizeof(rels)/sizeof(*rels)); j++) {
1935 rel = rels[j];
1936
1937 lo = 0; hi = arraylen-1;
1938 while (lo <= hi) {
1939 mid = (lo + hi) / 2;
1940 c = strcmp(p, array[mid]);
1941 if (c < 0)
1942 hi = mid-1;
1943 else if (c > 0)
1944 lo = mid+1;
1945 else
1946 break;
1947 }
1948
1949 if (c == 0) {
1950 if (rel == REL234_LT)
1951 ret = (mid > 0 ? array[--mid] : NULL);
1952 else if (rel == REL234_GT)
1953 ret = (mid < arraylen-1 ? array[++mid] : NULL);
1954 else
1955 ret = array[mid];
1956 } else {
1957 assert(lo == hi+1);
1958 if (rel == REL234_LT || rel == REL234_LE) {
1959 mid = hi;
1960 ret = (hi >= 0 ? array[hi] : NULL);
1961 } else if (rel == REL234_GT || rel == REL234_GE) {
1962 mid = lo;
1963 ret = (lo < arraylen ? array[lo] : NULL);
1964 } else
1965 ret = NULL;
1966 }
1967
1968 realret = findrelpos234(tree, p, NULL, rel, &index);
1969 if (realret != ret) {
1970 error("find(\"%s\",%s) gave %s should be %s",
1971 p, relnames[j], realret, ret);
1972 }
1973 if (realret && index != mid) {
1974 error("find(\"%s\",%s) gave %d should be %d",
1975 p, relnames[j], index, mid);
1976 }
1977 if (realret && rel == REL234_EQ) {
1978 realret2 = index234(tree, index);
1979 if (realret2 != realret) {
1980 error("find(\"%s\",%s) gave %s(%d) but %d -> %s",
1981 p, relnames[j], realret, index, index, realret2);
1982 }
1983 }
1984#if 0
1985 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],
1986 realret, index);
1987#endif
1988 }
1989 }
1990
1991 realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);
1992 if (arraylen && (realret != array[0] || index != 0)) {
1993 error("find(NULL,GT) gave %s(%d) should be %s(0)",
1994 realret, index, array[0]);
1995 } else if (!arraylen && (realret != NULL)) {
1996 error("find(NULL,GT) gave %s(%d) should be NULL",
1997 realret, index);
1998 }
1999
2000 realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);
2001 if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) {
2002 error("find(NULL,LT) gave %s(%d) should be %s(0)",
2003 realret, index, array[arraylen-1]);
2004 } else if (!arraylen && (realret != NULL)) {
2005 error("find(NULL,LT) gave %s(%d) should be NULL",
2006 realret, index);
2007 }
2008}
2009
2010void splittest(tree234 *tree, void **array, int arraylen) {
2011 int i;
2012 tree234 *tree3, *tree4;
2013 for (i = 0; i <= arraylen; i++) {
2014 tree3 = copytree234(tree, NULL, NULL);
2015 tree4 = splitpos234(tree3, i, false);
2016 verifytree(tree3, array, i);
2017 verifytree(tree4, array+i, arraylen-i);
2018 join234(tree3, tree4);
2019 freetree234(tree4); /* left empty by join */
2020 verifytree(tree3, array, arraylen);
2021 freetree234(tree3);
2022 }
2023}
2024
2025int main(void) {
2026 int in[NSTR];
2027 int i, j, k;
2028 int tworoot, tmplen;
2029 unsigned seed = 0;
2030 tree234 *tree2, *tree3, *tree4;
2031 int c;
2032
2033 setvbuf(stdout, NULL, _IOLBF, 0);
2034
2035 for (i = 0; i < (int)NSTR; i++) in[i] = 0;
2036 array = NULL;
2037 arraylen = arraysize = 0;
2038 tree = newtree234(mycmp);
2039 cmp = mycmp;
2040
2041 verify();
2042 for (i = 0; i < 10000; i++) {
2043 j = randomnumber(&seed);
2044 j %= NSTR;
2045 printf("trial: %d\n", i);
2046 if (in[j]) {
2047 printf("deleting %s (%d)\n", strings[j], j);
2048 deltest(strings[j]);
2049 in[j] = 0;
2050 } else {
2051 printf("adding %s (%d)\n", strings[j], j);
2052 addtest(strings[j]);
2053 in[j] = 1;
2054 }
2055 disptree(tree);
2056 findtest();
2057 }
2058
2059 while (arraylen > 0) {
2060 j = randomnumber(&seed);
2061 j %= arraylen;
2062 deltest(array[j]);
2063 }
2064
2065 freetree234(tree);
2066
2067 /*
2068 * Now try an unsorted tree. We don't really need to test
2069 * delpos234 because we know del234 is based on it, so it's
2070 * already been tested in the above sorted-tree code; but for
2071 * completeness we'll use it to tear down our unsorted tree
2072 * once we've built it.
2073 */
2074 tree = newtree234(NULL);
2075 cmp = NULL;
2076 verify();
2077 for (i = 0; i < 1000; i++) {
2078 printf("trial: %d\n", i);
2079 j = randomnumber(&seed);
2080 j %= NSTR;
2081 k = randomnumber(&seed);
2082 k %= count234(tree)+1;
2083 printf("adding string %s at index %d\n", strings[j], k);
2084 addpostest(strings[j], k);
2085 }
2086
2087 /*
2088 * While we have this tree in its full form, we'll take a copy
2089 * of it to use in split and join testing.
2090 */
2091 tree2 = copytree234(tree, NULL, NULL);
2092 verifytree(tree2, array, arraylen);/* check the copy is accurate */
2093 /*
2094 * Split tests. Split the tree at every possible point and
2095 * check the resulting subtrees.
2096 */
2097 tworoot = (!tree2->root->elems[1]);/* see if it has a 2-root */
2098 splittest(tree2, array, arraylen);
2099 /*
2100 * Now do the split test again, but on a tree that has a 2-root
2101 * (if the previous one didn't) or doesn't (if the previous one
2102 * did).
2103 */
2104 tmplen = arraylen;
2105 while ((!tree2->root->elems[1]) == tworoot) {
2106 delpos234(tree2, --tmplen);
2107 }
2108 printf("now trying splits on second tree\n");
2109 splittest(tree2, array, tmplen);
2110 freetree234(tree2);
2111
2112 /*
2113 * Back to the main testing of uncounted trees.
2114 */
2115 while (count234(tree) > 0) {
2116 printf("cleanup: tree size %d\n", count234(tree));
2117 j = randomnumber(&seed);
2118 j %= count234(tree);
2119 printf("deleting string %s from index %d\n", (char *)array[j], j);
2120 delpostest(j);
2121 }
2122 freetree234(tree);
2123
2124 /*
2125 * Finally, do some testing on split/join on _sorted_ trees. At
2126 * the same time, we'll be testing split on very small trees.
2127 */
2128 tree = newtree234(mycmp);
2129 cmp = mycmp;
2130 arraylen = 0;
2131 for (i = 0; i < 17; i++) {
2132 tree2 = copytree234(tree, NULL, NULL);
2133 splittest(tree2, array, arraylen);
2134 freetree234(tree2);
2135 if (i < 16)
2136 addtest(strings[i]);
2137 }
2138 freetree234(tree);
2139
2140 /*
2141 * Test silly cases of join: join(emptytree, emptytree), and
2142 * also ensure join correctly spots when sorted trees fail the
2143 * ordering constraint.
2144 */
2145 tree = newtree234(mycmp);
2146 tree2 = newtree234(mycmp);
2147 tree3 = newtree234(mycmp);
2148 tree4 = newtree234(mycmp);
2149 assert(mycmp(strings[0], strings[1]) < 0); /* just in case :-) */
2150 add234(tree2, strings[1]);
2151 add234(tree4, strings[0]);
2152 array[0] = strings[0];
2153 array[1] = strings[1];
2154 verifytree(tree, array, 0);
2155 verifytree(tree2, array+1, 1);
2156 verifytree(tree3, array, 0);
2157 verifytree(tree4, array, 1);
2158
2159 /*
2160 * So:
2161 * - join(tree,tree3) should leave both tree and tree3 unchanged.
2162 * - joinr(tree,tree2) should leave both tree and tree2 unchanged.
2163 * - join(tree4,tree3) should leave both tree3 and tree4 unchanged.
2164 * - join(tree, tree2) should move the element from tree2 to tree.
2165 * - joinr(tree4, tree3) should move the element from tree4 to tree3.
2166 * - join(tree,tree3) should return NULL and leave both unchanged.
2167 * - join(tree3,tree) should work and create a bigger tree in tree3.
2168 */
2169 assert(tree == join234(tree, tree3));
2170 verifytree(tree, array, 0);
2171 verifytree(tree3, array, 0);
2172 assert(tree2 == join234r(tree, tree2));
2173 verifytree(tree, array, 0);
2174 verifytree(tree2, array+1, 1);
2175 assert(tree4 == join234(tree4, tree3));
2176 verifytree(tree3, array, 0);
2177 verifytree(tree4, array, 1);
2178 assert(tree == join234(tree, tree2));
2179 verifytree(tree, array+1, 1);
2180 verifytree(tree2, array, 0);
2181 assert(tree3 == join234r(tree4, tree3));
2182 verifytree(tree3, array, 1);
2183 verifytree(tree4, array, 0);
2184 assert(NULL == join234(tree, tree3));
2185 verifytree(tree, array+1, 1);
2186 verifytree(tree3, array, 1);
2187 assert(tree3 == join234(tree3, tree));
2188 verifytree(tree3, array, 2);
2189 verifytree(tree, array, 0);
2190
2191 return 0;
2192}
2193
2194#endif
2195
2196#if 0 /* sorted list of strings might be useful */
2197{
2198 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
2199}
2200#endif
diff --git a/apps/plugins/puzzles/src/tree234.h b/apps/plugins/puzzles/src/tree234.h
index ccd943d69d..b58f939a0a 100644
--- a/apps/plugins/puzzles/src/tree234.h
+++ b/apps/plugins/puzzles/src/tree234.h
@@ -31,7 +31,11 @@
31#include <stdbool.h> 31#include <stdbool.h>
32 32
33/* 33/*
34 * This typedef is opaque outside tree234.c itself. 34 * This typedef is typically opaque outside tree234.c itself. But you
35 * can define TREE234_INTERNALS to get a definition of it and its
36 * subsidiary node structure, as long as you're prepared to commit to
37 * responding to changes in the internals (which probably means you're
38 * tree234.c itself or tree234-test.c).
35 */ 39 */
36typedef struct tree234_Tag tree234; 40typedef struct tree234_Tag tree234;
37 41
@@ -39,6 +43,24 @@ typedef int (*cmpfn234)(void *, void *);
39 43
40typedef void *(*copyfn234)(void *state, void *element); 44typedef void *(*copyfn234)(void *state, void *element);
41 45
46#ifdef TREE234_INTERNALS
47typedef struct node234_Tag node234;
48
49struct tree234_Tag {
50 node234 *root;
51 cmpfn234 cmp;
52};
53
54struct node234_Tag {
55 node234 *parent;
56 node234 *kids[4];
57 int counts[4];
58 void *elems[3];
59};
60
61int height234(tree234 *t);
62#endif
63
42/* 64/*
43 * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and 65 * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and
44 * lookups by key will fail: you can only look things up by numeric 66 * lookups by key will fail: you can only look things up by numeric
diff --git a/apps/plugins/puzzles/src/twiddle.R b/apps/plugins/puzzles/src/twiddle.R
deleted file mode 100644
index 1495c33181..0000000000
--- a/apps/plugins/puzzles/src/twiddle.R
+++ /dev/null
@@ -1,19 +0,0 @@
1# -*- makefile -*-
2
3twiddle : [X] GTK COMMON twiddle twiddle-icon|no-icon
4
5twiddle : [G] WINDOWS COMMON twiddle twiddle.res|noicon.res
6
7ALL += twiddle[COMBINED]
8
9!begin am gtk
10GAMES += twiddle
11!end
12
13!begin >list.c
14 A(twiddle) \
15!end
16
17!begin >gamedesc.txt
18twiddle:twiddle.exe:Twiddle:Rotational sliding block puzzle:Rotate the tiles around themselves to arrange them into order.
19!end
diff --git a/apps/plugins/puzzles/src/twiddle.c b/apps/plugins/puzzles/src/twiddle.c
index a107925aee..49d8db7825 100644
--- a/apps/plugins/puzzles/src/twiddle.c
+++ b/apps/plugins/puzzles/src/twiddle.c
@@ -10,7 +10,12 @@
10#include <string.h> 10#include <string.h>
11#include <assert.h> 11#include <assert.h>
12#include <ctype.h> 12#include <ctype.h>
13#include <math.h> 13#include <limits.h>
14#ifdef NO_TGMATH_H
15# include <math.h>
16#else
17# include <tgmath.h>
18#endif
14 19
15#include "puzzles.h" 20#include "puzzles.h"
16 21
@@ -123,14 +128,16 @@ static void decode_params(game_params *ret, char const *string)
123 while (*string) { 128 while (*string) {
124 if (*string == 'r') { 129 if (*string == 'r') {
125 ret->rowsonly = true; 130 ret->rowsonly = true;
131 string++;
126 } else if (*string == 'o') { 132 } else if (*string == 'o') {
127 ret->orientable = true; 133 ret->orientable = true;
134 string++;
128 } else if (*string == 'm') { 135 } else if (*string == 'm') {
129 string++; 136 string++;
130 ret->movetarget = atoi(string); 137 ret->movetarget = atoi(string);
131 while (string[1] && isdigit((unsigned char)string[1])) string++; 138 while (*string && isdigit((unsigned char)*string)) string++;
132 } 139 } else
133 string++; 140 string++;
134 } 141 }
135} 142}
136 143
@@ -210,6 +217,8 @@ static const char *validate_params(const game_params *params, bool full)
210 return "Width must be at least the rotating block size"; 217 return "Width must be at least the rotating block size";
211 if (params->h < params->n) 218 if (params->h < params->n)
212 return "Height must be at least the rotating block size"; 219 return "Height must be at least the rotating block size";
220 if (params->w > INT_MAX / params->h)
221 return "Width times height must not be unreasonably large";
213 if (params->movetarget < 0) 222 if (params->movetarget < 0)
214 return "Number of shuffling moves may not be negative"; 223 return "Number of shuffling moves may not be negative";
215 return NULL; 224 return NULL;
@@ -523,18 +532,6 @@ static void free_game(game_state *state)
523 sfree(state); 532 sfree(state);
524} 533}
525 534
526static int compare_int(const void *av, const void *bv)
527{
528 const int *a = (const int *)av;
529 const int *b = (const int *)bv;
530 if (*a < *b)
531 return -1;
532 else if (*a > *b)
533 return +1;
534 else
535 return 0;
536}
537
538static char *solve_game(const game_state *state, const game_state *currstate, 535static char *solve_game(const game_state *state, const game_state *currstate,
539 const char *aux, const char **error) 536 const char *aux, const char **error)
540{ 537{
@@ -615,7 +612,7 @@ static game_ui *new_ui(const game_state *state)
615 612
616 ui->cur_x = 0; 613 ui->cur_x = 0;
617 ui->cur_y = 0; 614 ui->cur_y = 0;
618 ui->cur_visible = false; 615 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
619 616
620 return ui; 617 return ui;
621} 618}
@@ -625,18 +622,20 @@ static void free_ui(game_ui *ui)
625 sfree(ui); 622 sfree(ui);
626} 623}
627 624
628static char *encode_ui(const game_ui *ui) 625static void game_changed_state(game_ui *ui, const game_state *oldstate,
629{ 626 const game_state *newstate)
630 return NULL;
631}
632
633static void decode_ui(game_ui *ui, const char *encoding)
634{ 627{
635} 628}
636 629
637static void game_changed_state(game_ui *ui, const game_state *oldstate, 630static const char *current_key_label(const game_ui *ui,
638 const game_state *newstate) 631 const game_state *state, int button)
639{ 632{
633 if (!ui->cur_visible) return "";
634 switch (button) {
635 case CURSOR_SELECT: return "Turn left";
636 case CURSOR_SELECT2: return "Turn right";
637 }
638 return "";
640} 639}
641 640
642struct game_drawstate { 641struct game_drawstate {
@@ -657,18 +656,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
657 656
658 button = button & (~MOD_MASK | MOD_NUM_KEYPAD); 657 button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
659 658
660 if (IS_CURSOR_MOVE(button)) { 659 if (IS_CURSOR_MOVE(button))
661 if (button == CURSOR_LEFT && ui->cur_x > 0) 660 return move_cursor(button, &ui->cur_x, &ui->cur_y, w-n+1, h-n+1,
662 ui->cur_x--; 661 false, &ui->cur_visible);
663 if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w))
664 ui->cur_x++;
665 if (button == CURSOR_UP && ui->cur_y > 0)
666 ui->cur_y--;
667 if (button == CURSOR_DOWN && (ui->cur_y+n) < (h))
668 ui->cur_y++;
669 ui->cur_visible = true;
670 return UI_UPDATE;
671 }
672 662
673 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 663 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
674 /* 664 /*
@@ -691,7 +681,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
691 dir = (button == CURSOR_SELECT2) ? -1 : +1; 681 dir = (button == CURSOR_SELECT2) ? -1 : +1;
692 } else { 682 } else {
693 ui->cur_visible = true; 683 ui->cur_visible = true;
694 return UI_UPDATE; 684 return MOVE_UI_UPDATE;
695 } 685 }
696 } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') { 686 } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
697 x = y = 0; 687 x = y = 0;
@@ -756,7 +746,7 @@ static game_state *execute_move(const game_state *from, const char *move)
756 * conveniently being able to get hold of a clean state from 746 * conveniently being able to get hold of a clean state from
757 * which to practise manoeuvres. 747 * which to practise manoeuvres.
758 */ 748 */
759 qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int); 749 qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_integers);
760 for (i = 0; i < ret->w*ret->h; i++) 750 for (i = 0; i < ret->w*ret->h; i++)
761 ret->grid[i] &= ~3; 751 ret->grid[i] &= ~3;
762 ret->used_solve = true; 752 ret->used_solve = true;
@@ -791,7 +781,7 @@ static game_state *execute_move(const game_state *from, const char *move)
791 */ 781 */
792 782
793static void game_compute_size(const game_params *params, int tilesize, 783static void game_compute_size(const game_params *params, int tilesize,
794 int *x, int *y) 784 const game_ui *ui, int *x, int *y)
795{ 785{
796 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 786 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
797 struct { int tilesize; } ads, *ds = &ads; 787 struct { int tilesize; } ads, *ds = &ads;
@@ -870,8 +860,8 @@ static void rotate(int *xy, struct rotation *rot)
870 xf2 = rot->c * xf + rot->s * yf; 860 xf2 = rot->c * xf + rot->s * yf;
871 yf2 = - rot->s * xf + rot->c * yf; 861 yf2 = - rot->s * xf + rot->c * yf;
872 862
873 xy[0] = (int)(xf2 + rot->ox + 0.5); /* round to nearest */ 863 xy[0] = (int)(xf2 + rot->ox + 0.5F); /* round to nearest */
874 xy[1] = (int)(yf2 + rot->oy + 0.5); /* round to nearest */ 864 xy[1] = (int)(yf2 + rot->oy + 0.5F); /* round to nearest */
875 } 865 }
876} 866}
877 867
@@ -1058,7 +1048,7 @@ static int highlight_colour(float angle)
1058 COL_LOWLIGHT, 1048 COL_LOWLIGHT,
1059 }; 1049 };
1060 1050
1061 return colours[(int)((angle + 2*PI) / (PI/16)) & 31]; 1051 return colours[(int)((angle + 2*(float)PI) / ((float)PI/16)) & 31];
1062} 1052}
1063 1053
1064static float game_anim_length_real(const game_state *oldstate, 1054static float game_anim_length_real(const game_state *oldstate,
@@ -1135,13 +1125,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1135 if (!ds->started) { 1125 if (!ds->started) {
1136 int coords[10]; 1126 int coords[10];
1137 1127
1138 draw_rect(dr, 0, 0,
1139 TILE_SIZE * state->w + 2 * BORDER,
1140 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
1141 draw_update(dr, 0, 0,
1142 TILE_SIZE * state->w + 2 * BORDER,
1143 TILE_SIZE * state->h + 2 * BORDER);
1144
1145 /* 1128 /*
1146 * Recessed area containing the whole puzzle. 1129 * Recessed area containing the whole puzzle.
1147 */ 1130 */
@@ -1189,7 +1172,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1189 rot->cw = rot->ch = TILE_SIZE * state->n; 1172 rot->cw = rot->ch = TILE_SIZE * state->n;
1190 rot->ox = rot->cx + rot->cw/2; 1173 rot->ox = rot->cx + rot->cw/2;
1191 rot->oy = rot->cy + rot->ch/2; 1174 rot->oy = rot->cy + rot->ch/2;
1192 angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max)); 1175 angle = ((-(float)PI/2 * lastr) * (1.0F - animtime / anim_max));
1193 rot->c = (float)cos(angle); 1176 rot->c = (float)cos(angle);
1194 rot->s = (float)sin(angle); 1177 rot->s = (float)sin(angle);
1195 1178
@@ -1282,19 +1265,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1282 } 1265 }
1283} 1266}
1284 1267
1285static bool game_timing_state(const game_state *state, game_ui *ui)
1286{
1287 return true;
1288}
1289
1290static void game_print_size(const game_params *params, float *x, float *y)
1291{
1292}
1293
1294static void game_print(drawing *dr, const game_state *state, int tilesize)
1295{
1296}
1297
1298#ifdef COMBINED 1268#ifdef COMBINED
1299#define thegame twiddle 1269#define thegame twiddle
1300#endif 1270#endif
@@ -1316,12 +1286,14 @@ const struct game thegame = {
1316 free_game, 1286 free_game,
1317 true, solve_game, 1287 true, solve_game,
1318 true, game_can_format_as_text_now, game_text_format, 1288 true, game_can_format_as_text_now, game_text_format,
1289 NULL, NULL, /* get_prefs, set_prefs */
1319 new_ui, 1290 new_ui,
1320 free_ui, 1291 free_ui,
1321 encode_ui, 1292 NULL, /* encode_ui */
1322 decode_ui, 1293 NULL, /* decode_ui */
1323 NULL, /* game_request_keys */ 1294 NULL, /* game_request_keys */
1324 game_changed_state, 1295 game_changed_state,
1296 current_key_label,
1325 interpret_move, 1297 interpret_move,
1326 execute_move, 1298 execute_move,
1327 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1299 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1333,9 +1305,9 @@ const struct game thegame = {
1333 game_flash_length, 1305 game_flash_length,
1334 game_get_cursor_location, 1306 game_get_cursor_location,
1335 game_status, 1307 game_status,
1336 false, false, game_print_size, game_print, 1308 false, false, NULL, NULL, /* print_size, print */
1337 true, /* wants_statusbar */ 1309 true, /* wants_statusbar */
1338 false, game_timing_state, 1310 false, NULL, /* timing_state */
1339 0, /* flags */ 1311 0, /* flags */
1340}; 1312};
1341 1313
diff --git a/apps/plugins/puzzles/src/undead.R b/apps/plugins/puzzles/src/undead.R
deleted file mode 100644
index 5907ed6b74..0000000000
--- a/apps/plugins/puzzles/src/undead.R
+++ /dev/null
@@ -1,18 +0,0 @@
1# -*- makefile -*-
2
3undead : [X] GTK COMMON undead undead-icon|no-icon
4undead : [G] WINDOWS COMMON undead undead.res|noicon.res
5
6ALL += undead[COMBINED]
7
8!begin am gtk
9GAMES += undead
10!end
11
12!begin >list.c
13 A(undead) \
14!end
15
16!begin >gamedesc.txt
17undead:undead.exe:Undead:Monster-placing puzzle:Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.
18!end
diff --git a/apps/plugins/puzzles/src/undead.c b/apps/plugins/puzzles/src/undead.c
index 4dba828d48..0213d0e2e4 100644
--- a/apps/plugins/puzzles/src/undead.c
+++ b/apps/plugins/puzzles/src/undead.c
@@ -35,7 +35,11 @@
35#include <string.h> 35#include <string.h>
36#include <assert.h> 36#include <assert.h>
37#include <ctype.h> 37#include <ctype.h>
38#include <math.h> 38#ifdef NO_TGMATH_H
39# include <math.h>
40#else
41# include <tgmath.h>
42#endif
39 43
40#include "puzzles.h" 44#include "puzzles.h"
41 45
@@ -193,9 +197,9 @@ static game_params *custom_params(const config_item *cfg)
193 197
194static const char *validate_params(const game_params *params, bool full) 198static const char *validate_params(const game_params *params, bool full)
195{ 199{
196 if ((params->w * params->h ) > 54) return "Grid is too big";
197 if (params->w < 3) return "Width must be at least 3"; 200 if (params->w < 3) return "Width must be at least 3";
198 if (params->h < 3) return "Height must be at least 3"; 201 if (params->h < 3) return "Height must be at least 3";
202 if (params->w > 54 / params->h) return "Grid is too big";
199 if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; 203 if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating";
200 return NULL; 204 return NULL;
201} 205}
@@ -972,7 +976,7 @@ static int path_cmp(const void *a, const void *b) {
972 976
973static char *new_game_desc(const game_params *params, random_state *rs, 977static char *new_game_desc(const game_params *params, random_state *rs,
974 char **aux, bool interactive) { 978 char **aux, bool interactive) {
975 int i,count,c,w,h,r,p,g; 979 int count,c,w,h,r,p,g;
976 game_state *new; 980 game_state *new;
977 981
978 /* Variables for puzzle generation algorithm */ 982 /* Variables for puzzle generation algorithm */
@@ -993,7 +997,6 @@ static char *new_game_desc(const game_params *params, random_state *rs,
993 char *e; 997 char *e;
994 char *desc; 998 char *desc;
995 999
996 i = 0;
997 while (true) { 1000 while (true) {
998 new = new_state(params); 1001 new = new_state(params);
999 abort = false; 1002 abort = false;
@@ -1031,7 +1034,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1031 /* Monsters / Mirrors ratio should be balanced */ 1034 /* Monsters / Mirrors ratio should be balanced */
1032 ratio = (float)new->common->num_total / 1035 ratio = (float)new->common->num_total /
1033 (float)(new->common->params.w * new->common->params.h); 1036 (float)(new->common->params.w * new->common->params.h);
1034 if (ratio < 0.48 || ratio > 0.78) { 1037 if (ratio < 0.48F || ratio > 0.78F) {
1035 free_game(new); 1038 free_game(new);
1036 continue; 1039 continue;
1037 } 1040 }
@@ -1253,7 +1256,6 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1253 * difficulty level, free memory and start from scratch */ 1256 * difficulty level, free memory and start from scratch */
1254 sfree(old_guess); 1257 sfree(old_guess);
1255 free_game(new); 1258 free_game(new);
1256 i++;
1257 } 1259 }
1258 1260
1259 /* We have a valid puzzle! */ 1261 /* We have a valid puzzle! */
@@ -1574,6 +1576,8 @@ static char *solve_game(const game_state *state_start, const game_state *currsta
1574 } 1576 }
1575 1577
1576/* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */ 1578/* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */
1579 (void)iterative_depth;
1580 (void)count_ambiguous;
1577 1581
1578 move = snewn(solve_state->common->num_total * 4 +2, char); 1582 move = snewn(solve_state->common->num_total * 4 +2, char);
1579 c = move; 1583 c = move;
@@ -1640,31 +1644,64 @@ struct game_ui {
1640 int hx, hy; /* as for solo.c, highlight pos */ 1644 int hx, hy; /* as for solo.c, highlight pos */
1641 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ 1645 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1642 bool ascii; 1646 bool ascii;
1647
1648 /*
1649 * User preference option: if the user right-clicks in a square
1650 * and presses a monster key to add/remove a pencil mark, do we
1651 * hide the mouse highlight again afterwards?
1652 *
1653 * Historically our answer was yes. The Android port prefers no.
1654 * There are advantages both ways, depending how much you dislike
1655 * the highlight cluttering your view. So it's a preference.
1656 */
1657 bool pencil_keep_highlight;
1643}; 1658};
1644 1659
1645static game_ui *new_ui(const game_state *state) 1660static game_ui *new_ui(const game_state *state)
1646{ 1661{
1647 game_ui *ui = snew(game_ui); 1662 game_ui *ui = snew(game_ui);
1648 ui->hx = ui->hy = 0;
1649 ui->hpencil = false; 1663 ui->hpencil = false;
1650 ui->hshow = false; 1664 ui->hx = ui->hy = ui->hshow = ui->hcursor =
1651 ui->hcursor = false; 1665 getenv_bool("PUZZLES_SHOW_CURSOR", false);
1652 ui->ascii = false; 1666 ui->ascii = false;
1667
1668 ui->pencil_keep_highlight = false;
1669
1653 return ui; 1670 return ui;
1654} 1671}
1655 1672
1656static void free_ui(game_ui *ui) { 1673static config_item *get_prefs(game_ui *ui)
1657 sfree(ui); 1674{
1658 return; 1675 config_item *ret;
1676
1677 ret = snewn(3, config_item);
1678
1679 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1680 ret[0].kw = "pencil-keep-highlight";
1681 ret[0].type = C_BOOLEAN;
1682 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1683
1684 ret[1].name = "Monster representation";
1685 ret[1].kw = "monsters";
1686 ret[1].type = C_CHOICES;
1687 ret[1].u.choices.choicenames = ":Pictures:Letters";
1688 ret[1].u.choices.choicekws = ":pictures:letters";
1689 ret[1].u.choices.selected = ui->ascii;
1690
1691 ret[2].name = NULL;
1692 ret[2].type = C_END;
1693
1694 return ret;
1659} 1695}
1660 1696
1661static char *encode_ui(const game_ui *ui) 1697static void set_prefs(game_ui *ui, const config_item *cfg)
1662{ 1698{
1663 return NULL; 1699 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1700 ui->ascii = cfg[1].u.choices.selected;
1664} 1701}
1665 1702
1666static void decode_ui(game_ui *ui, const char *encoding) 1703static void free_ui(game_ui *ui) {
1667{ 1704 sfree(ui);
1668 return; 1705 return;
1669} 1706}
1670 1707
@@ -1681,6 +1718,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1681 } 1718 }
1682} 1719}
1683 1720
1721static const char *current_key_label(const game_ui *ui,
1722 const game_state *state, int button)
1723{
1724 int xi;
1725
1726 if (ui->hshow && button == CURSOR_SELECT)
1727 return ui->hpencil ? "Ink" : "Pencil";
1728 if (button == CURSOR_SELECT2) {
1729 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1730 if (xi >= 0 && !state->common->fixed[xi]) return "Clear";
1731 }
1732 return "";
1733}
1734
1684struct game_drawstate { 1735struct game_drawstate {
1685 int tilesize; 1736 int tilesize;
1686 bool started, solved; 1737 bool started, solved;
@@ -1743,7 +1794,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1743 1794
1744 if (button == 'a' || button == 'A') { 1795 if (button == 'a' || button == 'A') {
1745 ui->ascii = !ui->ascii; 1796 ui->ascii = !ui->ascii;
1746 return UI_UPDATE; 1797 return MOVE_UI_UPDATE;
1747 } 1798 }
1748 1799
1749 if (button == 'm' || button == 'M') { 1800 if (button == 'm' || button == 'M') {
@@ -1755,22 +1806,30 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1755 if (xi >= 0 && !state->common->fixed[xi]) { 1806 if (xi >= 0 && !state->common->fixed[xi]) {
1756 if (button == 'g' || button == 'G' || button == '1') { 1807 if (button == 'g' || button == 'G' || button == '1') {
1757 if (!ui->hcursor) ui->hshow = false; 1808 if (!ui->hcursor) ui->hshow = false;
1809 if (state->guess[xi] == 1)
1810 return ui->hcursor ? NULL : MOVE_UI_UPDATE;
1758 sprintf(buf,"G%d",xi); 1811 sprintf(buf,"G%d",xi);
1759 return dupstr(buf); 1812 return dupstr(buf);
1760 } 1813 }
1761 if (button == 'v' || button == 'V' || button == '2') { 1814 if (button == 'v' || button == 'V' || button == '2') {
1762 if (!ui->hcursor) ui->hshow = false; 1815 if (!ui->hcursor) ui->hshow = false;
1816 if (state->guess[xi] == 2)
1817 return ui->hcursor ? NULL : MOVE_UI_UPDATE;
1763 sprintf(buf,"V%d",xi); 1818 sprintf(buf,"V%d",xi);
1764 return dupstr(buf); 1819 return dupstr(buf);
1765 } 1820 }
1766 if (button == 'z' || button == 'Z' || button == '3') { 1821 if (button == 'z' || button == 'Z' || button == '3') {
1767 if (!ui->hcursor) ui->hshow = false; 1822 if (!ui->hcursor) ui->hshow = false;
1823 if (state->guess[xi] == 4)
1824 return ui->hcursor ? NULL : MOVE_UI_UPDATE;
1768 sprintf(buf,"Z%d",xi); 1825 sprintf(buf,"Z%d",xi);
1769 return dupstr(buf); 1826 return dupstr(buf);
1770 } 1827 }
1771 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || 1828 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1772 button == '0' || button == '\b' ) { 1829 button == '0' || button == '\b' ) {
1773 if (!ui->hcursor) ui->hshow = false; 1830 if (!ui->hcursor) ui->hshow = false;
1831 if (state->guess[xi] == 7 && state->pencils[xi] == 0)
1832 return ui->hcursor ? NULL : MOVE_UI_UPDATE;
1774 sprintf(buf,"E%d",xi); 1833 sprintf(buf,"E%d",xi);
1775 return dupstr(buf); 1834 return dupstr(buf);
1776 } 1835 }
@@ -1790,45 +1849,42 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1790 } 1849 }
1791 ui->hshow = true; 1850 ui->hshow = true;
1792 ui->hcursor = true; 1851 ui->hcursor = true;
1793 return UI_UPDATE; 1852 return MOVE_UI_UPDATE;
1794 } 1853 }
1795 if (ui->hshow && button == CURSOR_SELECT) { 1854 if (ui->hshow && button == CURSOR_SELECT) {
1796 ui->hpencil = !ui->hpencil; 1855 ui->hpencil = !ui->hpencil;
1797 ui->hcursor = true; 1856 ui->hcursor = true;
1798 return UI_UPDATE; 1857 return MOVE_UI_UPDATE;
1799 } 1858 }
1800 1859
1801 if (ui->hshow && ui->hpencil) { 1860 if (ui->hshow && ui->hpencil) {
1802 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; 1861 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1803 if (xi >= 0 && !state->common->fixed[xi]) { 1862 if (xi >= 0 && !state->common->fixed[xi]) {
1863 buf[0] = '\0';
1864
1804 if (button == 'g' || button == 'G' || button == '1') { 1865 if (button == 'g' || button == 'G' || button == '1') {
1805 sprintf(buf,"g%d",xi); 1866 sprintf(buf,"g%d",xi);
1806 if (!ui->hcursor) { 1867 } else if (button == 'v' || button == 'V' || button == '2') {
1807 ui->hpencil = false;
1808 ui->hshow = false;
1809 }
1810 return dupstr(buf);
1811 }
1812 if (button == 'v' || button == 'V' || button == '2') {
1813 sprintf(buf,"v%d",xi); 1868 sprintf(buf,"v%d",xi);
1814 if (!ui->hcursor) { 1869 } else if (button == 'z' || button == 'Z' || button == '3') {
1815 ui->hpencil = false;
1816 ui->hshow = false;
1817 }
1818 return dupstr(buf);
1819 }
1820 if (button == 'z' || button == 'Z' || button == '3') {
1821 sprintf(buf,"z%d",xi); 1870 sprintf(buf,"z%d",xi);
1822 if (!ui->hcursor) { 1871 } else if (button == 'e' || button == 'E' ||
1823 ui->hpencil = false; 1872 button == CURSOR_SELECT2 || button == '0' ||
1824 ui->hshow = false; 1873 button == '\b') {
1825 } 1874 if (state->pencils[xi] == 0)
1826 return dupstr(buf); 1875 return ui->hcursor ? NULL : MOVE_UI_UPDATE;
1827 }
1828 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1829 button == '0' || button == '\b') {
1830 sprintf(buf,"E%d",xi); 1876 sprintf(buf,"E%d",xi);
1831 if (!ui->hcursor) { 1877 }
1878
1879 if (buf[0]) {
1880 /*
1881 * Hide the highlight after a keypress, if it was mouse-
1882 * generated. Also, don't hide it if this move has changed
1883 * pencil marks and the user preference says not to hide the
1884 * highlight in that situation.
1885 */
1886 if (!ui->hcursor &&
1887 !(ui->hpencil && ui->pencil_keep_highlight)) {
1832 ui->hpencil = false; 1888 ui->hpencil = false;
1833 ui->hshow = false; 1889 ui->hshow = false;
1834 } 1890 }
@@ -1847,14 +1903,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1847 ui->hpencil = false; 1903 ui->hpencil = false;
1848 ui->hcursor = false; 1904 ui->hcursor = false;
1849 ui->hx = gx; ui->hy = gy; 1905 ui->hx = gx; ui->hy = gy;
1850 return UI_UPDATE; 1906 return MOVE_UI_UPDATE;
1851 } 1907 }
1852 else if (button == RIGHT_BUTTON && g == 7) { 1908 else if (button == RIGHT_BUTTON && g == 7) {
1853 ui->hshow = true; 1909 ui->hshow = true;
1854 ui->hpencil = true; 1910 ui->hpencil = true;
1855 ui->hcursor = false; 1911 ui->hcursor = false;
1856 ui->hx = gx; ui->hy = gy; 1912 ui->hx = gx; ui->hy = gy;
1857 return UI_UPDATE; 1913 return MOVE_UI_UPDATE;
1858 } 1914 }
1859 } 1915 }
1860 else if (ui->hshow) { 1916 else if (ui->hshow) {
@@ -1865,14 +1921,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1865 ui->hpencil = false; 1921 ui->hpencil = false;
1866 ui->hcursor = false; 1922 ui->hcursor = false;
1867 ui->hx = 0; ui->hy = 0; 1923 ui->hx = 0; ui->hy = 0;
1868 return UI_UPDATE; 1924 return MOVE_UI_UPDATE;
1869 } 1925 }
1870 else { 1926 else {
1871 ui->hshow = true; 1927 ui->hshow = true;
1872 ui->hpencil = false; 1928 ui->hpencil = false;
1873 ui->hcursor = false; 1929 ui->hcursor = false;
1874 ui->hx = gx; ui->hy = gy; 1930 ui->hx = gx; ui->hy = gy;
1875 return UI_UPDATE; 1931 return MOVE_UI_UPDATE;
1876 } 1932 }
1877 } 1933 }
1878 else { 1934 else {
@@ -1880,7 +1936,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1880 ui->hpencil = false; 1936 ui->hpencil = false;
1881 ui->hcursor = false; 1937 ui->hcursor = false;
1882 ui->hx = gx; ui->hy = gy; 1938 ui->hx = gx; ui->hy = gy;
1883 return UI_UPDATE; 1939 return MOVE_UI_UPDATE;
1884 } 1940 }
1885 } 1941 }
1886 else if (button == RIGHT_BUTTON) { 1942 else if (button == RIGHT_BUTTON) {
@@ -1889,7 +1945,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1889 ui->hpencil = true; 1945 ui->hpencil = true;
1890 ui->hcursor = false; 1946 ui->hcursor = false;
1891 ui->hx = gx; ui->hy = gy; 1947 ui->hx = gx; ui->hy = gy;
1892 return UI_UPDATE; 1948 return MOVE_UI_UPDATE;
1893 } 1949 }
1894 else { 1950 else {
1895 if (gx == ui->hx && gy == ui->hy) { 1951 if (gx == ui->hx && gy == ui->hy) {
@@ -1897,14 +1953,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1897 ui->hpencil = false; 1953 ui->hpencil = false;
1898 ui->hcursor = false; 1954 ui->hcursor = false;
1899 ui->hx = 0; ui->hy = 0; 1955 ui->hx = 0; ui->hy = 0;
1900 return UI_UPDATE; 1956 return MOVE_UI_UPDATE;
1901 } 1957 }
1902 else if (g == 7) { 1958 else if (g == 7) {
1903 ui->hshow = true; 1959 ui->hshow = true;
1904 ui->hpencil = true; 1960 ui->hpencil = true;
1905 ui->hcursor = false; 1961 ui->hcursor = false;
1906 ui->hx = gx; ui->hy = gy; 1962 ui->hx = gx; ui->hy = gy;
1907 return UI_UPDATE; 1963 return MOVE_UI_UPDATE;
1908 } 1964 }
1909 } 1965 }
1910 } 1966 }
@@ -2056,11 +2112,11 @@ static game_state *execute_move(const game_state *state, const char *move)
2056 if (c == 'S') { 2112 if (c == 'S') {
2057 move++; 2113 move++;
2058 solver = true; 2114 solver = true;
2059 } 2115 } else if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
2060 if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' || 2116 c == 'g' || c == 'v' || c == 'z') {
2061 c == 'g' || c == 'v' || c == 'z') {
2062 move++; 2117 move++;
2063 sscanf(move, "%d%n", &x, &n); 2118 if (sscanf(move, "%d%n", &x, &n) != 1) goto badmove;
2119 if (x < 0 || x >= ret->common->num_total) goto badmove;
2064 if (c == 'G') ret->guess[x] = 1; 2120 if (c == 'G') ret->guess[x] = 1;
2065 if (c == 'V') ret->guess[x] = 2; 2121 if (c == 'V') ret->guess[x] = 2;
2066 if (c == 'Z') ret->guess[x] = 4; 2122 if (c == 'Z') ret->guess[x] = 4;
@@ -2069,23 +2125,26 @@ static game_state *execute_move(const game_state *state, const char *move)
2069 if (c == 'v') ret->pencils[x] ^= 2; 2125 if (c == 'v') ret->pencils[x] ^= 2;
2070 if (c == 'z') ret->pencils[x] ^= 4; 2126 if (c == 'z') ret->pencils[x] ^= 4;
2071 move += n; 2127 move += n;
2072 } 2128 } else if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
2073 if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 && 2129 is_clue(ret, x, y)) {
2074 is_clue(ret, x, y)) {
2075 ret->hints_done[clue_index(ret, x, y)] ^= 1; 2130 ret->hints_done[clue_index(ret, x, y)] ^= 1;
2076 move += n + 1; 2131 move += n + 1;
2077 } 2132 } else if (c == 'M') {
2078 if (c == 'M') {
2079 /* 2133 /*
2080 * Fill in absolutely all pencil marks in unfilled 2134 * Fill in absolutely all pencil marks in unfilled
2081 * squares, for those who like to play by the rigorous 2135 * squares, for those who like to play by the rigorous
2082 * approach of starting off in that state and eliminating 2136 * approach of starting off in that state and eliminating
2083 * things. 2137 * things.
2084 */ 2138 */
2085 for (i = 0; i < ret->common->wh; i++) 2139 for (i = 0; i < ret->common->num_total; i++)
2086 if (ret->guess[i] == 7) 2140 if (ret->guess[i] == 7)
2087 ret->pencils[i] = 7; 2141 ret->pencils[i] = 7;
2088 move++; 2142 move++;
2143 } else {
2144 /* Unknown move type. */
2145 badmove:
2146 free_game(ret);
2147 return NULL;
2089 } 2148 }
2090 if (*move == ';') move++; 2149 if (*move == ';') move++;
2091 } 2150 }
@@ -2118,7 +2177,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2118#define PREFERRED_TILE_SIZE 64 2177#define PREFERRED_TILE_SIZE 64
2119 2178
2120static void game_compute_size(const game_params *params, int tilesize, 2179static void game_compute_size(const game_params *params, int tilesize,
2121 int *x, int *y) 2180 const game_ui *ui, int *x, int *y)
2122{ 2181{
2123 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2182 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2124 struct { int tilesize; } ads, *ds = &ads; 2183 struct { int tilesize; } ads, *ds = &ads;
@@ -2402,7 +2461,7 @@ static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y,
2402static void draw_monster_count(drawing *dr, game_drawstate *ds, 2461static void draw_monster_count(drawing *dr, game_drawstate *ds,
2403 const game_state *state, int c, bool hflash) { 2462 const game_state *state, int c, bool hflash) {
2404 int dx,dy; 2463 int dx,dy;
2405 char buf[8]; 2464 char buf[MAX_DIGITS(int) + 1];
2406 char bufm[8]; 2465 char bufm[8];
2407 2466
2408 dy = TILESIZE/4; 2467 dy = TILESIZE/4;
@@ -2447,7 +2506,7 @@ static void draw_path_hint(drawing *dr, game_drawstate *ds,
2447 const struct game_params *params, 2506 const struct game_params *params,
2448 int hint_index, bool hflash, int hint) { 2507 int hint_index, bool hflash, int hint) {
2449 int x, y, color, dx, dy, text_dx, text_dy, text_size; 2508 int x, y, color, dx, dy, text_dx, text_dy, text_size;
2450 char buf[4]; 2509 char buf[MAX_DIGITS(int) + 1];
2451 2510
2452 if (ds->hint_errors[hint_index]) 2511 if (ds->hint_errors[hint_index])
2453 color = COL_ERROR; 2512 color = COL_ERROR;
@@ -2605,8 +2664,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2605 2664
2606 /* Draw static grid components at startup */ 2665 /* Draw static grid components at startup */
2607 if (!ds->started) { 2666 if (!ds->started) {
2608 draw_rect(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
2609 2*BORDER+(ds->h+3)*TILESIZE, COL_BACKGROUND);
2610 draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1, 2667 draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1,
2611 (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID); 2668 (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID);
2612 for (i=0;i<ds->w;i++) 2669 for (i=0;i<ds->w;i++)
@@ -2745,19 +2802,6 @@ static int game_status(const game_state *state)
2745 return state->solved; 2802 return state->solved;
2746} 2803}
2747 2804
2748static bool game_timing_state(const game_state *state, game_ui *ui)
2749{
2750 return true;
2751}
2752
2753static void game_print_size(const game_params *params, float *x, float *y)
2754{
2755}
2756
2757static void game_print(drawing *dr, const game_state *state, int tilesize)
2758{
2759}
2760
2761#ifdef COMBINED 2805#ifdef COMBINED
2762#define thegame undead 2806#define thegame undead
2763#endif 2807#endif
@@ -2779,12 +2823,14 @@ const struct game thegame = {
2779 free_game, 2823 free_game,
2780 true, solve_game, 2824 true, solve_game,
2781 true, game_can_format_as_text_now, game_text_format, 2825 true, game_can_format_as_text_now, game_text_format,
2826 get_prefs, set_prefs,
2782 new_ui, 2827 new_ui,
2783 free_ui, 2828 free_ui,
2784 encode_ui, 2829 NULL, /* encode_ui */
2785 decode_ui, 2830 NULL, /* decode_ui */
2786 game_request_keys, 2831 game_request_keys,
2787 game_changed_state, 2832 game_changed_state,
2833 current_key_label,
2788 interpret_move, 2834 interpret_move,
2789 execute_move, 2835 execute_move,
2790 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2836 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2796,8 +2842,8 @@ const struct game thegame = {
2796 game_flash_length, 2842 game_flash_length,
2797 game_get_cursor_location, 2843 game_get_cursor_location,
2798 game_status, 2844 game_status,
2799 false, false, game_print_size, game_print, 2845 false, false, NULL, NULL, /* print_size, print */
2800 false, /* wants_statusbar */ 2846 false, /* wants_statusbar */
2801 false, game_timing_state, 2847 false, NULL, /* timing_state */
2802 0, /* flags */ 2848 0, /* flags */
2803}; 2849};
diff --git a/apps/plugins/puzzles/src/unequal.R b/apps/plugins/puzzles/src/unequal.R
deleted file mode 100644
index 077407ffff..0000000000
--- a/apps/plugins/puzzles/src/unequal.R
+++ /dev/null
@@ -1,25 +0,0 @@
1# -*- makefile -*-
2
3UNEQUAL_EXTRA = LATIN
4UNEQUAL_EXTRA_SOLVER = LATIN_SOLVER
5
6unequal : [X] GTK COMMON unequal UNEQUAL_EXTRA unequal-icon|no-icon
7
8unequal : [G] WINDOWS COMMON unequal UNEQUAL_EXTRA unequal.res|noicon.res
9
10unequalsolver : [U] unequal[STANDALONE_SOLVER] UNEQUAL_EXTRA_SOLVER STANDALONE
11unequalsolver : [C] unequal[STANDALONE_SOLVER] UNEQUAL_EXTRA_SOLVER STANDALONE
12
13ALL += unequal[COMBINED] UNEQUAL_EXTRA
14
15!begin am gtk
16GAMES += unequal
17!end
18
19!begin >list.c
20 A(unequal) \
21!end
22
23!begin >gamedesc.txt
24unequal:unequal.exe:Unequal:Latin square puzzle:Complete the latin square in accordance with the > signs.
25!end
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
index 153954e510..ab32e4ab79 100644
--- a/apps/plugins/puzzles/src/unequal.c
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -21,7 +21,11 @@
21#include <string.h> 21#include <string.h>
22#include <assert.h> 22#include <assert.h>
23#include <ctype.h> 23#include <ctype.h>
24#include <math.h> 24#ifdef NO_TGMATH_H
25# include <math.h>
26#else
27# include <tgmath.h>
28#endif
25 29
26#include "puzzles.h" 30#include "puzzles.h"
27#include "latin.h" /* contains typedef for digit */ 31#include "latin.h" /* contains typedef for digit */
@@ -84,6 +88,7 @@ struct game_params {
84#define ADJ_TO_SPENT(x) ((x) << 9) 88#define ADJ_TO_SPENT(x) ((x) << 9)
85 89
86#define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT) 90#define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT)
91#define F_SPENT_MASK (F_SPENT_UP|F_SPENT_RIGHT|F_SPENT_DOWN|F_SPENT_LEFT)
87 92
88struct game_state { 93struct game_state {
89 int order; 94 int order;
@@ -890,13 +895,14 @@ static int solver_state(game_state *state, int maxdiff)
890 struct latin_solver solver; 895 struct latin_solver solver;
891 int diff; 896 int diff;
892 897
893 latin_solver_alloc(&solver, state->nums, state->order); 898 if (latin_solver_alloc(&solver, state->nums, state->order))
894 899 diff = latin_solver_main(&solver, maxdiff,
895 diff = latin_solver_main(&solver, maxdiff, 900 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
896 DIFF_LATIN, DIFF_SET, DIFF_EXTREME, 901 DIFF_EXTREME, DIFF_RECURSIVE,
897 DIFF_EXTREME, DIFF_RECURSIVE, 902 unequal_solvers, unequal_valid, ctx,
898 unequal_solvers, unequal_valid, ctx, 903 clone_ctx, free_ctx);
899 clone_ctx, free_ctx); 904 else
905 diff = DIFF_IMPOSSIBLE;
900 906
901 memcpy(state->hints, solver.cube, state->order*state->order*state->order); 907 memcpy(state->hints, solver.cube, state->order*state->order*state->order);
902 908
@@ -1073,7 +1079,7 @@ static int gg_best_clue(game_state *state, int *scratch, digit *latin)
1073} 1079}
1074 1080
1075#ifdef STANDALONE_SOLVER 1081#ifdef STANDALONE_SOLVER
1076int maxtries; 1082static int maxtries;
1077#define MAXTRIES maxtries 1083#define MAXTRIES maxtries
1078#else 1084#else
1079#define MAXTRIES 50 1085#define MAXTRIES 50
@@ -1430,6 +1436,17 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1430struct game_ui { 1436struct game_ui {
1431 int hx, hy; /* as for solo.c, highlight pos */ 1437 int hx, hy; /* as for solo.c, highlight pos */
1432 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ 1438 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1439
1440 /*
1441 * User preference option: if the user right-clicks in a square
1442 * and presses a number key to add/remove a pencil mark, do we
1443 * hide the mouse highlight again afterwards?
1444 *
1445 * Historically our answer was yes. The Android port prefers no.
1446 * There are advantages both ways, depending how much you dislike
1447 * the highlight cluttering your view. So it's a preference.
1448 */
1449 bool pencil_keep_highlight;
1433}; 1450};
1434 1451
1435static game_ui *new_ui(const game_state *state) 1452static game_ui *new_ui(const game_state *state)
@@ -1438,8 +1455,9 @@ static game_ui *new_ui(const game_state *state)
1438 1455
1439 ui->hx = ui->hy = 0; 1456 ui->hx = ui->hy = 0;
1440 ui->hpencil = false; 1457 ui->hpencil = false;
1441 ui->hshow = false; 1458 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1442 ui->hcursor = false; 1459
1460 ui->pencil_keep_highlight = false;
1443 1461
1444 return ui; 1462 return ui;
1445} 1463}
@@ -1449,13 +1467,26 @@ static void free_ui(game_ui *ui)
1449 sfree(ui); 1467 sfree(ui);
1450} 1468}
1451 1469
1452static char *encode_ui(const game_ui *ui) 1470static config_item *get_prefs(game_ui *ui)
1453{ 1471{
1454 return NULL; 1472 config_item *ret;
1473
1474 ret = snewn(2, config_item);
1475
1476 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1477 ret[0].kw = "pencil-keep-highlight";
1478 ret[0].type = C_BOOLEAN;
1479 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1480
1481 ret[1].name = NULL;
1482 ret[1].type = C_END;
1483
1484 return ret;
1455} 1485}
1456 1486
1457static void decode_ui(game_ui *ui, const char *encoding) 1487static void set_prefs(game_ui *ui, const config_item *cfg)
1458{ 1488{
1489 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1459} 1490}
1460 1491
1461static void game_changed_state(game_ui *ui, const game_state *oldstate, 1492static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1470,6 +1501,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1470 } 1501 }
1471} 1502}
1472 1503
1504static const char *current_key_label(const game_ui *ui,
1505 const game_state *state, int button)
1506{
1507 if (ui->hshow && IS_CURSOR_SELECT(button))
1508 return ui->hpencil ? "Ink" : "Pencil";
1509 return "";
1510}
1511
1473struct game_drawstate { 1512struct game_drawstate {
1474 int tilesize, order; 1513 int tilesize, order;
1475 bool started; 1514 bool started;
@@ -1491,7 +1530,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1491 char buf[80]; 1530 char buf[80];
1492 bool shift_or_control = button & (MOD_SHFT | MOD_CTRL); 1531 bool shift_or_control = button & (MOD_SHFT | MOD_CTRL);
1493 1532
1494 button &= ~MOD_MASK; 1533 button = STRIP_BUTTON_MODIFIERS(button);
1495 1534
1496 if (x >= 0 && x < ds->order && y >= 0 && y < ds->order && IS_MOUSE_DOWN(button)) { 1535 if (x >= 0 && x < ds->order && y >= 0 && y < ds->order && IS_MOUSE_DOWN(button)) {
1497 if (oy - COORD(y) > TILE_SIZE && ox - COORD(x) > TILE_SIZE) 1536 if (oy - COORD(y) > TILE_SIZE && ox - COORD(x) > TILE_SIZE)
@@ -1527,7 +1566,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1527 ui->hshow = true; 1566 ui->hshow = true;
1528 } 1567 }
1529 ui->hcursor = false; 1568 ui->hcursor = false;
1530 return UI_UPDATE; 1569 return MOVE_UI_UPDATE;
1531 } 1570 }
1532 if (button == RIGHT_BUTTON) { 1571 if (button == RIGHT_BUTTON) {
1533 /* pencil highlighting for non-filled squares */ 1572 /* pencil highlighting for non-filled squares */
@@ -1541,7 +1580,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1541 ui->hshow = true; 1580 ui->hshow = true;
1542 } 1581 }
1543 ui->hcursor = false; 1582 ui->hcursor = false;
1544 return UI_UPDATE; 1583 return MOVE_UI_UPDATE;
1545 } 1584 }
1546 } 1585 }
1547 1586
@@ -1549,7 +1588,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1549 if (shift_or_control) { 1588 if (shift_or_control) {
1550 int nx = ui->hx, ny = ui->hy, i; 1589 int nx = ui->hx, ny = ui->hy, i;
1551 bool self; 1590 bool self;
1552 move_cursor(button, &nx, &ny, ds->order, ds->order, false); 1591 move_cursor(button, &nx, &ny, ds->order, ds->order, false, NULL);
1553 ui->hshow = true; 1592 ui->hshow = true;
1554 ui->hcursor = true; 1593 ui->hcursor = true;
1555 1594
@@ -1557,12 +1596,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1557 ny != ui->hy + adjthan[i].dy); ++i); 1596 ny != ui->hy + adjthan[i].dy); ++i);
1558 1597
1559 if (i == 4) 1598 if (i == 4)
1560 return UI_UPDATE; /* invalid direction, i.e. out of 1599 return MOVE_UI_UPDATE; /* invalid direction, i.e. out of
1561 * the board */ 1600 * the board */
1562 1601
1563 if (!(GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f || 1602 if (!(GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f ||
1564 GRID(state, flags, nx, ny ) & adjthan[i].fo)) 1603 GRID(state, flags, nx, ny ) & adjthan[i].fo))
1565 return UI_UPDATE; /* no clue to toggle */ 1604 return MOVE_UI_UPDATE; /* no clue to toggle */
1566 1605
1567 if (state->mode == MODE_ADJACENT) 1606 if (state->mode == MODE_ADJACENT)
1568 self = (adjthan[i].dx >= 0 && adjthan[i].dy >= 0); 1607 self = (adjthan[i].dx >= 0 && adjthan[i].dy >= 0);
@@ -1570,24 +1609,23 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1570 self = (GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f); 1609 self = (GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f);
1571 1610
1572 if (self) 1611 if (self)
1573 sprintf(buf, "F%d,%d,%d", ui->hx, ui->hy, 1612 sprintf(buf, "F%d,%d,%u", ui->hx, ui->hy,
1574 ADJ_TO_SPENT(adjthan[i].f)); 1613 ADJ_TO_SPENT(adjthan[i].f));
1575 else 1614 else
1576 sprintf(buf, "F%d,%d,%d", nx, ny, 1615 sprintf(buf, "F%d,%d,%u", nx, ny,
1577 ADJ_TO_SPENT(adjthan[i].fo)); 1616 ADJ_TO_SPENT(adjthan[i].fo));
1578 1617
1579 return dupstr(buf); 1618 return dupstr(buf);
1580 } else { 1619 } else {
1581 move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order, false);
1582 ui->hshow = true;
1583 ui->hcursor = true; 1620 ui->hcursor = true;
1584 return UI_UPDATE; 1621 return move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order,
1622 false, &ui->hshow);
1585 } 1623 }
1586 } 1624 }
1587 if (ui->hshow && IS_CURSOR_SELECT(button)) { 1625 if (ui->hshow && IS_CURSOR_SELECT(button)) {
1588 ui->hpencil = !ui->hpencil; 1626 ui->hpencil = !ui->hpencil;
1589 ui->hcursor = true; 1627 ui->hcursor = true;
1590 return UI_UPDATE; 1628 return MOVE_UI_UPDATE;
1591 } 1629 }
1592 1630
1593 n = c2n(button, state->order); 1631 n = c2n(button, state->order);
@@ -1604,11 +1642,37 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1604 if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0) 1642 if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0)
1605 return NULL; /* can't change hints on filled square (!) */ 1643 return NULL; /* can't change hints on filled square (!) */
1606 1644
1645 /*
1646 * If you ask to fill a square with what it already contains,
1647 * or blank it when it's already empty, that has no effect...
1648 */
1649 if ((!ui->hpencil || n == 0) &&
1650 GRID(state, nums, ui->hx, ui->hy) == n) {
1651 bool anypencil = false;
1652 int i;
1653 for (i = 0; i < state->order; i++)
1654 anypencil = anypencil || HINT(state, ui->hx, ui->hy, i);
1655 if (!anypencil) {
1656 /* ... expect to remove the cursor in mouse mode. */
1657 if (!ui->hcursor) {
1658 ui->hshow = false;
1659 return MOVE_UI_UPDATE;
1660 }
1661 return NULL;
1662 }
1663 }
1607 1664
1608 sprintf(buf, "%c%d,%d,%d", 1665 sprintf(buf, "%c%d,%d,%d",
1609 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 1666 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1610 1667
1611 if (!ui->hcursor) ui->hshow = false; 1668 /*
1669 * Hide the highlight after a keypress, if it was mouse-
1670 * generated. Also, don't hide it if this move has changed
1671 * pencil marks and the user preference says not to hide the
1672 * highlight in that situation.
1673 */
1674 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1675 ui->hshow = false;
1612 1676
1613 return dupstr(buf); 1677 return dupstr(buf);
1614 } 1678 }
@@ -1624,7 +1688,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1624static game_state *execute_move(const game_state *state, const char *move) 1688static game_state *execute_move(const game_state *state, const char *move)
1625{ 1689{
1626 game_state *ret = NULL; 1690 game_state *ret = NULL;
1627 int x, y, n, i, rc; 1691 int x, y, n, i;
1628 1692
1629 debug(("execute_move: %s", move)); 1693 debug(("execute_move: %s", move));
1630 1694
@@ -1649,7 +1713,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1649 const char *p; 1713 const char *p;
1650 1714
1651 ret = dup_game(state); 1715 ret = dup_game(state);
1652 ret->completed = ret->cheated = true; 1716 ret->cheated = true;
1653 1717
1654 p = move+1; 1718 p = move+1;
1655 for (i = 0; i < state->order*state->order; i++) { 1719 for (i = 0; i < state->order*state->order; i++) {
@@ -1660,8 +1724,8 @@ static game_state *execute_move(const game_state *state, const char *move)
1660 p++; 1724 p++;
1661 } 1725 }
1662 if (*p) goto badmove; 1726 if (*p) goto badmove;
1663 rc = check_complete(ret->nums, ret, true); 1727 if (!ret->completed && check_complete(ret->nums, ret, true) > 0)
1664 assert(rc > 0); 1728 ret->completed = true;
1665 return ret; 1729 return ret;
1666 } else if (move[0] == 'M') { 1730 } else if (move[0] == 'M') {
1667 ret = dup_game(state); 1731 ret = dup_game(state);
@@ -1678,7 +1742,8 @@ static game_state *execute_move(const game_state *state, const char *move)
1678 check_complete(ret->nums, ret, true); 1742 check_complete(ret->nums, ret, true);
1679 return ret; 1743 return ret;
1680 } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 && 1744 } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1681 x >= 0 && x < state->order && y >= 0 && y < state->order) { 1745 x >= 0 && x < state->order && y >= 0 && y < state->order &&
1746 (n & ~F_SPENT_MASK) == 0) {
1682 ret = dup_game(state); 1747 ret = dup_game(state);
1683 GRID(ret, flags, x, y) ^= n; 1748 GRID(ret, flags, x, y) ^= n;
1684 return ret; 1749 return ret;
@@ -1696,7 +1761,7 @@ badmove:
1696#define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2) 1761#define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2)
1697 1762
1698static void game_compute_size(const game_params *params, int tilesize, 1763static void game_compute_size(const game_params *params, int tilesize,
1699 int *x, int *y) 1764 const game_ui *ui, int *x, int *y)
1700{ 1765{
1701 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1766 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1702 struct { int tilesize, order; } ads, *ds = &ads; 1767 struct { int tilesize, order; } ads, *ds = &ads;
@@ -1779,18 +1844,18 @@ static void draw_gt(drawing *dr, int ox, int oy,
1779{ 1844{
1780 int coords[12]; 1845 int coords[12];
1781 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0); 1846 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0);
1782 coords[0] = ox + xdx + dx1; 1847 coords[0] = ox + xdx;
1783 coords[1] = oy + xdy + dy1; 1848 coords[1] = oy + xdy;
1784 coords[2] = ox + xdx + dx1 + dx2; 1849 coords[2] = ox + xdx + dx1;
1785 coords[3] = oy + xdy + dy1 + dy2; 1850 coords[3] = oy + xdy + dy1;
1786 coords[4] = ox - xdx + dx1 + dx2; 1851 coords[4] = ox + xdx + dx1 + dx2;
1787 coords[5] = oy - xdy + dy1 + dy2; 1852 coords[5] = oy + xdy + dy1 + dy2;
1788 coords[6] = ox - xdx + dx1; 1853 coords[6] = ox - xdx + dx1 + dx2;
1789 coords[7] = oy - xdy + dy1; 1854 coords[7] = oy - xdy + dy1 + dy2;
1790 coords[8] = ox - xdx; 1855 coords[8] = ox - xdx + dx1;
1791 coords[9] = oy - xdy; 1856 coords[9] = oy - xdy + dy1;
1792 coords[10] = ox + xdx; 1857 coords[10] = ox - xdx;
1793 coords[11] = oy + xdy; 1858 coords[11] = oy - xdy;
1794 draw_polygon(dr, coords, 6, col, col); 1859 draw_polygon(dr, coords, 6, col, col);
1795} 1860}
1796 1861
@@ -2059,22 +2124,19 @@ static int game_status(const game_state *state)
2059 return state->completed ? +1 : 0; 2124 return state->completed ? +1 : 0;
2060} 2125}
2061 2126
2062static bool game_timing_state(const game_state *state, game_ui *ui) 2127static void game_print_size(const game_params *params, const game_ui *ui,
2063{ 2128 float *x, float *y)
2064 return true;
2065}
2066
2067static void game_print_size(const game_params *params, float *x, float *y)
2068{ 2129{
2069 int pw, ph; 2130 int pw, ph;
2070 2131
2071 /* 10mm squares by default, roughly the same as Grauniad. */ 2132 /* 10mm squares by default, roughly the same as Grauniad. */
2072 game_compute_size(params, 1000, &pw, &ph); 2133 game_compute_size(params, 1000, ui, &pw, &ph);
2073 *x = pw / 100.0F; 2134 *x = pw / 100.0F;
2074 *y = ph / 100.0F; 2135 *y = ph / 100.0F;
2075} 2136}
2076 2137
2077static void game_print(drawing *dr, const game_state *state, int tilesize) 2138static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2139 int tilesize)
2078{ 2140{
2079 int ink = print_mono_colour(dr, 0); 2141 int ink = print_mono_colour(dr, 0);
2080 int x, y, o = state->order, ox, oy, n; 2142 int x, y, o = state->order, ox, oy, n;
@@ -2133,12 +2195,14 @@ const struct game thegame = {
2133 free_game, 2195 free_game,
2134 true, solve_game, 2196 true, solve_game,
2135 true, game_can_format_as_text_now, game_text_format, 2197 true, game_can_format_as_text_now, game_text_format,
2198 get_prefs, set_prefs,
2136 new_ui, 2199 new_ui,
2137 free_ui, 2200 free_ui,
2138 encode_ui, 2201 NULL, /* encode_ui */
2139 decode_ui, 2202 NULL, /* decode_ui */
2140 game_request_keys, 2203 game_request_keys,
2141 game_changed_state, 2204 game_changed_state,
2205 current_key_label,
2142 interpret_move, 2206 interpret_move,
2143 execute_move, 2207 execute_move,
2144 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2208 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2152,7 +2216,7 @@ const struct game thegame = {
2152 game_status, 2216 game_status,
2153 true, false, game_print_size, game_print, 2217 true, false, game_print_size, game_print,
2154 false, /* wants_statusbar */ 2218 false, /* wants_statusbar */
2155 false, game_timing_state, 2219 false, NULL, /* timing_state */
2156 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2220 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2157}; 2221};
2158 2222
@@ -2165,7 +2229,7 @@ const struct game thegame = {
2165#include <time.h> 2229#include <time.h>
2166#include <stdarg.h> 2230#include <stdarg.h>
2167 2231
2168const char *quis = NULL; 2232static const char *quis = NULL;
2169 2233
2170#if 0 /* currently unused */ 2234#if 0 /* currently unused */
2171 2235
@@ -2233,13 +2297,14 @@ static int solve(game_params *p, char *desc, int debug)
2233 solver_show_working = debug; 2297 solver_show_working = debug;
2234 game_debug(state); 2298 game_debug(state);
2235 2299
2236 latin_solver_alloc(&solver, state->nums, state->order); 2300 if (latin_solver_alloc(&solver, state->nums, state->order))
2237 2301 diff = latin_solver_main(&solver, DIFF_RECURSIVE,
2238 diff = latin_solver_main(&solver, DIFF_RECURSIVE, 2302 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
2239 DIFF_LATIN, DIFF_SET, DIFF_EXTREME, 2303 DIFF_EXTREME, DIFF_RECURSIVE,
2240 DIFF_EXTREME, DIFF_RECURSIVE, 2304 unequal_solvers, unequal_valid, ctx,
2241 unequal_solvers, unequal_valid, ctx, 2305 clone_ctx, free_ctx);
2242 clone_ctx, free_ctx); 2306 else
2307 diff = DIFF_IMPOSSIBLE;
2243 2308
2244 free_ctx(ctx); 2309 free_ctx(ctx);
2245 2310
diff --git a/apps/plugins/puzzles/src/unfinished/CMakeLists.txt b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt
new file mode 100644
index 0000000000..0c1e331f9b
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/CMakeLists.txt
@@ -0,0 +1,31 @@
1puzzle(group
2 DISPLAYNAME "Group"
3 DESCRIPTION "Group theory puzzle"
4 OBJECTIVE "Complete the unfinished Cayley table of a group.")
5solver(group ${CMAKE_SOURCE_DIR}/latin.c)
6
7puzzle(separate
8 DISPLAYNAME "Separate"
9 DESCRIPTION "Rectangle-dividing puzzle"
10 OBJECTIVE "Partition the grid into regions containing one of each letter.")
11
12puzzle(slide
13 DISPLAYNAME "Slide"
14 DESCRIPTION "Sliding block puzzle"
15 OBJECTIVE "Slide the blocks to let the key block out.")
16solver(slide)
17
18puzzle(sokoban
19 DISPLAYNAME "Sokoban"
20 DESCRIPTION "Barrel-pushing puzzle"
21 OBJECTIVE "Push all the barrels into the target squares.")
22
23# These unfinished programs don't even have the structure of a puzzle
24# game yet; they're just command-line programs containing test
25# implementations of some of the needed functionality.
26
27cliprogram(numgame numgame.c)
28
29cliprogram(path path.c COMPILE_DEFINITIONS TEST_GEN)
30
31export_variables_to_parent_scope()
diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README
new file mode 100644
index 0000000000..c96ccc935a
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/README
@@ -0,0 +1,14 @@
1This subdirectory contains puzzle implementations which are
2half-written, fundamentally flawed, or in other ways unready to be
3shipped as part of the polished Puzzles collection.
4
5The CMake build system will _build_ all of the source in this
6directory (to ensure it hasn't become unbuildable), but they won't be
7included in all-in-one puzzle binaries or installed by 'make install'
8targets. If you want to temporarily change that, you can reconfigure
9your build by defining the CMake variable PUZZLES_ENABLE_UNFINISHED.
10For example,
11
12 cmake . -DPUZZLES_ENABLE_UNFINISHED="group;slide"
13
14will build as if both Group and Slide were fully official puzzles.
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c
new file mode 100644
index 0000000000..faffa89485
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.c
@@ -0,0 +1,2497 @@
1/*
2 * group.c: a Latin-square puzzle, but played with groups' Cayley
3 * tables. That is, you are given a Cayley table of a group with
4 * most elements blank and a few clues, and you must fill it in
5 * so as to preserve the group axioms.
6 *
7 * This is a perfectly playable and fully working puzzle, but I'm
8 * leaving it for the moment in the 'unfinished' directory because
9 * it's just too esoteric (not to mention _hard_) for me to be
10 * comfortable presenting it to the general public as something they
11 * might (implicitly) actually want to play.
12 *
13 * TODO:
14 *
15 * - more solver techniques?
16 * * Inverses: once we know that gh = e, we can immediately
17 * deduce hg = e as well; then for any gx=y we can deduce
18 * hy=x, and for any xg=y we have yh=x.
19 * * Hard-mode associativity: we currently deduce based on
20 * definite numbers in the grid, but we could also winnow
21 * based on _possible_ numbers.
22 * * My overambitious original thoughts included wondering if we
23 * could infer that there must be elements of certain orders
24 * (e.g. a group of order divisible by 5 must contain an
25 * element of order 5), but I think in fact this is probably
26 * silly.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33#include <ctype.h>
34#ifdef NO_TGMATH_H
35# include <math.h>
36#else
37# include <tgmath.h>
38#endif
39
40#include "puzzles.h"
41#include "latin.h"
42
43/*
44 * Difficulty levels. I do some macro ickery here to ensure that my
45 * enum and the various forms of my name list always match up.
46 */
47#define DIFFLIST(A) \
48 A(TRIVIAL,Trivial,NULL,t) \
49 A(NORMAL,Normal,solver_normal,n) \
50 A(HARD,Hard,solver_hard,h) \
51 A(EXTREME,Extreme,NULL,x) \
52 A(UNREASONABLE,Unreasonable,NULL,u)
53#define ENUM(upper,title,func,lower) DIFF_ ## upper,
54#define TITLE(upper,title,func,lower) #title,
55#define ENCODE(upper,title,func,lower) #lower
56#define CONFIG(upper,title,func,lower) ":" #title
57enum { DIFFLIST(ENUM) DIFFCOUNT };
58static char const *const group_diffnames[] = { DIFFLIST(TITLE) };
59static char const group_diffchars[] = DIFFLIST(ENCODE);
60#define DIFFCONFIG DIFFLIST(CONFIG)
61
62enum {
63 COL_BACKGROUND,
64 COL_GRID,
65 COL_USER,
66 COL_HIGHLIGHT,
67 COL_ERROR,
68 COL_PENCIL,
69 COL_DIAGONAL,
70 NCOLOURS
71};
72
73/*
74 * In identity mode, we number the elements e,a,b,c,d,f,g,h,...
75 * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way.
76 */
77#define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) )
78#define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) )
79
80#define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id)
81#define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z'))
82#define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1))
83
84struct game_params {
85 int w, diff;
86 bool id;
87};
88
89typedef struct group_common {
90 int refcount;
91 bool *immutable;
92} group_common;
93
94struct game_state {
95 game_params par;
96 digit *grid;
97 int *pencil; /* bitmaps using bits 1<<1..1<<n */
98 group_common *common;
99 bool completed, cheated;
100 digit *sequence; /* sequence of group elements shown */
101
102 /*
103 * This array indicates thick lines separating rows and columns
104 * placed and unplaced manually by the user as a visual aid, e.g.
105 * to delineate a subgroup and its cosets.
106 *
107 * When a line is placed, it's deemed to be between the two
108 * particular group elements that are on either side of it at the
109 * time; dragging those two away from each other automatically
110 * gets rid of the line. Hence, for a given element i, dividers[i]
111 * is either -1 (indicating no divider to the right of i), or some
112 * other element (indicating a divider to the right of i iff that
113 * element is the one right of it). These are eagerly cleared
114 * during drags.
115 */
116 int *dividers; /* thick lines between rows/cols */
117};
118
119static game_params *default_params(void)
120{
121 game_params *ret = snew(game_params);
122
123 ret->w = 6;
124 ret->diff = DIFF_NORMAL;
125 ret->id = true;
126
127 return ret;
128}
129
130static const struct game_params group_presets[] = {
131 { 6, DIFF_NORMAL, true },
132 { 6, DIFF_NORMAL, false },
133 { 8, DIFF_NORMAL, true },
134 { 8, DIFF_NORMAL, false },
135 { 8, DIFF_HARD, true },
136 { 8, DIFF_HARD, false },
137 { 12, DIFF_NORMAL, true },
138};
139
140static bool game_fetch_preset(int i, char **name, game_params **params)
141{
142 game_params *ret;
143 char buf[80];
144
145 if (i < 0 || i >= lenof(group_presets))
146 return false;
147
148 ret = snew(game_params);
149 *ret = group_presets[i]; /* structure copy */
150
151 sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff],
152 ret->id ? "" : ", identity hidden");
153
154 *name = dupstr(buf);
155 *params = ret;
156 return true;
157}
158
159static void free_params(game_params *params)
160{
161 sfree(params);
162}
163
164static game_params *dup_params(const game_params *params)
165{
166 game_params *ret = snew(game_params);
167 *ret = *params; /* structure copy */
168 return ret;
169}
170
171static void decode_params(game_params *params, char const *string)
172{
173 char const *p = string;
174
175 params->w = atoi(p);
176 while (*p && isdigit((unsigned char)*p)) p++;
177 params->diff = DIFF_NORMAL;
178 params->id = true;
179
180 while (*p) {
181 if (*p == 'd') {
182 int i;
183 p++;
184 params->diff = DIFFCOUNT+1; /* ...which is invalid */
185 if (*p) {
186 for (i = 0; i < DIFFCOUNT; i++) {
187 if (*p == group_diffchars[i])
188 params->diff = i;
189 }
190 p++;
191 }
192 } else if (*p == 'i') {
193 params->id = false;
194 p++;
195 } else {
196 /* unrecognised character */
197 p++;
198 }
199 }
200}
201
202static char *encode_params(const game_params *params, bool full)
203{
204 char ret[80];
205
206 sprintf(ret, "%d", params->w);
207 if (full)
208 sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]);
209 if (!params->id)
210 sprintf(ret + strlen(ret), "i");
211
212 return dupstr(ret);
213}
214
215static config_item *game_configure(const game_params *params)
216{
217 config_item *ret;
218 char buf[80];
219
220 ret = snewn(4, config_item);
221
222 ret[0].name = "Grid size";
223 ret[0].type = C_STRING;
224 sprintf(buf, "%d", params->w);
225 ret[0].u.string.sval = dupstr(buf);
226
227 ret[1].name = "Difficulty";
228 ret[1].type = C_CHOICES;
229 ret[1].u.choices.choicenames = DIFFCONFIG;
230 ret[1].u.choices.selected = params->diff;
231
232 ret[2].name = "Show identity";
233 ret[2].type = C_BOOLEAN;
234 ret[2].u.boolean.bval = params->id;
235
236 ret[3].name = NULL;
237 ret[3].type = C_END;
238
239 return ret;
240}
241
242static game_params *custom_params(const config_item *cfg)
243{
244 game_params *ret = snew(game_params);
245
246 ret->w = atoi(cfg[0].u.string.sval);
247 ret->diff = cfg[1].u.choices.selected;
248 ret->id = cfg[2].u.boolean.bval;
249
250 return ret;
251}
252
253static const char *validate_params(const game_params *params, bool full)
254{
255 if (params->w < 3 || params->w > 26)
256 return "Grid size must be between 3 and 26";
257 if (params->diff >= DIFFCOUNT)
258 return "Unknown difficulty rating";
259 if (!params->id && params->diff == DIFF_TRIVIAL) {
260 /*
261 * We can't have a Trivial-difficulty puzzle (i.e. latin
262 * square deductions only) without a clear identity, because
263 * identityless puzzles always have two rows and two columns
264 * entirely blank, and no latin-square deduction permits the
265 * distinguishing of two such rows.
266 */
267 return "Trivial puzzles must have an identity";
268 }
269 if (!params->id && params->w == 3) {
270 /*
271 * We can't have a 3x3 puzzle without an identity either,
272 * because 3x3 puzzles can't ever be harder than Trivial
273 * (there are no 3x3 latin squares which aren't also valid
274 * group tables, so enabling group-based deductions doesn't
275 * rule out any possible solutions) and - as above - Trivial
276 * puzzles can't not have an identity.
277 */
278 return "3x3 puzzles must have an identity";
279 }
280 return NULL;
281}
282
283/* ----------------------------------------------------------------------
284 * Solver.
285 */
286
287static int find_identity(struct latin_solver *solver)
288{
289 int w = solver->o;
290 digit *grid = solver->grid;
291 int i, j;
292
293 for (i = 0; i < w; i++)
294 for (j = 0; j < w; j++) {
295 if (grid[i*w+j] == i+1)
296 return j+1;
297 if (grid[i*w+j] == j+1)
298 return i+1;
299 }
300
301 return 0;
302}
303
304static int solver_normal(struct latin_solver *solver, void *vctx)
305{
306 int w = solver->o;
307#ifdef STANDALONE_SOLVER
308 char **names = solver->names;
309#endif
310 digit *grid = solver->grid;
311 int i, j, k;
312
313 /*
314 * Deduce using associativity: (ab)c = a(bc).
315 *
316 * So we pick any a,b,c we like; then if we know ab, bc, and
317 * (ab)c we can fill in a(bc).
318 */
319 for (i = 0; i < w; i++)
320 for (j = 0; j < w; j++)
321 for (k = 0; k < w; k++) {
322 if (!grid[i*w+j] || !grid[j*w+k])
323 continue;
324 if (grid[(grid[i*w+j]-1)*w+k] &&
325 !grid[i*w+(grid[j*w+k]-1)]) {
326 int x = grid[j*w+k]-1, y = i;
327 int n = grid[(grid[i*w+j]-1)*w+k];
328#ifdef STANDALONE_SOLVER
329 if (solver_show_working) {
330 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
331 solver_recurse_depth*4, "",
332 names[i], names[j], names[k],
333 names[grid[i*w+j]-1], names[k],
334 names[i], names[grid[j*w+k]-1]);
335 printf("%*s placing %s at (%d,%d)\n",
336 solver_recurse_depth*4, "",
337 names[n-1], x+1, y+1);
338 }
339#endif
340 if (solver->cube[(x*w+y)*w+n-1]) {
341 latin_solver_place(solver, x, y, n);
342 return 1;
343 } else {
344#ifdef STANDALONE_SOLVER
345 if (solver_show_working)
346 printf("%*s contradiction!\n",
347 solver_recurse_depth*4, "");
348 return -1;
349#endif
350 }
351 }
352 if (!grid[(grid[i*w+j]-1)*w+k] &&
353 grid[i*w+(grid[j*w+k]-1)]) {
354 int x = k, y = grid[i*w+j]-1;
355 int n = grid[i*w+(grid[j*w+k]-1)];
356#ifdef STANDALONE_SOLVER
357 if (solver_show_working) {
358 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
359 solver_recurse_depth*4, "",
360 names[i], names[j], names[k],
361 names[grid[i*w+j]-1], names[k],
362 names[i], names[grid[j*w+k]-1]);
363 printf("%*s placing %s at (%d,%d)\n",
364 solver_recurse_depth*4, "",
365 names[n-1], x+1, y+1);
366 }
367#endif
368 if (solver->cube[(x*w+y)*w+n-1]) {
369 latin_solver_place(solver, x, y, n);
370 return 1;
371 } else {
372#ifdef STANDALONE_SOLVER
373 if (solver_show_working)
374 printf("%*s contradiction!\n",
375 solver_recurse_depth*4, "");
376 return -1;
377#endif
378 }
379 }
380 }
381
382 /*
383 * Fill in the row and column for the group identity, if it's not
384 * already known and if we've just found out what it is.
385 */
386 i = find_identity(solver);
387 if (i) {
388 bool done_something = false;
389 for (j = 1; j <= w; j++) {
390 if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) {
391 done_something = true;
392 }
393 }
394 if (done_something) {
395#ifdef STANDALONE_SOLVER
396 if (solver_show_working) {
397 printf("%*s%s is the group identity\n",
398 solver_recurse_depth*4, "", names[i-1]);
399 }
400#endif
401 for (j = 1; j <= w; j++) {
402 if (!grid[(j-1)*w+(i-1)]) {
403 if (!cube(i-1, j-1, j)) {
404#ifdef STANDALONE_SOLVER
405 if (solver_show_working) {
406 printf("%*s but %s cannot go at (%d,%d) - "
407 "contradiction!\n",
408 solver_recurse_depth*4, "",
409 names[j-1], i, j);
410 }
411#endif
412 return -1;
413 }
414#ifdef STANDALONE_SOLVER
415 if (solver_show_working) {
416 printf("%*s placing %s at (%d,%d)\n",
417 solver_recurse_depth*4, "",
418 names[j-1], i, j);
419 }
420#endif
421 latin_solver_place(solver, i-1, j-1, j);
422 }
423 if (!grid[(i-1)*w+(j-1)]) {
424 if (!cube(j-1, i-1, j)) {
425#ifdef STANDALONE_SOLVER
426 if (solver_show_working) {
427 printf("%*s but %s cannot go at (%d,%d) - "
428 "contradiction!\n",
429 solver_recurse_depth*4, "",
430 names[j-1], j, i);
431 }
432#endif
433 return -1;
434 }
435#ifdef STANDALONE_SOLVER
436 if (solver_show_working) {
437 printf("%*s placing %s at (%d,%d)\n",
438 solver_recurse_depth*4, "",
439 names[j-1], j, i);
440 }
441#endif
442 latin_solver_place(solver, j-1, i-1, j);
443 }
444 }
445 return 1;
446 }
447 }
448
449 return 0;
450}
451
452static int solver_hard(struct latin_solver *solver, void *vctx)
453{
454 bool done_something = false;
455 int w = solver->o;
456#ifdef STANDALONE_SOLVER
457 char **names = solver->names;
458#endif
459 int i, j;
460
461 /*
462 * In identity-hidden mode, systematically rule out possibilities
463 * for the group identity.
464 *
465 * In solver_normal, we used the fact that any filled square in
466 * the grid whose contents _does_ match one of the elements it's
467 * the product of - that is, ab=a or ab=b - tells you immediately
468 * that the other element is the identity.
469 *
470 * Here, we use the flip side of that: any filled square in the
471 * grid whose contents does _not_ match either its row or column -
472 * that is, if ab is neither a nor b - tells you immediately that
473 * _neither_ of those elements is the identity. And if that's
474 * true, then we can also immediately rule out the possibility
475 * that it acts as the identity on any element at all.
476 */
477 for (i = 0; i < w; i++) {
478 bool i_can_be_id = true;
479#ifdef STANDALONE_SOLVER
480 char title[80];
481#endif
482
483 for (j = 0; j < w; j++) {
484 if (grid(i,j) && grid(i,j) != j+1) {
485#ifdef STANDALONE_SOLVER
486 if (solver_show_working)
487 sprintf(title, "%s cannot be the identity: "
488 "%s%s = %s =/= %s", names[i], names[i], names[j],
489 names[grid(i,j)-1], names[j]);
490#endif
491 i_can_be_id = false;
492 break;
493 }
494 if (grid(j,i) && grid(j,i) != j+1) {
495#ifdef STANDALONE_SOLVER
496 if (solver_show_working)
497 sprintf(title, "%s cannot be the identity: "
498 "%s%s = %s =/= %s", names[i], names[j], names[i],
499 names[grid(j,i)-1], names[j]);
500#endif
501 i_can_be_id = false;
502 break;
503 }
504 }
505
506 if (!i_can_be_id) {
507 /* Now rule out ij=j or ji=j for all j. */
508 for (j = 0; j < w; j++) {
509 if (cube(i, j, j+1)) {
510#ifdef STANDALONE_SOLVER
511 if (solver_show_working) {
512 if (title[0]) {
513 printf("%*s%s\n", solver_recurse_depth*4, "",
514 title);
515 title[0] = '\0';
516 }
517 printf("%*s ruling out %s at (%d,%d)\n",
518 solver_recurse_depth*4, "", names[j], i, j);
519 }
520#endif
521 cube(i, j, j+1) = false;
522 }
523 if (cube(j, i, j+1)) {
524#ifdef STANDALONE_SOLVER
525 if (solver_show_working) {
526 if (title[0]) {
527 printf("%*s%s\n", solver_recurse_depth*4, "",
528 title);
529 title[0] = '\0';
530 }
531 printf("%*s ruling out %s at (%d,%d)\n",
532 solver_recurse_depth*4, "", names[j], j, i);
533 }
534#endif
535 cube(j, i, j+1) = false;
536 }
537 }
538 }
539 }
540
541 return done_something;
542}
543
544#define SOLVER(upper,title,func,lower) func,
545static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) };
546
547static bool group_valid(struct latin_solver *solver, void *ctx)
548{
549 int w = solver->o;
550#ifdef STANDALONE_SOLVER
551 char **names = solver->names;
552#endif
553 int i, j, k;
554
555 for (i = 0; i < w; i++)
556 for (j = 0; j < w; j++)
557 for (k = 0; k < w; k++) {
558 int ij = grid(i, j) - 1;
559 int jk = grid(j, k) - 1;
560 int ij_k = grid(ij, k) - 1;
561 int i_jk = grid(i, jk) - 1;
562 if (ij_k != i_jk) {
563#ifdef STANDALONE_SOLVER
564 if (solver_show_working) {
565 printf("%*sfailure of associativity: "
566 "(%s%s)%s = %s%s = %s but "
567 "%s(%s%s) = %s%s = %s\n",
568 solver_recurse_depth*4, "",
569 names[i], names[j], names[k],
570 names[ij], names[k], names[ij_k],
571 names[i], names[j], names[k],
572 names[i], names[jk], names[i_jk]);
573 }
574#endif
575 return false;
576 }
577 }
578
579 return true;
580}
581
582static int solver(const game_params *params, digit *grid, int maxdiff)
583{
584 int w = params->w;
585 int ret;
586 struct latin_solver solver;
587
588#ifdef STANDALONE_SOLVER
589 char *p, text[100], *names[50];
590 int i;
591
592 for (i = 0, p = text; i < w; i++) {
593 names[i] = p;
594 *p++ = TOCHAR(i+1, params->id);
595 *p++ = '\0';
596 }
597 solver.names = names;
598#endif
599
600 if (latin_solver_alloc(&solver, grid, w))
601 ret = latin_solver_main(&solver, maxdiff,
602 DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
603 DIFF_EXTREME, DIFF_UNREASONABLE,
604 group_solvers, group_valid, NULL, NULL, NULL);
605 else
606 ret = diff_impossible;
607
608 latin_solver_free(&solver);
609
610 return ret;
611}
612
613/* ----------------------------------------------------------------------
614 * Grid generation.
615 */
616
617static char *encode_grid(char *desc, digit *grid, int area)
618{
619 int run, i;
620 char *p = desc;
621
622 run = 0;
623 for (i = 0; i <= area; i++) {
624 int n = (i < area ? grid[i] : -1);
625
626 if (!n)
627 run++;
628 else {
629 if (run) {
630 while (run > 0) {
631 int c = 'a' - 1 + run;
632 if (run > 26)
633 c = 'z';
634 *p++ = c;
635 run -= c - ('a' - 1);
636 }
637 } else {
638 /*
639 * If there's a number in the very top left or
640 * bottom right, there's no point putting an
641 * unnecessary _ before or after it.
642 */
643 if (p > desc && n > 0)
644 *p++ = '_';
645 }
646 if (n > 0)
647 p += sprintf(p, "%d", n);
648 run = 0;
649 }
650 }
651 return p;
652}
653
654/* ----- data generated by group.gap begins ----- */
655
656struct group {
657 unsigned long autosize;
658 int order, ngens;
659 const char *gens;
660};
661struct groups {
662 int ngroups;
663 const struct group *groups;
664};
665
666static const struct group groupdata[] = {
667 /* order 2 */
668 {1L, 2, 1, "BA"},
669 /* order 3 */
670 {2L, 3, 1, "BCA"},
671 /* order 4 */
672 {2L, 4, 1, "BCDA"},
673 {6L, 4, 2, "BADC" "CDAB"},
674 /* order 5 */
675 {4L, 5, 1, "BCDEA"},
676 /* order 6 */
677 {6L, 6, 2, "CFEBAD" "BADCFE"},
678 {2L, 6, 1, "DCFEBA"},
679 /* order 7 */
680 {6L, 7, 1, "BCDEFGA"},
681 /* order 8 */
682 {4L, 8, 1, "BCEFDGHA"},
683 {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"},
684 {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"},
685 {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"},
686 {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"},
687 /* order 9 */
688 {6L, 9, 1, "BDECGHFIA"},
689 {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"},
690 /* order 10 */
691 {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"},
692 {4L, 10, 1, "DCFEHGJIBA"},
693 /* order 11 */
694 {10L, 11, 1, "BCDEFGHIJKA"},
695 /* order 12 */
696 {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"},
697 {4L, 12, 1, "EHIJKCBLDGFA"},
698 {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"},
699 {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"},
700 {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"},
701 /* order 13 */
702 {12L, 13, 1, "BCDEFGHIJKLMA"},
703 /* order 14 */
704 {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"},
705 {6L, 14, 1, "FEHGJILKNMBADC"},
706 /* order 15 */
707 {8L, 15, 1, "EGHCJKFMNIOBLDA"},
708 /* order 16 */
709 {8L, 16, 1, "MKNPFOADBGLCIEHJ"},
710 {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"},
711 {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"},
712 {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"},
713 {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"},
714 {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"},
715 {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"},
716 {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"},
717 {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"},
718 {192L, 16, 3,
719 "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"},
720 {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"},
721 {192L, 16, 3,
722 "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"},
723 {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"},
724 {20160L, 16, 4,
725 "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN"
726 "DGIAKLBNCOEFPHJM"},
727 /* order 17 */
728 {16L, 17, 1, "EFGHIJKLMNOPQABCD"},
729 /* order 18 */
730 {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"},
731 {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"},
732 {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"},
733 {432L, 18, 3,
734 "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"},
735 {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"},
736 /* order 19 */
737 {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"},
738 /* order 20 */
739 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"},
740 {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"},
741 {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"},
742 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"},
743 {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"},
744 /* order 21 */
745 {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"},
746 {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"},
747 /* order 22 */
748 {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"},
749 {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"},
750 /* order 23 */
751 {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"},
752 /* order 24 */
753 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"},
754 {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"},
755 {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"},
756 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"},
757 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"},
758 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"},
759 {48L, 24, 3,
760 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
761 "HSNOPWLDTUVBRIAKXFGCQEMJ"},
762 {24L, 24, 3,
763 "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH"
764 "TRONXLWCHVUMSAIJPGFDEQBK"},
765 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"},
766 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"},
767 {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"},
768 {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"},
769 {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"},
770 {144L, 24, 3,
771 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
772 "BAFGHCDEMNOPIJKLTUVQRSXW"},
773 {336L, 24, 3,
774 "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG"
775 "HENOPJKLTUVBQRSAXFGWCDMI"},
776 /* order 25 */
777 {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"},
778 {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"},
779 /* order 26 */
780 {156L, 26, 2,
781 "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"},
782 {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"},
783};
784
785static const struct groups groups[] = {
786 {0, NULL}, /* trivial case: 0 */
787 {0, NULL}, /* trivial case: 1 */
788 {1, groupdata + 0}, /* 2 */
789 {1, groupdata + 1}, /* 3 */
790 {2, groupdata + 2}, /* 4 */
791 {1, groupdata + 4}, /* 5 */
792 {2, groupdata + 5}, /* 6 */
793 {1, groupdata + 7}, /* 7 */
794 {5, groupdata + 8}, /* 8 */
795 {2, groupdata + 13}, /* 9 */
796 {2, groupdata + 15}, /* 10 */
797 {1, groupdata + 17}, /* 11 */
798 {5, groupdata + 18}, /* 12 */
799 {1, groupdata + 23}, /* 13 */
800 {2, groupdata + 24}, /* 14 */
801 {1, groupdata + 26}, /* 15 */
802 {14, groupdata + 27}, /* 16 */
803 {1, groupdata + 41}, /* 17 */
804 {5, groupdata + 42}, /* 18 */
805 {1, groupdata + 47}, /* 19 */
806 {5, groupdata + 48}, /* 20 */
807 {2, groupdata + 53}, /* 21 */
808 {2, groupdata + 55}, /* 22 */
809 {1, groupdata + 57}, /* 23 */
810 {15, groupdata + 58}, /* 24 */
811 {2, groupdata + 73}, /* 25 */
812 {2, groupdata + 75}, /* 26 */
813};
814
815/* ----- data generated by group.gap ends ----- */
816
817static char *new_game_desc(const game_params *params, random_state *rs,
818 char **aux, bool interactive)
819{
820 int w = params->w, a = w*w;
821 digit *grid, *soln, *soln2;
822 int *indices;
823 int i, j, k, qh, qt;
824 int diff = params->diff;
825 const struct group *group;
826 char *desc, *p;
827
828 /*
829 * Difficulty exceptions: some combinations of size and
830 * difficulty cannot be satisfied, because all puzzles of at
831 * most that difficulty are actually even easier.
832 *
833 * Remember to re-test this whenever a change is made to the
834 * solver logic!
835 *
836 * I tested it using the following shell command:
837
838for d in t n h x u; do
839 for id in '' i; do
840 for i in {3..9}; do
841 echo -n "./group --generate 1 ${i}d${d}${id}: "
842 perl -e 'alarm 30; exec @ARGV' \
843 ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok
844 done
845 done
846done
847
848 * Of course, it's better to do that after taking the exceptions
849 * _out_, so as to detect exceptions that should be removed as
850 * well as those which should be added.
851 */
852 if (w < 5 && diff == DIFF_UNREASONABLE)
853 diff--;
854 if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME)
855 diff--;
856 if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD)
857 diff--;
858 if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL)
859 diff--;
860
861 grid = snewn(a, digit);
862 soln = snewn(a, digit);
863 soln2 = snewn(a, digit);
864 indices = snewn(a, int);
865
866 while (1) {
867 /*
868 * Construct a valid group table, by picking a group from
869 * the above data table, decompressing it into a full
870 * representation by BFS, and then randomly permuting its
871 * non-identity elements.
872 *
873 * We build the canonical table in 'soln' (and use 'grid' as
874 * our BFS queue), then transfer the table into 'grid'
875 * having shuffled the rows.
876 */
877 assert(w >= 2);
878 assert(w < lenof(groups));
879 group = groups[w].groups + random_upto(rs, groups[w].ngroups);
880 assert(group->order == w);
881 memset(soln, 0, a);
882 for (i = 0; i < w; i++)
883 soln[i] = i+1;
884 qh = qt = 0;
885 grid[qt++] = 1;
886 while (qh < qt) {
887 digit *row, *newrow;
888
889 i = grid[qh++];
890 row = soln + (i-1)*w;
891
892 for (j = 0; j < group->ngens; j++) {
893 int nri;
894 const char *gen = group->gens + j*w;
895
896 /*
897 * Apply each group generator to row, constructing a
898 * new row.
899 */
900 nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */
901 newrow = soln + (nri-1)*w;
902 if (!newrow[0]) { /* not done yet */
903 for (k = 0; k < w; k++)
904 newrow[k] = gen[row[k]-1] - 'A' + 1;
905 grid[qt++] = nri;
906 }
907 }
908 }
909 /* That's got the canonical table. Now shuffle it. */
910 for (i = 0; i < w; i++)
911 soln2[i] = i;
912 if (params->id) /* do we shuffle in the identity? */
913 shuffle(soln2+1, w-1, sizeof(*soln2), rs);
914 else
915 shuffle(soln2, w, sizeof(*soln2), rs);
916 for (i = 0; i < w; i++)
917 for (j = 0; j < w; j++)
918 grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1;
919
920 /*
921 * Remove entries one by one while the puzzle is still
922 * soluble at the appropriate difficulty level.
923 */
924 memcpy(soln, grid, a);
925 if (!params->id) {
926 /*
927 * Start by blanking the entire identity row and column,
928 * and also another row and column so that the player
929 * can't trivially determine which element is the
930 * identity.
931 */
932
933 j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */
934 for (i = 0; i < w; i++) {
935 grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0;
936 grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0;
937 }
938
939 memcpy(soln2, grid, a);
940 if (solver(params, soln2, diff) > diff)
941 continue; /* go round again if that didn't work */
942 }
943
944 k = 0;
945 for (i = (params->id ? 1 : 0); i < w; i++)
946 for (j = (params->id ? 1 : 0); j < w; j++)
947 if (grid[i*w+j])
948 indices[k++] = i*w+j;
949 shuffle(indices, k, sizeof(*indices), rs);
950
951 for (i = 0; i < k; i++) {
952 memcpy(soln2, grid, a);
953 soln2[indices[i]] = 0;
954 if (solver(params, soln2, diff) <= diff)
955 grid[indices[i]] = 0;
956 }
957
958 /*
959 * Make sure the puzzle isn't too easy.
960 */
961 if (diff > 0) {
962 memcpy(soln2, grid, a);
963 if (solver(params, soln2, diff-1) < diff)
964 continue; /* go round and try again */
965 }
966
967 /*
968 * Done.
969 */
970 break;
971 }
972
973 /*
974 * Encode the puzzle description.
975 */
976 desc = snewn(a*20, char);
977 p = encode_grid(desc, grid, a);
978 *p++ = '\0';
979 desc = sresize(desc, p - desc, char);
980
981 /*
982 * Encode the solution.
983 */
984 *aux = snewn(a+2, char);
985 (*aux)[0] = 'S';
986 for (i = 0; i < a; i++)
987 (*aux)[i+1] = TOCHAR(soln[i], params->id);
988 (*aux)[a+1] = '\0';
989
990 sfree(grid);
991 sfree(soln);
992 sfree(soln2);
993 sfree(indices);
994
995 return desc;
996}
997
998/* ----------------------------------------------------------------------
999 * Gameplay.
1000 */
1001
1002static const char *validate_grid_desc(const char **pdesc, int range, int area)
1003{
1004 const char *desc = *pdesc;
1005 int squares = 0;
1006 while (*desc && *desc != ',') {
1007 int n = *desc++;
1008 if (n >= 'a' && n <= 'z') {
1009 squares += n - 'a' + 1;
1010 } else if (n == '_') {
1011 /* do nothing */;
1012 } else if (n > '0' && n <= '9') {
1013 int val = atoi(desc-1);
1014 if (val < 1 || val > range)
1015 return "Out-of-range number in game description";
1016 squares++;
1017 while (*desc >= '0' && *desc <= '9')
1018 desc++;
1019 } else
1020 return "Invalid character in game description";
1021 }
1022
1023 if (squares < area)
1024 return "Not enough data to fill grid";
1025
1026 if (squares > area)
1027 return "Too much data to fit in grid";
1028 *pdesc = desc;
1029 return NULL;
1030}
1031
1032static const char *validate_desc(const game_params *params, const char *desc)
1033{
1034 int w = params->w, a = w*w;
1035 const char *p = desc;
1036
1037 return validate_grid_desc(&p, w, a);
1038}
1039
1040static const char *spec_to_grid(const char *desc, digit *grid, int area)
1041{
1042 int i = 0;
1043 while (*desc && *desc != ',') {
1044 int n = *desc++;
1045 if (n >= 'a' && n <= 'z') {
1046 int run = n - 'a' + 1;
1047 assert(i + run <= area);
1048 while (run-- > 0)
1049 grid[i++] = 0;
1050 } else if (n == '_') {
1051 /* do nothing */;
1052 } else if (n > '0' && n <= '9') {
1053 assert(i < area);
1054 grid[i++] = atoi(desc-1);
1055 while (*desc >= '0' && *desc <= '9')
1056 desc++;
1057 } else {
1058 assert(!"We can't get here");
1059 }
1060 }
1061 assert(i == area);
1062 return desc;
1063}
1064
1065static game_state *new_game(midend *me, const game_params *params,
1066 const char *desc)
1067{
1068 int w = params->w, a = w*w;
1069 game_state *state = snew(game_state);
1070 int i;
1071
1072 state->par = *params; /* structure copy */
1073 state->grid = snewn(a, digit);
1074 state->common = snew(group_common);
1075 state->common->refcount = 1;
1076 state->common->immutable = snewn(a, bool);
1077 state->pencil = snewn(a, int);
1078 for (i = 0; i < a; i++) {
1079 state->grid[i] = 0;
1080 state->common->immutable[i] = false;
1081 state->pencil[i] = 0;
1082 }
1083 state->sequence = snewn(w, digit);
1084 state->dividers = snewn(w, int);
1085 for (i = 0; i < w; i++) {
1086 state->sequence[i] = i;
1087 state->dividers[i] = -1;
1088 }
1089
1090 desc = spec_to_grid(desc, state->grid, a);
1091 for (i = 0; i < a; i++)
1092 if (state->grid[i] != 0)
1093 state->common->immutable[i] = true;
1094
1095 state->completed = false;
1096 state->cheated = false;
1097
1098 return state;
1099}
1100
1101static game_state *dup_game(const game_state *state)
1102{
1103 int w = state->par.w, a = w*w;
1104 game_state *ret = snew(game_state);
1105
1106 ret->par = state->par; /* structure copy */
1107
1108 ret->grid = snewn(a, digit);
1109 ret->common = state->common;
1110 ret->common->refcount++;
1111 ret->pencil = snewn(a, int);
1112 ret->sequence = snewn(w, digit);
1113 ret->dividers = snewn(w, int);
1114 memcpy(ret->grid, state->grid, a*sizeof(digit));
1115 memcpy(ret->pencil, state->pencil, a*sizeof(int));
1116 memcpy(ret->sequence, state->sequence, w*sizeof(digit));
1117 memcpy(ret->dividers, state->dividers, w*sizeof(int));
1118
1119 ret->completed = state->completed;
1120 ret->cheated = state->cheated;
1121
1122 return ret;
1123}
1124
1125static void free_game(game_state *state)
1126{
1127 sfree(state->grid);
1128 if (--state->common->refcount == 0) {
1129 sfree(state->common->immutable);
1130 sfree(state->common);
1131 }
1132 sfree(state->pencil);
1133 sfree(state->sequence);
1134 sfree(state);
1135}
1136
1137static char *solve_game(const game_state *state, const game_state *currstate,
1138 const char *aux, const char **error)
1139{
1140 int w = state->par.w, a = w*w;
1141 int i, ret;
1142 digit *soln;
1143 char *out;
1144
1145 if (aux)
1146 return dupstr(aux);
1147
1148 soln = snewn(a, digit);
1149 memcpy(soln, state->grid, a*sizeof(digit));
1150
1151 ret = solver(&state->par, soln, DIFFCOUNT-1);
1152
1153 if (ret == diff_impossible) {
1154 *error = "No solution exists for this puzzle";
1155 out = NULL;
1156 } else if (ret == diff_ambiguous) {
1157 *error = "Multiple solutions exist for this puzzle";
1158 out = NULL;
1159 } else {
1160 out = snewn(a+2, char);
1161 out[0] = 'S';
1162 for (i = 0; i < a; i++)
1163 out[i+1] = TOCHAR(soln[i], state->par.id);
1164 out[a+1] = '\0';
1165 }
1166
1167 sfree(soln);
1168 return out;
1169}
1170
1171static bool game_can_format_as_text_now(const game_params *params)
1172{
1173 return true;
1174}
1175
1176static char *game_text_format(const game_state *state)
1177{
1178 int w = state->par.w;
1179 int x, y;
1180 char *ret, *p, ch;
1181
1182 ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */
1183
1184 p = ret;
1185 for (y = 0; y < w; y++) {
1186 for (x = 0; x < w; x++) {
1187 digit d = state->grid[y*w+x];
1188
1189 if (d == 0) {
1190 ch = '.';
1191 } else {
1192 ch = TOCHAR(d, state->par.id);
1193 }
1194
1195 *p++ = ch;
1196 if (x == w-1) {
1197 *p++ = '\n';
1198 } else {
1199 *p++ = ' ';
1200 }
1201 }
1202 }
1203
1204 assert(p - ret == 2*w*w);
1205 *p = '\0';
1206 return ret;
1207}
1208
1209struct game_ui {
1210 /*
1211 * These are the coordinates of the primary highlighted square on
1212 * the grid, if hshow = 1.
1213 */
1214 int hx, hy;
1215 /*
1216 * These are the coordinates hx,hy _before_ they go through
1217 * state->sequence.
1218 */
1219 int ohx, ohy;
1220 /*
1221 * These variables give the length and displacement of a diagonal
1222 * sequence of highlighted squares starting at ohx,ohy (still if
1223 * hshow = 1). To find the squares' real coordinates, for 0<=i<dn,
1224 * compute ohx+i*odx and ohy+i*ody and then map through
1225 * state->sequence.
1226 */
1227 int odx, ody, odn;
1228 /*
1229 * This indicates whether the current highlight is a
1230 * pencil-mark one or a real one.
1231 */
1232 bool hpencil;
1233 /*
1234 * This indicates whether or not we're showing the highlight
1235 * (used to be hx = hy = -1); important so that when we're
1236 * using the cursor keys it doesn't keep coming back at a
1237 * fixed position. When hshow = 1, pressing a valid number
1238 * or letter key or Space will enter that number or letter in the grid.
1239 */
1240 bool hshow;
1241 /*
1242 * This indicates whether we're using the highlight as a cursor;
1243 * it means that it doesn't vanish on a keypress, and that it is
1244 * allowed on immutable squares.
1245 */
1246 bool hcursor;
1247 /*
1248 * This indicates whether we're dragging a table header to
1249 * reposition an entire row or column.
1250 */
1251 int drag; /* 0=none 1=row 2=col */
1252 int dragnum; /* element being dragged */
1253 int dragpos; /* its current position */
1254 int edgepos;
1255
1256 /*
1257 * User preference option: if the user right-clicks in a square
1258 * and presses a letter key to add/remove a pencil mark, do we
1259 * hide the mouse highlight again afterwards?
1260 *
1261 * Historically our answer was yes. The Android port prefers no.
1262 * There are advantages both ways, depending how much you dislike
1263 * the highlight cluttering your view. So it's a preference.
1264 */
1265 bool pencil_keep_highlight;
1266};
1267
1268static game_ui *new_ui(const game_state *state)
1269{
1270 game_ui *ui = snew(game_ui);
1271
1272 ui->hx = ui->hy = 0;
1273 ui->hpencil = false;
1274 ui->hshow = false;
1275 ui->hcursor = false;
1276 ui->drag = 0;
1277
1278 ui->pencil_keep_highlight = false;
1279
1280 return ui;
1281}
1282
1283static void free_ui(game_ui *ui)
1284{
1285 sfree(ui);
1286}
1287
1288static config_item *get_prefs(game_ui *ui)
1289{
1290 config_item *ret;
1291
1292 ret = snewn(2, config_item);
1293
1294 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1295 ret[0].kw = "pencil-keep-highlight";
1296 ret[0].type = C_BOOLEAN;
1297 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1298
1299 ret[1].name = NULL;
1300 ret[1].type = C_END;
1301
1302 return ret;
1303}
1304
1305static void set_prefs(game_ui *ui, const config_item *cfg)
1306{
1307 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1308}
1309
1310static void game_changed_state(game_ui *ui, const game_state *oldstate,
1311 const game_state *newstate)
1312{
1313 int w = newstate->par.w;
1314 /*
1315 * We prevent pencil-mode highlighting of a filled square, unless
1316 * we're using the cursor keys. So if the user has just filled in
1317 * a square which we had a pencil-mode highlight in (by Undo, or
1318 * by Redo, or by Solve), then we cancel the highlight.
1319 */
1320 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1321 newstate->grid[ui->hy * w + ui->hx] != 0) {
1322 ui->hshow = false;
1323 }
1324 if (ui->hshow && ui->odn > 1) {
1325 /*
1326 * Reordering of rows or columns within the range of a
1327 * multifill selection cancels the multifill and deselects
1328 * everything.
1329 */
1330 int i;
1331 for (i = 0; i < ui->odn; i++) {
1332 if (oldstate->sequence[ui->ohx + i*ui->odx] !=
1333 newstate->sequence[ui->ohx + i*ui->odx]) {
1334 ui->hshow = false;
1335 break;
1336 }
1337 if (oldstate->sequence[ui->ohy + i*ui->ody] !=
1338 newstate->sequence[ui->ohy + i*ui->ody]) {
1339 ui->hshow = false;
1340 break;
1341 }
1342 }
1343 } else if (ui->hshow &&
1344 (newstate->sequence[ui->ohx] != ui->hx ||
1345 newstate->sequence[ui->ohy] != ui->hy)) {
1346 /*
1347 * Otherwise, reordering of the row or column containing the
1348 * selection causes the selection to move with it.
1349 */
1350 int i;
1351 for (i = 0; i < w; i++) {
1352 if (newstate->sequence[i] == ui->hx)
1353 ui->ohx = i;
1354 if (newstate->sequence[i] == ui->hy)
1355 ui->ohy = i;
1356 }
1357 }
1358}
1359
1360static const char *current_key_label(const game_ui *ui,
1361 const game_state *state, int button)
1362{
1363 if (ui->hshow && button == CURSOR_SELECT)
1364 return ui->hpencil ? "Ink" : "Pencil";
1365 if (ui->hshow && button == CURSOR_SELECT2) {
1366 int w = state->par.w;
1367 int i;
1368 for (i = 0; i < ui->odn; i++) {
1369 int x = state->sequence[ui->ohx + i*ui->odx];
1370 int y = state->sequence[ui->ohy + i*ui->ody];
1371 int index = y*w+x;
1372 if (ui->hpencil && state->grid[index]) return "";
1373 if (state->common->immutable[index]) return "";
1374 }
1375 return "Clear";
1376 }
1377 return "";
1378}
1379
1380#define PREFERRED_TILESIZE 48
1381#define TILESIZE (ds->tilesize)
1382#define BORDER (TILESIZE / 2)
1383#define LEGEND (TILESIZE)
1384#define GRIDEXTRA max((TILESIZE / 32),1)
1385#define COORD(x) ((x)*TILESIZE + BORDER + LEGEND)
1386#define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1)
1387
1388#define FLASH_TIME 0.4F
1389
1390#define DF_DIVIDER_TOP 0x1000
1391#define DF_DIVIDER_BOT 0x2000
1392#define DF_DIVIDER_LEFT 0x4000
1393#define DF_DIVIDER_RIGHT 0x8000
1394#define DF_HIGHLIGHT 0x0400
1395#define DF_HIGHLIGHT_PENCIL 0x0200
1396#define DF_IMMUTABLE 0x0100
1397#define DF_LEGEND 0x0080
1398#define DF_DIGIT_MASK 0x001F
1399
1400#define EF_DIGIT_SHIFT 5
1401#define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1)
1402#define EF_LEFT_SHIFT 0
1403#define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT)
1404#define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL)
1405#define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT)
1406#define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT))
1407
1408struct game_drawstate {
1409 game_params par;
1410 int w, tilesize;
1411 bool started;
1412 long *tiles, *legend, *pencil, *errors;
1413 long *errtmp;
1414 digit *sequence;
1415};
1416
1417static bool check_errors(const game_state *state, long *errors)
1418{
1419 int w = state->par.w, a = w*w;
1420 digit *grid = state->grid;
1421 int i, j, k, x, y;
1422 bool errs = false;
1423
1424 /*
1425 * To verify that we have a valid group table, it suffices to
1426 * test latin-square-hood and associativity only. All the other
1427 * group axioms follow from those two.
1428 *
1429 * Proof:
1430 *
1431 * Associativity is given; closure is obvious from latin-
1432 * square-hood. We need to show that an identity exists and that
1433 * every element has an inverse.
1434 *
1435 * Identity: take any element a. There will be some element e
1436 * such that ea=a (in a latin square, every element occurs in
1437 * every row and column, so a must occur somewhere in the a
1438 * column, say on row e). For any other element b, there must
1439 * exist x such that ax=b (same argument from latin-square-hood
1440 * again), and then associativity gives us eb = e(ax) = (ea)x =
1441 * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A
1442 * similar argument tells us that there must be some f which is
1443 * a right-identity, and then we show they are the same element
1444 * by observing that ef must simultaneously equal e and equal f.
1445 *
1446 * Inverses: given any a, by the latin-square argument again,
1447 * there must exist p and q such that pa=e and aq=e (i.e. left-
1448 * and right-inverses). We can show these are equal by
1449 * associativity: p = pe = p(aq) = (pa)q = eq = q. []
1450 */
1451
1452 if (errors)
1453 for (i = 0; i < a; i++)
1454 errors[i] = 0;
1455
1456 for (y = 0; y < w; y++) {
1457 unsigned long mask = 0, errmask = 0;
1458 for (x = 0; x < w; x++) {
1459 unsigned long bit = 1UL << grid[y*w+x];
1460 errmask |= (mask & bit);
1461 mask |= bit;
1462 }
1463
1464 if (mask != (1 << (w+1)) - (1 << 1)) {
1465 errs = true;
1466 errmask &= ~1UL;
1467 if (errors) {
1468 for (x = 0; x < w; x++)
1469 if (errmask & (1UL << grid[y*w+x]))
1470 errors[y*w+x] |= EF_LATIN;
1471 }
1472 }
1473 }
1474
1475 for (x = 0; x < w; x++) {
1476 unsigned long mask = 0, errmask = 0;
1477 for (y = 0; y < w; y++) {
1478 unsigned long bit = 1UL << grid[y*w+x];
1479 errmask |= (mask & bit);
1480 mask |= bit;
1481 }
1482
1483 if (mask != (1 << (w+1)) - (1 << 1)) {
1484 errs = true;
1485 errmask &= ~1UL;
1486 if (errors) {
1487 for (y = 0; y < w; y++)
1488 if (errmask & (1UL << grid[y*w+x]))
1489 errors[y*w+x] |= EF_LATIN;
1490 }
1491 }
1492 }
1493
1494 for (i = 1; i < w; i++)
1495 for (j = 1; j < w; j++)
1496 for (k = 1; k < w; k++)
1497 if (grid[i*w+j] && grid[j*w+k] &&
1498 grid[(grid[i*w+j]-1)*w+k] &&
1499 grid[i*w+(grid[j*w+k]-1)] &&
1500 grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) {
1501 if (errors) {
1502 int a = i+1, b = j+1, c = k+1;
1503 int ab = grid[i*w+j], bc = grid[j*w+k];
1504 int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1);
1505 /*
1506 * If the appropriate error slot is already
1507 * used for one of the squares, we don't
1508 * fill either of them.
1509 */
1510 if (!(errors[left] & EF_LEFT_MASK) &&
1511 !(errors[right] & EF_RIGHT_MASK)) {
1512 long err;
1513 err = a;
1514 err = (err << EF_DIGIT_SHIFT) | b;
1515 err = (err << EF_DIGIT_SHIFT) | c;
1516 errors[left] |= err << EF_LEFT_SHIFT;
1517 errors[right] |= err << EF_RIGHT_SHIFT;
1518 }
1519 }
1520 errs = true;
1521 }
1522
1523 return errs;
1524}
1525
1526static int find_in_sequence(digit *seq, int len, digit n)
1527{
1528 int i;
1529
1530 for (i = 0; i < len; i++)
1531 if (seq[i] == n)
1532 return i;
1533
1534 assert(!"Should never get here");
1535 return -1;
1536}
1537
1538static char *interpret_move(const game_state *state, game_ui *ui,
1539 const game_drawstate *ds,
1540 int x, int y, int button)
1541{
1542 int w = state->par.w;
1543 int tx, ty;
1544 char buf[80];
1545
1546 button = STRIP_BUTTON_MODIFIERS(button);
1547
1548 tx = FROMCOORD(x);
1549 ty = FROMCOORD(y);
1550
1551 if (ui->drag) {
1552 if (IS_MOUSE_DRAG(button)) {
1553 int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx);
1554 ui->drag |= 4; /* some movement has happened */
1555 if (tcoord >= 0 && tcoord < w) {
1556 ui->dragpos = tcoord;
1557 return MOVE_UI_UPDATE;
1558 }
1559 } else if (IS_MOUSE_RELEASE(button)) {
1560 if (ui->drag & 4) {
1561 ui->drag = 0; /* end drag */
1562 if (state->sequence[ui->dragpos] == ui->dragnum)
1563 return MOVE_UI_UPDATE; /* drag was a no-op overall */
1564 sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos);
1565 return dupstr(buf);
1566 } else {
1567 ui->drag = 0; /* end 'drag' */
1568 if (ui->edgepos > 0 && ui->edgepos < w) {
1569 sprintf(buf, "V%d,%d",
1570 state->sequence[ui->edgepos-1],
1571 state->sequence[ui->edgepos]);
1572 return dupstr(buf);
1573 } else
1574 return MOVE_UI_UPDATE; /* no-op */
1575 }
1576 }
1577 } else if (IS_MOUSE_DOWN(button)) {
1578 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1579 int otx = tx, oty = ty;
1580 tx = state->sequence[tx];
1581 ty = state->sequence[ty];
1582 if (button == LEFT_BUTTON) {
1583 if (tx == ui->hx && ty == ui->hy &&
1584 ui->hshow && !ui->hpencil) {
1585 ui->hshow = false;
1586 } else {
1587 ui->hx = tx;
1588 ui->hy = ty;
1589 ui->ohx = otx;
1590 ui->ohy = oty;
1591 ui->odx = ui->ody = 0;
1592 ui->odn = 1;
1593 ui->hshow = !state->common->immutable[ty*w+tx];
1594 ui->hpencil = false;
1595 }
1596 ui->hcursor = false;
1597 return MOVE_UI_UPDATE;
1598 }
1599 if (button == RIGHT_BUTTON) {
1600 /*
1601 * Pencil-mode highlighting for non filled squares.
1602 */
1603 if (state->grid[ty*w+tx] == 0) {
1604 if (tx == ui->hx && ty == ui->hy &&
1605 ui->hshow && ui->hpencil) {
1606 ui->hshow = false;
1607 } else {
1608 ui->hpencil = true;
1609 ui->hx = tx;
1610 ui->hy = ty;
1611 ui->ohx = otx;
1612 ui->ohy = oty;
1613 ui->odx = ui->ody = 0;
1614 ui->odn = 1;
1615 ui->hshow = true;
1616 }
1617 } else {
1618 ui->hshow = false;
1619 }
1620 ui->hcursor = false;
1621 return MOVE_UI_UPDATE;
1622 }
1623 } else if (tx >= 0 && tx < w && ty == -1) {
1624 ui->drag = 2;
1625 ui->dragnum = state->sequence[tx];
1626 ui->dragpos = tx;
1627 ui->edgepos = FROMCOORD(x + TILESIZE/2);
1628 return MOVE_UI_UPDATE;
1629 } else if (ty >= 0 && ty < w && tx == -1) {
1630 ui->drag = 1;
1631 ui->dragnum = state->sequence[ty];
1632 ui->dragpos = ty;
1633 ui->edgepos = FROMCOORD(y + TILESIZE/2);
1634 return MOVE_UI_UPDATE;
1635 }
1636 } else if (IS_MOUSE_DRAG(button)) {
1637 if (!ui->hpencil &&
1638 tx >= 0 && tx < w && ty >= 0 && ty < w &&
1639 abs(tx - ui->ohx) == abs(ty - ui->ohy)) {
1640 ui->odn = abs(tx - ui->ohx) + 1;
1641 ui->odx = (tx < ui->ohx ? -1 : +1);
1642 ui->ody = (ty < ui->ohy ? -1 : +1);
1643 } else {
1644 ui->odx = ui->ody = 0;
1645 ui->odn = 1;
1646 }
1647 return MOVE_UI_UPDATE;
1648 }
1649
1650 if (IS_CURSOR_MOVE(button)) {
1651 int cx = find_in_sequence(state->sequence, w, ui->hx);
1652 int cy = find_in_sequence(state->sequence, w, ui->hy);
1653 move_cursor(button, &cx, &cy, w, w, false, NULL);
1654 ui->hx = state->sequence[cx];
1655 ui->hy = state->sequence[cy];
1656 ui->hshow = true;
1657 ui->hcursor = true;
1658 ui->ohx = cx;
1659 ui->ohy = cy;
1660 ui->odx = ui->ody = 0;
1661 ui->odn = 1;
1662 return MOVE_UI_UPDATE;
1663 }
1664 if (ui->hshow &&
1665 (button == CURSOR_SELECT)) {
1666 ui->hpencil = !ui->hpencil;
1667 ui->hcursor = true;
1668 return MOVE_UI_UPDATE;
1669 }
1670
1671 if (ui->hshow &&
1672 ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) ||
1673 button == CURSOR_SELECT2 || button == '\b')) {
1674 int n = FROMCHAR(button, state->par.id);
1675 int i, buflen;
1676 char *movebuf;
1677
1678 if (button == CURSOR_SELECT2 || button == '\b')
1679 n = 0;
1680
1681 for (i = 0; i < ui->odn; i++) {
1682 int x = state->sequence[ui->ohx + i*ui->odx];
1683 int y = state->sequence[ui->ohy + i*ui->ody];
1684 int index = y*w+x;
1685
1686 /*
1687 * Can't make pencil marks in a filled square. This can only
1688 * become highlighted if we're using cursor keys.
1689 */
1690 if (ui->hpencil && state->grid[index])
1691 return NULL;
1692
1693 /*
1694 * Can't do anything to an immutable square. Exception:
1695 * trying to set it to what it already was is OK (so that
1696 * multifilling can set a whole diagonal to a without
1697 * having to detour round the one immutable square in the
1698 * middle that already said a).
1699 */
1700 if (!ui->hpencil && state->grid[index] == n)
1701 /* OK even if it is immutable */;
1702 else if (state->common->immutable[index])
1703 return NULL;
1704 }
1705
1706 movebuf = snewn(80 * ui->odn, char);
1707 buflen = sprintf(movebuf, "%c%d,%d,%d",
1708 (char)(ui->hpencil && n > 0 ? 'P' : 'R'),
1709 ui->hx, ui->hy, n);
1710 for (i = 1; i < ui->odn; i++) {
1711 assert(buflen < i*80);
1712 buflen += sprintf(movebuf + buflen, "+%d,%d",
1713 state->sequence[ui->ohx + i*ui->odx],
1714 state->sequence[ui->ohy + i*ui->ody]);
1715 }
1716 movebuf = sresize(movebuf, buflen+1, char);
1717
1718 /*
1719 * Hide the highlight after a keypress, if it was mouse-
1720 * generated. Also, don't hide it if this move has changed
1721 * pencil marks and the user preference says not to hide the
1722 * highlight in that situation.
1723 */
1724 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1725 ui->hshow = false;
1726
1727 return movebuf;
1728 }
1729
1730 if (button == 'M' || button == 'm')
1731 return dupstr("M");
1732
1733 return NULL;
1734}
1735
1736static game_state *execute_move(const game_state *from, const char *move)
1737{
1738 int w = from->par.w, a = w*w;
1739 game_state *ret;
1740 int x, y, i, j, n, pos;
1741
1742 if (move[0] == 'S') {
1743 ret = dup_game(from);
1744 ret->completed = ret->cheated = true;
1745
1746 for (i = 0; i < a; i++) {
1747 if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) {
1748 free_game(ret);
1749 return NULL;
1750 }
1751 ret->grid[i] = FROMCHAR(move[i+1], from->par.id);
1752 ret->pencil[i] = 0;
1753 }
1754
1755 if (move[a+1] != '\0') {
1756 free_game(ret);
1757 return NULL;
1758 }
1759
1760 return ret;
1761 } else if ((move[0] == 'P' || move[0] == 'R') &&
1762 sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 &&
1763 n >= 0 && n <= w) {
1764 const char *mp = move + 1 + pos;
1765 bool pencil = (move[0] == 'P');
1766 ret = dup_game(from);
1767
1768 while (1) {
1769 if (x < 0 || x >= w || y < 0 || y >= w) {
1770 free_game(ret);
1771 return NULL;
1772 }
1773 if (from->common->immutable[y*w+x] &&
1774 !(!pencil && from->grid[y*w+x] == n))
1775 return NULL;
1776
1777 if (move[0] == 'P' && n > 0) {
1778 ret->pencil[y*w+x] ^= 1 << n;
1779 } else {
1780 ret->grid[y*w+x] = n;
1781 ret->pencil[y*w+x] = 0;
1782 }
1783
1784 if (!*mp)
1785 break;
1786
1787 if (*mp != '+')
1788 return NULL;
1789 if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2)
1790 return NULL;
1791 mp += pos;
1792 }
1793
1794 if (!ret->completed && !check_errors(ret, NULL))
1795 ret->completed = true;
1796
1797 return ret;
1798 } else if (move[0] == 'M') {
1799 /*
1800 * Fill in absolutely all pencil marks everywhere. (I
1801 * wouldn't use this for actual play, but it's a handy
1802 * starting point when following through a set of
1803 * diagnostics output by the standalone solver.)
1804 */
1805 ret = dup_game(from);
1806 for (i = 0; i < a; i++) {
1807 if (!ret->grid[i])
1808 ret->pencil[i] = (1 << (w+1)) - (1 << 1);
1809 }
1810 return ret;
1811 } else if (move[0] == 'D' &&
1812 sscanf(move+1, "%d,%d", &x, &y) == 2) {
1813 /*
1814 * Reorder the rows and columns so that digit x is in position
1815 * y.
1816 */
1817 ret = dup_game(from);
1818 for (i = j = 0; i < w; i++) {
1819 if (i == y) {
1820 ret->sequence[i] = x;
1821 } else {
1822 if (from->sequence[j] == x)
1823 j++;
1824 ret->sequence[i] = from->sequence[j++];
1825 }
1826 }
1827 /*
1828 * Eliminate any obsoleted dividers.
1829 */
1830 for (x = 0; x < w; x++) {
1831 int i = ret->sequence[x];
1832 int j = (x+1 < w ? ret->sequence[x+1] : -1);
1833 if (ret->dividers[i] != j)
1834 ret->dividers[i] = -1;
1835 }
1836 return ret;
1837 } else if (move[0] == 'V' &&
1838 sscanf(move+1, "%d,%d", &i, &j) == 2) {
1839 ret = dup_game(from);
1840 if (ret->dividers[i] == j)
1841 ret->dividers[i] = -1;
1842 else
1843 ret->dividers[i] = j;
1844 return ret;
1845 } else
1846 return NULL; /* couldn't parse move string */
1847}
1848
1849/* ----------------------------------------------------------------------
1850 * Drawing routines.
1851 */
1852
1853#define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND)
1854
1855static void game_compute_size(const game_params *params, int tilesize,
1856 const game_ui *ui, int *x, int *y)
1857{
1858 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1859 struct { int tilesize; } ads, *ds = &ads;
1860 ads.tilesize = tilesize;
1861
1862 *x = *y = SIZE(params->w);
1863}
1864
1865static void game_set_size(drawing *dr, game_drawstate *ds,
1866 const game_params *params, int tilesize)
1867{
1868 ds->tilesize = tilesize;
1869}
1870
1871static float *game_colours(frontend *fe, int *ncolours)
1872{
1873 float *ret = snewn(3 * NCOLOURS, float);
1874
1875 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1876
1877 ret[COL_GRID * 3 + 0] = 0.0F;
1878 ret[COL_GRID * 3 + 1] = 0.0F;
1879 ret[COL_GRID * 3 + 2] = 0.0F;
1880
1881 ret[COL_USER * 3 + 0] = 0.0F;
1882 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1883 ret[COL_USER * 3 + 2] = 0.0F;
1884
1885 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1886 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1887 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1888
1889 ret[COL_ERROR * 3 + 0] = 1.0F;
1890 ret[COL_ERROR * 3 + 1] = 0.0F;
1891 ret[COL_ERROR * 3 + 2] = 0.0F;
1892
1893 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1894 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1895 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1896
1897 ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0];
1898 ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1];
1899 ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2];
1900
1901 *ncolours = NCOLOURS;
1902 return ret;
1903}
1904
1905static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1906{
1907 int w = state->par.w, a = w*w;
1908 struct game_drawstate *ds = snew(struct game_drawstate);
1909 int i;
1910
1911 ds->w = w;
1912 ds->par = state->par; /* structure copy */
1913 ds->tilesize = 0;
1914 ds->started = false;
1915 ds->tiles = snewn(a, long);
1916 ds->legend = snewn(w, long);
1917 ds->pencil = snewn(a, long);
1918 ds->errors = snewn(a, long);
1919 ds->sequence = snewn(a, digit);
1920 for (i = 0; i < a; i++)
1921 ds->tiles[i] = ds->pencil[i] = -1;
1922 for (i = 0; i < w; i++)
1923 ds->legend[i] = -1;
1924 ds->errtmp = snewn(a, long);
1925
1926 return ds;
1927}
1928
1929static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1930{
1931 sfree(ds->tiles);
1932 sfree(ds->pencil);
1933 sfree(ds->errors);
1934 sfree(ds->errtmp);
1935 sfree(ds->sequence);
1936 sfree(ds);
1937}
1938
1939static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile,
1940 long pencil, long error)
1941{
1942 int w = ds->w /* , a = w*w */;
1943 int tx, ty, tw, th;
1944 int cx, cy, cw, ch;
1945 char str[64];
1946
1947 tx = BORDER + LEGEND + x * TILESIZE + 1;
1948 ty = BORDER + LEGEND + y * TILESIZE + 1;
1949
1950 cx = tx;
1951 cy = ty;
1952 cw = tw = TILESIZE-1;
1953 ch = th = TILESIZE-1;
1954
1955 if (tile & DF_LEGEND) {
1956 cx += TILESIZE/10;
1957 cy += TILESIZE/10;
1958 cw -= TILESIZE/5;
1959 ch -= TILESIZE/5;
1960 tile |= DF_IMMUTABLE;
1961 }
1962
1963 clip(dr, cx, cy, cw, ch);
1964
1965 /* background needs erasing */
1966 draw_rect(dr, cx, cy, cw, ch,
1967 (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT :
1968 (x == y) ? COL_DIAGONAL : COL_BACKGROUND);
1969
1970 /* dividers */
1971 if (tile & DF_DIVIDER_TOP)
1972 draw_rect(dr, cx, cy, cw, 1, COL_GRID);
1973 if (tile & DF_DIVIDER_BOT)
1974 draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID);
1975 if (tile & DF_DIVIDER_LEFT)
1976 draw_rect(dr, cx, cy, 1, ch, COL_GRID);
1977 if (tile & DF_DIVIDER_RIGHT)
1978 draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID);
1979
1980 /* pencil-mode highlight */
1981 if (tile & DF_HIGHLIGHT_PENCIL) {
1982 int coords[6];
1983 coords[0] = cx;
1984 coords[1] = cy;
1985 coords[2] = cx+cw/2;
1986 coords[3] = cy;
1987 coords[4] = cx;
1988 coords[5] = cy+ch/2;
1989 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1990 }
1991
1992 /* new number needs drawing? */
1993 if (tile & DF_DIGIT_MASK) {
1994 str[1] = '\0';
1995 str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id);
1996 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1997 FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1998 (error & EF_LATIN) ? COL_ERROR :
1999 (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str);
2000
2001 if (error & EF_LEFT_MASK) {
2002 int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2003 int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2004 int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK;
2005 char buf[10];
2006 sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id),
2007 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
2008 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6,
2009 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
2010 COL_ERROR, buf);
2011 }
2012 if (error & EF_RIGHT_MASK) {
2013 int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2014 int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
2015 int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK;
2016 char buf[10];
2017 sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id),
2018 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
2019 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6,
2020 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
2021 COL_ERROR, buf);
2022 }
2023 } else {
2024 int i, j, npencil;
2025 int pl, pr, pt, pb;
2026 float bestsize;
2027 int pw, ph, minph, pbest, fontsize;
2028
2029 /* Count the pencil marks required. */
2030 for (i = 1, npencil = 0; i <= w; i++)
2031 if (pencil & (1 << i))
2032 npencil++;
2033 if (npencil) {
2034
2035 minph = 2;
2036
2037 /*
2038 * Determine the bounding rectangle within which we're going
2039 * to put the pencil marks.
2040 */
2041 /* Start with the whole square */
2042 pl = tx + GRIDEXTRA;
2043 pr = pl + TILESIZE - GRIDEXTRA;
2044 pt = ty + GRIDEXTRA;
2045 pb = pt + TILESIZE - GRIDEXTRA;
2046
2047 /*
2048 * We arrange our pencil marks in a grid layout, with
2049 * the number of rows and columns adjusted to allow the
2050 * maximum font size.
2051 *
2052 * So now we work out what the grid size ought to be.
2053 */
2054 bestsize = 0.0;
2055 pbest = 0;
2056 /* Minimum */
2057 for (pw = 3; pw < max(npencil,4); pw++) {
2058 float fw, fh, fs;
2059
2060 ph = (npencil + pw - 1) / pw;
2061 ph = max(ph, minph);
2062 fw = (pr - pl) / (float)pw;
2063 fh = (pb - pt) / (float)ph;
2064 fs = min(fw, fh);
2065 if (fs > bestsize) {
2066 bestsize = fs;
2067 pbest = pw;
2068 }
2069 }
2070 assert(pbest > 0);
2071 pw = pbest;
2072 ph = (npencil + pw - 1) / pw;
2073 ph = max(ph, minph);
2074
2075 /*
2076 * Now we've got our grid dimensions, work out the pixel
2077 * size of a grid element, and round it to the nearest
2078 * pixel. (We don't want rounding errors to make the
2079 * grid look uneven at low pixel sizes.)
2080 */
2081 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
2082
2083 /*
2084 * Centre the resulting figure in the square.
2085 */
2086 pl = tx + (TILESIZE - fontsize * pw) / 2;
2087 pt = ty + (TILESIZE - fontsize * ph) / 2;
2088
2089 /*
2090 * Now actually draw the pencil marks.
2091 */
2092 for (i = 1, j = 0; i <= w; i++)
2093 if (pencil & (1 << i)) {
2094 int dx = j % pw, dy = j / pw;
2095
2096 str[1] = '\0';
2097 str[0] = TOCHAR(i, ds->par.id);
2098 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
2099 pt + fontsize * (2*dy+1) / 2,
2100 FONT_VARIABLE, fontsize,
2101 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
2102 j++;
2103 }
2104 }
2105 }
2106
2107 unclip(dr);
2108
2109 draw_update(dr, cx, cy, cw, ch);
2110}
2111
2112static void game_redraw(drawing *dr, game_drawstate *ds,
2113 const game_state *oldstate, const game_state *state,
2114 int dir, const game_ui *ui,
2115 float animtime, float flashtime)
2116{
2117 int w = state->par.w /*, a = w*w */;
2118 int x, y, i, j;
2119
2120 if (!ds->started) {
2121 /*
2122 * Big containing rectangle.
2123 */
2124 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
2125 w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
2126 COL_GRID);
2127
2128 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
2129
2130 ds->started = true;
2131 }
2132
2133 check_errors(state, ds->errtmp);
2134
2135 /*
2136 * Construct a modified version of state->sequence which takes
2137 * into account an unfinished drag operation.
2138 */
2139 if (ui->drag) {
2140 x = ui->dragnum;
2141 y = ui->dragpos;
2142 } else {
2143 x = y = -1;
2144 }
2145 for (i = j = 0; i < w; i++) {
2146 if (i == y) {
2147 ds->sequence[i] = x;
2148 } else {
2149 if (state->sequence[j] == x)
2150 j++;
2151 ds->sequence[i] = state->sequence[j++];
2152 }
2153 }
2154
2155 /*
2156 * Draw the table legend.
2157 */
2158 for (x = 0; x < w; x++) {
2159 int sx = ds->sequence[x];
2160 long tile = (sx+1) | DF_LEGEND;
2161 if (ds->legend[x] != tile) {
2162 ds->legend[x] = tile;
2163 draw_tile(dr, ds, -1, x, tile, 0, 0);
2164 draw_tile(dr, ds, x, -1, tile, 0, 0);
2165 }
2166 }
2167
2168 for (y = 0; y < w; y++) {
2169 int sy = ds->sequence[y];
2170 for (x = 0; x < w; x++) {
2171 long tile = 0L, pencil = 0L, error;
2172 int sx = ds->sequence[x];
2173
2174 if (state->grid[sy*w+sx])
2175 tile = state->grid[sy*w+sx];
2176 else
2177 pencil = (long)state->pencil[sy*w+sx];
2178
2179 if (state->common->immutable[sy*w+sx])
2180 tile |= DF_IMMUTABLE;
2181
2182 if ((ui->drag == 5 && ui->dragnum == sy) ||
2183 (ui->drag == 6 && ui->dragnum == sx)) {
2184 tile |= DF_HIGHLIGHT;
2185 } else if (ui->hshow) {
2186 int i = abs(x - ui->ohx);
2187 bool highlight = false;
2188 if (ui->odn > 1) {
2189 /*
2190 * When a diagonal multifill selection is shown,
2191 * we show it in its original grid position
2192 * regardless of in-progress row/col drags. Moving
2193 * every square about would be horrible.
2194 */
2195 if (i >= 0 && i < ui->odn &&
2196 x == ui->ohx + i*ui->odx &&
2197 y == ui->ohy + i*ui->ody)
2198 highlight = true;
2199 } else {
2200 /*
2201 * For a single square, we move its highlight
2202 * around with the drag.
2203 */
2204 highlight = (ui->hx == sx && ui->hy == sy);
2205 }
2206 if (highlight)
2207 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
2208 }
2209
2210 if (flashtime > 0 &&
2211 (flashtime <= FLASH_TIME/3 ||
2212 flashtime >= FLASH_TIME*2/3))
2213 tile |= DF_HIGHLIGHT; /* completion flash */
2214
2215 if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy)
2216 tile |= DF_DIVIDER_TOP;
2217 if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1])
2218 tile |= DF_DIVIDER_BOT;
2219 if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx)
2220 tile |= DF_DIVIDER_LEFT;
2221 if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1])
2222 tile |= DF_DIVIDER_RIGHT;
2223
2224 error = ds->errtmp[sy*w+sx];
2225
2226 if (ds->tiles[y*w+x] != tile ||
2227 ds->pencil[y*w+x] != pencil ||
2228 ds->errors[y*w+x] != error) {
2229 ds->tiles[y*w+x] = tile;
2230 ds->pencil[y*w+x] = pencil;
2231 ds->errors[y*w+x] = error;
2232 draw_tile(dr, ds, x, y, tile, pencil, error);
2233 }
2234 }
2235 }
2236}
2237
2238static float game_anim_length(const game_state *oldstate,
2239 const game_state *newstate, int dir, game_ui *ui)
2240{
2241 return 0.0F;
2242}
2243
2244static float game_flash_length(const game_state *oldstate,
2245 const game_state *newstate, int dir, game_ui *ui)
2246{
2247 if (!oldstate->completed && newstate->completed &&
2248 !oldstate->cheated && !newstate->cheated)
2249 return FLASH_TIME;
2250 return 0.0F;
2251}
2252
2253static void game_get_cursor_location(const game_ui *ui,
2254 const game_drawstate *ds,
2255 const game_state *state,
2256 const game_params *params,
2257 int *x, int *y, int *w, int *h)
2258{
2259}
2260
2261static int game_status(const game_state *state)
2262{
2263 return state->completed ? +1 : 0;
2264}
2265
2266static bool game_timing_state(const game_state *state, game_ui *ui)
2267{
2268 if (state->completed)
2269 return false;
2270 return true;
2271}
2272
2273static void game_print_size(const game_params *params, const game_ui *ui,
2274 float *x, float *y)
2275{
2276 int pw, ph;
2277
2278 /*
2279 * We use 9mm squares by default, like Solo.
2280 */
2281 game_compute_size(params, 900, ui, &pw, &ph);
2282 *x = pw / 100.0F;
2283 *y = ph / 100.0F;
2284}
2285
2286static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2287 int tilesize)
2288{
2289 int w = state->par.w;
2290 int ink = print_mono_colour(dr, 0);
2291 int x, y;
2292
2293 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2294 game_drawstate ads, *ds = &ads;
2295 game_set_size(dr, ds, NULL, tilesize);
2296
2297 /*
2298 * Border.
2299 */
2300 print_line_width(dr, 3 * TILESIZE / 40);
2301 draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND,
2302 w*TILESIZE, w*TILESIZE, ink);
2303
2304 /*
2305 * Legend on table.
2306 */
2307 for (x = 0; x < w; x++) {
2308 char str[2];
2309 str[1] = '\0';
2310 str[0] = TOCHAR(x+1, state->par.id);
2311 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2312 BORDER + TILESIZE/2,
2313 FONT_VARIABLE, TILESIZE/2,
2314 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2315 draw_text(dr, BORDER + TILESIZE/2,
2316 BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2317 FONT_VARIABLE, TILESIZE/2,
2318 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2319 }
2320
2321 /*
2322 * Main grid.
2323 */
2324 for (x = 1; x < w; x++) {
2325 print_line_width(dr, TILESIZE / 40);
2326 draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND,
2327 BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink);
2328 }
2329 for (y = 1; y < w; y++) {
2330 print_line_width(dr, TILESIZE / 40);
2331 draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE,
2332 BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink);
2333 }
2334
2335 /*
2336 * Numbers.
2337 */
2338 for (y = 0; y < w; y++)
2339 for (x = 0; x < w; x++)
2340 if (state->grid[y*w+x]) {
2341 char str[2];
2342 str[1] = '\0';
2343 str[0] = TOCHAR(state->grid[y*w+x], state->par.id);
2344 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2345 BORDER+LEGEND + y*TILESIZE + TILESIZE/2,
2346 FONT_VARIABLE, TILESIZE/2,
2347 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2348 }
2349}
2350
2351#ifdef COMBINED
2352#define thegame group
2353#endif
2354
2355const struct game thegame = {
2356 "Group", NULL, NULL,
2357 default_params,
2358 game_fetch_preset, NULL,
2359 decode_params,
2360 encode_params,
2361 free_params,
2362 dup_params,
2363 true, game_configure, custom_params,
2364 validate_params,
2365 new_game_desc,
2366 validate_desc,
2367 new_game,
2368 dup_game,
2369 free_game,
2370 true, solve_game,
2371 true, game_can_format_as_text_now, game_text_format,
2372 get_prefs, set_prefs,
2373 new_ui,
2374 free_ui,
2375 NULL, /* encode_ui */
2376 NULL, /* decode_ui */
2377 NULL, /* game_request_keys */
2378 game_changed_state,
2379 current_key_label,
2380 interpret_move,
2381 execute_move,
2382 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2383 game_colours,
2384 game_new_drawstate,
2385 game_free_drawstate,
2386 game_redraw,
2387 game_anim_length,
2388 game_flash_length,
2389 game_get_cursor_location,
2390 game_status,
2391 true, false, game_print_size, game_print,
2392 false, /* wants_statusbar */
2393 false, game_timing_state,
2394 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2395};
2396
2397#ifdef STANDALONE_SOLVER
2398
2399#include <stdarg.h>
2400
2401int main(int argc, char **argv)
2402{
2403 game_params *p;
2404 game_state *s;
2405 char *id = NULL, *desc;
2406 const char *err;
2407 digit *grid;
2408 bool grade = false;
2409 int ret, diff;
2410 bool really_show_working = false;
2411
2412 while (--argc > 0) {
2413 char *p = *++argv;
2414 if (!strcmp(p, "-v")) {
2415 really_show_working = true;
2416 } else if (!strcmp(p, "-g")) {
2417 grade = true;
2418 } else if (*p == '-') {
2419 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2420 return 1;
2421 } else {
2422 id = p;
2423 }
2424 }
2425
2426 if (!id) {
2427 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2428 return 1;
2429 }
2430
2431 desc = strchr(id, ':');
2432 if (!desc) {
2433 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2434 return 1;
2435 }
2436 *desc++ = '\0';
2437
2438 p = default_params();
2439 decode_params(p, id);
2440 err = validate_desc(p, desc);
2441 if (err) {
2442 fprintf(stderr, "%s: %s\n", argv[0], err);
2443 return 1;
2444 }
2445 s = new_game(NULL, p, desc);
2446
2447 grid = snewn(p->w * p->w, digit);
2448
2449 /*
2450 * When solving a Normal puzzle, we don't want to bother the
2451 * user with Hard-level deductions. For this reason, we grade
2452 * the puzzle internally before doing anything else.
2453 */
2454 ret = -1; /* placate optimiser */
2455 solver_show_working = 0;
2456 for (diff = 0; diff < DIFFCOUNT; diff++) {
2457 memcpy(grid, s->grid, p->w * p->w);
2458 ret = solver(&s->par, grid, diff);
2459 if (ret <= diff)
2460 break;
2461 }
2462
2463 if (diff == DIFFCOUNT) {
2464 if (really_show_working) {
2465 solver_show_working = true;
2466 memcpy(grid, s->grid, p->w * p->w);
2467 ret = solver(&s->par, grid, DIFFCOUNT - 1);
2468 }
2469 if (grade)
2470 printf("Difficulty rating: ambiguous\n");
2471 else
2472 printf("Unable to find a unique solution\n");
2473 } else {
2474 if (grade) {
2475 if (ret == diff_impossible)
2476 printf("Difficulty rating: impossible (no solution exists)\n");
2477 else
2478 printf("Difficulty rating: %s\n", group_diffnames[ret]);
2479 } else {
2480 solver_show_working = really_show_working;
2481 memcpy(grid, s->grid, p->w * p->w);
2482 ret = solver(&s->par, grid, diff);
2483 if (ret != diff)
2484 printf("Puzzle is inconsistent\n");
2485 else {
2486 memcpy(s->grid, grid, p->w * p->w);
2487 fputs(game_text_format(s), stdout);
2488 }
2489 }
2490 }
2491
2492 return 0;
2493}
2494
2495#endif
2496
2497/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/group.gap b/apps/plugins/puzzles/src/unfinished/group.gap
new file mode 100644
index 0000000000..280adf4664
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.gap
@@ -0,0 +1,97 @@
1# run this file with
2# gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr
3
4Print("/* ----- data generated by group.gap begins ----- */\n\n");
5Print("struct group {\n unsigned long autosize;\n");
6Print(" int order, ngens;\n const char *gens;\n};\n");
7Print("struct groups {\n int ngroups;\n");
8Print(" const struct group *groups;\n};\n\n");
9Print("static const struct group groupdata[] = {\n");
10offsets := [0];
11offset := 0;
12for n in [2..26] do
13 Print(" /* order ", n, " */\n");
14 for G in AllSmallGroups(n) do
15
16 # Construct a representation of the group G as a subgroup
17 # of a permutation group, and find its generators in that
18 # group.
19
20 # GAP has the 'IsomorphismPermGroup' function, but I don't want
21 # to use it because it doesn't guarantee that the permutation
22 # representation of the group forms a Cayley table. For example,
23 # C_4 could be represented as a subgroup of S_4 in many ways,
24 # and not all of them work: the group generated by (12) and (34)
25 # is clearly isomorphic to C_4 but its four elements do not form
26 # a Cayley table. The group generated by (12)(34) and (13)(24)
27 # is OK, though.
28 #
29 # Hence I construct the permutation representation _as_ the
30 # Cayley table, and then pick generators of that. This
31 # guarantees that when we rebuild the full group by BFS in
32 # group.c, we will end up with the right thing.
33
34 ge := Elements(G);
35 gi := [];
36 for g in ge do
37 gr := [];
38 for h in ge do
39 k := g*h;
40 for i in [1..n] do
41 if k = ge[i] then
42 Add(gr, i);
43 fi;
44 od;
45 od;
46 Add(gi, PermList(gr));
47 od;
48
49 # GAP has the 'GeneratorsOfGroup' function, but we don't want to
50 # use it because it's bad at picking generators - it thinks the
51 # generators of C_4 are [ (1,2)(3,4), (1,3,2,4) ] and that those
52 # of C_6 are [ (1,2,3)(4,5,6), (1,4)(2,5)(3,6) ] !
53
54 gl := ShallowCopy(Elements(gi));
55 Sort(gl, function(v,w) return Order(v) > Order(w); end);
56
57 gens := [];
58 for x in gl do
59 if gens = [] or not (x in gp) then
60 Add(gens, x);
61 gp := GroupWithGenerators(gens);
62 fi;
63 od;
64
65 # Construct the C representation of the group generators.
66 s := [];
67 for x in gens do
68 if Size(s) > 0 then
69 Add(s, '"');
70 Add(s, ' ');
71 Add(s, '"');
72 fi;
73 sep := "\\0";
74 for i in ListPerm(x) do
75 chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
76 Add(s, chars[i]);
77 od;
78 od;
79 s := JoinStringsWithSeparator([" {", String(Size(AutomorphismGroup(G))),
80 "L, ", String(Size(G)),
81 ", ", String(Size(gens)),
82 ", \"", s, "\"},\n"],"");
83 Print(s);
84 offset := offset + 1;
85 od;
86 Add(offsets, offset);
87od;
88Print("};\n\nstatic const struct groups groups[] = {\n");
89Print(" {0, NULL}, /* trivial case: 0 */\n");
90Print(" {0, NULL}, /* trivial case: 1 */\n");
91n := 2;
92for i in [1..Size(offsets)-1] do
93 Print(" {", offsets[i+1] - offsets[i], ", groupdata+",
94 offsets[i], "}, /* ", i+1, " */\n");
95od;
96Print("};\n\n/* ----- data generated by group.gap ends ----- */\n");
97quit;
diff --git a/apps/plugins/puzzles/src/unfinished/numgame.c b/apps/plugins/puzzles/src/unfinished/numgame.c
new file mode 100644
index 0000000000..cf919922e7
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/numgame.c
@@ -0,0 +1,1294 @@
1/*
2 * This program implements a breadth-first search which
3 * exhaustively solves the Countdown numbers game, and related
4 * games with slightly different rule sets such as `Flippo'.
5 *
6 * Currently it is simply a standalone command-line utility to
7 * which you provide a set of numbers and it tells you everything
8 * it can make together with how many different ways it can be
9 * made. I would like ultimately to turn it into the generator for
10 * a Puzzles puzzle, but I haven't even started on writing a
11 * Puzzles user interface yet.
12 */
13
14/*
15 * TODO:
16 *
17 * - start thinking about difficulty ratings
18 * + anything involving associative operations will be flagged
19 * as many-paths because of the associative options (e.g.
20 * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This
21 * is probably a _good_ thing, since those are unusually
22 * easy.
23 * + tree-structured calculations ((a*b)/(c+d)) have multiple
24 * paths because the independent branches of the tree can be
25 * evaluated in either order, whereas straight-line
26 * calculations with no branches will be considered easier.
27 * Can we do anything about this? It's certainly not clear to
28 * me that tree-structure calculations are _easier_, although
29 * I'm also not convinced they're harder.
30 * + I think for a realistic difficulty assessment we must also
31 * consider the `obviousness' of the arithmetic operations in
32 * some heuristic sense, and also (in Countdown) how many
33 * numbers ended up being used.
34 * - actually try some generations
35 * - at this point we're probably ready to start on the Puzzles
36 * integration.
37 */
38
39#include <stdio.h>
40#include <string.h>
41#include <limits.h>
42#include <assert.h>
43#ifdef NO_TGMATH_H
44# include <math.h>
45#else
46# include <tgmath.h>
47#endif
48
49#include "puzzles.h"
50#include "tree234.h"
51
52/*
53 * To search for numbers we can make, we employ a breadth-first
54 * search across the space of sets of input numbers. That is, for
55 * example, we start with the set (3,6,25,50,75,100); we apply
56 * moves which involve combining two numbers (e.g. adding the 50
57 * and the 75 takes us to the set (3,6,25,100,125); and then we see
58 * if we ever end up with a set containing (say) 952.
59 *
60 * If the rules are changed so that all the numbers must be used,
61 * this is easy to adjust to: we simply see if we end up with a set
62 * containing _only_ (say) 952.
63 *
64 * Obviously, we can vary the rules about permitted arithmetic
65 * operations simply by altering the set of valid moves in the bfs.
66 * However, there's one common rule in this sort of puzzle which
67 * takes a little more thought, and that's _concatenation_. For
68 * example, if you are given (say) four 4s and required to make 10,
69 * you are permitted to combine two of the 4s into a 44 to begin
70 * with, making (44-4)/4 = 10. However, you are generally not
71 * allowed to concatenate two numbers that _weren't_ both in the
72 * original input set (you couldn't multiply two 4s to get 16 and
73 * then concatenate a 4 on to it to make 164), so concatenation is
74 * not an operation which is valid in all situations.
75 *
76 * We could enforce this restriction by storing a flag alongside
77 * each number indicating whether or not it's an original number;
78 * the rules being that concatenation of two numbers is only valid
79 * if they both have the original flag, and that its output _also_
80 * has the original flag (so that you can concatenate three 4s into
81 * a 444), but that applying any other arithmetic operation clears
82 * the original flag on the output. However, we can get marginally
83 * simpler than that by observing that since concatenation has to
84 * happen to a number before any other operation, we can simply
85 * place all the concatenations at the start of the search. In
86 * other words, we have a global flag on an entire number _set_
87 * which indicates whether we are still permitted to perform
88 * concatenations; if so, we can concatenate any of the numbers in
89 * that set. Performing any other operation clears the flag.
90 */
91
92#define SETFLAG_CONCAT 1 /* we can do concatenation */
93
94struct sets;
95
96struct ancestor {
97 struct set *prev; /* index of ancestor set in set list */
98 unsigned char pa, pb, po, pr; /* operation that got here from prev */
99};
100
101struct set {
102 int *numbers; /* rationals stored as n,d pairs */
103 short nnumbers; /* # of rationals, so half # of ints */
104 short flags; /* SETFLAG_CONCAT only, at present */
105 int npaths; /* number of ways to reach this set */
106 struct ancestor a; /* primary ancestor */
107 struct ancestor *as; /* further ancestors, if we care */
108 int nas, assize;
109};
110
111struct output {
112 int number;
113 struct set *set;
114 int index; /* which number in the set is it? */
115 int npaths; /* number of ways to reach this */
116};
117
118#define SETLISTLEN 1024
119#define NUMBERLISTLEN 32768
120#define OUTPUTLISTLEN 1024
121struct operation;
122struct sets {
123 struct set **setlists;
124 int nsets, nsetlists, setlistsize;
125 tree234 *settree;
126 int **numberlists;
127 int nnumbers, nnumberlists, numberlistsize;
128 struct output **outputlists;
129 int noutputs, noutputlists, outputlistsize;
130 tree234 *outputtree;
131 const struct operation *const *ops;
132};
133
134#define OPFLAG_NEEDS_CONCAT 1
135#define OPFLAG_KEEPS_CONCAT 2
136#define OPFLAG_UNARY 4
137#define OPFLAG_UNARYPREFIX 8
138#define OPFLAG_FN 16
139
140struct operation {
141 /*
142 * Most operations should be shown in the output working, but
143 * concatenation should not; we just take the result of the
144 * concatenation and assume that it's obvious how it was
145 * derived.
146 */
147 int display;
148
149 /*
150 * Text display of the operator, in expressions and for
151 * debugging respectively.
152 */
153 const char *text, *dbgtext;
154
155 /*
156 * Flags dictating when the operator can be applied.
157 */
158 int flags;
159
160 /*
161 * Priority of the operator (for avoiding unnecessary
162 * parentheses when formatting it into a string).
163 */
164 int priority;
165
166 /*
167 * Associativity of the operator. Bit 0 means we need parens
168 * when the left operand of one of these operators is another
169 * instance of it, e.g. (2^3)^4. Bit 1 means we need parens
170 * when the right operand is another instance of the same
171 * operator, e.g. 2-(3-4). Thus:
172 *
173 * - this field is 0 for a fully associative operator, since
174 * we never need parens.
175 * - it's 1 for a right-associative operator.
176 * - it's 2 for a left-associative operator.
177 * - it's 3 for a _non_-associative operator (which always
178 * uses parens just to be sure).
179 */
180 int assoc;
181
182 /*
183 * Whether the operator is commutative. Saves time in the
184 * search if we don't have to try it both ways round.
185 */
186 int commutes;
187
188 /*
189 * Function which implements the operator. Returns true on
190 * success, false on failure. Takes two rationals and writes
191 * out a third.
192 */
193 int (*perform)(int *a, int *b, int *output);
194};
195
196struct rules {
197 const struct operation *const *ops;
198 int use_all;
199};
200
201#define MUL(r, a, b) do { \
202 (r) = (a) * (b); \
203 if ((b) && (a) && (r) / (b) != (a)) return false; \
204} while (0)
205
206#define ADD(r, a, b) do { \
207 (r) = (a) + (b); \
208 if ((a) > 0 && (b) > 0 && (r) < 0) return false; \
209 if ((a) < 0 && (b) < 0 && (r) > 0) return false; \
210} while (0)
211
212#define OUT(output, n, d) do { \
213 int g = gcd((n),(d)); \
214 if (g < 0) g = -g; \
215 if ((d) < 0) g = -g; \
216 if (g == -1 && (n) < -INT_MAX) return false; \
217 if (g == -1 && (d) < -INT_MAX) return false; \
218 (output)[0] = (n)/g; \
219 (output)[1] = (d)/g; \
220 assert((output)[1] > 0); \
221} while (0)
222
223static int gcd(int x, int y)
224{
225 while (x != 0 && y != 0) {
226 int t = x;
227 x = y;
228 y = t % y;
229 }
230
231 return abs(x + y); /* i.e. whichever one isn't zero */
232}
233
234static int perform_add(int *a, int *b, int *output)
235{
236 int at, bt, tn, bn;
237 /*
238 * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1)
239 */
240 MUL(at, a[0], b[1]);
241 MUL(bt, b[0], a[1]);
242 ADD(tn, at, bt);
243 MUL(bn, a[1], b[1]);
244 OUT(output, tn, bn);
245 return true;
246}
247
248static int perform_sub(int *a, int *b, int *output)
249{
250 int at, bt, tn, bn;
251 /*
252 * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1)
253 */
254 MUL(at, a[0], b[1]);
255 MUL(bt, b[0], a[1]);
256 ADD(tn, at, -bt);
257 MUL(bn, a[1], b[1]);
258 OUT(output, tn, bn);
259 return true;
260}
261
262static int perform_mul(int *a, int *b, int *output)
263{
264 int tn, bn;
265 /*
266 * a0/a1 * b0/b1 = (a0*b0) / (a1*b1)
267 */
268 MUL(tn, a[0], b[0]);
269 MUL(bn, a[1], b[1]);
270 OUT(output, tn, bn);
271 return true;
272}
273
274static int perform_div(int *a, int *b, int *output)
275{
276 int tn, bn;
277
278 /*
279 * Division by zero is outlawed.
280 */
281 if (b[0] == 0)
282 return false;
283
284 /*
285 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
286 */
287 MUL(tn, a[0], b[1]);
288 MUL(bn, a[1], b[0]);
289 OUT(output, tn, bn);
290 return true;
291}
292
293static int perform_exact_div(int *a, int *b, int *output)
294{
295 int tn, bn;
296
297 /*
298 * Division by zero is outlawed.
299 */
300 if (b[0] == 0)
301 return false;
302
303 /*
304 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
305 */
306 MUL(tn, a[0], b[1]);
307 MUL(bn, a[1], b[0]);
308 OUT(output, tn, bn);
309
310 /*
311 * Exact division means we require the result to be an integer.
312 */
313 return (output[1] == 1);
314}
315
316static int max_p10(int n, int *p10_r)
317{
318 /*
319 * Find the smallest power of ten strictly greater than n.
320 *
321 * Special case: we must return at least 10, even if n is
322 * zero. (This is because this function is used for finding
323 * the power of ten by which to multiply a number being
324 * concatenated to the front of n, and concatenating 1 to 0
325 * should yield 10 and not 1.)
326 */
327 int p10 = 10;
328 while (p10 <= (INT_MAX/10) && p10 <= n)
329 p10 *= 10;
330 if (p10 > INT_MAX/10)
331 return false; /* integer overflow */
332 *p10_r = p10;
333 return true;
334}
335
336static int perform_concat(int *a, int *b, int *output)
337{
338 int t1, t2, p10;
339
340 /*
341 * We can't concatenate anything which isn't a non-negative
342 * integer.
343 */
344 if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0)
345 return false;
346
347 /*
348 * For concatenation, we can safely assume leading zeroes
349 * aren't an issue. It isn't clear whether they `should' be
350 * allowed, but it turns out not to matter: concatenating a
351 * leading zero on to a number in order to harmlessly get rid
352 * of the zero is never necessary because unwanted zeroes can
353 * be disposed of by adding them to something instead. So we
354 * disallow them always.
355 *
356 * The only other possibility is that you might want to
357 * concatenate a leading zero on to something and then
358 * concatenate another non-zero digit on to _that_ (to make,
359 * for example, 106); but that's also unnecessary, because you
360 * can make 106 just as easily by concatenating the 0 on to the
361 * _end_ of the 1 first.
362 */
363 if (a[0] == 0)
364 return false;
365
366 if (!max_p10(b[0], &p10)) return false;
367
368 MUL(t1, p10, a[0]);
369 ADD(t2, t1, b[0]);
370 OUT(output, t2, 1);
371 return true;
372}
373
374#define IPOW(ret, x, y) do { \
375 int ipow_limit = (y); \
376 if ((x) == 1 || (x) == 0) ipow_limit = 1; \
377 else if ((x) == -1) ipow_limit &= 1; \
378 (ret) = 1; \
379 while (ipow_limit-- > 0) { \
380 int tmp; \
381 MUL(tmp, ret, x); \
382 ret = tmp; \
383 } \
384} while (0)
385
386static int perform_exp(int *a, int *b, int *output)
387{
388 int an, ad, xn, xd;
389
390 /*
391 * Exponentiation is permitted if the result is rational. This
392 * means that:
393 *
394 * - first we see whether we can take the (denominator-of-b)th
395 * root of a and get a rational; if not, we give up.
396 *
397 * - then we do take that root of a
398 *
399 * - then we multiply by itself (numerator-of-b) times.
400 */
401 if (b[1] > 1) {
402 an = (int)(0.5 + pow(a[0], 1.0/b[1]));
403 ad = (int)(0.5 + pow(a[1], 1.0/b[1]));
404 IPOW(xn, an, b[1]);
405 IPOW(xd, ad, b[1]);
406 if (xn != a[0] || xd != a[1])
407 return false;
408 } else {
409 an = a[0];
410 ad = a[1];
411 }
412 if (b[0] >= 0) {
413 IPOW(xn, an, b[0]);
414 IPOW(xd, ad, b[0]);
415 } else {
416 IPOW(xd, an, -b[0]);
417 IPOW(xn, ad, -b[0]);
418 }
419 if (xd == 0)
420 return false;
421
422 OUT(output, xn, xd);
423 return true;
424}
425
426static int perform_factorial(int *a, int *b, int *output)
427{
428 int ret, t, i;
429
430 /*
431 * Factorials of non-negative integers are permitted.
432 */
433 if (a[1] != 1 || a[0] < 0)
434 return false;
435
436 /*
437 * However, a special case: we don't take a factorial of
438 * anything which would thereby remain the same.
439 */
440 if (a[0] == 1 || a[0] == 2)
441 return false;
442
443 ret = 1;
444 for (i = 1; i <= a[0]; i++) {
445 MUL(t, ret, i);
446 ret = t;
447 }
448
449 OUT(output, ret, 1);
450 return true;
451}
452
453static int perform_decimal(int *a, int *b, int *output)
454{
455 int p10;
456
457 /*
458 * Add a decimal digit to the front of a number;
459 * fail if it's not an integer.
460 * So, 1 --> 0.1, 15 --> 0.15,
461 * or, rather, 1 --> 1/10, 15 --> 15/100,
462 * x --> x / (smallest power of 10 > than x)
463 *
464 */
465 if (a[1] != 1) return false;
466
467 if (!max_p10(a[0], &p10)) return false;
468
469 OUT(output, a[0], p10);
470 return true;
471}
472
473static int perform_recur(int *a, int *b, int *output)
474{
475 int p10, tn, bn;
476
477 /*
478 * This converts a number like .4 to .44444..., or .45 to .45454...
479 * The input number must be -1 < a < 1.
480 *
481 * Calculate the smallest power of 10 that divides the denominator exactly,
482 * returning if no such power of 10 exists. Then multiply the numerator
483 * up accordingly, and the new denominator becomes that power of 10 - 1.
484 */
485 if (abs(a[0]) >= abs(a[1])) return false; /* -1 < a < 1 */
486
487 p10 = 10;
488 while (p10 <= (INT_MAX/10)) {
489 if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found;
490 p10 *= 10;
491 }
492 return false;
493found:
494 tn = a[0] * (p10 / a[1]);
495 bn = p10 - 1;
496
497 OUT(output, tn, bn);
498 return true;
499}
500
501static int perform_root(int *a, int *b, int *output)
502{
503 /*
504 * A root B is: 1 iff a == 0
505 * B ^ (1/A) otherwise
506 */
507 int ainv[2], res;
508
509 if (a[0] == 0) {
510 OUT(output, 1, 1);
511 return true;
512 }
513
514 OUT(ainv, a[1], a[0]);
515 res = perform_exp(b, ainv, output);
516 return res;
517}
518
519static int perform_perc(int *a, int *b, int *output)
520{
521 if (a[0] == 0) return false; /* 0% = 0, uninteresting. */
522 if (a[1] > (INT_MAX/100)) return false;
523
524 OUT(output, a[0], a[1]*100);
525 return true;
526}
527
528static int perform_gamma(int *a, int *b, int *output)
529{
530 int asub1[2];
531
532 /*
533 * gamma(a) = (a-1)!
534 *
535 * special case not caught by perform_fact: gamma(1) is 1 so
536 * don't bother.
537 */
538 if (a[0] == 1 && a[1] == 1) return false;
539
540 OUT(asub1, a[0]-a[1], a[1]);
541 return perform_factorial(asub1, b, output);
542}
543
544static int perform_sqrt(int *a, int *b, int *output)
545{
546 int half[2] = { 1, 2 };
547
548 /*
549 * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops.
550 */
551 if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return false;
552
553 return perform_exp(a, half, output);
554}
555
556static const struct operation op_add = {
557 true, "+", "+", 0, 10, 0, true, perform_add
558};
559static const struct operation op_sub = {
560 true, "-", "-", 0, 10, 2, false, perform_sub
561};
562static const struct operation op_mul = {
563 true, "*", "*", 0, 20, 0, true, perform_mul
564};
565static const struct operation op_div = {
566 true, "/", "/", 0, 20, 2, false, perform_div
567};
568static const struct operation op_xdiv = {
569 true, "/", "/", 0, 20, 2, false, perform_exact_div
570};
571static const struct operation op_concat = {
572 false, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT,
573 1000, 0, false, perform_concat
574};
575static const struct operation op_exp = {
576 true, "^", "^", 0, 30, 1, false, perform_exp
577};
578static const struct operation op_factorial = {
579 true, "!", "!", OPFLAG_UNARY, 40, 0, false, perform_factorial
580};
581static const struct operation op_decimal = {
582 true, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, false, perform_decimal
583};
584static const struct operation op_recur = {
585 true, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, false, perform_recur
586};
587static const struct operation op_root = {
588 true, "v~", "root", 0, 30, 1, false, perform_root
589};
590static const struct operation op_perc = {
591 true, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, false, perform_perc
592};
593static const struct operation op_gamma = {
594 true, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, false, perform_gamma
595};
596static const struct operation op_sqrt = {
597 true, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, false, perform_sqrt
598};
599
600/*
601 * In Countdown, divisions resulting in fractions are disallowed.
602 * http://www.askoxford.com/wordgames/countdown/rules/
603 */
604static const struct operation *const ops_countdown[] = {
605 &op_add, &op_mul, &op_sub, &op_xdiv, NULL
606};
607static const struct rules rules_countdown = {
608 ops_countdown, false
609};
610
611/*
612 * A slightly different rule set which handles the reasonably well
613 * known puzzle of making 24 using two 3s and two 8s. For this we
614 * need rational rather than integer division.
615 */
616static const struct operation *const ops_3388[] = {
617 &op_add, &op_mul, &op_sub, &op_div, NULL
618};
619static const struct rules rules_3388 = {
620 ops_3388, true
621};
622
623/*
624 * A still more permissive rule set usable for the four-4s problem
625 * and similar things. Permits concatenation.
626 */
627static const struct operation *const ops_four4s[] = {
628 &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL
629};
630static const struct rules rules_four4s = {
631 ops_four4s, true
632};
633
634/*
635 * The most permissive ruleset I can think of. Permits
636 * exponentiation, and also silly unary operators like factorials.
637 */
638static const struct operation *const ops_anythinggoes[] = {
639 &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial,
640 &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL
641};
642static const struct rules rules_anythinggoes = {
643 ops_anythinggoes, true
644};
645
646#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \
647 (long long)(b)[0] * (a)[1] )
648
649static int addtoset(struct set *set, int newnumber[2])
650{
651 int i, j;
652
653 /* Find where we want to insert the new number */
654 for (i = 0; i < set->nnumbers &&
655 ratcmp(set->numbers+2*i, <, newnumber); i++);
656
657 /* Move everything else up */
658 for (j = set->nnumbers; j > i; j--) {
659 set->numbers[2*j] = set->numbers[2*j-2];
660 set->numbers[2*j+1] = set->numbers[2*j-1];
661 }
662
663 /* Insert the new number */
664 set->numbers[2*i] = newnumber[0];
665 set->numbers[2*i+1] = newnumber[1];
666
667 set->nnumbers++;
668
669 return i;
670}
671
672#define ensure(array, size, newlen, type) do { \
673 if ((newlen) > (size)) { \
674 (size) = (newlen) + 512; \
675 (array) = sresize((array), (size), type); \
676 } \
677} while (0)
678
679static int setcmp(void *av, void *bv)
680{
681 struct set *a = (struct set *)av;
682 struct set *b = (struct set *)bv;
683 int i;
684
685 if (a->nnumbers < b->nnumbers)
686 return -1;
687 else if (a->nnumbers > b->nnumbers)
688 return +1;
689
690 if (a->flags < b->flags)
691 return -1;
692 else if (a->flags > b->flags)
693 return +1;
694
695 for (i = 0; i < a->nnumbers; i++) {
696 if (ratcmp(a->numbers+2*i, <, b->numbers+2*i))
697 return -1;
698 else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i))
699 return +1;
700 }
701
702 return 0;
703}
704
705static int outputcmp(void *av, void *bv)
706{
707 struct output *a = (struct output *)av;
708 struct output *b = (struct output *)bv;
709
710 if (a->number < b->number)
711 return -1;
712 else if (a->number > b->number)
713 return +1;
714
715 return 0;
716}
717
718static int outputfindcmp(void *av, void *bv)
719{
720 int *a = (int *)av;
721 struct output *b = (struct output *)bv;
722
723 if (*a < b->number)
724 return -1;
725 else if (*a > b->number)
726 return +1;
727
728 return 0;
729}
730
731static void addset(struct sets *s, struct set *set, int multiple,
732 struct set *prev, int pa, int po, int pb, int pr)
733{
734 struct set *s2;
735 int npaths = (prev ? prev->npaths : 1);
736
737 assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN);
738 s2 = add234(s->settree, set);
739 if (s2 == set) {
740 /*
741 * New set added to the tree.
742 */
743 set->a.prev = prev;
744 set->a.pa = pa;
745 set->a.po = po;
746 set->a.pb = pb;
747 set->a.pr = pr;
748 set->npaths = npaths;
749 s->nsets++;
750 s->nnumbers += 2 * set->nnumbers;
751 set->as = NULL;
752 set->nas = set->assize = 0;
753 } else {
754 /*
755 * Rediscovered an existing set. Update its npaths.
756 */
757 s2->npaths += npaths;
758 /*
759 * And optionally enter it as an additional ancestor.
760 */
761 if (multiple) {
762 if (s2->nas >= s2->assize) {
763 s2->assize = s2->nas * 3 / 2 + 4;
764 s2->as = sresize(s2->as, s2->assize, struct ancestor);
765 }
766 s2->as[s2->nas].prev = prev;
767 s2->as[s2->nas].pa = pa;
768 s2->as[s2->nas].po = po;
769 s2->as[s2->nas].pb = pb;
770 s2->as[s2->nas].pr = pr;
771 s2->nas++;
772 }
773 }
774}
775
776static struct set *newset(struct sets *s, int nnumbers, int flags)
777{
778 struct set *sn;
779
780 ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *);
781 while (s->nsetlists <= s->nsets / SETLISTLEN)
782 s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set);
783 sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN;
784
785 if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN)
786 s->nnumbers = s->nnumberlists * NUMBERLISTLEN;
787 ensure(s->numberlists, s->numberlistsize,
788 s->nnumbers/NUMBERLISTLEN+1, int *);
789 while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN)
790 s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int);
791 sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] +
792 s->nnumbers % NUMBERLISTLEN;
793
794 /*
795 * Start the set off empty.
796 */
797 sn->nnumbers = 0;
798
799 sn->flags = flags;
800
801 return sn;
802}
803
804static int addoutput(struct sets *s, struct set *ss, int index, int *n)
805{
806 struct output *o, *o2;
807
808 /*
809 * Target numbers are always integers.
810 */
811 if (ss->numbers[2*index+1] != 1)
812 return false;
813
814 ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1,
815 struct output *);
816 while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN)
817 s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN,
818 struct output);
819 o = s->outputlists[s->noutputs / OUTPUTLISTLEN] +
820 s->noutputs % OUTPUTLISTLEN;
821
822 o->number = ss->numbers[2*index];
823 o->set = ss;
824 o->index = index;
825 o->npaths = ss->npaths;
826 o2 = add234(s->outputtree, o);
827 if (o2 != o) {
828 o2->npaths += o->npaths;
829 } else {
830 s->noutputs++;
831 }
832 *n = o->number;
833 return true;
834}
835
836static struct sets *do_search(int ninputs, int *inputs,
837 const struct rules *rules, int *target,
838 int debug, int multiple)
839{
840 struct sets *s;
841 struct set *sn;
842 int qpos, i;
843 const struct operation *const *ops = rules->ops;
844
845 s = snew(struct sets);
846 s->setlists = NULL;
847 s->nsets = s->nsetlists = s->setlistsize = 0;
848 s->numberlists = NULL;
849 s->nnumbers = s->nnumberlists = s->numberlistsize = 0;
850 s->outputlists = NULL;
851 s->noutputs = s->noutputlists = s->outputlistsize = 0;
852 s->settree = newtree234(setcmp);
853 s->outputtree = newtree234(outputcmp);
854 s->ops = ops;
855
856 /*
857 * Start with the input set.
858 */
859 sn = newset(s, ninputs, SETFLAG_CONCAT);
860 for (i = 0; i < ninputs; i++) {
861 int newnumber[2];
862 newnumber[0] = inputs[i];
863 newnumber[1] = 1;
864 addtoset(sn, newnumber);
865 }
866 addset(s, sn, multiple, NULL, 0, 0, 0, 0);
867
868 /*
869 * Now perform the breadth-first search: keep looping over sets
870 * until we run out of steam.
871 */
872 qpos = 0;
873 while (qpos < s->nsets) {
874 struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN;
875 struct set *sn;
876 int i, j, k, m;
877
878 if (debug) {
879 int i;
880 printf("processing set:");
881 for (i = 0; i < ss->nnumbers; i++) {
882 printf(" %d", ss->numbers[2*i]);
883 if (ss->numbers[2*i+1] != 1)
884 printf("/%d", ss->numbers[2*i+1]);
885 }
886 printf("\n");
887 }
888
889 /*
890 * Record all the valid output numbers in this state. We
891 * can always do this if there's only one number in the
892 * state; otherwise, we can only do it if we aren't
893 * required to use all the numbers in coming to our answer.
894 */
895 if (ss->nnumbers == 1 || !rules->use_all) {
896 for (i = 0; i < ss->nnumbers; i++) {
897 int n;
898
899 if (addoutput(s, ss, i, &n) && target && n == *target)
900 return s;
901 }
902 }
903
904 /*
905 * Try every possible operation from this state.
906 */
907 for (k = 0; ops[k] && ops[k]->perform; k++) {
908 if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) &&
909 !(ss->flags & SETFLAG_CONCAT))
910 continue; /* can't use this operation here */
911 for (i = 0; i < ss->nnumbers; i++) {
912 int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers);
913 for (j = 0; j < jlimit; j++) {
914 int n[2], newnn = ss->nnumbers;
915 int pa, po, pb, pr;
916
917 if (!(ops[k]->flags & OPFLAG_UNARY)) {
918 if (i == j)
919 continue; /* can't combine a number with itself */
920 if (i > j && ops[k]->commutes)
921 continue; /* no need to do this both ways round */
922 newnn--;
923 }
924 if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n))
925 continue; /* operation failed */
926
927 sn = newset(s, newnn, ss->flags);
928
929 if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT))
930 sn->flags &= ~SETFLAG_CONCAT;
931
932 for (m = 0; m < ss->nnumbers; m++) {
933 if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) &&
934 m == j))
935 continue;
936 sn->numbers[2*sn->nnumbers] = ss->numbers[2*m];
937 sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1];
938 sn->nnumbers++;
939 }
940 pa = i;
941 if (ops[k]->flags & OPFLAG_UNARY)
942 pb = sn->nnumbers+10;
943 else
944 pb = j;
945 po = k;
946 pr = addtoset(sn, n);
947 addset(s, sn, multiple, ss, pa, po, pb, pr);
948 if (debug) {
949 int i;
950 if (ops[k]->flags & OPFLAG_UNARYPREFIX)
951 printf(" %s %d ->", ops[po]->dbgtext, pa);
952 else if (ops[k]->flags & OPFLAG_UNARY)
953 printf(" %d %s ->", pa, ops[po]->dbgtext);
954 else
955 printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb);
956 for (i = 0; i < sn->nnumbers; i++) {
957 printf(" %d", sn->numbers[2*i]);
958 if (sn->numbers[2*i+1] != 1)
959 printf("/%d", sn->numbers[2*i+1]);
960 }
961 printf("\n");
962 }
963 }
964 }
965 }
966
967 qpos++;
968 }
969
970 return s;
971}
972
973static void free_sets(struct sets *s)
974{
975 int i;
976
977 freetree234(s->settree);
978 freetree234(s->outputtree);
979 for (i = 0; i < s->nsetlists; i++)
980 sfree(s->setlists[i]);
981 sfree(s->setlists);
982 for (i = 0; i < s->nnumberlists; i++)
983 sfree(s->numberlists[i]);
984 sfree(s->numberlists);
985 for (i = 0; i < s->noutputlists; i++)
986 sfree(s->outputlists[i]);
987 sfree(s->outputlists);
988 sfree(s);
989}
990
991/*
992 * Print a text formula for producing a given output.
993 */
994static void print_recurse(struct sets *s, struct set *ss, int pathindex,
995 int index, int priority, int assoc, int child);
996static void print_recurse_inner(struct sets *s, struct set *ss,
997 struct ancestor *a, int pathindex, int index,
998 int priority, int assoc, int child)
999{
1000 if (a->prev && index != a->pr) {
1001 int pi;
1002
1003 /*
1004 * This number was passed straight down from this set's
1005 * predecessor. Find its index in the previous set and
1006 * recurse to there.
1007 */
1008 pi = index;
1009 assert(pi != a->pr);
1010 if (pi > a->pr)
1011 pi--;
1012 if (pi >= min(a->pa, a->pb)) {
1013 pi++;
1014 if (pi >= max(a->pa, a->pb))
1015 pi++;
1016 }
1017 print_recurse(s, a->prev, pathindex, pi, priority, assoc, child);
1018 } else if (a->prev && index == a->pr &&
1019 s->ops[a->po]->display) {
1020 /*
1021 * This number was created by a displayed operator in the
1022 * transition from this set to its predecessor. Hence we
1023 * write an open paren, then recurse into the first
1024 * operand, then write the operator, then the second
1025 * operand, and finally close the paren.
1026 */
1027 const char *op;
1028 int parens, thispri, thisassoc;
1029
1030 /*
1031 * Determine whether we need parentheses.
1032 */
1033 thispri = s->ops[a->po]->priority;
1034 thisassoc = s->ops[a->po]->assoc;
1035 parens = (thispri < priority ||
1036 (thispri == priority && (assoc & child)));
1037
1038 if (parens)
1039 putchar('(');
1040
1041 if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)
1042 for (op = s->ops[a->po]->text; *op; op++)
1043 putchar(*op);
1044
1045 if (s->ops[a->po]->flags & OPFLAG_FN)
1046 putchar('(');
1047
1048 print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1);
1049
1050 if (s->ops[a->po]->flags & OPFLAG_FN)
1051 putchar(')');
1052
1053 if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX))
1054 for (op = s->ops[a->po]->text; *op; op++)
1055 putchar(*op);
1056
1057 if (!(s->ops[a->po]->flags & OPFLAG_UNARY))
1058 print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2);
1059
1060 if (parens)
1061 putchar(')');
1062 } else {
1063 /*
1064 * This number is either an original, or something formed
1065 * by a non-displayed operator (concatenation). Either way,
1066 * we display it as is.
1067 */
1068 printf("%d", ss->numbers[2*index]);
1069 if (ss->numbers[2*index+1] != 1)
1070 printf("/%d", ss->numbers[2*index+1]);
1071 }
1072}
1073static void print_recurse(struct sets *s, struct set *ss, int pathindex,
1074 int index, int priority, int assoc, int child)
1075{
1076 if (!ss->a.prev || pathindex < ss->a.prev->npaths) {
1077 print_recurse_inner(s, ss, &ss->a, pathindex,
1078 index, priority, assoc, child);
1079 } else {
1080 int i;
1081 pathindex -= ss->a.prev->npaths;
1082 for (i = 0; i < ss->nas; i++) {
1083 if (pathindex < ss->as[i].prev->npaths) {
1084 print_recurse_inner(s, ss, &ss->as[i], pathindex,
1085 index, priority, assoc, child);
1086 break;
1087 }
1088 pathindex -= ss->as[i].prev->npaths;
1089 }
1090 }
1091}
1092static void print(int pathindex, struct sets *s, struct output *o)
1093{
1094 print_recurse(s, o->set, pathindex, o->index, 0, 0, 0);
1095}
1096
1097/*
1098 * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm
1099 */
1100int main(int argc, char **argv)
1101{
1102 int doing_opts = true;
1103 const struct rules *rules = NULL;
1104 char *pname = argv[0];
1105 int got_target = false, target = 0;
1106 int numbers[10], nnumbers = 0;
1107 int verbose = false;
1108 int pathcounts = false;
1109 int multiple = false;
1110 int debug_bfs = false;
1111 int got_range = false, rangemin = 0, rangemax = 0;
1112
1113 struct output *o;
1114 struct sets *s;
1115 int i, start, limit;
1116
1117 while (--argc) {
1118 char *p = *++argv;
1119 int c;
1120
1121 if (doing_opts && *p == '-') {
1122 p++;
1123
1124 if (!strcmp(p, "-")) {
1125 doing_opts = false;
1126 continue;
1127 } else if (*p == '-') {
1128 p++;
1129 if (!strcmp(p, "debug-bfs")) {
1130 debug_bfs = true;
1131 } else {
1132 fprintf(stderr, "%s: option '--%s' not recognised\n",
1133 pname, p);
1134 }
1135 } else while (p && *p) switch (c = *p++) {
1136 case 'C':
1137 rules = &rules_countdown;
1138 break;
1139 case 'B':
1140 rules = &rules_3388;
1141 break;
1142 case 'D':
1143 rules = &rules_four4s;
1144 break;
1145 case 'A':
1146 rules = &rules_anythinggoes;
1147 break;
1148 case 'v':
1149 verbose = true;
1150 break;
1151 case 'p':
1152 pathcounts = true;
1153 break;
1154 case 'm':
1155 multiple = true;
1156 break;
1157 case 't':
1158 case 'r':
1159 {
1160 char *v;
1161 if (*p) {
1162 v = p;
1163 p = NULL;
1164 } else if (--argc) {
1165 v = *++argv;
1166 } else {
1167 fprintf(stderr, "%s: option '-%c' expects an"
1168 " argument\n", pname, c);
1169 return 1;
1170 }
1171 switch (c) {
1172 case 't':
1173 got_target = true;
1174 target = atoi(v);
1175 break;
1176 case 'r':
1177 {
1178 char *sep = strchr(v, '-');
1179 got_range = true;
1180 if (sep) {
1181 rangemin = atoi(v);
1182 rangemax = atoi(sep+1);
1183 } else {
1184 rangemin = 0;
1185 rangemax = atoi(v);
1186 }
1187 }
1188 break;
1189 }
1190 }
1191 break;
1192 default:
1193 fprintf(stderr, "%s: option '-%c' not"
1194 " recognised\n", pname, c);
1195 return 1;
1196 }
1197 } else {
1198 if (nnumbers >= lenof(numbers)) {
1199 fprintf(stderr, "%s: internal limit of %d numbers exceeded\n",
1200 pname, (int)lenof(numbers));
1201 return 1;
1202 } else {
1203 numbers[nnumbers++] = atoi(p);
1204 }
1205 }
1206 }
1207
1208 if (!rules) {
1209 fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname);
1210 return 1;
1211 }
1212
1213 if (!nnumbers) {
1214 fprintf(stderr, "%s: no input numbers specified\n", pname);
1215 return 1;
1216 }
1217
1218 if (got_range) {
1219 if (got_target) {
1220 fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname);
1221 return 1;
1222 }
1223 if (rangemin >= rangemax) {
1224 fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax);
1225 return 1;
1226 }
1227 }
1228
1229 s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL),
1230 debug_bfs, multiple);
1231
1232 if (got_target) {
1233 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1234 REL234_LE, &start);
1235 if (!o)
1236 start = -1;
1237 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1238 REL234_GE, &limit);
1239 if (!o)
1240 limit = -1;
1241 assert(start != -1 || limit != -1);
1242 if (start == -1)
1243 start = limit;
1244 else if (limit == -1)
1245 limit = start;
1246 limit++;
1247 } else if (got_range) {
1248 if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp,
1249 REL234_GE, &start) ||
1250 !findrelpos234(s->outputtree, &rangemax, outputfindcmp,
1251 REL234_LE, &limit)) {
1252 printf("No solutions available in specified range %d-%d\n", rangemin, rangemax);
1253 return 1;
1254 }
1255 limit++;
1256 } else {
1257 start = 0;
1258 limit = count234(s->outputtree);
1259 }
1260
1261 for (i = start; i < limit; i++) {
1262 char buf[256];
1263
1264 o = index234(s->outputtree, i);
1265
1266 sprintf(buf, "%d", o->number);
1267
1268 if (pathcounts)
1269 sprintf(buf + strlen(buf), " [%d]", o->npaths);
1270
1271 if (got_target || verbose) {
1272 int j, npaths;
1273
1274 if (multiple)
1275 npaths = o->npaths;
1276 else
1277 npaths = 1;
1278
1279 for (j = 0; j < npaths; j++) {
1280 printf("%s = ", buf);
1281 print(j, s, o);
1282 putchar('\n');
1283 }
1284 } else {
1285 printf("%s\n", buf);
1286 }
1287 }
1288
1289 free_sets(s);
1290
1291 return 0;
1292}
1293
1294/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/path.c b/apps/plugins/puzzles/src/unfinished/path.c
new file mode 100644
index 0000000000..2515ed0b71
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/path.c
@@ -0,0 +1,866 @@
1/*
2 * Experimental grid generator for Nikoli's `Number Link' puzzle.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include "puzzles.h"
10
11/*
12 * 2005-07-08: This is currently a Path grid generator which will
13 * construct valid grids at a plausible speed. However, the grids
14 * are not of suitable quality to be used directly as puzzles.
15 *
16 * The basic strategy is to start with an empty grid, and
17 * repeatedly either (a) add a new path to it, or (b) extend one
18 * end of a path by one square in some direction and push other
19 * paths into new shapes in the process. The effect of this is that
20 * we are able to construct a set of paths which between them fill
21 * the entire grid.
22 *
23 * Quality issues: if we set the main loop to do (a) where possible
24 * and (b) only where necessary, we end up with a grid containing a
25 * few too many small paths, which therefore doesn't make for an
26 * interesting puzzle. If we reverse the priority so that we do (b)
27 * where possible and (a) only where necessary, we end up with some
28 * staggeringly interwoven grids with very very few separate paths,
29 * but the result of this is that there's invariably a solution
30 * other than the intended one which leaves many grid squares
31 * unfilled. There's also a separate problem which is that many
32 * grids have really boring and obvious paths in them, such as the
33 * entire bottom row of the grid being taken up by a single path.
34 *
35 * It's not impossible that a few tweaks might eliminate or reduce
36 * the incidence of boring paths, and might also find a happy
37 * medium between too many and too few. There remains the question
38 * of unique solutions, however. I fear there is no alternative but
39 * to write - somehow! - a solver.
40 *
41 * While I'm here, some notes on UI strategy for the parts of the
42 * puzzle implementation that _aren't_ the generator:
43 *
44 * - data model is to track connections between adjacent squares,
45 * so that you aren't limited to extending a path out from each
46 * number but can also mark sections of path which you know
47 * _will_ come in handy later.
48 *
49 * - user interface is to click in one square and drag to an
50 * adjacent one, thus creating a link between them. We can
51 * probably tolerate rapid mouse motion causing a drag directly
52 * to a square which is a rook move away, but any other rapid
53 * motion is ambiguous and probably the best option is to wait
54 * until the mouse returns to a square we know how to reach.
55 *
56 * - a drag causing the current path to backtrack has the effect
57 * of removing bits of it.
58 *
59 * - the UI should enforce at all times the constraint that at
60 * most two links can come into any square.
61 *
62 * - my Cunning Plan for actually implementing this: the game_ui
63 * contains a grid-sized array, which is copied from the current
64 * game_state on starting a drag. While a drag is active, the
65 * contents of the game_ui is adjusted with every mouse motion,
66 * and is displayed _in place_ of the game_state itself. On
67 * termination of a drag, the game_ui array is copied back into
68 * the new game_state (or rather, a string move is encoded which
69 * has precisely the set of link changes to cause that effect).
70 */
71
72/*
73 * 2020-05-11: some thoughts on a solver.
74 *
75 * Consider this example puzzle, from Wikipedia:
76 *
77 * ---4---
78 * -3--25-
79 * ---31--
80 * ---5---
81 * -------
82 * --1----
83 * 2---4--
84 *
85 * The kind of deduction that a human wants to make here is: which way
86 * does the path between the 4s go? In particular, does it go round
87 * the left of the W-shaped cluster of endpoints, or round the right
88 * of it? It's clear at a glance that it must go to the right, because
89 * _any_ path between the 4s that goes to the left of that cluster, no
90 * matter what detailed direction it takes, will disconnect the
91 * remaining grid squares into two components, with the two 2s not in
92 * the same component. So we immediately know that the path between
93 * the 4s _must_ go round the right-hand side of the grid.
94 *
95 * How do you model that global and topological reasoning in a
96 * computer?
97 *
98 * The most plausible idea I've seen so far is to use fundamental
99 * groups. The fundamental group of loops based at a given point in a
100 * space is a free group, under loop concatenation and up to homotopy,
101 * generated by the loops that go in each direction around each hole
102 * in the space. In this case, the 'holes' are clues, or connected
103 * groups of clues.
104 *
105 * So you might be able to enumerate all the homotopy classes of paths
106 * between (say) the two 4s as follows. Start with any old path
107 * between them (say, find the first one that breadth-first search
108 * will give you). Choose one of the 4s to regard as the base point
109 * (arbitrarily). Then breadth-first search among the space of _paths_
110 * by the following procedure. Given a candidate path, append to it
111 * each of the possible loops that starts from the base point,
112 * circumnavigates one clue cluster, and returns to the base point.
113 * The result will typically be a path that retraces its steps and
114 * self-intersects. Now adjust it homotopically so that it doesn't. If
115 * that can't be done, then we haven't generated a fresh candidate
116 * path; if it can, then we've got a new path that is not homotopic to
117 * any path we already had, so add it to our list and queue it up to
118 * become the starting point of this search later.
119 *
120 * The idea is that this should exhaustively enumerate, up to
121 * homotopy, the different ways in which the two 4s can connect to
122 * each other within the constraint that you have to actually fit the
123 * path non-self-intersectingly into this grid. Then you can keep a
124 * list of those homotopy classes in mind, and start ruling them out
125 * by techniques like the connectivity approach described above.
126 * Hopefully you end up narrowing down to few enough homotopy classes
127 * that you can deduce something concrete about actual squares of the
128 * grid - for example, here, that if the path between 4s has to go
129 * round the right, then we know some specific squares it must go
130 * through, so we can fill those in. And then, having filled in a
131 * piece of the middle of a path, you can now regard connecting the
132 * ultimate endpoints to that mid-section as two separate subproblems,
133 * so you've reduced to a simpler instance of the same puzzle.
134 *
135 * But I don't know whether all of this actually works. I more or less
136 * believe the process for enumerating elements of the free group; but
137 * I'm not as confident that when you find a group element that won't
138 * fit in the grid, you'll never have to consider its descendants in
139 * the BFS either. And I'm assuming that 'unwind the self-intersection
140 * homotopically' is a thing that can actually be turned into a
141 * sensible algorithm.
142 *
143 * --------
144 *
145 * Another thing that might be needed is to characterise _which_
146 * homotopy class a given path is in.
147 *
148 * For this I think it's sufficient to choose a collection of paths
149 * along the _edges_ of the square grid, each of which connects two of
150 * the holes in the grid (including the grid exterior, which counts as
151 * a huge hole), such that they form a spanning tree between the
152 * holes. Then assign each of those paths an orientation, so that
153 * crossing it in one direction counts as 'positive' and the other
154 * 'negative'. Now analyse a candidate path from one square to another
155 * by following it and noting down which of those paths it crosses in
156 * which direction, then simplifying the result like a free group word
157 * (i.e. adjacent + and - crossings of the same path cancel out).
158 *
159 * --------
160 *
161 * If we choose those paths to be of minimal length, then we can get
162 * an upper bound on the number of homotopy classes by observing that
163 * you can't traverse any of those barriers more times than will fit
164 * non-self-intersectingly in the grid. That might be an alternative
165 * method of bounding the search through the fundamental group to only
166 * finitely many possibilities.
167 */
168
169/*
170 * Standard notation for directions.
171 */
172#define L 0
173#define U 1
174#define R 2
175#define D 3
176#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0)
177#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0)
178
179/*
180 * Perform a breadth-first search over a grid of squares with the
181 * colour of square (X,Y) given by grid[Y*w+X]. The search begins
182 * at (x,y), and finds all squares which are the same colour as
183 * (x,y) and reachable from it by orthogonal moves. On return:
184 * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if
185 * unreachable or a different colour
186 * - the returned value is the number of reachable squares,
187 * including (x,y) itself
188 * - list[0] up to list[returned value - 1] list those squares, in
189 * increasing order of distance from (x,y) (and in arbitrary
190 * order within that).
191 */
192static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list)
193{
194 int i, j, c, listsize, listdone;
195
196 /*
197 * Start by clearing the output arrays.
198 */
199 for (i = 0; i < w*h; i++)
200 dist[i] = list[i] = -1;
201
202 /*
203 * Set up the initial list.
204 */
205 listsize = 1;
206 listdone = 0;
207 list[0] = y*w+x;
208 dist[y*w+x] = 0;
209 c = grid[y*w+x];
210
211 /*
212 * Repeatedly process a square and add any extra squares to the
213 * end of list.
214 */
215 while (listdone < listsize) {
216 i = list[listdone++];
217 y = i / w;
218 x = i % w;
219 for (j = 0; j < 4; j++) {
220 int xx, yy, ii;
221
222 xx = x + DX(j);
223 yy = y + DY(j);
224 ii = yy*w+xx;
225
226 if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
227 grid[ii] == c && dist[ii] == -1) {
228 dist[ii] = dist[i] + 1;
229 assert(listsize < w*h);
230 list[listsize++] = ii;
231 }
232 }
233 }
234
235 return listsize;
236}
237
238struct genctx {
239 int w, h;
240 int *grid, *sparegrid, *sparegrid2, *sparegrid3;
241 int *dist, *list;
242
243 int npaths, pathsize;
244 int *pathends, *sparepathends; /* 2*npaths entries */
245 int *pathspare; /* npaths entries */
246 int *extends; /* 8*npaths entries */
247};
248
249static struct genctx *new_genctx(int w, int h)
250{
251 struct genctx *ctx = snew(struct genctx);
252 ctx->w = w;
253 ctx->h = h;
254 ctx->grid = snewn(w * h, int);
255 ctx->sparegrid = snewn(w * h, int);
256 ctx->sparegrid2 = snewn(w * h, int);
257 ctx->sparegrid3 = snewn(w * h, int);
258 ctx->dist = snewn(w * h, int);
259 ctx->list = snewn(w * h, int);
260 ctx->npaths = ctx->pathsize = 0;
261 ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL;
262 return ctx;
263}
264
265static void free_genctx(struct genctx *ctx)
266{
267 sfree(ctx->grid);
268 sfree(ctx->sparegrid);
269 sfree(ctx->sparegrid2);
270 sfree(ctx->sparegrid3);
271 sfree(ctx->dist);
272 sfree(ctx->list);
273 sfree(ctx->pathends);
274 sfree(ctx->sparepathends);
275 sfree(ctx->pathspare);
276 sfree(ctx->extends);
277}
278
279static int newpath(struct genctx *ctx)
280{
281 int n;
282
283 n = ctx->npaths++;
284 if (ctx->npaths > ctx->pathsize) {
285 ctx->pathsize += 16;
286 ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int);
287 ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int);
288 ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int);
289 ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int);
290 }
291 return n;
292}
293
294static int is_endpoint(struct genctx *ctx, int x, int y)
295{
296 int w = ctx->w, h = ctx->h, c;
297
298 assert(x >= 0 && x < w && y >= 0 && y < h);
299
300 c = ctx->grid[y*w+x];
301 if (c < 0)
302 return false; /* empty square is not an endpoint! */
303 assert(c >= 0 && c < ctx->npaths);
304 if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x)
305 return true;
306 return false;
307}
308
309/*
310 * Tries to extend a path by one square in the given direction,
311 * pushing other paths around if necessary. Returns true on success
312 * or false on failure.
313 */
314static int extend_path(struct genctx *ctx, int path, int end, int direction)
315{
316 int w = ctx->w, h = ctx->h;
317 int x, y, xe, ye, cut;
318 int i, j, jp, n, first, last;
319
320 assert(path >= 0 && path < ctx->npaths);
321 assert(end == 0 || end == 1);
322
323 /*
324 * Find the endpoint of the path and the point we plan to
325 * extend it into.
326 */
327 y = ctx->pathends[path * 2 + end] / w;
328 x = ctx->pathends[path * 2 + end] % w;
329 assert(x >= 0 && x < w && y >= 0 && y < h);
330
331 xe = x + DX(direction);
332 ye = y + DY(direction);
333 if (xe < 0 || xe >= w || ye < 0 || ye >= h)
334 return false; /* could not extend in this direction */
335
336 /*
337 * We don't extend paths _directly_ into endpoints of other
338 * paths, although we don't mind too much if a knock-on effect
339 * of an extension is to push part of another path into a third
340 * path's endpoint.
341 */
342 if (is_endpoint(ctx, xe, ye))
343 return false;
344
345 /*
346 * We can't extend a path back the way it came.
347 */
348 if (ctx->grid[ye*w+xe] == path)
349 return false;
350
351 /*
352 * Paths may not double back on themselves. Check if the new
353 * point is adjacent to any point of this path other than (x,y).
354 */
355 for (j = 0; j < 4; j++) {
356 int xf, yf;
357
358 xf = xe + DX(j);
359 yf = ye + DY(j);
360
361 if (xf >= 0 && xf < w && yf >= 0 && yf < h &&
362 (xf != x || yf != y) && ctx->grid[yf*w+xf] == path)
363 return false;
364 }
365
366 /*
367 * Now we're convinced it's valid to _attempt_ the extension.
368 * It may still fail if we run out of space to push other paths
369 * into.
370 *
371 * So now we can set up our temporary data structures. We will
372 * need:
373 *
374 * - a spare copy of the grid on which to gradually move paths
375 * around (sparegrid)
376 *
377 * - a second spare copy with which to remember how paths
378 * looked just before being cut (sparegrid2). FIXME: is
379 * sparegrid2 necessary? right now it's never different from
380 * grid itself
381 *
382 * - a third spare copy with which to do the internal
383 * calculations involved in reconstituting a cut path
384 * (sparegrid3)
385 *
386 * - something to track which paths currently need
387 * reconstituting after being cut, and which have already
388 * been cut (pathspare)
389 *
390 * - a spare copy of pathends to store the altered states in
391 * (sparepathends)
392 */
393 memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int));
394 memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int));
395 memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int));
396 for (i = 0; i < ctx->npaths; i++)
397 ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */
398
399 /*
400 * Working in sparegrid, actually extend the path. If it cuts
401 * another, begin a loop in which we restore any cut path by
402 * moving it out of the way.
403 */
404 cut = ctx->sparegrid[ye*w+xe];
405 ctx->sparegrid[ye*w+xe] = path;
406 ctx->sparepathends[path*2+end] = ye*w+xe;
407 ctx->pathspare[path] = 2; /* this one is sacrosanct */
408 if (cut >= 0) {
409 assert(cut >= 0 && cut < ctx->npaths);
410 ctx->pathspare[cut] = 1; /* broken */
411
412 while (1) {
413 for (i = 0; i < ctx->npaths; i++)
414 if (ctx->pathspare[i] == 1)
415 break;
416 if (i == ctx->npaths)
417 break; /* we're done */
418
419 /*
420 * Path i needs restoring. So walk along its original
421 * track (as given in sparegrid2) and see where it's
422 * been cut. Where it has, surround the cut points in
423 * the same colour, without overwriting already-fixed
424 * paths.
425 */
426 memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int));
427 n = bfs(w, h, ctx->sparegrid2,
428 ctx->pathends[i*2] % w, ctx->pathends[i*2] / w,
429 ctx->dist, ctx->list);
430 first = last = -1;
431if (ctx->sparegrid3[ctx->pathends[i*2]] != i ||
432 ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return false;/* FIXME */
433 for (j = 0; j < n; j++) {
434 jp = ctx->list[j];
435 assert(ctx->dist[jp] == j);
436 assert(ctx->sparegrid2[jp] == i);
437
438 /*
439 * Wipe out the original path in sparegrid.
440 */
441 if (ctx->sparegrid[jp] == i)
442 ctx->sparegrid[jp] = -1;
443
444 /*
445 * Be prepared to shorten the path at either end if
446 * the endpoints have been stomped on.
447 */
448 if (ctx->sparegrid3[jp] == i) {
449 if (first < 0)
450 first = jp;
451 last = jp;
452 }
453
454 if (ctx->sparegrid3[jp] != i) {
455 int jx = jp % w, jy = jp / w;
456 int dx, dy;
457 for (dy = -1; dy <= +1; dy++)
458 for (dx = -1; dx <= +1; dx++) {
459 int newp, newv;
460 if (!dy && !dx)
461 continue; /* central square */
462 if (jx+dx < 0 || jx+dx >= w ||
463 jy+dy < 0 || jy+dy >= h)
464 continue; /* out of range */
465 newp = (jy+dy)*w+(jx+dx);
466 newv = ctx->sparegrid3[newp];
467 if (newv >= 0 && (newv == i ||
468 ctx->pathspare[newv] == 2))
469 continue; /* can't use this square */
470 ctx->sparegrid3[newp] = i;
471 }
472 }
473 }
474
475 if (first < 0 || last < 0)
476 return false; /* path is completely wiped out! */
477
478 /*
479 * Now we've covered sparegrid3 in possible squares for
480 * the new layout of path i. Find the actual layout
481 * we're going to use by bfs: we want the shortest path
482 * from one endpoint to the other.
483 */
484 n = bfs(w, h, ctx->sparegrid3, first % w, first / w,
485 ctx->dist, ctx->list);
486 if (ctx->dist[last] < 2) {
487 /*
488 * Either there is no way to get between the path's
489 * endpoints, or the remaining endpoints simply
490 * aren't far enough apart to make the path viable
491 * any more. This means the entire push operation
492 * has failed.
493 */
494 return false;
495 }
496
497 /*
498 * Write the new path into sparegrid. Also save the new
499 * endpoint locations, in case they've changed.
500 */
501 jp = last;
502 j = ctx->dist[jp];
503 while (1) {
504 int d;
505
506 if (ctx->sparegrid[jp] >= 0) {
507 if (ctx->pathspare[ctx->sparegrid[jp]] == 2)
508 return false; /* somehow we've hit a fixed path */
509 ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */
510 }
511 ctx->sparegrid[jp] = i;
512
513 if (j == 0)
514 break;
515
516 /*
517 * Now look at the neighbours of jp to find one
518 * which has dist[] one less.
519 */
520 for (d = 0; d < 4; d++) {
521 int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d);
522 if (jx >= 0 && jx < w && jy >= 0 && jy < w &&
523 ctx->dist[jy*w+jx] == j-1) {
524 jp = jy*w+jx;
525 j--;
526 break;
527 }
528 }
529 assert(d < 4);
530 }
531
532 ctx->sparepathends[i*2] = first;
533 ctx->sparepathends[i*2+1] = last;
534/* printf("new ends of path %d: %d,%d\n", i, first, last); */
535 ctx->pathspare[i] = 2; /* fixed */
536 }
537 }
538
539 /*
540 * If we got here, the extension was successful!
541 */
542 memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int));
543 memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int));
544 return true;
545}
546
547/*
548 * Tries to add a new path to the grid.
549 */
550static int add_path(struct genctx *ctx, random_state *rs)
551{
552 int w = ctx->w, h = ctx->h;
553 int i, ii, n;
554
555 /*
556 * Our strategy is:
557 * - randomly choose an empty square in the grid
558 * - do a BFS from that point to find a long path starting
559 * from it
560 * - if we run out of viable empty squares, return failure.
561 */
562
563 /*
564 * Use `sparegrid' to collect a list of empty squares.
565 */
566 n = 0;
567 for (i = 0; i < w*h; i++)
568 if (ctx->grid[i] == -1)
569 ctx->sparegrid[n++] = i;
570
571 /*
572 * Shuffle the grid.
573 */
574 for (i = n; i-- > 1 ;) {
575 int k = random_upto(rs, i+1);
576 if (k != i) {
577 int t = ctx->sparegrid[i];
578 ctx->sparegrid[i] = ctx->sparegrid[k];
579 ctx->sparegrid[k] = t;
580 }
581 }
582
583 /*
584 * Loop over it trying to add paths. This looks like a
585 * horrifying N^4 algorithm (that is, (w*h)^2), but I predict
586 * that in fact the worst case will very rarely arise because
587 * when there's lots of grid space an attempt will succeed very
588 * quickly.
589 */
590 for (ii = 0; ii < n; ii++) {
591 int i = ctx->sparegrid[ii];
592 int y = i / w, x = i % w, nsq;
593 int r, c, j;
594
595 /*
596 * BFS from here to find long paths.
597 */
598 nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list);
599
600 /*
601 * If there aren't any long enough, give up immediately.
602 */
603 assert(nsq > 0); /* must be the start square at least! */
604 if (ctx->dist[ctx->list[nsq-1]] < 3)
605 continue;
606
607 /*
608 * Find the first viable endpoint in ctx->list (i.e. the
609 * first point with distance at least three). I could
610 * binary-search for this, but that would be O(log N)
611 * whereas in fact I can get a constant time bound by just
612 * searching up from the start - after all, there can be at
613 * most 13 points at _less_ than distance 3 from the
614 * starting one!
615 */
616 for (j = 0; j < nsq; j++)
617 if (ctx->dist[ctx->list[j]] >= 3)
618 break;
619 assert(j < nsq); /* we tested above that there was one */
620
621 /*
622 * Now we know that any element of `list' between j and nsq
623 * would be valid in principle. However, we want a few long
624 * paths rather than many small ones, so select only those
625 * elements which are either the maximum length or one
626 * below it.
627 */
628 while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]])
629 j++;
630 r = j + random_upto(rs, nsq - j);
631 j = ctx->list[r];
632
633 /*
634 * And that's our endpoint. Mark the new path on the grid.
635 */
636 c = newpath(ctx);
637 ctx->pathends[c*2] = i;
638 ctx->pathends[c*2+1] = j;
639 ctx->grid[j] = c;
640 while (j != i) {
641 int d, np, index, pts[4];
642 np = 0;
643 for (d = 0; d < 4; d++) {
644 int xn = (j % w) + DX(d), yn = (j / w) + DY(d);
645 if (xn >= 0 && xn < w && yn >= 0 && yn < w &&
646 ctx->dist[yn*w+xn] == ctx->dist[j] - 1)
647 pts[np++] = yn*w+xn;
648 }
649 if (np > 1)
650 index = random_upto(rs, np);
651 else
652 index = 0;
653 j = pts[index];
654 ctx->grid[j] = c;
655 }
656
657 return true;
658 }
659
660 return false;
661}
662
663/*
664 * The main grid generation loop.
665 */
666static void gridgen_mainloop(struct genctx *ctx, random_state *rs)
667{
668 int w = ctx->w, h = ctx->h;
669 int i, n;
670
671 /*
672 * The generation algorithm doesn't always converge. Loop round
673 * until it does.
674 */
675 while (1) {
676 for (i = 0; i < w*h; i++)
677 ctx->grid[i] = -1;
678 ctx->npaths = 0;
679
680 while (1) {
681 /*
682 * See if the grid is full.
683 */
684 for (i = 0; i < w*h; i++)
685 if (ctx->grid[i] < 0)
686 break;
687 if (i == w*h)
688 return;
689
690#ifdef GENERATION_DIAGNOSTICS
691 {
692 int x, y;
693 for (y = 0; y < h; y++) {
694 printf("|");
695 for (x = 0; x < w; x++) {
696 if (ctx->grid[y*w+x] >= 0)
697 printf("%2d", ctx->grid[y*w+x]);
698 else
699 printf(" .");
700 }
701 printf(" |\n");
702 }
703 }
704#endif
705 /*
706 * Try adding a path.
707 */
708 if (add_path(ctx, rs)) {
709#ifdef GENERATION_DIAGNOSTICS
710 printf("added path\n");
711#endif
712 continue;
713 }
714
715 /*
716 * Try extending a path. First list all the possible
717 * extensions.
718 */
719 for (i = 0; i < ctx->npaths * 8; i++)
720 ctx->extends[i] = i;
721 n = i;
722
723 /*
724 * Then shuffle the list.
725 */
726 for (i = n; i-- > 1 ;) {
727 int k = random_upto(rs, i+1);
728 if (k != i) {
729 int t = ctx->extends[i];
730 ctx->extends[i] = ctx->extends[k];
731 ctx->extends[k] = t;
732 }
733 }
734
735 /*
736 * Now try each one in turn until one works.
737 */
738 for (i = 0; i < n; i++) {
739 int p, d, e;
740 p = ctx->extends[i];
741 d = p % 4;
742 p /= 4;
743 e = p % 2;
744 p /= 2;
745
746#ifdef GENERATION_DIAGNOSTICS
747 printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e,
748 ctx->pathends[p*2+e] % w,
749 ctx->pathends[p*2+e] / w, d);
750#endif
751 if (extend_path(ctx, p, e, d)) {
752#ifdef GENERATION_DIAGNOSTICS
753 printf("extended path %d end %d (%d,%d) in dir %d\n", p, e,
754 ctx->pathends[p*2+e] % w,
755 ctx->pathends[p*2+e] / w, d);
756#endif
757 break;
758 }
759 }
760
761 if (i < n)
762 continue;
763
764 break;
765 }
766 }
767}
768
769/*
770 * Wrapper function which deals with the boring bits such as
771 * removing the solution from the generated grid, shuffling the
772 * numeric labels and creating/disposing of the context structure.
773 */
774static int *gridgen(int w, int h, random_state *rs)
775{
776 struct genctx *ctx;
777 int *ret;
778 int i;
779
780 ctx = new_genctx(w, h);
781
782 gridgen_mainloop(ctx, rs);
783
784 /*
785 * There is likely to be an ordering bias in the numbers
786 * (longer paths on lower numbers due to there having been more
787 * grid space when laying them down). So we must shuffle the
788 * numbers. We use ctx->pathspare for this.
789 *
790 * This is also as good a time as any to shift to numbering
791 * from 1, for display to the user.
792 */
793 for (i = 0; i < ctx->npaths; i++)
794 ctx->pathspare[i] = i+1;
795 for (i = ctx->npaths; i-- > 1 ;) {
796 int k = random_upto(rs, i+1);
797 if (k != i) {
798 int t = ctx->pathspare[i];
799 ctx->pathspare[i] = ctx->pathspare[k];
800 ctx->pathspare[k] = t;
801 }
802 }
803
804 /* FIXME: remove this at some point! */
805 {
806 int y, x;
807 for (y = 0; y < h; y++) {
808 printf("|");
809 for (x = 0; x < w; x++) {
810 assert(ctx->grid[y*w+x] >= 0);
811 printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]);
812 }
813 printf(" |\n");
814 }
815 printf("\n");
816 }
817
818 /*
819 * Clear the grid, and write in just the endpoints.
820 */
821 for (i = 0; i < w*h; i++)
822 ctx->grid[i] = 0;
823 for (i = 0; i < ctx->npaths; i++) {
824 ctx->grid[ctx->pathends[i*2]] =
825 ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i];
826 }
827
828 ret = ctx->grid;
829 ctx->grid = NULL;
830
831 free_genctx(ctx);
832
833 return ret;
834}
835
836#ifdef TEST_GEN
837
838#define TEST_GENERAL
839
840int main(void)
841{
842 int w = 10, h = 8;
843 random_state *rs = random_new("12345", 5);
844 int x, y, i, *grid;
845
846 for (i = 0; i < 10; i++) {
847 grid = gridgen(w, h, rs);
848
849 for (y = 0; y < h; y++) {
850 printf("|");
851 for (x = 0; x < w; x++) {
852 if (grid[y*w+x] > 0)
853 printf("%2d", grid[y*w+x]);
854 else
855 printf(" .");
856 }
857 printf(" |\n");
858 }
859 printf("\n");
860
861 sfree(grid);
862 }
863
864 return 0;
865}
866#endif
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c
new file mode 100644
index 0000000000..6ca07252ad
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.c
@@ -0,0 +1,861 @@
1/*
2 * separate.c: Implementation of `Block Puzzle', a Japanese-only
3 * Nikoli puzzle seen at
4 * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
5 *
6 * It's difficult to be absolutely sure of the rules since online
7 * Japanese translators are so bad, but looking at the sample
8 * puzzle it seems fairly clear that the rules of this one are
9 * very simple. You have an mxn grid in which every square
10 * contains a letter, there are k distinct letters with k dividing
11 * mn, and every letter occurs the same number of times; your aim
12 * is to find a partition of the grid into disjoint k-ominoes such
13 * that each k-omino contains exactly one of each letter.
14 *
15 * (It may be that Nikoli always have m,n,k equal to one another.
16 * However, I don't see that that's critical to the puzzle; k|mn
17 * is the only really important constraint, and even that could
18 * probably be dispensed with if some squares were marked as
19 * unused.)
20 */
21
22/*
23 * Current status: only the solver/generator is yet written, and
24 * although working in principle it's _very_ slow. It generates
25 * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and
26 * 7x7n7 only with a serious strain. I haven't dared try it higher
27 * than that yet.
28 *
29 * One idea to speed it up is to implement more of the solver.
30 * Ideas I've so far had include:
31 *
32 * - Generalise the deduction currently expressed as `an
33 * undersized chain with only one direction to extend must take
34 * it'. More generally, the deduction should say `if all the
35 * possible k-ominoes containing a given chain also contain
36 * square x, then mark square x as part of that k-omino'.
37 * + For example, consider this case:
38 *
39 * a ? b This represents the top left of a board; the letters
40 * ? ? ? a,b,c do not represent the letters used in the puzzle,
41 * c ? ? but indicate that those three squares are known to be
42 * of different ominoes. Now if k >= 4, we can immediately
43 * deduce that the square midway between b and c belongs to the
44 * same omino as a, because there is no way we can make a 4-or-
45 * more-omino containing a which does not also contain that square.
46 * (Most easily seen by imagining cutting that square out of the
47 * grid; then, clearly, the omino containing a has only two
48 * squares to expand into, and needs at least three.)
49 *
50 * The key difficulty with this mode of reasoning is
51 * identifying such squares. I can't immediately think of a
52 * simple algorithm for finding them on a wholesale basis.
53 *
54 * - Bfs out from a chain looking for the letters it lacks. For
55 * example, in this situation (top three rows of a 7x7n7 grid):
56 *
57 * +-----------+-+
58 * |E-A-F-B-C D|D|
59 * +------- ||
60 * |E-C-G-D G|G E|
61 * +-+--- |
62 * |E|E G A B F A|
63 *
64 * In this situation we can be sure that the top left chain
65 * E-A-F-B-C does extend rightwards to the D, because there is
66 * no other D within reach of that chain. Note also that the
67 * bfs can skip squares which are known to belong to other
68 * ominoes than this one.
69 *
70 * (This deduction, I fear, should only be used in an
71 * emergency, because it relies on _all_ squares within range
72 * of the bfs having particular values and so using it during
73 * incremental generation rather nails down a lot of the grid.)
74 *
75 * It's conceivable that another thing we could do would be to
76 * increase the flexibility in the grid generator: instead of
77 * nailing down the _value_ of any square depended on, merely nail
78 * down its equivalence to other squares. Unfortunately this turns
79 * the letter-selection phase of generation into a general graph
80 * colouring problem (we must draw a graph with equivalence
81 * classes of squares as the vertices, and an edge between any two
82 * vertices representing equivalence classes which contain squares
83 * that share an omino, and then k-colour the result) and hence
84 * requires recursion, which bodes ill for something we're doing
85 * that many times per generation.
86 *
87 * I suppose a simple thing I could try would be tuning the retry
88 * count, just in case it's set too high or too low for efficient
89 * generation.
90 */
91
92#include <stdio.h>
93#include <stdlib.h>
94#include <string.h>
95#include <assert.h>
96#include <ctype.h>
97#ifdef NO_TGMATH_H
98# include <math.h>
99#else
100# include <tgmath.h>
101#endif
102
103#include "puzzles.h"
104
105enum {
106 COL_BACKGROUND,
107 NCOLOURS
108};
109
110struct game_params {
111 int w, h, k;
112};
113
114struct game_state {
115 int FIXME;
116};
117
118static game_params *default_params(void)
119{
120 game_params *ret = snew(game_params);
121
122 ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */
123
124 return ret;
125}
126
127static bool game_fetch_preset(int i, char **name, game_params **params)
128{
129 return false;
130}
131
132static void free_params(game_params *params)
133{
134 sfree(params);
135}
136
137static game_params *dup_params(const game_params *params)
138{
139 game_params *ret = snew(game_params);
140 *ret = *params; /* structure copy */
141 return ret;
142}
143
144static void decode_params(game_params *params, char const *string)
145{
146 params->w = params->h = params->k = atoi(string);
147 while (*string && isdigit((unsigned char)*string)) string++;
148 if (*string == 'x') {
149 string++;
150 params->h = atoi(string);
151 while (*string && isdigit((unsigned char)*string)) string++;
152 }
153 if (*string == 'n') {
154 string++;
155 params->k = atoi(string);
156 while (*string && isdigit((unsigned char)*string)) string++;
157 }
158}
159
160static char *encode_params(const game_params *params, bool full)
161{
162 char buf[256];
163 sprintf(buf, "%dx%dn%d", params->w, params->h, params->k);
164 return dupstr(buf);
165}
166
167static config_item *game_configure(const game_params *params)
168{
169 return NULL;
170}
171
172static game_params *custom_params(const config_item *cfg)
173{
174 return NULL;
175}
176
177static const char *validate_params(const game_params *params, bool full)
178{
179 return NULL;
180}
181
182/* ----------------------------------------------------------------------
183 * Solver and generator.
184 */
185
186struct solver_scratch {
187 int w, h, k;
188
189 /*
190 * Tracks connectedness between squares.
191 */
192 DSF *dsf;
193
194 /*
195 * size[dsf_canonify(dsf, yx)] tracks the size of the
196 * connected component containing yx.
197 */
198 int *size;
199
200 /*
201 * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not
202 * the connected component containing yx includes letter i. If
203 * the value is -1, it doesn't; otherwise its value is the
204 * index in the main grid of the square which contributes that
205 * letter to the component.
206 */
207 int *contents;
208
209 /*
210 * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)]
211 * tracks whether or not the connected components containing
212 * yx1 and yx2 are known to be distinct.
213 */
214 bool *disconnect;
215
216 /*
217 * Temporary space used only inside particular solver loops.
218 */
219 int *tmp;
220};
221
222static struct solver_scratch *solver_scratch_new(int w, int h, int k)
223{
224 int wh = w*h;
225 struct solver_scratch *sc = snew(struct solver_scratch);
226
227 sc->w = w;
228 sc->h = h;
229 sc->k = k;
230
231 sc->dsf = dsf_new(wh);
232 sc->size = snewn(wh, int);
233 sc->contents = snewn(wh * k, int);
234 sc->disconnect = snewn(wh*wh, bool);
235 sc->tmp = snewn(wh, int);
236
237 return sc;
238}
239
240static void solver_scratch_free(struct solver_scratch *sc)
241{
242 dsf_free(sc->dsf);
243 sfree(sc->size);
244 sfree(sc->contents);
245 sfree(sc->disconnect);
246 sfree(sc->tmp);
247 sfree(sc);
248}
249
250static void solver_connect(struct solver_scratch *sc, int yx1, int yx2)
251{
252 int w = sc->w, h = sc->h, k = sc->k;
253 int wh = w*h;
254 int i, yxnew;
255
256 yx1 = dsf_canonify(sc->dsf, yx1);
257 yx2 = dsf_canonify(sc->dsf, yx2);
258 assert(yx1 != yx2);
259
260 /*
261 * To connect two components together into a bigger one, we
262 * start by merging them in the dsf itself.
263 */
264 dsf_merge(sc->dsf, yx1, yx2);
265 yxnew = dsf_canonify(sc->dsf, yx2);
266
267 /*
268 * The size of the new component is the sum of the sizes of the
269 * old ones.
270 */
271 sc->size[yxnew] = sc->size[yx1] + sc->size[yx2];
272
273 /*
274 * The contents bitmap of the new component is the union of the
275 * contents of the old ones.
276 *
277 * Given two numbers at most one of which is not -1, we can
278 * find the other one by adding the two and adding 1; this
279 * will yield -1 if both were -1 to begin with, otherwise the
280 * other.
281 *
282 * (A neater approach would be to take their bitwise AND, but
283 * this is unfortunately not well-defined standard C when done
284 * to signed integers.)
285 */
286 for (i = 0; i < k; i++) {
287 assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0);
288 sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] +
289 sc->contents[yx2*k+i] + 1);
290 }
291
292 /*
293 * We must combine the rows _and_ the columns in the disconnect
294 * matrix.
295 */
296 for (i = 0; i < wh; i++)
297 sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] ||
298 sc->disconnect[yx2*wh+i]);
299 for (i = 0; i < wh; i++)
300 sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] ||
301 sc->disconnect[i*wh+yx2]);
302}
303
304static void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2)
305{
306 int w = sc->w, h = sc->h;
307 int wh = w*h;
308
309 yx1 = dsf_canonify(sc->dsf, yx1);
310 yx2 = dsf_canonify(sc->dsf, yx2);
311 assert(yx1 != yx2);
312 assert(!sc->disconnect[yx1*wh+yx2]);
313 assert(!sc->disconnect[yx2*wh+yx1]);
314
315 /*
316 * Mark the components as disconnected from each other in the
317 * disconnect matrix.
318 */
319 sc->disconnect[yx1*wh+yx2] = true;
320 sc->disconnect[yx2*wh+yx1] = true;
321}
322
323static void solver_init(struct solver_scratch *sc)
324{
325 int w = sc->w, h = sc->h;
326 int wh = w*h;
327 int i;
328
329 /*
330 * Set up most of the scratch space. We don't set up the
331 * contents array, however, because this will change if we
332 * adjust the letter arrangement and re-run the solver.
333 */
334 dsf_reinit(sc->dsf);
335 for (i = 0; i < wh; i++) sc->size[i] = 1;
336 memset(sc->disconnect, 0, wh*wh * sizeof(bool));
337}
338
339static int solver_attempt(struct solver_scratch *sc, const unsigned char *grid,
340 bool *gen_lock)
341{
342 int w = sc->w, h = sc->h, k = sc->k;
343 int wh = w*h;
344 int i, x, y;
345 bool done_something_overall = false;
346
347 /*
348 * Set up the contents array from the grid.
349 */
350 for (i = 0; i < wh*k; i++)
351 sc->contents[i] = -1;
352 for (i = 0; i < wh; i++)
353 sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i;
354
355 while (1) {
356 bool done_something = false;
357
358 /*
359 * Go over the grid looking for reasons to add to the
360 * disconnect matrix. We're after pairs of squares which:
361 *
362 * - are adjacent in the grid
363 * - belong to distinct dsf components
364 * - their components are not already marked as
365 * disconnected
366 * - their components share a letter in common.
367 */
368 for (y = 0; y < h; y++) {
369 for (x = 0; x < w; x++) {
370 int dir;
371 for (dir = 0; dir < 2; dir++) {
372 int x2 = x + dir, y2 = y + 1 - dir;
373 int yx = y*w+x, yx2 = y2*w+x2;
374
375 if (x2 >= w || y2 >= h)
376 continue; /* one square is outside the grid */
377
378 yx = dsf_canonify(sc->dsf, yx);
379 yx2 = dsf_canonify(sc->dsf, yx2);
380 if (yx == yx2)
381 continue; /* same dsf component */
382
383 if (sc->disconnect[yx*wh+yx2])
384 continue; /* already known disconnected */
385
386 for (i = 0; i < k; i++)
387 if (sc->contents[yx*k+i] >= 0 &&
388 sc->contents[yx2*k+i] >= 0)
389 break;
390 if (i == k)
391 continue; /* no letter in common */
392
393 /*
394 * We've found one. Mark yx and yx2 as
395 * disconnected from each other.
396 */
397#ifdef SOLVER_DIAGNOSTICS
398 printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i);
399#endif
400 solver_disconnect(sc, yx, yx2);
401 done_something = done_something_overall = true;
402
403 /*
404 * We have just made a deduction which hinges
405 * on two particular grid squares being the
406 * same. If we are feeding back to a generator
407 * loop, we must therefore mark those squares
408 * as fixed in the generator, so that future
409 * rearrangement of the grid will not break
410 * the information on which we have already
411 * based deductions.
412 */
413 if (gen_lock) {
414 gen_lock[sc->contents[yx*k+i]] = true;
415 gen_lock[sc->contents[yx2*k+i]] = true;
416 }
417 }
418 }
419 }
420
421 /*
422 * Now go over the grid looking for dsf components which
423 * are below maximum size and only have one way to extend,
424 * and extending them.
425 */
426 for (i = 0; i < wh; i++)
427 sc->tmp[i] = -1;
428 for (y = 0; y < h; y++) {
429 for (x = 0; x < w; x++) {
430 int yx = dsf_canonify(sc->dsf, y*w+x);
431 int dir;
432
433 if (sc->size[yx] == k)
434 continue;
435
436 for (dir = 0; dir < 4; dir++) {
437 int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0);
438 int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0);
439 int yx2, yx2c;
440
441 if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w)
442 continue;
443 yx2 = y2*w+x2;
444 yx2c = dsf_canonify(sc->dsf, yx2);
445
446 if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) {
447 /*
448 * Component yx can be extended into square
449 * yx2.
450 */
451 if (sc->tmp[yx] == -1)
452 sc->tmp[yx] = yx2;
453 else if (sc->tmp[yx] != yx2)
454 sc->tmp[yx] = -2; /* multiple choices found */
455 }
456 }
457 }
458 }
459 for (i = 0; i < wh; i++) {
460 if (sc->tmp[i] >= 0) {
461 /*
462 * Make sure we haven't connected the two already
463 * during this loop (which could happen if for
464 * _both_ components this was the only way to
465 * extend them).
466 */
467 if (dsf_canonify(sc->dsf, i) ==
468 dsf_canonify(sc->dsf, sc->tmp[i]))
469 continue;
470
471#ifdef SOLVER_DIAGNOSTICS
472 printf("Connecting %d and %d\n", i, sc->tmp[i]);
473#endif
474 solver_connect(sc, i, sc->tmp[i]);
475 done_something = done_something_overall = true;
476 break;
477 }
478 }
479
480 if (!done_something)
481 break;
482 }
483
484 /*
485 * Return 0 if we haven't made any progress; 1 if we've done
486 * something but not solved it completely; 2 if we've solved
487 * it completely.
488 */
489 for (i = 0; i < wh; i++)
490 if (sc->size[dsf_canonify(sc->dsf, i)] != k)
491 break;
492 if (i == wh)
493 return 2;
494 if (done_something_overall)
495 return 1;
496 return 0;
497}
498
499static unsigned char *generate(int w, int h, int k, random_state *rs)
500{
501 int wh = w*h;
502 int n = wh/k;
503 struct solver_scratch *sc;
504 unsigned char *grid;
505 unsigned char *shuffled;
506 int i, j, m, retries;
507 int *permutation;
508 bool *gen_lock;
509
510 sc = solver_scratch_new(w, h, k);
511 grid = snewn(wh, unsigned char);
512 shuffled = snewn(k, unsigned char);
513 permutation = snewn(wh, int);
514 gen_lock = snewn(wh, bool);
515
516 do {
517 DSF *dsf = divvy_rectangle(w, h, k, rs);
518
519 /*
520 * Go through the dsf and find the indices of all the
521 * squares involved in each omino, in a manner conducive
522 * to per-omino indexing. We set permutation[i*k+j] to be
523 * the index of the jth square (ordered arbitrarily) in
524 * omino i.
525 */
526 for (i = j = 0; i < wh; i++)
527 if (dsf_canonify(dsf, i) == i) {
528 sc->tmp[i] = j;
529 /*
530 * During this loop and the following one, we use
531 * the last element of each row of permutation[]
532 * as a counter of the number of indices so far
533 * placed in it. When we place the final index of
534 * an omino, that counter is overwritten, but that
535 * doesn't matter because we'll never use it
536 * again. Of course this depends critically on
537 * divvy_rectangle() having returned correct
538 * results, or else chaos would ensue.
539 */
540 permutation[j*k+k-1] = 0;
541 j++;
542 }
543 for (i = 0; i < wh; i++) {
544 j = sc->tmp[dsf_canonify(dsf, i)];
545 m = permutation[j*k+k-1]++;
546 permutation[j*k+m] = i;
547 }
548
549 /*
550 * Track which squares' letters we have already depended
551 * on for deductions. This is gradually updated by
552 * solver_attempt().
553 */
554 memset(gen_lock, 0, wh * sizeof(bool));
555
556 /*
557 * Now repeatedly fill the grid with letters, and attempt
558 * to solve it. If the solver makes progress but does not
559 * fail completely, then gen_lock will have been updated
560 * and we try again. On a complete failure, though, we
561 * have no option but to give up and abandon this set of
562 * ominoes.
563 */
564 solver_init(sc);
565 retries = k*k;
566 while (1) {
567 /*
568 * Fill the grid with letters. We can safely use
569 * sc->tmp to hold the set of letters required at each
570 * stage, since it's at least size k and is currently
571 * unused.
572 */
573 for (i = 0; i < n; i++) {
574 /*
575 * First, determine the set of letters already
576 * placed in this omino by gen_lock.
577 */
578 for (j = 0; j < k; j++)
579 sc->tmp[j] = j;
580 for (j = 0; j < k; j++) {
581 int index = permutation[i*k+j];
582 int letter = grid[index];
583 if (gen_lock[index])
584 sc->tmp[letter] = -1;
585 }
586 /*
587 * Now collect together all the remaining letters
588 * and randomly shuffle them.
589 */
590 for (j = m = 0; j < k; j++)
591 if (sc->tmp[j] >= 0)
592 sc->tmp[m++] = sc->tmp[j];
593 shuffle(sc->tmp, m, sizeof(*sc->tmp), rs);
594 /*
595 * Finally, write the shuffled letters into the
596 * grid.
597 */
598 for (j = 0; j < k; j++) {
599 int index = permutation[i*k+j];
600 if (!gen_lock[index])
601 grid[index] = sc->tmp[--m];
602 }
603 assert(m == 0);
604 }
605
606 /*
607 * Now we have a candidate grid. Attempt to progress
608 * the solution.
609 */
610 m = solver_attempt(sc, grid, gen_lock);
611 if (m == 2 || /* success */
612 (m == 0 && retries-- <= 0)) /* failure */
613 break;
614 if (m == 1)
615 retries = k*k; /* reset this counter, and continue */
616 }
617
618 dsf_free(dsf);
619 } while (m == 0);
620
621 sfree(gen_lock);
622 sfree(permutation);
623 sfree(shuffled);
624 solver_scratch_free(sc);
625
626 return grid;
627}
628
629/* ----------------------------------------------------------------------
630 * End of solver/generator code.
631 */
632
633static char *new_game_desc(const game_params *params, random_state *rs,
634 char **aux, bool interactive)
635{
636 int w = params->w, h = params->h, wh = w*h, k = params->k;
637 unsigned char *grid;
638 char *desc;
639 int i;
640
641 grid = generate(w, h, k, rs);
642
643 desc = snewn(wh+1, char);
644 for (i = 0; i < wh; i++)
645 desc[i] = 'A' + grid[i];
646 desc[wh] = '\0';
647
648 sfree(grid);
649
650 return desc;
651}
652
653static const char *validate_desc(const game_params *params, const char *desc)
654{
655 return NULL;
656}
657
658static game_state *new_game(midend *me, const game_params *params,
659 const char *desc)
660{
661 game_state *state = snew(game_state);
662
663 state->FIXME = 0;
664
665 return state;
666}
667
668static game_state *dup_game(const game_state *state)
669{
670 game_state *ret = snew(game_state);
671
672 ret->FIXME = state->FIXME;
673
674 return ret;
675}
676
677static void free_game(game_state *state)
678{
679 sfree(state);
680}
681
682static char *solve_game(const game_state *state, const game_state *currstate,
683 const char *aux, const char **error)
684{
685 return NULL;
686}
687
688static bool game_can_format_as_text_now(const game_params *params)
689{
690 return true;
691}
692
693static char *game_text_format(const game_state *state)
694{
695 return NULL;
696}
697
698static game_ui *new_ui(const game_state *state)
699{
700 return NULL;
701}
702
703static void free_ui(game_ui *ui)
704{
705}
706
707static void game_changed_state(game_ui *ui, const game_state *oldstate,
708 const game_state *newstate)
709{
710}
711
712struct game_drawstate {
713 int tilesize;
714 int FIXME;
715};
716
717static char *interpret_move(const game_state *state, game_ui *ui,
718 const game_drawstate *ds,
719 int x, int y, int button)
720{
721 return NULL;
722}
723
724static game_state *execute_move(const game_state *state, const char *move)
725{
726 return NULL;
727}
728
729/* ----------------------------------------------------------------------
730 * Drawing routines.
731 */
732
733static void game_compute_size(const game_params *params, int tilesize,
734 const game_ui *ui, int *x, int *y)
735{
736 *x = *y = 10 * tilesize; /* FIXME */
737}
738
739static void game_set_size(drawing *dr, game_drawstate *ds,
740 const game_params *params, int tilesize)
741{
742 ds->tilesize = tilesize;
743}
744
745static float *game_colours(frontend *fe, int *ncolours)
746{
747 float *ret = snewn(3 * NCOLOURS, float);
748
749 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
750
751 *ncolours = NCOLOURS;
752 return ret;
753}
754
755static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
756{
757 struct game_drawstate *ds = snew(struct game_drawstate);
758
759 ds->tilesize = 0;
760 ds->FIXME = 0;
761
762 return ds;
763}
764
765static void game_free_drawstate(drawing *dr, game_drawstate *ds)
766{
767 sfree(ds);
768}
769
770static void game_redraw(drawing *dr, game_drawstate *ds,
771 const game_state *oldstate, const game_state *state,
772 int dir, const game_ui *ui,
773 float animtime, float flashtime)
774{
775}
776
777static float game_anim_length(const game_state *oldstate,
778 const game_state *newstate, int dir, game_ui *ui)
779{
780 return 0.0F;
781}
782
783static float game_flash_length(const game_state *oldstate,
784 const game_state *newstate, int dir, game_ui *ui)
785{
786 return 0.0F;
787}
788
789static void game_get_cursor_location(const game_ui *ui,
790 const game_drawstate *ds,
791 const game_state *state,
792 const game_params *params,
793 int *x, int *y, int *w, int *h)
794{
795}
796
797static int game_status(const game_state *state)
798{
799 return 0;
800}
801
802static bool game_timing_state(const game_state *state, game_ui *ui)
803{
804 return true;
805}
806
807static void game_print_size(const game_params *params, const game_ui *ui,
808 float *x, float *y)
809{
810}
811
812static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
813 int tilesize)
814{
815}
816
817#ifdef COMBINED
818#define thegame separate
819#endif
820
821const struct game thegame = {
822 "Separate", NULL, NULL,
823 default_params,
824 game_fetch_preset, NULL,
825 decode_params,
826 encode_params,
827 free_params,
828 dup_params,
829 false, game_configure, custom_params,
830 validate_params,
831 new_game_desc,
832 validate_desc,
833 new_game,
834 dup_game,
835 free_game,
836 false, solve_game,
837 false, game_can_format_as_text_now, game_text_format,
838 NULL, NULL, /* get_prefs, set_prefs */
839 new_ui,
840 free_ui,
841 NULL, /* encode_ui */
842 NULL, /* decode_ui */
843 NULL, /* game_request_keys */
844 game_changed_state,
845 NULL, /* current_key_label */
846 interpret_move,
847 execute_move,
848 20 /* FIXME */, game_compute_size, game_set_size,
849 game_colours,
850 game_new_drawstate,
851 game_free_drawstate,
852 game_redraw,
853 game_anim_length,
854 game_flash_length,
855 game_get_cursor_location,
856 game_status,
857 false, false, game_print_size, game_print,
858 false, /* wants_statusbar */
859 false, game_timing_state,
860 0, /* flags */
861};
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c
new file mode 100644
index 0000000000..4c4d98943e
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.c
@@ -0,0 +1,2444 @@
1/*
2 * slide.c: Implementation of the block-sliding puzzle `Klotski'.
3 */
4
5/*
6 * TODO:
7 *
8 * - Improve the generator.
9 * * actually, we seem to be mostly sensible already now. I
10 * want more choice over the type of main block and location
11 * of the exit/target, and I think I probably ought to give
12 * up on compactness and just bite the bullet and have the
13 * target area right outside the main wall, but mostly I
14 * think it's OK.
15 * * the move limit tends to make the game _slower_ to
16 * generate, which is odd. Perhaps investigate why.
17 *
18 * - Improve the graphics.
19 * * All the colours are a bit wishy-washy. _Some_ dark
20 * colours would surely not be excessive? Probably darken
21 * the tiles, the walls and the main block, and leave the
22 * target marker pale.
23 * * The cattle grid effect is still disgusting. Think of
24 * something completely different.
25 * * The highlight for next-piece-to-move in the solver is
26 * excessive, and the shadow blends in too well with the
27 * piece lowlights. Adjust both.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34#include <ctype.h>
35#ifdef NO_TGMATH_H
36# include <math.h>
37#else
38# include <tgmath.h>
39#endif
40
41#include "puzzles.h"
42#include "tree234.h"
43
44/*
45 * The implementation of this game revolves around the insight
46 * which makes an exhaustive-search solver feasible: although
47 * there are many blocks which can be rearranged in many ways, any
48 * two blocks of the same shape are _indistinguishable_ and hence
49 * the number of _distinct_ board layouts is generally much
50 * smaller. So we adopt a representation for board layouts which
51 * is inherently canonical, i.e. there are no two distinct
52 * representations which encode indistinguishable layouts.
53 *
54 * The way we do this is to encode each square of the board, in
55 * the normal left-to-right top-to-bottom order, as being one of
56 * the following things:
57 * - the first square (in the given order) of a block (`anchor')
58 * - special case of the above: the anchor for the _main_ block
59 * (i.e. the one which the aim of the game is to get to the
60 * target position)
61 * - a subsequent square of a block whose previous square was N
62 * squares ago
63 * - an impassable wall
64 *
65 * (We also separately store data about which board positions are
66 * forcefields only passable by the main block. We can't encode
67 * that in the main board data, because then the main block would
68 * destroy forcefields as it went over them.)
69 *
70 * Hence, for example, a 2x2 square block would be encoded as
71 * ANCHOR, followed by DIST(1), and w-2 squares later on there
72 * would be DIST(w-1) followed by DIST(1). So if you start at the
73 * last of those squares, the DIST numbers give you a linked list
74 * pointing back through all the other squares in the same block.
75 *
76 * So the solver simply does a bfs over all reachable positions,
77 * encoding them in this format and storing them in a tree234 to
78 * ensure it doesn't ever revisit an already-analysed position.
79 */
80
81enum {
82 /*
83 * The colours are arranged here so that every base colour is
84 * directly followed by its highlight colour and then its
85 * lowlight colour. Do not break this, or draw_tile() will get
86 * confused.
87 */
88 COL_BACKGROUND,
89 COL_HIGHLIGHT,
90 COL_LOWLIGHT,
91 COL_DRAGGING,
92 COL_DRAGGING_HIGHLIGHT,
93 COL_DRAGGING_LOWLIGHT,
94 COL_MAIN,
95 COL_MAIN_HIGHLIGHT,
96 COL_MAIN_LOWLIGHT,
97 COL_MAIN_DRAGGING,
98 COL_MAIN_DRAGGING_HIGHLIGHT,
99 COL_MAIN_DRAGGING_LOWLIGHT,
100 COL_TARGET,
101 COL_TARGET_HIGHLIGHT,
102 COL_TARGET_LOWLIGHT,
103 NCOLOURS
104};
105
106/*
107 * Board layout is a simple array of bytes. Each byte holds:
108 */
109#define ANCHOR 255 /* top-left-most square of some piece */
110#define MAINANCHOR 254 /* anchor of _main_ piece */
111#define EMPTY 253 /* empty square */
112#define WALL 252 /* immovable wall */
113#define MAXDIST 251
114/* all other values indicate distance back to previous square of same block */
115#define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 )
116#define DIST(x) (x)
117#define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR )
118#define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) )
119
120/*
121 * MAXDIST is the largest DIST value we can encode. This must
122 * therefore also be the maximum puzzle width in theory (although
123 * solver running time will dictate a much smaller limit in
124 * practice).
125 */
126#define MAXWID MAXDIST
127
128struct game_params {
129 int w, h;
130 int maxmoves;
131};
132
133struct game_immutable_state {
134 int refcount;
135 bool *forcefield;
136};
137
138struct game_solution {
139 int nmoves;
140 int *moves; /* just like from solve_board() */
141 int refcount;
142};
143
144struct game_state {
145 int w, h;
146 unsigned char *board;
147 int tx, ty; /* target coords for MAINANCHOR */
148 int minmoves; /* for display only */
149 int lastmoved, lastmoved_pos; /* for move counting */
150 int movecount;
151 int completed;
152 bool cheated;
153 struct game_immutable_state *imm;
154 struct game_solution *soln;
155 int soln_index;
156};
157
158static game_params *default_params(void)
159{
160 game_params *ret = snew(game_params);
161
162 ret->w = 7;
163 ret->h = 6;
164 ret->maxmoves = 40;
165
166 return ret;
167}
168
169static const struct game_params slide_presets[] = {
170 {7, 6, 25},
171 {7, 6, -1},
172 {8, 6, -1},
173};
174
175static bool game_fetch_preset(int i, char **name, game_params **params)
176{
177 game_params *ret;
178 char str[80];
179
180 if (i < 0 || i >= lenof(slide_presets))
181 return false;
182
183 ret = snew(game_params);
184 *ret = slide_presets[i];
185
186 sprintf(str, "%dx%d", ret->w, ret->h);
187 if (ret->maxmoves >= 0)
188 sprintf(str + strlen(str), ", max %d moves", ret->maxmoves);
189 else
190 sprintf(str + strlen(str), ", no move limit");
191
192 *name = dupstr(str);
193 *params = ret;
194 return true;
195}
196
197static void free_params(game_params *params)
198{
199 sfree(params);
200}
201
202static game_params *dup_params(const game_params *params)
203{
204 game_params *ret = snew(game_params);
205 *ret = *params; /* structure copy */
206 return ret;
207}
208
209static void decode_params(game_params *params, char const *string)
210{
211 params->w = params->h = atoi(string);
212 while (*string && isdigit((unsigned char)*string)) string++;
213 if (*string == 'x') {
214 string++;
215 params->h = atoi(string);
216 while (*string && isdigit((unsigned char)*string)) string++;
217 }
218 if (*string == 'm') {
219 string++;
220 params->maxmoves = atoi(string);
221 while (*string && isdigit((unsigned char)*string)) string++;
222 } else if (*string == 'u') {
223 string++;
224 params->maxmoves = -1;
225 }
226}
227
228static char *encode_params(const game_params *params, bool full)
229{
230 char data[256];
231
232 sprintf(data, "%dx%d", params->w, params->h);
233 if (params->maxmoves >= 0)
234 sprintf(data + strlen(data), "m%d", params->maxmoves);
235 else
236 sprintf(data + strlen(data), "u");
237
238 return dupstr(data);
239}
240
241static config_item *game_configure(const game_params *params)
242{
243 config_item *ret;
244 char buf[80];
245
246 ret = snewn(4, config_item);
247
248 ret[0].name = "Width";
249 ret[0].type = C_STRING;
250 sprintf(buf, "%d", params->w);
251 ret[0].u.string.sval = dupstr(buf);
252
253 ret[1].name = "Height";
254 ret[1].type = C_STRING;
255 sprintf(buf, "%d", params->h);
256 ret[1].u.string.sval = dupstr(buf);
257
258 ret[2].name = "Solution length limit";
259 ret[2].type = C_STRING;
260 sprintf(buf, "%d", params->maxmoves);
261 ret[2].u.string.sval = dupstr(buf);
262
263 ret[3].name = NULL;
264 ret[3].type = C_END;
265
266 return ret;
267}
268
269static game_params *custom_params(const config_item *cfg)
270{
271 game_params *ret = snew(game_params);
272
273 ret->w = atoi(cfg[0].u.string.sval);
274 ret->h = atoi(cfg[1].u.string.sval);
275 ret->maxmoves = atoi(cfg[2].u.string.sval);
276
277 return ret;
278}
279
280static const char *validate_params(const game_params *params, bool full)
281{
282 if (params->w > MAXWID)
283 return "Width must be at most " STR(MAXWID);
284
285 if (params->w < 5)
286 return "Width must be at least 5";
287 if (params->h < 4)
288 return "Height must be at least 4";
289
290 return NULL;
291}
292
293static char *board_text_format(int w, int h, unsigned char *data,
294 bool *forcefield)
295{
296 int wh = w*h;
297 DSF *dsf = dsf_new(wh);
298 int i, x, y;
299 int retpos, retlen = (w*2+2)*(h*2+1)+1;
300 char *ret = snewn(retlen, char);
301
302 for (i = 0; i < wh; i++)
303 if (ISDIST(data[i]))
304 dsf_merge(dsf, i - data[i], i);
305 retpos = 0;
306 for (y = 0; y < 2*h+1; y++) {
307 for (x = 0; x < 2*w+1; x++) {
308 int v;
309 int i = (y/2)*w+(x/2);
310
311#define dtype(i) (ISBLOCK(data[i]) ? \
312 dsf_canonify(dsf, i) : data[i])
313#define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \
314 data[t] == MAINANCHOR ? '*' : '%')
315
316 if (y % 2 && x % 2) {
317 int j = dtype(i);
318 v = dchar(j);
319 } else if (y % 2 && !(x % 2)) {
320 int j1 = (x > 0 ? dtype(i-1) : -1);
321 int j2 = (x < 2*w ? dtype(i) : -1);
322 if (j1 != j2)
323 v = '|';
324 else
325 v = dchar(j1);
326 } else if (!(y % 2) && (x % 2)) {
327 int j1 = (y > 0 ? dtype(i-w) : -1);
328 int j2 = (y < 2*h ? dtype(i) : -1);
329 if (j1 != j2)
330 v = '-';
331 else
332 v = dchar(j1);
333 } else {
334 int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1);
335 int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1);
336 int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1);
337 int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1);
338 if (j1 == j2 && j2 == j3 && j3 == j4)
339 v = dchar(j1);
340 else if (j1 == j2 && j3 == j4)
341 v = '|';
342 else if (j1 == j3 && j2 == j4)
343 v = '-';
344 else
345 v = '+';
346 }
347
348 assert(retpos < retlen);
349 ret[retpos++] = v;
350 }
351 assert(retpos < retlen);
352 ret[retpos++] = '\n';
353 }
354 assert(retpos < retlen);
355 ret[retpos++] = '\0';
356 assert(retpos == retlen);
357
358 return ret;
359}
360
361/* ----------------------------------------------------------------------
362 * Solver.
363 */
364
365/*
366 * During solver execution, the set of visited board positions is
367 * stored as a tree234 of the following structures. `w', `h' and
368 * `data' are obvious in meaning; `dist' represents the minimum
369 * distance to reach this position from the starting point.
370 *
371 * `prev' links each board to the board position from which it was
372 * most efficiently derived.
373 */
374struct board {
375 int w, h;
376 int dist;
377 struct board *prev;
378 unsigned char *data;
379};
380
381static int boardcmp(void *av, void *bv)
382{
383 struct board *a = (struct board *)av;
384 struct board *b = (struct board *)bv;
385 return memcmp(a->data, b->data, a->w * a->h);
386}
387
388static struct board *newboard(int w, int h, unsigned char *data)
389{
390 struct board *b = malloc(sizeof(struct board) + w*h);
391 b->data = (unsigned char *)b + sizeof(struct board);
392 memcpy(b->data, data, w*h);
393 b->w = w;
394 b->h = h;
395 b->dist = -1;
396 b->prev = NULL;
397 return b;
398}
399
400/*
401 * The actual solver. Given a board, attempt to find the minimum
402 * length of move sequence which moves MAINANCHOR to (tx,ty), or
403 * -1 if no solution exists. Returns that minimum length.
404 *
405 * Also, if `moveout' is provided, writes out the moves in the
406 * form of a sequence of pairs of integers indicating the source
407 * and destination points of the anchor of the moved piece in each
408 * move. Exactly twice as many integers are written as the number
409 * returned from solve_board(), and `moveout' receives an int *
410 * which is a pointer to a dynamically allocated array.
411 */
412static int solve_board(int w, int h, unsigned char *board,
413 bool *forcefield, int tx, int ty,
414 int movelimit, int **moveout)
415{
416 int wh = w*h;
417 struct board *b, *b2, *b3;
418 int *next, *which;
419 bool *anchors, *movereached;
420 int *movequeue, mqhead, mqtail;
421 tree234 *sorted, *queue;
422 int i, j, dir;
423 int qlen, lastdist;
424 int ret;
425
426#ifdef SOLVER_DIAGNOSTICS
427 {
428 char *t = board_text_format(w, h, board);
429 for (i = 0; i < h; i++) {
430 for (j = 0; j < w; j++) {
431 int c = board[i*w+j];
432 if (ISDIST(c))
433 printf("D%-3d", c);
434 else if (c == MAINANCHOR)
435 printf("M ");
436 else if (c == ANCHOR)
437 printf("A ");
438 else if (c == WALL)
439 printf("W ");
440 else if (c == EMPTY)
441 printf("E ");
442 }
443 printf("\n");
444 }
445
446 printf("Starting solver for:\n%s\n", t);
447 sfree(t);
448 }
449#endif
450
451 sorted = newtree234(boardcmp);
452 queue = newtree234(NULL);
453
454 b = newboard(w, h, board);
455 b->dist = 0;
456 add234(sorted, b);
457 addpos234(queue, b, 0);
458 qlen = 1;
459
460 next = snewn(wh, int);
461 anchors = snewn(wh, bool);
462 which = snewn(wh, int);
463 movereached = snewn(wh, bool);
464 movequeue = snewn(wh, int);
465 lastdist = -1;
466
467 while ((b = delpos234(queue, 0)) != NULL) {
468 qlen--;
469 if (movelimit >= 0 && b->dist >= movelimit) {
470 /*
471 * The problem is not soluble in under `movelimit'
472 * moves, so we can quit right now.
473 */
474 b2 = NULL;
475 goto done;
476 }
477 if (b->dist != lastdist) {
478#ifdef SOLVER_DIAGNOSTICS
479 printf("dist %d (%d)\n", b->dist, count234(sorted));
480#endif
481 lastdist = b->dist;
482 }
483 /*
484 * Find all the anchors and form a linked list of the
485 * squares within each block.
486 */
487 for (i = 0; i < wh; i++) {
488 next[i] = -1;
489 anchors[i] = false;
490 which[i] = -1;
491 if (ISANCHOR(b->data[i])) {
492 anchors[i] = true;
493 which[i] = i;
494 } else if (ISDIST(b->data[i])) {
495 j = i - b->data[i];
496 next[j] = i;
497 which[i] = which[j];
498 }
499 }
500
501 /*
502 * For each anchor, do an array-based BFS to find all the
503 * places we can slide it to.
504 */
505 for (i = 0; i < wh; i++) {
506 if (!anchors[i])
507 continue;
508
509 mqhead = mqtail = 0;
510 for (j = 0; j < wh; j++)
511 movereached[j] = false;
512 movequeue[mqtail++] = i;
513 while (mqhead < mqtail) {
514 int pos = movequeue[mqhead++];
515
516 /*
517 * Try to move in each direction from here.
518 */
519 for (dir = 0; dir < 4; dir++) {
520 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
521 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
522 int offset = dy*w + dx;
523 int newpos = pos + offset;
524 int d = newpos - i;
525
526 /*
527 * For each square involved in this block,
528 * check to see if the square d spaces away
529 * from it is either empty or part of the same
530 * block.
531 */
532 for (j = i; j >= 0; j = next[j]) {
533 int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx;
534 if (jy >= 0 && jy < h && jx >= 0 && jx < w &&
535 ((b->data[j+d] == EMPTY || which[j+d] == i) &&
536 (b->data[i] == MAINANCHOR || !forcefield[j+d])))
537 /* ok */;
538 else
539 break;
540 }
541 if (j >= 0)
542 continue; /* this direction wasn't feasible */
543
544 /*
545 * If we've already tried moving this piece
546 * here, leave it.
547 */
548 if (movereached[newpos])
549 continue;
550 movereached[newpos] = true;
551 movequeue[mqtail++] = newpos;
552
553 /*
554 * We have a viable move. Make it.
555 */
556 b2 = newboard(w, h, b->data);
557 for (j = i; j >= 0; j = next[j])
558 b2->data[j] = EMPTY;
559 for (j = i; j >= 0; j = next[j])
560 b2->data[j+d] = b->data[j];
561
562 b3 = add234(sorted, b2);
563 if (b3 != b2) {
564 sfree(b2); /* we already got one */
565 } else {
566 b2->dist = b->dist + 1;
567 b2->prev = b;
568 addpos234(queue, b2, qlen++);
569 if (b2->data[ty*w+tx] == MAINANCHOR)
570 goto done; /* search completed! */
571 }
572 }
573 }
574 }
575 }
576 b2 = NULL;
577
578 done:
579
580 if (b2) {
581 ret = b2->dist;
582 if (moveout) {
583 /*
584 * Now b2 represents the solved position. Backtrack to
585 * output the solution.
586 */
587 *moveout = snewn(ret * 2, int);
588 j = ret * 2;
589
590 while (b2->prev) {
591 int from = -1, to = -1;
592
593 b = b2->prev;
594
595 /*
596 * Scan b and b2 to find out which piece has
597 * moved.
598 */
599 for (i = 0; i < wh; i++) {
600 if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) {
601 assert(from == -1);
602 from = i;
603 } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){
604 assert(to == -1);
605 to = i;
606 }
607 }
608
609 assert(from >= 0 && to >= 0);
610 assert(j >= 2);
611 (*moveout)[--j] = to;
612 (*moveout)[--j] = from;
613
614 b2 = b;
615 }
616 assert(j == 0);
617 }
618 } else {
619 ret = -1; /* no solution */
620 if (moveout)
621 *moveout = NULL;
622 }
623
624 freetree234(queue);
625
626 while ((b = delpos234(sorted, 0)) != NULL)
627 sfree(b);
628 freetree234(sorted);
629
630 sfree(next);
631 sfree(anchors);
632 sfree(movereached);
633 sfree(movequeue);
634 sfree(which);
635
636 return ret;
637}
638
639/* ----------------------------------------------------------------------
640 * Random board generation.
641 */
642
643static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves,
644 random_state *rs, unsigned char **rboard,
645 bool **rforcefield, int movelimit)
646{
647 int wh = w*h;
648 unsigned char *board, *board2;
649 bool *forcefield;
650 bool *tried_merge;
651 DSF *dsf;
652 int *list, nlist, pos;
653 int tx, ty;
654 int i, j;
655 int moves = 0; /* placate optimiser */
656
657 /*
658 * Set up a board and fill it with singletons, except for a
659 * border of walls.
660 */
661 board = snewn(wh, unsigned char);
662 forcefield = snewn(wh, bool);
663 board2 = snewn(wh, unsigned char);
664 memset(board, ANCHOR, wh);
665 memset(forcefield, 0, wh * sizeof(bool));
666 for (i = 0; i < w; i++)
667 board[i] = board[i+w*(h-1)] = WALL;
668 for (i = 0; i < h; i++)
669 board[i*w] = board[i*w+(w-1)] = WALL;
670
671 tried_merge = snewn(wh * wh, bool);
672 memset(tried_merge, 0, wh*wh * sizeof(bool));
673 dsf = dsf_new(wh);
674
675 /*
676 * Invent a main piece at one extreme. (FIXME: vary the
677 * extreme, and the piece.)
678 */
679 board[w+1] = MAINANCHOR;
680 board[w+2] = DIST(1);
681 board[w*2+1] = DIST(w-1);
682 board[w*2+2] = DIST(1);
683
684 /*
685 * Invent a target position. (FIXME: vary this too.)
686 */
687 tx = w-2;
688 ty = h-3;
689 forcefield[ty*w+tx+1] = true;
690 forcefield[(ty+1)*w+tx+1] = true;
691 board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY;
692
693 /*
694 * Gradually remove singletons until the game becomes soluble.
695 */
696 for (j = w; j-- > 0 ;)
697 for (i = h; i-- > 0 ;)
698 if (board[i*w+j] == ANCHOR) {
699 /*
700 * See if the board is already soluble.
701 */
702 if ((moves = solve_board(w, h, board, forcefield,
703 tx, ty, movelimit, NULL)) >= 0)
704 goto soluble;
705
706 /*
707 * Otherwise, remove this piece.
708 */
709 board[i*w+j] = EMPTY;
710 }
711 assert(!"We shouldn't get here");
712 soluble:
713
714 /*
715 * Make a list of all the inter-block edges on the board.
716 */
717 list = snewn(wh*2, int);
718 nlist = 0;
719 for (i = 0; i+1 < w; i++)
720 for (j = 0; j < h; j++)
721 list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */
722 for (j = 0; j+1 < h; j++)
723 for (i = 0; i < w; i++)
724 list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */
725
726 /*
727 * Now go through that list in random order, trying to merge
728 * the blocks on each side of each edge.
729 */
730 shuffle(list, nlist, sizeof(*list), rs);
731 while (nlist > 0) {
732 int x1, y1, p1, c1;
733 int x2, y2, p2, c2;
734
735 pos = list[--nlist];
736 y1 = y2 = pos / (w*2);
737 x1 = x2 = (pos / 2) % w;
738 if (pos % 2)
739 y2++;
740 else
741 x2++;
742 p1 = y1*w+x1;
743 p2 = y2*w+x2;
744
745 /*
746 * Immediately abandon the attempt if we've already tried
747 * to merge the same pair of blocks along a different
748 * edge.
749 */
750 c1 = dsf_canonify(dsf, p1);
751 c2 = dsf_canonify(dsf, p2);
752 if (tried_merge[c1 * wh + c2])
753 continue;
754
755 /*
756 * In order to be mergeable, these two squares must each
757 * either be, or belong to, a non-main anchor, and their
758 * anchors must also be distinct.
759 */
760 if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2]))
761 continue;
762 while (ISDIST(board[p1]))
763 p1 -= board[p1];
764 while (ISDIST(board[p2]))
765 p2 -= board[p2];
766 if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2)
767 continue;
768
769 /*
770 * We can merge these blocks. Try it, and see if the
771 * puzzle remains soluble.
772 */
773 memcpy(board2, board, wh);
774 j = -1;
775 while (p1 < wh || p2 < wh) {
776 /*
777 * p1 and p2 are the squares at the head of each block
778 * list. Pick the smaller one and put it on the output
779 * block list.
780 */
781 i = min(p1, p2);
782 if (j < 0) {
783 board[i] = ANCHOR;
784 } else {
785 assert(i - j <= MAXDIST);
786 board[i] = DIST(i - j);
787 }
788 j = i;
789
790 /*
791 * Now advance whichever list that came from.
792 */
793 if (i == p1) {
794 do {
795 p1++;
796 } while (p1 < wh && board[p1] != DIST(p1-i));
797 } else {
798 do {
799 p2++;
800 } while (p2 < wh && board[p2] != DIST(p2-i));
801 }
802 }
803 j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL);
804 if (j < 0) {
805 /*
806 * Didn't work. Revert the merge.
807 */
808 memcpy(board, board2, wh);
809 tried_merge[c1 * wh + c2] = true;
810 tried_merge[c2 * wh + c1] = true;
811 } else {
812 int c;
813
814 moves = j;
815
816 dsf_merge(dsf, c1, c2);
817 c = dsf_canonify(dsf, c1);
818 for (i = 0; i < wh; i++)
819 tried_merge[c*wh+i] = (tried_merge[c1*wh+i] ||
820 tried_merge[c2*wh+i]);
821 for (i = 0; i < wh; i++)
822 tried_merge[i*wh+c] = (tried_merge[i*wh+c1] ||
823 tried_merge[i*wh+c2]);
824 }
825 }
826
827 dsf_free(dsf);
828 sfree(list);
829 sfree(tried_merge);
830 sfree(board2);
831
832 *rtx = tx;
833 *rty = ty;
834 *rboard = board;
835 *rforcefield = forcefield;
836 *minmoves = moves;
837}
838
839/* ----------------------------------------------------------------------
840 * End of solver/generator code.
841 */
842
843static char *new_game_desc(const game_params *params, random_state *rs,
844 char **aux, bool interactive)
845{
846 int w = params->w, h = params->h, wh = w*h;
847 int tx, ty, minmoves;
848 unsigned char *board;
849 bool *forcefield;
850 char *ret, *p;
851 int i;
852
853 generate_board(params->w, params->h, &tx, &ty, &minmoves, rs,
854 &board, &forcefield, params->maxmoves);
855#ifdef GENERATOR_DIAGNOSTICS
856 {
857 char *t = board_text_format(params->w, params->h, board);
858 printf("%s\n", t);
859 sfree(t);
860 }
861#endif
862
863 /*
864 * Encode as a game ID.
865 */
866 ret = snewn(wh * 6 + 40, char);
867 p = ret;
868 i = 0;
869 while (i < wh) {
870 if (ISDIST(board[i])) {
871 p += sprintf(p, "d%d", board[i]);
872 i++;
873 } else {
874 int count = 1;
875 int b = board[i];
876 bool f = forcefield[i];
877 int c = (b == ANCHOR ? 'a' :
878 b == MAINANCHOR ? 'm' :
879 b == EMPTY ? 'e' :
880 /* b == WALL ? */ 'w');
881 if (f) *p++ = 'f';
882 *p++ = c;
883 i++;
884 while (i < wh && board[i] == b && forcefield[i] == f)
885 i++, count++;
886 if (count > 1)
887 p += sprintf(p, "%d", count);
888 }
889 }
890 p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves);
891 ret = sresize(ret, p+1 - ret, char);
892
893 sfree(board);
894 sfree(forcefield);
895
896 return ret;
897}
898
899static const char *validate_desc(const game_params *params, const char *desc)
900{
901 int w = params->w, h = params->h, wh = w*h;
902 bool *active;
903 int *link;
904 int mains = 0;
905 int i, tx, ty, minmoves;
906 const char *ret;
907
908 active = snewn(wh, bool);
909 link = snewn(wh, int);
910 i = 0;
911
912 while (*desc && *desc != ',') {
913 if (i >= wh) {
914 ret = "Too much data in game description";
915 goto done;
916 }
917 link[i] = -1;
918 active[i] = false;
919 if (*desc == 'f' || *desc == 'F') {
920 desc++;
921 if (!*desc) {
922 ret = "Expected another character after 'f' in game "
923 "description";
924 goto done;
925 }
926 }
927
928 if (*desc == 'd' || *desc == 'D') {
929 int dist;
930
931 desc++;
932 if (!isdigit((unsigned char)*desc)) {
933 ret = "Expected a number after 'd' in game description";
934 goto done;
935 }
936 dist = atoi(desc);
937 while (*desc && isdigit((unsigned char)*desc)) desc++;
938
939 if (dist <= 0 || dist > i) {
940 ret = "Out-of-range number after 'd' in game description";
941 goto done;
942 }
943
944 if (!active[i - dist]) {
945 ret = "Invalid back-reference in game description";
946 goto done;
947 }
948
949 link[i] = i - dist;
950
951 active[i] = true;
952 active[link[i]] = false;
953 i++;
954 } else {
955 int c = *desc++;
956 int count = 1;
957
958 if (!strchr("aAmMeEwW", c)) {
959 ret = "Invalid character in game description";
960 goto done;
961 }
962 if (isdigit((unsigned char)*desc)) {
963 count = atoi(desc);
964 while (*desc && isdigit((unsigned char)*desc)) desc++;
965 }
966 if (i + count > wh) {
967 ret = "Too much data in game description";
968 goto done;
969 }
970 while (count-- > 0) {
971 active[i] = (strchr("aAmM", c) != NULL);
972 link[i] = -1;
973 if (strchr("mM", c) != NULL) {
974 mains++;
975 }
976 i++;
977 }
978 }
979 }
980 if (mains != 1) {
981 ret = (mains == 0 ? "No main piece specified in game description" :
982 "More than one main piece specified in game description");
983 goto done;
984 }
985 if (i < wh) {
986 ret = "Not enough data in game description";
987 goto done;
988 }
989
990 /*
991 * Now read the target coordinates.
992 */
993 i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves);
994 if (i < 2) {
995 ret = "No target coordinates specified";
996 goto done;
997 /*
998 * (but minmoves is optional)
999 */
1000 }
1001
1002 ret = NULL;
1003
1004 done:
1005 sfree(active);
1006 sfree(link);
1007 return ret;
1008}
1009
1010static game_state *new_game(midend *me, const game_params *params,
1011 const char *desc)
1012{
1013 int w = params->w, h = params->h, wh = w*h;
1014 game_state *state;
1015 int i;
1016
1017 state = snew(game_state);
1018 state->w = w;
1019 state->h = h;
1020 state->board = snewn(wh, unsigned char);
1021 state->lastmoved = state->lastmoved_pos = -1;
1022 state->movecount = 0;
1023 state->imm = snew(struct game_immutable_state);
1024 state->imm->refcount = 1;
1025 state->imm->forcefield = snewn(wh, bool);
1026
1027 i = 0;
1028
1029 while (*desc && *desc != ',') {
1030 bool f = false;
1031
1032 assert(i < wh);
1033
1034 if (*desc == 'f') {
1035 f = true;
1036 desc++;
1037 assert(*desc);
1038 }
1039
1040 if (*desc == 'd' || *desc == 'D') {
1041 int dist;
1042
1043 desc++;
1044 dist = atoi(desc);
1045 while (*desc && isdigit((unsigned char)*desc)) desc++;
1046
1047 state->board[i] = DIST(dist);
1048 state->imm->forcefield[i] = f;
1049
1050 i++;
1051 } else {
1052 int c = *desc++;
1053 int count = 1;
1054
1055 if (isdigit((unsigned char)*desc)) {
1056 count = atoi(desc);
1057 while (*desc && isdigit((unsigned char)*desc)) desc++;
1058 }
1059 assert(i + count <= wh);
1060
1061 c = (c == 'a' || c == 'A' ? ANCHOR :
1062 c == 'm' || c == 'M' ? MAINANCHOR :
1063 c == 'e' || c == 'E' ? EMPTY :
1064 /* c == 'w' || c == 'W' ? */ WALL);
1065
1066 while (count-- > 0) {
1067 state->board[i] = c;
1068 state->imm->forcefield[i] = f;
1069 i++;
1070 }
1071 }
1072 }
1073
1074 /*
1075 * Now read the target coordinates.
1076 */
1077 state->tx = state->ty = 0;
1078 state->minmoves = -1;
1079 i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves);
1080
1081 if (state->board[state->ty*w+state->tx] == MAINANCHOR)
1082 state->completed = 0; /* already complete! */
1083 else
1084 state->completed = -1;
1085
1086 state->cheated = false;
1087 state->soln = NULL;
1088 state->soln_index = -1;
1089
1090 return state;
1091}
1092
1093static game_state *dup_game(const game_state *state)
1094{
1095 int w = state->w, h = state->h, wh = w*h;
1096 game_state *ret = snew(game_state);
1097
1098 ret->w = state->w;
1099 ret->h = state->h;
1100 ret->board = snewn(wh, unsigned char);
1101 memcpy(ret->board, state->board, wh);
1102 ret->tx = state->tx;
1103 ret->ty = state->ty;
1104 ret->minmoves = state->minmoves;
1105 ret->lastmoved = state->lastmoved;
1106 ret->lastmoved_pos = state->lastmoved_pos;
1107 ret->movecount = state->movecount;
1108 ret->completed = state->completed;
1109 ret->cheated = state->cheated;
1110 ret->imm = state->imm;
1111 ret->imm->refcount++;
1112 ret->soln = state->soln;
1113 ret->soln_index = state->soln_index;
1114 if (ret->soln)
1115 ret->soln->refcount++;
1116
1117 return ret;
1118}
1119
1120static void free_game(game_state *state)
1121{
1122 if (--state->imm->refcount <= 0) {
1123 sfree(state->imm->forcefield);
1124 sfree(state->imm);
1125 }
1126 if (state->soln && --state->soln->refcount <= 0) {
1127 sfree(state->soln->moves);
1128 sfree(state->soln);
1129 }
1130 sfree(state->board);
1131 sfree(state);
1132}
1133
1134static char *solve_game(const game_state *state, const game_state *currstate,
1135 const char *aux, const char **error)
1136{
1137 int *moves;
1138 int nmoves;
1139 int i;
1140 char *ret, *p, sep;
1141
1142 /*
1143 * Run the solver and attempt to find the shortest solution
1144 * from the current position.
1145 */
1146 nmoves = solve_board(state->w, state->h, state->board,
1147 state->imm->forcefield, state->tx, state->ty,
1148 -1, &moves);
1149
1150 if (nmoves < 0) {
1151 *error = "Unable to find a solution to this puzzle";
1152 return NULL;
1153 }
1154 if (nmoves == 0) {
1155 *error = "Puzzle is already solved";
1156 return NULL;
1157 }
1158
1159 /*
1160 * Encode the resulting solution as a move string.
1161 */
1162 ret = snewn(nmoves * 40, char);
1163 p = ret;
1164 sep = 'S';
1165
1166 for (i = 0; i < nmoves; i++) {
1167 p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]);
1168 sep = ',';
1169 }
1170
1171 sfree(moves);
1172 assert(p - ret < nmoves * 40);
1173 ret = sresize(ret, p+1 - ret, char);
1174
1175 return ret;
1176}
1177
1178static bool game_can_format_as_text_now(const game_params *params)
1179{
1180 return true;
1181}
1182
1183static char *game_text_format(const game_state *state)
1184{
1185 return board_text_format(state->w, state->h, state->board,
1186 state->imm->forcefield);
1187}
1188
1189struct game_ui {
1190 bool dragging;
1191 int drag_anchor;
1192 int drag_offset_x, drag_offset_y;
1193 int drag_currpos;
1194 bool *reachable;
1195 int *bfs_queue; /* used as scratch in interpret_move */
1196};
1197
1198static game_ui *new_ui(const game_state *state)
1199{
1200 int w = state->w, h = state->h, wh = w*h;
1201 game_ui *ui = snew(game_ui);
1202
1203 ui->dragging = false;
1204 ui->drag_anchor = ui->drag_currpos = -1;
1205 ui->drag_offset_x = ui->drag_offset_y = -1;
1206 ui->reachable = snewn(wh, bool);
1207 memset(ui->reachable, 0, wh * sizeof(bool));
1208 ui->bfs_queue = snewn(wh, int);
1209
1210 return ui;
1211}
1212
1213static void free_ui(game_ui *ui)
1214{
1215 sfree(ui->bfs_queue);
1216 sfree(ui->reachable);
1217 sfree(ui);
1218}
1219
1220static void game_changed_state(game_ui *ui, const game_state *oldstate,
1221 const game_state *newstate)
1222{
1223}
1224
1225#define PREFERRED_TILESIZE 32
1226#define TILESIZE (ds->tilesize)
1227#define BORDER (TILESIZE/2)
1228#define COORD(x) ( (x) * TILESIZE + BORDER )
1229#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1230#define BORDER_WIDTH (1 + TILESIZE/20)
1231#define HIGHLIGHT_WIDTH (1 + TILESIZE/16)
1232
1233#define FLASH_INTERVAL 0.10F
1234#define FLASH_TIME 3*FLASH_INTERVAL
1235
1236struct game_drawstate {
1237 int tilesize;
1238 int w, h;
1239 unsigned long *grid; /* what's currently displayed */
1240};
1241
1242static char *interpret_move(const game_state *state, game_ui *ui,
1243 const game_drawstate *ds,
1244 int x, int y, int button)
1245{
1246 int w = state->w, h = state->h, wh = w*h;
1247 int tx, ty, i, j;
1248 int qhead, qtail;
1249
1250 if (button == LEFT_BUTTON) {
1251 tx = FROMCOORD(x);
1252 ty = FROMCOORD(y);
1253
1254 if (tx < 0 || tx >= w || ty < 0 || ty >= h ||
1255 !ISBLOCK(state->board[ty*w+tx]))
1256 return NULL; /* this click has no effect */
1257
1258 /*
1259 * User has clicked on a block. Find the block's anchor
1260 * and register that we've started dragging it.
1261 */
1262 i = ty*w+tx;
1263 while (ISDIST(state->board[i]))
1264 i -= state->board[i];
1265 assert(i >= 0 && i < wh);
1266
1267 ui->dragging = true;
1268 ui->drag_anchor = i;
1269 ui->drag_offset_x = tx - (i % w);
1270 ui->drag_offset_y = ty - (i / w);
1271 ui->drag_currpos = i;
1272
1273 /*
1274 * Now we immediately bfs out from the current location of
1275 * the anchor, to find all the places to which this block
1276 * can be dragged.
1277 */
1278 memset(ui->reachable, 0, wh * sizeof(bool));
1279 qhead = qtail = 0;
1280 ui->reachable[i] = true;
1281 ui->bfs_queue[qtail++] = i;
1282 for (j = i; j < wh; j++)
1283 if (state->board[j] == DIST(j - i))
1284 i = j;
1285 while (qhead < qtail) {
1286 int pos = ui->bfs_queue[qhead++];
1287 int x = pos % w, y = pos / w;
1288 int dir;
1289
1290 for (dir = 0; dir < 4; dir++) {
1291 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
1292 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
1293 int newpos;
1294
1295 if (x + dx < 0 || x + dx >= w ||
1296 y + dy < 0 || y + dy >= h)
1297 continue;
1298
1299 newpos = pos + dy*w + dx;
1300 if (ui->reachable[newpos])
1301 continue; /* already done this one */
1302
1303 /*
1304 * Now search the grid to see if the block we're
1305 * dragging could fit into this space.
1306 */
1307 for (j = i; j >= 0; j = (ISDIST(state->board[j]) ?
1308 j - state->board[j] : -1)) {
1309 int jx = (j+pos-ui->drag_anchor) % w;
1310 int jy = (j+pos-ui->drag_anchor) / w;
1311 int j2;
1312
1313 if (jx + dx < 0 || jx + dx >= w ||
1314 jy + dy < 0 || jy + dy >= h)
1315 break; /* this position isn't valid at all */
1316
1317 j2 = (j+pos-ui->drag_anchor) + dy*w + dx;
1318
1319 if (state->board[j2] == EMPTY &&
1320 (!state->imm->forcefield[j2] ||
1321 state->board[ui->drag_anchor] == MAINANCHOR))
1322 continue;
1323 while (ISDIST(state->board[j2]))
1324 j2 -= state->board[j2];
1325 assert(j2 >= 0 && j2 < wh);
1326 if (j2 == ui->drag_anchor)
1327 continue;
1328 else
1329 break;
1330 }
1331
1332 if (j < 0) {
1333 /*
1334 * If we got to the end of that loop without
1335 * disqualifying this position, mark it as
1336 * reachable for this drag.
1337 */
1338 ui->reachable[newpos] = true;
1339 ui->bfs_queue[qtail++] = newpos;
1340 }
1341 }
1342 }
1343
1344 /*
1345 * And that's it. Update the display to reflect the start
1346 * of a drag.
1347 */
1348 return MOVE_UI_UPDATE;
1349 } else if (button == LEFT_DRAG && ui->dragging) {
1350 int dist, distlimit, dx, dy, s, px, py;
1351
1352 tx = FROMCOORD(x);
1353 ty = FROMCOORD(y);
1354
1355 tx -= ui->drag_offset_x;
1356 ty -= ui->drag_offset_y;
1357
1358 /*
1359 * Now search outwards from (tx,ty), in order of Manhattan
1360 * distance, until we find a reachable square.
1361 */
1362 distlimit = w+tx;
1363 distlimit = max(distlimit, h+ty);
1364 distlimit = max(distlimit, tx);
1365 distlimit = max(distlimit, ty);
1366 for (dist = 0; dist <= distlimit; dist++) {
1367 for (dx = -dist; dx <= dist; dx++)
1368 for (s = -1; s <= +1; s += 2) {
1369 dy = s * (dist - abs(dx));
1370 px = tx + dx;
1371 py = ty + dy;
1372 if (px >= 0 && px < w && py >= 0 && py < h &&
1373 ui->reachable[py*w+px]) {
1374 ui->drag_currpos = py*w+px;
1375 return MOVE_UI_UPDATE;
1376 }
1377 }
1378 }
1379 return NULL; /* give up - this drag has no effect */
1380 } else if (button == LEFT_RELEASE && ui->dragging) {
1381 char data[256], *str;
1382
1383 /*
1384 * Terminate the drag, and if the piece has actually moved
1385 * then return a move string quoting the old and new
1386 * locations of the piece's anchor.
1387 */
1388 if (ui->drag_anchor != ui->drag_currpos) {
1389 sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos);
1390 str = dupstr(data);
1391 } else
1392 str = MOVE_UI_UPDATE;
1393
1394 ui->dragging = false;
1395 ui->drag_anchor = ui->drag_currpos = -1;
1396 ui->drag_offset_x = ui->drag_offset_y = -1;
1397 memset(ui->reachable, 0, wh * sizeof(bool));
1398
1399 return str;
1400 } else if (button == ' ' && state->soln) {
1401 /*
1402 * Make the next move in the stored solution.
1403 */
1404 char data[256];
1405 int a1, a2;
1406
1407 a1 = state->soln->moves[state->soln_index*2];
1408 a2 = state->soln->moves[state->soln_index*2+1];
1409 if (a1 == state->lastmoved_pos)
1410 a1 = state->lastmoved;
1411
1412 sprintf(data, "M%d-%d", a1, a2);
1413 return dupstr(data);
1414 }
1415
1416 return NULL;
1417}
1418
1419static bool move_piece(int w, int h, const unsigned char *src,
1420 unsigned char *dst, bool *ff, int from, int to)
1421{
1422 int wh = w*h;
1423 int i, j;
1424
1425 if (!ISANCHOR(dst[from]))
1426 return false;
1427
1428 /*
1429 * Scan to the far end of the piece's linked list.
1430 */
1431 for (i = j = from; j < wh; j++)
1432 if (src[j] == DIST(j - i))
1433 i = j;
1434
1435 /*
1436 * Remove the piece from its old location in the new
1437 * game state.
1438 */
1439 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1))
1440 dst[j] = EMPTY;
1441
1442 /*
1443 * And put it back in at the new location.
1444 */
1445 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) {
1446 int jn = j + to - from;
1447 if (jn < 0 || jn >= wh)
1448 return false;
1449 if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) {
1450 dst[jn] = src[j];
1451 } else {
1452 return false;
1453 }
1454 }
1455
1456 return true;
1457}
1458
1459static game_state *execute_move(const game_state *state, const char *move)
1460{
1461 int w = state->w, h = state->h /* , wh = w*h */;
1462 char c;
1463 int a1, a2, n, movesize;
1464 game_state *ret = dup_game(state);
1465
1466 while (*move) {
1467 c = *move;
1468 if (c == 'S') {
1469 /*
1470 * This is a solve move, so we just set up a stored
1471 * solution path.
1472 */
1473 if (ret->soln && --ret->soln->refcount <= 0) {
1474 sfree(ret->soln->moves);
1475 sfree(ret->soln);
1476 }
1477 ret->soln = snew(struct game_solution);
1478 ret->soln->nmoves = 0;
1479 ret->soln->moves = NULL;
1480 ret->soln->refcount = 1;
1481 ret->soln_index = 0;
1482 ret->cheated = true;
1483
1484 movesize = 0;
1485 move++;
1486 while (1) {
1487 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) {
1488 free_game(ret);
1489 return NULL;
1490 }
1491
1492 /*
1493 * Special case: if the first move in the solution
1494 * involves the piece for which we already have a
1495 * partial stored move, adjust the source point to
1496 * the original starting point of that piece.
1497 */
1498 if (ret->soln->nmoves == 0 && a1 == ret->lastmoved)
1499 a1 = ret->lastmoved_pos;
1500
1501 if (ret->soln->nmoves >= movesize) {
1502 movesize = (ret->soln->nmoves + 48) * 4 / 3;
1503 ret->soln->moves = sresize(ret->soln->moves,
1504 2*movesize, int);
1505 }
1506
1507 ret->soln->moves[2*ret->soln->nmoves] = a1;
1508 ret->soln->moves[2*ret->soln->nmoves+1] = a2;
1509 ret->soln->nmoves++;
1510 move += n;
1511 if (*move != ',')
1512 break;
1513 move++; /* eat comma */
1514 }
1515 } else if (c == 'M') {
1516 move++;
1517 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 ||
1518 !move_piece(w, h, state->board, ret->board,
1519 state->imm->forcefield, a1, a2)) {
1520 free_game(ret);
1521 return NULL;
1522 }
1523 if (a1 == ret->lastmoved) {
1524 /*
1525 * If the player has moved the same piece as they
1526 * moved last time, don't increment the move
1527 * count. In fact, if they've put the piece back
1528 * where it started from, _decrement_ the move
1529 * count.
1530 */
1531 if (a2 == ret->lastmoved_pos) {
1532 ret->movecount--; /* reverted last move */
1533 ret->lastmoved = ret->lastmoved_pos = -1;
1534 } else {
1535 ret->lastmoved = a2;
1536 /* don't change lastmoved_pos */
1537 }
1538 } else {
1539 ret->lastmoved = a2;
1540 ret->lastmoved_pos = a1;
1541 ret->movecount++;
1542 }
1543
1544 /*
1545 * If we have a stored solution path, see if we've
1546 * strayed from it or successfully made the next move
1547 * along it.
1548 */
1549 if (ret->soln && ret->lastmoved_pos >= 0) {
1550 if (ret->lastmoved_pos !=
1551 ret->soln->moves[ret->soln_index*2]) {
1552 /* strayed from the path */
1553 ret->soln->refcount--;
1554 assert(ret->soln->refcount > 0);
1555 /* `state' at least still exists */
1556 ret->soln = NULL;
1557 ret->soln_index = -1;
1558 } else if (ret->lastmoved ==
1559 ret->soln->moves[ret->soln_index*2+1]) {
1560 /* advanced along the path */
1561 ret->soln_index++;
1562 if (ret->soln_index >= ret->soln->nmoves) {
1563 /* finished the path! */
1564 ret->soln->refcount--;
1565 assert(ret->soln->refcount > 0);
1566 /* `state' at least still exists */
1567 ret->soln = NULL;
1568 ret->soln_index = -1;
1569 }
1570 }
1571 }
1572
1573 if (ret->board[a2] == MAINANCHOR &&
1574 a2 == ret->ty * w + ret->tx && ret->completed < 0)
1575 ret->completed = ret->movecount;
1576 move += n;
1577 } else {
1578 free_game(ret);
1579 return NULL;
1580 }
1581 if (*move == ';')
1582 move++;
1583 else if (*move) {
1584 free_game(ret);
1585 return NULL;
1586 }
1587 }
1588
1589 return ret;
1590}
1591
1592/* ----------------------------------------------------------------------
1593 * Drawing routines.
1594 */
1595
1596static void game_compute_size(const game_params *params, int tilesize,
1597 const game_ui *ui, int *x, int *y)
1598{
1599 /* fool the macros */
1600 struct dummy { int tilesize; } dummy, *ds = &dummy;
1601 dummy.tilesize = tilesize;
1602
1603 *x = params->w * TILESIZE + 2*BORDER;
1604 *y = params->h * TILESIZE + 2*BORDER;
1605}
1606
1607static void game_set_size(drawing *dr, game_drawstate *ds,
1608 const game_params *params, int tilesize)
1609{
1610 ds->tilesize = tilesize;
1611}
1612
1613static void raise_colour(float *target, float *src, float *limit)
1614{
1615 int i;
1616 for (i = 0; i < 3; i++)
1617 target[i] = (2*src[i] + limit[i]) / 3;
1618}
1619
1620static float *game_colours(frontend *fe, int *ncolours)
1621{
1622 float *ret = snewn(3 * NCOLOURS, float);
1623
1624 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1625
1626 /*
1627 * When dragging a tile, we light it up a bit.
1628 */
1629 raise_colour(ret+3*COL_DRAGGING,
1630 ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT);
1631 raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT,
1632 ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT);
1633 raise_colour(ret+3*COL_DRAGGING_LOWLIGHT,
1634 ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT);
1635
1636 /*
1637 * The main tile is tinted blue.
1638 */
1639 ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1640 ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1641 ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2];
1642 game_mkhighlight_specific(fe, ret, COL_MAIN,
1643 COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT);
1644
1645 /*
1646 * And we light that up a bit too when dragging.
1647 */
1648 raise_colour(ret+3*COL_MAIN_DRAGGING,
1649 ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT);
1650 raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT,
1651 ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1652 raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT,
1653 ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1654
1655 /*
1656 * The target area on the floor is tinted green.
1657 */
1658 ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1659 ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1];
1660 ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1661 game_mkhighlight_specific(fe, ret, COL_TARGET,
1662 COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT);
1663
1664 *ncolours = NCOLOURS;
1665 return ret;
1666}
1667
1668static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1669{
1670 int w = state->w, h = state->h, wh = w*h;
1671 struct game_drawstate *ds = snew(struct game_drawstate);
1672 int i;
1673
1674 ds->tilesize = 0;
1675 ds->w = w;
1676 ds->h = h;
1677 ds->grid = snewn(wh, unsigned long);
1678 for (i = 0; i < wh; i++)
1679 ds->grid[i] = ~(unsigned long)0;
1680
1681 return ds;
1682}
1683
1684static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1685{
1686 sfree(ds->grid);
1687 sfree(ds);
1688}
1689
1690#define BG_NORMAL 0x00000001UL
1691#define BG_TARGET 0x00000002UL
1692#define BG_FORCEFIELD 0x00000004UL
1693#define FLASH_LOW 0x00000008UL
1694#define FLASH_HIGH 0x00000010UL
1695#define FG_WALL 0x00000020UL
1696#define FG_MAIN 0x00000040UL
1697#define FG_NORMAL 0x00000080UL
1698#define FG_DRAGGING 0x00000100UL
1699#define FG_SHADOW 0x00000200UL
1700#define FG_SOLVEPIECE 0x00000400UL
1701#define FG_MAINPIECESH 11
1702#define FG_SHADOWSH 19
1703
1704#define PIECE_LBORDER 0x00000001UL
1705#define PIECE_TBORDER 0x00000002UL
1706#define PIECE_RBORDER 0x00000004UL
1707#define PIECE_BBORDER 0x00000008UL
1708#define PIECE_TLCORNER 0x00000010UL
1709#define PIECE_TRCORNER 0x00000020UL
1710#define PIECE_BLCORNER 0x00000040UL
1711#define PIECE_BRCORNER 0x00000080UL
1712#define PIECE_MASK 0x000000FFUL
1713
1714/*
1715 * Utility function.
1716 */
1717#define TYPE_MASK 0xF000
1718#define COL_MASK 0x0FFF
1719#define TYPE_RECT 0x0000
1720#define TYPE_TLCIRC 0x4000
1721#define TYPE_TRCIRC 0x5000
1722#define TYPE_BLCIRC 0x6000
1723#define TYPE_BRCIRC 0x7000
1724static void maybe_rect(drawing *dr, int x, int y, int w, int h,
1725 int coltype, int col2)
1726{
1727 int colour = coltype & COL_MASK, type = coltype & TYPE_MASK;
1728
1729 if (colour > NCOLOURS)
1730 return;
1731 if (type == TYPE_RECT) {
1732 draw_rect(dr, x, y, w, h, colour);
1733 } else {
1734 int cx, cy, r;
1735
1736 clip(dr, x, y, w, h);
1737
1738 cx = x;
1739 cy = y;
1740 r = w-1;
1741 if (type & 0x1000)
1742 cx += r;
1743 if (type & 0x2000)
1744 cy += r;
1745
1746 if (col2 == -1 || col2 == coltype) {
1747 assert(w == h);
1748 draw_circle(dr, cx, cy, r, colour, colour);
1749 } else {
1750 /*
1751 * We aim to draw a quadrant of a circle in two
1752 * different colours. We do this using Bresenham's
1753 * algorithm directly, because the Puzzles drawing API
1754 * doesn't have a draw-sector primitive.
1755 */
1756 int bx, by, bd, bd2;
1757 int xm = (type & 0x1000 ? -1 : +1);
1758 int ym = (type & 0x2000 ? -1 : +1);
1759
1760 by = r;
1761 bx = 0;
1762 bd = 0;
1763 while (by >= bx) {
1764 /*
1765 * Plot the point.
1766 */
1767 {
1768 int x1 = cx+xm*bx, y1 = cy+ym*bx;
1769 int x2, y2;
1770
1771 x2 = cx+xm*by; y2 = y1;
1772 draw_rect(dr, min(x1,x2), min(y1,y2),
1773 abs(x1-x2)+1, abs(y1-y2)+1, colour);
1774 x2 = x1; y2 = cy+ym*by;
1775 draw_rect(dr, min(x1,x2), min(y1,y2),
1776 abs(x1-x2)+1, abs(y1-y2)+1, col2);
1777 }
1778
1779 bd += 2*bx + 1;
1780 bd2 = bd - (2*by - 1);
1781 if (abs(bd2) < abs(bd)) {
1782 bd = bd2;
1783 by--;
1784 }
1785 bx++;
1786 }
1787 }
1788
1789 unclip(dr);
1790 }
1791}
1792
1793static void draw_wallpart(drawing *dr, game_drawstate *ds,
1794 int tx, int ty, unsigned long val,
1795 int cl, int cc, int ch)
1796{
1797 int coords[6];
1798
1799 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
1800 if (val & PIECE_LBORDER)
1801 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE,
1802 ch);
1803 if (val & PIECE_RBORDER)
1804 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1805 HIGHLIGHT_WIDTH, TILESIZE, cl);
1806 if (val & PIECE_TBORDER)
1807 draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch);
1808 if (val & PIECE_BBORDER)
1809 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1810 TILESIZE, HIGHLIGHT_WIDTH, cl);
1811 if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) {
1812 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1813 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1814 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1815 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1816 coords[0] = tx - 1;
1817 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1818 coords[2] = tx + HIGHLIGHT_WIDTH;
1819 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1820 coords[4] = tx - 1;
1821 coords[5] = ty + TILESIZE;
1822 draw_polygon(dr, coords, 3, ch, ch);
1823 unclip(dr);
1824 } else if (val & PIECE_BLCORNER) {
1825 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1826 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1827 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1828 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1829 coords[0] = tx - 1;
1830 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1831 coords[2] = tx + HIGHLIGHT_WIDTH;
1832 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1833 coords[4] = tx - 1;
1834 coords[5] = ty + TILESIZE;
1835 draw_polygon(dr, coords, 3, cl, cl);
1836 unclip(dr);
1837 }
1838 if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) {
1839 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1840 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1841 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1842 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1843 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1844 coords[1] = ty - 1;
1845 coords[2] = tx + TILESIZE;
1846 coords[3] = ty - 1;
1847 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1848 coords[5] = ty + HIGHLIGHT_WIDTH;
1849 draw_polygon(dr, coords, 3, ch, ch);
1850 unclip(dr);
1851 } else if (val & PIECE_TRCORNER) {
1852 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1853 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1854 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1855 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1856 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1857 coords[1] = ty - 1;
1858 coords[2] = tx + TILESIZE;
1859 coords[3] = ty - 1;
1860 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1861 coords[5] = ty + HIGHLIGHT_WIDTH;
1862 draw_polygon(dr, coords, 3, cl, cl);
1863 unclip(dr);
1864 }
1865 if (val & PIECE_TLCORNER)
1866 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1867 if (val & PIECE_BRCORNER)
1868 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH,
1869 ty+TILESIZE-HIGHLIGHT_WIDTH,
1870 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1871}
1872
1873static void draw_piecepart(drawing *dr, game_drawstate *ds,
1874 int tx, int ty, unsigned long val,
1875 int cl, int cc, int ch)
1876{
1877 int x[6], y[6];
1878
1879 /*
1880 * Drawing the blocks is hellishly fiddly. The blocks don't
1881 * stretch to the full size of the tile; there's a border
1882 * around them of size BORDER_WIDTH. Then they have bevelled
1883 * borders of size HIGHLIGHT_WIDTH, and also rounded corners.
1884 *
1885 * I tried for some time to find a clean and clever way to
1886 * figure out what needed drawing from the corner and border
1887 * flags, but in the end the cleanest way I could find was the
1888 * following. We divide the grid square into 25 parts by
1889 * ruling four horizontal and four vertical lines across it;
1890 * those lines are at BORDER_WIDTH and BORDER_WIDTH +
1891 * HIGHLIGHT_WIDTH from the top, from the bottom, from the
1892 * left and from the right. Then we carefully consider each of
1893 * the resulting 25 sections of square, and decide separately
1894 * what needs to go in it based on the flags. In complicated
1895 * cases there can be up to five possibilities affecting any
1896 * given section (no corner or border flags, just the corner
1897 * flag, one border flag, the other border flag, both border
1898 * flags). So there's a lot of very fiddly logic here and all
1899 * I could really think to do was give it my best shot and
1900 * then test it and correct all the typos. Not fun to write,
1901 * and I'm sure it isn't fun to read either, but it seems to
1902 * work.
1903 */
1904
1905 x[0] = tx;
1906 x[1] = x[0] + BORDER_WIDTH;
1907 x[2] = x[1] + HIGHLIGHT_WIDTH;
1908 x[5] = tx + TILESIZE;
1909 x[4] = x[5] - BORDER_WIDTH;
1910 x[3] = x[4] - HIGHLIGHT_WIDTH;
1911
1912 y[0] = ty;
1913 y[1] = y[0] + BORDER_WIDTH;
1914 y[2] = y[1] + HIGHLIGHT_WIDTH;
1915 y[5] = ty + TILESIZE;
1916 y[4] = y[5] - BORDER_WIDTH;
1917 y[3] = y[4] - HIGHLIGHT_WIDTH;
1918
1919#define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q]
1920
1921 maybe_rect(dr, RECT(0,0),
1922 (val & (PIECE_TLCORNER | PIECE_TBORDER |
1923 PIECE_LBORDER)) ? -1 : cc, -1);
1924 maybe_rect(dr, RECT(1,0),
1925 (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 :
1926 (val & PIECE_LBORDER) ? ch : cc, -1);
1927 maybe_rect(dr, RECT(2,0),
1928 (val & PIECE_TBORDER) ? -1 : cc, -1);
1929 maybe_rect(dr, RECT(3,0),
1930 (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 :
1931 (val & PIECE_RBORDER) ? cl : cc, -1);
1932 maybe_rect(dr, RECT(4,0),
1933 (val & (PIECE_TRCORNER | PIECE_TBORDER |
1934 PIECE_RBORDER)) ? -1 : cc, -1);
1935 maybe_rect(dr, RECT(0,1),
1936 (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 :
1937 (val & PIECE_TBORDER) ? ch : cc, -1);
1938 maybe_rect(dr, RECT(1,1),
1939 (val & PIECE_TLCORNER) ? cc : -1, -1);
1940 maybe_rect(dr, RECT(1,1),
1941 (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC :
1942 !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC :
1943 (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1);
1944 maybe_rect(dr, RECT(2,1),
1945 (val & PIECE_TBORDER) ? ch : cc, -1);
1946 maybe_rect(dr, RECT(3,1),
1947 (val & PIECE_TRCORNER) ? cc : -1, -1);
1948 maybe_rect(dr, RECT(3,1),
1949 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch :
1950 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl :
1951 !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC :
1952 (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC :
1953 cc, ch);
1954 maybe_rect(dr, RECT(4,1),
1955 (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 :
1956 (val & PIECE_TBORDER) ? ch : cc, -1);
1957 maybe_rect(dr, RECT(0,2),
1958 (val & PIECE_LBORDER) ? -1 : cc, -1);
1959 maybe_rect(dr, RECT(1,2),
1960 (val & PIECE_LBORDER) ? ch : cc, -1);
1961 maybe_rect(dr, RECT(2,2),
1962 cc, -1);
1963 maybe_rect(dr, RECT(3,2),
1964 (val & PIECE_RBORDER) ? cl : cc, -1);
1965 maybe_rect(dr, RECT(4,2),
1966 (val & PIECE_RBORDER) ? -1 : cc, -1);
1967 maybe_rect(dr, RECT(0,3),
1968 (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 :
1969 (val & PIECE_BBORDER) ? cl : cc, -1);
1970 maybe_rect(dr, RECT(1,3),
1971 (val & PIECE_BLCORNER) ? cc : -1, -1);
1972 maybe_rect(dr, RECT(1,3),
1973 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl :
1974 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch :
1975 !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC :
1976 (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC :
1977 cc, cl);
1978 maybe_rect(dr, RECT(2,3),
1979 (val & PIECE_BBORDER) ? cl : cc, -1);
1980 maybe_rect(dr, RECT(3,3),
1981 (val & PIECE_BRCORNER) ? cc : -1, -1);
1982 maybe_rect(dr, RECT(3,3),
1983 (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC :
1984 !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC :
1985 (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1);
1986 maybe_rect(dr, RECT(4,3),
1987 (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 :
1988 (val & PIECE_BBORDER) ? cl : cc, -1);
1989 maybe_rect(dr, RECT(0,4),
1990 (val & (PIECE_BLCORNER | PIECE_BBORDER |
1991 PIECE_LBORDER)) ? -1 : cc, -1);
1992 maybe_rect(dr, RECT(1,4),
1993 (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 :
1994 (val & PIECE_LBORDER) ? ch : cc, -1);
1995 maybe_rect(dr, RECT(2,4),
1996 (val & PIECE_BBORDER) ? -1 : cc, -1);
1997 maybe_rect(dr, RECT(3,4),
1998 (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 :
1999 (val & PIECE_RBORDER) ? cl : cc, -1);
2000 maybe_rect(dr, RECT(4,4),
2001 (val & (PIECE_BRCORNER | PIECE_BBORDER |
2002 PIECE_RBORDER)) ? -1 : cc, -1);
2003
2004#undef RECT
2005}
2006
2007static void draw_tile(drawing *dr, game_drawstate *ds,
2008 int x, int y, unsigned long val)
2009{
2010 int tx = COORD(x), ty = COORD(y);
2011 int cc, ch, cl;
2012
2013 /*
2014 * Draw the tile background.
2015 */
2016 if (val & BG_TARGET)
2017 cc = COL_TARGET;
2018 else
2019 cc = COL_BACKGROUND;
2020 ch = cc+1;
2021 cl = cc+2;
2022 if (val & FLASH_LOW)
2023 cc = cl;
2024 else if (val & FLASH_HIGH)
2025 cc = ch;
2026
2027 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
2028 if (val & BG_FORCEFIELD) {
2029 /*
2030 * Cattle-grid effect to indicate that nothing but the
2031 * main block can slide over this square.
2032 */
2033 int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH));
2034 int i;
2035
2036 for (i = 1; i < n; i += 3) {
2037 draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl);
2038 draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl);
2039 }
2040 }
2041
2042 /*
2043 * Draw the tile midground: a shadow of a block, for
2044 * displaying partial solutions.
2045 */
2046 if (val & FG_SHADOW) {
2047 draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK,
2048 cl, cl, cl);
2049 }
2050
2051 /*
2052 * Draw the tile foreground, i.e. some section of a block or
2053 * wall.
2054 */
2055 if (val & FG_WALL) {
2056 cc = COL_BACKGROUND;
2057 ch = cc+1;
2058 cl = cc+2;
2059 if (val & FLASH_LOW)
2060 cc = cl;
2061 else if (val & FLASH_HIGH)
2062 cc = ch;
2063
2064 draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2065 cl, cc, ch);
2066 } else if (val & (FG_MAIN | FG_NORMAL)) {
2067 if (val & FG_DRAGGING)
2068 cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING);
2069 else
2070 cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND);
2071 ch = cc+1;
2072 cl = cc+2;
2073
2074 if (val & FLASH_LOW)
2075 cc = cl;
2076 else if (val & (FLASH_HIGH | FG_SOLVEPIECE))
2077 cc = ch;
2078
2079 draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2080 cl, cc, ch);
2081 }
2082
2083 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
2084}
2085
2086static unsigned long find_piecepart(int w, int h, DSF *dsf, int x, int y)
2087{
2088 int i = y*w+x;
2089 int canon = dsf_canonify(dsf, i);
2090 unsigned long val = 0;
2091
2092 if (x == 0 || canon != dsf_canonify(dsf, i-1))
2093 val |= PIECE_LBORDER;
2094 if (y== 0 || canon != dsf_canonify(dsf, i-w))
2095 val |= PIECE_TBORDER;
2096 if (x == w-1 || canon != dsf_canonify(dsf, i+1))
2097 val |= PIECE_RBORDER;
2098 if (y == h-1 || canon != dsf_canonify(dsf, i+w))
2099 val |= PIECE_BBORDER;
2100 if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) &&
2101 canon != dsf_canonify(dsf, i-1-w))
2102 val |= PIECE_TLCORNER;
2103 if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) &&
2104 canon != dsf_canonify(dsf, i+1-w))
2105 val |= PIECE_TRCORNER;
2106 if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) &&
2107 canon != dsf_canonify(dsf, i-1+w))
2108 val |= PIECE_BLCORNER;
2109 if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) &&
2110 canon != dsf_canonify(dsf, i+1+w))
2111 val |= PIECE_BRCORNER;
2112 return val;
2113}
2114
2115static void game_redraw(drawing *dr, game_drawstate *ds,
2116 const game_state *oldstate, const game_state *state,
2117 int dir, const game_ui *ui,
2118 float animtime, float flashtime)
2119{
2120 int w = state->w, h = state->h, wh = w*h;
2121 unsigned char *board;
2122 DSF *dsf;
2123 int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst;
2124
2125 /*
2126 * Construct the board we'll be displaying (which may be
2127 * different from the one in state if ui describes a drag in
2128 * progress).
2129 */
2130 board = snewn(wh, unsigned char);
2131 memcpy(board, state->board, wh);
2132 if (ui->dragging) {
2133 bool mpret = move_piece(w, h, state->board, board,
2134 state->imm->forcefield,
2135 ui->drag_anchor, ui->drag_currpos);
2136 assert(mpret);
2137 }
2138
2139 if (state->soln) {
2140 solvesrc = state->soln->moves[state->soln_index*2];
2141 solvedst = state->soln->moves[state->soln_index*2+1];
2142 if (solvesrc == state->lastmoved_pos)
2143 solvesrc = state->lastmoved;
2144 if (solvesrc == ui->drag_anchor)
2145 solvesrc = ui->drag_currpos;
2146 } else
2147 solvesrc = solvedst = -1;
2148
2149 /*
2150 * Build a dsf out of that board, so we can conveniently tell
2151 * which edges are connected and which aren't.
2152 */
2153 dsf = dsf_new(wh);
2154 mainanchor = -1;
2155 for (y = 0; y < h; y++)
2156 for (x = 0; x < w; x++) {
2157 int i = y*w+x;
2158
2159 if (ISDIST(board[i]))
2160 dsf_merge(dsf, i, i - board[i]);
2161 if (board[i] == MAINANCHOR)
2162 mainanchor = i;
2163 if (board[i] == WALL) {
2164 if (x > 0 && board[i-1] == WALL)
2165 dsf_merge(dsf, i, i-1);
2166 if (y > 0 && board[i-w] == WALL)
2167 dsf_merge(dsf, i, i-w);
2168 }
2169 }
2170 assert(mainanchor >= 0);
2171 mainpos = dsf_canonify(dsf, mainanchor);
2172 dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1;
2173 solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1;
2174
2175 /*
2176 * Now we can construct the data about what we want to draw.
2177 */
2178 for (y = 0; y < h; y++)
2179 for (x = 0; x < w; x++) {
2180 int i = y*w+x;
2181 int j;
2182 unsigned long val;
2183 int canon;
2184
2185 /*
2186 * See if this square is part of the target area.
2187 */
2188 j = i + mainanchor - (state->ty * w + state->tx);
2189 while (j >= 0 && j < wh && ISDIST(board[j]))
2190 j -= board[j];
2191 if (j == mainanchor)
2192 val = BG_TARGET;
2193 else
2194 val = BG_NORMAL;
2195
2196 if (state->imm->forcefield[i])
2197 val |= BG_FORCEFIELD;
2198
2199 if (flashtime > 0) {
2200 int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1;
2201 val |= (flashtype ? FLASH_LOW : FLASH_HIGH);
2202 }
2203
2204 if (board[i] != EMPTY) {
2205 canon = dsf_canonify(dsf, i);
2206
2207 if (board[i] == WALL)
2208 val |= FG_WALL;
2209 else if (canon == mainpos)
2210 val |= FG_MAIN;
2211 else
2212 val |= FG_NORMAL;
2213 if (canon == dragpos)
2214 val |= FG_DRAGGING;
2215 if (canon == solvepos)
2216 val |= FG_SOLVEPIECE;
2217
2218 /*
2219 * Now look around to see if other squares
2220 * belonging to the same block are adjacent to us.
2221 */
2222 val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH;
2223 }
2224
2225 /*
2226 * If we're in the middle of showing a solution,
2227 * display a shadow piece for the target of the
2228 * current move.
2229 */
2230 if (solvepos >= 0) {
2231 int si = i - solvedst + solvesrc;
2232 if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) {
2233 val |= find_piecepart(w, h, dsf,
2234 si % w, si / w) << FG_SHADOWSH;
2235 val |= FG_SHADOW;
2236 }
2237 }
2238
2239 if (val != ds->grid[i]) {
2240 draw_tile(dr, ds, x, y, val);
2241 ds->grid[i] = val;
2242 }
2243 }
2244
2245 /*
2246 * Update the status bar.
2247 */
2248 {
2249 char statusbuf[256];
2250
2251 sprintf(statusbuf, "%sMoves: %d",
2252 (state->completed >= 0 ?
2253 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
2254 (state->cheated ? "Auto-solver used. " : "")),
2255 (state->completed >= 0 ? state->completed : state->movecount));
2256 if (state->minmoves >= 0)
2257 sprintf(statusbuf+strlen(statusbuf), " (min %d)",
2258 state->minmoves);
2259
2260 status_bar(dr, statusbuf);
2261 }
2262
2263 dsf_free(dsf);
2264 sfree(board);
2265}
2266
2267static float game_anim_length(const game_state *oldstate,
2268 const game_state *newstate, int dir, game_ui *ui)
2269{
2270 return 0.0F;
2271}
2272
2273static float game_flash_length(const game_state *oldstate,
2274 const game_state *newstate, int dir, game_ui *ui)
2275{
2276 if (oldstate->completed < 0 && newstate->completed >= 0)
2277 return FLASH_TIME;
2278
2279 return 0.0F;
2280}
2281
2282static void game_get_cursor_location(const game_ui *ui,
2283 const game_drawstate *ds,
2284 const game_state *state,
2285 const game_params *params,
2286 int *x, int *y, int *w, int *h)
2287{
2288}
2289
2290static int game_status(const game_state *state)
2291{
2292 return state->completed ? +1 : 0;
2293}
2294
2295static bool game_timing_state(const game_state *state, game_ui *ui)
2296{
2297 return true;
2298}
2299
2300static void game_print_size(const game_params *params, const game_ui *ui,
2301 float *x, float *y)
2302{
2303}
2304
2305static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2306 int tilesize)
2307{
2308}
2309
2310#ifdef COMBINED
2311#define thegame slide
2312#endif
2313
2314const struct game thegame = {
2315 "Slide", NULL, NULL,
2316 default_params,
2317 game_fetch_preset, NULL,
2318 decode_params,
2319 encode_params,
2320 free_params,
2321 dup_params,
2322 true, game_configure, custom_params,
2323 validate_params,
2324 new_game_desc,
2325 validate_desc,
2326 new_game,
2327 dup_game,
2328 free_game,
2329 true, solve_game,
2330 true, game_can_format_as_text_now, game_text_format,
2331 NULL, NULL, /* get_prefs, set_prefs */
2332 new_ui,
2333 free_ui,
2334 NULL, /* encode_ui */
2335 NULL, /* decode_ui */
2336 NULL, /* game_request_keys */
2337 game_changed_state,
2338 NULL, /* current_key_label */
2339 interpret_move,
2340 execute_move,
2341 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2342 game_colours,
2343 game_new_drawstate,
2344 game_free_drawstate,
2345 game_redraw,
2346 game_anim_length,
2347 game_flash_length,
2348 game_get_cursor_location,
2349 game_status,
2350 false, false, game_print_size, game_print,
2351 true, /* wants_statusbar */
2352 false, game_timing_state,
2353 0, /* flags */
2354};
2355
2356#ifdef STANDALONE_SOLVER
2357
2358#include <stdarg.h>
2359
2360int main(int argc, char **argv)
2361{
2362 game_params *p;
2363 game_state *s;
2364 char *id = NULL, *desc;
2365 const char *err;
2366 bool count = false;
2367 int ret;
2368 int *moves;
2369
2370 while (--argc > 0) {
2371 char *p = *++argv;
2372 /*
2373 if (!strcmp(p, "-v")) {
2374 verbose = true;
2375 } else
2376 */
2377 if (!strcmp(p, "-c")) {
2378 count = true;
2379 } else if (*p == '-') {
2380 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2381 return 1;
2382 } else {
2383 id = p;
2384 }
2385 }
2386
2387 if (!id) {
2388 fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]);
2389 return 1;
2390 }
2391
2392 desc = strchr(id, ':');
2393 if (!desc) {
2394 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2395 return 1;
2396 }
2397 *desc++ = '\0';
2398
2399 p = default_params();
2400 decode_params(p, id);
2401 err = validate_desc(p, desc);
2402 if (err) {
2403 fprintf(stderr, "%s: %s\n", argv[0], err);
2404 return 1;
2405 }
2406 s = new_game(NULL, p, desc);
2407
2408 ret = solve_board(s->w, s->h, s->board, s->imm->forcefield,
2409 s->tx, s->ty, -1, &moves);
2410 if (ret < 0) {
2411 printf("No solution found\n");
2412 } else {
2413 int index = 0;
2414 if (count) {
2415 printf("%d moves required\n", ret);
2416 return 0;
2417 }
2418 while (1) {
2419 bool moveret;
2420 char *text = board_text_format(s->w, s->h, s->board,
2421 s->imm->forcefield);
2422 game_state *s2;
2423
2424 printf("position %d:\n%s", index, text);
2425
2426 if (index >= ret)
2427 break;
2428
2429 s2 = dup_game(s);
2430 moveret = move_piece(s->w, s->h, s->board,
2431 s2->board, s->imm->forcefield,
2432 moves[index*2], moves[index*2+1]);
2433 assert(moveret);
2434
2435 free_game(s);
2436 s = s2;
2437 index++;
2438 }
2439 }
2440
2441 return 0;
2442}
2443
2444#endif
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c
new file mode 100644
index 0000000000..1f3c688af1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.c
@@ -0,0 +1,1476 @@
1/*
2 * sokoban.c: An implementation of the well-known Sokoban barrel-
3 * pushing game. Random generation is too simplistic to be
4 * credible, but the rest of the gameplay works well enough to use
5 * it with hand-written level descriptions.
6 */
7
8/*
9 * TODO:
10 *
11 * - I think it would be better to ditch the `prev' array, and
12 * instead make the `dist' array strictly monotonic (by having
13 * each distance be something like I*A+S, where A is the grid
14 * area, I the number of INITIAL squares trampled on, and S the
15 * number of harmless spaces moved through). This would permit
16 * the path-tracing when a pull is actually made to choose
17 * randomly from all the possible shortest routes, which would
18 * be superior in terms of eliminating directional bias.
19 * + So when tracing the path back to the current px,py, we
20 * look at all four adjacent squares, find the minimum
21 * distance, check that it's _strictly smaller_ than that of
22 * the current square, and restrict our choice to precisely
23 * those squares with that minimum distance.
24 * + The other place `prev' is currently used is in the check
25 * for consistency of a pull. We would have to replace the
26 * check for whether prev[ny*w+nx]==oy*w+ox with a check that
27 * made sure there was at least one adjacent square with a
28 * smaller distance which _wasn't_ oy*w+ox. Then when we did
29 * the path-tracing we'd also have to take this special case
30 * into account.
31 *
32 * - More discriminating choice of pull. (Snigger.)
33 * + favour putting targets in clumps
34 * + try to shoot for a reasonably consistent number of barrels
35 * (adjust willingness to generate a new barrel depending on
36 * how many are already present)
37 * + adjust willingness to break new ground depending on how
38 * much is already broken
39 *
40 * - generation time parameters:
41 * + enable NetHack mode (and find a better place for the hole)
42 * + decide how many of the remaining Is should be walls
43 *
44 * - at the end of generation, randomly position the starting
45 * player coordinates, probably by (somehow) reusing the same
46 * bfs currently inside the loop.
47 *
48 * - possible backtracking?
49 *
50 * - IWBNI we could spot completely unreachable bits of level at
51 * the outside, and not bother drawing grid lines for them. The
52 * NH levels currently look a bit weird with grid lines on the
53 * outside of the boundary.
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
66
67#include "puzzles.h"
68
69/*
70 * Various subsets of these constants are used during game
71 * generation, game play, game IDs and the game_drawstate.
72 */
73#define INITIAL 'i' /* used only in game generation */
74#define SPACE 's'
75#define WALL 'w'
76#define PIT 'p'
77#define DEEP_PIT 'd'
78#define TARGET 't'
79#define BARREL 'b'
80#define BARRELTARGET 'f' /* target is 'f'illed */
81#define PLAYER 'u' /* yo'u'; used in game IDs */
82#define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */
83#define INVALID '!' /* used in drawstate to force redraw */
84/*
85 * We also support the use of any capital letter as a barrel, which
86 * will be displayed with that letter as a label. (This facilitates
87 * people distributing annotated game IDs for particular Sokoban
88 * levels, so they can accompany them with verbal instructions
89 * about pushing particular barrels in particular ways.) Therefore,
90 * to find out whether something is a barrel, we need a test
91 * function which does a bit more than just comparing to BARREL.
92 *
93 * When resting on target squares, capital-letter barrels are
94 * replaced with their control-character value (A -> ^A).
95 */
96#define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET )
97#define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \
98 ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) )
99#define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \
100 (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) )
101#define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) )
102#define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) )
103#define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \
104 (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 )
105
106#define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0)
107#define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0)
108
109#define FLASH_LENGTH 0.3F
110
111enum {
112 COL_BACKGROUND,
113 COL_TARGET,
114 COL_PIT,
115 COL_DEEP_PIT,
116 COL_BARREL,
117 COL_PLAYER,
118 COL_TEXT,
119 COL_GRID,
120 COL_OUTLINE,
121 COL_HIGHLIGHT,
122 COL_LOWLIGHT,
123 COL_WALL,
124 NCOLOURS
125};
126
127struct game_params {
128 int w, h;
129 /*
130 * FIXME: a parameter involving degree of filling in?
131 */
132};
133
134struct game_state {
135 game_params p;
136 unsigned char *grid;
137 int px, py;
138 bool completed;
139};
140
141static game_params *default_params(void)
142{
143 game_params *ret = snew(game_params);
144
145 ret->w = 12;
146 ret->h = 10;
147
148 return ret;
149}
150
151static void free_params(game_params *params)
152{
153 sfree(params);
154}
155
156static game_params *dup_params(const game_params *params)
157{
158 game_params *ret = snew(game_params);
159 *ret = *params; /* structure copy */
160 return ret;
161}
162
163static const struct game_params sokoban_presets[] = {
164 { 12, 10 },
165 { 16, 12 },
166 { 20, 16 },
167};
168
169static bool game_fetch_preset(int i, char **name, game_params **params)
170{
171 game_params p, *ret;
172 char *retname;
173 char namebuf[80];
174
175 if (i < 0 || i >= lenof(sokoban_presets))
176 return false;
177
178 p = sokoban_presets[i];
179 ret = dup_params(&p);
180 sprintf(namebuf, "%dx%d", ret->w, ret->h);
181 retname = dupstr(namebuf);
182
183 *params = ret;
184 *name = retname;
185 return true;
186}
187
188static void decode_params(game_params *params, char const *string)
189{
190 params->w = params->h = atoi(string);
191 while (*string && isdigit((unsigned char)*string)) string++;
192 if (*string == 'x') {
193 string++;
194 params->h = atoi(string);
195 }
196}
197
198static char *encode_params(const game_params *params, bool full)
199{
200 char data[256];
201
202 sprintf(data, "%dx%d", params->w, params->h);
203
204 return dupstr(data);
205}
206
207static config_item *game_configure(const game_params *params)
208{
209 config_item *ret;
210 char buf[80];
211
212 ret = snewn(3, config_item);
213
214 ret[0].name = "Width";
215 ret[0].type = C_STRING;
216 sprintf(buf, "%d", params->w);
217 ret[0].u.string.sval = dupstr(buf);
218
219 ret[1].name = "Height";
220 ret[1].type = C_STRING;
221 sprintf(buf, "%d", params->h);
222 ret[1].u.string.sval = dupstr(buf);
223
224 ret[2].name = NULL;
225 ret[2].type = C_END;
226
227 return ret;
228}
229
230static game_params *custom_params(const config_item *cfg)
231{
232 game_params *ret = snew(game_params);
233
234 ret->w = atoi(cfg[0].u.string.sval);
235 ret->h = atoi(cfg[1].u.string.sval);
236
237 return ret;
238}
239
240static const char *validate_params(const game_params *params, bool full)
241{
242 if (params->w < 4 || params->h < 4)
243 return "Width and height must both be at least 4";
244
245 return NULL;
246}
247
248/* ----------------------------------------------------------------------
249 * Game generation mechanism.
250 *
251 * To generate a Sokoban level, we begin with a completely blank
252 * grid and make valid inverse moves. Grid squares can be in a
253 * number of states. The states are:
254 *
255 * - INITIAL: this square has not as yet been touched by any
256 * inverse move, which essentially means we haven't decided what
257 * it is yet.
258 *
259 * - SPACE: this square is a space.
260 *
261 * - TARGET: this square is a space which is also the target for a
262 * barrel.
263 *
264 * - BARREL: this square contains a barrel.
265 *
266 * - BARRELTARGET: this square contains a barrel _on_ a target.
267 *
268 * - WALL: this square is a wall.
269 *
270 * - PLAYER: this square contains the player.
271 *
272 * - PLAYERTARGET: this square contains the player on a target.
273 *
274 * We begin with every square of the in state INITIAL, apart from a
275 * solid ring of WALLs around the edge. We randomly position the
276 * PLAYER somewhere. Thereafter our valid moves are:
277 *
278 * - to move the PLAYER in one direction _pulling_ a barrel after
279 * us. For this to work, we must have SPACE or INITIAL in the
280 * direction we're moving, and BARREL or BARRELTARGET in the
281 * direction we're moving away from. We leave SPACE or TARGET
282 * respectively in the vacated square.
283 *
284 * - to create a new barrel by transforming an INITIAL square into
285 * BARRELTARGET.
286 *
287 * - to move the PLAYER freely through SPACE and TARGET squares,
288 * leaving SPACE or TARGET where it started.
289 *
290 * - to move the player through INITIAL squares, carving a tunnel
291 * of SPACEs as it goes.
292 *
293 * We try to avoid destroying INITIAL squares wherever possible (if
294 * there's a path to where we want to be using only SPACE, then we
295 * should always use that). At the end of generation, every square
296 * still in state INITIAL is one which was not required at any
297 * point during generation, which means we can randomly choose
298 * whether to make it SPACE or WALL.
299 *
300 * It's unclear as yet what the right strategy for wall placement
301 * should be. Too few WALLs will yield many alternative solutions
302 * to the puzzle, whereas too many might rule out so many
303 * possibilities that the intended solution becomes obvious.
304 */
305
306static void sokoban_generate(int w, int h, unsigned char *grid, int moves,
307 bool nethack, random_state *rs)
308{
309 struct pull {
310 int ox, oy, nx, ny, score;
311 };
312
313 struct pull *pulls;
314 int *dist, *prev, *heap;
315 int x, y, px, py, i, j, d, heapsize, npulls;
316
317 pulls = snewn(w * h * 4, struct pull);
318 dist = snewn(w * h, int);
319 prev = snewn(w * h, int);
320 heap = snewn(w * h, int);
321
322 /*
323 * Configure the initial grid.
324 */
325 for (y = 0; y < h; y++)
326 for (x = 0; x < w; x++)
327 grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ?
328 WALL : INITIAL);
329 if (nethack)
330 grid[1] = DEEP_PIT;
331
332 /*
333 * Place the player.
334 */
335 i = random_upto(rs, (w-2) * (h-2));
336 x = 1 + i % (w-2);
337 y = 1 + i / (w-2);
338 grid[y*w+x] = SPACE;
339 px = x;
340 py = y;
341
342 /*
343 * Now loop around making random inverse Sokoban moves. In this
344 * loop we aim to make one actual barrel-pull per iteration,
345 * plus as many free moves as are necessary to get into
346 * position for that pull.
347 */
348 while (moves-- >= 0) {
349 /*
350 * First enumerate all the viable barrel-pulls we can
351 * possibly make, counting two pulls of the same barrel in
352 * different directions as different. We also include pulls
353 * we can perform by creating a new barrel. Each pull is
354 * marked with the amount of violence it would have to do
355 * to the grid.
356 */
357 npulls = 0;
358 for (y = 0; y < h; y++)
359 for (x = 0; x < w; x++)
360 for (d = 0; d < 4; d++) {
361 int dx = DX(d);
362 int dy = DY(d);
363 int nx = x + dx, ny = y + dy;
364 int npx = nx + dx, npy = ny + dy;
365 int score = 0;
366
367 /*
368 * The candidate move is to put the player at
369 * (nx,ny), and move him to (npx,npy), pulling
370 * a barrel at (x,y) to (nx,ny). So first we
371 * must check that all those squares are within
372 * the boundaries of the grid. For this it is
373 * sufficient to check npx,npy.
374 */
375 if (npx < 0 || npx >= w || npy < 0 || npy >= h)
376 continue;
377
378 /*
379 * (x,y) must either be a barrel, or a square
380 * which we can convert into a barrel.
381 */
382 switch (grid[y*w+x]) {
383 case BARREL: case BARRELTARGET:
384 break;
385 case INITIAL:
386 if (nethack)
387 continue;
388 score += 10 /* new_barrel_score */;
389 break;
390 case DEEP_PIT:
391 if (!nethack)
392 continue;
393 break;
394 default:
395 continue;
396 }
397
398 /*
399 * (nx,ny) must either be a space, or a square
400 * which we can convert into a space.
401 */
402 switch (grid[ny*w+nx]) {
403 case SPACE: case TARGET:
404 break;
405 case INITIAL:
406 score += 3 /* new_space_score */;
407 break;
408 default:
409 continue;
410 }
411
412 /*
413 * (npx,npy) must also either be a space, or a
414 * square which we can convert into a space.
415 */
416 switch (grid[npy*w+npx]) {
417 case SPACE: case TARGET:
418 break;
419 case INITIAL:
420 score += 3 /* new_space_score */;
421 break;
422 default:
423 continue;
424 }
425
426 /*
427 * That's sufficient to tag this as a possible
428 * pull right now. We still don't know if we
429 * can reach the required player position, but
430 * that's a job for the subsequent BFS phase to
431 * tell us.
432 */
433 pulls[npulls].ox = x;
434 pulls[npulls].oy = y;
435 pulls[npulls].nx = nx;
436 pulls[npulls].ny = ny;
437 pulls[npulls].score = score;
438#ifdef GENERATION_DIAGNOSTICS
439 printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n",
440 pulls[npulls].ox, pulls[npulls].oy,
441 pulls[npulls].nx, pulls[npulls].ny,
442 pulls[npulls].score);
443#endif
444 npulls++;
445 }
446#ifdef GENERATION_DIAGNOSTICS
447 printf("found %d potential pulls\n", npulls);
448#endif
449
450 /*
451 * If there are no pulls available at all, we give up.
452 *
453 * (FIXME: or perhaps backtrack?)
454 */
455 if (npulls == 0)
456 break;
457
458 /*
459 * Now we do a BFS from our current position, to find all
460 * the squares we can get the player into.
461 *
462 * This BFS is unusually tricky. We want to give a positive
463 * distance only to squares which we have to carve through
464 * INITIALs to get to, which means we can't just stick
465 * every square we reach on the end of our to-do list.
466 * Instead, we must maintain our list as a proper priority
467 * queue.
468 */
469 for (i = 0; i < w*h; i++)
470 dist[i] = prev[i] = -1;
471
472 heap[0] = py*w+px;
473 heapsize = 1;
474 dist[py*w+px] = 0;
475
476#define PARENT(n) ( ((n)-1)/2 )
477#define LCHILD(n) ( 2*(n)+1 )
478#define RCHILD(n) ( 2*(n)+2 )
479#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
480
481 while (heapsize > 0) {
482 /*
483 * Pull the smallest element off the heap: it's at
484 * position 0. Move the arbitrary element from the very
485 * end of the heap into position 0.
486 */
487 y = heap[0] / w;
488 x = heap[0] % w;
489
490 heapsize--;
491 heap[0] = heap[heapsize];
492
493 /*
494 * Now repeatedly move that arbitrary element down the
495 * heap by swapping it with the more suitable of its
496 * children.
497 */
498 i = 0;
499 while (1) {
500 int lc, rc;
501
502 lc = LCHILD(i);
503 rc = RCHILD(i);
504
505 if (lc >= heapsize)
506 break; /* we've hit bottom */
507
508 if (rc >= heapsize) {
509 /*
510 * Special case: there is only one child to
511 * check.
512 */
513 if (dist[heap[i]] > dist[heap[lc]])
514 SWAP(heap[i], heap[lc]);
515
516 /* _Now_ we've hit bottom. */
517 break;
518 } else {
519 /*
520 * The common case: there are two children and
521 * we must check them both.
522 */
523 if (dist[heap[i]] > dist[heap[lc]] ||
524 dist[heap[i]] > dist[heap[rc]]) {
525 /*
526 * Pick the more appropriate child to swap with
527 * (i.e. the one which would want to be the
528 * parent if one were above the other - as one
529 * is about to be).
530 */
531 if (dist[heap[lc]] > dist[heap[rc]]) {
532 SWAP(heap[i], heap[rc]);
533 i = rc;
534 } else {
535 SWAP(heap[i], heap[lc]);
536 i = lc;
537 }
538 } else {
539 /* This element is in the right place; we're done. */
540 break;
541 }
542 }
543 }
544
545 /*
546 * OK, that's given us (x,y) for this phase of the
547 * search. Now try all directions from here.
548 */
549
550 for (d = 0; d < 4; d++) {
551 int dx = DX(d);
552 int dy = DY(d);
553 int nx = x + dx, ny = y + dy;
554 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
555 continue;
556 if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET &&
557 grid[ny*w+nx] != INITIAL)
558 continue;
559 if (dist[ny*w+nx] == -1) {
560 dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL);
561 prev[ny*w+nx] = y*w+x;
562
563 /*
564 * Now insert ny*w+nx at the end of the heap,
565 * and move it down to its appropriate resting
566 * place.
567 */
568 i = heapsize;
569 heap[heapsize++] = ny*w+nx;
570
571 /*
572 * Swap element n with its parent repeatedly to
573 * preserve the heap property.
574 */
575
576 while (i > 0) {
577 int p = PARENT(i);
578
579 if (dist[heap[p]] > dist[heap[i]]) {
580 SWAP(heap[p], heap[i]);
581 i = p;
582 } else
583 break;
584 }
585 }
586 }
587 }
588
589#undef PARENT
590#undef LCHILD
591#undef RCHILD
592#undef SWAP
593
594#ifdef GENERATION_DIAGNOSTICS
595 printf("distance map:\n");
596 for (i = 0; i < h; i++) {
597 for (j = 0; j < w; j++) {
598 int d = dist[i*w+j];
599 int c;
600 if (d < 0)
601 c = '#';
602 else if (d >= 36)
603 c = '!';
604 else if (d >= 10)
605 c = 'A' - 10 + d;
606 else
607 c = '0' + d;
608 putchar(c);
609 }
610 putchar('\n');
611 }
612#endif
613
614 /*
615 * Now we can go back through the `pulls' array, adjusting
616 * the score for each pull depending on how hard it is to
617 * reach its starting point, and also throwing out any
618 * whose starting points are genuinely unreachable even
619 * with the possibility of carving through INITIAL squares.
620 */
621 for (i = j = 0; i < npulls; i++) {
622#ifdef GENERATION_DIAGNOSTICS
623 printf("potential pull (%d,%d)-(%d,%d)",
624 pulls[i].ox, pulls[i].oy,
625 pulls[i].nx, pulls[i].ny);
626#endif
627 x = pulls[i].nx;
628 y = pulls[i].ny;
629 if (dist[y*w+x] < 0) {
630#ifdef GENERATION_DIAGNOSTICS
631 printf(" unreachable\n");
632#endif
633 continue; /* this pull isn't feasible at all */
634 } else {
635 /*
636 * Another nasty special case we have to check is
637 * whether the initial barrel location (ox,oy) is
638 * on the path used to reach the square. This can
639 * occur if that square is in state INITIAL: the
640 * pull is initially considered valid on the basis
641 * that the INITIAL can become BARRELTARGET, and
642 * it's also considered reachable on the basis that
643 * INITIAL can be turned into SPACE, but it can't
644 * be both at once.
645 *
646 * Fortunately, if (ox,oy) is on the path at all,
647 * it must be only one space from the end, so this
648 * is easy to spot and rule out.
649 */
650 if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) {
651#ifdef GENERATION_DIAGNOSTICS
652 printf(" goes through itself\n");
653#endif
654 continue; /* this pull isn't feasible at all */
655 }
656 pulls[j] = pulls[i]; /* structure copy */
657 pulls[j].score += dist[y*w+x] * 3 /* new_space_score */;
658#ifdef GENERATION_DIAGNOSTICS
659 printf(" reachable at distance %d (cost now %d)\n",
660 dist[y*w+x], pulls[j].score);
661#endif
662 j++;
663 }
664 }
665 npulls = j;
666
667 /*
668 * Again, if there are no pulls available at all, we give
669 * up.
670 *
671 * (FIXME: or perhaps backtrack?)
672 */
673 if (npulls == 0)
674 break;
675
676 /*
677 * Now choose which pull to make. On the one hand we should
678 * prefer pulls which do less damage to the INITIAL squares
679 * (thus, ones for which we can already get into position
680 * via existing SPACEs, and for which the barrel already
681 * exists and doesn't have to be invented); on the other,
682 * we want to avoid _always_ preferring such pulls, on the
683 * grounds that that will lead to levels without very much
684 * stuff in.
685 *
686 * When creating new barrels, we prefer creations which are
687 * next to existing TARGET squares.
688 *
689 * FIXME: for the moment I'll make this very simple indeed.
690 */
691 i = random_upto(rs, npulls);
692
693 /*
694 * Actually make the pull, including carving a path to get
695 * to the site if necessary.
696 */
697 x = pulls[i].nx;
698 y = pulls[i].ny;
699 while (prev[y*w+x] >= 0) {
700 int p;
701
702 if (grid[y*w+x] == INITIAL)
703 grid[y*w+x] = SPACE;
704
705 p = prev[y*w+x];
706 y = p / w;
707 x = p % w;
708 }
709 px = 2*pulls[i].nx - pulls[i].ox;
710 py = 2*pulls[i].ny - pulls[i].oy;
711 if (grid[py*w+px] == INITIAL)
712 grid[py*w+px] = SPACE;
713 if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET)
714 grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET;
715 else
716 grid[pulls[i].ny*w+pulls[i].nx] = BARREL;
717 if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL)
718 grid[pulls[i].oy*w+pulls[i].ox] = SPACE;
719 else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT)
720 grid[pulls[i].oy*w+pulls[i].ox] = TARGET;
721 }
722
723 sfree(heap);
724 sfree(prev);
725 sfree(dist);
726 sfree(pulls);
727
728 if (grid[py*w+px] == TARGET)
729 grid[py*w+px] = PLAYERTARGET;
730 else
731 grid[py*w+px] = PLAYER;
732}
733
734static char *new_game_desc(const game_params *params, random_state *rs,
735 char **aux, bool interactive)
736{
737 int w = params->w, h = params->h;
738 char *desc;
739 int desclen, descpos, descsize, prev, count;
740 unsigned char *grid;
741 int i, j;
742
743 /*
744 * FIXME: perhaps some more interesting means of choosing how
745 * many moves to try?
746 */
747 grid = snewn(w*h, unsigned char);
748 sokoban_generate(w, h, grid, w*h, false, rs);
749
750 desclen = descpos = descsize = 0;
751 desc = NULL;
752 prev = -1;
753 count = 0;
754 for (i = 0; i < w*h; i++) {
755 if (descsize < desclen + 40) {
756 descsize = desclen + 100;
757 desc = sresize(desc, descsize, char);
758 desc[desclen] = '\0';
759 }
760 switch (grid[i]) {
761 case INITIAL:
762 j = 'w'; /* FIXME: make some of these 's'? */
763 break;
764 case SPACE:
765 j = 's';
766 break;
767 case WALL:
768 j = 'w';
769 break;
770 case TARGET:
771 j = 't';
772 break;
773 case BARREL:
774 j = 'b';
775 break;
776 case BARRELTARGET:
777 j = 'f';
778 break;
779 case DEEP_PIT:
780 j = 'd';
781 break;
782 case PLAYER:
783 j = 'u';
784 break;
785 case PLAYERTARGET:
786 j = 'v';
787 break;
788 default:
789 j = '?';
790 break;
791 }
792 assert(j != '?');
793 if (j != prev) {
794 desc[desclen++] = j;
795 descpos = desclen;
796 prev = j;
797 count = 1;
798 } else {
799 count++;
800 desclen = descpos + sprintf(desc+descpos, "%d", count);
801 }
802 }
803
804 sfree(grid);
805
806 return desc;
807}
808
809static const char *validate_desc(const game_params *params, const char *desc)
810{
811 int w = params->w, h = params->h;
812 int area = 0;
813 int nplayers = 0;
814
815 while (*desc) {
816 int c = *desc++;
817 int n = 1;
818 if (*desc && isdigit((unsigned char)*desc)) {
819 n = atoi(desc);
820 while (*desc && isdigit((unsigned char)*desc)) desc++;
821 }
822
823 area += n;
824
825 if (c == PLAYER || c == PLAYERTARGET)
826 nplayers += n;
827 else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET ||
828 c == PIT || c == DEEP_PIT || IS_BARREL(c))
829 /* ok */;
830 else
831 return "Invalid character in game description";
832 }
833
834 if (area > w*h)
835 return "Too much data in game description";
836 if (area < w*h)
837 return "Too little data in game description";
838 if (nplayers < 1)
839 return "No starting player position specified";
840 if (nplayers > 1)
841 return "More than one starting player position specified";
842
843 return NULL;
844}
845
846static game_state *new_game(midend *me, const game_params *params,
847 const char *desc)
848{
849 int w = params->w, h = params->h;
850 game_state *state = snew(game_state);
851 int i;
852
853 state->p = *params; /* structure copy */
854 state->grid = snewn(w*h, unsigned char);
855 state->px = state->py = -1;
856 state->completed = false;
857
858 i = 0;
859
860 while (*desc) {
861 int c = *desc++;
862 int n = 1;
863 if (*desc && isdigit((unsigned char)*desc)) {
864 n = atoi(desc);
865 while (*desc && isdigit((unsigned char)*desc)) desc++;
866 }
867
868 if (c == PLAYER || c == PLAYERTARGET) {
869 state->py = i / w;
870 state->px = i % w;
871 c = IS_ON_TARGET(c) ? TARGET : SPACE;
872 }
873
874 while (n-- > 0)
875 state->grid[i++] = c;
876 }
877
878 assert(i == w*h);
879 assert(state->px != -1 && state->py != -1);
880
881 return state;
882}
883
884static game_state *dup_game(const game_state *state)
885{
886 int w = state->p.w, h = state->p.h;
887 game_state *ret = snew(game_state);
888
889 ret->p = state->p; /* structure copy */
890 ret->grid = snewn(w*h, unsigned char);
891 memcpy(ret->grid, state->grid, w*h);
892 ret->px = state->px;
893 ret->py = state->py;
894 ret->completed = state->completed;
895
896 return ret;
897}
898
899static void free_game(game_state *state)
900{
901 sfree(state->grid);
902 sfree(state);
903}
904
905static char *solve_game(const game_state *state, const game_state *currstate,
906 const char *aux, const char **error)
907{
908 return NULL;
909}
910
911static bool game_can_format_as_text_now(const game_params *params)
912{
913 return true;
914}
915
916static char *game_text_format(const game_state *state)
917{
918 return NULL;
919}
920
921static game_ui *new_ui(const game_state *state)
922{
923 return NULL;
924}
925
926static void free_ui(game_ui *ui)
927{
928}
929
930static void game_changed_state(game_ui *ui, const game_state *oldstate,
931 const game_state *newstate)
932{
933}
934
935struct game_drawstate {
936 game_params p;
937 int tilesize;
938 bool started;
939 unsigned short *grid;
940};
941
942#define PREFERRED_TILESIZE 32
943#define TILESIZE (ds->tilesize)
944#define BORDER (TILESIZE)
945#define HIGHLIGHT_WIDTH (TILESIZE / 10)
946#define COORD(x) ( (x) * TILESIZE + BORDER )
947#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
948
949/*
950 * I'm going to need to do most of the move-type analysis in both
951 * interpret_move and execute_move, so I'll abstract it out into a
952 * subfunction. move_type() returns -1 for an illegal move, 0 for a
953 * movement, and 1 for a push.
954 */
955static int move_type(const game_state *state, int dx, int dy)
956{
957 int w = state->p.w, h = state->p.h;
958 int px = state->px, py = state->py;
959 int nx, ny, nbx, nby;
960
961 assert(dx >= -1 && dx <= +1);
962 assert(dy >= -1 && dy <= +1);
963 assert(dx || dy);
964
965 nx = px + dx;
966 ny = py + dy;
967
968 /*
969 * Disallow any move that goes off the grid.
970 */
971 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
972 return -1;
973
974 /*
975 * Examine the target square of the move to see whether it's a
976 * space, a barrel, or a wall.
977 */
978
979 if (state->grid[ny*w+nx] == WALL ||
980 state->grid[ny*w+nx] == PIT ||
981 state->grid[ny*w+nx] == DEEP_PIT)
982 return -1; /* this one's easy; just disallow it */
983
984 if (IS_BARREL(state->grid[ny*w+nx])) {
985 /*
986 * This is a push move. For a start, that means it must not
987 * be diagonal.
988 */
989 if (dy && dx)
990 return -1;
991
992 /*
993 * Now find the location of the third square involved in
994 * the push, and stop if it's off the edge.
995 */
996 nbx = nx + dx;
997 nby = ny + dy;
998 if (nbx < 0 || nbx >= w || nby < 0 || nby >= h)
999 return -1;
1000
1001 /*
1002 * That third square must be able to accept a barrel.
1003 */
1004 if (state->grid[nby*w+nbx] == SPACE ||
1005 state->grid[nby*w+nbx] == TARGET ||
1006 state->grid[nby*w+nbx] == PIT ||
1007 state->grid[nby*w+nbx] == DEEP_PIT) {
1008 /*
1009 * The push is valid.
1010 */
1011 return 1;
1012 } else {
1013 return -1;
1014 }
1015 } else {
1016 /*
1017 * This is just an ordinary move. We've already checked the
1018 * target square, so the only thing left to check is that a
1019 * diagonal move has a space on one side to have notionally
1020 * gone through.
1021 */
1022 if (dx && dy &&
1023 state->grid[(py+dy)*w+px] != SPACE &&
1024 state->grid[(py+dy)*w+px] != TARGET &&
1025 state->grid[py*w+(px+dx)] != SPACE &&
1026 state->grid[py*w+(px+dx)] != TARGET)
1027 return -1;
1028
1029 /*
1030 * Otherwise, the move is valid.
1031 */
1032 return 0;
1033 }
1034}
1035
1036static char *interpret_move(const game_state *state, game_ui *ui,
1037 const game_drawstate *ds,
1038 int x, int y, int button)
1039{
1040 int dx=0, dy=0;
1041 char *move;
1042
1043 /*
1044 * Diagonal movement is supported as it is in NetHack: it's
1045 * for movement only (never pushing), and one of the two
1046 * squares adjacent to both the source and destination
1047 * squares must be free to move through. In other words, it
1048 * is only a shorthand for two orthogonal moves and cannot
1049 * change the nature of the actual puzzle game.
1050 */
1051 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1052 dx = 0, dy = -1;
1053 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1054 dx = 0, dy = +1;
1055 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1056 dx = -1, dy = 0;
1057 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1058 dx = +1, dy = 0;
1059 else if (button == (MOD_NUM_KEYPAD | '7'))
1060 dx = -1, dy = -1;
1061 else if (button == (MOD_NUM_KEYPAD | '9'))
1062 dx = +1, dy = -1;
1063 else if (button == (MOD_NUM_KEYPAD | '1'))
1064 dx = -1, dy = +1;
1065 else if (button == (MOD_NUM_KEYPAD | '3'))
1066 dx = +1, dy = +1;
1067 else if (button == LEFT_BUTTON)
1068 {
1069 if(x < COORD(state->px))
1070 dx = -1;
1071 else if (x > COORD(state->px + 1))
1072 dx = 1;
1073 if(y < COORD(state->py))
1074 dy = -1;
1075 else if (y > COORD(state->py + 1))
1076 dy = 1;
1077 }
1078 else
1079 return NULL;
1080
1081 if((dx == 0) && (dy == 0))
1082 return(NULL);
1083
1084 if (move_type(state, dx, dy) < 0)
1085 return NULL;
1086
1087 move = snewn(2, char);
1088 move[1] = '\0';
1089 move[0] = '5' - 3*dy + dx;
1090 return move;
1091}
1092
1093static game_state *execute_move(const game_state *state, const char *move)
1094{
1095 int w = state->p.w, h = state->p.h;
1096 int px = state->px, py = state->py;
1097 int dx, dy, nx, ny, nbx, nby, type, m, i;
1098 bool freebarrels, freetargets;
1099 game_state *ret;
1100
1101 if (*move < '1' || *move == '5' || *move > '9' || move[1])
1102 return NULL; /* invalid move string */
1103
1104 m = *move - '0';
1105 dx = (m + 2) % 3 - 1;
1106 dy = 2 - (m + 2) / 3;
1107 type = move_type(state, dx, dy);
1108 if (type < 0)
1109 return NULL;
1110
1111 ret = dup_game(state);
1112
1113 nx = px + dx;
1114 ny = py + dy;
1115 nbx = nx + dx;
1116 nby = ny + dy;
1117
1118 if (type) {
1119 int b;
1120
1121 /*
1122 * Push.
1123 */
1124 b = ret->grid[ny*w+nx];
1125 if (IS_ON_TARGET(b)) {
1126 ret->grid[ny*w+nx] = TARGET;
1127 b = DETARGETISE(b);
1128 } else
1129 ret->grid[ny*w+nx] = SPACE;
1130
1131 if (ret->grid[nby*w+nbx] == PIT)
1132 ret->grid[nby*w+nbx] = SPACE;
1133 else if (ret->grid[nby*w+nbx] == DEEP_PIT)
1134 /* do nothing - the pit eats the barrel and remains there */;
1135 else if (ret->grid[nby*w+nbx] == TARGET)
1136 ret->grid[nby*w+nbx] = TARGETISE(b);
1137 else
1138 ret->grid[nby*w+nbx] = b;
1139 }
1140
1141 ret->px = nx;
1142 ret->py = ny;
1143
1144 /*
1145 * Check for completion. This is surprisingly complicated,
1146 * given the presence of pits and deep pits, and also the fact
1147 * that some Sokoban levels with pits have fewer pits than
1148 * barrels (due to providing spares, e.g. NetHack's). I think
1149 * the completion condition in fact must be that the game
1150 * cannot become any _more_ complete. That is, _either_ there
1151 * are no remaining barrels not on targets, _or_ there is a
1152 * good reason why any such barrels cannot be placed. The only
1153 * available good reason is that there are no remaining pits,
1154 * no free target squares, and no deep pits at all.
1155 */
1156 if (!ret->completed) {
1157 freebarrels = false;
1158 freetargets = false;
1159 for (i = 0; i < w*h; i++) {
1160 int v = ret->grid[i];
1161
1162 if (IS_BARREL(v) && !IS_ON_TARGET(v))
1163 freebarrels = true;
1164 if (v == DEEP_PIT || v == PIT ||
1165 (!IS_BARREL(v) && IS_ON_TARGET(v)))
1166 freetargets = true;
1167 }
1168
1169 if (!freebarrels || !freetargets)
1170 ret->completed = true;
1171 }
1172
1173 return ret;
1174}
1175
1176/* ----------------------------------------------------------------------
1177 * Drawing routines.
1178 */
1179
1180static void game_compute_size(const game_params *params, int tilesize,
1181 const game_ui *ui, int *x, int *y)
1182{
1183 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1184 struct { int tilesize; } ads, *ds = &ads;
1185 ads.tilesize = tilesize;
1186
1187 *x = 2 * BORDER + 1 + params->w * TILESIZE;
1188 *y = 2 * BORDER + 1 + params->h * TILESIZE;
1189}
1190
1191static void game_set_size(drawing *dr, game_drawstate *ds,
1192 const game_params *params, int tilesize)
1193{
1194 ds->tilesize = tilesize;
1195}
1196
1197static float *game_colours(frontend *fe, int *ncolours)
1198{
1199 float *ret = snewn(3 * NCOLOURS, float);
1200 int i;
1201
1202 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1203
1204 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1205 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1206 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1207
1208 ret[COL_PLAYER * 3 + 0] = 0.0F;
1209 ret[COL_PLAYER * 3 + 1] = 1.0F;
1210 ret[COL_PLAYER * 3 + 2] = 0.0F;
1211
1212 ret[COL_BARREL * 3 + 0] = 0.6F;
1213 ret[COL_BARREL * 3 + 1] = 0.3F;
1214 ret[COL_BARREL * 3 + 2] = 0.0F;
1215
1216 ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1217 ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1218 ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1219
1220 ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2;
1221 ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2;
1222 ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2;
1223
1224 ret[COL_DEEP_PIT * 3 + 0] = 0.0F;
1225 ret[COL_DEEP_PIT * 3 + 1] = 0.0F;
1226 ret[COL_DEEP_PIT * 3 + 2] = 0.0F;
1227
1228 ret[COL_TEXT * 3 + 0] = 1.0F;
1229 ret[COL_TEXT * 3 + 1] = 1.0F;
1230 ret[COL_TEXT * 3 + 2] = 1.0F;
1231
1232 ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1233 ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1234 ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1235
1236 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1237 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1238 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1239
1240 for (i = 0; i < 3; i++) {
1241 ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] +
1242 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4;
1243 }
1244
1245 *ncolours = NCOLOURS;
1246 return ret;
1247}
1248
1249static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1250{
1251 int w = state->p.w, h = state->p.h;
1252 struct game_drawstate *ds = snew(struct game_drawstate);
1253 int i;
1254
1255 ds->tilesize = 0;
1256 ds->p = state->p; /* structure copy */
1257 ds->grid = snewn(w*h, unsigned short);
1258 for (i = 0; i < w*h; i++)
1259 ds->grid[i] = INVALID;
1260 ds->started = false;
1261
1262 return ds;
1263}
1264
1265static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1266{
1267 sfree(ds->grid);
1268 sfree(ds);
1269}
1270
1271static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v)
1272{
1273 int tx = COORD(x), ty = COORD(y);
1274 int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND);
1275
1276 v &= 0xFF;
1277
1278 clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
1279 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg);
1280
1281 if (v == WALL) {
1282 int coords[6];
1283
1284 coords[0] = tx + TILESIZE;
1285 coords[1] = ty + TILESIZE;
1286 coords[2] = tx + TILESIZE;
1287 coords[3] = ty + 1;
1288 coords[4] = tx + 1;
1289 coords[5] = ty + TILESIZE;
1290 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1291
1292 coords[0] = tx + 1;
1293 coords[1] = ty + 1;
1294 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1295
1296 draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH,
1297 TILESIZE - 2*HIGHLIGHT_WIDTH,
1298 TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL);
1299 } else if (v == PIT) {
1300 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1301 TILESIZE*3/7, COL_PIT, COL_OUTLINE);
1302 } else if (v == DEEP_PIT) {
1303 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1304 TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE);
1305 } else {
1306 if (IS_ON_TARGET(v)) {
1307 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1308 TILESIZE*3/7, COL_TARGET, COL_OUTLINE);
1309 }
1310 if (IS_PLAYER(v)) {
1311 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1312 TILESIZE/3, COL_PLAYER, COL_OUTLINE);
1313 } else if (IS_BARREL(v)) {
1314 char str[2];
1315
1316 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1317 TILESIZE/3, COL_BARREL, COL_OUTLINE);
1318 str[1] = '\0';
1319 str[0] = BARREL_LABEL(v);
1320 if (str[0]) {
1321 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1322 FONT_VARIABLE, TILESIZE/2,
1323 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str);
1324 }
1325 }
1326 }
1327
1328 unclip(dr);
1329 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1330}
1331
1332static void game_redraw(drawing *dr, game_drawstate *ds,
1333 const game_state *oldstate, const game_state *state,
1334 int dir, const game_ui *ui,
1335 float animtime, float flashtime)
1336{
1337 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1338 int x, y;
1339 int flashtype;
1340
1341 if (flashtime &&
1342 !((int)(flashtime * 3 / FLASH_LENGTH) % 2))
1343 flashtype = 0x100;
1344 else
1345 flashtype = 0;
1346
1347 /*
1348 * Initialise a fresh drawstate.
1349 */
1350 if (!ds->started) {
1351 /*
1352 * Draw the grid lines.
1353 */
1354 for (y = 0; y <= h; y++)
1355 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y),
1356 COL_LOWLIGHT);
1357 for (x = 0; x <= w; x++)
1358 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h),
1359 COL_LOWLIGHT);
1360
1361 ds->started = true;
1362 }
1363
1364 /*
1365 * Draw the grid contents.
1366 */
1367 for (y = 0; y < h; y++)
1368 for (x = 0; x < w; x++) {
1369 int v = state->grid[y*w+x];
1370 if (y == state->py && x == state->px) {
1371 if (v == TARGET)
1372 v = PLAYERTARGET;
1373 else {
1374 assert(v == SPACE);
1375 v = PLAYER;
1376 }
1377 }
1378
1379 v |= flashtype;
1380
1381 if (ds->grid[y*w+x] != v) {
1382 draw_tile(dr, ds, x, y, v);
1383 ds->grid[y*w+x] = v;
1384 }
1385 }
1386
1387}
1388
1389static float game_anim_length(const game_state *oldstate,
1390 const game_state *newstate, int dir, game_ui *ui)
1391{
1392 return 0.0F;
1393}
1394
1395static float game_flash_length(const game_state *oldstate,
1396 const game_state *newstate, int dir, game_ui *ui)
1397{
1398 if (!oldstate->completed && newstate->completed)
1399 return FLASH_LENGTH;
1400 else
1401 return 0.0F;
1402}
1403
1404static void game_get_cursor_location(const game_ui *ui,
1405 const game_drawstate *ds,
1406 const game_state *state,
1407 const game_params *params,
1408 int *x, int *y, int *w, int *h)
1409{
1410}
1411
1412static int game_status(const game_state *state)
1413{
1414 return state->completed ? +1 : 0;
1415}
1416
1417static bool game_timing_state(const game_state *state, game_ui *ui)
1418{
1419 return true;
1420}
1421
1422static void game_print_size(const game_params *params, const game_ui *ui,
1423 float *x, float *y)
1424{
1425}
1426
1427static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1428 int tilesize)
1429{
1430}
1431
1432#ifdef COMBINED
1433#define thegame sokoban
1434#endif
1435
1436const struct game thegame = {
1437 "Sokoban", NULL, NULL,
1438 default_params,
1439 game_fetch_preset, NULL,
1440 decode_params,
1441 encode_params,
1442 free_params,
1443 dup_params,
1444 true, game_configure, custom_params,
1445 validate_params,
1446 new_game_desc,
1447 validate_desc,
1448 new_game,
1449 dup_game,
1450 free_game,
1451 false, solve_game,
1452 false, game_can_format_as_text_now, game_text_format,
1453 NULL, NULL, /* get_prefs, set_prefs */
1454 new_ui,
1455 free_ui,
1456 NULL, /* encode_ui */
1457 NULL, /* decode_ui */
1458 NULL, /* game_request_keys */
1459 game_changed_state,
1460 NULL, /* current_key_label */
1461 interpret_move,
1462 execute_move,
1463 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1464 game_colours,
1465 game_new_drawstate,
1466 game_free_drawstate,
1467 game_redraw,
1468 game_anim_length,
1469 game_flash_length,
1470 game_get_cursor_location,
1471 game_status,
1472 false, false, game_print_size, game_print,
1473 false, /* wants_statusbar */
1474 false, game_timing_state,
1475 0, /* flags */
1476};
diff --git a/apps/plugins/puzzles/src/unruly.R b/apps/plugins/puzzles/src/unruly.R
deleted file mode 100644
index 064ccc35ba..0000000000
--- a/apps/plugins/puzzles/src/unruly.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3unruly : [X] GTK COMMON unruly unruly-icon|no-icon
4unruly : [G] WINDOWS COMMON unruly unruly.res|noicon.res
5
6unrulysolver : [U] unruly[STANDALONE_SOLVER] STANDALONE
7unrulysolver : [C] unruly[STANDALONE_SOLVER] STANDALONE
8
9ALL += unruly[COMBINED]
10
11!begin am gtk
12GAMES += unruly
13!end
14
15!begin >list.c
16 A(unruly) \
17!end
18
19!begin >gamedesc.txt
20unruly:unruly.exe:Unruly:Black and white grid puzzle:Fill in the black and white grid to avoid runs of three.
21!end
diff --git a/apps/plugins/puzzles/src/unruly.c b/apps/plugins/puzzles/src/unruly.c
index a1f06332e0..01685b7838 100644
--- a/apps/plugins/puzzles/src/unruly.c
+++ b/apps/plugins/puzzles/src/unruly.c
@@ -47,12 +47,16 @@
47#include <string.h> 47#include <string.h>
48#include <assert.h> 48#include <assert.h>
49#include <ctype.h> 49#include <ctype.h>
50#include <math.h> 50#ifdef NO_TGMATH_H
51# include <math.h>
52#else
53# include <tgmath.h>
54#endif
51 55
52#include "puzzles.h" 56#include "puzzles.h"
53 57
54#ifdef STANDALONE_SOLVER 58#ifdef STANDALONE_SOLVER
55bool solver_verbose = false; 59static bool solver_verbose = false;
56#endif 60#endif
57 61
58enum { 62enum {
@@ -81,6 +85,7 @@ struct game_params {
81 int diff; 85 int diff;
82}; 86};
83#define DIFFLIST(A) \ 87#define DIFFLIST(A) \
88 A(TRIVIAL,Trivial, t) \
84 A(EASY,Easy, e) \ 89 A(EASY,Easy, e) \
85 A(NORMAL,Normal, n) \ 90 A(NORMAL,Normal, n) \
86 91
@@ -95,6 +100,7 @@ static char const unruly_diffchars[] = DIFFLIST(ENCODE);
95#define DIFFCONFIG DIFFLIST(CONFIG) 100#define DIFFCONFIG DIFFLIST(CONFIG)
96 101
97static const struct game_params unruly_presets[] = { 102static const struct game_params unruly_presets[] = {
103 { 8, 8, false, DIFF_TRIVIAL},
98 { 8, 8, false, DIFF_EASY}, 104 { 8, 8, false, DIFF_EASY},
99 { 8, 8, false, DIFF_NORMAL}, 105 { 8, 8, false, DIFF_NORMAL},
100 {10, 10, false, DIFF_EASY}, 106 {10, 10, false, DIFF_EASY},
@@ -284,6 +290,8 @@ static const char *validate_params(const game_params *params, bool full)
284 return "Width and height must both be even"; 290 return "Width and height must both be even";
285 if (params->w2 < 6 || params->h2 < 6) 291 if (params->w2 < 6 || params->h2 < 6)
286 return "Width and height must be at least 6"; 292 return "Width and height must be at least 6";
293 if (params->w2 > INT_MAX / params->h2)
294 return "Width times height must not be unreasonably large";
287 if (params->unique) { 295 if (params->unique) {
288 static const long A177790[] = { 296 static const long A177790[] = {
289 /* 297 /*
@@ -349,7 +357,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
349 return NULL; 357 return NULL;
350} 358}
351 359
352static game_state *blank_state(int w2, int h2, bool unique) 360static game_state *blank_state(int w2, int h2, bool unique, bool new_common)
353{ 361{
354 game_state *state = snew(game_state); 362 game_state *state = snew(game_state);
355 int s = w2 * h2; 363 int s = w2 * h2;
@@ -358,12 +366,14 @@ static game_state *blank_state(int w2, int h2, bool unique)
358 state->h2 = h2; 366 state->h2 = h2;
359 state->unique = unique; 367 state->unique = unique;
360 state->grid = snewn(s, char); 368 state->grid = snewn(s, char);
361 state->common = snew(unruly_common);
362 state->common->refcount = 1;
363 state->common->immutable = snewn(s, bool);
364
365 memset(state->grid, EMPTY, s); 369 memset(state->grid, EMPTY, s);
366 memset(state->common->immutable, 0, s*sizeof(bool)); 370
371 if (new_common) {
372 state->common = snew(unruly_common);
373 state->common->refcount = 1;
374 state->common->immutable = snewn(s, bool);
375 memset(state->common->immutable, 0, s*sizeof(bool));
376 }
367 377
368 state->completed = state->cheated = false; 378 state->completed = state->cheated = false;
369 379
@@ -376,7 +386,7 @@ static game_state *new_game(midend *me, const game_params *params,
376 int w2 = params->w2, h2 = params->h2; 386 int w2 = params->w2, h2 = params->h2;
377 int s = w2 * h2; 387 int s = w2 * h2;
378 388
379 game_state *state = blank_state(w2, h2, params->unique); 389 game_state *state = blank_state(w2, h2, params->unique, true);
380 390
381 const char *p = desc; 391 const char *p = desc;
382 int pos = 0; 392 int pos = 0;
@@ -413,7 +423,7 @@ static game_state *dup_game(const game_state *state)
413 int w2 = state->w2, h2 = state->h2; 423 int w2 = state->w2, h2 = state->h2;
414 int s = w2 * h2; 424 int s = w2 * h2;
415 425
416 game_state *ret = blank_state(w2, h2, state->unique); 426 game_state *ret = blank_state(w2, h2, state->unique, false);
417 427
418 memcpy(ret->grid, state->grid, s); 428 memcpy(ret->grid, state->grid, s);
419 ret->common = state->common; 429 ret->common = state->common;
@@ -743,6 +753,61 @@ static int unruly_solver_fill_row(game_state *state, int i, bool horizontal,
743 return ret; 753 return ret;
744} 754}
745 755
756static int unruly_solver_check_single_gap(game_state *state,
757 int *complete, bool horizontal,
758 int *rowcount, int *colcount,
759 char fill)
760{
761 int w2 = state->w2, h2 = state->h2;
762 int count = (horizontal ? h2 : w2); /* number of rows to check */
763 int target = (horizontal ? w2 : h2) / 2; /* target number of 0s/1s */
764 int *other = (horizontal ? rowcount : colcount);
765
766 int ret = 0;
767
768 int i;
769 /* Check for completed rows/cols for one number, then fill in the rest */
770 for (i = 0; i < count; i++) {
771 if (complete[i] == target && other[i] == target - 1) {
772#ifdef STANDALONE_SOLVER
773 if (solver_verbose) {
774 printf("Solver: Row %i has only one square left which must be "
775 "%c\n", i, (fill == N_ZERO ? '0' : '1'));
776 }
777#endif
778 ret += unruly_solver_fill_row(state, i, horizontal, rowcount,
779 colcount, fill);
780 }
781 }
782
783 return ret;
784}
785
786static int unruly_solver_check_all_single_gap(game_state *state,
787 struct unruly_scratch *scratch)
788{
789 int ret = 0;
790
791 ret +=
792 unruly_solver_check_single_gap(state, scratch->ones_rows, true,
793 scratch->zeros_rows,
794 scratch->zeros_cols, N_ZERO);
795 ret +=
796 unruly_solver_check_single_gap(state, scratch->ones_cols, false,
797 scratch->zeros_rows,
798 scratch->zeros_cols, N_ZERO);
799 ret +=
800 unruly_solver_check_single_gap(state, scratch->zeros_rows, true,
801 scratch->ones_rows,
802 scratch->ones_cols, N_ONE);
803 ret +=
804 unruly_solver_check_single_gap(state, scratch->zeros_cols, false,
805 scratch->ones_rows,
806 scratch->ones_cols, N_ONE);
807
808 return ret;
809}
810
746static int unruly_solver_check_complete_nums(game_state *state, 811static int unruly_solver_check_complete_nums(game_state *state,
747 int *complete, bool horizontal, 812 int *complete, bool horizontal,
748 int *rowcount, int *colcount, 813 int *rowcount, int *colcount,
@@ -1140,11 +1205,24 @@ static int unruly_solve_game(game_state *state,
1140 1205
1141 /* Keep using the simpler techniques while they produce results */ 1206 /* Keep using the simpler techniques while they produce results */
1142 if (done) { 1207 if (done) {
1143 if (maxdiff < DIFF_EASY) 1208 if (maxdiff < DIFF_TRIVIAL)
1144 maxdiff = DIFF_EASY; 1209 maxdiff = DIFF_TRIVIAL;
1145 continue; 1210 continue;
1146 } 1211 }
1147 1212
1213 /* Check for rows with only one unfilled square */
1214 done += unruly_solver_check_all_single_gap(state, scratch);
1215
1216 if (done) {
1217 if (maxdiff < DIFF_TRIVIAL)
1218 maxdiff = DIFF_TRIVIAL;
1219 continue;
1220 }
1221
1222 /* Easy techniques */
1223 if (diff < DIFF_EASY)
1224 break;
1225
1148 /* Check for completed rows */ 1226 /* Check for completed rows */
1149 done += unruly_solver_check_all_complete_nums(state, scratch); 1227 done += unruly_solver_check_all_complete_nums(state, scratch);
1150 1228
@@ -1302,7 +1380,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1302 1380
1303 while (true) { 1381 while (true) {
1304 attempts++; 1382 attempts++;
1305 state = blank_state(w2, h2, params->unique); 1383 state = blank_state(w2, h2, params->unique, true);
1306 scratch = unruly_new_scratch(state); 1384 scratch = unruly_new_scratch(state);
1307 if (unruly_fill_game(state, scratch, rs)) 1385 if (unruly_fill_game(state, scratch, rs))
1308 break; 1386 break;
@@ -1320,6 +1398,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1320 temp_verbose = solver_verbose; 1398 temp_verbose = solver_verbose;
1321 solver_verbose = false; 1399 solver_verbose = false;
1322 } 1400 }
1401#else
1402 (void)attempts;
1323#endif 1403#endif
1324 1404
1325 unruly_free_scratch(scratch); 1405 unruly_free_scratch(scratch);
@@ -1439,7 +1519,7 @@ static game_ui *new_ui(const game_state *state)
1439 game_ui *ret = snew(game_ui); 1519 game_ui *ret = snew(game_ui);
1440 1520
1441 ret->cx = ret->cy = 0; 1521 ret->cx = ret->cy = 0;
1442 ret->cursor = false; 1522 ret->cursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1443 1523
1444 return ret; 1524 return ret;
1445} 1525}
@@ -1449,18 +1529,30 @@ static void free_ui(game_ui *ui)
1449 sfree(ui); 1529 sfree(ui);
1450} 1530}
1451 1531
1452static char *encode_ui(const game_ui *ui) 1532static void game_changed_state(game_ui *ui, const game_state *oldstate,
1453{ 1533 const game_state *newstate)
1454 return NULL;
1455}
1456
1457static void decode_ui(game_ui *ui, const char *encoding)
1458{ 1534{
1459} 1535}
1460 1536
1461static void game_changed_state(game_ui *ui, const game_state *oldstate, 1537static const char *current_key_label(const game_ui *ui,
1462 const game_state *newstate) 1538 const game_state *state, int button)
1463{ 1539{
1540 int hx = ui->cx, hy = ui->cy;
1541 int w2 = state->w2;
1542 char i = state->grid[hy * w2 + hx];
1543
1544 if (ui->cursor && IS_CURSOR_SELECT(button)) {
1545 if (state->common->immutable[hy * w2 + hx]) return "";
1546 switch (i) {
1547 case EMPTY:
1548 return button == CURSOR_SELECT ? "Black" : "White";
1549 case N_ONE:
1550 return button == CURSOR_SELECT ? "White" : "Empty";
1551 case N_ZERO:
1552 return button == CURSOR_SELECT ? "Empty" : "Black";
1553 }
1554 }
1555 return "";
1464} 1556}
1465 1557
1466struct game_drawstate { 1558struct game_drawstate {
@@ -1520,7 +1612,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1520 1612
1521 int w2 = state->w2, h2 = state->h2; 1613 int w2 = state->w2, h2 = state->h2;
1522 1614
1523 button &= ~MOD_MASK; 1615 char *nullret = MOVE_NO_EFFECT;
1616
1617 button = STRIP_BUTTON_MODIFIERS(button);
1524 1618
1525 /* Mouse click */ 1619 /* Mouse click */
1526 if (button == LEFT_BUTTON || button == RIGHT_BUTTON || 1620 if (button == LEFT_BUTTON || button == RIGHT_BUTTON ||
@@ -1529,17 +1623,18 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1529 && oy >= (ds->tilesize / 2) && gy < h2) { 1623 && oy >= (ds->tilesize / 2) && gy < h2) {
1530 hx = gx; 1624 hx = gx;
1531 hy = gy; 1625 hy = gy;
1532 ui->cursor = false; 1626 if (ui->cursor) {
1627 ui->cursor = false;
1628 nullret = MOVE_UI_UPDATE;
1629 }
1533 } else 1630 } else
1534 return NULL; 1631 return MOVE_UNUSED;
1535 } 1632 }
1536 1633
1537 /* Keyboard move */ 1634 /* Keyboard move */
1538 if (IS_CURSOR_MOVE(button)) { 1635 if (IS_CURSOR_MOVE(button))
1539 move_cursor(button, &ui->cx, &ui->cy, w2, h2, false); 1636 return move_cursor(button, &ui->cx, &ui->cy, w2, h2, false,
1540 ui->cursor = true; 1637 &ui->cursor);
1541 return UI_UPDATE;
1542 }
1543 1638
1544 /* Place one */ 1639 /* Place one */
1545 if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2 1640 if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2
@@ -1551,7 +1646,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1551 char c, i; 1646 char c, i;
1552 1647
1553 if (state->common->immutable[hy * w2 + hx]) 1648 if (state->common->immutable[hy * w2 + hx])
1554 return NULL; 1649 return nullret;
1555 1650
1556 c = '-'; 1651 c = '-';
1557 i = state->grid[hy * w2 + hx]; 1652 i = state->grid[hy * w2 + hx];
@@ -1571,13 +1666,13 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1571 1666
1572 if (state->grid[hy * w2 + hx] == 1667 if (state->grid[hy * w2 + hx] ==
1573 (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY)) 1668 (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY))
1574 return NULL; /* don't put no-ops on the undo chain */ 1669 return nullret; /* don't put no-ops on the undo chain */
1575 1670
1576 sprintf(buf, "P%c,%d,%d", c, hx, hy); 1671 sprintf(buf, "P%c,%d,%d", c, hx, hy);
1577 1672
1578 return dupstr(buf); 1673 return dupstr(buf);
1579 } 1674 }
1580 return NULL; 1675 return MOVE_UNUSED;
1581} 1676}
1582 1677
1583static game_state *execute_move(const game_state *state, const char *move) 1678static game_state *execute_move(const game_state *state, const char *move)
@@ -1637,7 +1732,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1637 */ 1732 */
1638 1733
1639static void game_compute_size(const game_params *params, int tilesize, 1734static void game_compute_size(const game_params *params, int tilesize,
1640 int *x, int *y) 1735 const game_ui *ui, int *x, int *y)
1641{ 1736{
1642 *x = tilesize * (params->w2 + 1); 1737 *x = tilesize * (params->w2 + 1);
1643 *y = tilesize * (params->h2 + 1); 1738 *y = tilesize * (params->h2 + 1);
@@ -1788,9 +1883,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1788 int x, y, i; 1883 int x, y, i;
1789 1884
1790 if (!ds->started) { 1885 if (!ds->started) {
1791 /* Main window background */
1792 draw_rect(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1),
1793 COL_BACKGROUND);
1794 /* Outer edge of grid */ 1886 /* Outer edge of grid */
1795 draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10, 1887 draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10,
1796 TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1, 1888 TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1,
@@ -1878,22 +1970,19 @@ static int game_status(const game_state *state)
1878 return state->completed ? +1 : 0; 1970 return state->completed ? +1 : 0;
1879} 1971}
1880 1972
1881static bool game_timing_state(const game_state *state, game_ui *ui) 1973static void game_print_size(const game_params *params, const game_ui *ui,
1882{ 1974 float *x, float *y)
1883 return true;
1884}
1885
1886static void game_print_size(const game_params *params, float *x, float *y)
1887{ 1975{
1888 int pw, ph; 1976 int pw, ph;
1889 1977
1890 /* Using 7mm squares */ 1978 /* Using 7mm squares */
1891 game_compute_size(params, 700, &pw, &ph); 1979 game_compute_size(params, 700, ui, &pw, &ph);
1892 *x = pw / 100.0F; 1980 *x = pw / 100.0F;
1893 *y = ph / 100.0F; 1981 *y = ph / 100.0F;
1894} 1982}
1895 1983
1896static void game_print(drawing *dr, const game_state *state, int tilesize) 1984static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1985 int tilesize)
1897{ 1986{
1898 int w2 = state->w2, h2 = state->h2; 1987 int w2 = state->w2, h2 = state->h2;
1899 int x, y; 1988 int x, y;
@@ -1946,12 +2035,14 @@ const struct game thegame = {
1946 free_game, 2035 free_game,
1947 true, solve_game, 2036 true, solve_game,
1948 true, game_can_format_as_text_now, game_text_format, 2037 true, game_can_format_as_text_now, game_text_format,
2038 NULL, NULL, /* get_prefs, set_prefs */
1949 new_ui, 2039 new_ui,
1950 free_ui, 2040 free_ui,
1951 encode_ui, 2041 NULL, /* encode_ui */
1952 decode_ui, 2042 NULL, /* decode_ui */
1953 NULL, /* game_request_keys */ 2043 NULL, /* game_request_keys */
1954 game_changed_state, 2044 game_changed_state,
2045 current_key_label,
1955 interpret_move, 2046 interpret_move,
1956 execute_move, 2047 execute_move,
1957 DEFAULT_TILE_SIZE, game_compute_size, game_set_size, 2048 DEFAULT_TILE_SIZE, game_compute_size, game_set_size,
@@ -1965,7 +2056,7 @@ const struct game thegame = {
1965 game_status, 2056 game_status,
1966 true, false, game_print_size, game_print, 2057 true, false, game_print_size, game_print,
1967 false, /* wants_statusbar */ 2058 false, /* wants_statusbar */
1968 false, game_timing_state, 2059 false, NULL, /* timing_state */
1969 0, /* flags */ 2060 0, /* flags */
1970}; 2061};
1971 2062
@@ -1979,7 +2070,7 @@ const struct game thegame = {
1979 2070
1980/* Most of the standalone solver code was copied from unequal.c and singles.c */ 2071/* Most of the standalone solver code was copied from unequal.c and singles.c */
1981 2072
1982const char *quis; 2073static const char *quis;
1983 2074
1984static void usage_exit(const char *msg) 2075static void usage_exit(const char *msg)
1985{ 2076{
diff --git a/apps/plugins/puzzles/src/untangle.R b/apps/plugins/puzzles/src/untangle.R
deleted file mode 100644
index a57f1e56fd..0000000000
--- a/apps/plugins/puzzles/src/untangle.R
+++ /dev/null
@@ -1,21 +0,0 @@
1# -*- makefile -*-
2
3UNTANGLE_EXTRA = tree234
4
5untangle : [X] GTK COMMON untangle UNTANGLE_EXTRA untangle-icon|no-icon
6
7untangle : [G] WINDOWS COMMON untangle UNTANGLE_EXTRA untangle.res|noicon.res
8
9ALL += untangle[COMBINED] UNTANGLE_EXTRA
10
11!begin am gtk
12GAMES += untangle
13!end
14
15!begin >list.c
16 A(untangle) \
17!end
18
19!begin >gamedesc.txt
20untangle:untangle.exe:Untangle:Planar graph layout puzzle:Reposition the points so that the lines do not cross.
21!end
diff --git a/apps/plugins/puzzles/src/untangle.c b/apps/plugins/puzzles/src/untangle.c
index 0d3e0e6135..f0647c83e9 100644
--- a/apps/plugins/puzzles/src/untangle.c
+++ b/apps/plugins/puzzles/src/untangle.c
@@ -20,10 +20,6 @@
20 * requirements are adequately expressed by a single scalar tile 20 * requirements are adequately expressed by a single scalar tile
21 * size), and probably complicate the rest of the puzzles' API as a 21 * size), and probably complicate the rest of the puzzles' API as a
22 * result. So I'm not sure I really want to do it. 22 * result. So I'm not sure I really want to do it.
23 *
24 * - It would be nice if we could somehow auto-detect a real `long
25 * long' type on the host platform and use it in place of my
26 * hand-hacked int64s. It'd be faster and more reliable.
27 */ 23 */
28 24
29#include <stdio.h> 25#include <stdio.h>
@@ -31,7 +27,15 @@
31#include <string.h> 27#include <string.h>
32#include <assert.h> 28#include <assert.h>
33#include <ctype.h> 29#include <ctype.h>
34#include <math.h> 30#include <limits.h>
31#ifdef NO_TGMATH_H
32# include <math.h>
33#else
34# include <tgmath.h>
35#endif
36#if HAVE_STDINT_H
37# include <stdint.h>
38#endif
35 39
36#include "puzzles.h" 40#include "puzzles.h"
37#include "tree234.h" 41#include "tree234.h"
@@ -39,7 +43,6 @@
39#define CIRCLE_RADIUS 6 43#define CIRCLE_RADIUS 6
40#define DRAG_THRESHOLD (CIRCLE_RADIUS * 2) 44#define DRAG_THRESHOLD (CIRCLE_RADIUS * 2)
41#define PREFERRED_TILESIZE 64 45#define PREFERRED_TILESIZE 64
42#define CURSOR_GRANULARITY 5
43 46
44#define FLASH_TIME 0.30F 47#define FLASH_TIME 0.30F
45#define ANIM_TIME 0.13F 48#define ANIM_TIME 0.13F
@@ -49,9 +52,7 @@ enum {
49 COL_SYSBACKGROUND, 52 COL_SYSBACKGROUND,
50 COL_BACKGROUND, 53 COL_BACKGROUND,
51 COL_LINE, 54 COL_LINE,
52#ifdef SHOW_CROSSINGS
53 COL_CROSSEDLINE, 55 COL_CROSSEDLINE,
54#endif
55 COL_OUTLINE, 56 COL_OUTLINE,
56 COL_POINT, 57 COL_POINT,
57 COL_DRAGPOINT, 58 COL_DRAGPOINT,
@@ -93,9 +94,7 @@ struct game_state {
93 game_params params; 94 game_params params;
94 int w, h; /* extent of coordinate system only */ 95 int w, h; /* extent of coordinate system only */
95 point *pts; 96 point *pts;
96#ifdef SHOW_CROSSINGS
97 int *crosses; /* mark edges which are crossed */ 97 int *crosses; /* mark edges which are crossed */
98#endif
99 struct graph *graph; 98 struct graph *graph;
100 bool completed, cheated, just_solved; 99 bool completed, cheated, just_solved;
101}; 100};
@@ -208,6 +207,8 @@ static const char *validate_params(const game_params *params, bool full)
208{ 207{
209 if (params->n < 4) 208 if (params->n < 4)
210 return "Number of points must be at least four"; 209 return "Number of points must be at least four";
210 if (params->n > INT_MAX / 3)
211 return "Number of points must not be unreasonably large";
211 return NULL; 212 return NULL;
212} 213}
213 214
@@ -216,6 +217,9 @@ static const char *validate_params(const game_params *params, bool full)
216 * integer overflow at the very core of cross(). 217 * integer overflow at the very core of cross().
217 */ 218 */
218 219
220#if !HAVE_STDINT_H
221/* For prehistoric C implementations, do this the hard way */
222
219typedef struct { 223typedef struct {
220 long hi; 224 long hi;
221 unsigned long lo; 225 unsigned long lo;
@@ -295,6 +299,21 @@ static int64 dotprod64(long a, long b, long p, long q)
295 return ab; 299 return ab;
296} 300}
297 301
302#else /* HAVE_STDINT_H */
303
304typedef int64_t int64;
305#define greater64(i,j) ((i) > (j))
306#define sign64(i) ((i) < 0 ? -1 : (i)==0 ? 0 : +1)
307#define mulu32to64(x,y) ((int64_t)(unsigned long)(x) * (unsigned long)(y))
308#define mul32to64(x,y) ((int64_t)(long)(x) * (long)(y))
309
310static int64 dotprod64(long a, long b, long p, long q)
311{
312 return (int64)a * b + (int64)p * q;
313}
314
315#endif /* HAVE_STDINT_H */
316
298/* 317/*
299 * Determine whether the line segments between a1 and a2, and 318 * Determine whether the line segments between a1 and a2, and
300 * between b1 and b2, intersect. We count it as an intersection if 319 * between b1 and b2, intersect. We count it as an intersection if
@@ -419,7 +438,9 @@ static void addedge(tree234 *edges, int a, int b)
419 e->a = min(a, b); 438 e->a = min(a, b);
420 e->b = max(a, b); 439 e->b = max(a, b);
421 440
422 add234(edges, e); 441 if (add234(edges, e) != e)
442 /* Duplicate edge. */
443 sfree(e);
423} 444}
424 445
425static bool isedge(tree234 *edges, int a, int b) 446static bool isedge(tree234 *edges, int a, int b)
@@ -441,8 +462,8 @@ typedef struct vertex {
441 462
442static int vertcmpC(const void *av, const void *bv) 463static int vertcmpC(const void *av, const void *bv)
443{ 464{
444 const vertex *a = (vertex *)av; 465 const vertex *a = (const vertex *)av;
445 const vertex *b = (vertex *)bv; 466 const vertex *b = (const vertex *)bv;
446 467
447 if (a->param < b->param) 468 if (a->param < b->param)
448 return -1; 469 return -1;
@@ -754,6 +775,8 @@ static const char *validate_desc(const game_params *params, const char *desc)
754 return "Expected ',' after number in game description"; 775 return "Expected ',' after number in game description";
755 desc++; /* eat comma */ 776 desc++; /* eat comma */
756 } 777 }
778 if (a == b)
779 return "Node linked to itself in game description";
757 } 780 }
758 781
759 return NULL; 782 return NULL;
@@ -765,10 +788,8 @@ static void mark_crossings(game_state *state)
765 int i, j; 788 int i, j;
766 edge *e, *e2; 789 edge *e, *e2;
767 790
768#ifdef SHOW_CROSSINGS
769 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) 791 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++)
770 state->crosses[i] = false; 792 state->crosses[i] = false;
771#endif
772 793
773 /* 794 /*
774 * Check correctness: for every pair of edges, see whether they 795 * Check correctness: for every pair of edges, see whether they
@@ -782,11 +803,7 @@ static void mark_crossings(game_state *state)
782 if (cross(state->pts[e2->a], state->pts[e2->b], 803 if (cross(state->pts[e2->a], state->pts[e2->b],
783 state->pts[e->a], state->pts[e->b])) { 804 state->pts[e->a], state->pts[e->b])) {
784 ok = false; 805 ok = false;
785#ifdef SHOW_CROSSINGS
786 state->crosses[i] = state->crosses[j] = true; 806 state->crosses[i] = state->crosses[j] = true;
787#else
788 goto done; /* multi-level break - sorry */
789#endif
790 } 807 }
791 } 808 }
792 } 809 }
@@ -795,9 +812,6 @@ static void mark_crossings(game_state *state)
795 * e == NULL if we've gone through all the edge pairs 812 * e == NULL if we've gone through all the edge pairs
796 * without finding a crossing. 813 * without finding a crossing.
797 */ 814 */
798#ifndef SHOW_CROSSINGS
799 done:
800#endif
801 if (ok) 815 if (ok)
802 state->completed = true; 816 state->completed = true;
803} 817}
@@ -834,10 +848,8 @@ static game_state *new_game(midend *me, const game_params *params,
834 addedge(state->graph->edges, a, b); 848 addedge(state->graph->edges, a, b);
835 } 849 }
836 850
837#ifdef SHOW_CROSSINGS
838 state->crosses = snewn(count234(state->graph->edges), int); 851 state->crosses = snewn(count234(state->graph->edges), int);
839 mark_crossings(state); /* sets up `crosses' and `completed' */ 852 mark_crossings(state); /* sets up `crosses' and `completed' */
840#endif
841 853
842 return state; 854 return state;
843} 855}
@@ -857,11 +869,9 @@ static game_state *dup_game(const game_state *state)
857 ret->completed = state->completed; 869 ret->completed = state->completed;
858 ret->cheated = state->cheated; 870 ret->cheated = state->cheated;
859 ret->just_solved = state->just_solved; 871 ret->just_solved = state->just_solved;
860#ifdef SHOW_CROSSINGS
861 ret->crosses = snewn(count234(ret->graph->edges), int); 872 ret->crosses = snewn(count234(ret->graph->edges), int);
862 memcpy(ret->crosses, state->crosses, 873 memcpy(ret->crosses, state->crosses,
863 count234(ret->graph->edges) * sizeof(int)); 874 count234(ret->graph->edges) * sizeof(int));
864#endif
865 875
866 return ret; 876 return ret;
867} 877}
@@ -1025,19 +1035,10 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1025 return ret; 1035 return ret;
1026} 1036}
1027 1037
1028static bool game_can_format_as_text_now(const game_params *params)
1029{
1030 return true;
1031}
1032
1033static char *game_text_format(const game_state *state)
1034{
1035 return NULL;
1036}
1037
1038struct game_ui { 1038struct game_ui {
1039 /* Invariant: at most one of {dragpoint, cursorpoint} may be valid
1040 * at any time. */
1039 int dragpoint; /* point being dragged; -1 if none */ 1041 int dragpoint; /* point being dragged; -1 if none */
1040
1041 int cursorpoint; /* point being highlighted, but 1042 int cursorpoint; /* point being highlighted, but
1042 * not dragged by the cursor, 1043 * not dragged by the cursor,
1043 * again -1 if none */ 1044 * again -1 if none */
@@ -1046,6 +1047,26 @@ struct game_ui {
1046 bool just_dragged; /* reset in game_changed_state */ 1047 bool just_dragged; /* reset in game_changed_state */
1047 bool just_moved; /* _set_ in game_changed_state */ 1048 bool just_moved; /* _set_ in game_changed_state */
1048 float anim_length; 1049 float anim_length;
1050
1051 /*
1052 * User preference option to snap dragged points to a coarse-ish
1053 * grid. Requested by a user who otherwise found themself spending
1054 * too much time struggling to get lines nicely horizontal or
1055 * vertical.
1056 */
1057 bool snap_to_grid;
1058
1059 /*
1060 * User preference option to highlight graph edges involved in a
1061 * crossing.
1062 */
1063 bool show_crossed_edges;
1064
1065 /*
1066 * User preference option to show vertices as numbers instead of
1067 * circular blobs, so you can easily tell them apart.
1068 */
1069 bool vertex_numbers;
1049}; 1070};
1050 1071
1051static game_ui *new_ui(const game_state *state) 1072static game_ui *new_ui(const game_state *state)
@@ -1054,21 +1075,51 @@ static game_ui *new_ui(const game_state *state)
1054 ui->dragpoint = -1; 1075 ui->dragpoint = -1;
1055 ui->cursorpoint = -1; 1076 ui->cursorpoint = -1;
1056 ui->just_moved = ui->just_dragged = false; 1077 ui->just_moved = ui->just_dragged = false;
1078 ui->snap_to_grid = false;
1079 ui->show_crossed_edges = false;
1080 ui->vertex_numbers = false;
1057 return ui; 1081 return ui;
1058} 1082}
1059 1083
1060static void free_ui(game_ui *ui) 1084static config_item *get_prefs(game_ui *ui)
1061{ 1085{
1062 sfree(ui); 1086 config_item *cfg;
1087
1088 cfg = snewn(4, config_item);
1089
1090 cfg[0].name = "Snap points to a grid";
1091 cfg[0].kw = "snap-to-grid";
1092 cfg[0].type = C_BOOLEAN;
1093 cfg[0].u.boolean.bval = ui->snap_to_grid;
1094
1095 cfg[1].name = "Show edges that cross another edge";
1096 cfg[1].kw = "show-crossed-edges";
1097 cfg[1].type = C_BOOLEAN;
1098 cfg[1].u.boolean.bval = ui->show_crossed_edges;
1099
1100 cfg[2].name = "Display style for vertices";
1101 cfg[2].kw = "vertex-style";
1102 cfg[2].type = C_CHOICES;
1103 cfg[2].u.choices.choicenames = ":Circles:Numbers";
1104 cfg[2].u.choices.choicekws = ":circle:number";
1105 cfg[2].u.choices.selected = ui->vertex_numbers;
1106
1107 cfg[3].name = NULL;
1108 cfg[3].type = C_END;
1109
1110 return cfg;
1063} 1111}
1064 1112
1065static char *encode_ui(const game_ui *ui) 1113static void set_prefs(game_ui *ui, const config_item *cfg)
1066{ 1114{
1067 return NULL; 1115 ui->snap_to_grid = cfg[0].u.boolean.bval;
1116 ui->show_crossed_edges = cfg[1].u.boolean.bval;
1117 ui->vertex_numbers = cfg[2].u.choices.selected;
1068} 1118}
1069 1119
1070static void decode_ui(game_ui *ui, const char *encoding) 1120static void free_ui(game_ui *ui)
1071{ 1121{
1122 sfree(ui);
1072} 1123}
1073 1124
1074static void game_changed_state(game_ui *ui, const game_state *oldstate, 1125static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1085,6 +1136,66 @@ struct game_drawstate {
1085 long *x, *y; 1136 long *x, *y;
1086}; 1137};
1087 1138
1139static void place_dragged_point(const game_state *state, game_ui *ui,
1140 const game_drawstate *ds, int x, int y)
1141{
1142 if (ui->snap_to_grid) {
1143 /*
1144 * We snap points to a grid that has n-1 vertices on each
1145 * side. This should be large enough to permit a straight-
1146 * line drawing of any n-vertex planar graph, and moreover,
1147 * any specific planar embedding of that graph.
1148 *
1149 * Source: David Eppstein's book 'Forbidden Configurations in
1150 * Discrete Geometry' mentions (section 16.3, page 182) that
1151 * the point configuration he describes as GRID(n-1,n-1) -
1152 * that is, the vertices of a square grid with n-1 vertices on
1153 * each side - is universal for n-vertex planar graphs. In
1154 * other words (from definitions earlier in the chapter), if a
1155 * graph G admits any drawing in the plane at all, then it can
1156 * be drawn with straight lines, and with all vertices being
1157 * vertices of that grid.
1158 *
1159 * That fact by itself only says that _some_ planar embedding
1160 * of G can be drawn in this grid. We'd prefer that _all_
1161 * embeddings of G can be so drawn, because 'snap to grid' is
1162 * supposed to be a UI affordance, not an extra puzzle
1163 * challenge, so we don't want to constrain the player's
1164 * choice of planar embedding.
1165 *
1166 * But it doesn't make a difference. Proof: given a specific
1167 * planar embedding of G, triangulate it, by adding extra
1168 * edges to every face of degree > 3. When this process
1169 * terminates with every face a triangle, we have a new graph
1170 * G' such that no edge can be added without it ceasing to be
1171 * planar. Standard theorems say that a maximal planar graph
1172 * is 3-connected, and that a 3-connected planar graph has a
1173 * _unique_ embedding. So any drawing of G' in the plane can
1174 * be converted into a drawing of G in the desired embedding,
1175 * by simply removing all the extra edges that we added to
1176 * turn G into G'. And G' is still an n-vertex planar graph,
1177 * hence it can be drawn in GRID(n-1,n-1). []
1178 */
1179 int d = state->params.n - 1;
1180
1181 /* Calculate position in terms of the snapping grid. */
1182 x = d * x / (state->w * ds->tilesize);
1183 y = d * y / (state->h * ds->tilesize);
1184 /* Convert to standard co-ordinates, applying a half-square offset. */
1185 ui->newpoint.x = (x * 2 + 1) * state->w;
1186 ui->newpoint.y = (y * 2 + 1) * state->h;
1187 ui->newpoint.d = d * 2;
1188 } else {
1189 ui->newpoint.x = x;
1190 ui->newpoint.y = y;
1191 ui->newpoint.d = ds->tilesize;
1192 }
1193}
1194
1195static float normsq(point pt) {
1196 return (pt.x * pt.x + pt.y * pt.y) / (pt.d * pt.d);
1197}
1198
1088static char *interpret_move(const game_state *state, game_ui *ui, 1199static char *interpret_move(const game_state *state, game_ui *ui,
1089 const game_drawstate *ds, 1200 const game_drawstate *ds,
1090 int x, int y, int button) 1201 int x, int y, int button)
@@ -1119,23 +1230,20 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1119 1230
1120 if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) { 1231 if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) {
1121 ui->dragpoint = best; 1232 ui->dragpoint = best;
1122 ui->cursorpoint = -1; 1233 ui->cursorpoint = -1; /* eliminate the cursor point, if any */
1123 ui->newpoint.x = x; 1234 place_dragged_point(state, ui, ds, x, y);
1124 ui->newpoint.y = y; 1235 return MOVE_UI_UPDATE;
1125 ui->newpoint.d = ds->tilesize;
1126 return UI_UPDATE;
1127 } 1236 }
1128 1237 return MOVE_NO_EFFECT;
1129 } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) { 1238 } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) {
1130 ui->newpoint.x = x; 1239 place_dragged_point(state, ui, ds, x, y);
1131 ui->newpoint.y = y; 1240 return MOVE_UI_UPDATE;
1132 ui->newpoint.d = ds->tilesize;
1133 return UI_UPDATE;
1134 } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) { 1241 } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) {
1135 int p = ui->dragpoint; 1242 int p = ui->dragpoint;
1136 char buf[80]; 1243 char buf[80];
1137 1244
1138 ui->dragpoint = -1; /* terminate drag, no matter what */ 1245 ui->dragpoint = -1; /* terminate drag, no matter what */
1246 ui->cursorpoint = -1; /* also eliminate the cursor point */
1139 1247
1140 /* 1248 /*
1141 * First, see if we're within range. The user can cancel a 1249 * First, see if we're within range. The user can cancel a
@@ -1145,7 +1253,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1145 ui->newpoint.x >= (long)state->w*ui->newpoint.d || 1253 ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
1146 ui->newpoint.y < 0 || 1254 ui->newpoint.y < 0 ||
1147 ui->newpoint.y >= (long)state->h*ui->newpoint.d) 1255 ui->newpoint.y >= (long)state->h*ui->newpoint.d)
1148 return UI_UPDATE; 1256 return MOVE_UI_UPDATE;
1149 1257
1150 /* 1258 /*
1151 * We aren't cancelling the drag. Construct a move string 1259 * We aren't cancelling the drag. Construct a move string
@@ -1155,109 +1263,140 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1155 ui->newpoint.x, ui->newpoint.y, ui->newpoint.d); 1263 ui->newpoint.x, ui->newpoint.y, ui->newpoint.d);
1156 ui->just_dragged = true; 1264 ui->just_dragged = true;
1157 return dupstr(buf); 1265 return dupstr(buf);
1266 } else if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
1267 return MOVE_NO_EFFECT;
1158 } 1268 }
1159 else if(IS_CURSOR_MOVE(button)) 1269 else if(IS_CURSOR_MOVE(button)) {
1160 { 1270 if(ui->dragpoint < 0) {
1161 if(ui->dragpoint < 0) 1271 /*
1162 { 1272 * We're selecting a point with the cursor keys.
1163 /* We're selecting a point here. */ 1273 *
1164 /* Search all the points and find the closest one (2-D) in 1274 * If no point is currently highlighted, we assume the "0"
1165 * the given direction. */ 1275 * point is highlighted to begin. Then, we search all the
1166 int i, best; 1276 * points and find the nearest one (by Euclidean distance)
1167 long bestd; 1277 * in the quadrant corresponding to the cursor key
1168 1278 * direction. A point is in the right quadrant if and only
1169 if(ui->cursorpoint < 0) 1279 * if the azimuth angle to that point from the cursor
1170 { 1280 * point is within a [-45 deg, +45 deg] interval from the
1281 * direction vector of the cursor key.
1282 *
1283 * An important corner case here is if another point is in
1284 * the exact same location as the currently highlighted
1285 * point (which is a possibility with the "snap to grid"
1286 * preference). In this case, we do not consider the other
1287 * point as a candidate point, so as to prevent the cursor
1288 * from being "stuck" on any point. The player can still
1289 * select the overlapped point by dragging the highlighted
1290 * point away and then navigating back.
1291 */
1292 int i, best = -1;
1293 float bestd = 0;
1294
1295 if(ui->cursorpoint < 0) {
1171 ui->cursorpoint = 0; 1296 ui->cursorpoint = 0;
1172 } 1297 }
1173 1298
1174 /* 1299 point cur = state->pts[ui->cursorpoint];
1175 * Begin drag. We drag the vertex _nearest_ to the pointer,
1176 * just in case one is nearly on top of another and we want
1177 * to drag the latter. However, we drag nothing at all if
1178 * the nearest vertex is outside DRAG_THRESHOLD.
1179 */
1180 best = -1;
1181 bestd = 0;
1182 1300
1183 for (i = 0; i < n; i++) { 1301 for (i = 0; i < n; i++) {
1184 long px, py, dx, dy, d; 1302 point delta;
1185 float angle; 1303 float distsq;
1186 int right_direction; 1304 point p = state->pts[i];
1305 int right_direction = false;
1306
1187 if(i == ui->cursorpoint) 1307 if(i == ui->cursorpoint)
1188 continue; 1308 continue;
1189 1309
1190 px = state->pts[i].x * ds->tilesize / state->pts[i].d; 1310 /* Compute the vector p - cur. Check that it lies in
1191 py = state->pts[i].y * ds->tilesize / state->pts[i].d; 1311 * the correct quadrant. */
1192 dx = px - state->pts[ui->cursorpoint].x * ds->tilesize / state->pts[ui->cursorpoint].d; 1312 delta.x = p.x * cur.d - cur.x * p.d;
1193 dy = py - state->pts[ui->cursorpoint].y * ds->tilesize / state->pts[ui->cursorpoint].d; 1313 delta.y = p.y * cur.d - cur.y * p.d;
1194 d = dx*dx + dy*dy; 1314 delta.d = cur.d * p.d;
1195 1315
1196 /* Figure out if this point falls into a 90 degree 1316 if(delta.x == 0 && delta.y == 0)
1197 * range extending from the current point */ 1317 continue; /* overlaps cursor point - skip */
1198 1318
1199 angle = atan2(-dy, dx); /* negate y to adjust for raster coordinates */ 1319 switch(button) {
1200 1320 case CURSOR_UP:
1201 /* offset to [0..2*PI] */ 1321 right_direction = (delta.y <= -delta.x) && (delta.y <= delta.x);
1202 if(angle < 0) 1322 break;
1203 angle += 2*PI; 1323 case CURSOR_DOWN:
1324 right_direction = (delta.y >= -delta.x) && (delta.y >= delta.x);
1325 break;
1326 case CURSOR_LEFT:
1327 right_direction = (delta.y >= delta.x) && (delta.y <= -delta.x);
1328 break;
1329 case CURSOR_RIGHT:
1330 right_direction = (delta.y <= delta.x) && (delta.y >= -delta.x);
1331 break;
1332 }
1204 1333
1205 right_direction = false; 1334 if(!right_direction)
1335 continue;
1206 1336
1207 if((button == CURSOR_UP && (1*PI/4 <= angle && angle <= 3*PI/4)) || 1337 /* Compute squared Euclidean distance */
1208 (button == CURSOR_LEFT && (3*PI/4 <= angle && angle <= 5*PI/4)) || 1338 distsq = normsq(delta);
1209 (button == CURSOR_DOWN && (5*PI/4 <= angle && angle <= 7*PI/4)) ||
1210 (button == CURSOR_RIGHT && (angle >= 7*PI/4 || angle <= 1*PI/4)))
1211 right_direction = true;
1212 1339
1213 if ((best == -1 || bestd > d) && right_direction) { 1340 if (best == -1 || distsq < bestd) {
1214 best = i; 1341 best = i;
1215 bestd = d; 1342 bestd = distsq;
1216 } 1343 }
1217 } 1344 }
1218 1345
1219 if(best >= 0) 1346 if(best >= 0) {
1220 {
1221 ui->cursorpoint = best; 1347 ui->cursorpoint = best;
1222 return UI_UPDATE; 1348 return MOVE_UI_UPDATE;
1223 } 1349 }
1224 } 1350 }
1225 else if(ui->dragpoint >= 0) 1351 else if(ui->dragpoint >= 0) {
1226 { 1352 /* Dragging a point with the cursor keys. */
1227 /* dragging */ 1353 int movement_increment = ds->tilesize / 2;
1228 switch(button) 1354 int dx = 0, dy = 0;
1229 { 1355
1356 switch(button) {
1230 case CURSOR_UP: 1357 case CURSOR_UP:
1231 ui->newpoint.y -= ds->tilesize / CURSOR_GRANULARITY; 1358 dy = -movement_increment;
1232 return UI_UPDATE; 1359 break;
1233 case CURSOR_DOWN: 1360 case CURSOR_DOWN:
1234 ui->newpoint.y += ds->tilesize / CURSOR_GRANULARITY; 1361 dy = movement_increment;
1235 return UI_UPDATE; 1362 break;
1236 case CURSOR_LEFT: 1363 case CURSOR_LEFT:
1237 ui->newpoint.x -= ds->tilesize / CURSOR_GRANULARITY; 1364 dx = -movement_increment;
1238 return UI_UPDATE; 1365 break;
1239 case CURSOR_RIGHT: 1366 case CURSOR_RIGHT:
1240 ui->newpoint.x += ds->tilesize / CURSOR_GRANULARITY; 1367 dx = movement_increment;
1241 return UI_UPDATE; 1368 break;
1242 default: 1369 default:
1243 break; 1370 break;
1244 } 1371 }
1372
1373 /* This code has a slightly inconvenient interaction with
1374 * the snap to grid feature: if the point being dragged
1375 * originates on a non-grid point which is in the bottom
1376 * half or right half (or both) of a grid cell (a 75%
1377 * probability), then dragging point right (if it
1378 * originates from the right half) or down (if it
1379 * originates from the bottom half) will cause the point
1380 * to move one more grid cell than intended in that
1381 * direction. I (F. Wei) it wasn't worth handling this
1382 * corner case - if anyone feels inclined, please feel
1383 * free to do so. */
1384 place_dragged_point(state, ui, ds,
1385 ui->newpoint.x * ds->tilesize / ui->newpoint.d + dx,
1386 ui->newpoint.y * ds->tilesize / ui->newpoint.d + dy);
1387 return MOVE_UI_UPDATE;
1245 } 1388 }
1246 } 1389 } else if(button == CURSOR_SELECT) {
1247 else if(IS_CURSOR_SELECT(button)) 1390 if(ui->dragpoint < 0 && ui->cursorpoint >= 0) {
1248 {
1249 if(ui->dragpoint < 0 && ui->cursorpoint >= 0)
1250 {
1251 /* begin drag */ 1391 /* begin drag */
1252 ui->dragpoint = ui->cursorpoint; 1392 ui->dragpoint = ui->cursorpoint;
1253 ui->cursorpoint = -1; 1393 ui->cursorpoint = -1;
1254 ui->newpoint.x = state->pts[ui->dragpoint].x * ds->tilesize / state->pts[ui->dragpoint].d; 1394 ui->newpoint.x = state->pts[ui->dragpoint].x * ds->tilesize / state->pts[ui->dragpoint].d;
1255 ui->newpoint.y = state->pts[ui->dragpoint].y * ds->tilesize / state->pts[ui->dragpoint].d; 1395 ui->newpoint.y = state->pts[ui->dragpoint].y * ds->tilesize / state->pts[ui->dragpoint].d;
1256 ui->newpoint.d = ds->tilesize; 1396 ui->newpoint.d = ds->tilesize;
1257 return UI_UPDATE; 1397 return MOVE_UI_UPDATE;
1258 } 1398 }
1259 else if(ui->dragpoint >= 0) 1399 else if(ui->dragpoint >= 0) {
1260 {
1261 /* end drag */ 1400 /* end drag */
1262 int p = ui->dragpoint; 1401 int p = ui->dragpoint;
1263 char buf[80]; 1402 char buf[80];
@@ -1273,7 +1412,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1273 ui->newpoint.x >= (long)state->w*ui->newpoint.d || 1412 ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
1274 ui->newpoint.y < 0 || 1413 ui->newpoint.y < 0 ||
1275 ui->newpoint.y >= (long)state->h*ui->newpoint.d) 1414 ui->newpoint.y >= (long)state->h*ui->newpoint.d)
1276 return UI_UPDATE; 1415 return MOVE_UI_UPDATE;
1277 1416
1278 /* 1417 /*
1279 * We aren't cancelling the drag. Construct a move string 1418 * We aren't cancelling the drag. Construct a move string
@@ -1284,14 +1423,29 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1284 ui->just_dragged = true; 1423 ui->just_dragged = true;
1285 return dupstr(buf); 1424 return dupstr(buf);
1286 } 1425 }
1287 else if(ui->cursorpoint < 0) 1426 else if(ui->cursorpoint < 0) {
1288 {
1289 ui->cursorpoint = 0; 1427 ui->cursorpoint = 0;
1290 return UI_UPDATE; 1428 return MOVE_UI_UPDATE;
1291 } 1429 }
1430 } else if(STRIP_BUTTON_MODIFIERS(button) == CURSOR_SELECT2 ||
1431 STRIP_BUTTON_MODIFIERS(button) == '\t') {
1432 /* Use spacebar or tab to cycle through the points. Shift
1433 * reverses cycle direction. */
1434 if(ui->dragpoint >= 0)
1435 return MOVE_NO_EFFECT;
1436 if(ui->cursorpoint < 0) {
1437 ui->cursorpoint = 0;
1438 return MOVE_UI_UPDATE;
1439 }
1440 assert(ui->cursorpoint >= 0);
1441
1442 /* cursorpoint is valid - increment it */
1443 int direction = (button & MOD_SHFT) ? -1 : 1;
1444 ui->cursorpoint = (ui->cursorpoint + direction + state->params.n) % state->params.n;
1445 return MOVE_UI_UPDATE;
1292 } 1446 }
1293 1447
1294 return NULL; 1448 return MOVE_UNUSED;
1295} 1449}
1296 1450
1297static game_state *execute_move(const game_state *state, const char *move) 1451static game_state *execute_move(const game_state *state, const char *move)
@@ -1334,7 +1488,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1334 */ 1488 */
1335 1489
1336static void game_compute_size(const game_params *params, int tilesize, 1490static void game_compute_size(const game_params *params, int tilesize,
1337 int *x, int *y) 1491 const game_ui *ui, int *x, int *y)
1338{ 1492{
1339 *x = *y = COORDLIMIT(params->n) * tilesize; 1493 *x = *y = COORDLIMIT(params->n) * tilesize;
1340} 1494}
@@ -1363,11 +1517,9 @@ static float *game_colours(frontend *fe, int *ncolours)
1363 ret[COL_LINE * 3 + 1] = 0.0F; 1517 ret[COL_LINE * 3 + 1] = 0.0F;
1364 ret[COL_LINE * 3 + 2] = 0.0F; 1518 ret[COL_LINE * 3 + 2] = 0.0F;
1365 1519
1366#ifdef SHOW_CROSSINGS
1367 ret[COL_CROSSEDLINE * 3 + 0] = 1.0F; 1520 ret[COL_CROSSEDLINE * 3 + 0] = 1.0F;
1368 ret[COL_CROSSEDLINE * 3 + 1] = 0.0F; 1521 ret[COL_CROSSEDLINE * 3 + 1] = 0.0F;
1369 ret[COL_CROSSEDLINE * 3 + 2] = 0.0F; 1522 ret[COL_CROSSEDLINE * 3 + 2] = 0.0F;
1370#endif
1371 1523
1372 ret[COL_OUTLINE * 3 + 0] = 0.0F; 1524 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1373 ret[COL_OUTLINE * 3 + 1] = 0.0F; 1525 ret[COL_OUTLINE * 3 + 1] = 0.0F;
@@ -1491,15 +1643,16 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1491 ds->y[i] = y; 1643 ds->y[i] = y;
1492 } 1644 }
1493 1645
1494 if (ds->bg == bg && ds->dragpoint == ui->dragpoint && ds->cursorpoint == ui->cursorpoint && !points_moved) 1646 if (ds->bg == bg &&
1647 ds->dragpoint == ui->dragpoint &&
1648 ds->cursorpoint == ui->cursorpoint && !points_moved)
1495 return; /* nothing to do */ 1649 return; /* nothing to do */
1496 1650
1497 ds->dragpoint = ui->dragpoint; 1651 ds->dragpoint = ui->dragpoint;
1652 ds->cursorpoint = ui->cursorpoint;
1498 ds->bg = bg; 1653 ds->bg = bg;
1499 1654
1500 game_compute_size(&state->params, ds->tilesize, &w, &h); 1655 game_compute_size(&state->params, ds->tilesize, ui, &w, &h);
1501
1502 clip(dr, 0, 0, w, h);
1503 draw_rect(dr, 0, 0, w, h, bg); 1656 draw_rect(dr, 0, 0, w, h, bg);
1504 1657
1505 /* 1658 /*
@@ -1508,23 +1661,28 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1508 1661
1509 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) { 1662 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
1510 draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b], 1663 draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b],
1511#ifdef SHOW_CROSSINGS 1664 ui->show_crossed_edges &&
1512 (oldstate?oldstate:state)->crosses[i] ? 1665 (oldstate?oldstate:state)->crosses[i] ?
1513 COL_CROSSEDLINE : 1666 COL_CROSSEDLINE : COL_LINE);
1514#endif
1515 COL_LINE);
1516 } 1667 }
1517 1668
1518 /* 1669 /*
1519 * Draw the points. 1670 * Draw the points.
1520 * 1671 *
1521 * When dragging, we should not only vary the colours, but 1672 * When dragging, we vary the point colours to highlight the drag
1522 * leave the point being dragged until last. 1673 * point and neighbour points. The draw order is defined so that
1674 * the most relevant points (i.e., the dragged point and cursor
1675 * point) are drawn last, so they appear on top of other points.
1523 */ 1676 */
1677 static const int draw_order[] = {
1678 COL_POINT,
1679 COL_NEIGHBOUR,
1680 COL_CURSORPOINT,
1681 COL_DRAGPOINT
1682 };
1683
1524 for (j = 0; j < 4; j++) { 1684 for (j = 0; j < 4; j++) {
1525 int thisc = (j == 0 ? COL_POINT : 1685 int thisc = draw_order[j];
1526 (j == 1 ? COL_NEIGHBOUR :
1527 j == 2 ? COL_CURSORPOINT : COL_DRAGPOINT));
1528 for (i = 0; i < state->params.n; i++) { 1686 for (i = 0; i < state->params.n; i++) {
1529 int c; 1687 int c;
1530 1688
@@ -1540,19 +1698,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1540 } 1698 }
1541 1699
1542 if (c == thisc) { 1700 if (c == thisc) {
1543#ifdef VERTEX_NUMBERS 1701 if (ui->vertex_numbers) {
1544 draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg); 1702 char buf[80];
1545 { 1703 draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg);
1546 char buf[80]; 1704 sprintf(buf, "%d", i);
1547 sprintf(buf, "%d", i); 1705 draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE,
1548 draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE,
1549 DRAG_THRESHOLD*3/2, 1706 DRAG_THRESHOLD*3/2,
1550 ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf); 1707 ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf);
1551 } 1708 } else {
1552#else 1709 draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS,
1553 draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS, 1710 c, COL_OUTLINE);
1554 c, COL_OUTLINE); 1711 }
1555#endif
1556 } 1712 }
1557 } 1713 }
1558 } 1714 }
@@ -1587,17 +1743,20 @@ static void game_get_cursor_location(const game_ui *ui,
1587 const game_params *params, 1743 const game_params *params,
1588 int *x, int *y, int *w, int *h) 1744 int *x, int *y, int *w, int *h)
1589{ 1745{
1590 if(ui->dragpoint >= 0 || ui->cursorpoint >= 0) { 1746 point pt;
1591 int idx = (ui->dragpoint >= 0) ? ui->dragpoint : ui->cursorpoint; 1747 if(ui->dragpoint >= 0)
1748 pt = ui->newpoint;
1749 else if(ui->cursorpoint >= 0)
1750 pt = state->pts[ui->cursorpoint];
1751 else
1752 return;
1592 1753
1593 int cx, cy; 1754 int cx = ds->tilesize * pt.x / pt.d;
1594 cx = ds->x[idx]; 1755 int cy = ds->tilesize * pt.y / pt.d;
1595 cy = ds->y[idx];
1596 1756
1597 *x = cx - CIRCLE_RADIUS; 1757 *x = cx - CIRCLE_RADIUS;
1598 *y = cy - CIRCLE_RADIUS; 1758 *y = cy - CIRCLE_RADIUS;
1599 *w = *h = 2 * CIRCLE_RADIUS + 1; 1759 *w = *h = 2 * CIRCLE_RADIUS + 1;
1600 }
1601} 1760}
1602 1761
1603static int game_status(const game_state *state) 1762static int game_status(const game_state *state)
@@ -1605,19 +1764,6 @@ static int game_status(const game_state *state)
1605 return state->completed ? +1 : 0; 1764 return state->completed ? +1 : 0;
1606} 1765}
1607 1766
1608static bool game_timing_state(const game_state *state, game_ui *ui)
1609{
1610 return true;
1611}
1612
1613static void game_print_size(const game_params *params, float *x, float *y)
1614{
1615}
1616
1617static void game_print(drawing *dr, const game_state *state, int tilesize)
1618{
1619}
1620
1621#ifdef COMBINED 1767#ifdef COMBINED
1622#define thegame untangle 1768#define thegame untangle
1623#endif 1769#endif
@@ -1638,13 +1784,15 @@ const struct game thegame = {
1638 dup_game, 1784 dup_game,
1639 free_game, 1785 free_game,
1640 true, solve_game, 1786 true, solve_game,
1641 false, game_can_format_as_text_now, game_text_format, 1787 false, NULL, NULL, /* can_format_as_text_now, text_format */
1788 get_prefs, set_prefs,
1642 new_ui, 1789 new_ui,
1643 free_ui, 1790 free_ui,
1644 encode_ui, 1791 NULL, /* encode_ui */
1645 decode_ui, 1792 NULL, /* decode_ui */
1646 NULL, /* game_request_keys */ 1793 NULL, /* game_request_keys */
1647 game_changed_state, 1794 game_changed_state,
1795 NULL, /* current_key_label */
1648 interpret_move, 1796 interpret_move,
1649 execute_move, 1797 execute_move,
1650 PREFERRED_TILESIZE, game_compute_size, game_set_size, 1798 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -1656,8 +1804,8 @@ const struct game thegame = {
1656 game_flash_length, 1804 game_flash_length,
1657 game_get_cursor_location, 1805 game_get_cursor_location,
1658 game_status, 1806 game_status,
1659 false, false, game_print_size, game_print, 1807 false, false, NULL, NULL, /* print_size, print */
1660 false, /* wants_statusbar */ 1808 false, /* wants_statusbar */
1661 false, game_timing_state, 1809 false, NULL, /* timing_state */
1662 SOLVE_ANIMATES, /* flags */ 1810 SOLVE_ANIMATES, /* flags */
1663}; 1811};
diff --git a/apps/plugins/puzzles/src/version.c b/apps/plugins/puzzles/src/version.c
index 1cef29feb7..73632f9b37 100644
--- a/apps/plugins/puzzles/src/version.c
+++ b/apps/plugins/puzzles/src/version.c
@@ -2,6 +2,7 @@
2 * Puzzles version numbering. 2 * Puzzles version numbering.
3 */ 3 */
4 4
5#include "puzzles.h"
5#include "version.h" 6#include "version.h"
6 7
7char ver[] = VER; 8char ver[] = VER;
diff --git a/apps/plugins/puzzles/src/version.h b/apps/plugins/puzzles/src/version.h
index 997e00592b..20d2800fee 100644
--- a/apps/plugins/puzzles/src/version.h
+++ b/apps/plugins/puzzles/src/version.h
@@ -9,3 +9,5 @@
9 */ 9 */
10 10
11#define VER "Unidentified build" 11#define VER "Unidentified build"
12
13#define VERSIONINFO_BINARY_VERSION 0,0,0,0
diff --git a/apps/plugins/puzzles/src/windows.c b/apps/plugins/puzzles/src/windows.c
deleted file mode 100644
index 38559c5454..0000000000
--- a/apps/plugins/puzzles/src/windows.c
+++ /dev/null
@@ -1,3821 +0,0 @@
1/*
2 * windows.c: Windows front end for my puzzle collection.
3 */
4
5#include <windows.h>
6#include <commctrl.h>
7#ifndef NO_HTMLHELP
8#include <htmlhelp.h>
9#endif /* NO_HTMLHELP */
10
11#ifdef _WIN32_WCE
12#include <commdlg.h>
13#include <aygshell.h>
14#endif
15
16#include <stdio.h>
17#include <assert.h>
18#include <ctype.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <limits.h>
22#include <time.h>
23
24#include "puzzles.h"
25
26#ifdef _WIN32_WCE
27#include "resource.h"
28#endif
29
30#define IDM_NEW 0x0010
31#define IDM_RESTART 0x0020
32#define IDM_UNDO 0x0030
33#define IDM_REDO 0x0040
34#define IDM_COPY 0x0050
35#define IDM_SOLVE 0x0060
36#define IDM_QUIT 0x0070
37#define IDM_CONFIG 0x0080
38#define IDM_DESC 0x0090
39#define IDM_SEED 0x00A0
40#define IDM_HELPC 0x00B0
41#define IDM_GAMEHELP 0x00C0
42#define IDM_ABOUT 0x00D0
43#define IDM_SAVE 0x00E0
44#define IDM_LOAD 0x00F0
45#define IDM_PRINT 0x0100
46#define IDM_PRESETS 0x0110
47#define IDM_GAMES 0x0300
48
49#define IDM_KEYEMUL 0x0400
50
51#define HELP_FILE_NAME "puzzles.hlp"
52#define HELP_CNT_NAME "puzzles.cnt"
53#ifndef NO_HTMLHELP
54#define CHM_FILE_NAME "puzzles.chm"
55#endif /* NO_HTMLHELP */
56
57#ifndef NO_HTMLHELP
58typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD);
59static htmlhelp_t htmlhelp;
60static HINSTANCE hh_dll;
61#endif /* NO_HTMLHELP */
62enum { NONE, HLP, CHM } help_type;
63char *help_path;
64bool help_has_contents;
65
66#ifndef FILENAME_MAX
67#define FILENAME_MAX (260)
68#endif
69
70#ifndef HGDI_ERROR
71#define HGDI_ERROR ((HANDLE)GDI_ERROR)
72#endif
73
74#ifdef COMBINED
75#define CLASSNAME "Puzzles"
76#else
77#define CLASSNAME thegame.name
78#endif
79
80#ifdef _WIN32_WCE
81
82/*
83 * Wrapper implementations of functions not supplied by the
84 * PocketPC API.
85 */
86
87#define SHGetSubMenu(hWndMB,ID_MENU) (HMENU)SendMessage((hWndMB), SHCMBM_GETSUBMENU, (WPARAM)0, (LPARAM)ID_MENU)
88
89#undef MessageBox
90
91int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
92{
93 TCHAR wText[2048];
94 TCHAR wCaption[2048];
95
96 MultiByteToWideChar (CP_ACP, 0, lpText, -1, wText, 2048);
97 MultiByteToWideChar (CP_ACP, 0, lpCaption, -1, wCaption, 2048);
98
99 return MessageBoxW (hWnd, wText, wCaption, uType);
100}
101
102BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, LPCSTR lpString)
103{
104 TCHAR wText[256];
105
106 MultiByteToWideChar (CP_ACP, 0, lpString, -1, wText, 256);
107 return SetDlgItemTextW(hDlg, nIDDlgItem, wText);
108}
109
110LPCSTR getenv(LPCSTR buf)
111{
112 return NULL;
113}
114
115BOOL GetKeyboardState(PBYTE pb)
116{
117 return false;
118}
119
120static TCHAR wClassName[256], wGameName[256];
121
122#endif
123
124#ifdef DEBUGGING
125static FILE *debug_fp = NULL;
126static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
127static int debug_got_console = 0;
128
129void dputs(char *buf)
130{
131 /*DWORD dw;
132
133 if (!debug_got_console) {
134 if (AllocConsole()) {
135 debug_got_console = 1;
136 debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
137 }
138 }
139 if (!debug_fp) {
140 debug_fp = fopen("debug.log", "w");
141 }
142
143 if (debug_hdl != INVALID_HANDLE_VALUE) {
144 WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
145 }
146 if (debug_fp) {
147 fputs(buf, debug_fp);
148 fflush(debug_fp);
149 }*/
150 OutputDebugString(buf);
151}
152
153void debug_printf(const char *fmt, ...)
154{
155 char buf[4096];
156 va_list ap;
157 static int debugging = -1;
158
159 if (debugging == -1)
160 debugging = getenv("DEBUG_PUZZLES") ? 1 : 0;
161
162 if (debugging) {
163 va_start(ap, fmt);
164 _vsnprintf(buf, 4095, fmt, ap);
165 dputs(buf);
166 va_end(ap);
167 }
168}
169#endif
170
171#ifndef _WIN32_WCE
172#define WINFLAGS (WS_OVERLAPPEDWINDOW &~ \
173 (WS_MAXIMIZEBOX | WS_OVERLAPPED))
174#else
175#define WINFLAGS (WS_CAPTION | WS_SYSMENU)
176#endif
177
178static void new_game_size(frontend *fe, float scale);
179
180struct font {
181 HFONT font;
182 int type;
183 int size;
184};
185
186struct cfg_aux {
187 int ctlid;
188};
189
190struct blitter {
191 HBITMAP bitmap;
192 frontend *fe;
193 int x, y, w, h;
194};
195
196enum { CFG_PRINT = CFG_FRONTEND_SPECIFIC };
197
198struct preset_menuitemref {
199 HMENU which_menu;
200 int item_index;
201};
202
203struct frontend {
204 const game *game;
205 midend *me;
206 HWND hwnd, statusbar, cfgbox;
207#ifdef _WIN32_WCE
208 HWND numpad; /* window handle for the numeric pad */
209#endif
210 HINSTANCE inst;
211 HBITMAP bitmap, prevbm;
212 RECT bitmapPosition; /* game bitmap position within game window */
213 HDC hdc;
214 COLORREF *colours;
215 HBRUSH *brushes;
216 HPEN *pens;
217 HRGN clip;
218 HMENU gamemenu, typemenu;
219 UINT timer;
220 DWORD timer_last_tickcount;
221 struct preset_menu *preset_menu;
222 struct preset_menuitemref *preset_menuitems;
223 int n_preset_menuitems;
224 struct font *fonts;
225 int nfonts, fontsize;
226 config_item *cfg;
227 struct cfg_aux *cfgaux;
228 int cfg_which, dlg_done;
229 HFONT cfgfont;
230 HBRUSH oldbr;
231 HPEN oldpen;
232 bool help_running;
233 enum { DRAWING, PRINTING, NOTHING } drawstatus;
234 DOCINFO di;
235 int printcount, printw, printh;
236 bool printsolns, printcurr, printcolour;
237 float printscale;
238 int printoffsetx, printoffsety;
239 float printpixelscale;
240 int fontstart;
241 int linewidth;
242 bool linedotted;
243 drawing *dr;
244 int xmin, ymin;
245 float puzz_scale;
246};
247
248void frontend_free(frontend *fe)
249{
250 midend_free(fe->me);
251
252 sfree(fe->colours);
253 sfree(fe->brushes);
254 sfree(fe->pens);
255 sfree(fe->fonts);
256
257 sfree(fe);
258}
259
260static void update_type_menu_tick(frontend *fe);
261static void update_copy_menu_greying(frontend *fe);
262
263void fatal(const char *fmt, ...)
264{
265 char buf[2048];
266 va_list ap;
267
268 va_start(ap, fmt);
269 vsprintf(buf, fmt, ap);
270 va_end(ap);
271
272 MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK);
273
274 exit(1);
275}
276
277char *geterrstr(void)
278{
279 LPVOID lpMsgBuf;
280 DWORD dw = GetLastError();
281 char *ret;
282
283 FormatMessage(
284 FORMAT_MESSAGE_ALLOCATE_BUFFER |
285 FORMAT_MESSAGE_FROM_SYSTEM,
286 NULL,
287 dw,
288 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
289 (LPTSTR) &lpMsgBuf,
290 0, NULL );
291
292 ret = dupstr(lpMsgBuf);
293
294 LocalFree(lpMsgBuf);
295
296 return ret;
297}
298
299void get_random_seed(void **randseed, int *randseedsize)
300{
301 SYSTEMTIME *st = snew(SYSTEMTIME);
302
303 GetLocalTime(st);
304
305 *randseed = (void *)st;
306 *randseedsize = sizeof(SYSTEMTIME);
307}
308
309static void win_status_bar(void *handle, const char *text)
310{
311#ifdef _WIN32_WCE
312 TCHAR wText[255];
313#endif
314 frontend *fe = (frontend *)handle;
315
316#ifdef _WIN32_WCE
317 MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 255);
318 SendMessage(fe->statusbar, SB_SETTEXT,
319 (WPARAM) 255 | SBT_NOBORDERS,
320 (LPARAM) wText);
321#else
322 SetWindowText(fe->statusbar, text);
323#endif
324}
325
326static blitter *win_blitter_new(void *handle, int w, int h)
327{
328 blitter *bl = snew(blitter);
329
330 memset(bl, 0, sizeof(blitter));
331 bl->w = w;
332 bl->h = h;
333 bl->bitmap = 0;
334
335 return bl;
336}
337
338static void win_blitter_free(void *handle, blitter *bl)
339{
340 if (bl->bitmap) DeleteObject(bl->bitmap);
341 sfree(bl);
342}
343
344static void blitter_mkbitmap(frontend *fe, blitter *bl)
345{
346 HDC hdc = GetDC(fe->hwnd);
347 bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h);
348 ReleaseDC(fe->hwnd, hdc);
349}
350
351/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */
352
353static void win_blitter_save(void *handle, blitter *bl, int x, int y)
354{
355 frontend *fe = (frontend *)handle;
356 HDC hdc_win, hdc_blit;
357 HBITMAP prev_blit;
358
359 assert(fe->drawstatus == DRAWING);
360
361 if (!bl->bitmap) blitter_mkbitmap(fe, bl);
362
363 bl->x = x; bl->y = y;
364
365 hdc_win = GetDC(fe->hwnd);
366 hdc_blit = CreateCompatibleDC(hdc_win);
367 if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError());
368
369 prev_blit = SelectObject(hdc_blit, bl->bitmap);
370 if (prev_blit == NULL || prev_blit == HGDI_ERROR)
371 fatal("SelectObject for hdc_main failed: 0x%x", GetLastError());
372
373 if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h,
374 fe->hdc, x, y, SRCCOPY))
375 fatal("BitBlt failed: 0x%x", GetLastError());
376
377 SelectObject(hdc_blit, prev_blit);
378 DeleteDC(hdc_blit);
379 ReleaseDC(fe->hwnd, hdc_win);
380}
381
382static void win_blitter_load(void *handle, blitter *bl, int x, int y)
383{
384 frontend *fe = (frontend *)handle;
385 HDC hdc_win, hdc_blit;
386 HBITMAP prev_blit;
387
388 assert(fe->drawstatus == DRAWING);
389
390 assert(bl->bitmap); /* we should always have saved before loading */
391
392 if (x == BLITTER_FROMSAVED) x = bl->x;
393 if (y == BLITTER_FROMSAVED) y = bl->y;
394
395 hdc_win = GetDC(fe->hwnd);
396 hdc_blit = CreateCompatibleDC(hdc_win);
397
398 prev_blit = SelectObject(hdc_blit, bl->bitmap);
399
400 BitBlt(fe->hdc, x, y, bl->w, bl->h,
401 hdc_blit, 0, 0, SRCCOPY);
402
403 SelectObject(hdc_blit, prev_blit);
404 DeleteDC(hdc_blit);
405 ReleaseDC(fe->hwnd, hdc_win);
406}
407
408void frontend_default_colour(frontend *fe, float *output)
409{
410 DWORD c = GetSysColor(COLOR_MENU); /* ick */
411
412 output[0] = (float)(GetRValue(c) / 255.0);
413 output[1] = (float)(GetGValue(c) / 255.0);
414 output[2] = (float)(GetBValue(c) / 255.0);
415}
416
417static POINT win_transform_point(frontend *fe, int x, int y)
418{
419 POINT ret;
420
421 assert(fe->drawstatus != NOTHING);
422
423 if (fe->drawstatus == PRINTING) {
424 ret.x = (int)(fe->printoffsetx + fe->printpixelscale * x);
425 ret.y = (int)(fe->printoffsety + fe->printpixelscale * y);
426 } else {
427 ret.x = x;
428 ret.y = y;
429 }
430
431 return ret;
432}
433
434static void win_text_colour(frontend *fe, int colour)
435{
436 assert(fe->drawstatus != NOTHING);
437
438 if (fe->drawstatus == PRINTING) {
439 int hatch;
440 float r, g, b;
441 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
442
443 /*
444 * Displaying text in hatched colours is not permitted.
445 */
446 assert(hatch < 0);
447
448 SetTextColor(fe->hdc, RGB(r * 255, g * 255, b * 255));
449 } else {
450 SetTextColor(fe->hdc, fe->colours[colour]);
451 }
452}
453
454static void win_set_brush(frontend *fe, int colour)
455{
456 HBRUSH br;
457 assert(fe->drawstatus != NOTHING);
458
459 if (fe->drawstatus == PRINTING) {
460 int hatch;
461 float r, g, b;
462 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
463
464 if (hatch < 0) {
465 br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255));
466 } else {
467#ifdef _WIN32_WCE
468 /*
469 * This is only ever required during printing, and the
470 * PocketPC port doesn't support printing.
471 */
472 fatal("CreateHatchBrush not supported");
473#else
474 br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL :
475 hatch == HATCH_SLASH ? HS_BDIAGONAL :
476 hatch == HATCH_HORIZ ? HS_HORIZONTAL :
477 hatch == HATCH_VERT ? HS_VERTICAL :
478 hatch == HATCH_PLUS ? HS_CROSS :
479 /* hatch == HATCH_X ? */ HS_DIAGCROSS,
480 RGB(0,0,0));
481#endif
482 }
483 } else {
484 br = fe->brushes[colour];
485 }
486 fe->oldbr = SelectObject(fe->hdc, br);
487}
488
489static void win_reset_brush(frontend *fe)
490{
491 HBRUSH br;
492
493 assert(fe->drawstatus != NOTHING);
494
495 br = SelectObject(fe->hdc, fe->oldbr);
496 if (fe->drawstatus == PRINTING)
497 DeleteObject(br);
498}
499
500static void win_set_pen(frontend *fe, int colour, bool thin)
501{
502 HPEN pen;
503 assert(fe->drawstatus != NOTHING);
504
505 if (fe->drawstatus == PRINTING) {
506 int hatch;
507 float r, g, b;
508 int width = thin ? 0 : fe->linewidth;
509
510 if (fe->linedotted)
511 width = 0;
512
513 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
514 /*
515 * Stroking in hatched colours is not permitted.
516 */
517 assert(hatch < 0);
518 pen = CreatePen(fe->linedotted ? PS_DOT : PS_SOLID,
519 width, RGB(r * 255, g * 255, b * 255));
520 } else {
521 pen = fe->pens[colour];
522 }
523 fe->oldpen = SelectObject(fe->hdc, pen);
524}
525
526static void win_reset_pen(frontend *fe)
527{
528 HPEN pen;
529
530 assert(fe->drawstatus != NOTHING);
531
532 pen = SelectObject(fe->hdc, fe->oldpen);
533 if (fe->drawstatus == PRINTING)
534 DeleteObject(pen);
535}
536
537static void win_clip(void *handle, int x, int y, int w, int h)
538{
539 frontend *fe = (frontend *)handle;
540 POINT p, q;
541
542 if (fe->drawstatus == NOTHING)
543 return;
544
545 p = win_transform_point(fe, x, y);
546 q = win_transform_point(fe, x+w, y+h);
547 IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y);
548}
549
550static void win_unclip(void *handle)
551{
552 frontend *fe = (frontend *)handle;
553
554 if (fe->drawstatus == NOTHING)
555 return;
556
557 SelectClipRgn(fe->hdc, NULL);
558}
559
560static void win_draw_text(void *handle, int x, int y, int fonttype,
561 int fontsize, int align, int colour,
562 const char *text)
563{
564 frontend *fe = (frontend *)handle;
565 POINT xy;
566 int i;
567 LOGFONT lf;
568
569 if (fe->drawstatus == NOTHING)
570 return;
571
572 if (fe->drawstatus == PRINTING)
573 fontsize = (int)(fontsize * fe->printpixelscale);
574
575 xy = win_transform_point(fe, x, y);
576
577 /*
578 * Find or create the font.
579 */
580 for (i = fe->fontstart; i < fe->nfonts; i++)
581 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
582 break;
583
584 if (i == fe->nfonts) {
585 if (fe->fontsize <= fe->nfonts) {
586 fe->fontsize = fe->nfonts + 10;
587 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
588 }
589
590 fe->nfonts++;
591
592 fe->fonts[i].type = fonttype;
593 fe->fonts[i].size = fontsize;
594
595 memset (&lf, 0, sizeof(LOGFONT));
596 lf.lfHeight = -fontsize;
597 lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD);
598 lf.lfCharSet = DEFAULT_CHARSET;
599 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
600 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
601 lf.lfQuality = DEFAULT_QUALITY;
602 lf.lfPitchAndFamily = (fonttype == FONT_FIXED ?
603 FIXED_PITCH | FF_DONTCARE :
604 VARIABLE_PITCH | FF_SWISS);
605#ifdef _WIN32_WCE
606 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
607#endif
608
609 fe->fonts[i].font = CreateFontIndirect(&lf);
610 }
611
612 /*
613 * Position and draw the text.
614 */
615 {
616 HFONT oldfont;
617 TEXTMETRIC tm;
618 SIZE size;
619 WCHAR wText[256];
620 MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256);
621
622 oldfont = SelectObject(fe->hdc, fe->fonts[i].font);
623 if (GetTextMetrics(fe->hdc, &tm)) {
624 if (align & ALIGN_VCENTRE)
625 xy.y -= (tm.tmAscent+tm.tmDescent)/2;
626 else
627 xy.y -= tm.tmAscent;
628 }
629 if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size))
630 {
631 if (align & ALIGN_HCENTRE)
632 xy.x -= size.cx / 2;
633 else if (align & ALIGN_HRIGHT)
634 xy.x -= size.cx;
635 }
636 SetBkMode(fe->hdc, TRANSPARENT);
637 win_text_colour(fe, colour);
638 ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL);
639 SelectObject(fe->hdc, oldfont);
640 }
641}
642
643static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour)
644{
645 frontend *fe = (frontend *)handle;
646 POINT p, q;
647
648 if (fe->drawstatus == NOTHING)
649 return;
650
651 if (fe->drawstatus == DRAWING && w == 1 && h == 1) {
652 /*
653 * Rectangle() appears to get uppity if asked to draw a 1x1
654 * rectangle, presumably on the grounds that that's beneath
655 * its dignity and you ought to be using SetPixel instead.
656 * So I will.
657 */
658 SetPixel(fe->hdc, x, y, fe->colours[colour]);
659 } else {
660 win_set_brush(fe, colour);
661 win_set_pen(fe, colour, true);
662 p = win_transform_point(fe, x, y);
663 q = win_transform_point(fe, x+w, y+h);
664 Rectangle(fe->hdc, p.x, p.y, q.x, q.y);
665 win_reset_brush(fe);
666 win_reset_pen(fe);
667 }
668}
669
670static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
671{
672 frontend *fe = (frontend *)handle;
673 POINT pp[2];
674
675 if (fe->drawstatus == NOTHING)
676 return;
677
678 win_set_pen(fe, colour, false);
679 pp[0] = win_transform_point(fe, x1, y1);
680 pp[1] = win_transform_point(fe, x2, y2);
681 Polyline(fe->hdc, pp, 2);
682 if (fe->drawstatus == DRAWING)
683 SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]);
684 win_reset_pen(fe);
685}
686
687static void win_draw_circle(void *handle, int cx, int cy, int radius,
688 int fillcolour, int outlinecolour)
689{
690 frontend *fe = (frontend *)handle;
691 POINT p, q;
692
693 assert(outlinecolour >= 0);
694
695 if (fe->drawstatus == NOTHING)
696 return;
697
698 if (fillcolour >= 0)
699 win_set_brush(fe, fillcolour);
700 else
701 fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH));
702
703 win_set_pen(fe, outlinecolour, false);
704 p = win_transform_point(fe, cx - radius, cy - radius);
705 q = win_transform_point(fe, cx + radius, cy + radius);
706 Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1);
707 win_reset_brush(fe);
708 win_reset_pen(fe);
709}
710
711static void win_draw_polygon(void *handle, int *coords, int npoints,
712 int fillcolour, int outlinecolour)
713{
714 frontend *fe = (frontend *)handle;
715 POINT *pts;
716 int i;
717
718 if (fe->drawstatus == NOTHING)
719 return;
720
721 pts = snewn(npoints+1, POINT);
722
723 for (i = 0; i <= npoints; i++) {
724 int j = (i < npoints ? i : 0);
725 pts[i] = win_transform_point(fe, coords[j*2], coords[j*2+1]);
726 }
727
728 assert(outlinecolour >= 0);
729
730 if (fillcolour >= 0) {
731 win_set_brush(fe, fillcolour);
732 win_set_pen(fe, outlinecolour, false);
733 Polygon(fe->hdc, pts, npoints);
734 win_reset_brush(fe);
735 win_reset_pen(fe);
736 } else {
737 win_set_pen(fe, outlinecolour, false);
738 Polyline(fe->hdc, pts, npoints+1);
739 win_reset_pen(fe);
740 }
741
742 sfree(pts);
743}
744
745static void win_start_draw(void *handle)
746{
747 frontend *fe = (frontend *)handle;
748 HDC hdc_win;
749
750 assert(fe->drawstatus == NOTHING);
751
752 hdc_win = GetDC(fe->hwnd);
753 fe->hdc = CreateCompatibleDC(hdc_win);
754 fe->prevbm = SelectObject(fe->hdc, fe->bitmap);
755 ReleaseDC(fe->hwnd, hdc_win);
756 fe->clip = NULL;
757#ifndef _WIN32_WCE
758 SetMapMode(fe->hdc, MM_TEXT);
759#endif
760 fe->drawstatus = DRAWING;
761}
762
763static void win_draw_update(void *handle, int x, int y, int w, int h)
764{
765 frontend *fe = (frontend *)handle;
766 RECT r;
767
768 if (fe->drawstatus != DRAWING)
769 return;
770
771 r.left = x;
772 r.top = y;
773 r.right = x + w;
774 r.bottom = y + h;
775
776 OffsetRect(&r, fe->bitmapPosition.left, fe->bitmapPosition.top);
777 InvalidateRect(fe->hwnd, &r, false);
778}
779
780static void win_end_draw(void *handle)
781{
782 frontend *fe = (frontend *)handle;
783 assert(fe->drawstatus == DRAWING);
784 SelectObject(fe->hdc, fe->prevbm);
785 DeleteDC(fe->hdc);
786 if (fe->clip) {
787 DeleteObject(fe->clip);
788 fe->clip = NULL;
789 }
790 fe->drawstatus = NOTHING;
791}
792
793static void win_line_width(void *handle, float width)
794{
795 frontend *fe = (frontend *)handle;
796
797 assert(fe->drawstatus != DRAWING);
798 if (fe->drawstatus == NOTHING)
799 return;
800
801 fe->linewidth = (int)(width * fe->printpixelscale);
802}
803
804static void win_line_dotted(void *handle, bool dotted)
805{
806 frontend *fe = (frontend *)handle;
807
808 assert(fe->drawstatus != DRAWING);
809 if (fe->drawstatus == NOTHING)
810 return;
811
812 fe->linedotted = dotted;
813}
814
815static void win_begin_doc(void *handle, int pages)
816{
817 frontend *fe = (frontend *)handle;
818
819 assert(fe->drawstatus != DRAWING);
820 if (fe->drawstatus == NOTHING)
821 return;
822
823 if (StartDoc(fe->hdc, &fe->di) <= 0) {
824 char *e = geterrstr();
825 MessageBox(fe->hwnd, e, "Error starting to print",
826 MB_ICONERROR | MB_OK);
827 sfree(e);
828 fe->drawstatus = NOTHING;
829 }
830
831 /*
832 * Push a marker on the font stack so that we won't use the
833 * same fonts for printing and drawing. (This is because
834 * drawing seems to look generally better in bold, but printing
835 * is better not in bold.)
836 */
837 fe->fontstart = fe->nfonts;
838}
839
840static void win_begin_page(void *handle, int number)
841{
842 frontend *fe = (frontend *)handle;
843
844 assert(fe->drawstatus != DRAWING);
845 if (fe->drawstatus == NOTHING)
846 return;
847
848 if (StartPage(fe->hdc) <= 0) {
849 char *e = geterrstr();
850 MessageBox(fe->hwnd, e, "Error starting a page",
851 MB_ICONERROR | MB_OK);
852 sfree(e);
853 fe->drawstatus = NOTHING;
854 }
855}
856
857static void win_begin_puzzle(void *handle, float xm, float xc,
858 float ym, float yc, int pw, int ph, float wmm)
859{
860 frontend *fe = (frontend *)handle;
861 int ppw, pph, pox, poy;
862 float mmpw, mmph, mmox, mmoy;
863 float scale;
864
865 assert(fe->drawstatus != DRAWING);
866 if (fe->drawstatus == NOTHING)
867 return;
868
869 ppw = GetDeviceCaps(fe->hdc, HORZRES);
870 pph = GetDeviceCaps(fe->hdc, VERTRES);
871 mmpw = (float)GetDeviceCaps(fe->hdc, HORZSIZE);
872 mmph = (float)GetDeviceCaps(fe->hdc, VERTSIZE);
873
874 /*
875 * Compute the puzzle's position on the logical page.
876 */
877 mmox = xm * mmpw + xc;
878 mmoy = ym * mmph + yc;
879
880 /*
881 * Work out what that comes to in pixels.
882 */
883 pox = (int)(mmox * (float)ppw / mmpw);
884 poy = (int)(mmoy * (float)pph / mmph);
885
886 /*
887 * And determine the scale.
888 *
889 * I need a scale such that the maximum puzzle-coordinate
890 * extent of the rectangle (pw * scale) is equal to the pixel
891 * equivalent of the puzzle's millimetre width (wmm * ppw /
892 * mmpw).
893 */
894 scale = (wmm * ppw) / (mmpw * pw);
895
896 /*
897 * Now store pox, poy and scale for use in the main drawing
898 * functions.
899 */
900 fe->printoffsetx = pox;
901 fe->printoffsety = poy;
902 fe->printpixelscale = scale;
903
904 fe->linewidth = 1;
905 fe->linedotted = false;
906}
907
908static void win_end_puzzle(void *handle)
909{
910 /* Nothing needs to be done here. */
911}
912
913static void win_end_page(void *handle, int number)
914{
915 frontend *fe = (frontend *)handle;
916
917 assert(fe->drawstatus != DRAWING);
918
919 if (fe->drawstatus == NOTHING)
920 return;
921
922 if (EndPage(fe->hdc) <= 0) {
923 char *e = geterrstr();
924 MessageBox(fe->hwnd, e, "Error finishing a page",
925 MB_ICONERROR | MB_OK);
926 sfree(e);
927 fe->drawstatus = NOTHING;
928 }
929}
930
931static void win_end_doc(void *handle)
932{
933 frontend *fe = (frontend *)handle;
934
935 assert(fe->drawstatus != DRAWING);
936
937 /*
938 * Free all the fonts created since we began printing.
939 */
940 while (fe->nfonts > fe->fontstart) {
941 fe->nfonts--;
942 DeleteObject(fe->fonts[fe->nfonts].font);
943 }
944 fe->fontstart = 0;
945
946 /*
947 * The MSDN web site sample code doesn't bother to call EndDoc
948 * if an error occurs half way through printing. I expect doing
949 * so would cause the erroneous document to actually be
950 * printed, or something equally undesirable.
951 */
952 if (fe->drawstatus == NOTHING)
953 return;
954
955 if (EndDoc(fe->hdc) <= 0) {
956 char *e = geterrstr();
957 MessageBox(fe->hwnd, e, "Error finishing printing",
958 MB_ICONERROR | MB_OK);
959 sfree(e);
960 fe->drawstatus = NOTHING;
961 }
962}
963
964char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
965{
966 /*
967 * We assume Windows can cope with any UTF-8 likely to be
968 * emitted by a puzzle.
969 */
970 return dupstr(strings[0]);
971}
972
973const struct drawing_api win_drawing = {
974 win_draw_text,
975 win_draw_rect,
976 win_draw_line,
977 win_draw_polygon,
978 win_draw_circle,
979 win_draw_update,
980 win_clip,
981 win_unclip,
982 win_start_draw,
983 win_end_draw,
984 win_status_bar,
985 win_blitter_new,
986 win_blitter_free,
987 win_blitter_save,
988 win_blitter_load,
989 win_begin_doc,
990 win_begin_page,
991 win_begin_puzzle,
992 win_end_puzzle,
993 win_end_page,
994 win_end_doc,
995 win_line_width,
996 win_line_dotted,
997 win_text_fallback,
998};
999
1000void print(frontend *fe)
1001{
1002#ifndef _WIN32_WCE
1003 PRINTDLG pd;
1004 char doctitle[256];
1005 document *doc;
1006 midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
1007 int i;
1008 const char *err = NULL;
1009
1010 /*
1011 * Create our document structure and fill it up with puzzles.
1012 */
1013 doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
1014 for (i = 0; i < fe->printcount; i++) {
1015 if (i == 0 && fe->printcurr) {
1016 err = midend_print_puzzle(fe->me, doc, fe->printsolns);
1017 } else {
1018 if (!nme) {
1019 game_params *params;
1020
1021 nme = midend_new(NULL, fe->game, NULL, NULL);
1022
1023 /*
1024 * Set the non-interactive mid-end to have the same
1025 * parameters as the standard one.
1026 */
1027 params = midend_get_params(fe->me);
1028 midend_set_params(nme, params);
1029 fe->game->free_params(params);
1030 }
1031
1032 midend_new_game(nme);
1033 err = midend_print_puzzle(nme, doc, fe->printsolns);
1034 }
1035 if (err)
1036 break;
1037 }
1038 if (nme)
1039 midend_free(nme);
1040
1041 if (err) {
1042 MessageBox(fe->hwnd, err, "Error preparing puzzles for printing",
1043 MB_ICONERROR | MB_OK);
1044 document_free(doc);
1045 return;
1046 }
1047
1048 memset(&pd, 0, sizeof(pd));
1049 pd.lStructSize = sizeof(pd);
1050 pd.hwndOwner = fe->hwnd;
1051 pd.hDevMode = NULL;
1052 pd.hDevNames = NULL;
1053 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC |
1054 PD_NOPAGENUMS | PD_NOSELECTION;
1055 pd.nCopies = 1;
1056 pd.nFromPage = pd.nToPage = 0xFFFF;
1057 pd.nMinPage = pd.nMaxPage = 1;
1058
1059 if (!PrintDlg(&pd)) {
1060 document_free(doc);
1061 return;
1062 }
1063
1064 /*
1065 * Now pd.hDC is a device context for the printer.
1066 */
1067
1068 /*
1069 * FIXME: IWBNI we put up an Abort box here.
1070 */
1071
1072 memset(&fe->di, 0, sizeof(fe->di));
1073 fe->di.cbSize = sizeof(fe->di);
1074 sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's"
1075 " Portable Puzzle Collection)", fe->game->name);
1076 fe->di.lpszDocName = doctitle;
1077 fe->di.lpszOutput = NULL;
1078 fe->di.lpszDatatype = NULL;
1079 fe->di.fwType = 0;
1080
1081 fe->drawstatus = PRINTING;
1082 fe->hdc = pd.hDC;
1083
1084 fe->dr = drawing_new(&win_drawing, NULL, fe);
1085 document_print(doc, fe->dr);
1086 drawing_free(fe->dr);
1087 fe->dr = NULL;
1088
1089 fe->drawstatus = NOTHING;
1090
1091 DeleteDC(pd.hDC);
1092 document_free(doc);
1093#endif
1094}
1095
1096void deactivate_timer(frontend *fe)
1097{
1098 if (!fe)
1099 return; /* for non-interactive midend */
1100 if (fe->hwnd) KillTimer(fe->hwnd, fe->timer);
1101 fe->timer = 0;
1102}
1103
1104void activate_timer(frontend *fe)
1105{
1106 if (!fe)
1107 return; /* for non-interactive midend */
1108 if (!fe->timer) {
1109 fe->timer = SetTimer(fe->hwnd, 1, 20, NULL);
1110 fe->timer_last_tickcount = GetTickCount();
1111 }
1112}
1113
1114void write_clip(HWND hwnd, char *data)
1115{
1116 HGLOBAL clipdata;
1117 int len, i, j;
1118 char *data2;
1119 void *lock;
1120
1121 /*
1122 * Windows expects CRLF in the clipboard, so we must convert
1123 * any \n that has come out of the puzzle backend.
1124 */
1125 len = 0;
1126 for (i = 0; data[i]; i++) {
1127 if (data[i] == '\n')
1128 len++;
1129 len++;
1130 }
1131 data2 = snewn(len+1, char);
1132 j = 0;
1133 for (i = 0; data[i]; i++) {
1134 if (data[i] == '\n')
1135 data2[j++] = '\r';
1136 data2[j++] = data[i];
1137 }
1138 assert(j == len);
1139 data2[j] = '\0';
1140
1141 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1142 if (!clipdata) {
1143 sfree(data2);
1144 return;
1145 }
1146 lock = GlobalLock(clipdata);
1147 if (!lock) {
1148 GlobalFree(clipdata);
1149 sfree(data2);
1150 return;
1151 }
1152 memcpy(lock, data2, len);
1153 ((unsigned char *) lock)[len] = 0;
1154 GlobalUnlock(clipdata);
1155
1156 if (OpenClipboard(hwnd)) {
1157 EmptyClipboard();
1158 SetClipboardData(CF_TEXT, clipdata);
1159 CloseClipboard();
1160 } else
1161 GlobalFree(clipdata);
1162
1163 sfree(data2);
1164}
1165
1166/*
1167 * Set up Help and see if we can find a help file.
1168 */
1169static void init_help(void)
1170{
1171#ifndef _WIN32_WCE
1172 char b[2048], *p, *q, *r;
1173 FILE *fp;
1174
1175 /*
1176 * Find the executable file path, so we can look alongside
1177 * it for help files. Trim the filename off the end.
1178 */
1179 GetModuleFileName(NULL, b, sizeof(b) - 1);
1180 r = b;
1181 p = strrchr(b, '\\');
1182 if (p && p >= r) r = p+1;
1183 q = strrchr(b, ':');
1184 if (q && q >= r) r = q+1;
1185
1186#ifndef NO_HTMLHELP
1187 /*
1188 * Try HTML Help first.
1189 */
1190 strcpy(r, CHM_FILE_NAME);
1191 if ( (fp = fopen(b, "r")) != NULL) {
1192 fclose(fp);
1193
1194 /*
1195 * We have a .CHM. See if we can use it.
1196 */
1197 hh_dll = LoadLibrary("hhctrl.ocx");
1198 if (hh_dll) {
1199 htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
1200 if (!htmlhelp)
1201 FreeLibrary(hh_dll);
1202 }
1203 if (htmlhelp) {
1204 help_path = dupstr(b);
1205 help_type = CHM;
1206 return;
1207 }
1208 }
1209#endif /* NO_HTMLHELP */
1210
1211 /*
1212 * Now try old-style .HLP.
1213 */
1214 strcpy(r, HELP_FILE_NAME);
1215 if ( (fp = fopen(b, "r")) != NULL) {
1216 fclose(fp);
1217
1218 help_path = dupstr(b);
1219 help_type = HLP;
1220
1221 /*
1222 * See if there's a .CNT file alongside it.
1223 */
1224 strcpy(r, HELP_CNT_NAME);
1225 if ( (fp = fopen(b, "r")) != NULL) {
1226 fclose(fp);
1227 help_has_contents = true;
1228 } else
1229 help_has_contents = false;
1230
1231 return;
1232 }
1233
1234 help_type = NONE; /* didn't find any */
1235#endif
1236}
1237
1238#ifndef _WIN32_WCE
1239
1240/*
1241 * Start Help.
1242 */
1243static void start_help(frontend *fe, const char *topic)
1244{
1245 char *str = NULL;
1246 int cmd;
1247
1248 switch (help_type) {
1249 case HLP:
1250 assert(help_path);
1251 if (topic) {
1252 str = snewn(10+strlen(topic), char);
1253 sprintf(str, "JI(`',`%s')", topic);
1254 cmd = HELP_COMMAND;
1255 } else if (help_has_contents) {
1256 cmd = HELP_FINDER;
1257 } else {
1258 cmd = HELP_CONTENTS;
1259 }
1260 WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
1261 fe->help_running = true;
1262 break;
1263 case CHM:
1264#ifndef NO_HTMLHELP
1265 assert(help_path);
1266 assert(htmlhelp);
1267 if (topic) {
1268 str = snewn(20 + strlen(topic) + strlen(help_path), char);
1269 sprintf(str, "%s::/%s.html>main", help_path, topic);
1270 } else {
1271 str = dupstr(help_path);
1272 }
1273 htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
1274 fe->help_running = true;
1275 break;
1276#endif /* NO_HTMLHELP */
1277 case NONE:
1278 assert(!"This shouldn't happen");
1279 break;
1280 }
1281
1282 sfree(str);
1283}
1284
1285/*
1286 * Stop Help on window cleanup.
1287 */
1288static void stop_help(frontend *fe)
1289{
1290 if (fe->help_running) {
1291 switch (help_type) {
1292 case HLP:
1293 WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
1294 break;
1295 case CHM:
1296#ifndef NO_HTMLHELP
1297 assert(htmlhelp);
1298 htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
1299 break;
1300#endif /* NO_HTMLHELP */
1301 case NONE:
1302 assert(!"This shouldn't happen");
1303 break;
1304 }
1305 fe->help_running = false;
1306 }
1307}
1308
1309#endif
1310
1311/*
1312 * Terminate Help on process exit.
1313 */
1314static void cleanup_help(void)
1315{
1316 /* Nothing to do currently.
1317 * (If we were running HTML Help single-threaded, this is where we'd
1318 * call HH_UNINITIALIZE.) */
1319}
1320
1321static int get_statusbar_height(frontend *fe)
1322{
1323 int sy;
1324 if (fe->statusbar) {
1325 RECT sr;
1326 GetWindowRect(fe->statusbar, &sr);
1327 sy = sr.bottom - sr.top;
1328 } else {
1329 sy = 0;
1330 }
1331 return sy;
1332}
1333
1334static void adjust_statusbar(frontend *fe, RECT *r)
1335{
1336 int sy;
1337
1338 if (!fe->statusbar) return;
1339
1340 sy = get_statusbar_height(fe);
1341#ifndef _WIN32_WCE
1342 SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left,
1343 sy, SWP_NOZORDER);
1344#endif
1345}
1346
1347static void get_menu_size(HWND wh, RECT *r)
1348{
1349 HMENU bar = GetMenu(wh);
1350 RECT rect;
1351 int i;
1352
1353 SetRect(r, 0, 0, 0, 0);
1354 for (i = 0; i < GetMenuItemCount(bar); i++) {
1355 GetMenuItemRect(wh, bar, i, &rect);
1356 UnionRect(r, r, &rect);
1357 }
1358}
1359
1360/*
1361 * Given a proposed new puzzle size (cx,cy), work out the actual
1362 * puzzle size that would be (px,py) and the window size including
1363 * furniture (wx,wy).
1364 */
1365
1366static bool check_window_resize(frontend *fe, int cx, int cy,
1367 int *px, int *py, int *wx, int *wy)
1368{
1369 RECT r;
1370 int x, y, sy = get_statusbar_height(fe);
1371 bool changed = false;
1372
1373 /* disallow making window thinner than menu bar */
1374 x = max(cx, fe->xmin);
1375 y = max(cy - sy, fe->ymin);
1376
1377 /*
1378 * See if we actually got the window size we wanted, and adjust
1379 * the puzzle size if not.
1380 */
1381 midend_size(fe->me, &x, &y, true);
1382 if (x != cx || y != cy) {
1383 /*
1384 * Resize the window, now we know what size we _really_
1385 * want it to be.
1386 */
1387 r.left = r.top = 0;
1388 r.right = x;
1389 r.bottom = y + sy;
1390 AdjustWindowRectEx(&r, WINFLAGS, true, 0);
1391 *wx = r.right - r.left;
1392 *wy = r.bottom - r.top;
1393 changed = true;
1394 }
1395
1396 *px = x;
1397 *py = y;
1398
1399 fe->puzz_scale =
1400 (float)midend_tilesize(fe->me) / (float)fe->game->preferred_tilesize;
1401
1402 return changed;
1403}
1404
1405/*
1406 * Given the current window size, make sure it's sane for the
1407 * current puzzle and resize if necessary.
1408 */
1409
1410static void check_window_size(frontend *fe, int *px, int *py)
1411{
1412 RECT r;
1413 int wx, wy, cx, cy;
1414
1415 GetClientRect(fe->hwnd, &r);
1416 cx = r.right - r.left;
1417 cy = r.bottom - r.top;
1418
1419 if (check_window_resize(fe, cx, cy, px, py, &wx, &wy)) {
1420#ifdef _WIN32_WCE
1421 SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy,
1422 SWP_NOMOVE | SWP_NOZORDER);
1423#endif
1424 ;
1425 }
1426
1427 GetClientRect(fe->hwnd, &r);
1428 adjust_statusbar(fe, &r);
1429}
1430
1431static void get_max_puzzle_size(frontend *fe, int *x, int *y)
1432{
1433 RECT r, sr;
1434
1435 if (SystemParametersInfo(SPI_GETWORKAREA, 0, &sr, false)) {
1436 *x = sr.right - sr.left;
1437 *y = sr.bottom - sr.top;
1438 r.left = 100;
1439 r.right = 200;
1440 r.top = 100;
1441 r.bottom = 200;
1442 AdjustWindowRectEx(&r, WINFLAGS, true, 0);
1443 *x -= r.right - r.left - 100;
1444 *y -= r.bottom - r.top - 100;
1445 } else {
1446 *x = *y = INT_MAX;
1447 }
1448
1449 if (fe->statusbar != NULL) {
1450 GetWindowRect(fe->statusbar, &sr);
1451 *y -= sr.bottom - sr.top;
1452 }
1453}
1454
1455#ifdef _WIN32_WCE
1456/* Toolbar buttons on the numeric pad */
1457static TBBUTTON tbNumpadButtons[] =
1458{
1459 {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1460 {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1461 {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1462 {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1463 {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1464 {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1465 {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1466 {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1467 {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1468 {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}
1469};
1470#endif
1471
1472/*
1473 * Allocate a new frontend structure and create its main window.
1474 */
1475static frontend *frontend_new(HINSTANCE inst)
1476{
1477 frontend *fe;
1478 const char *nogame = "Puzzles (no game selected)";
1479
1480 fe = snew(frontend);
1481
1482 fe->inst = inst;
1483
1484 fe->game = NULL;
1485 fe->me = NULL;
1486
1487 fe->timer = 0;
1488 fe->hwnd = NULL;
1489
1490 fe->help_running = false;
1491
1492 fe->drawstatus = NOTHING;
1493 fe->dr = NULL;
1494 fe->fontstart = 0;
1495
1496 fe->fonts = NULL;
1497 fe->nfonts = fe->fontsize = 0;
1498
1499 fe->colours = NULL;
1500 fe->brushes = NULL;
1501 fe->pens = NULL;
1502
1503 fe->puzz_scale = 1.0;
1504
1505 #ifdef _WIN32_WCE
1506 MultiByteToWideChar (CP_ACP, 0, nogame, -1, wGameName, 256);
1507 fe->hwnd = CreateWindowEx(0, wClassName, wGameName,
1508 WS_VISIBLE,
1509 CW_USEDEFAULT, CW_USEDEFAULT,
1510 CW_USEDEFAULT, CW_USEDEFAULT,
1511 NULL, NULL, inst, NULL);
1512
1513 {
1514 SHMENUBARINFO mbi;
1515 RECT rc, rcBar, rcTB, rcClient;
1516
1517 memset (&mbi, 0, sizeof(SHMENUBARINFO));
1518 mbi.cbSize = sizeof(SHMENUBARINFO);
1519 mbi.hwndParent = fe->hwnd;
1520 mbi.nToolBarId = IDR_MENUBAR1;
1521 mbi.hInstRes = inst;
1522
1523 SHCreateMenuBar(&mbi);
1524
1525 GetWindowRect(fe->hwnd, &rc);
1526 GetWindowRect(mbi.hwndMB, &rcBar);
1527 rc.bottom -= rcBar.bottom - rcBar.top;
1528 MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
1529
1530 fe->numpad = NULL;
1531 }
1532#else
1533 fe->hwnd = CreateWindowEx(0, CLASSNAME, nogame,
1534 WS_OVERLAPPEDWINDOW &~
1535 (WS_MAXIMIZEBOX),
1536 CW_USEDEFAULT, CW_USEDEFAULT,
1537 CW_USEDEFAULT, CW_USEDEFAULT,
1538 NULL, NULL, inst, NULL);
1539 if (!fe->hwnd) {
1540 DWORD lerr = GetLastError();
1541 printf("no window: 0x%x\n", (unsigned)lerr);
1542 }
1543#endif
1544
1545 fe->gamemenu = NULL;
1546 fe->preset_menu = NULL;
1547
1548 fe->statusbar = NULL;
1549 fe->bitmap = NULL;
1550
1551 SetWindowLongPtr(fe->hwnd, GWLP_USERDATA, (LONG_PTR)fe);
1552
1553 return fe;
1554}
1555
1556static void savefile_write(void *wctx, const void *buf, int len)
1557{
1558 FILE *fp = (FILE *)wctx;
1559 fwrite(buf, 1, len, fp);
1560}
1561
1562static bool savefile_read(void *wctx, void *buf, int len)
1563{
1564 FILE *fp = (FILE *)wctx;
1565 int ret;
1566
1567 ret = fread(buf, 1, len, fp);
1568 return (ret == len);
1569}
1570
1571/*
1572 * Create an appropriate midend structure to go in a puzzle window,
1573 * given a game type and/or a command-line argument.
1574 *
1575 * 'arg' can be either a game ID string (descriptive, random, or a
1576 * plain set of parameters) or the filename of a save file. The two
1577 * boolean flag arguments indicate which possibilities are
1578 * permissible.
1579 */
1580static midend *midend_for_new_game(frontend *fe, const game *cgame,
1581 char *arg, bool maybe_game_id,
1582 bool maybe_save_file, char **error)
1583{
1584 midend *me = NULL;
1585
1586 if (!arg) {
1587 if (me) midend_free(me);
1588 me = midend_new(fe, cgame, &win_drawing, fe);
1589 midend_new_game(me);
1590 } else {
1591 FILE *fp;
1592 const char *err_param, *err_load;
1593
1594 /*
1595 * See if arg is a valid filename of a save game file.
1596 */
1597 err_load = NULL;
1598 if (maybe_save_file && (fp = fopen(arg, "r")) != NULL) {
1599 const game *loadgame;
1600
1601#ifdef COMBINED
1602 /*
1603 * Find out what kind of game is stored in the save
1604 * file; if we're going to end up loading that, it
1605 * will have to override our caller's judgment as to
1606 * what game to initialise our midend with.
1607 */
1608 char *id_name;
1609 err_load = identify_game(&id_name, savefile_read, fp);
1610 if (!err_load) {
1611 int i;
1612 for (i = 0; i < gamecount; i++)
1613 if (!strcmp(id_name, gamelist[i]->name))
1614 break;
1615 if (i == gamecount) {
1616 err_load = "Save file is for a game not supported by"
1617 " this program";
1618 } else {
1619 loadgame = gamelist[i];
1620 rewind(fp); /* go back to the start for actual load */
1621 }
1622 }
1623#else
1624 loadgame = cgame;
1625#endif
1626 if (!err_load) {
1627 if (me) midend_free(me);
1628 me = midend_new(fe, loadgame, &win_drawing, fe);
1629 err_load = midend_deserialise(me, savefile_read, fp);
1630 }
1631 } else {
1632 err_load = "Unable to open file";
1633 }
1634
1635 if (maybe_game_id && (!maybe_save_file || err_load)) {
1636 /*
1637 * See if arg is a game description.
1638 */
1639 if (me) midend_free(me);
1640 me = midend_new(fe, cgame, &win_drawing, fe);
1641 err_param = midend_game_id(me, arg);
1642 if (!err_param) {
1643 midend_new_game(me);
1644 } else {
1645 if (maybe_save_file) {
1646 *error = snewn(256 + strlen(arg) + strlen(err_param) +
1647 strlen(err_load), char);
1648 sprintf(*error, "Supplied argument \"%s\" is neither a"
1649 " game ID (%s) nor a save file (%s)",
1650 arg, err_param, err_load);
1651 } else {
1652 *error = dupstr(err_param);
1653 }
1654 midend_free(me);
1655 sfree(fe);
1656 return NULL;
1657 }
1658 } else if (err_load) {
1659 *error = dupstr(err_load);
1660 midend_free(me);
1661 sfree(fe);
1662 return NULL;
1663 }
1664 }
1665
1666 return me;
1667}
1668
1669static void populate_preset_menu(frontend *fe,
1670 struct preset_menu *menu, HMENU winmenu)
1671{
1672 int i;
1673 for (i = 0; i < menu->n_entries; i++) {
1674 struct preset_menu_entry *entry = &menu->entries[i];
1675 UINT_PTR id_or_sub;
1676 UINT flags = MF_ENABLED;
1677
1678 if (entry->params) {
1679 id_or_sub = (UINT_PTR)(IDM_PRESETS + 0x10 * entry->id);
1680
1681 fe->preset_menuitems[entry->id].which_menu = winmenu;
1682 fe->preset_menuitems[entry->id].item_index =
1683 GetMenuItemCount(winmenu);
1684 } else {
1685 HMENU winsubmenu = CreateMenu();
1686 id_or_sub = (UINT_PTR)winsubmenu;
1687 flags |= MF_POPUP;
1688
1689 populate_preset_menu(fe, entry->submenu, winsubmenu);
1690 }
1691
1692 /*
1693 * FIXME: we ought to go through and do something with ampersands
1694 * here.
1695 */
1696
1697#ifndef _WIN32_WCE
1698 AppendMenu(winmenu, flags, id_or_sub, entry->title);
1699#else
1700 {
1701 TCHAR wName[255];
1702 MultiByteToWideChar(CP_ACP, 0, entry->title, -1, wName, 255);
1703 AppendMenu(winmenu, flags, id_or_sub, wName);
1704 }
1705#endif
1706 }
1707}
1708
1709/*
1710 * Populate a frontend structure with a new midend structure, and
1711 * create any window furniture that it needs.
1712 *
1713 * Previously-allocated memory and window furniture will be freed by
1714 * this function.
1715 *
1716 */
1717static int fe_set_midend(frontend *fe, midend *me)
1718{
1719 int x, y;
1720 RECT r;
1721
1722 if (fe->me) {
1723 midend_free(fe->me);
1724 fe->preset_menu = NULL;
1725 sfree(fe->preset_menuitems);
1726 }
1727 fe->me = me;
1728 fe->game = midend_which_game(fe->me);
1729
1730 {
1731 int i, ncolours;
1732 float *colours;
1733
1734 colours = midend_colours(fe->me, &ncolours);
1735
1736 if (fe->colours) sfree(fe->colours);
1737 if (fe->brushes) sfree(fe->brushes);
1738 if (fe->pens) sfree(fe->pens);
1739
1740 fe->colours = snewn(ncolours, COLORREF);
1741 fe->brushes = snewn(ncolours, HBRUSH);
1742 fe->pens = snewn(ncolours, HPEN);
1743
1744 for (i = 0; i < ncolours; i++) {
1745 fe->colours[i] = RGB(255 * colours[i*3+0],
1746 255 * colours[i*3+1],
1747 255 * colours[i*3+2]);
1748 fe->brushes[i] = CreateSolidBrush(fe->colours[i]);
1749 fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]);
1750 }
1751 sfree(colours);
1752 }
1753
1754 if (fe->statusbar)
1755 DestroyWindow(fe->statusbar);
1756 if (midend_wants_statusbar(fe->me)) {
1757 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME,
1758 TEXT(DEFAULT_STATUSBAR_TEXT),
1759 WS_CHILD | WS_VISIBLE,
1760 0, 0, 0, 0, /* status bar does these */
1761 NULL, NULL, fe->inst, NULL);
1762 } else
1763 fe->statusbar = NULL;
1764
1765 get_max_puzzle_size(fe, &x, &y);
1766 midend_size(fe->me, &x, &y, false);
1767
1768 r.left = r.top = 0;
1769 r.right = x;
1770 r.bottom = y;
1771 AdjustWindowRectEx(&r, WINFLAGS, true, 0);
1772
1773#ifdef _WIN32_WCE
1774 if (fe->numpad)
1775 DestroyWindow(fe->numpad);
1776 if (fe->game->flags & REQUIRE_NUMPAD)
1777 {
1778 fe->numpad = CreateToolbarEx (fe->hwnd,
1779 WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT,
1780 0, 10, fe->inst, IDR_PADTOOLBAR,
1781 tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON),
1782 0, 0, 14, 15, sizeof (TBBUTTON));
1783 GetWindowRect(fe->numpad, &rcTB);
1784 GetClientRect(fe->hwnd, &rcClient);
1785 MoveWindow(fe->numpad,
1786 0,
1787 rcClient.bottom - (rcTB.bottom - rcTB.top) - 1,
1788 rcClient.right,
1789 rcTB.bottom - rcTB.top,
1790 false);
1791 SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0);
1792 }
1793 else {
1794 fe->numpad = NULL;
1795 }
1796 MultiByteToWideChar (CP_ACP, 0, fe->game->name, -1, wGameName, 256);
1797 SetWindowText(fe->hwnd, wGameName);
1798#else
1799 SetWindowText(fe->hwnd, fe->game->name);
1800#endif
1801
1802 if (fe->statusbar)
1803 DestroyWindow(fe->statusbar);
1804 if (midend_wants_statusbar(fe->me)) {
1805 RECT sr;
1806 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
1807 WS_CHILD | WS_VISIBLE,
1808 0, 0, 0, 0, /* status bar does these */
1809 fe->hwnd, NULL, fe->inst, NULL);
1810#ifdef _WIN32_WCE
1811 /* Flat status bar looks better on the Pocket PC */
1812 SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) true, 0);
1813 SendMessage(fe->statusbar, SB_SETTEXT,
1814 (WPARAM) 255 | SBT_NOBORDERS,
1815 (LPARAM) L"");
1816#endif
1817
1818 /*
1819 * Now resize the window to take account of the status bar.
1820 */
1821 GetWindowRect(fe->statusbar, &sr);
1822 GetWindowRect(fe->hwnd, &r);
1823#ifndef _WIN32_WCE
1824 SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
1825 r.bottom - r.top + sr.bottom - sr.top,
1826 SWP_NOMOVE | SWP_NOZORDER);
1827#endif
1828 } else {
1829 fe->statusbar = NULL;
1830 }
1831
1832 {
1833 HMENU oldmenu = GetMenu(fe->hwnd);
1834
1835#ifndef _WIN32_WCE
1836 HMENU bar = CreateMenu();
1837 HMENU menu = CreateMenu();
1838 RECT menusize;
1839
1840 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game");
1841#else
1842 HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME);
1843 DeleteMenu(menu, 0, MF_BYPOSITION);
1844#endif
1845 fe->gamemenu = menu;
1846 AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New"));
1847 AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart"));
1848#ifndef _WIN32_WCE
1849 /* ...here I run out of sensible accelerator characters. */
1850 AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic..."));
1851 AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
1852#endif
1853
1854 assert(!fe->preset_menu);
1855
1856 fe->preset_menu = midend_get_presets(
1857 fe->me, &fe->n_preset_menuitems);
1858 fe->preset_menuitems = snewn(fe->n_preset_menuitems,
1859 struct preset_menuitemref);
1860 {
1861 int i;
1862 for (i = 0; i < fe->n_preset_menuitems; i++)
1863 fe->preset_menuitems[i].which_menu = NULL;
1864 }
1865 if (fe->preset_menu->n_entries > 0 || fe->game->can_configure) {
1866#ifndef _WIN32_WCE
1867 HMENU sub = CreateMenu();
1868
1869 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type");
1870#else
1871 HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
1872 DeleteMenu(sub, 0, MF_BYPOSITION);
1873#endif
1874
1875 populate_preset_menu(fe, fe->preset_menu, sub);
1876
1877 if (fe->game->can_configure) {
1878 AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom..."));
1879 }
1880
1881 fe->typemenu = sub;
1882 } else {
1883 fe->typemenu = INVALID_HANDLE_VALUE;
1884 }
1885
1886#ifdef COMBINED
1887#ifdef _WIN32_WCE
1888#error Windows CE does not support COMBINED build.
1889#endif
1890 {
1891 HMENU games = CreateMenu();
1892 int i;
1893
1894 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1895 AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)games, "&Other");
1896 for (i = 0; i < gamecount; i++) {
1897 if (strcmp(gamelist[i]->name, fe->game->name) != 0) {
1898 /* only include those games that aren't the same as the
1899 * game we're currently playing. */
1900 AppendMenu(games, MF_ENABLED, IDM_GAMES + i, gamelist[i]->name);
1901 }
1902 }
1903 }
1904#endif
1905
1906 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1907#ifndef _WIN32_WCE
1908 AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("&Load..."));
1909 AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("&Save..."));
1910 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1911 if (fe->game->can_print) {
1912 AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("&Print..."));
1913 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1914 }
1915#endif
1916 AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo"));
1917 AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo"));
1918#ifndef _WIN32_WCE
1919 if (fe->game->can_format_as_text_ever) {
1920 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1921 AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy"));
1922 }
1923#endif
1924 if (fe->game->can_solve) {
1925 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1926 AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
1927 }
1928 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1929#ifndef _WIN32_WCE
1930 AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
1931 menu = CreateMenu();
1932 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("&Help"));
1933#endif
1934 AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("&About"));
1935#ifndef _WIN32_WCE
1936 if (help_type != NONE) {
1937 char *item;
1938 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1939 AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("&Contents"));
1940 assert(fe->game->name);
1941 item = snewn(10+strlen(fe->game->name), char); /*ick*/
1942 sprintf(item, "&Help on %s", fe->game->name);
1943 AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item);
1944 sfree(item);
1945 }
1946 DestroyMenu(oldmenu);
1947 SetMenu(fe->hwnd, bar);
1948 get_menu_size(fe->hwnd, &menusize);
1949 fe->xmin = (menusize.right - menusize.left) + 25;
1950#endif
1951 }
1952
1953 if (fe->bitmap) DeleteObject(fe->bitmap);
1954 fe->bitmap = NULL;
1955 new_game_size(fe, fe->puzz_scale); /* initialises fe->bitmap */
1956
1957 return 0;
1958}
1959
1960static void show_window(frontend *fe)
1961{
1962 ShowWindow(fe->hwnd, SW_SHOWNORMAL);
1963 SetForegroundWindow(fe->hwnd);
1964
1965 update_type_menu_tick(fe);
1966 update_copy_menu_greying(fe);
1967
1968 midend_redraw(fe->me);
1969}
1970
1971#ifdef _WIN32_WCE
1972static HFONT dialog_title_font()
1973{
1974 static HFONT hf = NULL;
1975 LOGFONT lf;
1976
1977 if (hf)
1978 return hf;
1979
1980 memset (&lf, 0, sizeof(LOGFONT));
1981 lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */
1982 lf.lfWeight = FW_BOLD;
1983 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
1984
1985 return hf = CreateFontIndirect(&lf);
1986}
1987
1988static void make_dialog_full_screen(HWND hwnd)
1989{
1990 SHINITDLGINFO shidi;
1991
1992 /* Make dialog full screen */
1993 shidi.dwMask = SHIDIM_FLAGS;
1994 shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN |
1995 SHIDIF_EMPTYMENU;
1996 shidi.hDlg = hwnd;
1997 SHInitDialog(&shidi);
1998}
1999#endif
2000
2001static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg,
2002 WPARAM wParam, LPARAM lParam)
2003{
2004 frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
2005
2006 switch (msg) {
2007 case WM_INITDIALOG:
2008#ifdef _WIN32_WCE
2009 {
2010 char title[256];
2011
2012 make_dialog_full_screen(hwnd);
2013
2014 sprintf(title, "About %.250s", fe->game->name);
2015 SetDlgItemTextA(hwnd, IDC_ABOUT_CAPTION, title);
2016
2017 SendDlgItemMessage(hwnd, IDC_ABOUT_CAPTION, WM_SETFONT,
2018 (WPARAM) dialog_title_font(), 0);
2019
2020 SetDlgItemTextA(hwnd, IDC_ABOUT_GAME, fe->game->name);
2021 SetDlgItemTextA(hwnd, IDC_ABOUT_VERSION, ver);
2022 }
2023#endif
2024 return 1;
2025
2026 case WM_COMMAND:
2027 if (LOWORD(wParam) == IDOK)
2028#ifdef _WIN32_WCE
2029 EndDialog(hwnd, 1);
2030#else
2031 fe->dlg_done = 1;
2032#endif
2033 return 0;
2034
2035 case WM_CLOSE:
2036#ifdef _WIN32_WCE
2037 EndDialog(hwnd, 1);
2038#else
2039 fe->dlg_done = 1;
2040#endif
2041 return 0;
2042 }
2043
2044 return 0;
2045}
2046
2047/*
2048 * Wrappers on midend_{get,set}_config, which extend the CFG_*
2049 * enumeration to add CFG_PRINT.
2050 */
2051static config_item *frontend_get_config(frontend *fe, int which,
2052 char **wintitle)
2053{
2054 if (which < CFG_FRONTEND_SPECIFIC) {
2055 return midend_get_config(fe->me, which, wintitle);
2056 } else if (which == CFG_PRINT) {
2057 config_item *ret;
2058 int i;
2059
2060 *wintitle = snewn(40 + strlen(fe->game->name), char);
2061 sprintf(*wintitle, "%s print setup", fe->game->name);
2062
2063 ret = snewn(8, config_item);
2064
2065 i = 0;
2066
2067 ret[i].name = "Number of puzzles to print";
2068 ret[i].type = C_STRING;
2069 ret[i].u.string.sval = dupstr("1");
2070 i++;
2071
2072 ret[i].name = "Number of puzzles across the page";
2073 ret[i].type = C_STRING;
2074 ret[i].u.string.sval = dupstr("1");
2075 i++;
2076
2077 ret[i].name = "Number of puzzles down the page";
2078 ret[i].type = C_STRING;
2079 ret[i].u.string.sval = dupstr("1");
2080 i++;
2081
2082 ret[i].name = "Percentage of standard size";
2083 ret[i].type = C_STRING;
2084 ret[i].u.string.sval = dupstr("100.0");
2085 i++;
2086
2087 ret[i].name = "Include currently shown puzzle";
2088 ret[i].type = C_BOOLEAN;
2089 ret[i].u.boolean.bval = true;
2090 i++;
2091
2092 ret[i].name = "Print solutions";
2093 ret[i].type = C_BOOLEAN;
2094 ret[i].u.boolean.bval = false;
2095 i++;
2096
2097 if (fe->game->can_print_in_colour) {
2098 ret[i].name = "Print in colour";
2099 ret[i].type = C_BOOLEAN;
2100 ret[i].u.boolean.bval = false;
2101 i++;
2102 }
2103
2104 ret[i].name = NULL;
2105 ret[i].type = C_END;
2106 i++;
2107
2108 return ret;
2109 } else {
2110 assert(!"We should never get here");
2111 return NULL;
2112 }
2113}
2114
2115static const char *frontend_set_config(
2116 frontend *fe, int which, config_item *cfg)
2117{
2118 if (which < CFG_FRONTEND_SPECIFIC) {
2119 return midend_set_config(fe->me, which, cfg);
2120 } else if (which == CFG_PRINT) {
2121 if ((fe->printcount = atoi(cfg[0].u.string.sval)) <= 0)
2122 return "Number of puzzles to print should be at least one";
2123 if ((fe->printw = atoi(cfg[1].u.string.sval)) <= 0)
2124 return "Number of puzzles across the page should be at least one";
2125 if ((fe->printh = atoi(cfg[2].u.string.sval)) <= 0)
2126 return "Number of puzzles down the page should be at least one";
2127 if ((fe->printscale = (float)atof(cfg[3].u.string.sval)) <= 0)
2128 return "Print size should be positive";
2129 fe->printcurr = cfg[4].u.boolean.bval;
2130 fe->printsolns = cfg[5].u.boolean.bval;
2131 fe->printcolour = fe->game->can_print_in_colour &&
2132 cfg[6].u.boolean.bval;
2133 return NULL;
2134 } else {
2135 assert(!"We should never get here");
2136 return "Internal error";
2137 }
2138}
2139
2140#ifdef _WIN32_WCE
2141/* Separate version of mkctrl function for the Pocket PC. */
2142/* Control coordinates should be specified in dialog units. */
2143HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2144 LPCTSTR wclass, int wstyle,
2145 int exstyle, const char *wtext, INT_PTR wid)
2146{
2147 RECT rc;
2148 TCHAR wwtext[256];
2149
2150 /* Convert dialog units into pixels */
2151 rc.left = x1; rc.right = x2;
2152 rc.top = y1; rc.bottom = y2;
2153 MapDialogRect(fe->cfgbox, &rc);
2154
2155 MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256);
2156
2157 return CreateWindowEx(exstyle, wclass, wwtext,
2158 wstyle | WS_CHILD | WS_VISIBLE,
2159 rc.left, rc.top,
2160 rc.right - rc.left, rc.bottom - rc.top,
2161 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2162}
2163
2164static void create_config_controls(frontend * fe)
2165{
2166 int id, nctrls;
2167 int col1l, col1r, col2l, col2r, y;
2168 config_item *i;
2169 struct cfg_aux *j;
2170 HWND ctl;
2171
2172 /* Control placement done in dialog units */
2173 col1l = 4; col1r = 96; /* Label column */
2174 col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */
2175
2176 /*
2177 * Count the controls so we can allocate cfgaux.
2178 */
2179 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2180 nctrls++;
2181 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2182
2183 id = 1000;
2184 y = 22; /* Leave some room for the dialog title */
2185 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2186 switch (i->type) {
2187 case C_STRING:
2188 /*
2189 * Edit box with a label beside it.
2190 */
2191 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2192 TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2193 mkctrl(fe, col2l, col2r, y, y + 12,
2194 TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,
2195 0, "", (j->ctlid = id++));
2196 SetDlgItemTextA(fe->cfgbox, j->ctlid, i->u.string.sval);
2197 break;
2198
2199 case C_BOOLEAN:
2200 /*
2201 * Simple checkbox.
2202 */
2203 mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"),
2204 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2205 0, i->name, (j->ctlid = id++));
2206 CheckDlgButton(fe->cfgbox, j->ctlid, (i->u.boolean.bval != 0));
2207 break;
2208
2209 case C_CHOICES:
2210 /*
2211 * Drop-down list with a label beside it.
2212 */
2213 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2214 TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2215 ctl = mkctrl(fe, col2l, col2r, y, y + 48,
2216 TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP |
2217 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2218 0, "", (j->ctlid = id++));
2219 {
2220 char c;
2221 const char *p, *q;
2222 char *str;
2223
2224 p = i->u.choices.choicenames;
2225 c = *p++;
2226 while (*p) {
2227 q = p;
2228 while (*q && *q != c) q++;
2229 str = snewn(q-p+1, char);
2230 strncpy(str, p, q-p);
2231 str[q-p] = '\0';
2232 {
2233 TCHAR ws[50];
2234 MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50);
2235 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws);
2236 }
2237
2238 sfree(str);
2239 if (*q) q++;
2240 p = q;
2241 }
2242 }
2243 SendMessage(ctl, CB_SETCURSEL, i->u.choices.selected, 0);
2244 break;
2245 }
2246
2247 y += 15;
2248 }
2249
2250}
2251#endif
2252
2253static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
2254 WPARAM wParam, LPARAM lParam)
2255{
2256 frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
2257 config_item *i;
2258 struct cfg_aux *j;
2259
2260 switch (msg) {
2261 case WM_INITDIALOG:
2262#ifdef _WIN32_WCE
2263 {
2264 char *title;
2265
2266 fe = (frontend *) lParam;
2267 SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
2268 fe->cfgbox = hwnd;
2269
2270 fe->cfg = frontend_get_config(fe, fe->cfg_which, &title);
2271
2272 make_dialog_full_screen(hwnd);
2273
2274 SetDlgItemTextA(hwnd, IDC_CONFIG_CAPTION, title);
2275 SendDlgItemMessage(hwnd, IDC_CONFIG_CAPTION, WM_SETFONT,
2276 (WPARAM) dialog_title_font(), 0);
2277
2278 create_config_controls(fe);
2279 }
2280#endif
2281 return 1;
2282
2283 case WM_COMMAND:
2284 /*
2285 * OK and Cancel are special cases.
2286 */
2287 if ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
2288 if (LOWORD(wParam) == IDOK) {
2289 const char *err = frontend_set_config(
2290 fe, fe->cfg_which, fe->cfg);
2291
2292 if (err) {
2293 MessageBox(hwnd, err, "Validation error",
2294 MB_ICONERROR | MB_OK);
2295 } else {
2296#ifdef _WIN32_WCE
2297 EndDialog(hwnd, 2);
2298#else
2299 fe->dlg_done = 2;
2300#endif
2301 }
2302 } else {
2303#ifdef _WIN32_WCE
2304 EndDialog(hwnd, 1);
2305#else
2306 fe->dlg_done = 1;
2307#endif
2308 }
2309 return 0;
2310 }
2311
2312 /*
2313 * First find the control whose id this is.
2314 */
2315 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2316 if (j->ctlid == LOWORD(wParam))
2317 break;
2318 }
2319 if (i->type == C_END)
2320 return 0; /* not our problem */
2321
2322 if (i->type == C_STRING && HIWORD(wParam) == EN_CHANGE) {
2323 char buffer[4096];
2324#ifdef _WIN32_WCE
2325 TCHAR wBuffer[4096];
2326 GetDlgItemText(fe->cfgbox, j->ctlid, wBuffer, 4096);
2327 WideCharToMultiByte(CP_ACP, 0, wBuffer, -1, buffer, 4096, NULL, NULL);
2328#else
2329 GetDlgItemText(fe->cfgbox, j->ctlid, buffer, lenof(buffer));
2330#endif
2331 buffer[lenof(buffer)-1] = '\0';
2332 sfree(i->u.string.sval);
2333 i->u.string.sval = dupstr(buffer);
2334 } else if (i->type == C_BOOLEAN &&
2335 (HIWORD(wParam) == BN_CLICKED ||
2336 HIWORD(wParam) == BN_DBLCLK)) {
2337 i->u.boolean.bval = IsDlgButtonChecked(fe->cfgbox, j->ctlid);
2338 } else if (i->type == C_CHOICES &&
2339 HIWORD(wParam) == CBN_SELCHANGE) {
2340 i->u.choices.selected = SendDlgItemMessage(fe->cfgbox, j->ctlid,
2341 CB_GETCURSEL, 0, 0);
2342 }
2343
2344 return 0;
2345
2346 case WM_CLOSE:
2347 fe->dlg_done = 1;
2348 return 0;
2349 }
2350
2351 return 0;
2352}
2353
2354#ifndef _WIN32_WCE
2355HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2356 char *wclass, int wstyle,
2357 int exstyle, const char *wtext, INT_PTR wid)
2358{
2359 HWND ret;
2360 ret = CreateWindowEx(exstyle, wclass, wtext,
2361 wstyle | WS_CHILD | WS_VISIBLE, x1, y1, x2-x1, y2-y1,
2362 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2363 SendMessage(ret, WM_SETFONT, (WPARAM)fe->cfgfont, MAKELPARAM(true, 0));
2364 return ret;
2365}
2366#endif
2367
2368static void about(frontend *fe)
2369{
2370#ifdef _WIN32_WCE
2371 DialogBox(fe->inst, MAKEINTRESOURCE(IDD_ABOUT), fe->hwnd, AboutDlgProc);
2372#else
2373 int i;
2374 WNDCLASS wc;
2375 MSG msg;
2376 TEXTMETRIC tm;
2377 HDC hdc;
2378 HFONT oldfont;
2379 SIZE size;
2380 int gm, id;
2381 int winwidth, winheight, y;
2382 int height, width, maxwid;
2383 const char *strings[16];
2384 int lengths[16];
2385 int nstrings = 0;
2386 char titlebuf[512];
2387
2388 sprintf(titlebuf, "About %.250s", fe->game->name);
2389
2390 strings[nstrings++] = fe->game->name;
2391 strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection";
2392 strings[nstrings++] = ver;
2393
2394 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2395 wc.lpfnWndProc = DefDlgProc;
2396 wc.cbClsExtra = 0;
2397 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2398 wc.hInstance = fe->inst;
2399 wc.hIcon = NULL;
2400 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2401 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2402 wc.lpszMenuName = NULL;
2403 wc.lpszClassName = "GameAboutBox";
2404 RegisterClass(&wc);
2405
2406 hdc = GetDC(fe->hwnd);
2407 SetMapMode(hdc, MM_TEXT);
2408
2409 fe->dlg_done = 0;
2410
2411 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2412 0, 0, 0, 0,
2413 false, false, false, DEFAULT_CHARSET,
2414 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2415 DEFAULT_QUALITY,
2416 FF_SWISS,
2417 "MS Shell Dlg");
2418
2419 oldfont = SelectObject(hdc, fe->cfgfont);
2420 if (GetTextMetrics(hdc, &tm)) {
2421 height = tm.tmAscent + tm.tmDescent;
2422 width = tm.tmAveCharWidth;
2423 } else {
2424 height = width = 30;
2425 }
2426
2427 /*
2428 * Figure out the layout of the About box by measuring the
2429 * length of each piece of text.
2430 */
2431 maxwid = 0;
2432 winheight = height/2;
2433
2434 for (i = 0; i < nstrings; i++) {
2435 if (GetTextExtentPoint32(hdc, strings[i], strlen(strings[i]), &size))
2436 lengths[i] = size.cx;
2437 else
2438 lengths[i] = 0; /* *shrug* */
2439 if (maxwid < lengths[i])
2440 maxwid = lengths[i];
2441 winheight += height * 3 / 2 + (height / 2);
2442 }
2443
2444 winheight += height + height * 7 / 4; /* OK button */
2445 winwidth = maxwid + 4*width;
2446
2447 SelectObject(hdc, oldfont);
2448 ReleaseDC(fe->hwnd, hdc);
2449
2450 /*
2451 * Create the dialog, now that we know its size.
2452 */
2453 {
2454 RECT r, r2;
2455
2456 r.left = r.top = 0;
2457 r.right = winwidth;
2458 r.bottom = winheight;
2459
2460 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2461 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2462 WS_CAPTION | WS_SYSMENU*/) &~
2463 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2464 false, 0);
2465
2466 /*
2467 * Centre the dialog on its parent window.
2468 */
2469 r.right -= r.left;
2470 r.bottom -= r.top;
2471 GetWindowRect(fe->hwnd, &r2);
2472 r.left = (r2.left + r2.right - r.right) / 2;
2473 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2474 r.right += r.left;
2475 r.bottom += r.top;
2476
2477 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, titlebuf,
2478 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2479 WS_CAPTION | WS_SYSMENU,
2480 r.left, r.top,
2481 r.right-r.left, r.bottom-r.top,
2482 fe->hwnd, NULL, fe->inst, NULL);
2483 }
2484
2485 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, false);
2486
2487 SetWindowLongPtr(fe->cfgbox, GWLP_USERDATA, (LONG_PTR)fe);
2488 SetWindowLongPtr(fe->cfgbox, DWLP_DLGPROC, (LONG_PTR)AboutDlgProc);
2489
2490 id = 1000;
2491 y = height/2;
2492 for (i = 0; i < nstrings; i++) {
2493 int border = width*2 + (maxwid - lengths[i]) / 2;
2494 mkctrl(fe, border, border+lengths[i], y+height*1/8, y+height*9/8,
2495 "Static", 0, 0, strings[i], id++);
2496 y += height*3/2;
2497
2498 assert(y < winheight);
2499 y += height/2;
2500 }
2501
2502 y += height/2; /* extra space before OK */
2503 mkctrl(fe, width*2, maxwid+width*2, y, y+height*7/4, "BUTTON",
2504 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2505 "OK", IDOK);
2506
2507 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2508
2509 EnableWindow(fe->hwnd, false);
2510 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2511 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2512 if (!IsDialogMessage(fe->cfgbox, &msg))
2513 DispatchMessage(&msg);
2514 if (fe->dlg_done)
2515 break;
2516 }
2517 EnableWindow(fe->hwnd, true);
2518 SetForegroundWindow(fe->hwnd);
2519 DestroyWindow(fe->cfgbox);
2520 DeleteObject(fe->cfgfont);
2521#endif
2522}
2523
2524static bool get_config(frontend *fe, int which)
2525{
2526#ifdef _WIN32_WCE
2527 fe->cfg_which = which;
2528
2529 return DialogBoxParam(fe->inst,
2530 MAKEINTRESOURCE(IDD_CONFIG),
2531 fe->hwnd, ConfigDlgProc,
2532 (LPARAM) fe) == 2;
2533#else
2534 config_item *i;
2535 struct cfg_aux *j;
2536 char *title;
2537 WNDCLASS wc;
2538 MSG msg;
2539 TEXTMETRIC tm;
2540 HDC hdc;
2541 HFONT oldfont;
2542 SIZE size;
2543 HWND ctl;
2544 int gm, id, nctrls;
2545 int winwidth, winheight, col1l, col1r, col2l, col2r, y;
2546 int height, width, maxlabel, maxcheckbox;
2547
2548 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2549 wc.lpfnWndProc = DefDlgProc;
2550 wc.cbClsExtra = 0;
2551 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2552 wc.hInstance = fe->inst;
2553 wc.hIcon = NULL;
2554 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2555 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2556 wc.lpszMenuName = NULL;
2557 wc.lpszClassName = "GameConfigBox";
2558 RegisterClass(&wc);
2559
2560 hdc = GetDC(fe->hwnd);
2561 SetMapMode(hdc, MM_TEXT);
2562
2563 fe->dlg_done = 0;
2564
2565 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2566 0, 0, 0, 0,
2567 false, false, false, DEFAULT_CHARSET,
2568 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2569 DEFAULT_QUALITY,
2570 FF_SWISS,
2571 "MS Shell Dlg");
2572
2573 oldfont = SelectObject(hdc, fe->cfgfont);
2574 if (GetTextMetrics(hdc, &tm)) {
2575 height = tm.tmAscent + tm.tmDescent;
2576 width = tm.tmAveCharWidth;
2577 } else {
2578 height = width = 30;
2579 }
2580
2581 fe->cfg = frontend_get_config(fe, which, &title);
2582 fe->cfg_which = which;
2583
2584 /*
2585 * Figure out the layout of the config box by measuring the
2586 * length of each piece of text.
2587 */
2588 maxlabel = maxcheckbox = 0;
2589 winheight = height/2;
2590
2591 for (i = fe->cfg; i->type != C_END; i++) {
2592 switch (i->type) {
2593 case C_STRING:
2594 case C_CHOICES:
2595 /*
2596 * Both these control types have a label filling only
2597 * the left-hand column of the box.
2598 */
2599 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2600 maxlabel < size.cx)
2601 maxlabel = size.cx;
2602 winheight += height * 3 / 2 + (height / 2);
2603 break;
2604
2605 case C_BOOLEAN:
2606 /*
2607 * Checkboxes take up the whole of the box width.
2608 */
2609 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2610 maxcheckbox < size.cx)
2611 maxcheckbox = size.cx;
2612 winheight += height + (height / 2);
2613 break;
2614 }
2615 }
2616
2617 winheight += height + height * 7 / 4; /* OK / Cancel buttons */
2618
2619 col1l = 2*width;
2620 col1r = col1l + maxlabel;
2621 col2l = col1r + 2*width;
2622 col2r = col2l + 30*width;
2623 if (col2r < col1l+2*height+maxcheckbox)
2624 col2r = col1l+2*height+maxcheckbox;
2625 winwidth = col2r + 2*width;
2626
2627 SelectObject(hdc, oldfont);
2628 ReleaseDC(fe->hwnd, hdc);
2629
2630 /*
2631 * Create the dialog, now that we know its size.
2632 */
2633 {
2634 RECT r, r2;
2635
2636 r.left = r.top = 0;
2637 r.right = winwidth;
2638 r.bottom = winheight;
2639
2640 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2641 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2642 WS_CAPTION | WS_SYSMENU*/) &~
2643 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2644 false, 0);
2645
2646 /*
2647 * Centre the dialog on its parent window.
2648 */
2649 r.right -= r.left;
2650 r.bottom -= r.top;
2651 GetWindowRect(fe->hwnd, &r2);
2652 r.left = (r2.left + r2.right - r.right) / 2;
2653 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2654 r.right += r.left;
2655 r.bottom += r.top;
2656
2657 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, title,
2658 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2659 WS_CAPTION | WS_SYSMENU,
2660 r.left, r.top,
2661 r.right-r.left, r.bottom-r.top,
2662 fe->hwnd, NULL, fe->inst, NULL);
2663 sfree(title);
2664 }
2665
2666 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, false);
2667
2668 SetWindowLongPtr(fe->cfgbox, GWLP_USERDATA, (LONG_PTR)fe);
2669 SetWindowLongPtr(fe->cfgbox, DWLP_DLGPROC, (LONG_PTR)ConfigDlgProc);
2670
2671 /*
2672 * Count the controls so we can allocate cfgaux.
2673 */
2674 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2675 nctrls++;
2676 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2677
2678 id = 1000;
2679 y = height/2;
2680 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2681 switch (i->type) {
2682 case C_STRING:
2683 /*
2684 * Edit box with a label beside it.
2685 */
2686 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2687 "Static", 0, 0, i->name, id++);
2688 ctl = mkctrl(fe, col2l, col2r, y, y+height*3/2,
2689 "EDIT", WS_TABSTOP | ES_AUTOHSCROLL,
2690 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2691 SetWindowText(ctl, i->u.string.sval);
2692 y += height*3/2;
2693 break;
2694
2695 case C_BOOLEAN:
2696 /*
2697 * Simple checkbox.
2698 */
2699 mkctrl(fe, col1l, col2r, y, y+height, "BUTTON",
2700 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2701 0, i->name, (j->ctlid = id++));
2702 CheckDlgButton(fe->cfgbox, j->ctlid, i->u.boolean.bval);
2703 y += height;
2704 break;
2705
2706 case C_CHOICES:
2707 /*
2708 * Drop-down list with a label beside it.
2709 */
2710 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2711 "STATIC", 0, 0, i->name, id++);
2712 ctl = mkctrl(fe, col2l, col2r, y, y+height*41/2,
2713 "COMBOBOX", WS_TABSTOP |
2714 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2715 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2716 {
2717 char c;
2718 const char *p, *q;
2719 char *str;
2720
2721 SendMessage(ctl, CB_RESETCONTENT, 0, 0);
2722 p = i->u.choices.choicenames;
2723 c = *p++;
2724 while (*p) {
2725 q = p;
2726 while (*q && *q != c) q++;
2727 str = snewn(q-p+1, char);
2728 strncpy(str, p, q-p);
2729 str[q-p] = '\0';
2730 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)str);
2731 sfree(str);
2732 if (*q) q++;
2733 p = q;
2734 }
2735 }
2736
2737 SendMessage(ctl, CB_SETCURSEL, i->u.choices.selected, 0);
2738
2739 y += height*3/2;
2740 break;
2741 }
2742
2743 assert(y < winheight);
2744 y += height/2;
2745 }
2746
2747 y += height/2; /* extra space before OK and Cancel */
2748 mkctrl(fe, col1l, (col1l+col2r)/2-width, y, y+height*7/4, "BUTTON",
2749 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2750 "OK", IDOK);
2751 mkctrl(fe, (col1l+col2r)/2+width, col2r, y, y+height*7/4, "BUTTON",
2752 BS_PUSHBUTTON | WS_TABSTOP, 0, "Cancel", IDCANCEL);
2753
2754 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2755
2756 EnableWindow(fe->hwnd, false);
2757 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2758 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2759 if (!IsDialogMessage(fe->cfgbox, &msg))
2760 DispatchMessage(&msg);
2761 if (fe->dlg_done)
2762 break;
2763 }
2764 EnableWindow(fe->hwnd, true);
2765 SetForegroundWindow(fe->hwnd);
2766 DestroyWindow(fe->cfgbox);
2767 DeleteObject(fe->cfgfont);
2768
2769 free_cfg(fe->cfg);
2770 sfree(fe->cfgaux);
2771
2772 return (fe->dlg_done == 2);
2773#endif
2774}
2775
2776#ifdef _WIN32_WCE
2777static void calculate_bitmap_position(frontend *fe, int x, int y)
2778{
2779 /* Pocket PC - center the game in the full screen window */
2780 int yMargin;
2781 RECT rcClient;
2782
2783 GetClientRect(fe->hwnd, &rcClient);
2784 fe->bitmapPosition.left = (rcClient.right - x) / 2;
2785 yMargin = rcClient.bottom - y;
2786
2787 if (fe->numpad != NULL) {
2788 RECT rcPad;
2789 GetWindowRect(fe->numpad, &rcPad);
2790 yMargin -= rcPad.bottom - rcPad.top;
2791 }
2792
2793 if (fe->statusbar != NULL) {
2794 RECT rcStatus;
2795 GetWindowRect(fe->statusbar, &rcStatus);
2796 yMargin -= rcStatus.bottom - rcStatus.top;
2797 }
2798
2799 fe->bitmapPosition.top = yMargin / 2;
2800
2801 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2802 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2803}
2804#else
2805static void calculate_bitmap_position(frontend *fe, int x, int y)
2806{
2807 /* Plain Windows - position the game in the upper-left corner */
2808 fe->bitmapPosition.left = 0;
2809 fe->bitmapPosition.top = 0;
2810 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2811 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2812}
2813#endif
2814
2815static void new_bitmap(frontend *fe, int x, int y)
2816{
2817 HDC hdc;
2818
2819 if (fe->bitmap) DeleteObject(fe->bitmap);
2820
2821 hdc = GetDC(fe->hwnd);
2822 fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
2823 calculate_bitmap_position(fe, x, y);
2824 ReleaseDC(fe->hwnd, hdc);
2825}
2826
2827static void new_game_size(frontend *fe, float scale)
2828{
2829 RECT r, sr;
2830 int x, y;
2831
2832 get_max_puzzle_size(fe, &x, &y);
2833 midend_size(fe->me, &x, &y, false);
2834
2835 if (scale != 1.0) {
2836 x = (int)((float)x * fe->puzz_scale);
2837 y = (int)((float)y * fe->puzz_scale);
2838 midend_size(fe->me, &x, &y, true);
2839 }
2840 fe->ymin = (fe->xmin * y) / x;
2841
2842 r.left = r.top = 0;
2843 r.right = x;
2844 r.bottom = y;
2845 AdjustWindowRectEx(&r, WINFLAGS, true, 0);
2846
2847 if (fe->statusbar != NULL) {
2848 GetWindowRect(fe->statusbar, &sr);
2849 } else {
2850 sr.left = sr.right = sr.top = sr.bottom = 0;
2851 }
2852#ifndef _WIN32_WCE
2853 SetWindowPos(fe->hwnd, NULL, 0, 0,
2854 r.right - r.left,
2855 r.bottom - r.top + sr.bottom - sr.top,
2856 SWP_NOMOVE | SWP_NOZORDER);
2857#endif
2858
2859 check_window_size(fe, &x, &y);
2860
2861#ifndef _WIN32_WCE
2862 if (fe->statusbar != NULL)
2863 SetWindowPos(fe->statusbar, NULL, 0, y, x,
2864 sr.bottom - sr.top, SWP_NOZORDER);
2865#endif
2866
2867 new_bitmap(fe, x, y);
2868
2869#ifdef _WIN32_WCE
2870 InvalidateRect(fe->hwnd, NULL, true);
2871#endif
2872 midend_redraw(fe->me);
2873}
2874
2875/*
2876 * Given a proposed new window rect, work out the resulting
2877 * difference in client size (from current), and use to try
2878 * and resize the puzzle, returning (wx,wy) as the actual
2879 * new window size.
2880 */
2881
2882static void adjust_game_size(frontend *fe, RECT *proposed, bool isedge,
2883 int *wx_r, int *wy_r)
2884{
2885 RECT cr, wr;
2886 int nx, ny, xdiff, ydiff, wx, wy;
2887
2888 /* Work out the current window sizing, and thus the
2889 * difference in size we're asking for. */
2890 GetClientRect(fe->hwnd, &cr);
2891 wr = cr;
2892 AdjustWindowRectEx(&wr, WINFLAGS, true, 0);
2893
2894 xdiff = (proposed->right - proposed->left) - (wr.right - wr.left);
2895 ydiff = (proposed->bottom - proposed->top) - (wr.bottom - wr.top);
2896
2897 if (isedge) {
2898 /* These next four lines work around the fact that midend_size
2899 * is happy to shrink _but not grow_ if you change one dimension
2900 * but not the other. */
2901 if (xdiff > 0 && ydiff == 0)
2902 ydiff = (xdiff * (wr.right - wr.left)) / (wr.bottom - wr.top);
2903 if (xdiff == 0 && ydiff > 0)
2904 xdiff = (ydiff * (wr.bottom - wr.top)) / (wr.right - wr.left);
2905 }
2906
2907 if (check_window_resize(fe,
2908 (cr.right - cr.left) + xdiff,
2909 (cr.bottom - cr.top) + ydiff,
2910 &nx, &ny, &wx, &wy)) {
2911 new_bitmap(fe, nx, ny);
2912 midend_force_redraw(fe->me);
2913 } else {
2914 /* reset size to current window size */
2915 wx = wr.right - wr.left;
2916 wy = wr.bottom - wr.top;
2917 }
2918 /* Re-fetch rectangle; size limits mean we might not have
2919 * taken it quite to the mouse drag positions. */
2920 GetClientRect(fe->hwnd, &cr);
2921 adjust_statusbar(fe, &cr);
2922
2923 *wx_r = wx; *wy_r = wy;
2924}
2925
2926static void update_type_menu_tick(frontend *fe)
2927{
2928 int total, n, i;
2929
2930 if (fe->typemenu == INVALID_HANDLE_VALUE)
2931 return;
2932
2933 n = midend_which_preset(fe->me);
2934
2935 for (i = 0; i < fe->n_preset_menuitems; i++) {
2936 if (fe->preset_menuitems[i].which_menu) {
2937 int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
2938 CheckMenuItem(fe->preset_menuitems[i].which_menu,
2939 fe->preset_menuitems[i].item_index,
2940 MF_BYPOSITION | flag);
2941 }
2942 }
2943
2944 if (fe->game->can_configure) {
2945 int flag = (n < 0 ? MF_CHECKED : MF_UNCHECKED);
2946 /* "Custom" menu item is at the bottom of the top-level Type menu */
2947 total = GetMenuItemCount(fe->typemenu);
2948 CheckMenuItem(fe->typemenu, total - 1, MF_BYPOSITION | flag);
2949 }
2950
2951 DrawMenuBar(fe->hwnd);
2952}
2953
2954static void update_copy_menu_greying(frontend *fe)
2955{
2956 UINT enable = (midend_can_format_as_text_now(fe->me) ?
2957 MF_ENABLED : MF_GRAYED);
2958 EnableMenuItem(fe->gamemenu, IDM_COPY, MF_BYCOMMAND | enable);
2959}
2960
2961static void new_game_type(frontend *fe)
2962{
2963 midend_new_game(fe->me);
2964 new_game_size(fe, 1.0);
2965 update_type_menu_tick(fe);
2966 update_copy_menu_greying(fe);
2967}
2968
2969static bool is_alt_pressed(void)
2970{
2971 BYTE keystate[256];
2972 int r = GetKeyboardState(keystate);
2973 if (!r)
2974 return false;
2975 if (keystate[VK_MENU] & 0x80)
2976 return true;
2977 if (keystate[VK_RMENU] & 0x80)
2978 return true;
2979 return false;
2980}
2981
2982static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
2983 WPARAM wParam, LPARAM lParam)
2984{
2985 frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
2986 int cmd;
2987
2988 switch (message) {
2989 case WM_CLOSE:
2990 DestroyWindow(hwnd);
2991 return 0;
2992 case WM_COMMAND:
2993#ifdef _WIN32_WCE
2994 /* Numeric pad sends WM_COMMAND messages */
2995 if ((wParam >= IDM_KEYEMUL) && (wParam < IDM_KEYEMUL + 256))
2996 {
2997 midend_process_key(fe->me, 0, 0, wParam - IDM_KEYEMUL);
2998 }
2999#endif
3000 cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */
3001 switch (cmd) {
3002 case IDM_NEW:
3003 if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME))
3004 PostQuitMessage(0);
3005 break;
3006 case IDM_RESTART:
3007 midend_restart_game(fe->me);
3008 break;
3009 case IDM_UNDO:
3010 if (!midend_process_key(fe->me, 0, 0, UI_UNDO))
3011 PostQuitMessage(0);
3012 break;
3013 case IDM_REDO:
3014 if (!midend_process_key(fe->me, 0, 0, UI_REDO))
3015 PostQuitMessage(0);
3016 break;
3017 case IDM_COPY:
3018 {
3019 char *text = midend_text_format(fe->me);
3020 if (text)
3021 write_clip(hwnd, text);
3022 else
3023 MessageBeep(MB_ICONWARNING);
3024 sfree(text);
3025 }
3026 break;
3027 case IDM_SOLVE:
3028 {
3029 const char *msg = midend_solve(fe->me);
3030 if (msg)
3031 MessageBox(hwnd, msg, "Unable to solve",
3032 MB_ICONERROR | MB_OK);
3033 }
3034 break;
3035 case IDM_QUIT:
3036 if (!midend_process_key(fe->me, 0, 0, UI_QUIT))
3037 PostQuitMessage(0);
3038 break;
3039 case IDM_CONFIG:
3040 if (get_config(fe, CFG_SETTINGS))
3041 new_game_type(fe);
3042 break;
3043 case IDM_SEED:
3044 if (get_config(fe, CFG_SEED))
3045 new_game_type(fe);
3046 break;
3047 case IDM_DESC:
3048 if (get_config(fe, CFG_DESC))
3049 new_game_type(fe);
3050 break;
3051 case IDM_PRINT:
3052 if (get_config(fe, CFG_PRINT))
3053 print(fe);
3054 break;
3055 case IDM_ABOUT:
3056 about(fe);
3057 break;
3058 case IDM_LOAD:
3059 case IDM_SAVE:
3060 {
3061 OPENFILENAME of;
3062 char filename[FILENAME_MAX];
3063 int ret;
3064
3065 memset(&of, 0, sizeof(of));
3066 of.hwndOwner = hwnd;
3067 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
3068 of.lpstrCustomFilter = NULL;
3069 of.nFilterIndex = 1;
3070 of.lpstrFile = filename;
3071 filename[0] = '\0';
3072 of.nMaxFile = lenof(filename);
3073 of.lpstrFileTitle = NULL;
3074 of.lpstrTitle = (cmd == IDM_SAVE ?
3075 "Enter name of game file to save" :
3076 "Enter name of saved game file to load");
3077 of.Flags = 0;
3078#ifdef OPENFILENAME_SIZE_VERSION_400
3079 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
3080#else
3081 of.lStructSize = sizeof(of);
3082#endif
3083 of.lpstrInitialDir = NULL;
3084
3085 if (cmd == IDM_SAVE)
3086 ret = GetSaveFileName(&of);
3087 else
3088 ret = GetOpenFileName(&of);
3089
3090 if (ret) {
3091 if (cmd == IDM_SAVE) {
3092 FILE *fp;
3093
3094 if ((fp = fopen(filename, "r")) != NULL) {
3095 char buf[256 + FILENAME_MAX];
3096 fclose(fp);
3097 /* file exists */
3098
3099 sprintf(buf, "Are you sure you want to overwrite"
3100 " the file \"%.*s\"?",
3101 FILENAME_MAX, filename);
3102 if (MessageBox(hwnd, buf, "Question",
3103 MB_YESNO | MB_ICONQUESTION)
3104 != IDYES)
3105 break;
3106 }
3107
3108 fp = fopen(filename, "w");
3109
3110 if (!fp) {
3111 MessageBox(hwnd, "Unable to open save file",
3112 "Error", MB_ICONERROR | MB_OK);
3113 break;
3114 }
3115
3116 midend_serialise(fe->me, savefile_write, fp);
3117
3118 fclose(fp);
3119 } else {
3120 FILE *fp = fopen(filename, "r");
3121 const char *err = NULL;
3122 char *err_w = NULL;
3123 midend *me = fe->me;
3124#ifdef COMBINED
3125 char *id_name;
3126#endif
3127
3128 if (!fp) {
3129 MessageBox(hwnd, "Unable to open saved game file",
3130 "Error", MB_ICONERROR | MB_OK);
3131 break;
3132 }
3133
3134#ifdef COMBINED
3135 /*
3136 * This save file might be from a different
3137 * game.
3138 */
3139 err = identify_game(&id_name, savefile_read, fp);
3140 if (!err) {
3141 int i;
3142 for (i = 0; i < gamecount; i++)
3143 if (!strcmp(id_name, gamelist[i]->name))
3144 break;
3145 if (i == gamecount) {
3146 err = "Save file is for a game not "
3147 "supported by this program";
3148 } else {
3149 me = midend_for_new_game(fe, gamelist[i], NULL,
3150 false, false, &err_w);
3151 err = err_w;
3152 rewind(fp); /* for the actual load */
3153 }
3154 sfree(id_name);
3155 }
3156#endif
3157 if (!err)
3158 err = midend_deserialise(me, savefile_read, fp);
3159
3160 fclose(fp);
3161
3162 if (err) {
3163 MessageBox(hwnd, err, "Error", MB_ICONERROR|MB_OK);
3164 sfree(err_w);
3165 break;
3166 }
3167
3168 if (fe->me != me)
3169 fe_set_midend(fe, me);
3170 new_game_size(fe, 1.0);
3171 }
3172 }
3173 }
3174
3175 break;
3176#ifndef _WIN32_WCE
3177 case IDM_HELPC:
3178 start_help(fe, NULL);
3179 break;
3180 case IDM_GAMEHELP:
3181 assert(help_type != NONE);
3182 start_help(fe, help_type == CHM ?
3183 fe->game->htmlhelp_topic : fe->game->winhelp_topic);
3184 break;
3185#endif
3186 default:
3187#ifdef COMBINED
3188 if (wParam >= IDM_GAMES && wParam < (IDM_GAMES + (WPARAM)gamecount)) {
3189 int p = wParam - IDM_GAMES;
3190 char *error = NULL;
3191 fe_set_midend(fe, midend_for_new_game(fe, gamelist[p], NULL,
3192 false, false, &error));
3193 sfree(error);
3194 } else
3195#endif
3196 {
3197 game_params *preset = preset_menu_lookup_by_id(
3198 fe->preset_menu,
3199 ((wParam &~ 0xF) - IDM_PRESETS) / 0x10);
3200
3201 if (preset) {
3202 midend_set_params(fe->me, preset);
3203 new_game_type(fe);
3204 }
3205 }
3206 break;
3207 }
3208 break;
3209 case WM_DESTROY:
3210#ifndef _WIN32_WCE
3211 stop_help(fe);
3212#endif
3213 frontend_free(fe);
3214 PostQuitMessage(0);
3215 return 0;
3216 case WM_PAINT:
3217 {
3218 PAINTSTRUCT p;
3219 HDC hdc, hdc2;
3220 HBITMAP prevbm;
3221 RECT rcDest;
3222
3223 hdc = BeginPaint(hwnd, &p);
3224 hdc2 = CreateCompatibleDC(hdc);
3225 prevbm = SelectObject(hdc2, fe->bitmap);
3226#ifdef _WIN32_WCE
3227 FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH));
3228#endif
3229 IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint));
3230 BitBlt(hdc,
3231 rcDest.left, rcDest.top,
3232 rcDest.right - rcDest.left,
3233 rcDest.bottom - rcDest.top,
3234 hdc2,
3235 rcDest.left - fe->bitmapPosition.left,
3236 rcDest.top - fe->bitmapPosition.top,
3237 SRCCOPY);
3238 SelectObject(hdc2, prevbm);
3239 DeleteDC(hdc2);
3240 EndPaint(hwnd, &p);
3241 }
3242 return 0;
3243 case WM_KEYDOWN:
3244 {
3245 int key = -1;
3246 BYTE keystate[256];
3247 int r = GetKeyboardState(keystate);
3248 int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0;
3249 int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0;
3250
3251 switch (wParam) {
3252 case VK_LEFT:
3253 if (!(lParam & 0x01000000))
3254 key = MOD_NUM_KEYPAD | '4';
3255 else
3256 key = shift | ctrl | CURSOR_LEFT;
3257 break;
3258 case VK_RIGHT:
3259 if (!(lParam & 0x01000000))
3260 key = MOD_NUM_KEYPAD | '6';
3261 else
3262 key = shift | ctrl | CURSOR_RIGHT;
3263 break;
3264 case VK_UP:
3265 if (!(lParam & 0x01000000))
3266 key = MOD_NUM_KEYPAD | '8';
3267 else
3268 key = shift | ctrl | CURSOR_UP;
3269 break;
3270 case VK_DOWN:
3271 if (!(lParam & 0x01000000))
3272 key = MOD_NUM_KEYPAD | '2';
3273 else
3274 key = shift | ctrl | CURSOR_DOWN;
3275 break;
3276 /*
3277 * Diagonal keys on the numeric keypad.
3278 */
3279 case VK_PRIOR:
3280 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '9';
3281 break;
3282 case VK_NEXT:
3283 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '3';
3284 break;
3285 case VK_HOME:
3286 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '7';
3287 break;
3288 case VK_END:
3289 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '1';
3290 break;
3291 case VK_INSERT:
3292 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '0';
3293 break;
3294 case VK_CLEAR:
3295 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '5';
3296 break;
3297 /*
3298 * Numeric keypad keys with Num Lock on.
3299 */
3300 case VK_NUMPAD4: key = MOD_NUM_KEYPAD | '4'; break;
3301 case VK_NUMPAD6: key = MOD_NUM_KEYPAD | '6'; break;
3302 case VK_NUMPAD8: key = MOD_NUM_KEYPAD | '8'; break;
3303 case VK_NUMPAD2: key = MOD_NUM_KEYPAD | '2'; break;
3304 case VK_NUMPAD5: key = MOD_NUM_KEYPAD | '5'; break;
3305 case VK_NUMPAD9: key = MOD_NUM_KEYPAD | '9'; break;
3306 case VK_NUMPAD3: key = MOD_NUM_KEYPAD | '3'; break;
3307 case VK_NUMPAD7: key = MOD_NUM_KEYPAD | '7'; break;
3308 case VK_NUMPAD1: key = MOD_NUM_KEYPAD | '1'; break;
3309 case VK_NUMPAD0: key = MOD_NUM_KEYPAD | '0'; break;
3310 }
3311
3312 if (key != -1) {
3313 if (!midend_process_key(fe->me, 0, 0, key))
3314 PostQuitMessage(0);
3315 } else {
3316 MSG m;
3317 m.hwnd = hwnd;
3318 m.message = WM_KEYDOWN;
3319 m.wParam = wParam;
3320 m.lParam = lParam & 0xdfff;
3321 TranslateMessage(&m);
3322 }
3323 }
3324 break;
3325 case WM_LBUTTONDOWN:
3326 case WM_RBUTTONDOWN:
3327 case WM_MBUTTONDOWN:
3328 {
3329 int button;
3330
3331 /*
3332 * Shift-clicks count as middle-clicks, since otherwise
3333 * two-button Windows users won't have any kind of
3334 * middle click to use.
3335 */
3336 if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT))
3337 button = MIDDLE_BUTTON;
3338 else if (message == WM_RBUTTONDOWN || is_alt_pressed())
3339 button = RIGHT_BUTTON;
3340 else
3341#ifndef _WIN32_WCE
3342 button = LEFT_BUTTON;
3343#else
3344 if ((fe->game->flags & REQUIRE_RBUTTON) == 0)
3345 button = LEFT_BUTTON;
3346 else
3347 {
3348 SHRGINFO shrgi;
3349
3350 shrgi.cbSize = sizeof(SHRGINFO);
3351 shrgi.hwndClient = hwnd;
3352 shrgi.ptDown.x = (signed short)LOWORD(lParam);
3353 shrgi.ptDown.y = (signed short)HIWORD(lParam);
3354 shrgi.dwFlags = SHRG_RETURNCMD;
3355
3356 if (GN_CONTEXTMENU == SHRecognizeGesture(&shrgi))
3357 button = RIGHT_BUTTON;
3358 else
3359 button = LEFT_BUTTON;
3360 }
3361#endif
3362
3363 if (!midend_process_key(fe->me,
3364 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3365 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3366 button))
3367 PostQuitMessage(0);
3368
3369 SetCapture(hwnd);
3370 }
3371 break;
3372 case WM_LBUTTONUP:
3373 case WM_RBUTTONUP:
3374 case WM_MBUTTONUP:
3375 {
3376 int button;
3377
3378 /*
3379 * Shift-clicks count as middle-clicks, since otherwise
3380 * two-button Windows users won't have any kind of
3381 * middle click to use.
3382 */
3383 if (message == WM_MBUTTONUP || (wParam & MK_SHIFT))
3384 button = MIDDLE_RELEASE;
3385 else if (message == WM_RBUTTONUP || is_alt_pressed())
3386 button = RIGHT_RELEASE;
3387 else
3388 button = LEFT_RELEASE;
3389
3390 if (!midend_process_key(fe->me,
3391 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3392 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3393 button))
3394 PostQuitMessage(0);
3395
3396 ReleaseCapture();
3397 }
3398 break;
3399 case WM_MOUSEMOVE:
3400 {
3401 int button;
3402
3403 if (wParam & (MK_MBUTTON | MK_SHIFT))
3404 button = MIDDLE_DRAG;
3405 else if (wParam & MK_RBUTTON || is_alt_pressed())
3406 button = RIGHT_DRAG;
3407 else
3408 button = LEFT_DRAG;
3409
3410 if (!midend_process_key(fe->me,
3411 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3412 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3413 button))
3414 PostQuitMessage(0);
3415 }
3416 break;
3417 case WM_CHAR:
3418 {
3419 int key = (unsigned char)wParam;
3420 if (key == '\x1A') {
3421 BYTE keystate[256];
3422 if (GetKeyboardState(keystate) &&
3423 (keystate[VK_SHIFT] & 0x80) &&
3424 (keystate[VK_CONTROL] & 0x80))
3425 key = UI_REDO;
3426 }
3427 if (!midend_process_key(fe->me, 0, 0, key))
3428 PostQuitMessage(0);
3429 }
3430 return 0;
3431 case WM_TIMER:
3432 if (fe->timer) {
3433 DWORD now = GetTickCount();
3434 float elapsed = (float) (now - fe->timer_last_tickcount) * 0.001F;
3435 midend_timer(fe->me, elapsed);
3436 fe->timer_last_tickcount = now;
3437 }
3438 return 0;
3439#ifndef _WIN32_WCE
3440 case WM_SIZING:
3441 {
3442 RECT *sr = (RECT *)lParam;
3443 int wx, wy;
3444 bool isedge = false;
3445
3446 if (wParam == WMSZ_TOP ||
3447 wParam == WMSZ_RIGHT ||
3448 wParam == WMSZ_BOTTOM ||
3449 wParam == WMSZ_LEFT) isedge = true;
3450 adjust_game_size(fe, sr, isedge, &wx, &wy);
3451
3452 /* Given the window size the puzzles constrain
3453 * us to, work out which edge we should be moving. */
3454 if (wParam == WMSZ_TOP ||
3455 wParam == WMSZ_TOPLEFT ||
3456 wParam == WMSZ_TOPRIGHT) {
3457 sr->top = sr->bottom - wy;
3458 } else {
3459 sr->bottom = sr->top + wy;
3460 }
3461 if (wParam == WMSZ_LEFT ||
3462 wParam == WMSZ_TOPLEFT ||
3463 wParam == WMSZ_BOTTOMLEFT) {
3464 sr->left = sr->right - wx;
3465 } else {
3466 sr->right = sr->left + wx;
3467 }
3468 return true;
3469 }
3470 break;
3471#endif
3472 }
3473
3474 return DefWindowProc(hwnd, message, wParam, lParam);
3475}
3476
3477#ifdef _WIN32_WCE
3478static int FindPreviousInstance()
3479{
3480 /* Check if application is running. If it's running then focus on the window */
3481 HWND hOtherWnd = NULL;
3482
3483 hOtherWnd = FindWindow (wGameName, wGameName);
3484 if (hOtherWnd)
3485 {
3486 SetForegroundWindow (hOtherWnd);
3487 return true;
3488 }
3489
3490 return false;
3491}
3492#endif
3493
3494/*
3495 * Split a complete command line into argc/argv, attempting to do it
3496 * exactly the same way the Visual Studio C library would do it (so
3497 * that our console utilities, which receive argc and argv already
3498 * broken apart by the C library, will have their command lines
3499 * processed in the same way as the GUI utilities which get a whole
3500 * command line and must call this function).
3501 *
3502 * Does not modify the input command line.
3503 *
3504 * The final parameter (argstart) is used to return a second array
3505 * of char * pointers, the same length as argv, each one pointing
3506 * at the start of the corresponding element of argv in the
3507 * original command line. So if you get half way through processing
3508 * your command line in argc/argv form and then decide you want to
3509 * treat the rest as a raw string, you can. If you don't want to,
3510 * `argstart' can be safely left NULL.
3511 */
3512void split_into_argv(char *cmdline, int *argc, char ***argv,
3513 char ***argstart)
3514{
3515 char *p;
3516 char *outputline, *q;
3517 char **outputargv, **outputargstart;
3518 int outputargc;
3519
3520 /*
3521 * These argument-breaking rules apply to Visual Studio 7, which
3522 * is currently the compiler expected to be used for the Windows
3523 * port of my puzzles. Visual Studio 10 has different rules,
3524 * lacking the curious mod 3 behaviour of consecutive quotes
3525 * described below; I presume they fixed a bug. As and when we
3526 * migrate to a newer compiler, we'll have to adjust this to
3527 * match; however, for the moment we faithfully imitate in our GUI
3528 * utilities what our CLI utilities can't be prevented from doing.
3529 *
3530 * When I investigated this, at first glance the rules appeared to
3531 * be:
3532 *
3533 * - Single quotes are not special characters.
3534 *
3535 * - Double quotes are removed, but within them spaces cease
3536 * to be special.
3537 *
3538 * - Backslashes are _only_ special when a sequence of them
3539 * appear just before a double quote. In this situation,
3540 * they are treated like C backslashes: so \" just gives a
3541 * literal quote, \\" gives a literal backslash and then
3542 * opens or closes a double-quoted segment, \\\" gives a
3543 * literal backslash and then a literal quote, \\\\" gives
3544 * two literal backslashes and then opens/closes a
3545 * double-quoted segment, and so forth. Note that this
3546 * behaviour is identical inside and outside double quotes.
3547 *
3548 * - Two successive double quotes become one literal double
3549 * quote, but only _inside_ a double-quoted segment.
3550 * Outside, they just form an empty double-quoted segment
3551 * (which may cause an empty argument word).
3552 *
3553 * - That only leaves the interesting question of what happens
3554 * when one or more backslashes precedes two or more double
3555 * quotes, starting inside a double-quoted string. And the
3556 * answer to that appears somewhat bizarre. Here I tabulate
3557 * number of backslashes (across the top) against number of
3558 * quotes (down the left), and indicate how many backslashes
3559 * are output, how many quotes are output, and whether a
3560 * quoted segment is open at the end of the sequence:
3561 *
3562 * backslashes
3563 *
3564 * 0 1 2 3 4
3565 *
3566 * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y
3567 * --------+-----------------------------
3568 * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n
3569 * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n
3570 * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y
3571 * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n
3572 * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n
3573 * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y
3574 * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n
3575 * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n
3576 * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y
3577 * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n
3578 * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n
3579 *
3580 *
3581 * [Test fragment was of the form "a\\\"""b c" d.]
3582 *
3583 * There is very weird mod-3 behaviour going on here in the
3584 * number of quotes, and it even applies when there aren't any
3585 * backslashes! How ghastly.
3586 *
3587 * With a bit of thought, this extremely odd diagram suddenly
3588 * coalesced itself into a coherent, if still ghastly, model of
3589 * how things work:
3590 *
3591 * - As before, backslashes are only special when one or more
3592 * of them appear contiguously before at least one double
3593 * quote. In this situation the backslashes do exactly what
3594 * you'd expect: each one quotes the next thing in front of
3595 * it, so you end up with n/2 literal backslashes (if n is
3596 * even) or (n-1)/2 literal backslashes and a literal quote
3597 * (if n is odd). In the latter case the double quote
3598 * character right after the backslashes is used up.
3599 *
3600 * - After that, any remaining double quotes are processed. A
3601 * string of contiguous unescaped double quotes has a mod-3
3602 * behaviour:
3603 *
3604 * * inside a quoted segment, a quote ends the segment.
3605 * * _immediately_ after ending a quoted segment, a quote
3606 * simply produces a literal quote.
3607 * * otherwise, outside a quoted segment, a quote begins a
3608 * quoted segment.
3609 *
3610 * So, for example, if we started inside a quoted segment
3611 * then two contiguous quotes would close the segment and
3612 * produce a literal quote; three would close the segment,
3613 * produce a literal quote, and open a new segment. If we
3614 * started outside a quoted segment, then two contiguous
3615 * quotes would open and then close a segment, producing no
3616 * output (but potentially creating a zero-length argument);
3617 * but three quotes would open and close a segment and then
3618 * produce a literal quote.
3619 */
3620
3621 /*
3622 * First deal with the simplest of all special cases: if there
3623 * aren't any arguments, return 0,NULL,NULL.
3624 */
3625 while (*cmdline && isspace(*cmdline)) cmdline++;
3626 if (!*cmdline) {
3627 if (argc) *argc = 0;
3628 if (argv) *argv = NULL;
3629 if (argstart) *argstart = NULL;
3630 return;
3631 }
3632
3633 /*
3634 * This will guaranteeably be big enough; we can realloc it
3635 * down later.
3636 */
3637 outputline = snewn(1+strlen(cmdline), char);
3638 outputargv = snewn(strlen(cmdline)+1 / 2, char *);
3639 outputargstart = snewn(strlen(cmdline)+1 / 2, char *);
3640
3641 p = cmdline; q = outputline; outputargc = 0;
3642
3643 while (*p) {
3644 bool quote;
3645
3646 /* Skip whitespace searching for start of argument. */
3647 while (*p && isspace(*p)) p++;
3648 if (!*p) break;
3649
3650 /* We have an argument; start it. */
3651 outputargv[outputargc] = q;
3652 outputargstart[outputargc] = p;
3653 outputargc++;
3654 quote = false;
3655
3656 /* Copy data into the argument until it's finished. */
3657 while (*p) {
3658 if (!quote && isspace(*p))
3659 break; /* argument is finished */
3660
3661 if (*p == '"' || *p == '\\') {
3662 /*
3663 * We have a sequence of zero or more backslashes
3664 * followed by a sequence of zero or more quotes.
3665 * Count up how many of each, and then deal with
3666 * them as appropriate.
3667 */
3668 int i, slashes = 0, quotes = 0;
3669 while (*p == '\\') slashes++, p++;
3670 while (*p == '"') quotes++, p++;
3671
3672 if (!quotes) {
3673 /*
3674 * Special case: if there are no quotes,
3675 * slashes are not special at all, so just copy
3676 * n slashes to the output string.
3677 */
3678 while (slashes--) *q++ = '\\';
3679 } else {
3680 /* Slashes annihilate in pairs. */
3681 while (slashes >= 2) slashes -= 2, *q++ = '\\';
3682
3683 /* One remaining slash takes out the first quote. */
3684 if (slashes) quotes--, *q++ = '"';
3685
3686 if (quotes > 0) {
3687 /* Outside a quote segment, a quote starts one. */
3688 if (!quote) quotes--, quote = true;
3689
3690 /* Now we produce (n+1)/3 literal quotes... */
3691 for (i = 3; i <= quotes+1; i += 3) *q++ = '"';
3692
3693 /* ... and end in a quote segment iff 3 divides n. */
3694 quote = (quotes % 3 == 0);
3695 }
3696 }
3697 } else {
3698 *q++ = *p++;
3699 }
3700 }
3701
3702 /* At the end of an argument, just append a trailing NUL. */
3703 *q++ = '\0';
3704 }
3705
3706 outputargv = sresize(outputargv, outputargc, char *);
3707 outputargstart = sresize(outputargstart, outputargc, char *);
3708
3709 if (argc) *argc = outputargc;
3710 if (argv) *argv = outputargv; else sfree(outputargv);
3711 if (argstart) *argstart = outputargstart; else sfree(outputargstart);
3712}
3713
3714int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
3715{
3716 MSG msg;
3717 char *error = NULL;
3718 const game *gg;
3719 frontend *fe;
3720 midend *me;
3721 int argc;
3722 char **argv;
3723
3724 split_into_argv(cmdline, &argc, &argv, NULL);
3725
3726#ifdef _WIN32_WCE
3727 MultiByteToWideChar (CP_ACP, 0, CLASSNAME, -1, wClassName, 256);
3728 if (FindPreviousInstance ())
3729 return 0;
3730#endif
3731
3732 InitCommonControls();
3733
3734 if (!prev) {
3735 WNDCLASS wndclass;
3736
3737 wndclass.style = 0;
3738 wndclass.lpfnWndProc = WndProc;
3739 wndclass.cbClsExtra = 0;
3740 wndclass.cbWndExtra = 0;
3741 wndclass.hInstance = inst;
3742 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200));
3743#ifndef _WIN32_WCE
3744 if (!wndclass.hIcon) /* in case resource file is absent */
3745 wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION);
3746#endif
3747 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
3748 wndclass.hbrBackground = NULL;
3749 wndclass.lpszMenuName = NULL;
3750#ifdef _WIN32_WCE
3751 wndclass.lpszClassName = wClassName;
3752#else
3753 wndclass.lpszClassName = CLASSNAME;
3754#endif
3755
3756 RegisterClass(&wndclass);
3757 }
3758
3759 while (*cmdline && isspace((unsigned char)*cmdline))
3760 cmdline++;
3761
3762 init_help();
3763
3764#ifdef COMBINED
3765 gg = gamelist[0];
3766 if (argc > 0) {
3767 int i;
3768 for (i = 0; i < gamecount; i++) {
3769 const char *p = gamelist[i]->name;
3770 char *q = argv[0];
3771 while (*p && *q) {
3772 if (isspace((unsigned char)*p)) {
3773 while (*q && isspace((unsigned char)*q))
3774 q++;
3775 } else {
3776 if (tolower((unsigned char)*p) !=
3777 tolower((unsigned char)*q))
3778 break;
3779 q++;
3780 }
3781 p++;
3782 }
3783 if (!*p) {
3784 gg = gamelist[i];
3785 --argc;
3786 ++argv;
3787 break;
3788 }
3789 }
3790 }
3791#else
3792 gg = &thegame;
3793#endif
3794
3795 fe = frontend_new(inst);
3796 me = midend_for_new_game(fe, gg, argc > 0 ? argv[0] : NULL,
3797 true, true, &error);
3798 if (!me) {
3799 char buf[128];
3800#ifdef COMBINED
3801 sprintf(buf, "Puzzles Error");
3802#else
3803 sprintf(buf, "%.100s Error", gg->name);
3804#endif
3805 MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR);
3806 sfree(error);
3807 return 1;
3808 }
3809 fe_set_midend(fe, me);
3810 show_window(fe);
3811
3812 while (GetMessage(&msg, NULL, 0, 0)) {
3813 DispatchMessage(&msg);
3814 }
3815
3816 DestroyWindow(fe->hwnd);
3817 cleanup_help();
3818
3819 return msg.wParam;
3820}
3821/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/random_folder_advance_config.c b/apps/plugins/random_folder_advance_config.c
index 5688ff93d3..3d762c6db7 100644
--- a/apps/plugins/random_folder_advance_config.c
+++ b/apps/plugins/random_folder_advance_config.c
@@ -309,7 +309,6 @@ static int edit_list(void)
309 dirs_count = list->count; 309 dirs_count = list->count;
310 310
311 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL); 311 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
312 rb->gui_synclist_set_icon_callback(&lists,NULL);
313 rb->gui_synclist_set_nb_items(&lists,list->count); 312 rb->gui_synclist_set_nb_items(&lists,list->count);
314 rb->gui_synclist_select_item(&lists, 0); 313 rb->gui_synclist_select_item(&lists, 0);
315 314
diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c
index a89cc658cc..31b06d6f75 100644
--- a/apps/plugins/rb_info.c
+++ b/apps/plugins/rb_info.c
@@ -518,7 +518,6 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si
518 { 518 {
519 rb->gui_synclist_set_title(&lists, NULL,-1); 519 rb->gui_synclist_set_title(&lists, NULL,-1);
520 } 520 }
521 rb->gui_synclist_set_icon_callback(&lists,NULL);
522 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); 521 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
523 rb->gui_synclist_set_nb_items(&lists,items); 522 rb->gui_synclist_set_nb_items(&lists,items);
524 rb->gui_synclist_select_item(&lists, selected_item); 523 rb->gui_synclist_select_item(&lists, selected_item);
diff --git a/apps/plugins/rockbox-fonts.config b/apps/plugins/rockbox-fonts.config
index 4995d71864..8762ff5c74 100644
--- a/apps/plugins/rockbox-fonts.config
+++ b/apps/plugins/rockbox-fonts.config
@@ -54,12 +54,16 @@ font: 18-Adobe-Helvetica
54font: 18-Adobe-Helvetica-Bold 54font: 18-Adobe-Helvetica-Bold
55font: 18-Fixed 55font: 18-Fixed
56font: 18-Fixed-Bold 56font: 18-Fixed-Bold
57font: 18-Terminus
58font: 18-Terminus-Bold
57font: 19-Nimbus 59font: 19-Nimbus
58font: 20-Terminus 60font: 20-Terminus
59font: 20-Terminus-Bold 61font: 20-Terminus-Bold
60font: 21-Adobe-Helvetica 62font: 21-Adobe-Helvetica
61font: 21-Adobe-Helvetica-Bold 63font: 21-Adobe-Helvetica-Bold
62font: 22-ProFont 64font: 22-ProFont
65font: 22-Terminus
66font: 22-Terminus-Bold
63font: 24-Terminus 67font: 24-Terminus
64font: 24-Terminus-Bold 68font: 24-Terminus-Bold
65font: 27-Adobe-Helvetica 69font: 27-Adobe-Helvetica
diff --git a/apps/plugins/stats.c b/apps/plugins/stats.c
index b48259a0e4..8a511bea42 100644
--- a/apps/plugins/stats.c
+++ b/apps/plugins/stats.c
@@ -19,184 +19,27 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "plugin.h" 21#include "plugin.h"
22#include "lib/pluginlib_actions.h" 22#include "lib/mul_id3.h" /* collect_dir_stats & display_dir_stats */
23 23
24
25static int files, dirs, audiofiles, m3ufiles, imagefiles, videofiles, largestdir;
26static int lasttick;
27static bool cancel;
28
29
30/* we use PLA */
31#define STATS_STOP PLA_EXIT
32
33#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
34 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
35 || (CONFIG_KEYPAD == IPOD_4G_PAD)
36#define STATS_STOP2 PLA_UP
37#else
38#define STATS_STOP2 PLA_CANCEL
39#endif
40
41/* this set the context to use with PLA */
42static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
43
44/* we don't have yet a filetype attribute for image files */
45static const char *image_exts[] = {"bmp","jpg","jpe","jpeg","png","ppm"};
46
47/* neither for video ones */
48static const char *video_exts[] = {"mpg","mpeg","mpv","m2v"};
49
50static void prn(const char *str, int y)
51{
52 rb->lcd_puts(0,y,str);
53#ifdef HAVE_REMOTE_LCD
54 rb->lcd_remote_puts(0,y,str);
55#endif
56}
57
58static void update_screen(void)
59{
60 char buf[32];
61
62 rb->lcd_clear_display();
63#ifdef HAVE_REMOTE_LCD
64 rb->lcd_remote_clear_display();
65#endif
66
67 rb->snprintf(buf, sizeof(buf), "Total Files: %d", files);
68 prn(buf,0);
69 rb->snprintf(buf, sizeof(buf), "Audio: %d", audiofiles);
70 prn(buf,1);
71 rb->snprintf(buf, sizeof(buf), "Playlists: %d", m3ufiles);
72 prn(buf,2);
73 rb->snprintf(buf, sizeof(buf), "Images: %d", imagefiles);
74 prn(buf,3);
75 rb->snprintf(buf, sizeof(buf), "Videos: %d", videofiles);
76 prn(buf,4);
77 rb->snprintf(buf, sizeof(buf), "Directories: %d", dirs);
78 prn(buf,5);
79 rb->snprintf(buf, sizeof(buf), "Max files in Dir: %d", largestdir);
80 prn(buf,6);
81
82 rb->lcd_update();
83#ifdef HAVE_REMOTE_LCD
84 rb->lcd_remote_update();
85#endif
86}
87
88static void traversedir(char* location, char* name)
89{
90 int button;
91 struct dirent *entry;
92 DIR* dir;
93 char fullpath[MAX_PATH];
94 int files_in_dir = 0;
95
96 rb->snprintf(fullpath, sizeof(fullpath), "%s/%s", location, name);
97 dir = rb->opendir(fullpath);
98 if (dir) {
99 entry = rb->readdir(dir);
100 while (entry) {
101 if (cancel)
102 break;
103 /* Skip .. and . */
104 if (rb->strcmp(entry->d_name, ".") && rb->strcmp(entry->d_name, ".."))
105 {
106 struct dirinfo info = rb->dir_get_info(dir, entry);
107 if (info.attribute & ATTR_DIRECTORY) {
108 traversedir(fullpath, entry->d_name);
109 dirs++;
110 }
111 else {
112 files_in_dir++; files++;
113
114 /* get the filetype from the filename */
115 int attr = rb->filetype_get_attr(entry->d_name);
116 switch (attr & FILE_ATTR_MASK)
117 {
118 case FILE_ATTR_AUDIO:
119 audiofiles++;
120 break;
121
122 case FILE_ATTR_M3U:
123 m3ufiles++;
124 break;
125
126 default:
127 {
128 /* use hardcoded filetype_exts to count
129 * image and video files until we get
130 * new attributes added to filetypes.h */
131 char *ptr = rb->strrchr(entry->d_name,'.');
132 if(ptr) {
133 unsigned i;
134 ptr++;
135 for(i=0;i<ARRAYLEN(image_exts);i++) {
136 if(!rb->strcasecmp(ptr,image_exts[i])) {
137 imagefiles++; break;
138 }
139 }
140
141 if (i >= ARRAYLEN(image_exts)) {
142 /* not found above - try video files */
143 for(i=0;i<ARRAYLEN(video_exts);i++) {
144 if(!rb->strcasecmp(ptr,video_exts[i])) {
145 videofiles++; break;
146 }
147 }
148 }
149 }
150 } /* default: */
151 } /* switch */
152 }
153 }
154
155 if (*rb->current_tick - lasttick > (HZ/2)) {
156 update_screen();
157 lasttick = *rb->current_tick;
158 button = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts,
159 ARRAYLEN(plugin_contexts));
160 if (button == STATS_STOP || button == STATS_STOP2) {
161 cancel = true;
162 break;
163 }
164 }
165
166 entry = rb->readdir(dir);
167 }
168 rb->closedir(dir);
169 }
170 if (largestdir < files_in_dir)
171 largestdir = files_in_dir;
172}
173
174/* this is the plugin entry point */
175enum plugin_status plugin_start(const void* parameter) 24enum plugin_status plugin_start(const void* parameter)
176{ 25{
177 int button;
178
179 (void)parameter; 26 (void)parameter;
180 27 int button, success;
181 files = 0; 28 static struct dir_stats stats;
182 dirs = 0; 29 stats.dirname[0] = '/';
183 audiofiles = 0; 30 stats.count_all = true;
184 m3ufiles = 0; 31#ifdef HAVE_ADJUSTABLE_CPU_FREQ
185 imagefiles = 0; 32 rb->cpu_boost(true);
186 videofiles = 0; 33#endif
187 largestdir = 0; 34 display_dir_stats(&stats);
188 cancel = false; 35 success = collect_dir_stats(&stats, NULL);
189 36#ifdef HAVE_ADJUSTABLE_CPU_FREQ
190 rb->splash(HZ, "Counting..."); 37 rb->cpu_boost(false);
191 update_screen(); 38#endif
192 lasttick = *rb->current_tick; 39 if (!success)
193
194 traversedir("", "");
195 if (cancel) {
196 rb->splash(HZ, "Aborted");
197 return PLUGIN_OK; 40 return PLUGIN_OK;
198 } 41
199 update_screen(); 42 display_dir_stats(&stats);
200#ifdef HAVE_BACKLIGHT 43#ifdef HAVE_BACKLIGHT
201#ifdef HAVE_REMOTE_LCD 44#ifdef HAVE_REMOTE_LCD
202 rb->remote_backlight_on(); 45 rb->remote_backlight_on();
@@ -204,21 +47,15 @@ enum plugin_status plugin_start(const void* parameter)
204 rb->backlight_on(); 47 rb->backlight_on();
205#endif 48#endif
206 rb->splash(HZ, "Done"); 49 rb->splash(HZ, "Done");
207 update_screen(); 50 display_dir_stats(&stats);
208 while (1) { 51 while (1) {
209 52 switch (button = rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK))
210 button = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 53 {
211 ARRAYLEN(plugin_contexts)); 54 case ACTION_STD_CANCEL:
212 switch (button) {
213 case STATS_STOP:
214 case STATS_STOP2:
215 return PLUGIN_OK; 55 return PLUGIN_OK;
216 break;
217 default: 56 default:
218 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { 57 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
219 return PLUGIN_USB_CONNECTED; 58 return PLUGIN_USB_CONNECTED;
220 }
221 break;
222 } 59 }
223 } 60 }
224 return PLUGIN_OK; 61 return PLUGIN_OK;
diff --git a/apps/plugins/superdom.c b/apps/plugins/superdom.c
index efa6f4a22a..10373e1cc9 100644
--- a/apps/plugins/superdom.c
+++ b/apps/plugins/superdom.c
@@ -385,9 +385,13 @@ static int calc_strength(int colour, int x, int y)
385 if(a && b) /* diagonally adjacent, give less influence */ 385 if(a && b) /* diagonally adjacent, give less influence */
386 { 386 {
387 score += 5; 387 score += 5;
388 if(board[x + a][y + b].tank || board[x + a][y + b].farm) 388 if(board[x + a][y + b].tank)
389 score += 15; 389 score += 15;
390 if(board[x + a][y + b].plane || board[x + a][y + b].ind) 390 if(board[x + a][y + b].farm)
391 score += 15;
392 if(board[x + a][y + b].plane)
393 score += 20;
394 if (board[x + a][y + b].ind)
391 score += 20; 395 score += 20;
392 if(board[x + a][y + b].nuke) 396 if(board[x + a][y + b].nuke)
393 score += 10; 397 score += 10;
@@ -397,10 +401,14 @@ static int calc_strength(int colour, int x, int y)
397 else 401 else
398 { 402 {
399 score += 10; 403 score += 10;
400 if(board[x + a][y + b].tank || board[x + a][y + b].farm) 404 if(board[x + a][y + b].tank)
405 score += 30;
406 if(board[x + a][y + b].farm)
401 score += 30; 407 score += 30;
402 if(board[x + a][y + b].plane || board[x + a][y + b].ind) 408 if(board[x + a][y + b].plane)
403 score += 40; 409 score += 20;
410 if(board[x + a][y + b].ind)
411 score += 20;
404 if(board[x + a][y + b].nuke) 412 if(board[x + a][y + b].nuke)
405 score += 20; 413 score += 20;
406 if(board[x + a][y + b].men) 414 if(board[x + a][y + b].men)
@@ -1943,7 +1951,7 @@ static void computer_allocate(void)
1943 } 1951 }
1944 if(superdom_settings.compdiff>=AI_BUILD_INDS_FARMS_LEVEL && compres.cash>=PRICE_FACTORY+100) 1952 if(superdom_settings.compdiff>=AI_BUILD_INDS_FARMS_LEVEL && compres.cash>=PRICE_FACTORY+100)
1945 { 1953 {
1946 int i = 0; 1954 int cnt = 0;
1947 do 1955 do
1948 { 1956 {
1949 if(compres.farms<compres.inds) 1957 if(compres.farms<compres.inds)
@@ -1966,7 +1974,7 @@ static void computer_allocate(void)
1966 break; 1974 break;
1967 } 1975 }
1968 } 1976 }
1969 } while(compres.cash>=PRICE_FACTORY + 100 && i++ < 3); 1977 } while(compres.cash>=PRICE_FACTORY + 100 && cnt++ < 3);
1970 } 1978 }
1971 /* AI will buy nukes first if possible */ 1979 /* AI will buy nukes first if possible */
1972 if(compres.cash > PRICE_NUKE + PRICE_TANK && superdom_settings.compdiff>=AI_BUILD_NUKES_LEVEL) 1980 if(compres.cash > PRICE_NUKE + PRICE_TANK && superdom_settings.compdiff>=AI_BUILD_NUKES_LEVEL)
diff --git a/apps/plugins/tagcache/tagcache.c b/apps/plugins/tagcache/tagcache.c
index cce9efbed9..23651a4471 100644
--- a/apps/plugins/tagcache/tagcache.c
+++ b/apps/plugins/tagcache/tagcache.c
@@ -761,7 +761,6 @@ static int commit_menu(void)
761 int selection, ret = 0; 761 int selection, ret = 0;
762 762
763 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL); 763 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
764 rb->gui_synclist_set_icon_callback(&lists, NULL);
765 rb->gui_synclist_set_nb_items(&lists, 9); 764 rb->gui_synclist_set_nb_items(&lists, 9);
766 rb->gui_synclist_select_item(&lists, 0); 765 rb->gui_synclist_select_item(&lists, 0);
767 766
diff --git a/apps/plugins/text_editor.c b/apps/plugins/text_editor.c
index 8740606c58..f7d62ca07a 100644
--- a/apps/plugins/text_editor.c
+++ b/apps/plugins/text_editor.c
@@ -212,7 +212,6 @@ static bool save_changes(int overwrite)
212static void setup_lists(struct gui_synclist *lists, int sel) 212static void setup_lists(struct gui_synclist *lists, int sel)
213{ 213{
214 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL); 214 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
215 rb->gui_synclist_set_icon_callback(lists,NULL);
216 rb->gui_synclist_set_nb_items(lists,line_count); 215 rb->gui_synclist_set_nb_items(lists,line_count);
217 rb->gui_synclist_select_item(lists, sel); 216 rb->gui_synclist_select_item(lists, sel);
218 rb->gui_synclist_draw(lists); 217 rb->gui_synclist_draw(lists);
diff --git a/apps/plugins/vbrfix.c b/apps/plugins/vbrfix.c
index 5aaf2ffada..622a0a4f07 100644
--- a/apps/plugins/vbrfix.c
+++ b/apps/plugins/vbrfix.c
@@ -155,8 +155,8 @@ static bool vbr_fix(const char *selected_file)
155 155
156 xingupdate(0); 156 xingupdate(0);
157 157
158 rc = rb->mp3info(&entry, selected_file); 158 rc = rb->get_metadata(&entry, -1, selected_file);
159 if(rc) { 159 if(!rc) {
160 fileerror(rc); 160 fileerror(rc);
161 return true; 161 return true;
162 } 162 }
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 45bbfeef0b..d745a29cfe 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -34,6 +34,7 @@ rvf,viewers/video,4
34mp3,viewers/vbrfix,5 34mp3,viewers/vbrfix,5
35m3u,viewers/search,- 35m3u,viewers/search,-
36m3u,viewers/iriverify,- 36m3u,viewers/iriverify,-
37m3u,viewers/cue_playlist,-
37lrc,apps/lrcplayer,1 38lrc,apps/lrcplayer,1
38lrc8,apps/lrcplayer,1 39lrc8,apps/lrcplayer,1
39snc,apps/lrcplayer,1 40snc,apps/lrcplayer,1
diff --git a/apps/radio/presets.c b/apps/radio/presets.c
index 046525c3b6..b87d2dd285 100644
--- a/apps/radio/presets.c
+++ b/apps/radio/presets.c
@@ -485,7 +485,6 @@ int handle_radio_presets(void)
485 485
486 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL); 486 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
487 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON); 487 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
488 gui_synclist_set_icon_callback(&lists, NULL);
489 if(global_settings.talk_file) 488 if(global_settings.talk_file)
490 gui_synclist_set_voice_callback(&lists, presets_speak_name); 489 gui_synclist_set_voice_callback(&lists, presets_speak_name);
491 gui_synclist_set_nb_items(&lists, num_presets); 490 gui_synclist_set_nb_items(&lists, num_presets);
diff --git a/apps/recorder/keyboard.c b/apps/recorder/keyboard.c
index 736e9738c8..26ac6c1ae7 100644
--- a/apps/recorder/keyboard.c
+++ b/apps/recorder/keyboard.c
@@ -951,8 +951,8 @@ static void kbd_draw_picker(struct keyboard_parameters *pm,
951#ifdef HAVE_MORSE_INPUT 951#ifdef HAVE_MORSE_INPUT
952 if (state->morse_mode) 952 if (state->morse_mode)
953 { 953 {
954 const int w = 6, h = 8; /* sysfixed font width, height */ 954 const int w = 6, h = 9; /* sysfixed font width, height */
955 int i, j, x, y; 955 int i, iNext, j, x, y;
956 int sc_w = vp->width, sc_h = vp->height;//pm->main_y - pm->keyboard_margin - 1; 956 int sc_w = vp->width, sc_h = vp->height;//pm->main_y - pm->keyboard_margin - 1;
957 957
958 /* Draw morse code screen with sysfont */ 958 /* Draw morse code screen with sysfont */
@@ -960,37 +960,47 @@ static void kbd_draw_picker(struct keyboard_parameters *pm,
960 x = 0; 960 x = 0;
961 y = 0; 961 y = 0;
962 outline[1] = '\0'; 962 outline[1] = '\0';
963 963
964 /* Draw morse code table with code descriptions. */ 964 /* Draw morse code table with code descriptions. */
965 for (i = 0; morse_alphabets[i] != '\0'; i++) 965 for (i = 0; morse_alphabets[i] != '\0'; i++) {
966 {
967 int morse_code; 966 int morse_code;
968
969 outline[0] = morse_alphabets[i]; 967 outline[0] = morse_alphabets[i];
970 sc->putsxy(x, y, outline); 968 sc->putsxy(x, y, outline);
971
972 morse_code = morse_codes[i]; 969 morse_code = morse_codes[i];
973 for (j = 0; morse_code > 0x01; morse_code >>= 1) 970 for (j = 0; morse_code > 0x01; morse_code >>= 1) {
974 j++; 971 j++;
975 972 }
976 x += w + 3 + j*4; 973 x += w + 3 + j * 4;
977 morse_code = morse_codes[i]; 974 morse_code = morse_codes[i];
978 for (; morse_code > 0x01; morse_code >>= 1) 975 for (; morse_code > 0x01; morse_code >>= 1) {
979 {
980 x -= 4; 976 x -= 4;
981 if (morse_code & 0x01) 977 if (morse_code & 0x01) {
982 sc->fillrect(x, y + 2, 3, 4); 978 sc->fillrect(x, y + 2, 3, 4);
983 else 979 } else {
984 sc->fillrect(x, y + 3, 1, 2); 980 sc->fillrect(x, y + 3, 1, 2);
981 }
985 } 982 }
986 983 x += j * 4;
987 x += w*5 - 3; 984 iNext = i + 1;
988 if (x + w*6 >= sc_w) 985 if (morse_alphabets[iNext] == '\0') {
989 { 986 break;
987 }
988 morse_code = morse_codes[iNext];
989 for (j = 0; morse_code > 0x01; morse_code >>= 1) {
990 j++;
991 }
992 // If the next one will go out of line
993 bool needNewLine = x + w + 3 + j * 4 + w >= sc_w;
994 if (needNewLine) {
995 if (y + h >= sc_h) {
996 // No more height space
997 break;
998 }
990 x = 0; 999 x = 0;
991 y += h; 1000 y += h;
992 if (y + h >= sc_h) 1001 } else {
993 break; 1002 // Some pixels for spacing in the same line
1003 x += w;
994 } 1004 }
995 } 1005 }
996 } 1006 }
diff --git a/apps/screens.c b/apps/screens.c
index c88482d559..e3a73f3de2 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -656,10 +656,10 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
656 } 656 }
657 break; 657 break;
658 case LANG_FORMAT: 658 case LANG_FORMAT:
659 if (id3->codectype >= AFMT_NUM_CODECS) 659 if (id3->codectype == AFMT_UNKNOWN && info->track_ct > 1)
660 return NULL; 660 return NULL;
661 661
662 strmemccpy(buffer, audio_formats[id3->codectype].label, buffer_len); 662 strmemccpy(buffer, get_codec_string(id3->codectype), buffer_len);
663 663
664 val=buffer; 664 val=buffer;
665 if(say_it) 665 if(say_it)
@@ -900,7 +900,6 @@ int view_runtime(void)
900 gui_synclist_set_title(&lists, str(LANG_RUNNING_TIME), NOICON); 900 gui_synclist_set_title(&lists, str(LANG_RUNNING_TIME), NOICON);
901 if(global_settings.talk_menu) 901 if(global_settings.talk_menu)
902 gui_synclist_set_voice_callback(&lists, runtime_speak_data); 902 gui_synclist_set_voice_callback(&lists, runtime_speak_data);
903 gui_synclist_set_icon_callback(&lists, NULL);
904 gui_synclist_set_nb_items(&lists, 4); 903 gui_synclist_set_nb_items(&lists, 4);
905 904
906 while(1) 905 while(1)
diff --git a/apps/settings.h b/apps/settings.h
index 1df1e0b418..a9a9f647e3 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -78,6 +78,13 @@ enum
78 TRIG_TYPE_NEW_FILE 78 TRIG_TYPE_NEW_FILE
79}; 79};
80 80
81enum {
82 PLAYLIST_VIEWER_ENTRY_SHOW_FILE_NAME = 0,
83 PLAYLIST_VIEWER_ENTRY_SHOW_FULL_PATH = 1,
84 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM = 2,
85 PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE = 3
86};
87
81#ifdef HAVE_CROSSFADE 88#ifdef HAVE_CROSSFADE
82enum { 89enum {
83 CROSSFADE_ENABLE_OFF = 0, 90 CROSSFADE_ENABLE_OFF = 0,
@@ -686,6 +693,7 @@ struct user_settings
686 int screen_scroll_step; 693 int screen_scroll_step;
687 int show_path_in_browser; /* 0=off, 1=current directory, 2=full path */ 694 int show_path_in_browser; /* 0=off, 1=current directory, 2=full path */
688 bool offset_out_of_view; 695 bool offset_out_of_view;
696 bool disable_mainmenu_scrolling;
689 unsigned char icon_file[MAX_FILENAME+1]; 697 unsigned char icon_file[MAX_FILENAME+1];
690 unsigned char viewers_icon_file[MAX_FILENAME+1]; 698 unsigned char viewers_icon_file[MAX_FILENAME+1];
691 unsigned char font_file[MAX_FILENAME+1]; /* last font */ 699 unsigned char font_file[MAX_FILENAME+1]; /* last font */
@@ -803,6 +811,7 @@ struct user_settings
803#ifdef HAVE_REMOTE_LCD 811#ifdef HAVE_REMOTE_LCD
804 unsigned char remote_ui_vp_config[64]; /* viewport string for the remote lists */ 812 unsigned char remote_ui_vp_config[64]; /* viewport string for the remote lists */
805#endif 813#endif
814 char player_name[64]; /* Name of the local player */
806 815
807 struct compressor_settings compressor_settings; 816 struct compressor_settings compressor_settings;
808 817
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 09a45f1faa..a9f627bfa6 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -998,7 +998,7 @@ const struct settings_list settings[] = {
998 play_frequency, LANG_FREQUENCY, 0, "playback frequency", "auto", 998 play_frequency, LANG_FREQUENCY, 0, "playback frequency", "auto",
999 UNIT_KHZ, formatter_freq_unit_0_is_auto, 999 UNIT_KHZ, formatter_freq_unit_0_is_auto,
1000 getlang_freq_unit_0_is_auto, 1000 getlang_freq_unit_0_is_auto,
1001 playback_frequency_callback, 1001 playback_frequency_callback,
1002#if HAVE_PLAY_FREQ >= 192 1002#if HAVE_PLAY_FREQ >= 192
1003 7,0,SAMPR_44,SAMPR_48,SAMPR_88,SAMPR_96,SAMPR_176,SAMPR_192), 1003 7,0,SAMPR_44,SAMPR_48,SAMPR_88,SAMPR_96,SAMPR_176,SAMPR_192),
1004#elif HAVE_PLAY_FREQ >= 96 1004#elif HAVE_PLAY_FREQ >= 96
@@ -1120,7 +1120,11 @@ const struct settings_list settings[] = {
1120 SYSTEM_SETTING(NVRAM(4), topruntime, 0), 1120 SYSTEM_SETTING(NVRAM(4), topruntime, 0),
1121 INT_SETTING(F_BANFROMQS, max_files_in_playlist, 1121 INT_SETTING(F_BANFROMQS, max_files_in_playlist,
1122 LANG_MAX_FILES_IN_PLAYLIST, 1122 LANG_MAX_FILES_IN_PLAYLIST,
1123#if MEMORYSIZE > 1 1123#if CONFIG_CPU == PP5002 || CONFIG_CPU == PP5020 || CONFIG_CPU == PP5022
1124 /** Slow CPU benefits greatly from building smaller playlists
1125 On the iPod Mini 2nd gen, creating a playlist of 2000 entries takes around 10 seconds */
1126 2000,
1127#elif MEMORYSIZE > 1
1124 10000, 1128 10000,
1125#else 1129#else
1126 400, 1130 400,
@@ -1319,6 +1323,8 @@ const struct settings_list settings[] = {
1319#endif 1323#endif
1320 OFFON_SETTING(0, offset_out_of_view, LANG_SCREEN_SCROLL_VIEW, 1324 OFFON_SETTING(0, offset_out_of_view, LANG_SCREEN_SCROLL_VIEW,
1321 false, "Screen Scrolls Out Of View", NULL), 1325 false, "Screen Scrolls Out Of View", NULL),
1326 OFFON_SETTING(0, disable_mainmenu_scrolling, LANG_DISABLE_MAINMENU_SCROLLING,
1327 false, "Disable main menu scrolling", NULL),
1322 INT_SETTING(F_PADTITLE, scroll_step, LANG_SCROLL_STEP, 6, "scroll step", 1328 INT_SETTING(F_PADTITLE, scroll_step, LANG_SCROLL_STEP, 6, "scroll step",
1323 UNIT_PIXEL, 1, LCD_WIDTH, 1, NULL, NULL, lcd_scroll_step), 1329 UNIT_PIXEL, 1, LCD_WIDTH, 1, NULL, NULL, lcd_scroll_step),
1324 INT_SETTING(F_PADTITLE, screen_scroll_step, LANG_SCREEN_SCROLL_STEP, 16, 1330 INT_SETTING(F_PADTITLE, screen_scroll_step, LANG_SCREEN_SCROLL_STEP, 16,
@@ -1415,9 +1421,11 @@ const struct settings_list settings[] = {
1415 OFFON_SETTING(0,playlist_viewer_indices,LANG_SHOW_INDICES,true, 1421 OFFON_SETTING(0,playlist_viewer_indices,LANG_SHOW_INDICES,true,
1416 "playlist viewer indices",NULL), 1422 "playlist viewer indices",NULL),
1417 CHOICE_SETTING(0, playlist_viewer_track_display, LANG_TRACK_DISPLAY, 0, 1423 CHOICE_SETTING(0, playlist_viewer_track_display, LANG_TRACK_DISPLAY, 0,
1418 "playlist viewer track display","track name,full path", 1424 "playlist viewer track display",
1419 NULL, 2, ID2P(LANG_DISPLAY_TRACK_NAME_ONLY), 1425 "track name,full path,title and album from tags,title from tags",
1420 ID2P(LANG_DISPLAY_FULL_PATH)), 1426 NULL, 4, ID2P(LANG_DISPLAY_TRACK_NAME_ONLY),
1427 ID2P(LANG_DISPLAY_FULL_PATH),ID2P(LANG_DISPLAY_TITLEALBUM_FROMTAGS),
1428 ID2P(LANG_DISPLAY_TITLE_FROMTAGS)),
1421 CHOICE_SETTING(0, recursive_dir_insert, LANG_RECURSE_DIRECTORY , RECURSE_ON, 1429 CHOICE_SETTING(0, recursive_dir_insert, LANG_RECURSE_DIRECTORY , RECURSE_ON,
1422 "recursive directory insert", off_on_ask, NULL , 3 , 1430 "recursive directory insert", off_on_ask, NULL , 3 ,
1423 ID2P(LANG_OFF), ID2P(LANG_ON), ID2P(LANG_ASK)), 1431 ID2P(LANG_OFF), ID2P(LANG_ON), ID2P(LANG_ASK)),
@@ -1852,7 +1860,7 @@ const struct settings_list settings[] = {
1852 true, "warn when erasing dynamic playlist",NULL), 1860 true, "warn when erasing dynamic playlist",NULL),
1853 OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE, 1861 OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE,
1854 true, "keep current track when replacing playlist",NULL), 1862 true, "keep current track when replacing playlist",NULL),
1855 OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, false, 1863 OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, true,
1856 "show shuffled adding options", NULL), 1864 "show shuffled adding options", NULL),
1857 CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0, 1865 CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0,
1858 "show queue options", "off,on,in submenu", 1866 "show queue options", "off,on,in submenu",
@@ -1883,7 +1891,7 @@ const struct settings_list settings[] = {
1883#ifdef HAVE_BACKLIGHT 1891#ifdef HAVE_BACKLIGHT
1884 CHOICE_SETTING(0, backlight_on_button_hold, 1892 CHOICE_SETTING(0, backlight_on_button_hold,
1885 LANG_BACKLIGHT_ON_BUTTON_HOLD, 1893 LANG_BACKLIGHT_ON_BUTTON_HOLD,
1886#ifdef HAS_BUTTON_HOLD 1894#ifdef HAS_BUTTON_HOLD
1887 1, 1895 1,
1888#else 1896#else
1889 0, 1897 0,
@@ -2217,6 +2225,8 @@ const struct settings_list settings[] = {
2217 VIEWPORT_SETTING(remote_ui_vp_config, "remote ui viewport"), 2225 VIEWPORT_SETTING(remote_ui_vp_config, "remote ui viewport"),
2218#endif 2226#endif
2219 2227
2228 TEXT_SETTING(F_THEMESETTING, player_name, "player name", "", NULL, NULL),
2229
2220#ifdef HAVE_MORSE_INPUT 2230#ifdef HAVE_MORSE_INPUT
2221 OFFON_SETTING(0, morse_input, LANG_MORSE_INPUT, false, "morse input", NULL), 2231 OFFON_SETTING(0, morse_input, LANG_MORSE_INPUT, false, "morse input", NULL),
2222#endif 2232#endif
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index 6eda05ec44..6baa6e1328 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -176,20 +176,21 @@
176 176
177# Define the title of the main menu 177# Define the title of the main menu
178%menu_start "main" "Database" 178%menu_start "main" "Database"
179"Artist" -> canonicalartist -> album -> title = "fmt_title"
180"Album Artist" -> albumartist -> album -> title = "fmt_title" 179"Album Artist" -> albumartist -> album -> title = "fmt_title"
180"Artist" -> canonicalartist -> album -> title = "fmt_title"
181"Album" -> album -> title = "fmt_title" 181"Album" -> album -> title = "fmt_title"
182"Genre" -> genre -> canonicalartist -> album -> title = "fmt_title" 182"Genre" -> genre -> canonicalartist -> album -> title = "fmt_title"
183"Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title"
183"Composer" -> composer -> album -> title = "fmt_title" 184"Composer" -> composer -> album -> title = "fmt_title"
185"A to Z" ==> "a2z"
184"Track" ==> "track" 186"Track" ==> "track"
185"Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title" 187"Shuffle Songs" ~> title = "fmt_title"
188"Search" ==> "search"
186"User Rating" -> rating -> title = "fmt_title" 189"User Rating" -> rating -> title = "fmt_title"
187"Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title" 190"Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title"
188"A to Z..." ==> "a2z" 191"History" ==> "runtime"
189"History..." ==> "runtime" 192"Same as current" ==> "same"
190"Same as current..." ==> "same" 193"Custom view" ==> "custom"
191"Search..." ==> "search"
192"Custom view..." ==> "custom"
193 194
194# And finally set main menu as our root menu 195# And finally set main menu as our root menu
195%root_menu "main" 196%root_menu "main"
diff --git a/apps/tagtree.c b/apps/tagtree.c
index ea2a756a46..1a49936f45 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -56,6 +56,8 @@
56#include "playback.h" 56#include "playback.h"
57#include "strnatcmp.h" 57#include "strnatcmp.h"
58#include "panic.h" 58#include "panic.h"
59#include "onplay.h"
60#include "plugin.h"
59 61
60#define str_or_empty(x) (x ? x : "(NULL)") 62#define str_or_empty(x) (x ? x : "(NULL)")
61 63
@@ -64,6 +66,10 @@
64 66
65static int tagtree_play_folder(struct tree_context* c); 67static int tagtree_play_folder(struct tree_context* c);
66 68
69/* reuse of tagtree data after tagtree_play_folder() */
70static uint32_t loaded_entries_crc = 0;
71
72
67/* this needs to be same size as struct entry (tree.h) and name needs to be 73/* this needs to be same size as struct entry (tree.h) and name needs to be
68 * the first; so that they're compatible enough to walk arrays of both 74 * the first; so that they're compatible enough to walk arrays of both
69 * derefencing the name member*/ 75 * derefencing the name member*/
@@ -71,6 +77,7 @@ struct tagentry {
71 char* name; 77 char* name;
72 int newtable; 78 int newtable;
73 int extraseek; 79 int extraseek;
80 int customaction;
74}; 81};
75 82
76static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); 83static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
@@ -78,10 +85,10 @@ static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
78#define SEARCHSTR_SIZE 256 85#define SEARCHSTR_SIZE 256
79 86
80enum table { 87enum table {
81 ROOT = 1, 88 TABLE_ROOT = 1,
82 NAVIBROWSE, 89 TABLE_NAVIBROWSE,
83 ALLSUBENTRIES, 90 TABLE_ALLSUBENTRIES,
84 PLAYTRACK, 91 TABLE_PLAYTRACK,
85}; 92};
86 93
87static const struct id3_to_search_mapping { 94static const struct id3_to_search_mapping {
@@ -108,6 +115,7 @@ enum variables {
108 menu_next, 115 menu_next,
109 menu_load, 116 menu_load,
110 menu_reload, 117 menu_reload,
118 menu_shuffle_songs,
111}; 119};
112 120
113/* Capacity 10 000 entries (for example 10k different artists) */ 121/* Capacity 10 000 entries (for example 10k different artists) */
@@ -276,6 +284,20 @@ static struct buflib_callbacks ops = {
276 .shrink_callback = NULL, 284 .shrink_callback = NULL,
277}; 285};
278 286
287static uint32_t tagtree_data_crc(struct tree_context* c)
288{
289 char* buf;
290 uint32_t crc;
291 buf = core_get_data(tagtree_handle); /* data for the search clauses etc */
292 crc = crc_32(buf, tagtree_buf_used, c->dirlength);
293 buf = core_get_data(c->cache.name_buffer_handle); /* names */
294 crc = crc_32(buf, c->cache.name_buffer_size, crc);
295 buf = core_get_data(c->cache.entries_handle); /* tagentries */
296 crc = crc_32(buf, c->cache.max_entries * sizeof(struct tagentry), crc);
297 logf("%s 0x%x", __func__, crc);
298 return crc;
299}
300
279static void* tagtree_alloc(size_t size) 301static void* tagtree_alloc(size_t size)
280{ 302{
281 size = ALIGN_UP(size, sizeof(void*)); 303 size = ALIGN_UP(size, sizeof(void*));
@@ -338,6 +360,7 @@ static int get_tag(int *tag)
338 TAG_MATCH("Pm", tag_virt_playtime_min), 360 TAG_MATCH("Pm", tag_virt_playtime_min),
339 TAG_MATCH("Ps", tag_virt_playtime_sec), 361 TAG_MATCH("Ps", tag_virt_playtime_sec),
340 TAG_MATCH("->", menu_next), 362 TAG_MATCH("->", menu_next),
363 TAG_MATCH("~>", menu_shuffle_songs),
341 364
342 TAG_MATCH("==>", menu_load), 365 TAG_MATCH("==>", menu_load),
343 366
@@ -820,7 +843,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
820 return true; 843 return true;
821 } 844 }
822 845
823 if (entry->type != menu_next) 846 if (entry->type != menu_next && entry->type != menu_shuffle_songs)
824 return false; 847 return false;
825 848
826 while (inst->tagorder_count < MAX_TAGS) 849 while (inst->tagorder_count < MAX_TAGS)
@@ -847,7 +870,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
847 870
848 inst->tagorder_count++; 871 inst->tagorder_count++;
849 872
850 if (get_tag(&type) <= 0 || type != menu_next) 873 if (get_tag(&type) <= 0 || (type != menu_next && type != menu_shuffle_songs))
851 break; 874 break;
852 } 875 }
853 876
@@ -1245,6 +1268,7 @@ static void tagtree_unload(struct tree_context *c)
1245 dptr->name = NULL; 1268 dptr->name = NULL;
1246 dptr->newtable = 0; 1269 dptr->newtable = 0;
1247 dptr->extraseek = 0; 1270 dptr->extraseek = 0;
1271 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1248 dptr++; 1272 dptr++;
1249 } 1273 }
1250 } 1274 }
@@ -1452,9 +1476,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1452#else 1476#else
1453 true 1477 true
1454#endif 1478#endif
1455 , 0); 1479 , 0, 0, 0);
1456 1480
1457 if (c->currtable == ALLSUBENTRIES) 1481 if (c->currtable == TABLE_ALLSUBENTRIES)
1458 { 1482 {
1459 tag = tag_title; 1483 tag = tag_title;
1460 level--; 1484 level--;
@@ -1544,17 +1568,19 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1544 { 1568 {
1545 if (offset == 0) 1569 if (offset == 0)
1546 { 1570 {
1547 dptr->newtable = ALLSUBENTRIES; 1571 dptr->newtable = TABLE_ALLSUBENTRIES;
1548 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS); 1572 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
1573 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1549 dptr++; 1574 dptr++;
1550 current_entry_count++; 1575 current_entry_count++;
1551 special_entry_count++; 1576 special_entry_count++;
1552 } 1577 }
1553 if (offset <= 1) 1578 if (offset <= 1)
1554 { 1579 {
1555 dptr->newtable = NAVIBROWSE; 1580 dptr->newtable = TABLE_NAVIBROWSE;
1556 dptr->name = str(LANG_TAGNAVI_RANDOM); 1581 dptr->name = str(LANG_TAGNAVI_RANDOM);
1557 dptr->extraseek = -1; 1582 dptr->extraseek = -1;
1583 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1558 dptr++; 1584 dptr++;
1559 current_entry_count++; 1585 current_entry_count++;
1560 special_entry_count++; 1586 special_entry_count++;
@@ -1568,14 +1594,15 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1568 if (total_count++ < offset) 1594 if (total_count++ < offset)
1569 continue; 1595 continue;
1570 1596
1571 dptr->newtable = NAVIBROWSE; 1597 dptr->newtable = TABLE_NAVIBROWSE;
1572 if (tag == tag_title || tag == tag_filename) 1598 if (tag == tag_title || tag == tag_filename)
1573 { 1599 {
1574 dptr->newtable = PLAYTRACK; 1600 dptr->newtable = TABLE_PLAYTRACK;
1575 dptr->extraseek = tcs.idx_id; 1601 dptr->extraseek = tcs.idx_id;
1576 } 1602 }
1577 else 1603 else
1578 dptr->extraseek = tcs.result_seek; 1604 dptr->extraseek = tcs.result_seek;
1605 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1579 1606
1580 fmt = NULL; 1607 fmt = NULL;
1581 /* Check the format */ 1608 /* Check the format */
@@ -1676,7 +1703,7 @@ entry_skip_formatter:
1676 1703
1677 if (init) 1704 if (init)
1678 { 1705 {
1679 if (!show_search_progress(false, total_count)) 1706 if (!show_search_progress(false, total_count, 0, 0))
1680 { /* user aborted */ 1707 { /* user aborted */
1681 tagcache_search_finish(&tcs); 1708 tagcache_search_finish(&tcs);
1682 tree_unlock_cache(c); 1709 tree_unlock_cache(c);
@@ -1710,7 +1737,7 @@ entry_skip_formatter:
1710 1737
1711 while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz)) 1738 while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
1712 { 1739 {
1713 if (!show_search_progress(false, total_count)) 1740 if (!show_search_progress(false, total_count, 0, 0))
1714 break; 1741 break;
1715 total_count++; 1742 total_count++;
1716 } 1743 }
@@ -1758,7 +1785,7 @@ static int load_root(struct tree_context *c)
1758 int i; 1785 int i;
1759 1786
1760 tc = c; 1787 tc = c;
1761 c->currtable = ROOT; 1788 c->currtable = TABLE_ROOT;
1762 if (c->dirlevel == 0) 1789 if (c->dirlevel == 0)
1763 c->currextra = rootmenu; 1790 c->currextra = rootmenu;
1764 1791
@@ -1775,13 +1802,21 @@ static int load_root(struct tree_context *c)
1775 switch (menu->items[i]->type) 1802 switch (menu->items[i]->type)
1776 { 1803 {
1777 case menu_next: 1804 case menu_next:
1778 dptr->newtable = NAVIBROWSE; 1805 dptr->newtable = TABLE_NAVIBROWSE;
1779 dptr->extraseek = i; 1806 dptr->extraseek = i;
1807 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1780 break; 1808 break;
1781 1809
1782 case menu_load: 1810 case menu_load:
1783 dptr->newtable = ROOT; 1811 dptr->newtable = TABLE_ROOT;
1784 dptr->extraseek = menu->items[i]->link; 1812 dptr->extraseek = menu->items[i]->link;
1813 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1814 break;
1815
1816 case menu_shuffle_songs:
1817 dptr->newtable = TABLE_NAVIBROWSE;
1818 dptr->extraseek = i;
1819 dptr->customaction = ONPLAY_CUSTOMACTION_SHUFFLE_SONGS;
1785 break; 1820 break;
1786 } 1821 }
1787 1822
@@ -1796,6 +1831,8 @@ static int load_root(struct tree_context *c)
1796 1831
1797int tagtree_load(struct tree_context* c) 1832int tagtree_load(struct tree_context* c)
1798{ 1833{
1834 logf( "%s", __func__);
1835
1799 int count; 1836 int count;
1800 int table = c->currtable; 1837 int table = c->currtable;
1801 1838
@@ -1804,20 +1841,32 @@ int tagtree_load(struct tree_context* c)
1804 if (!table) 1841 if (!table)
1805 { 1842 {
1806 c->dirfull = false; 1843 c->dirfull = false;
1807 table = ROOT; 1844 table = TABLE_ROOT;
1808 c->currtable = table; 1845 c->currtable = table;
1809 c->currextra = rootmenu; 1846 c->currextra = rootmenu;
1810 } 1847 }
1811 1848
1812 switch (table) 1849 switch (table)
1813 { 1850 {
1814 case ROOT: 1851 case TABLE_ROOT:
1852 logf( "root...");
1815 count = load_root(c); 1853 count = load_root(c);
1816 break; 1854 break;
1817 1855
1818 case ALLSUBENTRIES: 1856 case TABLE_ALLSUBENTRIES:
1819 case NAVIBROWSE: 1857 case TABLE_NAVIBROWSE:
1820 logf("navibrowse..."); 1858 logf("navibrowse...");
1859
1860 if (loaded_entries_crc != 0)
1861 {
1862 if (loaded_entries_crc == tagtree_data_crc(c))
1863 {
1864 count = c->dirlength;
1865 logf("Reusing %d entries", count);
1866 break;
1867 }
1868 }
1869
1821 cpu_boost(true); 1870 cpu_boost(true);
1822 count = retrieve_entries(c, 0, true); 1871 count = retrieve_entries(c, 0, true);
1823 cpu_boost(false); 1872 cpu_boost(false);
@@ -1828,6 +1877,8 @@ int tagtree_load(struct tree_context* c)
1828 return -1; 1877 return -1;
1829 } 1878 }
1830 1879
1880 loaded_entries_crc = 0;
1881
1831 if (count < 0) 1882 if (count < 0)
1832 { 1883 {
1833 if (count != RELOAD_TAGTREE) 1884 if (count != RELOAD_TAGTREE)
@@ -1860,6 +1911,8 @@ int tagtree_load(struct tree_context* c)
1860 */ 1911 */
1861int tagtree_enter(struct tree_context* c, bool is_visible) 1912int tagtree_enter(struct tree_context* c, bool is_visible)
1862{ 1913{
1914 logf( "%s", __func__);
1915
1863 int rc = 0; 1916 int rc = 0;
1864 struct tagentry *dptr; 1917 struct tagentry *dptr;
1865 struct mp3entry *id3; 1918 struct mp3entry *id3;
@@ -1921,16 +1974,16 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
1921 core_pin(tagtree_handle); 1974 core_pin(tagtree_handle);
1922 1975
1923 switch (c->currtable) { 1976 switch (c->currtable) {
1924 case ROOT: 1977 case TABLE_ROOT:
1925 c->currextra = newextra; 1978 c->currextra = newextra;
1926 1979
1927 if (newextra == ROOT) 1980 if (newextra == TABLE_ROOT)
1928 { 1981 {
1929 menu = menus[seek]; 1982 menu = menus[seek];
1930 c->currextra = seek; 1983 c->currextra = seek;
1931 } 1984 }
1932 1985
1933 else if (newextra == NAVIBROWSE) 1986 else if (newextra == TABLE_NAVIBROWSE)
1934 { 1987 {
1935 int i, j; 1988 int i, j;
1936 1989
@@ -2005,9 +2058,9 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
2005 2058
2006 break; 2059 break;
2007 2060
2008 case NAVIBROWSE: 2061 case TABLE_NAVIBROWSE:
2009 case ALLSUBENTRIES: 2062 case TABLE_ALLSUBENTRIES:
2010 if (newextra == PLAYTRACK) 2063 if (newextra == TABLE_PLAYTRACK)
2011 { 2064 {
2012 adjust_selection = false; 2065 adjust_selection = false;
2013 2066
@@ -2059,6 +2112,7 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
2059/* Exits current database menu or table */ 2112/* Exits current database menu or table */
2060void tagtree_exit(struct tree_context* c, bool is_visible) 2113void tagtree_exit(struct tree_context* c, bool is_visible)
2061{ 2114{
2115 logf( "%s", __func__);
2062 if (is_visible) /* update selection history only for user-selected items */ 2116 if (is_visible) /* update selection history only for user-selected items */
2063 { 2117 {
2064 if (c->selected_item != selected_item_history[c->dirlevel]) 2118 if (c->selected_item != selected_item_history[c->dirlevel])
@@ -2102,15 +2156,54 @@ int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
2102 return 0; 2156 return 0;
2103} 2157}
2104 2158
2159int tagtree_get_custom_action(struct tree_context* c)
2160{
2161 return tagtree_get_entry(c, c->selected_item)->customaction;
2162}
2163
2164static void swap_array_bool(bool *a, bool *b)
2165{
2166 bool temp = *a;
2167 *a = *b;
2168 *b = temp;
2169}
2170
2171/**
2172 * Randomly shuffle an array using the Fisher-Yates algorithm :
2173 * https://en.wikipedia.org/wiki/Random_permutation
2174 * This algorithm has a linear complexity.
2175 * Don't forget to srand before call to use it with a relevant seed.
2176 */
2177static bool* fill_random_playlist_indexes(bool *bool_array, size_t arr_sz,
2178 size_t track_count, size_t max_slots)
2179{
2180 size_t i;
2181 if (track_count * sizeof(bool) > arr_sz || max_slots > track_count)
2182 return NULL;
2183
2184 for (i = 0; i < arr_sz; i++) /* fill max_slots with TRUE */
2185 bool_array[i] = i < max_slots;
2186
2187 /* shuffle bool array */
2188 for (i = track_count - 1; i > 0; i--)
2189 {
2190 int j = rand() % (i + 1);
2191 swap_array_bool(&bool_array[i], &bool_array[j]);
2192 }
2193 return bool_array;
2194}
2105 2195
2106static bool insert_all_playlist(struct tree_context *c, 2196static bool insert_all_playlist(struct tree_context *c,
2107 const char* playlist, bool new_playlist, 2197 const char* playlist, bool new_playlist,
2108 int position, bool queue) 2198 int position, bool queue)
2109{ 2199{
2110 struct tagcache_search tcs; 2200 struct tagcache_search tcs;
2111 int i, n; 2201 int n;
2112 int fd = -1; 2202 int fd = -1;
2113 unsigned long last_tick; 2203 unsigned long last_tick;
2204 int slots_remaining = 0;
2205 bool fill_randomly = false;
2206 bool *rand_bool_array = NULL;
2114 char buf[MAX_PATH]; 2207 char buf[MAX_PATH];
2115 2208
2116 cpu_boost(true); 2209 cpu_boost(true);
@@ -2148,40 +2241,114 @@ static bool insert_all_playlist(struct tree_context *c,
2148 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */ 2241 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */
2149 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 2242 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
2150 n = c->filesindir; 2243 n = c->filesindir;
2151 for (i = 0; i < n; i++) 2244
2245 if (playlist == NULL)
2152 { 2246 {
2247 int max_playlist_size = playlist_get_current()->max_playlist_size;
2248 slots_remaining = max_playlist_size - playlist_get_current()->amount;
2249 if (slots_remaining <= 0)
2250 {
2251 logf("Playlist has no space remaining");
2252 cpu_boost(false);
2253 return false;
2254 }
2153 2255
2154 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2256 fill_randomly = n > slots_remaining;
2257
2258 if (fill_randomly)
2259 {
2260 srand(current_tick);
2261 size_t bufsize = 0;
2262 bool *buffer = (bool *) plugin_get_buffer(&bufsize);
2263 rand_bool_array = fill_random_playlist_indexes(buffer, bufsize,
2264 n, slots_remaining);
2265
2266 talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true);
2267 splashf(HZ * 2, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY),
2268 slots_remaining);
2269 }
2270 }
2271
2272 bool exit_loop_now = false;
2273 for (int i = 0; i < n; i++)
2274 {
2155 if (TIME_AFTER(current_tick, last_tick + HZ/4)) 2275 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2156 { 2276 {
2277 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
2157 if (action_userabort(TIMEOUT_NOBLOCK)) 2278 if (action_userabort(TIMEOUT_NOBLOCK))
2279 {
2280 exit_loop_now = true;
2158 break; 2281 break;
2282 }
2159 last_tick = current_tick; 2283 last_tick = current_tick;
2160 } 2284 }
2161 2285
2162 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, 2286 if (playlist == NULL)
2163 tcs.type, buf, sizeof buf))
2164 { 2287 {
2165 continue; 2288 if (fill_randomly)
2289 {
2290 int remaining_tracks = n - i;
2291 if (remaining_tracks > slots_remaining)
2292 {
2293 if (rand_bool_array)
2294 {
2295 /* Skip the track if rand_bool_array[i] is FALSE */
2296 if (!rand_bool_array[i])
2297 continue;
2298 }
2299 else
2300 {
2301 /* Generate random value between 0 and remaining_tracks - 1 */
2302 int selrange = RAND_MAX / remaining_tracks; /* Improve distribution */
2303 int random;
2304
2305 for (int r = 0; r < 0x0FFF; r++) /* limit loops */
2306 {
2307 random = rand() / selrange;
2308 if (random < remaining_tracks)
2309 break;
2310 else
2311 random = 0;
2312 }
2313 /* Skip the track if random >= slots_remaining */
2314 if (random >= slots_remaining)
2315 continue;
2316 }
2317 }
2318 }
2166 } 2319 }
2167 2320
2321 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, tcs.type, buf, sizeof buf))
2322 continue;
2323
2168 if (playlist == NULL) 2324 if (playlist == NULL)
2169 { 2325 {
2170 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) 2326 if (fill_randomly)
2171 { 2327 {
2328 if (--slots_remaining <= 0)
2329 {
2330 exit_loop_now = true;
2331 break;
2332 }
2333 }
2334
2335 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) {
2172 logf("playlist_insert_track failed"); 2336 logf("playlist_insert_track failed");
2337 exit_loop_now = true;
2173 break; 2338 break;
2174 } 2339 }
2175 } 2340 }
2176 else if (fdprintf(fd, "%s\n", buf) <= 0) 2341 else if (fdprintf(fd, "%s\n", buf) <= 0)
2177 break; 2342 {
2178 2343 exit_loop_now = true;
2344 break;
2345 }
2179 yield(); 2346 yield();
2180
2181 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST) 2347 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
2182 {
2183 position = PLAYLIST_INSERT; 2348 position = PLAYLIST_INSERT;
2184 } 2349
2350 if (exit_loop_now)
2351 break;
2185 } 2352 }
2186 if (playlist == NULL) 2353 if (playlist == NULL)
2187 playlist_sync(NULL); 2354 playlist_sync(NULL);
@@ -2196,14 +2363,14 @@ static bool insert_all_playlist(struct tree_context *c,
2196static bool goto_allsubentries(int newtable) 2363static bool goto_allsubentries(int newtable)
2197{ 2364{
2198 int i = 0; 2365 int i = 0;
2199 while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES)) 2366 while (i < 2 && (newtable == TABLE_NAVIBROWSE || newtable == TABLE_ALLSUBENTRIES))
2200 { 2367 {
2201 tagtree_enter(tc, false); 2368 tagtree_enter(tc, false);
2202 tagtree_load(tc); 2369 tagtree_load(tc);
2203 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2370 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2204 i++; 2371 i++;
2205 } 2372 }
2206 return (newtable == PLAYTRACK); 2373 return (newtable == TABLE_PLAYTRACK);
2207} 2374}
2208 2375
2209static void reset_tc_to_prev(int dirlevel, int selected_item) 2376static void reset_tc_to_prev(int dirlevel, int selected_item)
@@ -2229,11 +2396,11 @@ static bool tagtree_insert_selection(int position, bool queue,
2229#else 2396#else
2230 true 2397 true
2231#endif 2398#endif
2232 , 0); 2399 , 0, 0, 0);
2233 2400
2234 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2401 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2235 2402
2236 if (newtable == PLAYTRACK) /* Insert a single track? */ 2403 if (newtable == TABLE_PLAYTRACK) /* Insert a single track? */
2237 { 2404 {
2238 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 2405 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
2239 return false; 2406 return false;
@@ -2342,6 +2509,7 @@ int tagtree_add_to_playlist(const char* playlist, bool new_playlist)
2342 2509
2343static int tagtree_play_folder(struct tree_context* c) 2510static int tagtree_play_folder(struct tree_context* c)
2344{ 2511{
2512 logf( "%s", __func__);
2345 int start_index = c->selected_item; 2513 int start_index = c->selected_item;
2346 2514
2347 if (playlist_create(NULL, NULL) < 0) 2515 if (playlist_create(NULL, NULL) < 0)
@@ -2353,14 +2521,26 @@ static int tagtree_play_folder(struct tree_context* c)
2353 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false)) 2521 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false))
2354 return -2; 2522 return -2;
2355 2523
2524 int n = c->filesindir;
2525 bool has_playlist_been_randomized = n > playlist_get_current()->max_playlist_size;
2526 if (has_playlist_been_randomized)
2527 {
2528 /* We need to recalculate the start index based on a percentage to put the user
2529 around its desired start position and avoid out of bounds */
2530
2531 int percentage_start_index = 100 * start_index / n;
2532 start_index = percentage_start_index * playlist_get_current()->amount / 100;
2533 }
2534
2356 if (global_settings.playlist_shuffle) 2535 if (global_settings.playlist_shuffle)
2357 { 2536 {
2358 start_index = playlist_shuffle(current_tick, c->selected_item); 2537 start_index = playlist_shuffle(current_tick, start_index);
2359 if (!global_settings.play_selected) 2538 if (!global_settings.play_selected)
2360 start_index = 0; 2539 start_index = 0;
2361 } 2540 }
2362 2541
2363 playlist_start(start_index, 0, 0); 2542 playlist_start(start_index, 0, 0);
2543 loaded_entries_crc = tagtree_data_crc(c); /* save crc in case we return */
2364 return 0; 2544 return 0;
2365} 2545}
2366 2546
@@ -2403,11 +2583,11 @@ char *tagtree_get_title(struct tree_context* c)
2403{ 2583{
2404 switch (c->currtable) 2584 switch (c->currtable)
2405 { 2585 {
2406 case ROOT: 2586 case TABLE_ROOT:
2407 return menu->title; 2587 return menu->title;
2408 2588
2409 case NAVIBROWSE: 2589 case TABLE_NAVIBROWSE:
2410 case ALLSUBENTRIES: 2590 case TABLE_ALLSUBENTRIES:
2411 return current_title[c->currextra]; 2591 return current_title[c->currextra];
2412 } 2592 }
2413 2593
@@ -2419,7 +2599,7 @@ int tagtree_get_attr(struct tree_context* c)
2419 int attr = -1; 2599 int attr = -1;
2420 switch (c->currtable) 2600 switch (c->currtable)
2421 { 2601 {
2422 case NAVIBROWSE: 2602 case TABLE_NAVIBROWSE:
2423 if (csi->tagorder[c->currextra] == tag_title 2603 if (csi->tagorder[c->currextra] == tag_title
2424 || csi->tagorder[c->currextra] == tag_virt_basename) 2604 || csi->tagorder[c->currextra] == tag_virt_basename)
2425 attr = FILE_ATTR_AUDIO; 2605 attr = FILE_ATTR_AUDIO;
@@ -2427,7 +2607,7 @@ int tagtree_get_attr(struct tree_context* c)
2427 attr = ATTR_DIRECTORY; 2607 attr = ATTR_DIRECTORY;
2428 break; 2608 break;
2429 2609
2430 case ALLSUBENTRIES: 2610 case TABLE_ALLSUBENTRIES:
2431 attr = FILE_ATTR_AUDIO; 2611 attr = FILE_ATTR_AUDIO;
2432 break; 2612 break;
2433 2613
diff --git a/apps/tagtree.h b/apps/tagtree.h
index 39ab545bd0..a57a5c2f80 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -45,6 +45,7 @@ char *tagtree_get_title(struct tree_context* c);
45int tagtree_get_attr(struct tree_context* c); 45int tagtree_get_attr(struct tree_context* c);
46int tagtree_get_icon(struct tree_context* c); 46int tagtree_get_icon(struct tree_context* c);
47int tagtree_get_filename(struct tree_context* c, char *buf, int buflen); 47int tagtree_get_filename(struct tree_context* c, char *buf, int buflen);
48int tagtree_get_custom_action(struct tree_context* c);
48bool tagtree_get_subentry_filename(char *buf, size_t bufsize); 49bool tagtree_get_subentry_filename(char *buf, size_t bufsize);
49bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name)); 50bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name));
50 51
diff --git a/apps/talk.c b/apps/talk.c
index 2e73001c46..b5622ad6d1 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -44,6 +44,7 @@
44#include "debug.h" 44#include "debug.h"
45#include "panic.h" 45#include "panic.h"
46#include "misc.h" /* time_split_units() */ 46#include "misc.h" /* time_split_units() */
47#include "mv.h"
47 48
48/***************** Constants *****************/ 49/***************** Constants *****************/
49 50
@@ -776,7 +777,7 @@ static int _talk_spell(const char* spell, size_t len, bool enqueue)
776 talk_id(VOICE_DOT, true); 777 talk_id(VOICE_DOT, true);
777 else if (c == ' ') 778 else if (c == ' ')
778 talk_id(VOICE_PAUSE, true); 779 talk_id(VOICE_PAUSE, true);
779 else if (c == '/') 780 else if (c == PATH_SEPCH)
780 talk_id(VOICE_CHAR_SLASH, true); 781 talk_id(VOICE_CHAR_SLASH, true);
781 782
782 while (QUEUE_LEVEL == QUEUE_SIZE - 1) /* queue full - busy loop */ 783 while (QUEUE_LEVEL == QUEUE_SIZE - 1) /* queue full - busy loop */
@@ -1139,12 +1140,12 @@ int talk_file(const char *root, const char *dir, const char *file,
1139{ 1140{
1140 char buf[MAX_PATH]; 1141 char buf[MAX_PATH];
1141 const char *fmt = "%s%s%s%s%s"; 1142 const char *fmt = "%s%s%s%s%s";
1142 /* Does root end with a slash */ 1143 /* Does root end with a slash? */
1143 if(root && root[0] && root[strlen(root)-1] != '/') 1144 if(root && root[0] && root[strlen(root)-1] != PATH_SEPCH)
1144 fmt = "%s/%s%s%s%s"; 1145 fmt = "%s" PATH_SEPSTR "%s%s%s%s";
1145 snprintf(buf, MAX_PATH, fmt, 1146 snprintf(buf, MAX_PATH, fmt,
1146 root ? root : "", 1147 root ? root : "",
1147 dir ? dir : "", dir ? "/" : "", 1148 dir ? dir : "", dir ? PATH_SEPSTR : "",
1148 file ? file : "", 1149 file ? file : "",
1149 ext ? ext : ""); 1150 ext ? ext : "");
1150 return _talk_file(buf, prefix_ids, enqueue); 1151 return _talk_file(buf, prefix_ids, enqueue);
@@ -1167,6 +1168,21 @@ int talk_file_or_spell(const char *dirname, const char *filename,
1167 return 0; 1168 return 0;
1168} 1169}
1169 1170
1171#ifdef HAVE_MULTIVOLUME
1172int talk_volume_id(int volume)
1173{
1174 if (volume == -1)
1175 return 0;
1176
1177 int drive = volume_drive(volume);
1178 // XXX voice "VOLUME" or something like that?
1179
1180 talk_id(drive? LANG_DISK_NAME_MMC : LANG_DISK_NAME_INTERNAL, true);
1181 talk_value(volume, UNIT_INT, true);
1182 return 1;
1183}
1184#endif
1185
1170/* Play a directory's .talk thumbnail, fallback to spelling the filename, or 1186/* Play a directory's .talk thumbnail, fallback to spelling the filename, or
1171 go straight to spelling depending on settings. */ 1187 go straight to spelling depending on settings. */
1172int talk_dir_or_spell(const char* dirname, 1188int talk_dir_or_spell(const char* dirname,
@@ -1174,13 +1190,14 @@ int talk_dir_or_spell(const char* dirname,
1174{ 1190{
1175 if (global_settings.talk_dir_clip) 1191 if (global_settings.talk_dir_clip)
1176 { /* .talk clips enabled */ 1192 { /* .talk clips enabled */
1177 if(talk_file(dirname, NULL, dir_thumbnail_name, NULL, 1193 if (talk_file(dirname, NULL, dir_thumbnail_name, NULL,
1178 prefix_ids, enqueue) >0) 1194 prefix_ids, enqueue) >0)
1179 return 0; 1195 return 0;
1180 } 1196 }
1181 if (global_settings.talk_dir == TALK_SPEAK_SPELL) 1197 if (global_settings.talk_dir == TALK_SPEAK_SPELL) {
1182 /* Either .talk clips disabled or as a fallback */ 1198 /* Either .talk clips disabled or as a fallback */
1183 return talk_spell_basename(dirname, prefix_ids, enqueue); 1199 return talk_spell_basename(dirname, prefix_ids, enqueue);
1200 }
1184 return 0; 1201 return 0;
1185} 1202}
1186 1203
@@ -1190,25 +1207,43 @@ int talk_fullpath(const char* path, bool enqueue)
1190{ 1207{
1191 do_enqueue(enqueue); /* cut off all the pending stuff */ 1208 do_enqueue(enqueue); /* cut off all the pending stuff */
1192 1209
1193 if(path[0] != '/') 1210 if(path[0] != PATH_SEPCH)
1194 /* path ought to start with /... */ 1211 /* path ought to start with /... */
1195 return talk_spell(path, true); 1212 return talk_spell(path, true);
1196 talk_id(VOICE_CHAR_SLASH, true); 1213 talk_id(VOICE_CHAR_SLASH, true);
1197 char buf[MAX_PATH]; 1214 char buf[MAX_PATH];
1198 strmemccpy(buf, path, MAX_PATH); 1215 strmemccpy(buf, path, MAX_PATH);
1199 char *start = buf+1; /* start of current component */ 1216 char *start = buf+1; /* start of current component */
1200 char *ptr = strchr(start, '/'); /* end of current component */ 1217 char *ptr = strchr(start, PATH_SEPCH); /* end of current component */
1201 while(ptr) { /* There are more slashes ahead */ 1218 while(ptr) { /* There are more slashes ahead */
1202 /* temporarily poke a NULL at end of component to truncate string */ 1219 /* temporarily poke a NULL at end of component to truncate string */
1203 *ptr = '\0'; 1220 *ptr = '\0';
1204 talk_dir_or_spell(buf, NULL, true); 1221#ifdef HAVE_MULTIVOLUME
1205 *ptr = '/'; /* restore string */ 1222 if (start == buf+1) {
1223 int vol = path_get_volume_id(buf+1);
1224 if (!talk_volume_id(vol))
1225 talk_dir_or_spell(buf, NULL, true);
1226 } else
1227#endif
1228 talk_dir_or_spell(buf, NULL, true);
1229 *ptr = PATH_SEPCH; /* restore string */
1206 talk_id(VOICE_CHAR_SLASH, true); 1230 talk_id(VOICE_CHAR_SLASH, true);
1207 start = ptr+1; /* setup for next component */ 1231 start = ptr+1; /* setup for next component */
1208 ptr = strchr(start, '/'); 1232 ptr = strchr(start, PATH_SEPCH);
1233 }
1234
1235 /* no more slashes, figure out final component */
1236 if (!*start) {
1237 return 1;
1238 }
1239
1240 DIR* dir = opendir(buf);
1241 if (dir) {
1242 closedir(dir);
1243 return talk_dir_or_spell(buf, NULL, true);
1244 } else {
1245 return talk_file_or_spell(NULL, buf, NULL, true);
1209 } 1246 }
1210 /* no more slashes, final component is a filename */
1211 return talk_file_or_spell(NULL, buf, NULL, true);
1212} 1247}
1213 1248
1214/* say a numeric value, this word ordering works for english, 1249/* say a numeric value, this word ordering works for english,
diff --git a/apps/talk.h b/apps/talk.h
index 6139b9ec46..507d5cbed1 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -145,6 +145,10 @@ void talk_fractional(char *tbuf, int value, int unit);
145void talk_time(const struct tm *tm, bool enqueue); 145void talk_time(const struct tm *tm, bool enqueue);
146void talk_date(const struct tm *tm, bool enqueue); 146void talk_date(const struct tm *tm, bool enqueue);
147 147
148#ifdef HAVE_MULTIVOLUME
149int talk_volume_id(int volume);
150#endif
151
148/* speaks hr, min, sec, ms; unit_idx is lowest or base unit of the time value */ 152/* speaks hr, min, sec, ms; unit_idx is lowest or base unit of the time value */
149int talk_time_intervals(long time, int unit_idx, bool enqueue); 153int talk_time_intervals(long time, int unit_idx, bool enqueue);
150 154
diff --git a/apps/tree.c b/apps/tree.c
index d9d23d277a..721fb8c1ef 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -735,6 +735,20 @@ static int dirbrowse(void)
735 oldbutton = button; 735 oldbutton = button;
736 gui_synclist_do_button(&tree_lists, &button); 736 gui_synclist_do_button(&tree_lists, &button);
737 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); 737 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists);
738 int customaction = ONPLAY_NO_CUSTOMACTION;
739 bool do_restore_display = true;
740 #ifdef HAVE_TAGCACHE
741 if (id3db && (button == ACTION_STD_OK || button == ACTION_STD_CONTEXT))
742 {
743 customaction = tagtree_get_custom_action(&tc);
744 if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS)
745 {
746 /* The code to insert shuffled is on the context branch of the switch so we always go here */
747 button = ACTION_STD_CONTEXT;
748 do_restore_display = false;
749 }
750 }
751 #endif
738 switch ( button ) { 752 switch ( button ) {
739 case ACTION_STD_OK: 753 case ACTION_STD_OK:
740 /* nothing to do if no files to display */ 754 /* nothing to do if no files to display */
@@ -773,7 +787,7 @@ static int dirbrowse(void)
773 default: 787 default:
774 break; 788 break;
775 } 789 }
776 restore = true; 790 restore = do_restore_display;
777 break; 791 break;
778 792
779 case ACTION_STD_CANCEL: 793 case ACTION_STD_CANCEL:
@@ -798,12 +812,12 @@ static int dirbrowse(void)
798 if (ft_exit(&tc) == 3) 812 if (ft_exit(&tc) == 3)
799 exit_func = true; 813 exit_func = true;
800 814
801 restore = true; 815 restore = do_restore_display;
802 break; 816 break;
803 817
804 case ACTION_TREE_STOP: 818 case ACTION_TREE_STOP:
805 if (list_stop_handler()) 819 if (list_stop_handler())
806 restore = true; 820 restore = do_restore_display;
807 break; 821 break;
808 822
809 case ACTION_STD_MENU: 823 case ACTION_STD_MENU:
@@ -851,7 +865,7 @@ static int dirbrowse(void)
851 skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); 865 skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
852 } 866 }
853 867
854 restore = true; 868 restore = do_restore_display;
855 break; 869 break;
856 } 870 }
857#endif 871#endif
@@ -872,7 +886,7 @@ static int dirbrowse(void)
872 break; 886 break;
873 887
874 if(!numentries) 888 if(!numentries)
875 onplay_result = onplay(NULL, 0, curr_context, hotkey); 889 onplay_result = onplay(NULL, 0, curr_context, hotkey, customaction);
876 else { 890 else {
877#ifdef HAVE_TAGCACHE 891#ifdef HAVE_TAGCACHE
878 if (id3db) 892 if (id3db)
@@ -902,7 +916,7 @@ static int dirbrowse(void)
902 ft_assemble_path(buf, sizeof(buf), currdir, entry->name); 916 ft_assemble_path(buf, sizeof(buf), currdir, entry->name);
903 917
904 } 918 }
905 onplay_result = onplay(buf, attr, curr_context, hotkey); 919 onplay_result = onplay(buf, attr, curr_context, hotkey, customaction);
906 } 920 }
907 switch (onplay_result) 921 switch (onplay_result)
908 { 922 {
@@ -911,7 +925,7 @@ static int dirbrowse(void)
911 break; 925 break;
912 926
913 case ONPLAY_OK: 927 case ONPLAY_OK:
914 restore = true; 928 restore = do_restore_display;
915 break; 929 break;
916 930
917 case ONPLAY_RELOAD_DIR: 931 case ONPLAY_RELOAD_DIR:
@@ -988,7 +1002,7 @@ static int dirbrowse(void)
988 1002
989 lastfilter = *tc.dirfilter; 1003 lastfilter = *tc.dirfilter;
990 lastsortcase = global_settings.sort_case; 1004 lastsortcase = global_settings.sort_case;
991 restore = true; 1005 restore = do_restore_display;
992 } 1006 }
993 1007
994 if (exit_func) 1008 if (exit_func)
@@ -1005,7 +1019,7 @@ static int dirbrowse(void)
1005 } 1019 }
1006 } 1020 }
1007 } 1021 }
1008 return true; 1022 return GO_TO_ROOT;
1009} 1023}
1010 1024
1011int create_playlist(void) 1025int create_playlist(void)
@@ -1101,12 +1115,13 @@ int rockbox_browse(struct browse_context *browse)
1101 ret_val = dirbrowse(); 1115 ret_val = dirbrowse();
1102 } 1116 }
1103 } 1117 }
1118
1119 tc.is_browsing = false;
1120
1104 backup_count--; 1121 backup_count--;
1105 if (backup_count >= 0) 1122 if (backup_count >= 0)
1106 tc = backups[backup_count]; 1123 tc = backups[backup_count];
1107 1124
1108 tc.is_browsing = false;
1109
1110 return ret_val; 1125 return ret_val;
1111} 1126}
1112 1127
@@ -1251,6 +1266,12 @@ static void say_filetype(int attr)
1251 1266
1252static int ft_play_dirname(char* name) 1267static int ft_play_dirname(char* name)
1253{ 1268{
1269#ifdef HAVE_MULTIVOLUME
1270 int vol = path_get_volume_id(name);
1271 if (talk_volume_id(vol))
1272 return 1;
1273#endif
1274
1254 return talk_file(tc.currdir, name, dir_thumbnail_name, NULL, 1275 return talk_file(tc.currdir, name, dir_thumbnail_name, NULL,
1255 global_settings.talk_filetype ? 1276 global_settings.talk_filetype ?
1256 TALK_IDARRAY(VOICE_DIR) : NULL, 1277 TALK_IDARRAY(VOICE_DIR) : NULL,
diff --git a/apps/tree.h b/apps/tree.h
index d13c75d434..e958bbf109 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -33,6 +33,9 @@ struct entry {
33 char *name; 33 char *name;
34 int attr; /* FAT attributes + file type flags */ 34 int attr; /* FAT attributes + file type flags */
35 unsigned time_write; /* Last write time */ 35 unsigned time_write; /* Last write time */
36 #ifdef HAVE_TAGCACHE
37 int customaction; /* db use */
38 #endif
36}; 39};
37 40
38#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ 41#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */