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
1081 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1082 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1087 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1088 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1089 &yy_ei.move_direction_initial
1093 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1094 &xx_ei.move_stepsize, TILEX / 8,
1095 &yy_ei.move_stepsize
1100 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1101 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1102 &yy_ei.move_enter_element
1106 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1107 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1108 &yy_ei.move_leave_element
1112 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1113 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1114 &yy_ei.move_leave_type
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1120 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1121 &yy_ei.slippery_type
1126 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1127 &xx_ei.explosion_type, EXPLODES_3X3,
1128 &yy_ei.explosion_type
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1133 &xx_ei.explosion_delay, 16,
1134 &yy_ei.explosion_delay
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1139 &xx_ei.ignition_delay, 8,
1140 &yy_ei.ignition_delay
1145 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1146 &xx_ei.content, EL_EMPTY_SPACE,
1148 &xx_num_contents, 1, 1
1151 // ---------- "num_change_pages" must be the last entry ---------------------
1154 -1, SAVE_CONF_ALWAYS,
1155 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1156 &xx_ei.num_change_pages, 1,
1157 &yy_ei.num_change_pages
1168 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1170 // ---------- "current_change_page" must be the first entry -----------------
1173 -1, SAVE_CONF_ALWAYS,
1174 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1175 &xx_current_change_page, -1
1178 // ---------- (the remaining entries can be in any order) -------------------
1182 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1183 &xx_change.can_change, FALSE
1188 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1189 &xx_event_bits[0], 0
1193 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1194 &xx_event_bits[1], 0
1199 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1200 &xx_change.trigger_player, CH_PLAYER_ANY
1204 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1205 &xx_change.trigger_side, CH_SIDE_ANY
1209 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1210 &xx_change.trigger_page, CH_PAGE_ANY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1216 &xx_change.target_element, EL_EMPTY_SPACE
1221 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1222 &xx_change.delay_fixed, 0
1226 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1227 &xx_change.delay_random, 0
1231 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1232 &xx_change.delay_frames, FRAMES_PER_SECOND
1237 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1238 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1244 &xx_change.explode, FALSE
1248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1249 &xx_change.use_target_content, FALSE
1253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1254 &xx_change.only_if_complete, FALSE
1258 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1259 &xx_change.use_random_replace, FALSE
1263 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1264 &xx_change.random_percentage, 100
1268 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1269 &xx_change.replace_when, CP_WHEN_EMPTY
1274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1275 &xx_change.has_action, FALSE
1279 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1280 &xx_change.action_type, CA_NO_ACTION
1284 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1285 &xx_change.action_mode, CA_MODE_UNDEFINED
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1290 &xx_change.action_arg, CA_ARG_UNDEFINED
1295 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1296 &xx_change.action_element, EL_EMPTY_SPACE
1301 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1302 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1303 &xx_num_contents, 1, 1
1313 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1317 TYPE_STRING, CONF_VALUE_BYTES(1),
1318 &xx_ei.description[0], -1, NULL,
1319 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1320 &xx_default_description[0]
1325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1326 &xx_ei.use_gfx_element, FALSE
1330 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1331 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1336 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1337 &xx_group.choice_mode, ANIM_RANDOM
1342 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1343 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1344 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1354 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1359 &li.block_snap_field, TRUE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1364 &li.continuous_snapping, TRUE
1368 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1369 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1374 &li.use_start_element[0], FALSE
1378 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1379 &li.start_element[0], EL_PLAYER_1
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1384 &li.use_artwork_element[0], FALSE
1388 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1389 &li.artwork_element[0], EL_PLAYER_1
1393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1394 &li.use_explosion_element[0], FALSE
1398 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1399 &li.explosion_element[0], EL_PLAYER_1
1414 filetype_id_list[] =
1416 { LEVEL_FILE_TYPE_RND, "RND" },
1417 { LEVEL_FILE_TYPE_BD, "BD" },
1418 { LEVEL_FILE_TYPE_EM, "EM" },
1419 { LEVEL_FILE_TYPE_SP, "SP" },
1420 { LEVEL_FILE_TYPE_DX, "DX" },
1421 { LEVEL_FILE_TYPE_SB, "SB" },
1422 { LEVEL_FILE_TYPE_DC, "DC" },
1423 { LEVEL_FILE_TYPE_MM, "MM" },
1424 { LEVEL_FILE_TYPE_MM, "DF" },
1429 // ============================================================================
1430 // level file functions
1431 // ============================================================================
1433 static boolean check_special_flags(char *flag)
1435 if (strEqual(options.special_flags, flag) ||
1436 strEqual(leveldir_current->special_flags, flag))
1442 static struct DateInfo getCurrentDate(void)
1444 time_t epoch_seconds = time(NULL);
1445 struct tm *now = localtime(&epoch_seconds);
1446 struct DateInfo date;
1448 date.year = now->tm_year + 1900;
1449 date.month = now->tm_mon + 1;
1450 date.day = now->tm_mday;
1452 date.src = DATE_SRC_CLOCK;
1457 static void resetEventFlags(struct ElementChangeInfo *change)
1461 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1462 change->has_event[i] = FALSE;
1465 static void resetEventBits(void)
1469 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1470 xx_event_bits[i] = 0;
1473 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1477 /* important: only change event flag if corresponding event bit is set
1478 (this is because all xx_event_bits[] values are loaded separately,
1479 and all xx_event_bits[] values are set back to zero before loading
1480 another value xx_event_bits[x] (each value representing 32 flags)) */
1482 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1483 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1484 change->has_event[i] = TRUE;
1487 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1491 /* in contrast to the above function setEventFlagsFromEventBits(), it
1492 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1493 depending on the corresponding change->has_event[i] values here, as
1494 all xx_event_bits[] values are reset in resetEventBits() before */
1496 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1497 if (change->has_event[i])
1498 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1501 static char *getDefaultElementDescription(struct ElementInfo *ei)
1503 static char description[MAX_ELEMENT_NAME_LEN + 1];
1504 char *default_description = (ei->custom_description != NULL ?
1505 ei->custom_description :
1506 ei->editor_description);
1509 // always start with reliable default values
1510 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1511 description[i] = '\0';
1513 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1514 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1516 return &description[0];
1519 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1521 char *default_description = getDefaultElementDescription(ei);
1524 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1525 ei->description[i] = default_description[i];
1528 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1532 for (i = 0; conf[i].data_type != -1; i++)
1534 int default_value = conf[i].default_value;
1535 int data_type = conf[i].data_type;
1536 int conf_type = conf[i].conf_type;
1537 int byte_mask = conf_type & CONF_MASK_BYTES;
1539 if (byte_mask == CONF_MASK_MULTI_BYTES)
1541 int default_num_entities = conf[i].default_num_entities;
1542 int max_num_entities = conf[i].max_num_entities;
1544 *(int *)(conf[i].num_entities) = default_num_entities;
1546 if (data_type == TYPE_STRING)
1548 char *default_string = conf[i].default_string;
1549 char *string = (char *)(conf[i].value);
1551 strncpy(string, default_string, max_num_entities);
1553 else if (data_type == TYPE_ELEMENT_LIST)
1555 int *element_array = (int *)(conf[i].value);
1558 for (j = 0; j < max_num_entities; j++)
1559 element_array[j] = default_value;
1561 else if (data_type == TYPE_CONTENT_LIST)
1563 struct Content *content = (struct Content *)(conf[i].value);
1566 for (c = 0; c < max_num_entities; c++)
1567 for (y = 0; y < 3; y++)
1568 for (x = 0; x < 3; x++)
1569 content[c].e[x][y] = default_value;
1572 else // constant size configuration data (1, 2 or 4 bytes)
1574 if (data_type == TYPE_BOOLEAN)
1575 *(boolean *)(conf[i].value) = default_value;
1577 *(int *) (conf[i].value) = default_value;
1582 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1586 for (i = 0; conf[i].data_type != -1; i++)
1588 int data_type = conf[i].data_type;
1589 int conf_type = conf[i].conf_type;
1590 int byte_mask = conf_type & CONF_MASK_BYTES;
1592 if (byte_mask == CONF_MASK_MULTI_BYTES)
1594 int max_num_entities = conf[i].max_num_entities;
1596 if (data_type == TYPE_STRING)
1598 char *string = (char *)(conf[i].value);
1599 char *string_copy = (char *)(conf[i].value_copy);
1601 strncpy(string_copy, string, max_num_entities);
1603 else if (data_type == TYPE_ELEMENT_LIST)
1605 int *element_array = (int *)(conf[i].value);
1606 int *element_array_copy = (int *)(conf[i].value_copy);
1609 for (j = 0; j < max_num_entities; j++)
1610 element_array_copy[j] = element_array[j];
1612 else if (data_type == TYPE_CONTENT_LIST)
1614 struct Content *content = (struct Content *)(conf[i].value);
1615 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1618 for (c = 0; c < max_num_entities; c++)
1619 for (y = 0; y < 3; y++)
1620 for (x = 0; x < 3; x++)
1621 content_copy[c].e[x][y] = content[c].e[x][y];
1624 else // constant size configuration data (1, 2 or 4 bytes)
1626 if (data_type == TYPE_BOOLEAN)
1627 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1629 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1634 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1638 xx_ei = *ei_from; // copy element data into temporary buffer
1639 yy_ei = *ei_to; // copy element data into temporary buffer
1641 copyConfigFromConfigList(chunk_config_CUSX_base);
1646 // ---------- reinitialize and copy change pages ----------
1648 ei_to->num_change_pages = ei_from->num_change_pages;
1649 ei_to->current_change_page = ei_from->current_change_page;
1651 setElementChangePages(ei_to, ei_to->num_change_pages);
1653 for (i = 0; i < ei_to->num_change_pages; i++)
1654 ei_to->change_page[i] = ei_from->change_page[i];
1656 // ---------- copy group element info ----------
1657 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1658 *ei_to->group = *ei_from->group;
1660 // mark this custom element as modified
1661 ei_to->modified_settings = TRUE;
1664 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1666 int change_page_size = sizeof(struct ElementChangeInfo);
1668 ei->num_change_pages = MAX(1, change_pages);
1671 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1673 if (ei->current_change_page >= ei->num_change_pages)
1674 ei->current_change_page = ei->num_change_pages - 1;
1676 ei->change = &ei->change_page[ei->current_change_page];
1679 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1681 xx_change = *change; // copy change data into temporary buffer
1683 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1685 *change = xx_change;
1687 resetEventFlags(change);
1689 change->direct_action = 0;
1690 change->other_action = 0;
1692 change->pre_change_function = NULL;
1693 change->change_function = NULL;
1694 change->post_change_function = NULL;
1697 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1701 li = *level; // copy level data into temporary buffer
1702 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1703 *level = li; // copy temporary buffer back to level data
1705 setLevelInfoToDefaults_EM();
1706 setLevelInfoToDefaults_SP();
1707 setLevelInfoToDefaults_MM();
1709 level->native_em_level = &native_em_level;
1710 level->native_sp_level = &native_sp_level;
1711 level->native_mm_level = &native_mm_level;
1713 level->file_version = FILE_VERSION_ACTUAL;
1714 level->game_version = GAME_VERSION_ACTUAL;
1716 level->creation_date = getCurrentDate();
1718 level->encoding_16bit_field = TRUE;
1719 level->encoding_16bit_yamyam = TRUE;
1720 level->encoding_16bit_amoeba = TRUE;
1722 // clear level name and level author string buffers
1723 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1724 level->name[i] = '\0';
1725 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1726 level->author[i] = '\0';
1728 // set level name and level author to default values
1729 strcpy(level->name, NAMELESS_LEVEL_NAME);
1730 strcpy(level->author, ANONYMOUS_NAME);
1732 // set level playfield to playable default level with player and exit
1733 for (x = 0; x < MAX_LEV_FIELDX; x++)
1734 for (y = 0; y < MAX_LEV_FIELDY; y++)
1735 level->field[x][y] = EL_SAND;
1737 level->field[0][0] = EL_PLAYER_1;
1738 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1740 BorderElement = EL_STEELWALL;
1742 // detect custom elements when loading them
1743 level->file_has_custom_elements = FALSE;
1745 // set all bug compatibility flags to "false" => do not emulate this bug
1746 level->use_action_after_change_bug = FALSE;
1748 if (leveldir_current)
1750 // try to determine better author name than 'anonymous'
1751 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1753 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1754 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1758 switch (LEVELCLASS(leveldir_current))
1760 case LEVELCLASS_TUTORIAL:
1761 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1764 case LEVELCLASS_CONTRIB:
1765 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1766 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1769 case LEVELCLASS_PRIVATE:
1770 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1771 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1775 // keep default value
1782 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1784 static boolean clipboard_elements_initialized = FALSE;
1787 InitElementPropertiesStatic();
1789 li = *level; // copy level data into temporary buffer
1790 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1791 *level = li; // copy temporary buffer back to level data
1793 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1796 struct ElementInfo *ei = &element_info[element];
1798 // never initialize clipboard elements after the very first time
1799 // (to be able to use clipboard elements between several levels)
1800 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1803 if (IS_ENVELOPE(element))
1805 int envelope_nr = element - EL_ENVELOPE_1;
1807 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1809 level->envelope[envelope_nr] = xx_envelope;
1812 if (IS_CUSTOM_ELEMENT(element) ||
1813 IS_GROUP_ELEMENT(element) ||
1814 IS_INTERNAL_ELEMENT(element))
1816 xx_ei = *ei; // copy element data into temporary buffer
1818 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1823 setElementChangePages(ei, 1);
1824 setElementChangeInfoToDefaults(ei->change);
1826 if (IS_CUSTOM_ELEMENT(element) ||
1827 IS_GROUP_ELEMENT(element) ||
1828 IS_INTERNAL_ELEMENT(element))
1830 setElementDescriptionToDefault(ei);
1832 ei->modified_settings = FALSE;
1835 if (IS_CUSTOM_ELEMENT(element) ||
1836 IS_INTERNAL_ELEMENT(element))
1838 // internal values used in level editor
1840 ei->access_type = 0;
1841 ei->access_layer = 0;
1842 ei->access_protected = 0;
1843 ei->walk_to_action = 0;
1844 ei->smash_targets = 0;
1847 ei->can_explode_by_fire = FALSE;
1848 ei->can_explode_smashed = FALSE;
1849 ei->can_explode_impact = FALSE;
1851 ei->current_change_page = 0;
1854 if (IS_GROUP_ELEMENT(element) ||
1855 IS_INTERNAL_ELEMENT(element))
1857 struct ElementGroupInfo *group;
1859 // initialize memory for list of elements in group
1860 if (ei->group == NULL)
1861 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1865 xx_group = *group; // copy group data into temporary buffer
1867 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1873 clipboard_elements_initialized = TRUE;
1876 static void setLevelInfoToDefaults(struct LevelInfo *level,
1877 boolean level_info_only,
1878 boolean reset_file_status)
1880 setLevelInfoToDefaults_Level(level);
1882 if (!level_info_only)
1883 setLevelInfoToDefaults_Elements(level);
1885 if (reset_file_status)
1887 level->no_valid_file = FALSE;
1888 level->no_level_file = FALSE;
1891 level->changed = FALSE;
1894 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1896 level_file_info->nr = 0;
1897 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1898 level_file_info->packed = FALSE;
1900 setString(&level_file_info->basename, NULL);
1901 setString(&level_file_info->filename, NULL);
1904 int getMappedElement_SB(int, boolean);
1906 static void ActivateLevelTemplate(void)
1910 if (check_special_flags("load_xsb_to_ces"))
1912 // fill smaller playfields with padding "beyond border wall" elements
1913 if (level.fieldx < level_template.fieldx ||
1914 level.fieldy < level_template.fieldy)
1916 short field[level.fieldx][level.fieldy];
1917 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1918 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1919 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1920 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1922 // copy old playfield (which is smaller than the visible area)
1923 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1924 field[x][y] = level.field[x][y];
1926 // fill new, larger playfield with "beyond border wall" elements
1927 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1928 level.field[x][y] = getMappedElement_SB('_', TRUE);
1930 // copy the old playfield to the middle of the new playfield
1931 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1932 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1934 level.fieldx = new_fieldx;
1935 level.fieldy = new_fieldy;
1939 // Currently there is no special action needed to activate the template
1940 // data, because 'element_info' property settings overwrite the original
1941 // level data, while all other variables do not change.
1943 // Exception: 'from_level_template' elements in the original level playfield
1944 // are overwritten with the corresponding elements at the same position in
1945 // playfield from the level template.
1947 for (x = 0; x < level.fieldx; x++)
1948 for (y = 0; y < level.fieldy; y++)
1949 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1950 level.field[x][y] = level_template.field[x][y];
1952 if (check_special_flags("load_xsb_to_ces"))
1954 struct LevelInfo level_backup = level;
1956 // overwrite all individual level settings from template level settings
1957 level = level_template;
1959 // restore level file info
1960 level.file_info = level_backup.file_info;
1962 // restore playfield size
1963 level.fieldx = level_backup.fieldx;
1964 level.fieldy = level_backup.fieldy;
1966 // restore playfield content
1967 for (x = 0; x < level.fieldx; x++)
1968 for (y = 0; y < level.fieldy; y++)
1969 level.field[x][y] = level_backup.field[x][y];
1971 // restore name and author from individual level
1972 strcpy(level.name, level_backup.name);
1973 strcpy(level.author, level_backup.author);
1975 // restore flag "use_custom_template"
1976 level.use_custom_template = level_backup.use_custom_template;
1980 static char *getLevelFilenameFromBasename(char *basename)
1982 static char *filename = NULL;
1984 checked_free(filename);
1986 filename = getPath2(getCurrentLevelDir(), basename);
1991 static int getFileTypeFromBasename(char *basename)
1993 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1995 static char *filename = NULL;
1996 struct stat file_status;
1998 // ---------- try to determine file type from filename ----------
2000 // check for typical filename of a Supaplex level package file
2001 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2002 return LEVEL_FILE_TYPE_SP;
2004 // check for typical filename of a Diamond Caves II level package file
2005 if (strSuffixLower(basename, ".dc") ||
2006 strSuffixLower(basename, ".dc2"))
2007 return LEVEL_FILE_TYPE_DC;
2009 // check for typical filename of a Sokoban level package file
2010 if (strSuffixLower(basename, ".xsb") &&
2011 strchr(basename, '%') == NULL)
2012 return LEVEL_FILE_TYPE_SB;
2014 // ---------- try to determine file type from filesize ----------
2016 checked_free(filename);
2017 filename = getPath2(getCurrentLevelDir(), basename);
2019 if (stat(filename, &file_status) == 0)
2021 // check for typical filesize of a Supaplex level package file
2022 if (file_status.st_size == 170496)
2023 return LEVEL_FILE_TYPE_SP;
2026 return LEVEL_FILE_TYPE_UNKNOWN;
2029 static int getFileTypeFromMagicBytes(char *filename, int type)
2033 if ((file = openFile(filename, MODE_READ)))
2035 char chunk_name[CHUNK_ID_LEN + 1];
2037 getFileChunkBE(file, chunk_name, NULL);
2039 if (strEqual(chunk_name, "MMII") ||
2040 strEqual(chunk_name, "MIRR"))
2041 type = LEVEL_FILE_TYPE_MM;
2049 static boolean checkForPackageFromBasename(char *basename)
2051 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2052 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2054 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2057 static char *getSingleLevelBasenameExt(int nr, char *extension)
2059 static char basename[MAX_FILENAME_LEN];
2062 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2064 sprintf(basename, "%03d.%s", nr, extension);
2069 static char *getSingleLevelBasename(int nr)
2071 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2074 static char *getPackedLevelBasename(int type)
2076 static char basename[MAX_FILENAME_LEN];
2077 char *directory = getCurrentLevelDir();
2079 DirectoryEntry *dir_entry;
2081 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2083 if ((dir = openDirectory(directory)) == NULL)
2085 Warn("cannot read current level directory '%s'", directory);
2090 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2092 char *entry_basename = dir_entry->basename;
2093 int entry_type = getFileTypeFromBasename(entry_basename);
2095 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2097 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2100 strcpy(basename, entry_basename);
2107 closeDirectory(dir);
2112 static char *getSingleLevelFilename(int nr)
2114 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2117 #if ENABLE_UNUSED_CODE
2118 static char *getPackedLevelFilename(int type)
2120 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2124 char *getDefaultLevelFilename(int nr)
2126 return getSingleLevelFilename(nr);
2129 #if ENABLE_UNUSED_CODE
2130 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2134 lfi->packed = FALSE;
2136 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2137 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2141 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2142 int type, char *format, ...)
2144 static char basename[MAX_FILENAME_LEN];
2147 va_start(ap, format);
2148 vsprintf(basename, format, ap);
2152 lfi->packed = FALSE;
2154 setString(&lfi->basename, basename);
2155 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2158 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2164 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2165 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2168 static int getFiletypeFromID(char *filetype_id)
2170 char *filetype_id_lower;
2171 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2174 if (filetype_id == NULL)
2175 return LEVEL_FILE_TYPE_UNKNOWN;
2177 filetype_id_lower = getStringToLower(filetype_id);
2179 for (i = 0; filetype_id_list[i].id != NULL; i++)
2181 char *id_lower = getStringToLower(filetype_id_list[i].id);
2183 if (strEqual(filetype_id_lower, id_lower))
2184 filetype = filetype_id_list[i].filetype;
2188 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2192 free(filetype_id_lower);
2197 char *getLocalLevelTemplateFilename(void)
2199 return getDefaultLevelFilename(-1);
2202 char *getGlobalLevelTemplateFilename(void)
2204 // global variable "leveldir_current" must be modified in the loop below
2205 LevelDirTree *leveldir_current_last = leveldir_current;
2206 char *filename = NULL;
2208 // check for template level in path from current to topmost tree node
2210 while (leveldir_current != NULL)
2212 filename = getDefaultLevelFilename(-1);
2214 if (fileExists(filename))
2217 leveldir_current = leveldir_current->node_parent;
2220 // restore global variable "leveldir_current" modified in above loop
2221 leveldir_current = leveldir_current_last;
2226 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2230 // special case: level number is negative => check for level template file
2233 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2234 getSingleLevelBasename(-1));
2236 // replace local level template filename with global template filename
2237 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2239 // no fallback if template file not existing
2243 // special case: check for file name/pattern specified in "levelinfo.conf"
2244 if (leveldir_current->level_filename != NULL)
2246 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2248 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2249 leveldir_current->level_filename, nr);
2251 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2253 if (fileExists(lfi->filename))
2256 else if (leveldir_current->level_filetype != NULL)
2258 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2260 // check for specified native level file with standard file name
2261 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2262 "%03d.%s", nr, LEVELFILE_EXTENSION);
2263 if (fileExists(lfi->filename))
2267 // check for native Rocks'n'Diamonds level file
2268 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2269 "%03d.%s", nr, LEVELFILE_EXTENSION);
2270 if (fileExists(lfi->filename))
2273 // check for Emerald Mine level file (V1)
2274 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2275 'a' + (nr / 10) % 26, '0' + nr % 10);
2276 if (fileExists(lfi->filename))
2278 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2279 'A' + (nr / 10) % 26, '0' + nr % 10);
2280 if (fileExists(lfi->filename))
2283 // check for Emerald Mine level file (V2 to V5)
2284 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2285 if (fileExists(lfi->filename))
2288 // check for Emerald Mine level file (V6 / single mode)
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2290 if (fileExists(lfi->filename))
2292 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2293 if (fileExists(lfi->filename))
2296 // check for Emerald Mine level file (V6 / teamwork mode)
2297 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2298 if (fileExists(lfi->filename))
2300 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2301 if (fileExists(lfi->filename))
2304 // check for various packed level file formats
2305 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2306 if (fileExists(lfi->filename))
2309 // no known level file found -- use default values (and fail later)
2310 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2311 "%03d.%s", nr, LEVELFILE_EXTENSION);
2314 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2316 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2317 lfi->type = getFileTypeFromBasename(lfi->basename);
2319 if (lfi->type == LEVEL_FILE_TYPE_RND)
2320 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2323 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2325 // always start with reliable default values
2326 setFileInfoToDefaults(level_file_info);
2328 level_file_info->nr = nr; // set requested level number
2330 determineLevelFileInfo_Filename(level_file_info);
2331 determineLevelFileInfo_Filetype(level_file_info);
2334 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2335 struct LevelFileInfo *lfi_to)
2337 lfi_to->nr = lfi_from->nr;
2338 lfi_to->type = lfi_from->type;
2339 lfi_to->packed = lfi_from->packed;
2341 setString(&lfi_to->basename, lfi_from->basename);
2342 setString(&lfi_to->filename, lfi_from->filename);
2345 // ----------------------------------------------------------------------------
2346 // functions for loading R'n'D level
2347 // ----------------------------------------------------------------------------
2349 int getMappedElement(int element)
2351 // remap some (historic, now obsolete) elements
2355 case EL_PLAYER_OBSOLETE:
2356 element = EL_PLAYER_1;
2359 case EL_KEY_OBSOLETE:
2363 case EL_EM_KEY_1_FILE_OBSOLETE:
2364 element = EL_EM_KEY_1;
2367 case EL_EM_KEY_2_FILE_OBSOLETE:
2368 element = EL_EM_KEY_2;
2371 case EL_EM_KEY_3_FILE_OBSOLETE:
2372 element = EL_EM_KEY_3;
2375 case EL_EM_KEY_4_FILE_OBSOLETE:
2376 element = EL_EM_KEY_4;
2379 case EL_ENVELOPE_OBSOLETE:
2380 element = EL_ENVELOPE_1;
2388 if (element >= NUM_FILE_ELEMENTS)
2390 Warn("invalid level element %d", element);
2392 element = EL_UNKNOWN;
2400 static int getMappedElementByVersion(int element, int game_version)
2402 // remap some elements due to certain game version
2404 if (game_version <= VERSION_IDENT(2,2,0,0))
2406 // map game font elements
2407 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2408 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2409 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2410 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2413 if (game_version < VERSION_IDENT(3,0,0,0))
2415 // map Supaplex gravity tube elements
2416 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2417 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2418 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2419 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2426 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2428 level->file_version = getFileVersion(file);
2429 level->game_version = getFileVersion(file);
2434 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2436 level->creation_date.year = getFile16BitBE(file);
2437 level->creation_date.month = getFile8Bit(file);
2438 level->creation_date.day = getFile8Bit(file);
2440 level->creation_date.src = DATE_SRC_LEVELFILE;
2445 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2447 int initial_player_stepsize;
2448 int initial_player_gravity;
2451 level->fieldx = getFile8Bit(file);
2452 level->fieldy = getFile8Bit(file);
2454 level->time = getFile16BitBE(file);
2455 level->gems_needed = getFile16BitBE(file);
2457 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2458 level->name[i] = getFile8Bit(file);
2459 level->name[MAX_LEVEL_NAME_LEN] = 0;
2461 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2462 level->score[i] = getFile8Bit(file);
2464 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2465 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2466 for (y = 0; y < 3; y++)
2467 for (x = 0; x < 3; x++)
2468 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2470 level->amoeba_speed = getFile8Bit(file);
2471 level->time_magic_wall = getFile8Bit(file);
2472 level->time_wheel = getFile8Bit(file);
2473 level->amoeba_content = getMappedElement(getFile8Bit(file));
2475 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2478 for (i = 0; i < MAX_PLAYERS; i++)
2479 level->initial_player_stepsize[i] = initial_player_stepsize;
2481 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2483 for (i = 0; i < MAX_PLAYERS; i++)
2484 level->initial_player_gravity[i] = initial_player_gravity;
2486 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2487 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2489 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2491 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2492 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2493 level->can_move_into_acid_bits = getFile32BitBE(file);
2494 level->dont_collide_with_bits = getFile8Bit(file);
2496 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2497 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2499 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2500 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2501 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 level->game_engine_type = getFile8Bit(file);
2505 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2510 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2514 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2515 level->name[i] = getFile8Bit(file);
2516 level->name[MAX_LEVEL_NAME_LEN] = 0;
2521 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2525 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2526 level->author[i] = getFile8Bit(file);
2527 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2532 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2535 int chunk_size_expected = level->fieldx * level->fieldy;
2537 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2538 stored with 16-bit encoding (and should be twice as big then).
2539 Even worse, playfield data was stored 16-bit when only yamyam content
2540 contained 16-bit elements and vice versa. */
2542 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2543 chunk_size_expected *= 2;
2545 if (chunk_size_expected != chunk_size)
2547 ReadUnusedBytesFromFile(file, chunk_size);
2548 return chunk_size_expected;
2551 for (y = 0; y < level->fieldy; y++)
2552 for (x = 0; x < level->fieldx; x++)
2553 level->field[x][y] =
2554 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2559 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2562 int header_size = 4;
2563 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2564 int chunk_size_expected = header_size + content_size;
2566 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2567 stored with 16-bit encoding (and should be twice as big then).
2568 Even worse, playfield data was stored 16-bit when only yamyam content
2569 contained 16-bit elements and vice versa. */
2571 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2572 chunk_size_expected += content_size;
2574 if (chunk_size_expected != chunk_size)
2576 ReadUnusedBytesFromFile(file, chunk_size);
2577 return chunk_size_expected;
2581 level->num_yamyam_contents = getFile8Bit(file);
2585 // correct invalid number of content fields -- should never happen
2586 if (level->num_yamyam_contents < 1 ||
2587 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2588 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2590 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2591 for (y = 0; y < 3; y++)
2592 for (x = 0; x < 3; x++)
2593 level->yamyam_content[i].e[x][y] =
2594 getMappedElement(level->encoding_16bit_field ?
2595 getFile16BitBE(file) : getFile8Bit(file));
2599 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2604 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2606 element = getMappedElement(getFile16BitBE(file));
2607 num_contents = getFile8Bit(file);
2609 getFile8Bit(file); // content x size (unused)
2610 getFile8Bit(file); // content y size (unused)
2612 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2614 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2615 for (y = 0; y < 3; y++)
2616 for (x = 0; x < 3; x++)
2617 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2619 // correct invalid number of content fields -- should never happen
2620 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2621 num_contents = STD_ELEMENT_CONTENTS;
2623 if (element == EL_YAMYAM)
2625 level->num_yamyam_contents = num_contents;
2627 for (i = 0; i < num_contents; i++)
2628 for (y = 0; y < 3; y++)
2629 for (x = 0; x < 3; x++)
2630 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2632 else if (element == EL_BD_AMOEBA)
2634 level->amoeba_content = content_array[0][0][0];
2638 Warn("cannot load content for element '%d'", element);
2644 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2650 int chunk_size_expected;
2652 element = getMappedElement(getFile16BitBE(file));
2653 if (!IS_ENVELOPE(element))
2654 element = EL_ENVELOPE_1;
2656 envelope_nr = element - EL_ENVELOPE_1;
2658 envelope_len = getFile16BitBE(file);
2660 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2661 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2663 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2665 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2666 if (chunk_size_expected != chunk_size)
2668 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2669 return chunk_size_expected;
2672 for (i = 0; i < envelope_len; i++)
2673 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2678 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2680 int num_changed_custom_elements = getFile16BitBE(file);
2681 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2684 if (chunk_size_expected != chunk_size)
2686 ReadUnusedBytesFromFile(file, chunk_size - 2);
2687 return chunk_size_expected;
2690 for (i = 0; i < num_changed_custom_elements; i++)
2692 int element = getMappedElement(getFile16BitBE(file));
2693 int properties = getFile32BitBE(file);
2695 if (IS_CUSTOM_ELEMENT(element))
2696 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2698 Warn("invalid custom element number %d", element);
2700 // older game versions that wrote level files with CUS1 chunks used
2701 // different default push delay values (not yet stored in level file)
2702 element_info[element].push_delay_fixed = 2;
2703 element_info[element].push_delay_random = 8;
2706 level->file_has_custom_elements = TRUE;
2711 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2713 int num_changed_custom_elements = getFile16BitBE(file);
2714 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2717 if (chunk_size_expected != chunk_size)
2719 ReadUnusedBytesFromFile(file, chunk_size - 2);
2720 return chunk_size_expected;
2723 for (i = 0; i < num_changed_custom_elements; i++)
2725 int element = getMappedElement(getFile16BitBE(file));
2726 int custom_target_element = getMappedElement(getFile16BitBE(file));
2728 if (IS_CUSTOM_ELEMENT(element))
2729 element_info[element].change->target_element = custom_target_element;
2731 Warn("invalid custom element number %d", element);
2734 level->file_has_custom_elements = TRUE;
2739 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2741 int num_changed_custom_elements = getFile16BitBE(file);
2742 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2745 if (chunk_size_expected != chunk_size)
2747 ReadUnusedBytesFromFile(file, chunk_size - 2);
2748 return chunk_size_expected;
2751 for (i = 0; i < num_changed_custom_elements; i++)
2753 int element = getMappedElement(getFile16BitBE(file));
2754 struct ElementInfo *ei = &element_info[element];
2755 unsigned int event_bits;
2757 if (!IS_CUSTOM_ELEMENT(element))
2759 Warn("invalid custom element number %d", element);
2761 element = EL_INTERNAL_DUMMY;
2764 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2765 ei->description[j] = getFile8Bit(file);
2766 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2768 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2770 // some free bytes for future properties and padding
2771 ReadUnusedBytesFromFile(file, 7);
2773 ei->use_gfx_element = getFile8Bit(file);
2774 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2776 ei->collect_score_initial = getFile8Bit(file);
2777 ei->collect_count_initial = getFile8Bit(file);
2779 ei->push_delay_fixed = getFile16BitBE(file);
2780 ei->push_delay_random = getFile16BitBE(file);
2781 ei->move_delay_fixed = getFile16BitBE(file);
2782 ei->move_delay_random = getFile16BitBE(file);
2784 ei->move_pattern = getFile16BitBE(file);
2785 ei->move_direction_initial = getFile8Bit(file);
2786 ei->move_stepsize = getFile8Bit(file);
2788 for (y = 0; y < 3; y++)
2789 for (x = 0; x < 3; x++)
2790 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2792 event_bits = getFile32BitBE(file);
2793 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2794 if (event_bits & (1 << j))
2795 ei->change->has_event[j] = TRUE;
2797 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2799 ei->change->delay_fixed = getFile16BitBE(file);
2800 ei->change->delay_random = getFile16BitBE(file);
2801 ei->change->delay_frames = getFile16BitBE(file);
2803 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2805 ei->change->explode = getFile8Bit(file);
2806 ei->change->use_target_content = getFile8Bit(file);
2807 ei->change->only_if_complete = getFile8Bit(file);
2808 ei->change->use_random_replace = getFile8Bit(file);
2810 ei->change->random_percentage = getFile8Bit(file);
2811 ei->change->replace_when = getFile8Bit(file);
2813 for (y = 0; y < 3; y++)
2814 for (x = 0; x < 3; x++)
2815 ei->change->target_content.e[x][y] =
2816 getMappedElement(getFile16BitBE(file));
2818 ei->slippery_type = getFile8Bit(file);
2820 // some free bytes for future properties and padding
2821 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2823 // mark that this custom element has been modified
2824 ei->modified_settings = TRUE;
2827 level->file_has_custom_elements = TRUE;
2832 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2834 struct ElementInfo *ei;
2835 int chunk_size_expected;
2839 // ---------- custom element base property values (96 bytes) ----------------
2841 element = getMappedElement(getFile16BitBE(file));
2843 if (!IS_CUSTOM_ELEMENT(element))
2845 Warn("invalid custom element number %d", element);
2847 ReadUnusedBytesFromFile(file, chunk_size - 2);
2852 ei = &element_info[element];
2854 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2855 ei->description[i] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2862 ei->num_change_pages = getFile8Bit(file);
2864 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size - 43);
2868 return chunk_size_expected;
2871 ei->ce_value_fixed_initial = getFile16BitBE(file);
2872 ei->ce_value_random_initial = getFile16BitBE(file);
2873 ei->use_last_ce_value = getFile8Bit(file);
2875 ei->use_gfx_element = getFile8Bit(file);
2876 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2878 ei->collect_score_initial = getFile8Bit(file);
2879 ei->collect_count_initial = getFile8Bit(file);
2881 ei->drop_delay_fixed = getFile8Bit(file);
2882 ei->push_delay_fixed = getFile8Bit(file);
2883 ei->drop_delay_random = getFile8Bit(file);
2884 ei->push_delay_random = getFile8Bit(file);
2885 ei->move_delay_fixed = getFile16BitBE(file);
2886 ei->move_delay_random = getFile16BitBE(file);
2888 // bits 0 - 15 of "move_pattern" ...
2889 ei->move_pattern = getFile16BitBE(file);
2890 ei->move_direction_initial = getFile8Bit(file);
2891 ei->move_stepsize = getFile8Bit(file);
2893 ei->slippery_type = getFile8Bit(file);
2895 for (y = 0; y < 3; y++)
2896 for (x = 0; x < 3; x++)
2897 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2899 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2900 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2901 ei->move_leave_type = getFile8Bit(file);
2903 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2904 ei->move_pattern |= (getFile16BitBE(file) << 16);
2906 ei->access_direction = getFile8Bit(file);
2908 ei->explosion_delay = getFile8Bit(file);
2909 ei->ignition_delay = getFile8Bit(file);
2910 ei->explosion_type = getFile8Bit(file);
2912 // some free bytes for future custom property values and padding
2913 ReadUnusedBytesFromFile(file, 1);
2915 // ---------- change page property values (48 bytes) ------------------------
2917 setElementChangePages(ei, ei->num_change_pages);
2919 for (i = 0; i < ei->num_change_pages; i++)
2921 struct ElementChangeInfo *change = &ei->change_page[i];
2922 unsigned int event_bits;
2924 // always start with reliable default values
2925 setElementChangeInfoToDefaults(change);
2927 // bits 0 - 31 of "has_event[]" ...
2928 event_bits = getFile32BitBE(file);
2929 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2930 if (event_bits & (1 << j))
2931 change->has_event[j] = TRUE;
2933 change->target_element = getMappedElement(getFile16BitBE(file));
2935 change->delay_fixed = getFile16BitBE(file);
2936 change->delay_random = getFile16BitBE(file);
2937 change->delay_frames = getFile16BitBE(file);
2939 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2941 change->explode = getFile8Bit(file);
2942 change->use_target_content = getFile8Bit(file);
2943 change->only_if_complete = getFile8Bit(file);
2944 change->use_random_replace = getFile8Bit(file);
2946 change->random_percentage = getFile8Bit(file);
2947 change->replace_when = getFile8Bit(file);
2949 for (y = 0; y < 3; y++)
2950 for (x = 0; x < 3; x++)
2951 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2953 change->can_change = getFile8Bit(file);
2955 change->trigger_side = getFile8Bit(file);
2957 change->trigger_player = getFile8Bit(file);
2958 change->trigger_page = getFile8Bit(file);
2960 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2961 CH_PAGE_ANY : (1 << change->trigger_page));
2963 change->has_action = getFile8Bit(file);
2964 change->action_type = getFile8Bit(file);
2965 change->action_mode = getFile8Bit(file);
2966 change->action_arg = getFile16BitBE(file);
2968 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2969 event_bits = getFile8Bit(file);
2970 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2971 if (event_bits & (1 << (j - 32)))
2972 change->has_event[j] = TRUE;
2975 // mark this custom element as modified
2976 ei->modified_settings = TRUE;
2978 level->file_has_custom_elements = TRUE;
2983 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2985 struct ElementInfo *ei;
2986 struct ElementGroupInfo *group;
2990 element = getMappedElement(getFile16BitBE(file));
2992 if (!IS_GROUP_ELEMENT(element))
2994 Warn("invalid group element number %d", element);
2996 ReadUnusedBytesFromFile(file, chunk_size - 2);
3001 ei = &element_info[element];
3003 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3004 ei->description[i] = getFile8Bit(file);
3005 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3007 group = element_info[element].group;
3009 group->num_elements = getFile8Bit(file);
3011 ei->use_gfx_element = getFile8Bit(file);
3012 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3014 group->choice_mode = getFile8Bit(file);
3016 // some free bytes for future values and padding
3017 ReadUnusedBytesFromFile(file, 3);
3019 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3020 group->element[i] = getMappedElement(getFile16BitBE(file));
3022 // mark this group element as modified
3023 element_info[element].modified_settings = TRUE;
3025 level->file_has_custom_elements = TRUE;
3030 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3031 int element, int real_element)
3033 int micro_chunk_size = 0;
3034 int conf_type = getFile8Bit(file);
3035 int byte_mask = conf_type & CONF_MASK_BYTES;
3036 boolean element_found = FALSE;
3039 micro_chunk_size += 1;
3041 if (byte_mask == CONF_MASK_MULTI_BYTES)
3043 int num_bytes = getFile16BitBE(file);
3044 byte *buffer = checked_malloc(num_bytes);
3046 ReadBytesFromFile(file, buffer, num_bytes);
3048 for (i = 0; conf[i].data_type != -1; i++)
3050 if (conf[i].element == element &&
3051 conf[i].conf_type == conf_type)
3053 int data_type = conf[i].data_type;
3054 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3055 int max_num_entities = conf[i].max_num_entities;
3057 if (num_entities > max_num_entities)
3059 Warn("truncating number of entities for element %d from %d to %d",
3060 element, num_entities, max_num_entities);
3062 num_entities = max_num_entities;
3065 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3066 data_type == TYPE_CONTENT_LIST))
3068 // for element and content lists, zero entities are not allowed
3069 Warn("found empty list of entities for element %d", element);
3071 // do not set "num_entities" here to prevent reading behind buffer
3073 *(int *)(conf[i].num_entities) = 1; // at least one is required
3077 *(int *)(conf[i].num_entities) = num_entities;
3080 element_found = TRUE;
3082 if (data_type == TYPE_STRING)
3084 char *string = (char *)(conf[i].value);
3087 for (j = 0; j < max_num_entities; j++)
3088 string[j] = (j < num_entities ? buffer[j] : '\0');
3090 else if (data_type == TYPE_ELEMENT_LIST)
3092 int *element_array = (int *)(conf[i].value);
3095 for (j = 0; j < num_entities; j++)
3097 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3099 else if (data_type == TYPE_CONTENT_LIST)
3101 struct Content *content= (struct Content *)(conf[i].value);
3104 for (c = 0; c < num_entities; c++)
3105 for (y = 0; y < 3; y++)
3106 for (x = 0; x < 3; x++)
3107 content[c].e[x][y] =
3108 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3111 element_found = FALSE;
3117 checked_free(buffer);
3119 micro_chunk_size += 2 + num_bytes;
3121 else // constant size configuration data (1, 2 or 4 bytes)
3123 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3124 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3125 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3127 for (i = 0; conf[i].data_type != -1; i++)
3129 if (conf[i].element == element &&
3130 conf[i].conf_type == conf_type)
3132 int data_type = conf[i].data_type;
3134 if (data_type == TYPE_ELEMENT)
3135 value = getMappedElement(value);
3137 if (data_type == TYPE_BOOLEAN)
3138 *(boolean *)(conf[i].value) = value;
3140 *(int *) (conf[i].value) = value;
3142 element_found = TRUE;
3148 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3153 char *error_conf_chunk_bytes =
3154 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3155 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3156 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3157 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3158 int error_element = real_element;
3160 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3161 error_conf_chunk_bytes, error_conf_chunk_token,
3162 error_element, EL_NAME(error_element));
3165 return micro_chunk_size;
3168 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3170 int real_chunk_size = 0;
3172 li = *level; // copy level data into temporary buffer
3174 while (!checkEndOfFile(file))
3176 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3178 if (real_chunk_size >= chunk_size)
3182 *level = li; // copy temporary buffer back to level data
3184 return real_chunk_size;
3187 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3189 int real_chunk_size = 0;
3191 li = *level; // copy level data into temporary buffer
3193 while (!checkEndOfFile(file))
3195 int element = getMappedElement(getFile16BitBE(file));
3197 real_chunk_size += 2;
3198 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3200 if (real_chunk_size >= chunk_size)
3204 *level = li; // copy temporary buffer back to level data
3206 return real_chunk_size;
3209 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3211 int real_chunk_size = 0;
3213 li = *level; // copy level data into temporary buffer
3215 while (!checkEndOfFile(file))
3217 int element = getMappedElement(getFile16BitBE(file));
3219 real_chunk_size += 2;
3220 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3222 if (real_chunk_size >= chunk_size)
3226 *level = li; // copy temporary buffer back to level data
3228 return real_chunk_size;
3231 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3233 int element = getMappedElement(getFile16BitBE(file));
3234 int envelope_nr = element - EL_ENVELOPE_1;
3235 int real_chunk_size = 2;
3237 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3239 while (!checkEndOfFile(file))
3241 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3244 if (real_chunk_size >= chunk_size)
3248 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3250 return real_chunk_size;
3253 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3255 int element = getMappedElement(getFile16BitBE(file));
3256 int real_chunk_size = 2;
3257 struct ElementInfo *ei = &element_info[element];
3260 xx_ei = *ei; // copy element data into temporary buffer
3262 xx_ei.num_change_pages = -1;
3264 while (!checkEndOfFile(file))
3266 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3268 if (xx_ei.num_change_pages != -1)
3271 if (real_chunk_size >= chunk_size)
3277 if (ei->num_change_pages == -1)
3279 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3282 ei->num_change_pages = 1;
3284 setElementChangePages(ei, 1);
3285 setElementChangeInfoToDefaults(ei->change);
3287 return real_chunk_size;
3290 // initialize number of change pages stored for this custom element
3291 setElementChangePages(ei, ei->num_change_pages);
3292 for (i = 0; i < ei->num_change_pages; i++)
3293 setElementChangeInfoToDefaults(&ei->change_page[i]);
3295 // start with reading properties for the first change page
3296 xx_current_change_page = 0;
3298 while (!checkEndOfFile(file))
3300 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3302 xx_change = *change; // copy change data into temporary buffer
3304 resetEventBits(); // reset bits; change page might have changed
3306 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3309 *change = xx_change;
3311 setEventFlagsFromEventBits(change);
3313 if (real_chunk_size >= chunk_size)
3317 level->file_has_custom_elements = TRUE;
3319 return real_chunk_size;
3322 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int real_chunk_size = 2;
3326 struct ElementInfo *ei = &element_info[element];
3327 struct ElementGroupInfo *group = ei->group;
3329 xx_ei = *ei; // copy element data into temporary buffer
3330 xx_group = *group; // copy group data into temporary buffer
3332 while (!checkEndOfFile(file))
3334 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3337 if (real_chunk_size >= chunk_size)
3344 level->file_has_custom_elements = TRUE;
3346 return real_chunk_size;
3349 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3350 struct LevelFileInfo *level_file_info,
3351 boolean level_info_only)
3353 char *filename = level_file_info->filename;
3354 char cookie[MAX_LINE_LEN];
3355 char chunk_name[CHUNK_ID_LEN + 1];
3359 if (!(file = openFile(filename, MODE_READ)))
3361 level->no_valid_file = TRUE;
3362 level->no_level_file = TRUE;
3364 if (level_info_only)
3367 Warn("cannot read level '%s' -- using empty level", filename);
3369 if (!setup.editor.use_template_for_new_levels)
3372 // if level file not found, try to initialize level data from template
3373 filename = getGlobalLevelTemplateFilename();
3375 if (!(file = openFile(filename, MODE_READ)))
3378 // default: for empty levels, use level template for custom elements
3379 level->use_custom_template = TRUE;
3381 level->no_valid_file = FALSE;
3384 getFileChunkBE(file, chunk_name, NULL);
3385 if (strEqual(chunk_name, "RND1"))
3387 getFile32BitBE(file); // not used
3389 getFileChunkBE(file, chunk_name, NULL);
3390 if (!strEqual(chunk_name, "CAVE"))
3392 level->no_valid_file = TRUE;
3394 Warn("unknown format of level file '%s'", filename);
3401 else // check for pre-2.0 file format with cookie string
3403 strcpy(cookie, chunk_name);
3404 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3406 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3407 cookie[strlen(cookie) - 1] = '\0';
3409 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3411 level->no_valid_file = TRUE;
3413 Warn("unknown format of level file '%s'", filename);
3420 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3422 level->no_valid_file = TRUE;
3424 Warn("unsupported version of level file '%s'", filename);
3431 // pre-2.0 level files have no game version, so use file version here
3432 level->game_version = level->file_version;
3435 if (level->file_version < FILE_VERSION_1_2)
3437 // level files from versions before 1.2.0 without chunk structure
3438 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3439 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3447 int (*loader)(File *, int, struct LevelInfo *);
3451 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3452 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3453 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3454 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3455 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3456 { "INFO", -1, LoadLevel_INFO },
3457 { "BODY", -1, LoadLevel_BODY },
3458 { "CONT", -1, LoadLevel_CONT },
3459 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3460 { "CNT3", -1, LoadLevel_CNT3 },
3461 { "CUS1", -1, LoadLevel_CUS1 },
3462 { "CUS2", -1, LoadLevel_CUS2 },
3463 { "CUS3", -1, LoadLevel_CUS3 },
3464 { "CUS4", -1, LoadLevel_CUS4 },
3465 { "GRP1", -1, LoadLevel_GRP1 },
3466 { "CONF", -1, LoadLevel_CONF },
3467 { "ELEM", -1, LoadLevel_ELEM },
3468 { "NOTE", -1, LoadLevel_NOTE },
3469 { "CUSX", -1, LoadLevel_CUSX },
3470 { "GRPX", -1, LoadLevel_GRPX },
3475 while (getFileChunkBE(file, chunk_name, &chunk_size))
3479 while (chunk_info[i].name != NULL &&
3480 !strEqual(chunk_name, chunk_info[i].name))
3483 if (chunk_info[i].name == NULL)
3485 Warn("unknown chunk '%s' in level file '%s'",
3486 chunk_name, filename);
3488 ReadUnusedBytesFromFile(file, chunk_size);
3490 else if (chunk_info[i].size != -1 &&
3491 chunk_info[i].size != chunk_size)
3493 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3494 chunk_size, chunk_name, filename);
3496 ReadUnusedBytesFromFile(file, chunk_size);
3500 // call function to load this level chunk
3501 int chunk_size_expected =
3502 (chunk_info[i].loader)(file, chunk_size, level);
3504 // the size of some chunks cannot be checked before reading other
3505 // chunks first (like "HEAD" and "BODY") that contain some header
3506 // information, so check them here
3507 if (chunk_size_expected != chunk_size)
3509 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3510 chunk_size, chunk_name, filename);
3520 // ----------------------------------------------------------------------------
3521 // functions for loading EM level
3522 // ----------------------------------------------------------------------------
3524 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3526 static int ball_xy[8][2] =
3537 struct LevelInfo_EM *level_em = level->native_em_level;
3538 struct CAVE *cav = level_em->cav;
3541 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3542 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3544 cav->time_seconds = level->time;
3545 cav->gems_needed = level->gems_needed;
3547 cav->emerald_score = level->score[SC_EMERALD];
3548 cav->diamond_score = level->score[SC_DIAMOND];
3549 cav->alien_score = level->score[SC_ROBOT];
3550 cav->tank_score = level->score[SC_SPACESHIP];
3551 cav->bug_score = level->score[SC_BUG];
3552 cav->eater_score = level->score[SC_YAMYAM];
3553 cav->nut_score = level->score[SC_NUT];
3554 cav->dynamite_score = level->score[SC_DYNAMITE];
3555 cav->key_score = level->score[SC_KEY];
3556 cav->exit_score = level->score[SC_TIME_BONUS];
3558 cav->num_eater_arrays = level->num_yamyam_contents;
3560 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3561 for (y = 0; y < 3; y++)
3562 for (x = 0; x < 3; x++)
3563 cav->eater_array[i][y * 3 + x] =
3564 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3566 cav->amoeba_time = level->amoeba_speed;
3567 cav->wonderwall_time = level->time_magic_wall;
3568 cav->wheel_time = level->time_wheel;
3570 cav->android_move_time = level->android_move_time;
3571 cav->android_clone_time = level->android_clone_time;
3572 cav->ball_random = level->ball_random;
3573 cav->ball_active = level->ball_active_initial;
3574 cav->ball_time = level->ball_time;
3575 cav->num_ball_arrays = level->num_ball_contents;
3577 cav->lenses_score = level->lenses_score;
3578 cav->magnify_score = level->magnify_score;
3579 cav->slurp_score = level->slurp_score;
3581 cav->lenses_time = level->lenses_time;
3582 cav->magnify_time = level->magnify_time;
3584 cav->wind_direction =
3585 map_direction_RND_to_EM(level->wind_direction_initial);
3587 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3588 for (j = 0; j < 8; j++)
3589 cav->ball_array[i][j] =
3590 map_element_RND_to_EM_cave(level->ball_content[i].
3591 e[ball_xy[j][0]][ball_xy[j][1]]);
3593 map_android_clone_elements_RND_to_EM(level);
3595 // first fill the complete playfield with the empty space element
3596 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3597 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3598 cav->cave[x][y] = Cblank;
3600 // then copy the real level contents from level file into the playfield
3601 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3603 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3605 if (level->field[x][y] == EL_AMOEBA_DEAD)
3606 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3608 cav->cave[x][y] = new_element;
3611 for (i = 0; i < MAX_PLAYERS; i++)
3613 cav->player_x[i] = -1;
3614 cav->player_y[i] = -1;
3617 // initialize player positions and delete players from the playfield
3618 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3620 if (ELEM_IS_PLAYER(level->field[x][y]))
3622 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3624 cav->player_x[player_nr] = x;
3625 cav->player_y[player_nr] = y;
3627 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3632 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3634 static int ball_xy[8][2] =
3645 struct LevelInfo_EM *level_em = level->native_em_level;
3646 struct CAVE *cav = level_em->cav;
3649 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3650 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3652 level->time = cav->time_seconds;
3653 level->gems_needed = cav->gems_needed;
3655 sprintf(level->name, "Level %d", level->file_info.nr);
3657 level->score[SC_EMERALD] = cav->emerald_score;
3658 level->score[SC_DIAMOND] = cav->diamond_score;
3659 level->score[SC_ROBOT] = cav->alien_score;
3660 level->score[SC_SPACESHIP] = cav->tank_score;
3661 level->score[SC_BUG] = cav->bug_score;
3662 level->score[SC_YAMYAM] = cav->eater_score;
3663 level->score[SC_NUT] = cav->nut_score;
3664 level->score[SC_DYNAMITE] = cav->dynamite_score;
3665 level->score[SC_KEY] = cav->key_score;
3666 level->score[SC_TIME_BONUS] = cav->exit_score;
3668 level->num_yamyam_contents = cav->num_eater_arrays;
3670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3671 for (y = 0; y < 3; y++)
3672 for (x = 0; x < 3; x++)
3673 level->yamyam_content[i].e[x][y] =
3674 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3676 level->amoeba_speed = cav->amoeba_time;
3677 level->time_magic_wall = cav->wonderwall_time;
3678 level->time_wheel = cav->wheel_time;
3680 level->android_move_time = cav->android_move_time;
3681 level->android_clone_time = cav->android_clone_time;
3682 level->ball_random = cav->ball_random;
3683 level->ball_active_initial = cav->ball_active;
3684 level->ball_time = cav->ball_time;
3685 level->num_ball_contents = cav->num_ball_arrays;
3687 level->lenses_score = cav->lenses_score;
3688 level->magnify_score = cav->magnify_score;
3689 level->slurp_score = cav->slurp_score;
3691 level->lenses_time = cav->lenses_time;
3692 level->magnify_time = cav->magnify_time;
3694 level->wind_direction_initial =
3695 map_direction_EM_to_RND(cav->wind_direction);
3697 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3698 for (j = 0; j < 8; j++)
3699 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3700 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3702 map_android_clone_elements_EM_to_RND(level);
3704 // convert the playfield (some elements need special treatment)
3705 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3707 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3709 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3710 new_element = EL_AMOEBA_DEAD;
3712 level->field[x][y] = new_element;
3715 for (i = 0; i < MAX_PLAYERS; i++)
3717 // in case of all players set to the same field, use the first player
3718 int nr = MAX_PLAYERS - i - 1;
3719 int jx = cav->player_x[nr];
3720 int jy = cav->player_y[nr];
3722 if (jx != -1 && jy != -1)
3723 level->field[jx][jy] = EL_PLAYER_1 + nr;
3728 // ----------------------------------------------------------------------------
3729 // functions for loading SP level
3730 // ----------------------------------------------------------------------------
3732 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3734 struct LevelInfo_SP *level_sp = level->native_sp_level;
3735 LevelInfoType *header = &level_sp->header;
3738 level_sp->width = level->fieldx;
3739 level_sp->height = level->fieldy;
3741 for (x = 0; x < level->fieldx; x++)
3742 for (y = 0; y < level->fieldy; y++)
3743 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3745 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3747 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3748 header->LevelTitle[i] = level->name[i];
3749 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3751 header->InfotronsNeeded = level->gems_needed;
3753 header->SpecialPortCount = 0;
3755 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3757 boolean gravity_port_found = FALSE;
3758 boolean gravity_port_valid = FALSE;
3759 int gravity_port_flag;
3760 int gravity_port_base_element;
3761 int element = level->field[x][y];
3763 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3764 element <= EL_SP_GRAVITY_ON_PORT_UP)
3766 gravity_port_found = TRUE;
3767 gravity_port_valid = TRUE;
3768 gravity_port_flag = 1;
3769 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3771 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3772 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3774 gravity_port_found = TRUE;
3775 gravity_port_valid = TRUE;
3776 gravity_port_flag = 0;
3777 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3779 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3780 element <= EL_SP_GRAVITY_PORT_UP)
3782 // change R'n'D style gravity inverting special port to normal port
3783 // (there are no gravity inverting ports in native Supaplex engine)
3785 gravity_port_found = TRUE;
3786 gravity_port_valid = FALSE;
3787 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3790 if (gravity_port_found)
3792 if (gravity_port_valid &&
3793 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3795 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3797 port->PortLocation = (y * level->fieldx + x) * 2;
3798 port->Gravity = gravity_port_flag;
3800 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3802 header->SpecialPortCount++;
3806 // change special gravity port to normal port
3808 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3811 level_sp->playfield[x][y] = element - EL_SP_START;
3816 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3818 struct LevelInfo_SP *level_sp = level->native_sp_level;
3819 LevelInfoType *header = &level_sp->header;
3820 boolean num_invalid_elements = 0;
3823 level->fieldx = level_sp->width;
3824 level->fieldy = level_sp->height;
3826 for (x = 0; x < level->fieldx; x++)
3828 for (y = 0; y < level->fieldy; y++)
3830 int element_old = level_sp->playfield[x][y];
3831 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3833 if (element_new == EL_UNKNOWN)
3835 num_invalid_elements++;
3837 Debug("level:native:SP", "invalid element %d at position %d, %d",
3841 level->field[x][y] = element_new;
3845 if (num_invalid_elements > 0)
3846 Warn("found %d invalid elements%s", num_invalid_elements,
3847 (!options.debug ? " (use '--debug' for more details)" : ""));
3849 for (i = 0; i < MAX_PLAYERS; i++)
3850 level->initial_player_gravity[i] =
3851 (header->InitialGravity == 1 ? TRUE : FALSE);
3853 // skip leading spaces
3854 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3855 if (header->LevelTitle[i] != ' ')
3859 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3860 level->name[j] = header->LevelTitle[i];
3861 level->name[j] = '\0';
3863 // cut trailing spaces
3865 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3866 level->name[j - 1] = '\0';
3868 level->gems_needed = header->InfotronsNeeded;
3870 for (i = 0; i < header->SpecialPortCount; i++)
3872 SpecialPortType *port = &header->SpecialPort[i];
3873 int port_location = port->PortLocation;
3874 int gravity = port->Gravity;
3875 int port_x, port_y, port_element;
3877 port_x = (port_location / 2) % level->fieldx;
3878 port_y = (port_location / 2) / level->fieldx;
3880 if (port_x < 0 || port_x >= level->fieldx ||
3881 port_y < 0 || port_y >= level->fieldy)
3883 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3888 port_element = level->field[port_x][port_y];
3890 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3891 port_element > EL_SP_GRAVITY_PORT_UP)
3893 Warn("no special port at position (%d, %d)", port_x, port_y);
3898 // change previous (wrong) gravity inverting special port to either
3899 // gravity enabling special port or gravity disabling special port
3900 level->field[port_x][port_y] +=
3901 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3902 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3905 // change special gravity ports without database entries to normal ports
3906 for (x = 0; x < level->fieldx; x++)
3907 for (y = 0; y < level->fieldy; y++)
3908 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3909 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3910 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3912 level->time = 0; // no time limit
3913 level->amoeba_speed = 0;
3914 level->time_magic_wall = 0;
3915 level->time_wheel = 0;
3916 level->amoeba_content = EL_EMPTY;
3919 // original Supaplex does not use score values -- use default values
3921 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3922 level->score[i] = 0;
3925 // there are no yamyams in supaplex levels
3926 for (i = 0; i < level->num_yamyam_contents; i++)
3927 for (x = 0; x < 3; x++)
3928 for (y = 0; y < 3; y++)
3929 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3932 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3934 struct LevelInfo_SP *level_sp = level->native_sp_level;
3935 struct DemoInfo_SP *demo = &level_sp->demo;
3938 // always start with reliable default values
3939 demo->is_available = FALSE;
3942 if (TAPE_IS_EMPTY(tape))
3945 demo->level_nr = tape.level_nr; // (currently not used)
3947 level_sp->header.DemoRandomSeed = tape.random_seed;
3951 for (i = 0; i < tape.length; i++)
3953 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3954 int demo_repeat = tape.pos[i].delay;
3955 int demo_entries = (demo_repeat + 15) / 16;
3957 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3959 Warn("tape truncated: size exceeds maximum SP demo size %d",
3965 for (j = 0; j < demo_repeat / 16; j++)
3966 demo->data[demo->length++] = 0xf0 | demo_action;
3968 if (demo_repeat % 16)
3969 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3972 demo->is_available = TRUE;
3975 static void setTapeInfoToDefaults(void);
3977 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3979 struct LevelInfo_SP *level_sp = level->native_sp_level;
3980 struct DemoInfo_SP *demo = &level_sp->demo;
3981 char *filename = level->file_info.filename;
3984 // always start with reliable default values
3985 setTapeInfoToDefaults();
3987 if (!demo->is_available)
3990 tape.level_nr = demo->level_nr; // (currently not used)
3991 tape.random_seed = level_sp->header.DemoRandomSeed;
3993 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3996 tape.pos[tape.counter].delay = 0;
3998 for (i = 0; i < demo->length; i++)
4000 int demo_action = demo->data[i] & 0x0f;
4001 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4002 int tape_action = map_key_SP_to_RND(demo_action);
4003 int tape_repeat = demo_repeat + 1;
4004 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4005 boolean success = 0;
4008 for (j = 0; j < tape_repeat; j++)
4009 success = TapeAddAction(action);
4013 Warn("SP demo truncated: size exceeds maximum tape size %d",
4020 TapeHaltRecording();
4024 // ----------------------------------------------------------------------------
4025 // functions for loading MM level
4026 // ----------------------------------------------------------------------------
4028 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4030 struct LevelInfo_MM *level_mm = level->native_mm_level;
4033 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4034 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4036 level_mm->time = level->time;
4037 level_mm->kettles_needed = level->gems_needed;
4038 level_mm->auto_count_kettles = level->auto_count_gems;
4040 level_mm->laser_red = level->mm_laser_red;
4041 level_mm->laser_green = level->mm_laser_green;
4042 level_mm->laser_blue = level->mm_laser_blue;
4044 strcpy(level_mm->name, level->name);
4045 strcpy(level_mm->author, level->author);
4047 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4048 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4049 level_mm->score[SC_KEY] = level->score[SC_KEY];
4050 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4051 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4053 level_mm->amoeba_speed = level->amoeba_speed;
4054 level_mm->time_fuse = level->mm_time_fuse;
4055 level_mm->time_bomb = level->mm_time_bomb;
4056 level_mm->time_ball = level->mm_time_ball;
4057 level_mm->time_block = level->mm_time_block;
4059 for (x = 0; x < level->fieldx; x++)
4060 for (y = 0; y < level->fieldy; y++)
4062 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4065 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4067 struct LevelInfo_MM *level_mm = level->native_mm_level;
4070 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4071 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4073 level->time = level_mm->time;
4074 level->gems_needed = level_mm->kettles_needed;
4075 level->auto_count_gems = level_mm->auto_count_kettles;
4077 level->mm_laser_red = level_mm->laser_red;
4078 level->mm_laser_green = level_mm->laser_green;
4079 level->mm_laser_blue = level_mm->laser_blue;
4081 strcpy(level->name, level_mm->name);
4083 // only overwrite author from 'levelinfo.conf' if author defined in level
4084 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4085 strcpy(level->author, level_mm->author);
4087 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4088 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4089 level->score[SC_KEY] = level_mm->score[SC_KEY];
4090 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4091 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4093 level->amoeba_speed = level_mm->amoeba_speed;
4094 level->mm_time_fuse = level_mm->time_fuse;
4095 level->mm_time_bomb = level_mm->time_bomb;
4096 level->mm_time_ball = level_mm->time_ball;
4097 level->mm_time_block = level_mm->time_block;
4099 for (x = 0; x < level->fieldx; x++)
4100 for (y = 0; y < level->fieldy; y++)
4101 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4105 // ----------------------------------------------------------------------------
4106 // functions for loading DC level
4107 // ----------------------------------------------------------------------------
4109 #define DC_LEVEL_HEADER_SIZE 344
4111 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4114 static int last_data_encoded;
4118 int diff_hi, diff_lo;
4119 int data_hi, data_lo;
4120 unsigned short data_decoded;
4124 last_data_encoded = 0;
4131 diff = data_encoded - last_data_encoded;
4132 diff_hi = diff & ~0xff;
4133 diff_lo = diff & 0xff;
4137 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4138 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4139 data_hi = data_hi & 0xff00;
4141 data_decoded = data_hi | data_lo;
4143 last_data_encoded = data_encoded;
4145 offset1 = (offset1 + 1) % 31;
4146 offset2 = offset2 & 0xff;
4148 return data_decoded;
4151 static int getMappedElement_DC(int element)
4159 // 0x0117 - 0x036e: (?)
4162 // 0x042d - 0x0684: (?)
4178 element = EL_CRYSTAL;
4181 case 0x0e77: // quicksand (boulder)
4182 element = EL_QUICKSAND_FAST_FULL;
4185 case 0x0e99: // slow quicksand (boulder)
4186 element = EL_QUICKSAND_FULL;
4190 element = EL_EM_EXIT_OPEN;
4194 element = EL_EM_EXIT_CLOSED;
4198 element = EL_EM_STEEL_EXIT_OPEN;
4202 element = EL_EM_STEEL_EXIT_CLOSED;
4205 case 0x0f4f: // dynamite (lit 1)
4206 element = EL_EM_DYNAMITE_ACTIVE;
4209 case 0x0f57: // dynamite (lit 2)
4210 element = EL_EM_DYNAMITE_ACTIVE;
4213 case 0x0f5f: // dynamite (lit 3)
4214 element = EL_EM_DYNAMITE_ACTIVE;
4217 case 0x0f67: // dynamite (lit 4)
4218 element = EL_EM_DYNAMITE_ACTIVE;
4225 element = EL_AMOEBA_WET;
4229 element = EL_AMOEBA_DROP;
4233 element = EL_DC_MAGIC_WALL;
4237 element = EL_SPACESHIP_UP;
4241 element = EL_SPACESHIP_DOWN;
4245 element = EL_SPACESHIP_LEFT;
4249 element = EL_SPACESHIP_RIGHT;
4253 element = EL_BUG_UP;
4257 element = EL_BUG_DOWN;
4261 element = EL_BUG_LEFT;
4265 element = EL_BUG_RIGHT;
4269 element = EL_MOLE_UP;
4273 element = EL_MOLE_DOWN;
4277 element = EL_MOLE_LEFT;
4281 element = EL_MOLE_RIGHT;
4289 element = EL_YAMYAM_UP;
4293 element = EL_SWITCHGATE_OPEN;
4297 element = EL_SWITCHGATE_CLOSED;
4301 element = EL_DC_SWITCHGATE_SWITCH_UP;
4305 element = EL_TIMEGATE_CLOSED;
4308 case 0x144c: // conveyor belt switch (green)
4309 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4312 case 0x144f: // conveyor belt switch (red)
4313 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4316 case 0x1452: // conveyor belt switch (blue)
4317 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4321 element = EL_CONVEYOR_BELT_3_MIDDLE;
4325 element = EL_CONVEYOR_BELT_3_LEFT;
4329 element = EL_CONVEYOR_BELT_3_RIGHT;
4333 element = EL_CONVEYOR_BELT_1_MIDDLE;
4337 element = EL_CONVEYOR_BELT_1_LEFT;
4341 element = EL_CONVEYOR_BELT_1_RIGHT;
4345 element = EL_CONVEYOR_BELT_4_MIDDLE;
4349 element = EL_CONVEYOR_BELT_4_LEFT;
4353 element = EL_CONVEYOR_BELT_4_RIGHT;
4357 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4361 element = EL_EXPANDABLE_WALL_VERTICAL;
4365 element = EL_EXPANDABLE_WALL_ANY;
4368 case 0x14ce: // growing steel wall (left/right)
4369 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4372 case 0x14df: // growing steel wall (up/down)
4373 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4376 case 0x14e8: // growing steel wall (up/down/left/right)
4377 element = EL_EXPANDABLE_STEELWALL_ANY;
4381 element = EL_SHIELD_DEADLY;
4385 element = EL_EXTRA_TIME;
4393 element = EL_EMPTY_SPACE;
4396 case 0x1578: // quicksand (empty)
4397 element = EL_QUICKSAND_FAST_EMPTY;
4400 case 0x1579: // slow quicksand (empty)
4401 element = EL_QUICKSAND_EMPTY;
4411 element = EL_EM_DYNAMITE;
4414 case 0x15a1: // key (red)
4415 element = EL_EM_KEY_1;
4418 case 0x15a2: // key (yellow)
4419 element = EL_EM_KEY_2;
4422 case 0x15a3: // key (blue)
4423 element = EL_EM_KEY_4;
4426 case 0x15a4: // key (green)
4427 element = EL_EM_KEY_3;
4430 case 0x15a5: // key (white)
4431 element = EL_DC_KEY_WHITE;
4435 element = EL_WALL_SLIPPERY;
4442 case 0x15a8: // wall (not round)
4446 case 0x15a9: // (blue)
4447 element = EL_CHAR_A;
4450 case 0x15aa: // (blue)
4451 element = EL_CHAR_B;
4454 case 0x15ab: // (blue)
4455 element = EL_CHAR_C;
4458 case 0x15ac: // (blue)
4459 element = EL_CHAR_D;
4462 case 0x15ad: // (blue)
4463 element = EL_CHAR_E;
4466 case 0x15ae: // (blue)
4467 element = EL_CHAR_F;
4470 case 0x15af: // (blue)
4471 element = EL_CHAR_G;
4474 case 0x15b0: // (blue)
4475 element = EL_CHAR_H;
4478 case 0x15b1: // (blue)
4479 element = EL_CHAR_I;
4482 case 0x15b2: // (blue)
4483 element = EL_CHAR_J;
4486 case 0x15b3: // (blue)
4487 element = EL_CHAR_K;
4490 case 0x15b4: // (blue)
4491 element = EL_CHAR_L;
4494 case 0x15b5: // (blue)
4495 element = EL_CHAR_M;
4498 case 0x15b6: // (blue)
4499 element = EL_CHAR_N;
4502 case 0x15b7: // (blue)
4503 element = EL_CHAR_O;
4506 case 0x15b8: // (blue)
4507 element = EL_CHAR_P;
4510 case 0x15b9: // (blue)
4511 element = EL_CHAR_Q;
4514 case 0x15ba: // (blue)
4515 element = EL_CHAR_R;
4518 case 0x15bb: // (blue)
4519 element = EL_CHAR_S;
4522 case 0x15bc: // (blue)
4523 element = EL_CHAR_T;
4526 case 0x15bd: // (blue)
4527 element = EL_CHAR_U;
4530 case 0x15be: // (blue)
4531 element = EL_CHAR_V;
4534 case 0x15bf: // (blue)
4535 element = EL_CHAR_W;
4538 case 0x15c0: // (blue)
4539 element = EL_CHAR_X;
4542 case 0x15c1: // (blue)
4543 element = EL_CHAR_Y;
4546 case 0x15c2: // (blue)
4547 element = EL_CHAR_Z;
4550 case 0x15c3: // (blue)
4551 element = EL_CHAR_AUMLAUT;
4554 case 0x15c4: // (blue)
4555 element = EL_CHAR_OUMLAUT;
4558 case 0x15c5: // (blue)
4559 element = EL_CHAR_UUMLAUT;
4562 case 0x15c6: // (blue)
4563 element = EL_CHAR_0;
4566 case 0x15c7: // (blue)
4567 element = EL_CHAR_1;
4570 case 0x15c8: // (blue)
4571 element = EL_CHAR_2;
4574 case 0x15c9: // (blue)
4575 element = EL_CHAR_3;
4578 case 0x15ca: // (blue)
4579 element = EL_CHAR_4;
4582 case 0x15cb: // (blue)
4583 element = EL_CHAR_5;
4586 case 0x15cc: // (blue)
4587 element = EL_CHAR_6;
4590 case 0x15cd: // (blue)
4591 element = EL_CHAR_7;
4594 case 0x15ce: // (blue)
4595 element = EL_CHAR_8;
4598 case 0x15cf: // (blue)
4599 element = EL_CHAR_9;
4602 case 0x15d0: // (blue)
4603 element = EL_CHAR_PERIOD;
4606 case 0x15d1: // (blue)
4607 element = EL_CHAR_EXCLAM;
4610 case 0x15d2: // (blue)
4611 element = EL_CHAR_COLON;
4614 case 0x15d3: // (blue)
4615 element = EL_CHAR_LESS;
4618 case 0x15d4: // (blue)
4619 element = EL_CHAR_GREATER;
4622 case 0x15d5: // (blue)
4623 element = EL_CHAR_QUESTION;
4626 case 0x15d6: // (blue)
4627 element = EL_CHAR_COPYRIGHT;
4630 case 0x15d7: // (blue)
4631 element = EL_CHAR_UP;
4634 case 0x15d8: // (blue)
4635 element = EL_CHAR_DOWN;
4638 case 0x15d9: // (blue)
4639 element = EL_CHAR_BUTTON;
4642 case 0x15da: // (blue)
4643 element = EL_CHAR_PLUS;
4646 case 0x15db: // (blue)
4647 element = EL_CHAR_MINUS;
4650 case 0x15dc: // (blue)
4651 element = EL_CHAR_APOSTROPHE;
4654 case 0x15dd: // (blue)
4655 element = EL_CHAR_PARENLEFT;
4658 case 0x15de: // (blue)
4659 element = EL_CHAR_PARENRIGHT;
4662 case 0x15df: // (green)
4663 element = EL_CHAR_A;
4666 case 0x15e0: // (green)
4667 element = EL_CHAR_B;
4670 case 0x15e1: // (green)
4671 element = EL_CHAR_C;
4674 case 0x15e2: // (green)
4675 element = EL_CHAR_D;
4678 case 0x15e3: // (green)
4679 element = EL_CHAR_E;
4682 case 0x15e4: // (green)
4683 element = EL_CHAR_F;
4686 case 0x15e5: // (green)
4687 element = EL_CHAR_G;
4690 case 0x15e6: // (green)
4691 element = EL_CHAR_H;
4694 case 0x15e7: // (green)
4695 element = EL_CHAR_I;
4698 case 0x15e8: // (green)
4699 element = EL_CHAR_J;
4702 case 0x15e9: // (green)
4703 element = EL_CHAR_K;
4706 case 0x15ea: // (green)
4707 element = EL_CHAR_L;
4710 case 0x15eb: // (green)
4711 element = EL_CHAR_M;
4714 case 0x15ec: // (green)
4715 element = EL_CHAR_N;
4718 case 0x15ed: // (green)
4719 element = EL_CHAR_O;
4722 case 0x15ee: // (green)
4723 element = EL_CHAR_P;
4726 case 0x15ef: // (green)
4727 element = EL_CHAR_Q;
4730 case 0x15f0: // (green)
4731 element = EL_CHAR_R;
4734 case 0x15f1: // (green)
4735 element = EL_CHAR_S;
4738 case 0x15f2: // (green)
4739 element = EL_CHAR_T;
4742 case 0x15f3: // (green)
4743 element = EL_CHAR_U;
4746 case 0x15f4: // (green)
4747 element = EL_CHAR_V;
4750 case 0x15f5: // (green)
4751 element = EL_CHAR_W;
4754 case 0x15f6: // (green)
4755 element = EL_CHAR_X;
4758 case 0x15f7: // (green)
4759 element = EL_CHAR_Y;
4762 case 0x15f8: // (green)
4763 element = EL_CHAR_Z;
4766 case 0x15f9: // (green)
4767 element = EL_CHAR_AUMLAUT;
4770 case 0x15fa: // (green)
4771 element = EL_CHAR_OUMLAUT;
4774 case 0x15fb: // (green)
4775 element = EL_CHAR_UUMLAUT;
4778 case 0x15fc: // (green)
4779 element = EL_CHAR_0;
4782 case 0x15fd: // (green)
4783 element = EL_CHAR_1;
4786 case 0x15fe: // (green)
4787 element = EL_CHAR_2;
4790 case 0x15ff: // (green)
4791 element = EL_CHAR_3;
4794 case 0x1600: // (green)
4795 element = EL_CHAR_4;
4798 case 0x1601: // (green)
4799 element = EL_CHAR_5;
4802 case 0x1602: // (green)
4803 element = EL_CHAR_6;
4806 case 0x1603: // (green)
4807 element = EL_CHAR_7;
4810 case 0x1604: // (green)
4811 element = EL_CHAR_8;
4814 case 0x1605: // (green)
4815 element = EL_CHAR_9;
4818 case 0x1606: // (green)
4819 element = EL_CHAR_PERIOD;
4822 case 0x1607: // (green)
4823 element = EL_CHAR_EXCLAM;
4826 case 0x1608: // (green)
4827 element = EL_CHAR_COLON;
4830 case 0x1609: // (green)
4831 element = EL_CHAR_LESS;
4834 case 0x160a: // (green)
4835 element = EL_CHAR_GREATER;
4838 case 0x160b: // (green)
4839 element = EL_CHAR_QUESTION;
4842 case 0x160c: // (green)
4843 element = EL_CHAR_COPYRIGHT;
4846 case 0x160d: // (green)
4847 element = EL_CHAR_UP;
4850 case 0x160e: // (green)
4851 element = EL_CHAR_DOWN;
4854 case 0x160f: // (green)
4855 element = EL_CHAR_BUTTON;
4858 case 0x1610: // (green)
4859 element = EL_CHAR_PLUS;
4862 case 0x1611: // (green)
4863 element = EL_CHAR_MINUS;
4866 case 0x1612: // (green)
4867 element = EL_CHAR_APOSTROPHE;
4870 case 0x1613: // (green)
4871 element = EL_CHAR_PARENLEFT;
4874 case 0x1614: // (green)
4875 element = EL_CHAR_PARENRIGHT;
4878 case 0x1615: // (blue steel)
4879 element = EL_STEEL_CHAR_A;
4882 case 0x1616: // (blue steel)
4883 element = EL_STEEL_CHAR_B;
4886 case 0x1617: // (blue steel)
4887 element = EL_STEEL_CHAR_C;
4890 case 0x1618: // (blue steel)
4891 element = EL_STEEL_CHAR_D;
4894 case 0x1619: // (blue steel)
4895 element = EL_STEEL_CHAR_E;
4898 case 0x161a: // (blue steel)
4899 element = EL_STEEL_CHAR_F;
4902 case 0x161b: // (blue steel)
4903 element = EL_STEEL_CHAR_G;
4906 case 0x161c: // (blue steel)
4907 element = EL_STEEL_CHAR_H;
4910 case 0x161d: // (blue steel)
4911 element = EL_STEEL_CHAR_I;
4914 case 0x161e: // (blue steel)
4915 element = EL_STEEL_CHAR_J;
4918 case 0x161f: // (blue steel)
4919 element = EL_STEEL_CHAR_K;
4922 case 0x1620: // (blue steel)
4923 element = EL_STEEL_CHAR_L;
4926 case 0x1621: // (blue steel)
4927 element = EL_STEEL_CHAR_M;
4930 case 0x1622: // (blue steel)
4931 element = EL_STEEL_CHAR_N;
4934 case 0x1623: // (blue steel)
4935 element = EL_STEEL_CHAR_O;
4938 case 0x1624: // (blue steel)
4939 element = EL_STEEL_CHAR_P;
4942 case 0x1625: // (blue steel)
4943 element = EL_STEEL_CHAR_Q;
4946 case 0x1626: // (blue steel)
4947 element = EL_STEEL_CHAR_R;
4950 case 0x1627: // (blue steel)
4951 element = EL_STEEL_CHAR_S;
4954 case 0x1628: // (blue steel)
4955 element = EL_STEEL_CHAR_T;
4958 case 0x1629: // (blue steel)
4959 element = EL_STEEL_CHAR_U;
4962 case 0x162a: // (blue steel)
4963 element = EL_STEEL_CHAR_V;
4966 case 0x162b: // (blue steel)
4967 element = EL_STEEL_CHAR_W;
4970 case 0x162c: // (blue steel)
4971 element = EL_STEEL_CHAR_X;
4974 case 0x162d: // (blue steel)
4975 element = EL_STEEL_CHAR_Y;
4978 case 0x162e: // (blue steel)
4979 element = EL_STEEL_CHAR_Z;
4982 case 0x162f: // (blue steel)
4983 element = EL_STEEL_CHAR_AUMLAUT;
4986 case 0x1630: // (blue steel)
4987 element = EL_STEEL_CHAR_OUMLAUT;
4990 case 0x1631: // (blue steel)
4991 element = EL_STEEL_CHAR_UUMLAUT;
4994 case 0x1632: // (blue steel)
4995 element = EL_STEEL_CHAR_0;
4998 case 0x1633: // (blue steel)
4999 element = EL_STEEL_CHAR_1;
5002 case 0x1634: // (blue steel)
5003 element = EL_STEEL_CHAR_2;
5006 case 0x1635: // (blue steel)
5007 element = EL_STEEL_CHAR_3;
5010 case 0x1636: // (blue steel)
5011 element = EL_STEEL_CHAR_4;
5014 case 0x1637: // (blue steel)
5015 element = EL_STEEL_CHAR_5;
5018 case 0x1638: // (blue steel)
5019 element = EL_STEEL_CHAR_6;
5022 case 0x1639: // (blue steel)
5023 element = EL_STEEL_CHAR_7;
5026 case 0x163a: // (blue steel)
5027 element = EL_STEEL_CHAR_8;
5030 case 0x163b: // (blue steel)
5031 element = EL_STEEL_CHAR_9;
5034 case 0x163c: // (blue steel)
5035 element = EL_STEEL_CHAR_PERIOD;
5038 case 0x163d: // (blue steel)
5039 element = EL_STEEL_CHAR_EXCLAM;
5042 case 0x163e: // (blue steel)
5043 element = EL_STEEL_CHAR_COLON;
5046 case 0x163f: // (blue steel)
5047 element = EL_STEEL_CHAR_LESS;
5050 case 0x1640: // (blue steel)
5051 element = EL_STEEL_CHAR_GREATER;
5054 case 0x1641: // (blue steel)
5055 element = EL_STEEL_CHAR_QUESTION;
5058 case 0x1642: // (blue steel)
5059 element = EL_STEEL_CHAR_COPYRIGHT;
5062 case 0x1643: // (blue steel)
5063 element = EL_STEEL_CHAR_UP;
5066 case 0x1644: // (blue steel)
5067 element = EL_STEEL_CHAR_DOWN;
5070 case 0x1645: // (blue steel)
5071 element = EL_STEEL_CHAR_BUTTON;
5074 case 0x1646: // (blue steel)
5075 element = EL_STEEL_CHAR_PLUS;
5078 case 0x1647: // (blue steel)
5079 element = EL_STEEL_CHAR_MINUS;
5082 case 0x1648: // (blue steel)
5083 element = EL_STEEL_CHAR_APOSTROPHE;
5086 case 0x1649: // (blue steel)
5087 element = EL_STEEL_CHAR_PARENLEFT;
5090 case 0x164a: // (blue steel)
5091 element = EL_STEEL_CHAR_PARENRIGHT;
5094 case 0x164b: // (green steel)
5095 element = EL_STEEL_CHAR_A;
5098 case 0x164c: // (green steel)
5099 element = EL_STEEL_CHAR_B;
5102 case 0x164d: // (green steel)
5103 element = EL_STEEL_CHAR_C;
5106 case 0x164e: // (green steel)
5107 element = EL_STEEL_CHAR_D;
5110 case 0x164f: // (green steel)
5111 element = EL_STEEL_CHAR_E;
5114 case 0x1650: // (green steel)
5115 element = EL_STEEL_CHAR_F;
5118 case 0x1651: // (green steel)
5119 element = EL_STEEL_CHAR_G;
5122 case 0x1652: // (green steel)
5123 element = EL_STEEL_CHAR_H;
5126 case 0x1653: // (green steel)
5127 element = EL_STEEL_CHAR_I;
5130 case 0x1654: // (green steel)
5131 element = EL_STEEL_CHAR_J;
5134 case 0x1655: // (green steel)
5135 element = EL_STEEL_CHAR_K;
5138 case 0x1656: // (green steel)
5139 element = EL_STEEL_CHAR_L;
5142 case 0x1657: // (green steel)
5143 element = EL_STEEL_CHAR_M;
5146 case 0x1658: // (green steel)
5147 element = EL_STEEL_CHAR_N;
5150 case 0x1659: // (green steel)
5151 element = EL_STEEL_CHAR_O;
5154 case 0x165a: // (green steel)
5155 element = EL_STEEL_CHAR_P;
5158 case 0x165b: // (green steel)
5159 element = EL_STEEL_CHAR_Q;
5162 case 0x165c: // (green steel)
5163 element = EL_STEEL_CHAR_R;
5166 case 0x165d: // (green steel)
5167 element = EL_STEEL_CHAR_S;
5170 case 0x165e: // (green steel)
5171 element = EL_STEEL_CHAR_T;
5174 case 0x165f: // (green steel)
5175 element = EL_STEEL_CHAR_U;
5178 case 0x1660: // (green steel)
5179 element = EL_STEEL_CHAR_V;
5182 case 0x1661: // (green steel)
5183 element = EL_STEEL_CHAR_W;
5186 case 0x1662: // (green steel)
5187 element = EL_STEEL_CHAR_X;
5190 case 0x1663: // (green steel)
5191 element = EL_STEEL_CHAR_Y;
5194 case 0x1664: // (green steel)
5195 element = EL_STEEL_CHAR_Z;
5198 case 0x1665: // (green steel)
5199 element = EL_STEEL_CHAR_AUMLAUT;
5202 case 0x1666: // (green steel)
5203 element = EL_STEEL_CHAR_OUMLAUT;
5206 case 0x1667: // (green steel)
5207 element = EL_STEEL_CHAR_UUMLAUT;
5210 case 0x1668: // (green steel)
5211 element = EL_STEEL_CHAR_0;
5214 case 0x1669: // (green steel)
5215 element = EL_STEEL_CHAR_1;
5218 case 0x166a: // (green steel)
5219 element = EL_STEEL_CHAR_2;
5222 case 0x166b: // (green steel)
5223 element = EL_STEEL_CHAR_3;
5226 case 0x166c: // (green steel)
5227 element = EL_STEEL_CHAR_4;
5230 case 0x166d: // (green steel)
5231 element = EL_STEEL_CHAR_5;
5234 case 0x166e: // (green steel)
5235 element = EL_STEEL_CHAR_6;
5238 case 0x166f: // (green steel)
5239 element = EL_STEEL_CHAR_7;
5242 case 0x1670: // (green steel)
5243 element = EL_STEEL_CHAR_8;
5246 case 0x1671: // (green steel)
5247 element = EL_STEEL_CHAR_9;
5250 case 0x1672: // (green steel)
5251 element = EL_STEEL_CHAR_PERIOD;
5254 case 0x1673: // (green steel)
5255 element = EL_STEEL_CHAR_EXCLAM;
5258 case 0x1674: // (green steel)
5259 element = EL_STEEL_CHAR_COLON;
5262 case 0x1675: // (green steel)
5263 element = EL_STEEL_CHAR_LESS;
5266 case 0x1676: // (green steel)
5267 element = EL_STEEL_CHAR_GREATER;
5270 case 0x1677: // (green steel)
5271 element = EL_STEEL_CHAR_QUESTION;
5274 case 0x1678: // (green steel)
5275 element = EL_STEEL_CHAR_COPYRIGHT;
5278 case 0x1679: // (green steel)
5279 element = EL_STEEL_CHAR_UP;
5282 case 0x167a: // (green steel)
5283 element = EL_STEEL_CHAR_DOWN;
5286 case 0x167b: // (green steel)
5287 element = EL_STEEL_CHAR_BUTTON;
5290 case 0x167c: // (green steel)
5291 element = EL_STEEL_CHAR_PLUS;
5294 case 0x167d: // (green steel)
5295 element = EL_STEEL_CHAR_MINUS;
5298 case 0x167e: // (green steel)
5299 element = EL_STEEL_CHAR_APOSTROPHE;
5302 case 0x167f: // (green steel)
5303 element = EL_STEEL_CHAR_PARENLEFT;
5306 case 0x1680: // (green steel)
5307 element = EL_STEEL_CHAR_PARENRIGHT;
5310 case 0x1681: // gate (red)
5311 element = EL_EM_GATE_1;
5314 case 0x1682: // secret gate (red)
5315 element = EL_EM_GATE_1_GRAY;
5318 case 0x1683: // gate (yellow)
5319 element = EL_EM_GATE_2;
5322 case 0x1684: // secret gate (yellow)
5323 element = EL_EM_GATE_2_GRAY;
5326 case 0x1685: // gate (blue)
5327 element = EL_EM_GATE_4;
5330 case 0x1686: // secret gate (blue)
5331 element = EL_EM_GATE_4_GRAY;
5334 case 0x1687: // gate (green)
5335 element = EL_EM_GATE_3;
5338 case 0x1688: // secret gate (green)
5339 element = EL_EM_GATE_3_GRAY;
5342 case 0x1689: // gate (white)
5343 element = EL_DC_GATE_WHITE;
5346 case 0x168a: // secret gate (white)
5347 element = EL_DC_GATE_WHITE_GRAY;
5350 case 0x168b: // secret gate (no key)
5351 element = EL_DC_GATE_FAKE_GRAY;
5355 element = EL_ROBOT_WHEEL;
5359 element = EL_DC_TIMEGATE_SWITCH;
5363 element = EL_ACID_POOL_BOTTOM;
5367 element = EL_ACID_POOL_TOPLEFT;
5371 element = EL_ACID_POOL_TOPRIGHT;
5375 element = EL_ACID_POOL_BOTTOMLEFT;
5379 element = EL_ACID_POOL_BOTTOMRIGHT;
5383 element = EL_STEELWALL;
5387 element = EL_STEELWALL_SLIPPERY;
5390 case 0x1695: // steel wall (not round)
5391 element = EL_STEELWALL;
5394 case 0x1696: // steel wall (left)
5395 element = EL_DC_STEELWALL_1_LEFT;
5398 case 0x1697: // steel wall (bottom)
5399 element = EL_DC_STEELWALL_1_BOTTOM;
5402 case 0x1698: // steel wall (right)
5403 element = EL_DC_STEELWALL_1_RIGHT;
5406 case 0x1699: // steel wall (top)
5407 element = EL_DC_STEELWALL_1_TOP;
5410 case 0x169a: // steel wall (left/bottom)
5411 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5414 case 0x169b: // steel wall (right/bottom)
5415 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5418 case 0x169c: // steel wall (right/top)
5419 element = EL_DC_STEELWALL_1_TOPRIGHT;
5422 case 0x169d: // steel wall (left/top)
5423 element = EL_DC_STEELWALL_1_TOPLEFT;
5426 case 0x169e: // steel wall (right/bottom small)
5427 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5430 case 0x169f: // steel wall (left/bottom small)
5431 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5434 case 0x16a0: // steel wall (right/top small)
5435 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5438 case 0x16a1: // steel wall (left/top small)
5439 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5442 case 0x16a2: // steel wall (left/right)
5443 element = EL_DC_STEELWALL_1_VERTICAL;
5446 case 0x16a3: // steel wall (top/bottom)
5447 element = EL_DC_STEELWALL_1_HORIZONTAL;
5450 case 0x16a4: // steel wall 2 (left end)
5451 element = EL_DC_STEELWALL_2_LEFT;
5454 case 0x16a5: // steel wall 2 (right end)
5455 element = EL_DC_STEELWALL_2_RIGHT;
5458 case 0x16a6: // steel wall 2 (top end)
5459 element = EL_DC_STEELWALL_2_TOP;
5462 case 0x16a7: // steel wall 2 (bottom end)
5463 element = EL_DC_STEELWALL_2_BOTTOM;
5466 case 0x16a8: // steel wall 2 (left/right)
5467 element = EL_DC_STEELWALL_2_HORIZONTAL;
5470 case 0x16a9: // steel wall 2 (up/down)
5471 element = EL_DC_STEELWALL_2_VERTICAL;
5474 case 0x16aa: // steel wall 2 (mid)
5475 element = EL_DC_STEELWALL_2_MIDDLE;
5479 element = EL_SIGN_EXCLAMATION;
5483 element = EL_SIGN_RADIOACTIVITY;
5487 element = EL_SIGN_STOP;
5491 element = EL_SIGN_WHEELCHAIR;
5495 element = EL_SIGN_PARKING;
5499 element = EL_SIGN_NO_ENTRY;
5503 element = EL_SIGN_HEART;
5507 element = EL_SIGN_GIVE_WAY;
5511 element = EL_SIGN_ENTRY_FORBIDDEN;
5515 element = EL_SIGN_EMERGENCY_EXIT;
5519 element = EL_SIGN_YIN_YANG;
5523 element = EL_WALL_EMERALD;
5527 element = EL_WALL_DIAMOND;
5531 element = EL_WALL_PEARL;
5535 element = EL_WALL_CRYSTAL;
5539 element = EL_INVISIBLE_WALL;
5543 element = EL_INVISIBLE_STEELWALL;
5547 // EL_INVISIBLE_SAND
5550 element = EL_LIGHT_SWITCH;
5554 element = EL_ENVELOPE_1;
5558 if (element >= 0x0117 && element <= 0x036e) // (?)
5559 element = EL_DIAMOND;
5560 else if (element >= 0x042d && element <= 0x0684) // (?)
5561 element = EL_EMERALD;
5562 else if (element >= 0x157c && element <= 0x158b)
5564 else if (element >= 0x1590 && element <= 0x159f)
5565 element = EL_DC_LANDMINE;
5566 else if (element >= 0x16bc && element <= 0x16cb)
5567 element = EL_INVISIBLE_SAND;
5570 Warn("unknown Diamond Caves element 0x%04x", element);
5572 element = EL_UNKNOWN;
5577 return getMappedElement(element);
5580 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5583 byte header[DC_LEVEL_HEADER_SIZE];
5585 int envelope_header_pos = 62;
5586 int envelope_content_pos = 94;
5587 int level_name_pos = 251;
5588 int level_author_pos = 292;
5589 int envelope_header_len;
5590 int envelope_content_len;
5592 int level_author_len;
5594 int num_yamyam_contents;
5597 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5599 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5601 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5603 header[i * 2 + 0] = header_word >> 8;
5604 header[i * 2 + 1] = header_word & 0xff;
5607 // read some values from level header to check level decoding integrity
5608 fieldx = header[6] | (header[7] << 8);
5609 fieldy = header[8] | (header[9] << 8);
5610 num_yamyam_contents = header[60] | (header[61] << 8);
5612 // do some simple sanity checks to ensure that level was correctly decoded
5613 if (fieldx < 1 || fieldx > 256 ||
5614 fieldy < 1 || fieldy > 256 ||
5615 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5617 level->no_valid_file = TRUE;
5619 Warn("cannot decode level from stream -- using empty level");
5624 // maximum envelope header size is 31 bytes
5625 envelope_header_len = header[envelope_header_pos];
5626 // maximum envelope content size is 110 (156?) bytes
5627 envelope_content_len = header[envelope_content_pos];
5629 // maximum level title size is 40 bytes
5630 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5631 // maximum level author size is 30 (51?) bytes
5632 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5636 for (i = 0; i < envelope_header_len; i++)
5637 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5638 level->envelope[0].text[envelope_size++] =
5639 header[envelope_header_pos + 1 + i];
5641 if (envelope_header_len > 0 && envelope_content_len > 0)
5643 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5644 level->envelope[0].text[envelope_size++] = '\n';
5645 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5646 level->envelope[0].text[envelope_size++] = '\n';
5649 for (i = 0; i < envelope_content_len; i++)
5650 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5651 level->envelope[0].text[envelope_size++] =
5652 header[envelope_content_pos + 1 + i];
5654 level->envelope[0].text[envelope_size] = '\0';
5656 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5657 level->envelope[0].ysize = 10;
5658 level->envelope[0].autowrap = TRUE;
5659 level->envelope[0].centered = TRUE;
5661 for (i = 0; i < level_name_len; i++)
5662 level->name[i] = header[level_name_pos + 1 + i];
5663 level->name[level_name_len] = '\0';
5665 for (i = 0; i < level_author_len; i++)
5666 level->author[i] = header[level_author_pos + 1 + i];
5667 level->author[level_author_len] = '\0';
5669 num_yamyam_contents = header[60] | (header[61] << 8);
5670 level->num_yamyam_contents =
5671 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5673 for (i = 0; i < num_yamyam_contents; i++)
5675 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5677 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5678 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5680 if (i < MAX_ELEMENT_CONTENTS)
5681 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5685 fieldx = header[6] | (header[7] << 8);
5686 fieldy = header[8] | (header[9] << 8);
5687 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5688 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5690 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5692 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5693 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5695 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5696 level->field[x][y] = getMappedElement_DC(element_dc);
5699 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5700 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5701 level->field[x][y] = EL_PLAYER_1;
5703 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5704 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5705 level->field[x][y] = EL_PLAYER_2;
5707 level->gems_needed = header[18] | (header[19] << 8);
5709 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5710 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5711 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5712 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5713 level->score[SC_NUT] = header[28] | (header[29] << 8);
5714 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5715 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5716 level->score[SC_BUG] = header[34] | (header[35] << 8);
5717 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5718 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5719 level->score[SC_KEY] = header[40] | (header[41] << 8);
5720 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5722 level->time = header[44] | (header[45] << 8);
5724 level->amoeba_speed = header[46] | (header[47] << 8);
5725 level->time_light = header[48] | (header[49] << 8);
5726 level->time_timegate = header[50] | (header[51] << 8);
5727 level->time_wheel = header[52] | (header[53] << 8);
5728 level->time_magic_wall = header[54] | (header[55] << 8);
5729 level->extra_time = header[56] | (header[57] << 8);
5730 level->shield_normal_time = header[58] | (header[59] << 8);
5732 // shield and extra time elements do not have a score
5733 level->score[SC_SHIELD] = 0;
5734 level->extra_time_score = 0;
5736 // set time for normal and deadly shields to the same value
5737 level->shield_deadly_time = level->shield_normal_time;
5739 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5740 // can slip down from flat walls, like normal walls and steel walls
5741 level->em_slippery_gems = TRUE;
5744 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5745 struct LevelFileInfo *level_file_info,
5746 boolean level_info_only)
5748 char *filename = level_file_info->filename;
5750 int num_magic_bytes = 8;
5751 char magic_bytes[num_magic_bytes + 1];
5752 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5754 if (!(file = openFile(filename, MODE_READ)))
5756 level->no_valid_file = TRUE;
5758 if (!level_info_only)
5759 Warn("cannot read level '%s' -- using empty level", filename);
5764 // fseek(file, 0x0000, SEEK_SET);
5766 if (level_file_info->packed)
5768 // read "magic bytes" from start of file
5769 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5770 magic_bytes[0] = '\0';
5772 // check "magic bytes" for correct file format
5773 if (!strPrefix(magic_bytes, "DC2"))
5775 level->no_valid_file = TRUE;
5777 Warn("unknown DC level file '%s' -- using empty level", filename);
5782 if (strPrefix(magic_bytes, "DC2Win95") ||
5783 strPrefix(magic_bytes, "DC2Win98"))
5785 int position_first_level = 0x00fa;
5786 int extra_bytes = 4;
5789 // advance file stream to first level inside the level package
5790 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5792 // each block of level data is followed by block of non-level data
5793 num_levels_to_skip *= 2;
5795 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5796 while (num_levels_to_skip >= 0)
5798 // advance file stream to next level inside the level package
5799 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5801 level->no_valid_file = TRUE;
5803 Warn("cannot fseek in file '%s' -- using empty level", filename);
5808 // skip apparently unused extra bytes following each level
5809 ReadUnusedBytesFromFile(file, extra_bytes);
5811 // read size of next level in level package
5812 skip_bytes = getFile32BitLE(file);
5814 num_levels_to_skip--;
5819 level->no_valid_file = TRUE;
5821 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5827 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5833 // ----------------------------------------------------------------------------
5834 // functions for loading SB level
5835 // ----------------------------------------------------------------------------
5837 int getMappedElement_SB(int element_ascii, boolean use_ces)
5845 sb_element_mapping[] =
5847 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5848 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5849 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5850 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5851 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5852 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5853 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5854 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5861 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5862 if (element_ascii == sb_element_mapping[i].ascii)
5863 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5865 return EL_UNDEFINED;
5868 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5869 struct LevelFileInfo *level_file_info,
5870 boolean level_info_only)
5872 char *filename = level_file_info->filename;
5873 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5874 char last_comment[MAX_LINE_LEN];
5875 char level_name[MAX_LINE_LEN];
5878 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5879 boolean read_continued_line = FALSE;
5880 boolean reading_playfield = FALSE;
5881 boolean got_valid_playfield_line = FALSE;
5882 boolean invalid_playfield_char = FALSE;
5883 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5884 int file_level_nr = 0;
5886 int x = 0, y = 0; // initialized to make compilers happy
5888 last_comment[0] = '\0';
5889 level_name[0] = '\0';
5891 if (!(file = openFile(filename, MODE_READ)))
5893 level->no_valid_file = TRUE;
5895 if (!level_info_only)
5896 Warn("cannot read level '%s' -- using empty level", filename);
5901 while (!checkEndOfFile(file))
5903 // level successfully read, but next level may follow here
5904 if (!got_valid_playfield_line && reading_playfield)
5906 // read playfield from single level file -- skip remaining file
5907 if (!level_file_info->packed)
5910 if (file_level_nr >= num_levels_to_skip)
5915 last_comment[0] = '\0';
5916 level_name[0] = '\0';
5918 reading_playfield = FALSE;
5921 got_valid_playfield_line = FALSE;
5923 // read next line of input file
5924 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5927 // check if line was completely read and is terminated by line break
5928 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5931 // cut trailing line break (this can be newline and/or carriage return)
5932 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5933 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5936 // copy raw input line for later use (mainly debugging output)
5937 strcpy(line_raw, line);
5939 if (read_continued_line)
5941 // append new line to existing line, if there is enough space
5942 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5943 strcat(previous_line, line_ptr);
5945 strcpy(line, previous_line); // copy storage buffer to line
5947 read_continued_line = FALSE;
5950 // if the last character is '\', continue at next line
5951 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5953 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5954 strcpy(previous_line, line); // copy line to storage buffer
5956 read_continued_line = TRUE;
5962 if (line[0] == '\0')
5965 // extract comment text from comment line
5968 for (line_ptr = line; *line_ptr; line_ptr++)
5969 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5972 strcpy(last_comment, line_ptr);
5977 // extract level title text from line containing level title
5978 if (line[0] == '\'')
5980 strcpy(level_name, &line[1]);
5982 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5983 level_name[strlen(level_name) - 1] = '\0';
5988 // skip lines containing only spaces (or empty lines)
5989 for (line_ptr = line; *line_ptr; line_ptr++)
5990 if (*line_ptr != ' ')
5992 if (*line_ptr == '\0')
5995 // at this point, we have found a line containing part of a playfield
5997 got_valid_playfield_line = TRUE;
5999 if (!reading_playfield)
6001 reading_playfield = TRUE;
6002 invalid_playfield_char = FALSE;
6004 for (x = 0; x < MAX_LEV_FIELDX; x++)
6005 for (y = 0; y < MAX_LEV_FIELDY; y++)
6006 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6011 // start with topmost tile row
6015 // skip playfield line if larger row than allowed
6016 if (y >= MAX_LEV_FIELDY)
6019 // start with leftmost tile column
6022 // read playfield elements from line
6023 for (line_ptr = line; *line_ptr; line_ptr++)
6025 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6027 // stop parsing playfield line if larger column than allowed
6028 if (x >= MAX_LEV_FIELDX)
6031 if (mapped_sb_element == EL_UNDEFINED)
6033 invalid_playfield_char = TRUE;
6038 level->field[x][y] = mapped_sb_element;
6040 // continue with next tile column
6043 level->fieldx = MAX(x, level->fieldx);
6046 if (invalid_playfield_char)
6048 // if first playfield line, treat invalid lines as comment lines
6050 reading_playfield = FALSE;
6055 // continue with next tile row
6063 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6064 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6066 if (!reading_playfield)
6068 level->no_valid_file = TRUE;
6070 Warn("cannot read level '%s' -- using empty level", filename);
6075 if (*level_name != '\0')
6077 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6078 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6080 else if (*last_comment != '\0')
6082 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6083 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6087 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6090 // set all empty fields beyond the border walls to invisible steel wall
6091 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6093 if ((x == 0 || x == level->fieldx - 1 ||
6094 y == 0 || y == level->fieldy - 1) &&
6095 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6096 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6097 level->field, level->fieldx, level->fieldy);
6100 // set special level settings for Sokoban levels
6103 level->use_step_counter = TRUE;
6105 if (load_xsb_to_ces)
6107 // special global settings can now be set in level template
6109 level->use_custom_template = TRUE;
6114 // -------------------------------------------------------------------------
6115 // functions for handling native levels
6116 // -------------------------------------------------------------------------
6118 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6119 struct LevelFileInfo *level_file_info,
6120 boolean level_info_only)
6122 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6123 level->no_valid_file = TRUE;
6126 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6127 struct LevelFileInfo *level_file_info,
6128 boolean level_info_only)
6132 // determine position of requested level inside level package
6133 if (level_file_info->packed)
6134 pos = level_file_info->nr - leveldir_current->first_level;
6136 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6137 level->no_valid_file = TRUE;
6140 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6141 struct LevelFileInfo *level_file_info,
6142 boolean level_info_only)
6144 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6145 level->no_valid_file = TRUE;
6148 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6150 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6151 CopyNativeLevel_RND_to_EM(level);
6152 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6153 CopyNativeLevel_RND_to_SP(level);
6154 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6155 CopyNativeLevel_RND_to_MM(level);
6158 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6160 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6161 CopyNativeLevel_EM_to_RND(level);
6162 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6163 CopyNativeLevel_SP_to_RND(level);
6164 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6165 CopyNativeLevel_MM_to_RND(level);
6168 void SaveNativeLevel(struct LevelInfo *level)
6170 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6172 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6173 char *filename = getLevelFilenameFromBasename(basename);
6175 CopyNativeLevel_RND_to_SP(level);
6176 CopyNativeTape_RND_to_SP(level);
6178 SaveNativeLevel_SP(filename);
6183 // ----------------------------------------------------------------------------
6184 // functions for loading generic level
6185 // ----------------------------------------------------------------------------
6187 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6188 struct LevelFileInfo *level_file_info,
6189 boolean level_info_only)
6191 // always start with reliable default values
6192 setLevelInfoToDefaults(level, level_info_only, TRUE);
6194 switch (level_file_info->type)
6196 case LEVEL_FILE_TYPE_RND:
6197 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6200 case LEVEL_FILE_TYPE_EM:
6201 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6202 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6205 case LEVEL_FILE_TYPE_SP:
6206 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6207 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6210 case LEVEL_FILE_TYPE_MM:
6211 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6212 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6215 case LEVEL_FILE_TYPE_DC:
6216 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6219 case LEVEL_FILE_TYPE_SB:
6220 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6224 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6228 // if level file is invalid, restore level structure to default values
6229 if (level->no_valid_file)
6230 setLevelInfoToDefaults(level, level_info_only, FALSE);
6232 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6233 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6235 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6236 CopyNativeLevel_Native_to_RND(level);
6239 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6241 static struct LevelFileInfo level_file_info;
6243 // always start with reliable default values
6244 setFileInfoToDefaults(&level_file_info);
6246 level_file_info.nr = 0; // unknown level number
6247 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6249 setString(&level_file_info.filename, filename);
6251 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6254 static void LoadLevel_InitVersion(struct LevelInfo *level)
6258 if (leveldir_current == NULL) // only when dumping level
6261 // all engine modifications also valid for levels which use latest engine
6262 if (level->game_version < VERSION_IDENT(3,2,0,5))
6264 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6265 level->score[SC_TIME_BONUS] /= 10;
6268 if (leveldir_current->latest_engine)
6270 // ---------- use latest game engine --------------------------------------
6272 /* For all levels which are forced to use the latest game engine version
6273 (normally all but user contributed, private and undefined levels), set
6274 the game engine version to the actual version; this allows for actual
6275 corrections in the game engine to take effect for existing, converted
6276 levels (from "classic" or other existing games) to make the emulation
6277 of the corresponding game more accurate, while (hopefully) not breaking
6278 existing levels created from other players. */
6280 level->game_version = GAME_VERSION_ACTUAL;
6282 /* Set special EM style gems behaviour: EM style gems slip down from
6283 normal, steel and growing wall. As this is a more fundamental change,
6284 it seems better to set the default behaviour to "off" (as it is more
6285 natural) and make it configurable in the level editor (as a property
6286 of gem style elements). Already existing converted levels (neither
6287 private nor contributed levels) are changed to the new behaviour. */
6289 if (level->file_version < FILE_VERSION_2_0)
6290 level->em_slippery_gems = TRUE;
6295 // ---------- use game engine the level was created with --------------------
6297 /* For all levels which are not forced to use the latest game engine
6298 version (normally user contributed, private and undefined levels),
6299 use the version of the game engine the levels were created for.
6301 Since 2.0.1, the game engine version is now directly stored
6302 in the level file (chunk "VERS"), so there is no need anymore
6303 to set the game version from the file version (except for old,
6304 pre-2.0 levels, where the game version is still taken from the
6305 file format version used to store the level -- see above). */
6307 // player was faster than enemies in 1.0.0 and before
6308 if (level->file_version == FILE_VERSION_1_0)
6309 for (i = 0; i < MAX_PLAYERS; i++)
6310 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6312 // default behaviour for EM style gems was "slippery" only in 2.0.1
6313 if (level->game_version == VERSION_IDENT(2,0,1,0))
6314 level->em_slippery_gems = TRUE;
6316 // springs could be pushed over pits before (pre-release version) 2.2.0
6317 if (level->game_version < VERSION_IDENT(2,2,0,0))
6318 level->use_spring_bug = TRUE;
6320 if (level->game_version < VERSION_IDENT(3,2,0,5))
6322 // time orb caused limited time in endless time levels before 3.2.0-5
6323 level->use_time_orb_bug = TRUE;
6325 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6326 level->block_snap_field = FALSE;
6328 // extra time score was same value as time left score before 3.2.0-5
6329 level->extra_time_score = level->score[SC_TIME_BONUS];
6332 if (level->game_version < VERSION_IDENT(3,2,0,7))
6334 // default behaviour for snapping was "not continuous" before 3.2.0-7
6335 level->continuous_snapping = FALSE;
6338 // only few elements were able to actively move into acid before 3.1.0
6339 // trigger settings did not exist before 3.1.0; set to default "any"
6340 if (level->game_version < VERSION_IDENT(3,1,0,0))
6342 // correct "can move into acid" settings (all zero in old levels)
6344 level->can_move_into_acid_bits = 0; // nothing can move into acid
6345 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6347 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6348 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6349 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6350 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6352 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6353 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6355 // correct trigger settings (stored as zero == "none" in old levels)
6357 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6359 int element = EL_CUSTOM_START + i;
6360 struct ElementInfo *ei = &element_info[element];
6362 for (j = 0; j < ei->num_change_pages; j++)
6364 struct ElementChangeInfo *change = &ei->change_page[j];
6366 change->trigger_player = CH_PLAYER_ANY;
6367 change->trigger_page = CH_PAGE_ANY;
6372 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6374 int element = EL_CUSTOM_256;
6375 struct ElementInfo *ei = &element_info[element];
6376 struct ElementChangeInfo *change = &ei->change_page[0];
6378 /* This is needed to fix a problem that was caused by a bugfix in function
6379 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6380 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6381 not replace walkable elements, but instead just placed the player on it,
6382 without placing the Sokoban field under the player). Unfortunately, this
6383 breaks "Snake Bite" style levels when the snake is halfway through a door
6384 that just closes (the snake head is still alive and can be moved in this
6385 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6386 player (without Sokoban element) which then gets killed as designed). */
6388 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6389 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6390 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6391 change->target_element = EL_PLAYER_1;
6394 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6395 if (level->game_version < VERSION_IDENT(3,2,5,0))
6397 /* This is needed to fix a problem that was caused by a bugfix in function
6398 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6399 corrects the behaviour when a custom element changes to another custom
6400 element with a higher element number that has change actions defined.
6401 Normally, only one change per frame is allowed for custom elements.
6402 Therefore, it is checked if a custom element already changed in the
6403 current frame; if it did, subsequent changes are suppressed.
6404 Unfortunately, this is only checked for element changes, but not for
6405 change actions, which are still executed. As the function above loops
6406 through all custom elements from lower to higher, an element change
6407 resulting in a lower CE number won't be checked again, while a target
6408 element with a higher number will also be checked, and potential change
6409 actions will get executed for this CE, too (which is wrong), while
6410 further changes are ignored (which is correct). As this bugfix breaks
6411 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6412 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6413 behaviour for existing levels and tapes that make use of this bug */
6415 level->use_action_after_change_bug = TRUE;
6418 // not centering level after relocating player was default only in 3.2.3
6419 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6420 level->shifted_relocation = TRUE;
6422 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6423 if (level->game_version < VERSION_IDENT(3,2,6,0))
6424 level->em_explodes_by_fire = TRUE;
6426 // levels were solved by the first player entering an exit up to 4.1.0.0
6427 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6428 level->solved_by_one_player = TRUE;
6430 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6431 if (level->game_version < VERSION_IDENT(4,1,1,1))
6432 level->use_life_bugs = TRUE;
6434 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6435 if (level->game_version < VERSION_IDENT(4,1,1,1))
6436 level->sb_objects_needed = FALSE;
6438 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6439 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6440 level->finish_dig_collect = FALSE;
6443 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6447 // map elements that have changed in newer versions
6448 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6449 level->game_version);
6450 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6451 for (x = 0; x < 3; x++)
6452 for (y = 0; y < 3; y++)
6453 level->yamyam_content[i].e[x][y] =
6454 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6455 level->game_version);
6459 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6463 // map custom element change events that have changed in newer versions
6464 // (these following values were accidentally changed in version 3.0.1)
6465 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6466 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6468 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6470 int element = EL_CUSTOM_START + i;
6472 // order of checking and copying events to be mapped is important
6473 // (do not change the start and end value -- they are constant)
6474 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6476 if (HAS_CHANGE_EVENT(element, j - 2))
6478 SET_CHANGE_EVENT(element, j - 2, FALSE);
6479 SET_CHANGE_EVENT(element, j, TRUE);
6483 // order of checking and copying events to be mapped is important
6484 // (do not change the start and end value -- they are constant)
6485 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6487 if (HAS_CHANGE_EVENT(element, j - 1))
6489 SET_CHANGE_EVENT(element, j - 1, FALSE);
6490 SET_CHANGE_EVENT(element, j, TRUE);
6496 // initialize "can_change" field for old levels with only one change page
6497 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6499 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6501 int element = EL_CUSTOM_START + i;
6503 if (CAN_CHANGE(element))
6504 element_info[element].change->can_change = TRUE;
6508 // correct custom element values (for old levels without these options)
6509 if (level->game_version < VERSION_IDENT(3,1,1,0))
6511 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6513 int element = EL_CUSTOM_START + i;
6514 struct ElementInfo *ei = &element_info[element];
6516 if (ei->access_direction == MV_NO_DIRECTION)
6517 ei->access_direction = MV_ALL_DIRECTIONS;
6521 // correct custom element values (fix invalid values for all versions)
6524 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6526 int element = EL_CUSTOM_START + i;
6527 struct ElementInfo *ei = &element_info[element];
6529 for (j = 0; j < ei->num_change_pages; j++)
6531 struct ElementChangeInfo *change = &ei->change_page[j];
6533 if (change->trigger_player == CH_PLAYER_NONE)
6534 change->trigger_player = CH_PLAYER_ANY;
6536 if (change->trigger_side == CH_SIDE_NONE)
6537 change->trigger_side = CH_SIDE_ANY;
6542 // initialize "can_explode" field for old levels which did not store this
6543 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6544 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6546 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6548 int element = EL_CUSTOM_START + i;
6550 if (EXPLODES_1X1_OLD(element))
6551 element_info[element].explosion_type = EXPLODES_1X1;
6553 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6554 EXPLODES_SMASHED(element) ||
6555 EXPLODES_IMPACT(element)));
6559 // correct previously hard-coded move delay values for maze runner style
6560 if (level->game_version < VERSION_IDENT(3,1,1,0))
6562 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6564 int element = EL_CUSTOM_START + i;
6566 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6568 // previously hard-coded and therefore ignored
6569 element_info[element].move_delay_fixed = 9;
6570 element_info[element].move_delay_random = 0;
6575 // set some other uninitialized values of custom elements in older levels
6576 if (level->game_version < VERSION_IDENT(3,1,0,0))
6578 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6580 int element = EL_CUSTOM_START + i;
6582 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6584 element_info[element].explosion_delay = 17;
6585 element_info[element].ignition_delay = 8;
6590 static void LoadLevel_InitElements(struct LevelInfo *level)
6592 LoadLevel_InitStandardElements(level);
6594 if (level->file_has_custom_elements)
6595 LoadLevel_InitCustomElements(level);
6597 // initialize element properties for level editor etc.
6598 InitElementPropertiesEngine(level->game_version);
6599 InitElementPropertiesGfxElement();
6602 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6606 // map elements that have changed in newer versions
6607 for (y = 0; y < level->fieldy; y++)
6608 for (x = 0; x < level->fieldx; x++)
6609 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6610 level->game_version);
6612 // clear unused playfield data (nicer if level gets resized in editor)
6613 for (x = 0; x < MAX_LEV_FIELDX; x++)
6614 for (y = 0; y < MAX_LEV_FIELDY; y++)
6615 if (x >= level->fieldx || y >= level->fieldy)
6616 level->field[x][y] = EL_EMPTY;
6618 // copy elements to runtime playfield array
6619 for (x = 0; x < MAX_LEV_FIELDX; x++)
6620 for (y = 0; y < MAX_LEV_FIELDY; y++)
6621 Tile[x][y] = level->field[x][y];
6623 // initialize level size variables for faster access
6624 lev_fieldx = level->fieldx;
6625 lev_fieldy = level->fieldy;
6627 // determine border element for this level
6628 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6629 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6634 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6636 struct LevelFileInfo *level_file_info = &level->file_info;
6638 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6639 CopyNativeLevel_RND_to_Native(level);
6642 static void LoadLevelTemplate_LoadAndInit(void)
6644 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6646 LoadLevel_InitVersion(&level_template);
6647 LoadLevel_InitElements(&level_template);
6649 ActivateLevelTemplate();
6652 void LoadLevelTemplate(int nr)
6654 if (!fileExists(getGlobalLevelTemplateFilename()))
6656 Warn("no level template found for this level");
6661 setLevelFileInfo(&level_template.file_info, nr);
6663 LoadLevelTemplate_LoadAndInit();
6666 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6668 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6670 LoadLevelTemplate_LoadAndInit();
6673 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6675 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6677 if (level.use_custom_template)
6679 if (network_level != NULL)
6680 LoadNetworkLevelTemplate(network_level);
6682 LoadLevelTemplate(-1);
6685 LoadLevel_InitVersion(&level);
6686 LoadLevel_InitElements(&level);
6687 LoadLevel_InitPlayfield(&level);
6689 LoadLevel_InitNativeEngines(&level);
6692 void LoadLevel(int nr)
6694 SetLevelSetInfo(leveldir_current->identifier, nr);
6696 setLevelFileInfo(&level.file_info, nr);
6698 LoadLevel_LoadAndInit(NULL);
6701 void LoadLevelInfoOnly(int nr)
6703 setLevelFileInfo(&level.file_info, nr);
6705 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6708 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6710 SetLevelSetInfo(network_level->leveldir_identifier,
6711 network_level->file_info.nr);
6713 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6715 LoadLevel_LoadAndInit(network_level);
6718 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6722 chunk_size += putFileVersion(file, level->file_version);
6723 chunk_size += putFileVersion(file, level->game_version);
6728 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6732 chunk_size += putFile16BitBE(file, level->creation_date.year);
6733 chunk_size += putFile8Bit(file, level->creation_date.month);
6734 chunk_size += putFile8Bit(file, level->creation_date.day);
6739 #if ENABLE_HISTORIC_CHUNKS
6740 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6744 putFile8Bit(file, level->fieldx);
6745 putFile8Bit(file, level->fieldy);
6747 putFile16BitBE(file, level->time);
6748 putFile16BitBE(file, level->gems_needed);
6750 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6751 putFile8Bit(file, level->name[i]);
6753 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6754 putFile8Bit(file, level->score[i]);
6756 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6757 for (y = 0; y < 3; y++)
6758 for (x = 0; x < 3; x++)
6759 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6760 level->yamyam_content[i].e[x][y]));
6761 putFile8Bit(file, level->amoeba_speed);
6762 putFile8Bit(file, level->time_magic_wall);
6763 putFile8Bit(file, level->time_wheel);
6764 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6765 level->amoeba_content));
6766 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6767 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6768 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6769 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6771 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6773 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6774 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6775 putFile32BitBE(file, level->can_move_into_acid_bits);
6776 putFile8Bit(file, level->dont_collide_with_bits);
6778 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6779 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6781 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6782 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6783 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6785 putFile8Bit(file, level->game_engine_type);
6787 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6791 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6796 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6797 chunk_size += putFile8Bit(file, level->name[i]);
6802 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6807 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6808 chunk_size += putFile8Bit(file, level->author[i]);
6813 #if ENABLE_HISTORIC_CHUNKS
6814 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6819 for (y = 0; y < level->fieldy; y++)
6820 for (x = 0; x < level->fieldx; x++)
6821 if (level->encoding_16bit_field)
6822 chunk_size += putFile16BitBE(file, level->field[x][y]);
6824 chunk_size += putFile8Bit(file, level->field[x][y]);
6830 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6835 for (y = 0; y < level->fieldy; y++)
6836 for (x = 0; x < level->fieldx; x++)
6837 chunk_size += putFile16BitBE(file, level->field[x][y]);
6842 #if ENABLE_HISTORIC_CHUNKS
6843 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6847 putFile8Bit(file, EL_YAMYAM);
6848 putFile8Bit(file, level->num_yamyam_contents);
6849 putFile8Bit(file, 0);
6850 putFile8Bit(file, 0);
6852 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6853 for (y = 0; y < 3; y++)
6854 for (x = 0; x < 3; x++)
6855 if (level->encoding_16bit_field)
6856 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6858 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6862 #if ENABLE_HISTORIC_CHUNKS
6863 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6866 int num_contents, content_xsize, content_ysize;
6867 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6869 if (element == EL_YAMYAM)
6871 num_contents = level->num_yamyam_contents;
6875 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6876 for (y = 0; y < 3; y++)
6877 for (x = 0; x < 3; x++)
6878 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6880 else if (element == EL_BD_AMOEBA)
6886 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6887 for (y = 0; y < 3; y++)
6888 for (x = 0; x < 3; x++)
6889 content_array[i][x][y] = EL_EMPTY;
6890 content_array[0][0][0] = level->amoeba_content;
6894 // chunk header already written -- write empty chunk data
6895 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6897 Warn("cannot save content for element '%d'", element);
6902 putFile16BitBE(file, element);
6903 putFile8Bit(file, num_contents);
6904 putFile8Bit(file, content_xsize);
6905 putFile8Bit(file, content_ysize);
6907 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6909 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6910 for (y = 0; y < 3; y++)
6911 for (x = 0; x < 3; x++)
6912 putFile16BitBE(file, content_array[i][x][y]);
6916 #if ENABLE_HISTORIC_CHUNKS
6917 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6919 int envelope_nr = element - EL_ENVELOPE_1;
6920 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6924 chunk_size += putFile16BitBE(file, element);
6925 chunk_size += putFile16BitBE(file, envelope_len);
6926 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6927 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6929 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6930 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6932 for (i = 0; i < envelope_len; i++)
6933 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6939 #if ENABLE_HISTORIC_CHUNKS
6940 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6941 int num_changed_custom_elements)
6945 putFile16BitBE(file, num_changed_custom_elements);
6947 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6949 int element = EL_CUSTOM_START + i;
6951 struct ElementInfo *ei = &element_info[element];
6953 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6955 if (check < num_changed_custom_elements)
6957 putFile16BitBE(file, element);
6958 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6965 if (check != num_changed_custom_elements) // should not happen
6966 Warn("inconsistent number of custom element properties");
6970 #if ENABLE_HISTORIC_CHUNKS
6971 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6972 int num_changed_custom_elements)
6976 putFile16BitBE(file, num_changed_custom_elements);
6978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6980 int element = EL_CUSTOM_START + i;
6982 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6984 if (check < num_changed_custom_elements)
6986 putFile16BitBE(file, element);
6987 putFile16BitBE(file, element_info[element].change->target_element);
6994 if (check != num_changed_custom_elements) // should not happen
6995 Warn("inconsistent number of custom target elements");
6999 #if ENABLE_HISTORIC_CHUNKS
7000 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7001 int num_changed_custom_elements)
7003 int i, j, x, y, check = 0;
7005 putFile16BitBE(file, num_changed_custom_elements);
7007 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7009 int element = EL_CUSTOM_START + i;
7010 struct ElementInfo *ei = &element_info[element];
7012 if (ei->modified_settings)
7014 if (check < num_changed_custom_elements)
7016 putFile16BitBE(file, element);
7018 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7019 putFile8Bit(file, ei->description[j]);
7021 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7023 // some free bytes for future properties and padding
7024 WriteUnusedBytesToFile(file, 7);
7026 putFile8Bit(file, ei->use_gfx_element);
7027 putFile16BitBE(file, ei->gfx_element_initial);
7029 putFile8Bit(file, ei->collect_score_initial);
7030 putFile8Bit(file, ei->collect_count_initial);
7032 putFile16BitBE(file, ei->push_delay_fixed);
7033 putFile16BitBE(file, ei->push_delay_random);
7034 putFile16BitBE(file, ei->move_delay_fixed);
7035 putFile16BitBE(file, ei->move_delay_random);
7037 putFile16BitBE(file, ei->move_pattern);
7038 putFile8Bit(file, ei->move_direction_initial);
7039 putFile8Bit(file, ei->move_stepsize);
7041 for (y = 0; y < 3; y++)
7042 for (x = 0; x < 3; x++)
7043 putFile16BitBE(file, ei->content.e[x][y]);
7045 putFile32BitBE(file, ei->change->events);
7047 putFile16BitBE(file, ei->change->target_element);
7049 putFile16BitBE(file, ei->change->delay_fixed);
7050 putFile16BitBE(file, ei->change->delay_random);
7051 putFile16BitBE(file, ei->change->delay_frames);
7053 putFile16BitBE(file, ei->change->initial_trigger_element);
7055 putFile8Bit(file, ei->change->explode);
7056 putFile8Bit(file, ei->change->use_target_content);
7057 putFile8Bit(file, ei->change->only_if_complete);
7058 putFile8Bit(file, ei->change->use_random_replace);
7060 putFile8Bit(file, ei->change->random_percentage);
7061 putFile8Bit(file, ei->change->replace_when);
7063 for (y = 0; y < 3; y++)
7064 for (x = 0; x < 3; x++)
7065 putFile16BitBE(file, ei->change->content.e[x][y]);
7067 putFile8Bit(file, ei->slippery_type);
7069 // some free bytes for future properties and padding
7070 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7077 if (check != num_changed_custom_elements) // should not happen
7078 Warn("inconsistent number of custom element properties");
7082 #if ENABLE_HISTORIC_CHUNKS
7083 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7085 struct ElementInfo *ei = &element_info[element];
7088 // ---------- custom element base property values (96 bytes) ----------------
7090 putFile16BitBE(file, element);
7092 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7093 putFile8Bit(file, ei->description[i]);
7095 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7097 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7099 putFile8Bit(file, ei->num_change_pages);
7101 putFile16BitBE(file, ei->ce_value_fixed_initial);
7102 putFile16BitBE(file, ei->ce_value_random_initial);
7103 putFile8Bit(file, ei->use_last_ce_value);
7105 putFile8Bit(file, ei->use_gfx_element);
7106 putFile16BitBE(file, ei->gfx_element_initial);
7108 putFile8Bit(file, ei->collect_score_initial);
7109 putFile8Bit(file, ei->collect_count_initial);
7111 putFile8Bit(file, ei->drop_delay_fixed);
7112 putFile8Bit(file, ei->push_delay_fixed);
7113 putFile8Bit(file, ei->drop_delay_random);
7114 putFile8Bit(file, ei->push_delay_random);
7115 putFile16BitBE(file, ei->move_delay_fixed);
7116 putFile16BitBE(file, ei->move_delay_random);
7118 // bits 0 - 15 of "move_pattern" ...
7119 putFile16BitBE(file, ei->move_pattern & 0xffff);
7120 putFile8Bit(file, ei->move_direction_initial);
7121 putFile8Bit(file, ei->move_stepsize);
7123 putFile8Bit(file, ei->slippery_type);
7125 for (y = 0; y < 3; y++)
7126 for (x = 0; x < 3; x++)
7127 putFile16BitBE(file, ei->content.e[x][y]);
7129 putFile16BitBE(file, ei->move_enter_element);
7130 putFile16BitBE(file, ei->move_leave_element);
7131 putFile8Bit(file, ei->move_leave_type);
7133 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7134 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7136 putFile8Bit(file, ei->access_direction);
7138 putFile8Bit(file, ei->explosion_delay);
7139 putFile8Bit(file, ei->ignition_delay);
7140 putFile8Bit(file, ei->explosion_type);
7142 // some free bytes for future custom property values and padding
7143 WriteUnusedBytesToFile(file, 1);
7145 // ---------- change page property values (48 bytes) ------------------------
7147 for (i = 0; i < ei->num_change_pages; i++)
7149 struct ElementChangeInfo *change = &ei->change_page[i];
7150 unsigned int event_bits;
7152 // bits 0 - 31 of "has_event[]" ...
7154 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7155 if (change->has_event[j])
7156 event_bits |= (1 << j);
7157 putFile32BitBE(file, event_bits);
7159 putFile16BitBE(file, change->target_element);
7161 putFile16BitBE(file, change->delay_fixed);
7162 putFile16BitBE(file, change->delay_random);
7163 putFile16BitBE(file, change->delay_frames);
7165 putFile16BitBE(file, change->initial_trigger_element);
7167 putFile8Bit(file, change->explode);
7168 putFile8Bit(file, change->use_target_content);
7169 putFile8Bit(file, change->only_if_complete);
7170 putFile8Bit(file, change->use_random_replace);
7172 putFile8Bit(file, change->random_percentage);
7173 putFile8Bit(file, change->replace_when);
7175 for (y = 0; y < 3; y++)
7176 for (x = 0; x < 3; x++)
7177 putFile16BitBE(file, change->target_content.e[x][y]);
7179 putFile8Bit(file, change->can_change);
7181 putFile8Bit(file, change->trigger_side);
7183 putFile8Bit(file, change->trigger_player);
7184 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7185 log_2(change->trigger_page)));
7187 putFile8Bit(file, change->has_action);
7188 putFile8Bit(file, change->action_type);
7189 putFile8Bit(file, change->action_mode);
7190 putFile16BitBE(file, change->action_arg);
7192 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7194 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7195 if (change->has_event[j])
7196 event_bits |= (1 << (j - 32));
7197 putFile8Bit(file, event_bits);
7202 #if ENABLE_HISTORIC_CHUNKS
7203 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7205 struct ElementInfo *ei = &element_info[element];
7206 struct ElementGroupInfo *group = ei->group;
7209 putFile16BitBE(file, element);
7211 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7212 putFile8Bit(file, ei->description[i]);
7214 putFile8Bit(file, group->num_elements);
7216 putFile8Bit(file, ei->use_gfx_element);
7217 putFile16BitBE(file, ei->gfx_element_initial);
7219 putFile8Bit(file, group->choice_mode);
7221 // some free bytes for future values and padding
7222 WriteUnusedBytesToFile(file, 3);
7224 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7225 putFile16BitBE(file, group->element[i]);
7229 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7230 boolean write_element)
7232 int save_type = entry->save_type;
7233 int data_type = entry->data_type;
7234 int conf_type = entry->conf_type;
7235 int byte_mask = conf_type & CONF_MASK_BYTES;
7236 int element = entry->element;
7237 int default_value = entry->default_value;
7239 boolean modified = FALSE;
7241 if (byte_mask != CONF_MASK_MULTI_BYTES)
7243 void *value_ptr = entry->value;
7244 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7247 // check if any settings have been modified before saving them
7248 if (value != default_value)
7251 // do not save if explicitly told or if unmodified default settings
7252 if ((save_type == SAVE_CONF_NEVER) ||
7253 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7257 num_bytes += putFile16BitBE(file, element);
7259 num_bytes += putFile8Bit(file, conf_type);
7260 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7261 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7262 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7265 else if (data_type == TYPE_STRING)
7267 char *default_string = entry->default_string;
7268 char *string = (char *)(entry->value);
7269 int string_length = strlen(string);
7272 // check if any settings have been modified before saving them
7273 if (!strEqual(string, default_string))
7276 // do not save if explicitly told or if unmodified default settings
7277 if ((save_type == SAVE_CONF_NEVER) ||
7278 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7282 num_bytes += putFile16BitBE(file, element);
7284 num_bytes += putFile8Bit(file, conf_type);
7285 num_bytes += putFile16BitBE(file, string_length);
7287 for (i = 0; i < string_length; i++)
7288 num_bytes += putFile8Bit(file, string[i]);
7290 else if (data_type == TYPE_ELEMENT_LIST)
7292 int *element_array = (int *)(entry->value);
7293 int num_elements = *(int *)(entry->num_entities);
7296 // check if any settings have been modified before saving them
7297 for (i = 0; i < num_elements; i++)
7298 if (element_array[i] != default_value)
7301 // do not save if explicitly told or if unmodified default settings
7302 if ((save_type == SAVE_CONF_NEVER) ||
7303 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7307 num_bytes += putFile16BitBE(file, element);
7309 num_bytes += putFile8Bit(file, conf_type);
7310 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7312 for (i = 0; i < num_elements; i++)
7313 num_bytes += putFile16BitBE(file, element_array[i]);
7315 else if (data_type == TYPE_CONTENT_LIST)
7317 struct Content *content = (struct Content *)(entry->value);
7318 int num_contents = *(int *)(entry->num_entities);
7321 // check if any settings have been modified before saving them
7322 for (i = 0; i < num_contents; i++)
7323 for (y = 0; y < 3; y++)
7324 for (x = 0; x < 3; x++)
7325 if (content[i].e[x][y] != default_value)
7328 // do not save if explicitly told or if unmodified default settings
7329 if ((save_type == SAVE_CONF_NEVER) ||
7330 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7334 num_bytes += putFile16BitBE(file, element);
7336 num_bytes += putFile8Bit(file, conf_type);
7337 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7339 for (i = 0; i < num_contents; i++)
7340 for (y = 0; y < 3; y++)
7341 for (x = 0; x < 3; x++)
7342 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7348 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7353 li = *level; // copy level data into temporary buffer
7355 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7356 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7361 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7366 li = *level; // copy level data into temporary buffer
7368 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7369 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7374 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7376 int envelope_nr = element - EL_ENVELOPE_1;
7380 chunk_size += putFile16BitBE(file, element);
7382 // copy envelope data into temporary buffer
7383 xx_envelope = level->envelope[envelope_nr];
7385 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7386 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7391 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7393 struct ElementInfo *ei = &element_info[element];
7397 chunk_size += putFile16BitBE(file, element);
7399 xx_ei = *ei; // copy element data into temporary buffer
7401 // set default description string for this specific element
7402 strcpy(xx_default_description, getDefaultElementDescription(ei));
7404 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7405 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7407 for (i = 0; i < ei->num_change_pages; i++)
7409 struct ElementChangeInfo *change = &ei->change_page[i];
7411 xx_current_change_page = i;
7413 xx_change = *change; // copy change data into temporary buffer
7416 setEventBitsFromEventFlags(change);
7418 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7419 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7426 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7428 struct ElementInfo *ei = &element_info[element];
7429 struct ElementGroupInfo *group = ei->group;
7433 chunk_size += putFile16BitBE(file, element);
7435 xx_ei = *ei; // copy element data into temporary buffer
7436 xx_group = *group; // copy group data into temporary buffer
7438 // set default description string for this specific element
7439 strcpy(xx_default_description, getDefaultElementDescription(ei));
7441 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7442 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7447 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7448 boolean save_as_template)
7454 if (!(file = fopen(filename, MODE_WRITE)))
7456 Warn("cannot save level file '%s'", filename);
7461 level->file_version = FILE_VERSION_ACTUAL;
7462 level->game_version = GAME_VERSION_ACTUAL;
7464 level->creation_date = getCurrentDate();
7466 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7467 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7469 chunk_size = SaveLevel_VERS(NULL, level);
7470 putFileChunkBE(file, "VERS", chunk_size);
7471 SaveLevel_VERS(file, level);
7473 chunk_size = SaveLevel_DATE(NULL, level);
7474 putFileChunkBE(file, "DATE", chunk_size);
7475 SaveLevel_DATE(file, level);
7477 chunk_size = SaveLevel_NAME(NULL, level);
7478 putFileChunkBE(file, "NAME", chunk_size);
7479 SaveLevel_NAME(file, level);
7481 chunk_size = SaveLevel_AUTH(NULL, level);
7482 putFileChunkBE(file, "AUTH", chunk_size);
7483 SaveLevel_AUTH(file, level);
7485 chunk_size = SaveLevel_INFO(NULL, level);
7486 putFileChunkBE(file, "INFO", chunk_size);
7487 SaveLevel_INFO(file, level);
7489 chunk_size = SaveLevel_BODY(NULL, level);
7490 putFileChunkBE(file, "BODY", chunk_size);
7491 SaveLevel_BODY(file, level);
7493 chunk_size = SaveLevel_ELEM(NULL, level);
7494 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7496 putFileChunkBE(file, "ELEM", chunk_size);
7497 SaveLevel_ELEM(file, level);
7500 for (i = 0; i < NUM_ENVELOPES; i++)
7502 int element = EL_ENVELOPE_1 + i;
7504 chunk_size = SaveLevel_NOTE(NULL, level, element);
7505 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7507 putFileChunkBE(file, "NOTE", chunk_size);
7508 SaveLevel_NOTE(file, level, element);
7512 // if not using template level, check for non-default custom/group elements
7513 if (!level->use_custom_template || save_as_template)
7515 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7517 int element = EL_CUSTOM_START + i;
7519 chunk_size = SaveLevel_CUSX(NULL, level, element);
7520 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7522 putFileChunkBE(file, "CUSX", chunk_size);
7523 SaveLevel_CUSX(file, level, element);
7527 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7529 int element = EL_GROUP_START + i;
7531 chunk_size = SaveLevel_GRPX(NULL, level, element);
7532 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7534 putFileChunkBE(file, "GRPX", chunk_size);
7535 SaveLevel_GRPX(file, level, element);
7542 SetFilePermissions(filename, PERMS_PRIVATE);
7545 void SaveLevel(int nr)
7547 char *filename = getDefaultLevelFilename(nr);
7549 SaveLevelFromFilename(&level, filename, FALSE);
7552 void SaveLevelTemplate(void)
7554 char *filename = getLocalLevelTemplateFilename();
7556 SaveLevelFromFilename(&level, filename, TRUE);
7559 boolean SaveLevelChecked(int nr)
7561 char *filename = getDefaultLevelFilename(nr);
7562 boolean new_level = !fileExists(filename);
7563 boolean level_saved = FALSE;
7565 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7570 Request("Level saved!", REQ_CONFIRM);
7578 void DumpLevel(struct LevelInfo *level)
7580 if (level->no_level_file || level->no_valid_file)
7582 Warn("cannot dump -- no valid level file found");
7588 Print("Level xxx (file version %08d, game version %08d)\n",
7589 level->file_version, level->game_version);
7592 Print("Level author: '%s'\n", level->author);
7593 Print("Level title: '%s'\n", level->name);
7595 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7597 Print("Level time: %d seconds\n", level->time);
7598 Print("Gems needed: %d\n", level->gems_needed);
7600 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7601 Print("Time for wheel: %d seconds\n", level->time_wheel);
7602 Print("Time for light: %d seconds\n", level->time_light);
7603 Print("Time for timegate: %d seconds\n", level->time_timegate);
7605 Print("Amoeba speed: %d\n", level->amoeba_speed);
7608 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7609 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7610 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7611 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7612 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7618 // ============================================================================
7619 // tape file functions
7620 // ============================================================================
7622 static void setTapeInfoToDefaults(void)
7626 // always start with reliable default values (empty tape)
7629 // default values (also for pre-1.2 tapes) with only the first player
7630 tape.player_participates[0] = TRUE;
7631 for (i = 1; i < MAX_PLAYERS; i++)
7632 tape.player_participates[i] = FALSE;
7634 // at least one (default: the first) player participates in every tape
7635 tape.num_participating_players = 1;
7637 tape.property_bits = TAPE_PROPERTY_NONE;
7639 tape.level_nr = level_nr;
7641 tape.changed = FALSE;
7643 tape.recording = FALSE;
7644 tape.playing = FALSE;
7645 tape.pausing = FALSE;
7647 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7648 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7650 tape.no_valid_file = FALSE;
7653 static int getTapePosSize(struct TapeInfo *tape)
7655 int tape_pos_size = 0;
7657 if (tape->use_key_actions)
7658 tape_pos_size += tape->num_participating_players;
7660 if (tape->use_mouse_actions)
7661 tape_pos_size += 3; // x and y position and mouse button mask
7663 tape_pos_size += 1; // tape action delay value
7665 return tape_pos_size;
7668 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7670 tape->use_key_actions = FALSE;
7671 tape->use_mouse_actions = FALSE;
7673 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7674 tape->use_key_actions = TRUE;
7676 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7677 tape->use_mouse_actions = TRUE;
7680 static int getTapeActionValue(struct TapeInfo *tape)
7682 return (tape->use_key_actions &&
7683 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7684 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7685 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7686 TAPE_ACTIONS_DEFAULT);
7689 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7691 tape->file_version = getFileVersion(file);
7692 tape->game_version = getFileVersion(file);
7697 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7701 tape->random_seed = getFile32BitBE(file);
7702 tape->date = getFile32BitBE(file);
7703 tape->length = getFile32BitBE(file);
7705 // read header fields that are new since version 1.2
7706 if (tape->file_version >= FILE_VERSION_1_2)
7708 byte store_participating_players = getFile8Bit(file);
7711 // since version 1.2, tapes store which players participate in the tape
7712 tape->num_participating_players = 0;
7713 for (i = 0; i < MAX_PLAYERS; i++)
7715 tape->player_participates[i] = FALSE;
7717 if (store_participating_players & (1 << i))
7719 tape->player_participates[i] = TRUE;
7720 tape->num_participating_players++;
7724 setTapeActionFlags(tape, getFile8Bit(file));
7726 tape->property_bits = getFile8Bit(file);
7728 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7730 engine_version = getFileVersion(file);
7731 if (engine_version > 0)
7732 tape->engine_version = engine_version;
7734 tape->engine_version = tape->game_version;
7740 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7742 tape->scr_fieldx = getFile8Bit(file);
7743 tape->scr_fieldy = getFile8Bit(file);
7748 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7750 int level_identifier_size;
7753 level_identifier_size = getFile16BitBE(file);
7755 tape->level_identifier =
7756 checked_realloc(tape->level_identifier, level_identifier_size);
7758 for (i = 0; i < level_identifier_size; i++)
7759 tape->level_identifier[i] = getFile8Bit(file);
7761 tape->level_nr = getFile16BitBE(file);
7763 chunk_size = 2 + level_identifier_size + 2;
7768 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7771 int tape_pos_size = getTapePosSize(tape);
7772 int chunk_size_expected = tape_pos_size * tape->length;
7774 if (chunk_size_expected != chunk_size)
7776 ReadUnusedBytesFromFile(file, chunk_size);
7777 return chunk_size_expected;
7780 for (i = 0; i < tape->length; i++)
7782 if (i >= MAX_TAPE_LEN)
7784 Warn("tape truncated -- size exceeds maximum tape size %d",
7787 // tape too large; read and ignore remaining tape data from this chunk
7788 for (;i < tape->length; i++)
7789 ReadUnusedBytesFromFile(file, tape_pos_size);
7794 if (tape->use_key_actions)
7796 for (j = 0; j < MAX_PLAYERS; j++)
7798 tape->pos[i].action[j] = MV_NONE;
7800 if (tape->player_participates[j])
7801 tape->pos[i].action[j] = getFile8Bit(file);
7805 if (tape->use_mouse_actions)
7807 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7808 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7809 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7812 tape->pos[i].delay = getFile8Bit(file);
7814 if (tape->file_version == FILE_VERSION_1_0)
7816 // eliminate possible diagonal moves in old tapes
7817 // this is only for backward compatibility
7819 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7820 byte action = tape->pos[i].action[0];
7821 int k, num_moves = 0;
7823 for (k = 0; k<4; k++)
7825 if (action & joy_dir[k])
7827 tape->pos[i + num_moves].action[0] = joy_dir[k];
7829 tape->pos[i + num_moves].delay = 0;
7838 tape->length += num_moves;
7841 else if (tape->file_version < FILE_VERSION_2_0)
7843 // convert pre-2.0 tapes to new tape format
7845 if (tape->pos[i].delay > 1)
7848 tape->pos[i + 1] = tape->pos[i];
7849 tape->pos[i + 1].delay = 1;
7852 for (j = 0; j < MAX_PLAYERS; j++)
7853 tape->pos[i].action[j] = MV_NONE;
7854 tape->pos[i].delay--;
7861 if (checkEndOfFile(file))
7865 if (i != tape->length)
7866 chunk_size = tape_pos_size * i;
7871 static void LoadTape_SokobanSolution(char *filename)
7874 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7876 if (!(file = openFile(filename, MODE_READ)))
7878 tape.no_valid_file = TRUE;
7883 while (!checkEndOfFile(file))
7885 unsigned char c = getByteFromFile(file);
7887 if (checkEndOfFile(file))
7894 tape.pos[tape.length].action[0] = MV_UP;
7895 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7901 tape.pos[tape.length].action[0] = MV_DOWN;
7902 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7908 tape.pos[tape.length].action[0] = MV_LEFT;
7909 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7915 tape.pos[tape.length].action[0] = MV_RIGHT;
7916 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7924 // ignore white-space characters
7928 tape.no_valid_file = TRUE;
7930 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7938 if (tape.no_valid_file)
7941 tape.length_frames = GetTapeLengthFrames();
7942 tape.length_seconds = GetTapeLengthSeconds();
7945 void LoadTapeFromFilename(char *filename)
7947 char cookie[MAX_LINE_LEN];
7948 char chunk_name[CHUNK_ID_LEN + 1];
7952 // always start with reliable default values
7953 setTapeInfoToDefaults();
7955 if (strSuffix(filename, ".sln"))
7957 LoadTape_SokobanSolution(filename);
7962 if (!(file = openFile(filename, MODE_READ)))
7964 tape.no_valid_file = TRUE;
7969 getFileChunkBE(file, chunk_name, NULL);
7970 if (strEqual(chunk_name, "RND1"))
7972 getFile32BitBE(file); // not used
7974 getFileChunkBE(file, chunk_name, NULL);
7975 if (!strEqual(chunk_name, "TAPE"))
7977 tape.no_valid_file = TRUE;
7979 Warn("unknown format of tape file '%s'", filename);
7986 else // check for pre-2.0 file format with cookie string
7988 strcpy(cookie, chunk_name);
7989 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7991 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7992 cookie[strlen(cookie) - 1] = '\0';
7994 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7996 tape.no_valid_file = TRUE;
7998 Warn("unknown format of tape file '%s'", filename);
8005 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8007 tape.no_valid_file = TRUE;
8009 Warn("unsupported version of tape file '%s'", filename);
8016 // pre-2.0 tape files have no game version, so use file version here
8017 tape.game_version = tape.file_version;
8020 if (tape.file_version < FILE_VERSION_1_2)
8022 // tape files from versions before 1.2.0 without chunk structure
8023 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8024 LoadTape_BODY(file, 2 * tape.length, &tape);
8032 int (*loader)(File *, int, struct TapeInfo *);
8036 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8037 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8038 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8039 { "INFO", -1, LoadTape_INFO },
8040 { "BODY", -1, LoadTape_BODY },
8044 while (getFileChunkBE(file, chunk_name, &chunk_size))
8048 while (chunk_info[i].name != NULL &&
8049 !strEqual(chunk_name, chunk_info[i].name))
8052 if (chunk_info[i].name == NULL)
8054 Warn("unknown chunk '%s' in tape file '%s'",
8055 chunk_name, filename);
8057 ReadUnusedBytesFromFile(file, chunk_size);
8059 else if (chunk_info[i].size != -1 &&
8060 chunk_info[i].size != chunk_size)
8062 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8063 chunk_size, chunk_name, filename);
8065 ReadUnusedBytesFromFile(file, chunk_size);
8069 // call function to load this tape chunk
8070 int chunk_size_expected =
8071 (chunk_info[i].loader)(file, chunk_size, &tape);
8073 // the size of some chunks cannot be checked before reading other
8074 // chunks first (like "HEAD" and "BODY") that contain some header
8075 // information, so check them here
8076 if (chunk_size_expected != chunk_size)
8078 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8079 chunk_size, chunk_name, filename);
8087 tape.length_frames = GetTapeLengthFrames();
8088 tape.length_seconds = GetTapeLengthSeconds();
8091 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8093 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8095 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8096 tape.engine_version);
8100 void LoadTape(int nr)
8102 char *filename = getTapeFilename(nr);
8104 LoadTapeFromFilename(filename);
8107 void LoadSolutionTape(int nr)
8109 char *filename = getSolutionTapeFilename(nr);
8111 LoadTapeFromFilename(filename);
8113 if (TAPE_IS_EMPTY(tape) &&
8114 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8115 level.native_sp_level->demo.is_available)
8116 CopyNativeTape_SP_to_RND(&level);
8119 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8121 // chunk required for team mode tapes with non-default screen size
8122 return (tape->num_participating_players > 1 &&
8123 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8124 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8127 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8129 putFileVersion(file, tape->file_version);
8130 putFileVersion(file, tape->game_version);
8133 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8136 byte store_participating_players = 0;
8138 // set bits for participating players for compact storage
8139 for (i = 0; i < MAX_PLAYERS; i++)
8140 if (tape->player_participates[i])
8141 store_participating_players |= (1 << i);
8143 putFile32BitBE(file, tape->random_seed);
8144 putFile32BitBE(file, tape->date);
8145 putFile32BitBE(file, tape->length);
8147 putFile8Bit(file, store_participating_players);
8149 putFile8Bit(file, getTapeActionValue(tape));
8151 putFile8Bit(file, tape->property_bits);
8153 // unused bytes not at the end here for 4-byte alignment of engine_version
8154 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8156 putFileVersion(file, tape->engine_version);
8159 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8161 putFile8Bit(file, tape->scr_fieldx);
8162 putFile8Bit(file, tape->scr_fieldy);
8165 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8167 int level_identifier_size = strlen(tape->level_identifier) + 1;
8170 putFile16BitBE(file, level_identifier_size);
8172 for (i = 0; i < level_identifier_size; i++)
8173 putFile8Bit(file, tape->level_identifier[i]);
8175 putFile16BitBE(file, tape->level_nr);
8178 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8182 for (i = 0; i < tape->length; i++)
8184 if (tape->use_key_actions)
8186 for (j = 0; j < MAX_PLAYERS; j++)
8187 if (tape->player_participates[j])
8188 putFile8Bit(file, tape->pos[i].action[j]);
8191 if (tape->use_mouse_actions)
8193 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8194 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8195 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8198 putFile8Bit(file, tape->pos[i].delay);
8202 void SaveTapeToFilename(char *filename)
8206 int info_chunk_size;
8207 int body_chunk_size;
8209 if (!(file = fopen(filename, MODE_WRITE)))
8211 Warn("cannot save level recording file '%s'", filename);
8216 tape_pos_size = getTapePosSize(&tape);
8218 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8219 body_chunk_size = tape_pos_size * tape.length;
8221 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8222 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8224 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8225 SaveTape_VERS(file, &tape);
8227 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8228 SaveTape_HEAD(file, &tape);
8230 if (checkSaveTape_SCRN(&tape))
8232 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8233 SaveTape_SCRN(file, &tape);
8236 putFileChunkBE(file, "INFO", info_chunk_size);
8237 SaveTape_INFO(file, &tape);
8239 putFileChunkBE(file, "BODY", body_chunk_size);
8240 SaveTape_BODY(file, &tape);
8244 SetFilePermissions(filename, PERMS_PRIVATE);
8247 void SaveTape(int nr)
8249 char *filename = getTapeFilename(nr);
8252 InitTapeDirectory(leveldir_current->subdir);
8254 tape.file_version = FILE_VERSION_ACTUAL;
8255 tape.game_version = GAME_VERSION_ACTUAL;
8257 tape.num_participating_players = 0;
8259 // count number of participating players
8260 for (i = 0; i < MAX_PLAYERS; i++)
8261 if (tape.player_participates[i])
8262 tape.num_participating_players++;
8264 SaveTapeToFilename(filename);
8266 tape.changed = FALSE;
8269 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8270 unsigned int req_state_added)
8272 char *filename = getTapeFilename(nr);
8273 boolean new_tape = !fileExists(filename);
8274 boolean tape_saved = FALSE;
8276 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8281 Request(msg_saved, REQ_CONFIRM | req_state_added);
8289 boolean SaveTapeChecked(int nr)
8291 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8294 boolean SaveTapeChecked_LevelSolved(int nr)
8296 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8297 "Level solved! Tape saved!", REQ_STAY_OPEN);
8300 void DumpTape(struct TapeInfo *tape)
8302 int tape_frame_counter;
8305 if (tape->no_valid_file)
8307 Warn("cannot dump -- no valid tape file found");
8313 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8314 tape->level_nr, tape->file_version, tape->game_version);
8315 Print(" (effective engine version %08d)\n",
8316 tape->engine_version);
8317 Print("Level series identifier: '%s'\n", tape->level_identifier);
8320 tape_frame_counter = 0;
8322 for (i = 0; i < tape->length; i++)
8324 if (i >= MAX_TAPE_LEN)
8329 for (j = 0; j < MAX_PLAYERS; j++)
8331 if (tape->player_participates[j])
8333 int action = tape->pos[i].action[j];
8335 Print("%d:%02x ", j, action);
8336 Print("[%c%c%c%c|%c%c] - ",
8337 (action & JOY_LEFT ? '<' : ' '),
8338 (action & JOY_RIGHT ? '>' : ' '),
8339 (action & JOY_UP ? '^' : ' '),
8340 (action & JOY_DOWN ? 'v' : ' '),
8341 (action & JOY_BUTTON_1 ? '1' : ' '),
8342 (action & JOY_BUTTON_2 ? '2' : ' '));
8346 Print("(%03d) ", tape->pos[i].delay);
8347 Print("[%05d]\n", tape_frame_counter);
8349 tape_frame_counter += tape->pos[i].delay;
8356 // ============================================================================
8357 // score file functions
8358 // ============================================================================
8360 void LoadScore(int nr)
8363 char *filename = getScoreFilename(nr);
8364 char cookie[MAX_LINE_LEN];
8365 char line[MAX_LINE_LEN];
8369 // always start with reliable default values
8370 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8372 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8373 highscore[i].Score = 0;
8376 if (!(file = fopen(filename, MODE_READ)))
8379 // check file identifier
8380 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8382 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8383 cookie[strlen(cookie) - 1] = '\0';
8385 if (!checkCookieString(cookie, SCORE_COOKIE))
8387 Warn("unknown format of score file '%s'", filename);
8394 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8396 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8397 Warn("fscanf() failed; %s", strerror(errno));
8399 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8402 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8403 line[strlen(line) - 1] = '\0';
8405 for (line_ptr = line; *line_ptr; line_ptr++)
8407 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8409 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8410 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8419 void SaveScore(int nr)
8422 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8423 char *filename = getScoreFilename(nr);
8426 // used instead of "leveldir_current->subdir" (for network games)
8427 InitScoreDirectory(levelset.identifier);
8429 if (!(file = fopen(filename, MODE_WRITE)))
8431 Warn("cannot save score for level %d", nr);
8436 fprintf(file, "%s\n\n", SCORE_COOKIE);
8438 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8439 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8443 SetFilePermissions(filename, permissions);
8447 // ============================================================================
8448 // setup file functions
8449 // ============================================================================
8451 #define TOKEN_STR_PLAYER_PREFIX "player_"
8454 static struct TokenInfo global_setup_tokens[] =
8458 &setup.player_name, "player_name"
8462 &setup.multiple_users, "multiple_users"
8466 &setup.sound, "sound"
8470 &setup.sound_loops, "repeating_sound_loops"
8474 &setup.sound_music, "background_music"
8478 &setup.sound_simple, "simple_sound_effects"
8482 &setup.toons, "toons"
8486 &setup.scroll_delay, "scroll_delay"
8490 &setup.forced_scroll_delay, "forced_scroll_delay"
8494 &setup.scroll_delay_value, "scroll_delay_value"
8498 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8502 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8506 &setup.fade_screens, "fade_screens"
8510 &setup.autorecord, "automatic_tape_recording"
8514 &setup.show_titlescreen, "show_titlescreen"
8518 &setup.quick_doors, "quick_doors"
8522 &setup.team_mode, "team_mode"
8526 &setup.handicap, "handicap"
8530 &setup.skip_levels, "skip_levels"
8534 &setup.increment_levels, "increment_levels"
8538 &setup.auto_play_next_level, "auto_play_next_level"
8542 &setup.skip_scores_after_game, "skip_scores_after_game"
8546 &setup.time_limit, "time_limit"
8550 &setup.fullscreen, "fullscreen"
8554 &setup.window_scaling_percent, "window_scaling_percent"
8558 &setup.window_scaling_quality, "window_scaling_quality"
8562 &setup.screen_rendering_mode, "screen_rendering_mode"
8566 &setup.vsync_mode, "vsync_mode"
8570 &setup.ask_on_escape, "ask_on_escape"
8574 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8578 &setup.ask_on_game_over, "ask_on_game_over"
8582 &setup.quick_switch, "quick_player_switch"
8586 &setup.input_on_focus, "input_on_focus"
8590 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8594 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8598 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8602 &setup.game_speed_extended, "game_speed_extended"
8606 &setup.game_frame_delay, "game_frame_delay"
8610 &setup.sp_show_border_elements, "sp_show_border_elements"
8614 &setup.small_game_graphics, "small_game_graphics"
8618 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8622 &setup.graphics_set, "graphics_set"
8626 &setup.sounds_set, "sounds_set"
8630 &setup.music_set, "music_set"
8634 &setup.override_level_graphics, "override_level_graphics"
8638 &setup.override_level_sounds, "override_level_sounds"
8642 &setup.override_level_music, "override_level_music"
8646 &setup.volume_simple, "volume_simple"
8650 &setup.volume_loops, "volume_loops"
8654 &setup.volume_music, "volume_music"
8658 &setup.network_mode, "network_mode"
8662 &setup.network_player_nr, "network_player"
8666 &setup.network_server_hostname, "network_server_hostname"
8670 &setup.touch.control_type, "touch.control_type"
8674 &setup.touch.move_distance, "touch.move_distance"
8678 &setup.touch.drop_distance, "touch.drop_distance"
8682 &setup.touch.transparency, "touch.transparency"
8686 &setup.touch.draw_outlined, "touch.draw_outlined"
8690 &setup.touch.draw_pressed, "touch.draw_pressed"
8694 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8698 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8702 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8706 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8710 static struct TokenInfo auto_setup_tokens[] =
8714 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8718 static struct TokenInfo editor_setup_tokens[] =
8722 &setup.editor.el_classic, "editor.el_classic"
8726 &setup.editor.el_custom, "editor.el_custom"
8730 &setup.editor.el_user_defined, "editor.el_user_defined"
8734 &setup.editor.el_dynamic, "editor.el_dynamic"
8738 &setup.editor.el_headlines, "editor.el_headlines"
8742 &setup.editor.show_element_token, "editor.show_element_token"
8746 static struct TokenInfo editor_cascade_setup_tokens[] =
8750 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8754 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8758 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8762 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8766 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8770 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8774 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8778 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8782 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8786 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8790 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8794 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8798 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8802 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8806 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8810 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8814 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8818 static struct TokenInfo shortcut_setup_tokens[] =
8822 &setup.shortcut.save_game, "shortcut.save_game"
8826 &setup.shortcut.load_game, "shortcut.load_game"
8830 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8834 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8838 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8842 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8846 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8850 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8854 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8858 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8862 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8866 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8870 &setup.shortcut.tape_record, "shortcut.tape_record"
8874 &setup.shortcut.tape_play, "shortcut.tape_play"
8878 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8882 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8886 &setup.shortcut.sound_music, "shortcut.sound_music"
8890 &setup.shortcut.snap_left, "shortcut.snap_left"
8894 &setup.shortcut.snap_right, "shortcut.snap_right"
8898 &setup.shortcut.snap_up, "shortcut.snap_up"
8902 &setup.shortcut.snap_down, "shortcut.snap_down"
8906 static struct SetupInputInfo setup_input;
8907 static struct TokenInfo player_setup_tokens[] =
8911 &setup_input.use_joystick, ".use_joystick"
8915 &setup_input.joy.device_name, ".joy.device_name"
8919 &setup_input.joy.xleft, ".joy.xleft"
8923 &setup_input.joy.xmiddle, ".joy.xmiddle"
8927 &setup_input.joy.xright, ".joy.xright"
8931 &setup_input.joy.yupper, ".joy.yupper"
8935 &setup_input.joy.ymiddle, ".joy.ymiddle"
8939 &setup_input.joy.ylower, ".joy.ylower"
8943 &setup_input.joy.snap, ".joy.snap_field"
8947 &setup_input.joy.drop, ".joy.place_bomb"
8951 &setup_input.key.left, ".key.move_left"
8955 &setup_input.key.right, ".key.move_right"
8959 &setup_input.key.up, ".key.move_up"
8963 &setup_input.key.down, ".key.move_down"
8967 &setup_input.key.snap, ".key.snap_field"
8971 &setup_input.key.drop, ".key.place_bomb"
8975 static struct TokenInfo system_setup_tokens[] =
8979 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
8983 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8987 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8991 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8995 static struct TokenInfo internal_setup_tokens[] =
8999 &setup.internal.program_title, "program_title"
9003 &setup.internal.program_version, "program_version"
9007 &setup.internal.program_author, "program_author"
9011 &setup.internal.program_email, "program_email"
9015 &setup.internal.program_website, "program_website"
9019 &setup.internal.program_copyright, "program_copyright"
9023 &setup.internal.program_company, "program_company"
9027 &setup.internal.program_icon_file, "program_icon_file"
9031 &setup.internal.default_graphics_set, "default_graphics_set"
9035 &setup.internal.default_sounds_set, "default_sounds_set"
9039 &setup.internal.default_music_set, "default_music_set"
9043 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9047 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9051 &setup.internal.fallback_music_file, "fallback_music_file"
9055 &setup.internal.default_level_series, "default_level_series"
9059 &setup.internal.default_window_width, "default_window_width"
9063 &setup.internal.default_window_height, "default_window_height"
9067 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9071 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9075 &setup.internal.create_user_levelset, "create_user_levelset"
9079 &setup.internal.menu_game, "menu_game"
9083 &setup.internal.menu_editor, "menu_editor"
9087 &setup.internal.menu_graphics, "menu_graphics"
9091 &setup.internal.menu_sound, "menu_sound"
9095 &setup.internal.menu_artwork, "menu_artwork"
9099 &setup.internal.menu_input, "menu_input"
9103 &setup.internal.menu_touch, "menu_touch"
9107 &setup.internal.menu_shortcuts, "menu_shortcuts"
9111 &setup.internal.menu_exit, "menu_exit"
9115 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9119 static struct TokenInfo debug_setup_tokens[] =
9123 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9127 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9131 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9135 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9139 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9143 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9147 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9151 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9155 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9159 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9163 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9167 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9171 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9175 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9179 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9183 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9187 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9191 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9195 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9199 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9203 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9206 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9210 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9214 &setup.debug.xsn_mode, "debug.xsn_mode"
9218 &setup.debug.xsn_percent, "debug.xsn_percent"
9222 static struct TokenInfo options_setup_tokens[] =
9226 &setup.options.verbose, "options.verbose"
9230 static void setSetupInfoToDefaults(struct SetupInfo *si)
9234 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9236 si->multiple_users = TRUE;
9239 si->sound_loops = TRUE;
9240 si->sound_music = TRUE;
9241 si->sound_simple = TRUE;
9243 si->scroll_delay = TRUE;
9244 si->forced_scroll_delay = FALSE;
9245 si->scroll_delay_value = STD_SCROLL_DELAY;
9246 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9247 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9248 si->fade_screens = TRUE;
9249 si->autorecord = TRUE;
9250 si->show_titlescreen = TRUE;
9251 si->quick_doors = FALSE;
9252 si->team_mode = FALSE;
9253 si->handicap = TRUE;
9254 si->skip_levels = TRUE;
9255 si->increment_levels = TRUE;
9256 si->auto_play_next_level = TRUE;
9257 si->skip_scores_after_game = FALSE;
9258 si->time_limit = TRUE;
9259 si->fullscreen = FALSE;
9260 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9261 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9262 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9263 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9264 si->ask_on_escape = TRUE;
9265 si->ask_on_escape_editor = TRUE;
9266 si->ask_on_game_over = TRUE;
9267 si->quick_switch = FALSE;
9268 si->input_on_focus = FALSE;
9269 si->prefer_aga_graphics = TRUE;
9270 si->prefer_lowpass_sounds = FALSE;
9271 si->prefer_extra_panel_items = TRUE;
9272 si->game_speed_extended = FALSE;
9273 si->game_frame_delay = GAME_FRAME_DELAY;
9274 si->sp_show_border_elements = FALSE;
9275 si->small_game_graphics = FALSE;
9276 si->show_snapshot_buttons = FALSE;
9278 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9279 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9280 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9282 si->override_level_graphics = FALSE;
9283 si->override_level_sounds = FALSE;
9284 si->override_level_music = FALSE;
9286 si->volume_simple = 100; // percent
9287 si->volume_loops = 100; // percent
9288 si->volume_music = 100; // percent
9290 si->network_mode = FALSE;
9291 si->network_player_nr = 0; // first player
9292 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9294 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9295 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9296 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9297 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9298 si->touch.draw_outlined = TRUE;
9299 si->touch.draw_pressed = TRUE;
9301 for (i = 0; i < 2; i++)
9303 char *default_grid_button[6][2] =
9309 { "111222", " vv " },
9310 { "111222", " vv " }
9312 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9313 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9314 int min_xsize = MIN(6, grid_xsize);
9315 int min_ysize = MIN(6, grid_ysize);
9316 int startx = grid_xsize - min_xsize;
9317 int starty = grid_ysize - min_ysize;
9320 // virtual buttons grid can only be set to defaults if video is initialized
9321 // (this will be repeated if virtual buttons are not loaded from setup file)
9322 if (video.initialized)
9324 si->touch.grid_xsize[i] = grid_xsize;
9325 si->touch.grid_ysize[i] = grid_ysize;
9329 si->touch.grid_xsize[i] = -1;
9330 si->touch.grid_ysize[i] = -1;
9333 for (x = 0; x < MAX_GRID_XSIZE; x++)
9334 for (y = 0; y < MAX_GRID_YSIZE; y++)
9335 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9337 for (x = 0; x < min_xsize; x++)
9338 for (y = 0; y < min_ysize; y++)
9339 si->touch.grid_button[i][x][starty + y] =
9340 default_grid_button[y][0][x];
9342 for (x = 0; x < min_xsize; x++)
9343 for (y = 0; y < min_ysize; y++)
9344 si->touch.grid_button[i][startx + x][starty + y] =
9345 default_grid_button[y][1][x];
9348 si->touch.grid_initialized = video.initialized;
9350 si->editor.el_boulderdash = TRUE;
9351 si->editor.el_emerald_mine = TRUE;
9352 si->editor.el_emerald_mine_club = TRUE;
9353 si->editor.el_more = TRUE;
9354 si->editor.el_sokoban = TRUE;
9355 si->editor.el_supaplex = TRUE;
9356 si->editor.el_diamond_caves = TRUE;
9357 si->editor.el_dx_boulderdash = TRUE;
9359 si->editor.el_mirror_magic = TRUE;
9360 si->editor.el_deflektor = TRUE;
9362 si->editor.el_chars = TRUE;
9363 si->editor.el_steel_chars = TRUE;
9365 si->editor.el_classic = TRUE;
9366 si->editor.el_custom = TRUE;
9368 si->editor.el_user_defined = FALSE;
9369 si->editor.el_dynamic = TRUE;
9371 si->editor.el_headlines = TRUE;
9373 si->editor.show_element_token = FALSE;
9375 si->editor.use_template_for_new_levels = TRUE;
9377 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9378 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9379 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9381 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9382 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9383 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9384 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9385 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9387 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9388 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9389 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9390 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9391 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9392 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9394 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9395 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9396 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9398 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9399 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9400 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9401 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9403 for (i = 0; i < MAX_PLAYERS; i++)
9405 si->input[i].use_joystick = FALSE;
9406 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9407 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9408 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9409 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9410 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9411 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9412 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9413 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9414 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9415 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9416 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9417 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9418 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9419 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9420 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9423 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9424 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9425 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9426 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9428 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9429 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9430 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9431 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9432 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9433 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9434 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9436 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9438 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9439 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9440 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9442 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9443 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9444 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9446 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9447 si->internal.choose_from_top_leveldir = FALSE;
9448 si->internal.show_scaling_in_title = TRUE;
9449 si->internal.create_user_levelset = TRUE;
9451 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9452 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9454 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9455 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9456 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9457 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9458 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9459 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9460 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9461 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9462 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9463 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9465 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9466 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9467 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9468 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9469 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9470 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9471 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9472 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9473 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9474 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9476 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9477 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9479 si->debug.show_frames_per_second = FALSE;
9481 si->debug.xsn_mode = AUTO;
9482 si->debug.xsn_percent = 0;
9484 si->options.verbose = FALSE;
9486 #if defined(PLATFORM_ANDROID)
9487 si->fullscreen = TRUE;
9490 setHideSetupEntry(&setup.debug.xsn_mode);
9493 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9495 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9498 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9500 si->editor_cascade.el_bd = TRUE;
9501 si->editor_cascade.el_em = TRUE;
9502 si->editor_cascade.el_emc = TRUE;
9503 si->editor_cascade.el_rnd = TRUE;
9504 si->editor_cascade.el_sb = TRUE;
9505 si->editor_cascade.el_sp = TRUE;
9506 si->editor_cascade.el_dc = TRUE;
9507 si->editor_cascade.el_dx = TRUE;
9509 si->editor_cascade.el_mm = TRUE;
9510 si->editor_cascade.el_df = TRUE;
9512 si->editor_cascade.el_chars = FALSE;
9513 si->editor_cascade.el_steel_chars = FALSE;
9514 si->editor_cascade.el_ce = FALSE;
9515 si->editor_cascade.el_ge = FALSE;
9516 si->editor_cascade.el_ref = FALSE;
9517 si->editor_cascade.el_user = FALSE;
9518 si->editor_cascade.el_dynamic = FALSE;
9521 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9523 static char *getHideSetupToken(void *setup_value)
9525 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9527 if (setup_value != NULL)
9528 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9530 return hide_setup_token;
9533 void setHideSetupEntry(void *setup_value)
9535 char *hide_setup_token = getHideSetupToken(setup_value);
9537 if (hide_setup_hash == NULL)
9538 hide_setup_hash = newSetupFileHash();
9540 if (setup_value != NULL)
9541 setHashEntry(hide_setup_hash, hide_setup_token, "");
9544 void removeHideSetupEntry(void *setup_value)
9546 char *hide_setup_token = getHideSetupToken(setup_value);
9548 if (setup_value != NULL)
9549 removeHashEntry(hide_setup_hash, hide_setup_token);
9552 boolean hideSetupEntry(void *setup_value)
9554 char *hide_setup_token = getHideSetupToken(setup_value);
9556 return (setup_value != NULL &&
9557 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9560 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9561 struct TokenInfo *token_info,
9562 int token_nr, char *token_text)
9564 char *token_hide_text = getStringCat2(token_text, ".hide");
9565 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9567 // set the value of this setup option in the setup option structure
9568 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9570 // check if this setup option should be hidden in the setup menu
9571 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9572 setHideSetupEntry(token_info[token_nr].value);
9574 free(token_hide_text);
9577 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9578 struct TokenInfo *token_info,
9581 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9582 token_info[token_nr].text);
9585 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9589 if (!setup_file_hash)
9592 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9593 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9595 setup.touch.grid_initialized = TRUE;
9596 for (i = 0; i < 2; i++)
9598 int grid_xsize = setup.touch.grid_xsize[i];
9599 int grid_ysize = setup.touch.grid_ysize[i];
9602 // if virtual buttons are not loaded from setup file, repeat initializing
9603 // virtual buttons grid with default values later when video is initialized
9604 if (grid_xsize == -1 ||
9607 setup.touch.grid_initialized = FALSE;
9612 for (y = 0; y < grid_ysize; y++)
9614 char token_string[MAX_LINE_LEN];
9616 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9618 char *value_string = getHashEntry(setup_file_hash, token_string);
9620 if (value_string == NULL)
9623 for (x = 0; x < grid_xsize; x++)
9625 char c = value_string[x];
9627 setup.touch.grid_button[i][x][y] =
9628 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9633 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9634 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9636 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9637 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9639 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9643 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9645 setup_input = setup.input[pnr];
9646 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9648 char full_token[100];
9650 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9651 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9654 setup.input[pnr] = setup_input;
9657 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9658 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9660 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9661 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9663 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9664 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9666 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9667 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9669 setHideRelatedSetupEntries();
9672 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9676 if (!setup_file_hash)
9679 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9680 setSetupInfo(auto_setup_tokens, i,
9681 getHashEntry(setup_file_hash,
9682 auto_setup_tokens[i].text));
9685 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9689 if (!setup_file_hash)
9692 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9693 setSetupInfo(editor_cascade_setup_tokens, i,
9694 getHashEntry(setup_file_hash,
9695 editor_cascade_setup_tokens[i].text));
9698 void LoadUserNames(void)
9700 int last_user_nr = user.nr;
9703 if (global.user_names != NULL)
9705 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9706 checked_free(global.user_names[i]);
9708 checked_free(global.user_names);
9711 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9713 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9717 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9719 if (setup_file_hash)
9721 char *player_name = getHashEntry(setup_file_hash, "player_name");
9723 global.user_names[i] = getFixedUserName(player_name);
9725 freeSetupFileHash(setup_file_hash);
9728 if (global.user_names[i] == NULL)
9729 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9732 user.nr = last_user_nr;
9735 void LoadSetupFromFilename(char *filename)
9737 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9739 if (setup_file_hash)
9741 decodeSetupFileHash(setup_file_hash);
9743 freeSetupFileHash(setup_file_hash);
9747 Debug("setup", "using default setup values");
9751 static void LoadSetup_SpecialPostProcessing(void)
9753 char *player_name_new;
9755 // needed to work around problems with fixed length strings
9756 player_name_new = getFixedUserName(setup.player_name);
9757 free(setup.player_name);
9758 setup.player_name = player_name_new;
9760 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9761 if (setup.scroll_delay == FALSE)
9763 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9764 setup.scroll_delay = TRUE; // now always "on"
9767 // make sure that scroll delay value stays inside valid range
9768 setup.scroll_delay_value =
9769 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9772 void LoadSetup(void)
9776 // always start with reliable default values
9777 setSetupInfoToDefaults(&setup);
9779 // try to load setup values from default setup file
9780 filename = getDefaultSetupFilename();
9782 if (fileExists(filename))
9783 LoadSetupFromFilename(filename);
9785 // try to load setup values from user setup file
9786 filename = getSetupFilename();
9788 LoadSetupFromFilename(filename);
9790 LoadSetup_SpecialPostProcessing();
9793 void LoadSetup_AutoSetup(void)
9795 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9796 SetupFileHash *setup_file_hash = NULL;
9798 // always start with reliable default values
9799 setSetupInfoToDefaults_AutoSetup(&setup);
9801 setup_file_hash = loadSetupFileHash(filename);
9803 if (setup_file_hash)
9805 decodeSetupFileHash_AutoSetup(setup_file_hash);
9807 freeSetupFileHash(setup_file_hash);
9813 void LoadSetup_EditorCascade(void)
9815 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9816 SetupFileHash *setup_file_hash = NULL;
9818 // always start with reliable default values
9819 setSetupInfoToDefaults_EditorCascade(&setup);
9821 setup_file_hash = loadSetupFileHash(filename);
9823 if (setup_file_hash)
9825 decodeSetupFileHash_EditorCascade(setup_file_hash);
9827 freeSetupFileHash(setup_file_hash);
9833 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9836 char mapping_guid[MAX_LINE_LEN];
9837 char *mapping_start, *mapping_end;
9839 // get GUID from game controller mapping line: copy complete line
9840 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9841 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9843 // get GUID from game controller mapping line: cut after GUID part
9844 mapping_start = strchr(mapping_guid, ',');
9845 if (mapping_start != NULL)
9846 *mapping_start = '\0';
9848 // cut newline from game controller mapping line
9849 mapping_end = strchr(mapping_line, '\n');
9850 if (mapping_end != NULL)
9851 *mapping_end = '\0';
9853 // add mapping entry to game controller mappings hash
9854 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9857 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9862 if (!(file = fopen(filename, MODE_READ)))
9864 Warn("cannot read game controller mappings file '%s'", filename);
9871 char line[MAX_LINE_LEN];
9873 if (!fgets(line, MAX_LINE_LEN, file))
9876 addGameControllerMappingToHash(mappings_hash, line);
9882 void SaveSetup(void)
9884 char *filename = getSetupFilename();
9888 InitUserDataDirectory();
9890 if (!(file = fopen(filename, MODE_WRITE)))
9892 Warn("cannot write setup file '%s'", filename);
9897 fprintFileHeader(file, SETUP_FILENAME);
9899 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9901 // just to make things nicer :)
9902 if (global_setup_tokens[i].value == &setup.multiple_users ||
9903 global_setup_tokens[i].value == &setup.sound ||
9904 global_setup_tokens[i].value == &setup.graphics_set ||
9905 global_setup_tokens[i].value == &setup.volume_simple ||
9906 global_setup_tokens[i].value == &setup.network_mode ||
9907 global_setup_tokens[i].value == &setup.touch.control_type ||
9908 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9909 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9910 fprintf(file, "\n");
9912 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9915 for (i = 0; i < 2; i++)
9917 int grid_xsize = setup.touch.grid_xsize[i];
9918 int grid_ysize = setup.touch.grid_ysize[i];
9921 fprintf(file, "\n");
9923 for (y = 0; y < grid_ysize; y++)
9925 char token_string[MAX_LINE_LEN];
9926 char value_string[MAX_LINE_LEN];
9928 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9930 for (x = 0; x < grid_xsize; x++)
9932 char c = setup.touch.grid_button[i][x][y];
9934 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9937 value_string[grid_xsize] = '\0';
9939 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9943 fprintf(file, "\n");
9944 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9945 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9947 fprintf(file, "\n");
9948 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9949 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9951 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9955 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9956 fprintf(file, "\n");
9958 setup_input = setup.input[pnr];
9959 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9960 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9963 fprintf(file, "\n");
9964 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9965 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9967 // (internal setup values not saved to user setup file)
9969 fprintf(file, "\n");
9970 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9971 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
9972 setup.debug.xsn_mode != AUTO)
9973 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9975 fprintf(file, "\n");
9976 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9977 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9981 SetFilePermissions(filename, PERMS_PRIVATE);
9984 void SaveSetup_AutoSetup(void)
9986 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9990 InitUserDataDirectory();
9992 if (!(file = fopen(filename, MODE_WRITE)))
9994 Warn("cannot write auto setup file '%s'", filename);
10001 fprintFileHeader(file, AUTOSETUP_FILENAME);
10003 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10004 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10008 SetFilePermissions(filename, PERMS_PRIVATE);
10013 void SaveSetup_EditorCascade(void)
10015 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10019 InitUserDataDirectory();
10021 if (!(file = fopen(filename, MODE_WRITE)))
10023 Warn("cannot write editor cascade state file '%s'", filename);
10030 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10032 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10033 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10037 SetFilePermissions(filename, PERMS_PRIVATE);
10042 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10047 if (!(file = fopen(filename, MODE_WRITE)))
10049 Warn("cannot write game controller mappings file '%s'", filename);
10054 BEGIN_HASH_ITERATION(mappings_hash, itr)
10056 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10058 END_HASH_ITERATION(mappings_hash, itr)
10063 void SaveSetup_AddGameControllerMapping(char *mapping)
10065 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10066 SetupFileHash *mappings_hash = newSetupFileHash();
10068 InitUserDataDirectory();
10070 // load existing personal game controller mappings
10071 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10073 // add new mapping to personal game controller mappings
10074 addGameControllerMappingToHash(mappings_hash, mapping);
10076 // save updated personal game controller mappings
10077 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10079 freeSetupFileHash(mappings_hash);
10083 void LoadCustomElementDescriptions(void)
10085 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10086 SetupFileHash *setup_file_hash;
10089 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10091 if (element_info[i].custom_description != NULL)
10093 free(element_info[i].custom_description);
10094 element_info[i].custom_description = NULL;
10098 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10101 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10103 char *token = getStringCat2(element_info[i].token_name, ".name");
10104 char *value = getHashEntry(setup_file_hash, token);
10107 element_info[i].custom_description = getStringCopy(value);
10112 freeSetupFileHash(setup_file_hash);
10115 static int getElementFromToken(char *token)
10117 char *value = getHashEntry(element_token_hash, token);
10120 return atoi(value);
10122 Warn("unknown element token '%s'", token);
10124 return EL_UNDEFINED;
10127 void FreeGlobalAnimEventInfo(void)
10129 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10131 if (gaei->event_list == NULL)
10136 for (i = 0; i < gaei->num_event_lists; i++)
10138 checked_free(gaei->event_list[i]->event_value);
10139 checked_free(gaei->event_list[i]);
10142 checked_free(gaei->event_list);
10144 gaei->event_list = NULL;
10145 gaei->num_event_lists = 0;
10148 static int AddGlobalAnimEventList(void)
10150 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10151 int list_pos = gaei->num_event_lists++;
10153 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10154 sizeof(struct GlobalAnimEventListInfo *));
10156 gaei->event_list[list_pos] =
10157 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10159 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10161 gaeli->event_value = NULL;
10162 gaeli->num_event_values = 0;
10167 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10169 // do not add empty global animation events
10170 if (event_value == ANIM_EVENT_NONE)
10173 // if list position is undefined, create new list
10174 if (list_pos == ANIM_EVENT_UNDEFINED)
10175 list_pos = AddGlobalAnimEventList();
10177 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10178 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10179 int value_pos = gaeli->num_event_values++;
10181 gaeli->event_value = checked_realloc(gaeli->event_value,
10182 gaeli->num_event_values * sizeof(int *));
10184 gaeli->event_value[value_pos] = event_value;
10189 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10191 if (list_pos == ANIM_EVENT_UNDEFINED)
10192 return ANIM_EVENT_NONE;
10194 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10195 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10197 return gaeli->event_value[value_pos];
10200 int GetGlobalAnimEventValueCount(int list_pos)
10202 if (list_pos == ANIM_EVENT_UNDEFINED)
10205 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10206 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10208 return gaeli->num_event_values;
10211 // This function checks if a string <s> of the format "string1, string2, ..."
10212 // exactly contains a string <s_contained>.
10214 static boolean string_has_parameter(char *s, char *s_contained)
10218 if (s == NULL || s_contained == NULL)
10221 if (strlen(s_contained) > strlen(s))
10224 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10226 char next_char = s[strlen(s_contained)];
10228 // check if next character is delimiter or whitespace
10229 return (next_char == ',' || next_char == '\0' ||
10230 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10233 // check if string contains another parameter string after a comma
10234 substring = strchr(s, ',');
10235 if (substring == NULL) // string does not contain a comma
10238 // advance string pointer to next character after the comma
10241 // skip potential whitespaces after the comma
10242 while (*substring == ' ' || *substring == '\t')
10245 return string_has_parameter(substring, s_contained);
10248 static int get_anim_parameter_value(char *s)
10250 int event_value[] =
10258 char *pattern_1[] =
10266 char *pattern_2 = ".part_";
10267 char *matching_char = NULL;
10269 int pattern_1_len = 0;
10270 int result = ANIM_EVENT_NONE;
10273 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10275 matching_char = strstr(s_ptr, pattern_1[i]);
10276 pattern_1_len = strlen(pattern_1[i]);
10277 result = event_value[i];
10279 if (matching_char != NULL)
10283 if (matching_char == NULL)
10284 return ANIM_EVENT_NONE;
10286 s_ptr = matching_char + pattern_1_len;
10288 // check for main animation number ("anim_X" or "anim_XX")
10289 if (*s_ptr >= '0' && *s_ptr <= '9')
10291 int gic_anim_nr = (*s_ptr++ - '0');
10293 if (*s_ptr >= '0' && *s_ptr <= '9')
10294 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10296 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10297 return ANIM_EVENT_NONE;
10299 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10303 // invalid main animation number specified
10305 return ANIM_EVENT_NONE;
10308 // check for animation part number ("part_X" or "part_XX") (optional)
10309 if (strPrefix(s_ptr, pattern_2))
10311 s_ptr += strlen(pattern_2);
10313 if (*s_ptr >= '0' && *s_ptr <= '9')
10315 int gic_part_nr = (*s_ptr++ - '0');
10317 if (*s_ptr >= '0' && *s_ptr <= '9')
10318 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10320 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10321 return ANIM_EVENT_NONE;
10323 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10327 // invalid animation part number specified
10329 return ANIM_EVENT_NONE;
10333 // discard result if next character is neither delimiter nor whitespace
10334 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10335 *s_ptr == ' ' || *s_ptr == '\t'))
10336 return ANIM_EVENT_NONE;
10341 static int get_anim_parameter_values(char *s)
10343 int list_pos = ANIM_EVENT_UNDEFINED;
10344 int event_value = ANIM_EVENT_DEFAULT;
10346 if (string_has_parameter(s, "any"))
10347 event_value |= ANIM_EVENT_ANY;
10349 if (string_has_parameter(s, "click:self") ||
10350 string_has_parameter(s, "click") ||
10351 string_has_parameter(s, "self"))
10352 event_value |= ANIM_EVENT_SELF;
10354 if (string_has_parameter(s, "unclick:any"))
10355 event_value |= ANIM_EVENT_UNCLICK_ANY;
10357 // if animation event found, add it to global animation event list
10358 if (event_value != ANIM_EVENT_NONE)
10359 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10363 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10364 event_value = get_anim_parameter_value(s);
10366 // if animation event found, add it to global animation event list
10367 if (event_value != ANIM_EVENT_NONE)
10368 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10370 // continue with next part of the string, starting with next comma
10371 s = strchr(s + 1, ',');
10377 static int get_anim_action_parameter_value(char *token)
10379 // check most common default case first to massively speed things up
10380 if (strEqual(token, ARG_UNDEFINED))
10381 return ANIM_EVENT_ACTION_NONE;
10383 int result = getImageIDFromToken(token);
10387 char *gfx_token = getStringCat2("gfx.", token);
10389 result = getImageIDFromToken(gfx_token);
10391 checked_free(gfx_token);
10396 Key key = getKeyFromX11KeyName(token);
10398 if (key != KSYM_UNDEFINED)
10399 result = -(int)key;
10403 result = ANIM_EVENT_ACTION_NONE;
10408 int get_parameter_value(char *value_raw, char *suffix, int type)
10410 char *value = getStringToLower(value_raw);
10411 int result = 0; // probably a save default value
10413 if (strEqual(suffix, ".direction"))
10415 result = (strEqual(value, "left") ? MV_LEFT :
10416 strEqual(value, "right") ? MV_RIGHT :
10417 strEqual(value, "up") ? MV_UP :
10418 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10420 else if (strEqual(suffix, ".position"))
10422 result = (strEqual(value, "left") ? POS_LEFT :
10423 strEqual(value, "right") ? POS_RIGHT :
10424 strEqual(value, "top") ? POS_TOP :
10425 strEqual(value, "upper") ? POS_UPPER :
10426 strEqual(value, "middle") ? POS_MIDDLE :
10427 strEqual(value, "lower") ? POS_LOWER :
10428 strEqual(value, "bottom") ? POS_BOTTOM :
10429 strEqual(value, "any") ? POS_ANY :
10430 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10432 else if (strEqual(suffix, ".align"))
10434 result = (strEqual(value, "left") ? ALIGN_LEFT :
10435 strEqual(value, "right") ? ALIGN_RIGHT :
10436 strEqual(value, "center") ? ALIGN_CENTER :
10437 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10439 else if (strEqual(suffix, ".valign"))
10441 result = (strEqual(value, "top") ? VALIGN_TOP :
10442 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10443 strEqual(value, "middle") ? VALIGN_MIDDLE :
10444 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10446 else if (strEqual(suffix, ".anim_mode"))
10448 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10449 string_has_parameter(value, "loop") ? ANIM_LOOP :
10450 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10451 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10452 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10453 string_has_parameter(value, "random") ? ANIM_RANDOM :
10454 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10455 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10456 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10457 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10458 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10459 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10460 string_has_parameter(value, "all") ? ANIM_ALL :
10463 if (string_has_parameter(value, "once"))
10464 result |= ANIM_ONCE;
10466 if (string_has_parameter(value, "reverse"))
10467 result |= ANIM_REVERSE;
10469 if (string_has_parameter(value, "opaque_player"))
10470 result |= ANIM_OPAQUE_PLAYER;
10472 if (string_has_parameter(value, "static_panel"))
10473 result |= ANIM_STATIC_PANEL;
10475 else if (strEqual(suffix, ".init_event") ||
10476 strEqual(suffix, ".anim_event"))
10478 result = get_anim_parameter_values(value);
10480 else if (strEqual(suffix, ".init_delay_action") ||
10481 strEqual(suffix, ".anim_delay_action") ||
10482 strEqual(suffix, ".post_delay_action") ||
10483 strEqual(suffix, ".init_event_action") ||
10484 strEqual(suffix, ".anim_event_action"))
10486 result = get_anim_action_parameter_value(value_raw);
10488 else if (strEqual(suffix, ".class"))
10490 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10491 get_hash_from_key(value));
10493 else if (strEqual(suffix, ".style"))
10495 result = STYLE_DEFAULT;
10497 if (string_has_parameter(value, "accurate_borders"))
10498 result |= STYLE_ACCURATE_BORDERS;
10500 if (string_has_parameter(value, "inner_corners"))
10501 result |= STYLE_INNER_CORNERS;
10503 if (string_has_parameter(value, "reverse"))
10504 result |= STYLE_REVERSE;
10506 if (string_has_parameter(value, "leftmost_position"))
10507 result |= STYLE_LEFTMOST_POSITION;
10509 if (string_has_parameter(value, "block_clicks"))
10510 result |= STYLE_BLOCK;
10512 if (string_has_parameter(value, "passthrough_clicks"))
10513 result |= STYLE_PASSTHROUGH;
10515 if (string_has_parameter(value, "multiple_actions"))
10516 result |= STYLE_MULTIPLE_ACTIONS;
10518 else if (strEqual(suffix, ".fade_mode"))
10520 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10521 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10522 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10523 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10524 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10525 FADE_MODE_DEFAULT);
10527 else if (strEqual(suffix, ".auto_delay_unit"))
10529 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10530 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10531 AUTO_DELAY_UNIT_DEFAULT);
10533 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10535 result = gfx.get_font_from_token_function(value);
10537 else // generic parameter of type integer or boolean
10539 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10540 type == TYPE_INTEGER ? get_integer_from_string(value) :
10541 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10542 ARG_UNDEFINED_VALUE);
10550 static int get_token_parameter_value(char *token, char *value_raw)
10554 if (token == NULL || value_raw == NULL)
10555 return ARG_UNDEFINED_VALUE;
10557 suffix = strrchr(token, '.');
10558 if (suffix == NULL)
10561 if (strEqual(suffix, ".element"))
10562 return getElementFromToken(value_raw);
10564 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10565 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10568 void InitMenuDesignSettings_Static(void)
10572 // always start with reliable default values from static default config
10573 for (i = 0; image_config_vars[i].token != NULL; i++)
10575 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10578 *image_config_vars[i].value =
10579 get_token_parameter_value(image_config_vars[i].token, value);
10583 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10587 // the following initializes hierarchical values from static configuration
10589 // special case: initialize "ARG_DEFAULT" values in static default config
10590 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10591 titlescreen_initial_first_default.fade_mode =
10592 title_initial_first_default.fade_mode;
10593 titlescreen_initial_first_default.fade_delay =
10594 title_initial_first_default.fade_delay;
10595 titlescreen_initial_first_default.post_delay =
10596 title_initial_first_default.post_delay;
10597 titlescreen_initial_first_default.auto_delay =
10598 title_initial_first_default.auto_delay;
10599 titlescreen_initial_first_default.auto_delay_unit =
10600 title_initial_first_default.auto_delay_unit;
10601 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10602 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10603 titlescreen_first_default.post_delay = title_first_default.post_delay;
10604 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10605 titlescreen_first_default.auto_delay_unit =
10606 title_first_default.auto_delay_unit;
10607 titlemessage_initial_first_default.fade_mode =
10608 title_initial_first_default.fade_mode;
10609 titlemessage_initial_first_default.fade_delay =
10610 title_initial_first_default.fade_delay;
10611 titlemessage_initial_first_default.post_delay =
10612 title_initial_first_default.post_delay;
10613 titlemessage_initial_first_default.auto_delay =
10614 title_initial_first_default.auto_delay;
10615 titlemessage_initial_first_default.auto_delay_unit =
10616 title_initial_first_default.auto_delay_unit;
10617 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10618 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10619 titlemessage_first_default.post_delay = title_first_default.post_delay;
10620 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10621 titlemessage_first_default.auto_delay_unit =
10622 title_first_default.auto_delay_unit;
10624 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10625 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10626 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10627 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10628 titlescreen_initial_default.auto_delay_unit =
10629 title_initial_default.auto_delay_unit;
10630 titlescreen_default.fade_mode = title_default.fade_mode;
10631 titlescreen_default.fade_delay = title_default.fade_delay;
10632 titlescreen_default.post_delay = title_default.post_delay;
10633 titlescreen_default.auto_delay = title_default.auto_delay;
10634 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10635 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10636 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10637 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10638 titlemessage_initial_default.auto_delay_unit =
10639 title_initial_default.auto_delay_unit;
10640 titlemessage_default.fade_mode = title_default.fade_mode;
10641 titlemessage_default.fade_delay = title_default.fade_delay;
10642 titlemessage_default.post_delay = title_default.post_delay;
10643 titlemessage_default.auto_delay = title_default.auto_delay;
10644 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10646 // special case: initialize "ARG_DEFAULT" values in static default config
10647 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10648 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10650 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10651 titlescreen_first[i] = titlescreen_first_default;
10652 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10653 titlemessage_first[i] = titlemessage_first_default;
10655 titlescreen_initial[i] = titlescreen_initial_default;
10656 titlescreen[i] = titlescreen_default;
10657 titlemessage_initial[i] = titlemessage_initial_default;
10658 titlemessage[i] = titlemessage_default;
10661 // special case: initialize "ARG_DEFAULT" values in static default config
10662 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10663 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10665 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10668 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10669 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10670 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10673 // special case: initialize "ARG_DEFAULT" values in static default config
10674 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10675 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10677 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10678 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10679 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10681 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10684 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10688 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10692 struct XY *dst, *src;
10694 game_buttons_xy[] =
10696 { &game.button.save, &game.button.stop },
10697 { &game.button.pause2, &game.button.pause },
10698 { &game.button.load, &game.button.play },
10699 { &game.button.undo, &game.button.stop },
10700 { &game.button.redo, &game.button.play },
10706 // special case: initialize later added SETUP list size from LEVELS value
10707 if (menu.list_size[GAME_MODE_SETUP] == -1)
10708 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10710 // set default position for snapshot buttons to stop/pause/play buttons
10711 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10712 if ((*game_buttons_xy[i].dst).x == -1 &&
10713 (*game_buttons_xy[i].dst).y == -1)
10714 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10716 // --------------------------------------------------------------------------
10717 // dynamic viewports (including playfield margins, borders and alignments)
10718 // --------------------------------------------------------------------------
10720 // dynamic viewports currently only supported for landscape mode
10721 int display_width = MAX(video.display_width, video.display_height);
10722 int display_height = MIN(video.display_width, video.display_height);
10724 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10726 struct RectWithBorder *vp_window = &viewport.window[i];
10727 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10728 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10729 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10730 boolean dynamic_window_width = (vp_window->min_width != -1);
10731 boolean dynamic_window_height = (vp_window->min_height != -1);
10732 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10733 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10735 // adjust window size if min/max width/height is specified
10737 if (vp_window->min_width != -1)
10739 int window_width = display_width;
10741 // when using static window height, use aspect ratio of display
10742 if (vp_window->min_height == -1)
10743 window_width = vp_window->height * display_width / display_height;
10745 vp_window->width = MAX(vp_window->min_width, window_width);
10748 if (vp_window->min_height != -1)
10750 int window_height = display_height;
10752 // when using static window width, use aspect ratio of display
10753 if (vp_window->min_width == -1)
10754 window_height = vp_window->width * display_height / display_width;
10756 vp_window->height = MAX(vp_window->min_height, window_height);
10759 if (vp_window->max_width != -1)
10760 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10762 if (vp_window->max_height != -1)
10763 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10765 int playfield_width = vp_window->width;
10766 int playfield_height = vp_window->height;
10768 // adjust playfield size and position according to specified margins
10770 playfield_width -= vp_playfield->margin_left;
10771 playfield_width -= vp_playfield->margin_right;
10773 playfield_height -= vp_playfield->margin_top;
10774 playfield_height -= vp_playfield->margin_bottom;
10776 // adjust playfield size if min/max width/height is specified
10778 if (vp_playfield->min_width != -1)
10779 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10781 if (vp_playfield->min_height != -1)
10782 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10784 if (vp_playfield->max_width != -1)
10785 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10787 if (vp_playfield->max_height != -1)
10788 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10790 // adjust playfield position according to specified alignment
10792 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10793 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10794 else if (vp_playfield->align == ALIGN_CENTER)
10795 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10796 else if (vp_playfield->align == ALIGN_RIGHT)
10797 vp_playfield->x += playfield_width - vp_playfield->width;
10799 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10800 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10801 else if (vp_playfield->valign == VALIGN_MIDDLE)
10802 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10803 else if (vp_playfield->valign == VALIGN_BOTTOM)
10804 vp_playfield->y += playfield_height - vp_playfield->height;
10806 vp_playfield->x += vp_playfield->margin_left;
10807 vp_playfield->y += vp_playfield->margin_top;
10809 // adjust individual playfield borders if only default border is specified
10811 if (vp_playfield->border_left == -1)
10812 vp_playfield->border_left = vp_playfield->border_size;
10813 if (vp_playfield->border_right == -1)
10814 vp_playfield->border_right = vp_playfield->border_size;
10815 if (vp_playfield->border_top == -1)
10816 vp_playfield->border_top = vp_playfield->border_size;
10817 if (vp_playfield->border_bottom == -1)
10818 vp_playfield->border_bottom = vp_playfield->border_size;
10820 // set dynamic playfield borders if borders are specified as undefined
10821 // (but only if window size was dynamic and playfield size was static)
10823 if (dynamic_window_width && !dynamic_playfield_width)
10825 if (vp_playfield->border_left == -1)
10827 vp_playfield->border_left = (vp_playfield->x -
10828 vp_playfield->margin_left);
10829 vp_playfield->x -= vp_playfield->border_left;
10830 vp_playfield->width += vp_playfield->border_left;
10833 if (vp_playfield->border_right == -1)
10835 vp_playfield->border_right = (vp_window->width -
10837 vp_playfield->width -
10838 vp_playfield->margin_right);
10839 vp_playfield->width += vp_playfield->border_right;
10843 if (dynamic_window_height && !dynamic_playfield_height)
10845 if (vp_playfield->border_top == -1)
10847 vp_playfield->border_top = (vp_playfield->y -
10848 vp_playfield->margin_top);
10849 vp_playfield->y -= vp_playfield->border_top;
10850 vp_playfield->height += vp_playfield->border_top;
10853 if (vp_playfield->border_bottom == -1)
10855 vp_playfield->border_bottom = (vp_window->height -
10857 vp_playfield->height -
10858 vp_playfield->margin_bottom);
10859 vp_playfield->height += vp_playfield->border_bottom;
10863 // adjust playfield size to be a multiple of a defined alignment tile size
10865 int align_size = vp_playfield->align_size;
10866 int playfield_xtiles = vp_playfield->width / align_size;
10867 int playfield_ytiles = vp_playfield->height / align_size;
10868 int playfield_width_corrected = playfield_xtiles * align_size;
10869 int playfield_height_corrected = playfield_ytiles * align_size;
10870 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10871 i == GFX_SPECIAL_ARG_EDITOR);
10873 if (is_playfield_mode &&
10874 dynamic_playfield_width &&
10875 vp_playfield->width != playfield_width_corrected)
10877 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10879 vp_playfield->width = playfield_width_corrected;
10881 if (vp_playfield->align == ALIGN_LEFT)
10883 vp_playfield->border_left += playfield_xdiff;
10885 else if (vp_playfield->align == ALIGN_RIGHT)
10887 vp_playfield->border_right += playfield_xdiff;
10889 else if (vp_playfield->align == ALIGN_CENTER)
10891 int border_left_diff = playfield_xdiff / 2;
10892 int border_right_diff = playfield_xdiff - border_left_diff;
10894 vp_playfield->border_left += border_left_diff;
10895 vp_playfield->border_right += border_right_diff;
10899 if (is_playfield_mode &&
10900 dynamic_playfield_height &&
10901 vp_playfield->height != playfield_height_corrected)
10903 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10905 vp_playfield->height = playfield_height_corrected;
10907 if (vp_playfield->valign == VALIGN_TOP)
10909 vp_playfield->border_top += playfield_ydiff;
10911 else if (vp_playfield->align == VALIGN_BOTTOM)
10913 vp_playfield->border_right += playfield_ydiff;
10915 else if (vp_playfield->align == VALIGN_MIDDLE)
10917 int border_top_diff = playfield_ydiff / 2;
10918 int border_bottom_diff = playfield_ydiff - border_top_diff;
10920 vp_playfield->border_top += border_top_diff;
10921 vp_playfield->border_bottom += border_bottom_diff;
10925 // adjust door positions according to specified alignment
10927 for (j = 0; j < 2; j++)
10929 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10931 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10932 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10933 else if (vp_door->align == ALIGN_CENTER)
10934 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10935 else if (vp_door->align == ALIGN_RIGHT)
10936 vp_door->x += vp_window->width - vp_door->width;
10938 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10939 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10940 else if (vp_door->valign == VALIGN_MIDDLE)
10941 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10942 else if (vp_door->valign == VALIGN_BOTTOM)
10943 vp_door->y += vp_window->height - vp_door->height;
10948 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10952 struct XYTileSize *dst, *src;
10955 editor_buttons_xy[] =
10958 &editor.button.element_left, &editor.palette.element_left,
10959 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10962 &editor.button.element_middle, &editor.palette.element_middle,
10963 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10966 &editor.button.element_right, &editor.palette.element_right,
10967 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10974 // set default position for element buttons to element graphics
10975 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10977 if ((*editor_buttons_xy[i].dst).x == -1 &&
10978 (*editor_buttons_xy[i].dst).y == -1)
10980 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10982 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10984 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10988 // adjust editor palette rows and columns if specified to be dynamic
10990 if (editor.palette.cols == -1)
10992 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10993 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10994 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10996 editor.palette.cols = (vp_width - sc_width) / bt_width;
10998 if (editor.palette.x == -1)
11000 int palette_width = editor.palette.cols * bt_width + sc_width;
11002 editor.palette.x = (vp_width - palette_width) / 2;
11006 if (editor.palette.rows == -1)
11008 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11009 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11010 int tx_height = getFontHeight(FONT_TEXT_2);
11012 editor.palette.rows = (vp_height - tx_height) / bt_height;
11014 if (editor.palette.y == -1)
11016 int palette_height = editor.palette.rows * bt_height + tx_height;
11018 editor.palette.y = (vp_height - palette_height) / 2;
11023 static void LoadMenuDesignSettingsFromFilename(char *filename)
11025 static struct TitleFadingInfo tfi;
11026 static struct TitleMessageInfo tmi;
11027 static struct TokenInfo title_tokens[] =
11029 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11030 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11031 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11032 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11033 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11037 static struct TokenInfo titlemessage_tokens[] =
11039 { TYPE_INTEGER, &tmi.x, ".x" },
11040 { TYPE_INTEGER, &tmi.y, ".y" },
11041 { TYPE_INTEGER, &tmi.width, ".width" },
11042 { TYPE_INTEGER, &tmi.height, ".height" },
11043 { TYPE_INTEGER, &tmi.chars, ".chars" },
11044 { TYPE_INTEGER, &tmi.lines, ".lines" },
11045 { TYPE_INTEGER, &tmi.align, ".align" },
11046 { TYPE_INTEGER, &tmi.valign, ".valign" },
11047 { TYPE_INTEGER, &tmi.font, ".font" },
11048 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11049 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11050 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11051 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11052 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11053 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11054 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11055 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11056 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11062 struct TitleFadingInfo *info;
11067 // initialize first titles from "enter screen" definitions, if defined
11068 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11069 { &title_first_default, "menu.enter_screen.TITLE" },
11071 // initialize title screens from "next screen" definitions, if defined
11072 { &title_initial_default, "menu.next_screen.TITLE" },
11073 { &title_default, "menu.next_screen.TITLE" },
11079 struct TitleMessageInfo *array;
11082 titlemessage_arrays[] =
11084 // initialize first titles from "enter screen" definitions, if defined
11085 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11086 { titlescreen_first, "menu.enter_screen.TITLE" },
11087 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11088 { titlemessage_first, "menu.enter_screen.TITLE" },
11090 // initialize titles from "next screen" definitions, if defined
11091 { titlescreen_initial, "menu.next_screen.TITLE" },
11092 { titlescreen, "menu.next_screen.TITLE" },
11093 { titlemessage_initial, "menu.next_screen.TITLE" },
11094 { titlemessage, "menu.next_screen.TITLE" },
11096 // overwrite titles with title definitions, if defined
11097 { titlescreen_initial_first, "[title_initial]" },
11098 { titlescreen_first, "[title]" },
11099 { titlemessage_initial_first, "[title_initial]" },
11100 { titlemessage_first, "[title]" },
11102 { titlescreen_initial, "[title_initial]" },
11103 { titlescreen, "[title]" },
11104 { titlemessage_initial, "[title_initial]" },
11105 { titlemessage, "[title]" },
11107 // overwrite titles with title screen/message definitions, if defined
11108 { titlescreen_initial_first, "[titlescreen_initial]" },
11109 { titlescreen_first, "[titlescreen]" },
11110 { titlemessage_initial_first, "[titlemessage_initial]" },
11111 { titlemessage_first, "[titlemessage]" },
11113 { titlescreen_initial, "[titlescreen_initial]" },
11114 { titlescreen, "[titlescreen]" },
11115 { titlemessage_initial, "[titlemessage_initial]" },
11116 { titlemessage, "[titlemessage]" },
11120 SetupFileHash *setup_file_hash;
11123 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11126 // the following initializes hierarchical values from dynamic configuration
11128 // special case: initialize with default values that may be overwritten
11129 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11130 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11132 struct TokenIntPtrInfo menu_config[] =
11134 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11135 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11136 { "menu.list_size", &menu.list_size[i] }
11139 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11141 char *token = menu_config[j].token;
11142 char *value = getHashEntry(setup_file_hash, token);
11145 *menu_config[j].value = get_integer_from_string(value);
11149 // special case: initialize with default values that may be overwritten
11150 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11151 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11153 struct TokenIntPtrInfo menu_config[] =
11155 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11156 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11157 { "menu.list_size.INFO", &menu.list_size_info[i] }
11160 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11162 char *token = menu_config[j].token;
11163 char *value = getHashEntry(setup_file_hash, token);
11166 *menu_config[j].value = get_integer_from_string(value);
11170 // special case: initialize with default values that may be overwritten
11171 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11172 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11174 struct TokenIntPtrInfo menu_config[] =
11176 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11177 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11180 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11182 char *token = menu_config[j].token;
11183 char *value = getHashEntry(setup_file_hash, token);
11186 *menu_config[j].value = get_integer_from_string(value);
11190 // special case: initialize with default values that may be overwritten
11191 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11192 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11194 struct TokenIntPtrInfo menu_config[] =
11196 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11197 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11198 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11199 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11200 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11201 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11202 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11203 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11204 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11207 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11209 char *token = menu_config[j].token;
11210 char *value = getHashEntry(setup_file_hash, token);
11213 *menu_config[j].value = get_integer_from_string(value);
11217 // special case: initialize with default values that may be overwritten
11218 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11219 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11221 struct TokenIntPtrInfo menu_config[] =
11223 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11224 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11225 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11226 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11227 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11228 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11229 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11230 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11231 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11234 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11236 char *token = menu_config[j].token;
11237 char *value = getHashEntry(setup_file_hash, token);
11240 *menu_config[j].value = get_token_parameter_value(token, value);
11244 // special case: initialize with default values that may be overwritten
11245 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11246 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11250 char *token_prefix;
11251 struct RectWithBorder *struct_ptr;
11255 { "viewport.window", &viewport.window[i] },
11256 { "viewport.playfield", &viewport.playfield[i] },
11257 { "viewport.door_1", &viewport.door_1[i] },
11258 { "viewport.door_2", &viewport.door_2[i] }
11261 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11263 struct TokenIntPtrInfo vp_config[] =
11265 { ".x", &vp_struct[j].struct_ptr->x },
11266 { ".y", &vp_struct[j].struct_ptr->y },
11267 { ".width", &vp_struct[j].struct_ptr->width },
11268 { ".height", &vp_struct[j].struct_ptr->height },
11269 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11270 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11271 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11272 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11273 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11274 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11275 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11276 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11277 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11278 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11279 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11280 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11281 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11282 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11283 { ".align", &vp_struct[j].struct_ptr->align },
11284 { ".valign", &vp_struct[j].struct_ptr->valign }
11287 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11289 char *token = getStringCat2(vp_struct[j].token_prefix,
11290 vp_config[k].token);
11291 char *value = getHashEntry(setup_file_hash, token);
11294 *vp_config[k].value = get_token_parameter_value(token, value);
11301 // special case: initialize with default values that may be overwritten
11302 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11303 for (i = 0; title_info[i].info != NULL; i++)
11305 struct TitleFadingInfo *info = title_info[i].info;
11306 char *base_token = title_info[i].text;
11308 for (j = 0; title_tokens[j].type != -1; j++)
11310 char *token = getStringCat2(base_token, title_tokens[j].text);
11311 char *value = getHashEntry(setup_file_hash, token);
11315 int parameter_value = get_token_parameter_value(token, value);
11319 *(int *)title_tokens[j].value = (int)parameter_value;
11328 // special case: initialize with default values that may be overwritten
11329 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11330 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11332 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11333 char *base_token = titlemessage_arrays[i].text;
11335 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11337 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11338 char *value = getHashEntry(setup_file_hash, token);
11342 int parameter_value = get_token_parameter_value(token, value);
11344 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11348 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11349 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11351 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11361 // special case: check if network and preview player positions are redefined,
11362 // to compare this later against the main menu level preview being redefined
11363 struct TokenIntPtrInfo menu_config_players[] =
11365 { "main.network_players.x", &menu.main.network_players.redefined },
11366 { "main.network_players.y", &menu.main.network_players.redefined },
11367 { "main.preview_players.x", &menu.main.preview_players.redefined },
11368 { "main.preview_players.y", &menu.main.preview_players.redefined },
11369 { "preview.x", &preview.redefined },
11370 { "preview.y", &preview.redefined }
11373 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11374 *menu_config_players[i].value = FALSE;
11376 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11377 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11378 *menu_config_players[i].value = TRUE;
11380 // read (and overwrite with) values that may be specified in config file
11381 for (i = 0; image_config_vars[i].token != NULL; i++)
11383 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11385 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11386 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11387 *image_config_vars[i].value =
11388 get_token_parameter_value(image_config_vars[i].token, value);
11391 freeSetupFileHash(setup_file_hash);
11394 void LoadMenuDesignSettings(void)
11396 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11398 InitMenuDesignSettings_Static();
11399 InitMenuDesignSettings_SpecialPreProcessing();
11401 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11403 // first look for special settings configured in level series config
11404 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11406 if (fileExists(filename_base))
11407 LoadMenuDesignSettingsFromFilename(filename_base);
11410 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11412 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11413 LoadMenuDesignSettingsFromFilename(filename_local);
11415 InitMenuDesignSettings_SpecialPostProcessing();
11418 void LoadMenuDesignSettings_AfterGraphics(void)
11420 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11423 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11425 char *filename = getEditorSetupFilename();
11426 SetupFileList *setup_file_list, *list;
11427 SetupFileHash *element_hash;
11428 int num_unknown_tokens = 0;
11431 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11434 element_hash = newSetupFileHash();
11436 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11437 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11439 // determined size may be larger than needed (due to unknown elements)
11441 for (list = setup_file_list; list != NULL; list = list->next)
11444 // add space for up to 3 more elements for padding that may be needed
11445 *num_elements += 3;
11447 // free memory for old list of elements, if needed
11448 checked_free(*elements);
11450 // allocate memory for new list of elements
11451 *elements = checked_malloc(*num_elements * sizeof(int));
11454 for (list = setup_file_list; list != NULL; list = list->next)
11456 char *value = getHashEntry(element_hash, list->token);
11458 if (value == NULL) // try to find obsolete token mapping
11460 char *mapped_token = get_mapped_token(list->token);
11462 if (mapped_token != NULL)
11464 value = getHashEntry(element_hash, mapped_token);
11466 free(mapped_token);
11472 (*elements)[(*num_elements)++] = atoi(value);
11476 if (num_unknown_tokens == 0)
11479 Warn("unknown token(s) found in config file:");
11480 Warn("- config file: '%s'", filename);
11482 num_unknown_tokens++;
11485 Warn("- token: '%s'", list->token);
11489 if (num_unknown_tokens > 0)
11492 while (*num_elements % 4) // pad with empty elements, if needed
11493 (*elements)[(*num_elements)++] = EL_EMPTY;
11495 freeSetupFileList(setup_file_list);
11496 freeSetupFileHash(element_hash);
11499 for (i = 0; i < *num_elements; i++)
11500 Debug("editor", "element '%s' [%d]\n",
11501 element_info[(*elements)[i]].token_name, (*elements)[i]);
11505 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11508 SetupFileHash *setup_file_hash = NULL;
11509 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11510 char *filename_music, *filename_prefix, *filename_info;
11516 token_to_value_ptr[] =
11518 { "title_header", &tmp_music_file_info.title_header },
11519 { "artist_header", &tmp_music_file_info.artist_header },
11520 { "album_header", &tmp_music_file_info.album_header },
11521 { "year_header", &tmp_music_file_info.year_header },
11523 { "title", &tmp_music_file_info.title },
11524 { "artist", &tmp_music_file_info.artist },
11525 { "album", &tmp_music_file_info.album },
11526 { "year", &tmp_music_file_info.year },
11532 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11533 getCustomMusicFilename(basename));
11535 if (filename_music == NULL)
11538 // ---------- try to replace file extension ----------
11540 filename_prefix = getStringCopy(filename_music);
11541 if (strrchr(filename_prefix, '.') != NULL)
11542 *strrchr(filename_prefix, '.') = '\0';
11543 filename_info = getStringCat2(filename_prefix, ".txt");
11545 if (fileExists(filename_info))
11546 setup_file_hash = loadSetupFileHash(filename_info);
11548 free(filename_prefix);
11549 free(filename_info);
11551 if (setup_file_hash == NULL)
11553 // ---------- try to add file extension ----------
11555 filename_prefix = getStringCopy(filename_music);
11556 filename_info = getStringCat2(filename_prefix, ".txt");
11558 if (fileExists(filename_info))
11559 setup_file_hash = loadSetupFileHash(filename_info);
11561 free(filename_prefix);
11562 free(filename_info);
11565 if (setup_file_hash == NULL)
11568 // ---------- music file info found ----------
11570 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11572 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11574 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11576 *token_to_value_ptr[i].value_ptr =
11577 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11580 tmp_music_file_info.basename = getStringCopy(basename);
11581 tmp_music_file_info.music = music;
11582 tmp_music_file_info.is_sound = is_sound;
11584 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11585 *new_music_file_info = tmp_music_file_info;
11587 return new_music_file_info;
11590 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11592 return get_music_file_info_ext(basename, music, FALSE);
11595 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11597 return get_music_file_info_ext(basename, sound, TRUE);
11600 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11601 char *basename, boolean is_sound)
11603 for (; list != NULL; list = list->next)
11604 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11610 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11612 return music_info_listed_ext(list, basename, FALSE);
11615 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11617 return music_info_listed_ext(list, basename, TRUE);
11620 void LoadMusicInfo(void)
11622 char *music_directory = getCustomMusicDirectory();
11623 int num_music = getMusicListSize();
11624 int num_music_noconf = 0;
11625 int num_sounds = getSoundListSize();
11627 DirectoryEntry *dir_entry;
11628 struct FileInfo *music, *sound;
11629 struct MusicFileInfo *next, **new;
11632 while (music_file_info != NULL)
11634 next = music_file_info->next;
11636 checked_free(music_file_info->basename);
11638 checked_free(music_file_info->title_header);
11639 checked_free(music_file_info->artist_header);
11640 checked_free(music_file_info->album_header);
11641 checked_free(music_file_info->year_header);
11643 checked_free(music_file_info->title);
11644 checked_free(music_file_info->artist);
11645 checked_free(music_file_info->album);
11646 checked_free(music_file_info->year);
11648 free(music_file_info);
11650 music_file_info = next;
11653 new = &music_file_info;
11655 for (i = 0; i < num_music; i++)
11657 music = getMusicListEntry(i);
11659 if (music->filename == NULL)
11662 if (strEqual(music->filename, UNDEFINED_FILENAME))
11665 // a configured file may be not recognized as music
11666 if (!FileIsMusic(music->filename))
11669 if (!music_info_listed(music_file_info, music->filename))
11671 *new = get_music_file_info(music->filename, i);
11674 new = &(*new)->next;
11678 if ((dir = openDirectory(music_directory)) == NULL)
11680 Warn("cannot read music directory '%s'", music_directory);
11685 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11687 char *basename = dir_entry->basename;
11688 boolean music_already_used = FALSE;
11691 // skip all music files that are configured in music config file
11692 for (i = 0; i < num_music; i++)
11694 music = getMusicListEntry(i);
11696 if (music->filename == NULL)
11699 if (strEqual(basename, music->filename))
11701 music_already_used = TRUE;
11706 if (music_already_used)
11709 if (!FileIsMusic(dir_entry->filename))
11712 if (!music_info_listed(music_file_info, basename))
11714 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11717 new = &(*new)->next;
11720 num_music_noconf++;
11723 closeDirectory(dir);
11725 for (i = 0; i < num_sounds; i++)
11727 sound = getSoundListEntry(i);
11729 if (sound->filename == NULL)
11732 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11735 // a configured file may be not recognized as sound
11736 if (!FileIsSound(sound->filename))
11739 if (!sound_info_listed(music_file_info, sound->filename))
11741 *new = get_sound_file_info(sound->filename, i);
11743 new = &(*new)->next;
11748 static void add_helpanim_entry(int element, int action, int direction,
11749 int delay, int *num_list_entries)
11751 struct HelpAnimInfo *new_list_entry;
11752 (*num_list_entries)++;
11755 checked_realloc(helpanim_info,
11756 *num_list_entries * sizeof(struct HelpAnimInfo));
11757 new_list_entry = &helpanim_info[*num_list_entries - 1];
11759 new_list_entry->element = element;
11760 new_list_entry->action = action;
11761 new_list_entry->direction = direction;
11762 new_list_entry->delay = delay;
11765 static void print_unknown_token(char *filename, char *token, int token_nr)
11770 Warn("unknown token(s) found in config file:");
11771 Warn("- config file: '%s'", filename);
11774 Warn("- token: '%s'", token);
11777 static void print_unknown_token_end(int token_nr)
11783 void LoadHelpAnimInfo(void)
11785 char *filename = getHelpAnimFilename();
11786 SetupFileList *setup_file_list = NULL, *list;
11787 SetupFileHash *element_hash, *action_hash, *direction_hash;
11788 int num_list_entries = 0;
11789 int num_unknown_tokens = 0;
11792 if (fileExists(filename))
11793 setup_file_list = loadSetupFileList(filename);
11795 if (setup_file_list == NULL)
11797 // use reliable default values from static configuration
11798 SetupFileList *insert_ptr;
11800 insert_ptr = setup_file_list =
11801 newSetupFileList(helpanim_config[0].token,
11802 helpanim_config[0].value);
11804 for (i = 1; helpanim_config[i].token; i++)
11805 insert_ptr = addListEntry(insert_ptr,
11806 helpanim_config[i].token,
11807 helpanim_config[i].value);
11810 element_hash = newSetupFileHash();
11811 action_hash = newSetupFileHash();
11812 direction_hash = newSetupFileHash();
11814 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11815 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11817 for (i = 0; i < NUM_ACTIONS; i++)
11818 setHashEntry(action_hash, element_action_info[i].suffix,
11819 i_to_a(element_action_info[i].value));
11821 // do not store direction index (bit) here, but direction value!
11822 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11823 setHashEntry(direction_hash, element_direction_info[i].suffix,
11824 i_to_a(1 << element_direction_info[i].value));
11826 for (list = setup_file_list; list != NULL; list = list->next)
11828 char *element_token, *action_token, *direction_token;
11829 char *element_value, *action_value, *direction_value;
11830 int delay = atoi(list->value);
11832 if (strEqual(list->token, "end"))
11834 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11839 /* first try to break element into element/action/direction parts;
11840 if this does not work, also accept combined "element[.act][.dir]"
11841 elements (like "dynamite.active"), which are unique elements */
11843 if (strchr(list->token, '.') == NULL) // token contains no '.'
11845 element_value = getHashEntry(element_hash, list->token);
11846 if (element_value != NULL) // element found
11847 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11848 &num_list_entries);
11851 // no further suffixes found -- this is not an element
11852 print_unknown_token(filename, list->token, num_unknown_tokens++);
11858 // token has format "<prefix>.<something>"
11860 action_token = strchr(list->token, '.'); // suffix may be action ...
11861 direction_token = action_token; // ... or direction
11863 element_token = getStringCopy(list->token);
11864 *strchr(element_token, '.') = '\0';
11866 element_value = getHashEntry(element_hash, element_token);
11868 if (element_value == NULL) // this is no element
11870 element_value = getHashEntry(element_hash, list->token);
11871 if (element_value != NULL) // combined element found
11872 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11873 &num_list_entries);
11875 print_unknown_token(filename, list->token, num_unknown_tokens++);
11877 free(element_token);
11882 action_value = getHashEntry(action_hash, action_token);
11884 if (action_value != NULL) // action found
11886 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11887 &num_list_entries);
11889 free(element_token);
11894 direction_value = getHashEntry(direction_hash, direction_token);
11896 if (direction_value != NULL) // direction found
11898 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11899 &num_list_entries);
11901 free(element_token);
11906 if (strchr(action_token + 1, '.') == NULL)
11908 // no further suffixes found -- this is not an action nor direction
11910 element_value = getHashEntry(element_hash, list->token);
11911 if (element_value != NULL) // combined element found
11912 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11913 &num_list_entries);
11915 print_unknown_token(filename, list->token, num_unknown_tokens++);
11917 free(element_token);
11922 // token has format "<prefix>.<suffix>.<something>"
11924 direction_token = strchr(action_token + 1, '.');
11926 action_token = getStringCopy(action_token);
11927 *strchr(action_token + 1, '.') = '\0';
11929 action_value = getHashEntry(action_hash, action_token);
11931 if (action_value == NULL) // this is no action
11933 element_value = getHashEntry(element_hash, list->token);
11934 if (element_value != NULL) // combined element found
11935 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11936 &num_list_entries);
11938 print_unknown_token(filename, list->token, num_unknown_tokens++);
11940 free(element_token);
11941 free(action_token);
11946 direction_value = getHashEntry(direction_hash, direction_token);
11948 if (direction_value != NULL) // direction found
11950 add_helpanim_entry(atoi(element_value), atoi(action_value),
11951 atoi(direction_value), delay, &num_list_entries);
11953 free(element_token);
11954 free(action_token);
11959 // this is no direction
11961 element_value = getHashEntry(element_hash, list->token);
11962 if (element_value != NULL) // combined element found
11963 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11964 &num_list_entries);
11966 print_unknown_token(filename, list->token, num_unknown_tokens++);
11968 free(element_token);
11969 free(action_token);
11972 print_unknown_token_end(num_unknown_tokens);
11974 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11975 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11977 freeSetupFileList(setup_file_list);
11978 freeSetupFileHash(element_hash);
11979 freeSetupFileHash(action_hash);
11980 freeSetupFileHash(direction_hash);
11983 for (i = 0; i < num_list_entries; i++)
11984 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
11985 EL_NAME(helpanim_info[i].element),
11986 helpanim_info[i].element,
11987 helpanim_info[i].action,
11988 helpanim_info[i].direction,
11989 helpanim_info[i].delay);
11993 void LoadHelpTextInfo(void)
11995 char *filename = getHelpTextFilename();
11998 if (helptext_info != NULL)
12000 freeSetupFileHash(helptext_info);
12001 helptext_info = NULL;
12004 if (fileExists(filename))
12005 helptext_info = loadSetupFileHash(filename);
12007 if (helptext_info == NULL)
12009 // use reliable default values from static configuration
12010 helptext_info = newSetupFileHash();
12012 for (i = 0; helptext_config[i].token; i++)
12013 setHashEntry(helptext_info,
12014 helptext_config[i].token,
12015 helptext_config[i].value);
12019 BEGIN_HASH_ITERATION(helptext_info, itr)
12021 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12022 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12024 END_HASH_ITERATION(hash, itr)
12029 // ----------------------------------------------------------------------------
12031 // ----------------------------------------------------------------------------
12033 #define MAX_NUM_CONVERT_LEVELS 1000
12035 void ConvertLevels(void)
12037 static LevelDirTree *convert_leveldir = NULL;
12038 static int convert_level_nr = -1;
12039 static int num_levels_handled = 0;
12040 static int num_levels_converted = 0;
12041 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12044 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12045 global.convert_leveldir);
12047 if (convert_leveldir == NULL)
12048 Fail("no such level identifier: '%s'", global.convert_leveldir);
12050 leveldir_current = convert_leveldir;
12052 if (global.convert_level_nr != -1)
12054 convert_leveldir->first_level = global.convert_level_nr;
12055 convert_leveldir->last_level = global.convert_level_nr;
12058 convert_level_nr = convert_leveldir->first_level;
12060 PrintLine("=", 79);
12061 Print("Converting levels\n");
12062 PrintLine("-", 79);
12063 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12064 Print("Level series name: '%s'\n", convert_leveldir->name);
12065 Print("Level series author: '%s'\n", convert_leveldir->author);
12066 Print("Number of levels: %d\n", convert_leveldir->levels);
12067 PrintLine("=", 79);
12070 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12071 levels_failed[i] = FALSE;
12073 while (convert_level_nr <= convert_leveldir->last_level)
12075 char *level_filename;
12078 level_nr = convert_level_nr++;
12080 Print("Level %03d: ", level_nr);
12082 LoadLevel(level_nr);
12083 if (level.no_level_file || level.no_valid_file)
12085 Print("(no level)\n");
12089 Print("converting level ... ");
12091 level_filename = getDefaultLevelFilename(level_nr);
12092 new_level = !fileExists(level_filename);
12096 SaveLevel(level_nr);
12098 num_levels_converted++;
12100 Print("converted.\n");
12104 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12105 levels_failed[level_nr] = TRUE;
12107 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12110 num_levels_handled++;
12114 PrintLine("=", 79);
12115 Print("Number of levels handled: %d\n", num_levels_handled);
12116 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12117 (num_levels_handled ?
12118 num_levels_converted * 100 / num_levels_handled : 0));
12119 PrintLine("-", 79);
12120 Print("Summary (for automatic parsing by scripts):\n");
12121 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12122 convert_leveldir->identifier, num_levels_converted,
12123 num_levels_handled,
12124 (num_levels_handled ?
12125 num_levels_converted * 100 / num_levels_handled : 0));
12127 if (num_levels_handled != num_levels_converted)
12129 Print(", FAILED:");
12130 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12131 if (levels_failed[i])
12136 PrintLine("=", 79);
12138 CloseAllAndExit(0);
12142 // ----------------------------------------------------------------------------
12143 // create and save images for use in level sketches (raw BMP format)
12144 // ----------------------------------------------------------------------------
12146 void CreateLevelSketchImages(void)
12152 InitElementPropertiesGfxElement();
12154 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12155 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12157 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12159 int element = getMappedElement(i);
12160 char basename1[16];
12161 char basename2[16];
12165 sprintf(basename1, "%04d.bmp", i);
12166 sprintf(basename2, "%04ds.bmp", i);
12168 filename1 = getPath2(global.create_images_dir, basename1);
12169 filename2 = getPath2(global.create_images_dir, basename2);
12171 DrawSizedElement(0, 0, element, TILESIZE);
12172 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12174 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12175 Fail("cannot save level sketch image file '%s'", filename1);
12177 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12178 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12180 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12181 Fail("cannot save level sketch image file '%s'", filename2);
12186 // create corresponding SQL statements (for normal and small images)
12189 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12190 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12193 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12194 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12196 // optional: create content for forum level sketch demonstration post
12198 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12201 FreeBitmap(bitmap1);
12202 FreeBitmap(bitmap2);
12205 fprintf(stderr, "\n");
12207 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12209 CloseAllAndExit(0);
12213 // ----------------------------------------------------------------------------
12214 // create and save images for custom and group elements (raw BMP format)
12215 // ----------------------------------------------------------------------------
12217 void CreateCustomElementImages(char *directory)
12219 char *src_basename = "RocksCE-template.ilbm";
12220 char *dst_basename = "RocksCE.bmp";
12221 char *src_filename = getPath2(directory, src_basename);
12222 char *dst_filename = getPath2(directory, dst_basename);
12223 Bitmap *src_bitmap;
12225 int yoffset_ce = 0;
12226 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12229 InitVideoDefaults();
12231 ReCreateBitmap(&backbuffer, video.width, video.height);
12233 src_bitmap = LoadImage(src_filename);
12235 bitmap = CreateBitmap(TILEX * 16 * 2,
12236 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12239 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12246 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12247 TILEX * x, TILEY * y + yoffset_ce);
12249 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12251 TILEX * x + TILEX * 16,
12252 TILEY * y + yoffset_ce);
12254 for (j = 2; j >= 0; j--)
12258 BlitBitmap(src_bitmap, bitmap,
12259 TILEX + c * 7, 0, 6, 10,
12260 TILEX * x + 6 + j * 7,
12261 TILEY * y + 11 + yoffset_ce);
12263 BlitBitmap(src_bitmap, bitmap,
12264 TILEX + c * 8, TILEY, 6, 10,
12265 TILEX * 16 + TILEX * x + 6 + j * 8,
12266 TILEY * y + 10 + yoffset_ce);
12272 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12279 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12280 TILEX * x, TILEY * y + yoffset_ge);
12282 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12284 TILEX * x + TILEX * 16,
12285 TILEY * y + yoffset_ge);
12287 for (j = 1; j >= 0; j--)
12291 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12292 TILEX * x + 6 + j * 10,
12293 TILEY * y + 11 + yoffset_ge);
12295 BlitBitmap(src_bitmap, bitmap,
12296 TILEX + c * 8, TILEY + 12, 6, 10,
12297 TILEX * 16 + TILEX * x + 10 + j * 8,
12298 TILEY * y + 10 + yoffset_ge);
12304 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12305 Fail("cannot save CE graphics file '%s'", dst_filename);
12307 FreeBitmap(bitmap);
12309 CloseAllAndExit(0);