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 char *level_identifier = NULL;
7769 int level_identifier_size;
7772 level_identifier_size = getFile16BitBE(file);
7774 level_identifier = checked_malloc(level_identifier_size);
7776 for (i = 0; i < level_identifier_size; i++)
7777 level_identifier[i] = getFile8Bit(file);
7779 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7780 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7782 checked_free(level_identifier);
7784 tape->level_nr = getFile16BitBE(file);
7786 chunk_size = 2 + level_identifier_size + 2;
7791 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7794 int tape_pos_size = getTapePosSize(tape);
7795 int chunk_size_expected = tape_pos_size * tape->length;
7797 if (chunk_size_expected != chunk_size)
7799 ReadUnusedBytesFromFile(file, chunk_size);
7800 return chunk_size_expected;
7803 for (i = 0; i < tape->length; i++)
7805 if (i >= MAX_TAPE_LEN)
7807 Warn("tape truncated -- size exceeds maximum tape size %d",
7810 // tape too large; read and ignore remaining tape data from this chunk
7811 for (;i < tape->length; i++)
7812 ReadUnusedBytesFromFile(file, tape_pos_size);
7817 if (tape->use_key_actions)
7819 for (j = 0; j < MAX_PLAYERS; j++)
7821 tape->pos[i].action[j] = MV_NONE;
7823 if (tape->player_participates[j])
7824 tape->pos[i].action[j] = getFile8Bit(file);
7828 if (tape->use_mouse_actions)
7830 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7831 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7832 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7835 tape->pos[i].delay = getFile8Bit(file);
7837 if (tape->file_version == FILE_VERSION_1_0)
7839 // eliminate possible diagonal moves in old tapes
7840 // this is only for backward compatibility
7842 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7843 byte action = tape->pos[i].action[0];
7844 int k, num_moves = 0;
7846 for (k = 0; k<4; k++)
7848 if (action & joy_dir[k])
7850 tape->pos[i + num_moves].action[0] = joy_dir[k];
7852 tape->pos[i + num_moves].delay = 0;
7861 tape->length += num_moves;
7864 else if (tape->file_version < FILE_VERSION_2_0)
7866 // convert pre-2.0 tapes to new tape format
7868 if (tape->pos[i].delay > 1)
7871 tape->pos[i + 1] = tape->pos[i];
7872 tape->pos[i + 1].delay = 1;
7875 for (j = 0; j < MAX_PLAYERS; j++)
7876 tape->pos[i].action[j] = MV_NONE;
7877 tape->pos[i].delay--;
7884 if (checkEndOfFile(file))
7888 if (i != tape->length)
7889 chunk_size = tape_pos_size * i;
7894 static void LoadTape_SokobanSolution(char *filename)
7897 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7899 if (!(file = openFile(filename, MODE_READ)))
7901 tape.no_valid_file = TRUE;
7906 while (!checkEndOfFile(file))
7908 unsigned char c = getByteFromFile(file);
7910 if (checkEndOfFile(file))
7917 tape.pos[tape.length].action[0] = MV_UP;
7918 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7924 tape.pos[tape.length].action[0] = MV_DOWN;
7925 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7931 tape.pos[tape.length].action[0] = MV_LEFT;
7932 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7938 tape.pos[tape.length].action[0] = MV_RIGHT;
7939 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7947 // ignore white-space characters
7951 tape.no_valid_file = TRUE;
7953 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7961 if (tape.no_valid_file)
7964 tape.length_frames = GetTapeLengthFrames();
7965 tape.length_seconds = GetTapeLengthSeconds();
7968 void LoadTapeFromFilename(char *filename)
7970 char cookie[MAX_LINE_LEN];
7971 char chunk_name[CHUNK_ID_LEN + 1];
7975 // always start with reliable default values
7976 setTapeInfoToDefaults();
7978 if (strSuffix(filename, ".sln"))
7980 LoadTape_SokobanSolution(filename);
7985 if (!(file = openFile(filename, MODE_READ)))
7987 tape.no_valid_file = TRUE;
7992 getFileChunkBE(file, chunk_name, NULL);
7993 if (strEqual(chunk_name, "RND1"))
7995 getFile32BitBE(file); // not used
7997 getFileChunkBE(file, chunk_name, NULL);
7998 if (!strEqual(chunk_name, "TAPE"))
8000 tape.no_valid_file = TRUE;
8002 Warn("unknown format of tape file '%s'", filename);
8009 else // check for pre-2.0 file format with cookie string
8011 strcpy(cookie, chunk_name);
8012 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8014 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8015 cookie[strlen(cookie) - 1] = '\0';
8017 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8019 tape.no_valid_file = TRUE;
8021 Warn("unknown format of tape file '%s'", filename);
8028 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8030 tape.no_valid_file = TRUE;
8032 Warn("unsupported version of tape file '%s'", filename);
8039 // pre-2.0 tape files have no game version, so use file version here
8040 tape.game_version = tape.file_version;
8043 if (tape.file_version < FILE_VERSION_1_2)
8045 // tape files from versions before 1.2.0 without chunk structure
8046 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8047 LoadTape_BODY(file, 2 * tape.length, &tape);
8055 int (*loader)(File *, int, struct TapeInfo *);
8059 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8060 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8061 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8062 { "INFO", -1, LoadTape_INFO },
8063 { "BODY", -1, LoadTape_BODY },
8067 while (getFileChunkBE(file, chunk_name, &chunk_size))
8071 while (chunk_info[i].name != NULL &&
8072 !strEqual(chunk_name, chunk_info[i].name))
8075 if (chunk_info[i].name == NULL)
8077 Warn("unknown chunk '%s' in tape file '%s'",
8078 chunk_name, filename);
8080 ReadUnusedBytesFromFile(file, chunk_size);
8082 else if (chunk_info[i].size != -1 &&
8083 chunk_info[i].size != chunk_size)
8085 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8086 chunk_size, chunk_name, filename);
8088 ReadUnusedBytesFromFile(file, chunk_size);
8092 // call function to load this tape chunk
8093 int chunk_size_expected =
8094 (chunk_info[i].loader)(file, chunk_size, &tape);
8096 // the size of some chunks cannot be checked before reading other
8097 // chunks first (like "HEAD" and "BODY") that contain some header
8098 // information, so check them here
8099 if (chunk_size_expected != chunk_size)
8101 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8102 chunk_size, chunk_name, filename);
8110 tape.length_frames = GetTapeLengthFrames();
8111 tape.length_seconds = GetTapeLengthSeconds();
8114 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8116 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8118 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8119 tape.engine_version);
8123 void LoadTape(int nr)
8125 char *filename = getTapeFilename(nr);
8127 LoadTapeFromFilename(filename);
8130 void LoadSolutionTape(int nr)
8132 char *filename = getSolutionTapeFilename(nr);
8134 LoadTapeFromFilename(filename);
8136 if (TAPE_IS_EMPTY(tape) &&
8137 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8138 level.native_sp_level->demo.is_available)
8139 CopyNativeTape_SP_to_RND(&level);
8142 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8144 // chunk required for team mode tapes with non-default screen size
8145 return (tape->num_participating_players > 1 &&
8146 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8147 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8150 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8152 putFileVersion(file, tape->file_version);
8153 putFileVersion(file, tape->game_version);
8156 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8159 byte store_participating_players = 0;
8161 // set bits for participating players for compact storage
8162 for (i = 0; i < MAX_PLAYERS; i++)
8163 if (tape->player_participates[i])
8164 store_participating_players |= (1 << i);
8166 putFile32BitBE(file, tape->random_seed);
8167 putFile32BitBE(file, tape->date);
8168 putFile32BitBE(file, tape->length);
8170 putFile8Bit(file, store_participating_players);
8172 putFile8Bit(file, getTapeActionValue(tape));
8174 putFile8Bit(file, tape->property_bits);
8176 // unused bytes not at the end here for 4-byte alignment of engine_version
8177 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8179 putFileVersion(file, tape->engine_version);
8182 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8184 putFile8Bit(file, tape->scr_fieldx);
8185 putFile8Bit(file, tape->scr_fieldy);
8188 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8190 int level_identifier_size = strlen(tape->level_identifier) + 1;
8193 putFile16BitBE(file, level_identifier_size);
8195 for (i = 0; i < level_identifier_size; i++)
8196 putFile8Bit(file, tape->level_identifier[i]);
8198 putFile16BitBE(file, tape->level_nr);
8201 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8205 for (i = 0; i < tape->length; i++)
8207 if (tape->use_key_actions)
8209 for (j = 0; j < MAX_PLAYERS; j++)
8210 if (tape->player_participates[j])
8211 putFile8Bit(file, tape->pos[i].action[j]);
8214 if (tape->use_mouse_actions)
8216 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8217 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8218 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8221 putFile8Bit(file, tape->pos[i].delay);
8225 void SaveTapeToFilename(char *filename)
8229 int info_chunk_size;
8230 int body_chunk_size;
8232 if (!(file = fopen(filename, MODE_WRITE)))
8234 Warn("cannot save level recording file '%s'", filename);
8239 tape_pos_size = getTapePosSize(&tape);
8241 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8242 body_chunk_size = tape_pos_size * tape.length;
8244 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8245 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8247 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8248 SaveTape_VERS(file, &tape);
8250 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8251 SaveTape_HEAD(file, &tape);
8253 if (checkSaveTape_SCRN(&tape))
8255 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8256 SaveTape_SCRN(file, &tape);
8259 putFileChunkBE(file, "INFO", info_chunk_size);
8260 SaveTape_INFO(file, &tape);
8262 putFileChunkBE(file, "BODY", body_chunk_size);
8263 SaveTape_BODY(file, &tape);
8267 SetFilePermissions(filename, PERMS_PRIVATE);
8270 void SaveTape(int nr)
8272 char *filename = getTapeFilename(nr);
8275 InitTapeDirectory(leveldir_current->subdir);
8277 tape.file_version = FILE_VERSION_ACTUAL;
8278 tape.game_version = GAME_VERSION_ACTUAL;
8280 tape.num_participating_players = 0;
8282 // count number of participating players
8283 for (i = 0; i < MAX_PLAYERS; i++)
8284 if (tape.player_participates[i])
8285 tape.num_participating_players++;
8287 SaveTapeToFilename(filename);
8289 tape.changed = FALSE;
8292 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8293 unsigned int req_state_added)
8295 char *filename = getTapeFilename(nr);
8296 boolean new_tape = !fileExists(filename);
8297 boolean tape_saved = FALSE;
8299 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8304 Request(msg_saved, REQ_CONFIRM | req_state_added);
8312 boolean SaveTapeChecked(int nr)
8314 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8317 boolean SaveTapeChecked_LevelSolved(int nr)
8319 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8320 "Level solved! Tape saved!", REQ_STAY_OPEN);
8323 void DumpTape(struct TapeInfo *tape)
8325 int tape_frame_counter;
8328 if (tape->no_valid_file)
8330 Warn("cannot dump -- no valid tape file found");
8336 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8337 tape->level_nr, tape->file_version, tape->game_version);
8338 Print(" (effective engine version %08d)\n",
8339 tape->engine_version);
8340 Print("Level series identifier: '%s'\n", tape->level_identifier);
8343 tape_frame_counter = 0;
8345 for (i = 0; i < tape->length; i++)
8347 if (i >= MAX_TAPE_LEN)
8352 for (j = 0; j < MAX_PLAYERS; j++)
8354 if (tape->player_participates[j])
8356 int action = tape->pos[i].action[j];
8358 Print("%d:%02x ", j, action);
8359 Print("[%c%c%c%c|%c%c] - ",
8360 (action & JOY_LEFT ? '<' : ' '),
8361 (action & JOY_RIGHT ? '>' : ' '),
8362 (action & JOY_UP ? '^' : ' '),
8363 (action & JOY_DOWN ? 'v' : ' '),
8364 (action & JOY_BUTTON_1 ? '1' : ' '),
8365 (action & JOY_BUTTON_2 ? '2' : ' '));
8369 Print("(%03d) ", tape->pos[i].delay);
8370 Print("[%05d]\n", tape_frame_counter);
8372 tape_frame_counter += tape->pos[i].delay;
8379 // ============================================================================
8380 // score file functions
8381 // ============================================================================
8383 void LoadScore(int nr)
8386 char *filename = getScoreFilename(nr);
8387 char cookie[MAX_LINE_LEN];
8388 char line[MAX_LINE_LEN];
8392 // always start with reliable default values
8393 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8395 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8396 highscore[i].Score = 0;
8399 if (!(file = fopen(filename, MODE_READ)))
8402 // check file identifier
8403 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8405 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8406 cookie[strlen(cookie) - 1] = '\0';
8408 if (!checkCookieString(cookie, SCORE_COOKIE))
8410 Warn("unknown format of score file '%s'", filename);
8417 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8419 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8420 Warn("fscanf() failed; %s", strerror(errno));
8422 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8425 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8426 line[strlen(line) - 1] = '\0';
8428 for (line_ptr = line; *line_ptr; line_ptr++)
8430 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8432 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8433 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8442 void SaveScore(int nr)
8445 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8446 char *filename = getScoreFilename(nr);
8449 // used instead of "leveldir_current->subdir" (for network games)
8450 InitScoreDirectory(levelset.identifier);
8452 if (!(file = fopen(filename, MODE_WRITE)))
8454 Warn("cannot save score for level %d", nr);
8459 fprintf(file, "%s\n\n", SCORE_COOKIE);
8461 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8462 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8466 SetFilePermissions(filename, permissions);
8470 // ============================================================================
8471 // setup file functions
8472 // ============================================================================
8474 #define TOKEN_STR_PLAYER_PREFIX "player_"
8477 static struct TokenInfo global_setup_tokens[] =
8481 &setup.player_name, "player_name"
8485 &setup.multiple_users, "multiple_users"
8489 &setup.sound, "sound"
8493 &setup.sound_loops, "repeating_sound_loops"
8497 &setup.sound_music, "background_music"
8501 &setup.sound_simple, "simple_sound_effects"
8505 &setup.toons, "toons"
8509 &setup.scroll_delay, "scroll_delay"
8513 &setup.forced_scroll_delay, "forced_scroll_delay"
8517 &setup.scroll_delay_value, "scroll_delay_value"
8521 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8525 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8529 &setup.fade_screens, "fade_screens"
8533 &setup.autorecord, "automatic_tape_recording"
8537 &setup.show_titlescreen, "show_titlescreen"
8541 &setup.quick_doors, "quick_doors"
8545 &setup.team_mode, "team_mode"
8549 &setup.handicap, "handicap"
8553 &setup.skip_levels, "skip_levels"
8557 &setup.increment_levels, "increment_levels"
8561 &setup.auto_play_next_level, "auto_play_next_level"
8565 &setup.count_score_after_game, "count_score_after_game"
8569 &setup.show_scores_after_game, "show_scores_after_game"
8573 &setup.time_limit, "time_limit"
8577 &setup.fullscreen, "fullscreen"
8581 &setup.window_scaling_percent, "window_scaling_percent"
8585 &setup.window_scaling_quality, "window_scaling_quality"
8589 &setup.screen_rendering_mode, "screen_rendering_mode"
8593 &setup.vsync_mode, "vsync_mode"
8597 &setup.ask_on_escape, "ask_on_escape"
8601 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8605 &setup.ask_on_game_over, "ask_on_game_over"
8609 &setup.ask_on_quit_game, "ask_on_quit_game"
8613 &setup.ask_on_quit_program, "ask_on_quit_program"
8617 &setup.quick_switch, "quick_player_switch"
8621 &setup.input_on_focus, "input_on_focus"
8625 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8629 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8633 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8637 &setup.game_speed_extended, "game_speed_extended"
8641 &setup.game_frame_delay, "game_frame_delay"
8645 &setup.sp_show_border_elements, "sp_show_border_elements"
8649 &setup.small_game_graphics, "small_game_graphics"
8653 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8657 &setup.graphics_set, "graphics_set"
8661 &setup.sounds_set, "sounds_set"
8665 &setup.music_set, "music_set"
8669 &setup.override_level_graphics, "override_level_graphics"
8673 &setup.override_level_sounds, "override_level_sounds"
8677 &setup.override_level_music, "override_level_music"
8681 &setup.volume_simple, "volume_simple"
8685 &setup.volume_loops, "volume_loops"
8689 &setup.volume_music, "volume_music"
8693 &setup.network_mode, "network_mode"
8697 &setup.network_player_nr, "network_player"
8701 &setup.network_server_hostname, "network_server_hostname"
8705 &setup.touch.control_type, "touch.control_type"
8709 &setup.touch.move_distance, "touch.move_distance"
8713 &setup.touch.drop_distance, "touch.drop_distance"
8717 &setup.touch.transparency, "touch.transparency"
8721 &setup.touch.draw_outlined, "touch.draw_outlined"
8725 &setup.touch.draw_pressed, "touch.draw_pressed"
8729 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8733 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8737 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8741 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8745 static struct TokenInfo auto_setup_tokens[] =
8749 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8753 static struct TokenInfo editor_setup_tokens[] =
8757 &setup.editor.el_classic, "editor.el_classic"
8761 &setup.editor.el_custom, "editor.el_custom"
8765 &setup.editor.el_user_defined, "editor.el_user_defined"
8769 &setup.editor.el_dynamic, "editor.el_dynamic"
8773 &setup.editor.el_headlines, "editor.el_headlines"
8777 &setup.editor.show_element_token, "editor.show_element_token"
8781 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
8785 static struct TokenInfo editor_cascade_setup_tokens[] =
8789 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8793 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8797 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8801 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8805 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8809 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8813 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8817 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8821 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8825 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8829 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8833 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8837 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8841 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8845 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8849 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8853 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8857 static struct TokenInfo shortcut_setup_tokens[] =
8861 &setup.shortcut.save_game, "shortcut.save_game"
8865 &setup.shortcut.load_game, "shortcut.load_game"
8869 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8873 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8877 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8881 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8885 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8889 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8893 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8897 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8901 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8905 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8909 &setup.shortcut.tape_record, "shortcut.tape_record"
8913 &setup.shortcut.tape_play, "shortcut.tape_play"
8917 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8921 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8925 &setup.shortcut.sound_music, "shortcut.sound_music"
8929 &setup.shortcut.snap_left, "shortcut.snap_left"
8933 &setup.shortcut.snap_right, "shortcut.snap_right"
8937 &setup.shortcut.snap_up, "shortcut.snap_up"
8941 &setup.shortcut.snap_down, "shortcut.snap_down"
8945 static struct SetupInputInfo setup_input;
8946 static struct TokenInfo player_setup_tokens[] =
8950 &setup_input.use_joystick, ".use_joystick"
8954 &setup_input.joy.device_name, ".joy.device_name"
8958 &setup_input.joy.xleft, ".joy.xleft"
8962 &setup_input.joy.xmiddle, ".joy.xmiddle"
8966 &setup_input.joy.xright, ".joy.xright"
8970 &setup_input.joy.yupper, ".joy.yupper"
8974 &setup_input.joy.ymiddle, ".joy.ymiddle"
8978 &setup_input.joy.ylower, ".joy.ylower"
8982 &setup_input.joy.snap, ".joy.snap_field"
8986 &setup_input.joy.drop, ".joy.place_bomb"
8990 &setup_input.key.left, ".key.move_left"
8994 &setup_input.key.right, ".key.move_right"
8998 &setup_input.key.up, ".key.move_up"
9002 &setup_input.key.down, ".key.move_down"
9006 &setup_input.key.snap, ".key.snap_field"
9010 &setup_input.key.drop, ".key.place_bomb"
9014 static struct TokenInfo system_setup_tokens[] =
9018 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9022 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9026 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9030 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9034 static struct TokenInfo internal_setup_tokens[] =
9038 &setup.internal.program_title, "program_title"
9042 &setup.internal.program_version, "program_version"
9046 &setup.internal.program_author, "program_author"
9050 &setup.internal.program_email, "program_email"
9054 &setup.internal.program_website, "program_website"
9058 &setup.internal.program_copyright, "program_copyright"
9062 &setup.internal.program_company, "program_company"
9066 &setup.internal.program_icon_file, "program_icon_file"
9070 &setup.internal.default_graphics_set, "default_graphics_set"
9074 &setup.internal.default_sounds_set, "default_sounds_set"
9078 &setup.internal.default_music_set, "default_music_set"
9082 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9086 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9090 &setup.internal.fallback_music_file, "fallback_music_file"
9094 &setup.internal.default_level_series, "default_level_series"
9098 &setup.internal.default_window_width, "default_window_width"
9102 &setup.internal.default_window_height, "default_window_height"
9106 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9110 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9114 &setup.internal.create_user_levelset, "create_user_levelset"
9118 &setup.internal.menu_game, "menu_game"
9122 &setup.internal.menu_editor, "menu_editor"
9126 &setup.internal.menu_graphics, "menu_graphics"
9130 &setup.internal.menu_sound, "menu_sound"
9134 &setup.internal.menu_artwork, "menu_artwork"
9138 &setup.internal.menu_input, "menu_input"
9142 &setup.internal.menu_touch, "menu_touch"
9146 &setup.internal.menu_shortcuts, "menu_shortcuts"
9150 &setup.internal.menu_exit, "menu_exit"
9154 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9158 static struct TokenInfo debug_setup_tokens[] =
9162 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9166 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9170 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9174 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9178 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9182 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9186 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9190 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9194 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9198 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9202 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9206 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9210 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9214 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9218 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9222 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9226 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9230 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9234 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9238 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9242 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9245 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9249 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9253 &setup.debug.xsn_mode, "debug.xsn_mode"
9257 &setup.debug.xsn_percent, "debug.xsn_percent"
9261 static struct TokenInfo options_setup_tokens[] =
9265 &setup.options.verbose, "options.verbose"
9269 static void setSetupInfoToDefaults(struct SetupInfo *si)
9273 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9275 si->multiple_users = TRUE;
9278 si->sound_loops = TRUE;
9279 si->sound_music = TRUE;
9280 si->sound_simple = TRUE;
9282 si->scroll_delay = TRUE;
9283 si->forced_scroll_delay = FALSE;
9284 si->scroll_delay_value = STD_SCROLL_DELAY;
9285 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9286 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9287 si->fade_screens = TRUE;
9288 si->autorecord = TRUE;
9289 si->show_titlescreen = TRUE;
9290 si->quick_doors = FALSE;
9291 si->team_mode = FALSE;
9292 si->handicap = TRUE;
9293 si->skip_levels = TRUE;
9294 si->increment_levels = TRUE;
9295 si->auto_play_next_level = TRUE;
9296 si->count_score_after_game = TRUE;
9297 si->show_scores_after_game = TRUE;
9298 si->time_limit = TRUE;
9299 si->fullscreen = FALSE;
9300 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9301 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9302 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9303 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9304 si->ask_on_escape = TRUE;
9305 si->ask_on_escape_editor = TRUE;
9306 si->ask_on_game_over = TRUE;
9307 si->ask_on_quit_game = TRUE;
9308 si->ask_on_quit_program = TRUE;
9309 si->quick_switch = FALSE;
9310 si->input_on_focus = FALSE;
9311 si->prefer_aga_graphics = TRUE;
9312 si->prefer_lowpass_sounds = FALSE;
9313 si->prefer_extra_panel_items = TRUE;
9314 si->game_speed_extended = FALSE;
9315 si->game_frame_delay = GAME_FRAME_DELAY;
9316 si->sp_show_border_elements = FALSE;
9317 si->small_game_graphics = FALSE;
9318 si->show_snapshot_buttons = FALSE;
9320 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9321 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9322 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9324 si->override_level_graphics = FALSE;
9325 si->override_level_sounds = FALSE;
9326 si->override_level_music = FALSE;
9328 si->volume_simple = 100; // percent
9329 si->volume_loops = 100; // percent
9330 si->volume_music = 100; // percent
9332 si->network_mode = FALSE;
9333 si->network_player_nr = 0; // first player
9334 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9336 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9337 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9338 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9339 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9340 si->touch.draw_outlined = TRUE;
9341 si->touch.draw_pressed = TRUE;
9343 for (i = 0; i < 2; i++)
9345 char *default_grid_button[6][2] =
9351 { "111222", " vv " },
9352 { "111222", " vv " }
9354 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9355 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9356 int min_xsize = MIN(6, grid_xsize);
9357 int min_ysize = MIN(6, grid_ysize);
9358 int startx = grid_xsize - min_xsize;
9359 int starty = grid_ysize - min_ysize;
9362 // virtual buttons grid can only be set to defaults if video is initialized
9363 // (this will be repeated if virtual buttons are not loaded from setup file)
9364 if (video.initialized)
9366 si->touch.grid_xsize[i] = grid_xsize;
9367 si->touch.grid_ysize[i] = grid_ysize;
9371 si->touch.grid_xsize[i] = -1;
9372 si->touch.grid_ysize[i] = -1;
9375 for (x = 0; x < MAX_GRID_XSIZE; x++)
9376 for (y = 0; y < MAX_GRID_YSIZE; y++)
9377 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9379 for (x = 0; x < min_xsize; x++)
9380 for (y = 0; y < min_ysize; y++)
9381 si->touch.grid_button[i][x][starty + y] =
9382 default_grid_button[y][0][x];
9384 for (x = 0; x < min_xsize; x++)
9385 for (y = 0; y < min_ysize; y++)
9386 si->touch.grid_button[i][startx + x][starty + y] =
9387 default_grid_button[y][1][x];
9390 si->touch.grid_initialized = video.initialized;
9392 si->editor.el_boulderdash = TRUE;
9393 si->editor.el_emerald_mine = TRUE;
9394 si->editor.el_emerald_mine_club = TRUE;
9395 si->editor.el_more = TRUE;
9396 si->editor.el_sokoban = TRUE;
9397 si->editor.el_supaplex = TRUE;
9398 si->editor.el_diamond_caves = TRUE;
9399 si->editor.el_dx_boulderdash = TRUE;
9401 si->editor.el_mirror_magic = TRUE;
9402 si->editor.el_deflektor = TRUE;
9404 si->editor.el_chars = TRUE;
9405 si->editor.el_steel_chars = TRUE;
9407 si->editor.el_classic = TRUE;
9408 si->editor.el_custom = TRUE;
9410 si->editor.el_user_defined = FALSE;
9411 si->editor.el_dynamic = TRUE;
9413 si->editor.el_headlines = TRUE;
9415 si->editor.show_element_token = FALSE;
9417 si->editor.show_read_only_warning = TRUE;
9419 si->editor.use_template_for_new_levels = TRUE;
9421 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9422 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9423 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9425 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9426 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9427 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9428 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9429 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9431 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9432 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9433 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9434 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9435 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9436 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9438 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9439 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9440 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9442 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9443 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9444 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9445 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9447 for (i = 0; i < MAX_PLAYERS; i++)
9449 si->input[i].use_joystick = FALSE;
9450 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9451 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9452 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9453 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9454 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9455 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9456 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9457 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9458 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9459 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9460 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9461 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9462 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9463 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9464 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9467 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9468 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9469 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9470 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9472 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9473 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9474 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9475 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9476 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9477 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9478 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9480 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9482 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9483 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9484 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9486 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9487 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9488 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9490 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9491 si->internal.choose_from_top_leveldir = FALSE;
9492 si->internal.show_scaling_in_title = TRUE;
9493 si->internal.create_user_levelset = TRUE;
9495 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9496 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9498 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9499 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9500 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9501 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9502 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9503 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9504 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9505 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9506 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9507 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9509 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9510 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9511 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9512 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9513 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9514 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9515 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9516 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9517 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9518 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9520 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9521 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9523 si->debug.show_frames_per_second = FALSE;
9525 si->debug.xsn_mode = AUTO;
9526 si->debug.xsn_percent = 0;
9528 si->options.verbose = FALSE;
9530 #if defined(PLATFORM_ANDROID)
9531 si->fullscreen = TRUE;
9534 setHideSetupEntry(&setup.debug.xsn_mode);
9537 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9539 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9542 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9544 si->editor_cascade.el_bd = TRUE;
9545 si->editor_cascade.el_em = TRUE;
9546 si->editor_cascade.el_emc = TRUE;
9547 si->editor_cascade.el_rnd = TRUE;
9548 si->editor_cascade.el_sb = TRUE;
9549 si->editor_cascade.el_sp = TRUE;
9550 si->editor_cascade.el_dc = TRUE;
9551 si->editor_cascade.el_dx = TRUE;
9553 si->editor_cascade.el_mm = TRUE;
9554 si->editor_cascade.el_df = TRUE;
9556 si->editor_cascade.el_chars = FALSE;
9557 si->editor_cascade.el_steel_chars = FALSE;
9558 si->editor_cascade.el_ce = FALSE;
9559 si->editor_cascade.el_ge = FALSE;
9560 si->editor_cascade.el_ref = FALSE;
9561 si->editor_cascade.el_user = FALSE;
9562 si->editor_cascade.el_dynamic = FALSE;
9565 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9567 static char *getHideSetupToken(void *setup_value)
9569 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9571 if (setup_value != NULL)
9572 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9574 return hide_setup_token;
9577 void setHideSetupEntry(void *setup_value)
9579 char *hide_setup_token = getHideSetupToken(setup_value);
9581 if (hide_setup_hash == NULL)
9582 hide_setup_hash = newSetupFileHash();
9584 if (setup_value != NULL)
9585 setHashEntry(hide_setup_hash, hide_setup_token, "");
9588 void removeHideSetupEntry(void *setup_value)
9590 char *hide_setup_token = getHideSetupToken(setup_value);
9592 if (setup_value != NULL)
9593 removeHashEntry(hide_setup_hash, hide_setup_token);
9596 boolean hideSetupEntry(void *setup_value)
9598 char *hide_setup_token = getHideSetupToken(setup_value);
9600 return (setup_value != NULL &&
9601 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9604 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9605 struct TokenInfo *token_info,
9606 int token_nr, char *token_text)
9608 char *token_hide_text = getStringCat2(token_text, ".hide");
9609 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9611 // set the value of this setup option in the setup option structure
9612 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9614 // check if this setup option should be hidden in the setup menu
9615 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9616 setHideSetupEntry(token_info[token_nr].value);
9618 free(token_hide_text);
9621 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9622 struct TokenInfo *token_info,
9625 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9626 token_info[token_nr].text);
9629 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9633 if (!setup_file_hash)
9636 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9637 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9639 setup.touch.grid_initialized = TRUE;
9640 for (i = 0; i < 2; i++)
9642 int grid_xsize = setup.touch.grid_xsize[i];
9643 int grid_ysize = setup.touch.grid_ysize[i];
9646 // if virtual buttons are not loaded from setup file, repeat initializing
9647 // virtual buttons grid with default values later when video is initialized
9648 if (grid_xsize == -1 ||
9651 setup.touch.grid_initialized = FALSE;
9656 for (y = 0; y < grid_ysize; y++)
9658 char token_string[MAX_LINE_LEN];
9660 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9662 char *value_string = getHashEntry(setup_file_hash, token_string);
9664 if (value_string == NULL)
9667 for (x = 0; x < grid_xsize; x++)
9669 char c = value_string[x];
9671 setup.touch.grid_button[i][x][y] =
9672 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9677 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9678 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9680 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9681 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9683 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9687 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9689 setup_input = setup.input[pnr];
9690 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9692 char full_token[100];
9694 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9695 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9698 setup.input[pnr] = setup_input;
9701 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9702 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9704 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9705 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9707 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9708 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9710 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9711 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9713 setHideRelatedSetupEntries();
9716 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9720 if (!setup_file_hash)
9723 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9724 setSetupInfo(auto_setup_tokens, i,
9725 getHashEntry(setup_file_hash,
9726 auto_setup_tokens[i].text));
9729 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9733 if (!setup_file_hash)
9736 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9737 setSetupInfo(editor_cascade_setup_tokens, i,
9738 getHashEntry(setup_file_hash,
9739 editor_cascade_setup_tokens[i].text));
9742 void LoadUserNames(void)
9744 int last_user_nr = user.nr;
9747 if (global.user_names != NULL)
9749 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9750 checked_free(global.user_names[i]);
9752 checked_free(global.user_names);
9755 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9757 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9761 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9763 if (setup_file_hash)
9765 char *player_name = getHashEntry(setup_file_hash, "player_name");
9767 global.user_names[i] = getFixedUserName(player_name);
9769 freeSetupFileHash(setup_file_hash);
9772 if (global.user_names[i] == NULL)
9773 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9776 user.nr = last_user_nr;
9779 void LoadSetupFromFilename(char *filename)
9781 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9783 if (setup_file_hash)
9785 decodeSetupFileHash(setup_file_hash);
9787 freeSetupFileHash(setup_file_hash);
9791 Debug("setup", "using default setup values");
9795 static void LoadSetup_SpecialPostProcessing(void)
9797 char *player_name_new;
9799 // needed to work around problems with fixed length strings
9800 player_name_new = getFixedUserName(setup.player_name);
9801 free(setup.player_name);
9802 setup.player_name = player_name_new;
9804 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9805 if (setup.scroll_delay == FALSE)
9807 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9808 setup.scroll_delay = TRUE; // now always "on"
9811 // make sure that scroll delay value stays inside valid range
9812 setup.scroll_delay_value =
9813 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9816 void LoadSetup(void)
9820 // always start with reliable default values
9821 setSetupInfoToDefaults(&setup);
9823 // try to load setup values from default setup file
9824 filename = getDefaultSetupFilename();
9826 if (fileExists(filename))
9827 LoadSetupFromFilename(filename);
9829 // try to load setup values from user setup file
9830 filename = getSetupFilename();
9832 LoadSetupFromFilename(filename);
9834 LoadSetup_SpecialPostProcessing();
9837 void LoadSetup_AutoSetup(void)
9839 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9840 SetupFileHash *setup_file_hash = NULL;
9842 // always start with reliable default values
9843 setSetupInfoToDefaults_AutoSetup(&setup);
9845 setup_file_hash = loadSetupFileHash(filename);
9847 if (setup_file_hash)
9849 decodeSetupFileHash_AutoSetup(setup_file_hash);
9851 freeSetupFileHash(setup_file_hash);
9857 void LoadSetup_EditorCascade(void)
9859 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9860 SetupFileHash *setup_file_hash = NULL;
9862 // always start with reliable default values
9863 setSetupInfoToDefaults_EditorCascade(&setup);
9865 setup_file_hash = loadSetupFileHash(filename);
9867 if (setup_file_hash)
9869 decodeSetupFileHash_EditorCascade(setup_file_hash);
9871 freeSetupFileHash(setup_file_hash);
9877 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9880 char mapping_guid[MAX_LINE_LEN];
9881 char *mapping_start, *mapping_end;
9883 // get GUID from game controller mapping line: copy complete line
9884 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9885 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9887 // get GUID from game controller mapping line: cut after GUID part
9888 mapping_start = strchr(mapping_guid, ',');
9889 if (mapping_start != NULL)
9890 *mapping_start = '\0';
9892 // cut newline from game controller mapping line
9893 mapping_end = strchr(mapping_line, '\n');
9894 if (mapping_end != NULL)
9895 *mapping_end = '\0';
9897 // add mapping entry to game controller mappings hash
9898 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9901 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9906 if (!(file = fopen(filename, MODE_READ)))
9908 Warn("cannot read game controller mappings file '%s'", filename);
9915 char line[MAX_LINE_LEN];
9917 if (!fgets(line, MAX_LINE_LEN, file))
9920 addGameControllerMappingToHash(mappings_hash, line);
9926 void SaveSetup(void)
9928 char *filename = getSetupFilename();
9932 InitUserDataDirectory();
9934 if (!(file = fopen(filename, MODE_WRITE)))
9936 Warn("cannot write setup file '%s'", filename);
9941 fprintFileHeader(file, SETUP_FILENAME);
9943 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9945 // just to make things nicer :)
9946 if (global_setup_tokens[i].value == &setup.multiple_users ||
9947 global_setup_tokens[i].value == &setup.sound ||
9948 global_setup_tokens[i].value == &setup.graphics_set ||
9949 global_setup_tokens[i].value == &setup.volume_simple ||
9950 global_setup_tokens[i].value == &setup.network_mode ||
9951 global_setup_tokens[i].value == &setup.touch.control_type ||
9952 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9953 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9954 fprintf(file, "\n");
9956 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9959 for (i = 0; i < 2; i++)
9961 int grid_xsize = setup.touch.grid_xsize[i];
9962 int grid_ysize = setup.touch.grid_ysize[i];
9965 fprintf(file, "\n");
9967 for (y = 0; y < grid_ysize; y++)
9969 char token_string[MAX_LINE_LEN];
9970 char value_string[MAX_LINE_LEN];
9972 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9974 for (x = 0; x < grid_xsize; x++)
9976 char c = setup.touch.grid_button[i][x][y];
9978 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9981 value_string[grid_xsize] = '\0';
9983 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9987 fprintf(file, "\n");
9988 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9989 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9991 fprintf(file, "\n");
9992 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9993 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9995 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9999 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10000 fprintf(file, "\n");
10002 setup_input = setup.input[pnr];
10003 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10004 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
10007 fprintf(file, "\n");
10008 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10009 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
10011 // (internal setup values not saved to user setup file)
10013 fprintf(file, "\n");
10014 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10015 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10016 setup.debug.xsn_mode != AUTO)
10017 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10019 fprintf(file, "\n");
10020 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10021 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10025 SetFilePermissions(filename, PERMS_PRIVATE);
10028 void SaveSetup_AutoSetup(void)
10030 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10034 InitUserDataDirectory();
10036 if (!(file = fopen(filename, MODE_WRITE)))
10038 Warn("cannot write auto setup file '%s'", filename);
10045 fprintFileHeader(file, AUTOSETUP_FILENAME);
10047 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10048 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10052 SetFilePermissions(filename, PERMS_PRIVATE);
10057 void SaveSetup_EditorCascade(void)
10059 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10063 InitUserDataDirectory();
10065 if (!(file = fopen(filename, MODE_WRITE)))
10067 Warn("cannot write editor cascade state file '%s'", filename);
10074 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10076 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10077 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10081 SetFilePermissions(filename, PERMS_PRIVATE);
10086 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10091 if (!(file = fopen(filename, MODE_WRITE)))
10093 Warn("cannot write game controller mappings file '%s'", filename);
10098 BEGIN_HASH_ITERATION(mappings_hash, itr)
10100 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10102 END_HASH_ITERATION(mappings_hash, itr)
10107 void SaveSetup_AddGameControllerMapping(char *mapping)
10109 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10110 SetupFileHash *mappings_hash = newSetupFileHash();
10112 InitUserDataDirectory();
10114 // load existing personal game controller mappings
10115 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10117 // add new mapping to personal game controller mappings
10118 addGameControllerMappingToHash(mappings_hash, mapping);
10120 // save updated personal game controller mappings
10121 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10123 freeSetupFileHash(mappings_hash);
10127 void LoadCustomElementDescriptions(void)
10129 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10130 SetupFileHash *setup_file_hash;
10133 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10135 if (element_info[i].custom_description != NULL)
10137 free(element_info[i].custom_description);
10138 element_info[i].custom_description = NULL;
10142 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10145 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10147 char *token = getStringCat2(element_info[i].token_name, ".name");
10148 char *value = getHashEntry(setup_file_hash, token);
10151 element_info[i].custom_description = getStringCopy(value);
10156 freeSetupFileHash(setup_file_hash);
10159 static int getElementFromToken(char *token)
10161 char *value = getHashEntry(element_token_hash, token);
10164 return atoi(value);
10166 Warn("unknown element token '%s'", token);
10168 return EL_UNDEFINED;
10171 void FreeGlobalAnimEventInfo(void)
10173 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10175 if (gaei->event_list == NULL)
10180 for (i = 0; i < gaei->num_event_lists; i++)
10182 checked_free(gaei->event_list[i]->event_value);
10183 checked_free(gaei->event_list[i]);
10186 checked_free(gaei->event_list);
10188 gaei->event_list = NULL;
10189 gaei->num_event_lists = 0;
10192 static int AddGlobalAnimEventList(void)
10194 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10195 int list_pos = gaei->num_event_lists++;
10197 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10198 sizeof(struct GlobalAnimEventListInfo *));
10200 gaei->event_list[list_pos] =
10201 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10203 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10205 gaeli->event_value = NULL;
10206 gaeli->num_event_values = 0;
10211 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10213 // do not add empty global animation events
10214 if (event_value == ANIM_EVENT_NONE)
10217 // if list position is undefined, create new list
10218 if (list_pos == ANIM_EVENT_UNDEFINED)
10219 list_pos = AddGlobalAnimEventList();
10221 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10222 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10223 int value_pos = gaeli->num_event_values++;
10225 gaeli->event_value = checked_realloc(gaeli->event_value,
10226 gaeli->num_event_values * sizeof(int *));
10228 gaeli->event_value[value_pos] = event_value;
10233 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10235 if (list_pos == ANIM_EVENT_UNDEFINED)
10236 return ANIM_EVENT_NONE;
10238 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10239 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10241 return gaeli->event_value[value_pos];
10244 int GetGlobalAnimEventValueCount(int list_pos)
10246 if (list_pos == ANIM_EVENT_UNDEFINED)
10249 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10250 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10252 return gaeli->num_event_values;
10255 // This function checks if a string <s> of the format "string1, string2, ..."
10256 // exactly contains a string <s_contained>.
10258 static boolean string_has_parameter(char *s, char *s_contained)
10262 if (s == NULL || s_contained == NULL)
10265 if (strlen(s_contained) > strlen(s))
10268 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10270 char next_char = s[strlen(s_contained)];
10272 // check if next character is delimiter or whitespace
10273 return (next_char == ',' || next_char == '\0' ||
10274 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10277 // check if string contains another parameter string after a comma
10278 substring = strchr(s, ',');
10279 if (substring == NULL) // string does not contain a comma
10282 // advance string pointer to next character after the comma
10285 // skip potential whitespaces after the comma
10286 while (*substring == ' ' || *substring == '\t')
10289 return string_has_parameter(substring, s_contained);
10292 static int get_anim_parameter_value(char *s)
10294 int event_value[] =
10302 char *pattern_1[] =
10310 char *pattern_2 = ".part_";
10311 char *matching_char = NULL;
10313 int pattern_1_len = 0;
10314 int result = ANIM_EVENT_NONE;
10317 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10319 matching_char = strstr(s_ptr, pattern_1[i]);
10320 pattern_1_len = strlen(pattern_1[i]);
10321 result = event_value[i];
10323 if (matching_char != NULL)
10327 if (matching_char == NULL)
10328 return ANIM_EVENT_NONE;
10330 s_ptr = matching_char + pattern_1_len;
10332 // check for main animation number ("anim_X" or "anim_XX")
10333 if (*s_ptr >= '0' && *s_ptr <= '9')
10335 int gic_anim_nr = (*s_ptr++ - '0');
10337 if (*s_ptr >= '0' && *s_ptr <= '9')
10338 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10340 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10341 return ANIM_EVENT_NONE;
10343 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10347 // invalid main animation number specified
10349 return ANIM_EVENT_NONE;
10352 // check for animation part number ("part_X" or "part_XX") (optional)
10353 if (strPrefix(s_ptr, pattern_2))
10355 s_ptr += strlen(pattern_2);
10357 if (*s_ptr >= '0' && *s_ptr <= '9')
10359 int gic_part_nr = (*s_ptr++ - '0');
10361 if (*s_ptr >= '0' && *s_ptr <= '9')
10362 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10364 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10365 return ANIM_EVENT_NONE;
10367 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10371 // invalid animation part number specified
10373 return ANIM_EVENT_NONE;
10377 // discard result if next character is neither delimiter nor whitespace
10378 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10379 *s_ptr == ' ' || *s_ptr == '\t'))
10380 return ANIM_EVENT_NONE;
10385 static int get_anim_parameter_values(char *s)
10387 int list_pos = ANIM_EVENT_UNDEFINED;
10388 int event_value = ANIM_EVENT_DEFAULT;
10390 if (string_has_parameter(s, "any"))
10391 event_value |= ANIM_EVENT_ANY;
10393 if (string_has_parameter(s, "click:self") ||
10394 string_has_parameter(s, "click") ||
10395 string_has_parameter(s, "self"))
10396 event_value |= ANIM_EVENT_SELF;
10398 if (string_has_parameter(s, "unclick:any"))
10399 event_value |= ANIM_EVENT_UNCLICK_ANY;
10401 // if animation event found, add it to global animation event list
10402 if (event_value != ANIM_EVENT_NONE)
10403 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10407 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10408 event_value = get_anim_parameter_value(s);
10410 // if animation event found, add it to global animation event list
10411 if (event_value != ANIM_EVENT_NONE)
10412 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10414 // continue with next part of the string, starting with next comma
10415 s = strchr(s + 1, ',');
10421 static int get_anim_action_parameter_value(char *token)
10423 // check most common default case first to massively speed things up
10424 if (strEqual(token, ARG_UNDEFINED))
10425 return ANIM_EVENT_ACTION_NONE;
10427 int result = getImageIDFromToken(token);
10431 char *gfx_token = getStringCat2("gfx.", token);
10433 result = getImageIDFromToken(gfx_token);
10435 checked_free(gfx_token);
10440 Key key = getKeyFromX11KeyName(token);
10442 if (key != KSYM_UNDEFINED)
10443 result = -(int)key;
10447 result = ANIM_EVENT_ACTION_NONE;
10452 int get_parameter_value(char *value_raw, char *suffix, int type)
10454 char *value = getStringToLower(value_raw);
10455 int result = 0; // probably a save default value
10457 if (strEqual(suffix, ".direction"))
10459 result = (strEqual(value, "left") ? MV_LEFT :
10460 strEqual(value, "right") ? MV_RIGHT :
10461 strEqual(value, "up") ? MV_UP :
10462 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10464 else if (strEqual(suffix, ".position"))
10466 result = (strEqual(value, "left") ? POS_LEFT :
10467 strEqual(value, "right") ? POS_RIGHT :
10468 strEqual(value, "top") ? POS_TOP :
10469 strEqual(value, "upper") ? POS_UPPER :
10470 strEqual(value, "middle") ? POS_MIDDLE :
10471 strEqual(value, "lower") ? POS_LOWER :
10472 strEqual(value, "bottom") ? POS_BOTTOM :
10473 strEqual(value, "any") ? POS_ANY :
10474 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10476 else if (strEqual(suffix, ".align"))
10478 result = (strEqual(value, "left") ? ALIGN_LEFT :
10479 strEqual(value, "right") ? ALIGN_RIGHT :
10480 strEqual(value, "center") ? ALIGN_CENTER :
10481 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10483 else if (strEqual(suffix, ".valign"))
10485 result = (strEqual(value, "top") ? VALIGN_TOP :
10486 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10487 strEqual(value, "middle") ? VALIGN_MIDDLE :
10488 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10490 else if (strEqual(suffix, ".anim_mode"))
10492 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10493 string_has_parameter(value, "loop") ? ANIM_LOOP :
10494 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10495 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10496 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10497 string_has_parameter(value, "random") ? ANIM_RANDOM :
10498 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10499 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10500 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10501 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10502 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10503 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10504 string_has_parameter(value, "all") ? ANIM_ALL :
10507 if (string_has_parameter(value, "once"))
10508 result |= ANIM_ONCE;
10510 if (string_has_parameter(value, "reverse"))
10511 result |= ANIM_REVERSE;
10513 if (string_has_parameter(value, "opaque_player"))
10514 result |= ANIM_OPAQUE_PLAYER;
10516 if (string_has_parameter(value, "static_panel"))
10517 result |= ANIM_STATIC_PANEL;
10519 else if (strEqual(suffix, ".init_event") ||
10520 strEqual(suffix, ".anim_event"))
10522 result = get_anim_parameter_values(value);
10524 else if (strEqual(suffix, ".init_delay_action") ||
10525 strEqual(suffix, ".anim_delay_action") ||
10526 strEqual(suffix, ".post_delay_action") ||
10527 strEqual(suffix, ".init_event_action") ||
10528 strEqual(suffix, ".anim_event_action"))
10530 result = get_anim_action_parameter_value(value_raw);
10532 else if (strEqual(suffix, ".class"))
10534 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10535 get_hash_from_key(value));
10537 else if (strEqual(suffix, ".style"))
10539 result = STYLE_DEFAULT;
10541 if (string_has_parameter(value, "accurate_borders"))
10542 result |= STYLE_ACCURATE_BORDERS;
10544 if (string_has_parameter(value, "inner_corners"))
10545 result |= STYLE_INNER_CORNERS;
10547 if (string_has_parameter(value, "reverse"))
10548 result |= STYLE_REVERSE;
10550 if (string_has_parameter(value, "leftmost_position"))
10551 result |= STYLE_LEFTMOST_POSITION;
10553 if (string_has_parameter(value, "block_clicks"))
10554 result |= STYLE_BLOCK;
10556 if (string_has_parameter(value, "passthrough_clicks"))
10557 result |= STYLE_PASSTHROUGH;
10559 if (string_has_parameter(value, "multiple_actions"))
10560 result |= STYLE_MULTIPLE_ACTIONS;
10562 else if (strEqual(suffix, ".fade_mode"))
10564 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10565 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10566 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10567 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10568 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10569 FADE_MODE_DEFAULT);
10571 else if (strEqual(suffix, ".auto_delay_unit"))
10573 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10574 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10575 AUTO_DELAY_UNIT_DEFAULT);
10577 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10579 result = gfx.get_font_from_token_function(value);
10581 else // generic parameter of type integer or boolean
10583 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10584 type == TYPE_INTEGER ? get_integer_from_string(value) :
10585 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10586 ARG_UNDEFINED_VALUE);
10594 static int get_token_parameter_value(char *token, char *value_raw)
10598 if (token == NULL || value_raw == NULL)
10599 return ARG_UNDEFINED_VALUE;
10601 suffix = strrchr(token, '.');
10602 if (suffix == NULL)
10605 if (strEqual(suffix, ".element"))
10606 return getElementFromToken(value_raw);
10608 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10609 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10612 void InitMenuDesignSettings_Static(void)
10616 // always start with reliable default values from static default config
10617 for (i = 0; image_config_vars[i].token != NULL; i++)
10619 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10622 *image_config_vars[i].value =
10623 get_token_parameter_value(image_config_vars[i].token, value);
10627 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10631 // the following initializes hierarchical values from static configuration
10633 // special case: initialize "ARG_DEFAULT" values in static default config
10634 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10635 titlescreen_initial_first_default.fade_mode =
10636 title_initial_first_default.fade_mode;
10637 titlescreen_initial_first_default.fade_delay =
10638 title_initial_first_default.fade_delay;
10639 titlescreen_initial_first_default.post_delay =
10640 title_initial_first_default.post_delay;
10641 titlescreen_initial_first_default.auto_delay =
10642 title_initial_first_default.auto_delay;
10643 titlescreen_initial_first_default.auto_delay_unit =
10644 title_initial_first_default.auto_delay_unit;
10645 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10646 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10647 titlescreen_first_default.post_delay = title_first_default.post_delay;
10648 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10649 titlescreen_first_default.auto_delay_unit =
10650 title_first_default.auto_delay_unit;
10651 titlemessage_initial_first_default.fade_mode =
10652 title_initial_first_default.fade_mode;
10653 titlemessage_initial_first_default.fade_delay =
10654 title_initial_first_default.fade_delay;
10655 titlemessage_initial_first_default.post_delay =
10656 title_initial_first_default.post_delay;
10657 titlemessage_initial_first_default.auto_delay =
10658 title_initial_first_default.auto_delay;
10659 titlemessage_initial_first_default.auto_delay_unit =
10660 title_initial_first_default.auto_delay_unit;
10661 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10662 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10663 titlemessage_first_default.post_delay = title_first_default.post_delay;
10664 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10665 titlemessage_first_default.auto_delay_unit =
10666 title_first_default.auto_delay_unit;
10668 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10669 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10670 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10671 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10672 titlescreen_initial_default.auto_delay_unit =
10673 title_initial_default.auto_delay_unit;
10674 titlescreen_default.fade_mode = title_default.fade_mode;
10675 titlescreen_default.fade_delay = title_default.fade_delay;
10676 titlescreen_default.post_delay = title_default.post_delay;
10677 titlescreen_default.auto_delay = title_default.auto_delay;
10678 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10679 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10680 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10681 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10682 titlemessage_initial_default.auto_delay_unit =
10683 title_initial_default.auto_delay_unit;
10684 titlemessage_default.fade_mode = title_default.fade_mode;
10685 titlemessage_default.fade_delay = title_default.fade_delay;
10686 titlemessage_default.post_delay = title_default.post_delay;
10687 titlemessage_default.auto_delay = title_default.auto_delay;
10688 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10690 // special case: initialize "ARG_DEFAULT" values in static default config
10691 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10692 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10694 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10695 titlescreen_first[i] = titlescreen_first_default;
10696 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10697 titlemessage_first[i] = titlemessage_first_default;
10699 titlescreen_initial[i] = titlescreen_initial_default;
10700 titlescreen[i] = titlescreen_default;
10701 titlemessage_initial[i] = titlemessage_initial_default;
10702 titlemessage[i] = titlemessage_default;
10705 // special case: initialize "ARG_DEFAULT" values in static default config
10706 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10707 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10709 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10712 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10713 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10714 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10717 // special case: initialize "ARG_DEFAULT" values in static default config
10718 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10719 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10721 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10722 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10723 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10725 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10728 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10732 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10736 struct XY *dst, *src;
10738 game_buttons_xy[] =
10740 { &game.button.save, &game.button.stop },
10741 { &game.button.pause2, &game.button.pause },
10742 { &game.button.load, &game.button.play },
10743 { &game.button.undo, &game.button.stop },
10744 { &game.button.redo, &game.button.play },
10750 // special case: initialize later added SETUP list size from LEVELS value
10751 if (menu.list_size[GAME_MODE_SETUP] == -1)
10752 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10754 // set default position for snapshot buttons to stop/pause/play buttons
10755 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10756 if ((*game_buttons_xy[i].dst).x == -1 &&
10757 (*game_buttons_xy[i].dst).y == -1)
10758 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10760 // --------------------------------------------------------------------------
10761 // dynamic viewports (including playfield margins, borders and alignments)
10762 // --------------------------------------------------------------------------
10764 // dynamic viewports currently only supported for landscape mode
10765 int display_width = MAX(video.display_width, video.display_height);
10766 int display_height = MIN(video.display_width, video.display_height);
10768 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10770 struct RectWithBorder *vp_window = &viewport.window[i];
10771 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10772 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10773 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10774 boolean dynamic_window_width = (vp_window->min_width != -1);
10775 boolean dynamic_window_height = (vp_window->min_height != -1);
10776 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10777 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10779 // adjust window size if min/max width/height is specified
10781 if (vp_window->min_width != -1)
10783 int window_width = display_width;
10785 // when using static window height, use aspect ratio of display
10786 if (vp_window->min_height == -1)
10787 window_width = vp_window->height * display_width / display_height;
10789 vp_window->width = MAX(vp_window->min_width, window_width);
10792 if (vp_window->min_height != -1)
10794 int window_height = display_height;
10796 // when using static window width, use aspect ratio of display
10797 if (vp_window->min_width == -1)
10798 window_height = vp_window->width * display_height / display_width;
10800 vp_window->height = MAX(vp_window->min_height, window_height);
10803 if (vp_window->max_width != -1)
10804 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10806 if (vp_window->max_height != -1)
10807 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10809 int playfield_width = vp_window->width;
10810 int playfield_height = vp_window->height;
10812 // adjust playfield size and position according to specified margins
10814 playfield_width -= vp_playfield->margin_left;
10815 playfield_width -= vp_playfield->margin_right;
10817 playfield_height -= vp_playfield->margin_top;
10818 playfield_height -= vp_playfield->margin_bottom;
10820 // adjust playfield size if min/max width/height is specified
10822 if (vp_playfield->min_width != -1)
10823 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10825 if (vp_playfield->min_height != -1)
10826 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10828 if (vp_playfield->max_width != -1)
10829 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10831 if (vp_playfield->max_height != -1)
10832 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10834 // adjust playfield position according to specified alignment
10836 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10837 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10838 else if (vp_playfield->align == ALIGN_CENTER)
10839 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10840 else if (vp_playfield->align == ALIGN_RIGHT)
10841 vp_playfield->x += playfield_width - vp_playfield->width;
10843 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10844 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10845 else if (vp_playfield->valign == VALIGN_MIDDLE)
10846 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10847 else if (vp_playfield->valign == VALIGN_BOTTOM)
10848 vp_playfield->y += playfield_height - vp_playfield->height;
10850 vp_playfield->x += vp_playfield->margin_left;
10851 vp_playfield->y += vp_playfield->margin_top;
10853 // adjust individual playfield borders if only default border is specified
10855 if (vp_playfield->border_left == -1)
10856 vp_playfield->border_left = vp_playfield->border_size;
10857 if (vp_playfield->border_right == -1)
10858 vp_playfield->border_right = vp_playfield->border_size;
10859 if (vp_playfield->border_top == -1)
10860 vp_playfield->border_top = vp_playfield->border_size;
10861 if (vp_playfield->border_bottom == -1)
10862 vp_playfield->border_bottom = vp_playfield->border_size;
10864 // set dynamic playfield borders if borders are specified as undefined
10865 // (but only if window size was dynamic and playfield size was static)
10867 if (dynamic_window_width && !dynamic_playfield_width)
10869 if (vp_playfield->border_left == -1)
10871 vp_playfield->border_left = (vp_playfield->x -
10872 vp_playfield->margin_left);
10873 vp_playfield->x -= vp_playfield->border_left;
10874 vp_playfield->width += vp_playfield->border_left;
10877 if (vp_playfield->border_right == -1)
10879 vp_playfield->border_right = (vp_window->width -
10881 vp_playfield->width -
10882 vp_playfield->margin_right);
10883 vp_playfield->width += vp_playfield->border_right;
10887 if (dynamic_window_height && !dynamic_playfield_height)
10889 if (vp_playfield->border_top == -1)
10891 vp_playfield->border_top = (vp_playfield->y -
10892 vp_playfield->margin_top);
10893 vp_playfield->y -= vp_playfield->border_top;
10894 vp_playfield->height += vp_playfield->border_top;
10897 if (vp_playfield->border_bottom == -1)
10899 vp_playfield->border_bottom = (vp_window->height -
10901 vp_playfield->height -
10902 vp_playfield->margin_bottom);
10903 vp_playfield->height += vp_playfield->border_bottom;
10907 // adjust playfield size to be a multiple of a defined alignment tile size
10909 int align_size = vp_playfield->align_size;
10910 int playfield_xtiles = vp_playfield->width / align_size;
10911 int playfield_ytiles = vp_playfield->height / align_size;
10912 int playfield_width_corrected = playfield_xtiles * align_size;
10913 int playfield_height_corrected = playfield_ytiles * align_size;
10914 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10915 i == GFX_SPECIAL_ARG_EDITOR);
10917 if (is_playfield_mode &&
10918 dynamic_playfield_width &&
10919 vp_playfield->width != playfield_width_corrected)
10921 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10923 vp_playfield->width = playfield_width_corrected;
10925 if (vp_playfield->align == ALIGN_LEFT)
10927 vp_playfield->border_left += playfield_xdiff;
10929 else if (vp_playfield->align == ALIGN_RIGHT)
10931 vp_playfield->border_right += playfield_xdiff;
10933 else if (vp_playfield->align == ALIGN_CENTER)
10935 int border_left_diff = playfield_xdiff / 2;
10936 int border_right_diff = playfield_xdiff - border_left_diff;
10938 vp_playfield->border_left += border_left_diff;
10939 vp_playfield->border_right += border_right_diff;
10943 if (is_playfield_mode &&
10944 dynamic_playfield_height &&
10945 vp_playfield->height != playfield_height_corrected)
10947 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10949 vp_playfield->height = playfield_height_corrected;
10951 if (vp_playfield->valign == VALIGN_TOP)
10953 vp_playfield->border_top += playfield_ydiff;
10955 else if (vp_playfield->align == VALIGN_BOTTOM)
10957 vp_playfield->border_right += playfield_ydiff;
10959 else if (vp_playfield->align == VALIGN_MIDDLE)
10961 int border_top_diff = playfield_ydiff / 2;
10962 int border_bottom_diff = playfield_ydiff - border_top_diff;
10964 vp_playfield->border_top += border_top_diff;
10965 vp_playfield->border_bottom += border_bottom_diff;
10969 // adjust door positions according to specified alignment
10971 for (j = 0; j < 2; j++)
10973 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10975 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10976 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10977 else if (vp_door->align == ALIGN_CENTER)
10978 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10979 else if (vp_door->align == ALIGN_RIGHT)
10980 vp_door->x += vp_window->width - vp_door->width;
10982 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10983 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10984 else if (vp_door->valign == VALIGN_MIDDLE)
10985 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10986 else if (vp_door->valign == VALIGN_BOTTOM)
10987 vp_door->y += vp_window->height - vp_door->height;
10992 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10996 struct XYTileSize *dst, *src;
10999 editor_buttons_xy[] =
11002 &editor.button.element_left, &editor.palette.element_left,
11003 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
11006 &editor.button.element_middle, &editor.palette.element_middle,
11007 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
11010 &editor.button.element_right, &editor.palette.element_right,
11011 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11018 // set default position for element buttons to element graphics
11019 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11021 if ((*editor_buttons_xy[i].dst).x == -1 &&
11022 (*editor_buttons_xy[i].dst).y == -1)
11024 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11026 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11028 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11032 // adjust editor palette rows and columns if specified to be dynamic
11034 if (editor.palette.cols == -1)
11036 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11037 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11038 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11040 editor.palette.cols = (vp_width - sc_width) / bt_width;
11042 if (editor.palette.x == -1)
11044 int palette_width = editor.palette.cols * bt_width + sc_width;
11046 editor.palette.x = (vp_width - palette_width) / 2;
11050 if (editor.palette.rows == -1)
11052 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11053 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11054 int tx_height = getFontHeight(FONT_TEXT_2);
11056 editor.palette.rows = (vp_height - tx_height) / bt_height;
11058 if (editor.palette.y == -1)
11060 int palette_height = editor.palette.rows * bt_height + tx_height;
11062 editor.palette.y = (vp_height - palette_height) / 2;
11067 static void LoadMenuDesignSettingsFromFilename(char *filename)
11069 static struct TitleFadingInfo tfi;
11070 static struct TitleMessageInfo tmi;
11071 static struct TokenInfo title_tokens[] =
11073 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11074 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11075 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11076 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11077 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11081 static struct TokenInfo titlemessage_tokens[] =
11083 { TYPE_INTEGER, &tmi.x, ".x" },
11084 { TYPE_INTEGER, &tmi.y, ".y" },
11085 { TYPE_INTEGER, &tmi.width, ".width" },
11086 { TYPE_INTEGER, &tmi.height, ".height" },
11087 { TYPE_INTEGER, &tmi.chars, ".chars" },
11088 { TYPE_INTEGER, &tmi.lines, ".lines" },
11089 { TYPE_INTEGER, &tmi.align, ".align" },
11090 { TYPE_INTEGER, &tmi.valign, ".valign" },
11091 { TYPE_INTEGER, &tmi.font, ".font" },
11092 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11093 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11094 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11095 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11096 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11097 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11098 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11099 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11100 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11106 struct TitleFadingInfo *info;
11111 // initialize first titles from "enter screen" definitions, if defined
11112 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11113 { &title_first_default, "menu.enter_screen.TITLE" },
11115 // initialize title screens from "next screen" definitions, if defined
11116 { &title_initial_default, "menu.next_screen.TITLE" },
11117 { &title_default, "menu.next_screen.TITLE" },
11123 struct TitleMessageInfo *array;
11126 titlemessage_arrays[] =
11128 // initialize first titles from "enter screen" definitions, if defined
11129 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11130 { titlescreen_first, "menu.enter_screen.TITLE" },
11131 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11132 { titlemessage_first, "menu.enter_screen.TITLE" },
11134 // initialize titles from "next screen" definitions, if defined
11135 { titlescreen_initial, "menu.next_screen.TITLE" },
11136 { titlescreen, "menu.next_screen.TITLE" },
11137 { titlemessage_initial, "menu.next_screen.TITLE" },
11138 { titlemessage, "menu.next_screen.TITLE" },
11140 // overwrite titles with title definitions, if defined
11141 { titlescreen_initial_first, "[title_initial]" },
11142 { titlescreen_first, "[title]" },
11143 { titlemessage_initial_first, "[title_initial]" },
11144 { titlemessage_first, "[title]" },
11146 { titlescreen_initial, "[title_initial]" },
11147 { titlescreen, "[title]" },
11148 { titlemessage_initial, "[title_initial]" },
11149 { titlemessage, "[title]" },
11151 // overwrite titles with title screen/message definitions, if defined
11152 { titlescreen_initial_first, "[titlescreen_initial]" },
11153 { titlescreen_first, "[titlescreen]" },
11154 { titlemessage_initial_first, "[titlemessage_initial]" },
11155 { titlemessage_first, "[titlemessage]" },
11157 { titlescreen_initial, "[titlescreen_initial]" },
11158 { titlescreen, "[titlescreen]" },
11159 { titlemessage_initial, "[titlemessage_initial]" },
11160 { titlemessage, "[titlemessage]" },
11164 SetupFileHash *setup_file_hash;
11167 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11170 // the following initializes hierarchical values from dynamic configuration
11172 // special case: initialize with default values that may be overwritten
11173 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11174 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11176 struct TokenIntPtrInfo menu_config[] =
11178 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11179 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11180 { "menu.list_size", &menu.list_size[i] }
11183 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11185 char *token = menu_config[j].token;
11186 char *value = getHashEntry(setup_file_hash, token);
11189 *menu_config[j].value = get_integer_from_string(value);
11193 // special case: initialize with default values that may be overwritten
11194 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11195 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11197 struct TokenIntPtrInfo menu_config[] =
11199 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11200 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11201 { "menu.list_size.INFO", &menu.list_size_info[i] }
11204 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11206 char *token = menu_config[j].token;
11207 char *value = getHashEntry(setup_file_hash, token);
11210 *menu_config[j].value = get_integer_from_string(value);
11214 // special case: initialize with default values that may be overwritten
11215 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11216 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11218 struct TokenIntPtrInfo menu_config[] =
11220 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11221 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11224 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11226 char *token = menu_config[j].token;
11227 char *value = getHashEntry(setup_file_hash, token);
11230 *menu_config[j].value = get_integer_from_string(value);
11234 // special case: initialize with default values that may be overwritten
11235 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11236 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11238 struct TokenIntPtrInfo menu_config[] =
11240 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11241 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11242 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11243 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11244 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11245 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11246 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11247 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11248 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11251 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11253 char *token = menu_config[j].token;
11254 char *value = getHashEntry(setup_file_hash, token);
11257 *menu_config[j].value = get_integer_from_string(value);
11261 // special case: initialize with default values that may be overwritten
11262 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11263 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11265 struct TokenIntPtrInfo menu_config[] =
11267 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11268 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11269 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11270 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11271 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11272 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11273 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11274 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11275 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11278 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11280 char *token = menu_config[j].token;
11281 char *value = getHashEntry(setup_file_hash, token);
11284 *menu_config[j].value = get_token_parameter_value(token, value);
11288 // special case: initialize with default values that may be overwritten
11289 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11290 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11294 char *token_prefix;
11295 struct RectWithBorder *struct_ptr;
11299 { "viewport.window", &viewport.window[i] },
11300 { "viewport.playfield", &viewport.playfield[i] },
11301 { "viewport.door_1", &viewport.door_1[i] },
11302 { "viewport.door_2", &viewport.door_2[i] }
11305 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11307 struct TokenIntPtrInfo vp_config[] =
11309 { ".x", &vp_struct[j].struct_ptr->x },
11310 { ".y", &vp_struct[j].struct_ptr->y },
11311 { ".width", &vp_struct[j].struct_ptr->width },
11312 { ".height", &vp_struct[j].struct_ptr->height },
11313 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11314 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11315 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11316 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11317 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11318 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11319 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11320 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11321 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11322 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11323 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11324 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11325 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11326 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11327 { ".align", &vp_struct[j].struct_ptr->align },
11328 { ".valign", &vp_struct[j].struct_ptr->valign }
11331 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11333 char *token = getStringCat2(vp_struct[j].token_prefix,
11334 vp_config[k].token);
11335 char *value = getHashEntry(setup_file_hash, token);
11338 *vp_config[k].value = get_token_parameter_value(token, value);
11345 // special case: initialize with default values that may be overwritten
11346 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11347 for (i = 0; title_info[i].info != NULL; i++)
11349 struct TitleFadingInfo *info = title_info[i].info;
11350 char *base_token = title_info[i].text;
11352 for (j = 0; title_tokens[j].type != -1; j++)
11354 char *token = getStringCat2(base_token, title_tokens[j].text);
11355 char *value = getHashEntry(setup_file_hash, token);
11359 int parameter_value = get_token_parameter_value(token, value);
11363 *(int *)title_tokens[j].value = (int)parameter_value;
11372 // special case: initialize with default values that may be overwritten
11373 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11374 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11376 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11377 char *base_token = titlemessage_arrays[i].text;
11379 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11381 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11382 char *value = getHashEntry(setup_file_hash, token);
11386 int parameter_value = get_token_parameter_value(token, value);
11388 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11392 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11393 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11395 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11405 // special case: check if network and preview player positions are redefined,
11406 // to compare this later against the main menu level preview being redefined
11407 struct TokenIntPtrInfo menu_config_players[] =
11409 { "main.network_players.x", &menu.main.network_players.redefined },
11410 { "main.network_players.y", &menu.main.network_players.redefined },
11411 { "main.preview_players.x", &menu.main.preview_players.redefined },
11412 { "main.preview_players.y", &menu.main.preview_players.redefined },
11413 { "preview.x", &preview.redefined },
11414 { "preview.y", &preview.redefined }
11417 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11418 *menu_config_players[i].value = FALSE;
11420 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11421 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11422 *menu_config_players[i].value = TRUE;
11424 // read (and overwrite with) values that may be specified in config file
11425 for (i = 0; image_config_vars[i].token != NULL; i++)
11427 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11429 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11430 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11431 *image_config_vars[i].value =
11432 get_token_parameter_value(image_config_vars[i].token, value);
11435 freeSetupFileHash(setup_file_hash);
11438 void LoadMenuDesignSettings(void)
11440 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11442 InitMenuDesignSettings_Static();
11443 InitMenuDesignSettings_SpecialPreProcessing();
11445 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11447 // first look for special settings configured in level series config
11448 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11450 if (fileExists(filename_base))
11451 LoadMenuDesignSettingsFromFilename(filename_base);
11454 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11456 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11457 LoadMenuDesignSettingsFromFilename(filename_local);
11459 InitMenuDesignSettings_SpecialPostProcessing();
11462 void LoadMenuDesignSettings_AfterGraphics(void)
11464 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11467 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11469 char *filename = getEditorSetupFilename();
11470 SetupFileList *setup_file_list, *list;
11471 SetupFileHash *element_hash;
11472 int num_unknown_tokens = 0;
11475 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11478 element_hash = newSetupFileHash();
11480 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11481 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11483 // determined size may be larger than needed (due to unknown elements)
11485 for (list = setup_file_list; list != NULL; list = list->next)
11488 // add space for up to 3 more elements for padding that may be needed
11489 *num_elements += 3;
11491 // free memory for old list of elements, if needed
11492 checked_free(*elements);
11494 // allocate memory for new list of elements
11495 *elements = checked_malloc(*num_elements * sizeof(int));
11498 for (list = setup_file_list; list != NULL; list = list->next)
11500 char *value = getHashEntry(element_hash, list->token);
11502 if (value == NULL) // try to find obsolete token mapping
11504 char *mapped_token = get_mapped_token(list->token);
11506 if (mapped_token != NULL)
11508 value = getHashEntry(element_hash, mapped_token);
11510 free(mapped_token);
11516 (*elements)[(*num_elements)++] = atoi(value);
11520 if (num_unknown_tokens == 0)
11523 Warn("unknown token(s) found in config file:");
11524 Warn("- config file: '%s'", filename);
11526 num_unknown_tokens++;
11529 Warn("- token: '%s'", list->token);
11533 if (num_unknown_tokens > 0)
11536 while (*num_elements % 4) // pad with empty elements, if needed
11537 (*elements)[(*num_elements)++] = EL_EMPTY;
11539 freeSetupFileList(setup_file_list);
11540 freeSetupFileHash(element_hash);
11543 for (i = 0; i < *num_elements; i++)
11544 Debug("editor", "element '%s' [%d]\n",
11545 element_info[(*elements)[i]].token_name, (*elements)[i]);
11549 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11552 SetupFileHash *setup_file_hash = NULL;
11553 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11554 char *filename_music, *filename_prefix, *filename_info;
11560 token_to_value_ptr[] =
11562 { "title_header", &tmp_music_file_info.title_header },
11563 { "artist_header", &tmp_music_file_info.artist_header },
11564 { "album_header", &tmp_music_file_info.album_header },
11565 { "year_header", &tmp_music_file_info.year_header },
11567 { "title", &tmp_music_file_info.title },
11568 { "artist", &tmp_music_file_info.artist },
11569 { "album", &tmp_music_file_info.album },
11570 { "year", &tmp_music_file_info.year },
11576 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11577 getCustomMusicFilename(basename));
11579 if (filename_music == NULL)
11582 // ---------- try to replace file extension ----------
11584 filename_prefix = getStringCopy(filename_music);
11585 if (strrchr(filename_prefix, '.') != NULL)
11586 *strrchr(filename_prefix, '.') = '\0';
11587 filename_info = getStringCat2(filename_prefix, ".txt");
11589 if (fileExists(filename_info))
11590 setup_file_hash = loadSetupFileHash(filename_info);
11592 free(filename_prefix);
11593 free(filename_info);
11595 if (setup_file_hash == NULL)
11597 // ---------- try to add file extension ----------
11599 filename_prefix = getStringCopy(filename_music);
11600 filename_info = getStringCat2(filename_prefix, ".txt");
11602 if (fileExists(filename_info))
11603 setup_file_hash = loadSetupFileHash(filename_info);
11605 free(filename_prefix);
11606 free(filename_info);
11609 if (setup_file_hash == NULL)
11612 // ---------- music file info found ----------
11614 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11616 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11618 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11620 *token_to_value_ptr[i].value_ptr =
11621 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11624 tmp_music_file_info.basename = getStringCopy(basename);
11625 tmp_music_file_info.music = music;
11626 tmp_music_file_info.is_sound = is_sound;
11628 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11629 *new_music_file_info = tmp_music_file_info;
11631 return new_music_file_info;
11634 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11636 return get_music_file_info_ext(basename, music, FALSE);
11639 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11641 return get_music_file_info_ext(basename, sound, TRUE);
11644 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11645 char *basename, boolean is_sound)
11647 for (; list != NULL; list = list->next)
11648 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11654 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11656 return music_info_listed_ext(list, basename, FALSE);
11659 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11661 return music_info_listed_ext(list, basename, TRUE);
11664 void LoadMusicInfo(void)
11666 char *music_directory = getCustomMusicDirectory();
11667 int num_music = getMusicListSize();
11668 int num_music_noconf = 0;
11669 int num_sounds = getSoundListSize();
11671 DirectoryEntry *dir_entry;
11672 struct FileInfo *music, *sound;
11673 struct MusicFileInfo *next, **new;
11676 while (music_file_info != NULL)
11678 next = music_file_info->next;
11680 checked_free(music_file_info->basename);
11682 checked_free(music_file_info->title_header);
11683 checked_free(music_file_info->artist_header);
11684 checked_free(music_file_info->album_header);
11685 checked_free(music_file_info->year_header);
11687 checked_free(music_file_info->title);
11688 checked_free(music_file_info->artist);
11689 checked_free(music_file_info->album);
11690 checked_free(music_file_info->year);
11692 free(music_file_info);
11694 music_file_info = next;
11697 new = &music_file_info;
11699 for (i = 0; i < num_music; i++)
11701 music = getMusicListEntry(i);
11703 if (music->filename == NULL)
11706 if (strEqual(music->filename, UNDEFINED_FILENAME))
11709 // a configured file may be not recognized as music
11710 if (!FileIsMusic(music->filename))
11713 if (!music_info_listed(music_file_info, music->filename))
11715 *new = get_music_file_info(music->filename, i);
11718 new = &(*new)->next;
11722 if ((dir = openDirectory(music_directory)) == NULL)
11724 Warn("cannot read music directory '%s'", music_directory);
11729 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11731 char *basename = dir_entry->basename;
11732 boolean music_already_used = FALSE;
11735 // skip all music files that are configured in music config file
11736 for (i = 0; i < num_music; i++)
11738 music = getMusicListEntry(i);
11740 if (music->filename == NULL)
11743 if (strEqual(basename, music->filename))
11745 music_already_used = TRUE;
11750 if (music_already_used)
11753 if (!FileIsMusic(dir_entry->filename))
11756 if (!music_info_listed(music_file_info, basename))
11758 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11761 new = &(*new)->next;
11764 num_music_noconf++;
11767 closeDirectory(dir);
11769 for (i = 0; i < num_sounds; i++)
11771 sound = getSoundListEntry(i);
11773 if (sound->filename == NULL)
11776 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11779 // a configured file may be not recognized as sound
11780 if (!FileIsSound(sound->filename))
11783 if (!sound_info_listed(music_file_info, sound->filename))
11785 *new = get_sound_file_info(sound->filename, i);
11787 new = &(*new)->next;
11792 static void add_helpanim_entry(int element, int action, int direction,
11793 int delay, int *num_list_entries)
11795 struct HelpAnimInfo *new_list_entry;
11796 (*num_list_entries)++;
11799 checked_realloc(helpanim_info,
11800 *num_list_entries * sizeof(struct HelpAnimInfo));
11801 new_list_entry = &helpanim_info[*num_list_entries - 1];
11803 new_list_entry->element = element;
11804 new_list_entry->action = action;
11805 new_list_entry->direction = direction;
11806 new_list_entry->delay = delay;
11809 static void print_unknown_token(char *filename, char *token, int token_nr)
11814 Warn("unknown token(s) found in config file:");
11815 Warn("- config file: '%s'", filename);
11818 Warn("- token: '%s'", token);
11821 static void print_unknown_token_end(int token_nr)
11827 void LoadHelpAnimInfo(void)
11829 char *filename = getHelpAnimFilename();
11830 SetupFileList *setup_file_list = NULL, *list;
11831 SetupFileHash *element_hash, *action_hash, *direction_hash;
11832 int num_list_entries = 0;
11833 int num_unknown_tokens = 0;
11836 if (fileExists(filename))
11837 setup_file_list = loadSetupFileList(filename);
11839 if (setup_file_list == NULL)
11841 // use reliable default values from static configuration
11842 SetupFileList *insert_ptr;
11844 insert_ptr = setup_file_list =
11845 newSetupFileList(helpanim_config[0].token,
11846 helpanim_config[0].value);
11848 for (i = 1; helpanim_config[i].token; i++)
11849 insert_ptr = addListEntry(insert_ptr,
11850 helpanim_config[i].token,
11851 helpanim_config[i].value);
11854 element_hash = newSetupFileHash();
11855 action_hash = newSetupFileHash();
11856 direction_hash = newSetupFileHash();
11858 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11859 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11861 for (i = 0; i < NUM_ACTIONS; i++)
11862 setHashEntry(action_hash, element_action_info[i].suffix,
11863 i_to_a(element_action_info[i].value));
11865 // do not store direction index (bit) here, but direction value!
11866 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11867 setHashEntry(direction_hash, element_direction_info[i].suffix,
11868 i_to_a(1 << element_direction_info[i].value));
11870 for (list = setup_file_list; list != NULL; list = list->next)
11872 char *element_token, *action_token, *direction_token;
11873 char *element_value, *action_value, *direction_value;
11874 int delay = atoi(list->value);
11876 if (strEqual(list->token, "end"))
11878 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11883 /* first try to break element into element/action/direction parts;
11884 if this does not work, also accept combined "element[.act][.dir]"
11885 elements (like "dynamite.active"), which are unique elements */
11887 if (strchr(list->token, '.') == NULL) // token contains no '.'
11889 element_value = getHashEntry(element_hash, list->token);
11890 if (element_value != NULL) // element found
11891 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11892 &num_list_entries);
11895 // no further suffixes found -- this is not an element
11896 print_unknown_token(filename, list->token, num_unknown_tokens++);
11902 // token has format "<prefix>.<something>"
11904 action_token = strchr(list->token, '.'); // suffix may be action ...
11905 direction_token = action_token; // ... or direction
11907 element_token = getStringCopy(list->token);
11908 *strchr(element_token, '.') = '\0';
11910 element_value = getHashEntry(element_hash, element_token);
11912 if (element_value == NULL) // this is no element
11914 element_value = getHashEntry(element_hash, list->token);
11915 if (element_value != NULL) // combined element found
11916 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11917 &num_list_entries);
11919 print_unknown_token(filename, list->token, num_unknown_tokens++);
11921 free(element_token);
11926 action_value = getHashEntry(action_hash, action_token);
11928 if (action_value != NULL) // action found
11930 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11931 &num_list_entries);
11933 free(element_token);
11938 direction_value = getHashEntry(direction_hash, direction_token);
11940 if (direction_value != NULL) // direction found
11942 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11943 &num_list_entries);
11945 free(element_token);
11950 if (strchr(action_token + 1, '.') == NULL)
11952 // no further suffixes found -- this is not an action nor direction
11954 element_value = getHashEntry(element_hash, list->token);
11955 if (element_value != NULL) // combined element found
11956 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11957 &num_list_entries);
11959 print_unknown_token(filename, list->token, num_unknown_tokens++);
11961 free(element_token);
11966 // token has format "<prefix>.<suffix>.<something>"
11968 direction_token = strchr(action_token + 1, '.');
11970 action_token = getStringCopy(action_token);
11971 *strchr(action_token + 1, '.') = '\0';
11973 action_value = getHashEntry(action_hash, action_token);
11975 if (action_value == NULL) // this is no action
11977 element_value = getHashEntry(element_hash, list->token);
11978 if (element_value != NULL) // combined element found
11979 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11980 &num_list_entries);
11982 print_unknown_token(filename, list->token, num_unknown_tokens++);
11984 free(element_token);
11985 free(action_token);
11990 direction_value = getHashEntry(direction_hash, direction_token);
11992 if (direction_value != NULL) // direction found
11994 add_helpanim_entry(atoi(element_value), atoi(action_value),
11995 atoi(direction_value), delay, &num_list_entries);
11997 free(element_token);
11998 free(action_token);
12003 // this is no direction
12005 element_value = getHashEntry(element_hash, list->token);
12006 if (element_value != NULL) // combined element found
12007 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12008 &num_list_entries);
12010 print_unknown_token(filename, list->token, num_unknown_tokens++);
12012 free(element_token);
12013 free(action_token);
12016 print_unknown_token_end(num_unknown_tokens);
12018 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12019 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12021 freeSetupFileList(setup_file_list);
12022 freeSetupFileHash(element_hash);
12023 freeSetupFileHash(action_hash);
12024 freeSetupFileHash(direction_hash);
12027 for (i = 0; i < num_list_entries; i++)
12028 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12029 EL_NAME(helpanim_info[i].element),
12030 helpanim_info[i].element,
12031 helpanim_info[i].action,
12032 helpanim_info[i].direction,
12033 helpanim_info[i].delay);
12037 void LoadHelpTextInfo(void)
12039 char *filename = getHelpTextFilename();
12042 if (helptext_info != NULL)
12044 freeSetupFileHash(helptext_info);
12045 helptext_info = NULL;
12048 if (fileExists(filename))
12049 helptext_info = loadSetupFileHash(filename);
12051 if (helptext_info == NULL)
12053 // use reliable default values from static configuration
12054 helptext_info = newSetupFileHash();
12056 for (i = 0; helptext_config[i].token; i++)
12057 setHashEntry(helptext_info,
12058 helptext_config[i].token,
12059 helptext_config[i].value);
12063 BEGIN_HASH_ITERATION(helptext_info, itr)
12065 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12066 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12068 END_HASH_ITERATION(hash, itr)
12073 // ----------------------------------------------------------------------------
12075 // ----------------------------------------------------------------------------
12077 #define MAX_NUM_CONVERT_LEVELS 1000
12079 void ConvertLevels(void)
12081 static LevelDirTree *convert_leveldir = NULL;
12082 static int convert_level_nr = -1;
12083 static int num_levels_handled = 0;
12084 static int num_levels_converted = 0;
12085 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12088 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12089 global.convert_leveldir);
12091 if (convert_leveldir == NULL)
12092 Fail("no such level identifier: '%s'", global.convert_leveldir);
12094 leveldir_current = convert_leveldir;
12096 if (global.convert_level_nr != -1)
12098 convert_leveldir->first_level = global.convert_level_nr;
12099 convert_leveldir->last_level = global.convert_level_nr;
12102 convert_level_nr = convert_leveldir->first_level;
12104 PrintLine("=", 79);
12105 Print("Converting levels\n");
12106 PrintLine("-", 79);
12107 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12108 Print("Level series name: '%s'\n", convert_leveldir->name);
12109 Print("Level series author: '%s'\n", convert_leveldir->author);
12110 Print("Number of levels: %d\n", convert_leveldir->levels);
12111 PrintLine("=", 79);
12114 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12115 levels_failed[i] = FALSE;
12117 while (convert_level_nr <= convert_leveldir->last_level)
12119 char *level_filename;
12122 level_nr = convert_level_nr++;
12124 Print("Level %03d: ", level_nr);
12126 LoadLevel(level_nr);
12127 if (level.no_level_file || level.no_valid_file)
12129 Print("(no level)\n");
12133 Print("converting level ... ");
12135 level_filename = getDefaultLevelFilename(level_nr);
12136 new_level = !fileExists(level_filename);
12140 SaveLevel(level_nr);
12142 num_levels_converted++;
12144 Print("converted.\n");
12148 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12149 levels_failed[level_nr] = TRUE;
12151 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12154 num_levels_handled++;
12158 PrintLine("=", 79);
12159 Print("Number of levels handled: %d\n", num_levels_handled);
12160 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12161 (num_levels_handled ?
12162 num_levels_converted * 100 / num_levels_handled : 0));
12163 PrintLine("-", 79);
12164 Print("Summary (for automatic parsing by scripts):\n");
12165 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12166 convert_leveldir->identifier, num_levels_converted,
12167 num_levels_handled,
12168 (num_levels_handled ?
12169 num_levels_converted * 100 / num_levels_handled : 0));
12171 if (num_levels_handled != num_levels_converted)
12173 Print(", FAILED:");
12174 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12175 if (levels_failed[i])
12180 PrintLine("=", 79);
12182 CloseAllAndExit(0);
12186 // ----------------------------------------------------------------------------
12187 // create and save images for use in level sketches (raw BMP format)
12188 // ----------------------------------------------------------------------------
12190 void CreateLevelSketchImages(void)
12196 InitElementPropertiesGfxElement();
12198 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12199 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12201 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12203 int element = getMappedElement(i);
12204 char basename1[16];
12205 char basename2[16];
12209 sprintf(basename1, "%04d.bmp", i);
12210 sprintf(basename2, "%04ds.bmp", i);
12212 filename1 = getPath2(global.create_images_dir, basename1);
12213 filename2 = getPath2(global.create_images_dir, basename2);
12215 DrawSizedElement(0, 0, element, TILESIZE);
12216 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12218 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12219 Fail("cannot save level sketch image file '%s'", filename1);
12221 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12222 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12224 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12225 Fail("cannot save level sketch image file '%s'", filename2);
12230 // create corresponding SQL statements (for normal and small images)
12233 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12234 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12237 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12238 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12240 // optional: create content for forum level sketch demonstration post
12242 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12245 FreeBitmap(bitmap1);
12246 FreeBitmap(bitmap2);
12249 fprintf(stderr, "\n");
12251 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12253 CloseAllAndExit(0);
12257 // ----------------------------------------------------------------------------
12258 // create and save images for custom and group elements (raw BMP format)
12259 // ----------------------------------------------------------------------------
12261 void CreateCustomElementImages(char *directory)
12263 char *src_basename = "RocksCE-template.ilbm";
12264 char *dst_basename = "RocksCE.bmp";
12265 char *src_filename = getPath2(directory, src_basename);
12266 char *dst_filename = getPath2(directory, dst_basename);
12267 Bitmap *src_bitmap;
12269 int yoffset_ce = 0;
12270 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12273 InitVideoDefaults();
12275 ReCreateBitmap(&backbuffer, video.width, video.height);
12277 src_bitmap = LoadImage(src_filename);
12279 bitmap = CreateBitmap(TILEX * 16 * 2,
12280 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12283 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12290 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12291 TILEX * x, TILEY * y + yoffset_ce);
12293 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12295 TILEX * x + TILEX * 16,
12296 TILEY * y + yoffset_ce);
12298 for (j = 2; j >= 0; j--)
12302 BlitBitmap(src_bitmap, bitmap,
12303 TILEX + c * 7, 0, 6, 10,
12304 TILEX * x + 6 + j * 7,
12305 TILEY * y + 11 + yoffset_ce);
12307 BlitBitmap(src_bitmap, bitmap,
12308 TILEX + c * 8, TILEY, 6, 10,
12309 TILEX * 16 + TILEX * x + 6 + j * 8,
12310 TILEY * y + 10 + yoffset_ce);
12316 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12323 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12324 TILEX * x, TILEY * y + yoffset_ge);
12326 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12328 TILEX * x + TILEX * 16,
12329 TILEY * y + yoffset_ge);
12331 for (j = 1; j >= 0; j--)
12335 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12336 TILEX * x + 6 + j * 10,
12337 TILEY * y + 11 + yoffset_ge);
12339 BlitBitmap(src_bitmap, bitmap,
12340 TILEX + c * 8, TILEY + 12, 6, 10,
12341 TILEX * 16 + TILEX * x + 10 + j * 8,
12342 TILEY * y + 10 + yoffset_ge);
12348 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12349 Fail("cannot save CE graphics file '%s'", dst_filename);
12351 FreeBitmap(bitmap);
12353 CloseAllAndExit(0);