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"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
65 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
66 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
68 // file identifier strings
69 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
70 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
71 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
73 // values for deciding when (not) to save configuration data
74 #define SAVE_CONF_NEVER 0
75 #define SAVE_CONF_ALWAYS 1
76 #define SAVE_CONF_WHEN_CHANGED -1
78 // values for chunks using micro chunks
79 #define CONF_MASK_1_BYTE 0x00
80 #define CONF_MASK_2_BYTE 0x40
81 #define CONF_MASK_4_BYTE 0x80
82 #define CONF_MASK_MULTI_BYTES 0xc0
84 #define CONF_MASK_BYTES 0xc0
85 #define CONF_MASK_TOKEN 0x3f
87 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
88 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
89 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
90 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
92 // these definitions are just for convenience of use and readability
93 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
94 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
95 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
96 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
98 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
99 (x) == CONF_MASK_2_BYTE ? 2 : \
100 (x) == CONF_MASK_4_BYTE ? 4 : 0)
102 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
103 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
104 #define CONF_ELEMENT_NUM_BYTES (2)
106 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
107 (t) == TYPE_ELEMENT_LIST ? \
108 CONF_ELEMENT_NUM_BYTES : \
109 (t) == TYPE_CONTENT || \
110 (t) == TYPE_CONTENT_LIST ? \
111 CONF_CONTENT_NUM_BYTES : 1)
113 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
114 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
115 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
117 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
119 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
120 CONF_ELEMENT_NUM_BYTES)
121 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
122 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
124 // temporary variables used to store pointers to structure members
125 static struct LevelInfo li;
126 static struct ElementInfo xx_ei, yy_ei;
127 static struct ElementChangeInfo xx_change;
128 static struct ElementGroupInfo xx_group;
129 static struct EnvelopeInfo xx_envelope;
130 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
131 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
132 static int xx_num_contents;
133 static int xx_current_change_page;
134 static char xx_default_string_empty[1] = "";
135 static int xx_string_length_unused;
137 struct LevelFileConfigInfo
139 int element; // element for which data is to be stored
140 int save_type; // save data always, never or when changed
141 int data_type; // data type (used internally, not stored)
142 int conf_type; // micro chunk identifier (stored in file)
145 void *value; // variable that holds the data to be stored
146 int default_value; // initial default value for this variable
149 void *value_copy; // variable that holds the data to be copied
150 void *num_entities; // number of entities for multi-byte data
151 int default_num_entities; // default number of entities for this data
152 int max_num_entities; // maximal number of entities for this data
153 char *default_string; // optional default string for string data
156 static struct LevelFileConfigInfo chunk_config_INFO[] =
158 // ---------- values not related to single elements -------------------------
161 -1, SAVE_CONF_ALWAYS,
162 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
163 &li.game_engine_type, GAME_ENGINE_TYPE_RND
167 -1, SAVE_CONF_ALWAYS,
168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
169 &li.fieldx, STD_LEV_FIELDX
172 -1, SAVE_CONF_ALWAYS,
173 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
174 &li.fieldy, STD_LEV_FIELDY
178 -1, SAVE_CONF_ALWAYS,
179 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
197 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
198 &li.use_step_counter, FALSE
203 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
204 &li.wind_direction_initial, MV_NONE
209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
210 &li.em_slippery_gems, FALSE
215 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
216 &li.use_custom_template, FALSE
221 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
222 &li.can_move_into_acid_bits, ~0 // default: everything can
227 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
228 &li.dont_collide_with_bits, ~0 // default: always deadly
233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
234 &li.em_explodes_by_fire, FALSE
239 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
240 &li.score[SC_TIME_BONUS], 1
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
246 &li.auto_exit_sokoban, FALSE
251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
252 &li.auto_count_gems, FALSE
257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
258 &li.solved_by_one_player, FALSE
263 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
264 &li.time_score_base, 1
274 static struct LevelFileConfigInfo chunk_config_ELEM[] =
276 // (these values are the same for each player)
279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
280 &li.block_last_field, FALSE // default case for EM levels
284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
285 &li.sp_block_last_field, TRUE // default case for SP levels
289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
290 &li.instant_relocation, FALSE
294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
295 &li.can_pass_to_walkable, FALSE
299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
300 &li.block_snap_field, TRUE
304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
305 &li.continuous_snapping, TRUE
309 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
310 &li.shifted_relocation, FALSE
314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
315 &li.lazy_relocation, FALSE
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
320 &li.finish_dig_collect, TRUE
323 // (these values are different for each player)
326 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
327 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
332 &li.initial_player_gravity[0], FALSE
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
337 &li.use_start_element[0], FALSE
341 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
342 &li.start_element[0], EL_PLAYER_1
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
347 &li.use_artwork_element[0], FALSE
351 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
352 &li.artwork_element[0], EL_PLAYER_1
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
357 &li.use_explosion_element[0], FALSE
361 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
362 &li.explosion_element[0], EL_PLAYER_1
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
367 &li.use_initial_inventory[0], FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
372 &li.initial_inventory_size[0], 1
376 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
377 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
378 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[1], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[1], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[1], EL_PLAYER_2
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[1], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[1], EL_PLAYER_2
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[1], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[1], EL_PLAYER_2
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[1], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[1], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[2], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[2], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[2], EL_PLAYER_3
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[2], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[2], EL_PLAYER_3
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[2], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[2], EL_PLAYER_3
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[2], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[2], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[3], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[3], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[3], EL_PLAYER_4
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[3], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[3], EL_PLAYER_4
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[3], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[3], EL_PLAYER_4
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[3], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[3], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_EMERALD], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_DIAMOND], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_BUG], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_SPACESHIP], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_PACMAN], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_NUT], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_DYNAMITE], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_KEY], 10
602 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
603 &li.score[SC_PEARL], 10
608 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
609 &li.score[SC_CRYSTAL], 10
614 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
615 &li.amoeba_content, EL_DIAMOND
619 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
624 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
625 &li.grow_into_diggable, TRUE
630 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
631 &li.yamyam_content, EL_ROCK, NULL,
632 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
636 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
637 &li.score[SC_YAMYAM], 10
642 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
643 &li.score[SC_ROBOT], 10
647 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
653 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.time_magic_wall, 10
665 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
666 &li.game_of_life[0], 2
670 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
671 &li.game_of_life[1], 3
675 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
676 &li.game_of_life[2], 3
680 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
681 &li.game_of_life[3], 3
685 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
686 &li.use_life_bugs, FALSE
691 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
696 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
701 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
706 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
711 EL_TIMEGATE_SWITCH, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.time_timegate, 10
717 EL_LIGHT_SWITCH_ACTIVE, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
723 EL_SHIELD_NORMAL, -1,
724 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
725 &li.shield_normal_time, 10
728 EL_SHIELD_NORMAL, -1,
729 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 &li.score[SC_SHIELD], 10
734 EL_SHIELD_DEADLY, -1,
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 &li.shield_deadly_time, 10
739 EL_SHIELD_DEADLY, -1,
740 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
741 &li.score[SC_SHIELD], 10
746 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
752 &li.extra_time_score, 10
756 EL_TIME_ORB_FULL, -1,
757 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 &li.time_orb_time, 10
761 EL_TIME_ORB_FULL, -1,
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
763 &li.use_time_orb_bug, FALSE
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.use_spring_bug, FALSE
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
775 &li.android_move_time, 10
779 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
780 &li.android_clone_time, 10
783 EL_EMC_ANDROID, SAVE_CONF_NEVER,
784 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
785 &li.android_clone_element[0], EL_EMPTY, NULL,
786 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
790 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
791 &li.android_clone_element[0], EL_EMPTY, NULL,
792 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 EL_EMC_MAGNIFIER, -1,
808 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
809 &li.magnify_score, 10
812 EL_EMC_MAGNIFIER, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
818 EL_EMC_MAGIC_BALL, -1,
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 EL_EMC_MAGIC_BALL, -1,
824 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
825 &li.ball_random, FALSE
828 EL_EMC_MAGIC_BALL, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
830 &li.ball_active_initial, FALSE
833 EL_EMC_MAGIC_BALL, -1,
834 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
835 &li.ball_content, EL_EMPTY, NULL,
836 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
840 EL_SOKOBAN_FIELD_EMPTY, -1,
841 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
842 &li.sb_fields_needed, TRUE
846 EL_SOKOBAN_OBJECT, -1,
847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
848 &li.sb_objects_needed, TRUE
853 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
854 &li.mm_laser_red, FALSE
858 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
859 &li.mm_laser_green, FALSE
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
864 &li.mm_laser_blue, TRUE
869 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
870 &li.df_laser_red, TRUE
874 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
875 &li.df_laser_green, TRUE
879 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
880 &li.df_laser_blue, FALSE
884 EL_MM_FUSE_ACTIVE, -1,
885 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
890 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
899 EL_MM_STEEL_BLOCK, -1,
900 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
901 &li.mm_time_block, 75
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
906 &li.score[SC_ELEM_BONUS], 10
909 // ---------- unused values -------------------------------------------------
912 EL_UNKNOWN, SAVE_CONF_NEVER,
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.score[SC_UNKNOWN_15], 10
924 static struct LevelFileConfigInfo chunk_config_NOTE[] =
928 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
929 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
933 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
934 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
939 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
940 &xx_envelope.autowrap, FALSE
944 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
945 &xx_envelope.centered, FALSE
950 TYPE_STRING, CONF_VALUE_BYTES(1),
951 &xx_envelope.text, -1, NULL,
952 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
953 &xx_default_string_empty[0]
963 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
967 TYPE_STRING, CONF_VALUE_BYTES(1),
968 &xx_ei.description[0], -1,
969 &yy_ei.description[0],
970 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
971 &xx_default_description[0]
976 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
977 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
978 &yy_ei.properties[EP_BITFIELD_BASE_NR]
980 #if ENABLE_RESERVED_CODE
981 // (reserved for later use)
984 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
985 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
986 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
992 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
993 &xx_ei.use_gfx_element, FALSE,
994 &yy_ei.use_gfx_element
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
999 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1000 &yy_ei.gfx_element_initial
1005 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1006 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1007 &yy_ei.access_direction
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1013 &xx_ei.collect_score_initial, 10,
1014 &yy_ei.collect_score_initial
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1019 &xx_ei.collect_count_initial, 1,
1020 &yy_ei.collect_count_initial
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1026 &xx_ei.ce_value_fixed_initial, 0,
1027 &yy_ei.ce_value_fixed_initial
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1032 &xx_ei.ce_value_random_initial, 0,
1033 &yy_ei.ce_value_random_initial
1037 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1038 &xx_ei.use_last_ce_value, FALSE,
1039 &yy_ei.use_last_ce_value
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1045 &xx_ei.push_delay_fixed, 8,
1046 &yy_ei.push_delay_fixed
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1051 &xx_ei.push_delay_random, 8,
1052 &yy_ei.push_delay_random
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1057 &xx_ei.drop_delay_fixed, 0,
1058 &yy_ei.drop_delay_fixed
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1063 &xx_ei.drop_delay_random, 0,
1064 &yy_ei.drop_delay_random
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1069 &xx_ei.move_delay_fixed, 0,
1070 &yy_ei.move_delay_fixed
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1075 &xx_ei.move_delay_random, 0,
1076 &yy_ei.move_delay_random
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1081 &xx_ei.step_delay_fixed, 0,
1082 &yy_ei.step_delay_fixed
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1087 &xx_ei.step_delay_random, 0,
1088 &yy_ei.step_delay_random
1093 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1094 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1099 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1100 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1101 &yy_ei.move_direction_initial
1105 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1106 &xx_ei.move_stepsize, TILEX / 8,
1107 &yy_ei.move_stepsize
1112 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1113 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1114 &yy_ei.move_enter_element
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1119 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1120 &yy_ei.move_leave_element
1124 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1125 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1126 &yy_ei.move_leave_type
1131 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1132 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1133 &yy_ei.slippery_type
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1139 &xx_ei.explosion_type, EXPLODES_3X3,
1140 &yy_ei.explosion_type
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1145 &xx_ei.explosion_delay, 16,
1146 &yy_ei.explosion_delay
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1151 &xx_ei.ignition_delay, 8,
1152 &yy_ei.ignition_delay
1157 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1158 &xx_ei.content, EL_EMPTY_SPACE,
1160 &xx_num_contents, 1, 1
1163 // ---------- "num_change_pages" must be the last entry ---------------------
1166 -1, SAVE_CONF_ALWAYS,
1167 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1168 &xx_ei.num_change_pages, 1,
1169 &yy_ei.num_change_pages
1180 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1182 // ---------- "current_change_page" must be the first entry -----------------
1185 -1, SAVE_CONF_ALWAYS,
1186 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1187 &xx_current_change_page, -1
1190 // ---------- (the remaining entries can be in any order) -------------------
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1195 &xx_change.can_change, FALSE
1200 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1201 &xx_event_bits[0], 0
1205 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1206 &xx_event_bits[1], 0
1211 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1212 &xx_change.trigger_player, CH_PLAYER_ANY
1216 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1217 &xx_change.trigger_side, CH_SIDE_ANY
1221 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1222 &xx_change.trigger_page, CH_PAGE_ANY
1227 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1228 &xx_change.target_element, EL_EMPTY_SPACE
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1234 &xx_change.delay_fixed, 0
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1239 &xx_change.delay_random, 0
1243 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1244 &xx_change.delay_frames, FRAMES_PER_SECOND
1249 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1250 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1256 &xx_change.explode, FALSE
1260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1261 &xx_change.use_target_content, FALSE
1265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1266 &xx_change.only_if_complete, FALSE
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1271 &xx_change.use_random_replace, FALSE
1275 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1276 &xx_change.random_percentage, 100
1280 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1281 &xx_change.replace_when, CP_WHEN_EMPTY
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1287 &xx_change.has_action, FALSE
1291 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1292 &xx_change.action_type, CA_NO_ACTION
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1297 &xx_change.action_mode, CA_MODE_UNDEFINED
1301 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1302 &xx_change.action_arg, CA_ARG_UNDEFINED
1307 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1308 &xx_change.action_element, EL_EMPTY_SPACE
1313 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1314 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1315 &xx_num_contents, 1, 1
1325 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1329 TYPE_STRING, CONF_VALUE_BYTES(1),
1330 &xx_ei.description[0], -1, NULL,
1331 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1332 &xx_default_description[0]
1337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1338 &xx_ei.use_gfx_element, FALSE
1342 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1343 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1348 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1349 &xx_group.choice_mode, ANIM_RANDOM
1354 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1355 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1356 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1366 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1371 &li.block_snap_field, TRUE
1375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1376 &li.continuous_snapping, TRUE
1380 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1381 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1386 &li.use_start_element[0], FALSE
1390 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1391 &li.start_element[0], EL_PLAYER_1
1395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1396 &li.use_artwork_element[0], FALSE
1400 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1401 &li.artwork_element[0], EL_PLAYER_1
1405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1406 &li.use_explosion_element[0], FALSE
1410 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1411 &li.explosion_element[0], EL_PLAYER_1
1426 filetype_id_list[] =
1428 { LEVEL_FILE_TYPE_RND, "RND" },
1429 { LEVEL_FILE_TYPE_BD, "BD" },
1430 { LEVEL_FILE_TYPE_EM, "EM" },
1431 { LEVEL_FILE_TYPE_SP, "SP" },
1432 { LEVEL_FILE_TYPE_DX, "DX" },
1433 { LEVEL_FILE_TYPE_SB, "SB" },
1434 { LEVEL_FILE_TYPE_DC, "DC" },
1435 { LEVEL_FILE_TYPE_MM, "MM" },
1436 { LEVEL_FILE_TYPE_MM, "DF" },
1441 // ============================================================================
1442 // level file functions
1443 // ============================================================================
1445 static boolean check_special_flags(char *flag)
1447 if (strEqual(options.special_flags, flag) ||
1448 strEqual(leveldir_current->special_flags, flag))
1454 static struct DateInfo getCurrentDate(void)
1456 time_t epoch_seconds = time(NULL);
1457 struct tm *now = localtime(&epoch_seconds);
1458 struct DateInfo date;
1460 date.year = now->tm_year + 1900;
1461 date.month = now->tm_mon + 1;
1462 date.day = now->tm_mday;
1464 date.src = DATE_SRC_CLOCK;
1469 static void resetEventFlags(struct ElementChangeInfo *change)
1473 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1474 change->has_event[i] = FALSE;
1477 static void resetEventBits(void)
1481 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1482 xx_event_bits[i] = 0;
1485 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1489 /* important: only change event flag if corresponding event bit is set
1490 (this is because all xx_event_bits[] values are loaded separately,
1491 and all xx_event_bits[] values are set back to zero before loading
1492 another value xx_event_bits[x] (each value representing 32 flags)) */
1494 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1495 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1496 change->has_event[i] = TRUE;
1499 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1503 /* in contrast to the above function setEventFlagsFromEventBits(), it
1504 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1505 depending on the corresponding change->has_event[i] values here, as
1506 all xx_event_bits[] values are reset in resetEventBits() before */
1508 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1509 if (change->has_event[i])
1510 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1513 static char *getDefaultElementDescription(struct ElementInfo *ei)
1515 static char description[MAX_ELEMENT_NAME_LEN + 1];
1516 char *default_description = (ei->custom_description != NULL ?
1517 ei->custom_description :
1518 ei->editor_description);
1521 // always start with reliable default values
1522 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1523 description[i] = '\0';
1525 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1526 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1528 return &description[0];
1531 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1533 char *default_description = getDefaultElementDescription(ei);
1536 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1537 ei->description[i] = default_description[i];
1540 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1544 for (i = 0; conf[i].data_type != -1; i++)
1546 int default_value = conf[i].default_value;
1547 int data_type = conf[i].data_type;
1548 int conf_type = conf[i].conf_type;
1549 int byte_mask = conf_type & CONF_MASK_BYTES;
1551 if (byte_mask == CONF_MASK_MULTI_BYTES)
1553 int default_num_entities = conf[i].default_num_entities;
1554 int max_num_entities = conf[i].max_num_entities;
1556 *(int *)(conf[i].num_entities) = default_num_entities;
1558 if (data_type == TYPE_STRING)
1560 char *default_string = conf[i].default_string;
1561 char *string = (char *)(conf[i].value);
1563 strncpy(string, default_string, max_num_entities);
1565 else if (data_type == TYPE_ELEMENT_LIST)
1567 int *element_array = (int *)(conf[i].value);
1570 for (j = 0; j < max_num_entities; j++)
1571 element_array[j] = default_value;
1573 else if (data_type == TYPE_CONTENT_LIST)
1575 struct Content *content = (struct Content *)(conf[i].value);
1578 for (c = 0; c < max_num_entities; c++)
1579 for (y = 0; y < 3; y++)
1580 for (x = 0; x < 3; x++)
1581 content[c].e[x][y] = default_value;
1584 else // constant size configuration data (1, 2 or 4 bytes)
1586 if (data_type == TYPE_BOOLEAN)
1587 *(boolean *)(conf[i].value) = default_value;
1589 *(int *) (conf[i].value) = default_value;
1594 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1598 for (i = 0; conf[i].data_type != -1; i++)
1600 int data_type = conf[i].data_type;
1601 int conf_type = conf[i].conf_type;
1602 int byte_mask = conf_type & CONF_MASK_BYTES;
1604 if (byte_mask == CONF_MASK_MULTI_BYTES)
1606 int max_num_entities = conf[i].max_num_entities;
1608 if (data_type == TYPE_STRING)
1610 char *string = (char *)(conf[i].value);
1611 char *string_copy = (char *)(conf[i].value_copy);
1613 strncpy(string_copy, string, max_num_entities);
1615 else if (data_type == TYPE_ELEMENT_LIST)
1617 int *element_array = (int *)(conf[i].value);
1618 int *element_array_copy = (int *)(conf[i].value_copy);
1621 for (j = 0; j < max_num_entities; j++)
1622 element_array_copy[j] = element_array[j];
1624 else if (data_type == TYPE_CONTENT_LIST)
1626 struct Content *content = (struct Content *)(conf[i].value);
1627 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1630 for (c = 0; c < max_num_entities; c++)
1631 for (y = 0; y < 3; y++)
1632 for (x = 0; x < 3; x++)
1633 content_copy[c].e[x][y] = content[c].e[x][y];
1636 else // constant size configuration data (1, 2 or 4 bytes)
1638 if (data_type == TYPE_BOOLEAN)
1639 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1641 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1646 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1650 xx_ei = *ei_from; // copy element data into temporary buffer
1651 yy_ei = *ei_to; // copy element data into temporary buffer
1653 copyConfigFromConfigList(chunk_config_CUSX_base);
1658 // ---------- reinitialize and copy change pages ----------
1660 ei_to->num_change_pages = ei_from->num_change_pages;
1661 ei_to->current_change_page = ei_from->current_change_page;
1663 setElementChangePages(ei_to, ei_to->num_change_pages);
1665 for (i = 0; i < ei_to->num_change_pages; i++)
1666 ei_to->change_page[i] = ei_from->change_page[i];
1668 // ---------- copy group element info ----------
1669 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1670 *ei_to->group = *ei_from->group;
1672 // mark this custom element as modified
1673 ei_to->modified_settings = TRUE;
1676 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1678 int change_page_size = sizeof(struct ElementChangeInfo);
1680 ei->num_change_pages = MAX(1, change_pages);
1683 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1685 if (ei->current_change_page >= ei->num_change_pages)
1686 ei->current_change_page = ei->num_change_pages - 1;
1688 ei->change = &ei->change_page[ei->current_change_page];
1691 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1693 xx_change = *change; // copy change data into temporary buffer
1695 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1697 *change = xx_change;
1699 resetEventFlags(change);
1701 change->direct_action = 0;
1702 change->other_action = 0;
1704 change->pre_change_function = NULL;
1705 change->change_function = NULL;
1706 change->post_change_function = NULL;
1709 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1713 li = *level; // copy level data into temporary buffer
1714 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1715 *level = li; // copy temporary buffer back to level data
1717 setLevelInfoToDefaults_EM();
1718 setLevelInfoToDefaults_SP();
1719 setLevelInfoToDefaults_MM();
1721 level->native_em_level = &native_em_level;
1722 level->native_sp_level = &native_sp_level;
1723 level->native_mm_level = &native_mm_level;
1725 level->file_version = FILE_VERSION_ACTUAL;
1726 level->game_version = GAME_VERSION_ACTUAL;
1728 level->creation_date = getCurrentDate();
1730 level->encoding_16bit_field = TRUE;
1731 level->encoding_16bit_yamyam = TRUE;
1732 level->encoding_16bit_amoeba = TRUE;
1734 // clear level name and level author string buffers
1735 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1736 level->name[i] = '\0';
1737 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1738 level->author[i] = '\0';
1740 // set level name and level author to default values
1741 strcpy(level->name, NAMELESS_LEVEL_NAME);
1742 strcpy(level->author, ANONYMOUS_NAME);
1744 // set level playfield to playable default level with player and exit
1745 for (x = 0; x < MAX_LEV_FIELDX; x++)
1746 for (y = 0; y < MAX_LEV_FIELDY; y++)
1747 level->field[x][y] = EL_SAND;
1749 level->field[0][0] = EL_PLAYER_1;
1750 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1752 BorderElement = EL_STEELWALL;
1754 // detect custom elements when loading them
1755 level->file_has_custom_elements = FALSE;
1757 // set all bug compatibility flags to "false" => do not emulate this bug
1758 level->use_action_after_change_bug = FALSE;
1760 if (leveldir_current)
1762 // try to determine better author name than 'anonymous'
1763 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1765 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1766 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1770 switch (LEVELCLASS(leveldir_current))
1772 case LEVELCLASS_TUTORIAL:
1773 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1776 case LEVELCLASS_CONTRIB:
1777 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1778 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1781 case LEVELCLASS_PRIVATE:
1782 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1783 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1787 // keep default value
1794 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1796 static boolean clipboard_elements_initialized = FALSE;
1799 InitElementPropertiesStatic();
1801 li = *level; // copy level data into temporary buffer
1802 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1803 *level = li; // copy temporary buffer back to level data
1805 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1808 struct ElementInfo *ei = &element_info[element];
1810 // never initialize clipboard elements after the very first time
1811 // (to be able to use clipboard elements between several levels)
1812 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1815 if (IS_ENVELOPE(element))
1817 int envelope_nr = element - EL_ENVELOPE_1;
1819 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1821 level->envelope[envelope_nr] = xx_envelope;
1824 if (IS_CUSTOM_ELEMENT(element) ||
1825 IS_GROUP_ELEMENT(element) ||
1826 IS_INTERNAL_ELEMENT(element))
1828 xx_ei = *ei; // copy element data into temporary buffer
1830 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1835 setElementChangePages(ei, 1);
1836 setElementChangeInfoToDefaults(ei->change);
1838 if (IS_CUSTOM_ELEMENT(element) ||
1839 IS_GROUP_ELEMENT(element) ||
1840 IS_INTERNAL_ELEMENT(element))
1842 setElementDescriptionToDefault(ei);
1844 ei->modified_settings = FALSE;
1847 if (IS_CUSTOM_ELEMENT(element) ||
1848 IS_INTERNAL_ELEMENT(element))
1850 // internal values used in level editor
1852 ei->access_type = 0;
1853 ei->access_layer = 0;
1854 ei->access_protected = 0;
1855 ei->walk_to_action = 0;
1856 ei->smash_targets = 0;
1859 ei->can_explode_by_fire = FALSE;
1860 ei->can_explode_smashed = FALSE;
1861 ei->can_explode_impact = FALSE;
1863 ei->current_change_page = 0;
1866 if (IS_GROUP_ELEMENT(element) ||
1867 IS_INTERNAL_ELEMENT(element))
1869 struct ElementGroupInfo *group;
1871 // initialize memory for list of elements in group
1872 if (ei->group == NULL)
1873 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1877 xx_group = *group; // copy group data into temporary buffer
1879 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1885 clipboard_elements_initialized = TRUE;
1888 static void setLevelInfoToDefaults(struct LevelInfo *level,
1889 boolean level_info_only,
1890 boolean reset_file_status)
1892 setLevelInfoToDefaults_Level(level);
1894 if (!level_info_only)
1895 setLevelInfoToDefaults_Elements(level);
1897 if (reset_file_status)
1899 level->no_valid_file = FALSE;
1900 level->no_level_file = FALSE;
1903 level->changed = FALSE;
1906 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1908 level_file_info->nr = 0;
1909 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1910 level_file_info->packed = FALSE;
1912 setString(&level_file_info->basename, NULL);
1913 setString(&level_file_info->filename, NULL);
1916 int getMappedElement_SB(int, boolean);
1918 static void ActivateLevelTemplate(void)
1922 if (check_special_flags("load_xsb_to_ces"))
1924 // fill smaller playfields with padding "beyond border wall" elements
1925 if (level.fieldx < level_template.fieldx ||
1926 level.fieldy < level_template.fieldy)
1928 short field[level.fieldx][level.fieldy];
1929 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1930 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1931 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1932 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1934 // copy old playfield (which is smaller than the visible area)
1935 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1936 field[x][y] = level.field[x][y];
1938 // fill new, larger playfield with "beyond border wall" elements
1939 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1940 level.field[x][y] = getMappedElement_SB('_', TRUE);
1942 // copy the old playfield to the middle of the new playfield
1943 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1944 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1946 level.fieldx = new_fieldx;
1947 level.fieldy = new_fieldy;
1951 // Currently there is no special action needed to activate the template
1952 // data, because 'element_info' property settings overwrite the original
1953 // level data, while all other variables do not change.
1955 // Exception: 'from_level_template' elements in the original level playfield
1956 // are overwritten with the corresponding elements at the same position in
1957 // playfield from the level template.
1959 for (x = 0; x < level.fieldx; x++)
1960 for (y = 0; y < level.fieldy; y++)
1961 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1962 level.field[x][y] = level_template.field[x][y];
1964 if (check_special_flags("load_xsb_to_ces"))
1966 struct LevelInfo level_backup = level;
1968 // overwrite all individual level settings from template level settings
1969 level = level_template;
1971 // restore level file info
1972 level.file_info = level_backup.file_info;
1974 // restore playfield size
1975 level.fieldx = level_backup.fieldx;
1976 level.fieldy = level_backup.fieldy;
1978 // restore playfield content
1979 for (x = 0; x < level.fieldx; x++)
1980 for (y = 0; y < level.fieldy; y++)
1981 level.field[x][y] = level_backup.field[x][y];
1983 // restore name and author from individual level
1984 strcpy(level.name, level_backup.name);
1985 strcpy(level.author, level_backup.author);
1987 // restore flag "use_custom_template"
1988 level.use_custom_template = level_backup.use_custom_template;
1992 static char *getLevelFilenameFromBasename(char *basename)
1994 static char *filename = NULL;
1996 checked_free(filename);
1998 filename = getPath2(getCurrentLevelDir(), basename);
2003 static int getFileTypeFromBasename(char *basename)
2005 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2007 static char *filename = NULL;
2008 struct stat file_status;
2010 // ---------- try to determine file type from filename ----------
2012 // check for typical filename of a Supaplex level package file
2013 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2014 return LEVEL_FILE_TYPE_SP;
2016 // check for typical filename of a Diamond Caves II level package file
2017 if (strSuffixLower(basename, ".dc") ||
2018 strSuffixLower(basename, ".dc2"))
2019 return LEVEL_FILE_TYPE_DC;
2021 // check for typical filename of a Sokoban level package file
2022 if (strSuffixLower(basename, ".xsb") &&
2023 strchr(basename, '%') == NULL)
2024 return LEVEL_FILE_TYPE_SB;
2026 // ---------- try to determine file type from filesize ----------
2028 checked_free(filename);
2029 filename = getPath2(getCurrentLevelDir(), basename);
2031 if (stat(filename, &file_status) == 0)
2033 // check for typical filesize of a Supaplex level package file
2034 if (file_status.st_size == 170496)
2035 return LEVEL_FILE_TYPE_SP;
2038 return LEVEL_FILE_TYPE_UNKNOWN;
2041 static int getFileTypeFromMagicBytes(char *filename, int type)
2045 if ((file = openFile(filename, MODE_READ)))
2047 char chunk_name[CHUNK_ID_LEN + 1];
2049 getFileChunkBE(file, chunk_name, NULL);
2051 if (strEqual(chunk_name, "MMII") ||
2052 strEqual(chunk_name, "MIRR"))
2053 type = LEVEL_FILE_TYPE_MM;
2061 static boolean checkForPackageFromBasename(char *basename)
2063 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2064 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2066 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2069 static char *getSingleLevelBasenameExt(int nr, char *extension)
2071 static char basename[MAX_FILENAME_LEN];
2074 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2076 sprintf(basename, "%03d.%s", nr, extension);
2081 static char *getSingleLevelBasename(int nr)
2083 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2086 static char *getPackedLevelBasename(int type)
2088 static char basename[MAX_FILENAME_LEN];
2089 char *directory = getCurrentLevelDir();
2091 DirectoryEntry *dir_entry;
2093 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2095 if ((dir = openDirectory(directory)) == NULL)
2097 Warn("cannot read current level directory '%s'", directory);
2102 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2104 char *entry_basename = dir_entry->basename;
2105 int entry_type = getFileTypeFromBasename(entry_basename);
2107 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2109 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2112 strcpy(basename, entry_basename);
2119 closeDirectory(dir);
2124 static char *getSingleLevelFilename(int nr)
2126 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2129 #if ENABLE_UNUSED_CODE
2130 static char *getPackedLevelFilename(int type)
2132 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2136 char *getDefaultLevelFilename(int nr)
2138 return getSingleLevelFilename(nr);
2141 #if ENABLE_UNUSED_CODE
2142 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2146 lfi->packed = FALSE;
2148 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2149 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2153 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2154 int type, char *format, ...)
2156 static char basename[MAX_FILENAME_LEN];
2159 va_start(ap, format);
2160 vsprintf(basename, format, ap);
2164 lfi->packed = FALSE;
2166 setString(&lfi->basename, basename);
2167 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2170 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2176 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2177 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2180 static int getFiletypeFromID(char *filetype_id)
2182 char *filetype_id_lower;
2183 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2186 if (filetype_id == NULL)
2187 return LEVEL_FILE_TYPE_UNKNOWN;
2189 filetype_id_lower = getStringToLower(filetype_id);
2191 for (i = 0; filetype_id_list[i].id != NULL; i++)
2193 char *id_lower = getStringToLower(filetype_id_list[i].id);
2195 if (strEqual(filetype_id_lower, id_lower))
2196 filetype = filetype_id_list[i].filetype;
2200 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2204 free(filetype_id_lower);
2209 char *getLocalLevelTemplateFilename(void)
2211 return getDefaultLevelFilename(-1);
2214 char *getGlobalLevelTemplateFilename(void)
2216 // global variable "leveldir_current" must be modified in the loop below
2217 LevelDirTree *leveldir_current_last = leveldir_current;
2218 char *filename = NULL;
2220 // check for template level in path from current to topmost tree node
2222 while (leveldir_current != NULL)
2224 filename = getDefaultLevelFilename(-1);
2226 if (fileExists(filename))
2229 leveldir_current = leveldir_current->node_parent;
2232 // restore global variable "leveldir_current" modified in above loop
2233 leveldir_current = leveldir_current_last;
2238 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2242 // special case: level number is negative => check for level template file
2245 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2246 getSingleLevelBasename(-1));
2248 // replace local level template filename with global template filename
2249 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2251 // no fallback if template file not existing
2255 // special case: check for file name/pattern specified in "levelinfo.conf"
2256 if (leveldir_current->level_filename != NULL)
2258 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2260 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2261 leveldir_current->level_filename, nr);
2263 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2265 if (fileExists(lfi->filename))
2268 else if (leveldir_current->level_filetype != NULL)
2270 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2272 // check for specified native level file with standard file name
2273 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2274 "%03d.%s", nr, LEVELFILE_EXTENSION);
2275 if (fileExists(lfi->filename))
2279 // check for native Rocks'n'Diamonds level file
2280 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2281 "%03d.%s", nr, LEVELFILE_EXTENSION);
2282 if (fileExists(lfi->filename))
2285 // check for Emerald Mine level file (V1)
2286 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2287 'a' + (nr / 10) % 26, '0' + nr % 10);
2288 if (fileExists(lfi->filename))
2290 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2291 'A' + (nr / 10) % 26, '0' + nr % 10);
2292 if (fileExists(lfi->filename))
2295 // check for Emerald Mine level file (V2 to V5)
2296 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2297 if (fileExists(lfi->filename))
2300 // check for Emerald Mine level file (V6 / single mode)
2301 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2302 if (fileExists(lfi->filename))
2304 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2305 if (fileExists(lfi->filename))
2308 // check for Emerald Mine level file (V6 / teamwork mode)
2309 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2310 if (fileExists(lfi->filename))
2312 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2313 if (fileExists(lfi->filename))
2316 // check for various packed level file formats
2317 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2318 if (fileExists(lfi->filename))
2321 // no known level file found -- use default values (and fail later)
2322 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2323 "%03d.%s", nr, LEVELFILE_EXTENSION);
2326 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2328 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2329 lfi->type = getFileTypeFromBasename(lfi->basename);
2331 if (lfi->type == LEVEL_FILE_TYPE_RND)
2332 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2335 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2337 // always start with reliable default values
2338 setFileInfoToDefaults(level_file_info);
2340 level_file_info->nr = nr; // set requested level number
2342 determineLevelFileInfo_Filename(level_file_info);
2343 determineLevelFileInfo_Filetype(level_file_info);
2346 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2347 struct LevelFileInfo *lfi_to)
2349 lfi_to->nr = lfi_from->nr;
2350 lfi_to->type = lfi_from->type;
2351 lfi_to->packed = lfi_from->packed;
2353 setString(&lfi_to->basename, lfi_from->basename);
2354 setString(&lfi_to->filename, lfi_from->filename);
2357 // ----------------------------------------------------------------------------
2358 // functions for loading R'n'D level
2359 // ----------------------------------------------------------------------------
2361 int getMappedElement(int element)
2363 // remap some (historic, now obsolete) elements
2367 case EL_PLAYER_OBSOLETE:
2368 element = EL_PLAYER_1;
2371 case EL_KEY_OBSOLETE:
2375 case EL_EM_KEY_1_FILE_OBSOLETE:
2376 element = EL_EM_KEY_1;
2379 case EL_EM_KEY_2_FILE_OBSOLETE:
2380 element = EL_EM_KEY_2;
2383 case EL_EM_KEY_3_FILE_OBSOLETE:
2384 element = EL_EM_KEY_3;
2387 case EL_EM_KEY_4_FILE_OBSOLETE:
2388 element = EL_EM_KEY_4;
2391 case EL_ENVELOPE_OBSOLETE:
2392 element = EL_ENVELOPE_1;
2400 if (element >= NUM_FILE_ELEMENTS)
2402 Warn("invalid level element %d", element);
2404 element = EL_UNKNOWN;
2412 static int getMappedElementByVersion(int element, int game_version)
2414 // remap some elements due to certain game version
2416 if (game_version <= VERSION_IDENT(2,2,0,0))
2418 // map game font elements
2419 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2420 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2421 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2422 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2425 if (game_version < VERSION_IDENT(3,0,0,0))
2427 // map Supaplex gravity tube elements
2428 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2429 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2430 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2431 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2438 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2440 level->file_version = getFileVersion(file);
2441 level->game_version = getFileVersion(file);
2446 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2448 level->creation_date.year = getFile16BitBE(file);
2449 level->creation_date.month = getFile8Bit(file);
2450 level->creation_date.day = getFile8Bit(file);
2452 level->creation_date.src = DATE_SRC_LEVELFILE;
2457 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2459 int initial_player_stepsize;
2460 int initial_player_gravity;
2463 level->fieldx = getFile8Bit(file);
2464 level->fieldy = getFile8Bit(file);
2466 level->time = getFile16BitBE(file);
2467 level->gems_needed = getFile16BitBE(file);
2469 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2470 level->name[i] = getFile8Bit(file);
2471 level->name[MAX_LEVEL_NAME_LEN] = 0;
2473 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2474 level->score[i] = getFile8Bit(file);
2476 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2477 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2478 for (y = 0; y < 3; y++)
2479 for (x = 0; x < 3; x++)
2480 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2482 level->amoeba_speed = getFile8Bit(file);
2483 level->time_magic_wall = getFile8Bit(file);
2484 level->time_wheel = getFile8Bit(file);
2485 level->amoeba_content = getMappedElement(getFile8Bit(file));
2487 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2490 for (i = 0; i < MAX_PLAYERS; i++)
2491 level->initial_player_stepsize[i] = initial_player_stepsize;
2493 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2495 for (i = 0; i < MAX_PLAYERS; i++)
2496 level->initial_player_gravity[i] = initial_player_gravity;
2498 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2499 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2501 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2504 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2505 level->can_move_into_acid_bits = getFile32BitBE(file);
2506 level->dont_collide_with_bits = getFile8Bit(file);
2508 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2509 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2512 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2513 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515 level->game_engine_type = getFile8Bit(file);
2517 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2522 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2526 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2527 level->name[i] = getFile8Bit(file);
2528 level->name[MAX_LEVEL_NAME_LEN] = 0;
2533 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2537 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2538 level->author[i] = getFile8Bit(file);
2539 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2544 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2547 int chunk_size_expected = level->fieldx * level->fieldy;
2549 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2550 stored with 16-bit encoding (and should be twice as big then).
2551 Even worse, playfield data was stored 16-bit when only yamyam content
2552 contained 16-bit elements and vice versa. */
2554 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2555 chunk_size_expected *= 2;
2557 if (chunk_size_expected != chunk_size)
2559 ReadUnusedBytesFromFile(file, chunk_size);
2560 return chunk_size_expected;
2563 for (y = 0; y < level->fieldy; y++)
2564 for (x = 0; x < level->fieldx; x++)
2565 level->field[x][y] =
2566 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2571 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2574 int header_size = 4;
2575 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2576 int chunk_size_expected = header_size + content_size;
2578 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2579 stored with 16-bit encoding (and should be twice as big then).
2580 Even worse, playfield data was stored 16-bit when only yamyam content
2581 contained 16-bit elements and vice versa. */
2583 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2584 chunk_size_expected += content_size;
2586 if (chunk_size_expected != chunk_size)
2588 ReadUnusedBytesFromFile(file, chunk_size);
2589 return chunk_size_expected;
2593 level->num_yamyam_contents = getFile8Bit(file);
2597 // correct invalid number of content fields -- should never happen
2598 if (level->num_yamyam_contents < 1 ||
2599 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2600 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2602 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2603 for (y = 0; y < 3; y++)
2604 for (x = 0; x < 3; x++)
2605 level->yamyam_content[i].e[x][y] =
2606 getMappedElement(level->encoding_16bit_field ?
2607 getFile16BitBE(file) : getFile8Bit(file));
2611 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2616 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2618 element = getMappedElement(getFile16BitBE(file));
2619 num_contents = getFile8Bit(file);
2621 getFile8Bit(file); // content x size (unused)
2622 getFile8Bit(file); // content y size (unused)
2624 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2626 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2627 for (y = 0; y < 3; y++)
2628 for (x = 0; x < 3; x++)
2629 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2631 // correct invalid number of content fields -- should never happen
2632 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2633 num_contents = STD_ELEMENT_CONTENTS;
2635 if (element == EL_YAMYAM)
2637 level->num_yamyam_contents = num_contents;
2639 for (i = 0; i < num_contents; i++)
2640 for (y = 0; y < 3; y++)
2641 for (x = 0; x < 3; x++)
2642 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2644 else if (element == EL_BD_AMOEBA)
2646 level->amoeba_content = content_array[0][0][0];
2650 Warn("cannot load content for element '%d'", element);
2656 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2662 int chunk_size_expected;
2664 element = getMappedElement(getFile16BitBE(file));
2665 if (!IS_ENVELOPE(element))
2666 element = EL_ENVELOPE_1;
2668 envelope_nr = element - EL_ENVELOPE_1;
2670 envelope_len = getFile16BitBE(file);
2672 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2673 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2675 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2677 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2678 if (chunk_size_expected != chunk_size)
2680 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2681 return chunk_size_expected;
2684 for (i = 0; i < envelope_len; i++)
2685 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2690 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2692 int num_changed_custom_elements = getFile16BitBE(file);
2693 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2696 if (chunk_size_expected != chunk_size)
2698 ReadUnusedBytesFromFile(file, chunk_size - 2);
2699 return chunk_size_expected;
2702 for (i = 0; i < num_changed_custom_elements; i++)
2704 int element = getMappedElement(getFile16BitBE(file));
2705 int properties = getFile32BitBE(file);
2707 if (IS_CUSTOM_ELEMENT(element))
2708 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2710 Warn("invalid custom element number %d", element);
2712 // older game versions that wrote level files with CUS1 chunks used
2713 // different default push delay values (not yet stored in level file)
2714 element_info[element].push_delay_fixed = 2;
2715 element_info[element].push_delay_random = 8;
2718 level->file_has_custom_elements = TRUE;
2723 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2725 int num_changed_custom_elements = getFile16BitBE(file);
2726 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2729 if (chunk_size_expected != chunk_size)
2731 ReadUnusedBytesFromFile(file, chunk_size - 2);
2732 return chunk_size_expected;
2735 for (i = 0; i < num_changed_custom_elements; i++)
2737 int element = getMappedElement(getFile16BitBE(file));
2738 int custom_target_element = getMappedElement(getFile16BitBE(file));
2740 if (IS_CUSTOM_ELEMENT(element))
2741 element_info[element].change->target_element = custom_target_element;
2743 Warn("invalid custom element number %d", element);
2746 level->file_has_custom_elements = TRUE;
2751 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2753 int num_changed_custom_elements = getFile16BitBE(file);
2754 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2757 if (chunk_size_expected != chunk_size)
2759 ReadUnusedBytesFromFile(file, chunk_size - 2);
2760 return chunk_size_expected;
2763 for (i = 0; i < num_changed_custom_elements; i++)
2765 int element = getMappedElement(getFile16BitBE(file));
2766 struct ElementInfo *ei = &element_info[element];
2767 unsigned int event_bits;
2769 if (!IS_CUSTOM_ELEMENT(element))
2771 Warn("invalid custom element number %d", element);
2773 element = EL_INTERNAL_DUMMY;
2776 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2777 ei->description[j] = getFile8Bit(file);
2778 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2780 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2782 // some free bytes for future properties and padding
2783 ReadUnusedBytesFromFile(file, 7);
2785 ei->use_gfx_element = getFile8Bit(file);
2786 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2788 ei->collect_score_initial = getFile8Bit(file);
2789 ei->collect_count_initial = getFile8Bit(file);
2791 ei->push_delay_fixed = getFile16BitBE(file);
2792 ei->push_delay_random = getFile16BitBE(file);
2793 ei->move_delay_fixed = getFile16BitBE(file);
2794 ei->move_delay_random = getFile16BitBE(file);
2796 ei->move_pattern = getFile16BitBE(file);
2797 ei->move_direction_initial = getFile8Bit(file);
2798 ei->move_stepsize = getFile8Bit(file);
2800 for (y = 0; y < 3; y++)
2801 for (x = 0; x < 3; x++)
2802 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2804 event_bits = getFile32BitBE(file);
2805 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2806 if (event_bits & (1 << j))
2807 ei->change->has_event[j] = TRUE;
2809 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2811 ei->change->delay_fixed = getFile16BitBE(file);
2812 ei->change->delay_random = getFile16BitBE(file);
2813 ei->change->delay_frames = getFile16BitBE(file);
2815 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2817 ei->change->explode = getFile8Bit(file);
2818 ei->change->use_target_content = getFile8Bit(file);
2819 ei->change->only_if_complete = getFile8Bit(file);
2820 ei->change->use_random_replace = getFile8Bit(file);
2822 ei->change->random_percentage = getFile8Bit(file);
2823 ei->change->replace_when = getFile8Bit(file);
2825 for (y = 0; y < 3; y++)
2826 for (x = 0; x < 3; x++)
2827 ei->change->target_content.e[x][y] =
2828 getMappedElement(getFile16BitBE(file));
2830 ei->slippery_type = getFile8Bit(file);
2832 // some free bytes for future properties and padding
2833 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2835 // mark that this custom element has been modified
2836 ei->modified_settings = TRUE;
2839 level->file_has_custom_elements = TRUE;
2844 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2846 struct ElementInfo *ei;
2847 int chunk_size_expected;
2851 // ---------- custom element base property values (96 bytes) ----------------
2853 element = getMappedElement(getFile16BitBE(file));
2855 if (!IS_CUSTOM_ELEMENT(element))
2857 Warn("invalid custom element number %d", element);
2859 ReadUnusedBytesFromFile(file, chunk_size - 2);
2864 ei = &element_info[element];
2866 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2867 ei->description[i] = getFile8Bit(file);
2868 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2870 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2872 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2874 ei->num_change_pages = getFile8Bit(file);
2876 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2877 if (chunk_size_expected != chunk_size)
2879 ReadUnusedBytesFromFile(file, chunk_size - 43);
2880 return chunk_size_expected;
2883 ei->ce_value_fixed_initial = getFile16BitBE(file);
2884 ei->ce_value_random_initial = getFile16BitBE(file);
2885 ei->use_last_ce_value = getFile8Bit(file);
2887 ei->use_gfx_element = getFile8Bit(file);
2888 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2890 ei->collect_score_initial = getFile8Bit(file);
2891 ei->collect_count_initial = getFile8Bit(file);
2893 ei->drop_delay_fixed = getFile8Bit(file);
2894 ei->push_delay_fixed = getFile8Bit(file);
2895 ei->drop_delay_random = getFile8Bit(file);
2896 ei->push_delay_random = getFile8Bit(file);
2897 ei->move_delay_fixed = getFile16BitBE(file);
2898 ei->move_delay_random = getFile16BitBE(file);
2900 // bits 0 - 15 of "move_pattern" ...
2901 ei->move_pattern = getFile16BitBE(file);
2902 ei->move_direction_initial = getFile8Bit(file);
2903 ei->move_stepsize = getFile8Bit(file);
2905 ei->slippery_type = getFile8Bit(file);
2907 for (y = 0; y < 3; y++)
2908 for (x = 0; x < 3; x++)
2909 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2911 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2912 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2913 ei->move_leave_type = getFile8Bit(file);
2915 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2916 ei->move_pattern |= (getFile16BitBE(file) << 16);
2918 ei->access_direction = getFile8Bit(file);
2920 ei->explosion_delay = getFile8Bit(file);
2921 ei->ignition_delay = getFile8Bit(file);
2922 ei->explosion_type = getFile8Bit(file);
2924 // some free bytes for future custom property values and padding
2925 ReadUnusedBytesFromFile(file, 1);
2927 // ---------- change page property values (48 bytes) ------------------------
2929 setElementChangePages(ei, ei->num_change_pages);
2931 for (i = 0; i < ei->num_change_pages; i++)
2933 struct ElementChangeInfo *change = &ei->change_page[i];
2934 unsigned int event_bits;
2936 // always start with reliable default values
2937 setElementChangeInfoToDefaults(change);
2939 // bits 0 - 31 of "has_event[]" ...
2940 event_bits = getFile32BitBE(file);
2941 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2942 if (event_bits & (1 << j))
2943 change->has_event[j] = TRUE;
2945 change->target_element = getMappedElement(getFile16BitBE(file));
2947 change->delay_fixed = getFile16BitBE(file);
2948 change->delay_random = getFile16BitBE(file);
2949 change->delay_frames = getFile16BitBE(file);
2951 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2953 change->explode = getFile8Bit(file);
2954 change->use_target_content = getFile8Bit(file);
2955 change->only_if_complete = getFile8Bit(file);
2956 change->use_random_replace = getFile8Bit(file);
2958 change->random_percentage = getFile8Bit(file);
2959 change->replace_when = getFile8Bit(file);
2961 for (y = 0; y < 3; y++)
2962 for (x = 0; x < 3; x++)
2963 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2965 change->can_change = getFile8Bit(file);
2967 change->trigger_side = getFile8Bit(file);
2969 change->trigger_player = getFile8Bit(file);
2970 change->trigger_page = getFile8Bit(file);
2972 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2973 CH_PAGE_ANY : (1 << change->trigger_page));
2975 change->has_action = getFile8Bit(file);
2976 change->action_type = getFile8Bit(file);
2977 change->action_mode = getFile8Bit(file);
2978 change->action_arg = getFile16BitBE(file);
2980 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2981 event_bits = getFile8Bit(file);
2982 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2983 if (event_bits & (1 << (j - 32)))
2984 change->has_event[j] = TRUE;
2987 // mark this custom element as modified
2988 ei->modified_settings = TRUE;
2990 level->file_has_custom_elements = TRUE;
2995 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2997 struct ElementInfo *ei;
2998 struct ElementGroupInfo *group;
3002 element = getMappedElement(getFile16BitBE(file));
3004 if (!IS_GROUP_ELEMENT(element))
3006 Warn("invalid group element number %d", element);
3008 ReadUnusedBytesFromFile(file, chunk_size - 2);
3013 ei = &element_info[element];
3015 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3016 ei->description[i] = getFile8Bit(file);
3017 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3019 group = element_info[element].group;
3021 group->num_elements = getFile8Bit(file);
3023 ei->use_gfx_element = getFile8Bit(file);
3024 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3026 group->choice_mode = getFile8Bit(file);
3028 // some free bytes for future values and padding
3029 ReadUnusedBytesFromFile(file, 3);
3031 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3032 group->element[i] = getMappedElement(getFile16BitBE(file));
3034 // mark this group element as modified
3035 element_info[element].modified_settings = TRUE;
3037 level->file_has_custom_elements = TRUE;
3042 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3043 int element, int real_element)
3045 int micro_chunk_size = 0;
3046 int conf_type = getFile8Bit(file);
3047 int byte_mask = conf_type & CONF_MASK_BYTES;
3048 boolean element_found = FALSE;
3051 micro_chunk_size += 1;
3053 if (byte_mask == CONF_MASK_MULTI_BYTES)
3055 int num_bytes = getFile16BitBE(file);
3056 byte *buffer = checked_malloc(num_bytes);
3058 ReadBytesFromFile(file, buffer, num_bytes);
3060 for (i = 0; conf[i].data_type != -1; i++)
3062 if (conf[i].element == element &&
3063 conf[i].conf_type == conf_type)
3065 int data_type = conf[i].data_type;
3066 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3067 int max_num_entities = conf[i].max_num_entities;
3069 if (num_entities > max_num_entities)
3071 Warn("truncating number of entities for element %d from %d to %d",
3072 element, num_entities, max_num_entities);
3074 num_entities = max_num_entities;
3077 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3078 data_type == TYPE_CONTENT_LIST))
3080 // for element and content lists, zero entities are not allowed
3081 Warn("found empty list of entities for element %d", element);
3083 // do not set "num_entities" here to prevent reading behind buffer
3085 *(int *)(conf[i].num_entities) = 1; // at least one is required
3089 *(int *)(conf[i].num_entities) = num_entities;
3092 element_found = TRUE;
3094 if (data_type == TYPE_STRING)
3096 char *string = (char *)(conf[i].value);
3099 for (j = 0; j < max_num_entities; j++)
3100 string[j] = (j < num_entities ? buffer[j] : '\0');
3102 else if (data_type == TYPE_ELEMENT_LIST)
3104 int *element_array = (int *)(conf[i].value);
3107 for (j = 0; j < num_entities; j++)
3109 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3111 else if (data_type == TYPE_CONTENT_LIST)
3113 struct Content *content= (struct Content *)(conf[i].value);
3116 for (c = 0; c < num_entities; c++)
3117 for (y = 0; y < 3; y++)
3118 for (x = 0; x < 3; x++)
3119 content[c].e[x][y] =
3120 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3123 element_found = FALSE;
3129 checked_free(buffer);
3131 micro_chunk_size += 2 + num_bytes;
3133 else // constant size configuration data (1, 2 or 4 bytes)
3135 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3136 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3137 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3146 if (data_type == TYPE_ELEMENT)
3147 value = getMappedElement(value);
3149 if (data_type == TYPE_BOOLEAN)
3150 *(boolean *)(conf[i].value) = value;
3152 *(int *) (conf[i].value) = value;
3154 element_found = TRUE;
3160 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3165 char *error_conf_chunk_bytes =
3166 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3167 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3168 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3169 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3170 int error_element = real_element;
3172 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3173 error_conf_chunk_bytes, error_conf_chunk_token,
3174 error_element, EL_NAME(error_element));
3177 return micro_chunk_size;
3180 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3182 int real_chunk_size = 0;
3184 li = *level; // copy level data into temporary buffer
3186 while (!checkEndOfFile(file))
3188 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3190 if (real_chunk_size >= chunk_size)
3194 *level = li; // copy temporary buffer back to level data
3196 return real_chunk_size;
3199 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3201 int real_chunk_size = 0;
3203 li = *level; // copy level data into temporary buffer
3205 while (!checkEndOfFile(file))
3207 int element = getMappedElement(getFile16BitBE(file));
3209 real_chunk_size += 2;
3210 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3212 if (real_chunk_size >= chunk_size)
3216 *level = li; // copy temporary buffer back to level data
3218 return real_chunk_size;
3221 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3223 int real_chunk_size = 0;
3225 li = *level; // copy level data into temporary buffer
3227 while (!checkEndOfFile(file))
3229 int element = getMappedElement(getFile16BitBE(file));
3231 real_chunk_size += 2;
3232 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3234 if (real_chunk_size >= chunk_size)
3238 *level = li; // copy temporary buffer back to level data
3240 return real_chunk_size;
3243 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3245 int element = getMappedElement(getFile16BitBE(file));
3246 int envelope_nr = element - EL_ENVELOPE_1;
3247 int real_chunk_size = 2;
3249 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3251 while (!checkEndOfFile(file))
3253 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3256 if (real_chunk_size >= chunk_size)
3260 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3262 return real_chunk_size;
3265 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3267 int element = getMappedElement(getFile16BitBE(file));
3268 int real_chunk_size = 2;
3269 struct ElementInfo *ei = &element_info[element];
3272 xx_ei = *ei; // copy element data into temporary buffer
3274 xx_ei.num_change_pages = -1;
3276 while (!checkEndOfFile(file))
3278 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3280 if (xx_ei.num_change_pages != -1)
3283 if (real_chunk_size >= chunk_size)
3289 if (ei->num_change_pages == -1)
3291 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3294 ei->num_change_pages = 1;
3296 setElementChangePages(ei, 1);
3297 setElementChangeInfoToDefaults(ei->change);
3299 return real_chunk_size;
3302 // initialize number of change pages stored for this custom element
3303 setElementChangePages(ei, ei->num_change_pages);
3304 for (i = 0; i < ei->num_change_pages; i++)
3305 setElementChangeInfoToDefaults(&ei->change_page[i]);
3307 // start with reading properties for the first change page
3308 xx_current_change_page = 0;
3310 while (!checkEndOfFile(file))
3312 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3314 xx_change = *change; // copy change data into temporary buffer
3316 resetEventBits(); // reset bits; change page might have changed
3318 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3321 *change = xx_change;
3323 setEventFlagsFromEventBits(change);
3325 if (real_chunk_size >= chunk_size)
3329 level->file_has_custom_elements = TRUE;
3331 return real_chunk_size;
3334 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3336 int element = getMappedElement(getFile16BitBE(file));
3337 int real_chunk_size = 2;
3338 struct ElementInfo *ei = &element_info[element];
3339 struct ElementGroupInfo *group = ei->group;
3341 xx_ei = *ei; // copy element data into temporary buffer
3342 xx_group = *group; // copy group data into temporary buffer
3344 while (!checkEndOfFile(file))
3346 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3349 if (real_chunk_size >= chunk_size)
3356 level->file_has_custom_elements = TRUE;
3358 return real_chunk_size;
3361 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3362 struct LevelFileInfo *level_file_info,
3363 boolean level_info_only)
3365 char *filename = level_file_info->filename;
3366 char cookie[MAX_LINE_LEN];
3367 char chunk_name[CHUNK_ID_LEN + 1];
3371 if (!(file = openFile(filename, MODE_READ)))
3373 level->no_valid_file = TRUE;
3374 level->no_level_file = TRUE;
3376 if (level_info_only)
3379 Warn("cannot read level '%s' -- using empty level", filename);
3381 if (!setup.editor.use_template_for_new_levels)
3384 // if level file not found, try to initialize level data from template
3385 filename = getGlobalLevelTemplateFilename();
3387 if (!(file = openFile(filename, MODE_READ)))
3390 // default: for empty levels, use level template for custom elements
3391 level->use_custom_template = TRUE;
3393 level->no_valid_file = FALSE;
3396 getFileChunkBE(file, chunk_name, NULL);
3397 if (strEqual(chunk_name, "RND1"))
3399 getFile32BitBE(file); // not used
3401 getFileChunkBE(file, chunk_name, NULL);
3402 if (!strEqual(chunk_name, "CAVE"))
3404 level->no_valid_file = TRUE;
3406 Warn("unknown format of level file '%s'", filename);
3413 else // check for pre-2.0 file format with cookie string
3415 strcpy(cookie, chunk_name);
3416 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3418 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3419 cookie[strlen(cookie) - 1] = '\0';
3421 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3423 level->no_valid_file = TRUE;
3425 Warn("unknown format of level file '%s'", filename);
3432 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3434 level->no_valid_file = TRUE;
3436 Warn("unsupported version of level file '%s'", filename);
3443 // pre-2.0 level files have no game version, so use file version here
3444 level->game_version = level->file_version;
3447 if (level->file_version < FILE_VERSION_1_2)
3449 // level files from versions before 1.2.0 without chunk structure
3450 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3451 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3459 int (*loader)(File *, int, struct LevelInfo *);
3463 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3464 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3465 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3466 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3467 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3468 { "INFO", -1, LoadLevel_INFO },
3469 { "BODY", -1, LoadLevel_BODY },
3470 { "CONT", -1, LoadLevel_CONT },
3471 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3472 { "CNT3", -1, LoadLevel_CNT3 },
3473 { "CUS1", -1, LoadLevel_CUS1 },
3474 { "CUS2", -1, LoadLevel_CUS2 },
3475 { "CUS3", -1, LoadLevel_CUS3 },
3476 { "CUS4", -1, LoadLevel_CUS4 },
3477 { "GRP1", -1, LoadLevel_GRP1 },
3478 { "CONF", -1, LoadLevel_CONF },
3479 { "ELEM", -1, LoadLevel_ELEM },
3480 { "NOTE", -1, LoadLevel_NOTE },
3481 { "CUSX", -1, LoadLevel_CUSX },
3482 { "GRPX", -1, LoadLevel_GRPX },
3487 while (getFileChunkBE(file, chunk_name, &chunk_size))
3491 while (chunk_info[i].name != NULL &&
3492 !strEqual(chunk_name, chunk_info[i].name))
3495 if (chunk_info[i].name == NULL)
3497 Warn("unknown chunk '%s' in level file '%s'",
3498 chunk_name, filename);
3500 ReadUnusedBytesFromFile(file, chunk_size);
3502 else if (chunk_info[i].size != -1 &&
3503 chunk_info[i].size != chunk_size)
3505 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3506 chunk_size, chunk_name, filename);
3508 ReadUnusedBytesFromFile(file, chunk_size);
3512 // call function to load this level chunk
3513 int chunk_size_expected =
3514 (chunk_info[i].loader)(file, chunk_size, level);
3516 // the size of some chunks cannot be checked before reading other
3517 // chunks first (like "HEAD" and "BODY") that contain some header
3518 // information, so check them here
3519 if (chunk_size_expected != chunk_size)
3521 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3522 chunk_size, chunk_name, filename);
3532 // ----------------------------------------------------------------------------
3533 // functions for loading EM level
3534 // ----------------------------------------------------------------------------
3536 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3538 static int ball_xy[8][2] =
3549 struct LevelInfo_EM *level_em = level->native_em_level;
3550 struct CAVE *cav = level_em->cav;
3553 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3554 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3556 cav->time_seconds = level->time;
3557 cav->gems_needed = level->gems_needed;
3559 cav->emerald_score = level->score[SC_EMERALD];
3560 cav->diamond_score = level->score[SC_DIAMOND];
3561 cav->alien_score = level->score[SC_ROBOT];
3562 cav->tank_score = level->score[SC_SPACESHIP];
3563 cav->bug_score = level->score[SC_BUG];
3564 cav->eater_score = level->score[SC_YAMYAM];
3565 cav->nut_score = level->score[SC_NUT];
3566 cav->dynamite_score = level->score[SC_DYNAMITE];
3567 cav->key_score = level->score[SC_KEY];
3568 cav->exit_score = level->score[SC_TIME_BONUS];
3570 cav->num_eater_arrays = level->num_yamyam_contents;
3572 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3573 for (y = 0; y < 3; y++)
3574 for (x = 0; x < 3; x++)
3575 cav->eater_array[i][y * 3 + x] =
3576 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3578 cav->amoeba_time = level->amoeba_speed;
3579 cav->wonderwall_time = level->time_magic_wall;
3580 cav->wheel_time = level->time_wheel;
3582 cav->android_move_time = level->android_move_time;
3583 cav->android_clone_time = level->android_clone_time;
3584 cav->ball_random = level->ball_random;
3585 cav->ball_active = level->ball_active_initial;
3586 cav->ball_time = level->ball_time;
3587 cav->num_ball_arrays = level->num_ball_contents;
3589 cav->lenses_score = level->lenses_score;
3590 cav->magnify_score = level->magnify_score;
3591 cav->slurp_score = level->slurp_score;
3593 cav->lenses_time = level->lenses_time;
3594 cav->magnify_time = level->magnify_time;
3596 cav->wind_direction =
3597 map_direction_RND_to_EM(level->wind_direction_initial);
3599 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3600 for (j = 0; j < 8; j++)
3601 cav->ball_array[i][j] =
3602 map_element_RND_to_EM_cave(level->ball_content[i].
3603 e[ball_xy[j][0]][ball_xy[j][1]]);
3605 map_android_clone_elements_RND_to_EM(level);
3607 // first fill the complete playfield with the empty space element
3608 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3609 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3610 cav->cave[x][y] = Cblank;
3612 // then copy the real level contents from level file into the playfield
3613 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3615 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3617 if (level->field[x][y] == EL_AMOEBA_DEAD)
3618 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3620 cav->cave[x][y] = new_element;
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 cav->player_x[i] = -1;
3626 cav->player_y[i] = -1;
3629 // initialize player positions and delete players from the playfield
3630 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3632 if (ELEM_IS_PLAYER(level->field[x][y]))
3634 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3636 cav->player_x[player_nr] = x;
3637 cav->player_y[player_nr] = y;
3639 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3644 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3646 static int ball_xy[8][2] =
3657 struct LevelInfo_EM *level_em = level->native_em_level;
3658 struct CAVE *cav = level_em->cav;
3661 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3662 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3664 level->time = cav->time_seconds;
3665 level->gems_needed = cav->gems_needed;
3667 sprintf(level->name, "Level %d", level->file_info.nr);
3669 level->score[SC_EMERALD] = cav->emerald_score;
3670 level->score[SC_DIAMOND] = cav->diamond_score;
3671 level->score[SC_ROBOT] = cav->alien_score;
3672 level->score[SC_SPACESHIP] = cav->tank_score;
3673 level->score[SC_BUG] = cav->bug_score;
3674 level->score[SC_YAMYAM] = cav->eater_score;
3675 level->score[SC_NUT] = cav->nut_score;
3676 level->score[SC_DYNAMITE] = cav->dynamite_score;
3677 level->score[SC_KEY] = cav->key_score;
3678 level->score[SC_TIME_BONUS] = cav->exit_score;
3680 level->num_yamyam_contents = cav->num_eater_arrays;
3682 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 level->yamyam_content[i].e[x][y] =
3686 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3688 level->amoeba_speed = cav->amoeba_time;
3689 level->time_magic_wall = cav->wonderwall_time;
3690 level->time_wheel = cav->wheel_time;
3692 level->android_move_time = cav->android_move_time;
3693 level->android_clone_time = cav->android_clone_time;
3694 level->ball_random = cav->ball_random;
3695 level->ball_active_initial = cav->ball_active;
3696 level->ball_time = cav->ball_time;
3697 level->num_ball_contents = cav->num_ball_arrays;
3699 level->lenses_score = cav->lenses_score;
3700 level->magnify_score = cav->magnify_score;
3701 level->slurp_score = cav->slurp_score;
3703 level->lenses_time = cav->lenses_time;
3704 level->magnify_time = cav->magnify_time;
3706 level->wind_direction_initial =
3707 map_direction_EM_to_RND(cav->wind_direction);
3709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3710 for (j = 0; j < 8; j++)
3711 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3712 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3714 map_android_clone_elements_EM_to_RND(level);
3716 // convert the playfield (some elements need special treatment)
3717 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3719 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3721 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3722 new_element = EL_AMOEBA_DEAD;
3724 level->field[x][y] = new_element;
3727 for (i = 0; i < MAX_PLAYERS; i++)
3729 // in case of all players set to the same field, use the first player
3730 int nr = MAX_PLAYERS - i - 1;
3731 int jx = cav->player_x[nr];
3732 int jy = cav->player_y[nr];
3734 if (jx != -1 && jy != -1)
3735 level->field[jx][jy] = EL_PLAYER_1 + nr;
3738 // time score is counted for each 10 seconds left in Emerald Mine levels
3739 level->time_score_base = 10;
3743 // ----------------------------------------------------------------------------
3744 // functions for loading SP level
3745 // ----------------------------------------------------------------------------
3747 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3749 struct LevelInfo_SP *level_sp = level->native_sp_level;
3750 LevelInfoType *header = &level_sp->header;
3753 level_sp->width = level->fieldx;
3754 level_sp->height = level->fieldy;
3756 for (x = 0; x < level->fieldx; x++)
3757 for (y = 0; y < level->fieldy; y++)
3758 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3760 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3762 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3763 header->LevelTitle[i] = level->name[i];
3764 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3766 header->InfotronsNeeded = level->gems_needed;
3768 header->SpecialPortCount = 0;
3770 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3772 boolean gravity_port_found = FALSE;
3773 boolean gravity_port_valid = FALSE;
3774 int gravity_port_flag;
3775 int gravity_port_base_element;
3776 int element = level->field[x][y];
3778 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3779 element <= EL_SP_GRAVITY_ON_PORT_UP)
3781 gravity_port_found = TRUE;
3782 gravity_port_valid = TRUE;
3783 gravity_port_flag = 1;
3784 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3786 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3787 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3789 gravity_port_found = TRUE;
3790 gravity_port_valid = TRUE;
3791 gravity_port_flag = 0;
3792 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3794 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3795 element <= EL_SP_GRAVITY_PORT_UP)
3797 // change R'n'D style gravity inverting special port to normal port
3798 // (there are no gravity inverting ports in native Supaplex engine)
3800 gravity_port_found = TRUE;
3801 gravity_port_valid = FALSE;
3802 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3805 if (gravity_port_found)
3807 if (gravity_port_valid &&
3808 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3810 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3812 port->PortLocation = (y * level->fieldx + x) * 2;
3813 port->Gravity = gravity_port_flag;
3815 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3817 header->SpecialPortCount++;
3821 // change special gravity port to normal port
3823 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3826 level_sp->playfield[x][y] = element - EL_SP_START;
3831 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3833 struct LevelInfo_SP *level_sp = level->native_sp_level;
3834 LevelInfoType *header = &level_sp->header;
3835 boolean num_invalid_elements = 0;
3838 level->fieldx = level_sp->width;
3839 level->fieldy = level_sp->height;
3841 for (x = 0; x < level->fieldx; x++)
3843 for (y = 0; y < level->fieldy; y++)
3845 int element_old = level_sp->playfield[x][y];
3846 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3848 if (element_new == EL_UNKNOWN)
3850 num_invalid_elements++;
3852 Debug("level:native:SP", "invalid element %d at position %d, %d",
3856 level->field[x][y] = element_new;
3860 if (num_invalid_elements > 0)
3861 Warn("found %d invalid elements%s", num_invalid_elements,
3862 (!options.debug ? " (use '--debug' for more details)" : ""));
3864 for (i = 0; i < MAX_PLAYERS; i++)
3865 level->initial_player_gravity[i] =
3866 (header->InitialGravity == 1 ? TRUE : FALSE);
3868 // skip leading spaces
3869 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3870 if (header->LevelTitle[i] != ' ')
3874 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3875 level->name[j] = header->LevelTitle[i];
3876 level->name[j] = '\0';
3878 // cut trailing spaces
3880 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3881 level->name[j - 1] = '\0';
3883 level->gems_needed = header->InfotronsNeeded;
3885 for (i = 0; i < header->SpecialPortCount; i++)
3887 SpecialPortType *port = &header->SpecialPort[i];
3888 int port_location = port->PortLocation;
3889 int gravity = port->Gravity;
3890 int port_x, port_y, port_element;
3892 port_x = (port_location / 2) % level->fieldx;
3893 port_y = (port_location / 2) / level->fieldx;
3895 if (port_x < 0 || port_x >= level->fieldx ||
3896 port_y < 0 || port_y >= level->fieldy)
3898 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3903 port_element = level->field[port_x][port_y];
3905 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3906 port_element > EL_SP_GRAVITY_PORT_UP)
3908 Warn("no special port at position (%d, %d)", port_x, port_y);
3913 // change previous (wrong) gravity inverting special port to either
3914 // gravity enabling special port or gravity disabling special port
3915 level->field[port_x][port_y] +=
3916 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3917 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3920 // change special gravity ports without database entries to normal ports
3921 for (x = 0; x < level->fieldx; x++)
3922 for (y = 0; y < level->fieldy; y++)
3923 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3924 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3925 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3927 level->time = 0; // no time limit
3928 level->amoeba_speed = 0;
3929 level->time_magic_wall = 0;
3930 level->time_wheel = 0;
3931 level->amoeba_content = EL_EMPTY;
3934 // original Supaplex does not use score values -- use default values
3936 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3937 level->score[i] = 0;
3940 // there are no yamyams in supaplex levels
3941 for (i = 0; i < level->num_yamyam_contents; i++)
3942 for (x = 0; x < 3; x++)
3943 for (y = 0; y < 3; y++)
3944 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3947 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3949 struct LevelInfo_SP *level_sp = level->native_sp_level;
3950 struct DemoInfo_SP *demo = &level_sp->demo;
3953 // always start with reliable default values
3954 demo->is_available = FALSE;
3957 if (TAPE_IS_EMPTY(tape))
3960 demo->level_nr = tape.level_nr; // (currently not used)
3962 level_sp->header.DemoRandomSeed = tape.random_seed;
3966 for (i = 0; i < tape.length; i++)
3968 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3969 int demo_repeat = tape.pos[i].delay;
3970 int demo_entries = (demo_repeat + 15) / 16;
3972 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3974 Warn("tape truncated: size exceeds maximum SP demo size %d",
3980 for (j = 0; j < demo_repeat / 16; j++)
3981 demo->data[demo->length++] = 0xf0 | demo_action;
3983 if (demo_repeat % 16)
3984 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3987 demo->is_available = TRUE;
3990 static void setTapeInfoToDefaults(void);
3992 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3994 struct LevelInfo_SP *level_sp = level->native_sp_level;
3995 struct DemoInfo_SP *demo = &level_sp->demo;
3996 char *filename = level->file_info.filename;
3999 // always start with reliable default values
4000 setTapeInfoToDefaults();
4002 if (!demo->is_available)
4005 tape.level_nr = demo->level_nr; // (currently not used)
4006 tape.random_seed = level_sp->header.DemoRandomSeed;
4008 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4011 tape.pos[tape.counter].delay = 0;
4013 for (i = 0; i < demo->length; i++)
4015 int demo_action = demo->data[i] & 0x0f;
4016 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4017 int tape_action = map_key_SP_to_RND(demo_action);
4018 int tape_repeat = demo_repeat + 1;
4019 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4020 boolean success = 0;
4023 for (j = 0; j < tape_repeat; j++)
4024 success = TapeAddAction(action);
4028 Warn("SP demo truncated: size exceeds maximum tape size %d",
4035 TapeHaltRecording();
4039 // ----------------------------------------------------------------------------
4040 // functions for loading MM level
4041 // ----------------------------------------------------------------------------
4043 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4045 struct LevelInfo_MM *level_mm = level->native_mm_level;
4048 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4049 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4051 level_mm->time = level->time;
4052 level_mm->kettles_needed = level->gems_needed;
4053 level_mm->auto_count_kettles = level->auto_count_gems;
4055 level_mm->laser_red = level->mm_laser_red;
4056 level_mm->laser_green = level->mm_laser_green;
4057 level_mm->laser_blue = level->mm_laser_blue;
4059 strcpy(level_mm->name, level->name);
4060 strcpy(level_mm->author, level->author);
4062 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4063 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4064 level_mm->score[SC_KEY] = level->score[SC_KEY];
4065 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4066 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4068 level_mm->amoeba_speed = level->amoeba_speed;
4069 level_mm->time_fuse = level->mm_time_fuse;
4070 level_mm->time_bomb = level->mm_time_bomb;
4071 level_mm->time_ball = level->mm_time_ball;
4072 level_mm->time_block = level->mm_time_block;
4074 for (x = 0; x < level->fieldx; x++)
4075 for (y = 0; y < level->fieldy; y++)
4077 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4080 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4082 struct LevelInfo_MM *level_mm = level->native_mm_level;
4085 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4086 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4088 level->time = level_mm->time;
4089 level->gems_needed = level_mm->kettles_needed;
4090 level->auto_count_gems = level_mm->auto_count_kettles;
4092 level->mm_laser_red = level_mm->laser_red;
4093 level->mm_laser_green = level_mm->laser_green;
4094 level->mm_laser_blue = level_mm->laser_blue;
4096 strcpy(level->name, level_mm->name);
4098 // only overwrite author from 'levelinfo.conf' if author defined in level
4099 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4100 strcpy(level->author, level_mm->author);
4102 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4103 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4104 level->score[SC_KEY] = level_mm->score[SC_KEY];
4105 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4106 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4108 level->amoeba_speed = level_mm->amoeba_speed;
4109 level->mm_time_fuse = level_mm->time_fuse;
4110 level->mm_time_bomb = level_mm->time_bomb;
4111 level->mm_time_ball = level_mm->time_ball;
4112 level->mm_time_block = level_mm->time_block;
4114 for (x = 0; x < level->fieldx; x++)
4115 for (y = 0; y < level->fieldy; y++)
4116 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4120 // ----------------------------------------------------------------------------
4121 // functions for loading DC level
4122 // ----------------------------------------------------------------------------
4124 #define DC_LEVEL_HEADER_SIZE 344
4126 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4129 static int last_data_encoded;
4133 int diff_hi, diff_lo;
4134 int data_hi, data_lo;
4135 unsigned short data_decoded;
4139 last_data_encoded = 0;
4146 diff = data_encoded - last_data_encoded;
4147 diff_hi = diff & ~0xff;
4148 diff_lo = diff & 0xff;
4152 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4153 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4154 data_hi = data_hi & 0xff00;
4156 data_decoded = data_hi | data_lo;
4158 last_data_encoded = data_encoded;
4160 offset1 = (offset1 + 1) % 31;
4161 offset2 = offset2 & 0xff;
4163 return data_decoded;
4166 static int getMappedElement_DC(int element)
4174 // 0x0117 - 0x036e: (?)
4177 // 0x042d - 0x0684: (?)
4193 element = EL_CRYSTAL;
4196 case 0x0e77: // quicksand (boulder)
4197 element = EL_QUICKSAND_FAST_FULL;
4200 case 0x0e99: // slow quicksand (boulder)
4201 element = EL_QUICKSAND_FULL;
4205 element = EL_EM_EXIT_OPEN;
4209 element = EL_EM_EXIT_CLOSED;
4213 element = EL_EM_STEEL_EXIT_OPEN;
4217 element = EL_EM_STEEL_EXIT_CLOSED;
4220 case 0x0f4f: // dynamite (lit 1)
4221 element = EL_EM_DYNAMITE_ACTIVE;
4224 case 0x0f57: // dynamite (lit 2)
4225 element = EL_EM_DYNAMITE_ACTIVE;
4228 case 0x0f5f: // dynamite (lit 3)
4229 element = EL_EM_DYNAMITE_ACTIVE;
4232 case 0x0f67: // dynamite (lit 4)
4233 element = EL_EM_DYNAMITE_ACTIVE;
4240 element = EL_AMOEBA_WET;
4244 element = EL_AMOEBA_DROP;
4248 element = EL_DC_MAGIC_WALL;
4252 element = EL_SPACESHIP_UP;
4256 element = EL_SPACESHIP_DOWN;
4260 element = EL_SPACESHIP_LEFT;
4264 element = EL_SPACESHIP_RIGHT;
4268 element = EL_BUG_UP;
4272 element = EL_BUG_DOWN;
4276 element = EL_BUG_LEFT;
4280 element = EL_BUG_RIGHT;
4284 element = EL_MOLE_UP;
4288 element = EL_MOLE_DOWN;
4292 element = EL_MOLE_LEFT;
4296 element = EL_MOLE_RIGHT;
4304 element = EL_YAMYAM_UP;
4308 element = EL_SWITCHGATE_OPEN;
4312 element = EL_SWITCHGATE_CLOSED;
4316 element = EL_DC_SWITCHGATE_SWITCH_UP;
4320 element = EL_TIMEGATE_CLOSED;
4323 case 0x144c: // conveyor belt switch (green)
4324 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4327 case 0x144f: // conveyor belt switch (red)
4328 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4331 case 0x1452: // conveyor belt switch (blue)
4332 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4336 element = EL_CONVEYOR_BELT_3_MIDDLE;
4340 element = EL_CONVEYOR_BELT_3_LEFT;
4344 element = EL_CONVEYOR_BELT_3_RIGHT;
4348 element = EL_CONVEYOR_BELT_1_MIDDLE;
4352 element = EL_CONVEYOR_BELT_1_LEFT;
4356 element = EL_CONVEYOR_BELT_1_RIGHT;
4360 element = EL_CONVEYOR_BELT_4_MIDDLE;
4364 element = EL_CONVEYOR_BELT_4_LEFT;
4368 element = EL_CONVEYOR_BELT_4_RIGHT;
4372 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4376 element = EL_EXPANDABLE_WALL_VERTICAL;
4380 element = EL_EXPANDABLE_WALL_ANY;
4383 case 0x14ce: // growing steel wall (left/right)
4384 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4387 case 0x14df: // growing steel wall (up/down)
4388 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4391 case 0x14e8: // growing steel wall (up/down/left/right)
4392 element = EL_EXPANDABLE_STEELWALL_ANY;
4396 element = EL_SHIELD_DEADLY;
4400 element = EL_EXTRA_TIME;
4408 element = EL_EMPTY_SPACE;
4411 case 0x1578: // quicksand (empty)
4412 element = EL_QUICKSAND_FAST_EMPTY;
4415 case 0x1579: // slow quicksand (empty)
4416 element = EL_QUICKSAND_EMPTY;
4426 element = EL_EM_DYNAMITE;
4429 case 0x15a1: // key (red)
4430 element = EL_EM_KEY_1;
4433 case 0x15a2: // key (yellow)
4434 element = EL_EM_KEY_2;
4437 case 0x15a3: // key (blue)
4438 element = EL_EM_KEY_4;
4441 case 0x15a4: // key (green)
4442 element = EL_EM_KEY_3;
4445 case 0x15a5: // key (white)
4446 element = EL_DC_KEY_WHITE;
4450 element = EL_WALL_SLIPPERY;
4457 case 0x15a8: // wall (not round)
4461 case 0x15a9: // (blue)
4462 element = EL_CHAR_A;
4465 case 0x15aa: // (blue)
4466 element = EL_CHAR_B;
4469 case 0x15ab: // (blue)
4470 element = EL_CHAR_C;
4473 case 0x15ac: // (blue)
4474 element = EL_CHAR_D;
4477 case 0x15ad: // (blue)
4478 element = EL_CHAR_E;
4481 case 0x15ae: // (blue)
4482 element = EL_CHAR_F;
4485 case 0x15af: // (blue)
4486 element = EL_CHAR_G;
4489 case 0x15b0: // (blue)
4490 element = EL_CHAR_H;
4493 case 0x15b1: // (blue)
4494 element = EL_CHAR_I;
4497 case 0x15b2: // (blue)
4498 element = EL_CHAR_J;
4501 case 0x15b3: // (blue)
4502 element = EL_CHAR_K;
4505 case 0x15b4: // (blue)
4506 element = EL_CHAR_L;
4509 case 0x15b5: // (blue)
4510 element = EL_CHAR_M;
4513 case 0x15b6: // (blue)
4514 element = EL_CHAR_N;
4517 case 0x15b7: // (blue)
4518 element = EL_CHAR_O;
4521 case 0x15b8: // (blue)
4522 element = EL_CHAR_P;
4525 case 0x15b9: // (blue)
4526 element = EL_CHAR_Q;
4529 case 0x15ba: // (blue)
4530 element = EL_CHAR_R;
4533 case 0x15bb: // (blue)
4534 element = EL_CHAR_S;
4537 case 0x15bc: // (blue)
4538 element = EL_CHAR_T;
4541 case 0x15bd: // (blue)
4542 element = EL_CHAR_U;
4545 case 0x15be: // (blue)
4546 element = EL_CHAR_V;
4549 case 0x15bf: // (blue)
4550 element = EL_CHAR_W;
4553 case 0x15c0: // (blue)
4554 element = EL_CHAR_X;
4557 case 0x15c1: // (blue)
4558 element = EL_CHAR_Y;
4561 case 0x15c2: // (blue)
4562 element = EL_CHAR_Z;
4565 case 0x15c3: // (blue)
4566 element = EL_CHAR_AUMLAUT;
4569 case 0x15c4: // (blue)
4570 element = EL_CHAR_OUMLAUT;
4573 case 0x15c5: // (blue)
4574 element = EL_CHAR_UUMLAUT;
4577 case 0x15c6: // (blue)
4578 element = EL_CHAR_0;
4581 case 0x15c7: // (blue)
4582 element = EL_CHAR_1;
4585 case 0x15c8: // (blue)
4586 element = EL_CHAR_2;
4589 case 0x15c9: // (blue)
4590 element = EL_CHAR_3;
4593 case 0x15ca: // (blue)
4594 element = EL_CHAR_4;
4597 case 0x15cb: // (blue)
4598 element = EL_CHAR_5;
4601 case 0x15cc: // (blue)
4602 element = EL_CHAR_6;
4605 case 0x15cd: // (blue)
4606 element = EL_CHAR_7;
4609 case 0x15ce: // (blue)
4610 element = EL_CHAR_8;
4613 case 0x15cf: // (blue)
4614 element = EL_CHAR_9;
4617 case 0x15d0: // (blue)
4618 element = EL_CHAR_PERIOD;
4621 case 0x15d1: // (blue)
4622 element = EL_CHAR_EXCLAM;
4625 case 0x15d2: // (blue)
4626 element = EL_CHAR_COLON;
4629 case 0x15d3: // (blue)
4630 element = EL_CHAR_LESS;
4633 case 0x15d4: // (blue)
4634 element = EL_CHAR_GREATER;
4637 case 0x15d5: // (blue)
4638 element = EL_CHAR_QUESTION;
4641 case 0x15d6: // (blue)
4642 element = EL_CHAR_COPYRIGHT;
4645 case 0x15d7: // (blue)
4646 element = EL_CHAR_UP;
4649 case 0x15d8: // (blue)
4650 element = EL_CHAR_DOWN;
4653 case 0x15d9: // (blue)
4654 element = EL_CHAR_BUTTON;
4657 case 0x15da: // (blue)
4658 element = EL_CHAR_PLUS;
4661 case 0x15db: // (blue)
4662 element = EL_CHAR_MINUS;
4665 case 0x15dc: // (blue)
4666 element = EL_CHAR_APOSTROPHE;
4669 case 0x15dd: // (blue)
4670 element = EL_CHAR_PARENLEFT;
4673 case 0x15de: // (blue)
4674 element = EL_CHAR_PARENRIGHT;
4677 case 0x15df: // (green)
4678 element = EL_CHAR_A;
4681 case 0x15e0: // (green)
4682 element = EL_CHAR_B;
4685 case 0x15e1: // (green)
4686 element = EL_CHAR_C;
4689 case 0x15e2: // (green)
4690 element = EL_CHAR_D;
4693 case 0x15e3: // (green)
4694 element = EL_CHAR_E;
4697 case 0x15e4: // (green)
4698 element = EL_CHAR_F;
4701 case 0x15e5: // (green)
4702 element = EL_CHAR_G;
4705 case 0x15e6: // (green)
4706 element = EL_CHAR_H;
4709 case 0x15e7: // (green)
4710 element = EL_CHAR_I;
4713 case 0x15e8: // (green)
4714 element = EL_CHAR_J;
4717 case 0x15e9: // (green)
4718 element = EL_CHAR_K;
4721 case 0x15ea: // (green)
4722 element = EL_CHAR_L;
4725 case 0x15eb: // (green)
4726 element = EL_CHAR_M;
4729 case 0x15ec: // (green)
4730 element = EL_CHAR_N;
4733 case 0x15ed: // (green)
4734 element = EL_CHAR_O;
4737 case 0x15ee: // (green)
4738 element = EL_CHAR_P;
4741 case 0x15ef: // (green)
4742 element = EL_CHAR_Q;
4745 case 0x15f0: // (green)
4746 element = EL_CHAR_R;
4749 case 0x15f1: // (green)
4750 element = EL_CHAR_S;
4753 case 0x15f2: // (green)
4754 element = EL_CHAR_T;
4757 case 0x15f3: // (green)
4758 element = EL_CHAR_U;
4761 case 0x15f4: // (green)
4762 element = EL_CHAR_V;
4765 case 0x15f5: // (green)
4766 element = EL_CHAR_W;
4769 case 0x15f6: // (green)
4770 element = EL_CHAR_X;
4773 case 0x15f7: // (green)
4774 element = EL_CHAR_Y;
4777 case 0x15f8: // (green)
4778 element = EL_CHAR_Z;
4781 case 0x15f9: // (green)
4782 element = EL_CHAR_AUMLAUT;
4785 case 0x15fa: // (green)
4786 element = EL_CHAR_OUMLAUT;
4789 case 0x15fb: // (green)
4790 element = EL_CHAR_UUMLAUT;
4793 case 0x15fc: // (green)
4794 element = EL_CHAR_0;
4797 case 0x15fd: // (green)
4798 element = EL_CHAR_1;
4801 case 0x15fe: // (green)
4802 element = EL_CHAR_2;
4805 case 0x15ff: // (green)
4806 element = EL_CHAR_3;
4809 case 0x1600: // (green)
4810 element = EL_CHAR_4;
4813 case 0x1601: // (green)
4814 element = EL_CHAR_5;
4817 case 0x1602: // (green)
4818 element = EL_CHAR_6;
4821 case 0x1603: // (green)
4822 element = EL_CHAR_7;
4825 case 0x1604: // (green)
4826 element = EL_CHAR_8;
4829 case 0x1605: // (green)
4830 element = EL_CHAR_9;
4833 case 0x1606: // (green)
4834 element = EL_CHAR_PERIOD;
4837 case 0x1607: // (green)
4838 element = EL_CHAR_EXCLAM;
4841 case 0x1608: // (green)
4842 element = EL_CHAR_COLON;
4845 case 0x1609: // (green)
4846 element = EL_CHAR_LESS;
4849 case 0x160a: // (green)
4850 element = EL_CHAR_GREATER;
4853 case 0x160b: // (green)
4854 element = EL_CHAR_QUESTION;
4857 case 0x160c: // (green)
4858 element = EL_CHAR_COPYRIGHT;
4861 case 0x160d: // (green)
4862 element = EL_CHAR_UP;
4865 case 0x160e: // (green)
4866 element = EL_CHAR_DOWN;
4869 case 0x160f: // (green)
4870 element = EL_CHAR_BUTTON;
4873 case 0x1610: // (green)
4874 element = EL_CHAR_PLUS;
4877 case 0x1611: // (green)
4878 element = EL_CHAR_MINUS;
4881 case 0x1612: // (green)
4882 element = EL_CHAR_APOSTROPHE;
4885 case 0x1613: // (green)
4886 element = EL_CHAR_PARENLEFT;
4889 case 0x1614: // (green)
4890 element = EL_CHAR_PARENRIGHT;
4893 case 0x1615: // (blue steel)
4894 element = EL_STEEL_CHAR_A;
4897 case 0x1616: // (blue steel)
4898 element = EL_STEEL_CHAR_B;
4901 case 0x1617: // (blue steel)
4902 element = EL_STEEL_CHAR_C;
4905 case 0x1618: // (blue steel)
4906 element = EL_STEEL_CHAR_D;
4909 case 0x1619: // (blue steel)
4910 element = EL_STEEL_CHAR_E;
4913 case 0x161a: // (blue steel)
4914 element = EL_STEEL_CHAR_F;
4917 case 0x161b: // (blue steel)
4918 element = EL_STEEL_CHAR_G;
4921 case 0x161c: // (blue steel)
4922 element = EL_STEEL_CHAR_H;
4925 case 0x161d: // (blue steel)
4926 element = EL_STEEL_CHAR_I;
4929 case 0x161e: // (blue steel)
4930 element = EL_STEEL_CHAR_J;
4933 case 0x161f: // (blue steel)
4934 element = EL_STEEL_CHAR_K;
4937 case 0x1620: // (blue steel)
4938 element = EL_STEEL_CHAR_L;
4941 case 0x1621: // (blue steel)
4942 element = EL_STEEL_CHAR_M;
4945 case 0x1622: // (blue steel)
4946 element = EL_STEEL_CHAR_N;
4949 case 0x1623: // (blue steel)
4950 element = EL_STEEL_CHAR_O;
4953 case 0x1624: // (blue steel)
4954 element = EL_STEEL_CHAR_P;
4957 case 0x1625: // (blue steel)
4958 element = EL_STEEL_CHAR_Q;
4961 case 0x1626: // (blue steel)
4962 element = EL_STEEL_CHAR_R;
4965 case 0x1627: // (blue steel)
4966 element = EL_STEEL_CHAR_S;
4969 case 0x1628: // (blue steel)
4970 element = EL_STEEL_CHAR_T;
4973 case 0x1629: // (blue steel)
4974 element = EL_STEEL_CHAR_U;
4977 case 0x162a: // (blue steel)
4978 element = EL_STEEL_CHAR_V;
4981 case 0x162b: // (blue steel)
4982 element = EL_STEEL_CHAR_W;
4985 case 0x162c: // (blue steel)
4986 element = EL_STEEL_CHAR_X;
4989 case 0x162d: // (blue steel)
4990 element = EL_STEEL_CHAR_Y;
4993 case 0x162e: // (blue steel)
4994 element = EL_STEEL_CHAR_Z;
4997 case 0x162f: // (blue steel)
4998 element = EL_STEEL_CHAR_AUMLAUT;
5001 case 0x1630: // (blue steel)
5002 element = EL_STEEL_CHAR_OUMLAUT;
5005 case 0x1631: // (blue steel)
5006 element = EL_STEEL_CHAR_UUMLAUT;
5009 case 0x1632: // (blue steel)
5010 element = EL_STEEL_CHAR_0;
5013 case 0x1633: // (blue steel)
5014 element = EL_STEEL_CHAR_1;
5017 case 0x1634: // (blue steel)
5018 element = EL_STEEL_CHAR_2;
5021 case 0x1635: // (blue steel)
5022 element = EL_STEEL_CHAR_3;
5025 case 0x1636: // (blue steel)
5026 element = EL_STEEL_CHAR_4;
5029 case 0x1637: // (blue steel)
5030 element = EL_STEEL_CHAR_5;
5033 case 0x1638: // (blue steel)
5034 element = EL_STEEL_CHAR_6;
5037 case 0x1639: // (blue steel)
5038 element = EL_STEEL_CHAR_7;
5041 case 0x163a: // (blue steel)
5042 element = EL_STEEL_CHAR_8;
5045 case 0x163b: // (blue steel)
5046 element = EL_STEEL_CHAR_9;
5049 case 0x163c: // (blue steel)
5050 element = EL_STEEL_CHAR_PERIOD;
5053 case 0x163d: // (blue steel)
5054 element = EL_STEEL_CHAR_EXCLAM;
5057 case 0x163e: // (blue steel)
5058 element = EL_STEEL_CHAR_COLON;
5061 case 0x163f: // (blue steel)
5062 element = EL_STEEL_CHAR_LESS;
5065 case 0x1640: // (blue steel)
5066 element = EL_STEEL_CHAR_GREATER;
5069 case 0x1641: // (blue steel)
5070 element = EL_STEEL_CHAR_QUESTION;
5073 case 0x1642: // (blue steel)
5074 element = EL_STEEL_CHAR_COPYRIGHT;
5077 case 0x1643: // (blue steel)
5078 element = EL_STEEL_CHAR_UP;
5081 case 0x1644: // (blue steel)
5082 element = EL_STEEL_CHAR_DOWN;
5085 case 0x1645: // (blue steel)
5086 element = EL_STEEL_CHAR_BUTTON;
5089 case 0x1646: // (blue steel)
5090 element = EL_STEEL_CHAR_PLUS;
5093 case 0x1647: // (blue steel)
5094 element = EL_STEEL_CHAR_MINUS;
5097 case 0x1648: // (blue steel)
5098 element = EL_STEEL_CHAR_APOSTROPHE;
5101 case 0x1649: // (blue steel)
5102 element = EL_STEEL_CHAR_PARENLEFT;
5105 case 0x164a: // (blue steel)
5106 element = EL_STEEL_CHAR_PARENRIGHT;
5109 case 0x164b: // (green steel)
5110 element = EL_STEEL_CHAR_A;
5113 case 0x164c: // (green steel)
5114 element = EL_STEEL_CHAR_B;
5117 case 0x164d: // (green steel)
5118 element = EL_STEEL_CHAR_C;
5121 case 0x164e: // (green steel)
5122 element = EL_STEEL_CHAR_D;
5125 case 0x164f: // (green steel)
5126 element = EL_STEEL_CHAR_E;
5129 case 0x1650: // (green steel)
5130 element = EL_STEEL_CHAR_F;
5133 case 0x1651: // (green steel)
5134 element = EL_STEEL_CHAR_G;
5137 case 0x1652: // (green steel)
5138 element = EL_STEEL_CHAR_H;
5141 case 0x1653: // (green steel)
5142 element = EL_STEEL_CHAR_I;
5145 case 0x1654: // (green steel)
5146 element = EL_STEEL_CHAR_J;
5149 case 0x1655: // (green steel)
5150 element = EL_STEEL_CHAR_K;
5153 case 0x1656: // (green steel)
5154 element = EL_STEEL_CHAR_L;
5157 case 0x1657: // (green steel)
5158 element = EL_STEEL_CHAR_M;
5161 case 0x1658: // (green steel)
5162 element = EL_STEEL_CHAR_N;
5165 case 0x1659: // (green steel)
5166 element = EL_STEEL_CHAR_O;
5169 case 0x165a: // (green steel)
5170 element = EL_STEEL_CHAR_P;
5173 case 0x165b: // (green steel)
5174 element = EL_STEEL_CHAR_Q;
5177 case 0x165c: // (green steel)
5178 element = EL_STEEL_CHAR_R;
5181 case 0x165d: // (green steel)
5182 element = EL_STEEL_CHAR_S;
5185 case 0x165e: // (green steel)
5186 element = EL_STEEL_CHAR_T;
5189 case 0x165f: // (green steel)
5190 element = EL_STEEL_CHAR_U;
5193 case 0x1660: // (green steel)
5194 element = EL_STEEL_CHAR_V;
5197 case 0x1661: // (green steel)
5198 element = EL_STEEL_CHAR_W;
5201 case 0x1662: // (green steel)
5202 element = EL_STEEL_CHAR_X;
5205 case 0x1663: // (green steel)
5206 element = EL_STEEL_CHAR_Y;
5209 case 0x1664: // (green steel)
5210 element = EL_STEEL_CHAR_Z;
5213 case 0x1665: // (green steel)
5214 element = EL_STEEL_CHAR_AUMLAUT;
5217 case 0x1666: // (green steel)
5218 element = EL_STEEL_CHAR_OUMLAUT;
5221 case 0x1667: // (green steel)
5222 element = EL_STEEL_CHAR_UUMLAUT;
5225 case 0x1668: // (green steel)
5226 element = EL_STEEL_CHAR_0;
5229 case 0x1669: // (green steel)
5230 element = EL_STEEL_CHAR_1;
5233 case 0x166a: // (green steel)
5234 element = EL_STEEL_CHAR_2;
5237 case 0x166b: // (green steel)
5238 element = EL_STEEL_CHAR_3;
5241 case 0x166c: // (green steel)
5242 element = EL_STEEL_CHAR_4;
5245 case 0x166d: // (green steel)
5246 element = EL_STEEL_CHAR_5;
5249 case 0x166e: // (green steel)
5250 element = EL_STEEL_CHAR_6;
5253 case 0x166f: // (green steel)
5254 element = EL_STEEL_CHAR_7;
5257 case 0x1670: // (green steel)
5258 element = EL_STEEL_CHAR_8;
5261 case 0x1671: // (green steel)
5262 element = EL_STEEL_CHAR_9;
5265 case 0x1672: // (green steel)
5266 element = EL_STEEL_CHAR_PERIOD;
5269 case 0x1673: // (green steel)
5270 element = EL_STEEL_CHAR_EXCLAM;
5273 case 0x1674: // (green steel)
5274 element = EL_STEEL_CHAR_COLON;
5277 case 0x1675: // (green steel)
5278 element = EL_STEEL_CHAR_LESS;
5281 case 0x1676: // (green steel)
5282 element = EL_STEEL_CHAR_GREATER;
5285 case 0x1677: // (green steel)
5286 element = EL_STEEL_CHAR_QUESTION;
5289 case 0x1678: // (green steel)
5290 element = EL_STEEL_CHAR_COPYRIGHT;
5293 case 0x1679: // (green steel)
5294 element = EL_STEEL_CHAR_UP;
5297 case 0x167a: // (green steel)
5298 element = EL_STEEL_CHAR_DOWN;
5301 case 0x167b: // (green steel)
5302 element = EL_STEEL_CHAR_BUTTON;
5305 case 0x167c: // (green steel)
5306 element = EL_STEEL_CHAR_PLUS;
5309 case 0x167d: // (green steel)
5310 element = EL_STEEL_CHAR_MINUS;
5313 case 0x167e: // (green steel)
5314 element = EL_STEEL_CHAR_APOSTROPHE;
5317 case 0x167f: // (green steel)
5318 element = EL_STEEL_CHAR_PARENLEFT;
5321 case 0x1680: // (green steel)
5322 element = EL_STEEL_CHAR_PARENRIGHT;
5325 case 0x1681: // gate (red)
5326 element = EL_EM_GATE_1;
5329 case 0x1682: // secret gate (red)
5330 element = EL_EM_GATE_1_GRAY;
5333 case 0x1683: // gate (yellow)
5334 element = EL_EM_GATE_2;
5337 case 0x1684: // secret gate (yellow)
5338 element = EL_EM_GATE_2_GRAY;
5341 case 0x1685: // gate (blue)
5342 element = EL_EM_GATE_4;
5345 case 0x1686: // secret gate (blue)
5346 element = EL_EM_GATE_4_GRAY;
5349 case 0x1687: // gate (green)
5350 element = EL_EM_GATE_3;
5353 case 0x1688: // secret gate (green)
5354 element = EL_EM_GATE_3_GRAY;
5357 case 0x1689: // gate (white)
5358 element = EL_DC_GATE_WHITE;
5361 case 0x168a: // secret gate (white)
5362 element = EL_DC_GATE_WHITE_GRAY;
5365 case 0x168b: // secret gate (no key)
5366 element = EL_DC_GATE_FAKE_GRAY;
5370 element = EL_ROBOT_WHEEL;
5374 element = EL_DC_TIMEGATE_SWITCH;
5378 element = EL_ACID_POOL_BOTTOM;
5382 element = EL_ACID_POOL_TOPLEFT;
5386 element = EL_ACID_POOL_TOPRIGHT;
5390 element = EL_ACID_POOL_BOTTOMLEFT;
5394 element = EL_ACID_POOL_BOTTOMRIGHT;
5398 element = EL_STEELWALL;
5402 element = EL_STEELWALL_SLIPPERY;
5405 case 0x1695: // steel wall (not round)
5406 element = EL_STEELWALL;
5409 case 0x1696: // steel wall (left)
5410 element = EL_DC_STEELWALL_1_LEFT;
5413 case 0x1697: // steel wall (bottom)
5414 element = EL_DC_STEELWALL_1_BOTTOM;
5417 case 0x1698: // steel wall (right)
5418 element = EL_DC_STEELWALL_1_RIGHT;
5421 case 0x1699: // steel wall (top)
5422 element = EL_DC_STEELWALL_1_TOP;
5425 case 0x169a: // steel wall (left/bottom)
5426 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5429 case 0x169b: // steel wall (right/bottom)
5430 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5433 case 0x169c: // steel wall (right/top)
5434 element = EL_DC_STEELWALL_1_TOPRIGHT;
5437 case 0x169d: // steel wall (left/top)
5438 element = EL_DC_STEELWALL_1_TOPLEFT;
5441 case 0x169e: // steel wall (right/bottom small)
5442 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5445 case 0x169f: // steel wall (left/bottom small)
5446 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5449 case 0x16a0: // steel wall (right/top small)
5450 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5453 case 0x16a1: // steel wall (left/top small)
5454 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5457 case 0x16a2: // steel wall (left/right)
5458 element = EL_DC_STEELWALL_1_VERTICAL;
5461 case 0x16a3: // steel wall (top/bottom)
5462 element = EL_DC_STEELWALL_1_HORIZONTAL;
5465 case 0x16a4: // steel wall 2 (left end)
5466 element = EL_DC_STEELWALL_2_LEFT;
5469 case 0x16a5: // steel wall 2 (right end)
5470 element = EL_DC_STEELWALL_2_RIGHT;
5473 case 0x16a6: // steel wall 2 (top end)
5474 element = EL_DC_STEELWALL_2_TOP;
5477 case 0x16a7: // steel wall 2 (bottom end)
5478 element = EL_DC_STEELWALL_2_BOTTOM;
5481 case 0x16a8: // steel wall 2 (left/right)
5482 element = EL_DC_STEELWALL_2_HORIZONTAL;
5485 case 0x16a9: // steel wall 2 (up/down)
5486 element = EL_DC_STEELWALL_2_VERTICAL;
5489 case 0x16aa: // steel wall 2 (mid)
5490 element = EL_DC_STEELWALL_2_MIDDLE;
5494 element = EL_SIGN_EXCLAMATION;
5498 element = EL_SIGN_RADIOACTIVITY;
5502 element = EL_SIGN_STOP;
5506 element = EL_SIGN_WHEELCHAIR;
5510 element = EL_SIGN_PARKING;
5514 element = EL_SIGN_NO_ENTRY;
5518 element = EL_SIGN_HEART;
5522 element = EL_SIGN_GIVE_WAY;
5526 element = EL_SIGN_ENTRY_FORBIDDEN;
5530 element = EL_SIGN_EMERGENCY_EXIT;
5534 element = EL_SIGN_YIN_YANG;
5538 element = EL_WALL_EMERALD;
5542 element = EL_WALL_DIAMOND;
5546 element = EL_WALL_PEARL;
5550 element = EL_WALL_CRYSTAL;
5554 element = EL_INVISIBLE_WALL;
5558 element = EL_INVISIBLE_STEELWALL;
5562 // EL_INVISIBLE_SAND
5565 element = EL_LIGHT_SWITCH;
5569 element = EL_ENVELOPE_1;
5573 if (element >= 0x0117 && element <= 0x036e) // (?)
5574 element = EL_DIAMOND;
5575 else if (element >= 0x042d && element <= 0x0684) // (?)
5576 element = EL_EMERALD;
5577 else if (element >= 0x157c && element <= 0x158b)
5579 else if (element >= 0x1590 && element <= 0x159f)
5580 element = EL_DC_LANDMINE;
5581 else if (element >= 0x16bc && element <= 0x16cb)
5582 element = EL_INVISIBLE_SAND;
5585 Warn("unknown Diamond Caves element 0x%04x", element);
5587 element = EL_UNKNOWN;
5592 return getMappedElement(element);
5595 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5598 byte header[DC_LEVEL_HEADER_SIZE];
5600 int envelope_header_pos = 62;
5601 int envelope_content_pos = 94;
5602 int level_name_pos = 251;
5603 int level_author_pos = 292;
5604 int envelope_header_len;
5605 int envelope_content_len;
5607 int level_author_len;
5609 int num_yamyam_contents;
5612 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5614 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5616 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5618 header[i * 2 + 0] = header_word >> 8;
5619 header[i * 2 + 1] = header_word & 0xff;
5622 // read some values from level header to check level decoding integrity
5623 fieldx = header[6] | (header[7] << 8);
5624 fieldy = header[8] | (header[9] << 8);
5625 num_yamyam_contents = header[60] | (header[61] << 8);
5627 // do some simple sanity checks to ensure that level was correctly decoded
5628 if (fieldx < 1 || fieldx > 256 ||
5629 fieldy < 1 || fieldy > 256 ||
5630 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5632 level->no_valid_file = TRUE;
5634 Warn("cannot decode level from stream -- using empty level");
5639 // maximum envelope header size is 31 bytes
5640 envelope_header_len = header[envelope_header_pos];
5641 // maximum envelope content size is 110 (156?) bytes
5642 envelope_content_len = header[envelope_content_pos];
5644 // maximum level title size is 40 bytes
5645 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5646 // maximum level author size is 30 (51?) bytes
5647 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5651 for (i = 0; i < envelope_header_len; i++)
5652 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5653 level->envelope[0].text[envelope_size++] =
5654 header[envelope_header_pos + 1 + i];
5656 if (envelope_header_len > 0 && envelope_content_len > 0)
5658 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5659 level->envelope[0].text[envelope_size++] = '\n';
5660 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5661 level->envelope[0].text[envelope_size++] = '\n';
5664 for (i = 0; i < envelope_content_len; i++)
5665 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5666 level->envelope[0].text[envelope_size++] =
5667 header[envelope_content_pos + 1 + i];
5669 level->envelope[0].text[envelope_size] = '\0';
5671 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5672 level->envelope[0].ysize = 10;
5673 level->envelope[0].autowrap = TRUE;
5674 level->envelope[0].centered = TRUE;
5676 for (i = 0; i < level_name_len; i++)
5677 level->name[i] = header[level_name_pos + 1 + i];
5678 level->name[level_name_len] = '\0';
5680 for (i = 0; i < level_author_len; i++)
5681 level->author[i] = header[level_author_pos + 1 + i];
5682 level->author[level_author_len] = '\0';
5684 num_yamyam_contents = header[60] | (header[61] << 8);
5685 level->num_yamyam_contents =
5686 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5688 for (i = 0; i < num_yamyam_contents; i++)
5690 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5692 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5693 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5695 if (i < MAX_ELEMENT_CONTENTS)
5696 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5700 fieldx = header[6] | (header[7] << 8);
5701 fieldy = header[8] | (header[9] << 8);
5702 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5703 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5705 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5707 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5708 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5710 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5711 level->field[x][y] = getMappedElement_DC(element_dc);
5714 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5715 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5716 level->field[x][y] = EL_PLAYER_1;
5718 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5719 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5720 level->field[x][y] = EL_PLAYER_2;
5722 level->gems_needed = header[18] | (header[19] << 8);
5724 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5725 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5726 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5727 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5728 level->score[SC_NUT] = header[28] | (header[29] << 8);
5729 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5730 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5731 level->score[SC_BUG] = header[34] | (header[35] << 8);
5732 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5733 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5734 level->score[SC_KEY] = header[40] | (header[41] << 8);
5735 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5737 level->time = header[44] | (header[45] << 8);
5739 level->amoeba_speed = header[46] | (header[47] << 8);
5740 level->time_light = header[48] | (header[49] << 8);
5741 level->time_timegate = header[50] | (header[51] << 8);
5742 level->time_wheel = header[52] | (header[53] << 8);
5743 level->time_magic_wall = header[54] | (header[55] << 8);
5744 level->extra_time = header[56] | (header[57] << 8);
5745 level->shield_normal_time = header[58] | (header[59] << 8);
5747 // shield and extra time elements do not have a score
5748 level->score[SC_SHIELD] = 0;
5749 level->extra_time_score = 0;
5751 // set time for normal and deadly shields to the same value
5752 level->shield_deadly_time = level->shield_normal_time;
5754 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5755 // can slip down from flat walls, like normal walls and steel walls
5756 level->em_slippery_gems = TRUE;
5758 // time score is counted for each 10 seconds left in Diamond Caves levels
5759 level->time_score_base = 10;
5762 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5763 struct LevelFileInfo *level_file_info,
5764 boolean level_info_only)
5766 char *filename = level_file_info->filename;
5768 int num_magic_bytes = 8;
5769 char magic_bytes[num_magic_bytes + 1];
5770 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5772 if (!(file = openFile(filename, MODE_READ)))
5774 level->no_valid_file = TRUE;
5776 if (!level_info_only)
5777 Warn("cannot read level '%s' -- using empty level", filename);
5782 // fseek(file, 0x0000, SEEK_SET);
5784 if (level_file_info->packed)
5786 // read "magic bytes" from start of file
5787 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5788 magic_bytes[0] = '\0';
5790 // check "magic bytes" for correct file format
5791 if (!strPrefix(magic_bytes, "DC2"))
5793 level->no_valid_file = TRUE;
5795 Warn("unknown DC level file '%s' -- using empty level", filename);
5800 if (strPrefix(magic_bytes, "DC2Win95") ||
5801 strPrefix(magic_bytes, "DC2Win98"))
5803 int position_first_level = 0x00fa;
5804 int extra_bytes = 4;
5807 // advance file stream to first level inside the level package
5808 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5810 // each block of level data is followed by block of non-level data
5811 num_levels_to_skip *= 2;
5813 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5814 while (num_levels_to_skip >= 0)
5816 // advance file stream to next level inside the level package
5817 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5819 level->no_valid_file = TRUE;
5821 Warn("cannot fseek in file '%s' -- using empty level", filename);
5826 // skip apparently unused extra bytes following each level
5827 ReadUnusedBytesFromFile(file, extra_bytes);
5829 // read size of next level in level package
5830 skip_bytes = getFile32BitLE(file);
5832 num_levels_to_skip--;
5837 level->no_valid_file = TRUE;
5839 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5845 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5851 // ----------------------------------------------------------------------------
5852 // functions for loading SB level
5853 // ----------------------------------------------------------------------------
5855 int getMappedElement_SB(int element_ascii, boolean use_ces)
5863 sb_element_mapping[] =
5865 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5866 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5867 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5868 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5869 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5870 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5871 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5872 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5879 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5880 if (element_ascii == sb_element_mapping[i].ascii)
5881 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5883 return EL_UNDEFINED;
5886 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5887 struct LevelFileInfo *level_file_info,
5888 boolean level_info_only)
5890 char *filename = level_file_info->filename;
5891 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5892 char last_comment[MAX_LINE_LEN];
5893 char level_name[MAX_LINE_LEN];
5896 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5897 boolean read_continued_line = FALSE;
5898 boolean reading_playfield = FALSE;
5899 boolean got_valid_playfield_line = FALSE;
5900 boolean invalid_playfield_char = FALSE;
5901 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5902 int file_level_nr = 0;
5904 int x = 0, y = 0; // initialized to make compilers happy
5906 last_comment[0] = '\0';
5907 level_name[0] = '\0';
5909 if (!(file = openFile(filename, MODE_READ)))
5911 level->no_valid_file = TRUE;
5913 if (!level_info_only)
5914 Warn("cannot read level '%s' -- using empty level", filename);
5919 while (!checkEndOfFile(file))
5921 // level successfully read, but next level may follow here
5922 if (!got_valid_playfield_line && reading_playfield)
5924 // read playfield from single level file -- skip remaining file
5925 if (!level_file_info->packed)
5928 if (file_level_nr >= num_levels_to_skip)
5933 last_comment[0] = '\0';
5934 level_name[0] = '\0';
5936 reading_playfield = FALSE;
5939 got_valid_playfield_line = FALSE;
5941 // read next line of input file
5942 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5945 // check if line was completely read and is terminated by line break
5946 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5949 // cut trailing line break (this can be newline and/or carriage return)
5950 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5951 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5954 // copy raw input line for later use (mainly debugging output)
5955 strcpy(line_raw, line);
5957 if (read_continued_line)
5959 // append new line to existing line, if there is enough space
5960 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5961 strcat(previous_line, line_ptr);
5963 strcpy(line, previous_line); // copy storage buffer to line
5965 read_continued_line = FALSE;
5968 // if the last character is '\', continue at next line
5969 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5971 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5972 strcpy(previous_line, line); // copy line to storage buffer
5974 read_continued_line = TRUE;
5980 if (line[0] == '\0')
5983 // extract comment text from comment line
5986 for (line_ptr = line; *line_ptr; line_ptr++)
5987 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5990 strcpy(last_comment, line_ptr);
5995 // extract level title text from line containing level title
5996 if (line[0] == '\'')
5998 strcpy(level_name, &line[1]);
6000 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6001 level_name[strlen(level_name) - 1] = '\0';
6006 // skip lines containing only spaces (or empty lines)
6007 for (line_ptr = line; *line_ptr; line_ptr++)
6008 if (*line_ptr != ' ')
6010 if (*line_ptr == '\0')
6013 // at this point, we have found a line containing part of a playfield
6015 got_valid_playfield_line = TRUE;
6017 if (!reading_playfield)
6019 reading_playfield = TRUE;
6020 invalid_playfield_char = FALSE;
6022 for (x = 0; x < MAX_LEV_FIELDX; x++)
6023 for (y = 0; y < MAX_LEV_FIELDY; y++)
6024 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6029 // start with topmost tile row
6033 // skip playfield line if larger row than allowed
6034 if (y >= MAX_LEV_FIELDY)
6037 // start with leftmost tile column
6040 // read playfield elements from line
6041 for (line_ptr = line; *line_ptr; line_ptr++)
6043 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6045 // stop parsing playfield line if larger column than allowed
6046 if (x >= MAX_LEV_FIELDX)
6049 if (mapped_sb_element == EL_UNDEFINED)
6051 invalid_playfield_char = TRUE;
6056 level->field[x][y] = mapped_sb_element;
6058 // continue with next tile column
6061 level->fieldx = MAX(x, level->fieldx);
6064 if (invalid_playfield_char)
6066 // if first playfield line, treat invalid lines as comment lines
6068 reading_playfield = FALSE;
6073 // continue with next tile row
6081 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6082 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6084 if (!reading_playfield)
6086 level->no_valid_file = TRUE;
6088 Warn("cannot read level '%s' -- using empty level", filename);
6093 if (*level_name != '\0')
6095 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6096 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6098 else if (*last_comment != '\0')
6100 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6101 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6105 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6108 // set all empty fields beyond the border walls to invisible steel wall
6109 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6111 if ((x == 0 || x == level->fieldx - 1 ||
6112 y == 0 || y == level->fieldy - 1) &&
6113 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6114 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6115 level->field, level->fieldx, level->fieldy);
6118 // set special level settings for Sokoban levels
6121 level->use_step_counter = TRUE;
6123 if (load_xsb_to_ces)
6125 // special global settings can now be set in level template
6127 level->use_custom_template = TRUE;
6132 // -------------------------------------------------------------------------
6133 // functions for handling native levels
6134 // -------------------------------------------------------------------------
6136 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6137 struct LevelFileInfo *level_file_info,
6138 boolean level_info_only)
6140 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6141 level->no_valid_file = TRUE;
6144 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6145 struct LevelFileInfo *level_file_info,
6146 boolean level_info_only)
6150 // determine position of requested level inside level package
6151 if (level_file_info->packed)
6152 pos = level_file_info->nr - leveldir_current->first_level;
6154 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6155 level->no_valid_file = TRUE;
6158 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6159 struct LevelFileInfo *level_file_info,
6160 boolean level_info_only)
6162 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6163 level->no_valid_file = TRUE;
6166 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6168 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6169 CopyNativeLevel_RND_to_EM(level);
6170 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6171 CopyNativeLevel_RND_to_SP(level);
6172 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6173 CopyNativeLevel_RND_to_MM(level);
6176 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6178 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6179 CopyNativeLevel_EM_to_RND(level);
6180 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6181 CopyNativeLevel_SP_to_RND(level);
6182 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6183 CopyNativeLevel_MM_to_RND(level);
6186 void SaveNativeLevel(struct LevelInfo *level)
6188 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6190 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6191 char *filename = getLevelFilenameFromBasename(basename);
6193 CopyNativeLevel_RND_to_SP(level);
6194 CopyNativeTape_RND_to_SP(level);
6196 SaveNativeLevel_SP(filename);
6201 // ----------------------------------------------------------------------------
6202 // functions for loading generic level
6203 // ----------------------------------------------------------------------------
6205 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6206 struct LevelFileInfo *level_file_info,
6207 boolean level_info_only)
6209 // always start with reliable default values
6210 setLevelInfoToDefaults(level, level_info_only, TRUE);
6212 switch (level_file_info->type)
6214 case LEVEL_FILE_TYPE_RND:
6215 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6218 case LEVEL_FILE_TYPE_EM:
6219 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6220 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6223 case LEVEL_FILE_TYPE_SP:
6224 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6225 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6228 case LEVEL_FILE_TYPE_MM:
6229 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6230 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6233 case LEVEL_FILE_TYPE_DC:
6234 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6237 case LEVEL_FILE_TYPE_SB:
6238 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6242 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6246 // if level file is invalid, restore level structure to default values
6247 if (level->no_valid_file)
6248 setLevelInfoToDefaults(level, level_info_only, FALSE);
6250 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6251 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6253 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6254 CopyNativeLevel_Native_to_RND(level);
6257 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6259 static struct LevelFileInfo level_file_info;
6261 // always start with reliable default values
6262 setFileInfoToDefaults(&level_file_info);
6264 level_file_info.nr = 0; // unknown level number
6265 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6267 setString(&level_file_info.filename, filename);
6269 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6272 static void LoadLevel_InitVersion(struct LevelInfo *level)
6276 if (leveldir_current == NULL) // only when dumping level
6279 // all engine modifications also valid for levels which use latest engine
6280 if (level->game_version < VERSION_IDENT(3,2,0,5))
6282 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6283 level->time_score_base = 10;
6286 if (leveldir_current->latest_engine)
6288 // ---------- use latest game engine --------------------------------------
6290 /* For all levels which are forced to use the latest game engine version
6291 (normally all but user contributed, private and undefined levels), set
6292 the game engine version to the actual version; this allows for actual
6293 corrections in the game engine to take effect for existing, converted
6294 levels (from "classic" or other existing games) to make the emulation
6295 of the corresponding game more accurate, while (hopefully) not breaking
6296 existing levels created from other players. */
6298 level->game_version = GAME_VERSION_ACTUAL;
6300 /* Set special EM style gems behaviour: EM style gems slip down from
6301 normal, steel and growing wall. As this is a more fundamental change,
6302 it seems better to set the default behaviour to "off" (as it is more
6303 natural) and make it configurable in the level editor (as a property
6304 of gem style elements). Already existing converted levels (neither
6305 private nor contributed levels) are changed to the new behaviour. */
6307 if (level->file_version < FILE_VERSION_2_0)
6308 level->em_slippery_gems = TRUE;
6313 // ---------- use game engine the level was created with --------------------
6315 /* For all levels which are not forced to use the latest game engine
6316 version (normally user contributed, private and undefined levels),
6317 use the version of the game engine the levels were created for.
6319 Since 2.0.1, the game engine version is now directly stored
6320 in the level file (chunk "VERS"), so there is no need anymore
6321 to set the game version from the file version (except for old,
6322 pre-2.0 levels, where the game version is still taken from the
6323 file format version used to store the level -- see above). */
6325 // player was faster than enemies in 1.0.0 and before
6326 if (level->file_version == FILE_VERSION_1_0)
6327 for (i = 0; i < MAX_PLAYERS; i++)
6328 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6330 // default behaviour for EM style gems was "slippery" only in 2.0.1
6331 if (level->game_version == VERSION_IDENT(2,0,1,0))
6332 level->em_slippery_gems = TRUE;
6334 // springs could be pushed over pits before (pre-release version) 2.2.0
6335 if (level->game_version < VERSION_IDENT(2,2,0,0))
6336 level->use_spring_bug = TRUE;
6338 if (level->game_version < VERSION_IDENT(3,2,0,5))
6340 // time orb caused limited time in endless time levels before 3.2.0-5
6341 level->use_time_orb_bug = TRUE;
6343 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6344 level->block_snap_field = FALSE;
6346 // extra time score was same value as time left score before 3.2.0-5
6347 level->extra_time_score = level->score[SC_TIME_BONUS];
6350 if (level->game_version < VERSION_IDENT(3,2,0,7))
6352 // default behaviour for snapping was "not continuous" before 3.2.0-7
6353 level->continuous_snapping = FALSE;
6356 // only few elements were able to actively move into acid before 3.1.0
6357 // trigger settings did not exist before 3.1.0; set to default "any"
6358 if (level->game_version < VERSION_IDENT(3,1,0,0))
6360 // correct "can move into acid" settings (all zero in old levels)
6362 level->can_move_into_acid_bits = 0; // nothing can move into acid
6363 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6365 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6366 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6367 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6368 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6370 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6371 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6373 // correct trigger settings (stored as zero == "none" in old levels)
6375 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6377 int element = EL_CUSTOM_START + i;
6378 struct ElementInfo *ei = &element_info[element];
6380 for (j = 0; j < ei->num_change_pages; j++)
6382 struct ElementChangeInfo *change = &ei->change_page[j];
6384 change->trigger_player = CH_PLAYER_ANY;
6385 change->trigger_page = CH_PAGE_ANY;
6390 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6392 int element = EL_CUSTOM_256;
6393 struct ElementInfo *ei = &element_info[element];
6394 struct ElementChangeInfo *change = &ei->change_page[0];
6396 /* This is needed to fix a problem that was caused by a bugfix in function
6397 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6398 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6399 not replace walkable elements, but instead just placed the player on it,
6400 without placing the Sokoban field under the player). Unfortunately, this
6401 breaks "Snake Bite" style levels when the snake is halfway through a door
6402 that just closes (the snake head is still alive and can be moved in this
6403 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6404 player (without Sokoban element) which then gets killed as designed). */
6406 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6407 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6408 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6409 change->target_element = EL_PLAYER_1;
6412 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6413 if (level->game_version < VERSION_IDENT(3,2,5,0))
6415 /* This is needed to fix a problem that was caused by a bugfix in function
6416 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6417 corrects the behaviour when a custom element changes to another custom
6418 element with a higher element number that has change actions defined.
6419 Normally, only one change per frame is allowed for custom elements.
6420 Therefore, it is checked if a custom element already changed in the
6421 current frame; if it did, subsequent changes are suppressed.
6422 Unfortunately, this is only checked for element changes, but not for
6423 change actions, which are still executed. As the function above loops
6424 through all custom elements from lower to higher, an element change
6425 resulting in a lower CE number won't be checked again, while a target
6426 element with a higher number will also be checked, and potential change
6427 actions will get executed for this CE, too (which is wrong), while
6428 further changes are ignored (which is correct). As this bugfix breaks
6429 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6430 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6431 behaviour for existing levels and tapes that make use of this bug */
6433 level->use_action_after_change_bug = TRUE;
6436 // not centering level after relocating player was default only in 3.2.3
6437 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6438 level->shifted_relocation = TRUE;
6440 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6441 if (level->game_version < VERSION_IDENT(3,2,6,0))
6442 level->em_explodes_by_fire = TRUE;
6444 // levels were solved by the first player entering an exit up to 4.1.0.0
6445 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6446 level->solved_by_one_player = TRUE;
6448 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6449 if (level->game_version < VERSION_IDENT(4,1,1,1))
6450 level->use_life_bugs = TRUE;
6452 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6453 if (level->game_version < VERSION_IDENT(4,1,1,1))
6454 level->sb_objects_needed = FALSE;
6456 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6457 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6458 level->finish_dig_collect = FALSE;
6461 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6465 // map elements that have changed in newer versions
6466 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6467 level->game_version);
6468 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6469 for (x = 0; x < 3; x++)
6470 for (y = 0; y < 3; y++)
6471 level->yamyam_content[i].e[x][y] =
6472 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6473 level->game_version);
6477 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6481 // map custom element change events that have changed in newer versions
6482 // (these following values were accidentally changed in version 3.0.1)
6483 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6484 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6486 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6488 int element = EL_CUSTOM_START + i;
6490 // order of checking and copying events to be mapped is important
6491 // (do not change the start and end value -- they are constant)
6492 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6494 if (HAS_CHANGE_EVENT(element, j - 2))
6496 SET_CHANGE_EVENT(element, j - 2, FALSE);
6497 SET_CHANGE_EVENT(element, j, TRUE);
6501 // order of checking and copying events to be mapped is important
6502 // (do not change the start and end value -- they are constant)
6503 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6505 if (HAS_CHANGE_EVENT(element, j - 1))
6507 SET_CHANGE_EVENT(element, j - 1, FALSE);
6508 SET_CHANGE_EVENT(element, j, TRUE);
6514 // initialize "can_change" field for old levels with only one change page
6515 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6519 int element = EL_CUSTOM_START + i;
6521 if (CAN_CHANGE(element))
6522 element_info[element].change->can_change = TRUE;
6526 // correct custom element values (for old levels without these options)
6527 if (level->game_version < VERSION_IDENT(3,1,1,0))
6529 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6531 int element = EL_CUSTOM_START + i;
6532 struct ElementInfo *ei = &element_info[element];
6534 if (ei->access_direction == MV_NO_DIRECTION)
6535 ei->access_direction = MV_ALL_DIRECTIONS;
6539 // correct custom element values (fix invalid values for all versions)
6542 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6544 int element = EL_CUSTOM_START + i;
6545 struct ElementInfo *ei = &element_info[element];
6547 for (j = 0; j < ei->num_change_pages; j++)
6549 struct ElementChangeInfo *change = &ei->change_page[j];
6551 if (change->trigger_player == CH_PLAYER_NONE)
6552 change->trigger_player = CH_PLAYER_ANY;
6554 if (change->trigger_side == CH_SIDE_NONE)
6555 change->trigger_side = CH_SIDE_ANY;
6560 // initialize "can_explode" field for old levels which did not store this
6561 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6562 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6564 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6566 int element = EL_CUSTOM_START + i;
6568 if (EXPLODES_1X1_OLD(element))
6569 element_info[element].explosion_type = EXPLODES_1X1;
6571 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6572 EXPLODES_SMASHED(element) ||
6573 EXPLODES_IMPACT(element)));
6577 // correct previously hard-coded move delay values for maze runner style
6578 if (level->game_version < VERSION_IDENT(3,1,1,0))
6580 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6582 int element = EL_CUSTOM_START + i;
6584 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6586 // previously hard-coded and therefore ignored
6587 element_info[element].move_delay_fixed = 9;
6588 element_info[element].move_delay_random = 0;
6593 // set some other uninitialized values of custom elements in older levels
6594 if (level->game_version < VERSION_IDENT(3,1,0,0))
6596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6598 int element = EL_CUSTOM_START + i;
6600 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6602 element_info[element].explosion_delay = 17;
6603 element_info[element].ignition_delay = 8;
6608 static void LoadLevel_InitElements(struct LevelInfo *level)
6610 LoadLevel_InitStandardElements(level);
6612 if (level->file_has_custom_elements)
6613 LoadLevel_InitCustomElements(level);
6615 // initialize element properties for level editor etc.
6616 InitElementPropertiesEngine(level->game_version);
6617 InitElementPropertiesGfxElement();
6620 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6624 // map elements that have changed in newer versions
6625 for (y = 0; y < level->fieldy; y++)
6626 for (x = 0; x < level->fieldx; x++)
6627 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6628 level->game_version);
6630 // clear unused playfield data (nicer if level gets resized in editor)
6631 for (x = 0; x < MAX_LEV_FIELDX; x++)
6632 for (y = 0; y < MAX_LEV_FIELDY; y++)
6633 if (x >= level->fieldx || y >= level->fieldy)
6634 level->field[x][y] = EL_EMPTY;
6636 // copy elements to runtime playfield array
6637 for (x = 0; x < MAX_LEV_FIELDX; x++)
6638 for (y = 0; y < MAX_LEV_FIELDY; y++)
6639 Tile[x][y] = level->field[x][y];
6641 // initialize level size variables for faster access
6642 lev_fieldx = level->fieldx;
6643 lev_fieldy = level->fieldy;
6645 // determine border element for this level
6646 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6647 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6652 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6654 struct LevelFileInfo *level_file_info = &level->file_info;
6656 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6657 CopyNativeLevel_RND_to_Native(level);
6660 static void LoadLevelTemplate_LoadAndInit(void)
6662 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6664 LoadLevel_InitVersion(&level_template);
6665 LoadLevel_InitElements(&level_template);
6667 ActivateLevelTemplate();
6670 void LoadLevelTemplate(int nr)
6672 if (!fileExists(getGlobalLevelTemplateFilename()))
6674 Warn("no level template found for this level");
6679 setLevelFileInfo(&level_template.file_info, nr);
6681 LoadLevelTemplate_LoadAndInit();
6684 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6686 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6688 LoadLevelTemplate_LoadAndInit();
6691 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6693 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6695 if (level.use_custom_template)
6697 if (network_level != NULL)
6698 LoadNetworkLevelTemplate(network_level);
6700 LoadLevelTemplate(-1);
6703 LoadLevel_InitVersion(&level);
6704 LoadLevel_InitElements(&level);
6705 LoadLevel_InitPlayfield(&level);
6707 LoadLevel_InitNativeEngines(&level);
6710 void LoadLevel(int nr)
6712 SetLevelSetInfo(leveldir_current->identifier, nr);
6714 setLevelFileInfo(&level.file_info, nr);
6716 LoadLevel_LoadAndInit(NULL);
6719 void LoadLevelInfoOnly(int nr)
6721 setLevelFileInfo(&level.file_info, nr);
6723 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6726 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6728 SetLevelSetInfo(network_level->leveldir_identifier,
6729 network_level->file_info.nr);
6731 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6733 LoadLevel_LoadAndInit(network_level);
6736 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6740 chunk_size += putFileVersion(file, level->file_version);
6741 chunk_size += putFileVersion(file, level->game_version);
6746 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6750 chunk_size += putFile16BitBE(file, level->creation_date.year);
6751 chunk_size += putFile8Bit(file, level->creation_date.month);
6752 chunk_size += putFile8Bit(file, level->creation_date.day);
6757 #if ENABLE_HISTORIC_CHUNKS
6758 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6762 putFile8Bit(file, level->fieldx);
6763 putFile8Bit(file, level->fieldy);
6765 putFile16BitBE(file, level->time);
6766 putFile16BitBE(file, level->gems_needed);
6768 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6769 putFile8Bit(file, level->name[i]);
6771 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6772 putFile8Bit(file, level->score[i]);
6774 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6775 for (y = 0; y < 3; y++)
6776 for (x = 0; x < 3; x++)
6777 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6778 level->yamyam_content[i].e[x][y]));
6779 putFile8Bit(file, level->amoeba_speed);
6780 putFile8Bit(file, level->time_magic_wall);
6781 putFile8Bit(file, level->time_wheel);
6782 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6783 level->amoeba_content));
6784 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6785 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6786 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6787 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6789 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6791 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6792 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6793 putFile32BitBE(file, level->can_move_into_acid_bits);
6794 putFile8Bit(file, level->dont_collide_with_bits);
6796 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6797 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6799 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6800 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6801 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6803 putFile8Bit(file, level->game_engine_type);
6805 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6809 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6814 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6815 chunk_size += putFile8Bit(file, level->name[i]);
6820 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6825 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6826 chunk_size += putFile8Bit(file, level->author[i]);
6831 #if ENABLE_HISTORIC_CHUNKS
6832 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6837 for (y = 0; y < level->fieldy; y++)
6838 for (x = 0; x < level->fieldx; x++)
6839 if (level->encoding_16bit_field)
6840 chunk_size += putFile16BitBE(file, level->field[x][y]);
6842 chunk_size += putFile8Bit(file, level->field[x][y]);
6848 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6853 for (y = 0; y < level->fieldy; y++)
6854 for (x = 0; x < level->fieldx; x++)
6855 chunk_size += putFile16BitBE(file, level->field[x][y]);
6860 #if ENABLE_HISTORIC_CHUNKS
6861 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6865 putFile8Bit(file, EL_YAMYAM);
6866 putFile8Bit(file, level->num_yamyam_contents);
6867 putFile8Bit(file, 0);
6868 putFile8Bit(file, 0);
6870 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6871 for (y = 0; y < 3; y++)
6872 for (x = 0; x < 3; x++)
6873 if (level->encoding_16bit_field)
6874 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6876 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6880 #if ENABLE_HISTORIC_CHUNKS
6881 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6884 int num_contents, content_xsize, content_ysize;
6885 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6887 if (element == EL_YAMYAM)
6889 num_contents = level->num_yamyam_contents;
6893 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6894 for (y = 0; y < 3; y++)
6895 for (x = 0; x < 3; x++)
6896 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6898 else if (element == EL_BD_AMOEBA)
6904 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6905 for (y = 0; y < 3; y++)
6906 for (x = 0; x < 3; x++)
6907 content_array[i][x][y] = EL_EMPTY;
6908 content_array[0][0][0] = level->amoeba_content;
6912 // chunk header already written -- write empty chunk data
6913 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6915 Warn("cannot save content for element '%d'", element);
6920 putFile16BitBE(file, element);
6921 putFile8Bit(file, num_contents);
6922 putFile8Bit(file, content_xsize);
6923 putFile8Bit(file, content_ysize);
6925 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6927 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6928 for (y = 0; y < 3; y++)
6929 for (x = 0; x < 3; x++)
6930 putFile16BitBE(file, content_array[i][x][y]);
6934 #if ENABLE_HISTORIC_CHUNKS
6935 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6937 int envelope_nr = element - EL_ENVELOPE_1;
6938 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6942 chunk_size += putFile16BitBE(file, element);
6943 chunk_size += putFile16BitBE(file, envelope_len);
6944 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6945 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6947 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6948 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6950 for (i = 0; i < envelope_len; i++)
6951 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6957 #if ENABLE_HISTORIC_CHUNKS
6958 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6959 int num_changed_custom_elements)
6963 putFile16BitBE(file, num_changed_custom_elements);
6965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6967 int element = EL_CUSTOM_START + i;
6969 struct ElementInfo *ei = &element_info[element];
6971 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6973 if (check < num_changed_custom_elements)
6975 putFile16BitBE(file, element);
6976 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6983 if (check != num_changed_custom_elements) // should not happen
6984 Warn("inconsistent number of custom element properties");
6988 #if ENABLE_HISTORIC_CHUNKS
6989 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6990 int num_changed_custom_elements)
6994 putFile16BitBE(file, num_changed_custom_elements);
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6998 int element = EL_CUSTOM_START + i;
7000 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7002 if (check < num_changed_custom_elements)
7004 putFile16BitBE(file, element);
7005 putFile16BitBE(file, element_info[element].change->target_element);
7012 if (check != num_changed_custom_elements) // should not happen
7013 Warn("inconsistent number of custom target elements");
7017 #if ENABLE_HISTORIC_CHUNKS
7018 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7019 int num_changed_custom_elements)
7021 int i, j, x, y, check = 0;
7023 putFile16BitBE(file, num_changed_custom_elements);
7025 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7027 int element = EL_CUSTOM_START + i;
7028 struct ElementInfo *ei = &element_info[element];
7030 if (ei->modified_settings)
7032 if (check < num_changed_custom_elements)
7034 putFile16BitBE(file, element);
7036 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7037 putFile8Bit(file, ei->description[j]);
7039 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7041 // some free bytes for future properties and padding
7042 WriteUnusedBytesToFile(file, 7);
7044 putFile8Bit(file, ei->use_gfx_element);
7045 putFile16BitBE(file, ei->gfx_element_initial);
7047 putFile8Bit(file, ei->collect_score_initial);
7048 putFile8Bit(file, ei->collect_count_initial);
7050 putFile16BitBE(file, ei->push_delay_fixed);
7051 putFile16BitBE(file, ei->push_delay_random);
7052 putFile16BitBE(file, ei->move_delay_fixed);
7053 putFile16BitBE(file, ei->move_delay_random);
7055 putFile16BitBE(file, ei->move_pattern);
7056 putFile8Bit(file, ei->move_direction_initial);
7057 putFile8Bit(file, ei->move_stepsize);
7059 for (y = 0; y < 3; y++)
7060 for (x = 0; x < 3; x++)
7061 putFile16BitBE(file, ei->content.e[x][y]);
7063 putFile32BitBE(file, ei->change->events);
7065 putFile16BitBE(file, ei->change->target_element);
7067 putFile16BitBE(file, ei->change->delay_fixed);
7068 putFile16BitBE(file, ei->change->delay_random);
7069 putFile16BitBE(file, ei->change->delay_frames);
7071 putFile16BitBE(file, ei->change->initial_trigger_element);
7073 putFile8Bit(file, ei->change->explode);
7074 putFile8Bit(file, ei->change->use_target_content);
7075 putFile8Bit(file, ei->change->only_if_complete);
7076 putFile8Bit(file, ei->change->use_random_replace);
7078 putFile8Bit(file, ei->change->random_percentage);
7079 putFile8Bit(file, ei->change->replace_when);
7081 for (y = 0; y < 3; y++)
7082 for (x = 0; x < 3; x++)
7083 putFile16BitBE(file, ei->change->content.e[x][y]);
7085 putFile8Bit(file, ei->slippery_type);
7087 // some free bytes for future properties and padding
7088 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7095 if (check != num_changed_custom_elements) // should not happen
7096 Warn("inconsistent number of custom element properties");
7100 #if ENABLE_HISTORIC_CHUNKS
7101 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7103 struct ElementInfo *ei = &element_info[element];
7106 // ---------- custom element base property values (96 bytes) ----------------
7108 putFile16BitBE(file, element);
7110 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7111 putFile8Bit(file, ei->description[i]);
7113 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7115 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7117 putFile8Bit(file, ei->num_change_pages);
7119 putFile16BitBE(file, ei->ce_value_fixed_initial);
7120 putFile16BitBE(file, ei->ce_value_random_initial);
7121 putFile8Bit(file, ei->use_last_ce_value);
7123 putFile8Bit(file, ei->use_gfx_element);
7124 putFile16BitBE(file, ei->gfx_element_initial);
7126 putFile8Bit(file, ei->collect_score_initial);
7127 putFile8Bit(file, ei->collect_count_initial);
7129 putFile8Bit(file, ei->drop_delay_fixed);
7130 putFile8Bit(file, ei->push_delay_fixed);
7131 putFile8Bit(file, ei->drop_delay_random);
7132 putFile8Bit(file, ei->push_delay_random);
7133 putFile16BitBE(file, ei->move_delay_fixed);
7134 putFile16BitBE(file, ei->move_delay_random);
7136 // bits 0 - 15 of "move_pattern" ...
7137 putFile16BitBE(file, ei->move_pattern & 0xffff);
7138 putFile8Bit(file, ei->move_direction_initial);
7139 putFile8Bit(file, ei->move_stepsize);
7141 putFile8Bit(file, ei->slippery_type);
7143 for (y = 0; y < 3; y++)
7144 for (x = 0; x < 3; x++)
7145 putFile16BitBE(file, ei->content.e[x][y]);
7147 putFile16BitBE(file, ei->move_enter_element);
7148 putFile16BitBE(file, ei->move_leave_element);
7149 putFile8Bit(file, ei->move_leave_type);
7151 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7152 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7154 putFile8Bit(file, ei->access_direction);
7156 putFile8Bit(file, ei->explosion_delay);
7157 putFile8Bit(file, ei->ignition_delay);
7158 putFile8Bit(file, ei->explosion_type);
7160 // some free bytes for future custom property values and padding
7161 WriteUnusedBytesToFile(file, 1);
7163 // ---------- change page property values (48 bytes) ------------------------
7165 for (i = 0; i < ei->num_change_pages; i++)
7167 struct ElementChangeInfo *change = &ei->change_page[i];
7168 unsigned int event_bits;
7170 // bits 0 - 31 of "has_event[]" ...
7172 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7173 if (change->has_event[j])
7174 event_bits |= (1 << j);
7175 putFile32BitBE(file, event_bits);
7177 putFile16BitBE(file, change->target_element);
7179 putFile16BitBE(file, change->delay_fixed);
7180 putFile16BitBE(file, change->delay_random);
7181 putFile16BitBE(file, change->delay_frames);
7183 putFile16BitBE(file, change->initial_trigger_element);
7185 putFile8Bit(file, change->explode);
7186 putFile8Bit(file, change->use_target_content);
7187 putFile8Bit(file, change->only_if_complete);
7188 putFile8Bit(file, change->use_random_replace);
7190 putFile8Bit(file, change->random_percentage);
7191 putFile8Bit(file, change->replace_when);
7193 for (y = 0; y < 3; y++)
7194 for (x = 0; x < 3; x++)
7195 putFile16BitBE(file, change->target_content.e[x][y]);
7197 putFile8Bit(file, change->can_change);
7199 putFile8Bit(file, change->trigger_side);
7201 putFile8Bit(file, change->trigger_player);
7202 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7203 log_2(change->trigger_page)));
7205 putFile8Bit(file, change->has_action);
7206 putFile8Bit(file, change->action_type);
7207 putFile8Bit(file, change->action_mode);
7208 putFile16BitBE(file, change->action_arg);
7210 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7212 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7213 if (change->has_event[j])
7214 event_bits |= (1 << (j - 32));
7215 putFile8Bit(file, event_bits);
7220 #if ENABLE_HISTORIC_CHUNKS
7221 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7223 struct ElementInfo *ei = &element_info[element];
7224 struct ElementGroupInfo *group = ei->group;
7227 putFile16BitBE(file, element);
7229 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7230 putFile8Bit(file, ei->description[i]);
7232 putFile8Bit(file, group->num_elements);
7234 putFile8Bit(file, ei->use_gfx_element);
7235 putFile16BitBE(file, ei->gfx_element_initial);
7237 putFile8Bit(file, group->choice_mode);
7239 // some free bytes for future values and padding
7240 WriteUnusedBytesToFile(file, 3);
7242 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7243 putFile16BitBE(file, group->element[i]);
7247 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7248 boolean write_element)
7250 int save_type = entry->save_type;
7251 int data_type = entry->data_type;
7252 int conf_type = entry->conf_type;
7253 int byte_mask = conf_type & CONF_MASK_BYTES;
7254 int element = entry->element;
7255 int default_value = entry->default_value;
7257 boolean modified = FALSE;
7259 if (byte_mask != CONF_MASK_MULTI_BYTES)
7261 void *value_ptr = entry->value;
7262 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7265 // check if any settings have been modified before saving them
7266 if (value != default_value)
7269 // do not save if explicitly told or if unmodified default settings
7270 if ((save_type == SAVE_CONF_NEVER) ||
7271 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7275 num_bytes += putFile16BitBE(file, element);
7277 num_bytes += putFile8Bit(file, conf_type);
7278 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7279 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7280 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7283 else if (data_type == TYPE_STRING)
7285 char *default_string = entry->default_string;
7286 char *string = (char *)(entry->value);
7287 int string_length = strlen(string);
7290 // check if any settings have been modified before saving them
7291 if (!strEqual(string, default_string))
7294 // do not save if explicitly told or if unmodified default settings
7295 if ((save_type == SAVE_CONF_NEVER) ||
7296 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7300 num_bytes += putFile16BitBE(file, element);
7302 num_bytes += putFile8Bit(file, conf_type);
7303 num_bytes += putFile16BitBE(file, string_length);
7305 for (i = 0; i < string_length; i++)
7306 num_bytes += putFile8Bit(file, string[i]);
7308 else if (data_type == TYPE_ELEMENT_LIST)
7310 int *element_array = (int *)(entry->value);
7311 int num_elements = *(int *)(entry->num_entities);
7314 // check if any settings have been modified before saving them
7315 for (i = 0; i < num_elements; i++)
7316 if (element_array[i] != default_value)
7319 // do not save if explicitly told or if unmodified default settings
7320 if ((save_type == SAVE_CONF_NEVER) ||
7321 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7325 num_bytes += putFile16BitBE(file, element);
7327 num_bytes += putFile8Bit(file, conf_type);
7328 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7330 for (i = 0; i < num_elements; i++)
7331 num_bytes += putFile16BitBE(file, element_array[i]);
7333 else if (data_type == TYPE_CONTENT_LIST)
7335 struct Content *content = (struct Content *)(entry->value);
7336 int num_contents = *(int *)(entry->num_entities);
7339 // check if any settings have been modified before saving them
7340 for (i = 0; i < num_contents; i++)
7341 for (y = 0; y < 3; y++)
7342 for (x = 0; x < 3; x++)
7343 if (content[i].e[x][y] != default_value)
7346 // do not save if explicitly told or if unmodified default settings
7347 if ((save_type == SAVE_CONF_NEVER) ||
7348 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7352 num_bytes += putFile16BitBE(file, element);
7354 num_bytes += putFile8Bit(file, conf_type);
7355 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7357 for (i = 0; i < num_contents; i++)
7358 for (y = 0; y < 3; y++)
7359 for (x = 0; x < 3; x++)
7360 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7366 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7371 li = *level; // copy level data into temporary buffer
7373 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7374 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7379 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7384 li = *level; // copy level data into temporary buffer
7386 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7387 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7392 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7394 int envelope_nr = element - EL_ENVELOPE_1;
7398 chunk_size += putFile16BitBE(file, element);
7400 // copy envelope data into temporary buffer
7401 xx_envelope = level->envelope[envelope_nr];
7403 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7404 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7409 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7411 struct ElementInfo *ei = &element_info[element];
7415 chunk_size += putFile16BitBE(file, element);
7417 xx_ei = *ei; // copy element data into temporary buffer
7419 // set default description string for this specific element
7420 strcpy(xx_default_description, getDefaultElementDescription(ei));
7422 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7423 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7425 for (i = 0; i < ei->num_change_pages; i++)
7427 struct ElementChangeInfo *change = &ei->change_page[i];
7429 xx_current_change_page = i;
7431 xx_change = *change; // copy change data into temporary buffer
7434 setEventBitsFromEventFlags(change);
7436 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7437 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7444 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7446 struct ElementInfo *ei = &element_info[element];
7447 struct ElementGroupInfo *group = ei->group;
7451 chunk_size += putFile16BitBE(file, element);
7453 xx_ei = *ei; // copy element data into temporary buffer
7454 xx_group = *group; // copy group data into temporary buffer
7456 // set default description string for this specific element
7457 strcpy(xx_default_description, getDefaultElementDescription(ei));
7459 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7460 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7465 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7466 boolean save_as_template)
7472 if (!(file = fopen(filename, MODE_WRITE)))
7474 Warn("cannot save level file '%s'", filename);
7479 level->file_version = FILE_VERSION_ACTUAL;
7480 level->game_version = GAME_VERSION_ACTUAL;
7482 level->creation_date = getCurrentDate();
7484 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7485 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7487 chunk_size = SaveLevel_VERS(NULL, level);
7488 putFileChunkBE(file, "VERS", chunk_size);
7489 SaveLevel_VERS(file, level);
7491 chunk_size = SaveLevel_DATE(NULL, level);
7492 putFileChunkBE(file, "DATE", chunk_size);
7493 SaveLevel_DATE(file, level);
7495 chunk_size = SaveLevel_NAME(NULL, level);
7496 putFileChunkBE(file, "NAME", chunk_size);
7497 SaveLevel_NAME(file, level);
7499 chunk_size = SaveLevel_AUTH(NULL, level);
7500 putFileChunkBE(file, "AUTH", chunk_size);
7501 SaveLevel_AUTH(file, level);
7503 chunk_size = SaveLevel_INFO(NULL, level);
7504 putFileChunkBE(file, "INFO", chunk_size);
7505 SaveLevel_INFO(file, level);
7507 chunk_size = SaveLevel_BODY(NULL, level);
7508 putFileChunkBE(file, "BODY", chunk_size);
7509 SaveLevel_BODY(file, level);
7511 chunk_size = SaveLevel_ELEM(NULL, level);
7512 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7514 putFileChunkBE(file, "ELEM", chunk_size);
7515 SaveLevel_ELEM(file, level);
7518 for (i = 0; i < NUM_ENVELOPES; i++)
7520 int element = EL_ENVELOPE_1 + i;
7522 chunk_size = SaveLevel_NOTE(NULL, level, element);
7523 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7525 putFileChunkBE(file, "NOTE", chunk_size);
7526 SaveLevel_NOTE(file, level, element);
7530 // if not using template level, check for non-default custom/group elements
7531 if (!level->use_custom_template || save_as_template)
7533 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7535 int element = EL_CUSTOM_START + i;
7537 chunk_size = SaveLevel_CUSX(NULL, level, element);
7538 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7540 putFileChunkBE(file, "CUSX", chunk_size);
7541 SaveLevel_CUSX(file, level, element);
7545 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7547 int element = EL_GROUP_START + i;
7549 chunk_size = SaveLevel_GRPX(NULL, level, element);
7550 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7552 putFileChunkBE(file, "GRPX", chunk_size);
7553 SaveLevel_GRPX(file, level, element);
7560 SetFilePermissions(filename, PERMS_PRIVATE);
7563 void SaveLevel(int nr)
7565 char *filename = getDefaultLevelFilename(nr);
7567 SaveLevelFromFilename(&level, filename, FALSE);
7570 void SaveLevelTemplate(void)
7572 char *filename = getLocalLevelTemplateFilename();
7574 SaveLevelFromFilename(&level, filename, TRUE);
7577 boolean SaveLevelChecked(int nr)
7579 char *filename = getDefaultLevelFilename(nr);
7580 boolean new_level = !fileExists(filename);
7581 boolean level_saved = FALSE;
7583 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7588 Request("Level saved!", REQ_CONFIRM);
7596 void DumpLevel(struct LevelInfo *level)
7598 if (level->no_level_file || level->no_valid_file)
7600 Warn("cannot dump -- no valid level file found");
7606 Print("Level xxx (file version %08d, game version %08d)\n",
7607 level->file_version, level->game_version);
7610 Print("Level author: '%s'\n", level->author);
7611 Print("Level title: '%s'\n", level->name);
7613 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7615 Print("Level time: %d seconds\n", level->time);
7616 Print("Gems needed: %d\n", level->gems_needed);
7618 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7619 Print("Time for wheel: %d seconds\n", level->time_wheel);
7620 Print("Time for light: %d seconds\n", level->time_light);
7621 Print("Time for timegate: %d seconds\n", level->time_timegate);
7623 Print("Amoeba speed: %d\n", level->amoeba_speed);
7626 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7627 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7628 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7629 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7630 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7636 // ============================================================================
7637 // tape file functions
7638 // ============================================================================
7640 static void setTapeInfoToDefaults(void)
7644 // always start with reliable default values (empty tape)
7647 // default values (also for pre-1.2 tapes) with only the first player
7648 tape.player_participates[0] = TRUE;
7649 for (i = 1; i < MAX_PLAYERS; i++)
7650 tape.player_participates[i] = FALSE;
7652 // at least one (default: the first) player participates in every tape
7653 tape.num_participating_players = 1;
7655 tape.property_bits = TAPE_PROPERTY_NONE;
7657 tape.level_nr = level_nr;
7659 tape.changed = FALSE;
7661 tape.recording = FALSE;
7662 tape.playing = FALSE;
7663 tape.pausing = FALSE;
7665 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7666 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7668 tape.no_valid_file = FALSE;
7671 static int getTapePosSize(struct TapeInfo *tape)
7673 int tape_pos_size = 0;
7675 if (tape->use_key_actions)
7676 tape_pos_size += tape->num_participating_players;
7678 if (tape->use_mouse_actions)
7679 tape_pos_size += 3; // x and y position and mouse button mask
7681 tape_pos_size += 1; // tape action delay value
7683 return tape_pos_size;
7686 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7688 tape->use_key_actions = FALSE;
7689 tape->use_mouse_actions = FALSE;
7691 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7692 tape->use_key_actions = TRUE;
7694 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7695 tape->use_mouse_actions = TRUE;
7698 static int getTapeActionValue(struct TapeInfo *tape)
7700 return (tape->use_key_actions &&
7701 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7702 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7703 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7704 TAPE_ACTIONS_DEFAULT);
7707 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7709 tape->file_version = getFileVersion(file);
7710 tape->game_version = getFileVersion(file);
7715 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7719 tape->random_seed = getFile32BitBE(file);
7720 tape->date = getFile32BitBE(file);
7721 tape->length = getFile32BitBE(file);
7723 // read header fields that are new since version 1.2
7724 if (tape->file_version >= FILE_VERSION_1_2)
7726 byte store_participating_players = getFile8Bit(file);
7729 // since version 1.2, tapes store which players participate in the tape
7730 tape->num_participating_players = 0;
7731 for (i = 0; i < MAX_PLAYERS; i++)
7733 tape->player_participates[i] = FALSE;
7735 if (store_participating_players & (1 << i))
7737 tape->player_participates[i] = TRUE;
7738 tape->num_participating_players++;
7742 setTapeActionFlags(tape, getFile8Bit(file));
7744 tape->property_bits = getFile8Bit(file);
7746 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7748 engine_version = getFileVersion(file);
7749 if (engine_version > 0)
7750 tape->engine_version = engine_version;
7752 tape->engine_version = tape->game_version;
7758 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7760 tape->scr_fieldx = getFile8Bit(file);
7761 tape->scr_fieldy = getFile8Bit(file);
7766 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7768 int level_identifier_size;
7771 level_identifier_size = getFile16BitBE(file);
7773 tape->level_identifier =
7774 checked_realloc(tape->level_identifier, level_identifier_size);
7776 for (i = 0; i < level_identifier_size; i++)
7777 tape->level_identifier[i] = getFile8Bit(file);
7779 tape->level_nr = getFile16BitBE(file);
7781 chunk_size = 2 + level_identifier_size + 2;
7786 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7789 int tape_pos_size = getTapePosSize(tape);
7790 int chunk_size_expected = tape_pos_size * tape->length;
7792 if (chunk_size_expected != chunk_size)
7794 ReadUnusedBytesFromFile(file, chunk_size);
7795 return chunk_size_expected;
7798 for (i = 0; i < tape->length; i++)
7800 if (i >= MAX_TAPE_LEN)
7802 Warn("tape truncated -- size exceeds maximum tape size %d",
7805 // tape too large; read and ignore remaining tape data from this chunk
7806 for (;i < tape->length; i++)
7807 ReadUnusedBytesFromFile(file, tape_pos_size);
7812 if (tape->use_key_actions)
7814 for (j = 0; j < MAX_PLAYERS; j++)
7816 tape->pos[i].action[j] = MV_NONE;
7818 if (tape->player_participates[j])
7819 tape->pos[i].action[j] = getFile8Bit(file);
7823 if (tape->use_mouse_actions)
7825 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7826 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7827 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7830 tape->pos[i].delay = getFile8Bit(file);
7832 if (tape->file_version == FILE_VERSION_1_0)
7834 // eliminate possible diagonal moves in old tapes
7835 // this is only for backward compatibility
7837 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7838 byte action = tape->pos[i].action[0];
7839 int k, num_moves = 0;
7841 for (k = 0; k<4; k++)
7843 if (action & joy_dir[k])
7845 tape->pos[i + num_moves].action[0] = joy_dir[k];
7847 tape->pos[i + num_moves].delay = 0;
7856 tape->length += num_moves;
7859 else if (tape->file_version < FILE_VERSION_2_0)
7861 // convert pre-2.0 tapes to new tape format
7863 if (tape->pos[i].delay > 1)
7866 tape->pos[i + 1] = tape->pos[i];
7867 tape->pos[i + 1].delay = 1;
7870 for (j = 0; j < MAX_PLAYERS; j++)
7871 tape->pos[i].action[j] = MV_NONE;
7872 tape->pos[i].delay--;
7879 if (checkEndOfFile(file))
7883 if (i != tape->length)
7884 chunk_size = tape_pos_size * i;
7889 static void LoadTape_SokobanSolution(char *filename)
7892 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7894 if (!(file = openFile(filename, MODE_READ)))
7896 tape.no_valid_file = TRUE;
7901 while (!checkEndOfFile(file))
7903 unsigned char c = getByteFromFile(file);
7905 if (checkEndOfFile(file))
7912 tape.pos[tape.length].action[0] = MV_UP;
7913 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7919 tape.pos[tape.length].action[0] = MV_DOWN;
7920 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7926 tape.pos[tape.length].action[0] = MV_LEFT;
7927 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7933 tape.pos[tape.length].action[0] = MV_RIGHT;
7934 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7942 // ignore white-space characters
7946 tape.no_valid_file = TRUE;
7948 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7956 if (tape.no_valid_file)
7959 tape.length_frames = GetTapeLengthFrames();
7960 tape.length_seconds = GetTapeLengthSeconds();
7963 void LoadTapeFromFilename(char *filename)
7965 char cookie[MAX_LINE_LEN];
7966 char chunk_name[CHUNK_ID_LEN + 1];
7970 // always start with reliable default values
7971 setTapeInfoToDefaults();
7973 if (strSuffix(filename, ".sln"))
7975 LoadTape_SokobanSolution(filename);
7980 if (!(file = openFile(filename, MODE_READ)))
7982 tape.no_valid_file = TRUE;
7987 getFileChunkBE(file, chunk_name, NULL);
7988 if (strEqual(chunk_name, "RND1"))
7990 getFile32BitBE(file); // not used
7992 getFileChunkBE(file, chunk_name, NULL);
7993 if (!strEqual(chunk_name, "TAPE"))
7995 tape.no_valid_file = TRUE;
7997 Warn("unknown format of tape file '%s'", filename);
8004 else // check for pre-2.0 file format with cookie string
8006 strcpy(cookie, chunk_name);
8007 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8009 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8010 cookie[strlen(cookie) - 1] = '\0';
8012 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8014 tape.no_valid_file = TRUE;
8016 Warn("unknown format of tape file '%s'", filename);
8023 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8025 tape.no_valid_file = TRUE;
8027 Warn("unsupported version of tape file '%s'", filename);
8034 // pre-2.0 tape files have no game version, so use file version here
8035 tape.game_version = tape.file_version;
8038 if (tape.file_version < FILE_VERSION_1_2)
8040 // tape files from versions before 1.2.0 without chunk structure
8041 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8042 LoadTape_BODY(file, 2 * tape.length, &tape);
8050 int (*loader)(File *, int, struct TapeInfo *);
8054 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8055 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8056 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8057 { "INFO", -1, LoadTape_INFO },
8058 { "BODY", -1, LoadTape_BODY },
8062 while (getFileChunkBE(file, chunk_name, &chunk_size))
8066 while (chunk_info[i].name != NULL &&
8067 !strEqual(chunk_name, chunk_info[i].name))
8070 if (chunk_info[i].name == NULL)
8072 Warn("unknown chunk '%s' in tape file '%s'",
8073 chunk_name, filename);
8075 ReadUnusedBytesFromFile(file, chunk_size);
8077 else if (chunk_info[i].size != -1 &&
8078 chunk_info[i].size != chunk_size)
8080 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8081 chunk_size, chunk_name, filename);
8083 ReadUnusedBytesFromFile(file, chunk_size);
8087 // call function to load this tape chunk
8088 int chunk_size_expected =
8089 (chunk_info[i].loader)(file, chunk_size, &tape);
8091 // the size of some chunks cannot be checked before reading other
8092 // chunks first (like "HEAD" and "BODY") that contain some header
8093 // information, so check them here
8094 if (chunk_size_expected != chunk_size)
8096 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8097 chunk_size, chunk_name, filename);
8105 tape.length_frames = GetTapeLengthFrames();
8106 tape.length_seconds = GetTapeLengthSeconds();
8109 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8111 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8113 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8114 tape.engine_version);
8118 void LoadTape(int nr)
8120 char *filename = getTapeFilename(nr);
8122 LoadTapeFromFilename(filename);
8125 void LoadSolutionTape(int nr)
8127 char *filename = getSolutionTapeFilename(nr);
8129 LoadTapeFromFilename(filename);
8131 if (TAPE_IS_EMPTY(tape) &&
8132 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8133 level.native_sp_level->demo.is_available)
8134 CopyNativeTape_SP_to_RND(&level);
8137 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8139 // chunk required for team mode tapes with non-default screen size
8140 return (tape->num_participating_players > 1 &&
8141 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8142 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8145 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8147 putFileVersion(file, tape->file_version);
8148 putFileVersion(file, tape->game_version);
8151 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8154 byte store_participating_players = 0;
8156 // set bits for participating players for compact storage
8157 for (i = 0; i < MAX_PLAYERS; i++)
8158 if (tape->player_participates[i])
8159 store_participating_players |= (1 << i);
8161 putFile32BitBE(file, tape->random_seed);
8162 putFile32BitBE(file, tape->date);
8163 putFile32BitBE(file, tape->length);
8165 putFile8Bit(file, store_participating_players);
8167 putFile8Bit(file, getTapeActionValue(tape));
8169 putFile8Bit(file, tape->property_bits);
8171 // unused bytes not at the end here for 4-byte alignment of engine_version
8172 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8174 putFileVersion(file, tape->engine_version);
8177 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8179 putFile8Bit(file, tape->scr_fieldx);
8180 putFile8Bit(file, tape->scr_fieldy);
8183 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8185 int level_identifier_size = strlen(tape->level_identifier) + 1;
8188 putFile16BitBE(file, level_identifier_size);
8190 for (i = 0; i < level_identifier_size; i++)
8191 putFile8Bit(file, tape->level_identifier[i]);
8193 putFile16BitBE(file, tape->level_nr);
8196 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8200 for (i = 0; i < tape->length; i++)
8202 if (tape->use_key_actions)
8204 for (j = 0; j < MAX_PLAYERS; j++)
8205 if (tape->player_participates[j])
8206 putFile8Bit(file, tape->pos[i].action[j]);
8209 if (tape->use_mouse_actions)
8211 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8212 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8213 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8216 putFile8Bit(file, tape->pos[i].delay);
8220 void SaveTapeToFilename(char *filename)
8224 int info_chunk_size;
8225 int body_chunk_size;
8227 if (!(file = fopen(filename, MODE_WRITE)))
8229 Warn("cannot save level recording file '%s'", filename);
8234 tape_pos_size = getTapePosSize(&tape);
8236 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8237 body_chunk_size = tape_pos_size * tape.length;
8239 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8240 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8242 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8243 SaveTape_VERS(file, &tape);
8245 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8246 SaveTape_HEAD(file, &tape);
8248 if (checkSaveTape_SCRN(&tape))
8250 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8251 SaveTape_SCRN(file, &tape);
8254 putFileChunkBE(file, "INFO", info_chunk_size);
8255 SaveTape_INFO(file, &tape);
8257 putFileChunkBE(file, "BODY", body_chunk_size);
8258 SaveTape_BODY(file, &tape);
8262 SetFilePermissions(filename, PERMS_PRIVATE);
8265 void SaveTape(int nr)
8267 char *filename = getTapeFilename(nr);
8270 InitTapeDirectory(leveldir_current->subdir);
8272 tape.file_version = FILE_VERSION_ACTUAL;
8273 tape.game_version = GAME_VERSION_ACTUAL;
8275 tape.num_participating_players = 0;
8277 // count number of participating players
8278 for (i = 0; i < MAX_PLAYERS; i++)
8279 if (tape.player_participates[i])
8280 tape.num_participating_players++;
8282 SaveTapeToFilename(filename);
8284 tape.changed = FALSE;
8287 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8288 unsigned int req_state_added)
8290 char *filename = getTapeFilename(nr);
8291 boolean new_tape = !fileExists(filename);
8292 boolean tape_saved = FALSE;
8294 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8299 Request(msg_saved, REQ_CONFIRM | req_state_added);
8307 boolean SaveTapeChecked(int nr)
8309 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8312 boolean SaveTapeChecked_LevelSolved(int nr)
8314 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8315 "Level solved! Tape saved!", REQ_STAY_OPEN);
8318 void DumpTape(struct TapeInfo *tape)
8320 int tape_frame_counter;
8323 if (tape->no_valid_file)
8325 Warn("cannot dump -- no valid tape file found");
8331 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8332 tape->level_nr, tape->file_version, tape->game_version);
8333 Print(" (effective engine version %08d)\n",
8334 tape->engine_version);
8335 Print("Level series identifier: '%s'\n", tape->level_identifier);
8338 tape_frame_counter = 0;
8340 for (i = 0; i < tape->length; i++)
8342 if (i >= MAX_TAPE_LEN)
8347 for (j = 0; j < MAX_PLAYERS; j++)
8349 if (tape->player_participates[j])
8351 int action = tape->pos[i].action[j];
8353 Print("%d:%02x ", j, action);
8354 Print("[%c%c%c%c|%c%c] - ",
8355 (action & JOY_LEFT ? '<' : ' '),
8356 (action & JOY_RIGHT ? '>' : ' '),
8357 (action & JOY_UP ? '^' : ' '),
8358 (action & JOY_DOWN ? 'v' : ' '),
8359 (action & JOY_BUTTON_1 ? '1' : ' '),
8360 (action & JOY_BUTTON_2 ? '2' : ' '));
8364 Print("(%03d) ", tape->pos[i].delay);
8365 Print("[%05d]\n", tape_frame_counter);
8367 tape_frame_counter += tape->pos[i].delay;
8374 // ============================================================================
8375 // score file functions
8376 // ============================================================================
8378 void LoadScore(int nr)
8381 char *filename = getScoreFilename(nr);
8382 char cookie[MAX_LINE_LEN];
8383 char line[MAX_LINE_LEN];
8387 // always start with reliable default values
8388 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8390 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8391 highscore[i].Score = 0;
8394 if (!(file = fopen(filename, MODE_READ)))
8397 // check file identifier
8398 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8400 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8401 cookie[strlen(cookie) - 1] = '\0';
8403 if (!checkCookieString(cookie, SCORE_COOKIE))
8405 Warn("unknown format of score file '%s'", filename);
8412 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8414 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8415 Warn("fscanf() failed; %s", strerror(errno));
8417 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8420 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8421 line[strlen(line) - 1] = '\0';
8423 for (line_ptr = line; *line_ptr; line_ptr++)
8425 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8427 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8428 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8437 void SaveScore(int nr)
8440 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8441 char *filename = getScoreFilename(nr);
8444 // used instead of "leveldir_current->subdir" (for network games)
8445 InitScoreDirectory(levelset.identifier);
8447 if (!(file = fopen(filename, MODE_WRITE)))
8449 Warn("cannot save score for level %d", nr);
8454 fprintf(file, "%s\n\n", SCORE_COOKIE);
8456 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8457 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8461 SetFilePermissions(filename, permissions);
8465 // ============================================================================
8466 // setup file functions
8467 // ============================================================================
8469 #define TOKEN_STR_PLAYER_PREFIX "player_"
8472 static struct TokenInfo global_setup_tokens[] =
8476 &setup.player_name, "player_name"
8480 &setup.multiple_users, "multiple_users"
8484 &setup.sound, "sound"
8488 &setup.sound_loops, "repeating_sound_loops"
8492 &setup.sound_music, "background_music"
8496 &setup.sound_simple, "simple_sound_effects"
8500 &setup.toons, "toons"
8504 &setup.scroll_delay, "scroll_delay"
8508 &setup.forced_scroll_delay, "forced_scroll_delay"
8512 &setup.scroll_delay_value, "scroll_delay_value"
8516 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8520 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8524 &setup.fade_screens, "fade_screens"
8528 &setup.autorecord, "automatic_tape_recording"
8532 &setup.show_titlescreen, "show_titlescreen"
8536 &setup.quick_doors, "quick_doors"
8540 &setup.team_mode, "team_mode"
8544 &setup.handicap, "handicap"
8548 &setup.skip_levels, "skip_levels"
8552 &setup.increment_levels, "increment_levels"
8556 &setup.auto_play_next_level, "auto_play_next_level"
8560 &setup.count_score_after_game, "count_score_after_game"
8564 &setup.show_scores_after_game, "show_scores_after_game"
8568 &setup.time_limit, "time_limit"
8572 &setup.fullscreen, "fullscreen"
8576 &setup.window_scaling_percent, "window_scaling_percent"
8580 &setup.window_scaling_quality, "window_scaling_quality"
8584 &setup.screen_rendering_mode, "screen_rendering_mode"
8588 &setup.vsync_mode, "vsync_mode"
8592 &setup.ask_on_escape, "ask_on_escape"
8596 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8600 &setup.ask_on_game_over, "ask_on_game_over"
8604 &setup.quick_switch, "quick_player_switch"
8608 &setup.input_on_focus, "input_on_focus"
8612 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8616 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8620 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8624 &setup.game_speed_extended, "game_speed_extended"
8628 &setup.game_frame_delay, "game_frame_delay"
8632 &setup.sp_show_border_elements, "sp_show_border_elements"
8636 &setup.small_game_graphics, "small_game_graphics"
8640 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8644 &setup.graphics_set, "graphics_set"
8648 &setup.sounds_set, "sounds_set"
8652 &setup.music_set, "music_set"
8656 &setup.override_level_graphics, "override_level_graphics"
8660 &setup.override_level_sounds, "override_level_sounds"
8664 &setup.override_level_music, "override_level_music"
8668 &setup.volume_simple, "volume_simple"
8672 &setup.volume_loops, "volume_loops"
8676 &setup.volume_music, "volume_music"
8680 &setup.network_mode, "network_mode"
8684 &setup.network_player_nr, "network_player"
8688 &setup.network_server_hostname, "network_server_hostname"
8692 &setup.touch.control_type, "touch.control_type"
8696 &setup.touch.move_distance, "touch.move_distance"
8700 &setup.touch.drop_distance, "touch.drop_distance"
8704 &setup.touch.transparency, "touch.transparency"
8708 &setup.touch.draw_outlined, "touch.draw_outlined"
8712 &setup.touch.draw_pressed, "touch.draw_pressed"
8716 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8720 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8724 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8728 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8732 static struct TokenInfo auto_setup_tokens[] =
8736 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8740 static struct TokenInfo editor_setup_tokens[] =
8744 &setup.editor.el_classic, "editor.el_classic"
8748 &setup.editor.el_custom, "editor.el_custom"
8752 &setup.editor.el_user_defined, "editor.el_user_defined"
8756 &setup.editor.el_dynamic, "editor.el_dynamic"
8760 &setup.editor.el_headlines, "editor.el_headlines"
8764 &setup.editor.show_element_token, "editor.show_element_token"
8768 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
8772 static struct TokenInfo editor_cascade_setup_tokens[] =
8776 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8780 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8784 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8788 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8792 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8796 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8800 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8804 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8808 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8812 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8816 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8820 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8824 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8828 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8832 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8836 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8840 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8844 static struct TokenInfo shortcut_setup_tokens[] =
8848 &setup.shortcut.save_game, "shortcut.save_game"
8852 &setup.shortcut.load_game, "shortcut.load_game"
8856 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8860 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8864 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8868 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8872 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8876 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8880 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8884 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8888 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8892 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8896 &setup.shortcut.tape_record, "shortcut.tape_record"
8900 &setup.shortcut.tape_play, "shortcut.tape_play"
8904 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8908 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8912 &setup.shortcut.sound_music, "shortcut.sound_music"
8916 &setup.shortcut.snap_left, "shortcut.snap_left"
8920 &setup.shortcut.snap_right, "shortcut.snap_right"
8924 &setup.shortcut.snap_up, "shortcut.snap_up"
8928 &setup.shortcut.snap_down, "shortcut.snap_down"
8932 static struct SetupInputInfo setup_input;
8933 static struct TokenInfo player_setup_tokens[] =
8937 &setup_input.use_joystick, ".use_joystick"
8941 &setup_input.joy.device_name, ".joy.device_name"
8945 &setup_input.joy.xleft, ".joy.xleft"
8949 &setup_input.joy.xmiddle, ".joy.xmiddle"
8953 &setup_input.joy.xright, ".joy.xright"
8957 &setup_input.joy.yupper, ".joy.yupper"
8961 &setup_input.joy.ymiddle, ".joy.ymiddle"
8965 &setup_input.joy.ylower, ".joy.ylower"
8969 &setup_input.joy.snap, ".joy.snap_field"
8973 &setup_input.joy.drop, ".joy.place_bomb"
8977 &setup_input.key.left, ".key.move_left"
8981 &setup_input.key.right, ".key.move_right"
8985 &setup_input.key.up, ".key.move_up"
8989 &setup_input.key.down, ".key.move_down"
8993 &setup_input.key.snap, ".key.snap_field"
8997 &setup_input.key.drop, ".key.place_bomb"
9001 static struct TokenInfo system_setup_tokens[] =
9005 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9009 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9013 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9017 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9021 static struct TokenInfo internal_setup_tokens[] =
9025 &setup.internal.program_title, "program_title"
9029 &setup.internal.program_version, "program_version"
9033 &setup.internal.program_author, "program_author"
9037 &setup.internal.program_email, "program_email"
9041 &setup.internal.program_website, "program_website"
9045 &setup.internal.program_copyright, "program_copyright"
9049 &setup.internal.program_company, "program_company"
9053 &setup.internal.program_icon_file, "program_icon_file"
9057 &setup.internal.default_graphics_set, "default_graphics_set"
9061 &setup.internal.default_sounds_set, "default_sounds_set"
9065 &setup.internal.default_music_set, "default_music_set"
9069 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9073 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9077 &setup.internal.fallback_music_file, "fallback_music_file"
9081 &setup.internal.default_level_series, "default_level_series"
9085 &setup.internal.default_window_width, "default_window_width"
9089 &setup.internal.default_window_height, "default_window_height"
9093 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9097 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9101 &setup.internal.create_user_levelset, "create_user_levelset"
9105 &setup.internal.menu_game, "menu_game"
9109 &setup.internal.menu_editor, "menu_editor"
9113 &setup.internal.menu_graphics, "menu_graphics"
9117 &setup.internal.menu_sound, "menu_sound"
9121 &setup.internal.menu_artwork, "menu_artwork"
9125 &setup.internal.menu_input, "menu_input"
9129 &setup.internal.menu_touch, "menu_touch"
9133 &setup.internal.menu_shortcuts, "menu_shortcuts"
9137 &setup.internal.menu_exit, "menu_exit"
9141 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9145 static struct TokenInfo debug_setup_tokens[] =
9149 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9153 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9157 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9161 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9165 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9169 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9173 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9177 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9181 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9185 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9189 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9193 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9197 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9201 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9205 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9209 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9213 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9217 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9221 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9225 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9229 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9232 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9236 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9240 &setup.debug.xsn_mode, "debug.xsn_mode"
9244 &setup.debug.xsn_percent, "debug.xsn_percent"
9248 static struct TokenInfo options_setup_tokens[] =
9252 &setup.options.verbose, "options.verbose"
9256 static void setSetupInfoToDefaults(struct SetupInfo *si)
9260 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9262 si->multiple_users = TRUE;
9265 si->sound_loops = TRUE;
9266 si->sound_music = TRUE;
9267 si->sound_simple = TRUE;
9269 si->scroll_delay = TRUE;
9270 si->forced_scroll_delay = FALSE;
9271 si->scroll_delay_value = STD_SCROLL_DELAY;
9272 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9273 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9274 si->fade_screens = TRUE;
9275 si->autorecord = TRUE;
9276 si->show_titlescreen = TRUE;
9277 si->quick_doors = FALSE;
9278 si->team_mode = FALSE;
9279 si->handicap = TRUE;
9280 si->skip_levels = TRUE;
9281 si->increment_levels = TRUE;
9282 si->auto_play_next_level = TRUE;
9283 si->count_score_after_game = TRUE;
9284 si->show_scores_after_game = TRUE;
9285 si->time_limit = TRUE;
9286 si->fullscreen = FALSE;
9287 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9288 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9289 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9290 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9291 si->ask_on_escape = TRUE;
9292 si->ask_on_escape_editor = TRUE;
9293 si->ask_on_game_over = TRUE;
9294 si->quick_switch = FALSE;
9295 si->input_on_focus = FALSE;
9296 si->prefer_aga_graphics = TRUE;
9297 si->prefer_lowpass_sounds = FALSE;
9298 si->prefer_extra_panel_items = TRUE;
9299 si->game_speed_extended = FALSE;
9300 si->game_frame_delay = GAME_FRAME_DELAY;
9301 si->sp_show_border_elements = FALSE;
9302 si->small_game_graphics = FALSE;
9303 si->show_snapshot_buttons = FALSE;
9305 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9306 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9307 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9309 si->override_level_graphics = FALSE;
9310 si->override_level_sounds = FALSE;
9311 si->override_level_music = FALSE;
9313 si->volume_simple = 100; // percent
9314 si->volume_loops = 100; // percent
9315 si->volume_music = 100; // percent
9317 si->network_mode = FALSE;
9318 si->network_player_nr = 0; // first player
9319 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9321 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9322 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9323 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9324 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9325 si->touch.draw_outlined = TRUE;
9326 si->touch.draw_pressed = TRUE;
9328 for (i = 0; i < 2; i++)
9330 char *default_grid_button[6][2] =
9336 { "111222", " vv " },
9337 { "111222", " vv " }
9339 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9340 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9341 int min_xsize = MIN(6, grid_xsize);
9342 int min_ysize = MIN(6, grid_ysize);
9343 int startx = grid_xsize - min_xsize;
9344 int starty = grid_ysize - min_ysize;
9347 // virtual buttons grid can only be set to defaults if video is initialized
9348 // (this will be repeated if virtual buttons are not loaded from setup file)
9349 if (video.initialized)
9351 si->touch.grid_xsize[i] = grid_xsize;
9352 si->touch.grid_ysize[i] = grid_ysize;
9356 si->touch.grid_xsize[i] = -1;
9357 si->touch.grid_ysize[i] = -1;
9360 for (x = 0; x < MAX_GRID_XSIZE; x++)
9361 for (y = 0; y < MAX_GRID_YSIZE; y++)
9362 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9364 for (x = 0; x < min_xsize; x++)
9365 for (y = 0; y < min_ysize; y++)
9366 si->touch.grid_button[i][x][starty + y] =
9367 default_grid_button[y][0][x];
9369 for (x = 0; x < min_xsize; x++)
9370 for (y = 0; y < min_ysize; y++)
9371 si->touch.grid_button[i][startx + x][starty + y] =
9372 default_grid_button[y][1][x];
9375 si->touch.grid_initialized = video.initialized;
9377 si->editor.el_boulderdash = TRUE;
9378 si->editor.el_emerald_mine = TRUE;
9379 si->editor.el_emerald_mine_club = TRUE;
9380 si->editor.el_more = TRUE;
9381 si->editor.el_sokoban = TRUE;
9382 si->editor.el_supaplex = TRUE;
9383 si->editor.el_diamond_caves = TRUE;
9384 si->editor.el_dx_boulderdash = TRUE;
9386 si->editor.el_mirror_magic = TRUE;
9387 si->editor.el_deflektor = TRUE;
9389 si->editor.el_chars = TRUE;
9390 si->editor.el_steel_chars = TRUE;
9392 si->editor.el_classic = TRUE;
9393 si->editor.el_custom = TRUE;
9395 si->editor.el_user_defined = FALSE;
9396 si->editor.el_dynamic = TRUE;
9398 si->editor.el_headlines = TRUE;
9400 si->editor.show_element_token = FALSE;
9402 si->editor.show_read_only_warning = TRUE;
9404 si->editor.use_template_for_new_levels = TRUE;
9406 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9407 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9408 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9410 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9411 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9412 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9413 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9414 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9416 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9417 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9418 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9419 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9420 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9421 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9423 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9424 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9425 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9427 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9428 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9429 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9430 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9432 for (i = 0; i < MAX_PLAYERS; i++)
9434 si->input[i].use_joystick = FALSE;
9435 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9436 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9437 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9438 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9439 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9440 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9441 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9442 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9443 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9444 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9445 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9446 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9447 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9448 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9449 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9452 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9453 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9454 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9455 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9457 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9458 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9459 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9460 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9461 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9462 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9463 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9465 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9467 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9468 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9469 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9471 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9472 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9473 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9475 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9476 si->internal.choose_from_top_leveldir = FALSE;
9477 si->internal.show_scaling_in_title = TRUE;
9478 si->internal.create_user_levelset = TRUE;
9480 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9481 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9483 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9484 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9485 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9486 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9487 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9488 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9489 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9490 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9491 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9492 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9494 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9495 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9496 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9497 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9498 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9499 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9500 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9501 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9502 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9503 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9505 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9506 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9508 si->debug.show_frames_per_second = FALSE;
9510 si->debug.xsn_mode = AUTO;
9511 si->debug.xsn_percent = 0;
9513 si->options.verbose = FALSE;
9515 #if defined(PLATFORM_ANDROID)
9516 si->fullscreen = TRUE;
9519 setHideSetupEntry(&setup.debug.xsn_mode);
9522 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9524 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9527 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9529 si->editor_cascade.el_bd = TRUE;
9530 si->editor_cascade.el_em = TRUE;
9531 si->editor_cascade.el_emc = TRUE;
9532 si->editor_cascade.el_rnd = TRUE;
9533 si->editor_cascade.el_sb = TRUE;
9534 si->editor_cascade.el_sp = TRUE;
9535 si->editor_cascade.el_dc = TRUE;
9536 si->editor_cascade.el_dx = TRUE;
9538 si->editor_cascade.el_mm = TRUE;
9539 si->editor_cascade.el_df = TRUE;
9541 si->editor_cascade.el_chars = FALSE;
9542 si->editor_cascade.el_steel_chars = FALSE;
9543 si->editor_cascade.el_ce = FALSE;
9544 si->editor_cascade.el_ge = FALSE;
9545 si->editor_cascade.el_ref = FALSE;
9546 si->editor_cascade.el_user = FALSE;
9547 si->editor_cascade.el_dynamic = FALSE;
9550 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9552 static char *getHideSetupToken(void *setup_value)
9554 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9556 if (setup_value != NULL)
9557 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9559 return hide_setup_token;
9562 void setHideSetupEntry(void *setup_value)
9564 char *hide_setup_token = getHideSetupToken(setup_value);
9566 if (hide_setup_hash == NULL)
9567 hide_setup_hash = newSetupFileHash();
9569 if (setup_value != NULL)
9570 setHashEntry(hide_setup_hash, hide_setup_token, "");
9573 void removeHideSetupEntry(void *setup_value)
9575 char *hide_setup_token = getHideSetupToken(setup_value);
9577 if (setup_value != NULL)
9578 removeHashEntry(hide_setup_hash, hide_setup_token);
9581 boolean hideSetupEntry(void *setup_value)
9583 char *hide_setup_token = getHideSetupToken(setup_value);
9585 return (setup_value != NULL &&
9586 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9589 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9590 struct TokenInfo *token_info,
9591 int token_nr, char *token_text)
9593 char *token_hide_text = getStringCat2(token_text, ".hide");
9594 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9596 // set the value of this setup option in the setup option structure
9597 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9599 // check if this setup option should be hidden in the setup menu
9600 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9601 setHideSetupEntry(token_info[token_nr].value);
9603 free(token_hide_text);
9606 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9607 struct TokenInfo *token_info,
9610 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9611 token_info[token_nr].text);
9614 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9618 if (!setup_file_hash)
9621 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9622 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9624 setup.touch.grid_initialized = TRUE;
9625 for (i = 0; i < 2; i++)
9627 int grid_xsize = setup.touch.grid_xsize[i];
9628 int grid_ysize = setup.touch.grid_ysize[i];
9631 // if virtual buttons are not loaded from setup file, repeat initializing
9632 // virtual buttons grid with default values later when video is initialized
9633 if (grid_xsize == -1 ||
9636 setup.touch.grid_initialized = FALSE;
9641 for (y = 0; y < grid_ysize; y++)
9643 char token_string[MAX_LINE_LEN];
9645 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9647 char *value_string = getHashEntry(setup_file_hash, token_string);
9649 if (value_string == NULL)
9652 for (x = 0; x < grid_xsize; x++)
9654 char c = value_string[x];
9656 setup.touch.grid_button[i][x][y] =
9657 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9662 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9663 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9665 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9666 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9668 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9672 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9674 setup_input = setup.input[pnr];
9675 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9677 char full_token[100];
9679 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9680 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9683 setup.input[pnr] = setup_input;
9686 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9687 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9689 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9690 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9692 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9693 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9695 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9696 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9698 setHideRelatedSetupEntries();
9701 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9705 if (!setup_file_hash)
9708 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9709 setSetupInfo(auto_setup_tokens, i,
9710 getHashEntry(setup_file_hash,
9711 auto_setup_tokens[i].text));
9714 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9718 if (!setup_file_hash)
9721 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9722 setSetupInfo(editor_cascade_setup_tokens, i,
9723 getHashEntry(setup_file_hash,
9724 editor_cascade_setup_tokens[i].text));
9727 void LoadUserNames(void)
9729 int last_user_nr = user.nr;
9732 if (global.user_names != NULL)
9734 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9735 checked_free(global.user_names[i]);
9737 checked_free(global.user_names);
9740 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9742 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9746 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9748 if (setup_file_hash)
9750 char *player_name = getHashEntry(setup_file_hash, "player_name");
9752 global.user_names[i] = getFixedUserName(player_name);
9754 freeSetupFileHash(setup_file_hash);
9757 if (global.user_names[i] == NULL)
9758 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9761 user.nr = last_user_nr;
9764 void LoadSetupFromFilename(char *filename)
9766 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9768 if (setup_file_hash)
9770 decodeSetupFileHash(setup_file_hash);
9772 freeSetupFileHash(setup_file_hash);
9776 Debug("setup", "using default setup values");
9780 static void LoadSetup_SpecialPostProcessing(void)
9782 char *player_name_new;
9784 // needed to work around problems with fixed length strings
9785 player_name_new = getFixedUserName(setup.player_name);
9786 free(setup.player_name);
9787 setup.player_name = player_name_new;
9789 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9790 if (setup.scroll_delay == FALSE)
9792 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9793 setup.scroll_delay = TRUE; // now always "on"
9796 // make sure that scroll delay value stays inside valid range
9797 setup.scroll_delay_value =
9798 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9801 void LoadSetup(void)
9805 // always start with reliable default values
9806 setSetupInfoToDefaults(&setup);
9808 // try to load setup values from default setup file
9809 filename = getDefaultSetupFilename();
9811 if (fileExists(filename))
9812 LoadSetupFromFilename(filename);
9814 // try to load setup values from user setup file
9815 filename = getSetupFilename();
9817 LoadSetupFromFilename(filename);
9819 LoadSetup_SpecialPostProcessing();
9822 void LoadSetup_AutoSetup(void)
9824 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9825 SetupFileHash *setup_file_hash = NULL;
9827 // always start with reliable default values
9828 setSetupInfoToDefaults_AutoSetup(&setup);
9830 setup_file_hash = loadSetupFileHash(filename);
9832 if (setup_file_hash)
9834 decodeSetupFileHash_AutoSetup(setup_file_hash);
9836 freeSetupFileHash(setup_file_hash);
9842 void LoadSetup_EditorCascade(void)
9844 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9845 SetupFileHash *setup_file_hash = NULL;
9847 // always start with reliable default values
9848 setSetupInfoToDefaults_EditorCascade(&setup);
9850 setup_file_hash = loadSetupFileHash(filename);
9852 if (setup_file_hash)
9854 decodeSetupFileHash_EditorCascade(setup_file_hash);
9856 freeSetupFileHash(setup_file_hash);
9862 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9865 char mapping_guid[MAX_LINE_LEN];
9866 char *mapping_start, *mapping_end;
9868 // get GUID from game controller mapping line: copy complete line
9869 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9870 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9872 // get GUID from game controller mapping line: cut after GUID part
9873 mapping_start = strchr(mapping_guid, ',');
9874 if (mapping_start != NULL)
9875 *mapping_start = '\0';
9877 // cut newline from game controller mapping line
9878 mapping_end = strchr(mapping_line, '\n');
9879 if (mapping_end != NULL)
9880 *mapping_end = '\0';
9882 // add mapping entry to game controller mappings hash
9883 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9886 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9891 if (!(file = fopen(filename, MODE_READ)))
9893 Warn("cannot read game controller mappings file '%s'", filename);
9900 char line[MAX_LINE_LEN];
9902 if (!fgets(line, MAX_LINE_LEN, file))
9905 addGameControllerMappingToHash(mappings_hash, line);
9911 void SaveSetup(void)
9913 char *filename = getSetupFilename();
9917 InitUserDataDirectory();
9919 if (!(file = fopen(filename, MODE_WRITE)))
9921 Warn("cannot write setup file '%s'", filename);
9926 fprintFileHeader(file, SETUP_FILENAME);
9928 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9930 // just to make things nicer :)
9931 if (global_setup_tokens[i].value == &setup.multiple_users ||
9932 global_setup_tokens[i].value == &setup.sound ||
9933 global_setup_tokens[i].value == &setup.graphics_set ||
9934 global_setup_tokens[i].value == &setup.volume_simple ||
9935 global_setup_tokens[i].value == &setup.network_mode ||
9936 global_setup_tokens[i].value == &setup.touch.control_type ||
9937 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9938 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9939 fprintf(file, "\n");
9941 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9944 for (i = 0; i < 2; i++)
9946 int grid_xsize = setup.touch.grid_xsize[i];
9947 int grid_ysize = setup.touch.grid_ysize[i];
9950 fprintf(file, "\n");
9952 for (y = 0; y < grid_ysize; y++)
9954 char token_string[MAX_LINE_LEN];
9955 char value_string[MAX_LINE_LEN];
9957 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9959 for (x = 0; x < grid_xsize; x++)
9961 char c = setup.touch.grid_button[i][x][y];
9963 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9966 value_string[grid_xsize] = '\0';
9968 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9972 fprintf(file, "\n");
9973 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9974 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9976 fprintf(file, "\n");
9977 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9978 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9980 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9984 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9985 fprintf(file, "\n");
9987 setup_input = setup.input[pnr];
9988 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9989 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9992 fprintf(file, "\n");
9993 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9994 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9996 // (internal setup values not saved to user setup file)
9998 fprintf(file, "\n");
9999 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10000 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10001 setup.debug.xsn_mode != AUTO)
10002 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10004 fprintf(file, "\n");
10005 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10006 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10010 SetFilePermissions(filename, PERMS_PRIVATE);
10013 void SaveSetup_AutoSetup(void)
10015 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10019 InitUserDataDirectory();
10021 if (!(file = fopen(filename, MODE_WRITE)))
10023 Warn("cannot write auto setup file '%s'", filename);
10030 fprintFileHeader(file, AUTOSETUP_FILENAME);
10032 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10033 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10037 SetFilePermissions(filename, PERMS_PRIVATE);
10042 void SaveSetup_EditorCascade(void)
10044 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10048 InitUserDataDirectory();
10050 if (!(file = fopen(filename, MODE_WRITE)))
10052 Warn("cannot write editor cascade state file '%s'", filename);
10059 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10061 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10062 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10066 SetFilePermissions(filename, PERMS_PRIVATE);
10071 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10076 if (!(file = fopen(filename, MODE_WRITE)))
10078 Warn("cannot write game controller mappings file '%s'", filename);
10083 BEGIN_HASH_ITERATION(mappings_hash, itr)
10085 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10087 END_HASH_ITERATION(mappings_hash, itr)
10092 void SaveSetup_AddGameControllerMapping(char *mapping)
10094 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10095 SetupFileHash *mappings_hash = newSetupFileHash();
10097 InitUserDataDirectory();
10099 // load existing personal game controller mappings
10100 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10102 // add new mapping to personal game controller mappings
10103 addGameControllerMappingToHash(mappings_hash, mapping);
10105 // save updated personal game controller mappings
10106 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10108 freeSetupFileHash(mappings_hash);
10112 void LoadCustomElementDescriptions(void)
10114 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10115 SetupFileHash *setup_file_hash;
10118 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10120 if (element_info[i].custom_description != NULL)
10122 free(element_info[i].custom_description);
10123 element_info[i].custom_description = NULL;
10127 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10130 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10132 char *token = getStringCat2(element_info[i].token_name, ".name");
10133 char *value = getHashEntry(setup_file_hash, token);
10136 element_info[i].custom_description = getStringCopy(value);
10141 freeSetupFileHash(setup_file_hash);
10144 static int getElementFromToken(char *token)
10146 char *value = getHashEntry(element_token_hash, token);
10149 return atoi(value);
10151 Warn("unknown element token '%s'", token);
10153 return EL_UNDEFINED;
10156 void FreeGlobalAnimEventInfo(void)
10158 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10160 if (gaei->event_list == NULL)
10165 for (i = 0; i < gaei->num_event_lists; i++)
10167 checked_free(gaei->event_list[i]->event_value);
10168 checked_free(gaei->event_list[i]);
10171 checked_free(gaei->event_list);
10173 gaei->event_list = NULL;
10174 gaei->num_event_lists = 0;
10177 static int AddGlobalAnimEventList(void)
10179 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10180 int list_pos = gaei->num_event_lists++;
10182 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10183 sizeof(struct GlobalAnimEventListInfo *));
10185 gaei->event_list[list_pos] =
10186 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10188 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10190 gaeli->event_value = NULL;
10191 gaeli->num_event_values = 0;
10196 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10198 // do not add empty global animation events
10199 if (event_value == ANIM_EVENT_NONE)
10202 // if list position is undefined, create new list
10203 if (list_pos == ANIM_EVENT_UNDEFINED)
10204 list_pos = AddGlobalAnimEventList();
10206 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10207 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10208 int value_pos = gaeli->num_event_values++;
10210 gaeli->event_value = checked_realloc(gaeli->event_value,
10211 gaeli->num_event_values * sizeof(int *));
10213 gaeli->event_value[value_pos] = event_value;
10218 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10220 if (list_pos == ANIM_EVENT_UNDEFINED)
10221 return ANIM_EVENT_NONE;
10223 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10224 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10226 return gaeli->event_value[value_pos];
10229 int GetGlobalAnimEventValueCount(int list_pos)
10231 if (list_pos == ANIM_EVENT_UNDEFINED)
10234 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10235 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10237 return gaeli->num_event_values;
10240 // This function checks if a string <s> of the format "string1, string2, ..."
10241 // exactly contains a string <s_contained>.
10243 static boolean string_has_parameter(char *s, char *s_contained)
10247 if (s == NULL || s_contained == NULL)
10250 if (strlen(s_contained) > strlen(s))
10253 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10255 char next_char = s[strlen(s_contained)];
10257 // check if next character is delimiter or whitespace
10258 return (next_char == ',' || next_char == '\0' ||
10259 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10262 // check if string contains another parameter string after a comma
10263 substring = strchr(s, ',');
10264 if (substring == NULL) // string does not contain a comma
10267 // advance string pointer to next character after the comma
10270 // skip potential whitespaces after the comma
10271 while (*substring == ' ' || *substring == '\t')
10274 return string_has_parameter(substring, s_contained);
10277 static int get_anim_parameter_value(char *s)
10279 int event_value[] =
10287 char *pattern_1[] =
10295 char *pattern_2 = ".part_";
10296 char *matching_char = NULL;
10298 int pattern_1_len = 0;
10299 int result = ANIM_EVENT_NONE;
10302 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10304 matching_char = strstr(s_ptr, pattern_1[i]);
10305 pattern_1_len = strlen(pattern_1[i]);
10306 result = event_value[i];
10308 if (matching_char != NULL)
10312 if (matching_char == NULL)
10313 return ANIM_EVENT_NONE;
10315 s_ptr = matching_char + pattern_1_len;
10317 // check for main animation number ("anim_X" or "anim_XX")
10318 if (*s_ptr >= '0' && *s_ptr <= '9')
10320 int gic_anim_nr = (*s_ptr++ - '0');
10322 if (*s_ptr >= '0' && *s_ptr <= '9')
10323 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10325 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10326 return ANIM_EVENT_NONE;
10328 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10332 // invalid main animation number specified
10334 return ANIM_EVENT_NONE;
10337 // check for animation part number ("part_X" or "part_XX") (optional)
10338 if (strPrefix(s_ptr, pattern_2))
10340 s_ptr += strlen(pattern_2);
10342 if (*s_ptr >= '0' && *s_ptr <= '9')
10344 int gic_part_nr = (*s_ptr++ - '0');
10346 if (*s_ptr >= '0' && *s_ptr <= '9')
10347 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10349 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10350 return ANIM_EVENT_NONE;
10352 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10356 // invalid animation part number specified
10358 return ANIM_EVENT_NONE;
10362 // discard result if next character is neither delimiter nor whitespace
10363 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10364 *s_ptr == ' ' || *s_ptr == '\t'))
10365 return ANIM_EVENT_NONE;
10370 static int get_anim_parameter_values(char *s)
10372 int list_pos = ANIM_EVENT_UNDEFINED;
10373 int event_value = ANIM_EVENT_DEFAULT;
10375 if (string_has_parameter(s, "any"))
10376 event_value |= ANIM_EVENT_ANY;
10378 if (string_has_parameter(s, "click:self") ||
10379 string_has_parameter(s, "click") ||
10380 string_has_parameter(s, "self"))
10381 event_value |= ANIM_EVENT_SELF;
10383 if (string_has_parameter(s, "unclick:any"))
10384 event_value |= ANIM_EVENT_UNCLICK_ANY;
10386 // if animation event found, add it to global animation event list
10387 if (event_value != ANIM_EVENT_NONE)
10388 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10392 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10393 event_value = get_anim_parameter_value(s);
10395 // if animation event found, add it to global animation event list
10396 if (event_value != ANIM_EVENT_NONE)
10397 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10399 // continue with next part of the string, starting with next comma
10400 s = strchr(s + 1, ',');
10406 static int get_anim_action_parameter_value(char *token)
10408 // check most common default case first to massively speed things up
10409 if (strEqual(token, ARG_UNDEFINED))
10410 return ANIM_EVENT_ACTION_NONE;
10412 int result = getImageIDFromToken(token);
10416 char *gfx_token = getStringCat2("gfx.", token);
10418 result = getImageIDFromToken(gfx_token);
10420 checked_free(gfx_token);
10425 Key key = getKeyFromX11KeyName(token);
10427 if (key != KSYM_UNDEFINED)
10428 result = -(int)key;
10432 result = ANIM_EVENT_ACTION_NONE;
10437 int get_parameter_value(char *value_raw, char *suffix, int type)
10439 char *value = getStringToLower(value_raw);
10440 int result = 0; // probably a save default value
10442 if (strEqual(suffix, ".direction"))
10444 result = (strEqual(value, "left") ? MV_LEFT :
10445 strEqual(value, "right") ? MV_RIGHT :
10446 strEqual(value, "up") ? MV_UP :
10447 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10449 else if (strEqual(suffix, ".position"))
10451 result = (strEqual(value, "left") ? POS_LEFT :
10452 strEqual(value, "right") ? POS_RIGHT :
10453 strEqual(value, "top") ? POS_TOP :
10454 strEqual(value, "upper") ? POS_UPPER :
10455 strEqual(value, "middle") ? POS_MIDDLE :
10456 strEqual(value, "lower") ? POS_LOWER :
10457 strEqual(value, "bottom") ? POS_BOTTOM :
10458 strEqual(value, "any") ? POS_ANY :
10459 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10461 else if (strEqual(suffix, ".align"))
10463 result = (strEqual(value, "left") ? ALIGN_LEFT :
10464 strEqual(value, "right") ? ALIGN_RIGHT :
10465 strEqual(value, "center") ? ALIGN_CENTER :
10466 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10468 else if (strEqual(suffix, ".valign"))
10470 result = (strEqual(value, "top") ? VALIGN_TOP :
10471 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10472 strEqual(value, "middle") ? VALIGN_MIDDLE :
10473 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10475 else if (strEqual(suffix, ".anim_mode"))
10477 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10478 string_has_parameter(value, "loop") ? ANIM_LOOP :
10479 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10480 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10481 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10482 string_has_parameter(value, "random") ? ANIM_RANDOM :
10483 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10484 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10485 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10486 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10487 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10488 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10489 string_has_parameter(value, "all") ? ANIM_ALL :
10492 if (string_has_parameter(value, "once"))
10493 result |= ANIM_ONCE;
10495 if (string_has_parameter(value, "reverse"))
10496 result |= ANIM_REVERSE;
10498 if (string_has_parameter(value, "opaque_player"))
10499 result |= ANIM_OPAQUE_PLAYER;
10501 if (string_has_parameter(value, "static_panel"))
10502 result |= ANIM_STATIC_PANEL;
10504 else if (strEqual(suffix, ".init_event") ||
10505 strEqual(suffix, ".anim_event"))
10507 result = get_anim_parameter_values(value);
10509 else if (strEqual(suffix, ".init_delay_action") ||
10510 strEqual(suffix, ".anim_delay_action") ||
10511 strEqual(suffix, ".post_delay_action") ||
10512 strEqual(suffix, ".init_event_action") ||
10513 strEqual(suffix, ".anim_event_action"))
10515 result = get_anim_action_parameter_value(value_raw);
10517 else if (strEqual(suffix, ".class"))
10519 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10520 get_hash_from_key(value));
10522 else if (strEqual(suffix, ".style"))
10524 result = STYLE_DEFAULT;
10526 if (string_has_parameter(value, "accurate_borders"))
10527 result |= STYLE_ACCURATE_BORDERS;
10529 if (string_has_parameter(value, "inner_corners"))
10530 result |= STYLE_INNER_CORNERS;
10532 if (string_has_parameter(value, "reverse"))
10533 result |= STYLE_REVERSE;
10535 if (string_has_parameter(value, "leftmost_position"))
10536 result |= STYLE_LEFTMOST_POSITION;
10538 if (string_has_parameter(value, "block_clicks"))
10539 result |= STYLE_BLOCK;
10541 if (string_has_parameter(value, "passthrough_clicks"))
10542 result |= STYLE_PASSTHROUGH;
10544 if (string_has_parameter(value, "multiple_actions"))
10545 result |= STYLE_MULTIPLE_ACTIONS;
10547 else if (strEqual(suffix, ".fade_mode"))
10549 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10550 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10551 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10552 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10553 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10554 FADE_MODE_DEFAULT);
10556 else if (strEqual(suffix, ".auto_delay_unit"))
10558 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10559 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10560 AUTO_DELAY_UNIT_DEFAULT);
10562 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10564 result = gfx.get_font_from_token_function(value);
10566 else // generic parameter of type integer or boolean
10568 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10569 type == TYPE_INTEGER ? get_integer_from_string(value) :
10570 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10571 ARG_UNDEFINED_VALUE);
10579 static int get_token_parameter_value(char *token, char *value_raw)
10583 if (token == NULL || value_raw == NULL)
10584 return ARG_UNDEFINED_VALUE;
10586 suffix = strrchr(token, '.');
10587 if (suffix == NULL)
10590 if (strEqual(suffix, ".element"))
10591 return getElementFromToken(value_raw);
10593 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10594 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10597 void InitMenuDesignSettings_Static(void)
10601 // always start with reliable default values from static default config
10602 for (i = 0; image_config_vars[i].token != NULL; i++)
10604 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10607 *image_config_vars[i].value =
10608 get_token_parameter_value(image_config_vars[i].token, value);
10612 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10616 // the following initializes hierarchical values from static configuration
10618 // special case: initialize "ARG_DEFAULT" values in static default config
10619 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10620 titlescreen_initial_first_default.fade_mode =
10621 title_initial_first_default.fade_mode;
10622 titlescreen_initial_first_default.fade_delay =
10623 title_initial_first_default.fade_delay;
10624 titlescreen_initial_first_default.post_delay =
10625 title_initial_first_default.post_delay;
10626 titlescreen_initial_first_default.auto_delay =
10627 title_initial_first_default.auto_delay;
10628 titlescreen_initial_first_default.auto_delay_unit =
10629 title_initial_first_default.auto_delay_unit;
10630 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10631 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10632 titlescreen_first_default.post_delay = title_first_default.post_delay;
10633 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10634 titlescreen_first_default.auto_delay_unit =
10635 title_first_default.auto_delay_unit;
10636 titlemessage_initial_first_default.fade_mode =
10637 title_initial_first_default.fade_mode;
10638 titlemessage_initial_first_default.fade_delay =
10639 title_initial_first_default.fade_delay;
10640 titlemessage_initial_first_default.post_delay =
10641 title_initial_first_default.post_delay;
10642 titlemessage_initial_first_default.auto_delay =
10643 title_initial_first_default.auto_delay;
10644 titlemessage_initial_first_default.auto_delay_unit =
10645 title_initial_first_default.auto_delay_unit;
10646 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10647 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10648 titlemessage_first_default.post_delay = title_first_default.post_delay;
10649 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10650 titlemessage_first_default.auto_delay_unit =
10651 title_first_default.auto_delay_unit;
10653 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10654 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10655 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10656 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10657 titlescreen_initial_default.auto_delay_unit =
10658 title_initial_default.auto_delay_unit;
10659 titlescreen_default.fade_mode = title_default.fade_mode;
10660 titlescreen_default.fade_delay = title_default.fade_delay;
10661 titlescreen_default.post_delay = title_default.post_delay;
10662 titlescreen_default.auto_delay = title_default.auto_delay;
10663 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10664 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10665 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10666 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10667 titlemessage_initial_default.auto_delay_unit =
10668 title_initial_default.auto_delay_unit;
10669 titlemessage_default.fade_mode = title_default.fade_mode;
10670 titlemessage_default.fade_delay = title_default.fade_delay;
10671 titlemessage_default.post_delay = title_default.post_delay;
10672 titlemessage_default.auto_delay = title_default.auto_delay;
10673 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10675 // special case: initialize "ARG_DEFAULT" values in static default config
10676 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10677 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10679 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10680 titlescreen_first[i] = titlescreen_first_default;
10681 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10682 titlemessage_first[i] = titlemessage_first_default;
10684 titlescreen_initial[i] = titlescreen_initial_default;
10685 titlescreen[i] = titlescreen_default;
10686 titlemessage_initial[i] = titlemessage_initial_default;
10687 titlemessage[i] = titlemessage_default;
10690 // special case: initialize "ARG_DEFAULT" values in static default config
10691 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10692 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10694 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10697 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10698 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10699 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10702 // special case: initialize "ARG_DEFAULT" values in static default config
10703 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10704 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10706 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10707 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10708 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10710 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10713 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10717 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10721 struct XY *dst, *src;
10723 game_buttons_xy[] =
10725 { &game.button.save, &game.button.stop },
10726 { &game.button.pause2, &game.button.pause },
10727 { &game.button.load, &game.button.play },
10728 { &game.button.undo, &game.button.stop },
10729 { &game.button.redo, &game.button.play },
10735 // special case: initialize later added SETUP list size from LEVELS value
10736 if (menu.list_size[GAME_MODE_SETUP] == -1)
10737 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10739 // set default position for snapshot buttons to stop/pause/play buttons
10740 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10741 if ((*game_buttons_xy[i].dst).x == -1 &&
10742 (*game_buttons_xy[i].dst).y == -1)
10743 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10745 // --------------------------------------------------------------------------
10746 // dynamic viewports (including playfield margins, borders and alignments)
10747 // --------------------------------------------------------------------------
10749 // dynamic viewports currently only supported for landscape mode
10750 int display_width = MAX(video.display_width, video.display_height);
10751 int display_height = MIN(video.display_width, video.display_height);
10753 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10755 struct RectWithBorder *vp_window = &viewport.window[i];
10756 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10757 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10758 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10759 boolean dynamic_window_width = (vp_window->min_width != -1);
10760 boolean dynamic_window_height = (vp_window->min_height != -1);
10761 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10762 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10764 // adjust window size if min/max width/height is specified
10766 if (vp_window->min_width != -1)
10768 int window_width = display_width;
10770 // when using static window height, use aspect ratio of display
10771 if (vp_window->min_height == -1)
10772 window_width = vp_window->height * display_width / display_height;
10774 vp_window->width = MAX(vp_window->min_width, window_width);
10777 if (vp_window->min_height != -1)
10779 int window_height = display_height;
10781 // when using static window width, use aspect ratio of display
10782 if (vp_window->min_width == -1)
10783 window_height = vp_window->width * display_height / display_width;
10785 vp_window->height = MAX(vp_window->min_height, window_height);
10788 if (vp_window->max_width != -1)
10789 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10791 if (vp_window->max_height != -1)
10792 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10794 int playfield_width = vp_window->width;
10795 int playfield_height = vp_window->height;
10797 // adjust playfield size and position according to specified margins
10799 playfield_width -= vp_playfield->margin_left;
10800 playfield_width -= vp_playfield->margin_right;
10802 playfield_height -= vp_playfield->margin_top;
10803 playfield_height -= vp_playfield->margin_bottom;
10805 // adjust playfield size if min/max width/height is specified
10807 if (vp_playfield->min_width != -1)
10808 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10810 if (vp_playfield->min_height != -1)
10811 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10813 if (vp_playfield->max_width != -1)
10814 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10816 if (vp_playfield->max_height != -1)
10817 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10819 // adjust playfield position according to specified alignment
10821 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10822 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10823 else if (vp_playfield->align == ALIGN_CENTER)
10824 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10825 else if (vp_playfield->align == ALIGN_RIGHT)
10826 vp_playfield->x += playfield_width - vp_playfield->width;
10828 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10829 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10830 else if (vp_playfield->valign == VALIGN_MIDDLE)
10831 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10832 else if (vp_playfield->valign == VALIGN_BOTTOM)
10833 vp_playfield->y += playfield_height - vp_playfield->height;
10835 vp_playfield->x += vp_playfield->margin_left;
10836 vp_playfield->y += vp_playfield->margin_top;
10838 // adjust individual playfield borders if only default border is specified
10840 if (vp_playfield->border_left == -1)
10841 vp_playfield->border_left = vp_playfield->border_size;
10842 if (vp_playfield->border_right == -1)
10843 vp_playfield->border_right = vp_playfield->border_size;
10844 if (vp_playfield->border_top == -1)
10845 vp_playfield->border_top = vp_playfield->border_size;
10846 if (vp_playfield->border_bottom == -1)
10847 vp_playfield->border_bottom = vp_playfield->border_size;
10849 // set dynamic playfield borders if borders are specified as undefined
10850 // (but only if window size was dynamic and playfield size was static)
10852 if (dynamic_window_width && !dynamic_playfield_width)
10854 if (vp_playfield->border_left == -1)
10856 vp_playfield->border_left = (vp_playfield->x -
10857 vp_playfield->margin_left);
10858 vp_playfield->x -= vp_playfield->border_left;
10859 vp_playfield->width += vp_playfield->border_left;
10862 if (vp_playfield->border_right == -1)
10864 vp_playfield->border_right = (vp_window->width -
10866 vp_playfield->width -
10867 vp_playfield->margin_right);
10868 vp_playfield->width += vp_playfield->border_right;
10872 if (dynamic_window_height && !dynamic_playfield_height)
10874 if (vp_playfield->border_top == -1)
10876 vp_playfield->border_top = (vp_playfield->y -
10877 vp_playfield->margin_top);
10878 vp_playfield->y -= vp_playfield->border_top;
10879 vp_playfield->height += vp_playfield->border_top;
10882 if (vp_playfield->border_bottom == -1)
10884 vp_playfield->border_bottom = (vp_window->height -
10886 vp_playfield->height -
10887 vp_playfield->margin_bottom);
10888 vp_playfield->height += vp_playfield->border_bottom;
10892 // adjust playfield size to be a multiple of a defined alignment tile size
10894 int align_size = vp_playfield->align_size;
10895 int playfield_xtiles = vp_playfield->width / align_size;
10896 int playfield_ytiles = vp_playfield->height / align_size;
10897 int playfield_width_corrected = playfield_xtiles * align_size;
10898 int playfield_height_corrected = playfield_ytiles * align_size;
10899 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10900 i == GFX_SPECIAL_ARG_EDITOR);
10902 if (is_playfield_mode &&
10903 dynamic_playfield_width &&
10904 vp_playfield->width != playfield_width_corrected)
10906 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10908 vp_playfield->width = playfield_width_corrected;
10910 if (vp_playfield->align == ALIGN_LEFT)
10912 vp_playfield->border_left += playfield_xdiff;
10914 else if (vp_playfield->align == ALIGN_RIGHT)
10916 vp_playfield->border_right += playfield_xdiff;
10918 else if (vp_playfield->align == ALIGN_CENTER)
10920 int border_left_diff = playfield_xdiff / 2;
10921 int border_right_diff = playfield_xdiff - border_left_diff;
10923 vp_playfield->border_left += border_left_diff;
10924 vp_playfield->border_right += border_right_diff;
10928 if (is_playfield_mode &&
10929 dynamic_playfield_height &&
10930 vp_playfield->height != playfield_height_corrected)
10932 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10934 vp_playfield->height = playfield_height_corrected;
10936 if (vp_playfield->valign == VALIGN_TOP)
10938 vp_playfield->border_top += playfield_ydiff;
10940 else if (vp_playfield->align == VALIGN_BOTTOM)
10942 vp_playfield->border_right += playfield_ydiff;
10944 else if (vp_playfield->align == VALIGN_MIDDLE)
10946 int border_top_diff = playfield_ydiff / 2;
10947 int border_bottom_diff = playfield_ydiff - border_top_diff;
10949 vp_playfield->border_top += border_top_diff;
10950 vp_playfield->border_bottom += border_bottom_diff;
10954 // adjust door positions according to specified alignment
10956 for (j = 0; j < 2; j++)
10958 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10960 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10961 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10962 else if (vp_door->align == ALIGN_CENTER)
10963 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10964 else if (vp_door->align == ALIGN_RIGHT)
10965 vp_door->x += vp_window->width - vp_door->width;
10967 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10968 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10969 else if (vp_door->valign == VALIGN_MIDDLE)
10970 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10971 else if (vp_door->valign == VALIGN_BOTTOM)
10972 vp_door->y += vp_window->height - vp_door->height;
10977 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10981 struct XYTileSize *dst, *src;
10984 editor_buttons_xy[] =
10987 &editor.button.element_left, &editor.palette.element_left,
10988 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10991 &editor.button.element_middle, &editor.palette.element_middle,
10992 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10995 &editor.button.element_right, &editor.palette.element_right,
10996 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11003 // set default position for element buttons to element graphics
11004 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11006 if ((*editor_buttons_xy[i].dst).x == -1 &&
11007 (*editor_buttons_xy[i].dst).y == -1)
11009 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11011 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11013 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11017 // adjust editor palette rows and columns if specified to be dynamic
11019 if (editor.palette.cols == -1)
11021 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11022 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11023 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11025 editor.palette.cols = (vp_width - sc_width) / bt_width;
11027 if (editor.palette.x == -1)
11029 int palette_width = editor.palette.cols * bt_width + sc_width;
11031 editor.palette.x = (vp_width - palette_width) / 2;
11035 if (editor.palette.rows == -1)
11037 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11038 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11039 int tx_height = getFontHeight(FONT_TEXT_2);
11041 editor.palette.rows = (vp_height - tx_height) / bt_height;
11043 if (editor.palette.y == -1)
11045 int palette_height = editor.palette.rows * bt_height + tx_height;
11047 editor.palette.y = (vp_height - palette_height) / 2;
11052 static void LoadMenuDesignSettingsFromFilename(char *filename)
11054 static struct TitleFadingInfo tfi;
11055 static struct TitleMessageInfo tmi;
11056 static struct TokenInfo title_tokens[] =
11058 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11059 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11060 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11061 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11062 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11066 static struct TokenInfo titlemessage_tokens[] =
11068 { TYPE_INTEGER, &tmi.x, ".x" },
11069 { TYPE_INTEGER, &tmi.y, ".y" },
11070 { TYPE_INTEGER, &tmi.width, ".width" },
11071 { TYPE_INTEGER, &tmi.height, ".height" },
11072 { TYPE_INTEGER, &tmi.chars, ".chars" },
11073 { TYPE_INTEGER, &tmi.lines, ".lines" },
11074 { TYPE_INTEGER, &tmi.align, ".align" },
11075 { TYPE_INTEGER, &tmi.valign, ".valign" },
11076 { TYPE_INTEGER, &tmi.font, ".font" },
11077 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11078 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11079 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11080 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11081 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11082 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11083 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11084 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11085 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11091 struct TitleFadingInfo *info;
11096 // initialize first titles from "enter screen" definitions, if defined
11097 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11098 { &title_first_default, "menu.enter_screen.TITLE" },
11100 // initialize title screens from "next screen" definitions, if defined
11101 { &title_initial_default, "menu.next_screen.TITLE" },
11102 { &title_default, "menu.next_screen.TITLE" },
11108 struct TitleMessageInfo *array;
11111 titlemessage_arrays[] =
11113 // initialize first titles from "enter screen" definitions, if defined
11114 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11115 { titlescreen_first, "menu.enter_screen.TITLE" },
11116 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11117 { titlemessage_first, "menu.enter_screen.TITLE" },
11119 // initialize titles from "next screen" definitions, if defined
11120 { titlescreen_initial, "menu.next_screen.TITLE" },
11121 { titlescreen, "menu.next_screen.TITLE" },
11122 { titlemessage_initial, "menu.next_screen.TITLE" },
11123 { titlemessage, "menu.next_screen.TITLE" },
11125 // overwrite titles with title definitions, if defined
11126 { titlescreen_initial_first, "[title_initial]" },
11127 { titlescreen_first, "[title]" },
11128 { titlemessage_initial_first, "[title_initial]" },
11129 { titlemessage_first, "[title]" },
11131 { titlescreen_initial, "[title_initial]" },
11132 { titlescreen, "[title]" },
11133 { titlemessage_initial, "[title_initial]" },
11134 { titlemessage, "[title]" },
11136 // overwrite titles with title screen/message definitions, if defined
11137 { titlescreen_initial_first, "[titlescreen_initial]" },
11138 { titlescreen_first, "[titlescreen]" },
11139 { titlemessage_initial_first, "[titlemessage_initial]" },
11140 { titlemessage_first, "[titlemessage]" },
11142 { titlescreen_initial, "[titlescreen_initial]" },
11143 { titlescreen, "[titlescreen]" },
11144 { titlemessage_initial, "[titlemessage_initial]" },
11145 { titlemessage, "[titlemessage]" },
11149 SetupFileHash *setup_file_hash;
11152 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11155 // the following initializes hierarchical values from dynamic configuration
11157 // special case: initialize with default values that may be overwritten
11158 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11159 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11161 struct TokenIntPtrInfo menu_config[] =
11163 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11164 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11165 { "menu.list_size", &menu.list_size[i] }
11168 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11170 char *token = menu_config[j].token;
11171 char *value = getHashEntry(setup_file_hash, token);
11174 *menu_config[j].value = get_integer_from_string(value);
11178 // special case: initialize with default values that may be overwritten
11179 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11180 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11182 struct TokenIntPtrInfo menu_config[] =
11184 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11185 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11186 { "menu.list_size.INFO", &menu.list_size_info[i] }
11189 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11191 char *token = menu_config[j].token;
11192 char *value = getHashEntry(setup_file_hash, token);
11195 *menu_config[j].value = get_integer_from_string(value);
11199 // special case: initialize with default values that may be overwritten
11200 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11201 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11203 struct TokenIntPtrInfo menu_config[] =
11205 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11206 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11209 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11211 char *token = menu_config[j].token;
11212 char *value = getHashEntry(setup_file_hash, token);
11215 *menu_config[j].value = get_integer_from_string(value);
11219 // special case: initialize with default values that may be overwritten
11220 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11221 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11223 struct TokenIntPtrInfo menu_config[] =
11225 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11226 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11227 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11228 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11229 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11230 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11231 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11232 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11233 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11236 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11238 char *token = menu_config[j].token;
11239 char *value = getHashEntry(setup_file_hash, token);
11242 *menu_config[j].value = get_integer_from_string(value);
11246 // special case: initialize with default values that may be overwritten
11247 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11248 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11250 struct TokenIntPtrInfo menu_config[] =
11252 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11253 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11254 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11255 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11256 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11257 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11258 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11259 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11260 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11263 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11265 char *token = menu_config[j].token;
11266 char *value = getHashEntry(setup_file_hash, token);
11269 *menu_config[j].value = get_token_parameter_value(token, value);
11273 // special case: initialize with default values that may be overwritten
11274 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11275 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11279 char *token_prefix;
11280 struct RectWithBorder *struct_ptr;
11284 { "viewport.window", &viewport.window[i] },
11285 { "viewport.playfield", &viewport.playfield[i] },
11286 { "viewport.door_1", &viewport.door_1[i] },
11287 { "viewport.door_2", &viewport.door_2[i] }
11290 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11292 struct TokenIntPtrInfo vp_config[] =
11294 { ".x", &vp_struct[j].struct_ptr->x },
11295 { ".y", &vp_struct[j].struct_ptr->y },
11296 { ".width", &vp_struct[j].struct_ptr->width },
11297 { ".height", &vp_struct[j].struct_ptr->height },
11298 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11299 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11300 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11301 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11302 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11303 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11304 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11305 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11306 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11307 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11308 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11309 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11310 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11311 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11312 { ".align", &vp_struct[j].struct_ptr->align },
11313 { ".valign", &vp_struct[j].struct_ptr->valign }
11316 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11318 char *token = getStringCat2(vp_struct[j].token_prefix,
11319 vp_config[k].token);
11320 char *value = getHashEntry(setup_file_hash, token);
11323 *vp_config[k].value = get_token_parameter_value(token, value);
11330 // special case: initialize with default values that may be overwritten
11331 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11332 for (i = 0; title_info[i].info != NULL; i++)
11334 struct TitleFadingInfo *info = title_info[i].info;
11335 char *base_token = title_info[i].text;
11337 for (j = 0; title_tokens[j].type != -1; j++)
11339 char *token = getStringCat2(base_token, title_tokens[j].text);
11340 char *value = getHashEntry(setup_file_hash, token);
11344 int parameter_value = get_token_parameter_value(token, value);
11348 *(int *)title_tokens[j].value = (int)parameter_value;
11357 // special case: initialize with default values that may be overwritten
11358 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11359 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11361 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11362 char *base_token = titlemessage_arrays[i].text;
11364 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11366 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11367 char *value = getHashEntry(setup_file_hash, token);
11371 int parameter_value = get_token_parameter_value(token, value);
11373 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11377 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11378 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11380 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11390 // special case: check if network and preview player positions are redefined,
11391 // to compare this later against the main menu level preview being redefined
11392 struct TokenIntPtrInfo menu_config_players[] =
11394 { "main.network_players.x", &menu.main.network_players.redefined },
11395 { "main.network_players.y", &menu.main.network_players.redefined },
11396 { "main.preview_players.x", &menu.main.preview_players.redefined },
11397 { "main.preview_players.y", &menu.main.preview_players.redefined },
11398 { "preview.x", &preview.redefined },
11399 { "preview.y", &preview.redefined }
11402 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11403 *menu_config_players[i].value = FALSE;
11405 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11406 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11407 *menu_config_players[i].value = TRUE;
11409 // read (and overwrite with) values that may be specified in config file
11410 for (i = 0; image_config_vars[i].token != NULL; i++)
11412 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11414 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11415 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11416 *image_config_vars[i].value =
11417 get_token_parameter_value(image_config_vars[i].token, value);
11420 freeSetupFileHash(setup_file_hash);
11423 void LoadMenuDesignSettings(void)
11425 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11427 InitMenuDesignSettings_Static();
11428 InitMenuDesignSettings_SpecialPreProcessing();
11430 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11432 // first look for special settings configured in level series config
11433 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11435 if (fileExists(filename_base))
11436 LoadMenuDesignSettingsFromFilename(filename_base);
11439 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11441 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11442 LoadMenuDesignSettingsFromFilename(filename_local);
11444 InitMenuDesignSettings_SpecialPostProcessing();
11447 void LoadMenuDesignSettings_AfterGraphics(void)
11449 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11452 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11454 char *filename = getEditorSetupFilename();
11455 SetupFileList *setup_file_list, *list;
11456 SetupFileHash *element_hash;
11457 int num_unknown_tokens = 0;
11460 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11463 element_hash = newSetupFileHash();
11465 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11466 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11468 // determined size may be larger than needed (due to unknown elements)
11470 for (list = setup_file_list; list != NULL; list = list->next)
11473 // add space for up to 3 more elements for padding that may be needed
11474 *num_elements += 3;
11476 // free memory for old list of elements, if needed
11477 checked_free(*elements);
11479 // allocate memory for new list of elements
11480 *elements = checked_malloc(*num_elements * sizeof(int));
11483 for (list = setup_file_list; list != NULL; list = list->next)
11485 char *value = getHashEntry(element_hash, list->token);
11487 if (value == NULL) // try to find obsolete token mapping
11489 char *mapped_token = get_mapped_token(list->token);
11491 if (mapped_token != NULL)
11493 value = getHashEntry(element_hash, mapped_token);
11495 free(mapped_token);
11501 (*elements)[(*num_elements)++] = atoi(value);
11505 if (num_unknown_tokens == 0)
11508 Warn("unknown token(s) found in config file:");
11509 Warn("- config file: '%s'", filename);
11511 num_unknown_tokens++;
11514 Warn("- token: '%s'", list->token);
11518 if (num_unknown_tokens > 0)
11521 while (*num_elements % 4) // pad with empty elements, if needed
11522 (*elements)[(*num_elements)++] = EL_EMPTY;
11524 freeSetupFileList(setup_file_list);
11525 freeSetupFileHash(element_hash);
11528 for (i = 0; i < *num_elements; i++)
11529 Debug("editor", "element '%s' [%d]\n",
11530 element_info[(*elements)[i]].token_name, (*elements)[i]);
11534 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11537 SetupFileHash *setup_file_hash = NULL;
11538 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11539 char *filename_music, *filename_prefix, *filename_info;
11545 token_to_value_ptr[] =
11547 { "title_header", &tmp_music_file_info.title_header },
11548 { "artist_header", &tmp_music_file_info.artist_header },
11549 { "album_header", &tmp_music_file_info.album_header },
11550 { "year_header", &tmp_music_file_info.year_header },
11552 { "title", &tmp_music_file_info.title },
11553 { "artist", &tmp_music_file_info.artist },
11554 { "album", &tmp_music_file_info.album },
11555 { "year", &tmp_music_file_info.year },
11561 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11562 getCustomMusicFilename(basename));
11564 if (filename_music == NULL)
11567 // ---------- try to replace file extension ----------
11569 filename_prefix = getStringCopy(filename_music);
11570 if (strrchr(filename_prefix, '.') != NULL)
11571 *strrchr(filename_prefix, '.') = '\0';
11572 filename_info = getStringCat2(filename_prefix, ".txt");
11574 if (fileExists(filename_info))
11575 setup_file_hash = loadSetupFileHash(filename_info);
11577 free(filename_prefix);
11578 free(filename_info);
11580 if (setup_file_hash == NULL)
11582 // ---------- try to add file extension ----------
11584 filename_prefix = getStringCopy(filename_music);
11585 filename_info = getStringCat2(filename_prefix, ".txt");
11587 if (fileExists(filename_info))
11588 setup_file_hash = loadSetupFileHash(filename_info);
11590 free(filename_prefix);
11591 free(filename_info);
11594 if (setup_file_hash == NULL)
11597 // ---------- music file info found ----------
11599 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11601 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11603 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11605 *token_to_value_ptr[i].value_ptr =
11606 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11609 tmp_music_file_info.basename = getStringCopy(basename);
11610 tmp_music_file_info.music = music;
11611 tmp_music_file_info.is_sound = is_sound;
11613 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11614 *new_music_file_info = tmp_music_file_info;
11616 return new_music_file_info;
11619 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11621 return get_music_file_info_ext(basename, music, FALSE);
11624 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11626 return get_music_file_info_ext(basename, sound, TRUE);
11629 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11630 char *basename, boolean is_sound)
11632 for (; list != NULL; list = list->next)
11633 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11639 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11641 return music_info_listed_ext(list, basename, FALSE);
11644 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11646 return music_info_listed_ext(list, basename, TRUE);
11649 void LoadMusicInfo(void)
11651 char *music_directory = getCustomMusicDirectory();
11652 int num_music = getMusicListSize();
11653 int num_music_noconf = 0;
11654 int num_sounds = getSoundListSize();
11656 DirectoryEntry *dir_entry;
11657 struct FileInfo *music, *sound;
11658 struct MusicFileInfo *next, **new;
11661 while (music_file_info != NULL)
11663 next = music_file_info->next;
11665 checked_free(music_file_info->basename);
11667 checked_free(music_file_info->title_header);
11668 checked_free(music_file_info->artist_header);
11669 checked_free(music_file_info->album_header);
11670 checked_free(music_file_info->year_header);
11672 checked_free(music_file_info->title);
11673 checked_free(music_file_info->artist);
11674 checked_free(music_file_info->album);
11675 checked_free(music_file_info->year);
11677 free(music_file_info);
11679 music_file_info = next;
11682 new = &music_file_info;
11684 for (i = 0; i < num_music; i++)
11686 music = getMusicListEntry(i);
11688 if (music->filename == NULL)
11691 if (strEqual(music->filename, UNDEFINED_FILENAME))
11694 // a configured file may be not recognized as music
11695 if (!FileIsMusic(music->filename))
11698 if (!music_info_listed(music_file_info, music->filename))
11700 *new = get_music_file_info(music->filename, i);
11703 new = &(*new)->next;
11707 if ((dir = openDirectory(music_directory)) == NULL)
11709 Warn("cannot read music directory '%s'", music_directory);
11714 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11716 char *basename = dir_entry->basename;
11717 boolean music_already_used = FALSE;
11720 // skip all music files that are configured in music config file
11721 for (i = 0; i < num_music; i++)
11723 music = getMusicListEntry(i);
11725 if (music->filename == NULL)
11728 if (strEqual(basename, music->filename))
11730 music_already_used = TRUE;
11735 if (music_already_used)
11738 if (!FileIsMusic(dir_entry->filename))
11741 if (!music_info_listed(music_file_info, basename))
11743 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11746 new = &(*new)->next;
11749 num_music_noconf++;
11752 closeDirectory(dir);
11754 for (i = 0; i < num_sounds; i++)
11756 sound = getSoundListEntry(i);
11758 if (sound->filename == NULL)
11761 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11764 // a configured file may be not recognized as sound
11765 if (!FileIsSound(sound->filename))
11768 if (!sound_info_listed(music_file_info, sound->filename))
11770 *new = get_sound_file_info(sound->filename, i);
11772 new = &(*new)->next;
11777 static void add_helpanim_entry(int element, int action, int direction,
11778 int delay, int *num_list_entries)
11780 struct HelpAnimInfo *new_list_entry;
11781 (*num_list_entries)++;
11784 checked_realloc(helpanim_info,
11785 *num_list_entries * sizeof(struct HelpAnimInfo));
11786 new_list_entry = &helpanim_info[*num_list_entries - 1];
11788 new_list_entry->element = element;
11789 new_list_entry->action = action;
11790 new_list_entry->direction = direction;
11791 new_list_entry->delay = delay;
11794 static void print_unknown_token(char *filename, char *token, int token_nr)
11799 Warn("unknown token(s) found in config file:");
11800 Warn("- config file: '%s'", filename);
11803 Warn("- token: '%s'", token);
11806 static void print_unknown_token_end(int token_nr)
11812 void LoadHelpAnimInfo(void)
11814 char *filename = getHelpAnimFilename();
11815 SetupFileList *setup_file_list = NULL, *list;
11816 SetupFileHash *element_hash, *action_hash, *direction_hash;
11817 int num_list_entries = 0;
11818 int num_unknown_tokens = 0;
11821 if (fileExists(filename))
11822 setup_file_list = loadSetupFileList(filename);
11824 if (setup_file_list == NULL)
11826 // use reliable default values from static configuration
11827 SetupFileList *insert_ptr;
11829 insert_ptr = setup_file_list =
11830 newSetupFileList(helpanim_config[0].token,
11831 helpanim_config[0].value);
11833 for (i = 1; helpanim_config[i].token; i++)
11834 insert_ptr = addListEntry(insert_ptr,
11835 helpanim_config[i].token,
11836 helpanim_config[i].value);
11839 element_hash = newSetupFileHash();
11840 action_hash = newSetupFileHash();
11841 direction_hash = newSetupFileHash();
11843 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11844 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11846 for (i = 0; i < NUM_ACTIONS; i++)
11847 setHashEntry(action_hash, element_action_info[i].suffix,
11848 i_to_a(element_action_info[i].value));
11850 // do not store direction index (bit) here, but direction value!
11851 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11852 setHashEntry(direction_hash, element_direction_info[i].suffix,
11853 i_to_a(1 << element_direction_info[i].value));
11855 for (list = setup_file_list; list != NULL; list = list->next)
11857 char *element_token, *action_token, *direction_token;
11858 char *element_value, *action_value, *direction_value;
11859 int delay = atoi(list->value);
11861 if (strEqual(list->token, "end"))
11863 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11868 /* first try to break element into element/action/direction parts;
11869 if this does not work, also accept combined "element[.act][.dir]"
11870 elements (like "dynamite.active"), which are unique elements */
11872 if (strchr(list->token, '.') == NULL) // token contains no '.'
11874 element_value = getHashEntry(element_hash, list->token);
11875 if (element_value != NULL) // element found
11876 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11877 &num_list_entries);
11880 // no further suffixes found -- this is not an element
11881 print_unknown_token(filename, list->token, num_unknown_tokens++);
11887 // token has format "<prefix>.<something>"
11889 action_token = strchr(list->token, '.'); // suffix may be action ...
11890 direction_token = action_token; // ... or direction
11892 element_token = getStringCopy(list->token);
11893 *strchr(element_token, '.') = '\0';
11895 element_value = getHashEntry(element_hash, element_token);
11897 if (element_value == NULL) // this is no element
11899 element_value = getHashEntry(element_hash, list->token);
11900 if (element_value != NULL) // combined element found
11901 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11902 &num_list_entries);
11904 print_unknown_token(filename, list->token, num_unknown_tokens++);
11906 free(element_token);
11911 action_value = getHashEntry(action_hash, action_token);
11913 if (action_value != NULL) // action found
11915 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11916 &num_list_entries);
11918 free(element_token);
11923 direction_value = getHashEntry(direction_hash, direction_token);
11925 if (direction_value != NULL) // direction found
11927 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11928 &num_list_entries);
11930 free(element_token);
11935 if (strchr(action_token + 1, '.') == NULL)
11937 // no further suffixes found -- this is not an action nor direction
11939 element_value = getHashEntry(element_hash, list->token);
11940 if (element_value != NULL) // combined element found
11941 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11942 &num_list_entries);
11944 print_unknown_token(filename, list->token, num_unknown_tokens++);
11946 free(element_token);
11951 // token has format "<prefix>.<suffix>.<something>"
11953 direction_token = strchr(action_token + 1, '.');
11955 action_token = getStringCopy(action_token);
11956 *strchr(action_token + 1, '.') = '\0';
11958 action_value = getHashEntry(action_hash, action_token);
11960 if (action_value == NULL) // this is no action
11962 element_value = getHashEntry(element_hash, list->token);
11963 if (element_value != NULL) // combined element found
11964 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11965 &num_list_entries);
11967 print_unknown_token(filename, list->token, num_unknown_tokens++);
11969 free(element_token);
11970 free(action_token);
11975 direction_value = getHashEntry(direction_hash, direction_token);
11977 if (direction_value != NULL) // direction found
11979 add_helpanim_entry(atoi(element_value), atoi(action_value),
11980 atoi(direction_value), delay, &num_list_entries);
11982 free(element_token);
11983 free(action_token);
11988 // this is no direction
11990 element_value = getHashEntry(element_hash, list->token);
11991 if (element_value != NULL) // combined element found
11992 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11993 &num_list_entries);
11995 print_unknown_token(filename, list->token, num_unknown_tokens++);
11997 free(element_token);
11998 free(action_token);
12001 print_unknown_token_end(num_unknown_tokens);
12003 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12004 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12006 freeSetupFileList(setup_file_list);
12007 freeSetupFileHash(element_hash);
12008 freeSetupFileHash(action_hash);
12009 freeSetupFileHash(direction_hash);
12012 for (i = 0; i < num_list_entries; i++)
12013 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12014 EL_NAME(helpanim_info[i].element),
12015 helpanim_info[i].element,
12016 helpanim_info[i].action,
12017 helpanim_info[i].direction,
12018 helpanim_info[i].delay);
12022 void LoadHelpTextInfo(void)
12024 char *filename = getHelpTextFilename();
12027 if (helptext_info != NULL)
12029 freeSetupFileHash(helptext_info);
12030 helptext_info = NULL;
12033 if (fileExists(filename))
12034 helptext_info = loadSetupFileHash(filename);
12036 if (helptext_info == NULL)
12038 // use reliable default values from static configuration
12039 helptext_info = newSetupFileHash();
12041 for (i = 0; helptext_config[i].token; i++)
12042 setHashEntry(helptext_info,
12043 helptext_config[i].token,
12044 helptext_config[i].value);
12048 BEGIN_HASH_ITERATION(helptext_info, itr)
12050 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12051 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12053 END_HASH_ITERATION(hash, itr)
12058 // ----------------------------------------------------------------------------
12060 // ----------------------------------------------------------------------------
12062 #define MAX_NUM_CONVERT_LEVELS 1000
12064 void ConvertLevels(void)
12066 static LevelDirTree *convert_leveldir = NULL;
12067 static int convert_level_nr = -1;
12068 static int num_levels_handled = 0;
12069 static int num_levels_converted = 0;
12070 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12073 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12074 global.convert_leveldir);
12076 if (convert_leveldir == NULL)
12077 Fail("no such level identifier: '%s'", global.convert_leveldir);
12079 leveldir_current = convert_leveldir;
12081 if (global.convert_level_nr != -1)
12083 convert_leveldir->first_level = global.convert_level_nr;
12084 convert_leveldir->last_level = global.convert_level_nr;
12087 convert_level_nr = convert_leveldir->first_level;
12089 PrintLine("=", 79);
12090 Print("Converting levels\n");
12091 PrintLine("-", 79);
12092 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12093 Print("Level series name: '%s'\n", convert_leveldir->name);
12094 Print("Level series author: '%s'\n", convert_leveldir->author);
12095 Print("Number of levels: %d\n", convert_leveldir->levels);
12096 PrintLine("=", 79);
12099 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12100 levels_failed[i] = FALSE;
12102 while (convert_level_nr <= convert_leveldir->last_level)
12104 char *level_filename;
12107 level_nr = convert_level_nr++;
12109 Print("Level %03d: ", level_nr);
12111 LoadLevel(level_nr);
12112 if (level.no_level_file || level.no_valid_file)
12114 Print("(no level)\n");
12118 Print("converting level ... ");
12120 level_filename = getDefaultLevelFilename(level_nr);
12121 new_level = !fileExists(level_filename);
12125 SaveLevel(level_nr);
12127 num_levels_converted++;
12129 Print("converted.\n");
12133 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12134 levels_failed[level_nr] = TRUE;
12136 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12139 num_levels_handled++;
12143 PrintLine("=", 79);
12144 Print("Number of levels handled: %d\n", num_levels_handled);
12145 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12146 (num_levels_handled ?
12147 num_levels_converted * 100 / num_levels_handled : 0));
12148 PrintLine("-", 79);
12149 Print("Summary (for automatic parsing by scripts):\n");
12150 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12151 convert_leveldir->identifier, num_levels_converted,
12152 num_levels_handled,
12153 (num_levels_handled ?
12154 num_levels_converted * 100 / num_levels_handled : 0));
12156 if (num_levels_handled != num_levels_converted)
12158 Print(", FAILED:");
12159 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12160 if (levels_failed[i])
12165 PrintLine("=", 79);
12167 CloseAllAndExit(0);
12171 // ----------------------------------------------------------------------------
12172 // create and save images for use in level sketches (raw BMP format)
12173 // ----------------------------------------------------------------------------
12175 void CreateLevelSketchImages(void)
12181 InitElementPropertiesGfxElement();
12183 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12184 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12186 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12188 int element = getMappedElement(i);
12189 char basename1[16];
12190 char basename2[16];
12194 sprintf(basename1, "%04d.bmp", i);
12195 sprintf(basename2, "%04ds.bmp", i);
12197 filename1 = getPath2(global.create_images_dir, basename1);
12198 filename2 = getPath2(global.create_images_dir, basename2);
12200 DrawSizedElement(0, 0, element, TILESIZE);
12201 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12203 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12204 Fail("cannot save level sketch image file '%s'", filename1);
12206 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12207 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12209 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12210 Fail("cannot save level sketch image file '%s'", filename2);
12215 // create corresponding SQL statements (for normal and small images)
12218 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12219 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12222 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12223 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12225 // optional: create content for forum level sketch demonstration post
12227 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12230 FreeBitmap(bitmap1);
12231 FreeBitmap(bitmap2);
12234 fprintf(stderr, "\n");
12236 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12238 CloseAllAndExit(0);
12242 // ----------------------------------------------------------------------------
12243 // create and save images for custom and group elements (raw BMP format)
12244 // ----------------------------------------------------------------------------
12246 void CreateCustomElementImages(char *directory)
12248 char *src_basename = "RocksCE-template.ilbm";
12249 char *dst_basename = "RocksCE.bmp";
12250 char *src_filename = getPath2(directory, src_basename);
12251 char *dst_filename = getPath2(directory, dst_basename);
12252 Bitmap *src_bitmap;
12254 int yoffset_ce = 0;
12255 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12258 InitVideoDefaults();
12260 ReCreateBitmap(&backbuffer, video.width, video.height);
12262 src_bitmap = LoadImage(src_filename);
12264 bitmap = CreateBitmap(TILEX * 16 * 2,
12265 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12268 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12275 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12276 TILEX * x, TILEY * y + yoffset_ce);
12278 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12280 TILEX * x + TILEX * 16,
12281 TILEY * y + yoffset_ce);
12283 for (j = 2; j >= 0; j--)
12287 BlitBitmap(src_bitmap, bitmap,
12288 TILEX + c * 7, 0, 6, 10,
12289 TILEX * x + 6 + j * 7,
12290 TILEY * y + 11 + yoffset_ce);
12292 BlitBitmap(src_bitmap, bitmap,
12293 TILEX + c * 8, TILEY, 6, 10,
12294 TILEX * 16 + TILEX * x + 6 + j * 8,
12295 TILEY * y + 10 + yoffset_ce);
12301 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12308 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12309 TILEX * x, TILEY * y + yoffset_ge);
12311 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12313 TILEX * x + TILEX * 16,
12314 TILEY * y + yoffset_ge);
12316 for (j = 1; j >= 0; j--)
12320 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12321 TILEX * x + 6 + j * 10,
12322 TILEY * y + 11 + yoffset_ge);
12324 BlitBitmap(src_bitmap, bitmap,
12325 TILEX + c * 8, TILEY + 12, 6, 10,
12326 TILEX * 16 + TILEX * x + 10 + j * 8,
12327 TILEY * y + 10 + yoffset_ge);
12333 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12334 Fail("cannot save CE graphics file '%s'", dst_filename);
12336 FreeBitmap(bitmap);
12338 CloseAllAndExit(0);