1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
662 &li.bd_clock_extra_time, 30
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
668 &li.bd_voodoo_collects_diamonds, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &li.bd_voodoo_hurt_kills_player, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
678 &li.bd_voodoo_dies_by_rock, FALSE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
683 &li.bd_voodoo_vanish_by_explosion, TRUE
686 EL_BD_VOODOO_DOLL, -1,
687 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
688 &li.bd_voodoo_penalty_time, 30
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
694 &li.bd_slime_is_predictable, TRUE
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
699 &li.bd_slime_permeability_rate, 100
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
704 &li.bd_slime_permeability_bits_c64, 0
708 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
709 &li.bd_slime_random_seed_c64, -1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_acid_eats_element, EL_BD_SAND
719 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
720 &li.bd_acid_spread_rate, 3
724 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
725 &li.bd_acid_turns_to_element, EL_EMPTY
730 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
731 &li.bd_biter_move_delay, 0
735 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
736 &li.bd_biter_eats_element, EL_BD_DIAMOND
741 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
742 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
746 EL_BD_EXPANDABLE_WALL_ANY, -1,
747 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
748 &li.bd_change_expanding_wall, FALSE
751 // (the following values are related to various game elements)
755 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
756 &li.score[SC_EMERALD], 10
761 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
762 &li.score[SC_DIAMOND], 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
768 &li.score[SC_BUG], 10
773 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
774 &li.score[SC_SPACESHIP], 10
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 &li.score[SC_PACMAN], 10
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 &li.score[SC_NUT], 10
791 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
792 &li.score[SC_DYNAMITE], 10
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 &li.score[SC_KEY], 10
803 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
804 &li.score[SC_PEARL], 10
809 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
810 &li.score[SC_CRYSTAL], 10
813 // (amoeba values used by R'n'D game engine only)
816 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
817 &li.amoeba_content, EL_DIAMOND
821 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
826 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
827 &li.grow_into_diggable, TRUE
829 // (amoeba values used by BD game engine only)
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
833 &li.bd_amoeba_wait_for_hatching, FALSE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
838 &li.bd_amoeba_start_immediately, TRUE
842 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
843 &li.bd_amoeba_2_explode_by_amoeba, TRUE
847 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
848 &li.bd_amoeba_threshold_too_big, 200
852 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
853 &li.bd_amoeba_slow_growth_time, 200
857 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
858 &li.bd_amoeba_slow_growth_rate, 3
862 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
863 &li.bd_amoeba_fast_growth_rate, 25
867 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
868 &li.bd_amoeba_content_too_big, EL_BD_ROCK
872 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
873 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
878 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
879 &li.bd_amoeba_2_threshold_too_big, 200
883 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
884 &li.bd_amoeba_2_slow_growth_time, 200
888 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
889 &li.bd_amoeba_2_slow_growth_rate, 3
893 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
894 &li.bd_amoeba_2_fast_growth_rate, 25
898 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
899 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
903 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
904 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
908 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
909 &li.bd_amoeba_2_content_exploding, EL_EMPTY
913 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
914 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
919 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
920 &li.yamyam_content, EL_ROCK, NULL,
921 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
926 &li.score[SC_YAMYAM], 10
931 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
932 &li.score[SC_ROBOT], 10
936 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
948 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
949 &li.time_magic_wall, 10
954 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
955 &li.game_of_life[0], 2
959 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
960 &li.game_of_life[1], 3
964 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
965 &li.game_of_life[2], 3
969 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
970 &li.game_of_life[3], 3
974 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
975 &li.use_life_bugs, FALSE
980 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
985 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
990 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
995 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1000 EL_TIMEGATE_SWITCH, -1,
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1002 &li.time_timegate, 10
1006 EL_LIGHT_SWITCH_ACTIVE, -1,
1007 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 EL_SHIELD_NORMAL, -1,
1013 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1014 &li.shield_normal_time, 10
1017 EL_SHIELD_NORMAL, -1,
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1019 &li.score[SC_SHIELD], 10
1023 EL_SHIELD_DEADLY, -1,
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1025 &li.shield_deadly_time, 10
1028 EL_SHIELD_DEADLY, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1030 &li.score[SC_SHIELD], 10
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1041 &li.extra_time_score, 10
1045 EL_TIME_ORB_FULL, -1,
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1047 &li.time_orb_time, 10
1050 EL_TIME_ORB_FULL, -1,
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1052 &li.use_time_orb_bug, FALSE
1057 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1058 &li.use_spring_bug, FALSE
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.android_move_time, 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1069 &li.android_clone_time, 10
1072 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1073 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1074 &li.android_clone_element[0], EL_EMPTY, NULL,
1075 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1079 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1080 &li.android_clone_element[0], EL_EMPTY, NULL,
1081 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1087 &li.lenses_score, 10
1091 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1096 EL_EMC_MAGNIFIER, -1,
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1098 &li.magnify_score, 10
1101 EL_EMC_MAGNIFIER, -1,
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1103 &li.magnify_time, 10
1107 EL_EMC_MAGIC_BALL, -1,
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1112 EL_EMC_MAGIC_BALL, -1,
1113 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1114 &li.ball_random, FALSE
1117 EL_EMC_MAGIC_BALL, -1,
1118 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1119 &li.ball_active_initial, FALSE
1122 EL_EMC_MAGIC_BALL, -1,
1123 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1124 &li.ball_content, EL_EMPTY, NULL,
1125 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1129 EL_SOKOBAN_FIELD_EMPTY, -1,
1130 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1131 &li.sb_fields_needed, TRUE
1135 EL_SOKOBAN_OBJECT, -1,
1136 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1137 &li.sb_objects_needed, TRUE
1142 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1143 &li.mm_laser_red, FALSE
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1148 &li.mm_laser_green, FALSE
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1153 &li.mm_laser_blue, TRUE
1158 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1159 &li.df_laser_red, TRUE
1163 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1164 &li.df_laser_green, TRUE
1168 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1169 &li.df_laser_blue, FALSE
1173 EL_MM_FUSE_ACTIVE, -1,
1174 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1175 &li.mm_time_fuse, 25
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1180 &li.mm_time_bomb, 75
1184 EL_MM_GRAY_BALL, -1,
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1186 &li.mm_time_ball, 75
1189 EL_MM_GRAY_BALL, -1,
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1191 &li.mm_ball_choice_mode, ANIM_RANDOM
1194 EL_MM_GRAY_BALL, -1,
1195 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1196 &li.mm_ball_content, EL_EMPTY, NULL,
1197 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1200 EL_MM_GRAY_BALL, -1,
1201 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1202 &li.rotate_mm_ball_content, TRUE
1205 EL_MM_GRAY_BALL, -1,
1206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1207 &li.explode_mm_ball, FALSE
1211 EL_MM_STEEL_BLOCK, -1,
1212 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1213 &li.mm_time_block, 75
1216 EL_MM_LIGHTBALL, -1,
1217 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1218 &li.score[SC_ELEM_BONUS], 10
1228 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1232 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1233 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1237 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1238 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1244 &xx_envelope.autowrap, FALSE
1248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1249 &xx_envelope.centered, FALSE
1254 TYPE_STRING, CONF_VALUE_BYTES(1),
1255 &xx_envelope.text, -1, NULL,
1256 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1257 &xx_default_string_empty[0]
1267 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1271 TYPE_STRING, CONF_VALUE_BYTES(1),
1272 &xx_ei.description[0], -1,
1273 &yy_ei.description[0],
1274 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1275 &xx_default_description[0]
1280 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1281 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1282 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1284 #if ENABLE_RESERVED_CODE
1285 // (reserved for later use)
1288 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1289 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1290 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1297 &xx_ei.use_gfx_element, FALSE,
1298 &yy_ei.use_gfx_element
1302 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1303 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1304 &yy_ei.gfx_element_initial
1309 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1310 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1311 &yy_ei.access_direction
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1317 &xx_ei.collect_score_initial, 10,
1318 &yy_ei.collect_score_initial
1322 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1323 &xx_ei.collect_count_initial, 1,
1324 &yy_ei.collect_count_initial
1329 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1330 &xx_ei.ce_value_fixed_initial, 0,
1331 &yy_ei.ce_value_fixed_initial
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1336 &xx_ei.ce_value_random_initial, 0,
1337 &yy_ei.ce_value_random_initial
1341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1342 &xx_ei.use_last_ce_value, FALSE,
1343 &yy_ei.use_last_ce_value
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1349 &xx_ei.push_delay_fixed, 8,
1350 &yy_ei.push_delay_fixed
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1355 &xx_ei.push_delay_random, 8,
1356 &yy_ei.push_delay_random
1360 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1361 &xx_ei.drop_delay_fixed, 0,
1362 &yy_ei.drop_delay_fixed
1366 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1367 &xx_ei.drop_delay_random, 0,
1368 &yy_ei.drop_delay_random
1372 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1373 &xx_ei.move_delay_fixed, 0,
1374 &yy_ei.move_delay_fixed
1378 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1379 &xx_ei.move_delay_random, 0,
1380 &yy_ei.move_delay_random
1384 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1385 &xx_ei.step_delay_fixed, 0,
1386 &yy_ei.step_delay_fixed
1390 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1391 &xx_ei.step_delay_random, 0,
1392 &yy_ei.step_delay_random
1397 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1398 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1403 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1404 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1405 &yy_ei.move_direction_initial
1409 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1410 &xx_ei.move_stepsize, TILEX / 8,
1411 &yy_ei.move_stepsize
1416 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1417 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1418 &yy_ei.move_enter_element
1422 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1423 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1424 &yy_ei.move_leave_element
1428 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1429 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1430 &yy_ei.move_leave_type
1435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1436 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1437 &yy_ei.slippery_type
1442 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1443 &xx_ei.explosion_type, EXPLODES_3X3,
1444 &yy_ei.explosion_type
1448 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1449 &xx_ei.explosion_delay, 16,
1450 &yy_ei.explosion_delay
1454 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1455 &xx_ei.ignition_delay, 8,
1456 &yy_ei.ignition_delay
1461 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1462 &xx_ei.content, EL_EMPTY_SPACE,
1464 &xx_num_contents, 1, 1
1467 // ---------- "num_change_pages" must be the last entry ---------------------
1470 -1, SAVE_CONF_ALWAYS,
1471 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1472 &xx_ei.num_change_pages, 1,
1473 &yy_ei.num_change_pages
1484 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1486 // ---------- "current_change_page" must be the first entry -----------------
1489 -1, SAVE_CONF_ALWAYS,
1490 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1491 &xx_current_change_page, -1
1494 // ---------- (the remaining entries can be in any order) -------------------
1498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1499 &xx_change.can_change, FALSE
1504 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1505 &xx_event_bits[0], 0
1509 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1510 &xx_event_bits[1], 0
1515 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1516 &xx_change.trigger_player, CH_PLAYER_ANY
1520 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1521 &xx_change.trigger_side, CH_SIDE_ANY
1525 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1526 &xx_change.trigger_page, CH_PAGE_ANY
1531 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1532 &xx_change.target_element, EL_EMPTY_SPACE
1537 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1538 &xx_change.delay_fixed, 0
1542 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1543 &xx_change.delay_random, 0
1547 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1548 &xx_change.delay_frames, FRAMES_PER_SECOND
1553 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1554 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1560 &xx_change.explode, FALSE
1564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1565 &xx_change.use_target_content, FALSE
1569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1570 &xx_change.only_if_complete, FALSE
1574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1575 &xx_change.use_random_replace, FALSE
1579 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1580 &xx_change.random_percentage, 100
1584 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1585 &xx_change.replace_when, CP_WHEN_EMPTY
1590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1591 &xx_change.has_action, FALSE
1595 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1596 &xx_change.action_type, CA_NO_ACTION
1600 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1601 &xx_change.action_mode, CA_MODE_UNDEFINED
1605 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1606 &xx_change.action_arg, CA_ARG_UNDEFINED
1611 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1612 &xx_change.action_element, EL_EMPTY_SPACE
1617 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1618 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1619 &xx_num_contents, 1, 1
1629 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1633 TYPE_STRING, CONF_VALUE_BYTES(1),
1634 &xx_ei.description[0], -1, NULL,
1635 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1636 &xx_default_description[0]
1641 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1642 &xx_ei.use_gfx_element, FALSE
1646 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1647 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1652 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1653 &xx_group.choice_mode, ANIM_RANDOM
1658 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1659 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1660 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1670 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1674 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1675 &xx_ei.use_gfx_element, FALSE
1679 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1680 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1690 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1694 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1695 &li.block_snap_field, TRUE
1699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1700 &li.continuous_snapping, TRUE
1704 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1705 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1709 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1710 &li.use_start_element[0], FALSE
1714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1715 &li.start_element[0], EL_PLAYER_1
1719 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1720 &li.use_artwork_element[0], FALSE
1724 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1725 &li.artwork_element[0], EL_PLAYER_1
1729 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1730 &li.use_explosion_element[0], FALSE
1734 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1735 &li.explosion_element[0], EL_PLAYER_1
1750 filetype_id_list[] =
1752 { LEVEL_FILE_TYPE_RND, "RND" },
1753 { LEVEL_FILE_TYPE_BD, "BD" },
1754 { LEVEL_FILE_TYPE_EM, "EM" },
1755 { LEVEL_FILE_TYPE_SP, "SP" },
1756 { LEVEL_FILE_TYPE_DX, "DX" },
1757 { LEVEL_FILE_TYPE_SB, "SB" },
1758 { LEVEL_FILE_TYPE_DC, "DC" },
1759 { LEVEL_FILE_TYPE_MM, "MM" },
1760 { LEVEL_FILE_TYPE_MM, "DF" },
1765 // ============================================================================
1766 // level file functions
1767 // ============================================================================
1769 static boolean check_special_flags(char *flag)
1771 if (strEqual(options.special_flags, flag) ||
1772 strEqual(leveldir_current->special_flags, flag))
1778 static struct DateInfo getCurrentDate(void)
1780 time_t epoch_seconds = time(NULL);
1781 struct tm *now = localtime(&epoch_seconds);
1782 struct DateInfo date;
1784 date.year = now->tm_year + 1900;
1785 date.month = now->tm_mon + 1;
1786 date.day = now->tm_mday;
1788 date.src = DATE_SRC_CLOCK;
1793 static void resetEventFlags(struct ElementChangeInfo *change)
1797 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1798 change->has_event[i] = FALSE;
1801 static void resetEventBits(void)
1805 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1806 xx_event_bits[i] = 0;
1809 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1813 /* important: only change event flag if corresponding event bit is set
1814 (this is because all xx_event_bits[] values are loaded separately,
1815 and all xx_event_bits[] values are set back to zero before loading
1816 another value xx_event_bits[x] (each value representing 32 flags)) */
1818 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1819 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1820 change->has_event[i] = TRUE;
1823 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1827 /* in contrast to the above function setEventFlagsFromEventBits(), it
1828 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1829 depending on the corresponding change->has_event[i] values here, as
1830 all xx_event_bits[] values are reset in resetEventBits() before */
1832 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1833 if (change->has_event[i])
1834 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1837 static char *getDefaultElementDescription(struct ElementInfo *ei)
1839 static char description[MAX_ELEMENT_NAME_LEN + 1];
1840 char *default_description = (ei->custom_description != NULL ?
1841 ei->custom_description :
1842 ei->editor_description);
1845 // always start with reliable default values
1846 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1847 description[i] = '\0';
1849 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1850 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1852 return &description[0];
1855 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1857 char *default_description = getDefaultElementDescription(ei);
1860 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1861 ei->description[i] = default_description[i];
1864 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1868 for (i = 0; conf[i].data_type != -1; i++)
1870 int default_value = conf[i].default_value;
1871 int data_type = conf[i].data_type;
1872 int conf_type = conf[i].conf_type;
1873 int byte_mask = conf_type & CONF_MASK_BYTES;
1875 if (byte_mask == CONF_MASK_MULTI_BYTES)
1877 int default_num_entities = conf[i].default_num_entities;
1878 int max_num_entities = conf[i].max_num_entities;
1880 *(int *)(conf[i].num_entities) = default_num_entities;
1882 if (data_type == TYPE_STRING)
1884 char *default_string = conf[i].default_string;
1885 char *string = (char *)(conf[i].value);
1887 strncpy(string, default_string, max_num_entities);
1889 else if (data_type == TYPE_ELEMENT_LIST)
1891 int *element_array = (int *)(conf[i].value);
1894 for (j = 0; j < max_num_entities; j++)
1895 element_array[j] = default_value;
1897 else if (data_type == TYPE_CONTENT_LIST)
1899 struct Content *content = (struct Content *)(conf[i].value);
1902 for (c = 0; c < max_num_entities; c++)
1903 for (y = 0; y < 3; y++)
1904 for (x = 0; x < 3; x++)
1905 content[c].e[x][y] = default_value;
1908 else // constant size configuration data (1, 2 or 4 bytes)
1910 if (data_type == TYPE_BOOLEAN)
1911 *(boolean *)(conf[i].value) = default_value;
1913 *(int *) (conf[i].value) = default_value;
1918 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1922 for (i = 0; conf[i].data_type != -1; i++)
1924 int data_type = conf[i].data_type;
1925 int conf_type = conf[i].conf_type;
1926 int byte_mask = conf_type & CONF_MASK_BYTES;
1928 if (byte_mask == CONF_MASK_MULTI_BYTES)
1930 int max_num_entities = conf[i].max_num_entities;
1932 if (data_type == TYPE_STRING)
1934 char *string = (char *)(conf[i].value);
1935 char *string_copy = (char *)(conf[i].value_copy);
1937 strncpy(string_copy, string, max_num_entities);
1939 else if (data_type == TYPE_ELEMENT_LIST)
1941 int *element_array = (int *)(conf[i].value);
1942 int *element_array_copy = (int *)(conf[i].value_copy);
1945 for (j = 0; j < max_num_entities; j++)
1946 element_array_copy[j] = element_array[j];
1948 else if (data_type == TYPE_CONTENT_LIST)
1950 struct Content *content = (struct Content *)(conf[i].value);
1951 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1954 for (c = 0; c < max_num_entities; c++)
1955 for (y = 0; y < 3; y++)
1956 for (x = 0; x < 3; x++)
1957 content_copy[c].e[x][y] = content[c].e[x][y];
1960 else // constant size configuration data (1, 2 or 4 bytes)
1962 if (data_type == TYPE_BOOLEAN)
1963 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1965 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1970 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1974 xx_ei = *ei_from; // copy element data into temporary buffer
1975 yy_ei = *ei_to; // copy element data into temporary buffer
1977 copyConfigFromConfigList(chunk_config_CUSX_base);
1982 // ---------- reinitialize and copy change pages ----------
1984 ei_to->num_change_pages = ei_from->num_change_pages;
1985 ei_to->current_change_page = ei_from->current_change_page;
1987 setElementChangePages(ei_to, ei_to->num_change_pages);
1989 for (i = 0; i < ei_to->num_change_pages; i++)
1990 ei_to->change_page[i] = ei_from->change_page[i];
1992 // ---------- copy group element info ----------
1993 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1994 *ei_to->group = *ei_from->group;
1996 // mark this custom element as modified
1997 ei_to->modified_settings = TRUE;
2000 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2002 int change_page_size = sizeof(struct ElementChangeInfo);
2004 ei->num_change_pages = MAX(1, change_pages);
2007 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2009 if (ei->current_change_page >= ei->num_change_pages)
2010 ei->current_change_page = ei->num_change_pages - 1;
2012 ei->change = &ei->change_page[ei->current_change_page];
2015 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2017 xx_change = *change; // copy change data into temporary buffer
2019 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2021 *change = xx_change;
2023 resetEventFlags(change);
2025 change->direct_action = 0;
2026 change->other_action = 0;
2028 change->pre_change_function = NULL;
2029 change->change_function = NULL;
2030 change->post_change_function = NULL;
2033 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2037 li = *level; // copy level data into temporary buffer
2038 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2039 *level = li; // copy temporary buffer back to level data
2041 setLevelInfoToDefaults_BD();
2042 setLevelInfoToDefaults_EM();
2043 setLevelInfoToDefaults_SP();
2044 setLevelInfoToDefaults_MM();
2046 level->native_bd_level = &native_bd_level;
2047 level->native_em_level = &native_em_level;
2048 level->native_sp_level = &native_sp_level;
2049 level->native_mm_level = &native_mm_level;
2051 level->file_version = FILE_VERSION_ACTUAL;
2052 level->game_version = GAME_VERSION_ACTUAL;
2054 level->creation_date = getCurrentDate();
2056 level->encoding_16bit_field = TRUE;
2057 level->encoding_16bit_yamyam = TRUE;
2058 level->encoding_16bit_amoeba = TRUE;
2060 // clear level name and level author string buffers
2061 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2062 level->name[i] = '\0';
2063 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2064 level->author[i] = '\0';
2066 // set level name and level author to default values
2067 strcpy(level->name, NAMELESS_LEVEL_NAME);
2068 strcpy(level->author, ANONYMOUS_NAME);
2070 // set level playfield to playable default level with player and exit
2071 for (x = 0; x < MAX_LEV_FIELDX; x++)
2072 for (y = 0; y < MAX_LEV_FIELDY; y++)
2073 level->field[x][y] = EL_SAND;
2075 level->field[0][0] = EL_PLAYER_1;
2076 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2078 BorderElement = EL_STEELWALL;
2080 // detect custom elements when loading them
2081 level->file_has_custom_elements = FALSE;
2083 // set all bug compatibility flags to "false" => do not emulate this bug
2084 level->use_action_after_change_bug = FALSE;
2086 if (leveldir_current)
2088 // try to determine better author name than 'anonymous'
2089 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2091 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2092 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2096 switch (LEVELCLASS(leveldir_current))
2098 case LEVELCLASS_TUTORIAL:
2099 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2102 case LEVELCLASS_CONTRIB:
2103 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2104 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2107 case LEVELCLASS_PRIVATE:
2108 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2109 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2113 // keep default value
2120 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2122 static boolean clipboard_elements_initialized = FALSE;
2125 InitElementPropertiesStatic();
2127 li = *level; // copy level data into temporary buffer
2128 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2129 *level = li; // copy temporary buffer back to level data
2131 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2134 struct ElementInfo *ei = &element_info[element];
2136 if (element == EL_MM_GRAY_BALL)
2138 struct LevelInfo_MM *level_mm = level->native_mm_level;
2141 for (j = 0; j < level->num_mm_ball_contents; j++)
2142 level->mm_ball_content[j] =
2143 map_element_MM_to_RND(level_mm->ball_content[j]);
2146 // never initialize clipboard elements after the very first time
2147 // (to be able to use clipboard elements between several levels)
2148 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2151 if (IS_ENVELOPE(element))
2153 int envelope_nr = element - EL_ENVELOPE_1;
2155 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2157 level->envelope[envelope_nr] = xx_envelope;
2160 if (IS_CUSTOM_ELEMENT(element) ||
2161 IS_GROUP_ELEMENT(element) ||
2162 IS_INTERNAL_ELEMENT(element))
2164 xx_ei = *ei; // copy element data into temporary buffer
2166 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2171 setElementChangePages(ei, 1);
2172 setElementChangeInfoToDefaults(ei->change);
2174 if (IS_CUSTOM_ELEMENT(element) ||
2175 IS_GROUP_ELEMENT(element))
2177 setElementDescriptionToDefault(ei);
2179 ei->modified_settings = FALSE;
2182 if (IS_CUSTOM_ELEMENT(element) ||
2183 IS_INTERNAL_ELEMENT(element))
2185 // internal values used in level editor
2187 ei->access_type = 0;
2188 ei->access_layer = 0;
2189 ei->access_protected = 0;
2190 ei->walk_to_action = 0;
2191 ei->smash_targets = 0;
2194 ei->can_explode_by_fire = FALSE;
2195 ei->can_explode_smashed = FALSE;
2196 ei->can_explode_impact = FALSE;
2198 ei->current_change_page = 0;
2201 if (IS_GROUP_ELEMENT(element) ||
2202 IS_INTERNAL_ELEMENT(element))
2204 struct ElementGroupInfo *group;
2206 // initialize memory for list of elements in group
2207 if (ei->group == NULL)
2208 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2212 xx_group = *group; // copy group data into temporary buffer
2214 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2219 if (IS_EMPTY_ELEMENT(element) ||
2220 IS_INTERNAL_ELEMENT(element))
2222 xx_ei = *ei; // copy element data into temporary buffer
2224 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2230 clipboard_elements_initialized = TRUE;
2233 static void setLevelInfoToDefaults(struct LevelInfo *level,
2234 boolean level_info_only,
2235 boolean reset_file_status)
2237 setLevelInfoToDefaults_Level(level);
2239 if (!level_info_only)
2240 setLevelInfoToDefaults_Elements(level);
2242 if (reset_file_status)
2244 level->no_valid_file = FALSE;
2245 level->no_level_file = FALSE;
2248 level->changed = FALSE;
2251 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2253 level_file_info->nr = 0;
2254 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2255 level_file_info->packed = FALSE;
2257 setString(&level_file_info->basename, NULL);
2258 setString(&level_file_info->filename, NULL);
2261 int getMappedElement_SB(int, boolean);
2263 static void ActivateLevelTemplate(void)
2267 if (check_special_flags("load_xsb_to_ces"))
2269 // fill smaller playfields with padding "beyond border wall" elements
2270 if (level.fieldx < level_template.fieldx ||
2271 level.fieldy < level_template.fieldy)
2273 short field[level.fieldx][level.fieldy];
2274 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2275 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2276 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2277 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2279 // copy old playfield (which is smaller than the visible area)
2280 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2281 field[x][y] = level.field[x][y];
2283 // fill new, larger playfield with "beyond border wall" elements
2284 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2285 level.field[x][y] = getMappedElement_SB('_', TRUE);
2287 // copy the old playfield to the middle of the new playfield
2288 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2289 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2291 level.fieldx = new_fieldx;
2292 level.fieldy = new_fieldy;
2296 // Currently there is no special action needed to activate the template
2297 // data, because 'element_info' property settings overwrite the original
2298 // level data, while all other variables do not change.
2300 // Exception: 'from_level_template' elements in the original level playfield
2301 // are overwritten with the corresponding elements at the same position in
2302 // playfield from the level template.
2304 for (x = 0; x < level.fieldx; x++)
2305 for (y = 0; y < level.fieldy; y++)
2306 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2307 level.field[x][y] = level_template.field[x][y];
2309 if (check_special_flags("load_xsb_to_ces"))
2311 struct LevelInfo level_backup = level;
2313 // overwrite all individual level settings from template level settings
2314 level = level_template;
2316 // restore level file info
2317 level.file_info = level_backup.file_info;
2319 // restore playfield size
2320 level.fieldx = level_backup.fieldx;
2321 level.fieldy = level_backup.fieldy;
2323 // restore playfield content
2324 for (x = 0; x < level.fieldx; x++)
2325 for (y = 0; y < level.fieldy; y++)
2326 level.field[x][y] = level_backup.field[x][y];
2328 // restore name and author from individual level
2329 strcpy(level.name, level_backup.name);
2330 strcpy(level.author, level_backup.author);
2332 // restore flag "use_custom_template"
2333 level.use_custom_template = level_backup.use_custom_template;
2337 static boolean checkForPackageFromBasename_BD(char *basename)
2339 // check for native BD level file extensions
2340 if (!strSuffixLower(basename, ".bd") &&
2341 !strSuffixLower(basename, ".bdr") &&
2342 !strSuffixLower(basename, ".brc") &&
2343 !strSuffixLower(basename, ".gds"))
2346 // check for standard single-level BD files (like "001.bd")
2347 if (strSuffixLower(basename, ".bd") &&
2348 strlen(basename) == 6 &&
2349 basename[0] >= '0' && basename[0] <= '9' &&
2350 basename[1] >= '0' && basename[1] <= '9' &&
2351 basename[2] >= '0' && basename[2] <= '9')
2354 // this is a level package in native BD file format
2358 static char *getLevelFilenameFromBasename(char *basename)
2360 static char *filename = NULL;
2362 checked_free(filename);
2364 filename = getPath2(getCurrentLevelDir(), basename);
2369 static int getFileTypeFromBasename(char *basename)
2371 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2373 static char *filename = NULL;
2374 struct stat file_status;
2376 // ---------- try to determine file type from filename ----------
2378 // check for typical filename of a Supaplex level package file
2379 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2380 return LEVEL_FILE_TYPE_SP;
2382 // check for typical filename of a Diamond Caves II level package file
2383 if (strSuffixLower(basename, ".dc") ||
2384 strSuffixLower(basename, ".dc2"))
2385 return LEVEL_FILE_TYPE_DC;
2387 // check for typical filename of a Sokoban level package file
2388 if (strSuffixLower(basename, ".xsb") &&
2389 strchr(basename, '%') == NULL)
2390 return LEVEL_FILE_TYPE_SB;
2392 // check for typical filename of a Boulder Dash (GDash) level package file
2393 if (checkForPackageFromBasename_BD(basename))
2394 return LEVEL_FILE_TYPE_BD;
2396 // ---------- try to determine file type from filesize ----------
2398 checked_free(filename);
2399 filename = getPath2(getCurrentLevelDir(), basename);
2401 if (stat(filename, &file_status) == 0)
2403 // check for typical filesize of a Supaplex level package file
2404 if (file_status.st_size == 170496)
2405 return LEVEL_FILE_TYPE_SP;
2408 return LEVEL_FILE_TYPE_UNKNOWN;
2411 static int getFileTypeFromMagicBytes(char *filename, int type)
2415 if ((file = openFile(filename, MODE_READ)))
2417 char chunk_name[CHUNK_ID_LEN + 1];
2419 getFileChunkBE(file, chunk_name, NULL);
2421 if (strEqual(chunk_name, "MMII") ||
2422 strEqual(chunk_name, "MIRR"))
2423 type = LEVEL_FILE_TYPE_MM;
2431 static boolean checkForPackageFromBasename(char *basename)
2433 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2434 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2436 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2439 static char *getSingleLevelBasenameExt(int nr, char *extension)
2441 static char basename[MAX_FILENAME_LEN];
2444 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2446 sprintf(basename, "%03d.%s", nr, extension);
2451 static char *getSingleLevelBasename(int nr)
2453 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2456 static char *getPackedLevelBasename(int type)
2458 static char basename[MAX_FILENAME_LEN];
2459 char *directory = getCurrentLevelDir();
2461 DirectoryEntry *dir_entry;
2463 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2465 if ((dir = openDirectory(directory)) == NULL)
2467 Warn("cannot read current level directory '%s'", directory);
2472 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2474 char *entry_basename = dir_entry->basename;
2475 int entry_type = getFileTypeFromBasename(entry_basename);
2477 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2479 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2482 strcpy(basename, entry_basename);
2489 closeDirectory(dir);
2494 static char *getSingleLevelFilename(int nr)
2496 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2499 #if ENABLE_UNUSED_CODE
2500 static char *getPackedLevelFilename(int type)
2502 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2506 char *getDefaultLevelFilename(int nr)
2508 return getSingleLevelFilename(nr);
2511 #if ENABLE_UNUSED_CODE
2512 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2516 lfi->packed = FALSE;
2518 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2519 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2523 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2524 int type, char *format, ...)
2526 static char basename[MAX_FILENAME_LEN];
2529 va_start(ap, format);
2530 vsprintf(basename, format, ap);
2534 lfi->packed = FALSE;
2536 setString(&lfi->basename, basename);
2537 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2540 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2546 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2547 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2550 static int getFiletypeFromID(char *filetype_id)
2552 char *filetype_id_lower;
2553 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2556 if (filetype_id == NULL)
2557 return LEVEL_FILE_TYPE_UNKNOWN;
2559 filetype_id_lower = getStringToLower(filetype_id);
2561 for (i = 0; filetype_id_list[i].id != NULL; i++)
2563 char *id_lower = getStringToLower(filetype_id_list[i].id);
2565 if (strEqual(filetype_id_lower, id_lower))
2566 filetype = filetype_id_list[i].filetype;
2570 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2574 free(filetype_id_lower);
2579 char *getLocalLevelTemplateFilename(void)
2581 return getDefaultLevelFilename(-1);
2584 char *getGlobalLevelTemplateFilename(void)
2586 // global variable "leveldir_current" must be modified in the loop below
2587 LevelDirTree *leveldir_current_last = leveldir_current;
2588 char *filename = NULL;
2590 // check for template level in path from current to topmost tree node
2592 while (leveldir_current != NULL)
2594 filename = getDefaultLevelFilename(-1);
2596 if (fileExists(filename))
2599 leveldir_current = leveldir_current->node_parent;
2602 // restore global variable "leveldir_current" modified in above loop
2603 leveldir_current = leveldir_current_last;
2608 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2612 // special case: level number is negative => check for level template file
2615 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2616 getSingleLevelBasename(-1));
2618 // replace local level template filename with global template filename
2619 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2621 // no fallback if template file not existing
2625 // special case: check for file name/pattern specified in "levelinfo.conf"
2626 if (leveldir_current->level_filename != NULL)
2628 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2630 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2631 leveldir_current->level_filename, nr);
2633 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2635 if (fileExists(lfi->filename))
2638 else if (leveldir_current->level_filetype != NULL)
2640 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2642 // check for specified native level file with standard file name
2643 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2644 "%03d.%s", nr, LEVELFILE_EXTENSION);
2645 if (fileExists(lfi->filename))
2649 // check for native Rocks'n'Diamonds level file
2650 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2651 "%03d.%s", nr, LEVELFILE_EXTENSION);
2652 if (fileExists(lfi->filename))
2655 // check for native Boulder Dash level file
2656 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2657 if (fileExists(lfi->filename))
2660 // check for Emerald Mine level file (V1)
2661 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2662 'a' + (nr / 10) % 26, '0' + nr % 10);
2663 if (fileExists(lfi->filename))
2665 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2666 'A' + (nr / 10) % 26, '0' + nr % 10);
2667 if (fileExists(lfi->filename))
2670 // check for Emerald Mine level file (V2 to V5)
2671 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2672 if (fileExists(lfi->filename))
2675 // check for Emerald Mine level file (V6 / single mode)
2676 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2677 if (fileExists(lfi->filename))
2679 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2680 if (fileExists(lfi->filename))
2683 // check for Emerald Mine level file (V6 / teamwork mode)
2684 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2685 if (fileExists(lfi->filename))
2687 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2688 if (fileExists(lfi->filename))
2691 // check for various packed level file formats
2692 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2693 if (fileExists(lfi->filename))
2696 // no known level file found -- use default values (and fail later)
2697 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2698 "%03d.%s", nr, LEVELFILE_EXTENSION);
2701 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2703 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2704 lfi->type = getFileTypeFromBasename(lfi->basename);
2706 if (lfi->type == LEVEL_FILE_TYPE_RND)
2707 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2710 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2712 // always start with reliable default values
2713 setFileInfoToDefaults(level_file_info);
2715 level_file_info->nr = nr; // set requested level number
2717 determineLevelFileInfo_Filename(level_file_info);
2718 determineLevelFileInfo_Filetype(level_file_info);
2721 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2722 struct LevelFileInfo *lfi_to)
2724 lfi_to->nr = lfi_from->nr;
2725 lfi_to->type = lfi_from->type;
2726 lfi_to->packed = lfi_from->packed;
2728 setString(&lfi_to->basename, lfi_from->basename);
2729 setString(&lfi_to->filename, lfi_from->filename);
2732 // ----------------------------------------------------------------------------
2733 // functions for loading R'n'D level
2734 // ----------------------------------------------------------------------------
2736 int getMappedElement(int element)
2738 // remap some (historic, now obsolete) elements
2742 case EL_PLAYER_OBSOLETE:
2743 element = EL_PLAYER_1;
2746 case EL_KEY_OBSOLETE:
2750 case EL_EM_KEY_1_FILE_OBSOLETE:
2751 element = EL_EM_KEY_1;
2754 case EL_EM_KEY_2_FILE_OBSOLETE:
2755 element = EL_EM_KEY_2;
2758 case EL_EM_KEY_3_FILE_OBSOLETE:
2759 element = EL_EM_KEY_3;
2762 case EL_EM_KEY_4_FILE_OBSOLETE:
2763 element = EL_EM_KEY_4;
2766 case EL_ENVELOPE_OBSOLETE:
2767 element = EL_ENVELOPE_1;
2775 if (element >= NUM_FILE_ELEMENTS)
2777 Warn("invalid level element %d", element);
2779 element = EL_UNKNOWN;
2787 static int getMappedElementByVersion(int element, int game_version)
2789 // remap some elements due to certain game version
2791 if (game_version <= VERSION_IDENT(2,2,0,0))
2793 // map game font elements
2794 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2795 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2796 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2797 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2800 if (game_version < VERSION_IDENT(3,0,0,0))
2802 // map Supaplex gravity tube elements
2803 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2804 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2805 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2806 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2813 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2815 level->file_version = getFileVersion(file);
2816 level->game_version = getFileVersion(file);
2821 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2823 level->creation_date.year = getFile16BitBE(file);
2824 level->creation_date.month = getFile8Bit(file);
2825 level->creation_date.day = getFile8Bit(file);
2827 level->creation_date.src = DATE_SRC_LEVELFILE;
2832 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2834 int initial_player_stepsize;
2835 int initial_player_gravity;
2838 level->fieldx = getFile8Bit(file);
2839 level->fieldy = getFile8Bit(file);
2841 level->time = getFile16BitBE(file);
2842 level->gems_needed = getFile16BitBE(file);
2844 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2845 level->name[i] = getFile8Bit(file);
2846 level->name[MAX_LEVEL_NAME_LEN] = 0;
2848 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2849 level->score[i] = getFile8Bit(file);
2851 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2852 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2853 for (y = 0; y < 3; y++)
2854 for (x = 0; x < 3; x++)
2855 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2857 level->amoeba_speed = getFile8Bit(file);
2858 level->time_magic_wall = getFile8Bit(file);
2859 level->time_wheel = getFile8Bit(file);
2860 level->amoeba_content = getMappedElement(getFile8Bit(file));
2862 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2865 for (i = 0; i < MAX_PLAYERS; i++)
2866 level->initial_player_stepsize[i] = initial_player_stepsize;
2868 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2870 for (i = 0; i < MAX_PLAYERS; i++)
2871 level->initial_player_gravity[i] = initial_player_gravity;
2873 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2874 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2876 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2878 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2879 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2880 level->can_move_into_acid_bits = getFile32BitBE(file);
2881 level->dont_collide_with_bits = getFile8Bit(file);
2883 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2884 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2886 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2887 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2888 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2890 level->game_engine_type = getFile8Bit(file);
2892 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2897 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2901 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2902 level->name[i] = getFile8Bit(file);
2903 level->name[MAX_LEVEL_NAME_LEN] = 0;
2908 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2912 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2913 level->author[i] = getFile8Bit(file);
2914 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2919 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2922 int chunk_size_expected = level->fieldx * level->fieldy;
2924 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2925 stored with 16-bit encoding (and should be twice as big then).
2926 Even worse, playfield data was stored 16-bit when only yamyam content
2927 contained 16-bit elements and vice versa. */
2929 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2930 chunk_size_expected *= 2;
2932 if (chunk_size_expected != chunk_size)
2934 ReadUnusedBytesFromFile(file, chunk_size);
2935 return chunk_size_expected;
2938 for (y = 0; y < level->fieldy; y++)
2939 for (x = 0; x < level->fieldx; x++)
2940 level->field[x][y] =
2941 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2946 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2949 int header_size = 4;
2950 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2951 int chunk_size_expected = header_size + content_size;
2953 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2954 stored with 16-bit encoding (and should be twice as big then).
2955 Even worse, playfield data was stored 16-bit when only yamyam content
2956 contained 16-bit elements and vice versa. */
2958 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2959 chunk_size_expected += content_size;
2961 if (chunk_size_expected != chunk_size)
2963 ReadUnusedBytesFromFile(file, chunk_size);
2964 return chunk_size_expected;
2968 level->num_yamyam_contents = getFile8Bit(file);
2972 // correct invalid number of content fields -- should never happen
2973 if (level->num_yamyam_contents < 1 ||
2974 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2975 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2977 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2978 for (y = 0; y < 3; y++)
2979 for (x = 0; x < 3; x++)
2980 level->yamyam_content[i].e[x][y] =
2981 getMappedElement(level->encoding_16bit_field ?
2982 getFile16BitBE(file) : getFile8Bit(file));
2986 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2991 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2993 element = getMappedElement(getFile16BitBE(file));
2994 num_contents = getFile8Bit(file);
2996 getFile8Bit(file); // content x size (unused)
2997 getFile8Bit(file); // content y size (unused)
2999 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3001 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3002 for (y = 0; y < 3; y++)
3003 for (x = 0; x < 3; x++)
3004 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3006 // correct invalid number of content fields -- should never happen
3007 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3008 num_contents = STD_ELEMENT_CONTENTS;
3010 if (element == EL_YAMYAM)
3012 level->num_yamyam_contents = num_contents;
3014 for (i = 0; i < num_contents; i++)
3015 for (y = 0; y < 3; y++)
3016 for (x = 0; x < 3; x++)
3017 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3019 else if (element == EL_BD_AMOEBA)
3021 level->amoeba_content = content_array[0][0][0];
3025 Warn("cannot load content for element '%d'", element);
3031 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3037 int chunk_size_expected;
3039 element = getMappedElement(getFile16BitBE(file));
3040 if (!IS_ENVELOPE(element))
3041 element = EL_ENVELOPE_1;
3043 envelope_nr = element - EL_ENVELOPE_1;
3045 envelope_len = getFile16BitBE(file);
3047 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3048 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3050 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3052 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3053 if (chunk_size_expected != chunk_size)
3055 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3056 return chunk_size_expected;
3059 for (i = 0; i < envelope_len; i++)
3060 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3065 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3067 int num_changed_custom_elements = getFile16BitBE(file);
3068 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3071 if (chunk_size_expected != chunk_size)
3073 ReadUnusedBytesFromFile(file, chunk_size - 2);
3074 return chunk_size_expected;
3077 for (i = 0; i < num_changed_custom_elements; i++)
3079 int element = getMappedElement(getFile16BitBE(file));
3080 int properties = getFile32BitBE(file);
3082 if (IS_CUSTOM_ELEMENT(element))
3083 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3085 Warn("invalid custom element number %d", element);
3087 // older game versions that wrote level files with CUS1 chunks used
3088 // different default push delay values (not yet stored in level file)
3089 element_info[element].push_delay_fixed = 2;
3090 element_info[element].push_delay_random = 8;
3093 level->file_has_custom_elements = TRUE;
3098 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3100 int num_changed_custom_elements = getFile16BitBE(file);
3101 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3104 if (chunk_size_expected != chunk_size)
3106 ReadUnusedBytesFromFile(file, chunk_size - 2);
3107 return chunk_size_expected;
3110 for (i = 0; i < num_changed_custom_elements; i++)
3112 int element = getMappedElement(getFile16BitBE(file));
3113 int custom_target_element = getMappedElement(getFile16BitBE(file));
3115 if (IS_CUSTOM_ELEMENT(element))
3116 element_info[element].change->target_element = custom_target_element;
3118 Warn("invalid custom element number %d", element);
3121 level->file_has_custom_elements = TRUE;
3126 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3128 int num_changed_custom_elements = getFile16BitBE(file);
3129 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3132 if (chunk_size_expected != chunk_size)
3134 ReadUnusedBytesFromFile(file, chunk_size - 2);
3135 return chunk_size_expected;
3138 for (i = 0; i < num_changed_custom_elements; i++)
3140 int element = getMappedElement(getFile16BitBE(file));
3141 struct ElementInfo *ei = &element_info[element];
3142 unsigned int event_bits;
3144 if (!IS_CUSTOM_ELEMENT(element))
3146 Warn("invalid custom element number %d", element);
3148 element = EL_INTERNAL_DUMMY;
3151 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3152 ei->description[j] = getFile8Bit(file);
3153 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3155 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3157 // some free bytes for future properties and padding
3158 ReadUnusedBytesFromFile(file, 7);
3160 ei->use_gfx_element = getFile8Bit(file);
3161 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3163 ei->collect_score_initial = getFile8Bit(file);
3164 ei->collect_count_initial = getFile8Bit(file);
3166 ei->push_delay_fixed = getFile16BitBE(file);
3167 ei->push_delay_random = getFile16BitBE(file);
3168 ei->move_delay_fixed = getFile16BitBE(file);
3169 ei->move_delay_random = getFile16BitBE(file);
3171 ei->move_pattern = getFile16BitBE(file);
3172 ei->move_direction_initial = getFile8Bit(file);
3173 ei->move_stepsize = getFile8Bit(file);
3175 for (y = 0; y < 3; y++)
3176 for (x = 0; x < 3; x++)
3177 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3179 // bits 0 - 31 of "has_event[]"
3180 event_bits = getFile32BitBE(file);
3181 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3182 if (event_bits & (1u << j))
3183 ei->change->has_event[j] = TRUE;
3185 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3187 ei->change->delay_fixed = getFile16BitBE(file);
3188 ei->change->delay_random = getFile16BitBE(file);
3189 ei->change->delay_frames = getFile16BitBE(file);
3191 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3193 ei->change->explode = getFile8Bit(file);
3194 ei->change->use_target_content = getFile8Bit(file);
3195 ei->change->only_if_complete = getFile8Bit(file);
3196 ei->change->use_random_replace = getFile8Bit(file);
3198 ei->change->random_percentage = getFile8Bit(file);
3199 ei->change->replace_when = getFile8Bit(file);
3201 for (y = 0; y < 3; y++)
3202 for (x = 0; x < 3; x++)
3203 ei->change->target_content.e[x][y] =
3204 getMappedElement(getFile16BitBE(file));
3206 ei->slippery_type = getFile8Bit(file);
3208 // some free bytes for future properties and padding
3209 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3211 // mark that this custom element has been modified
3212 ei->modified_settings = TRUE;
3215 level->file_has_custom_elements = TRUE;
3220 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3222 struct ElementInfo *ei;
3223 int chunk_size_expected;
3227 // ---------- custom element base property values (96 bytes) ----------------
3229 element = getMappedElement(getFile16BitBE(file));
3231 if (!IS_CUSTOM_ELEMENT(element))
3233 Warn("invalid custom element number %d", element);
3235 ReadUnusedBytesFromFile(file, chunk_size - 2);
3240 ei = &element_info[element];
3242 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3243 ei->description[i] = getFile8Bit(file);
3244 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3246 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3248 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3250 ei->num_change_pages = getFile8Bit(file);
3252 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3253 if (chunk_size_expected != chunk_size)
3255 ReadUnusedBytesFromFile(file, chunk_size - 43);
3256 return chunk_size_expected;
3259 ei->ce_value_fixed_initial = getFile16BitBE(file);
3260 ei->ce_value_random_initial = getFile16BitBE(file);
3261 ei->use_last_ce_value = getFile8Bit(file);
3263 ei->use_gfx_element = getFile8Bit(file);
3264 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3266 ei->collect_score_initial = getFile8Bit(file);
3267 ei->collect_count_initial = getFile8Bit(file);
3269 ei->drop_delay_fixed = getFile8Bit(file);
3270 ei->push_delay_fixed = getFile8Bit(file);
3271 ei->drop_delay_random = getFile8Bit(file);
3272 ei->push_delay_random = getFile8Bit(file);
3273 ei->move_delay_fixed = getFile16BitBE(file);
3274 ei->move_delay_random = getFile16BitBE(file);
3276 // bits 0 - 15 of "move_pattern" ...
3277 ei->move_pattern = getFile16BitBE(file);
3278 ei->move_direction_initial = getFile8Bit(file);
3279 ei->move_stepsize = getFile8Bit(file);
3281 ei->slippery_type = getFile8Bit(file);
3283 for (y = 0; y < 3; y++)
3284 for (x = 0; x < 3; x++)
3285 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3287 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3288 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3289 ei->move_leave_type = getFile8Bit(file);
3291 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3292 ei->move_pattern |= (getFile16BitBE(file) << 16);
3294 ei->access_direction = getFile8Bit(file);
3296 ei->explosion_delay = getFile8Bit(file);
3297 ei->ignition_delay = getFile8Bit(file);
3298 ei->explosion_type = getFile8Bit(file);
3300 // some free bytes for future custom property values and padding
3301 ReadUnusedBytesFromFile(file, 1);
3303 // ---------- change page property values (48 bytes) ------------------------
3305 setElementChangePages(ei, ei->num_change_pages);
3307 for (i = 0; i < ei->num_change_pages; i++)
3309 struct ElementChangeInfo *change = &ei->change_page[i];
3310 unsigned int event_bits;
3312 // always start with reliable default values
3313 setElementChangeInfoToDefaults(change);
3315 // bits 0 - 31 of "has_event[]" ...
3316 event_bits = getFile32BitBE(file);
3317 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3318 if (event_bits & (1u << j))
3319 change->has_event[j] = TRUE;
3321 change->target_element = getMappedElement(getFile16BitBE(file));
3323 change->delay_fixed = getFile16BitBE(file);
3324 change->delay_random = getFile16BitBE(file);
3325 change->delay_frames = getFile16BitBE(file);
3327 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3329 change->explode = getFile8Bit(file);
3330 change->use_target_content = getFile8Bit(file);
3331 change->only_if_complete = getFile8Bit(file);
3332 change->use_random_replace = getFile8Bit(file);
3334 change->random_percentage = getFile8Bit(file);
3335 change->replace_when = getFile8Bit(file);
3337 for (y = 0; y < 3; y++)
3338 for (x = 0; x < 3; x++)
3339 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3341 change->can_change = getFile8Bit(file);
3343 change->trigger_side = getFile8Bit(file);
3345 change->trigger_player = getFile8Bit(file);
3346 change->trigger_page = getFile8Bit(file);
3348 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3349 CH_PAGE_ANY : (1 << change->trigger_page));
3351 change->has_action = getFile8Bit(file);
3352 change->action_type = getFile8Bit(file);
3353 change->action_mode = getFile8Bit(file);
3354 change->action_arg = getFile16BitBE(file);
3356 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3357 event_bits = getFile8Bit(file);
3358 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3359 if (event_bits & (1u << (j - 32)))
3360 change->has_event[j] = TRUE;
3363 // mark this custom element as modified
3364 ei->modified_settings = TRUE;
3366 level->file_has_custom_elements = TRUE;
3371 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3373 struct ElementInfo *ei;
3374 struct ElementGroupInfo *group;
3378 element = getMappedElement(getFile16BitBE(file));
3380 if (!IS_GROUP_ELEMENT(element))
3382 Warn("invalid group element number %d", element);
3384 ReadUnusedBytesFromFile(file, chunk_size - 2);
3389 ei = &element_info[element];
3391 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3392 ei->description[i] = getFile8Bit(file);
3393 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3395 group = element_info[element].group;
3397 group->num_elements = getFile8Bit(file);
3399 ei->use_gfx_element = getFile8Bit(file);
3400 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3402 group->choice_mode = getFile8Bit(file);
3404 // some free bytes for future values and padding
3405 ReadUnusedBytesFromFile(file, 3);
3407 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3408 group->element[i] = getMappedElement(getFile16BitBE(file));
3410 // mark this group element as modified
3411 element_info[element].modified_settings = TRUE;
3413 level->file_has_custom_elements = TRUE;
3418 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3419 int element, int real_element)
3421 int micro_chunk_size = 0;
3422 int conf_type = getFile8Bit(file);
3423 int byte_mask = conf_type & CONF_MASK_BYTES;
3424 boolean element_found = FALSE;
3427 micro_chunk_size += 1;
3429 if (byte_mask == CONF_MASK_MULTI_BYTES)
3431 int num_bytes = getFile16BitBE(file);
3432 byte *buffer = checked_malloc(num_bytes);
3434 ReadBytesFromFile(file, buffer, num_bytes);
3436 for (i = 0; conf[i].data_type != -1; i++)
3438 if (conf[i].element == element &&
3439 conf[i].conf_type == conf_type)
3441 int data_type = conf[i].data_type;
3442 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3443 int max_num_entities = conf[i].max_num_entities;
3445 if (num_entities > max_num_entities)
3447 Warn("truncating number of entities for element %d from %d to %d",
3448 element, num_entities, max_num_entities);
3450 num_entities = max_num_entities;
3453 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3454 data_type == TYPE_CONTENT_LIST))
3456 // for element and content lists, zero entities are not allowed
3457 Warn("found empty list of entities for element %d", element);
3459 // do not set "num_entities" here to prevent reading behind buffer
3461 *(int *)(conf[i].num_entities) = 1; // at least one is required
3465 *(int *)(conf[i].num_entities) = num_entities;
3468 element_found = TRUE;
3470 if (data_type == TYPE_STRING)
3472 char *string = (char *)(conf[i].value);
3475 for (j = 0; j < max_num_entities; j++)
3476 string[j] = (j < num_entities ? buffer[j] : '\0');
3478 else if (data_type == TYPE_ELEMENT_LIST)
3480 int *element_array = (int *)(conf[i].value);
3483 for (j = 0; j < num_entities; j++)
3485 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3487 else if (data_type == TYPE_CONTENT_LIST)
3489 struct Content *content= (struct Content *)(conf[i].value);
3492 for (c = 0; c < num_entities; c++)
3493 for (y = 0; y < 3; y++)
3494 for (x = 0; x < 3; x++)
3495 content[c].e[x][y] =
3496 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3499 element_found = FALSE;
3505 checked_free(buffer);
3507 micro_chunk_size += 2 + num_bytes;
3509 else // constant size configuration data (1, 2 or 4 bytes)
3511 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3512 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3513 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3515 for (i = 0; conf[i].data_type != -1; i++)
3517 if (conf[i].element == element &&
3518 conf[i].conf_type == conf_type)
3520 int data_type = conf[i].data_type;
3522 if (data_type == TYPE_ELEMENT)
3523 value = getMappedElement(value);
3525 if (data_type == TYPE_BOOLEAN)
3526 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3528 *(int *) (conf[i].value) = value;
3530 element_found = TRUE;
3536 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3541 char *error_conf_chunk_bytes =
3542 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3543 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3544 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3545 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3546 int error_element = real_element;
3548 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3549 error_conf_chunk_bytes, error_conf_chunk_token,
3550 error_element, EL_NAME(error_element));
3553 return micro_chunk_size;
3556 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3558 int real_chunk_size = 0;
3560 li = *level; // copy level data into temporary buffer
3562 while (!checkEndOfFile(file))
3564 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3566 if (real_chunk_size >= chunk_size)
3570 *level = li; // copy temporary buffer back to level data
3572 return real_chunk_size;
3575 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3577 int real_chunk_size = 0;
3579 li = *level; // copy level data into temporary buffer
3581 while (!checkEndOfFile(file))
3583 int element = getMappedElement(getFile16BitBE(file));
3585 real_chunk_size += 2;
3586 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3588 if (real_chunk_size >= chunk_size)
3592 *level = li; // copy temporary buffer back to level data
3594 return real_chunk_size;
3597 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3599 int real_chunk_size = 0;
3601 li = *level; // copy level data into temporary buffer
3603 while (!checkEndOfFile(file))
3605 int element = getMappedElement(getFile16BitBE(file));
3607 real_chunk_size += 2;
3608 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3610 if (real_chunk_size >= chunk_size)
3614 *level = li; // copy temporary buffer back to level data
3616 return real_chunk_size;
3619 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3621 int element = getMappedElement(getFile16BitBE(file));
3622 int envelope_nr = element - EL_ENVELOPE_1;
3623 int real_chunk_size = 2;
3625 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3627 while (!checkEndOfFile(file))
3629 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3632 if (real_chunk_size >= chunk_size)
3636 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3638 return real_chunk_size;
3641 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3643 int element = getMappedElement(getFile16BitBE(file));
3644 int real_chunk_size = 2;
3645 struct ElementInfo *ei = &element_info[element];
3648 xx_ei = *ei; // copy element data into temporary buffer
3650 xx_ei.num_change_pages = -1;
3652 while (!checkEndOfFile(file))
3654 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3656 if (xx_ei.num_change_pages != -1)
3659 if (real_chunk_size >= chunk_size)
3665 if (ei->num_change_pages == -1)
3667 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3670 ei->num_change_pages = 1;
3672 setElementChangePages(ei, 1);
3673 setElementChangeInfoToDefaults(ei->change);
3675 return real_chunk_size;
3678 // initialize number of change pages stored for this custom element
3679 setElementChangePages(ei, ei->num_change_pages);
3680 for (i = 0; i < ei->num_change_pages; i++)
3681 setElementChangeInfoToDefaults(&ei->change_page[i]);
3683 // start with reading properties for the first change page
3684 xx_current_change_page = 0;
3686 while (!checkEndOfFile(file))
3688 // level file might contain invalid change page number
3689 if (xx_current_change_page >= ei->num_change_pages)
3692 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3694 xx_change = *change; // copy change data into temporary buffer
3696 resetEventBits(); // reset bits; change page might have changed
3698 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3701 *change = xx_change;
3703 setEventFlagsFromEventBits(change);
3705 if (real_chunk_size >= chunk_size)
3709 level->file_has_custom_elements = TRUE;
3711 return real_chunk_size;
3714 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3716 int element = getMappedElement(getFile16BitBE(file));
3717 int real_chunk_size = 2;
3718 struct ElementInfo *ei = &element_info[element];
3719 struct ElementGroupInfo *group = ei->group;
3724 xx_ei = *ei; // copy element data into temporary buffer
3725 xx_group = *group; // copy group data into temporary buffer
3727 while (!checkEndOfFile(file))
3729 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3732 if (real_chunk_size >= chunk_size)
3739 level->file_has_custom_elements = TRUE;
3741 return real_chunk_size;
3744 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3746 int element = getMappedElement(getFile16BitBE(file));
3747 int real_chunk_size = 2;
3748 struct ElementInfo *ei = &element_info[element];
3750 xx_ei = *ei; // copy element data into temporary buffer
3752 while (!checkEndOfFile(file))
3754 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3757 if (real_chunk_size >= chunk_size)
3763 level->file_has_custom_elements = TRUE;
3765 return real_chunk_size;
3768 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3769 struct LevelFileInfo *level_file_info,
3770 boolean level_info_only)
3772 char *filename = level_file_info->filename;
3773 char cookie[MAX_LINE_LEN];
3774 char chunk_name[CHUNK_ID_LEN + 1];
3778 if (!(file = openFile(filename, MODE_READ)))
3780 level->no_valid_file = TRUE;
3781 level->no_level_file = TRUE;
3783 if (level_info_only)
3786 Warn("cannot read level '%s' -- using empty level", filename);
3788 if (!setup.editor.use_template_for_new_levels)
3791 // if level file not found, try to initialize level data from template
3792 filename = getGlobalLevelTemplateFilename();
3794 if (!(file = openFile(filename, MODE_READ)))
3797 // default: for empty levels, use level template for custom elements
3798 level->use_custom_template = TRUE;
3800 level->no_valid_file = FALSE;
3803 getFileChunkBE(file, chunk_name, NULL);
3804 if (strEqual(chunk_name, "RND1"))
3806 getFile32BitBE(file); // not used
3808 getFileChunkBE(file, chunk_name, NULL);
3809 if (!strEqual(chunk_name, "CAVE"))
3811 level->no_valid_file = TRUE;
3813 Warn("unknown format of level file '%s'", filename);
3820 else // check for pre-2.0 file format with cookie string
3822 strcpy(cookie, chunk_name);
3823 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3825 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3826 cookie[strlen(cookie) - 1] = '\0';
3828 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3830 level->no_valid_file = TRUE;
3832 Warn("unknown format of level file '%s'", filename);
3839 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3841 level->no_valid_file = TRUE;
3843 Warn("unsupported version of level file '%s'", filename);
3850 // pre-2.0 level files have no game version, so use file version here
3851 level->game_version = level->file_version;
3854 if (level->file_version < FILE_VERSION_1_2)
3856 // level files from versions before 1.2.0 without chunk structure
3857 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3858 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3866 int (*loader)(File *, int, struct LevelInfo *);
3870 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3871 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3872 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3873 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3874 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3875 { "INFO", -1, LoadLevel_INFO },
3876 { "BODY", -1, LoadLevel_BODY },
3877 { "CONT", -1, LoadLevel_CONT },
3878 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3879 { "CNT3", -1, LoadLevel_CNT3 },
3880 { "CUS1", -1, LoadLevel_CUS1 },
3881 { "CUS2", -1, LoadLevel_CUS2 },
3882 { "CUS3", -1, LoadLevel_CUS3 },
3883 { "CUS4", -1, LoadLevel_CUS4 },
3884 { "GRP1", -1, LoadLevel_GRP1 },
3885 { "CONF", -1, LoadLevel_CONF },
3886 { "ELEM", -1, LoadLevel_ELEM },
3887 { "NOTE", -1, LoadLevel_NOTE },
3888 { "CUSX", -1, LoadLevel_CUSX },
3889 { "GRPX", -1, LoadLevel_GRPX },
3890 { "EMPX", -1, LoadLevel_EMPX },
3895 while (getFileChunkBE(file, chunk_name, &chunk_size))
3899 while (chunk_info[i].name != NULL &&
3900 !strEqual(chunk_name, chunk_info[i].name))
3903 if (chunk_info[i].name == NULL)
3905 Warn("unknown chunk '%s' in level file '%s'",
3906 chunk_name, filename);
3908 ReadUnusedBytesFromFile(file, chunk_size);
3910 else if (chunk_info[i].size != -1 &&
3911 chunk_info[i].size != chunk_size)
3913 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3914 chunk_size, chunk_name, filename);
3916 ReadUnusedBytesFromFile(file, chunk_size);
3920 // call function to load this level chunk
3921 int chunk_size_expected =
3922 (chunk_info[i].loader)(file, chunk_size, level);
3924 if (chunk_size_expected < 0)
3926 Warn("error reading chunk '%s' in level file '%s'",
3927 chunk_name, filename);
3932 // the size of some chunks cannot be checked before reading other
3933 // chunks first (like "HEAD" and "BODY") that contain some header
3934 // information, so check them here
3935 if (chunk_size_expected != chunk_size)
3937 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3938 chunk_size, chunk_name, filename);
3950 // ----------------------------------------------------------------------------
3951 // functions for loading BD level
3952 // ----------------------------------------------------------------------------
3954 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3956 struct LevelInfo_BD *level_bd = level->native_bd_level;
3957 GdCave *cave = NULL; // will be changed below
3958 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3959 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3962 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3964 // cave and map newly allocated when set to defaults above
3965 cave = level_bd->cave;
3968 cave->intermission = level->bd_intermission;
3971 cave->level_time[0] = level->time;
3972 cave->level_diamonds[0] = level->gems_needed;
3975 cave->scheduling = level->bd_scheduling_type;
3976 cave->pal_timing = level->bd_pal_timing;
3977 cave->level_speed[0] = level->bd_cycle_delay_ms;
3978 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3979 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3980 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3983 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3984 cave->diamond_value = level->score[SC_EMERALD];
3985 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3987 // compatibility settings
3988 cave->lineshift = level->bd_line_shifting_borders;
3989 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3990 cave->short_explosions = level->bd_short_explosions;
3991 cave->gravity_affects_all = level->bd_gravity_affects_all;
3993 // player properties
3994 cave->diagonal_movements = level->bd_diagonal_movements;
3995 cave->active_is_first_found = level->bd_topmost_player_active;
3996 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3997 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3998 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3999 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
4001 // element properties
4002 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4003 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4004 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4005 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4006 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4007 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4008 cave->level_magic_wall_time[0] = level->time_magic_wall;
4009 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4010 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4011 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4012 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4013 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4014 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4015 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4016 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4017 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4018 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4019 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4020 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4021 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4023 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4024 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4025 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4026 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4027 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4028 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4030 cave->slime_predictable = level->bd_slime_is_predictable;
4031 cave->slime_correct_random = level->bd_slime_correct_random;
4032 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4033 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4034 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4035 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4037 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4038 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4039 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4041 cave->biter_delay_frame = level->bd_biter_move_delay;
4042 cave->biter_eat = map_element_RND_to_BD(level->bd_biter_eats_element);
4044 cave->bladder_converts_by = map_element_RND_to_BD(level->bd_bladder_converts_by_element);
4046 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4049 strncpy(cave->name, level->name, sizeof(GdString));
4050 cave->name[sizeof(GdString) - 1] = '\0';
4052 // playfield elements
4053 for (x = 0; x < cave->w; x++)
4054 for (y = 0; y < cave->h; y++)
4055 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4058 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4060 struct LevelInfo_BD *level_bd = level->native_bd_level;
4061 GdCave *cave = level_bd->cave;
4062 int bd_level_nr = level_bd->level_nr;
4065 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4066 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4069 level->bd_intermission = cave->intermission;
4072 level->time = cave->level_time[bd_level_nr];
4073 level->gems_needed = cave->level_diamonds[bd_level_nr];
4076 level->bd_scheduling_type = cave->scheduling;
4077 level->bd_pal_timing = cave->pal_timing;
4078 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4079 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4080 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4081 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4084 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4085 level->score[SC_EMERALD] = cave->diamond_value;
4086 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4088 // compatibility settings
4089 level->bd_line_shifting_borders = cave->lineshift;
4090 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4091 level->bd_short_explosions = cave->short_explosions;
4092 level->bd_gravity_affects_all = cave->gravity_affects_all;
4094 // player properties
4095 level->bd_diagonal_movements = cave->diagonal_movements;
4096 level->bd_topmost_player_active = cave->active_is_first_found;
4097 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4098 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4099 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4100 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4102 // element properties
4103 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4104 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4105 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4106 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4107 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4108 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4109 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4110 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4111 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4112 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4113 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4114 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4115 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4116 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4117 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4118 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4119 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4120 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4121 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4122 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4124 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4125 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4126 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4127 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4128 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4129 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4131 level->bd_slime_is_predictable = cave->slime_predictable;
4132 level->bd_slime_correct_random = cave->slime_correct_random;
4133 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4134 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4135 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4136 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4138 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4139 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4140 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4142 level->bd_biter_move_delay = cave->biter_delay_frame;
4143 level->bd_biter_eats_element = map_element_BD_to_RND(cave->biter_eat);
4145 level->bd_bladder_converts_by_element = map_element_BD_to_RND(cave->bladder_converts_by);
4147 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4150 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4152 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4153 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4155 // playfield elements
4156 for (x = 0; x < level->fieldx; x++)
4157 for (y = 0; y < level->fieldy; y++)
4158 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4160 checked_free(cave_name);
4163 static void setTapeInfoToDefaults(void);
4165 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4167 struct LevelInfo_BD *level_bd = level->native_bd_level;
4168 GdCave *cave = level_bd->cave;
4169 GdReplay *replay = level_bd->replay;
4175 // always start with reliable default values
4176 setTapeInfoToDefaults();
4178 tape.level_nr = level_nr; // (currently not used)
4179 tape.random_seed = replay->seed;
4181 TapeSetDateFromIsoDateString(replay->date);
4184 tape.pos[tape.counter].delay = 0;
4186 tape.bd_replay = TRUE;
4188 // all time calculations only used to display approximate tape time
4189 int cave_speed = cave->speed;
4190 int milliseconds_game = 0;
4191 int milliseconds_elapsed = 20;
4193 for (i = 0; i < replay->movements->len; i++)
4195 int replay_action = replay->movements->data[i];
4196 int tape_action = map_action_BD_to_RND(replay_action);
4197 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4198 boolean success = 0;
4202 success = TapeAddAction(action);
4204 milliseconds_game += milliseconds_elapsed;
4206 if (milliseconds_game >= cave_speed)
4208 milliseconds_game -= cave_speed;
4215 tape.pos[tape.counter].delay = 0;
4216 tape.pos[tape.counter].action[0] = 0;
4220 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4226 TapeHaltRecording();
4230 // ----------------------------------------------------------------------------
4231 // functions for loading EM level
4232 // ----------------------------------------------------------------------------
4234 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4236 static int ball_xy[8][2] =
4247 struct LevelInfo_EM *level_em = level->native_em_level;
4248 struct CAVE *cav = level_em->cav;
4251 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4252 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4254 cav->time_seconds = level->time;
4255 cav->gems_needed = level->gems_needed;
4257 cav->emerald_score = level->score[SC_EMERALD];
4258 cav->diamond_score = level->score[SC_DIAMOND];
4259 cav->alien_score = level->score[SC_ROBOT];
4260 cav->tank_score = level->score[SC_SPACESHIP];
4261 cav->bug_score = level->score[SC_BUG];
4262 cav->eater_score = level->score[SC_YAMYAM];
4263 cav->nut_score = level->score[SC_NUT];
4264 cav->dynamite_score = level->score[SC_DYNAMITE];
4265 cav->key_score = level->score[SC_KEY];
4266 cav->exit_score = level->score[SC_TIME_BONUS];
4268 cav->num_eater_arrays = level->num_yamyam_contents;
4270 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4271 for (y = 0; y < 3; y++)
4272 for (x = 0; x < 3; x++)
4273 cav->eater_array[i][y * 3 + x] =
4274 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4276 cav->amoeba_time = level->amoeba_speed;
4277 cav->wonderwall_time = level->time_magic_wall;
4278 cav->wheel_time = level->time_wheel;
4280 cav->android_move_time = level->android_move_time;
4281 cav->android_clone_time = level->android_clone_time;
4282 cav->ball_random = level->ball_random;
4283 cav->ball_active = level->ball_active_initial;
4284 cav->ball_time = level->ball_time;
4285 cav->num_ball_arrays = level->num_ball_contents;
4287 cav->lenses_score = level->lenses_score;
4288 cav->magnify_score = level->magnify_score;
4289 cav->slurp_score = level->slurp_score;
4291 cav->lenses_time = level->lenses_time;
4292 cav->magnify_time = level->magnify_time;
4294 cav->wind_time = 9999;
4295 cav->wind_direction =
4296 map_direction_RND_to_EM(level->wind_direction_initial);
4298 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4299 for (j = 0; j < 8; j++)
4300 cav->ball_array[i][j] =
4301 map_element_RND_to_EM_cave(level->ball_content[i].
4302 e[ball_xy[j][0]][ball_xy[j][1]]);
4304 map_android_clone_elements_RND_to_EM(level);
4306 // first fill the complete playfield with the empty space element
4307 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4308 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4309 cav->cave[x][y] = Cblank;
4311 // then copy the real level contents from level file into the playfield
4312 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4314 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4316 if (level->field[x][y] == EL_AMOEBA_DEAD)
4317 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4319 cav->cave[x][y] = new_element;
4322 for (i = 0; i < MAX_PLAYERS; i++)
4324 cav->player_x[i] = -1;
4325 cav->player_y[i] = -1;
4328 // initialize player positions and delete players from the playfield
4329 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4331 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4333 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4335 cav->player_x[player_nr] = x;
4336 cav->player_y[player_nr] = y;
4338 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4343 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4345 static int ball_xy[8][2] =
4356 struct LevelInfo_EM *level_em = level->native_em_level;
4357 struct CAVE *cav = level_em->cav;
4360 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4361 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4363 level->time = cav->time_seconds;
4364 level->gems_needed = cav->gems_needed;
4366 sprintf(level->name, "Level %d", level->file_info.nr);
4368 level->score[SC_EMERALD] = cav->emerald_score;
4369 level->score[SC_DIAMOND] = cav->diamond_score;
4370 level->score[SC_ROBOT] = cav->alien_score;
4371 level->score[SC_SPACESHIP] = cav->tank_score;
4372 level->score[SC_BUG] = cav->bug_score;
4373 level->score[SC_YAMYAM] = cav->eater_score;
4374 level->score[SC_NUT] = cav->nut_score;
4375 level->score[SC_DYNAMITE] = cav->dynamite_score;
4376 level->score[SC_KEY] = cav->key_score;
4377 level->score[SC_TIME_BONUS] = cav->exit_score;
4379 level->num_yamyam_contents = cav->num_eater_arrays;
4381 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4382 for (y = 0; y < 3; y++)
4383 for (x = 0; x < 3; x++)
4384 level->yamyam_content[i].e[x][y] =
4385 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4387 level->amoeba_speed = cav->amoeba_time;
4388 level->time_magic_wall = cav->wonderwall_time;
4389 level->time_wheel = cav->wheel_time;
4391 level->android_move_time = cav->android_move_time;
4392 level->android_clone_time = cav->android_clone_time;
4393 level->ball_random = cav->ball_random;
4394 level->ball_active_initial = cav->ball_active;
4395 level->ball_time = cav->ball_time;
4396 level->num_ball_contents = cav->num_ball_arrays;
4398 level->lenses_score = cav->lenses_score;
4399 level->magnify_score = cav->magnify_score;
4400 level->slurp_score = cav->slurp_score;
4402 level->lenses_time = cav->lenses_time;
4403 level->magnify_time = cav->magnify_time;
4405 level->wind_direction_initial =
4406 map_direction_EM_to_RND(cav->wind_direction);
4408 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4409 for (j = 0; j < 8; j++)
4410 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4411 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4413 map_android_clone_elements_EM_to_RND(level);
4415 // convert the playfield (some elements need special treatment)
4416 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4418 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4420 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4421 new_element = EL_AMOEBA_DEAD;
4423 level->field[x][y] = new_element;
4426 for (i = 0; i < MAX_PLAYERS; i++)
4428 // in case of all players set to the same field, use the first player
4429 int nr = MAX_PLAYERS - i - 1;
4430 int jx = cav->player_x[nr];
4431 int jy = cav->player_y[nr];
4433 if (jx != -1 && jy != -1)
4434 level->field[jx][jy] = EL_PLAYER_1 + nr;
4437 // time score is counted for each 10 seconds left in Emerald Mine levels
4438 level->time_score_base = 10;
4442 // ----------------------------------------------------------------------------
4443 // functions for loading SP level
4444 // ----------------------------------------------------------------------------
4446 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4448 struct LevelInfo_SP *level_sp = level->native_sp_level;
4449 LevelInfoType *header = &level_sp->header;
4452 level_sp->width = level->fieldx;
4453 level_sp->height = level->fieldy;
4455 for (x = 0; x < level->fieldx; x++)
4456 for (y = 0; y < level->fieldy; y++)
4457 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4459 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4461 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4462 header->LevelTitle[i] = level->name[i];
4463 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4465 header->InfotronsNeeded = level->gems_needed;
4467 header->SpecialPortCount = 0;
4469 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4471 boolean gravity_port_found = FALSE;
4472 boolean gravity_port_valid = FALSE;
4473 int gravity_port_flag;
4474 int gravity_port_base_element;
4475 int element = level->field[x][y];
4477 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4478 element <= EL_SP_GRAVITY_ON_PORT_UP)
4480 gravity_port_found = TRUE;
4481 gravity_port_valid = TRUE;
4482 gravity_port_flag = 1;
4483 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4485 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4486 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4488 gravity_port_found = TRUE;
4489 gravity_port_valid = TRUE;
4490 gravity_port_flag = 0;
4491 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4493 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4494 element <= EL_SP_GRAVITY_PORT_UP)
4496 // change R'n'D style gravity inverting special port to normal port
4497 // (there are no gravity inverting ports in native Supaplex engine)
4499 gravity_port_found = TRUE;
4500 gravity_port_valid = FALSE;
4501 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4504 if (gravity_port_found)
4506 if (gravity_port_valid &&
4507 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4509 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4511 port->PortLocation = (y * level->fieldx + x) * 2;
4512 port->Gravity = gravity_port_flag;
4514 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4516 header->SpecialPortCount++;
4520 // change special gravity port to normal port
4522 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4525 level_sp->playfield[x][y] = element - EL_SP_START;
4530 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4532 struct LevelInfo_SP *level_sp = level->native_sp_level;
4533 LevelInfoType *header = &level_sp->header;
4534 boolean num_invalid_elements = 0;
4537 level->fieldx = level_sp->width;
4538 level->fieldy = level_sp->height;
4540 for (x = 0; x < level->fieldx; x++)
4542 for (y = 0; y < level->fieldy; y++)
4544 int element_old = level_sp->playfield[x][y];
4545 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4547 if (element_new == EL_UNKNOWN)
4549 num_invalid_elements++;
4551 Debug("level:native:SP", "invalid element %d at position %d, %d",
4555 level->field[x][y] = element_new;
4559 if (num_invalid_elements > 0)
4560 Warn("found %d invalid elements%s", num_invalid_elements,
4561 (!options.debug ? " (use '--debug' for more details)" : ""));
4563 for (i = 0; i < MAX_PLAYERS; i++)
4564 level->initial_player_gravity[i] =
4565 (header->InitialGravity == 1 ? TRUE : FALSE);
4567 // skip leading spaces
4568 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4569 if (header->LevelTitle[i] != ' ')
4573 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4574 level->name[j] = header->LevelTitle[i];
4575 level->name[j] = '\0';
4577 // cut trailing spaces
4579 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4580 level->name[j - 1] = '\0';
4582 level->gems_needed = header->InfotronsNeeded;
4584 for (i = 0; i < header->SpecialPortCount; i++)
4586 SpecialPortType *port = &header->SpecialPort[i];
4587 int port_location = port->PortLocation;
4588 int gravity = port->Gravity;
4589 int port_x, port_y, port_element;
4591 port_x = (port_location / 2) % level->fieldx;
4592 port_y = (port_location / 2) / level->fieldx;
4594 if (port_x < 0 || port_x >= level->fieldx ||
4595 port_y < 0 || port_y >= level->fieldy)
4597 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4602 port_element = level->field[port_x][port_y];
4604 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4605 port_element > EL_SP_GRAVITY_PORT_UP)
4607 Warn("no special port at position (%d, %d)", port_x, port_y);
4612 // change previous (wrong) gravity inverting special port to either
4613 // gravity enabling special port or gravity disabling special port
4614 level->field[port_x][port_y] +=
4615 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4616 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4619 // change special gravity ports without database entries to normal ports
4620 for (x = 0; x < level->fieldx; x++)
4621 for (y = 0; y < level->fieldy; y++)
4622 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4623 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4624 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4626 level->time = 0; // no time limit
4627 level->amoeba_speed = 0;
4628 level->time_magic_wall = 0;
4629 level->time_wheel = 0;
4630 level->amoeba_content = EL_EMPTY;
4632 // original Supaplex does not use score values -- rate by playing time
4633 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4634 level->score[i] = 0;
4636 level->rate_time_over_score = TRUE;
4638 // there are no yamyams in supaplex levels
4639 for (i = 0; i < level->num_yamyam_contents; i++)
4640 for (x = 0; x < 3; x++)
4641 for (y = 0; y < 3; y++)
4642 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4645 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4647 struct LevelInfo_SP *level_sp = level->native_sp_level;
4648 struct DemoInfo_SP *demo = &level_sp->demo;
4651 // always start with reliable default values
4652 demo->is_available = FALSE;
4655 if (TAPE_IS_EMPTY(tape))
4658 demo->level_nr = tape.level_nr; // (currently not used)
4660 level_sp->header.DemoRandomSeed = tape.random_seed;
4664 for (i = 0; i < tape.length; i++)
4666 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4667 int demo_repeat = tape.pos[i].delay;
4668 int demo_entries = (demo_repeat + 15) / 16;
4670 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4672 Warn("tape truncated: size exceeds maximum SP demo size %d",
4678 for (j = 0; j < demo_repeat / 16; j++)
4679 demo->data[demo->length++] = 0xf0 | demo_action;
4681 if (demo_repeat % 16)
4682 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4685 demo->is_available = TRUE;
4688 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4690 struct LevelInfo_SP *level_sp = level->native_sp_level;
4691 struct DemoInfo_SP *demo = &level_sp->demo;
4692 char *filename = level->file_info.filename;
4695 // always start with reliable default values
4696 setTapeInfoToDefaults();
4698 if (!demo->is_available)
4701 tape.level_nr = demo->level_nr; // (currently not used)
4702 tape.random_seed = level_sp->header.DemoRandomSeed;
4704 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4707 tape.pos[tape.counter].delay = 0;
4709 for (i = 0; i < demo->length; i++)
4711 int demo_action = demo->data[i] & 0x0f;
4712 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4713 int tape_action = map_key_SP_to_RND(demo_action);
4714 int tape_repeat = demo_repeat + 1;
4715 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4716 boolean success = 0;
4719 for (j = 0; j < tape_repeat; j++)
4720 success = TapeAddAction(action);
4724 Warn("SP demo truncated: size exceeds maximum tape size %d",
4731 TapeHaltRecording();
4735 // ----------------------------------------------------------------------------
4736 // functions for loading MM level
4737 // ----------------------------------------------------------------------------
4739 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4741 struct LevelInfo_MM *level_mm = level->native_mm_level;
4744 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4745 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4747 level_mm->time = level->time;
4748 level_mm->kettles_needed = level->gems_needed;
4749 level_mm->auto_count_kettles = level->auto_count_gems;
4751 level_mm->mm_laser_red = level->mm_laser_red;
4752 level_mm->mm_laser_green = level->mm_laser_green;
4753 level_mm->mm_laser_blue = level->mm_laser_blue;
4755 level_mm->df_laser_red = level->df_laser_red;
4756 level_mm->df_laser_green = level->df_laser_green;
4757 level_mm->df_laser_blue = level->df_laser_blue;
4759 strcpy(level_mm->name, level->name);
4760 strcpy(level_mm->author, level->author);
4762 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4763 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4764 level_mm->score[SC_KEY] = level->score[SC_KEY];
4765 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4766 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4768 level_mm->amoeba_speed = level->amoeba_speed;
4769 level_mm->time_fuse = level->mm_time_fuse;
4770 level_mm->time_bomb = level->mm_time_bomb;
4771 level_mm->time_ball = level->mm_time_ball;
4772 level_mm->time_block = level->mm_time_block;
4774 level_mm->num_ball_contents = level->num_mm_ball_contents;
4775 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4776 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4777 level_mm->explode_ball = level->explode_mm_ball;
4779 for (i = 0; i < level->num_mm_ball_contents; i++)
4780 level_mm->ball_content[i] =
4781 map_element_RND_to_MM(level->mm_ball_content[i]);
4783 for (x = 0; x < level->fieldx; x++)
4784 for (y = 0; y < level->fieldy; y++)
4786 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4789 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4791 struct LevelInfo_MM *level_mm = level->native_mm_level;
4794 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4795 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4797 level->time = level_mm->time;
4798 level->gems_needed = level_mm->kettles_needed;
4799 level->auto_count_gems = level_mm->auto_count_kettles;
4801 level->mm_laser_red = level_mm->mm_laser_red;
4802 level->mm_laser_green = level_mm->mm_laser_green;
4803 level->mm_laser_blue = level_mm->mm_laser_blue;
4805 level->df_laser_red = level_mm->df_laser_red;
4806 level->df_laser_green = level_mm->df_laser_green;
4807 level->df_laser_blue = level_mm->df_laser_blue;
4809 strcpy(level->name, level_mm->name);
4811 // only overwrite author from 'levelinfo.conf' if author defined in level
4812 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4813 strcpy(level->author, level_mm->author);
4815 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4816 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4817 level->score[SC_KEY] = level_mm->score[SC_KEY];
4818 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4819 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4821 level->amoeba_speed = level_mm->amoeba_speed;
4822 level->mm_time_fuse = level_mm->time_fuse;
4823 level->mm_time_bomb = level_mm->time_bomb;
4824 level->mm_time_ball = level_mm->time_ball;
4825 level->mm_time_block = level_mm->time_block;
4827 level->num_mm_ball_contents = level_mm->num_ball_contents;
4828 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4829 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4830 level->explode_mm_ball = level_mm->explode_ball;
4832 for (i = 0; i < level->num_mm_ball_contents; i++)
4833 level->mm_ball_content[i] =
4834 map_element_MM_to_RND(level_mm->ball_content[i]);
4836 for (x = 0; x < level->fieldx; x++)
4837 for (y = 0; y < level->fieldy; y++)
4838 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4842 // ----------------------------------------------------------------------------
4843 // functions for loading DC level
4844 // ----------------------------------------------------------------------------
4846 #define DC_LEVEL_HEADER_SIZE 344
4848 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4851 static int last_data_encoded;
4855 int diff_hi, diff_lo;
4856 int data_hi, data_lo;
4857 unsigned short data_decoded;
4861 last_data_encoded = 0;
4868 diff = data_encoded - last_data_encoded;
4869 diff_hi = diff & ~0xff;
4870 diff_lo = diff & 0xff;
4874 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4875 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4876 data_hi = data_hi & 0xff00;
4878 data_decoded = data_hi | data_lo;
4880 last_data_encoded = data_encoded;
4882 offset1 = (offset1 + 1) % 31;
4883 offset2 = offset2 & 0xff;
4885 return data_decoded;
4888 static int getMappedElement_DC(int element)
4896 // 0x0117 - 0x036e: (?)
4899 // 0x042d - 0x0684: (?)
4915 element = EL_CRYSTAL;
4918 case 0x0e77: // quicksand (boulder)
4919 element = EL_QUICKSAND_FAST_FULL;
4922 case 0x0e99: // slow quicksand (boulder)
4923 element = EL_QUICKSAND_FULL;
4927 element = EL_EM_EXIT_OPEN;
4931 element = EL_EM_EXIT_CLOSED;
4935 element = EL_EM_STEEL_EXIT_OPEN;
4939 element = EL_EM_STEEL_EXIT_CLOSED;
4942 case 0x0f4f: // dynamite (lit 1)
4943 element = EL_EM_DYNAMITE_ACTIVE;
4946 case 0x0f57: // dynamite (lit 2)
4947 element = EL_EM_DYNAMITE_ACTIVE;
4950 case 0x0f5f: // dynamite (lit 3)
4951 element = EL_EM_DYNAMITE_ACTIVE;
4954 case 0x0f67: // dynamite (lit 4)
4955 element = EL_EM_DYNAMITE_ACTIVE;
4962 element = EL_AMOEBA_WET;
4966 element = EL_AMOEBA_DROP;
4970 element = EL_DC_MAGIC_WALL;
4974 element = EL_SPACESHIP_UP;
4978 element = EL_SPACESHIP_DOWN;
4982 element = EL_SPACESHIP_LEFT;
4986 element = EL_SPACESHIP_RIGHT;
4990 element = EL_BUG_UP;
4994 element = EL_BUG_DOWN;
4998 element = EL_BUG_LEFT;
5002 element = EL_BUG_RIGHT;
5006 element = EL_MOLE_UP;
5010 element = EL_MOLE_DOWN;
5014 element = EL_MOLE_LEFT;
5018 element = EL_MOLE_RIGHT;
5026 element = EL_YAMYAM_UP;
5030 element = EL_SWITCHGATE_OPEN;
5034 element = EL_SWITCHGATE_CLOSED;
5038 element = EL_DC_SWITCHGATE_SWITCH_UP;
5042 element = EL_TIMEGATE_CLOSED;
5045 case 0x144c: // conveyor belt switch (green)
5046 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5049 case 0x144f: // conveyor belt switch (red)
5050 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5053 case 0x1452: // conveyor belt switch (blue)
5054 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5058 element = EL_CONVEYOR_BELT_3_MIDDLE;
5062 element = EL_CONVEYOR_BELT_3_LEFT;
5066 element = EL_CONVEYOR_BELT_3_RIGHT;
5070 element = EL_CONVEYOR_BELT_1_MIDDLE;
5074 element = EL_CONVEYOR_BELT_1_LEFT;
5078 element = EL_CONVEYOR_BELT_1_RIGHT;
5082 element = EL_CONVEYOR_BELT_4_MIDDLE;
5086 element = EL_CONVEYOR_BELT_4_LEFT;
5090 element = EL_CONVEYOR_BELT_4_RIGHT;
5094 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5098 element = EL_EXPANDABLE_WALL_VERTICAL;
5102 element = EL_EXPANDABLE_WALL_ANY;
5105 case 0x14ce: // growing steel wall (left/right)
5106 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5109 case 0x14df: // growing steel wall (up/down)
5110 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5113 case 0x14e8: // growing steel wall (up/down/left/right)
5114 element = EL_EXPANDABLE_STEELWALL_ANY;
5118 element = EL_SHIELD_DEADLY;
5122 element = EL_EXTRA_TIME;
5130 element = EL_EMPTY_SPACE;
5133 case 0x1578: // quicksand (empty)
5134 element = EL_QUICKSAND_FAST_EMPTY;
5137 case 0x1579: // slow quicksand (empty)
5138 element = EL_QUICKSAND_EMPTY;
5148 element = EL_EM_DYNAMITE;
5151 case 0x15a1: // key (red)
5152 element = EL_EM_KEY_1;
5155 case 0x15a2: // key (yellow)
5156 element = EL_EM_KEY_2;
5159 case 0x15a3: // key (blue)
5160 element = EL_EM_KEY_4;
5163 case 0x15a4: // key (green)
5164 element = EL_EM_KEY_3;
5167 case 0x15a5: // key (white)
5168 element = EL_DC_KEY_WHITE;
5172 element = EL_WALL_SLIPPERY;
5179 case 0x15a8: // wall (not round)
5183 case 0x15a9: // (blue)
5184 element = EL_CHAR_A;
5187 case 0x15aa: // (blue)
5188 element = EL_CHAR_B;
5191 case 0x15ab: // (blue)
5192 element = EL_CHAR_C;
5195 case 0x15ac: // (blue)
5196 element = EL_CHAR_D;
5199 case 0x15ad: // (blue)
5200 element = EL_CHAR_E;
5203 case 0x15ae: // (blue)
5204 element = EL_CHAR_F;
5207 case 0x15af: // (blue)
5208 element = EL_CHAR_G;
5211 case 0x15b0: // (blue)
5212 element = EL_CHAR_H;
5215 case 0x15b1: // (blue)
5216 element = EL_CHAR_I;
5219 case 0x15b2: // (blue)
5220 element = EL_CHAR_J;
5223 case 0x15b3: // (blue)
5224 element = EL_CHAR_K;
5227 case 0x15b4: // (blue)
5228 element = EL_CHAR_L;
5231 case 0x15b5: // (blue)
5232 element = EL_CHAR_M;
5235 case 0x15b6: // (blue)
5236 element = EL_CHAR_N;
5239 case 0x15b7: // (blue)
5240 element = EL_CHAR_O;
5243 case 0x15b8: // (blue)
5244 element = EL_CHAR_P;
5247 case 0x15b9: // (blue)
5248 element = EL_CHAR_Q;
5251 case 0x15ba: // (blue)
5252 element = EL_CHAR_R;
5255 case 0x15bb: // (blue)
5256 element = EL_CHAR_S;
5259 case 0x15bc: // (blue)
5260 element = EL_CHAR_T;
5263 case 0x15bd: // (blue)
5264 element = EL_CHAR_U;
5267 case 0x15be: // (blue)
5268 element = EL_CHAR_V;
5271 case 0x15bf: // (blue)
5272 element = EL_CHAR_W;
5275 case 0x15c0: // (blue)
5276 element = EL_CHAR_X;
5279 case 0x15c1: // (blue)
5280 element = EL_CHAR_Y;
5283 case 0x15c2: // (blue)
5284 element = EL_CHAR_Z;
5287 case 0x15c3: // (blue)
5288 element = EL_CHAR_AUMLAUT;
5291 case 0x15c4: // (blue)
5292 element = EL_CHAR_OUMLAUT;
5295 case 0x15c5: // (blue)
5296 element = EL_CHAR_UUMLAUT;
5299 case 0x15c6: // (blue)
5300 element = EL_CHAR_0;
5303 case 0x15c7: // (blue)
5304 element = EL_CHAR_1;
5307 case 0x15c8: // (blue)
5308 element = EL_CHAR_2;
5311 case 0x15c9: // (blue)
5312 element = EL_CHAR_3;
5315 case 0x15ca: // (blue)
5316 element = EL_CHAR_4;
5319 case 0x15cb: // (blue)
5320 element = EL_CHAR_5;
5323 case 0x15cc: // (blue)
5324 element = EL_CHAR_6;
5327 case 0x15cd: // (blue)
5328 element = EL_CHAR_7;
5331 case 0x15ce: // (blue)
5332 element = EL_CHAR_8;
5335 case 0x15cf: // (blue)
5336 element = EL_CHAR_9;
5339 case 0x15d0: // (blue)
5340 element = EL_CHAR_PERIOD;
5343 case 0x15d1: // (blue)
5344 element = EL_CHAR_EXCLAM;
5347 case 0x15d2: // (blue)
5348 element = EL_CHAR_COLON;
5351 case 0x15d3: // (blue)
5352 element = EL_CHAR_LESS;
5355 case 0x15d4: // (blue)
5356 element = EL_CHAR_GREATER;
5359 case 0x15d5: // (blue)
5360 element = EL_CHAR_QUESTION;
5363 case 0x15d6: // (blue)
5364 element = EL_CHAR_COPYRIGHT;
5367 case 0x15d7: // (blue)
5368 element = EL_CHAR_UP;
5371 case 0x15d8: // (blue)
5372 element = EL_CHAR_DOWN;
5375 case 0x15d9: // (blue)
5376 element = EL_CHAR_BUTTON;
5379 case 0x15da: // (blue)
5380 element = EL_CHAR_PLUS;
5383 case 0x15db: // (blue)
5384 element = EL_CHAR_MINUS;
5387 case 0x15dc: // (blue)
5388 element = EL_CHAR_APOSTROPHE;
5391 case 0x15dd: // (blue)
5392 element = EL_CHAR_PARENLEFT;
5395 case 0x15de: // (blue)
5396 element = EL_CHAR_PARENRIGHT;
5399 case 0x15df: // (green)
5400 element = EL_CHAR_A;
5403 case 0x15e0: // (green)
5404 element = EL_CHAR_B;
5407 case 0x15e1: // (green)
5408 element = EL_CHAR_C;
5411 case 0x15e2: // (green)
5412 element = EL_CHAR_D;
5415 case 0x15e3: // (green)
5416 element = EL_CHAR_E;
5419 case 0x15e4: // (green)
5420 element = EL_CHAR_F;
5423 case 0x15e5: // (green)
5424 element = EL_CHAR_G;
5427 case 0x15e6: // (green)
5428 element = EL_CHAR_H;
5431 case 0x15e7: // (green)
5432 element = EL_CHAR_I;
5435 case 0x15e8: // (green)
5436 element = EL_CHAR_J;
5439 case 0x15e9: // (green)
5440 element = EL_CHAR_K;
5443 case 0x15ea: // (green)
5444 element = EL_CHAR_L;
5447 case 0x15eb: // (green)
5448 element = EL_CHAR_M;
5451 case 0x15ec: // (green)
5452 element = EL_CHAR_N;
5455 case 0x15ed: // (green)
5456 element = EL_CHAR_O;
5459 case 0x15ee: // (green)
5460 element = EL_CHAR_P;
5463 case 0x15ef: // (green)
5464 element = EL_CHAR_Q;
5467 case 0x15f0: // (green)
5468 element = EL_CHAR_R;
5471 case 0x15f1: // (green)
5472 element = EL_CHAR_S;
5475 case 0x15f2: // (green)
5476 element = EL_CHAR_T;
5479 case 0x15f3: // (green)
5480 element = EL_CHAR_U;
5483 case 0x15f4: // (green)
5484 element = EL_CHAR_V;
5487 case 0x15f5: // (green)
5488 element = EL_CHAR_W;
5491 case 0x15f6: // (green)
5492 element = EL_CHAR_X;
5495 case 0x15f7: // (green)
5496 element = EL_CHAR_Y;
5499 case 0x15f8: // (green)
5500 element = EL_CHAR_Z;
5503 case 0x15f9: // (green)
5504 element = EL_CHAR_AUMLAUT;
5507 case 0x15fa: // (green)
5508 element = EL_CHAR_OUMLAUT;
5511 case 0x15fb: // (green)
5512 element = EL_CHAR_UUMLAUT;
5515 case 0x15fc: // (green)
5516 element = EL_CHAR_0;
5519 case 0x15fd: // (green)
5520 element = EL_CHAR_1;
5523 case 0x15fe: // (green)
5524 element = EL_CHAR_2;
5527 case 0x15ff: // (green)
5528 element = EL_CHAR_3;
5531 case 0x1600: // (green)
5532 element = EL_CHAR_4;
5535 case 0x1601: // (green)
5536 element = EL_CHAR_5;
5539 case 0x1602: // (green)
5540 element = EL_CHAR_6;
5543 case 0x1603: // (green)
5544 element = EL_CHAR_7;
5547 case 0x1604: // (green)
5548 element = EL_CHAR_8;
5551 case 0x1605: // (green)
5552 element = EL_CHAR_9;
5555 case 0x1606: // (green)
5556 element = EL_CHAR_PERIOD;
5559 case 0x1607: // (green)
5560 element = EL_CHAR_EXCLAM;
5563 case 0x1608: // (green)
5564 element = EL_CHAR_COLON;
5567 case 0x1609: // (green)
5568 element = EL_CHAR_LESS;
5571 case 0x160a: // (green)
5572 element = EL_CHAR_GREATER;
5575 case 0x160b: // (green)
5576 element = EL_CHAR_QUESTION;
5579 case 0x160c: // (green)
5580 element = EL_CHAR_COPYRIGHT;
5583 case 0x160d: // (green)
5584 element = EL_CHAR_UP;
5587 case 0x160e: // (green)
5588 element = EL_CHAR_DOWN;
5591 case 0x160f: // (green)
5592 element = EL_CHAR_BUTTON;
5595 case 0x1610: // (green)
5596 element = EL_CHAR_PLUS;
5599 case 0x1611: // (green)
5600 element = EL_CHAR_MINUS;
5603 case 0x1612: // (green)
5604 element = EL_CHAR_APOSTROPHE;
5607 case 0x1613: // (green)
5608 element = EL_CHAR_PARENLEFT;
5611 case 0x1614: // (green)
5612 element = EL_CHAR_PARENRIGHT;
5615 case 0x1615: // (blue steel)
5616 element = EL_STEEL_CHAR_A;
5619 case 0x1616: // (blue steel)
5620 element = EL_STEEL_CHAR_B;
5623 case 0x1617: // (blue steel)
5624 element = EL_STEEL_CHAR_C;
5627 case 0x1618: // (blue steel)
5628 element = EL_STEEL_CHAR_D;
5631 case 0x1619: // (blue steel)
5632 element = EL_STEEL_CHAR_E;
5635 case 0x161a: // (blue steel)
5636 element = EL_STEEL_CHAR_F;
5639 case 0x161b: // (blue steel)
5640 element = EL_STEEL_CHAR_G;
5643 case 0x161c: // (blue steel)
5644 element = EL_STEEL_CHAR_H;
5647 case 0x161d: // (blue steel)
5648 element = EL_STEEL_CHAR_I;
5651 case 0x161e: // (blue steel)
5652 element = EL_STEEL_CHAR_J;
5655 case 0x161f: // (blue steel)
5656 element = EL_STEEL_CHAR_K;
5659 case 0x1620: // (blue steel)
5660 element = EL_STEEL_CHAR_L;
5663 case 0x1621: // (blue steel)
5664 element = EL_STEEL_CHAR_M;
5667 case 0x1622: // (blue steel)
5668 element = EL_STEEL_CHAR_N;
5671 case 0x1623: // (blue steel)
5672 element = EL_STEEL_CHAR_O;
5675 case 0x1624: // (blue steel)
5676 element = EL_STEEL_CHAR_P;
5679 case 0x1625: // (blue steel)
5680 element = EL_STEEL_CHAR_Q;
5683 case 0x1626: // (blue steel)
5684 element = EL_STEEL_CHAR_R;
5687 case 0x1627: // (blue steel)
5688 element = EL_STEEL_CHAR_S;
5691 case 0x1628: // (blue steel)
5692 element = EL_STEEL_CHAR_T;
5695 case 0x1629: // (blue steel)
5696 element = EL_STEEL_CHAR_U;
5699 case 0x162a: // (blue steel)
5700 element = EL_STEEL_CHAR_V;
5703 case 0x162b: // (blue steel)
5704 element = EL_STEEL_CHAR_W;
5707 case 0x162c: // (blue steel)
5708 element = EL_STEEL_CHAR_X;
5711 case 0x162d: // (blue steel)
5712 element = EL_STEEL_CHAR_Y;
5715 case 0x162e: // (blue steel)
5716 element = EL_STEEL_CHAR_Z;
5719 case 0x162f: // (blue steel)
5720 element = EL_STEEL_CHAR_AUMLAUT;
5723 case 0x1630: // (blue steel)
5724 element = EL_STEEL_CHAR_OUMLAUT;
5727 case 0x1631: // (blue steel)
5728 element = EL_STEEL_CHAR_UUMLAUT;
5731 case 0x1632: // (blue steel)
5732 element = EL_STEEL_CHAR_0;
5735 case 0x1633: // (blue steel)
5736 element = EL_STEEL_CHAR_1;
5739 case 0x1634: // (blue steel)
5740 element = EL_STEEL_CHAR_2;
5743 case 0x1635: // (blue steel)
5744 element = EL_STEEL_CHAR_3;
5747 case 0x1636: // (blue steel)
5748 element = EL_STEEL_CHAR_4;
5751 case 0x1637: // (blue steel)
5752 element = EL_STEEL_CHAR_5;
5755 case 0x1638: // (blue steel)
5756 element = EL_STEEL_CHAR_6;
5759 case 0x1639: // (blue steel)
5760 element = EL_STEEL_CHAR_7;
5763 case 0x163a: // (blue steel)
5764 element = EL_STEEL_CHAR_8;
5767 case 0x163b: // (blue steel)
5768 element = EL_STEEL_CHAR_9;
5771 case 0x163c: // (blue steel)
5772 element = EL_STEEL_CHAR_PERIOD;
5775 case 0x163d: // (blue steel)
5776 element = EL_STEEL_CHAR_EXCLAM;
5779 case 0x163e: // (blue steel)
5780 element = EL_STEEL_CHAR_COLON;
5783 case 0x163f: // (blue steel)
5784 element = EL_STEEL_CHAR_LESS;
5787 case 0x1640: // (blue steel)
5788 element = EL_STEEL_CHAR_GREATER;
5791 case 0x1641: // (blue steel)
5792 element = EL_STEEL_CHAR_QUESTION;
5795 case 0x1642: // (blue steel)
5796 element = EL_STEEL_CHAR_COPYRIGHT;
5799 case 0x1643: // (blue steel)
5800 element = EL_STEEL_CHAR_UP;
5803 case 0x1644: // (blue steel)
5804 element = EL_STEEL_CHAR_DOWN;
5807 case 0x1645: // (blue steel)
5808 element = EL_STEEL_CHAR_BUTTON;
5811 case 0x1646: // (blue steel)
5812 element = EL_STEEL_CHAR_PLUS;
5815 case 0x1647: // (blue steel)
5816 element = EL_STEEL_CHAR_MINUS;
5819 case 0x1648: // (blue steel)
5820 element = EL_STEEL_CHAR_APOSTROPHE;
5823 case 0x1649: // (blue steel)
5824 element = EL_STEEL_CHAR_PARENLEFT;
5827 case 0x164a: // (blue steel)
5828 element = EL_STEEL_CHAR_PARENRIGHT;
5831 case 0x164b: // (green steel)
5832 element = EL_STEEL_CHAR_A;
5835 case 0x164c: // (green steel)
5836 element = EL_STEEL_CHAR_B;
5839 case 0x164d: // (green steel)
5840 element = EL_STEEL_CHAR_C;
5843 case 0x164e: // (green steel)
5844 element = EL_STEEL_CHAR_D;
5847 case 0x164f: // (green steel)
5848 element = EL_STEEL_CHAR_E;
5851 case 0x1650: // (green steel)
5852 element = EL_STEEL_CHAR_F;
5855 case 0x1651: // (green steel)
5856 element = EL_STEEL_CHAR_G;
5859 case 0x1652: // (green steel)
5860 element = EL_STEEL_CHAR_H;
5863 case 0x1653: // (green steel)
5864 element = EL_STEEL_CHAR_I;
5867 case 0x1654: // (green steel)
5868 element = EL_STEEL_CHAR_J;
5871 case 0x1655: // (green steel)
5872 element = EL_STEEL_CHAR_K;
5875 case 0x1656: // (green steel)
5876 element = EL_STEEL_CHAR_L;
5879 case 0x1657: // (green steel)
5880 element = EL_STEEL_CHAR_M;
5883 case 0x1658: // (green steel)
5884 element = EL_STEEL_CHAR_N;
5887 case 0x1659: // (green steel)
5888 element = EL_STEEL_CHAR_O;
5891 case 0x165a: // (green steel)
5892 element = EL_STEEL_CHAR_P;
5895 case 0x165b: // (green steel)
5896 element = EL_STEEL_CHAR_Q;
5899 case 0x165c: // (green steel)
5900 element = EL_STEEL_CHAR_R;
5903 case 0x165d: // (green steel)
5904 element = EL_STEEL_CHAR_S;
5907 case 0x165e: // (green steel)
5908 element = EL_STEEL_CHAR_T;
5911 case 0x165f: // (green steel)
5912 element = EL_STEEL_CHAR_U;
5915 case 0x1660: // (green steel)
5916 element = EL_STEEL_CHAR_V;
5919 case 0x1661: // (green steel)
5920 element = EL_STEEL_CHAR_W;
5923 case 0x1662: // (green steel)
5924 element = EL_STEEL_CHAR_X;
5927 case 0x1663: // (green steel)
5928 element = EL_STEEL_CHAR_Y;
5931 case 0x1664: // (green steel)
5932 element = EL_STEEL_CHAR_Z;
5935 case 0x1665: // (green steel)
5936 element = EL_STEEL_CHAR_AUMLAUT;
5939 case 0x1666: // (green steel)
5940 element = EL_STEEL_CHAR_OUMLAUT;
5943 case 0x1667: // (green steel)
5944 element = EL_STEEL_CHAR_UUMLAUT;
5947 case 0x1668: // (green steel)
5948 element = EL_STEEL_CHAR_0;
5951 case 0x1669: // (green steel)
5952 element = EL_STEEL_CHAR_1;
5955 case 0x166a: // (green steel)
5956 element = EL_STEEL_CHAR_2;
5959 case 0x166b: // (green steel)
5960 element = EL_STEEL_CHAR_3;
5963 case 0x166c: // (green steel)
5964 element = EL_STEEL_CHAR_4;
5967 case 0x166d: // (green steel)
5968 element = EL_STEEL_CHAR_5;
5971 case 0x166e: // (green steel)
5972 element = EL_STEEL_CHAR_6;
5975 case 0x166f: // (green steel)
5976 element = EL_STEEL_CHAR_7;
5979 case 0x1670: // (green steel)
5980 element = EL_STEEL_CHAR_8;
5983 case 0x1671: // (green steel)
5984 element = EL_STEEL_CHAR_9;
5987 case 0x1672: // (green steel)
5988 element = EL_STEEL_CHAR_PERIOD;
5991 case 0x1673: // (green steel)
5992 element = EL_STEEL_CHAR_EXCLAM;
5995 case 0x1674: // (green steel)
5996 element = EL_STEEL_CHAR_COLON;
5999 case 0x1675: // (green steel)
6000 element = EL_STEEL_CHAR_LESS;
6003 case 0x1676: // (green steel)
6004 element = EL_STEEL_CHAR_GREATER;
6007 case 0x1677: // (green steel)
6008 element = EL_STEEL_CHAR_QUESTION;
6011 case 0x1678: // (green steel)
6012 element = EL_STEEL_CHAR_COPYRIGHT;
6015 case 0x1679: // (green steel)
6016 element = EL_STEEL_CHAR_UP;
6019 case 0x167a: // (green steel)
6020 element = EL_STEEL_CHAR_DOWN;
6023 case 0x167b: // (green steel)
6024 element = EL_STEEL_CHAR_BUTTON;
6027 case 0x167c: // (green steel)
6028 element = EL_STEEL_CHAR_PLUS;
6031 case 0x167d: // (green steel)
6032 element = EL_STEEL_CHAR_MINUS;
6035 case 0x167e: // (green steel)
6036 element = EL_STEEL_CHAR_APOSTROPHE;
6039 case 0x167f: // (green steel)
6040 element = EL_STEEL_CHAR_PARENLEFT;
6043 case 0x1680: // (green steel)
6044 element = EL_STEEL_CHAR_PARENRIGHT;
6047 case 0x1681: // gate (red)
6048 element = EL_EM_GATE_1;
6051 case 0x1682: // secret gate (red)
6052 element = EL_EM_GATE_1_GRAY;
6055 case 0x1683: // gate (yellow)
6056 element = EL_EM_GATE_2;
6059 case 0x1684: // secret gate (yellow)
6060 element = EL_EM_GATE_2_GRAY;
6063 case 0x1685: // gate (blue)
6064 element = EL_EM_GATE_4;
6067 case 0x1686: // secret gate (blue)
6068 element = EL_EM_GATE_4_GRAY;
6071 case 0x1687: // gate (green)
6072 element = EL_EM_GATE_3;
6075 case 0x1688: // secret gate (green)
6076 element = EL_EM_GATE_3_GRAY;
6079 case 0x1689: // gate (white)
6080 element = EL_DC_GATE_WHITE;
6083 case 0x168a: // secret gate (white)
6084 element = EL_DC_GATE_WHITE_GRAY;
6087 case 0x168b: // secret gate (no key)
6088 element = EL_DC_GATE_FAKE_GRAY;
6092 element = EL_ROBOT_WHEEL;
6096 element = EL_DC_TIMEGATE_SWITCH;
6100 element = EL_ACID_POOL_BOTTOM;
6104 element = EL_ACID_POOL_TOPLEFT;
6108 element = EL_ACID_POOL_TOPRIGHT;
6112 element = EL_ACID_POOL_BOTTOMLEFT;
6116 element = EL_ACID_POOL_BOTTOMRIGHT;
6120 element = EL_STEELWALL;
6124 element = EL_STEELWALL_SLIPPERY;
6127 case 0x1695: // steel wall (not round)
6128 element = EL_STEELWALL;
6131 case 0x1696: // steel wall (left)
6132 element = EL_DC_STEELWALL_1_LEFT;
6135 case 0x1697: // steel wall (bottom)
6136 element = EL_DC_STEELWALL_1_BOTTOM;
6139 case 0x1698: // steel wall (right)
6140 element = EL_DC_STEELWALL_1_RIGHT;
6143 case 0x1699: // steel wall (top)
6144 element = EL_DC_STEELWALL_1_TOP;
6147 case 0x169a: // steel wall (left/bottom)
6148 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6151 case 0x169b: // steel wall (right/bottom)
6152 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6155 case 0x169c: // steel wall (right/top)
6156 element = EL_DC_STEELWALL_1_TOPRIGHT;
6159 case 0x169d: // steel wall (left/top)
6160 element = EL_DC_STEELWALL_1_TOPLEFT;
6163 case 0x169e: // steel wall (right/bottom small)
6164 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6167 case 0x169f: // steel wall (left/bottom small)
6168 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6171 case 0x16a0: // steel wall (right/top small)
6172 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6175 case 0x16a1: // steel wall (left/top small)
6176 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6179 case 0x16a2: // steel wall (left/right)
6180 element = EL_DC_STEELWALL_1_VERTICAL;
6183 case 0x16a3: // steel wall (top/bottom)
6184 element = EL_DC_STEELWALL_1_HORIZONTAL;
6187 case 0x16a4: // steel wall 2 (left end)
6188 element = EL_DC_STEELWALL_2_LEFT;
6191 case 0x16a5: // steel wall 2 (right end)
6192 element = EL_DC_STEELWALL_2_RIGHT;
6195 case 0x16a6: // steel wall 2 (top end)
6196 element = EL_DC_STEELWALL_2_TOP;
6199 case 0x16a7: // steel wall 2 (bottom end)
6200 element = EL_DC_STEELWALL_2_BOTTOM;
6203 case 0x16a8: // steel wall 2 (left/right)
6204 element = EL_DC_STEELWALL_2_HORIZONTAL;
6207 case 0x16a9: // steel wall 2 (up/down)
6208 element = EL_DC_STEELWALL_2_VERTICAL;
6211 case 0x16aa: // steel wall 2 (mid)
6212 element = EL_DC_STEELWALL_2_MIDDLE;
6216 element = EL_SIGN_EXCLAMATION;
6220 element = EL_SIGN_RADIOACTIVITY;
6224 element = EL_SIGN_STOP;
6228 element = EL_SIGN_WHEELCHAIR;
6232 element = EL_SIGN_PARKING;
6236 element = EL_SIGN_NO_ENTRY;
6240 element = EL_SIGN_HEART;
6244 element = EL_SIGN_GIVE_WAY;
6248 element = EL_SIGN_ENTRY_FORBIDDEN;
6252 element = EL_SIGN_EMERGENCY_EXIT;
6256 element = EL_SIGN_YIN_YANG;
6260 element = EL_WALL_EMERALD;
6264 element = EL_WALL_DIAMOND;
6268 element = EL_WALL_PEARL;
6272 element = EL_WALL_CRYSTAL;
6276 element = EL_INVISIBLE_WALL;
6280 element = EL_INVISIBLE_STEELWALL;
6284 // EL_INVISIBLE_SAND
6287 element = EL_LIGHT_SWITCH;
6291 element = EL_ENVELOPE_1;
6295 if (element >= 0x0117 && element <= 0x036e) // (?)
6296 element = EL_DIAMOND;
6297 else if (element >= 0x042d && element <= 0x0684) // (?)
6298 element = EL_EMERALD;
6299 else if (element >= 0x157c && element <= 0x158b)
6301 else if (element >= 0x1590 && element <= 0x159f)
6302 element = EL_DC_LANDMINE;
6303 else if (element >= 0x16bc && element <= 0x16cb)
6304 element = EL_INVISIBLE_SAND;
6307 Warn("unknown Diamond Caves element 0x%04x", element);
6309 element = EL_UNKNOWN;
6314 return getMappedElement(element);
6317 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6319 byte header[DC_LEVEL_HEADER_SIZE];
6321 int envelope_header_pos = 62;
6322 int envelope_content_pos = 94;
6323 int level_name_pos = 251;
6324 int level_author_pos = 292;
6325 int envelope_header_len;
6326 int envelope_content_len;
6328 int level_author_len;
6330 int num_yamyam_contents;
6333 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6335 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6337 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6339 header[i * 2 + 0] = header_word >> 8;
6340 header[i * 2 + 1] = header_word & 0xff;
6343 // read some values from level header to check level decoding integrity
6344 fieldx = header[6] | (header[7] << 8);
6345 fieldy = header[8] | (header[9] << 8);
6346 num_yamyam_contents = header[60] | (header[61] << 8);
6348 // do some simple sanity checks to ensure that level was correctly decoded
6349 if (fieldx < 1 || fieldx > 256 ||
6350 fieldy < 1 || fieldy > 256 ||
6351 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6353 level->no_valid_file = TRUE;
6355 Warn("cannot decode level from stream -- using empty level");
6360 // maximum envelope header size is 31 bytes
6361 envelope_header_len = header[envelope_header_pos];
6362 // maximum envelope content size is 110 (156?) bytes
6363 envelope_content_len = header[envelope_content_pos];
6365 // maximum level title size is 40 bytes
6366 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6367 // maximum level author size is 30 (51?) bytes
6368 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6372 for (i = 0; i < envelope_header_len; i++)
6373 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6374 level->envelope[0].text[envelope_size++] =
6375 header[envelope_header_pos + 1 + i];
6377 if (envelope_header_len > 0 && envelope_content_len > 0)
6379 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6380 level->envelope[0].text[envelope_size++] = '\n';
6381 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6382 level->envelope[0].text[envelope_size++] = '\n';
6385 for (i = 0; i < envelope_content_len; i++)
6386 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6387 level->envelope[0].text[envelope_size++] =
6388 header[envelope_content_pos + 1 + i];
6390 level->envelope[0].text[envelope_size] = '\0';
6392 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6393 level->envelope[0].ysize = 10;
6394 level->envelope[0].autowrap = TRUE;
6395 level->envelope[0].centered = TRUE;
6397 for (i = 0; i < level_name_len; i++)
6398 level->name[i] = header[level_name_pos + 1 + i];
6399 level->name[level_name_len] = '\0';
6401 for (i = 0; i < level_author_len; i++)
6402 level->author[i] = header[level_author_pos + 1 + i];
6403 level->author[level_author_len] = '\0';
6405 num_yamyam_contents = header[60] | (header[61] << 8);
6406 level->num_yamyam_contents =
6407 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6409 for (i = 0; i < num_yamyam_contents; i++)
6411 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6413 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6414 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6416 if (i < MAX_ELEMENT_CONTENTS)
6417 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6421 fieldx = header[6] | (header[7] << 8);
6422 fieldy = header[8] | (header[9] << 8);
6423 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6424 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6426 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6428 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6429 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6431 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6432 level->field[x][y] = getMappedElement_DC(element_dc);
6435 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6436 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6437 level->field[x][y] = EL_PLAYER_1;
6439 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6440 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6441 level->field[x][y] = EL_PLAYER_2;
6443 level->gems_needed = header[18] | (header[19] << 8);
6445 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6446 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6447 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6448 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6449 level->score[SC_NUT] = header[28] | (header[29] << 8);
6450 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6451 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6452 level->score[SC_BUG] = header[34] | (header[35] << 8);
6453 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6454 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6455 level->score[SC_KEY] = header[40] | (header[41] << 8);
6456 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6458 level->time = header[44] | (header[45] << 8);
6460 level->amoeba_speed = header[46] | (header[47] << 8);
6461 level->time_light = header[48] | (header[49] << 8);
6462 level->time_timegate = header[50] | (header[51] << 8);
6463 level->time_wheel = header[52] | (header[53] << 8);
6464 level->time_magic_wall = header[54] | (header[55] << 8);
6465 level->extra_time = header[56] | (header[57] << 8);
6466 level->shield_normal_time = header[58] | (header[59] << 8);
6468 // shield and extra time elements do not have a score
6469 level->score[SC_SHIELD] = 0;
6470 level->extra_time_score = 0;
6472 // set time for normal and deadly shields to the same value
6473 level->shield_deadly_time = level->shield_normal_time;
6475 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6476 // can slip down from flat walls, like normal walls and steel walls
6477 level->em_slippery_gems = TRUE;
6479 // time score is counted for each 10 seconds left in Diamond Caves levels
6480 level->time_score_base = 10;
6483 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6484 struct LevelFileInfo *level_file_info,
6485 boolean level_info_only)
6487 char *filename = level_file_info->filename;
6489 int num_magic_bytes = 8;
6490 char magic_bytes[num_magic_bytes + 1];
6491 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6493 if (!(file = openFile(filename, MODE_READ)))
6495 level->no_valid_file = TRUE;
6497 if (!level_info_only)
6498 Warn("cannot read level '%s' -- using empty level", filename);
6503 // fseek(file, 0x0000, SEEK_SET);
6505 if (level_file_info->packed)
6507 // read "magic bytes" from start of file
6508 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6509 magic_bytes[0] = '\0';
6511 // check "magic bytes" for correct file format
6512 if (!strPrefix(magic_bytes, "DC2"))
6514 level->no_valid_file = TRUE;
6516 Warn("unknown DC level file '%s' -- using empty level", filename);
6521 if (strPrefix(magic_bytes, "DC2Win95") ||
6522 strPrefix(magic_bytes, "DC2Win98"))
6524 int position_first_level = 0x00fa;
6525 int extra_bytes = 4;
6528 // advance file stream to first level inside the level package
6529 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6531 // each block of level data is followed by block of non-level data
6532 num_levels_to_skip *= 2;
6534 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6535 while (num_levels_to_skip >= 0)
6537 // advance file stream to next level inside the level package
6538 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6540 level->no_valid_file = TRUE;
6542 Warn("cannot fseek in file '%s' -- using empty level", filename);
6547 // skip apparently unused extra bytes following each level
6548 ReadUnusedBytesFromFile(file, extra_bytes);
6550 // read size of next level in level package
6551 skip_bytes = getFile32BitLE(file);
6553 num_levels_to_skip--;
6558 level->no_valid_file = TRUE;
6560 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6566 LoadLevelFromFileStream_DC(file, level);
6572 // ----------------------------------------------------------------------------
6573 // functions for loading SB level
6574 // ----------------------------------------------------------------------------
6576 int getMappedElement_SB(int element_ascii, boolean use_ces)
6584 sb_element_mapping[] =
6586 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6587 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6588 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6589 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6590 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6591 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6592 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6593 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6600 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6601 if (element_ascii == sb_element_mapping[i].ascii)
6602 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6604 return EL_UNDEFINED;
6607 static void SetLevelSettings_SB(struct LevelInfo *level)
6611 level->use_step_counter = TRUE;
6614 level->score[SC_TIME_BONUS] = 0;
6615 level->time_score_base = 1;
6616 level->rate_time_over_score = TRUE;
6619 level->auto_exit_sokoban = TRUE;
6622 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6623 struct LevelFileInfo *level_file_info,
6624 boolean level_info_only)
6626 char *filename = level_file_info->filename;
6627 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6628 char last_comment[MAX_LINE_LEN];
6629 char level_name[MAX_LINE_LEN];
6632 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6633 boolean read_continued_line = FALSE;
6634 boolean reading_playfield = FALSE;
6635 boolean got_valid_playfield_line = FALSE;
6636 boolean invalid_playfield_char = FALSE;
6637 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6638 int file_level_nr = 0;
6639 int x = 0, y = 0; // initialized to make compilers happy
6641 last_comment[0] = '\0';
6642 level_name[0] = '\0';
6644 if (!(file = openFile(filename, MODE_READ)))
6646 level->no_valid_file = TRUE;
6648 if (!level_info_only)
6649 Warn("cannot read level '%s' -- using empty level", filename);
6654 while (!checkEndOfFile(file))
6656 // level successfully read, but next level may follow here
6657 if (!got_valid_playfield_line && reading_playfield)
6659 // read playfield from single level file -- skip remaining file
6660 if (!level_file_info->packed)
6663 if (file_level_nr >= num_levels_to_skip)
6668 last_comment[0] = '\0';
6669 level_name[0] = '\0';
6671 reading_playfield = FALSE;
6674 got_valid_playfield_line = FALSE;
6676 // read next line of input file
6677 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6680 // cut trailing line break (this can be newline and/or carriage return)
6681 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6682 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6685 // copy raw input line for later use (mainly debugging output)
6686 strcpy(line_raw, line);
6688 if (read_continued_line)
6690 // append new line to existing line, if there is enough space
6691 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6692 strcat(previous_line, line_ptr);
6694 strcpy(line, previous_line); // copy storage buffer to line
6696 read_continued_line = FALSE;
6699 // if the last character is '\', continue at next line
6700 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6702 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6703 strcpy(previous_line, line); // copy line to storage buffer
6705 read_continued_line = TRUE;
6711 if (line[0] == '\0')
6714 // extract comment text from comment line
6717 for (line_ptr = line; *line_ptr; line_ptr++)
6718 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6721 strcpy(last_comment, line_ptr);
6726 // extract level title text from line containing level title
6727 if (line[0] == '\'')
6729 strcpy(level_name, &line[1]);
6731 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6732 level_name[strlen(level_name) - 1] = '\0';
6737 // skip lines containing only spaces (or empty lines)
6738 for (line_ptr = line; *line_ptr; line_ptr++)
6739 if (*line_ptr != ' ')
6741 if (*line_ptr == '\0')
6744 // at this point, we have found a line containing part of a playfield
6746 got_valid_playfield_line = TRUE;
6748 if (!reading_playfield)
6750 reading_playfield = TRUE;
6751 invalid_playfield_char = FALSE;
6753 for (x = 0; x < MAX_LEV_FIELDX; x++)
6754 for (y = 0; y < MAX_LEV_FIELDY; y++)
6755 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6760 // start with topmost tile row
6764 // skip playfield line if larger row than allowed
6765 if (y >= MAX_LEV_FIELDY)
6768 // start with leftmost tile column
6771 // read playfield elements from line
6772 for (line_ptr = line; *line_ptr; line_ptr++)
6774 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6776 // stop parsing playfield line if larger column than allowed
6777 if (x >= MAX_LEV_FIELDX)
6780 if (mapped_sb_element == EL_UNDEFINED)
6782 invalid_playfield_char = TRUE;
6787 level->field[x][y] = mapped_sb_element;
6789 // continue with next tile column
6792 level->fieldx = MAX(x, level->fieldx);
6795 if (invalid_playfield_char)
6797 // if first playfield line, treat invalid lines as comment lines
6799 reading_playfield = FALSE;
6804 // continue with next tile row
6812 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6813 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6815 if (!reading_playfield)
6817 level->no_valid_file = TRUE;
6819 Warn("cannot read level '%s' -- using empty level", filename);
6824 if (*level_name != '\0')
6826 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6827 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6829 else if (*last_comment != '\0')
6831 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6832 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6836 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6839 // set all empty fields beyond the border walls to invisible steel wall
6840 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6842 if ((x == 0 || x == level->fieldx - 1 ||
6843 y == 0 || y == level->fieldy - 1) &&
6844 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6845 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6846 level->field, level->fieldx, level->fieldy);
6849 // set special level settings for Sokoban levels
6850 SetLevelSettings_SB(level);
6852 if (load_xsb_to_ces)
6854 // special global settings can now be set in level template
6855 level->use_custom_template = TRUE;
6860 // -------------------------------------------------------------------------
6861 // functions for handling native levels
6862 // -------------------------------------------------------------------------
6864 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6865 struct LevelFileInfo *level_file_info,
6866 boolean level_info_only)
6870 // determine position of requested level inside level package
6871 if (level_file_info->packed)
6872 pos = level_file_info->nr - leveldir_current->first_level;
6874 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6875 level->no_valid_file = TRUE;
6878 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6879 struct LevelFileInfo *level_file_info,
6880 boolean level_info_only)
6882 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6883 level->no_valid_file = TRUE;
6886 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6887 struct LevelFileInfo *level_file_info,
6888 boolean level_info_only)
6892 // determine position of requested level inside level package
6893 if (level_file_info->packed)
6894 pos = level_file_info->nr - leveldir_current->first_level;
6896 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6897 level->no_valid_file = TRUE;
6900 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6901 struct LevelFileInfo *level_file_info,
6902 boolean level_info_only)
6904 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6905 level->no_valid_file = TRUE;
6908 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6910 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6911 CopyNativeLevel_RND_to_BD(level);
6912 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6913 CopyNativeLevel_RND_to_EM(level);
6914 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6915 CopyNativeLevel_RND_to_SP(level);
6916 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6917 CopyNativeLevel_RND_to_MM(level);
6920 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6922 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6923 CopyNativeLevel_BD_to_RND(level);
6924 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6925 CopyNativeLevel_EM_to_RND(level);
6926 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6927 CopyNativeLevel_SP_to_RND(level);
6928 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6929 CopyNativeLevel_MM_to_RND(level);
6932 void SaveNativeLevel(struct LevelInfo *level)
6934 // saving native level files only supported for some game engines
6935 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6936 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6939 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6940 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6941 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6942 char *filename = getLevelFilenameFromBasename(basename);
6944 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6947 boolean success = FALSE;
6949 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6951 CopyNativeLevel_RND_to_BD(level);
6952 // CopyNativeTape_RND_to_BD(level);
6954 success = SaveNativeLevel_BD(filename);
6956 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6958 CopyNativeLevel_RND_to_SP(level);
6959 CopyNativeTape_RND_to_SP(level);
6961 success = SaveNativeLevel_SP(filename);
6965 Request("Native level file saved!", REQ_CONFIRM);
6967 Request("Failed to save native level file!", REQ_CONFIRM);
6971 // ----------------------------------------------------------------------------
6972 // functions for loading generic level
6973 // ----------------------------------------------------------------------------
6975 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6976 struct LevelFileInfo *level_file_info,
6977 boolean level_info_only)
6979 // always start with reliable default values
6980 setLevelInfoToDefaults(level, level_info_only, TRUE);
6982 switch (level_file_info->type)
6984 case LEVEL_FILE_TYPE_RND:
6985 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6988 case LEVEL_FILE_TYPE_BD:
6989 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6990 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6993 case LEVEL_FILE_TYPE_EM:
6994 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6995 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6998 case LEVEL_FILE_TYPE_SP:
6999 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7000 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7003 case LEVEL_FILE_TYPE_MM:
7004 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7005 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7008 case LEVEL_FILE_TYPE_DC:
7009 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7012 case LEVEL_FILE_TYPE_SB:
7013 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7017 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7021 // if level file is invalid, restore level structure to default values
7022 if (level->no_valid_file)
7023 setLevelInfoToDefaults(level, level_info_only, FALSE);
7025 if (check_special_flags("use_native_bd_game_engine"))
7026 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7028 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7029 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7031 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7032 CopyNativeLevel_Native_to_RND(level);
7035 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7037 static struct LevelFileInfo level_file_info;
7039 // always start with reliable default values
7040 setFileInfoToDefaults(&level_file_info);
7042 level_file_info.nr = 0; // unknown level number
7043 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7045 setString(&level_file_info.filename, filename);
7047 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7050 static void LoadLevel_InitVersion(struct LevelInfo *level)
7054 if (leveldir_current == NULL) // only when dumping level
7057 // all engine modifications also valid for levels which use latest engine
7058 if (level->game_version < VERSION_IDENT(3,2,0,5))
7060 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7061 level->time_score_base = 10;
7064 if (leveldir_current->latest_engine)
7066 // ---------- use latest game engine --------------------------------------
7068 /* For all levels which are forced to use the latest game engine version
7069 (normally all but user contributed, private and undefined levels), set
7070 the game engine version to the actual version; this allows for actual
7071 corrections in the game engine to take effect for existing, converted
7072 levels (from "classic" or other existing games) to make the emulation
7073 of the corresponding game more accurate, while (hopefully) not breaking
7074 existing levels created from other players. */
7076 level->game_version = GAME_VERSION_ACTUAL;
7078 /* Set special EM style gems behaviour: EM style gems slip down from
7079 normal, steel and growing wall. As this is a more fundamental change,
7080 it seems better to set the default behaviour to "off" (as it is more
7081 natural) and make it configurable in the level editor (as a property
7082 of gem style elements). Already existing converted levels (neither
7083 private nor contributed levels) are changed to the new behaviour. */
7085 if (level->file_version < FILE_VERSION_2_0)
7086 level->em_slippery_gems = TRUE;
7091 // ---------- use game engine the level was created with --------------------
7093 /* For all levels which are not forced to use the latest game engine
7094 version (normally user contributed, private and undefined levels),
7095 use the version of the game engine the levels were created for.
7097 Since 2.0.1, the game engine version is now directly stored
7098 in the level file (chunk "VERS"), so there is no need anymore
7099 to set the game version from the file version (except for old,
7100 pre-2.0 levels, where the game version is still taken from the
7101 file format version used to store the level -- see above). */
7103 // player was faster than enemies in 1.0.0 and before
7104 if (level->file_version == FILE_VERSION_1_0)
7105 for (i = 0; i < MAX_PLAYERS; i++)
7106 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7108 // default behaviour for EM style gems was "slippery" only in 2.0.1
7109 if (level->game_version == VERSION_IDENT(2,0,1,0))
7110 level->em_slippery_gems = TRUE;
7112 // springs could be pushed over pits before (pre-release version) 2.2.0
7113 if (level->game_version < VERSION_IDENT(2,2,0,0))
7114 level->use_spring_bug = TRUE;
7116 if (level->game_version < VERSION_IDENT(3,2,0,5))
7118 // time orb caused limited time in endless time levels before 3.2.0-5
7119 level->use_time_orb_bug = TRUE;
7121 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7122 level->block_snap_field = FALSE;
7124 // extra time score was same value as time left score before 3.2.0-5
7125 level->extra_time_score = level->score[SC_TIME_BONUS];
7128 if (level->game_version < VERSION_IDENT(3,2,0,7))
7130 // default behaviour for snapping was "not continuous" before 3.2.0-7
7131 level->continuous_snapping = FALSE;
7134 // only few elements were able to actively move into acid before 3.1.0
7135 // trigger settings did not exist before 3.1.0; set to default "any"
7136 if (level->game_version < VERSION_IDENT(3,1,0,0))
7138 // correct "can move into acid" settings (all zero in old levels)
7140 level->can_move_into_acid_bits = 0; // nothing can move into acid
7141 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7143 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7144 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7145 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7146 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7148 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7149 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7151 // correct trigger settings (stored as zero == "none" in old levels)
7153 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7155 int element = EL_CUSTOM_START + i;
7156 struct ElementInfo *ei = &element_info[element];
7158 for (j = 0; j < ei->num_change_pages; j++)
7160 struct ElementChangeInfo *change = &ei->change_page[j];
7162 change->trigger_player = CH_PLAYER_ANY;
7163 change->trigger_page = CH_PAGE_ANY;
7168 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7170 int element = EL_CUSTOM_256;
7171 struct ElementInfo *ei = &element_info[element];
7172 struct ElementChangeInfo *change = &ei->change_page[0];
7174 /* This is needed to fix a problem that was caused by a bugfix in function
7175 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7176 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7177 not replace walkable elements, but instead just placed the player on it,
7178 without placing the Sokoban field under the player). Unfortunately, this
7179 breaks "Snake Bite" style levels when the snake is halfway through a door
7180 that just closes (the snake head is still alive and can be moved in this
7181 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7182 player (without Sokoban element) which then gets killed as designed). */
7184 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7185 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7186 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7187 change->target_element = EL_PLAYER_1;
7190 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7191 if (level->game_version < VERSION_IDENT(3,2,5,0))
7193 /* This is needed to fix a problem that was caused by a bugfix in function
7194 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7195 corrects the behaviour when a custom element changes to another custom
7196 element with a higher element number that has change actions defined.
7197 Normally, only one change per frame is allowed for custom elements.
7198 Therefore, it is checked if a custom element already changed in the
7199 current frame; if it did, subsequent changes are suppressed.
7200 Unfortunately, this is only checked for element changes, but not for
7201 change actions, which are still executed. As the function above loops
7202 through all custom elements from lower to higher, an element change
7203 resulting in a lower CE number won't be checked again, while a target
7204 element with a higher number will also be checked, and potential change
7205 actions will get executed for this CE, too (which is wrong), while
7206 further changes are ignored (which is correct). As this bugfix breaks
7207 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7208 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7209 behaviour for existing levels and tapes that make use of this bug */
7211 level->use_action_after_change_bug = TRUE;
7214 // not centering level after relocating player was default only in 3.2.3
7215 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7216 level->shifted_relocation = TRUE;
7218 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7219 if (level->game_version < VERSION_IDENT(3,2,6,0))
7220 level->em_explodes_by_fire = TRUE;
7222 // levels were solved by the first player entering an exit up to 4.1.0.0
7223 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7224 level->solved_by_one_player = TRUE;
7226 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7227 if (level->game_version < VERSION_IDENT(4,1,1,1))
7228 level->use_life_bugs = TRUE;
7230 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7231 if (level->game_version < VERSION_IDENT(4,1,1,1))
7232 level->sb_objects_needed = FALSE;
7234 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7235 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7236 level->finish_dig_collect = FALSE;
7238 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7239 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7240 level->keep_walkable_ce = TRUE;
7243 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7245 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7248 // check if this level is (not) a Sokoban level
7249 for (y = 0; y < level->fieldy; y++)
7250 for (x = 0; x < level->fieldx; x++)
7251 if (!IS_SB_ELEMENT(Tile[x][y]))
7252 is_sokoban_level = FALSE;
7254 if (is_sokoban_level)
7256 // set special level settings for Sokoban levels
7257 SetLevelSettings_SB(level);
7261 static void LoadLevel_InitSettings(struct LevelInfo *level)
7263 // adjust level settings for (non-native) Sokoban-style levels
7264 LoadLevel_InitSettings_SB(level);
7266 // rename levels with title "nameless level" or if renaming is forced
7267 if (leveldir_current->empty_level_name != NULL &&
7268 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7269 leveldir_current->force_level_name))
7270 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7271 leveldir_current->empty_level_name, level_nr);
7274 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7278 // map elements that have changed in newer versions
7279 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7280 level->game_version);
7281 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7282 for (x = 0; x < 3; x++)
7283 for (y = 0; y < 3; y++)
7284 level->yamyam_content[i].e[x][y] =
7285 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7286 level->game_version);
7290 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7294 // map custom element change events that have changed in newer versions
7295 // (these following values were accidentally changed in version 3.0.1)
7296 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7297 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7299 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7301 int element = EL_CUSTOM_START + i;
7303 // order of checking and copying events to be mapped is important
7304 // (do not change the start and end value -- they are constant)
7305 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7307 if (HAS_CHANGE_EVENT(element, j - 2))
7309 SET_CHANGE_EVENT(element, j - 2, FALSE);
7310 SET_CHANGE_EVENT(element, j, TRUE);
7314 // order of checking and copying events to be mapped is important
7315 // (do not change the start and end value -- they are constant)
7316 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7318 if (HAS_CHANGE_EVENT(element, j - 1))
7320 SET_CHANGE_EVENT(element, j - 1, FALSE);
7321 SET_CHANGE_EVENT(element, j, TRUE);
7327 // initialize "can_change" field for old levels with only one change page
7328 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7330 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7332 int element = EL_CUSTOM_START + i;
7334 if (CAN_CHANGE(element))
7335 element_info[element].change->can_change = TRUE;
7339 // correct custom element values (for old levels without these options)
7340 if (level->game_version < VERSION_IDENT(3,1,1,0))
7342 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7344 int element = EL_CUSTOM_START + i;
7345 struct ElementInfo *ei = &element_info[element];
7347 if (ei->access_direction == MV_NO_DIRECTION)
7348 ei->access_direction = MV_ALL_DIRECTIONS;
7352 // correct custom element values (fix invalid values for all versions)
7355 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7357 int element = EL_CUSTOM_START + i;
7358 struct ElementInfo *ei = &element_info[element];
7360 for (j = 0; j < ei->num_change_pages; j++)
7362 struct ElementChangeInfo *change = &ei->change_page[j];
7364 if (change->trigger_player == CH_PLAYER_NONE)
7365 change->trigger_player = CH_PLAYER_ANY;
7367 if (change->trigger_side == CH_SIDE_NONE)
7368 change->trigger_side = CH_SIDE_ANY;
7373 // initialize "can_explode" field for old levels which did not store this
7374 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7375 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7377 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7379 int element = EL_CUSTOM_START + i;
7381 if (EXPLODES_1X1_OLD(element))
7382 element_info[element].explosion_type = EXPLODES_1X1;
7384 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7385 EXPLODES_SMASHED(element) ||
7386 EXPLODES_IMPACT(element)));
7390 // correct previously hard-coded move delay values for maze runner style
7391 if (level->game_version < VERSION_IDENT(3,1,1,0))
7393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7395 int element = EL_CUSTOM_START + i;
7397 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7399 // previously hard-coded and therefore ignored
7400 element_info[element].move_delay_fixed = 9;
7401 element_info[element].move_delay_random = 0;
7406 // set some other uninitialized values of custom elements in older levels
7407 if (level->game_version < VERSION_IDENT(3,1,0,0))
7409 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7411 int element = EL_CUSTOM_START + i;
7413 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7415 element_info[element].explosion_delay = 17;
7416 element_info[element].ignition_delay = 8;
7420 // set mouse click change events to work for left/middle/right mouse button
7421 if (level->game_version < VERSION_IDENT(4,2,3,0))
7423 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7425 int element = EL_CUSTOM_START + i;
7426 struct ElementInfo *ei = &element_info[element];
7428 for (j = 0; j < ei->num_change_pages; j++)
7430 struct ElementChangeInfo *change = &ei->change_page[j];
7432 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7433 change->has_event[CE_PRESSED_BY_MOUSE] ||
7434 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7435 change->has_event[CE_MOUSE_PRESSED_ON_X])
7436 change->trigger_side = CH_SIDE_ANY;
7442 static void LoadLevel_InitElements(struct LevelInfo *level)
7444 LoadLevel_InitStandardElements(level);
7446 if (level->file_has_custom_elements)
7447 LoadLevel_InitCustomElements(level);
7449 // initialize element properties for level editor etc.
7450 InitElementPropertiesEngine(level->game_version);
7451 InitElementPropertiesGfxElement();
7454 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7458 // map elements that have changed in newer versions
7459 for (y = 0; y < level->fieldy; y++)
7460 for (x = 0; x < level->fieldx; x++)
7461 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7462 level->game_version);
7464 // clear unused playfield data (nicer if level gets resized in editor)
7465 for (x = 0; x < MAX_LEV_FIELDX; x++)
7466 for (y = 0; y < MAX_LEV_FIELDY; y++)
7467 if (x >= level->fieldx || y >= level->fieldy)
7468 level->field[x][y] = EL_EMPTY;
7470 // copy elements to runtime playfield array
7471 for (x = 0; x < MAX_LEV_FIELDX; x++)
7472 for (y = 0; y < MAX_LEV_FIELDY; y++)
7473 Tile[x][y] = level->field[x][y];
7475 // initialize level size variables for faster access
7476 lev_fieldx = level->fieldx;
7477 lev_fieldy = level->fieldy;
7479 // determine border element for this level
7480 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7481 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7486 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7488 struct LevelFileInfo *level_file_info = &level->file_info;
7490 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7491 CopyNativeLevel_RND_to_Native(level);
7494 static void LoadLevelTemplate_LoadAndInit(void)
7496 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7498 LoadLevel_InitVersion(&level_template);
7499 LoadLevel_InitElements(&level_template);
7500 LoadLevel_InitSettings(&level_template);
7502 ActivateLevelTemplate();
7505 void LoadLevelTemplate(int nr)
7507 if (!fileExists(getGlobalLevelTemplateFilename()))
7509 Warn("no level template found for this level");
7514 setLevelFileInfo(&level_template.file_info, nr);
7516 LoadLevelTemplate_LoadAndInit();
7519 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7521 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7523 LoadLevelTemplate_LoadAndInit();
7526 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7528 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7530 if (level.use_custom_template)
7532 if (network_level != NULL)
7533 LoadNetworkLevelTemplate(network_level);
7535 LoadLevelTemplate(-1);
7538 LoadLevel_InitVersion(&level);
7539 LoadLevel_InitElements(&level);
7540 LoadLevel_InitPlayfield(&level);
7541 LoadLevel_InitSettings(&level);
7543 LoadLevel_InitNativeEngines(&level);
7546 void LoadLevel(int nr)
7548 SetLevelSetInfo(leveldir_current->identifier, nr);
7550 setLevelFileInfo(&level.file_info, nr);
7552 LoadLevel_LoadAndInit(NULL);
7555 void LoadLevelInfoOnly(int nr)
7557 setLevelFileInfo(&level.file_info, nr);
7559 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7562 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7564 SetLevelSetInfo(network_level->leveldir_identifier,
7565 network_level->file_info.nr);
7567 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7569 LoadLevel_LoadAndInit(network_level);
7572 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7576 chunk_size += putFileVersion(file, level->file_version);
7577 chunk_size += putFileVersion(file, level->game_version);
7582 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7586 chunk_size += putFile16BitBE(file, level->creation_date.year);
7587 chunk_size += putFile8Bit(file, level->creation_date.month);
7588 chunk_size += putFile8Bit(file, level->creation_date.day);
7593 #if ENABLE_HISTORIC_CHUNKS
7594 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7598 putFile8Bit(file, level->fieldx);
7599 putFile8Bit(file, level->fieldy);
7601 putFile16BitBE(file, level->time);
7602 putFile16BitBE(file, level->gems_needed);
7604 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7605 putFile8Bit(file, level->name[i]);
7607 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7608 putFile8Bit(file, level->score[i]);
7610 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7611 for (y = 0; y < 3; y++)
7612 for (x = 0; x < 3; x++)
7613 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7614 level->yamyam_content[i].e[x][y]));
7615 putFile8Bit(file, level->amoeba_speed);
7616 putFile8Bit(file, level->time_magic_wall);
7617 putFile8Bit(file, level->time_wheel);
7618 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7619 level->amoeba_content));
7620 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7621 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7622 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7623 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7625 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7627 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7628 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7629 putFile32BitBE(file, level->can_move_into_acid_bits);
7630 putFile8Bit(file, level->dont_collide_with_bits);
7632 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7633 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7635 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7636 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7637 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7639 putFile8Bit(file, level->game_engine_type);
7641 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7645 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7650 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7651 chunk_size += putFile8Bit(file, level->name[i]);
7656 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7661 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7662 chunk_size += putFile8Bit(file, level->author[i]);
7667 #if ENABLE_HISTORIC_CHUNKS
7668 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7673 for (y = 0; y < level->fieldy; y++)
7674 for (x = 0; x < level->fieldx; x++)
7675 if (level->encoding_16bit_field)
7676 chunk_size += putFile16BitBE(file, level->field[x][y]);
7678 chunk_size += putFile8Bit(file, level->field[x][y]);
7684 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7689 for (y = 0; y < level->fieldy; y++)
7690 for (x = 0; x < level->fieldx; x++)
7691 chunk_size += putFile16BitBE(file, level->field[x][y]);
7696 #if ENABLE_HISTORIC_CHUNKS
7697 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7701 putFile8Bit(file, EL_YAMYAM);
7702 putFile8Bit(file, level->num_yamyam_contents);
7703 putFile8Bit(file, 0);
7704 putFile8Bit(file, 0);
7706 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7707 for (y = 0; y < 3; y++)
7708 for (x = 0; x < 3; x++)
7709 if (level->encoding_16bit_field)
7710 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7712 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7716 #if ENABLE_HISTORIC_CHUNKS
7717 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7720 int num_contents, content_xsize, content_ysize;
7721 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7723 if (element == EL_YAMYAM)
7725 num_contents = level->num_yamyam_contents;
7729 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7730 for (y = 0; y < 3; y++)
7731 for (x = 0; x < 3; x++)
7732 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7734 else if (element == EL_BD_AMOEBA)
7740 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7741 for (y = 0; y < 3; y++)
7742 for (x = 0; x < 3; x++)
7743 content_array[i][x][y] = EL_EMPTY;
7744 content_array[0][0][0] = level->amoeba_content;
7748 // chunk header already written -- write empty chunk data
7749 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7751 Warn("cannot save content for element '%d'", element);
7756 putFile16BitBE(file, element);
7757 putFile8Bit(file, num_contents);
7758 putFile8Bit(file, content_xsize);
7759 putFile8Bit(file, content_ysize);
7761 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7763 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7764 for (y = 0; y < 3; y++)
7765 for (x = 0; x < 3; x++)
7766 putFile16BitBE(file, content_array[i][x][y]);
7770 #if ENABLE_HISTORIC_CHUNKS
7771 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7773 int envelope_nr = element - EL_ENVELOPE_1;
7774 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7778 chunk_size += putFile16BitBE(file, element);
7779 chunk_size += putFile16BitBE(file, envelope_len);
7780 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7781 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7783 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7784 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7786 for (i = 0; i < envelope_len; i++)
7787 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7793 #if ENABLE_HISTORIC_CHUNKS
7794 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7795 int num_changed_custom_elements)
7799 putFile16BitBE(file, num_changed_custom_elements);
7801 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7803 int element = EL_CUSTOM_START + i;
7805 struct ElementInfo *ei = &element_info[element];
7807 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7809 if (check < num_changed_custom_elements)
7811 putFile16BitBE(file, element);
7812 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7819 if (check != num_changed_custom_elements) // should not happen
7820 Warn("inconsistent number of custom element properties");
7824 #if ENABLE_HISTORIC_CHUNKS
7825 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7826 int num_changed_custom_elements)
7830 putFile16BitBE(file, num_changed_custom_elements);
7832 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7834 int element = EL_CUSTOM_START + i;
7836 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7838 if (check < num_changed_custom_elements)
7840 putFile16BitBE(file, element);
7841 putFile16BitBE(file, element_info[element].change->target_element);
7848 if (check != num_changed_custom_elements) // should not happen
7849 Warn("inconsistent number of custom target elements");
7853 #if ENABLE_HISTORIC_CHUNKS
7854 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7855 int num_changed_custom_elements)
7857 int i, j, x, y, check = 0;
7859 putFile16BitBE(file, num_changed_custom_elements);
7861 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7863 int element = EL_CUSTOM_START + i;
7864 struct ElementInfo *ei = &element_info[element];
7866 if (ei->modified_settings)
7868 if (check < num_changed_custom_elements)
7870 putFile16BitBE(file, element);
7872 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7873 putFile8Bit(file, ei->description[j]);
7875 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7877 // some free bytes for future properties and padding
7878 WriteUnusedBytesToFile(file, 7);
7880 putFile8Bit(file, ei->use_gfx_element);
7881 putFile16BitBE(file, ei->gfx_element_initial);
7883 putFile8Bit(file, ei->collect_score_initial);
7884 putFile8Bit(file, ei->collect_count_initial);
7886 putFile16BitBE(file, ei->push_delay_fixed);
7887 putFile16BitBE(file, ei->push_delay_random);
7888 putFile16BitBE(file, ei->move_delay_fixed);
7889 putFile16BitBE(file, ei->move_delay_random);
7891 putFile16BitBE(file, ei->move_pattern);
7892 putFile8Bit(file, ei->move_direction_initial);
7893 putFile8Bit(file, ei->move_stepsize);
7895 for (y = 0; y < 3; y++)
7896 for (x = 0; x < 3; x++)
7897 putFile16BitBE(file, ei->content.e[x][y]);
7899 putFile32BitBE(file, ei->change->events);
7901 putFile16BitBE(file, ei->change->target_element);
7903 putFile16BitBE(file, ei->change->delay_fixed);
7904 putFile16BitBE(file, ei->change->delay_random);
7905 putFile16BitBE(file, ei->change->delay_frames);
7907 putFile16BitBE(file, ei->change->initial_trigger_element);
7909 putFile8Bit(file, ei->change->explode);
7910 putFile8Bit(file, ei->change->use_target_content);
7911 putFile8Bit(file, ei->change->only_if_complete);
7912 putFile8Bit(file, ei->change->use_random_replace);
7914 putFile8Bit(file, ei->change->random_percentage);
7915 putFile8Bit(file, ei->change->replace_when);
7917 for (y = 0; y < 3; y++)
7918 for (x = 0; x < 3; x++)
7919 putFile16BitBE(file, ei->change->content.e[x][y]);
7921 putFile8Bit(file, ei->slippery_type);
7923 // some free bytes for future properties and padding
7924 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7931 if (check != num_changed_custom_elements) // should not happen
7932 Warn("inconsistent number of custom element properties");
7936 #if ENABLE_HISTORIC_CHUNKS
7937 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7939 struct ElementInfo *ei = &element_info[element];
7942 // ---------- custom element base property values (96 bytes) ----------------
7944 putFile16BitBE(file, element);
7946 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7947 putFile8Bit(file, ei->description[i]);
7949 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7951 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7953 putFile8Bit(file, ei->num_change_pages);
7955 putFile16BitBE(file, ei->ce_value_fixed_initial);
7956 putFile16BitBE(file, ei->ce_value_random_initial);
7957 putFile8Bit(file, ei->use_last_ce_value);
7959 putFile8Bit(file, ei->use_gfx_element);
7960 putFile16BitBE(file, ei->gfx_element_initial);
7962 putFile8Bit(file, ei->collect_score_initial);
7963 putFile8Bit(file, ei->collect_count_initial);
7965 putFile8Bit(file, ei->drop_delay_fixed);
7966 putFile8Bit(file, ei->push_delay_fixed);
7967 putFile8Bit(file, ei->drop_delay_random);
7968 putFile8Bit(file, ei->push_delay_random);
7969 putFile16BitBE(file, ei->move_delay_fixed);
7970 putFile16BitBE(file, ei->move_delay_random);
7972 // bits 0 - 15 of "move_pattern" ...
7973 putFile16BitBE(file, ei->move_pattern & 0xffff);
7974 putFile8Bit(file, ei->move_direction_initial);
7975 putFile8Bit(file, ei->move_stepsize);
7977 putFile8Bit(file, ei->slippery_type);
7979 for (y = 0; y < 3; y++)
7980 for (x = 0; x < 3; x++)
7981 putFile16BitBE(file, ei->content.e[x][y]);
7983 putFile16BitBE(file, ei->move_enter_element);
7984 putFile16BitBE(file, ei->move_leave_element);
7985 putFile8Bit(file, ei->move_leave_type);
7987 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7988 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7990 putFile8Bit(file, ei->access_direction);
7992 putFile8Bit(file, ei->explosion_delay);
7993 putFile8Bit(file, ei->ignition_delay);
7994 putFile8Bit(file, ei->explosion_type);
7996 // some free bytes for future custom property values and padding
7997 WriteUnusedBytesToFile(file, 1);
7999 // ---------- change page property values (48 bytes) ------------------------
8001 for (i = 0; i < ei->num_change_pages; i++)
8003 struct ElementChangeInfo *change = &ei->change_page[i];
8004 unsigned int event_bits;
8006 // bits 0 - 31 of "has_event[]" ...
8008 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8009 if (change->has_event[j])
8010 event_bits |= (1u << j);
8011 putFile32BitBE(file, event_bits);
8013 putFile16BitBE(file, change->target_element);
8015 putFile16BitBE(file, change->delay_fixed);
8016 putFile16BitBE(file, change->delay_random);
8017 putFile16BitBE(file, change->delay_frames);
8019 putFile16BitBE(file, change->initial_trigger_element);
8021 putFile8Bit(file, change->explode);
8022 putFile8Bit(file, change->use_target_content);
8023 putFile8Bit(file, change->only_if_complete);
8024 putFile8Bit(file, change->use_random_replace);
8026 putFile8Bit(file, change->random_percentage);
8027 putFile8Bit(file, change->replace_when);
8029 for (y = 0; y < 3; y++)
8030 for (x = 0; x < 3; x++)
8031 putFile16BitBE(file, change->target_content.e[x][y]);
8033 putFile8Bit(file, change->can_change);
8035 putFile8Bit(file, change->trigger_side);
8037 putFile8Bit(file, change->trigger_player);
8038 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8039 log_2(change->trigger_page)));
8041 putFile8Bit(file, change->has_action);
8042 putFile8Bit(file, change->action_type);
8043 putFile8Bit(file, change->action_mode);
8044 putFile16BitBE(file, change->action_arg);
8046 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8048 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8049 if (change->has_event[j])
8050 event_bits |= (1u << (j - 32));
8051 putFile8Bit(file, event_bits);
8056 #if ENABLE_HISTORIC_CHUNKS
8057 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8059 struct ElementInfo *ei = &element_info[element];
8060 struct ElementGroupInfo *group = ei->group;
8063 putFile16BitBE(file, element);
8065 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8066 putFile8Bit(file, ei->description[i]);
8068 putFile8Bit(file, group->num_elements);
8070 putFile8Bit(file, ei->use_gfx_element);
8071 putFile16BitBE(file, ei->gfx_element_initial);
8073 putFile8Bit(file, group->choice_mode);
8075 // some free bytes for future values and padding
8076 WriteUnusedBytesToFile(file, 3);
8078 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8079 putFile16BitBE(file, group->element[i]);
8083 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8084 boolean write_element)
8086 int save_type = entry->save_type;
8087 int data_type = entry->data_type;
8088 int conf_type = entry->conf_type;
8089 int byte_mask = conf_type & CONF_MASK_BYTES;
8090 int element = entry->element;
8091 int default_value = entry->default_value;
8093 boolean modified = FALSE;
8095 if (byte_mask != CONF_MASK_MULTI_BYTES)
8097 void *value_ptr = entry->value;
8098 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8101 // check if any settings have been modified before saving them
8102 if (value != default_value)
8105 // do not save if explicitly told or if unmodified default settings
8106 if ((save_type == SAVE_CONF_NEVER) ||
8107 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8111 num_bytes += putFile16BitBE(file, element);
8113 num_bytes += putFile8Bit(file, conf_type);
8114 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8115 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8116 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8119 else if (data_type == TYPE_STRING)
8121 char *default_string = entry->default_string;
8122 char *string = (char *)(entry->value);
8123 int string_length = strlen(string);
8126 // check if any settings have been modified before saving them
8127 if (!strEqual(string, default_string))
8130 // do not save if explicitly told or if unmodified default settings
8131 if ((save_type == SAVE_CONF_NEVER) ||
8132 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8136 num_bytes += putFile16BitBE(file, element);
8138 num_bytes += putFile8Bit(file, conf_type);
8139 num_bytes += putFile16BitBE(file, string_length);
8141 for (i = 0; i < string_length; i++)
8142 num_bytes += putFile8Bit(file, string[i]);
8144 else if (data_type == TYPE_ELEMENT_LIST)
8146 int *element_array = (int *)(entry->value);
8147 int num_elements = *(int *)(entry->num_entities);
8150 // check if any settings have been modified before saving them
8151 for (i = 0; i < num_elements; i++)
8152 if (element_array[i] != default_value)
8155 // do not save if explicitly told or if unmodified default settings
8156 if ((save_type == SAVE_CONF_NEVER) ||
8157 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8161 num_bytes += putFile16BitBE(file, element);
8163 num_bytes += putFile8Bit(file, conf_type);
8164 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8166 for (i = 0; i < num_elements; i++)
8167 num_bytes += putFile16BitBE(file, element_array[i]);
8169 else if (data_type == TYPE_CONTENT_LIST)
8171 struct Content *content = (struct Content *)(entry->value);
8172 int num_contents = *(int *)(entry->num_entities);
8175 // check if any settings have been modified before saving them
8176 for (i = 0; i < num_contents; i++)
8177 for (y = 0; y < 3; y++)
8178 for (x = 0; x < 3; x++)
8179 if (content[i].e[x][y] != default_value)
8182 // do not save if explicitly told or if unmodified default settings
8183 if ((save_type == SAVE_CONF_NEVER) ||
8184 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8188 num_bytes += putFile16BitBE(file, element);
8190 num_bytes += putFile8Bit(file, conf_type);
8191 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8193 for (i = 0; i < num_contents; i++)
8194 for (y = 0; y < 3; y++)
8195 for (x = 0; x < 3; x++)
8196 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8202 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8207 li = *level; // copy level data into temporary buffer
8209 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8210 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8215 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8220 li = *level; // copy level data into temporary buffer
8222 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8223 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8228 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8230 int envelope_nr = element - EL_ENVELOPE_1;
8234 chunk_size += putFile16BitBE(file, element);
8236 // copy envelope data into temporary buffer
8237 xx_envelope = level->envelope[envelope_nr];
8239 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8240 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8245 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8247 struct ElementInfo *ei = &element_info[element];
8251 chunk_size += putFile16BitBE(file, element);
8253 xx_ei = *ei; // copy element data into temporary buffer
8255 // set default description string for this specific element
8256 strcpy(xx_default_description, getDefaultElementDescription(ei));
8258 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8259 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8261 for (i = 0; i < ei->num_change_pages; i++)
8263 struct ElementChangeInfo *change = &ei->change_page[i];
8265 xx_current_change_page = i;
8267 xx_change = *change; // copy change data into temporary buffer
8270 setEventBitsFromEventFlags(change);
8272 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8273 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8280 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8282 struct ElementInfo *ei = &element_info[element];
8283 struct ElementGroupInfo *group = ei->group;
8287 chunk_size += putFile16BitBE(file, element);
8289 xx_ei = *ei; // copy element data into temporary buffer
8290 xx_group = *group; // copy group data into temporary buffer
8292 // set default description string for this specific element
8293 strcpy(xx_default_description, getDefaultElementDescription(ei));
8295 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8296 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8301 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8303 struct ElementInfo *ei = &element_info[element];
8307 chunk_size += putFile16BitBE(file, element);
8309 xx_ei = *ei; // copy element data into temporary buffer
8311 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8312 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8317 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8318 boolean save_as_template)
8324 if (!(file = fopen(filename, MODE_WRITE)))
8326 Warn("cannot save level file '%s'", filename);
8331 level->file_version = FILE_VERSION_ACTUAL;
8332 level->game_version = GAME_VERSION_ACTUAL;
8334 level->creation_date = getCurrentDate();
8336 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8337 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8339 chunk_size = SaveLevel_VERS(NULL, level);
8340 putFileChunkBE(file, "VERS", chunk_size);
8341 SaveLevel_VERS(file, level);
8343 chunk_size = SaveLevel_DATE(NULL, level);
8344 putFileChunkBE(file, "DATE", chunk_size);
8345 SaveLevel_DATE(file, level);
8347 chunk_size = SaveLevel_NAME(NULL, level);
8348 putFileChunkBE(file, "NAME", chunk_size);
8349 SaveLevel_NAME(file, level);
8351 chunk_size = SaveLevel_AUTH(NULL, level);
8352 putFileChunkBE(file, "AUTH", chunk_size);
8353 SaveLevel_AUTH(file, level);
8355 chunk_size = SaveLevel_INFO(NULL, level);
8356 putFileChunkBE(file, "INFO", chunk_size);
8357 SaveLevel_INFO(file, level);
8359 chunk_size = SaveLevel_BODY(NULL, level);
8360 putFileChunkBE(file, "BODY", chunk_size);
8361 SaveLevel_BODY(file, level);
8363 chunk_size = SaveLevel_ELEM(NULL, level);
8364 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8366 putFileChunkBE(file, "ELEM", chunk_size);
8367 SaveLevel_ELEM(file, level);
8370 for (i = 0; i < NUM_ENVELOPES; i++)
8372 int element = EL_ENVELOPE_1 + i;
8374 chunk_size = SaveLevel_NOTE(NULL, level, element);
8375 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8377 putFileChunkBE(file, "NOTE", chunk_size);
8378 SaveLevel_NOTE(file, level, element);
8382 // if not using template level, check for non-default custom/group elements
8383 if (!level->use_custom_template || save_as_template)
8385 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8387 int element = EL_CUSTOM_START + i;
8389 chunk_size = SaveLevel_CUSX(NULL, level, element);
8390 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8392 putFileChunkBE(file, "CUSX", chunk_size);
8393 SaveLevel_CUSX(file, level, element);
8397 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8399 int element = EL_GROUP_START + i;
8401 chunk_size = SaveLevel_GRPX(NULL, level, element);
8402 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8404 putFileChunkBE(file, "GRPX", chunk_size);
8405 SaveLevel_GRPX(file, level, element);
8409 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8411 int element = GET_EMPTY_ELEMENT(i);
8413 chunk_size = SaveLevel_EMPX(NULL, level, element);
8414 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8416 putFileChunkBE(file, "EMPX", chunk_size);
8417 SaveLevel_EMPX(file, level, element);
8424 SetFilePermissions(filename, PERMS_PRIVATE);
8427 void SaveLevel(int nr)
8429 char *filename = getDefaultLevelFilename(nr);
8431 SaveLevelFromFilename(&level, filename, FALSE);
8434 void SaveLevelTemplate(void)
8436 char *filename = getLocalLevelTemplateFilename();
8438 SaveLevelFromFilename(&level, filename, TRUE);
8441 boolean SaveLevelChecked(int nr)
8443 char *filename = getDefaultLevelFilename(nr);
8444 boolean new_level = !fileExists(filename);
8445 boolean level_saved = FALSE;
8447 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8452 Request("Level saved!", REQ_CONFIRM);
8460 void DumpLevel(struct LevelInfo *level)
8462 if (level->no_level_file || level->no_valid_file)
8464 Warn("cannot dump -- no valid level file found");
8470 Print("Level xxx (file version %08d, game version %08d)\n",
8471 level->file_version, level->game_version);
8474 Print("Level author: '%s'\n", level->author);
8475 Print("Level title: '%s'\n", level->name);
8477 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8479 Print("Level time: %d seconds\n", level->time);
8480 Print("Gems needed: %d\n", level->gems_needed);
8482 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8483 Print("Time for wheel: %d seconds\n", level->time_wheel);
8484 Print("Time for light: %d seconds\n", level->time_light);
8485 Print("Time for timegate: %d seconds\n", level->time_timegate);
8487 Print("Amoeba speed: %d\n", level->amoeba_speed);
8490 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8491 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8492 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8493 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8494 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8495 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8501 for (i = 0; i < NUM_ENVELOPES; i++)
8503 char *text = level->envelope[i].text;
8504 int text_len = strlen(text);
8505 boolean has_text = FALSE;
8507 for (j = 0; j < text_len; j++)
8508 if (text[j] != ' ' && text[j] != '\n')
8514 Print("Envelope %d:\n'%s'\n", i + 1, text);
8522 void DumpLevels(void)
8524 static LevelDirTree *dumplevel_leveldir = NULL;
8526 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8527 global.dumplevel_leveldir);
8529 if (dumplevel_leveldir == NULL)
8530 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8532 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8533 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8534 Fail("no such level number: %d", global.dumplevel_level_nr);
8536 leveldir_current = dumplevel_leveldir;
8538 LoadLevel(global.dumplevel_level_nr);
8545 // ============================================================================
8546 // tape file functions
8547 // ============================================================================
8549 static void setTapeInfoToDefaults(void)
8553 // always start with reliable default values (empty tape)
8556 // default values (also for pre-1.2 tapes) with only the first player
8557 tape.player_participates[0] = TRUE;
8558 for (i = 1; i < MAX_PLAYERS; i++)
8559 tape.player_participates[i] = FALSE;
8561 // at least one (default: the first) player participates in every tape
8562 tape.num_participating_players = 1;
8564 tape.property_bits = TAPE_PROPERTY_NONE;
8566 tape.level_nr = level_nr;
8568 tape.changed = FALSE;
8569 tape.solved = FALSE;
8571 tape.recording = FALSE;
8572 tape.playing = FALSE;
8573 tape.pausing = FALSE;
8575 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8576 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8578 tape.no_info_chunk = TRUE;
8579 tape.no_valid_file = FALSE;
8582 static int getTapePosSize(struct TapeInfo *tape)
8584 int tape_pos_size = 0;
8586 if (tape->use_key_actions)
8587 tape_pos_size += tape->num_participating_players;
8589 if (tape->use_mouse_actions)
8590 tape_pos_size += 3; // x and y position and mouse button mask
8592 tape_pos_size += 1; // tape action delay value
8594 return tape_pos_size;
8597 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8599 tape->use_key_actions = FALSE;
8600 tape->use_mouse_actions = FALSE;
8602 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8603 tape->use_key_actions = TRUE;
8605 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8606 tape->use_mouse_actions = TRUE;
8609 static int getTapeActionValue(struct TapeInfo *tape)
8611 return (tape->use_key_actions &&
8612 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8613 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8614 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8615 TAPE_ACTIONS_DEFAULT);
8618 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8620 tape->file_version = getFileVersion(file);
8621 tape->game_version = getFileVersion(file);
8626 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8630 tape->random_seed = getFile32BitBE(file);
8631 tape->date = getFile32BitBE(file);
8632 tape->length = getFile32BitBE(file);
8634 // read header fields that are new since version 1.2
8635 if (tape->file_version >= FILE_VERSION_1_2)
8637 byte store_participating_players = getFile8Bit(file);
8640 // since version 1.2, tapes store which players participate in the tape
8641 tape->num_participating_players = 0;
8642 for (i = 0; i < MAX_PLAYERS; i++)
8644 tape->player_participates[i] = FALSE;
8646 if (store_participating_players & (1 << i))
8648 tape->player_participates[i] = TRUE;
8649 tape->num_participating_players++;
8653 setTapeActionFlags(tape, getFile8Bit(file));
8655 tape->property_bits = getFile8Bit(file);
8656 tape->solved = getFile8Bit(file);
8658 engine_version = getFileVersion(file);
8659 if (engine_version > 0)
8660 tape->engine_version = engine_version;
8662 tape->engine_version = tape->game_version;
8668 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8670 tape->scr_fieldx = getFile8Bit(file);
8671 tape->scr_fieldy = getFile8Bit(file);
8676 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8678 char *level_identifier = NULL;
8679 int level_identifier_size;
8682 tape->no_info_chunk = FALSE;
8684 level_identifier_size = getFile16BitBE(file);
8686 level_identifier = checked_malloc(level_identifier_size);
8688 for (i = 0; i < level_identifier_size; i++)
8689 level_identifier[i] = getFile8Bit(file);
8691 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8692 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8694 checked_free(level_identifier);
8696 tape->level_nr = getFile16BitBE(file);
8698 chunk_size = 2 + level_identifier_size + 2;
8703 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8706 int tape_pos_size = getTapePosSize(tape);
8707 int chunk_size_expected = tape_pos_size * tape->length;
8709 if (chunk_size_expected != chunk_size)
8711 ReadUnusedBytesFromFile(file, chunk_size);
8712 return chunk_size_expected;
8715 for (i = 0; i < tape->length; i++)
8717 if (i >= MAX_TAPE_LEN)
8719 Warn("tape truncated -- size exceeds maximum tape size %d",
8722 // tape too large; read and ignore remaining tape data from this chunk
8723 for (;i < tape->length; i++)
8724 ReadUnusedBytesFromFile(file, tape_pos_size);
8729 if (tape->use_key_actions)
8731 for (j = 0; j < MAX_PLAYERS; j++)
8733 tape->pos[i].action[j] = MV_NONE;
8735 if (tape->player_participates[j])
8736 tape->pos[i].action[j] = getFile8Bit(file);
8740 if (tape->use_mouse_actions)
8742 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8743 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8744 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8747 tape->pos[i].delay = getFile8Bit(file);
8749 if (tape->file_version == FILE_VERSION_1_0)
8751 // eliminate possible diagonal moves in old tapes
8752 // this is only for backward compatibility
8754 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8755 byte action = tape->pos[i].action[0];
8756 int k, num_moves = 0;
8758 for (k = 0; k < 4; k++)
8760 if (action & joy_dir[k])
8762 tape->pos[i + num_moves].action[0] = joy_dir[k];
8764 tape->pos[i + num_moves].delay = 0;
8773 tape->length += num_moves;
8776 else if (tape->file_version < FILE_VERSION_2_0)
8778 // convert pre-2.0 tapes to new tape format
8780 if (tape->pos[i].delay > 1)
8783 tape->pos[i + 1] = tape->pos[i];
8784 tape->pos[i + 1].delay = 1;
8787 for (j = 0; j < MAX_PLAYERS; j++)
8788 tape->pos[i].action[j] = MV_NONE;
8789 tape->pos[i].delay--;
8796 if (checkEndOfFile(file))
8800 if (i != tape->length)
8801 chunk_size = tape_pos_size * i;
8806 static void LoadTape_SokobanSolution(char *filename)
8809 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8811 if (!(file = openFile(filename, MODE_READ)))
8813 tape.no_valid_file = TRUE;
8818 while (!checkEndOfFile(file))
8820 unsigned char c = getByteFromFile(file);
8822 if (checkEndOfFile(file))
8829 tape.pos[tape.length].action[0] = MV_UP;
8830 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8836 tape.pos[tape.length].action[0] = MV_DOWN;
8837 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8843 tape.pos[tape.length].action[0] = MV_LEFT;
8844 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8850 tape.pos[tape.length].action[0] = MV_RIGHT;
8851 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8859 // ignore white-space characters
8863 tape.no_valid_file = TRUE;
8865 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8873 if (tape.no_valid_file)
8876 tape.length_frames = GetTapeLengthFrames();
8877 tape.length_seconds = GetTapeLengthSeconds();
8880 void LoadTapeFromFilename(char *filename)
8882 char cookie[MAX_LINE_LEN];
8883 char chunk_name[CHUNK_ID_LEN + 1];
8887 // always start with reliable default values
8888 setTapeInfoToDefaults();
8890 if (strSuffix(filename, ".sln"))
8892 LoadTape_SokobanSolution(filename);
8897 if (!(file = openFile(filename, MODE_READ)))
8899 tape.no_valid_file = TRUE;
8904 getFileChunkBE(file, chunk_name, NULL);
8905 if (strEqual(chunk_name, "RND1"))
8907 getFile32BitBE(file); // not used
8909 getFileChunkBE(file, chunk_name, NULL);
8910 if (!strEqual(chunk_name, "TAPE"))
8912 tape.no_valid_file = TRUE;
8914 Warn("unknown format of tape file '%s'", filename);
8921 else // check for pre-2.0 file format with cookie string
8923 strcpy(cookie, chunk_name);
8924 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8926 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8927 cookie[strlen(cookie) - 1] = '\0';
8929 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8931 tape.no_valid_file = TRUE;
8933 Warn("unknown format of tape file '%s'", filename);
8940 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8942 tape.no_valid_file = TRUE;
8944 Warn("unsupported version of tape file '%s'", filename);
8951 // pre-2.0 tape files have no game version, so use file version here
8952 tape.game_version = tape.file_version;
8955 if (tape.file_version < FILE_VERSION_1_2)
8957 // tape files from versions before 1.2.0 without chunk structure
8958 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8959 LoadTape_BODY(file, 2 * tape.length, &tape);
8967 int (*loader)(File *, int, struct TapeInfo *);
8971 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8972 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8973 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8974 { "INFO", -1, LoadTape_INFO },
8975 { "BODY", -1, LoadTape_BODY },
8979 while (getFileChunkBE(file, chunk_name, &chunk_size))
8983 while (chunk_info[i].name != NULL &&
8984 !strEqual(chunk_name, chunk_info[i].name))
8987 if (chunk_info[i].name == NULL)
8989 Warn("unknown chunk '%s' in tape file '%s'",
8990 chunk_name, filename);
8992 ReadUnusedBytesFromFile(file, chunk_size);
8994 else if (chunk_info[i].size != -1 &&
8995 chunk_info[i].size != chunk_size)
8997 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8998 chunk_size, chunk_name, filename);
9000 ReadUnusedBytesFromFile(file, chunk_size);
9004 // call function to load this tape chunk
9005 int chunk_size_expected =
9006 (chunk_info[i].loader)(file, chunk_size, &tape);
9008 // the size of some chunks cannot be checked before reading other
9009 // chunks first (like "HEAD" and "BODY") that contain some header
9010 // information, so check them here
9011 if (chunk_size_expected != chunk_size)
9013 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9014 chunk_size, chunk_name, filename);
9022 tape.length_frames = GetTapeLengthFrames();
9023 tape.length_seconds = GetTapeLengthSeconds();
9026 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9028 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9030 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9031 tape.engine_version);
9035 void LoadTape(int nr)
9037 char *filename = getTapeFilename(nr);
9039 LoadTapeFromFilename(filename);
9042 void LoadSolutionTape(int nr)
9044 char *filename = getSolutionTapeFilename(nr);
9046 LoadTapeFromFilename(filename);
9048 if (TAPE_IS_EMPTY(tape))
9050 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9051 level.native_bd_level->replay != NULL)
9052 CopyNativeTape_BD_to_RND(&level);
9053 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9054 level.native_sp_level->demo.is_available)
9055 CopyNativeTape_SP_to_RND(&level);
9059 void LoadScoreTape(char *score_tape_basename, int nr)
9061 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9063 LoadTapeFromFilename(filename);
9066 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9068 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9070 LoadTapeFromFilename(filename);
9073 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9075 // chunk required for team mode tapes with non-default screen size
9076 return (tape->num_participating_players > 1 &&
9077 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9078 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9081 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9083 putFileVersion(file, tape->file_version);
9084 putFileVersion(file, tape->game_version);
9087 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9090 byte store_participating_players = 0;
9092 // set bits for participating players for compact storage
9093 for (i = 0; i < MAX_PLAYERS; i++)
9094 if (tape->player_participates[i])
9095 store_participating_players |= (1 << i);
9097 putFile32BitBE(file, tape->random_seed);
9098 putFile32BitBE(file, tape->date);
9099 putFile32BitBE(file, tape->length);
9101 putFile8Bit(file, store_participating_players);
9103 putFile8Bit(file, getTapeActionValue(tape));
9105 putFile8Bit(file, tape->property_bits);
9106 putFile8Bit(file, tape->solved);
9108 putFileVersion(file, tape->engine_version);
9111 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9113 putFile8Bit(file, tape->scr_fieldx);
9114 putFile8Bit(file, tape->scr_fieldy);
9117 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9119 int level_identifier_size = strlen(tape->level_identifier) + 1;
9122 putFile16BitBE(file, level_identifier_size);
9124 for (i = 0; i < level_identifier_size; i++)
9125 putFile8Bit(file, tape->level_identifier[i]);
9127 putFile16BitBE(file, tape->level_nr);
9130 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9134 for (i = 0; i < tape->length; i++)
9136 if (tape->use_key_actions)
9138 for (j = 0; j < MAX_PLAYERS; j++)
9139 if (tape->player_participates[j])
9140 putFile8Bit(file, tape->pos[i].action[j]);
9143 if (tape->use_mouse_actions)
9145 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9146 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9147 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9150 putFile8Bit(file, tape->pos[i].delay);
9154 void SaveTapeToFilename(char *filename)
9158 int info_chunk_size;
9159 int body_chunk_size;
9161 if (!(file = fopen(filename, MODE_WRITE)))
9163 Warn("cannot save level recording file '%s'", filename);
9168 tape_pos_size = getTapePosSize(&tape);
9170 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9171 body_chunk_size = tape_pos_size * tape.length;
9173 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9174 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9176 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9177 SaveTape_VERS(file, &tape);
9179 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9180 SaveTape_HEAD(file, &tape);
9182 if (checkSaveTape_SCRN(&tape))
9184 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9185 SaveTape_SCRN(file, &tape);
9188 putFileChunkBE(file, "INFO", info_chunk_size);
9189 SaveTape_INFO(file, &tape);
9191 putFileChunkBE(file, "BODY", body_chunk_size);
9192 SaveTape_BODY(file, &tape);
9196 SetFilePermissions(filename, PERMS_PRIVATE);
9199 static void SaveTapeExt(char *filename)
9203 tape.file_version = FILE_VERSION_ACTUAL;
9204 tape.game_version = GAME_VERSION_ACTUAL;
9206 tape.num_participating_players = 0;
9208 // count number of participating players
9209 for (i = 0; i < MAX_PLAYERS; i++)
9210 if (tape.player_participates[i])
9211 tape.num_participating_players++;
9213 SaveTapeToFilename(filename);
9215 tape.changed = FALSE;
9218 void SaveTape(int nr)
9220 char *filename = getTapeFilename(nr);
9222 InitTapeDirectory(leveldir_current->subdir);
9224 SaveTapeExt(filename);
9227 void SaveScoreTape(int nr)
9229 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9231 // used instead of "leveldir_current->subdir" (for network games)
9232 InitScoreTapeDirectory(levelset.identifier, nr);
9234 SaveTapeExt(filename);
9237 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9238 unsigned int req_state_added)
9240 char *filename = getTapeFilename(nr);
9241 boolean new_tape = !fileExists(filename);
9242 boolean tape_saved = FALSE;
9244 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9249 Request(msg_saved, REQ_CONFIRM | req_state_added);
9257 boolean SaveTapeChecked(int nr)
9259 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9262 boolean SaveTapeChecked_LevelSolved(int nr)
9264 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9265 "Level solved! Tape saved!", REQ_STAY_OPEN);
9268 void DumpTape(struct TapeInfo *tape)
9270 int tape_frame_counter;
9273 if (tape->no_valid_file)
9275 Warn("cannot dump -- no valid tape file found");
9282 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9283 tape->level_nr, tape->file_version, tape->game_version);
9284 Print(" (effective engine version %08d)\n",
9285 tape->engine_version);
9286 Print("Level series identifier: '%s'\n", tape->level_identifier);
9288 Print("Solution tape: %s\n",
9289 tape->solved ? "yes" :
9290 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9292 Print("Special tape properties: ");
9293 if (tape->property_bits == TAPE_PROPERTY_NONE)
9295 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9296 Print("[em_random_bug]");
9297 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9298 Print("[game_speed]");
9299 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9301 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9302 Print("[single_step]");
9303 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9304 Print("[snapshot]");
9305 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9306 Print("[replayed]");
9307 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9308 Print("[tas_keys]");
9309 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9310 Print("[small_graphics]");
9313 int year2 = tape->date / 10000;
9314 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9315 int month_index_raw = (tape->date / 100) % 100;
9316 int month_index = month_index_raw % 12; // prevent invalid index
9317 int month = month_index + 1;
9318 int day = tape->date % 100;
9320 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9324 tape_frame_counter = 0;
9326 for (i = 0; i < tape->length; i++)
9328 if (i >= MAX_TAPE_LEN)
9333 for (j = 0; j < MAX_PLAYERS; j++)
9335 if (tape->player_participates[j])
9337 int action = tape->pos[i].action[j];
9339 Print("%d:%02x ", j, action);
9340 Print("[%c%c%c%c|%c%c] - ",
9341 (action & JOY_LEFT ? '<' : ' '),
9342 (action & JOY_RIGHT ? '>' : ' '),
9343 (action & JOY_UP ? '^' : ' '),
9344 (action & JOY_DOWN ? 'v' : ' '),
9345 (action & JOY_BUTTON_1 ? '1' : ' '),
9346 (action & JOY_BUTTON_2 ? '2' : ' '));
9350 Print("(%03d) ", tape->pos[i].delay);
9351 Print("[%05d]\n", tape_frame_counter);
9353 tape_frame_counter += tape->pos[i].delay;
9359 void DumpTapes(void)
9361 static LevelDirTree *dumptape_leveldir = NULL;
9363 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9364 global.dumptape_leveldir);
9366 if (dumptape_leveldir == NULL)
9367 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9369 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9370 global.dumptape_level_nr > dumptape_leveldir->last_level)
9371 Fail("no such level number: %d", global.dumptape_level_nr);
9373 leveldir_current = dumptape_leveldir;
9375 if (options.mytapes)
9376 LoadTape(global.dumptape_level_nr);
9378 LoadSolutionTape(global.dumptape_level_nr);
9386 // ============================================================================
9387 // score file functions
9388 // ============================================================================
9390 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9394 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9396 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9397 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9398 scores->entry[i].score = 0;
9399 scores->entry[i].time = 0;
9401 scores->entry[i].id = -1;
9402 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9403 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9404 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9405 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9406 strcpy(scores->entry[i].country_code, "??");
9409 scores->num_entries = 0;
9410 scores->last_added = -1;
9411 scores->last_added_local = -1;
9413 scores->updated = FALSE;
9414 scores->uploaded = FALSE;
9415 scores->tape_downloaded = FALSE;
9416 scores->force_last_added = FALSE;
9418 // The following values are intentionally not reset here:
9422 // - continue_playing
9423 // - continue_on_return
9426 static void setScoreInfoToDefaults(void)
9428 setScoreInfoToDefaultsExt(&scores);
9431 static void setServerScoreInfoToDefaults(void)
9433 setScoreInfoToDefaultsExt(&server_scores);
9436 static void LoadScore_OLD(int nr)
9439 char *filename = getScoreFilename(nr);
9440 char cookie[MAX_LINE_LEN];
9441 char line[MAX_LINE_LEN];
9445 if (!(file = fopen(filename, MODE_READ)))
9448 // check file identifier
9449 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9451 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9452 cookie[strlen(cookie) - 1] = '\0';
9454 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9456 Warn("unknown format of score file '%s'", filename);
9463 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9465 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9466 Warn("fscanf() failed; %s", strerror(errno));
9468 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9471 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9472 line[strlen(line) - 1] = '\0';
9474 for (line_ptr = line; *line_ptr; line_ptr++)
9476 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9478 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9479 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9488 static void ConvertScore_OLD(void)
9490 // only convert score to time for levels that rate playing time over score
9491 if (!level.rate_time_over_score)
9494 // convert old score to playing time for score-less levels (like Supaplex)
9495 int time_final_max = 999;
9498 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9500 int score = scores.entry[i].score;
9502 if (score > 0 && score < time_final_max)
9503 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9507 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9509 scores->file_version = getFileVersion(file);
9510 scores->game_version = getFileVersion(file);
9515 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9517 char *level_identifier = NULL;
9518 int level_identifier_size;
9521 level_identifier_size = getFile16BitBE(file);
9523 level_identifier = checked_malloc(level_identifier_size);
9525 for (i = 0; i < level_identifier_size; i++)
9526 level_identifier[i] = getFile8Bit(file);
9528 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9529 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9531 checked_free(level_identifier);
9533 scores->level_nr = getFile16BitBE(file);
9534 scores->num_entries = getFile16BitBE(file);
9536 chunk_size = 2 + level_identifier_size + 2 + 2;
9541 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9545 for (i = 0; i < scores->num_entries; i++)
9547 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9548 scores->entry[i].name[j] = getFile8Bit(file);
9550 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9553 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9558 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9562 for (i = 0; i < scores->num_entries; i++)
9563 scores->entry[i].score = getFile16BitBE(file);
9565 chunk_size = scores->num_entries * 2;
9570 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9574 for (i = 0; i < scores->num_entries; i++)
9575 scores->entry[i].score = getFile32BitBE(file);
9577 chunk_size = scores->num_entries * 4;
9582 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9586 for (i = 0; i < scores->num_entries; i++)
9587 scores->entry[i].time = getFile32BitBE(file);
9589 chunk_size = scores->num_entries * 4;
9594 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9598 for (i = 0; i < scores->num_entries; i++)
9600 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9601 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9603 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9606 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9611 void LoadScore(int nr)
9613 char *filename = getScoreFilename(nr);
9614 char cookie[MAX_LINE_LEN];
9615 char chunk_name[CHUNK_ID_LEN + 1];
9617 boolean old_score_file_format = FALSE;
9620 // always start with reliable default values
9621 setScoreInfoToDefaults();
9623 if (!(file = openFile(filename, MODE_READ)))
9626 getFileChunkBE(file, chunk_name, NULL);
9627 if (strEqual(chunk_name, "RND1"))
9629 getFile32BitBE(file); // not used
9631 getFileChunkBE(file, chunk_name, NULL);
9632 if (!strEqual(chunk_name, "SCOR"))
9634 Warn("unknown format of score file '%s'", filename);
9641 else // check for old file format with cookie string
9643 strcpy(cookie, chunk_name);
9644 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9646 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9647 cookie[strlen(cookie) - 1] = '\0';
9649 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9651 Warn("unknown format of score file '%s'", filename);
9658 old_score_file_format = TRUE;
9661 if (old_score_file_format)
9663 // score files from versions before 4.2.4.0 without chunk structure
9666 // convert score to time, if possible (mainly for Supaplex levels)
9675 int (*loader)(File *, int, struct ScoreInfo *);
9679 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9680 { "INFO", -1, LoadScore_INFO },
9681 { "NAME", -1, LoadScore_NAME },
9682 { "SCOR", -1, LoadScore_SCOR },
9683 { "SC4R", -1, LoadScore_SC4R },
9684 { "TIME", -1, LoadScore_TIME },
9685 { "TAPE", -1, LoadScore_TAPE },
9690 while (getFileChunkBE(file, chunk_name, &chunk_size))
9694 while (chunk_info[i].name != NULL &&
9695 !strEqual(chunk_name, chunk_info[i].name))
9698 if (chunk_info[i].name == NULL)
9700 Warn("unknown chunk '%s' in score file '%s'",
9701 chunk_name, filename);
9703 ReadUnusedBytesFromFile(file, chunk_size);
9705 else if (chunk_info[i].size != -1 &&
9706 chunk_info[i].size != chunk_size)
9708 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9709 chunk_size, chunk_name, filename);
9711 ReadUnusedBytesFromFile(file, chunk_size);
9715 // call function to load this score chunk
9716 int chunk_size_expected =
9717 (chunk_info[i].loader)(file, chunk_size, &scores);
9719 // the size of some chunks cannot be checked before reading other
9720 // chunks first (like "HEAD" and "BODY") that contain some header
9721 // information, so check them here
9722 if (chunk_size_expected != chunk_size)
9724 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9725 chunk_size, chunk_name, filename);
9734 #if ENABLE_HISTORIC_CHUNKS
9735 void SaveScore_OLD(int nr)
9738 char *filename = getScoreFilename(nr);
9741 // used instead of "leveldir_current->subdir" (for network games)
9742 InitScoreDirectory(levelset.identifier);
9744 if (!(file = fopen(filename, MODE_WRITE)))
9746 Warn("cannot save score for level %d", nr);
9751 fprintf(file, "%s\n\n", SCORE_COOKIE);
9753 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9754 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9758 SetFilePermissions(filename, PERMS_PRIVATE);
9762 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9764 putFileVersion(file, scores->file_version);
9765 putFileVersion(file, scores->game_version);
9768 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9770 int level_identifier_size = strlen(scores->level_identifier) + 1;
9773 putFile16BitBE(file, level_identifier_size);
9775 for (i = 0; i < level_identifier_size; i++)
9776 putFile8Bit(file, scores->level_identifier[i]);
9778 putFile16BitBE(file, scores->level_nr);
9779 putFile16BitBE(file, scores->num_entries);
9782 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9786 for (i = 0; i < scores->num_entries; i++)
9788 int name_size = strlen(scores->entry[i].name);
9790 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9791 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9795 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9799 for (i = 0; i < scores->num_entries; i++)
9800 putFile16BitBE(file, scores->entry[i].score);
9803 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9807 for (i = 0; i < scores->num_entries; i++)
9808 putFile32BitBE(file, scores->entry[i].score);
9811 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9815 for (i = 0; i < scores->num_entries; i++)
9816 putFile32BitBE(file, scores->entry[i].time);
9819 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9823 for (i = 0; i < scores->num_entries; i++)
9825 int size = strlen(scores->entry[i].tape_basename);
9827 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9828 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9832 static void SaveScoreToFilename(char *filename)
9835 int info_chunk_size;
9836 int name_chunk_size;
9837 int scor_chunk_size;
9838 int sc4r_chunk_size;
9839 int time_chunk_size;
9840 int tape_chunk_size;
9841 boolean has_large_score_values;
9844 if (!(file = fopen(filename, MODE_WRITE)))
9846 Warn("cannot save score file '%s'", filename);
9851 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9852 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9853 scor_chunk_size = scores.num_entries * 2;
9854 sc4r_chunk_size = scores.num_entries * 4;
9855 time_chunk_size = scores.num_entries * 4;
9856 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9858 has_large_score_values = FALSE;
9859 for (i = 0; i < scores.num_entries; i++)
9860 if (scores.entry[i].score > 0xffff)
9861 has_large_score_values = TRUE;
9863 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9864 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9866 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9867 SaveScore_VERS(file, &scores);
9869 putFileChunkBE(file, "INFO", info_chunk_size);
9870 SaveScore_INFO(file, &scores);
9872 putFileChunkBE(file, "NAME", name_chunk_size);
9873 SaveScore_NAME(file, &scores);
9875 if (has_large_score_values)
9877 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9878 SaveScore_SC4R(file, &scores);
9882 putFileChunkBE(file, "SCOR", scor_chunk_size);
9883 SaveScore_SCOR(file, &scores);
9886 putFileChunkBE(file, "TIME", time_chunk_size);
9887 SaveScore_TIME(file, &scores);
9889 putFileChunkBE(file, "TAPE", tape_chunk_size);
9890 SaveScore_TAPE(file, &scores);
9894 SetFilePermissions(filename, PERMS_PRIVATE);
9897 void SaveScore(int nr)
9899 char *filename = getScoreFilename(nr);
9902 // used instead of "leveldir_current->subdir" (for network games)
9903 InitScoreDirectory(levelset.identifier);
9905 scores.file_version = FILE_VERSION_ACTUAL;
9906 scores.game_version = GAME_VERSION_ACTUAL;
9908 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9909 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9910 scores.level_nr = level_nr;
9912 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9913 if (scores.entry[i].score == 0 &&
9914 scores.entry[i].time == 0 &&
9915 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9918 scores.num_entries = i;
9920 if (scores.num_entries == 0)
9923 SaveScoreToFilename(filename);
9926 static void LoadServerScoreFromCache(int nr)
9928 struct ScoreEntry score_entry;
9937 { &score_entry.score, FALSE, 0 },
9938 { &score_entry.time, FALSE, 0 },
9939 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9940 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9941 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9942 { &score_entry.id, FALSE, 0 },
9943 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9944 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9945 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9946 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9950 char *filename = getScoreCacheFilename(nr);
9951 SetupFileHash *score_hash = loadSetupFileHash(filename);
9954 server_scores.num_entries = 0;
9956 if (score_hash == NULL)
9959 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9961 score_entry = server_scores.entry[i];
9963 for (j = 0; score_mapping[j].value != NULL; j++)
9967 sprintf(token, "%02d.%d", i, j);
9969 char *value = getHashEntry(score_hash, token);
9974 if (score_mapping[j].is_string)
9976 char *score_value = (char *)score_mapping[j].value;
9977 int value_size = score_mapping[j].string_size;
9979 strncpy(score_value, value, value_size);
9980 score_value[value_size] = '\0';
9984 int *score_value = (int *)score_mapping[j].value;
9986 *score_value = atoi(value);
9989 server_scores.num_entries = i + 1;
9992 server_scores.entry[i] = score_entry;
9995 freeSetupFileHash(score_hash);
9998 void LoadServerScore(int nr, boolean download_score)
10000 if (!setup.use_api_server)
10003 // always start with reliable default values
10004 setServerScoreInfoToDefaults();
10006 // 1st step: load server scores from cache file (which may not exist)
10007 // (this should prevent reading it while the thread is writing to it)
10008 LoadServerScoreFromCache(nr);
10010 if (download_score && runtime.use_api_server)
10012 // 2nd step: download server scores from score server to cache file
10013 // (as thread, as it might time out if the server is not reachable)
10014 ApiGetScoreAsThread(nr);
10018 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10020 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10022 // if score tape not uploaded, ask for uploading missing tapes later
10023 if (!setup.has_remaining_tapes)
10024 setup.ask_for_remaining_tapes = TRUE;
10026 setup.provide_uploading_tapes = TRUE;
10027 setup.has_remaining_tapes = TRUE;
10029 SaveSetup_ServerSetup();
10032 void SaveServerScore(int nr, boolean tape_saved)
10034 if (!runtime.use_api_server)
10036 PrepareScoreTapesForUpload(leveldir_current->subdir);
10041 ApiAddScoreAsThread(nr, tape_saved, NULL);
10044 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10045 char *score_tape_filename)
10047 if (!runtime.use_api_server)
10050 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10053 void LoadLocalAndServerScore(int nr, boolean download_score)
10055 int last_added_local = scores.last_added_local;
10056 boolean force_last_added = scores.force_last_added;
10058 // needed if only showing server scores
10059 setScoreInfoToDefaults();
10061 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10064 // restore last added local score entry (before merging server scores)
10065 scores.last_added = scores.last_added_local = last_added_local;
10067 if (setup.use_api_server &&
10068 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10070 // load server scores from cache file and trigger update from server
10071 LoadServerScore(nr, download_score);
10073 // merge local scores with scores from server
10074 MergeServerScore();
10077 if (force_last_added)
10078 scores.force_last_added = force_last_added;
10082 // ============================================================================
10083 // setup file functions
10084 // ============================================================================
10086 #define TOKEN_STR_PLAYER_PREFIX "player_"
10089 static struct TokenInfo global_setup_tokens[] =
10093 &setup.player_name, "player_name"
10097 &setup.multiple_users, "multiple_users"
10101 &setup.sound, "sound"
10105 &setup.sound_loops, "repeating_sound_loops"
10109 &setup.sound_music, "background_music"
10113 &setup.sound_simple, "simple_sound_effects"
10117 &setup.toons, "toons"
10121 &setup.global_animations, "global_animations"
10125 &setup.scroll_delay, "scroll_delay"
10129 &setup.forced_scroll_delay, "forced_scroll_delay"
10133 &setup.scroll_delay_value, "scroll_delay_value"
10137 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10141 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10145 &setup.fade_screens, "fade_screens"
10149 &setup.autorecord, "automatic_tape_recording"
10153 &setup.autorecord_after_replay, "autorecord_after_replay"
10157 &setup.auto_pause_on_start, "auto_pause_on_start"
10161 &setup.show_titlescreen, "show_titlescreen"
10165 &setup.quick_doors, "quick_doors"
10169 &setup.team_mode, "team_mode"
10173 &setup.handicap, "handicap"
10177 &setup.skip_levels, "skip_levels"
10181 &setup.increment_levels, "increment_levels"
10185 &setup.auto_play_next_level, "auto_play_next_level"
10189 &setup.count_score_after_game, "count_score_after_game"
10193 &setup.show_scores_after_game, "show_scores_after_game"
10197 &setup.time_limit, "time_limit"
10201 &setup.fullscreen, "fullscreen"
10205 &setup.window_scaling_percent, "window_scaling_percent"
10209 &setup.window_scaling_quality, "window_scaling_quality"
10213 &setup.screen_rendering_mode, "screen_rendering_mode"
10217 &setup.vsync_mode, "vsync_mode"
10221 &setup.ask_on_escape, "ask_on_escape"
10225 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10229 &setup.ask_on_game_over, "ask_on_game_over"
10233 &setup.ask_on_quit_game, "ask_on_quit_game"
10237 &setup.ask_on_quit_program, "ask_on_quit_program"
10241 &setup.quick_switch, "quick_player_switch"
10245 &setup.input_on_focus, "input_on_focus"
10249 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10253 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10257 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10261 &setup.game_speed_extended, "game_speed_extended"
10265 &setup.game_frame_delay, "game_frame_delay"
10269 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10273 &setup.bd_skip_hatching, "bd_skip_hatching"
10277 &setup.bd_scroll_delay, "bd_scroll_delay"
10281 &setup.bd_smooth_movements, "bd_smooth_movements"
10285 &setup.sp_show_border_elements, "sp_show_border_elements"
10289 &setup.small_game_graphics, "small_game_graphics"
10293 &setup.show_load_save_buttons, "show_load_save_buttons"
10297 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10301 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10305 &setup.graphics_set, "graphics_set"
10309 &setup.sounds_set, "sounds_set"
10313 &setup.music_set, "music_set"
10317 &setup.override_level_graphics, "override_level_graphics"
10321 &setup.override_level_sounds, "override_level_sounds"
10325 &setup.override_level_music, "override_level_music"
10329 &setup.volume_simple, "volume_simple"
10333 &setup.volume_loops, "volume_loops"
10337 &setup.volume_music, "volume_music"
10341 &setup.network_mode, "network_mode"
10345 &setup.network_player_nr, "network_player"
10349 &setup.network_server_hostname, "network_server_hostname"
10353 &setup.touch.control_type, "touch.control_type"
10357 &setup.touch.move_distance, "touch.move_distance"
10361 &setup.touch.drop_distance, "touch.drop_distance"
10365 &setup.touch.transparency, "touch.transparency"
10369 &setup.touch.draw_outlined, "touch.draw_outlined"
10373 &setup.touch.draw_pressed, "touch.draw_pressed"
10377 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10381 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10385 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10389 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10393 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10397 static struct TokenInfo auto_setup_tokens[] =
10401 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10405 static struct TokenInfo server_setup_tokens[] =
10409 &setup.player_uuid, "player_uuid"
10413 &setup.player_version, "player_version"
10417 &setup.use_api_server, TEST_PREFIX "use_api_server"
10421 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10425 &setup.api_server_password, TEST_PREFIX "api_server_password"
10429 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10433 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10437 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10441 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10445 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10449 static struct TokenInfo editor_setup_tokens[] =
10453 &setup.editor.el_classic, "editor.el_classic"
10457 &setup.editor.el_custom, "editor.el_custom"
10461 &setup.editor.el_user_defined, "editor.el_user_defined"
10465 &setup.editor.el_dynamic, "editor.el_dynamic"
10469 &setup.editor.el_headlines, "editor.el_headlines"
10473 &setup.editor.show_element_token, "editor.show_element_token"
10477 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10481 static struct TokenInfo editor_cascade_setup_tokens[] =
10485 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10489 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10493 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10497 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10501 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10505 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10509 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10513 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10517 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10521 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10525 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10529 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10533 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10537 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10541 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10545 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10549 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10553 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10557 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10561 static struct TokenInfo shortcut_setup_tokens[] =
10565 &setup.shortcut.save_game, "shortcut.save_game"
10569 &setup.shortcut.load_game, "shortcut.load_game"
10573 &setup.shortcut.restart_game, "shortcut.restart_game"
10577 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10581 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10585 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10589 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10593 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10597 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10601 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10605 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10609 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10613 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10617 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10621 &setup.shortcut.tape_record, "shortcut.tape_record"
10625 &setup.shortcut.tape_play, "shortcut.tape_play"
10629 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10633 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10637 &setup.shortcut.sound_music, "shortcut.sound_music"
10641 &setup.shortcut.snap_left, "shortcut.snap_left"
10645 &setup.shortcut.snap_right, "shortcut.snap_right"
10649 &setup.shortcut.snap_up, "shortcut.snap_up"
10653 &setup.shortcut.snap_down, "shortcut.snap_down"
10657 static struct SetupInputInfo setup_input;
10658 static struct TokenInfo player_setup_tokens[] =
10662 &setup_input.use_joystick, ".use_joystick"
10666 &setup_input.joy.device_name, ".joy.device_name"
10670 &setup_input.joy.xleft, ".joy.xleft"
10674 &setup_input.joy.xmiddle, ".joy.xmiddle"
10678 &setup_input.joy.xright, ".joy.xright"
10682 &setup_input.joy.yupper, ".joy.yupper"
10686 &setup_input.joy.ymiddle, ".joy.ymiddle"
10690 &setup_input.joy.ylower, ".joy.ylower"
10694 &setup_input.joy.snap, ".joy.snap_field"
10698 &setup_input.joy.drop, ".joy.place_bomb"
10702 &setup_input.key.left, ".key.move_left"
10706 &setup_input.key.right, ".key.move_right"
10710 &setup_input.key.up, ".key.move_up"
10714 &setup_input.key.down, ".key.move_down"
10718 &setup_input.key.snap, ".key.snap_field"
10722 &setup_input.key.drop, ".key.place_bomb"
10726 static struct TokenInfo system_setup_tokens[] =
10730 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10734 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10738 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10742 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10746 static struct TokenInfo internal_setup_tokens[] =
10750 &setup.internal.program_title, "program_title"
10754 &setup.internal.program_version, "program_version"
10758 &setup.internal.program_author, "program_author"
10762 &setup.internal.program_email, "program_email"
10766 &setup.internal.program_website, "program_website"
10770 &setup.internal.program_copyright, "program_copyright"
10774 &setup.internal.program_company, "program_company"
10778 &setup.internal.program_icon_file, "program_icon_file"
10782 &setup.internal.default_graphics_set, "default_graphics_set"
10786 &setup.internal.default_sounds_set, "default_sounds_set"
10790 &setup.internal.default_music_set, "default_music_set"
10794 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10798 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10802 &setup.internal.fallback_music_file, "fallback_music_file"
10806 &setup.internal.default_level_series, "default_level_series"
10810 &setup.internal.default_window_width, "default_window_width"
10814 &setup.internal.default_window_height, "default_window_height"
10818 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10822 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10826 &setup.internal.create_user_levelset, "create_user_levelset"
10830 &setup.internal.info_screens_from_main, "info_screens_from_main"
10834 &setup.internal.menu_game, "menu_game"
10838 &setup.internal.menu_engines, "menu_engines"
10842 &setup.internal.menu_editor, "menu_editor"
10846 &setup.internal.menu_graphics, "menu_graphics"
10850 &setup.internal.menu_sound, "menu_sound"
10854 &setup.internal.menu_artwork, "menu_artwork"
10858 &setup.internal.menu_input, "menu_input"
10862 &setup.internal.menu_touch, "menu_touch"
10866 &setup.internal.menu_shortcuts, "menu_shortcuts"
10870 &setup.internal.menu_exit, "menu_exit"
10874 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10878 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10882 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10886 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10890 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10894 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10898 &setup.internal.info_title, "info_title"
10902 &setup.internal.info_elements, "info_elements"
10906 &setup.internal.info_music, "info_music"
10910 &setup.internal.info_credits, "info_credits"
10914 &setup.internal.info_program, "info_program"
10918 &setup.internal.info_version, "info_version"
10922 &setup.internal.info_levelset, "info_levelset"
10926 &setup.internal.info_exit, "info_exit"
10930 static struct TokenInfo debug_setup_tokens[] =
10934 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10938 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10942 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10946 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10950 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10954 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10958 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10962 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10966 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10970 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10974 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10978 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10982 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10986 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10990 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10994 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10998 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11002 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11006 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11010 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11014 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11017 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11021 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11025 &setup.debug.xsn_mode, "debug.xsn_mode"
11029 &setup.debug.xsn_percent, "debug.xsn_percent"
11033 static struct TokenInfo options_setup_tokens[] =
11037 &setup.options.verbose, "options.verbose"
11041 &setup.options.debug, "options.debug"
11045 &setup.options.debug_mode, "options.debug_mode"
11049 static void setSetupInfoToDefaults(struct SetupInfo *si)
11053 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11055 si->multiple_users = TRUE;
11058 si->sound_loops = TRUE;
11059 si->sound_music = TRUE;
11060 si->sound_simple = TRUE;
11062 si->global_animations = TRUE;
11063 si->scroll_delay = TRUE;
11064 si->forced_scroll_delay = FALSE;
11065 si->scroll_delay_value = STD_SCROLL_DELAY;
11066 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11067 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11068 si->fade_screens = TRUE;
11069 si->autorecord = TRUE;
11070 si->autorecord_after_replay = TRUE;
11071 si->auto_pause_on_start = FALSE;
11072 si->show_titlescreen = TRUE;
11073 si->quick_doors = FALSE;
11074 si->team_mode = FALSE;
11075 si->handicap = TRUE;
11076 si->skip_levels = TRUE;
11077 si->increment_levels = TRUE;
11078 si->auto_play_next_level = TRUE;
11079 si->count_score_after_game = TRUE;
11080 si->show_scores_after_game = TRUE;
11081 si->time_limit = TRUE;
11082 si->fullscreen = FALSE;
11083 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11084 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11085 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11086 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11087 si->ask_on_escape = TRUE;
11088 si->ask_on_escape_editor = TRUE;
11089 si->ask_on_game_over = TRUE;
11090 si->ask_on_quit_game = TRUE;
11091 si->ask_on_quit_program = TRUE;
11092 si->quick_switch = FALSE;
11093 si->input_on_focus = FALSE;
11094 si->prefer_aga_graphics = TRUE;
11095 si->prefer_lowpass_sounds = FALSE;
11096 si->prefer_extra_panel_items = TRUE;
11097 si->game_speed_extended = FALSE;
11098 si->game_frame_delay = GAME_FRAME_DELAY;
11099 si->bd_skip_uncovering = FALSE;
11100 si->bd_skip_hatching = FALSE;
11101 si->bd_scroll_delay = TRUE;
11102 si->bd_smooth_movements = AUTO;
11103 si->sp_show_border_elements = FALSE;
11104 si->small_game_graphics = FALSE;
11105 si->show_load_save_buttons = FALSE;
11106 si->show_undo_redo_buttons = FALSE;
11107 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11109 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11110 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11111 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11113 si->override_level_graphics = FALSE;
11114 si->override_level_sounds = FALSE;
11115 si->override_level_music = FALSE;
11117 si->volume_simple = 100; // percent
11118 si->volume_loops = 100; // percent
11119 si->volume_music = 100; // percent
11121 si->network_mode = FALSE;
11122 si->network_player_nr = 0; // first player
11123 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11125 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11126 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11127 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11128 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11129 si->touch.draw_outlined = TRUE;
11130 si->touch.draw_pressed = TRUE;
11132 for (i = 0; i < 2; i++)
11134 char *default_grid_button[6][2] =
11140 { "111222", " vv " },
11141 { "111222", " vv " }
11143 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11144 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11145 int min_xsize = MIN(6, grid_xsize);
11146 int min_ysize = MIN(6, grid_ysize);
11147 int startx = grid_xsize - min_xsize;
11148 int starty = grid_ysize - min_ysize;
11151 // virtual buttons grid can only be set to defaults if video is initialized
11152 // (this will be repeated if virtual buttons are not loaded from setup file)
11153 if (video.initialized)
11155 si->touch.grid_xsize[i] = grid_xsize;
11156 si->touch.grid_ysize[i] = grid_ysize;
11160 si->touch.grid_xsize[i] = -1;
11161 si->touch.grid_ysize[i] = -1;
11164 for (x = 0; x < MAX_GRID_XSIZE; x++)
11165 for (y = 0; y < MAX_GRID_YSIZE; y++)
11166 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11168 for (x = 0; x < min_xsize; x++)
11169 for (y = 0; y < min_ysize; y++)
11170 si->touch.grid_button[i][x][starty + y] =
11171 default_grid_button[y][0][x];
11173 for (x = 0; x < min_xsize; x++)
11174 for (y = 0; y < min_ysize; y++)
11175 si->touch.grid_button[i][startx + x][starty + y] =
11176 default_grid_button[y][1][x];
11179 si->touch.grid_initialized = video.initialized;
11181 si->touch.overlay_buttons = FALSE;
11183 si->editor.el_boulderdash = TRUE;
11184 si->editor.el_boulderdash_native = TRUE;
11185 si->editor.el_emerald_mine = TRUE;
11186 si->editor.el_emerald_mine_club = TRUE;
11187 si->editor.el_more = TRUE;
11188 si->editor.el_sokoban = TRUE;
11189 si->editor.el_supaplex = TRUE;
11190 si->editor.el_diamond_caves = TRUE;
11191 si->editor.el_dx_boulderdash = TRUE;
11193 si->editor.el_mirror_magic = TRUE;
11194 si->editor.el_deflektor = TRUE;
11196 si->editor.el_chars = TRUE;
11197 si->editor.el_steel_chars = TRUE;
11199 si->editor.el_classic = TRUE;
11200 si->editor.el_custom = TRUE;
11202 si->editor.el_user_defined = FALSE;
11203 si->editor.el_dynamic = TRUE;
11205 si->editor.el_headlines = TRUE;
11207 si->editor.show_element_token = FALSE;
11209 si->editor.show_read_only_warning = TRUE;
11211 si->editor.use_template_for_new_levels = TRUE;
11213 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11214 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11215 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11216 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11217 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11219 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11220 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11221 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11222 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11223 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11225 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11226 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11227 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11228 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11229 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11230 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11232 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11233 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11234 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11236 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11237 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11238 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11239 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11241 for (i = 0; i < MAX_PLAYERS; i++)
11243 si->input[i].use_joystick = FALSE;
11244 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11245 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11246 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11247 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11248 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11249 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11250 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11251 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11252 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11253 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11254 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11255 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11256 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11257 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11258 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11261 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11262 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11263 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11264 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11266 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11267 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11268 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11269 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11270 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11271 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11272 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11274 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11276 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11277 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11278 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11280 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11281 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11282 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11284 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11285 si->internal.choose_from_top_leveldir = FALSE;
11286 si->internal.show_scaling_in_title = TRUE;
11287 si->internal.create_user_levelset = TRUE;
11288 si->internal.info_screens_from_main = FALSE;
11290 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11291 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11293 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11294 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11295 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11296 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11297 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11298 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11299 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11300 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11301 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11302 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11304 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11305 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11306 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11307 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11308 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11309 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11310 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11311 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11312 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11313 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11315 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11316 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11318 si->debug.show_frames_per_second = FALSE;
11320 si->debug.xsn_mode = AUTO;
11321 si->debug.xsn_percent = 0;
11323 si->options.verbose = FALSE;
11324 si->options.debug = FALSE;
11325 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11327 #if defined(PLATFORM_ANDROID)
11328 si->fullscreen = TRUE;
11329 si->touch.overlay_buttons = TRUE;
11332 setHideSetupEntry(&setup.debug.xsn_mode);
11335 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11337 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11340 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11342 si->player_uuid = NULL; // (will be set later)
11343 si->player_version = 1; // (will be set later)
11345 si->use_api_server = TRUE;
11346 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11347 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11348 si->ask_for_uploading_tapes = TRUE;
11349 si->ask_for_remaining_tapes = FALSE;
11350 si->provide_uploading_tapes = TRUE;
11351 si->ask_for_using_api_server = TRUE;
11352 si->has_remaining_tapes = FALSE;
11355 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11357 si->editor_cascade.el_bd = TRUE;
11358 si->editor_cascade.el_bd_native = TRUE;
11359 si->editor_cascade.el_em = TRUE;
11360 si->editor_cascade.el_emc = TRUE;
11361 si->editor_cascade.el_rnd = TRUE;
11362 si->editor_cascade.el_sb = TRUE;
11363 si->editor_cascade.el_sp = TRUE;
11364 si->editor_cascade.el_dc = TRUE;
11365 si->editor_cascade.el_dx = TRUE;
11367 si->editor_cascade.el_mm = TRUE;
11368 si->editor_cascade.el_df = TRUE;
11370 si->editor_cascade.el_chars = FALSE;
11371 si->editor_cascade.el_steel_chars = FALSE;
11372 si->editor_cascade.el_ce = FALSE;
11373 si->editor_cascade.el_ge = FALSE;
11374 si->editor_cascade.el_es = FALSE;
11375 si->editor_cascade.el_ref = FALSE;
11376 si->editor_cascade.el_user = FALSE;
11377 si->editor_cascade.el_dynamic = FALSE;
11380 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11382 static char *getHideSetupToken(void *setup_value)
11384 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11386 if (setup_value != NULL)
11387 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11389 return hide_setup_token;
11392 void setHideSetupEntry(void *setup_value)
11394 char *hide_setup_token = getHideSetupToken(setup_value);
11396 if (hide_setup_hash == NULL)
11397 hide_setup_hash = newSetupFileHash();
11399 if (setup_value != NULL)
11400 setHashEntry(hide_setup_hash, hide_setup_token, "");
11403 void removeHideSetupEntry(void *setup_value)
11405 char *hide_setup_token = getHideSetupToken(setup_value);
11407 if (setup_value != NULL)
11408 removeHashEntry(hide_setup_hash, hide_setup_token);
11411 boolean hideSetupEntry(void *setup_value)
11413 char *hide_setup_token = getHideSetupToken(setup_value);
11415 return (setup_value != NULL &&
11416 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11419 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11420 struct TokenInfo *token_info,
11421 int token_nr, char *token_text)
11423 char *token_hide_text = getStringCat2(token_text, ".hide");
11424 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11426 // set the value of this setup option in the setup option structure
11427 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11429 // check if this setup option should be hidden in the setup menu
11430 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11431 setHideSetupEntry(token_info[token_nr].value);
11433 free(token_hide_text);
11436 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11437 struct TokenInfo *token_info,
11440 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11441 token_info[token_nr].text);
11444 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11448 if (!setup_file_hash)
11451 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11452 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11454 setup.touch.grid_initialized = TRUE;
11455 for (i = 0; i < 2; i++)
11457 int grid_xsize = setup.touch.grid_xsize[i];
11458 int grid_ysize = setup.touch.grid_ysize[i];
11461 // if virtual buttons are not loaded from setup file, repeat initializing
11462 // virtual buttons grid with default values later when video is initialized
11463 if (grid_xsize == -1 ||
11466 setup.touch.grid_initialized = FALSE;
11471 for (y = 0; y < grid_ysize; y++)
11473 char token_string[MAX_LINE_LEN];
11475 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11477 char *value_string = getHashEntry(setup_file_hash, token_string);
11479 if (value_string == NULL)
11482 for (x = 0; x < grid_xsize; x++)
11484 char c = value_string[x];
11486 setup.touch.grid_button[i][x][y] =
11487 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11492 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11493 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11495 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11496 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11498 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11502 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11504 setup_input = setup.input[pnr];
11505 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11507 char full_token[100];
11509 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11510 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11513 setup.input[pnr] = setup_input;
11516 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11517 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11519 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11520 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11522 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11523 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11525 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11526 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11528 setHideRelatedSetupEntries();
11531 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11535 if (!setup_file_hash)
11538 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11539 setSetupInfo(auto_setup_tokens, i,
11540 getHashEntry(setup_file_hash,
11541 auto_setup_tokens[i].text));
11544 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11548 if (!setup_file_hash)
11551 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11552 setSetupInfo(server_setup_tokens, i,
11553 getHashEntry(setup_file_hash,
11554 server_setup_tokens[i].text));
11557 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11561 if (!setup_file_hash)
11564 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11565 setSetupInfo(editor_cascade_setup_tokens, i,
11566 getHashEntry(setup_file_hash,
11567 editor_cascade_setup_tokens[i].text));
11570 void LoadUserNames(void)
11572 int last_user_nr = user.nr;
11575 if (global.user_names != NULL)
11577 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11578 checked_free(global.user_names[i]);
11580 checked_free(global.user_names);
11583 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11585 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11589 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11591 if (setup_file_hash)
11593 char *player_name = getHashEntry(setup_file_hash, "player_name");
11595 global.user_names[i] = getFixedUserName(player_name);
11597 freeSetupFileHash(setup_file_hash);
11600 if (global.user_names[i] == NULL)
11601 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11604 user.nr = last_user_nr;
11607 void LoadSetupFromFilename(char *filename)
11609 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11611 if (setup_file_hash)
11613 decodeSetupFileHash_Default(setup_file_hash);
11615 freeSetupFileHash(setup_file_hash);
11619 Debug("setup", "using default setup values");
11623 static void LoadSetup_SpecialPostProcessing(void)
11625 char *player_name_new;
11627 // needed to work around problems with fixed length strings
11628 player_name_new = getFixedUserName(setup.player_name);
11629 free(setup.player_name);
11630 setup.player_name = player_name_new;
11632 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11633 if (setup.scroll_delay == FALSE)
11635 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11636 setup.scroll_delay = TRUE; // now always "on"
11639 // make sure that scroll delay value stays inside valid range
11640 setup.scroll_delay_value =
11641 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11644 void LoadSetup_Default(void)
11648 // always start with reliable default values
11649 setSetupInfoToDefaults(&setup);
11651 // try to load setup values from default setup file
11652 filename = getDefaultSetupFilename();
11654 if (fileExists(filename))
11655 LoadSetupFromFilename(filename);
11657 // try to load setup values from platform setup file
11658 filename = getPlatformSetupFilename();
11660 if (fileExists(filename))
11661 LoadSetupFromFilename(filename);
11663 // try to load setup values from user setup file
11664 filename = getSetupFilename();
11666 LoadSetupFromFilename(filename);
11668 LoadSetup_SpecialPostProcessing();
11671 void LoadSetup_AutoSetup(void)
11673 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11674 SetupFileHash *setup_file_hash = NULL;
11676 // always start with reliable default values
11677 setSetupInfoToDefaults_AutoSetup(&setup);
11679 setup_file_hash = loadSetupFileHash(filename);
11681 if (setup_file_hash)
11683 decodeSetupFileHash_AutoSetup(setup_file_hash);
11685 freeSetupFileHash(setup_file_hash);
11691 void LoadSetup_ServerSetup(void)
11693 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11694 SetupFileHash *setup_file_hash = NULL;
11696 // always start with reliable default values
11697 setSetupInfoToDefaults_ServerSetup(&setup);
11699 setup_file_hash = loadSetupFileHash(filename);
11701 if (setup_file_hash)
11703 decodeSetupFileHash_ServerSetup(setup_file_hash);
11705 freeSetupFileHash(setup_file_hash);
11710 if (setup.player_uuid == NULL)
11712 // player UUID does not yet exist in setup file
11713 setup.player_uuid = getStringCopy(getUUID());
11714 setup.player_version = 2;
11716 SaveSetup_ServerSetup();
11720 void LoadSetup_EditorCascade(void)
11722 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11723 SetupFileHash *setup_file_hash = NULL;
11725 // always start with reliable default values
11726 setSetupInfoToDefaults_EditorCascade(&setup);
11728 setup_file_hash = loadSetupFileHash(filename);
11730 if (setup_file_hash)
11732 decodeSetupFileHash_EditorCascade(setup_file_hash);
11734 freeSetupFileHash(setup_file_hash);
11740 void LoadSetup(void)
11742 LoadSetup_Default();
11743 LoadSetup_AutoSetup();
11744 LoadSetup_ServerSetup();
11745 LoadSetup_EditorCascade();
11748 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11749 char *mapping_line)
11751 char mapping_guid[MAX_LINE_LEN];
11752 char *mapping_start, *mapping_end;
11754 // get GUID from game controller mapping line: copy complete line
11755 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11756 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11758 // get GUID from game controller mapping line: cut after GUID part
11759 mapping_start = strchr(mapping_guid, ',');
11760 if (mapping_start != NULL)
11761 *mapping_start = '\0';
11763 // cut newline from game controller mapping line
11764 mapping_end = strchr(mapping_line, '\n');
11765 if (mapping_end != NULL)
11766 *mapping_end = '\0';
11768 // add mapping entry to game controller mappings hash
11769 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11772 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11777 if (!(file = fopen(filename, MODE_READ)))
11779 Warn("cannot read game controller mappings file '%s'", filename);
11784 while (!feof(file))
11786 char line[MAX_LINE_LEN];
11788 if (!fgets(line, MAX_LINE_LEN, file))
11791 addGameControllerMappingToHash(mappings_hash, line);
11797 void SaveSetup_Default(void)
11799 char *filename = getSetupFilename();
11803 InitUserDataDirectory();
11805 if (!(file = fopen(filename, MODE_WRITE)))
11807 Warn("cannot write setup file '%s'", filename);
11812 fprintFileHeader(file, SETUP_FILENAME);
11814 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11816 // just to make things nicer :)
11817 if (global_setup_tokens[i].value == &setup.multiple_users ||
11818 global_setup_tokens[i].value == &setup.sound ||
11819 global_setup_tokens[i].value == &setup.graphics_set ||
11820 global_setup_tokens[i].value == &setup.volume_simple ||
11821 global_setup_tokens[i].value == &setup.network_mode ||
11822 global_setup_tokens[i].value == &setup.touch.control_type ||
11823 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11824 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11825 fprintf(file, "\n");
11827 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11830 for (i = 0; i < 2; i++)
11832 int grid_xsize = setup.touch.grid_xsize[i];
11833 int grid_ysize = setup.touch.grid_ysize[i];
11836 fprintf(file, "\n");
11838 for (y = 0; y < grid_ysize; y++)
11840 char token_string[MAX_LINE_LEN];
11841 char value_string[MAX_LINE_LEN];
11843 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11845 for (x = 0; x < grid_xsize; x++)
11847 char c = setup.touch.grid_button[i][x][y];
11849 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11852 value_string[grid_xsize] = '\0';
11854 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11858 fprintf(file, "\n");
11859 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11860 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11862 fprintf(file, "\n");
11863 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11864 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11866 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11870 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11871 fprintf(file, "\n");
11873 setup_input = setup.input[pnr];
11874 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11875 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11878 fprintf(file, "\n");
11879 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11880 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11882 // (internal setup values not saved to user setup file)
11884 fprintf(file, "\n");
11885 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11886 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11887 setup.debug.xsn_mode != AUTO)
11888 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11890 fprintf(file, "\n");
11891 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11892 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11896 SetFilePermissions(filename, PERMS_PRIVATE);
11899 void SaveSetup_AutoSetup(void)
11901 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11905 InitUserDataDirectory();
11907 if (!(file = fopen(filename, MODE_WRITE)))
11909 Warn("cannot write auto setup file '%s'", filename);
11916 fprintFileHeader(file, AUTOSETUP_FILENAME);
11918 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11919 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11923 SetFilePermissions(filename, PERMS_PRIVATE);
11928 void SaveSetup_ServerSetup(void)
11930 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11934 InitUserDataDirectory();
11936 if (!(file = fopen(filename, MODE_WRITE)))
11938 Warn("cannot write server setup file '%s'", filename);
11945 fprintFileHeader(file, SERVERSETUP_FILENAME);
11947 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11949 // just to make things nicer :)
11950 if (server_setup_tokens[i].value == &setup.use_api_server)
11951 fprintf(file, "\n");
11953 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11958 SetFilePermissions(filename, PERMS_PRIVATE);
11963 void SaveSetup_EditorCascade(void)
11965 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11969 InitUserDataDirectory();
11971 if (!(file = fopen(filename, MODE_WRITE)))
11973 Warn("cannot write editor cascade state file '%s'", filename);
11980 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11982 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11983 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11987 SetFilePermissions(filename, PERMS_PRIVATE);
11992 void SaveSetup(void)
11994 SaveSetup_Default();
11995 SaveSetup_AutoSetup();
11996 SaveSetup_ServerSetup();
11997 SaveSetup_EditorCascade();
12000 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12005 if (!(file = fopen(filename, MODE_WRITE)))
12007 Warn("cannot write game controller mappings file '%s'", filename);
12012 BEGIN_HASH_ITERATION(mappings_hash, itr)
12014 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12016 END_HASH_ITERATION(mappings_hash, itr)
12021 void SaveSetup_AddGameControllerMapping(char *mapping)
12023 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12024 SetupFileHash *mappings_hash = newSetupFileHash();
12026 InitUserDataDirectory();
12028 // load existing personal game controller mappings
12029 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12031 // add new mapping to personal game controller mappings
12032 addGameControllerMappingToHash(mappings_hash, mapping);
12034 // save updated personal game controller mappings
12035 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12037 freeSetupFileHash(mappings_hash);
12041 void LoadCustomElementDescriptions(void)
12043 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12044 SetupFileHash *setup_file_hash;
12047 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12049 if (element_info[i].custom_description != NULL)
12051 free(element_info[i].custom_description);
12052 element_info[i].custom_description = NULL;
12056 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12059 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12061 char *token = getStringCat2(element_info[i].token_name, ".name");
12062 char *value = getHashEntry(setup_file_hash, token);
12065 element_info[i].custom_description = getStringCopy(value);
12070 freeSetupFileHash(setup_file_hash);
12073 static int getElementFromToken(char *token)
12075 char *value = getHashEntry(element_token_hash, token);
12078 return atoi(value);
12080 Warn("unknown element token '%s'", token);
12082 return EL_UNDEFINED;
12085 void FreeGlobalAnimEventInfo(void)
12087 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12089 if (gaei->event_list == NULL)
12094 for (i = 0; i < gaei->num_event_lists; i++)
12096 checked_free(gaei->event_list[i]->event_value);
12097 checked_free(gaei->event_list[i]);
12100 checked_free(gaei->event_list);
12102 gaei->event_list = NULL;
12103 gaei->num_event_lists = 0;
12106 static int AddGlobalAnimEventList(void)
12108 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12109 int list_pos = gaei->num_event_lists++;
12111 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12112 sizeof(struct GlobalAnimEventListInfo *));
12114 gaei->event_list[list_pos] =
12115 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12117 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12119 gaeli->event_value = NULL;
12120 gaeli->num_event_values = 0;
12125 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12127 // do not add empty global animation events
12128 if (event_value == ANIM_EVENT_NONE)
12131 // if list position is undefined, create new list
12132 if (list_pos == ANIM_EVENT_UNDEFINED)
12133 list_pos = AddGlobalAnimEventList();
12135 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12136 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12137 int value_pos = gaeli->num_event_values++;
12139 gaeli->event_value = checked_realloc(gaeli->event_value,
12140 gaeli->num_event_values * sizeof(int *));
12142 gaeli->event_value[value_pos] = event_value;
12147 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12149 if (list_pos == ANIM_EVENT_UNDEFINED)
12150 return ANIM_EVENT_NONE;
12152 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12153 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12155 return gaeli->event_value[value_pos];
12158 int GetGlobalAnimEventValueCount(int list_pos)
12160 if (list_pos == ANIM_EVENT_UNDEFINED)
12163 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12164 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12166 return gaeli->num_event_values;
12169 // This function checks if a string <s> of the format "string1, string2, ..."
12170 // exactly contains a string <s_contained>.
12172 static boolean string_has_parameter(char *s, char *s_contained)
12176 if (s == NULL || s_contained == NULL)
12179 if (strlen(s_contained) > strlen(s))
12182 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12184 char next_char = s[strlen(s_contained)];
12186 // check if next character is delimiter or whitespace
12187 if (next_char == ',' || next_char == '\0' ||
12188 next_char == ' ' || next_char == '\t')
12192 // check if string contains another parameter string after a comma
12193 substring = strchr(s, ',');
12194 if (substring == NULL) // string does not contain a comma
12197 // advance string pointer to next character after the comma
12200 // skip potential whitespaces after the comma
12201 while (*substring == ' ' || *substring == '\t')
12204 return string_has_parameter(substring, s_contained);
12207 static int get_anim_parameter_value_ce(char *s)
12210 char *pattern_1 = "ce_change:custom_";
12211 char *pattern_2 = ".page_";
12212 int pattern_1_len = strlen(pattern_1);
12213 char *matching_char = strstr(s_ptr, pattern_1);
12214 int result = ANIM_EVENT_NONE;
12216 if (matching_char == NULL)
12217 return ANIM_EVENT_NONE;
12219 result = ANIM_EVENT_CE_CHANGE;
12221 s_ptr = matching_char + pattern_1_len;
12223 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12224 if (*s_ptr >= '0' && *s_ptr <= '9')
12226 int gic_ce_nr = (*s_ptr++ - '0');
12228 if (*s_ptr >= '0' && *s_ptr <= '9')
12230 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12232 if (*s_ptr >= '0' && *s_ptr <= '9')
12233 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12236 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12237 return ANIM_EVENT_NONE;
12239 // custom element stored as 0 to 255
12242 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12246 // invalid custom element number specified
12248 return ANIM_EVENT_NONE;
12251 // check for change page number ("page_X" or "page_XX") (optional)
12252 if (strPrefix(s_ptr, pattern_2))
12254 s_ptr += strlen(pattern_2);
12256 if (*s_ptr >= '0' && *s_ptr <= '9')
12258 int gic_page_nr = (*s_ptr++ - '0');
12260 if (*s_ptr >= '0' && *s_ptr <= '9')
12261 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12263 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12264 return ANIM_EVENT_NONE;
12266 // change page stored as 1 to 32 (0 means "all change pages")
12268 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12272 // invalid animation part number specified
12274 return ANIM_EVENT_NONE;
12278 // discard result if next character is neither delimiter nor whitespace
12279 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12280 *s_ptr == ' ' || *s_ptr == '\t'))
12281 return ANIM_EVENT_NONE;
12286 static int get_anim_parameter_value(char *s)
12288 int event_value[] =
12296 char *pattern_1[] =
12304 char *pattern_2 = ".part_";
12305 char *matching_char = NULL;
12307 int pattern_1_len = 0;
12308 int result = ANIM_EVENT_NONE;
12311 result = get_anim_parameter_value_ce(s);
12313 if (result != ANIM_EVENT_NONE)
12316 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12318 matching_char = strstr(s_ptr, pattern_1[i]);
12319 pattern_1_len = strlen(pattern_1[i]);
12320 result = event_value[i];
12322 if (matching_char != NULL)
12326 if (matching_char == NULL)
12327 return ANIM_EVENT_NONE;
12329 s_ptr = matching_char + pattern_1_len;
12331 // check for main animation number ("anim_X" or "anim_XX")
12332 if (*s_ptr >= '0' && *s_ptr <= '9')
12334 int gic_anim_nr = (*s_ptr++ - '0');
12336 if (*s_ptr >= '0' && *s_ptr <= '9')
12337 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12339 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12340 return ANIM_EVENT_NONE;
12342 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12346 // invalid main animation number specified
12348 return ANIM_EVENT_NONE;
12351 // check for animation part number ("part_X" or "part_XX") (optional)
12352 if (strPrefix(s_ptr, pattern_2))
12354 s_ptr += strlen(pattern_2);
12356 if (*s_ptr >= '0' && *s_ptr <= '9')
12358 int gic_part_nr = (*s_ptr++ - '0');
12360 if (*s_ptr >= '0' && *s_ptr <= '9')
12361 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12363 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12364 return ANIM_EVENT_NONE;
12366 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12370 // invalid animation part number specified
12372 return ANIM_EVENT_NONE;
12376 // discard result if next character is neither delimiter nor whitespace
12377 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12378 *s_ptr == ' ' || *s_ptr == '\t'))
12379 return ANIM_EVENT_NONE;
12384 static int get_anim_parameter_values(char *s)
12386 int list_pos = ANIM_EVENT_UNDEFINED;
12387 int event_value = ANIM_EVENT_DEFAULT;
12389 if (string_has_parameter(s, "any"))
12390 event_value |= ANIM_EVENT_ANY;
12392 if (string_has_parameter(s, "click:self") ||
12393 string_has_parameter(s, "click") ||
12394 string_has_parameter(s, "self"))
12395 event_value |= ANIM_EVENT_SELF;
12397 if (string_has_parameter(s, "unclick:any"))
12398 event_value |= ANIM_EVENT_UNCLICK_ANY;
12400 // if animation event found, add it to global animation event list
12401 if (event_value != ANIM_EVENT_NONE)
12402 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12406 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12407 event_value = get_anim_parameter_value(s);
12409 // if animation event found, add it to global animation event list
12410 if (event_value != ANIM_EVENT_NONE)
12411 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12413 // continue with next part of the string, starting with next comma
12414 s = strchr(s + 1, ',');
12420 static int get_anim_action_parameter_value(char *token)
12422 // check most common default case first to massively speed things up
12423 if (strEqual(token, ARG_UNDEFINED))
12424 return ANIM_EVENT_ACTION_NONE;
12426 int result = getImageIDFromToken(token);
12430 char *gfx_token = getStringCat2("gfx.", token);
12432 result = getImageIDFromToken(gfx_token);
12434 checked_free(gfx_token);
12439 Key key = getKeyFromX11KeyName(token);
12441 if (key != KSYM_UNDEFINED)
12442 result = -(int)key;
12449 result = get_hash_from_string(token); // unsigned int => int
12450 result = ABS(result); // may be negative now
12451 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12453 setHashEntry(anim_url_hash, int2str(result, 0), token);
12458 result = ANIM_EVENT_ACTION_NONE;
12463 int get_parameter_value(char *value_raw, char *suffix, int type)
12465 char *value = getStringToLower(value_raw);
12466 int result = 0; // probably a save default value
12468 if (strEqual(suffix, ".direction"))
12470 result = (strEqual(value, "left") ? MV_LEFT :
12471 strEqual(value, "right") ? MV_RIGHT :
12472 strEqual(value, "up") ? MV_UP :
12473 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12475 else if (strEqual(suffix, ".position"))
12477 result = (strEqual(value, "left") ? POS_LEFT :
12478 strEqual(value, "right") ? POS_RIGHT :
12479 strEqual(value, "top") ? POS_TOP :
12480 strEqual(value, "upper") ? POS_UPPER :
12481 strEqual(value, "middle") ? POS_MIDDLE :
12482 strEqual(value, "lower") ? POS_LOWER :
12483 strEqual(value, "bottom") ? POS_BOTTOM :
12484 strEqual(value, "any") ? POS_ANY :
12485 strEqual(value, "ce") ? POS_CE :
12486 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12487 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12489 else if (strEqual(suffix, ".align"))
12491 result = (strEqual(value, "left") ? ALIGN_LEFT :
12492 strEqual(value, "right") ? ALIGN_RIGHT :
12493 strEqual(value, "center") ? ALIGN_CENTER :
12494 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12496 else if (strEqual(suffix, ".valign"))
12498 result = (strEqual(value, "top") ? VALIGN_TOP :
12499 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12500 strEqual(value, "middle") ? VALIGN_MIDDLE :
12501 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12503 else if (strEqual(suffix, ".anim_mode"))
12505 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12506 string_has_parameter(value, "loop") ? ANIM_LOOP :
12507 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12508 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12509 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12510 string_has_parameter(value, "random") ? ANIM_RANDOM :
12511 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12512 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12513 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12514 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12515 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12516 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12517 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12518 string_has_parameter(value, "all") ? ANIM_ALL :
12519 string_has_parameter(value, "tiled") ? ANIM_TILED :
12520 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12523 if (string_has_parameter(value, "once"))
12524 result |= ANIM_ONCE;
12526 if (string_has_parameter(value, "reverse"))
12527 result |= ANIM_REVERSE;
12529 if (string_has_parameter(value, "opaque_player"))
12530 result |= ANIM_OPAQUE_PLAYER;
12532 if (string_has_parameter(value, "static_panel"))
12533 result |= ANIM_STATIC_PANEL;
12535 else if (strEqual(suffix, ".init_event") ||
12536 strEqual(suffix, ".anim_event"))
12538 result = get_anim_parameter_values(value);
12540 else if (strEqual(suffix, ".init_delay_action") ||
12541 strEqual(suffix, ".anim_delay_action") ||
12542 strEqual(suffix, ".post_delay_action") ||
12543 strEqual(suffix, ".init_event_action") ||
12544 strEqual(suffix, ".anim_event_action"))
12546 result = get_anim_action_parameter_value(value_raw);
12548 else if (strEqual(suffix, ".class"))
12550 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12551 get_hash_from_string(value));
12553 else if (strEqual(suffix, ".style"))
12555 result = STYLE_DEFAULT;
12557 if (string_has_parameter(value, "accurate_borders"))
12558 result |= STYLE_ACCURATE_BORDERS;
12560 if (string_has_parameter(value, "inner_corners"))
12561 result |= STYLE_INNER_CORNERS;
12563 if (string_has_parameter(value, "reverse"))
12564 result |= STYLE_REVERSE;
12566 if (string_has_parameter(value, "leftmost_position"))
12567 result |= STYLE_LEFTMOST_POSITION;
12569 if (string_has_parameter(value, "block_clicks"))
12570 result |= STYLE_BLOCK;
12572 if (string_has_parameter(value, "passthrough_clicks"))
12573 result |= STYLE_PASSTHROUGH;
12575 if (string_has_parameter(value, "multiple_actions"))
12576 result |= STYLE_MULTIPLE_ACTIONS;
12578 if (string_has_parameter(value, "consume_ce_event"))
12579 result |= STYLE_CONSUME_CE_EVENT;
12581 else if (strEqual(suffix, ".fade_mode"))
12583 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12584 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12585 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12586 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12587 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12588 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12589 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12590 FADE_MODE_DEFAULT);
12592 else if (strEqual(suffix, ".auto_delay_unit"))
12594 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12595 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12596 AUTO_DELAY_UNIT_DEFAULT);
12598 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12600 result = gfx.get_font_from_token_function(value);
12602 else // generic parameter of type integer or boolean
12604 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12605 type == TYPE_INTEGER ? get_integer_from_string(value) :
12606 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12607 ARG_UNDEFINED_VALUE);
12615 static int get_token_parameter_value(char *token, char *value_raw)
12619 if (token == NULL || value_raw == NULL)
12620 return ARG_UNDEFINED_VALUE;
12622 suffix = strrchr(token, '.');
12623 if (suffix == NULL)
12626 if (strEqual(suffix, ".element"))
12627 return getElementFromToken(value_raw);
12629 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12630 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12633 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12634 boolean ignore_defaults)
12638 for (i = 0; image_config_vars[i].token != NULL; i++)
12640 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12642 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12643 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12647 *image_config_vars[i].value =
12648 get_token_parameter_value(image_config_vars[i].token, value);
12652 void InitMenuDesignSettings_Static(void)
12654 // always start with reliable default values from static default config
12655 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12658 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12662 // the following initializes hierarchical values from static configuration
12664 // special case: initialize "ARG_DEFAULT" values in static default config
12665 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12666 titlescreen_initial_first_default.fade_mode =
12667 title_initial_first_default.fade_mode;
12668 titlescreen_initial_first_default.fade_delay =
12669 title_initial_first_default.fade_delay;
12670 titlescreen_initial_first_default.post_delay =
12671 title_initial_first_default.post_delay;
12672 titlescreen_initial_first_default.auto_delay =
12673 title_initial_first_default.auto_delay;
12674 titlescreen_initial_first_default.auto_delay_unit =
12675 title_initial_first_default.auto_delay_unit;
12676 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12677 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12678 titlescreen_first_default.post_delay = title_first_default.post_delay;
12679 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12680 titlescreen_first_default.auto_delay_unit =
12681 title_first_default.auto_delay_unit;
12682 titlemessage_initial_first_default.fade_mode =
12683 title_initial_first_default.fade_mode;
12684 titlemessage_initial_first_default.fade_delay =
12685 title_initial_first_default.fade_delay;
12686 titlemessage_initial_first_default.post_delay =
12687 title_initial_first_default.post_delay;
12688 titlemessage_initial_first_default.auto_delay =
12689 title_initial_first_default.auto_delay;
12690 titlemessage_initial_first_default.auto_delay_unit =
12691 title_initial_first_default.auto_delay_unit;
12692 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12693 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12694 titlemessage_first_default.post_delay = title_first_default.post_delay;
12695 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12696 titlemessage_first_default.auto_delay_unit =
12697 title_first_default.auto_delay_unit;
12699 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12700 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12701 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12702 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12703 titlescreen_initial_default.auto_delay_unit =
12704 title_initial_default.auto_delay_unit;
12705 titlescreen_default.fade_mode = title_default.fade_mode;
12706 titlescreen_default.fade_delay = title_default.fade_delay;
12707 titlescreen_default.post_delay = title_default.post_delay;
12708 titlescreen_default.auto_delay = title_default.auto_delay;
12709 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12710 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12711 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12712 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12713 titlemessage_initial_default.auto_delay_unit =
12714 title_initial_default.auto_delay_unit;
12715 titlemessage_default.fade_mode = title_default.fade_mode;
12716 titlemessage_default.fade_delay = title_default.fade_delay;
12717 titlemessage_default.post_delay = title_default.post_delay;
12718 titlemessage_default.auto_delay = title_default.auto_delay;
12719 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12721 // special case: initialize "ARG_DEFAULT" values in static default config
12722 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12723 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12725 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12726 titlescreen_first[i] = titlescreen_first_default;
12727 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12728 titlemessage_first[i] = titlemessage_first_default;
12730 titlescreen_initial[i] = titlescreen_initial_default;
12731 titlescreen[i] = titlescreen_default;
12732 titlemessage_initial[i] = titlemessage_initial_default;
12733 titlemessage[i] = titlemessage_default;
12736 // special case: initialize "ARG_DEFAULT" values in static default config
12737 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12738 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12740 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12743 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12744 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12745 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12748 // special case: initialize "ARG_DEFAULT" values in static default config
12749 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12750 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12752 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12753 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12754 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12756 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12759 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12763 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12767 struct XY *dst, *src;
12769 game_buttons_xy[] =
12771 { &game.button.save, &game.button.stop },
12772 { &game.button.pause2, &game.button.pause },
12773 { &game.button.load, &game.button.play },
12774 { &game.button.undo, &game.button.stop },
12775 { &game.button.redo, &game.button.play },
12781 // special case: initialize later added SETUP list size from LEVELS value
12782 if (menu.list_size[GAME_MODE_SETUP] == -1)
12783 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12785 // set default position for snapshot buttons to stop/pause/play buttons
12786 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12787 if ((*game_buttons_xy[i].dst).x == -1 &&
12788 (*game_buttons_xy[i].dst).y == -1)
12789 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12791 // --------------------------------------------------------------------------
12792 // dynamic viewports (including playfield margins, borders and alignments)
12793 // --------------------------------------------------------------------------
12795 // dynamic viewports currently only supported for landscape mode
12796 int display_width = MAX(video.display_width, video.display_height);
12797 int display_height = MIN(video.display_width, video.display_height);
12799 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12801 struct RectWithBorder *vp_window = &viewport.window[i];
12802 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12803 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12804 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12805 boolean dynamic_window_width = (vp_window->min_width != -1);
12806 boolean dynamic_window_height = (vp_window->min_height != -1);
12807 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12808 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12810 // adjust window size if min/max width/height is specified
12812 if (vp_window->min_width != -1)
12814 int window_width = display_width;
12816 // when using static window height, use aspect ratio of display
12817 if (vp_window->min_height == -1)
12818 window_width = vp_window->height * display_width / display_height;
12820 vp_window->width = MAX(vp_window->min_width, window_width);
12823 if (vp_window->min_height != -1)
12825 int window_height = display_height;
12827 // when using static window width, use aspect ratio of display
12828 if (vp_window->min_width == -1)
12829 window_height = vp_window->width * display_height / display_width;
12831 vp_window->height = MAX(vp_window->min_height, window_height);
12834 if (vp_window->max_width != -1)
12835 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12837 if (vp_window->max_height != -1)
12838 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12840 int playfield_width = vp_window->width;
12841 int playfield_height = vp_window->height;
12843 // adjust playfield size and position according to specified margins
12845 playfield_width -= vp_playfield->margin_left;
12846 playfield_width -= vp_playfield->margin_right;
12848 playfield_height -= vp_playfield->margin_top;
12849 playfield_height -= vp_playfield->margin_bottom;
12851 // adjust playfield size if min/max width/height is specified
12853 if (vp_playfield->min_width != -1)
12854 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12856 if (vp_playfield->min_height != -1)
12857 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12859 if (vp_playfield->max_width != -1)
12860 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12862 if (vp_playfield->max_height != -1)
12863 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12865 // adjust playfield position according to specified alignment
12867 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12868 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12869 else if (vp_playfield->align == ALIGN_CENTER)
12870 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12871 else if (vp_playfield->align == ALIGN_RIGHT)
12872 vp_playfield->x += playfield_width - vp_playfield->width;
12874 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12875 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12876 else if (vp_playfield->valign == VALIGN_MIDDLE)
12877 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12878 else if (vp_playfield->valign == VALIGN_BOTTOM)
12879 vp_playfield->y += playfield_height - vp_playfield->height;
12881 vp_playfield->x += vp_playfield->margin_left;
12882 vp_playfield->y += vp_playfield->margin_top;
12884 // adjust individual playfield borders if only default border is specified
12886 if (vp_playfield->border_left == -1)
12887 vp_playfield->border_left = vp_playfield->border_size;
12888 if (vp_playfield->border_right == -1)
12889 vp_playfield->border_right = vp_playfield->border_size;
12890 if (vp_playfield->border_top == -1)
12891 vp_playfield->border_top = vp_playfield->border_size;
12892 if (vp_playfield->border_bottom == -1)
12893 vp_playfield->border_bottom = vp_playfield->border_size;
12895 // set dynamic playfield borders if borders are specified as undefined
12896 // (but only if window size was dynamic and playfield size was static)
12898 if (dynamic_window_width && !dynamic_playfield_width)
12900 if (vp_playfield->border_left == -1)
12902 vp_playfield->border_left = (vp_playfield->x -
12903 vp_playfield->margin_left);
12904 vp_playfield->x -= vp_playfield->border_left;
12905 vp_playfield->width += vp_playfield->border_left;
12908 if (vp_playfield->border_right == -1)
12910 vp_playfield->border_right = (vp_window->width -
12912 vp_playfield->width -
12913 vp_playfield->margin_right);
12914 vp_playfield->width += vp_playfield->border_right;
12918 if (dynamic_window_height && !dynamic_playfield_height)
12920 if (vp_playfield->border_top == -1)
12922 vp_playfield->border_top = (vp_playfield->y -
12923 vp_playfield->margin_top);
12924 vp_playfield->y -= vp_playfield->border_top;
12925 vp_playfield->height += vp_playfield->border_top;
12928 if (vp_playfield->border_bottom == -1)
12930 vp_playfield->border_bottom = (vp_window->height -
12932 vp_playfield->height -
12933 vp_playfield->margin_bottom);
12934 vp_playfield->height += vp_playfield->border_bottom;
12938 // adjust playfield size to be a multiple of a defined alignment tile size
12940 int align_size = vp_playfield->align_size;
12941 int playfield_xtiles = vp_playfield->width / align_size;
12942 int playfield_ytiles = vp_playfield->height / align_size;
12943 int playfield_width_corrected = playfield_xtiles * align_size;
12944 int playfield_height_corrected = playfield_ytiles * align_size;
12945 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12946 i == GFX_SPECIAL_ARG_EDITOR);
12948 if (is_playfield_mode &&
12949 dynamic_playfield_width &&
12950 vp_playfield->width != playfield_width_corrected)
12952 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12954 vp_playfield->width = playfield_width_corrected;
12956 if (vp_playfield->align == ALIGN_LEFT)
12958 vp_playfield->border_left += playfield_xdiff;
12960 else if (vp_playfield->align == ALIGN_RIGHT)
12962 vp_playfield->border_right += playfield_xdiff;
12964 else if (vp_playfield->align == ALIGN_CENTER)
12966 int border_left_diff = playfield_xdiff / 2;
12967 int border_right_diff = playfield_xdiff - border_left_diff;
12969 vp_playfield->border_left += border_left_diff;
12970 vp_playfield->border_right += border_right_diff;
12974 if (is_playfield_mode &&
12975 dynamic_playfield_height &&
12976 vp_playfield->height != playfield_height_corrected)
12978 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12980 vp_playfield->height = playfield_height_corrected;
12982 if (vp_playfield->valign == VALIGN_TOP)
12984 vp_playfield->border_top += playfield_ydiff;
12986 else if (vp_playfield->align == VALIGN_BOTTOM)
12988 vp_playfield->border_right += playfield_ydiff;
12990 else if (vp_playfield->align == VALIGN_MIDDLE)
12992 int border_top_diff = playfield_ydiff / 2;
12993 int border_bottom_diff = playfield_ydiff - border_top_diff;
12995 vp_playfield->border_top += border_top_diff;
12996 vp_playfield->border_bottom += border_bottom_diff;
13000 // adjust door positions according to specified alignment
13002 for (j = 0; j < 2; j++)
13004 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13006 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13007 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13008 else if (vp_door->align == ALIGN_CENTER)
13009 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13010 else if (vp_door->align == ALIGN_RIGHT)
13011 vp_door->x += vp_window->width - vp_door->width;
13013 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13014 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13015 else if (vp_door->valign == VALIGN_MIDDLE)
13016 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13017 else if (vp_door->valign == VALIGN_BOTTOM)
13018 vp_door->y += vp_window->height - vp_door->height;
13023 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13027 struct XYTileSize *dst, *src;
13030 editor_buttons_xy[] =
13033 &editor.button.element_left, &editor.palette.element_left,
13034 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13037 &editor.button.element_middle, &editor.palette.element_middle,
13038 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13041 &editor.button.element_right, &editor.palette.element_right,
13042 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13049 // set default position for element buttons to element graphics
13050 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13052 if ((*editor_buttons_xy[i].dst).x == -1 &&
13053 (*editor_buttons_xy[i].dst).y == -1)
13055 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13057 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13059 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13063 // adjust editor palette rows and columns if specified to be dynamic
13065 if (editor.palette.cols == -1)
13067 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13068 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13069 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13071 editor.palette.cols = (vp_width - sc_width) / bt_width;
13073 if (editor.palette.x == -1)
13075 int palette_width = editor.palette.cols * bt_width + sc_width;
13077 editor.palette.x = (vp_width - palette_width) / 2;
13081 if (editor.palette.rows == -1)
13083 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13084 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13085 int tx_height = getFontHeight(FONT_TEXT_2);
13087 editor.palette.rows = (vp_height - tx_height) / bt_height;
13089 if (editor.palette.y == -1)
13091 int palette_height = editor.palette.rows * bt_height + tx_height;
13093 editor.palette.y = (vp_height - palette_height) / 2;
13098 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13099 boolean initialize)
13101 // special case: check if network and preview player positions are redefined,
13102 // to compare this later against the main menu level preview being redefined
13103 struct TokenIntPtrInfo menu_config_players[] =
13105 { "main.network_players.x", &menu.main.network_players.redefined },
13106 { "main.network_players.y", &menu.main.network_players.redefined },
13107 { "main.preview_players.x", &menu.main.preview_players.redefined },
13108 { "main.preview_players.y", &menu.main.preview_players.redefined },
13109 { "preview.x", &preview.redefined },
13110 { "preview.y", &preview.redefined }
13116 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13117 *menu_config_players[i].value = FALSE;
13121 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13122 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13123 *menu_config_players[i].value = TRUE;
13127 static void InitMenuDesignSettings_PreviewPlayers(void)
13129 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13132 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13134 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13137 static void LoadMenuDesignSettingsFromFilename(char *filename)
13139 static struct TitleFadingInfo tfi;
13140 static struct TitleMessageInfo tmi;
13141 static struct TokenInfo title_tokens[] =
13143 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13144 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13145 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13146 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13147 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13151 static struct TokenInfo titlemessage_tokens[] =
13153 { TYPE_INTEGER, &tmi.x, ".x" },
13154 { TYPE_INTEGER, &tmi.y, ".y" },
13155 { TYPE_INTEGER, &tmi.width, ".width" },
13156 { TYPE_INTEGER, &tmi.height, ".height" },
13157 { TYPE_INTEGER, &tmi.chars, ".chars" },
13158 { TYPE_INTEGER, &tmi.lines, ".lines" },
13159 { TYPE_INTEGER, &tmi.align, ".align" },
13160 { TYPE_INTEGER, &tmi.valign, ".valign" },
13161 { TYPE_INTEGER, &tmi.font, ".font" },
13162 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13163 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13164 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13165 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13166 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13167 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13168 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13169 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13170 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13176 struct TitleFadingInfo *info;
13181 // initialize first titles from "enter screen" definitions, if defined
13182 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13183 { &title_first_default, "menu.enter_screen.TITLE" },
13185 // initialize title screens from "next screen" definitions, if defined
13186 { &title_initial_default, "menu.next_screen.TITLE" },
13187 { &title_default, "menu.next_screen.TITLE" },
13193 struct TitleMessageInfo *array;
13196 titlemessage_arrays[] =
13198 // initialize first titles from "enter screen" definitions, if defined
13199 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13200 { titlescreen_first, "menu.enter_screen.TITLE" },
13201 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13202 { titlemessage_first, "menu.enter_screen.TITLE" },
13204 // initialize titles from "next screen" definitions, if defined
13205 { titlescreen_initial, "menu.next_screen.TITLE" },
13206 { titlescreen, "menu.next_screen.TITLE" },
13207 { titlemessage_initial, "menu.next_screen.TITLE" },
13208 { titlemessage, "menu.next_screen.TITLE" },
13210 // overwrite titles with title definitions, if defined
13211 { titlescreen_initial_first, "[title_initial]" },
13212 { titlescreen_first, "[title]" },
13213 { titlemessage_initial_first, "[title_initial]" },
13214 { titlemessage_first, "[title]" },
13216 { titlescreen_initial, "[title_initial]" },
13217 { titlescreen, "[title]" },
13218 { titlemessage_initial, "[title_initial]" },
13219 { titlemessage, "[title]" },
13221 // overwrite titles with title screen/message definitions, if defined
13222 { titlescreen_initial_first, "[titlescreen_initial]" },
13223 { titlescreen_first, "[titlescreen]" },
13224 { titlemessage_initial_first, "[titlemessage_initial]" },
13225 { titlemessage_first, "[titlemessage]" },
13227 { titlescreen_initial, "[titlescreen_initial]" },
13228 { titlescreen, "[titlescreen]" },
13229 { titlemessage_initial, "[titlemessage_initial]" },
13230 { titlemessage, "[titlemessage]" },
13234 SetupFileHash *setup_file_hash;
13237 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13240 // the following initializes hierarchical values from dynamic configuration
13242 // special case: initialize with default values that may be overwritten
13243 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13244 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13246 struct TokenIntPtrInfo menu_config[] =
13248 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13249 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13250 { "menu.list_size", &menu.list_size[i] }
13253 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13255 char *token = menu_config[j].token;
13256 char *value = getHashEntry(setup_file_hash, token);
13259 *menu_config[j].value = get_integer_from_string(value);
13263 // special case: initialize with default values that may be overwritten
13264 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13265 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13267 struct TokenIntPtrInfo menu_config[] =
13269 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13270 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13271 { "menu.list_size.INFO", &menu.list_size_info[i] },
13272 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13273 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13276 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13278 char *token = menu_config[j].token;
13279 char *value = getHashEntry(setup_file_hash, token);
13282 *menu_config[j].value = get_integer_from_string(value);
13286 // special case: initialize with default values that may be overwritten
13287 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13288 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13290 struct TokenIntPtrInfo menu_config[] =
13292 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13293 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13296 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13298 char *token = menu_config[j].token;
13299 char *value = getHashEntry(setup_file_hash, token);
13302 *menu_config[j].value = get_integer_from_string(value);
13306 // special case: initialize with default values that may be overwritten
13307 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13308 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13310 struct TokenIntPtrInfo menu_config[] =
13312 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13313 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13314 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13315 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13316 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13317 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13318 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13319 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13320 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13321 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13324 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13326 char *token = menu_config[j].token;
13327 char *value = getHashEntry(setup_file_hash, token);
13330 *menu_config[j].value = get_integer_from_string(value);
13334 // special case: initialize with default values that may be overwritten
13335 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13336 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13338 struct TokenIntPtrInfo menu_config[] =
13340 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13341 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13342 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13343 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13344 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13345 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13346 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13347 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13348 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13351 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13353 char *token = menu_config[j].token;
13354 char *value = getHashEntry(setup_file_hash, token);
13357 *menu_config[j].value = get_token_parameter_value(token, value);
13361 // special case: initialize with default values that may be overwritten
13362 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13363 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13367 char *token_prefix;
13368 struct RectWithBorder *struct_ptr;
13372 { "viewport.window", &viewport.window[i] },
13373 { "viewport.playfield", &viewport.playfield[i] },
13374 { "viewport.door_1", &viewport.door_1[i] },
13375 { "viewport.door_2", &viewport.door_2[i] }
13378 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13380 struct TokenIntPtrInfo vp_config[] =
13382 { ".x", &vp_struct[j].struct_ptr->x },
13383 { ".y", &vp_struct[j].struct_ptr->y },
13384 { ".width", &vp_struct[j].struct_ptr->width },
13385 { ".height", &vp_struct[j].struct_ptr->height },
13386 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13387 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13388 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13389 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13390 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13391 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13392 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13393 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13394 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13395 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13396 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13397 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13398 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13399 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13400 { ".align", &vp_struct[j].struct_ptr->align },
13401 { ".valign", &vp_struct[j].struct_ptr->valign }
13404 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13406 char *token = getStringCat2(vp_struct[j].token_prefix,
13407 vp_config[k].token);
13408 char *value = getHashEntry(setup_file_hash, token);
13411 *vp_config[k].value = get_token_parameter_value(token, value);
13418 // special case: initialize with default values that may be overwritten
13419 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13420 for (i = 0; title_info[i].info != NULL; i++)
13422 struct TitleFadingInfo *info = title_info[i].info;
13423 char *base_token = title_info[i].text;
13425 for (j = 0; title_tokens[j].type != -1; j++)
13427 char *token = getStringCat2(base_token, title_tokens[j].text);
13428 char *value = getHashEntry(setup_file_hash, token);
13432 int parameter_value = get_token_parameter_value(token, value);
13436 *(int *)title_tokens[j].value = (int)parameter_value;
13445 // special case: initialize with default values that may be overwritten
13446 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13447 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13449 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13450 char *base_token = titlemessage_arrays[i].text;
13452 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13454 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13455 char *value = getHashEntry(setup_file_hash, token);
13459 int parameter_value = get_token_parameter_value(token, value);
13461 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13465 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13466 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13468 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13478 // read (and overwrite with) values that may be specified in config file
13479 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13481 // special case: check if network and preview player positions are redefined
13482 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13484 freeSetupFileHash(setup_file_hash);
13487 void LoadMenuDesignSettings(void)
13489 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13491 InitMenuDesignSettings_Static();
13492 InitMenuDesignSettings_SpecialPreProcessing();
13493 InitMenuDesignSettings_PreviewPlayers();
13495 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13497 // first look for special settings configured in level series config
13498 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13500 if (fileExists(filename_base))
13501 LoadMenuDesignSettingsFromFilename(filename_base);
13504 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13506 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13507 LoadMenuDesignSettingsFromFilename(filename_local);
13509 InitMenuDesignSettings_SpecialPostProcessing();
13512 void LoadMenuDesignSettings_AfterGraphics(void)
13514 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13517 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13518 boolean ignore_defaults)
13522 for (i = 0; sound_config_vars[i].token != NULL; i++)
13524 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13526 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13527 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13531 *sound_config_vars[i].value =
13532 get_token_parameter_value(sound_config_vars[i].token, value);
13536 void InitSoundSettings_Static(void)
13538 // always start with reliable default values from static default config
13539 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13542 static void LoadSoundSettingsFromFilename(char *filename)
13544 SetupFileHash *setup_file_hash;
13546 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13549 // read (and overwrite with) values that may be specified in config file
13550 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13552 freeSetupFileHash(setup_file_hash);
13555 void LoadSoundSettings(void)
13557 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13559 InitSoundSettings_Static();
13561 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13563 // first look for special settings configured in level series config
13564 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13566 if (fileExists(filename_base))
13567 LoadSoundSettingsFromFilename(filename_base);
13570 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13572 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13573 LoadSoundSettingsFromFilename(filename_local);
13576 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13578 char *filename = getEditorSetupFilename();
13579 SetupFileList *setup_file_list, *list;
13580 SetupFileHash *element_hash;
13581 int num_unknown_tokens = 0;
13584 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13587 element_hash = newSetupFileHash();
13589 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13590 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13592 // determined size may be larger than needed (due to unknown elements)
13594 for (list = setup_file_list; list != NULL; list = list->next)
13597 // add space for up to 3 more elements for padding that may be needed
13598 *num_elements += 3;
13600 // free memory for old list of elements, if needed
13601 checked_free(*elements);
13603 // allocate memory for new list of elements
13604 *elements = checked_malloc(*num_elements * sizeof(int));
13607 for (list = setup_file_list; list != NULL; list = list->next)
13609 char *value = getHashEntry(element_hash, list->token);
13611 if (value == NULL) // try to find obsolete token mapping
13613 char *mapped_token = get_mapped_token(list->token);
13615 if (mapped_token != NULL)
13617 value = getHashEntry(element_hash, mapped_token);
13619 free(mapped_token);
13625 (*elements)[(*num_elements)++] = atoi(value);
13629 if (num_unknown_tokens == 0)
13632 Warn("unknown token(s) found in config file:");
13633 Warn("- config file: '%s'", filename);
13635 num_unknown_tokens++;
13638 Warn("- token: '%s'", list->token);
13642 if (num_unknown_tokens > 0)
13645 while (*num_elements % 4) // pad with empty elements, if needed
13646 (*elements)[(*num_elements)++] = EL_EMPTY;
13648 freeSetupFileList(setup_file_list);
13649 freeSetupFileHash(element_hash);
13652 for (i = 0; i < *num_elements; i++)
13653 Debug("editor", "element '%s' [%d]\n",
13654 element_info[(*elements)[i]].token_name, (*elements)[i]);
13658 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13661 SetupFileHash *setup_file_hash = NULL;
13662 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13663 char *filename_music, *filename_prefix, *filename_info;
13669 token_to_value_ptr[] =
13671 { "title_header", &tmp_music_file_info.title_header },
13672 { "artist_header", &tmp_music_file_info.artist_header },
13673 { "album_header", &tmp_music_file_info.album_header },
13674 { "year_header", &tmp_music_file_info.year_header },
13675 { "played_header", &tmp_music_file_info.played_header },
13677 { "title", &tmp_music_file_info.title },
13678 { "artist", &tmp_music_file_info.artist },
13679 { "album", &tmp_music_file_info.album },
13680 { "year", &tmp_music_file_info.year },
13681 { "played", &tmp_music_file_info.played },
13687 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13688 getCustomMusicFilename(basename));
13690 if (filename_music == NULL)
13693 // ---------- try to replace file extension ----------
13695 filename_prefix = getStringCopy(filename_music);
13696 if (strrchr(filename_prefix, '.') != NULL)
13697 *strrchr(filename_prefix, '.') = '\0';
13698 filename_info = getStringCat2(filename_prefix, ".txt");
13700 if (fileExists(filename_info))
13701 setup_file_hash = loadSetupFileHash(filename_info);
13703 free(filename_prefix);
13704 free(filename_info);
13706 if (setup_file_hash == NULL)
13708 // ---------- try to add file extension ----------
13710 filename_prefix = getStringCopy(filename_music);
13711 filename_info = getStringCat2(filename_prefix, ".txt");
13713 if (fileExists(filename_info))
13714 setup_file_hash = loadSetupFileHash(filename_info);
13716 free(filename_prefix);
13717 free(filename_info);
13720 if (setup_file_hash == NULL)
13723 // ---------- music file info found ----------
13725 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13727 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13729 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13731 *token_to_value_ptr[i].value_ptr =
13732 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13735 tmp_music_file_info.basename = getStringCopy(basename);
13736 tmp_music_file_info.music = music;
13737 tmp_music_file_info.is_sound = is_sound;
13739 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13740 *new_music_file_info = tmp_music_file_info;
13742 return new_music_file_info;
13745 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13747 return get_music_file_info_ext(basename, music, FALSE);
13750 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13752 return get_music_file_info_ext(basename, sound, TRUE);
13755 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13756 char *basename, boolean is_sound)
13758 for (; list != NULL; list = list->next)
13759 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13765 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13767 return music_info_listed_ext(list, basename, FALSE);
13770 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13772 return music_info_listed_ext(list, basename, TRUE);
13775 void LoadMusicInfo(void)
13777 int num_music_noconf = getMusicListSize_NoConf();
13778 int num_music = getMusicListSize();
13779 int num_sounds = getSoundListSize();
13780 struct FileInfo *music, *sound;
13781 struct MusicFileInfo *next, **new;
13785 while (music_file_info != NULL)
13787 next = music_file_info->next;
13789 checked_free(music_file_info->basename);
13791 checked_free(music_file_info->title_header);
13792 checked_free(music_file_info->artist_header);
13793 checked_free(music_file_info->album_header);
13794 checked_free(music_file_info->year_header);
13795 checked_free(music_file_info->played_header);
13797 checked_free(music_file_info->title);
13798 checked_free(music_file_info->artist);
13799 checked_free(music_file_info->album);
13800 checked_free(music_file_info->year);
13801 checked_free(music_file_info->played);
13803 free(music_file_info);
13805 music_file_info = next;
13808 new = &music_file_info;
13810 // get (configured or unconfigured) music file info for all levels
13811 for (i = leveldir_current->first_level;
13812 i <= leveldir_current->last_level; i++)
13816 if (levelset.music[i] != MUS_UNDEFINED)
13818 // get music file info for configured level music
13819 music_nr = levelset.music[i];
13821 else if (num_music_noconf > 0)
13823 // get music file info for unconfigured level music
13824 int level_pos = i - leveldir_current->first_level;
13826 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13833 char *basename = getMusicInfoEntryFilename(music_nr);
13835 if (basename == NULL)
13838 if (!music_info_listed(music_file_info, basename))
13840 *new = get_music_file_info(basename, music_nr);
13843 new = &(*new)->next;
13847 // get music file info for all remaining configured music files
13848 for (i = 0; i < num_music; i++)
13850 music = getMusicListEntry(i);
13852 if (music->filename == NULL)
13855 if (strEqual(music->filename, UNDEFINED_FILENAME))
13858 // a configured file may be not recognized as music
13859 if (!FileIsMusic(music->filename))
13862 if (!music_info_listed(music_file_info, music->filename))
13864 *new = get_music_file_info(music->filename, i);
13867 new = &(*new)->next;
13871 // get sound file info for all configured sound files
13872 for (i = 0; i < num_sounds; i++)
13874 sound = getSoundListEntry(i);
13876 if (sound->filename == NULL)
13879 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13882 // a configured file may be not recognized as sound
13883 if (!FileIsSound(sound->filename))
13886 if (!sound_info_listed(music_file_info, sound->filename))
13888 *new = get_sound_file_info(sound->filename, i);
13890 new = &(*new)->next;
13894 // add pointers to previous list nodes
13896 struct MusicFileInfo *node = music_file_info;
13898 while (node != NULL)
13901 node->next->prev = node;
13907 static void add_helpanim_entry(int element, int action, int direction,
13908 int delay, int *num_list_entries)
13910 struct HelpAnimInfo *new_list_entry;
13911 (*num_list_entries)++;
13914 checked_realloc(helpanim_info,
13915 *num_list_entries * sizeof(struct HelpAnimInfo));
13916 new_list_entry = &helpanim_info[*num_list_entries - 1];
13918 new_list_entry->element = element;
13919 new_list_entry->action = action;
13920 new_list_entry->direction = direction;
13921 new_list_entry->delay = delay;
13924 static void print_unknown_token(char *filename, char *token, int token_nr)
13929 Warn("unknown token(s) found in config file:");
13930 Warn("- config file: '%s'", filename);
13933 Warn("- token: '%s'", token);
13936 static void print_unknown_token_end(int token_nr)
13942 void LoadHelpAnimInfo(void)
13944 char *filename = getHelpAnimFilename();
13945 SetupFileList *setup_file_list = NULL, *list;
13946 SetupFileHash *element_hash, *action_hash, *direction_hash;
13947 int num_list_entries = 0;
13948 int num_unknown_tokens = 0;
13951 if (fileExists(filename))
13952 setup_file_list = loadSetupFileList(filename);
13954 if (setup_file_list == NULL)
13956 // use reliable default values from static configuration
13957 SetupFileList *insert_ptr;
13959 insert_ptr = setup_file_list =
13960 newSetupFileList(helpanim_config[0].token,
13961 helpanim_config[0].value);
13963 for (i = 1; helpanim_config[i].token; i++)
13964 insert_ptr = addListEntry(insert_ptr,
13965 helpanim_config[i].token,
13966 helpanim_config[i].value);
13969 element_hash = newSetupFileHash();
13970 action_hash = newSetupFileHash();
13971 direction_hash = newSetupFileHash();
13973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13974 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13976 for (i = 0; i < NUM_ACTIONS; i++)
13977 setHashEntry(action_hash, element_action_info[i].suffix,
13978 i_to_a(element_action_info[i].value));
13980 // do not store direction index (bit) here, but direction value!
13981 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13982 setHashEntry(direction_hash, element_direction_info[i].suffix,
13983 i_to_a(1 << element_direction_info[i].value));
13985 for (list = setup_file_list; list != NULL; list = list->next)
13987 char *element_token, *action_token, *direction_token;
13988 char *element_value, *action_value, *direction_value;
13989 int delay = atoi(list->value);
13991 if (strEqual(list->token, "end"))
13993 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13998 /* first try to break element into element/action/direction parts;
13999 if this does not work, also accept combined "element[.act][.dir]"
14000 elements (like "dynamite.active"), which are unique elements */
14002 if (strchr(list->token, '.') == NULL) // token contains no '.'
14004 element_value = getHashEntry(element_hash, list->token);
14005 if (element_value != NULL) // element found
14006 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14007 &num_list_entries);
14010 // no further suffixes found -- this is not an element
14011 print_unknown_token(filename, list->token, num_unknown_tokens++);
14017 // token has format "<prefix>.<something>"
14019 action_token = strchr(list->token, '.'); // suffix may be action ...
14020 direction_token = action_token; // ... or direction
14022 element_token = getStringCopy(list->token);
14023 *strchr(element_token, '.') = '\0';
14025 element_value = getHashEntry(element_hash, element_token);
14027 if (element_value == NULL) // this is no element
14029 element_value = getHashEntry(element_hash, list->token);
14030 if (element_value != NULL) // combined element found
14031 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14032 &num_list_entries);
14034 print_unknown_token(filename, list->token, num_unknown_tokens++);
14036 free(element_token);
14041 action_value = getHashEntry(action_hash, action_token);
14043 if (action_value != NULL) // action found
14045 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14046 &num_list_entries);
14048 free(element_token);
14053 direction_value = getHashEntry(direction_hash, direction_token);
14055 if (direction_value != NULL) // direction found
14057 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14058 &num_list_entries);
14060 free(element_token);
14065 if (strchr(action_token + 1, '.') == NULL)
14067 // no further suffixes found -- this is not an action nor direction
14069 element_value = getHashEntry(element_hash, list->token);
14070 if (element_value != NULL) // combined element found
14071 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14072 &num_list_entries);
14074 print_unknown_token(filename, list->token, num_unknown_tokens++);
14076 free(element_token);
14081 // token has format "<prefix>.<suffix>.<something>"
14083 direction_token = strchr(action_token + 1, '.');
14085 action_token = getStringCopy(action_token);
14086 *strchr(action_token + 1, '.') = '\0';
14088 action_value = getHashEntry(action_hash, action_token);
14090 if (action_value == NULL) // this is no action
14092 element_value = getHashEntry(element_hash, list->token);
14093 if (element_value != NULL) // combined element found
14094 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14095 &num_list_entries);
14097 print_unknown_token(filename, list->token, num_unknown_tokens++);
14099 free(element_token);
14100 free(action_token);
14105 direction_value = getHashEntry(direction_hash, direction_token);
14107 if (direction_value != NULL) // direction found
14109 add_helpanim_entry(atoi(element_value), atoi(action_value),
14110 atoi(direction_value), delay, &num_list_entries);
14112 free(element_token);
14113 free(action_token);
14118 // this is no direction
14120 element_value = getHashEntry(element_hash, list->token);
14121 if (element_value != NULL) // combined element found
14122 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14123 &num_list_entries);
14125 print_unknown_token(filename, list->token, num_unknown_tokens++);
14127 free(element_token);
14128 free(action_token);
14131 print_unknown_token_end(num_unknown_tokens);
14133 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14134 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14136 freeSetupFileList(setup_file_list);
14137 freeSetupFileHash(element_hash);
14138 freeSetupFileHash(action_hash);
14139 freeSetupFileHash(direction_hash);
14142 for (i = 0; i < num_list_entries; i++)
14143 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14144 EL_NAME(helpanim_info[i].element),
14145 helpanim_info[i].element,
14146 helpanim_info[i].action,
14147 helpanim_info[i].direction,
14148 helpanim_info[i].delay);
14152 void LoadHelpTextInfo(void)
14154 char *filename = getHelpTextFilename();
14157 if (helptext_info != NULL)
14159 freeSetupFileHash(helptext_info);
14160 helptext_info = NULL;
14163 if (fileExists(filename))
14164 helptext_info = loadSetupFileHash(filename);
14166 if (helptext_info == NULL)
14168 // use reliable default values from static configuration
14169 helptext_info = newSetupFileHash();
14171 for (i = 0; helptext_config[i].token; i++)
14172 setHashEntry(helptext_info,
14173 helptext_config[i].token,
14174 helptext_config[i].value);
14178 BEGIN_HASH_ITERATION(helptext_info, itr)
14180 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14181 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14183 END_HASH_ITERATION(hash, itr)
14188 // ----------------------------------------------------------------------------
14190 // ----------------------------------------------------------------------------
14192 #define MAX_NUM_CONVERT_LEVELS 1000
14194 void ConvertLevels(void)
14196 static LevelDirTree *convert_leveldir = NULL;
14197 static int convert_level_nr = -1;
14198 static int num_levels_handled = 0;
14199 static int num_levels_converted = 0;
14200 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14203 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14204 global.convert_leveldir);
14206 if (convert_leveldir == NULL)
14207 Fail("no such level identifier: '%s'", global.convert_leveldir);
14209 leveldir_current = convert_leveldir;
14211 if (global.convert_level_nr != -1)
14213 convert_leveldir->first_level = global.convert_level_nr;
14214 convert_leveldir->last_level = global.convert_level_nr;
14217 convert_level_nr = convert_leveldir->first_level;
14219 PrintLine("=", 79);
14220 Print("Converting levels\n");
14221 PrintLine("-", 79);
14222 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14223 Print("Level series name: '%s'\n", convert_leveldir->name);
14224 Print("Level series author: '%s'\n", convert_leveldir->author);
14225 Print("Number of levels: %d\n", convert_leveldir->levels);
14226 PrintLine("=", 79);
14229 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14230 levels_failed[i] = FALSE;
14232 while (convert_level_nr <= convert_leveldir->last_level)
14234 char *level_filename;
14237 level_nr = convert_level_nr++;
14239 Print("Level %03d: ", level_nr);
14241 LoadLevel(level_nr);
14242 if (level.no_level_file || level.no_valid_file)
14244 Print("(no level)\n");
14248 Print("converting level ... ");
14251 // special case: conversion of some EMC levels as requested by ACME
14252 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14255 level_filename = getDefaultLevelFilename(level_nr);
14256 new_level = !fileExists(level_filename);
14260 SaveLevel(level_nr);
14262 num_levels_converted++;
14264 Print("converted.\n");
14268 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14269 levels_failed[level_nr] = TRUE;
14271 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14274 num_levels_handled++;
14278 PrintLine("=", 79);
14279 Print("Number of levels handled: %d\n", num_levels_handled);
14280 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14281 (num_levels_handled ?
14282 num_levels_converted * 100 / num_levels_handled : 0));
14283 PrintLine("-", 79);
14284 Print("Summary (for automatic parsing by scripts):\n");
14285 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14286 convert_leveldir->identifier, num_levels_converted,
14287 num_levels_handled,
14288 (num_levels_handled ?
14289 num_levels_converted * 100 / num_levels_handled : 0));
14291 if (num_levels_handled != num_levels_converted)
14293 Print(", FAILED:");
14294 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14295 if (levels_failed[i])
14300 PrintLine("=", 79);
14302 CloseAllAndExit(0);
14306 // ----------------------------------------------------------------------------
14307 // create and save images for use in level sketches (raw BMP format)
14308 // ----------------------------------------------------------------------------
14310 void CreateLevelSketchImages(void)
14316 InitElementPropertiesGfxElement();
14318 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14319 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14321 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14323 int element = getMappedElement(i);
14324 char basename1[16];
14325 char basename2[16];
14329 sprintf(basename1, "%04d.bmp", i);
14330 sprintf(basename2, "%04ds.bmp", i);
14332 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14333 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14335 DrawSizedElement(0, 0, element, TILESIZE);
14336 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14338 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14339 Fail("cannot save level sketch image file '%s'", filename1);
14341 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14342 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14344 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14345 Fail("cannot save level sketch image file '%s'", filename2);
14350 // create corresponding SQL statements (for normal and small images)
14353 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14354 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14357 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14358 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14360 // optional: create content for forum level sketch demonstration post
14362 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14365 FreeBitmap(bitmap1);
14366 FreeBitmap(bitmap2);
14369 fprintf(stderr, "\n");
14371 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14373 CloseAllAndExit(0);
14377 // ----------------------------------------------------------------------------
14378 // create and save images for element collecting animations (raw BMP format)
14379 // ----------------------------------------------------------------------------
14381 static boolean createCollectImage(int element)
14383 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14386 void CreateCollectElementImages(void)
14390 int anim_frames = num_steps - 1;
14391 int tile_size = TILESIZE;
14392 int anim_width = tile_size * anim_frames;
14393 int anim_height = tile_size;
14394 int num_collect_images = 0;
14395 int pos_collect_images = 0;
14397 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14398 if (createCollectImage(i))
14399 num_collect_images++;
14401 Info("Creating %d element collecting animation images ...",
14402 num_collect_images);
14404 int dst_width = anim_width * 2;
14405 int dst_height = anim_height * num_collect_images / 2;
14406 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14407 char *basename_bmp = "RocksCollect.bmp";
14408 char *basename_png = "RocksCollect.png";
14409 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14410 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14411 int len_filename_bmp = strlen(filename_bmp);
14412 int len_filename_png = strlen(filename_png);
14413 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14414 char cmd_convert[max_command_len];
14416 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14420 // force using RGBA surface for destination bitmap
14421 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14422 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14424 dst_bitmap->surface =
14425 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14427 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14429 if (!createCollectImage(i))
14432 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14433 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14434 int graphic = el2img(i);
14435 char *token_name = element_info[i].token_name;
14436 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14437 Bitmap *src_bitmap;
14440 Info("- creating collecting image for '%s' ...", token_name);
14442 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14444 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14445 tile_size, tile_size, 0, 0);
14447 // force using RGBA surface for temporary bitmap (using transparent black)
14448 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14449 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14451 tmp_bitmap->surface =
14452 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14454 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14456 for (j = 0; j < anim_frames; j++)
14458 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14459 int frame_size = frame_size_final * num_steps;
14460 int offset = (tile_size - frame_size_final) / 2;
14461 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14463 while (frame_size > frame_size_final)
14467 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14469 FreeBitmap(frame_bitmap);
14471 frame_bitmap = half_bitmap;
14474 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14475 frame_size_final, frame_size_final,
14476 dst_x + j * tile_size + offset, dst_y + offset);
14478 FreeBitmap(frame_bitmap);
14481 tmp_bitmap->surface_masked = NULL;
14483 FreeBitmap(tmp_bitmap);
14485 pos_collect_images++;
14488 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14489 Fail("cannot save element collecting image file '%s'", filename_bmp);
14491 FreeBitmap(dst_bitmap);
14493 Info("Converting image file from BMP to PNG ...");
14495 if (system(cmd_convert) != 0)
14496 Fail("converting image file failed");
14498 unlink(filename_bmp);
14502 CloseAllAndExit(0);
14506 // ----------------------------------------------------------------------------
14507 // create and save images for custom and group elements (raw BMP format)
14508 // ----------------------------------------------------------------------------
14510 void CreateCustomElementImages(char *directory)
14512 char *src_basename = "RocksCE-template.ilbm";
14513 char *dst_basename = "RocksCE.bmp";
14514 char *src_filename = getPath2(directory, src_basename);
14515 char *dst_filename = getPath2(directory, dst_basename);
14516 Bitmap *src_bitmap;
14518 int yoffset_ce = 0;
14519 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14522 InitVideoDefaults();
14524 ReCreateBitmap(&backbuffer, video.width, video.height);
14526 src_bitmap = LoadImage(src_filename);
14528 bitmap = CreateBitmap(TILEX * 16 * 2,
14529 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14532 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14539 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14540 TILEX * x, TILEY * y + yoffset_ce);
14542 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14544 TILEX * x + TILEX * 16,
14545 TILEY * y + yoffset_ce);
14547 for (j = 2; j >= 0; j--)
14551 BlitBitmap(src_bitmap, bitmap,
14552 TILEX + c * 7, 0, 6, 10,
14553 TILEX * x + 6 + j * 7,
14554 TILEY * y + 11 + yoffset_ce);
14556 BlitBitmap(src_bitmap, bitmap,
14557 TILEX + c * 8, TILEY, 6, 10,
14558 TILEX * 16 + TILEX * x + 6 + j * 8,
14559 TILEY * y + 10 + yoffset_ce);
14565 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14572 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14573 TILEX * x, TILEY * y + yoffset_ge);
14575 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14577 TILEX * x + TILEX * 16,
14578 TILEY * y + yoffset_ge);
14580 for (j = 1; j >= 0; j--)
14584 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14585 TILEX * x + 6 + j * 10,
14586 TILEY * y + 11 + yoffset_ge);
14588 BlitBitmap(src_bitmap, bitmap,
14589 TILEX + c * 8, TILEY + 12, 6, 10,
14590 TILEX * 16 + TILEX * x + 10 + j * 8,
14591 TILEY * y + 10 + yoffset_ge);
14597 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14598 Fail("cannot save CE graphics file '%s'", dst_filename);
14600 FreeBitmap(bitmap);
14602 CloseAllAndExit(0);