1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
279 &li.bd_intermission, FALSE
284 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
285 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
291 &li.bd_pal_timing, FALSE
296 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
297 &li.bd_cycle_delay_ms, 200
302 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
303 &li.bd_cycle_delay_c64, 0
308 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
309 &li.bd_hatching_delay_cycles, 21
314 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
315 &li.bd_hatching_delay_seconds, 2
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
321 &li.bd_line_shifting_borders, FALSE
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
327 &li.bd_wraparound_objects, FALSE
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
333 &li.bd_scan_first_and_last_row, TRUE
343 static struct LevelFileConfigInfo chunk_config_ELEM[] =
345 // (these values are the same for each player)
348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
349 &li.block_last_field, FALSE // default case for EM levels
353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
354 &li.sp_block_last_field, TRUE // default case for SP levels
358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
359 &li.instant_relocation, FALSE
363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
364 &li.can_pass_to_walkable, FALSE
368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
369 &li.block_snap_field, TRUE
373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
374 &li.continuous_snapping, TRUE
378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
379 &li.shifted_relocation, FALSE
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
384 &li.lazy_relocation, FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
389 &li.finish_dig_collect, TRUE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
394 &li.keep_walkable_ce, FALSE
397 // (these values are different for each player)
400 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
401 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
406 &li.initial_player_gravity[0], FALSE
410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
411 &li.use_start_element[0], FALSE
415 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
416 &li.start_element[0], EL_PLAYER_1
420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
421 &li.use_artwork_element[0], FALSE
425 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
426 &li.artwork_element[0], EL_PLAYER_1
430 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
431 &li.use_explosion_element[0], FALSE
435 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
436 &li.explosion_element[0], EL_PLAYER_1
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
441 &li.use_initial_inventory[0], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
446 &li.initial_inventory_size[0], 1
450 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
451 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
452 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
457 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
458 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
463 &li.initial_player_gravity[1], FALSE
467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
468 &li.use_start_element[1], FALSE
472 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
473 &li.start_element[1], EL_PLAYER_2
477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
478 &li.use_artwork_element[1], FALSE
482 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
483 &li.artwork_element[1], EL_PLAYER_2
487 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
488 &li.use_explosion_element[1], FALSE
492 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
493 &li.explosion_element[1], EL_PLAYER_2
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
498 &li.use_initial_inventory[1], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
503 &li.initial_inventory_size[1], 1
507 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
508 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
509 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
514 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
515 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
519 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
520 &li.initial_player_gravity[2], FALSE
524 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
525 &li.use_start_element[2], FALSE
529 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
530 &li.start_element[2], EL_PLAYER_3
534 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
535 &li.use_artwork_element[2], FALSE
539 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
540 &li.artwork_element[2], EL_PLAYER_3
544 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
545 &li.use_explosion_element[2], FALSE
549 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
550 &li.explosion_element[2], EL_PLAYER_3
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
555 &li.use_initial_inventory[2], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
560 &li.initial_inventory_size[2], 1
564 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
565 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
566 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
571 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
572 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
576 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
577 &li.initial_player_gravity[3], FALSE
581 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
582 &li.use_start_element[3], FALSE
586 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
587 &li.start_element[3], EL_PLAYER_4
591 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
592 &li.use_artwork_element[3], FALSE
596 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
597 &li.artwork_element[3], EL_PLAYER_4
601 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
602 &li.use_explosion_element[3], FALSE
606 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
607 &li.explosion_element[3], EL_PLAYER_4
611 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
612 &li.use_initial_inventory[3], FALSE
616 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
617 &li.initial_inventory_size[3], 1
621 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
622 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
623 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
626 // (these values are only valid for BD style levels)
629 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
630 &li.bd_diagonal_movements, FALSE
635 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
636 &li.score[SC_DIAMOND_EXTRA], 20
639 // (the following values are related to various game elements)
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_EMERALD], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_DIAMOND], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_BUG], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_SPACESHIP], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_PACMAN], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_NUT], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_DYNAMITE], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_KEY], 10
691 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 &li.score[SC_PEARL], 10
697 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
698 &li.score[SC_CRYSTAL], 10
703 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
704 &li.amoeba_content, EL_DIAMOND
708 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
713 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
714 &li.grow_into_diggable, TRUE
719 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
720 &li.yamyam_content, EL_ROCK, NULL,
721 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
725 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
726 &li.score[SC_YAMYAM], 10
731 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
732 &li.score[SC_ROBOT], 10
736 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
742 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
749 &li.time_magic_wall, 10
754 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
755 &li.game_of_life[0], 2
759 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
760 &li.game_of_life[1], 3
764 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
765 &li.game_of_life[2], 3
769 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
770 &li.game_of_life[3], 3
774 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
775 &li.use_life_bugs, FALSE
780 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
785 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
790 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
795 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
800 EL_TIMEGATE_SWITCH, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.time_timegate, 10
806 EL_LIGHT_SWITCH_ACTIVE, -1,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
812 EL_SHIELD_NORMAL, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
814 &li.shield_normal_time, 10
817 EL_SHIELD_NORMAL, -1,
818 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
819 &li.score[SC_SHIELD], 10
823 EL_SHIELD_DEADLY, -1,
824 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
825 &li.shield_deadly_time, 10
828 EL_SHIELD_DEADLY, -1,
829 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
830 &li.score[SC_SHIELD], 10
835 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
840 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
841 &li.extra_time_score, 10
845 EL_TIME_ORB_FULL, -1,
846 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
847 &li.time_orb_time, 10
850 EL_TIME_ORB_FULL, -1,
851 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
852 &li.use_time_orb_bug, FALSE
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
858 &li.use_spring_bug, FALSE
863 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 &li.android_move_time, 10
868 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
869 &li.android_clone_time, 10
872 EL_EMC_ANDROID, SAVE_CONF_NEVER,
873 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
874 &li.android_clone_element[0], EL_EMPTY, NULL,
875 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
879 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
880 &li.android_clone_element[0], EL_EMPTY, NULL,
881 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
886 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
891 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
896 EL_EMC_MAGNIFIER, -1,
897 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
898 &li.magnify_score, 10
901 EL_EMC_MAGNIFIER, -1,
902 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
907 EL_EMC_MAGIC_BALL, -1,
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
912 EL_EMC_MAGIC_BALL, -1,
913 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
914 &li.ball_random, FALSE
917 EL_EMC_MAGIC_BALL, -1,
918 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
919 &li.ball_active_initial, FALSE
922 EL_EMC_MAGIC_BALL, -1,
923 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
924 &li.ball_content, EL_EMPTY, NULL,
925 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
929 EL_SOKOBAN_FIELD_EMPTY, -1,
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
931 &li.sb_fields_needed, TRUE
935 EL_SOKOBAN_OBJECT, -1,
936 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
937 &li.sb_objects_needed, TRUE
942 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
943 &li.mm_laser_red, FALSE
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
948 &li.mm_laser_green, FALSE
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
953 &li.mm_laser_blue, TRUE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
959 &li.df_laser_red, TRUE
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
964 &li.df_laser_green, TRUE
968 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
969 &li.df_laser_blue, FALSE
973 EL_MM_FUSE_ACTIVE, -1,
974 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
979 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
985 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
990 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
991 &li.mm_ball_choice_mode, ANIM_RANDOM
995 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
996 &li.mm_ball_content, EL_EMPTY, NULL,
997 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1000 EL_MM_GRAY_BALL, -1,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1002 &li.rotate_mm_ball_content, TRUE
1005 EL_MM_GRAY_BALL, -1,
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1007 &li.explode_mm_ball, FALSE
1011 EL_MM_STEEL_BLOCK, -1,
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1013 &li.mm_time_block, 75
1016 EL_MM_LIGHTBALL, -1,
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.score[SC_ELEM_BONUS], 10
1028 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1032 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1033 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1037 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1038 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1043 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1044 &xx_envelope.autowrap, FALSE
1048 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1049 &xx_envelope.centered, FALSE
1054 TYPE_STRING, CONF_VALUE_BYTES(1),
1055 &xx_envelope.text, -1, NULL,
1056 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1057 &xx_default_string_empty[0]
1067 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1071 TYPE_STRING, CONF_VALUE_BYTES(1),
1072 &xx_ei.description[0], -1,
1073 &yy_ei.description[0],
1074 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1075 &xx_default_description[0]
1080 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1081 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1082 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1084 #if ENABLE_RESERVED_CODE
1085 // (reserved for later use)
1088 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1089 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1090 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1096 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1097 &xx_ei.use_gfx_element, FALSE,
1098 &yy_ei.use_gfx_element
1102 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1103 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1104 &yy_ei.gfx_element_initial
1109 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1110 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1111 &yy_ei.access_direction
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1117 &xx_ei.collect_score_initial, 10,
1118 &yy_ei.collect_score_initial
1122 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1123 &xx_ei.collect_count_initial, 1,
1124 &yy_ei.collect_count_initial
1129 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1130 &xx_ei.ce_value_fixed_initial, 0,
1131 &yy_ei.ce_value_fixed_initial
1135 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1136 &xx_ei.ce_value_random_initial, 0,
1137 &yy_ei.ce_value_random_initial
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1142 &xx_ei.use_last_ce_value, FALSE,
1143 &yy_ei.use_last_ce_value
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1149 &xx_ei.push_delay_fixed, 8,
1150 &yy_ei.push_delay_fixed
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1155 &xx_ei.push_delay_random, 8,
1156 &yy_ei.push_delay_random
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1161 &xx_ei.drop_delay_fixed, 0,
1162 &yy_ei.drop_delay_fixed
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1167 &xx_ei.drop_delay_random, 0,
1168 &yy_ei.drop_delay_random
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1173 &xx_ei.move_delay_fixed, 0,
1174 &yy_ei.move_delay_fixed
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1179 &xx_ei.move_delay_random, 0,
1180 &yy_ei.move_delay_random
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1185 &xx_ei.step_delay_fixed, 0,
1186 &yy_ei.step_delay_fixed
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1191 &xx_ei.step_delay_random, 0,
1192 &yy_ei.step_delay_random
1197 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1198 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1203 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1204 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1205 &yy_ei.move_direction_initial
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1210 &xx_ei.move_stepsize, TILEX / 8,
1211 &yy_ei.move_stepsize
1216 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1217 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1218 &yy_ei.move_enter_element
1222 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1223 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1224 &yy_ei.move_leave_element
1228 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1229 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1230 &yy_ei.move_leave_type
1235 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1236 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1237 &yy_ei.slippery_type
1242 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1243 &xx_ei.explosion_type, EXPLODES_3X3,
1244 &yy_ei.explosion_type
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1249 &xx_ei.explosion_delay, 16,
1250 &yy_ei.explosion_delay
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1255 &xx_ei.ignition_delay, 8,
1256 &yy_ei.ignition_delay
1261 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1262 &xx_ei.content, EL_EMPTY_SPACE,
1264 &xx_num_contents, 1, 1
1267 // ---------- "num_change_pages" must be the last entry ---------------------
1270 -1, SAVE_CONF_ALWAYS,
1271 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1272 &xx_ei.num_change_pages, 1,
1273 &yy_ei.num_change_pages
1284 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1286 // ---------- "current_change_page" must be the first entry -----------------
1289 -1, SAVE_CONF_ALWAYS,
1290 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1291 &xx_current_change_page, -1
1294 // ---------- (the remaining entries can be in any order) -------------------
1298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1299 &xx_change.can_change, FALSE
1304 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1305 &xx_event_bits[0], 0
1309 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1310 &xx_event_bits[1], 0
1315 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1316 &xx_change.trigger_player, CH_PLAYER_ANY
1320 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1321 &xx_change.trigger_side, CH_SIDE_ANY
1325 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1326 &xx_change.trigger_page, CH_PAGE_ANY
1331 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1332 &xx_change.target_element, EL_EMPTY_SPACE
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1338 &xx_change.delay_fixed, 0
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1343 &xx_change.delay_random, 0
1347 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1348 &xx_change.delay_frames, FRAMES_PER_SECOND
1353 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1354 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1360 &xx_change.explode, FALSE
1364 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1365 &xx_change.use_target_content, FALSE
1369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1370 &xx_change.only_if_complete, FALSE
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1375 &xx_change.use_random_replace, FALSE
1379 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1380 &xx_change.random_percentage, 100
1384 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1385 &xx_change.replace_when, CP_WHEN_EMPTY
1390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1391 &xx_change.has_action, FALSE
1395 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1396 &xx_change.action_type, CA_NO_ACTION
1400 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1401 &xx_change.action_mode, CA_MODE_UNDEFINED
1405 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1406 &xx_change.action_arg, CA_ARG_UNDEFINED
1411 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1412 &xx_change.action_element, EL_EMPTY_SPACE
1417 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1418 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1419 &xx_num_contents, 1, 1
1429 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1433 TYPE_STRING, CONF_VALUE_BYTES(1),
1434 &xx_ei.description[0], -1, NULL,
1435 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1436 &xx_default_description[0]
1441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1442 &xx_ei.use_gfx_element, FALSE
1446 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1447 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1452 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1453 &xx_group.choice_mode, ANIM_RANDOM
1458 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1459 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1460 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1470 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1475 &xx_ei.use_gfx_element, FALSE
1479 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1480 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1490 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1495 &li.block_snap_field, TRUE
1499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1500 &li.continuous_snapping, TRUE
1504 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1505 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1510 &li.use_start_element[0], FALSE
1514 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1515 &li.start_element[0], EL_PLAYER_1
1519 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1520 &li.use_artwork_element[0], FALSE
1524 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1525 &li.artwork_element[0], EL_PLAYER_1
1529 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1530 &li.use_explosion_element[0], FALSE
1534 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1535 &li.explosion_element[0], EL_PLAYER_1
1550 filetype_id_list[] =
1552 { LEVEL_FILE_TYPE_RND, "RND" },
1553 { LEVEL_FILE_TYPE_BD, "BD" },
1554 { LEVEL_FILE_TYPE_EM, "EM" },
1555 { LEVEL_FILE_TYPE_SP, "SP" },
1556 { LEVEL_FILE_TYPE_DX, "DX" },
1557 { LEVEL_FILE_TYPE_SB, "SB" },
1558 { LEVEL_FILE_TYPE_DC, "DC" },
1559 { LEVEL_FILE_TYPE_MM, "MM" },
1560 { LEVEL_FILE_TYPE_MM, "DF" },
1565 // ============================================================================
1566 // level file functions
1567 // ============================================================================
1569 static boolean check_special_flags(char *flag)
1571 if (strEqual(options.special_flags, flag) ||
1572 strEqual(leveldir_current->special_flags, flag))
1578 static struct DateInfo getCurrentDate(void)
1580 time_t epoch_seconds = time(NULL);
1581 struct tm *now = localtime(&epoch_seconds);
1582 struct DateInfo date;
1584 date.year = now->tm_year + 1900;
1585 date.month = now->tm_mon + 1;
1586 date.day = now->tm_mday;
1588 date.src = DATE_SRC_CLOCK;
1593 static void resetEventFlags(struct ElementChangeInfo *change)
1597 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1598 change->has_event[i] = FALSE;
1601 static void resetEventBits(void)
1605 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1606 xx_event_bits[i] = 0;
1609 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1613 /* important: only change event flag if corresponding event bit is set
1614 (this is because all xx_event_bits[] values are loaded separately,
1615 and all xx_event_bits[] values are set back to zero before loading
1616 another value xx_event_bits[x] (each value representing 32 flags)) */
1618 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1619 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1620 change->has_event[i] = TRUE;
1623 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1627 /* in contrast to the above function setEventFlagsFromEventBits(), it
1628 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1629 depending on the corresponding change->has_event[i] values here, as
1630 all xx_event_bits[] values are reset in resetEventBits() before */
1632 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1633 if (change->has_event[i])
1634 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1637 static char *getDefaultElementDescription(struct ElementInfo *ei)
1639 static char description[MAX_ELEMENT_NAME_LEN + 1];
1640 char *default_description = (ei->custom_description != NULL ?
1641 ei->custom_description :
1642 ei->editor_description);
1645 // always start with reliable default values
1646 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1647 description[i] = '\0';
1649 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1650 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1652 return &description[0];
1655 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1657 char *default_description = getDefaultElementDescription(ei);
1660 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1661 ei->description[i] = default_description[i];
1664 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1668 for (i = 0; conf[i].data_type != -1; i++)
1670 int default_value = conf[i].default_value;
1671 int data_type = conf[i].data_type;
1672 int conf_type = conf[i].conf_type;
1673 int byte_mask = conf_type & CONF_MASK_BYTES;
1675 if (byte_mask == CONF_MASK_MULTI_BYTES)
1677 int default_num_entities = conf[i].default_num_entities;
1678 int max_num_entities = conf[i].max_num_entities;
1680 *(int *)(conf[i].num_entities) = default_num_entities;
1682 if (data_type == TYPE_STRING)
1684 char *default_string = conf[i].default_string;
1685 char *string = (char *)(conf[i].value);
1687 strncpy(string, default_string, max_num_entities);
1689 else if (data_type == TYPE_ELEMENT_LIST)
1691 int *element_array = (int *)(conf[i].value);
1694 for (j = 0; j < max_num_entities; j++)
1695 element_array[j] = default_value;
1697 else if (data_type == TYPE_CONTENT_LIST)
1699 struct Content *content = (struct Content *)(conf[i].value);
1702 for (c = 0; c < max_num_entities; c++)
1703 for (y = 0; y < 3; y++)
1704 for (x = 0; x < 3; x++)
1705 content[c].e[x][y] = default_value;
1708 else // constant size configuration data (1, 2 or 4 bytes)
1710 if (data_type == TYPE_BOOLEAN)
1711 *(boolean *)(conf[i].value) = default_value;
1713 *(int *) (conf[i].value) = default_value;
1718 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1722 for (i = 0; conf[i].data_type != -1; i++)
1724 int data_type = conf[i].data_type;
1725 int conf_type = conf[i].conf_type;
1726 int byte_mask = conf_type & CONF_MASK_BYTES;
1728 if (byte_mask == CONF_MASK_MULTI_BYTES)
1730 int max_num_entities = conf[i].max_num_entities;
1732 if (data_type == TYPE_STRING)
1734 char *string = (char *)(conf[i].value);
1735 char *string_copy = (char *)(conf[i].value_copy);
1737 strncpy(string_copy, string, max_num_entities);
1739 else if (data_type == TYPE_ELEMENT_LIST)
1741 int *element_array = (int *)(conf[i].value);
1742 int *element_array_copy = (int *)(conf[i].value_copy);
1745 for (j = 0; j < max_num_entities; j++)
1746 element_array_copy[j] = element_array[j];
1748 else if (data_type == TYPE_CONTENT_LIST)
1750 struct Content *content = (struct Content *)(conf[i].value);
1751 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1754 for (c = 0; c < max_num_entities; c++)
1755 for (y = 0; y < 3; y++)
1756 for (x = 0; x < 3; x++)
1757 content_copy[c].e[x][y] = content[c].e[x][y];
1760 else // constant size configuration data (1, 2 or 4 bytes)
1762 if (data_type == TYPE_BOOLEAN)
1763 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1765 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1770 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1774 xx_ei = *ei_from; // copy element data into temporary buffer
1775 yy_ei = *ei_to; // copy element data into temporary buffer
1777 copyConfigFromConfigList(chunk_config_CUSX_base);
1782 // ---------- reinitialize and copy change pages ----------
1784 ei_to->num_change_pages = ei_from->num_change_pages;
1785 ei_to->current_change_page = ei_from->current_change_page;
1787 setElementChangePages(ei_to, ei_to->num_change_pages);
1789 for (i = 0; i < ei_to->num_change_pages; i++)
1790 ei_to->change_page[i] = ei_from->change_page[i];
1792 // ---------- copy group element info ----------
1793 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1794 *ei_to->group = *ei_from->group;
1796 // mark this custom element as modified
1797 ei_to->modified_settings = TRUE;
1800 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1802 int change_page_size = sizeof(struct ElementChangeInfo);
1804 ei->num_change_pages = MAX(1, change_pages);
1807 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1809 if (ei->current_change_page >= ei->num_change_pages)
1810 ei->current_change_page = ei->num_change_pages - 1;
1812 ei->change = &ei->change_page[ei->current_change_page];
1815 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1817 xx_change = *change; // copy change data into temporary buffer
1819 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1821 *change = xx_change;
1823 resetEventFlags(change);
1825 change->direct_action = 0;
1826 change->other_action = 0;
1828 change->pre_change_function = NULL;
1829 change->change_function = NULL;
1830 change->post_change_function = NULL;
1833 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1837 li = *level; // copy level data into temporary buffer
1838 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1839 *level = li; // copy temporary buffer back to level data
1841 setLevelInfoToDefaults_BD();
1842 setLevelInfoToDefaults_EM();
1843 setLevelInfoToDefaults_SP();
1844 setLevelInfoToDefaults_MM();
1846 level->native_bd_level = &native_bd_level;
1847 level->native_em_level = &native_em_level;
1848 level->native_sp_level = &native_sp_level;
1849 level->native_mm_level = &native_mm_level;
1851 level->file_version = FILE_VERSION_ACTUAL;
1852 level->game_version = GAME_VERSION_ACTUAL;
1854 level->creation_date = getCurrentDate();
1856 level->encoding_16bit_field = TRUE;
1857 level->encoding_16bit_yamyam = TRUE;
1858 level->encoding_16bit_amoeba = TRUE;
1860 // clear level name and level author string buffers
1861 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1862 level->name[i] = '\0';
1863 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1864 level->author[i] = '\0';
1866 // set level name and level author to default values
1867 strcpy(level->name, NAMELESS_LEVEL_NAME);
1868 strcpy(level->author, ANONYMOUS_NAME);
1870 // set level playfield to playable default level with player and exit
1871 for (x = 0; x < MAX_LEV_FIELDX; x++)
1872 for (y = 0; y < MAX_LEV_FIELDY; y++)
1873 level->field[x][y] = EL_SAND;
1875 level->field[0][0] = EL_PLAYER_1;
1876 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1878 BorderElement = EL_STEELWALL;
1880 // detect custom elements when loading them
1881 level->file_has_custom_elements = FALSE;
1883 // set all bug compatibility flags to "false" => do not emulate this bug
1884 level->use_action_after_change_bug = FALSE;
1886 if (leveldir_current)
1888 // try to determine better author name than 'anonymous'
1889 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1891 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1892 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1896 switch (LEVELCLASS(leveldir_current))
1898 case LEVELCLASS_TUTORIAL:
1899 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1902 case LEVELCLASS_CONTRIB:
1903 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1904 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1907 case LEVELCLASS_PRIVATE:
1908 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1909 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1913 // keep default value
1920 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1922 static boolean clipboard_elements_initialized = FALSE;
1925 InitElementPropertiesStatic();
1927 li = *level; // copy level data into temporary buffer
1928 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1929 *level = li; // copy temporary buffer back to level data
1931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1934 struct ElementInfo *ei = &element_info[element];
1936 if (element == EL_MM_GRAY_BALL)
1938 struct LevelInfo_MM *level_mm = level->native_mm_level;
1941 for (j = 0; j < level->num_mm_ball_contents; j++)
1942 level->mm_ball_content[j] =
1943 map_element_MM_to_RND(level_mm->ball_content[j]);
1946 // never initialize clipboard elements after the very first time
1947 // (to be able to use clipboard elements between several levels)
1948 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1951 if (IS_ENVELOPE(element))
1953 int envelope_nr = element - EL_ENVELOPE_1;
1955 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1957 level->envelope[envelope_nr] = xx_envelope;
1960 if (IS_CUSTOM_ELEMENT(element) ||
1961 IS_GROUP_ELEMENT(element) ||
1962 IS_INTERNAL_ELEMENT(element))
1964 xx_ei = *ei; // copy element data into temporary buffer
1966 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1971 setElementChangePages(ei, 1);
1972 setElementChangeInfoToDefaults(ei->change);
1974 if (IS_CUSTOM_ELEMENT(element) ||
1975 IS_GROUP_ELEMENT(element))
1977 setElementDescriptionToDefault(ei);
1979 ei->modified_settings = FALSE;
1982 if (IS_CUSTOM_ELEMENT(element) ||
1983 IS_INTERNAL_ELEMENT(element))
1985 // internal values used in level editor
1987 ei->access_type = 0;
1988 ei->access_layer = 0;
1989 ei->access_protected = 0;
1990 ei->walk_to_action = 0;
1991 ei->smash_targets = 0;
1994 ei->can_explode_by_fire = FALSE;
1995 ei->can_explode_smashed = FALSE;
1996 ei->can_explode_impact = FALSE;
1998 ei->current_change_page = 0;
2001 if (IS_GROUP_ELEMENT(element) ||
2002 IS_INTERNAL_ELEMENT(element))
2004 struct ElementGroupInfo *group;
2006 // initialize memory for list of elements in group
2007 if (ei->group == NULL)
2008 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2012 xx_group = *group; // copy group data into temporary buffer
2014 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2019 if (IS_EMPTY_ELEMENT(element) ||
2020 IS_INTERNAL_ELEMENT(element))
2022 xx_ei = *ei; // copy element data into temporary buffer
2024 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2030 clipboard_elements_initialized = TRUE;
2033 static void setLevelInfoToDefaults(struct LevelInfo *level,
2034 boolean level_info_only,
2035 boolean reset_file_status)
2037 setLevelInfoToDefaults_Level(level);
2039 if (!level_info_only)
2040 setLevelInfoToDefaults_Elements(level);
2042 if (reset_file_status)
2044 level->no_valid_file = FALSE;
2045 level->no_level_file = FALSE;
2048 level->changed = FALSE;
2051 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2053 level_file_info->nr = 0;
2054 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2055 level_file_info->packed = FALSE;
2057 setString(&level_file_info->basename, NULL);
2058 setString(&level_file_info->filename, NULL);
2061 int getMappedElement_SB(int, boolean);
2063 static void ActivateLevelTemplate(void)
2067 if (check_special_flags("load_xsb_to_ces"))
2069 // fill smaller playfields with padding "beyond border wall" elements
2070 if (level.fieldx < level_template.fieldx ||
2071 level.fieldy < level_template.fieldy)
2073 short field[level.fieldx][level.fieldy];
2074 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2075 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2076 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2077 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2079 // copy old playfield (which is smaller than the visible area)
2080 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2081 field[x][y] = level.field[x][y];
2083 // fill new, larger playfield with "beyond border wall" elements
2084 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2085 level.field[x][y] = getMappedElement_SB('_', TRUE);
2087 // copy the old playfield to the middle of the new playfield
2088 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2089 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2091 level.fieldx = new_fieldx;
2092 level.fieldy = new_fieldy;
2096 // Currently there is no special action needed to activate the template
2097 // data, because 'element_info' property settings overwrite the original
2098 // level data, while all other variables do not change.
2100 // Exception: 'from_level_template' elements in the original level playfield
2101 // are overwritten with the corresponding elements at the same position in
2102 // playfield from the level template.
2104 for (x = 0; x < level.fieldx; x++)
2105 for (y = 0; y < level.fieldy; y++)
2106 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2107 level.field[x][y] = level_template.field[x][y];
2109 if (check_special_flags("load_xsb_to_ces"))
2111 struct LevelInfo level_backup = level;
2113 // overwrite all individual level settings from template level settings
2114 level = level_template;
2116 // restore level file info
2117 level.file_info = level_backup.file_info;
2119 // restore playfield size
2120 level.fieldx = level_backup.fieldx;
2121 level.fieldy = level_backup.fieldy;
2123 // restore playfield content
2124 for (x = 0; x < level.fieldx; x++)
2125 for (y = 0; y < level.fieldy; y++)
2126 level.field[x][y] = level_backup.field[x][y];
2128 // restore name and author from individual level
2129 strcpy(level.name, level_backup.name);
2130 strcpy(level.author, level_backup.author);
2132 // restore flag "use_custom_template"
2133 level.use_custom_template = level_backup.use_custom_template;
2137 static boolean checkForPackageFromBasename_BD(char *basename)
2139 // check for native BD level file extensions
2140 if (!strSuffixLower(basename, ".bd") &&
2141 !strSuffixLower(basename, ".bdr") &&
2142 !strSuffixLower(basename, ".brc") &&
2143 !strSuffixLower(basename, ".gds"))
2146 // check for standard single-level BD files (like "001.bd")
2147 if (strSuffixLower(basename, ".bd") &&
2148 strlen(basename) == 6 &&
2149 basename[0] >= '0' && basename[0] <= '9' &&
2150 basename[1] >= '0' && basename[1] <= '9' &&
2151 basename[2] >= '0' && basename[2] <= '9')
2154 // this is a level package in native BD file format
2158 static char *getLevelFilenameFromBasename(char *basename)
2160 static char *filename = NULL;
2162 checked_free(filename);
2164 filename = getPath2(getCurrentLevelDir(), basename);
2169 static int getFileTypeFromBasename(char *basename)
2171 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2173 static char *filename = NULL;
2174 struct stat file_status;
2176 // ---------- try to determine file type from filename ----------
2178 // check for typical filename of a Supaplex level package file
2179 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2180 return LEVEL_FILE_TYPE_SP;
2182 // check for typical filename of a Diamond Caves II level package file
2183 if (strSuffixLower(basename, ".dc") ||
2184 strSuffixLower(basename, ".dc2"))
2185 return LEVEL_FILE_TYPE_DC;
2187 // check for typical filename of a Sokoban level package file
2188 if (strSuffixLower(basename, ".xsb") &&
2189 strchr(basename, '%') == NULL)
2190 return LEVEL_FILE_TYPE_SB;
2192 // check for typical filename of a Boulder Dash (GDash) level package file
2193 if (checkForPackageFromBasename_BD(basename))
2194 return LEVEL_FILE_TYPE_BD;
2196 // ---------- try to determine file type from filesize ----------
2198 checked_free(filename);
2199 filename = getPath2(getCurrentLevelDir(), basename);
2201 if (stat(filename, &file_status) == 0)
2203 // check for typical filesize of a Supaplex level package file
2204 if (file_status.st_size == 170496)
2205 return LEVEL_FILE_TYPE_SP;
2208 return LEVEL_FILE_TYPE_UNKNOWN;
2211 static int getFileTypeFromMagicBytes(char *filename, int type)
2215 if ((file = openFile(filename, MODE_READ)))
2217 char chunk_name[CHUNK_ID_LEN + 1];
2219 getFileChunkBE(file, chunk_name, NULL);
2221 if (strEqual(chunk_name, "MMII") ||
2222 strEqual(chunk_name, "MIRR"))
2223 type = LEVEL_FILE_TYPE_MM;
2231 static boolean checkForPackageFromBasename(char *basename)
2233 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2234 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2236 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2239 static char *getSingleLevelBasenameExt(int nr, char *extension)
2241 static char basename[MAX_FILENAME_LEN];
2244 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2246 sprintf(basename, "%03d.%s", nr, extension);
2251 static char *getSingleLevelBasename(int nr)
2253 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2256 static char *getPackedLevelBasename(int type)
2258 static char basename[MAX_FILENAME_LEN];
2259 char *directory = getCurrentLevelDir();
2261 DirectoryEntry *dir_entry;
2263 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2265 if ((dir = openDirectory(directory)) == NULL)
2267 Warn("cannot read current level directory '%s'", directory);
2272 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2274 char *entry_basename = dir_entry->basename;
2275 int entry_type = getFileTypeFromBasename(entry_basename);
2277 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2279 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2282 strcpy(basename, entry_basename);
2289 closeDirectory(dir);
2294 static char *getSingleLevelFilename(int nr)
2296 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2299 #if ENABLE_UNUSED_CODE
2300 static char *getPackedLevelFilename(int type)
2302 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2306 char *getDefaultLevelFilename(int nr)
2308 return getSingleLevelFilename(nr);
2311 #if ENABLE_UNUSED_CODE
2312 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2316 lfi->packed = FALSE;
2318 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2319 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2323 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2324 int type, char *format, ...)
2326 static char basename[MAX_FILENAME_LEN];
2329 va_start(ap, format);
2330 vsprintf(basename, format, ap);
2334 lfi->packed = FALSE;
2336 setString(&lfi->basename, basename);
2337 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2340 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2346 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2347 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2350 static int getFiletypeFromID(char *filetype_id)
2352 char *filetype_id_lower;
2353 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2356 if (filetype_id == NULL)
2357 return LEVEL_FILE_TYPE_UNKNOWN;
2359 filetype_id_lower = getStringToLower(filetype_id);
2361 for (i = 0; filetype_id_list[i].id != NULL; i++)
2363 char *id_lower = getStringToLower(filetype_id_list[i].id);
2365 if (strEqual(filetype_id_lower, id_lower))
2366 filetype = filetype_id_list[i].filetype;
2370 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2374 free(filetype_id_lower);
2379 char *getLocalLevelTemplateFilename(void)
2381 return getDefaultLevelFilename(-1);
2384 char *getGlobalLevelTemplateFilename(void)
2386 // global variable "leveldir_current" must be modified in the loop below
2387 LevelDirTree *leveldir_current_last = leveldir_current;
2388 char *filename = NULL;
2390 // check for template level in path from current to topmost tree node
2392 while (leveldir_current != NULL)
2394 filename = getDefaultLevelFilename(-1);
2396 if (fileExists(filename))
2399 leveldir_current = leveldir_current->node_parent;
2402 // restore global variable "leveldir_current" modified in above loop
2403 leveldir_current = leveldir_current_last;
2408 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2412 // special case: level number is negative => check for level template file
2415 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2416 getSingleLevelBasename(-1));
2418 // replace local level template filename with global template filename
2419 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2421 // no fallback if template file not existing
2425 // special case: check for file name/pattern specified in "levelinfo.conf"
2426 if (leveldir_current->level_filename != NULL)
2428 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2430 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2431 leveldir_current->level_filename, nr);
2433 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2435 if (fileExists(lfi->filename))
2438 else if (leveldir_current->level_filetype != NULL)
2440 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2442 // check for specified native level file with standard file name
2443 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2444 "%03d.%s", nr, LEVELFILE_EXTENSION);
2445 if (fileExists(lfi->filename))
2449 // check for native Rocks'n'Diamonds level file
2450 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2451 "%03d.%s", nr, LEVELFILE_EXTENSION);
2452 if (fileExists(lfi->filename))
2455 // check for native Boulder Dash level file
2456 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2457 if (fileExists(lfi->filename))
2460 // check for Emerald Mine level file (V1)
2461 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2462 'a' + (nr / 10) % 26, '0' + nr % 10);
2463 if (fileExists(lfi->filename))
2465 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2466 'A' + (nr / 10) % 26, '0' + nr % 10);
2467 if (fileExists(lfi->filename))
2470 // check for Emerald Mine level file (V2 to V5)
2471 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2472 if (fileExists(lfi->filename))
2475 // check for Emerald Mine level file (V6 / single mode)
2476 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2477 if (fileExists(lfi->filename))
2479 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2480 if (fileExists(lfi->filename))
2483 // check for Emerald Mine level file (V6 / teamwork mode)
2484 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2485 if (fileExists(lfi->filename))
2487 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2488 if (fileExists(lfi->filename))
2491 // check for various packed level file formats
2492 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2493 if (fileExists(lfi->filename))
2496 // no known level file found -- use default values (and fail later)
2497 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2498 "%03d.%s", nr, LEVELFILE_EXTENSION);
2501 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2503 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2504 lfi->type = getFileTypeFromBasename(lfi->basename);
2506 if (lfi->type == LEVEL_FILE_TYPE_RND)
2507 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2510 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2512 // always start with reliable default values
2513 setFileInfoToDefaults(level_file_info);
2515 level_file_info->nr = nr; // set requested level number
2517 determineLevelFileInfo_Filename(level_file_info);
2518 determineLevelFileInfo_Filetype(level_file_info);
2521 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2522 struct LevelFileInfo *lfi_to)
2524 lfi_to->nr = lfi_from->nr;
2525 lfi_to->type = lfi_from->type;
2526 lfi_to->packed = lfi_from->packed;
2528 setString(&lfi_to->basename, lfi_from->basename);
2529 setString(&lfi_to->filename, lfi_from->filename);
2532 // ----------------------------------------------------------------------------
2533 // functions for loading R'n'D level
2534 // ----------------------------------------------------------------------------
2536 int getMappedElement(int element)
2538 // remap some (historic, now obsolete) elements
2542 case EL_PLAYER_OBSOLETE:
2543 element = EL_PLAYER_1;
2546 case EL_KEY_OBSOLETE:
2550 case EL_EM_KEY_1_FILE_OBSOLETE:
2551 element = EL_EM_KEY_1;
2554 case EL_EM_KEY_2_FILE_OBSOLETE:
2555 element = EL_EM_KEY_2;
2558 case EL_EM_KEY_3_FILE_OBSOLETE:
2559 element = EL_EM_KEY_3;
2562 case EL_EM_KEY_4_FILE_OBSOLETE:
2563 element = EL_EM_KEY_4;
2566 case EL_ENVELOPE_OBSOLETE:
2567 element = EL_ENVELOPE_1;
2575 if (element >= NUM_FILE_ELEMENTS)
2577 Warn("invalid level element %d", element);
2579 element = EL_UNKNOWN;
2587 static int getMappedElementByVersion(int element, int game_version)
2589 // remap some elements due to certain game version
2591 if (game_version <= VERSION_IDENT(2,2,0,0))
2593 // map game font elements
2594 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2595 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2596 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2597 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2600 if (game_version < VERSION_IDENT(3,0,0,0))
2602 // map Supaplex gravity tube elements
2603 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2604 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2605 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2606 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2613 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2615 level->file_version = getFileVersion(file);
2616 level->game_version = getFileVersion(file);
2621 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2623 level->creation_date.year = getFile16BitBE(file);
2624 level->creation_date.month = getFile8Bit(file);
2625 level->creation_date.day = getFile8Bit(file);
2627 level->creation_date.src = DATE_SRC_LEVELFILE;
2632 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2634 int initial_player_stepsize;
2635 int initial_player_gravity;
2638 level->fieldx = getFile8Bit(file);
2639 level->fieldy = getFile8Bit(file);
2641 level->time = getFile16BitBE(file);
2642 level->gems_needed = getFile16BitBE(file);
2644 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2645 level->name[i] = getFile8Bit(file);
2646 level->name[MAX_LEVEL_NAME_LEN] = 0;
2648 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2649 level->score[i] = getFile8Bit(file);
2651 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2652 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2653 for (y = 0; y < 3; y++)
2654 for (x = 0; x < 3; x++)
2655 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2657 level->amoeba_speed = getFile8Bit(file);
2658 level->time_magic_wall = getFile8Bit(file);
2659 level->time_wheel = getFile8Bit(file);
2660 level->amoeba_content = getMappedElement(getFile8Bit(file));
2662 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2665 for (i = 0; i < MAX_PLAYERS; i++)
2666 level->initial_player_stepsize[i] = initial_player_stepsize;
2668 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2670 for (i = 0; i < MAX_PLAYERS; i++)
2671 level->initial_player_gravity[i] = initial_player_gravity;
2673 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2674 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2676 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2678 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2679 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2680 level->can_move_into_acid_bits = getFile32BitBE(file);
2681 level->dont_collide_with_bits = getFile8Bit(file);
2683 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2684 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2686 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2687 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2688 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2690 level->game_engine_type = getFile8Bit(file);
2692 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2697 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2701 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2702 level->name[i] = getFile8Bit(file);
2703 level->name[MAX_LEVEL_NAME_LEN] = 0;
2708 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2712 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2713 level->author[i] = getFile8Bit(file);
2714 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2719 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2722 int chunk_size_expected = level->fieldx * level->fieldy;
2724 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2725 stored with 16-bit encoding (and should be twice as big then).
2726 Even worse, playfield data was stored 16-bit when only yamyam content
2727 contained 16-bit elements and vice versa. */
2729 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2730 chunk_size_expected *= 2;
2732 if (chunk_size_expected != chunk_size)
2734 ReadUnusedBytesFromFile(file, chunk_size);
2735 return chunk_size_expected;
2738 for (y = 0; y < level->fieldy; y++)
2739 for (x = 0; x < level->fieldx; x++)
2740 level->field[x][y] =
2741 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2746 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2749 int header_size = 4;
2750 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2751 int chunk_size_expected = header_size + content_size;
2753 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2754 stored with 16-bit encoding (and should be twice as big then).
2755 Even worse, playfield data was stored 16-bit when only yamyam content
2756 contained 16-bit elements and vice versa. */
2758 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2759 chunk_size_expected += content_size;
2761 if (chunk_size_expected != chunk_size)
2763 ReadUnusedBytesFromFile(file, chunk_size);
2764 return chunk_size_expected;
2768 level->num_yamyam_contents = getFile8Bit(file);
2772 // correct invalid number of content fields -- should never happen
2773 if (level->num_yamyam_contents < 1 ||
2774 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2775 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2777 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2778 for (y = 0; y < 3; y++)
2779 for (x = 0; x < 3; x++)
2780 level->yamyam_content[i].e[x][y] =
2781 getMappedElement(level->encoding_16bit_field ?
2782 getFile16BitBE(file) : getFile8Bit(file));
2786 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2791 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2793 element = getMappedElement(getFile16BitBE(file));
2794 num_contents = getFile8Bit(file);
2796 getFile8Bit(file); // content x size (unused)
2797 getFile8Bit(file); // content y size (unused)
2799 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2801 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2802 for (y = 0; y < 3; y++)
2803 for (x = 0; x < 3; x++)
2804 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2806 // correct invalid number of content fields -- should never happen
2807 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2808 num_contents = STD_ELEMENT_CONTENTS;
2810 if (element == EL_YAMYAM)
2812 level->num_yamyam_contents = num_contents;
2814 for (i = 0; i < num_contents; i++)
2815 for (y = 0; y < 3; y++)
2816 for (x = 0; x < 3; x++)
2817 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2819 else if (element == EL_BD_AMOEBA)
2821 level->amoeba_content = content_array[0][0][0];
2825 Warn("cannot load content for element '%d'", element);
2831 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2837 int chunk_size_expected;
2839 element = getMappedElement(getFile16BitBE(file));
2840 if (!IS_ENVELOPE(element))
2841 element = EL_ENVELOPE_1;
2843 envelope_nr = element - EL_ENVELOPE_1;
2845 envelope_len = getFile16BitBE(file);
2847 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2848 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2850 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2852 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2853 if (chunk_size_expected != chunk_size)
2855 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2856 return chunk_size_expected;
2859 for (i = 0; i < envelope_len; i++)
2860 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2865 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2867 int num_changed_custom_elements = getFile16BitBE(file);
2868 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2871 if (chunk_size_expected != chunk_size)
2873 ReadUnusedBytesFromFile(file, chunk_size - 2);
2874 return chunk_size_expected;
2877 for (i = 0; i < num_changed_custom_elements; i++)
2879 int element = getMappedElement(getFile16BitBE(file));
2880 int properties = getFile32BitBE(file);
2882 if (IS_CUSTOM_ELEMENT(element))
2883 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2885 Warn("invalid custom element number %d", element);
2887 // older game versions that wrote level files with CUS1 chunks used
2888 // different default push delay values (not yet stored in level file)
2889 element_info[element].push_delay_fixed = 2;
2890 element_info[element].push_delay_random = 8;
2893 level->file_has_custom_elements = TRUE;
2898 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2900 int num_changed_custom_elements = getFile16BitBE(file);
2901 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2904 if (chunk_size_expected != chunk_size)
2906 ReadUnusedBytesFromFile(file, chunk_size - 2);
2907 return chunk_size_expected;
2910 for (i = 0; i < num_changed_custom_elements; i++)
2912 int element = getMappedElement(getFile16BitBE(file));
2913 int custom_target_element = getMappedElement(getFile16BitBE(file));
2915 if (IS_CUSTOM_ELEMENT(element))
2916 element_info[element].change->target_element = custom_target_element;
2918 Warn("invalid custom element number %d", element);
2921 level->file_has_custom_elements = TRUE;
2926 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2928 int num_changed_custom_elements = getFile16BitBE(file);
2929 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2932 if (chunk_size_expected != chunk_size)
2934 ReadUnusedBytesFromFile(file, chunk_size - 2);
2935 return chunk_size_expected;
2938 for (i = 0; i < num_changed_custom_elements; i++)
2940 int element = getMappedElement(getFile16BitBE(file));
2941 struct ElementInfo *ei = &element_info[element];
2942 unsigned int event_bits;
2944 if (!IS_CUSTOM_ELEMENT(element))
2946 Warn("invalid custom element number %d", element);
2948 element = EL_INTERNAL_DUMMY;
2951 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2952 ei->description[j] = getFile8Bit(file);
2953 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2955 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2957 // some free bytes for future properties and padding
2958 ReadUnusedBytesFromFile(file, 7);
2960 ei->use_gfx_element = getFile8Bit(file);
2961 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2963 ei->collect_score_initial = getFile8Bit(file);
2964 ei->collect_count_initial = getFile8Bit(file);
2966 ei->push_delay_fixed = getFile16BitBE(file);
2967 ei->push_delay_random = getFile16BitBE(file);
2968 ei->move_delay_fixed = getFile16BitBE(file);
2969 ei->move_delay_random = getFile16BitBE(file);
2971 ei->move_pattern = getFile16BitBE(file);
2972 ei->move_direction_initial = getFile8Bit(file);
2973 ei->move_stepsize = getFile8Bit(file);
2975 for (y = 0; y < 3; y++)
2976 for (x = 0; x < 3; x++)
2977 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2979 // bits 0 - 31 of "has_event[]"
2980 event_bits = getFile32BitBE(file);
2981 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2982 if (event_bits & (1u << j))
2983 ei->change->has_event[j] = TRUE;
2985 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2987 ei->change->delay_fixed = getFile16BitBE(file);
2988 ei->change->delay_random = getFile16BitBE(file);
2989 ei->change->delay_frames = getFile16BitBE(file);
2991 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2993 ei->change->explode = getFile8Bit(file);
2994 ei->change->use_target_content = getFile8Bit(file);
2995 ei->change->only_if_complete = getFile8Bit(file);
2996 ei->change->use_random_replace = getFile8Bit(file);
2998 ei->change->random_percentage = getFile8Bit(file);
2999 ei->change->replace_when = getFile8Bit(file);
3001 for (y = 0; y < 3; y++)
3002 for (x = 0; x < 3; x++)
3003 ei->change->target_content.e[x][y] =
3004 getMappedElement(getFile16BitBE(file));
3006 ei->slippery_type = getFile8Bit(file);
3008 // some free bytes for future properties and padding
3009 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3011 // mark that this custom element has been modified
3012 ei->modified_settings = TRUE;
3015 level->file_has_custom_elements = TRUE;
3020 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3022 struct ElementInfo *ei;
3023 int chunk_size_expected;
3027 // ---------- custom element base property values (96 bytes) ----------------
3029 element = getMappedElement(getFile16BitBE(file));
3031 if (!IS_CUSTOM_ELEMENT(element))
3033 Warn("invalid custom element number %d", element);
3035 ReadUnusedBytesFromFile(file, chunk_size - 2);
3040 ei = &element_info[element];
3042 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3043 ei->description[i] = getFile8Bit(file);
3044 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3046 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3048 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3050 ei->num_change_pages = getFile8Bit(file);
3052 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3053 if (chunk_size_expected != chunk_size)
3055 ReadUnusedBytesFromFile(file, chunk_size - 43);
3056 return chunk_size_expected;
3059 ei->ce_value_fixed_initial = getFile16BitBE(file);
3060 ei->ce_value_random_initial = getFile16BitBE(file);
3061 ei->use_last_ce_value = getFile8Bit(file);
3063 ei->use_gfx_element = getFile8Bit(file);
3064 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3066 ei->collect_score_initial = getFile8Bit(file);
3067 ei->collect_count_initial = getFile8Bit(file);
3069 ei->drop_delay_fixed = getFile8Bit(file);
3070 ei->push_delay_fixed = getFile8Bit(file);
3071 ei->drop_delay_random = getFile8Bit(file);
3072 ei->push_delay_random = getFile8Bit(file);
3073 ei->move_delay_fixed = getFile16BitBE(file);
3074 ei->move_delay_random = getFile16BitBE(file);
3076 // bits 0 - 15 of "move_pattern" ...
3077 ei->move_pattern = getFile16BitBE(file);
3078 ei->move_direction_initial = getFile8Bit(file);
3079 ei->move_stepsize = getFile8Bit(file);
3081 ei->slippery_type = getFile8Bit(file);
3083 for (y = 0; y < 3; y++)
3084 for (x = 0; x < 3; x++)
3085 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3087 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3088 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3089 ei->move_leave_type = getFile8Bit(file);
3091 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3092 ei->move_pattern |= (getFile16BitBE(file) << 16);
3094 ei->access_direction = getFile8Bit(file);
3096 ei->explosion_delay = getFile8Bit(file);
3097 ei->ignition_delay = getFile8Bit(file);
3098 ei->explosion_type = getFile8Bit(file);
3100 // some free bytes for future custom property values and padding
3101 ReadUnusedBytesFromFile(file, 1);
3103 // ---------- change page property values (48 bytes) ------------------------
3105 setElementChangePages(ei, ei->num_change_pages);
3107 for (i = 0; i < ei->num_change_pages; i++)
3109 struct ElementChangeInfo *change = &ei->change_page[i];
3110 unsigned int event_bits;
3112 // always start with reliable default values
3113 setElementChangeInfoToDefaults(change);
3115 // bits 0 - 31 of "has_event[]" ...
3116 event_bits = getFile32BitBE(file);
3117 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3118 if (event_bits & (1u << j))
3119 change->has_event[j] = TRUE;
3121 change->target_element = getMappedElement(getFile16BitBE(file));
3123 change->delay_fixed = getFile16BitBE(file);
3124 change->delay_random = getFile16BitBE(file);
3125 change->delay_frames = getFile16BitBE(file);
3127 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3129 change->explode = getFile8Bit(file);
3130 change->use_target_content = getFile8Bit(file);
3131 change->only_if_complete = getFile8Bit(file);
3132 change->use_random_replace = getFile8Bit(file);
3134 change->random_percentage = getFile8Bit(file);
3135 change->replace_when = getFile8Bit(file);
3137 for (y = 0; y < 3; y++)
3138 for (x = 0; x < 3; x++)
3139 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3141 change->can_change = getFile8Bit(file);
3143 change->trigger_side = getFile8Bit(file);
3145 change->trigger_player = getFile8Bit(file);
3146 change->trigger_page = getFile8Bit(file);
3148 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3149 CH_PAGE_ANY : (1 << change->trigger_page));
3151 change->has_action = getFile8Bit(file);
3152 change->action_type = getFile8Bit(file);
3153 change->action_mode = getFile8Bit(file);
3154 change->action_arg = getFile16BitBE(file);
3156 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3157 event_bits = getFile8Bit(file);
3158 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3159 if (event_bits & (1u << (j - 32)))
3160 change->has_event[j] = TRUE;
3163 // mark this custom element as modified
3164 ei->modified_settings = TRUE;
3166 level->file_has_custom_elements = TRUE;
3171 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3173 struct ElementInfo *ei;
3174 struct ElementGroupInfo *group;
3178 element = getMappedElement(getFile16BitBE(file));
3180 if (!IS_GROUP_ELEMENT(element))
3182 Warn("invalid group element number %d", element);
3184 ReadUnusedBytesFromFile(file, chunk_size - 2);
3189 ei = &element_info[element];
3191 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3192 ei->description[i] = getFile8Bit(file);
3193 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3195 group = element_info[element].group;
3197 group->num_elements = getFile8Bit(file);
3199 ei->use_gfx_element = getFile8Bit(file);
3200 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3202 group->choice_mode = getFile8Bit(file);
3204 // some free bytes for future values and padding
3205 ReadUnusedBytesFromFile(file, 3);
3207 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3208 group->element[i] = getMappedElement(getFile16BitBE(file));
3210 // mark this group element as modified
3211 element_info[element].modified_settings = TRUE;
3213 level->file_has_custom_elements = TRUE;
3218 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3219 int element, int real_element)
3221 int micro_chunk_size = 0;
3222 int conf_type = getFile8Bit(file);
3223 int byte_mask = conf_type & CONF_MASK_BYTES;
3224 boolean element_found = FALSE;
3227 micro_chunk_size += 1;
3229 if (byte_mask == CONF_MASK_MULTI_BYTES)
3231 int num_bytes = getFile16BitBE(file);
3232 byte *buffer = checked_malloc(num_bytes);
3234 ReadBytesFromFile(file, buffer, num_bytes);
3236 for (i = 0; conf[i].data_type != -1; i++)
3238 if (conf[i].element == element &&
3239 conf[i].conf_type == conf_type)
3241 int data_type = conf[i].data_type;
3242 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3243 int max_num_entities = conf[i].max_num_entities;
3245 if (num_entities > max_num_entities)
3247 Warn("truncating number of entities for element %d from %d to %d",
3248 element, num_entities, max_num_entities);
3250 num_entities = max_num_entities;
3253 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3254 data_type == TYPE_CONTENT_LIST))
3256 // for element and content lists, zero entities are not allowed
3257 Warn("found empty list of entities for element %d", element);
3259 // do not set "num_entities" here to prevent reading behind buffer
3261 *(int *)(conf[i].num_entities) = 1; // at least one is required
3265 *(int *)(conf[i].num_entities) = num_entities;
3268 element_found = TRUE;
3270 if (data_type == TYPE_STRING)
3272 char *string = (char *)(conf[i].value);
3275 for (j = 0; j < max_num_entities; j++)
3276 string[j] = (j < num_entities ? buffer[j] : '\0');
3278 else if (data_type == TYPE_ELEMENT_LIST)
3280 int *element_array = (int *)(conf[i].value);
3283 for (j = 0; j < num_entities; j++)
3285 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3287 else if (data_type == TYPE_CONTENT_LIST)
3289 struct Content *content= (struct Content *)(conf[i].value);
3292 for (c = 0; c < num_entities; c++)
3293 for (y = 0; y < 3; y++)
3294 for (x = 0; x < 3; x++)
3295 content[c].e[x][y] =
3296 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3299 element_found = FALSE;
3305 checked_free(buffer);
3307 micro_chunk_size += 2 + num_bytes;
3309 else // constant size configuration data (1, 2 or 4 bytes)
3311 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3312 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3313 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3315 for (i = 0; conf[i].data_type != -1; i++)
3317 if (conf[i].element == element &&
3318 conf[i].conf_type == conf_type)
3320 int data_type = conf[i].data_type;
3322 if (data_type == TYPE_ELEMENT)
3323 value = getMappedElement(value);
3325 if (data_type == TYPE_BOOLEAN)
3326 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3328 *(int *) (conf[i].value) = value;
3330 element_found = TRUE;
3336 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3341 char *error_conf_chunk_bytes =
3342 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3343 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3344 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3345 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3346 int error_element = real_element;
3348 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3349 error_conf_chunk_bytes, error_conf_chunk_token,
3350 error_element, EL_NAME(error_element));
3353 return micro_chunk_size;
3356 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3358 int real_chunk_size = 0;
3360 li = *level; // copy level data into temporary buffer
3362 while (!checkEndOfFile(file))
3364 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3366 if (real_chunk_size >= chunk_size)
3370 *level = li; // copy temporary buffer back to level data
3372 return real_chunk_size;
3375 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3377 int real_chunk_size = 0;
3379 li = *level; // copy level data into temporary buffer
3381 while (!checkEndOfFile(file))
3383 int element = getMappedElement(getFile16BitBE(file));
3385 real_chunk_size += 2;
3386 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3388 if (real_chunk_size >= chunk_size)
3392 *level = li; // copy temporary buffer back to level data
3394 return real_chunk_size;
3397 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3399 int real_chunk_size = 0;
3401 li = *level; // copy level data into temporary buffer
3403 while (!checkEndOfFile(file))
3405 int element = getMappedElement(getFile16BitBE(file));
3407 real_chunk_size += 2;
3408 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3410 if (real_chunk_size >= chunk_size)
3414 *level = li; // copy temporary buffer back to level data
3416 return real_chunk_size;
3419 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3421 int element = getMappedElement(getFile16BitBE(file));
3422 int envelope_nr = element - EL_ENVELOPE_1;
3423 int real_chunk_size = 2;
3425 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3427 while (!checkEndOfFile(file))
3429 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3432 if (real_chunk_size >= chunk_size)
3436 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3438 return real_chunk_size;
3441 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3443 int element = getMappedElement(getFile16BitBE(file));
3444 int real_chunk_size = 2;
3445 struct ElementInfo *ei = &element_info[element];
3448 xx_ei = *ei; // copy element data into temporary buffer
3450 xx_ei.num_change_pages = -1;
3452 while (!checkEndOfFile(file))
3454 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3456 if (xx_ei.num_change_pages != -1)
3459 if (real_chunk_size >= chunk_size)
3465 if (ei->num_change_pages == -1)
3467 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3470 ei->num_change_pages = 1;
3472 setElementChangePages(ei, 1);
3473 setElementChangeInfoToDefaults(ei->change);
3475 return real_chunk_size;
3478 // initialize number of change pages stored for this custom element
3479 setElementChangePages(ei, ei->num_change_pages);
3480 for (i = 0; i < ei->num_change_pages; i++)
3481 setElementChangeInfoToDefaults(&ei->change_page[i]);
3483 // start with reading properties for the first change page
3484 xx_current_change_page = 0;
3486 while (!checkEndOfFile(file))
3488 // level file might contain invalid change page number
3489 if (xx_current_change_page >= ei->num_change_pages)
3492 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3494 xx_change = *change; // copy change data into temporary buffer
3496 resetEventBits(); // reset bits; change page might have changed
3498 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3501 *change = xx_change;
3503 setEventFlagsFromEventBits(change);
3505 if (real_chunk_size >= chunk_size)
3509 level->file_has_custom_elements = TRUE;
3511 return real_chunk_size;
3514 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3516 int element = getMappedElement(getFile16BitBE(file));
3517 int real_chunk_size = 2;
3518 struct ElementInfo *ei = &element_info[element];
3519 struct ElementGroupInfo *group = ei->group;
3524 xx_ei = *ei; // copy element data into temporary buffer
3525 xx_group = *group; // copy group data into temporary buffer
3527 while (!checkEndOfFile(file))
3529 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3532 if (real_chunk_size >= chunk_size)
3539 level->file_has_custom_elements = TRUE;
3541 return real_chunk_size;
3544 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3546 int element = getMappedElement(getFile16BitBE(file));
3547 int real_chunk_size = 2;
3548 struct ElementInfo *ei = &element_info[element];
3550 xx_ei = *ei; // copy element data into temporary buffer
3552 while (!checkEndOfFile(file))
3554 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3557 if (real_chunk_size >= chunk_size)
3563 level->file_has_custom_elements = TRUE;
3565 return real_chunk_size;
3568 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3569 struct LevelFileInfo *level_file_info,
3570 boolean level_info_only)
3572 char *filename = level_file_info->filename;
3573 char cookie[MAX_LINE_LEN];
3574 char chunk_name[CHUNK_ID_LEN + 1];
3578 if (!(file = openFile(filename, MODE_READ)))
3580 level->no_valid_file = TRUE;
3581 level->no_level_file = TRUE;
3583 if (level_info_only)
3586 Warn("cannot read level '%s' -- using empty level", filename);
3588 if (!setup.editor.use_template_for_new_levels)
3591 // if level file not found, try to initialize level data from template
3592 filename = getGlobalLevelTemplateFilename();
3594 if (!(file = openFile(filename, MODE_READ)))
3597 // default: for empty levels, use level template for custom elements
3598 level->use_custom_template = TRUE;
3600 level->no_valid_file = FALSE;
3603 getFileChunkBE(file, chunk_name, NULL);
3604 if (strEqual(chunk_name, "RND1"))
3606 getFile32BitBE(file); // not used
3608 getFileChunkBE(file, chunk_name, NULL);
3609 if (!strEqual(chunk_name, "CAVE"))
3611 level->no_valid_file = TRUE;
3613 Warn("unknown format of level file '%s'", filename);
3620 else // check for pre-2.0 file format with cookie string
3622 strcpy(cookie, chunk_name);
3623 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3625 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3626 cookie[strlen(cookie) - 1] = '\0';
3628 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3630 level->no_valid_file = TRUE;
3632 Warn("unknown format of level file '%s'", filename);
3639 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3641 level->no_valid_file = TRUE;
3643 Warn("unsupported version of level file '%s'", filename);
3650 // pre-2.0 level files have no game version, so use file version here
3651 level->game_version = level->file_version;
3654 if (level->file_version < FILE_VERSION_1_2)
3656 // level files from versions before 1.2.0 without chunk structure
3657 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3658 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3666 int (*loader)(File *, int, struct LevelInfo *);
3670 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3671 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3672 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3673 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3674 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3675 { "INFO", -1, LoadLevel_INFO },
3676 { "BODY", -1, LoadLevel_BODY },
3677 { "CONT", -1, LoadLevel_CONT },
3678 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3679 { "CNT3", -1, LoadLevel_CNT3 },
3680 { "CUS1", -1, LoadLevel_CUS1 },
3681 { "CUS2", -1, LoadLevel_CUS2 },
3682 { "CUS3", -1, LoadLevel_CUS3 },
3683 { "CUS4", -1, LoadLevel_CUS4 },
3684 { "GRP1", -1, LoadLevel_GRP1 },
3685 { "CONF", -1, LoadLevel_CONF },
3686 { "ELEM", -1, LoadLevel_ELEM },
3687 { "NOTE", -1, LoadLevel_NOTE },
3688 { "CUSX", -1, LoadLevel_CUSX },
3689 { "GRPX", -1, LoadLevel_GRPX },
3690 { "EMPX", -1, LoadLevel_EMPX },
3695 while (getFileChunkBE(file, chunk_name, &chunk_size))
3699 while (chunk_info[i].name != NULL &&
3700 !strEqual(chunk_name, chunk_info[i].name))
3703 if (chunk_info[i].name == NULL)
3705 Warn("unknown chunk '%s' in level file '%s'",
3706 chunk_name, filename);
3708 ReadUnusedBytesFromFile(file, chunk_size);
3710 else if (chunk_info[i].size != -1 &&
3711 chunk_info[i].size != chunk_size)
3713 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3714 chunk_size, chunk_name, filename);
3716 ReadUnusedBytesFromFile(file, chunk_size);
3720 // call function to load this level chunk
3721 int chunk_size_expected =
3722 (chunk_info[i].loader)(file, chunk_size, level);
3724 if (chunk_size_expected < 0)
3726 Warn("error reading chunk '%s' in level file '%s'",
3727 chunk_name, filename);
3732 // the size of some chunks cannot be checked before reading other
3733 // chunks first (like "HEAD" and "BODY") that contain some header
3734 // information, so check them here
3735 if (chunk_size_expected != chunk_size)
3737 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3738 chunk_size, chunk_name, filename);
3750 // ----------------------------------------------------------------------------
3751 // functions for loading BD level
3752 // ----------------------------------------------------------------------------
3754 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3756 struct LevelInfo_BD *level_bd = level->native_bd_level;
3757 GdCave *cave = NULL; // will be changed below
3758 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3759 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3762 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3764 // cave and map newly allocated when set to defaults above
3765 cave = level_bd->cave;
3767 for (i = 0; i < 5; i++)
3769 cave->level_time[i] = level->time;
3770 cave->level_diamonds[i] = level->gems_needed;
3771 cave->level_magic_wall_time[i] = level->time_magic_wall;
3773 cave->level_speed[i] = level->bd_cycle_delay_ms;
3774 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3775 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3776 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3778 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3781 cave->diamond_value = level->score[SC_EMERALD];
3782 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3784 cave->scheduling = level->bd_scheduling_type;
3785 cave->pal_timing = level->bd_pal_timing;
3786 cave->intermission = level->bd_intermission;
3787 cave->diagonal_movements = level->bd_diagonal_movements;
3789 cave->lineshift = level->bd_line_shifting_borders;
3790 cave->wraparound_objects = level->bd_wraparound_objects;
3791 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3793 strncpy(cave->name, level->name, sizeof(GdString));
3794 cave->name[sizeof(GdString) - 1] = '\0';
3796 for (x = 0; x < cave->w; x++)
3797 for (y = 0; y < cave->h; y++)
3798 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3801 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3803 struct LevelInfo_BD *level_bd = level->native_bd_level;
3804 GdCave *cave = level_bd->cave;
3805 int bd_level_nr = level_bd->level_nr;
3808 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3809 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3811 level->time = cave->level_time[bd_level_nr];
3812 level->gems_needed = cave->level_diamonds[bd_level_nr];
3813 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3815 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3816 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3817 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3818 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3820 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3821 level->score[SC_EMERALD] = cave->diamond_value;
3822 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3824 level->bd_scheduling_type = cave->scheduling;
3825 level->bd_pal_timing = cave->pal_timing;
3826 level->bd_intermission = cave->intermission;
3827 level->bd_diagonal_movements = cave->diagonal_movements;
3829 level->bd_line_shifting_borders = cave->lineshift;
3830 level->bd_wraparound_objects = cave->wraparound_objects;
3831 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3833 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3835 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3836 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3838 for (x = 0; x < level->fieldx; x++)
3839 for (y = 0; y < level->fieldy; y++)
3840 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3842 checked_free(cave_name);
3845 static void setTapeInfoToDefaults(void);
3847 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3849 struct LevelInfo_BD *level_bd = level->native_bd_level;
3850 GdCave *cave = level_bd->cave;
3851 GdReplay *replay = level_bd->replay;
3857 // always start with reliable default values
3858 setTapeInfoToDefaults();
3860 tape.level_nr = level_nr; // (currently not used)
3861 tape.random_seed = replay->seed;
3863 TapeSetDateFromIsoDateString(replay->date);
3866 tape.pos[tape.counter].delay = 0;
3868 tape.bd_replay = TRUE;
3870 // all time calculations only used to display approximate tape time
3871 int cave_speed = cave->speed;
3872 int milliseconds_game = 0;
3873 int milliseconds_elapsed = 20;
3875 for (i = 0; i < replay->movements->len; i++)
3877 int replay_action = replay->movements->data[i];
3878 int tape_action = map_action_BD_to_RND(replay_action);
3879 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3880 boolean success = 0;
3884 success = TapeAddAction(action);
3886 milliseconds_game += milliseconds_elapsed;
3888 if (milliseconds_game >= cave_speed)
3890 milliseconds_game -= cave_speed;
3897 tape.pos[tape.counter].delay = 0;
3898 tape.pos[tape.counter].action[0] = 0;
3902 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3908 TapeHaltRecording();
3912 // ----------------------------------------------------------------------------
3913 // functions for loading EM level
3914 // ----------------------------------------------------------------------------
3916 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3918 static int ball_xy[8][2] =
3929 struct LevelInfo_EM *level_em = level->native_em_level;
3930 struct CAVE *cav = level_em->cav;
3933 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3934 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3936 cav->time_seconds = level->time;
3937 cav->gems_needed = level->gems_needed;
3939 cav->emerald_score = level->score[SC_EMERALD];
3940 cav->diamond_score = level->score[SC_DIAMOND];
3941 cav->alien_score = level->score[SC_ROBOT];
3942 cav->tank_score = level->score[SC_SPACESHIP];
3943 cav->bug_score = level->score[SC_BUG];
3944 cav->eater_score = level->score[SC_YAMYAM];
3945 cav->nut_score = level->score[SC_NUT];
3946 cav->dynamite_score = level->score[SC_DYNAMITE];
3947 cav->key_score = level->score[SC_KEY];
3948 cav->exit_score = level->score[SC_TIME_BONUS];
3950 cav->num_eater_arrays = level->num_yamyam_contents;
3952 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3953 for (y = 0; y < 3; y++)
3954 for (x = 0; x < 3; x++)
3955 cav->eater_array[i][y * 3 + x] =
3956 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3958 cav->amoeba_time = level->amoeba_speed;
3959 cav->wonderwall_time = level->time_magic_wall;
3960 cav->wheel_time = level->time_wheel;
3962 cav->android_move_time = level->android_move_time;
3963 cav->android_clone_time = level->android_clone_time;
3964 cav->ball_random = level->ball_random;
3965 cav->ball_active = level->ball_active_initial;
3966 cav->ball_time = level->ball_time;
3967 cav->num_ball_arrays = level->num_ball_contents;
3969 cav->lenses_score = level->lenses_score;
3970 cav->magnify_score = level->magnify_score;
3971 cav->slurp_score = level->slurp_score;
3973 cav->lenses_time = level->lenses_time;
3974 cav->magnify_time = level->magnify_time;
3976 cav->wind_time = 9999;
3977 cav->wind_direction =
3978 map_direction_RND_to_EM(level->wind_direction_initial);
3980 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3981 for (j = 0; j < 8; j++)
3982 cav->ball_array[i][j] =
3983 map_element_RND_to_EM_cave(level->ball_content[i].
3984 e[ball_xy[j][0]][ball_xy[j][1]]);
3986 map_android_clone_elements_RND_to_EM(level);
3988 // first fill the complete playfield with the empty space element
3989 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3990 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3991 cav->cave[x][y] = Cblank;
3993 // then copy the real level contents from level file into the playfield
3994 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3996 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3998 if (level->field[x][y] == EL_AMOEBA_DEAD)
3999 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4001 cav->cave[x][y] = new_element;
4004 for (i = 0; i < MAX_PLAYERS; i++)
4006 cav->player_x[i] = -1;
4007 cav->player_y[i] = -1;
4010 // initialize player positions and delete players from the playfield
4011 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4013 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4015 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4017 cav->player_x[player_nr] = x;
4018 cav->player_y[player_nr] = y;
4020 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4025 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4027 static int ball_xy[8][2] =
4038 struct LevelInfo_EM *level_em = level->native_em_level;
4039 struct CAVE *cav = level_em->cav;
4042 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4043 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4045 level->time = cav->time_seconds;
4046 level->gems_needed = cav->gems_needed;
4048 sprintf(level->name, "Level %d", level->file_info.nr);
4050 level->score[SC_EMERALD] = cav->emerald_score;
4051 level->score[SC_DIAMOND] = cav->diamond_score;
4052 level->score[SC_ROBOT] = cav->alien_score;
4053 level->score[SC_SPACESHIP] = cav->tank_score;
4054 level->score[SC_BUG] = cav->bug_score;
4055 level->score[SC_YAMYAM] = cav->eater_score;
4056 level->score[SC_NUT] = cav->nut_score;
4057 level->score[SC_DYNAMITE] = cav->dynamite_score;
4058 level->score[SC_KEY] = cav->key_score;
4059 level->score[SC_TIME_BONUS] = cav->exit_score;
4061 level->num_yamyam_contents = cav->num_eater_arrays;
4063 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4064 for (y = 0; y < 3; y++)
4065 for (x = 0; x < 3; x++)
4066 level->yamyam_content[i].e[x][y] =
4067 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4069 level->amoeba_speed = cav->amoeba_time;
4070 level->time_magic_wall = cav->wonderwall_time;
4071 level->time_wheel = cav->wheel_time;
4073 level->android_move_time = cav->android_move_time;
4074 level->android_clone_time = cav->android_clone_time;
4075 level->ball_random = cav->ball_random;
4076 level->ball_active_initial = cav->ball_active;
4077 level->ball_time = cav->ball_time;
4078 level->num_ball_contents = cav->num_ball_arrays;
4080 level->lenses_score = cav->lenses_score;
4081 level->magnify_score = cav->magnify_score;
4082 level->slurp_score = cav->slurp_score;
4084 level->lenses_time = cav->lenses_time;
4085 level->magnify_time = cav->magnify_time;
4087 level->wind_direction_initial =
4088 map_direction_EM_to_RND(cav->wind_direction);
4090 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4091 for (j = 0; j < 8; j++)
4092 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4093 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4095 map_android_clone_elements_EM_to_RND(level);
4097 // convert the playfield (some elements need special treatment)
4098 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4100 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4102 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4103 new_element = EL_AMOEBA_DEAD;
4105 level->field[x][y] = new_element;
4108 for (i = 0; i < MAX_PLAYERS; i++)
4110 // in case of all players set to the same field, use the first player
4111 int nr = MAX_PLAYERS - i - 1;
4112 int jx = cav->player_x[nr];
4113 int jy = cav->player_y[nr];
4115 if (jx != -1 && jy != -1)
4116 level->field[jx][jy] = EL_PLAYER_1 + nr;
4119 // time score is counted for each 10 seconds left in Emerald Mine levels
4120 level->time_score_base = 10;
4124 // ----------------------------------------------------------------------------
4125 // functions for loading SP level
4126 // ----------------------------------------------------------------------------
4128 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4130 struct LevelInfo_SP *level_sp = level->native_sp_level;
4131 LevelInfoType *header = &level_sp->header;
4134 level_sp->width = level->fieldx;
4135 level_sp->height = level->fieldy;
4137 for (x = 0; x < level->fieldx; x++)
4138 for (y = 0; y < level->fieldy; y++)
4139 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4141 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4143 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4144 header->LevelTitle[i] = level->name[i];
4145 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4147 header->InfotronsNeeded = level->gems_needed;
4149 header->SpecialPortCount = 0;
4151 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4153 boolean gravity_port_found = FALSE;
4154 boolean gravity_port_valid = FALSE;
4155 int gravity_port_flag;
4156 int gravity_port_base_element;
4157 int element = level->field[x][y];
4159 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4160 element <= EL_SP_GRAVITY_ON_PORT_UP)
4162 gravity_port_found = TRUE;
4163 gravity_port_valid = TRUE;
4164 gravity_port_flag = 1;
4165 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4167 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4168 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4170 gravity_port_found = TRUE;
4171 gravity_port_valid = TRUE;
4172 gravity_port_flag = 0;
4173 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4175 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4176 element <= EL_SP_GRAVITY_PORT_UP)
4178 // change R'n'D style gravity inverting special port to normal port
4179 // (there are no gravity inverting ports in native Supaplex engine)
4181 gravity_port_found = TRUE;
4182 gravity_port_valid = FALSE;
4183 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4186 if (gravity_port_found)
4188 if (gravity_port_valid &&
4189 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4191 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4193 port->PortLocation = (y * level->fieldx + x) * 2;
4194 port->Gravity = gravity_port_flag;
4196 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4198 header->SpecialPortCount++;
4202 // change special gravity port to normal port
4204 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4207 level_sp->playfield[x][y] = element - EL_SP_START;
4212 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4214 struct LevelInfo_SP *level_sp = level->native_sp_level;
4215 LevelInfoType *header = &level_sp->header;
4216 boolean num_invalid_elements = 0;
4219 level->fieldx = level_sp->width;
4220 level->fieldy = level_sp->height;
4222 for (x = 0; x < level->fieldx; x++)
4224 for (y = 0; y < level->fieldy; y++)
4226 int element_old = level_sp->playfield[x][y];
4227 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4229 if (element_new == EL_UNKNOWN)
4231 num_invalid_elements++;
4233 Debug("level:native:SP", "invalid element %d at position %d, %d",
4237 level->field[x][y] = element_new;
4241 if (num_invalid_elements > 0)
4242 Warn("found %d invalid elements%s", num_invalid_elements,
4243 (!options.debug ? " (use '--debug' for more details)" : ""));
4245 for (i = 0; i < MAX_PLAYERS; i++)
4246 level->initial_player_gravity[i] =
4247 (header->InitialGravity == 1 ? TRUE : FALSE);
4249 // skip leading spaces
4250 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4251 if (header->LevelTitle[i] != ' ')
4255 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4256 level->name[j] = header->LevelTitle[i];
4257 level->name[j] = '\0';
4259 // cut trailing spaces
4261 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4262 level->name[j - 1] = '\0';
4264 level->gems_needed = header->InfotronsNeeded;
4266 for (i = 0; i < header->SpecialPortCount; i++)
4268 SpecialPortType *port = &header->SpecialPort[i];
4269 int port_location = port->PortLocation;
4270 int gravity = port->Gravity;
4271 int port_x, port_y, port_element;
4273 port_x = (port_location / 2) % level->fieldx;
4274 port_y = (port_location / 2) / level->fieldx;
4276 if (port_x < 0 || port_x >= level->fieldx ||
4277 port_y < 0 || port_y >= level->fieldy)
4279 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4284 port_element = level->field[port_x][port_y];
4286 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4287 port_element > EL_SP_GRAVITY_PORT_UP)
4289 Warn("no special port at position (%d, %d)", port_x, port_y);
4294 // change previous (wrong) gravity inverting special port to either
4295 // gravity enabling special port or gravity disabling special port
4296 level->field[port_x][port_y] +=
4297 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4298 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4301 // change special gravity ports without database entries to normal ports
4302 for (x = 0; x < level->fieldx; x++)
4303 for (y = 0; y < level->fieldy; y++)
4304 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4305 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4306 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4308 level->time = 0; // no time limit
4309 level->amoeba_speed = 0;
4310 level->time_magic_wall = 0;
4311 level->time_wheel = 0;
4312 level->amoeba_content = EL_EMPTY;
4314 // original Supaplex does not use score values -- rate by playing time
4315 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4316 level->score[i] = 0;
4318 level->rate_time_over_score = TRUE;
4320 // there are no yamyams in supaplex levels
4321 for (i = 0; i < level->num_yamyam_contents; i++)
4322 for (x = 0; x < 3; x++)
4323 for (y = 0; y < 3; y++)
4324 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4327 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4329 struct LevelInfo_SP *level_sp = level->native_sp_level;
4330 struct DemoInfo_SP *demo = &level_sp->demo;
4333 // always start with reliable default values
4334 demo->is_available = FALSE;
4337 if (TAPE_IS_EMPTY(tape))
4340 demo->level_nr = tape.level_nr; // (currently not used)
4342 level_sp->header.DemoRandomSeed = tape.random_seed;
4346 for (i = 0; i < tape.length; i++)
4348 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4349 int demo_repeat = tape.pos[i].delay;
4350 int demo_entries = (demo_repeat + 15) / 16;
4352 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4354 Warn("tape truncated: size exceeds maximum SP demo size %d",
4360 for (j = 0; j < demo_repeat / 16; j++)
4361 demo->data[demo->length++] = 0xf0 | demo_action;
4363 if (demo_repeat % 16)
4364 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4367 demo->is_available = TRUE;
4370 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4372 struct LevelInfo_SP *level_sp = level->native_sp_level;
4373 struct DemoInfo_SP *demo = &level_sp->demo;
4374 char *filename = level->file_info.filename;
4377 // always start with reliable default values
4378 setTapeInfoToDefaults();
4380 if (!demo->is_available)
4383 tape.level_nr = demo->level_nr; // (currently not used)
4384 tape.random_seed = level_sp->header.DemoRandomSeed;
4386 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4389 tape.pos[tape.counter].delay = 0;
4391 for (i = 0; i < demo->length; i++)
4393 int demo_action = demo->data[i] & 0x0f;
4394 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4395 int tape_action = map_key_SP_to_RND(demo_action);
4396 int tape_repeat = demo_repeat + 1;
4397 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4398 boolean success = 0;
4401 for (j = 0; j < tape_repeat; j++)
4402 success = TapeAddAction(action);
4406 Warn("SP demo truncated: size exceeds maximum tape size %d",
4413 TapeHaltRecording();
4417 // ----------------------------------------------------------------------------
4418 // functions for loading MM level
4419 // ----------------------------------------------------------------------------
4421 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4423 struct LevelInfo_MM *level_mm = level->native_mm_level;
4426 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4427 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4429 level_mm->time = level->time;
4430 level_mm->kettles_needed = level->gems_needed;
4431 level_mm->auto_count_kettles = level->auto_count_gems;
4433 level_mm->mm_laser_red = level->mm_laser_red;
4434 level_mm->mm_laser_green = level->mm_laser_green;
4435 level_mm->mm_laser_blue = level->mm_laser_blue;
4437 level_mm->df_laser_red = level->df_laser_red;
4438 level_mm->df_laser_green = level->df_laser_green;
4439 level_mm->df_laser_blue = level->df_laser_blue;
4441 strcpy(level_mm->name, level->name);
4442 strcpy(level_mm->author, level->author);
4444 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4445 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4446 level_mm->score[SC_KEY] = level->score[SC_KEY];
4447 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4448 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4450 level_mm->amoeba_speed = level->amoeba_speed;
4451 level_mm->time_fuse = level->mm_time_fuse;
4452 level_mm->time_bomb = level->mm_time_bomb;
4453 level_mm->time_ball = level->mm_time_ball;
4454 level_mm->time_block = level->mm_time_block;
4456 level_mm->num_ball_contents = level->num_mm_ball_contents;
4457 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4458 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4459 level_mm->explode_ball = level->explode_mm_ball;
4461 for (i = 0; i < level->num_mm_ball_contents; i++)
4462 level_mm->ball_content[i] =
4463 map_element_RND_to_MM(level->mm_ball_content[i]);
4465 for (x = 0; x < level->fieldx; x++)
4466 for (y = 0; y < level->fieldy; y++)
4468 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4471 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4473 struct LevelInfo_MM *level_mm = level->native_mm_level;
4476 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4477 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4479 level->time = level_mm->time;
4480 level->gems_needed = level_mm->kettles_needed;
4481 level->auto_count_gems = level_mm->auto_count_kettles;
4483 level->mm_laser_red = level_mm->mm_laser_red;
4484 level->mm_laser_green = level_mm->mm_laser_green;
4485 level->mm_laser_blue = level_mm->mm_laser_blue;
4487 level->df_laser_red = level_mm->df_laser_red;
4488 level->df_laser_green = level_mm->df_laser_green;
4489 level->df_laser_blue = level_mm->df_laser_blue;
4491 strcpy(level->name, level_mm->name);
4493 // only overwrite author from 'levelinfo.conf' if author defined in level
4494 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4495 strcpy(level->author, level_mm->author);
4497 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4498 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4499 level->score[SC_KEY] = level_mm->score[SC_KEY];
4500 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4501 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4503 level->amoeba_speed = level_mm->amoeba_speed;
4504 level->mm_time_fuse = level_mm->time_fuse;
4505 level->mm_time_bomb = level_mm->time_bomb;
4506 level->mm_time_ball = level_mm->time_ball;
4507 level->mm_time_block = level_mm->time_block;
4509 level->num_mm_ball_contents = level_mm->num_ball_contents;
4510 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4511 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4512 level->explode_mm_ball = level_mm->explode_ball;
4514 for (i = 0; i < level->num_mm_ball_contents; i++)
4515 level->mm_ball_content[i] =
4516 map_element_MM_to_RND(level_mm->ball_content[i]);
4518 for (x = 0; x < level->fieldx; x++)
4519 for (y = 0; y < level->fieldy; y++)
4520 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4524 // ----------------------------------------------------------------------------
4525 // functions for loading DC level
4526 // ----------------------------------------------------------------------------
4528 #define DC_LEVEL_HEADER_SIZE 344
4530 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4533 static int last_data_encoded;
4537 int diff_hi, diff_lo;
4538 int data_hi, data_lo;
4539 unsigned short data_decoded;
4543 last_data_encoded = 0;
4550 diff = data_encoded - last_data_encoded;
4551 diff_hi = diff & ~0xff;
4552 diff_lo = diff & 0xff;
4556 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4557 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4558 data_hi = data_hi & 0xff00;
4560 data_decoded = data_hi | data_lo;
4562 last_data_encoded = data_encoded;
4564 offset1 = (offset1 + 1) % 31;
4565 offset2 = offset2 & 0xff;
4567 return data_decoded;
4570 static int getMappedElement_DC(int element)
4578 // 0x0117 - 0x036e: (?)
4581 // 0x042d - 0x0684: (?)
4597 element = EL_CRYSTAL;
4600 case 0x0e77: // quicksand (boulder)
4601 element = EL_QUICKSAND_FAST_FULL;
4604 case 0x0e99: // slow quicksand (boulder)
4605 element = EL_QUICKSAND_FULL;
4609 element = EL_EM_EXIT_OPEN;
4613 element = EL_EM_EXIT_CLOSED;
4617 element = EL_EM_STEEL_EXIT_OPEN;
4621 element = EL_EM_STEEL_EXIT_CLOSED;
4624 case 0x0f4f: // dynamite (lit 1)
4625 element = EL_EM_DYNAMITE_ACTIVE;
4628 case 0x0f57: // dynamite (lit 2)
4629 element = EL_EM_DYNAMITE_ACTIVE;
4632 case 0x0f5f: // dynamite (lit 3)
4633 element = EL_EM_DYNAMITE_ACTIVE;
4636 case 0x0f67: // dynamite (lit 4)
4637 element = EL_EM_DYNAMITE_ACTIVE;
4644 element = EL_AMOEBA_WET;
4648 element = EL_AMOEBA_DROP;
4652 element = EL_DC_MAGIC_WALL;
4656 element = EL_SPACESHIP_UP;
4660 element = EL_SPACESHIP_DOWN;
4664 element = EL_SPACESHIP_LEFT;
4668 element = EL_SPACESHIP_RIGHT;
4672 element = EL_BUG_UP;
4676 element = EL_BUG_DOWN;
4680 element = EL_BUG_LEFT;
4684 element = EL_BUG_RIGHT;
4688 element = EL_MOLE_UP;
4692 element = EL_MOLE_DOWN;
4696 element = EL_MOLE_LEFT;
4700 element = EL_MOLE_RIGHT;
4708 element = EL_YAMYAM_UP;
4712 element = EL_SWITCHGATE_OPEN;
4716 element = EL_SWITCHGATE_CLOSED;
4720 element = EL_DC_SWITCHGATE_SWITCH_UP;
4724 element = EL_TIMEGATE_CLOSED;
4727 case 0x144c: // conveyor belt switch (green)
4728 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4731 case 0x144f: // conveyor belt switch (red)
4732 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4735 case 0x1452: // conveyor belt switch (blue)
4736 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4740 element = EL_CONVEYOR_BELT_3_MIDDLE;
4744 element = EL_CONVEYOR_BELT_3_LEFT;
4748 element = EL_CONVEYOR_BELT_3_RIGHT;
4752 element = EL_CONVEYOR_BELT_1_MIDDLE;
4756 element = EL_CONVEYOR_BELT_1_LEFT;
4760 element = EL_CONVEYOR_BELT_1_RIGHT;
4764 element = EL_CONVEYOR_BELT_4_MIDDLE;
4768 element = EL_CONVEYOR_BELT_4_LEFT;
4772 element = EL_CONVEYOR_BELT_4_RIGHT;
4776 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4780 element = EL_EXPANDABLE_WALL_VERTICAL;
4784 element = EL_EXPANDABLE_WALL_ANY;
4787 case 0x14ce: // growing steel wall (left/right)
4788 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4791 case 0x14df: // growing steel wall (up/down)
4792 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4795 case 0x14e8: // growing steel wall (up/down/left/right)
4796 element = EL_EXPANDABLE_STEELWALL_ANY;
4800 element = EL_SHIELD_DEADLY;
4804 element = EL_EXTRA_TIME;
4812 element = EL_EMPTY_SPACE;
4815 case 0x1578: // quicksand (empty)
4816 element = EL_QUICKSAND_FAST_EMPTY;
4819 case 0x1579: // slow quicksand (empty)
4820 element = EL_QUICKSAND_EMPTY;
4830 element = EL_EM_DYNAMITE;
4833 case 0x15a1: // key (red)
4834 element = EL_EM_KEY_1;
4837 case 0x15a2: // key (yellow)
4838 element = EL_EM_KEY_2;
4841 case 0x15a3: // key (blue)
4842 element = EL_EM_KEY_4;
4845 case 0x15a4: // key (green)
4846 element = EL_EM_KEY_3;
4849 case 0x15a5: // key (white)
4850 element = EL_DC_KEY_WHITE;
4854 element = EL_WALL_SLIPPERY;
4861 case 0x15a8: // wall (not round)
4865 case 0x15a9: // (blue)
4866 element = EL_CHAR_A;
4869 case 0x15aa: // (blue)
4870 element = EL_CHAR_B;
4873 case 0x15ab: // (blue)
4874 element = EL_CHAR_C;
4877 case 0x15ac: // (blue)
4878 element = EL_CHAR_D;
4881 case 0x15ad: // (blue)
4882 element = EL_CHAR_E;
4885 case 0x15ae: // (blue)
4886 element = EL_CHAR_F;
4889 case 0x15af: // (blue)
4890 element = EL_CHAR_G;
4893 case 0x15b0: // (blue)
4894 element = EL_CHAR_H;
4897 case 0x15b1: // (blue)
4898 element = EL_CHAR_I;
4901 case 0x15b2: // (blue)
4902 element = EL_CHAR_J;
4905 case 0x15b3: // (blue)
4906 element = EL_CHAR_K;
4909 case 0x15b4: // (blue)
4910 element = EL_CHAR_L;
4913 case 0x15b5: // (blue)
4914 element = EL_CHAR_M;
4917 case 0x15b6: // (blue)
4918 element = EL_CHAR_N;
4921 case 0x15b7: // (blue)
4922 element = EL_CHAR_O;
4925 case 0x15b8: // (blue)
4926 element = EL_CHAR_P;
4929 case 0x15b9: // (blue)
4930 element = EL_CHAR_Q;
4933 case 0x15ba: // (blue)
4934 element = EL_CHAR_R;
4937 case 0x15bb: // (blue)
4938 element = EL_CHAR_S;
4941 case 0x15bc: // (blue)
4942 element = EL_CHAR_T;
4945 case 0x15bd: // (blue)
4946 element = EL_CHAR_U;
4949 case 0x15be: // (blue)
4950 element = EL_CHAR_V;
4953 case 0x15bf: // (blue)
4954 element = EL_CHAR_W;
4957 case 0x15c0: // (blue)
4958 element = EL_CHAR_X;
4961 case 0x15c1: // (blue)
4962 element = EL_CHAR_Y;
4965 case 0x15c2: // (blue)
4966 element = EL_CHAR_Z;
4969 case 0x15c3: // (blue)
4970 element = EL_CHAR_AUMLAUT;
4973 case 0x15c4: // (blue)
4974 element = EL_CHAR_OUMLAUT;
4977 case 0x15c5: // (blue)
4978 element = EL_CHAR_UUMLAUT;
4981 case 0x15c6: // (blue)
4982 element = EL_CHAR_0;
4985 case 0x15c7: // (blue)
4986 element = EL_CHAR_1;
4989 case 0x15c8: // (blue)
4990 element = EL_CHAR_2;
4993 case 0x15c9: // (blue)
4994 element = EL_CHAR_3;
4997 case 0x15ca: // (blue)
4998 element = EL_CHAR_4;
5001 case 0x15cb: // (blue)
5002 element = EL_CHAR_5;
5005 case 0x15cc: // (blue)
5006 element = EL_CHAR_6;
5009 case 0x15cd: // (blue)
5010 element = EL_CHAR_7;
5013 case 0x15ce: // (blue)
5014 element = EL_CHAR_8;
5017 case 0x15cf: // (blue)
5018 element = EL_CHAR_9;
5021 case 0x15d0: // (blue)
5022 element = EL_CHAR_PERIOD;
5025 case 0x15d1: // (blue)
5026 element = EL_CHAR_EXCLAM;
5029 case 0x15d2: // (blue)
5030 element = EL_CHAR_COLON;
5033 case 0x15d3: // (blue)
5034 element = EL_CHAR_LESS;
5037 case 0x15d4: // (blue)
5038 element = EL_CHAR_GREATER;
5041 case 0x15d5: // (blue)
5042 element = EL_CHAR_QUESTION;
5045 case 0x15d6: // (blue)
5046 element = EL_CHAR_COPYRIGHT;
5049 case 0x15d7: // (blue)
5050 element = EL_CHAR_UP;
5053 case 0x15d8: // (blue)
5054 element = EL_CHAR_DOWN;
5057 case 0x15d9: // (blue)
5058 element = EL_CHAR_BUTTON;
5061 case 0x15da: // (blue)
5062 element = EL_CHAR_PLUS;
5065 case 0x15db: // (blue)
5066 element = EL_CHAR_MINUS;
5069 case 0x15dc: // (blue)
5070 element = EL_CHAR_APOSTROPHE;
5073 case 0x15dd: // (blue)
5074 element = EL_CHAR_PARENLEFT;
5077 case 0x15de: // (blue)
5078 element = EL_CHAR_PARENRIGHT;
5081 case 0x15df: // (green)
5082 element = EL_CHAR_A;
5085 case 0x15e0: // (green)
5086 element = EL_CHAR_B;
5089 case 0x15e1: // (green)
5090 element = EL_CHAR_C;
5093 case 0x15e2: // (green)
5094 element = EL_CHAR_D;
5097 case 0x15e3: // (green)
5098 element = EL_CHAR_E;
5101 case 0x15e4: // (green)
5102 element = EL_CHAR_F;
5105 case 0x15e5: // (green)
5106 element = EL_CHAR_G;
5109 case 0x15e6: // (green)
5110 element = EL_CHAR_H;
5113 case 0x15e7: // (green)
5114 element = EL_CHAR_I;
5117 case 0x15e8: // (green)
5118 element = EL_CHAR_J;
5121 case 0x15e9: // (green)
5122 element = EL_CHAR_K;
5125 case 0x15ea: // (green)
5126 element = EL_CHAR_L;
5129 case 0x15eb: // (green)
5130 element = EL_CHAR_M;
5133 case 0x15ec: // (green)
5134 element = EL_CHAR_N;
5137 case 0x15ed: // (green)
5138 element = EL_CHAR_O;
5141 case 0x15ee: // (green)
5142 element = EL_CHAR_P;
5145 case 0x15ef: // (green)
5146 element = EL_CHAR_Q;
5149 case 0x15f0: // (green)
5150 element = EL_CHAR_R;
5153 case 0x15f1: // (green)
5154 element = EL_CHAR_S;
5157 case 0x15f2: // (green)
5158 element = EL_CHAR_T;
5161 case 0x15f3: // (green)
5162 element = EL_CHAR_U;
5165 case 0x15f4: // (green)
5166 element = EL_CHAR_V;
5169 case 0x15f5: // (green)
5170 element = EL_CHAR_W;
5173 case 0x15f6: // (green)
5174 element = EL_CHAR_X;
5177 case 0x15f7: // (green)
5178 element = EL_CHAR_Y;
5181 case 0x15f8: // (green)
5182 element = EL_CHAR_Z;
5185 case 0x15f9: // (green)
5186 element = EL_CHAR_AUMLAUT;
5189 case 0x15fa: // (green)
5190 element = EL_CHAR_OUMLAUT;
5193 case 0x15fb: // (green)
5194 element = EL_CHAR_UUMLAUT;
5197 case 0x15fc: // (green)
5198 element = EL_CHAR_0;
5201 case 0x15fd: // (green)
5202 element = EL_CHAR_1;
5205 case 0x15fe: // (green)
5206 element = EL_CHAR_2;
5209 case 0x15ff: // (green)
5210 element = EL_CHAR_3;
5213 case 0x1600: // (green)
5214 element = EL_CHAR_4;
5217 case 0x1601: // (green)
5218 element = EL_CHAR_5;
5221 case 0x1602: // (green)
5222 element = EL_CHAR_6;
5225 case 0x1603: // (green)
5226 element = EL_CHAR_7;
5229 case 0x1604: // (green)
5230 element = EL_CHAR_8;
5233 case 0x1605: // (green)
5234 element = EL_CHAR_9;
5237 case 0x1606: // (green)
5238 element = EL_CHAR_PERIOD;
5241 case 0x1607: // (green)
5242 element = EL_CHAR_EXCLAM;
5245 case 0x1608: // (green)
5246 element = EL_CHAR_COLON;
5249 case 0x1609: // (green)
5250 element = EL_CHAR_LESS;
5253 case 0x160a: // (green)
5254 element = EL_CHAR_GREATER;
5257 case 0x160b: // (green)
5258 element = EL_CHAR_QUESTION;
5261 case 0x160c: // (green)
5262 element = EL_CHAR_COPYRIGHT;
5265 case 0x160d: // (green)
5266 element = EL_CHAR_UP;
5269 case 0x160e: // (green)
5270 element = EL_CHAR_DOWN;
5273 case 0x160f: // (green)
5274 element = EL_CHAR_BUTTON;
5277 case 0x1610: // (green)
5278 element = EL_CHAR_PLUS;
5281 case 0x1611: // (green)
5282 element = EL_CHAR_MINUS;
5285 case 0x1612: // (green)
5286 element = EL_CHAR_APOSTROPHE;
5289 case 0x1613: // (green)
5290 element = EL_CHAR_PARENLEFT;
5293 case 0x1614: // (green)
5294 element = EL_CHAR_PARENRIGHT;
5297 case 0x1615: // (blue steel)
5298 element = EL_STEEL_CHAR_A;
5301 case 0x1616: // (blue steel)
5302 element = EL_STEEL_CHAR_B;
5305 case 0x1617: // (blue steel)
5306 element = EL_STEEL_CHAR_C;
5309 case 0x1618: // (blue steel)
5310 element = EL_STEEL_CHAR_D;
5313 case 0x1619: // (blue steel)
5314 element = EL_STEEL_CHAR_E;
5317 case 0x161a: // (blue steel)
5318 element = EL_STEEL_CHAR_F;
5321 case 0x161b: // (blue steel)
5322 element = EL_STEEL_CHAR_G;
5325 case 0x161c: // (blue steel)
5326 element = EL_STEEL_CHAR_H;
5329 case 0x161d: // (blue steel)
5330 element = EL_STEEL_CHAR_I;
5333 case 0x161e: // (blue steel)
5334 element = EL_STEEL_CHAR_J;
5337 case 0x161f: // (blue steel)
5338 element = EL_STEEL_CHAR_K;
5341 case 0x1620: // (blue steel)
5342 element = EL_STEEL_CHAR_L;
5345 case 0x1621: // (blue steel)
5346 element = EL_STEEL_CHAR_M;
5349 case 0x1622: // (blue steel)
5350 element = EL_STEEL_CHAR_N;
5353 case 0x1623: // (blue steel)
5354 element = EL_STEEL_CHAR_O;
5357 case 0x1624: // (blue steel)
5358 element = EL_STEEL_CHAR_P;
5361 case 0x1625: // (blue steel)
5362 element = EL_STEEL_CHAR_Q;
5365 case 0x1626: // (blue steel)
5366 element = EL_STEEL_CHAR_R;
5369 case 0x1627: // (blue steel)
5370 element = EL_STEEL_CHAR_S;
5373 case 0x1628: // (blue steel)
5374 element = EL_STEEL_CHAR_T;
5377 case 0x1629: // (blue steel)
5378 element = EL_STEEL_CHAR_U;
5381 case 0x162a: // (blue steel)
5382 element = EL_STEEL_CHAR_V;
5385 case 0x162b: // (blue steel)
5386 element = EL_STEEL_CHAR_W;
5389 case 0x162c: // (blue steel)
5390 element = EL_STEEL_CHAR_X;
5393 case 0x162d: // (blue steel)
5394 element = EL_STEEL_CHAR_Y;
5397 case 0x162e: // (blue steel)
5398 element = EL_STEEL_CHAR_Z;
5401 case 0x162f: // (blue steel)
5402 element = EL_STEEL_CHAR_AUMLAUT;
5405 case 0x1630: // (blue steel)
5406 element = EL_STEEL_CHAR_OUMLAUT;
5409 case 0x1631: // (blue steel)
5410 element = EL_STEEL_CHAR_UUMLAUT;
5413 case 0x1632: // (blue steel)
5414 element = EL_STEEL_CHAR_0;
5417 case 0x1633: // (blue steel)
5418 element = EL_STEEL_CHAR_1;
5421 case 0x1634: // (blue steel)
5422 element = EL_STEEL_CHAR_2;
5425 case 0x1635: // (blue steel)
5426 element = EL_STEEL_CHAR_3;
5429 case 0x1636: // (blue steel)
5430 element = EL_STEEL_CHAR_4;
5433 case 0x1637: // (blue steel)
5434 element = EL_STEEL_CHAR_5;
5437 case 0x1638: // (blue steel)
5438 element = EL_STEEL_CHAR_6;
5441 case 0x1639: // (blue steel)
5442 element = EL_STEEL_CHAR_7;
5445 case 0x163a: // (blue steel)
5446 element = EL_STEEL_CHAR_8;
5449 case 0x163b: // (blue steel)
5450 element = EL_STEEL_CHAR_9;
5453 case 0x163c: // (blue steel)
5454 element = EL_STEEL_CHAR_PERIOD;
5457 case 0x163d: // (blue steel)
5458 element = EL_STEEL_CHAR_EXCLAM;
5461 case 0x163e: // (blue steel)
5462 element = EL_STEEL_CHAR_COLON;
5465 case 0x163f: // (blue steel)
5466 element = EL_STEEL_CHAR_LESS;
5469 case 0x1640: // (blue steel)
5470 element = EL_STEEL_CHAR_GREATER;
5473 case 0x1641: // (blue steel)
5474 element = EL_STEEL_CHAR_QUESTION;
5477 case 0x1642: // (blue steel)
5478 element = EL_STEEL_CHAR_COPYRIGHT;
5481 case 0x1643: // (blue steel)
5482 element = EL_STEEL_CHAR_UP;
5485 case 0x1644: // (blue steel)
5486 element = EL_STEEL_CHAR_DOWN;
5489 case 0x1645: // (blue steel)
5490 element = EL_STEEL_CHAR_BUTTON;
5493 case 0x1646: // (blue steel)
5494 element = EL_STEEL_CHAR_PLUS;
5497 case 0x1647: // (blue steel)
5498 element = EL_STEEL_CHAR_MINUS;
5501 case 0x1648: // (blue steel)
5502 element = EL_STEEL_CHAR_APOSTROPHE;
5505 case 0x1649: // (blue steel)
5506 element = EL_STEEL_CHAR_PARENLEFT;
5509 case 0x164a: // (blue steel)
5510 element = EL_STEEL_CHAR_PARENRIGHT;
5513 case 0x164b: // (green steel)
5514 element = EL_STEEL_CHAR_A;
5517 case 0x164c: // (green steel)
5518 element = EL_STEEL_CHAR_B;
5521 case 0x164d: // (green steel)
5522 element = EL_STEEL_CHAR_C;
5525 case 0x164e: // (green steel)
5526 element = EL_STEEL_CHAR_D;
5529 case 0x164f: // (green steel)
5530 element = EL_STEEL_CHAR_E;
5533 case 0x1650: // (green steel)
5534 element = EL_STEEL_CHAR_F;
5537 case 0x1651: // (green steel)
5538 element = EL_STEEL_CHAR_G;
5541 case 0x1652: // (green steel)
5542 element = EL_STEEL_CHAR_H;
5545 case 0x1653: // (green steel)
5546 element = EL_STEEL_CHAR_I;
5549 case 0x1654: // (green steel)
5550 element = EL_STEEL_CHAR_J;
5553 case 0x1655: // (green steel)
5554 element = EL_STEEL_CHAR_K;
5557 case 0x1656: // (green steel)
5558 element = EL_STEEL_CHAR_L;
5561 case 0x1657: // (green steel)
5562 element = EL_STEEL_CHAR_M;
5565 case 0x1658: // (green steel)
5566 element = EL_STEEL_CHAR_N;
5569 case 0x1659: // (green steel)
5570 element = EL_STEEL_CHAR_O;
5573 case 0x165a: // (green steel)
5574 element = EL_STEEL_CHAR_P;
5577 case 0x165b: // (green steel)
5578 element = EL_STEEL_CHAR_Q;
5581 case 0x165c: // (green steel)
5582 element = EL_STEEL_CHAR_R;
5585 case 0x165d: // (green steel)
5586 element = EL_STEEL_CHAR_S;
5589 case 0x165e: // (green steel)
5590 element = EL_STEEL_CHAR_T;
5593 case 0x165f: // (green steel)
5594 element = EL_STEEL_CHAR_U;
5597 case 0x1660: // (green steel)
5598 element = EL_STEEL_CHAR_V;
5601 case 0x1661: // (green steel)
5602 element = EL_STEEL_CHAR_W;
5605 case 0x1662: // (green steel)
5606 element = EL_STEEL_CHAR_X;
5609 case 0x1663: // (green steel)
5610 element = EL_STEEL_CHAR_Y;
5613 case 0x1664: // (green steel)
5614 element = EL_STEEL_CHAR_Z;
5617 case 0x1665: // (green steel)
5618 element = EL_STEEL_CHAR_AUMLAUT;
5621 case 0x1666: // (green steel)
5622 element = EL_STEEL_CHAR_OUMLAUT;
5625 case 0x1667: // (green steel)
5626 element = EL_STEEL_CHAR_UUMLAUT;
5629 case 0x1668: // (green steel)
5630 element = EL_STEEL_CHAR_0;
5633 case 0x1669: // (green steel)
5634 element = EL_STEEL_CHAR_1;
5637 case 0x166a: // (green steel)
5638 element = EL_STEEL_CHAR_2;
5641 case 0x166b: // (green steel)
5642 element = EL_STEEL_CHAR_3;
5645 case 0x166c: // (green steel)
5646 element = EL_STEEL_CHAR_4;
5649 case 0x166d: // (green steel)
5650 element = EL_STEEL_CHAR_5;
5653 case 0x166e: // (green steel)
5654 element = EL_STEEL_CHAR_6;
5657 case 0x166f: // (green steel)
5658 element = EL_STEEL_CHAR_7;
5661 case 0x1670: // (green steel)
5662 element = EL_STEEL_CHAR_8;
5665 case 0x1671: // (green steel)
5666 element = EL_STEEL_CHAR_9;
5669 case 0x1672: // (green steel)
5670 element = EL_STEEL_CHAR_PERIOD;
5673 case 0x1673: // (green steel)
5674 element = EL_STEEL_CHAR_EXCLAM;
5677 case 0x1674: // (green steel)
5678 element = EL_STEEL_CHAR_COLON;
5681 case 0x1675: // (green steel)
5682 element = EL_STEEL_CHAR_LESS;
5685 case 0x1676: // (green steel)
5686 element = EL_STEEL_CHAR_GREATER;
5689 case 0x1677: // (green steel)
5690 element = EL_STEEL_CHAR_QUESTION;
5693 case 0x1678: // (green steel)
5694 element = EL_STEEL_CHAR_COPYRIGHT;
5697 case 0x1679: // (green steel)
5698 element = EL_STEEL_CHAR_UP;
5701 case 0x167a: // (green steel)
5702 element = EL_STEEL_CHAR_DOWN;
5705 case 0x167b: // (green steel)
5706 element = EL_STEEL_CHAR_BUTTON;
5709 case 0x167c: // (green steel)
5710 element = EL_STEEL_CHAR_PLUS;
5713 case 0x167d: // (green steel)
5714 element = EL_STEEL_CHAR_MINUS;
5717 case 0x167e: // (green steel)
5718 element = EL_STEEL_CHAR_APOSTROPHE;
5721 case 0x167f: // (green steel)
5722 element = EL_STEEL_CHAR_PARENLEFT;
5725 case 0x1680: // (green steel)
5726 element = EL_STEEL_CHAR_PARENRIGHT;
5729 case 0x1681: // gate (red)
5730 element = EL_EM_GATE_1;
5733 case 0x1682: // secret gate (red)
5734 element = EL_EM_GATE_1_GRAY;
5737 case 0x1683: // gate (yellow)
5738 element = EL_EM_GATE_2;
5741 case 0x1684: // secret gate (yellow)
5742 element = EL_EM_GATE_2_GRAY;
5745 case 0x1685: // gate (blue)
5746 element = EL_EM_GATE_4;
5749 case 0x1686: // secret gate (blue)
5750 element = EL_EM_GATE_4_GRAY;
5753 case 0x1687: // gate (green)
5754 element = EL_EM_GATE_3;
5757 case 0x1688: // secret gate (green)
5758 element = EL_EM_GATE_3_GRAY;
5761 case 0x1689: // gate (white)
5762 element = EL_DC_GATE_WHITE;
5765 case 0x168a: // secret gate (white)
5766 element = EL_DC_GATE_WHITE_GRAY;
5769 case 0x168b: // secret gate (no key)
5770 element = EL_DC_GATE_FAKE_GRAY;
5774 element = EL_ROBOT_WHEEL;
5778 element = EL_DC_TIMEGATE_SWITCH;
5782 element = EL_ACID_POOL_BOTTOM;
5786 element = EL_ACID_POOL_TOPLEFT;
5790 element = EL_ACID_POOL_TOPRIGHT;
5794 element = EL_ACID_POOL_BOTTOMLEFT;
5798 element = EL_ACID_POOL_BOTTOMRIGHT;
5802 element = EL_STEELWALL;
5806 element = EL_STEELWALL_SLIPPERY;
5809 case 0x1695: // steel wall (not round)
5810 element = EL_STEELWALL;
5813 case 0x1696: // steel wall (left)
5814 element = EL_DC_STEELWALL_1_LEFT;
5817 case 0x1697: // steel wall (bottom)
5818 element = EL_DC_STEELWALL_1_BOTTOM;
5821 case 0x1698: // steel wall (right)
5822 element = EL_DC_STEELWALL_1_RIGHT;
5825 case 0x1699: // steel wall (top)
5826 element = EL_DC_STEELWALL_1_TOP;
5829 case 0x169a: // steel wall (left/bottom)
5830 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5833 case 0x169b: // steel wall (right/bottom)
5834 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5837 case 0x169c: // steel wall (right/top)
5838 element = EL_DC_STEELWALL_1_TOPRIGHT;
5841 case 0x169d: // steel wall (left/top)
5842 element = EL_DC_STEELWALL_1_TOPLEFT;
5845 case 0x169e: // steel wall (right/bottom small)
5846 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5849 case 0x169f: // steel wall (left/bottom small)
5850 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5853 case 0x16a0: // steel wall (right/top small)
5854 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5857 case 0x16a1: // steel wall (left/top small)
5858 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5861 case 0x16a2: // steel wall (left/right)
5862 element = EL_DC_STEELWALL_1_VERTICAL;
5865 case 0x16a3: // steel wall (top/bottom)
5866 element = EL_DC_STEELWALL_1_HORIZONTAL;
5869 case 0x16a4: // steel wall 2 (left end)
5870 element = EL_DC_STEELWALL_2_LEFT;
5873 case 0x16a5: // steel wall 2 (right end)
5874 element = EL_DC_STEELWALL_2_RIGHT;
5877 case 0x16a6: // steel wall 2 (top end)
5878 element = EL_DC_STEELWALL_2_TOP;
5881 case 0x16a7: // steel wall 2 (bottom end)
5882 element = EL_DC_STEELWALL_2_BOTTOM;
5885 case 0x16a8: // steel wall 2 (left/right)
5886 element = EL_DC_STEELWALL_2_HORIZONTAL;
5889 case 0x16a9: // steel wall 2 (up/down)
5890 element = EL_DC_STEELWALL_2_VERTICAL;
5893 case 0x16aa: // steel wall 2 (mid)
5894 element = EL_DC_STEELWALL_2_MIDDLE;
5898 element = EL_SIGN_EXCLAMATION;
5902 element = EL_SIGN_RADIOACTIVITY;
5906 element = EL_SIGN_STOP;
5910 element = EL_SIGN_WHEELCHAIR;
5914 element = EL_SIGN_PARKING;
5918 element = EL_SIGN_NO_ENTRY;
5922 element = EL_SIGN_HEART;
5926 element = EL_SIGN_GIVE_WAY;
5930 element = EL_SIGN_ENTRY_FORBIDDEN;
5934 element = EL_SIGN_EMERGENCY_EXIT;
5938 element = EL_SIGN_YIN_YANG;
5942 element = EL_WALL_EMERALD;
5946 element = EL_WALL_DIAMOND;
5950 element = EL_WALL_PEARL;
5954 element = EL_WALL_CRYSTAL;
5958 element = EL_INVISIBLE_WALL;
5962 element = EL_INVISIBLE_STEELWALL;
5966 // EL_INVISIBLE_SAND
5969 element = EL_LIGHT_SWITCH;
5973 element = EL_ENVELOPE_1;
5977 if (element >= 0x0117 && element <= 0x036e) // (?)
5978 element = EL_DIAMOND;
5979 else if (element >= 0x042d && element <= 0x0684) // (?)
5980 element = EL_EMERALD;
5981 else if (element >= 0x157c && element <= 0x158b)
5983 else if (element >= 0x1590 && element <= 0x159f)
5984 element = EL_DC_LANDMINE;
5985 else if (element >= 0x16bc && element <= 0x16cb)
5986 element = EL_INVISIBLE_SAND;
5989 Warn("unknown Diamond Caves element 0x%04x", element);
5991 element = EL_UNKNOWN;
5996 return getMappedElement(element);
5999 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6001 byte header[DC_LEVEL_HEADER_SIZE];
6003 int envelope_header_pos = 62;
6004 int envelope_content_pos = 94;
6005 int level_name_pos = 251;
6006 int level_author_pos = 292;
6007 int envelope_header_len;
6008 int envelope_content_len;
6010 int level_author_len;
6012 int num_yamyam_contents;
6015 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6017 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6019 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6021 header[i * 2 + 0] = header_word >> 8;
6022 header[i * 2 + 1] = header_word & 0xff;
6025 // read some values from level header to check level decoding integrity
6026 fieldx = header[6] | (header[7] << 8);
6027 fieldy = header[8] | (header[9] << 8);
6028 num_yamyam_contents = header[60] | (header[61] << 8);
6030 // do some simple sanity checks to ensure that level was correctly decoded
6031 if (fieldx < 1 || fieldx > 256 ||
6032 fieldy < 1 || fieldy > 256 ||
6033 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6035 level->no_valid_file = TRUE;
6037 Warn("cannot decode level from stream -- using empty level");
6042 // maximum envelope header size is 31 bytes
6043 envelope_header_len = header[envelope_header_pos];
6044 // maximum envelope content size is 110 (156?) bytes
6045 envelope_content_len = header[envelope_content_pos];
6047 // maximum level title size is 40 bytes
6048 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6049 // maximum level author size is 30 (51?) bytes
6050 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6054 for (i = 0; i < envelope_header_len; i++)
6055 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6056 level->envelope[0].text[envelope_size++] =
6057 header[envelope_header_pos + 1 + i];
6059 if (envelope_header_len > 0 && envelope_content_len > 0)
6061 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6062 level->envelope[0].text[envelope_size++] = '\n';
6063 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6064 level->envelope[0].text[envelope_size++] = '\n';
6067 for (i = 0; i < envelope_content_len; i++)
6068 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6069 level->envelope[0].text[envelope_size++] =
6070 header[envelope_content_pos + 1 + i];
6072 level->envelope[0].text[envelope_size] = '\0';
6074 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6075 level->envelope[0].ysize = 10;
6076 level->envelope[0].autowrap = TRUE;
6077 level->envelope[0].centered = TRUE;
6079 for (i = 0; i < level_name_len; i++)
6080 level->name[i] = header[level_name_pos + 1 + i];
6081 level->name[level_name_len] = '\0';
6083 for (i = 0; i < level_author_len; i++)
6084 level->author[i] = header[level_author_pos + 1 + i];
6085 level->author[level_author_len] = '\0';
6087 num_yamyam_contents = header[60] | (header[61] << 8);
6088 level->num_yamyam_contents =
6089 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6091 for (i = 0; i < num_yamyam_contents; i++)
6093 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6095 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6096 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6098 if (i < MAX_ELEMENT_CONTENTS)
6099 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6103 fieldx = header[6] | (header[7] << 8);
6104 fieldy = header[8] | (header[9] << 8);
6105 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6106 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6108 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6110 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6111 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6113 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6114 level->field[x][y] = getMappedElement_DC(element_dc);
6117 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6118 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6119 level->field[x][y] = EL_PLAYER_1;
6121 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6122 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6123 level->field[x][y] = EL_PLAYER_2;
6125 level->gems_needed = header[18] | (header[19] << 8);
6127 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6128 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6129 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6130 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6131 level->score[SC_NUT] = header[28] | (header[29] << 8);
6132 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6133 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6134 level->score[SC_BUG] = header[34] | (header[35] << 8);
6135 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6136 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6137 level->score[SC_KEY] = header[40] | (header[41] << 8);
6138 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6140 level->time = header[44] | (header[45] << 8);
6142 level->amoeba_speed = header[46] | (header[47] << 8);
6143 level->time_light = header[48] | (header[49] << 8);
6144 level->time_timegate = header[50] | (header[51] << 8);
6145 level->time_wheel = header[52] | (header[53] << 8);
6146 level->time_magic_wall = header[54] | (header[55] << 8);
6147 level->extra_time = header[56] | (header[57] << 8);
6148 level->shield_normal_time = header[58] | (header[59] << 8);
6150 // shield and extra time elements do not have a score
6151 level->score[SC_SHIELD] = 0;
6152 level->extra_time_score = 0;
6154 // set time for normal and deadly shields to the same value
6155 level->shield_deadly_time = level->shield_normal_time;
6157 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6158 // can slip down from flat walls, like normal walls and steel walls
6159 level->em_slippery_gems = TRUE;
6161 // time score is counted for each 10 seconds left in Diamond Caves levels
6162 level->time_score_base = 10;
6165 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6166 struct LevelFileInfo *level_file_info,
6167 boolean level_info_only)
6169 char *filename = level_file_info->filename;
6171 int num_magic_bytes = 8;
6172 char magic_bytes[num_magic_bytes + 1];
6173 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6175 if (!(file = openFile(filename, MODE_READ)))
6177 level->no_valid_file = TRUE;
6179 if (!level_info_only)
6180 Warn("cannot read level '%s' -- using empty level", filename);
6185 // fseek(file, 0x0000, SEEK_SET);
6187 if (level_file_info->packed)
6189 // read "magic bytes" from start of file
6190 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6191 magic_bytes[0] = '\0';
6193 // check "magic bytes" for correct file format
6194 if (!strPrefix(magic_bytes, "DC2"))
6196 level->no_valid_file = TRUE;
6198 Warn("unknown DC level file '%s' -- using empty level", filename);
6203 if (strPrefix(magic_bytes, "DC2Win95") ||
6204 strPrefix(magic_bytes, "DC2Win98"))
6206 int position_first_level = 0x00fa;
6207 int extra_bytes = 4;
6210 // advance file stream to first level inside the level package
6211 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6213 // each block of level data is followed by block of non-level data
6214 num_levels_to_skip *= 2;
6216 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6217 while (num_levels_to_skip >= 0)
6219 // advance file stream to next level inside the level package
6220 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6222 level->no_valid_file = TRUE;
6224 Warn("cannot fseek in file '%s' -- using empty level", filename);
6229 // skip apparently unused extra bytes following each level
6230 ReadUnusedBytesFromFile(file, extra_bytes);
6232 // read size of next level in level package
6233 skip_bytes = getFile32BitLE(file);
6235 num_levels_to_skip--;
6240 level->no_valid_file = TRUE;
6242 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6248 LoadLevelFromFileStream_DC(file, level);
6254 // ----------------------------------------------------------------------------
6255 // functions for loading SB level
6256 // ----------------------------------------------------------------------------
6258 int getMappedElement_SB(int element_ascii, boolean use_ces)
6266 sb_element_mapping[] =
6268 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6269 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6270 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6271 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6272 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6273 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6274 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6275 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6282 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6283 if (element_ascii == sb_element_mapping[i].ascii)
6284 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6286 return EL_UNDEFINED;
6289 static void SetLevelSettings_SB(struct LevelInfo *level)
6293 level->use_step_counter = TRUE;
6296 level->score[SC_TIME_BONUS] = 0;
6297 level->time_score_base = 1;
6298 level->rate_time_over_score = TRUE;
6301 level->auto_exit_sokoban = TRUE;
6304 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6305 struct LevelFileInfo *level_file_info,
6306 boolean level_info_only)
6308 char *filename = level_file_info->filename;
6309 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6310 char last_comment[MAX_LINE_LEN];
6311 char level_name[MAX_LINE_LEN];
6314 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6315 boolean read_continued_line = FALSE;
6316 boolean reading_playfield = FALSE;
6317 boolean got_valid_playfield_line = FALSE;
6318 boolean invalid_playfield_char = FALSE;
6319 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6320 int file_level_nr = 0;
6321 int x = 0, y = 0; // initialized to make compilers happy
6323 last_comment[0] = '\0';
6324 level_name[0] = '\0';
6326 if (!(file = openFile(filename, MODE_READ)))
6328 level->no_valid_file = TRUE;
6330 if (!level_info_only)
6331 Warn("cannot read level '%s' -- using empty level", filename);
6336 while (!checkEndOfFile(file))
6338 // level successfully read, but next level may follow here
6339 if (!got_valid_playfield_line && reading_playfield)
6341 // read playfield from single level file -- skip remaining file
6342 if (!level_file_info->packed)
6345 if (file_level_nr >= num_levels_to_skip)
6350 last_comment[0] = '\0';
6351 level_name[0] = '\0';
6353 reading_playfield = FALSE;
6356 got_valid_playfield_line = FALSE;
6358 // read next line of input file
6359 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6362 // cut trailing line break (this can be newline and/or carriage return)
6363 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6364 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6367 // copy raw input line for later use (mainly debugging output)
6368 strcpy(line_raw, line);
6370 if (read_continued_line)
6372 // append new line to existing line, if there is enough space
6373 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6374 strcat(previous_line, line_ptr);
6376 strcpy(line, previous_line); // copy storage buffer to line
6378 read_continued_line = FALSE;
6381 // if the last character is '\', continue at next line
6382 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6384 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6385 strcpy(previous_line, line); // copy line to storage buffer
6387 read_continued_line = TRUE;
6393 if (line[0] == '\0')
6396 // extract comment text from comment line
6399 for (line_ptr = line; *line_ptr; line_ptr++)
6400 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6403 strcpy(last_comment, line_ptr);
6408 // extract level title text from line containing level title
6409 if (line[0] == '\'')
6411 strcpy(level_name, &line[1]);
6413 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6414 level_name[strlen(level_name) - 1] = '\0';
6419 // skip lines containing only spaces (or empty lines)
6420 for (line_ptr = line; *line_ptr; line_ptr++)
6421 if (*line_ptr != ' ')
6423 if (*line_ptr == '\0')
6426 // at this point, we have found a line containing part of a playfield
6428 got_valid_playfield_line = TRUE;
6430 if (!reading_playfield)
6432 reading_playfield = TRUE;
6433 invalid_playfield_char = FALSE;
6435 for (x = 0; x < MAX_LEV_FIELDX; x++)
6436 for (y = 0; y < MAX_LEV_FIELDY; y++)
6437 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6442 // start with topmost tile row
6446 // skip playfield line if larger row than allowed
6447 if (y >= MAX_LEV_FIELDY)
6450 // start with leftmost tile column
6453 // read playfield elements from line
6454 for (line_ptr = line; *line_ptr; line_ptr++)
6456 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6458 // stop parsing playfield line if larger column than allowed
6459 if (x >= MAX_LEV_FIELDX)
6462 if (mapped_sb_element == EL_UNDEFINED)
6464 invalid_playfield_char = TRUE;
6469 level->field[x][y] = mapped_sb_element;
6471 // continue with next tile column
6474 level->fieldx = MAX(x, level->fieldx);
6477 if (invalid_playfield_char)
6479 // if first playfield line, treat invalid lines as comment lines
6481 reading_playfield = FALSE;
6486 // continue with next tile row
6494 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6495 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6497 if (!reading_playfield)
6499 level->no_valid_file = TRUE;
6501 Warn("cannot read level '%s' -- using empty level", filename);
6506 if (*level_name != '\0')
6508 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6509 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6511 else if (*last_comment != '\0')
6513 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6514 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6518 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6521 // set all empty fields beyond the border walls to invisible steel wall
6522 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6524 if ((x == 0 || x == level->fieldx - 1 ||
6525 y == 0 || y == level->fieldy - 1) &&
6526 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6527 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6528 level->field, level->fieldx, level->fieldy);
6531 // set special level settings for Sokoban levels
6532 SetLevelSettings_SB(level);
6534 if (load_xsb_to_ces)
6536 // special global settings can now be set in level template
6537 level->use_custom_template = TRUE;
6542 // -------------------------------------------------------------------------
6543 // functions for handling native levels
6544 // -------------------------------------------------------------------------
6546 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6547 struct LevelFileInfo *level_file_info,
6548 boolean level_info_only)
6552 // determine position of requested level inside level package
6553 if (level_file_info->packed)
6554 pos = level_file_info->nr - leveldir_current->first_level;
6556 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6557 level->no_valid_file = TRUE;
6560 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6561 struct LevelFileInfo *level_file_info,
6562 boolean level_info_only)
6564 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6565 level->no_valid_file = TRUE;
6568 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6569 struct LevelFileInfo *level_file_info,
6570 boolean level_info_only)
6574 // determine position of requested level inside level package
6575 if (level_file_info->packed)
6576 pos = level_file_info->nr - leveldir_current->first_level;
6578 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6579 level->no_valid_file = TRUE;
6582 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6583 struct LevelFileInfo *level_file_info,
6584 boolean level_info_only)
6586 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6587 level->no_valid_file = TRUE;
6590 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6592 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6593 CopyNativeLevel_RND_to_BD(level);
6594 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6595 CopyNativeLevel_RND_to_EM(level);
6596 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6597 CopyNativeLevel_RND_to_SP(level);
6598 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6599 CopyNativeLevel_RND_to_MM(level);
6602 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6604 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6605 CopyNativeLevel_BD_to_RND(level);
6606 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6607 CopyNativeLevel_EM_to_RND(level);
6608 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6609 CopyNativeLevel_SP_to_RND(level);
6610 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6611 CopyNativeLevel_MM_to_RND(level);
6614 void SaveNativeLevel(struct LevelInfo *level)
6616 // saving native level files only supported for some game engines
6617 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6618 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6621 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6622 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6623 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6624 char *filename = getLevelFilenameFromBasename(basename);
6626 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6629 boolean success = FALSE;
6631 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6633 CopyNativeLevel_RND_to_BD(level);
6634 // CopyNativeTape_RND_to_BD(level);
6636 success = SaveNativeLevel_BD(filename);
6638 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6640 CopyNativeLevel_RND_to_SP(level);
6641 CopyNativeTape_RND_to_SP(level);
6643 success = SaveNativeLevel_SP(filename);
6647 Request("Native level file saved!", REQ_CONFIRM);
6649 Request("Failed to save native level file!", REQ_CONFIRM);
6653 // ----------------------------------------------------------------------------
6654 // functions for loading generic level
6655 // ----------------------------------------------------------------------------
6657 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6658 struct LevelFileInfo *level_file_info,
6659 boolean level_info_only)
6661 // always start with reliable default values
6662 setLevelInfoToDefaults(level, level_info_only, TRUE);
6664 switch (level_file_info->type)
6666 case LEVEL_FILE_TYPE_RND:
6667 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6670 case LEVEL_FILE_TYPE_BD:
6671 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6672 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6675 case LEVEL_FILE_TYPE_EM:
6676 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6677 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6680 case LEVEL_FILE_TYPE_SP:
6681 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6682 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6685 case LEVEL_FILE_TYPE_MM:
6686 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6687 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6690 case LEVEL_FILE_TYPE_DC:
6691 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6694 case LEVEL_FILE_TYPE_SB:
6695 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6699 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6703 // if level file is invalid, restore level structure to default values
6704 if (level->no_valid_file)
6705 setLevelInfoToDefaults(level, level_info_only, FALSE);
6707 if (check_special_flags("use_native_bd_game_engine"))
6708 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6710 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6711 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6713 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6714 CopyNativeLevel_Native_to_RND(level);
6717 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6719 static struct LevelFileInfo level_file_info;
6721 // always start with reliable default values
6722 setFileInfoToDefaults(&level_file_info);
6724 level_file_info.nr = 0; // unknown level number
6725 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6727 setString(&level_file_info.filename, filename);
6729 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6732 static void LoadLevel_InitVersion(struct LevelInfo *level)
6736 if (leveldir_current == NULL) // only when dumping level
6739 // all engine modifications also valid for levels which use latest engine
6740 if (level->game_version < VERSION_IDENT(3,2,0,5))
6742 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6743 level->time_score_base = 10;
6746 if (leveldir_current->latest_engine)
6748 // ---------- use latest game engine --------------------------------------
6750 /* For all levels which are forced to use the latest game engine version
6751 (normally all but user contributed, private and undefined levels), set
6752 the game engine version to the actual version; this allows for actual
6753 corrections in the game engine to take effect for existing, converted
6754 levels (from "classic" or other existing games) to make the emulation
6755 of the corresponding game more accurate, while (hopefully) not breaking
6756 existing levels created from other players. */
6758 level->game_version = GAME_VERSION_ACTUAL;
6760 /* Set special EM style gems behaviour: EM style gems slip down from
6761 normal, steel and growing wall. As this is a more fundamental change,
6762 it seems better to set the default behaviour to "off" (as it is more
6763 natural) and make it configurable in the level editor (as a property
6764 of gem style elements). Already existing converted levels (neither
6765 private nor contributed levels) are changed to the new behaviour. */
6767 if (level->file_version < FILE_VERSION_2_0)
6768 level->em_slippery_gems = TRUE;
6773 // ---------- use game engine the level was created with --------------------
6775 /* For all levels which are not forced to use the latest game engine
6776 version (normally user contributed, private and undefined levels),
6777 use the version of the game engine the levels were created for.
6779 Since 2.0.1, the game engine version is now directly stored
6780 in the level file (chunk "VERS"), so there is no need anymore
6781 to set the game version from the file version (except for old,
6782 pre-2.0 levels, where the game version is still taken from the
6783 file format version used to store the level -- see above). */
6785 // player was faster than enemies in 1.0.0 and before
6786 if (level->file_version == FILE_VERSION_1_0)
6787 for (i = 0; i < MAX_PLAYERS; i++)
6788 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6790 // default behaviour for EM style gems was "slippery" only in 2.0.1
6791 if (level->game_version == VERSION_IDENT(2,0,1,0))
6792 level->em_slippery_gems = TRUE;
6794 // springs could be pushed over pits before (pre-release version) 2.2.0
6795 if (level->game_version < VERSION_IDENT(2,2,0,0))
6796 level->use_spring_bug = TRUE;
6798 if (level->game_version < VERSION_IDENT(3,2,0,5))
6800 // time orb caused limited time in endless time levels before 3.2.0-5
6801 level->use_time_orb_bug = TRUE;
6803 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6804 level->block_snap_field = FALSE;
6806 // extra time score was same value as time left score before 3.2.0-5
6807 level->extra_time_score = level->score[SC_TIME_BONUS];
6810 if (level->game_version < VERSION_IDENT(3,2,0,7))
6812 // default behaviour for snapping was "not continuous" before 3.2.0-7
6813 level->continuous_snapping = FALSE;
6816 // only few elements were able to actively move into acid before 3.1.0
6817 // trigger settings did not exist before 3.1.0; set to default "any"
6818 if (level->game_version < VERSION_IDENT(3,1,0,0))
6820 // correct "can move into acid" settings (all zero in old levels)
6822 level->can_move_into_acid_bits = 0; // nothing can move into acid
6823 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6825 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6826 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6827 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6828 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6830 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6831 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6833 // correct trigger settings (stored as zero == "none" in old levels)
6835 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6837 int element = EL_CUSTOM_START + i;
6838 struct ElementInfo *ei = &element_info[element];
6840 for (j = 0; j < ei->num_change_pages; j++)
6842 struct ElementChangeInfo *change = &ei->change_page[j];
6844 change->trigger_player = CH_PLAYER_ANY;
6845 change->trigger_page = CH_PAGE_ANY;
6850 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6852 int element = EL_CUSTOM_256;
6853 struct ElementInfo *ei = &element_info[element];
6854 struct ElementChangeInfo *change = &ei->change_page[0];
6856 /* This is needed to fix a problem that was caused by a bugfix in function
6857 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6858 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6859 not replace walkable elements, but instead just placed the player on it,
6860 without placing the Sokoban field under the player). Unfortunately, this
6861 breaks "Snake Bite" style levels when the snake is halfway through a door
6862 that just closes (the snake head is still alive and can be moved in this
6863 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6864 player (without Sokoban element) which then gets killed as designed). */
6866 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6867 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6868 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6869 change->target_element = EL_PLAYER_1;
6872 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6873 if (level->game_version < VERSION_IDENT(3,2,5,0))
6875 /* This is needed to fix a problem that was caused by a bugfix in function
6876 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6877 corrects the behaviour when a custom element changes to another custom
6878 element with a higher element number that has change actions defined.
6879 Normally, only one change per frame is allowed for custom elements.
6880 Therefore, it is checked if a custom element already changed in the
6881 current frame; if it did, subsequent changes are suppressed.
6882 Unfortunately, this is only checked for element changes, but not for
6883 change actions, which are still executed. As the function above loops
6884 through all custom elements from lower to higher, an element change
6885 resulting in a lower CE number won't be checked again, while a target
6886 element with a higher number will also be checked, and potential change
6887 actions will get executed for this CE, too (which is wrong), while
6888 further changes are ignored (which is correct). As this bugfix breaks
6889 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6890 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6891 behaviour for existing levels and tapes that make use of this bug */
6893 level->use_action_after_change_bug = TRUE;
6896 // not centering level after relocating player was default only in 3.2.3
6897 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6898 level->shifted_relocation = TRUE;
6900 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6901 if (level->game_version < VERSION_IDENT(3,2,6,0))
6902 level->em_explodes_by_fire = TRUE;
6904 // levels were solved by the first player entering an exit up to 4.1.0.0
6905 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6906 level->solved_by_one_player = TRUE;
6908 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6909 if (level->game_version < VERSION_IDENT(4,1,1,1))
6910 level->use_life_bugs = TRUE;
6912 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6913 if (level->game_version < VERSION_IDENT(4,1,1,1))
6914 level->sb_objects_needed = FALSE;
6916 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6917 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6918 level->finish_dig_collect = FALSE;
6920 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6921 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6922 level->keep_walkable_ce = TRUE;
6925 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6927 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6930 // check if this level is (not) a Sokoban level
6931 for (y = 0; y < level->fieldy; y++)
6932 for (x = 0; x < level->fieldx; x++)
6933 if (!IS_SB_ELEMENT(Tile[x][y]))
6934 is_sokoban_level = FALSE;
6936 if (is_sokoban_level)
6938 // set special level settings for Sokoban levels
6939 SetLevelSettings_SB(level);
6943 static void LoadLevel_InitSettings(struct LevelInfo *level)
6945 // adjust level settings for (non-native) Sokoban-style levels
6946 LoadLevel_InitSettings_SB(level);
6948 // rename levels with title "nameless level" or if renaming is forced
6949 if (leveldir_current->empty_level_name != NULL &&
6950 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6951 leveldir_current->force_level_name))
6952 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6953 leveldir_current->empty_level_name, level_nr);
6956 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6960 // map elements that have changed in newer versions
6961 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6962 level->game_version);
6963 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6964 for (x = 0; x < 3; x++)
6965 for (y = 0; y < 3; y++)
6966 level->yamyam_content[i].e[x][y] =
6967 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6968 level->game_version);
6972 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6976 // map custom element change events that have changed in newer versions
6977 // (these following values were accidentally changed in version 3.0.1)
6978 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6979 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6983 int element = EL_CUSTOM_START + i;
6985 // order of checking and copying events to be mapped is important
6986 // (do not change the start and end value -- they are constant)
6987 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6989 if (HAS_CHANGE_EVENT(element, j - 2))
6991 SET_CHANGE_EVENT(element, j - 2, FALSE);
6992 SET_CHANGE_EVENT(element, j, TRUE);
6996 // order of checking and copying events to be mapped is important
6997 // (do not change the start and end value -- they are constant)
6998 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7000 if (HAS_CHANGE_EVENT(element, j - 1))
7002 SET_CHANGE_EVENT(element, j - 1, FALSE);
7003 SET_CHANGE_EVENT(element, j, TRUE);
7009 // initialize "can_change" field for old levels with only one change page
7010 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7014 int element = EL_CUSTOM_START + i;
7016 if (CAN_CHANGE(element))
7017 element_info[element].change->can_change = TRUE;
7021 // correct custom element values (for old levels without these options)
7022 if (level->game_version < VERSION_IDENT(3,1,1,0))
7024 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7026 int element = EL_CUSTOM_START + i;
7027 struct ElementInfo *ei = &element_info[element];
7029 if (ei->access_direction == MV_NO_DIRECTION)
7030 ei->access_direction = MV_ALL_DIRECTIONS;
7034 // correct custom element values (fix invalid values for all versions)
7037 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7039 int element = EL_CUSTOM_START + i;
7040 struct ElementInfo *ei = &element_info[element];
7042 for (j = 0; j < ei->num_change_pages; j++)
7044 struct ElementChangeInfo *change = &ei->change_page[j];
7046 if (change->trigger_player == CH_PLAYER_NONE)
7047 change->trigger_player = CH_PLAYER_ANY;
7049 if (change->trigger_side == CH_SIDE_NONE)
7050 change->trigger_side = CH_SIDE_ANY;
7055 // initialize "can_explode" field for old levels which did not store this
7056 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7057 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7059 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7061 int element = EL_CUSTOM_START + i;
7063 if (EXPLODES_1X1_OLD(element))
7064 element_info[element].explosion_type = EXPLODES_1X1;
7066 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7067 EXPLODES_SMASHED(element) ||
7068 EXPLODES_IMPACT(element)));
7072 // correct previously hard-coded move delay values for maze runner style
7073 if (level->game_version < VERSION_IDENT(3,1,1,0))
7075 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7077 int element = EL_CUSTOM_START + i;
7079 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7081 // previously hard-coded and therefore ignored
7082 element_info[element].move_delay_fixed = 9;
7083 element_info[element].move_delay_random = 0;
7088 // set some other uninitialized values of custom elements in older levels
7089 if (level->game_version < VERSION_IDENT(3,1,0,0))
7091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7093 int element = EL_CUSTOM_START + i;
7095 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7097 element_info[element].explosion_delay = 17;
7098 element_info[element].ignition_delay = 8;
7102 // set mouse click change events to work for left/middle/right mouse button
7103 if (level->game_version < VERSION_IDENT(4,2,3,0))
7105 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7107 int element = EL_CUSTOM_START + i;
7108 struct ElementInfo *ei = &element_info[element];
7110 for (j = 0; j < ei->num_change_pages; j++)
7112 struct ElementChangeInfo *change = &ei->change_page[j];
7114 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7115 change->has_event[CE_PRESSED_BY_MOUSE] ||
7116 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7117 change->has_event[CE_MOUSE_PRESSED_ON_X])
7118 change->trigger_side = CH_SIDE_ANY;
7124 static void LoadLevel_InitElements(struct LevelInfo *level)
7126 LoadLevel_InitStandardElements(level);
7128 if (level->file_has_custom_elements)
7129 LoadLevel_InitCustomElements(level);
7131 // initialize element properties for level editor etc.
7132 InitElementPropertiesEngine(level->game_version);
7133 InitElementPropertiesGfxElement();
7136 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7140 // map elements that have changed in newer versions
7141 for (y = 0; y < level->fieldy; y++)
7142 for (x = 0; x < level->fieldx; x++)
7143 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7144 level->game_version);
7146 // clear unused playfield data (nicer if level gets resized in editor)
7147 for (x = 0; x < MAX_LEV_FIELDX; x++)
7148 for (y = 0; y < MAX_LEV_FIELDY; y++)
7149 if (x >= level->fieldx || y >= level->fieldy)
7150 level->field[x][y] = EL_EMPTY;
7152 // copy elements to runtime playfield array
7153 for (x = 0; x < MAX_LEV_FIELDX; x++)
7154 for (y = 0; y < MAX_LEV_FIELDY; y++)
7155 Tile[x][y] = level->field[x][y];
7157 // initialize level size variables for faster access
7158 lev_fieldx = level->fieldx;
7159 lev_fieldy = level->fieldy;
7161 // determine border element for this level
7162 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7163 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7168 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7170 struct LevelFileInfo *level_file_info = &level->file_info;
7172 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7173 CopyNativeLevel_RND_to_Native(level);
7176 static void LoadLevelTemplate_LoadAndInit(void)
7178 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7180 LoadLevel_InitVersion(&level_template);
7181 LoadLevel_InitElements(&level_template);
7182 LoadLevel_InitSettings(&level_template);
7184 ActivateLevelTemplate();
7187 void LoadLevelTemplate(int nr)
7189 if (!fileExists(getGlobalLevelTemplateFilename()))
7191 Warn("no level template found for this level");
7196 setLevelFileInfo(&level_template.file_info, nr);
7198 LoadLevelTemplate_LoadAndInit();
7201 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7203 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7205 LoadLevelTemplate_LoadAndInit();
7208 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7210 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7212 if (level.use_custom_template)
7214 if (network_level != NULL)
7215 LoadNetworkLevelTemplate(network_level);
7217 LoadLevelTemplate(-1);
7220 LoadLevel_InitVersion(&level);
7221 LoadLevel_InitElements(&level);
7222 LoadLevel_InitPlayfield(&level);
7223 LoadLevel_InitSettings(&level);
7225 LoadLevel_InitNativeEngines(&level);
7228 void LoadLevel(int nr)
7230 SetLevelSetInfo(leveldir_current->identifier, nr);
7232 setLevelFileInfo(&level.file_info, nr);
7234 LoadLevel_LoadAndInit(NULL);
7237 void LoadLevelInfoOnly(int nr)
7239 setLevelFileInfo(&level.file_info, nr);
7241 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7244 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7246 SetLevelSetInfo(network_level->leveldir_identifier,
7247 network_level->file_info.nr);
7249 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7251 LoadLevel_LoadAndInit(network_level);
7254 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7258 chunk_size += putFileVersion(file, level->file_version);
7259 chunk_size += putFileVersion(file, level->game_version);
7264 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7268 chunk_size += putFile16BitBE(file, level->creation_date.year);
7269 chunk_size += putFile8Bit(file, level->creation_date.month);
7270 chunk_size += putFile8Bit(file, level->creation_date.day);
7275 #if ENABLE_HISTORIC_CHUNKS
7276 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7280 putFile8Bit(file, level->fieldx);
7281 putFile8Bit(file, level->fieldy);
7283 putFile16BitBE(file, level->time);
7284 putFile16BitBE(file, level->gems_needed);
7286 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7287 putFile8Bit(file, level->name[i]);
7289 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7290 putFile8Bit(file, level->score[i]);
7292 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7293 for (y = 0; y < 3; y++)
7294 for (x = 0; x < 3; x++)
7295 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7296 level->yamyam_content[i].e[x][y]));
7297 putFile8Bit(file, level->amoeba_speed);
7298 putFile8Bit(file, level->time_magic_wall);
7299 putFile8Bit(file, level->time_wheel);
7300 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7301 level->amoeba_content));
7302 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7303 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7304 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7305 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7307 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7309 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7310 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7311 putFile32BitBE(file, level->can_move_into_acid_bits);
7312 putFile8Bit(file, level->dont_collide_with_bits);
7314 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7315 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7317 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7318 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7319 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7321 putFile8Bit(file, level->game_engine_type);
7323 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7327 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7332 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7333 chunk_size += putFile8Bit(file, level->name[i]);
7338 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7343 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7344 chunk_size += putFile8Bit(file, level->author[i]);
7349 #if ENABLE_HISTORIC_CHUNKS
7350 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7355 for (y = 0; y < level->fieldy; y++)
7356 for (x = 0; x < level->fieldx; x++)
7357 if (level->encoding_16bit_field)
7358 chunk_size += putFile16BitBE(file, level->field[x][y]);
7360 chunk_size += putFile8Bit(file, level->field[x][y]);
7366 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7371 for (y = 0; y < level->fieldy; y++)
7372 for (x = 0; x < level->fieldx; x++)
7373 chunk_size += putFile16BitBE(file, level->field[x][y]);
7378 #if ENABLE_HISTORIC_CHUNKS
7379 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7383 putFile8Bit(file, EL_YAMYAM);
7384 putFile8Bit(file, level->num_yamyam_contents);
7385 putFile8Bit(file, 0);
7386 putFile8Bit(file, 0);
7388 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7389 for (y = 0; y < 3; y++)
7390 for (x = 0; x < 3; x++)
7391 if (level->encoding_16bit_field)
7392 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7394 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7398 #if ENABLE_HISTORIC_CHUNKS
7399 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7402 int num_contents, content_xsize, content_ysize;
7403 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7405 if (element == EL_YAMYAM)
7407 num_contents = level->num_yamyam_contents;
7411 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7412 for (y = 0; y < 3; y++)
7413 for (x = 0; x < 3; x++)
7414 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7416 else if (element == EL_BD_AMOEBA)
7422 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7423 for (y = 0; y < 3; y++)
7424 for (x = 0; x < 3; x++)
7425 content_array[i][x][y] = EL_EMPTY;
7426 content_array[0][0][0] = level->amoeba_content;
7430 // chunk header already written -- write empty chunk data
7431 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7433 Warn("cannot save content for element '%d'", element);
7438 putFile16BitBE(file, element);
7439 putFile8Bit(file, num_contents);
7440 putFile8Bit(file, content_xsize);
7441 putFile8Bit(file, content_ysize);
7443 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7445 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7446 for (y = 0; y < 3; y++)
7447 for (x = 0; x < 3; x++)
7448 putFile16BitBE(file, content_array[i][x][y]);
7452 #if ENABLE_HISTORIC_CHUNKS
7453 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7455 int envelope_nr = element - EL_ENVELOPE_1;
7456 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7460 chunk_size += putFile16BitBE(file, element);
7461 chunk_size += putFile16BitBE(file, envelope_len);
7462 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7463 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7465 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7466 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7468 for (i = 0; i < envelope_len; i++)
7469 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7475 #if ENABLE_HISTORIC_CHUNKS
7476 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7477 int num_changed_custom_elements)
7481 putFile16BitBE(file, num_changed_custom_elements);
7483 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7485 int element = EL_CUSTOM_START + i;
7487 struct ElementInfo *ei = &element_info[element];
7489 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7491 if (check < num_changed_custom_elements)
7493 putFile16BitBE(file, element);
7494 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7501 if (check != num_changed_custom_elements) // should not happen
7502 Warn("inconsistent number of custom element properties");
7506 #if ENABLE_HISTORIC_CHUNKS
7507 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7508 int num_changed_custom_elements)
7512 putFile16BitBE(file, num_changed_custom_elements);
7514 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7516 int element = EL_CUSTOM_START + i;
7518 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7520 if (check < num_changed_custom_elements)
7522 putFile16BitBE(file, element);
7523 putFile16BitBE(file, element_info[element].change->target_element);
7530 if (check != num_changed_custom_elements) // should not happen
7531 Warn("inconsistent number of custom target elements");
7535 #if ENABLE_HISTORIC_CHUNKS
7536 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7537 int num_changed_custom_elements)
7539 int i, j, x, y, check = 0;
7541 putFile16BitBE(file, num_changed_custom_elements);
7543 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7545 int element = EL_CUSTOM_START + i;
7546 struct ElementInfo *ei = &element_info[element];
7548 if (ei->modified_settings)
7550 if (check < num_changed_custom_elements)
7552 putFile16BitBE(file, element);
7554 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7555 putFile8Bit(file, ei->description[j]);
7557 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7559 // some free bytes for future properties and padding
7560 WriteUnusedBytesToFile(file, 7);
7562 putFile8Bit(file, ei->use_gfx_element);
7563 putFile16BitBE(file, ei->gfx_element_initial);
7565 putFile8Bit(file, ei->collect_score_initial);
7566 putFile8Bit(file, ei->collect_count_initial);
7568 putFile16BitBE(file, ei->push_delay_fixed);
7569 putFile16BitBE(file, ei->push_delay_random);
7570 putFile16BitBE(file, ei->move_delay_fixed);
7571 putFile16BitBE(file, ei->move_delay_random);
7573 putFile16BitBE(file, ei->move_pattern);
7574 putFile8Bit(file, ei->move_direction_initial);
7575 putFile8Bit(file, ei->move_stepsize);
7577 for (y = 0; y < 3; y++)
7578 for (x = 0; x < 3; x++)
7579 putFile16BitBE(file, ei->content.e[x][y]);
7581 putFile32BitBE(file, ei->change->events);
7583 putFile16BitBE(file, ei->change->target_element);
7585 putFile16BitBE(file, ei->change->delay_fixed);
7586 putFile16BitBE(file, ei->change->delay_random);
7587 putFile16BitBE(file, ei->change->delay_frames);
7589 putFile16BitBE(file, ei->change->initial_trigger_element);
7591 putFile8Bit(file, ei->change->explode);
7592 putFile8Bit(file, ei->change->use_target_content);
7593 putFile8Bit(file, ei->change->only_if_complete);
7594 putFile8Bit(file, ei->change->use_random_replace);
7596 putFile8Bit(file, ei->change->random_percentage);
7597 putFile8Bit(file, ei->change->replace_when);
7599 for (y = 0; y < 3; y++)
7600 for (x = 0; x < 3; x++)
7601 putFile16BitBE(file, ei->change->content.e[x][y]);
7603 putFile8Bit(file, ei->slippery_type);
7605 // some free bytes for future properties and padding
7606 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7613 if (check != num_changed_custom_elements) // should not happen
7614 Warn("inconsistent number of custom element properties");
7618 #if ENABLE_HISTORIC_CHUNKS
7619 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7621 struct ElementInfo *ei = &element_info[element];
7624 // ---------- custom element base property values (96 bytes) ----------------
7626 putFile16BitBE(file, element);
7628 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7629 putFile8Bit(file, ei->description[i]);
7631 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7633 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7635 putFile8Bit(file, ei->num_change_pages);
7637 putFile16BitBE(file, ei->ce_value_fixed_initial);
7638 putFile16BitBE(file, ei->ce_value_random_initial);
7639 putFile8Bit(file, ei->use_last_ce_value);
7641 putFile8Bit(file, ei->use_gfx_element);
7642 putFile16BitBE(file, ei->gfx_element_initial);
7644 putFile8Bit(file, ei->collect_score_initial);
7645 putFile8Bit(file, ei->collect_count_initial);
7647 putFile8Bit(file, ei->drop_delay_fixed);
7648 putFile8Bit(file, ei->push_delay_fixed);
7649 putFile8Bit(file, ei->drop_delay_random);
7650 putFile8Bit(file, ei->push_delay_random);
7651 putFile16BitBE(file, ei->move_delay_fixed);
7652 putFile16BitBE(file, ei->move_delay_random);
7654 // bits 0 - 15 of "move_pattern" ...
7655 putFile16BitBE(file, ei->move_pattern & 0xffff);
7656 putFile8Bit(file, ei->move_direction_initial);
7657 putFile8Bit(file, ei->move_stepsize);
7659 putFile8Bit(file, ei->slippery_type);
7661 for (y = 0; y < 3; y++)
7662 for (x = 0; x < 3; x++)
7663 putFile16BitBE(file, ei->content.e[x][y]);
7665 putFile16BitBE(file, ei->move_enter_element);
7666 putFile16BitBE(file, ei->move_leave_element);
7667 putFile8Bit(file, ei->move_leave_type);
7669 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7670 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7672 putFile8Bit(file, ei->access_direction);
7674 putFile8Bit(file, ei->explosion_delay);
7675 putFile8Bit(file, ei->ignition_delay);
7676 putFile8Bit(file, ei->explosion_type);
7678 // some free bytes for future custom property values and padding
7679 WriteUnusedBytesToFile(file, 1);
7681 // ---------- change page property values (48 bytes) ------------------------
7683 for (i = 0; i < ei->num_change_pages; i++)
7685 struct ElementChangeInfo *change = &ei->change_page[i];
7686 unsigned int event_bits;
7688 // bits 0 - 31 of "has_event[]" ...
7690 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7691 if (change->has_event[j])
7692 event_bits |= (1u << j);
7693 putFile32BitBE(file, event_bits);
7695 putFile16BitBE(file, change->target_element);
7697 putFile16BitBE(file, change->delay_fixed);
7698 putFile16BitBE(file, change->delay_random);
7699 putFile16BitBE(file, change->delay_frames);
7701 putFile16BitBE(file, change->initial_trigger_element);
7703 putFile8Bit(file, change->explode);
7704 putFile8Bit(file, change->use_target_content);
7705 putFile8Bit(file, change->only_if_complete);
7706 putFile8Bit(file, change->use_random_replace);
7708 putFile8Bit(file, change->random_percentage);
7709 putFile8Bit(file, change->replace_when);
7711 for (y = 0; y < 3; y++)
7712 for (x = 0; x < 3; x++)
7713 putFile16BitBE(file, change->target_content.e[x][y]);
7715 putFile8Bit(file, change->can_change);
7717 putFile8Bit(file, change->trigger_side);
7719 putFile8Bit(file, change->trigger_player);
7720 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7721 log_2(change->trigger_page)));
7723 putFile8Bit(file, change->has_action);
7724 putFile8Bit(file, change->action_type);
7725 putFile8Bit(file, change->action_mode);
7726 putFile16BitBE(file, change->action_arg);
7728 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7730 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7731 if (change->has_event[j])
7732 event_bits |= (1u << (j - 32));
7733 putFile8Bit(file, event_bits);
7738 #if ENABLE_HISTORIC_CHUNKS
7739 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7741 struct ElementInfo *ei = &element_info[element];
7742 struct ElementGroupInfo *group = ei->group;
7745 putFile16BitBE(file, element);
7747 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7748 putFile8Bit(file, ei->description[i]);
7750 putFile8Bit(file, group->num_elements);
7752 putFile8Bit(file, ei->use_gfx_element);
7753 putFile16BitBE(file, ei->gfx_element_initial);
7755 putFile8Bit(file, group->choice_mode);
7757 // some free bytes for future values and padding
7758 WriteUnusedBytesToFile(file, 3);
7760 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7761 putFile16BitBE(file, group->element[i]);
7765 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7766 boolean write_element)
7768 int save_type = entry->save_type;
7769 int data_type = entry->data_type;
7770 int conf_type = entry->conf_type;
7771 int byte_mask = conf_type & CONF_MASK_BYTES;
7772 int element = entry->element;
7773 int default_value = entry->default_value;
7775 boolean modified = FALSE;
7777 if (byte_mask != CONF_MASK_MULTI_BYTES)
7779 void *value_ptr = entry->value;
7780 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7783 // check if any settings have been modified before saving them
7784 if (value != default_value)
7787 // do not save if explicitly told or if unmodified default settings
7788 if ((save_type == SAVE_CONF_NEVER) ||
7789 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7793 num_bytes += putFile16BitBE(file, element);
7795 num_bytes += putFile8Bit(file, conf_type);
7796 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7797 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7798 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7801 else if (data_type == TYPE_STRING)
7803 char *default_string = entry->default_string;
7804 char *string = (char *)(entry->value);
7805 int string_length = strlen(string);
7808 // check if any settings have been modified before saving them
7809 if (!strEqual(string, default_string))
7812 // do not save if explicitly told or if unmodified default settings
7813 if ((save_type == SAVE_CONF_NEVER) ||
7814 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7818 num_bytes += putFile16BitBE(file, element);
7820 num_bytes += putFile8Bit(file, conf_type);
7821 num_bytes += putFile16BitBE(file, string_length);
7823 for (i = 0; i < string_length; i++)
7824 num_bytes += putFile8Bit(file, string[i]);
7826 else if (data_type == TYPE_ELEMENT_LIST)
7828 int *element_array = (int *)(entry->value);
7829 int num_elements = *(int *)(entry->num_entities);
7832 // check if any settings have been modified before saving them
7833 for (i = 0; i < num_elements; i++)
7834 if (element_array[i] != default_value)
7837 // do not save if explicitly told or if unmodified default settings
7838 if ((save_type == SAVE_CONF_NEVER) ||
7839 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7843 num_bytes += putFile16BitBE(file, element);
7845 num_bytes += putFile8Bit(file, conf_type);
7846 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7848 for (i = 0; i < num_elements; i++)
7849 num_bytes += putFile16BitBE(file, element_array[i]);
7851 else if (data_type == TYPE_CONTENT_LIST)
7853 struct Content *content = (struct Content *)(entry->value);
7854 int num_contents = *(int *)(entry->num_entities);
7857 // check if any settings have been modified before saving them
7858 for (i = 0; i < num_contents; i++)
7859 for (y = 0; y < 3; y++)
7860 for (x = 0; x < 3; x++)
7861 if (content[i].e[x][y] != default_value)
7864 // do not save if explicitly told or if unmodified default settings
7865 if ((save_type == SAVE_CONF_NEVER) ||
7866 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7870 num_bytes += putFile16BitBE(file, element);
7872 num_bytes += putFile8Bit(file, conf_type);
7873 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7875 for (i = 0; i < num_contents; i++)
7876 for (y = 0; y < 3; y++)
7877 for (x = 0; x < 3; x++)
7878 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7884 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7889 li = *level; // copy level data into temporary buffer
7891 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7892 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7897 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7902 li = *level; // copy level data into temporary buffer
7904 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7905 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7910 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7912 int envelope_nr = element - EL_ENVELOPE_1;
7916 chunk_size += putFile16BitBE(file, element);
7918 // copy envelope data into temporary buffer
7919 xx_envelope = level->envelope[envelope_nr];
7921 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7922 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7927 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7929 struct ElementInfo *ei = &element_info[element];
7933 chunk_size += putFile16BitBE(file, element);
7935 xx_ei = *ei; // copy element data into temporary buffer
7937 // set default description string for this specific element
7938 strcpy(xx_default_description, getDefaultElementDescription(ei));
7940 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7941 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7943 for (i = 0; i < ei->num_change_pages; i++)
7945 struct ElementChangeInfo *change = &ei->change_page[i];
7947 xx_current_change_page = i;
7949 xx_change = *change; // copy change data into temporary buffer
7952 setEventBitsFromEventFlags(change);
7954 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7955 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7962 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7964 struct ElementInfo *ei = &element_info[element];
7965 struct ElementGroupInfo *group = ei->group;
7969 chunk_size += putFile16BitBE(file, element);
7971 xx_ei = *ei; // copy element data into temporary buffer
7972 xx_group = *group; // copy group data into temporary buffer
7974 // set default description string for this specific element
7975 strcpy(xx_default_description, getDefaultElementDescription(ei));
7977 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7978 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7983 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7985 struct ElementInfo *ei = &element_info[element];
7989 chunk_size += putFile16BitBE(file, element);
7991 xx_ei = *ei; // copy element data into temporary buffer
7993 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7994 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7999 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8000 boolean save_as_template)
8006 if (!(file = fopen(filename, MODE_WRITE)))
8008 Warn("cannot save level file '%s'", filename);
8013 level->file_version = FILE_VERSION_ACTUAL;
8014 level->game_version = GAME_VERSION_ACTUAL;
8016 level->creation_date = getCurrentDate();
8018 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8019 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8021 chunk_size = SaveLevel_VERS(NULL, level);
8022 putFileChunkBE(file, "VERS", chunk_size);
8023 SaveLevel_VERS(file, level);
8025 chunk_size = SaveLevel_DATE(NULL, level);
8026 putFileChunkBE(file, "DATE", chunk_size);
8027 SaveLevel_DATE(file, level);
8029 chunk_size = SaveLevel_NAME(NULL, level);
8030 putFileChunkBE(file, "NAME", chunk_size);
8031 SaveLevel_NAME(file, level);
8033 chunk_size = SaveLevel_AUTH(NULL, level);
8034 putFileChunkBE(file, "AUTH", chunk_size);
8035 SaveLevel_AUTH(file, level);
8037 chunk_size = SaveLevel_INFO(NULL, level);
8038 putFileChunkBE(file, "INFO", chunk_size);
8039 SaveLevel_INFO(file, level);
8041 chunk_size = SaveLevel_BODY(NULL, level);
8042 putFileChunkBE(file, "BODY", chunk_size);
8043 SaveLevel_BODY(file, level);
8045 chunk_size = SaveLevel_ELEM(NULL, level);
8046 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8048 putFileChunkBE(file, "ELEM", chunk_size);
8049 SaveLevel_ELEM(file, level);
8052 for (i = 0; i < NUM_ENVELOPES; i++)
8054 int element = EL_ENVELOPE_1 + i;
8056 chunk_size = SaveLevel_NOTE(NULL, level, element);
8057 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8059 putFileChunkBE(file, "NOTE", chunk_size);
8060 SaveLevel_NOTE(file, level, element);
8064 // if not using template level, check for non-default custom/group elements
8065 if (!level->use_custom_template || save_as_template)
8067 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8069 int element = EL_CUSTOM_START + i;
8071 chunk_size = SaveLevel_CUSX(NULL, level, element);
8072 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8074 putFileChunkBE(file, "CUSX", chunk_size);
8075 SaveLevel_CUSX(file, level, element);
8079 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8081 int element = EL_GROUP_START + i;
8083 chunk_size = SaveLevel_GRPX(NULL, level, element);
8084 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8086 putFileChunkBE(file, "GRPX", chunk_size);
8087 SaveLevel_GRPX(file, level, element);
8091 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8093 int element = GET_EMPTY_ELEMENT(i);
8095 chunk_size = SaveLevel_EMPX(NULL, level, element);
8096 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8098 putFileChunkBE(file, "EMPX", chunk_size);
8099 SaveLevel_EMPX(file, level, element);
8106 SetFilePermissions(filename, PERMS_PRIVATE);
8109 void SaveLevel(int nr)
8111 char *filename = getDefaultLevelFilename(nr);
8113 SaveLevelFromFilename(&level, filename, FALSE);
8116 void SaveLevelTemplate(void)
8118 char *filename = getLocalLevelTemplateFilename();
8120 SaveLevelFromFilename(&level, filename, TRUE);
8123 boolean SaveLevelChecked(int nr)
8125 char *filename = getDefaultLevelFilename(nr);
8126 boolean new_level = !fileExists(filename);
8127 boolean level_saved = FALSE;
8129 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8134 Request("Level saved!", REQ_CONFIRM);
8142 void DumpLevel(struct LevelInfo *level)
8144 if (level->no_level_file || level->no_valid_file)
8146 Warn("cannot dump -- no valid level file found");
8152 Print("Level xxx (file version %08d, game version %08d)\n",
8153 level->file_version, level->game_version);
8156 Print("Level author: '%s'\n", level->author);
8157 Print("Level title: '%s'\n", level->name);
8159 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8161 Print("Level time: %d seconds\n", level->time);
8162 Print("Gems needed: %d\n", level->gems_needed);
8164 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8165 Print("Time for wheel: %d seconds\n", level->time_wheel);
8166 Print("Time for light: %d seconds\n", level->time_light);
8167 Print("Time for timegate: %d seconds\n", level->time_timegate);
8169 Print("Amoeba speed: %d\n", level->amoeba_speed);
8172 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8173 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8174 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8175 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8176 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8177 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8183 for (i = 0; i < NUM_ENVELOPES; i++)
8185 char *text = level->envelope[i].text;
8186 int text_len = strlen(text);
8187 boolean has_text = FALSE;
8189 for (j = 0; j < text_len; j++)
8190 if (text[j] != ' ' && text[j] != '\n')
8196 Print("Envelope %d:\n'%s'\n", i + 1, text);
8204 void DumpLevels(void)
8206 static LevelDirTree *dumplevel_leveldir = NULL;
8208 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8209 global.dumplevel_leveldir);
8211 if (dumplevel_leveldir == NULL)
8212 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8214 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8215 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8216 Fail("no such level number: %d", global.dumplevel_level_nr);
8218 leveldir_current = dumplevel_leveldir;
8220 LoadLevel(global.dumplevel_level_nr);
8227 // ============================================================================
8228 // tape file functions
8229 // ============================================================================
8231 static void setTapeInfoToDefaults(void)
8235 // always start with reliable default values (empty tape)
8238 // default values (also for pre-1.2 tapes) with only the first player
8239 tape.player_participates[0] = TRUE;
8240 for (i = 1; i < MAX_PLAYERS; i++)
8241 tape.player_participates[i] = FALSE;
8243 // at least one (default: the first) player participates in every tape
8244 tape.num_participating_players = 1;
8246 tape.property_bits = TAPE_PROPERTY_NONE;
8248 tape.level_nr = level_nr;
8250 tape.changed = FALSE;
8251 tape.solved = FALSE;
8253 tape.recording = FALSE;
8254 tape.playing = FALSE;
8255 tape.pausing = FALSE;
8257 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8258 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8260 tape.no_info_chunk = TRUE;
8261 tape.no_valid_file = FALSE;
8264 static int getTapePosSize(struct TapeInfo *tape)
8266 int tape_pos_size = 0;
8268 if (tape->use_key_actions)
8269 tape_pos_size += tape->num_participating_players;
8271 if (tape->use_mouse_actions)
8272 tape_pos_size += 3; // x and y position and mouse button mask
8274 tape_pos_size += 1; // tape action delay value
8276 return tape_pos_size;
8279 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8281 tape->use_key_actions = FALSE;
8282 tape->use_mouse_actions = FALSE;
8284 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8285 tape->use_key_actions = TRUE;
8287 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8288 tape->use_mouse_actions = TRUE;
8291 static int getTapeActionValue(struct TapeInfo *tape)
8293 return (tape->use_key_actions &&
8294 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8295 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8296 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8297 TAPE_ACTIONS_DEFAULT);
8300 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8302 tape->file_version = getFileVersion(file);
8303 tape->game_version = getFileVersion(file);
8308 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8312 tape->random_seed = getFile32BitBE(file);
8313 tape->date = getFile32BitBE(file);
8314 tape->length = getFile32BitBE(file);
8316 // read header fields that are new since version 1.2
8317 if (tape->file_version >= FILE_VERSION_1_2)
8319 byte store_participating_players = getFile8Bit(file);
8322 // since version 1.2, tapes store which players participate in the tape
8323 tape->num_participating_players = 0;
8324 for (i = 0; i < MAX_PLAYERS; i++)
8326 tape->player_participates[i] = FALSE;
8328 if (store_participating_players & (1 << i))
8330 tape->player_participates[i] = TRUE;
8331 tape->num_participating_players++;
8335 setTapeActionFlags(tape, getFile8Bit(file));
8337 tape->property_bits = getFile8Bit(file);
8338 tape->solved = getFile8Bit(file);
8340 engine_version = getFileVersion(file);
8341 if (engine_version > 0)
8342 tape->engine_version = engine_version;
8344 tape->engine_version = tape->game_version;
8350 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8352 tape->scr_fieldx = getFile8Bit(file);
8353 tape->scr_fieldy = getFile8Bit(file);
8358 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8360 char *level_identifier = NULL;
8361 int level_identifier_size;
8364 tape->no_info_chunk = FALSE;
8366 level_identifier_size = getFile16BitBE(file);
8368 level_identifier = checked_malloc(level_identifier_size);
8370 for (i = 0; i < level_identifier_size; i++)
8371 level_identifier[i] = getFile8Bit(file);
8373 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8374 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8376 checked_free(level_identifier);
8378 tape->level_nr = getFile16BitBE(file);
8380 chunk_size = 2 + level_identifier_size + 2;
8385 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8388 int tape_pos_size = getTapePosSize(tape);
8389 int chunk_size_expected = tape_pos_size * tape->length;
8391 if (chunk_size_expected != chunk_size)
8393 ReadUnusedBytesFromFile(file, chunk_size);
8394 return chunk_size_expected;
8397 for (i = 0; i < tape->length; i++)
8399 if (i >= MAX_TAPE_LEN)
8401 Warn("tape truncated -- size exceeds maximum tape size %d",
8404 // tape too large; read and ignore remaining tape data from this chunk
8405 for (;i < tape->length; i++)
8406 ReadUnusedBytesFromFile(file, tape_pos_size);
8411 if (tape->use_key_actions)
8413 for (j = 0; j < MAX_PLAYERS; j++)
8415 tape->pos[i].action[j] = MV_NONE;
8417 if (tape->player_participates[j])
8418 tape->pos[i].action[j] = getFile8Bit(file);
8422 if (tape->use_mouse_actions)
8424 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8425 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8426 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8429 tape->pos[i].delay = getFile8Bit(file);
8431 if (tape->file_version == FILE_VERSION_1_0)
8433 // eliminate possible diagonal moves in old tapes
8434 // this is only for backward compatibility
8436 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8437 byte action = tape->pos[i].action[0];
8438 int k, num_moves = 0;
8440 for (k = 0; k < 4; k++)
8442 if (action & joy_dir[k])
8444 tape->pos[i + num_moves].action[0] = joy_dir[k];
8446 tape->pos[i + num_moves].delay = 0;
8455 tape->length += num_moves;
8458 else if (tape->file_version < FILE_VERSION_2_0)
8460 // convert pre-2.0 tapes to new tape format
8462 if (tape->pos[i].delay > 1)
8465 tape->pos[i + 1] = tape->pos[i];
8466 tape->pos[i + 1].delay = 1;
8469 for (j = 0; j < MAX_PLAYERS; j++)
8470 tape->pos[i].action[j] = MV_NONE;
8471 tape->pos[i].delay--;
8478 if (checkEndOfFile(file))
8482 if (i != tape->length)
8483 chunk_size = tape_pos_size * i;
8488 static void LoadTape_SokobanSolution(char *filename)
8491 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8493 if (!(file = openFile(filename, MODE_READ)))
8495 tape.no_valid_file = TRUE;
8500 while (!checkEndOfFile(file))
8502 unsigned char c = getByteFromFile(file);
8504 if (checkEndOfFile(file))
8511 tape.pos[tape.length].action[0] = MV_UP;
8512 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8518 tape.pos[tape.length].action[0] = MV_DOWN;
8519 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8525 tape.pos[tape.length].action[0] = MV_LEFT;
8526 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8532 tape.pos[tape.length].action[0] = MV_RIGHT;
8533 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8541 // ignore white-space characters
8545 tape.no_valid_file = TRUE;
8547 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8555 if (tape.no_valid_file)
8558 tape.length_frames = GetTapeLengthFrames();
8559 tape.length_seconds = GetTapeLengthSeconds();
8562 void LoadTapeFromFilename(char *filename)
8564 char cookie[MAX_LINE_LEN];
8565 char chunk_name[CHUNK_ID_LEN + 1];
8569 // always start with reliable default values
8570 setTapeInfoToDefaults();
8572 if (strSuffix(filename, ".sln"))
8574 LoadTape_SokobanSolution(filename);
8579 if (!(file = openFile(filename, MODE_READ)))
8581 tape.no_valid_file = TRUE;
8586 getFileChunkBE(file, chunk_name, NULL);
8587 if (strEqual(chunk_name, "RND1"))
8589 getFile32BitBE(file); // not used
8591 getFileChunkBE(file, chunk_name, NULL);
8592 if (!strEqual(chunk_name, "TAPE"))
8594 tape.no_valid_file = TRUE;
8596 Warn("unknown format of tape file '%s'", filename);
8603 else // check for pre-2.0 file format with cookie string
8605 strcpy(cookie, chunk_name);
8606 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8608 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8609 cookie[strlen(cookie) - 1] = '\0';
8611 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8613 tape.no_valid_file = TRUE;
8615 Warn("unknown format of tape file '%s'", filename);
8622 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8624 tape.no_valid_file = TRUE;
8626 Warn("unsupported version of tape file '%s'", filename);
8633 // pre-2.0 tape files have no game version, so use file version here
8634 tape.game_version = tape.file_version;
8637 if (tape.file_version < FILE_VERSION_1_2)
8639 // tape files from versions before 1.2.0 without chunk structure
8640 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8641 LoadTape_BODY(file, 2 * tape.length, &tape);
8649 int (*loader)(File *, int, struct TapeInfo *);
8653 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8654 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8655 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8656 { "INFO", -1, LoadTape_INFO },
8657 { "BODY", -1, LoadTape_BODY },
8661 while (getFileChunkBE(file, chunk_name, &chunk_size))
8665 while (chunk_info[i].name != NULL &&
8666 !strEqual(chunk_name, chunk_info[i].name))
8669 if (chunk_info[i].name == NULL)
8671 Warn("unknown chunk '%s' in tape file '%s'",
8672 chunk_name, filename);
8674 ReadUnusedBytesFromFile(file, chunk_size);
8676 else if (chunk_info[i].size != -1 &&
8677 chunk_info[i].size != chunk_size)
8679 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8680 chunk_size, chunk_name, filename);
8682 ReadUnusedBytesFromFile(file, chunk_size);
8686 // call function to load this tape chunk
8687 int chunk_size_expected =
8688 (chunk_info[i].loader)(file, chunk_size, &tape);
8690 // the size of some chunks cannot be checked before reading other
8691 // chunks first (like "HEAD" and "BODY") that contain some header
8692 // information, so check them here
8693 if (chunk_size_expected != chunk_size)
8695 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8696 chunk_size, chunk_name, filename);
8704 tape.length_frames = GetTapeLengthFrames();
8705 tape.length_seconds = GetTapeLengthSeconds();
8708 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8710 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8712 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8713 tape.engine_version);
8717 void LoadTape(int nr)
8719 char *filename = getTapeFilename(nr);
8721 LoadTapeFromFilename(filename);
8724 void LoadSolutionTape(int nr)
8726 char *filename = getSolutionTapeFilename(nr);
8728 LoadTapeFromFilename(filename);
8730 if (TAPE_IS_EMPTY(tape))
8732 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8733 level.native_bd_level->replay != NULL)
8734 CopyNativeTape_BD_to_RND(&level);
8735 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8736 level.native_sp_level->demo.is_available)
8737 CopyNativeTape_SP_to_RND(&level);
8741 void LoadScoreTape(char *score_tape_basename, int nr)
8743 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8745 LoadTapeFromFilename(filename);
8748 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8750 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8752 LoadTapeFromFilename(filename);
8755 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8757 // chunk required for team mode tapes with non-default screen size
8758 return (tape->num_participating_players > 1 &&
8759 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8760 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8763 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8765 putFileVersion(file, tape->file_version);
8766 putFileVersion(file, tape->game_version);
8769 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8772 byte store_participating_players = 0;
8774 // set bits for participating players for compact storage
8775 for (i = 0; i < MAX_PLAYERS; i++)
8776 if (tape->player_participates[i])
8777 store_participating_players |= (1 << i);
8779 putFile32BitBE(file, tape->random_seed);
8780 putFile32BitBE(file, tape->date);
8781 putFile32BitBE(file, tape->length);
8783 putFile8Bit(file, store_participating_players);
8785 putFile8Bit(file, getTapeActionValue(tape));
8787 putFile8Bit(file, tape->property_bits);
8788 putFile8Bit(file, tape->solved);
8790 putFileVersion(file, tape->engine_version);
8793 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8795 putFile8Bit(file, tape->scr_fieldx);
8796 putFile8Bit(file, tape->scr_fieldy);
8799 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8801 int level_identifier_size = strlen(tape->level_identifier) + 1;
8804 putFile16BitBE(file, level_identifier_size);
8806 for (i = 0; i < level_identifier_size; i++)
8807 putFile8Bit(file, tape->level_identifier[i]);
8809 putFile16BitBE(file, tape->level_nr);
8812 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8816 for (i = 0; i < tape->length; i++)
8818 if (tape->use_key_actions)
8820 for (j = 0; j < MAX_PLAYERS; j++)
8821 if (tape->player_participates[j])
8822 putFile8Bit(file, tape->pos[i].action[j]);
8825 if (tape->use_mouse_actions)
8827 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8828 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8829 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8832 putFile8Bit(file, tape->pos[i].delay);
8836 void SaveTapeToFilename(char *filename)
8840 int info_chunk_size;
8841 int body_chunk_size;
8843 if (!(file = fopen(filename, MODE_WRITE)))
8845 Warn("cannot save level recording file '%s'", filename);
8850 tape_pos_size = getTapePosSize(&tape);
8852 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8853 body_chunk_size = tape_pos_size * tape.length;
8855 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8856 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8858 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8859 SaveTape_VERS(file, &tape);
8861 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8862 SaveTape_HEAD(file, &tape);
8864 if (checkSaveTape_SCRN(&tape))
8866 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8867 SaveTape_SCRN(file, &tape);
8870 putFileChunkBE(file, "INFO", info_chunk_size);
8871 SaveTape_INFO(file, &tape);
8873 putFileChunkBE(file, "BODY", body_chunk_size);
8874 SaveTape_BODY(file, &tape);
8878 SetFilePermissions(filename, PERMS_PRIVATE);
8881 static void SaveTapeExt(char *filename)
8885 tape.file_version = FILE_VERSION_ACTUAL;
8886 tape.game_version = GAME_VERSION_ACTUAL;
8888 tape.num_participating_players = 0;
8890 // count number of participating players
8891 for (i = 0; i < MAX_PLAYERS; i++)
8892 if (tape.player_participates[i])
8893 tape.num_participating_players++;
8895 SaveTapeToFilename(filename);
8897 tape.changed = FALSE;
8900 void SaveTape(int nr)
8902 char *filename = getTapeFilename(nr);
8904 InitTapeDirectory(leveldir_current->subdir);
8906 SaveTapeExt(filename);
8909 void SaveScoreTape(int nr)
8911 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8913 // used instead of "leveldir_current->subdir" (for network games)
8914 InitScoreTapeDirectory(levelset.identifier, nr);
8916 SaveTapeExt(filename);
8919 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8920 unsigned int req_state_added)
8922 char *filename = getTapeFilename(nr);
8923 boolean new_tape = !fileExists(filename);
8924 boolean tape_saved = FALSE;
8926 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8931 Request(msg_saved, REQ_CONFIRM | req_state_added);
8939 boolean SaveTapeChecked(int nr)
8941 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8944 boolean SaveTapeChecked_LevelSolved(int nr)
8946 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8947 "Level solved! Tape saved!", REQ_STAY_OPEN);
8950 void DumpTape(struct TapeInfo *tape)
8952 int tape_frame_counter;
8955 if (tape->no_valid_file)
8957 Warn("cannot dump -- no valid tape file found");
8964 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8965 tape->level_nr, tape->file_version, tape->game_version);
8966 Print(" (effective engine version %08d)\n",
8967 tape->engine_version);
8968 Print("Level series identifier: '%s'\n", tape->level_identifier);
8970 Print("Solution tape: %s\n",
8971 tape->solved ? "yes" :
8972 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8974 Print("Special tape properties: ");
8975 if (tape->property_bits == TAPE_PROPERTY_NONE)
8977 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8978 Print("[em_random_bug]");
8979 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8980 Print("[game_speed]");
8981 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8983 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8984 Print("[single_step]");
8985 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8986 Print("[snapshot]");
8987 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8988 Print("[replayed]");
8989 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8990 Print("[tas_keys]");
8991 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8992 Print("[small_graphics]");
8995 int year2 = tape->date / 10000;
8996 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8997 int month_index_raw = (tape->date / 100) % 100;
8998 int month_index = month_index_raw % 12; // prevent invalid index
8999 int month = month_index + 1;
9000 int day = tape->date % 100;
9002 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9006 tape_frame_counter = 0;
9008 for (i = 0; i < tape->length; i++)
9010 if (i >= MAX_TAPE_LEN)
9015 for (j = 0; j < MAX_PLAYERS; j++)
9017 if (tape->player_participates[j])
9019 int action = tape->pos[i].action[j];
9021 Print("%d:%02x ", j, action);
9022 Print("[%c%c%c%c|%c%c] - ",
9023 (action & JOY_LEFT ? '<' : ' '),
9024 (action & JOY_RIGHT ? '>' : ' '),
9025 (action & JOY_UP ? '^' : ' '),
9026 (action & JOY_DOWN ? 'v' : ' '),
9027 (action & JOY_BUTTON_1 ? '1' : ' '),
9028 (action & JOY_BUTTON_2 ? '2' : ' '));
9032 Print("(%03d) ", tape->pos[i].delay);
9033 Print("[%05d]\n", tape_frame_counter);
9035 tape_frame_counter += tape->pos[i].delay;
9041 void DumpTapes(void)
9043 static LevelDirTree *dumptape_leveldir = NULL;
9045 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9046 global.dumptape_leveldir);
9048 if (dumptape_leveldir == NULL)
9049 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9051 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9052 global.dumptape_level_nr > dumptape_leveldir->last_level)
9053 Fail("no such level number: %d", global.dumptape_level_nr);
9055 leveldir_current = dumptape_leveldir;
9057 if (options.mytapes)
9058 LoadTape(global.dumptape_level_nr);
9060 LoadSolutionTape(global.dumptape_level_nr);
9068 // ============================================================================
9069 // score file functions
9070 // ============================================================================
9072 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9076 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9078 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9079 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9080 scores->entry[i].score = 0;
9081 scores->entry[i].time = 0;
9083 scores->entry[i].id = -1;
9084 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9085 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9086 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9087 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9088 strcpy(scores->entry[i].country_code, "??");
9091 scores->num_entries = 0;
9092 scores->last_added = -1;
9093 scores->last_added_local = -1;
9095 scores->updated = FALSE;
9096 scores->uploaded = FALSE;
9097 scores->tape_downloaded = FALSE;
9098 scores->force_last_added = FALSE;
9100 // The following values are intentionally not reset here:
9104 // - continue_playing
9105 // - continue_on_return
9108 static void setScoreInfoToDefaults(void)
9110 setScoreInfoToDefaultsExt(&scores);
9113 static void setServerScoreInfoToDefaults(void)
9115 setScoreInfoToDefaultsExt(&server_scores);
9118 static void LoadScore_OLD(int nr)
9121 char *filename = getScoreFilename(nr);
9122 char cookie[MAX_LINE_LEN];
9123 char line[MAX_LINE_LEN];
9127 if (!(file = fopen(filename, MODE_READ)))
9130 // check file identifier
9131 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9133 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9134 cookie[strlen(cookie) - 1] = '\0';
9136 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9138 Warn("unknown format of score file '%s'", filename);
9145 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9147 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9148 Warn("fscanf() failed; %s", strerror(errno));
9150 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9153 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9154 line[strlen(line) - 1] = '\0';
9156 for (line_ptr = line; *line_ptr; line_ptr++)
9158 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9160 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9161 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9170 static void ConvertScore_OLD(void)
9172 // only convert score to time for levels that rate playing time over score
9173 if (!level.rate_time_over_score)
9176 // convert old score to playing time for score-less levels (like Supaplex)
9177 int time_final_max = 999;
9180 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9182 int score = scores.entry[i].score;
9184 if (score > 0 && score < time_final_max)
9185 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9189 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9191 scores->file_version = getFileVersion(file);
9192 scores->game_version = getFileVersion(file);
9197 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9199 char *level_identifier = NULL;
9200 int level_identifier_size;
9203 level_identifier_size = getFile16BitBE(file);
9205 level_identifier = checked_malloc(level_identifier_size);
9207 for (i = 0; i < level_identifier_size; i++)
9208 level_identifier[i] = getFile8Bit(file);
9210 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9211 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9213 checked_free(level_identifier);
9215 scores->level_nr = getFile16BitBE(file);
9216 scores->num_entries = getFile16BitBE(file);
9218 chunk_size = 2 + level_identifier_size + 2 + 2;
9223 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9227 for (i = 0; i < scores->num_entries; i++)
9229 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9230 scores->entry[i].name[j] = getFile8Bit(file);
9232 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9235 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9240 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9244 for (i = 0; i < scores->num_entries; i++)
9245 scores->entry[i].score = getFile16BitBE(file);
9247 chunk_size = scores->num_entries * 2;
9252 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9256 for (i = 0; i < scores->num_entries; i++)
9257 scores->entry[i].score = getFile32BitBE(file);
9259 chunk_size = scores->num_entries * 4;
9264 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9268 for (i = 0; i < scores->num_entries; i++)
9269 scores->entry[i].time = getFile32BitBE(file);
9271 chunk_size = scores->num_entries * 4;
9276 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9280 for (i = 0; i < scores->num_entries; i++)
9282 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9283 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9285 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9288 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9293 void LoadScore(int nr)
9295 char *filename = getScoreFilename(nr);
9296 char cookie[MAX_LINE_LEN];
9297 char chunk_name[CHUNK_ID_LEN + 1];
9299 boolean old_score_file_format = FALSE;
9302 // always start with reliable default values
9303 setScoreInfoToDefaults();
9305 if (!(file = openFile(filename, MODE_READ)))
9308 getFileChunkBE(file, chunk_name, NULL);
9309 if (strEqual(chunk_name, "RND1"))
9311 getFile32BitBE(file); // not used
9313 getFileChunkBE(file, chunk_name, NULL);
9314 if (!strEqual(chunk_name, "SCOR"))
9316 Warn("unknown format of score file '%s'", filename);
9323 else // check for old file format with cookie string
9325 strcpy(cookie, chunk_name);
9326 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9328 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9329 cookie[strlen(cookie) - 1] = '\0';
9331 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9333 Warn("unknown format of score file '%s'", filename);
9340 old_score_file_format = TRUE;
9343 if (old_score_file_format)
9345 // score files from versions before 4.2.4.0 without chunk structure
9348 // convert score to time, if possible (mainly for Supaplex levels)
9357 int (*loader)(File *, int, struct ScoreInfo *);
9361 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9362 { "INFO", -1, LoadScore_INFO },
9363 { "NAME", -1, LoadScore_NAME },
9364 { "SCOR", -1, LoadScore_SCOR },
9365 { "SC4R", -1, LoadScore_SC4R },
9366 { "TIME", -1, LoadScore_TIME },
9367 { "TAPE", -1, LoadScore_TAPE },
9372 while (getFileChunkBE(file, chunk_name, &chunk_size))
9376 while (chunk_info[i].name != NULL &&
9377 !strEqual(chunk_name, chunk_info[i].name))
9380 if (chunk_info[i].name == NULL)
9382 Warn("unknown chunk '%s' in score file '%s'",
9383 chunk_name, filename);
9385 ReadUnusedBytesFromFile(file, chunk_size);
9387 else if (chunk_info[i].size != -1 &&
9388 chunk_info[i].size != chunk_size)
9390 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9391 chunk_size, chunk_name, filename);
9393 ReadUnusedBytesFromFile(file, chunk_size);
9397 // call function to load this score chunk
9398 int chunk_size_expected =
9399 (chunk_info[i].loader)(file, chunk_size, &scores);
9401 // the size of some chunks cannot be checked before reading other
9402 // chunks first (like "HEAD" and "BODY") that contain some header
9403 // information, so check them here
9404 if (chunk_size_expected != chunk_size)
9406 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9407 chunk_size, chunk_name, filename);
9416 #if ENABLE_HISTORIC_CHUNKS
9417 void SaveScore_OLD(int nr)
9420 char *filename = getScoreFilename(nr);
9423 // used instead of "leveldir_current->subdir" (for network games)
9424 InitScoreDirectory(levelset.identifier);
9426 if (!(file = fopen(filename, MODE_WRITE)))
9428 Warn("cannot save score for level %d", nr);
9433 fprintf(file, "%s\n\n", SCORE_COOKIE);
9435 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9436 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9440 SetFilePermissions(filename, PERMS_PRIVATE);
9444 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9446 putFileVersion(file, scores->file_version);
9447 putFileVersion(file, scores->game_version);
9450 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9452 int level_identifier_size = strlen(scores->level_identifier) + 1;
9455 putFile16BitBE(file, level_identifier_size);
9457 for (i = 0; i < level_identifier_size; i++)
9458 putFile8Bit(file, scores->level_identifier[i]);
9460 putFile16BitBE(file, scores->level_nr);
9461 putFile16BitBE(file, scores->num_entries);
9464 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9468 for (i = 0; i < scores->num_entries; i++)
9470 int name_size = strlen(scores->entry[i].name);
9472 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9473 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9477 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9481 for (i = 0; i < scores->num_entries; i++)
9482 putFile16BitBE(file, scores->entry[i].score);
9485 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9489 for (i = 0; i < scores->num_entries; i++)
9490 putFile32BitBE(file, scores->entry[i].score);
9493 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9497 for (i = 0; i < scores->num_entries; i++)
9498 putFile32BitBE(file, scores->entry[i].time);
9501 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9505 for (i = 0; i < scores->num_entries; i++)
9507 int size = strlen(scores->entry[i].tape_basename);
9509 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9510 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9514 static void SaveScoreToFilename(char *filename)
9517 int info_chunk_size;
9518 int name_chunk_size;
9519 int scor_chunk_size;
9520 int sc4r_chunk_size;
9521 int time_chunk_size;
9522 int tape_chunk_size;
9523 boolean has_large_score_values;
9526 if (!(file = fopen(filename, MODE_WRITE)))
9528 Warn("cannot save score file '%s'", filename);
9533 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9534 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9535 scor_chunk_size = scores.num_entries * 2;
9536 sc4r_chunk_size = scores.num_entries * 4;
9537 time_chunk_size = scores.num_entries * 4;
9538 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9540 has_large_score_values = FALSE;
9541 for (i = 0; i < scores.num_entries; i++)
9542 if (scores.entry[i].score > 0xffff)
9543 has_large_score_values = TRUE;
9545 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9546 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9548 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9549 SaveScore_VERS(file, &scores);
9551 putFileChunkBE(file, "INFO", info_chunk_size);
9552 SaveScore_INFO(file, &scores);
9554 putFileChunkBE(file, "NAME", name_chunk_size);
9555 SaveScore_NAME(file, &scores);
9557 if (has_large_score_values)
9559 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9560 SaveScore_SC4R(file, &scores);
9564 putFileChunkBE(file, "SCOR", scor_chunk_size);
9565 SaveScore_SCOR(file, &scores);
9568 putFileChunkBE(file, "TIME", time_chunk_size);
9569 SaveScore_TIME(file, &scores);
9571 putFileChunkBE(file, "TAPE", tape_chunk_size);
9572 SaveScore_TAPE(file, &scores);
9576 SetFilePermissions(filename, PERMS_PRIVATE);
9579 void SaveScore(int nr)
9581 char *filename = getScoreFilename(nr);
9584 // used instead of "leveldir_current->subdir" (for network games)
9585 InitScoreDirectory(levelset.identifier);
9587 scores.file_version = FILE_VERSION_ACTUAL;
9588 scores.game_version = GAME_VERSION_ACTUAL;
9590 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9591 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9592 scores.level_nr = level_nr;
9594 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9595 if (scores.entry[i].score == 0 &&
9596 scores.entry[i].time == 0 &&
9597 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9600 scores.num_entries = i;
9602 if (scores.num_entries == 0)
9605 SaveScoreToFilename(filename);
9608 static void LoadServerScoreFromCache(int nr)
9610 struct ScoreEntry score_entry;
9619 { &score_entry.score, FALSE, 0 },
9620 { &score_entry.time, FALSE, 0 },
9621 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9622 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9623 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9624 { &score_entry.id, FALSE, 0 },
9625 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9626 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9627 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9628 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9632 char *filename = getScoreCacheFilename(nr);
9633 SetupFileHash *score_hash = loadSetupFileHash(filename);
9636 server_scores.num_entries = 0;
9638 if (score_hash == NULL)
9641 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9643 score_entry = server_scores.entry[i];
9645 for (j = 0; score_mapping[j].value != NULL; j++)
9649 sprintf(token, "%02d.%d", i, j);
9651 char *value = getHashEntry(score_hash, token);
9656 if (score_mapping[j].is_string)
9658 char *score_value = (char *)score_mapping[j].value;
9659 int value_size = score_mapping[j].string_size;
9661 strncpy(score_value, value, value_size);
9662 score_value[value_size] = '\0';
9666 int *score_value = (int *)score_mapping[j].value;
9668 *score_value = atoi(value);
9671 server_scores.num_entries = i + 1;
9674 server_scores.entry[i] = score_entry;
9677 freeSetupFileHash(score_hash);
9680 void LoadServerScore(int nr, boolean download_score)
9682 if (!setup.use_api_server)
9685 // always start with reliable default values
9686 setServerScoreInfoToDefaults();
9688 // 1st step: load server scores from cache file (which may not exist)
9689 // (this should prevent reading it while the thread is writing to it)
9690 LoadServerScoreFromCache(nr);
9692 if (download_score && runtime.use_api_server)
9694 // 2nd step: download server scores from score server to cache file
9695 // (as thread, as it might time out if the server is not reachable)
9696 ApiGetScoreAsThread(nr);
9700 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9702 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9704 // if score tape not uploaded, ask for uploading missing tapes later
9705 if (!setup.has_remaining_tapes)
9706 setup.ask_for_remaining_tapes = TRUE;
9708 setup.provide_uploading_tapes = TRUE;
9709 setup.has_remaining_tapes = TRUE;
9711 SaveSetup_ServerSetup();
9714 void SaveServerScore(int nr, boolean tape_saved)
9716 if (!runtime.use_api_server)
9718 PrepareScoreTapesForUpload(leveldir_current->subdir);
9723 ApiAddScoreAsThread(nr, tape_saved, NULL);
9726 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9727 char *score_tape_filename)
9729 if (!runtime.use_api_server)
9732 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9735 void LoadLocalAndServerScore(int nr, boolean download_score)
9737 int last_added_local = scores.last_added_local;
9738 boolean force_last_added = scores.force_last_added;
9740 // needed if only showing server scores
9741 setScoreInfoToDefaults();
9743 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9746 // restore last added local score entry (before merging server scores)
9747 scores.last_added = scores.last_added_local = last_added_local;
9749 if (setup.use_api_server &&
9750 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9752 // load server scores from cache file and trigger update from server
9753 LoadServerScore(nr, download_score);
9755 // merge local scores with scores from server
9759 if (force_last_added)
9760 scores.force_last_added = force_last_added;
9764 // ============================================================================
9765 // setup file functions
9766 // ============================================================================
9768 #define TOKEN_STR_PLAYER_PREFIX "player_"
9771 static struct TokenInfo global_setup_tokens[] =
9775 &setup.player_name, "player_name"
9779 &setup.multiple_users, "multiple_users"
9783 &setup.sound, "sound"
9787 &setup.sound_loops, "repeating_sound_loops"
9791 &setup.sound_music, "background_music"
9795 &setup.sound_simple, "simple_sound_effects"
9799 &setup.toons, "toons"
9803 &setup.global_animations, "global_animations"
9807 &setup.scroll_delay, "scroll_delay"
9811 &setup.forced_scroll_delay, "forced_scroll_delay"
9815 &setup.scroll_delay_value, "scroll_delay_value"
9819 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9823 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9827 &setup.fade_screens, "fade_screens"
9831 &setup.autorecord, "automatic_tape_recording"
9835 &setup.autorecord_after_replay, "autorecord_after_replay"
9839 &setup.auto_pause_on_start, "auto_pause_on_start"
9843 &setup.show_titlescreen, "show_titlescreen"
9847 &setup.quick_doors, "quick_doors"
9851 &setup.team_mode, "team_mode"
9855 &setup.handicap, "handicap"
9859 &setup.skip_levels, "skip_levels"
9863 &setup.increment_levels, "increment_levels"
9867 &setup.auto_play_next_level, "auto_play_next_level"
9871 &setup.count_score_after_game, "count_score_after_game"
9875 &setup.show_scores_after_game, "show_scores_after_game"
9879 &setup.time_limit, "time_limit"
9883 &setup.fullscreen, "fullscreen"
9887 &setup.window_scaling_percent, "window_scaling_percent"
9891 &setup.window_scaling_quality, "window_scaling_quality"
9895 &setup.screen_rendering_mode, "screen_rendering_mode"
9899 &setup.vsync_mode, "vsync_mode"
9903 &setup.ask_on_escape, "ask_on_escape"
9907 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9911 &setup.ask_on_game_over, "ask_on_game_over"
9915 &setup.ask_on_quit_game, "ask_on_quit_game"
9919 &setup.ask_on_quit_program, "ask_on_quit_program"
9923 &setup.quick_switch, "quick_player_switch"
9927 &setup.input_on_focus, "input_on_focus"
9931 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9935 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9939 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9943 &setup.game_speed_extended, "game_speed_extended"
9947 &setup.game_frame_delay, "game_frame_delay"
9951 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9955 &setup.bd_skip_hatching, "bd_skip_hatching"
9959 &setup.bd_scroll_delay, "bd_scroll_delay"
9963 &setup.bd_smooth_movements, "bd_smooth_movements"
9967 &setup.sp_show_border_elements, "sp_show_border_elements"
9971 &setup.small_game_graphics, "small_game_graphics"
9975 &setup.show_load_save_buttons, "show_load_save_buttons"
9979 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9983 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9987 &setup.graphics_set, "graphics_set"
9991 &setup.sounds_set, "sounds_set"
9995 &setup.music_set, "music_set"
9999 &setup.override_level_graphics, "override_level_graphics"
10003 &setup.override_level_sounds, "override_level_sounds"
10007 &setup.override_level_music, "override_level_music"
10011 &setup.volume_simple, "volume_simple"
10015 &setup.volume_loops, "volume_loops"
10019 &setup.volume_music, "volume_music"
10023 &setup.network_mode, "network_mode"
10027 &setup.network_player_nr, "network_player"
10031 &setup.network_server_hostname, "network_server_hostname"
10035 &setup.touch.control_type, "touch.control_type"
10039 &setup.touch.move_distance, "touch.move_distance"
10043 &setup.touch.drop_distance, "touch.drop_distance"
10047 &setup.touch.transparency, "touch.transparency"
10051 &setup.touch.draw_outlined, "touch.draw_outlined"
10055 &setup.touch.draw_pressed, "touch.draw_pressed"
10059 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10063 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10067 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10071 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10075 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10079 static struct TokenInfo auto_setup_tokens[] =
10083 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10087 static struct TokenInfo server_setup_tokens[] =
10091 &setup.player_uuid, "player_uuid"
10095 &setup.player_version, "player_version"
10099 &setup.use_api_server, TEST_PREFIX "use_api_server"
10103 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10107 &setup.api_server_password, TEST_PREFIX "api_server_password"
10111 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10115 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10119 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10123 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10127 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10131 static struct TokenInfo editor_setup_tokens[] =
10135 &setup.editor.el_classic, "editor.el_classic"
10139 &setup.editor.el_custom, "editor.el_custom"
10143 &setup.editor.el_user_defined, "editor.el_user_defined"
10147 &setup.editor.el_dynamic, "editor.el_dynamic"
10151 &setup.editor.el_headlines, "editor.el_headlines"
10155 &setup.editor.show_element_token, "editor.show_element_token"
10159 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10163 static struct TokenInfo editor_cascade_setup_tokens[] =
10167 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10171 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10175 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10179 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10183 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10187 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10191 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10195 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10199 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10203 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10207 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10211 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10215 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10219 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10223 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10227 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10231 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10235 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10239 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10243 static struct TokenInfo shortcut_setup_tokens[] =
10247 &setup.shortcut.save_game, "shortcut.save_game"
10251 &setup.shortcut.load_game, "shortcut.load_game"
10255 &setup.shortcut.restart_game, "shortcut.restart_game"
10259 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10263 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10267 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10271 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10275 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10279 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10283 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10287 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10291 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10295 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10299 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10303 &setup.shortcut.tape_record, "shortcut.tape_record"
10307 &setup.shortcut.tape_play, "shortcut.tape_play"
10311 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10315 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10319 &setup.shortcut.sound_music, "shortcut.sound_music"
10323 &setup.shortcut.snap_left, "shortcut.snap_left"
10327 &setup.shortcut.snap_right, "shortcut.snap_right"
10331 &setup.shortcut.snap_up, "shortcut.snap_up"
10335 &setup.shortcut.snap_down, "shortcut.snap_down"
10339 static struct SetupInputInfo setup_input;
10340 static struct TokenInfo player_setup_tokens[] =
10344 &setup_input.use_joystick, ".use_joystick"
10348 &setup_input.joy.device_name, ".joy.device_name"
10352 &setup_input.joy.xleft, ".joy.xleft"
10356 &setup_input.joy.xmiddle, ".joy.xmiddle"
10360 &setup_input.joy.xright, ".joy.xright"
10364 &setup_input.joy.yupper, ".joy.yupper"
10368 &setup_input.joy.ymiddle, ".joy.ymiddle"
10372 &setup_input.joy.ylower, ".joy.ylower"
10376 &setup_input.joy.snap, ".joy.snap_field"
10380 &setup_input.joy.drop, ".joy.place_bomb"
10384 &setup_input.key.left, ".key.move_left"
10388 &setup_input.key.right, ".key.move_right"
10392 &setup_input.key.up, ".key.move_up"
10396 &setup_input.key.down, ".key.move_down"
10400 &setup_input.key.snap, ".key.snap_field"
10404 &setup_input.key.drop, ".key.place_bomb"
10408 static struct TokenInfo system_setup_tokens[] =
10412 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10416 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10420 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10424 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10428 static struct TokenInfo internal_setup_tokens[] =
10432 &setup.internal.program_title, "program_title"
10436 &setup.internal.program_version, "program_version"
10440 &setup.internal.program_author, "program_author"
10444 &setup.internal.program_email, "program_email"
10448 &setup.internal.program_website, "program_website"
10452 &setup.internal.program_copyright, "program_copyright"
10456 &setup.internal.program_company, "program_company"
10460 &setup.internal.program_icon_file, "program_icon_file"
10464 &setup.internal.default_graphics_set, "default_graphics_set"
10468 &setup.internal.default_sounds_set, "default_sounds_set"
10472 &setup.internal.default_music_set, "default_music_set"
10476 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10480 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10484 &setup.internal.fallback_music_file, "fallback_music_file"
10488 &setup.internal.default_level_series, "default_level_series"
10492 &setup.internal.default_window_width, "default_window_width"
10496 &setup.internal.default_window_height, "default_window_height"
10500 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10504 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10508 &setup.internal.create_user_levelset, "create_user_levelset"
10512 &setup.internal.info_screens_from_main, "info_screens_from_main"
10516 &setup.internal.menu_game, "menu_game"
10520 &setup.internal.menu_engines, "menu_engines"
10524 &setup.internal.menu_editor, "menu_editor"
10528 &setup.internal.menu_graphics, "menu_graphics"
10532 &setup.internal.menu_sound, "menu_sound"
10536 &setup.internal.menu_artwork, "menu_artwork"
10540 &setup.internal.menu_input, "menu_input"
10544 &setup.internal.menu_touch, "menu_touch"
10548 &setup.internal.menu_shortcuts, "menu_shortcuts"
10552 &setup.internal.menu_exit, "menu_exit"
10556 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10560 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10564 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10568 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10572 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10576 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10580 &setup.internal.info_title, "info_title"
10584 &setup.internal.info_elements, "info_elements"
10588 &setup.internal.info_music, "info_music"
10592 &setup.internal.info_credits, "info_credits"
10596 &setup.internal.info_program, "info_program"
10600 &setup.internal.info_version, "info_version"
10604 &setup.internal.info_levelset, "info_levelset"
10608 &setup.internal.info_exit, "info_exit"
10612 static struct TokenInfo debug_setup_tokens[] =
10616 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10620 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10624 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10628 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10632 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10636 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10640 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10644 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10648 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10652 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10656 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10660 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10664 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10668 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10672 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10676 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10680 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10684 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10688 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10692 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10696 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10699 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10703 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10707 &setup.debug.xsn_mode, "debug.xsn_mode"
10711 &setup.debug.xsn_percent, "debug.xsn_percent"
10715 static struct TokenInfo options_setup_tokens[] =
10719 &setup.options.verbose, "options.verbose"
10723 &setup.options.debug, "options.debug"
10727 &setup.options.debug_mode, "options.debug_mode"
10731 static void setSetupInfoToDefaults(struct SetupInfo *si)
10735 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10737 si->multiple_users = TRUE;
10740 si->sound_loops = TRUE;
10741 si->sound_music = TRUE;
10742 si->sound_simple = TRUE;
10744 si->global_animations = TRUE;
10745 si->scroll_delay = TRUE;
10746 si->forced_scroll_delay = FALSE;
10747 si->scroll_delay_value = STD_SCROLL_DELAY;
10748 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10749 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10750 si->fade_screens = TRUE;
10751 si->autorecord = TRUE;
10752 si->autorecord_after_replay = TRUE;
10753 si->auto_pause_on_start = FALSE;
10754 si->show_titlescreen = TRUE;
10755 si->quick_doors = FALSE;
10756 si->team_mode = FALSE;
10757 si->handicap = TRUE;
10758 si->skip_levels = TRUE;
10759 si->increment_levels = TRUE;
10760 si->auto_play_next_level = TRUE;
10761 si->count_score_after_game = TRUE;
10762 si->show_scores_after_game = TRUE;
10763 si->time_limit = TRUE;
10764 si->fullscreen = FALSE;
10765 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10766 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10767 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10768 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10769 si->ask_on_escape = TRUE;
10770 si->ask_on_escape_editor = TRUE;
10771 si->ask_on_game_over = TRUE;
10772 si->ask_on_quit_game = TRUE;
10773 si->ask_on_quit_program = TRUE;
10774 si->quick_switch = FALSE;
10775 si->input_on_focus = FALSE;
10776 si->prefer_aga_graphics = TRUE;
10777 si->prefer_lowpass_sounds = FALSE;
10778 si->prefer_extra_panel_items = TRUE;
10779 si->game_speed_extended = FALSE;
10780 si->game_frame_delay = GAME_FRAME_DELAY;
10781 si->bd_skip_uncovering = FALSE;
10782 si->bd_skip_hatching = FALSE;
10783 si->bd_scroll_delay = TRUE;
10784 si->bd_smooth_movements = AUTO;
10785 si->sp_show_border_elements = FALSE;
10786 si->small_game_graphics = FALSE;
10787 si->show_load_save_buttons = FALSE;
10788 si->show_undo_redo_buttons = FALSE;
10789 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10791 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10792 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10793 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10795 si->override_level_graphics = FALSE;
10796 si->override_level_sounds = FALSE;
10797 si->override_level_music = FALSE;
10799 si->volume_simple = 100; // percent
10800 si->volume_loops = 100; // percent
10801 si->volume_music = 100; // percent
10803 si->network_mode = FALSE;
10804 si->network_player_nr = 0; // first player
10805 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10807 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10808 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10809 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10810 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10811 si->touch.draw_outlined = TRUE;
10812 si->touch.draw_pressed = TRUE;
10814 for (i = 0; i < 2; i++)
10816 char *default_grid_button[6][2] =
10822 { "111222", " vv " },
10823 { "111222", " vv " }
10825 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10826 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10827 int min_xsize = MIN(6, grid_xsize);
10828 int min_ysize = MIN(6, grid_ysize);
10829 int startx = grid_xsize - min_xsize;
10830 int starty = grid_ysize - min_ysize;
10833 // virtual buttons grid can only be set to defaults if video is initialized
10834 // (this will be repeated if virtual buttons are not loaded from setup file)
10835 if (video.initialized)
10837 si->touch.grid_xsize[i] = grid_xsize;
10838 si->touch.grid_ysize[i] = grid_ysize;
10842 si->touch.grid_xsize[i] = -1;
10843 si->touch.grid_ysize[i] = -1;
10846 for (x = 0; x < MAX_GRID_XSIZE; x++)
10847 for (y = 0; y < MAX_GRID_YSIZE; y++)
10848 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10850 for (x = 0; x < min_xsize; x++)
10851 for (y = 0; y < min_ysize; y++)
10852 si->touch.grid_button[i][x][starty + y] =
10853 default_grid_button[y][0][x];
10855 for (x = 0; x < min_xsize; x++)
10856 for (y = 0; y < min_ysize; y++)
10857 si->touch.grid_button[i][startx + x][starty + y] =
10858 default_grid_button[y][1][x];
10861 si->touch.grid_initialized = video.initialized;
10863 si->touch.overlay_buttons = FALSE;
10865 si->editor.el_boulderdash = TRUE;
10866 si->editor.el_boulderdash_native = TRUE;
10867 si->editor.el_emerald_mine = TRUE;
10868 si->editor.el_emerald_mine_club = TRUE;
10869 si->editor.el_more = TRUE;
10870 si->editor.el_sokoban = TRUE;
10871 si->editor.el_supaplex = TRUE;
10872 si->editor.el_diamond_caves = TRUE;
10873 si->editor.el_dx_boulderdash = TRUE;
10875 si->editor.el_mirror_magic = TRUE;
10876 si->editor.el_deflektor = TRUE;
10878 si->editor.el_chars = TRUE;
10879 si->editor.el_steel_chars = TRUE;
10881 si->editor.el_classic = TRUE;
10882 si->editor.el_custom = TRUE;
10884 si->editor.el_user_defined = FALSE;
10885 si->editor.el_dynamic = TRUE;
10887 si->editor.el_headlines = TRUE;
10889 si->editor.show_element_token = FALSE;
10891 si->editor.show_read_only_warning = TRUE;
10893 si->editor.use_template_for_new_levels = TRUE;
10895 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10896 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10897 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10898 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10899 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10901 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10902 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10903 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10904 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10905 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10907 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10908 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10909 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10910 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10911 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10912 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10914 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10915 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10916 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10918 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10919 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10920 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10921 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10923 for (i = 0; i < MAX_PLAYERS; i++)
10925 si->input[i].use_joystick = FALSE;
10926 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10927 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10928 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10929 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10930 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10931 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10932 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10933 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10934 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10935 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10936 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10937 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10938 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10939 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10940 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10943 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10944 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10945 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10946 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10948 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10949 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10950 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10951 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10952 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10953 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10954 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10956 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10958 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10959 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10960 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10962 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10963 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10964 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10966 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10967 si->internal.choose_from_top_leveldir = FALSE;
10968 si->internal.show_scaling_in_title = TRUE;
10969 si->internal.create_user_levelset = TRUE;
10970 si->internal.info_screens_from_main = FALSE;
10972 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10973 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10975 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10976 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10977 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10978 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10979 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10980 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10981 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10982 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10983 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10984 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10986 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10987 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10988 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10989 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10990 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10991 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10992 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10993 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10994 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10995 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10997 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10998 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11000 si->debug.show_frames_per_second = FALSE;
11002 si->debug.xsn_mode = AUTO;
11003 si->debug.xsn_percent = 0;
11005 si->options.verbose = FALSE;
11006 si->options.debug = FALSE;
11007 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11009 #if defined(PLATFORM_ANDROID)
11010 si->fullscreen = TRUE;
11011 si->touch.overlay_buttons = TRUE;
11014 setHideSetupEntry(&setup.debug.xsn_mode);
11017 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11019 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11022 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11024 si->player_uuid = NULL; // (will be set later)
11025 si->player_version = 1; // (will be set later)
11027 si->use_api_server = TRUE;
11028 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11029 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11030 si->ask_for_uploading_tapes = TRUE;
11031 si->ask_for_remaining_tapes = FALSE;
11032 si->provide_uploading_tapes = TRUE;
11033 si->ask_for_using_api_server = TRUE;
11034 si->has_remaining_tapes = FALSE;
11037 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11039 si->editor_cascade.el_bd = TRUE;
11040 si->editor_cascade.el_bd_native = TRUE;
11041 si->editor_cascade.el_em = TRUE;
11042 si->editor_cascade.el_emc = TRUE;
11043 si->editor_cascade.el_rnd = TRUE;
11044 si->editor_cascade.el_sb = TRUE;
11045 si->editor_cascade.el_sp = TRUE;
11046 si->editor_cascade.el_dc = TRUE;
11047 si->editor_cascade.el_dx = TRUE;
11049 si->editor_cascade.el_mm = TRUE;
11050 si->editor_cascade.el_df = TRUE;
11052 si->editor_cascade.el_chars = FALSE;
11053 si->editor_cascade.el_steel_chars = FALSE;
11054 si->editor_cascade.el_ce = FALSE;
11055 si->editor_cascade.el_ge = FALSE;
11056 si->editor_cascade.el_es = FALSE;
11057 si->editor_cascade.el_ref = FALSE;
11058 si->editor_cascade.el_user = FALSE;
11059 si->editor_cascade.el_dynamic = FALSE;
11062 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11064 static char *getHideSetupToken(void *setup_value)
11066 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11068 if (setup_value != NULL)
11069 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11071 return hide_setup_token;
11074 void setHideSetupEntry(void *setup_value)
11076 char *hide_setup_token = getHideSetupToken(setup_value);
11078 if (hide_setup_hash == NULL)
11079 hide_setup_hash = newSetupFileHash();
11081 if (setup_value != NULL)
11082 setHashEntry(hide_setup_hash, hide_setup_token, "");
11085 void removeHideSetupEntry(void *setup_value)
11087 char *hide_setup_token = getHideSetupToken(setup_value);
11089 if (setup_value != NULL)
11090 removeHashEntry(hide_setup_hash, hide_setup_token);
11093 boolean hideSetupEntry(void *setup_value)
11095 char *hide_setup_token = getHideSetupToken(setup_value);
11097 return (setup_value != NULL &&
11098 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11101 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11102 struct TokenInfo *token_info,
11103 int token_nr, char *token_text)
11105 char *token_hide_text = getStringCat2(token_text, ".hide");
11106 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11108 // set the value of this setup option in the setup option structure
11109 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11111 // check if this setup option should be hidden in the setup menu
11112 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11113 setHideSetupEntry(token_info[token_nr].value);
11115 free(token_hide_text);
11118 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11119 struct TokenInfo *token_info,
11122 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11123 token_info[token_nr].text);
11126 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11130 if (!setup_file_hash)
11133 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11134 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11136 setup.touch.grid_initialized = TRUE;
11137 for (i = 0; i < 2; i++)
11139 int grid_xsize = setup.touch.grid_xsize[i];
11140 int grid_ysize = setup.touch.grid_ysize[i];
11143 // if virtual buttons are not loaded from setup file, repeat initializing
11144 // virtual buttons grid with default values later when video is initialized
11145 if (grid_xsize == -1 ||
11148 setup.touch.grid_initialized = FALSE;
11153 for (y = 0; y < grid_ysize; y++)
11155 char token_string[MAX_LINE_LEN];
11157 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11159 char *value_string = getHashEntry(setup_file_hash, token_string);
11161 if (value_string == NULL)
11164 for (x = 0; x < grid_xsize; x++)
11166 char c = value_string[x];
11168 setup.touch.grid_button[i][x][y] =
11169 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11174 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11175 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11177 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11178 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11180 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11184 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11186 setup_input = setup.input[pnr];
11187 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11189 char full_token[100];
11191 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11192 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11195 setup.input[pnr] = setup_input;
11198 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11199 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11201 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11202 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11204 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11205 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11207 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11208 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11210 setHideRelatedSetupEntries();
11213 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11217 if (!setup_file_hash)
11220 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11221 setSetupInfo(auto_setup_tokens, i,
11222 getHashEntry(setup_file_hash,
11223 auto_setup_tokens[i].text));
11226 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11230 if (!setup_file_hash)
11233 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11234 setSetupInfo(server_setup_tokens, i,
11235 getHashEntry(setup_file_hash,
11236 server_setup_tokens[i].text));
11239 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11243 if (!setup_file_hash)
11246 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11247 setSetupInfo(editor_cascade_setup_tokens, i,
11248 getHashEntry(setup_file_hash,
11249 editor_cascade_setup_tokens[i].text));
11252 void LoadUserNames(void)
11254 int last_user_nr = user.nr;
11257 if (global.user_names != NULL)
11259 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11260 checked_free(global.user_names[i]);
11262 checked_free(global.user_names);
11265 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11267 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11271 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11273 if (setup_file_hash)
11275 char *player_name = getHashEntry(setup_file_hash, "player_name");
11277 global.user_names[i] = getFixedUserName(player_name);
11279 freeSetupFileHash(setup_file_hash);
11282 if (global.user_names[i] == NULL)
11283 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11286 user.nr = last_user_nr;
11289 void LoadSetupFromFilename(char *filename)
11291 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11293 if (setup_file_hash)
11295 decodeSetupFileHash_Default(setup_file_hash);
11297 freeSetupFileHash(setup_file_hash);
11301 Debug("setup", "using default setup values");
11305 static void LoadSetup_SpecialPostProcessing(void)
11307 char *player_name_new;
11309 // needed to work around problems with fixed length strings
11310 player_name_new = getFixedUserName(setup.player_name);
11311 free(setup.player_name);
11312 setup.player_name = player_name_new;
11314 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11315 if (setup.scroll_delay == FALSE)
11317 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11318 setup.scroll_delay = TRUE; // now always "on"
11321 // make sure that scroll delay value stays inside valid range
11322 setup.scroll_delay_value =
11323 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11326 void LoadSetup_Default(void)
11330 // always start with reliable default values
11331 setSetupInfoToDefaults(&setup);
11333 // try to load setup values from default setup file
11334 filename = getDefaultSetupFilename();
11336 if (fileExists(filename))
11337 LoadSetupFromFilename(filename);
11339 // try to load setup values from platform setup file
11340 filename = getPlatformSetupFilename();
11342 if (fileExists(filename))
11343 LoadSetupFromFilename(filename);
11345 // try to load setup values from user setup file
11346 filename = getSetupFilename();
11348 LoadSetupFromFilename(filename);
11350 LoadSetup_SpecialPostProcessing();
11353 void LoadSetup_AutoSetup(void)
11355 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11356 SetupFileHash *setup_file_hash = NULL;
11358 // always start with reliable default values
11359 setSetupInfoToDefaults_AutoSetup(&setup);
11361 setup_file_hash = loadSetupFileHash(filename);
11363 if (setup_file_hash)
11365 decodeSetupFileHash_AutoSetup(setup_file_hash);
11367 freeSetupFileHash(setup_file_hash);
11373 void LoadSetup_ServerSetup(void)
11375 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11376 SetupFileHash *setup_file_hash = NULL;
11378 // always start with reliable default values
11379 setSetupInfoToDefaults_ServerSetup(&setup);
11381 setup_file_hash = loadSetupFileHash(filename);
11383 if (setup_file_hash)
11385 decodeSetupFileHash_ServerSetup(setup_file_hash);
11387 freeSetupFileHash(setup_file_hash);
11392 if (setup.player_uuid == NULL)
11394 // player UUID does not yet exist in setup file
11395 setup.player_uuid = getStringCopy(getUUID());
11396 setup.player_version = 2;
11398 SaveSetup_ServerSetup();
11402 void LoadSetup_EditorCascade(void)
11404 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11405 SetupFileHash *setup_file_hash = NULL;
11407 // always start with reliable default values
11408 setSetupInfoToDefaults_EditorCascade(&setup);
11410 setup_file_hash = loadSetupFileHash(filename);
11412 if (setup_file_hash)
11414 decodeSetupFileHash_EditorCascade(setup_file_hash);
11416 freeSetupFileHash(setup_file_hash);
11422 void LoadSetup(void)
11424 LoadSetup_Default();
11425 LoadSetup_AutoSetup();
11426 LoadSetup_ServerSetup();
11427 LoadSetup_EditorCascade();
11430 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11431 char *mapping_line)
11433 char mapping_guid[MAX_LINE_LEN];
11434 char *mapping_start, *mapping_end;
11436 // get GUID from game controller mapping line: copy complete line
11437 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11438 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11440 // get GUID from game controller mapping line: cut after GUID part
11441 mapping_start = strchr(mapping_guid, ',');
11442 if (mapping_start != NULL)
11443 *mapping_start = '\0';
11445 // cut newline from game controller mapping line
11446 mapping_end = strchr(mapping_line, '\n');
11447 if (mapping_end != NULL)
11448 *mapping_end = '\0';
11450 // add mapping entry to game controller mappings hash
11451 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11454 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11459 if (!(file = fopen(filename, MODE_READ)))
11461 Warn("cannot read game controller mappings file '%s'", filename);
11466 while (!feof(file))
11468 char line[MAX_LINE_LEN];
11470 if (!fgets(line, MAX_LINE_LEN, file))
11473 addGameControllerMappingToHash(mappings_hash, line);
11479 void SaveSetup_Default(void)
11481 char *filename = getSetupFilename();
11485 InitUserDataDirectory();
11487 if (!(file = fopen(filename, MODE_WRITE)))
11489 Warn("cannot write setup file '%s'", filename);
11494 fprintFileHeader(file, SETUP_FILENAME);
11496 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11498 // just to make things nicer :)
11499 if (global_setup_tokens[i].value == &setup.multiple_users ||
11500 global_setup_tokens[i].value == &setup.sound ||
11501 global_setup_tokens[i].value == &setup.graphics_set ||
11502 global_setup_tokens[i].value == &setup.volume_simple ||
11503 global_setup_tokens[i].value == &setup.network_mode ||
11504 global_setup_tokens[i].value == &setup.touch.control_type ||
11505 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11506 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11507 fprintf(file, "\n");
11509 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11512 for (i = 0; i < 2; i++)
11514 int grid_xsize = setup.touch.grid_xsize[i];
11515 int grid_ysize = setup.touch.grid_ysize[i];
11518 fprintf(file, "\n");
11520 for (y = 0; y < grid_ysize; y++)
11522 char token_string[MAX_LINE_LEN];
11523 char value_string[MAX_LINE_LEN];
11525 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11527 for (x = 0; x < grid_xsize; x++)
11529 char c = setup.touch.grid_button[i][x][y];
11531 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11534 value_string[grid_xsize] = '\0';
11536 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11540 fprintf(file, "\n");
11541 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11542 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11544 fprintf(file, "\n");
11545 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11546 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11548 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11552 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11553 fprintf(file, "\n");
11555 setup_input = setup.input[pnr];
11556 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11557 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11560 fprintf(file, "\n");
11561 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11562 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11564 // (internal setup values not saved to user setup file)
11566 fprintf(file, "\n");
11567 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11568 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11569 setup.debug.xsn_mode != AUTO)
11570 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11572 fprintf(file, "\n");
11573 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11574 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11578 SetFilePermissions(filename, PERMS_PRIVATE);
11581 void SaveSetup_AutoSetup(void)
11583 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11587 InitUserDataDirectory();
11589 if (!(file = fopen(filename, MODE_WRITE)))
11591 Warn("cannot write auto setup file '%s'", filename);
11598 fprintFileHeader(file, AUTOSETUP_FILENAME);
11600 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11601 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11605 SetFilePermissions(filename, PERMS_PRIVATE);
11610 void SaveSetup_ServerSetup(void)
11612 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11616 InitUserDataDirectory();
11618 if (!(file = fopen(filename, MODE_WRITE)))
11620 Warn("cannot write server setup file '%s'", filename);
11627 fprintFileHeader(file, SERVERSETUP_FILENAME);
11629 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11631 // just to make things nicer :)
11632 if (server_setup_tokens[i].value == &setup.use_api_server)
11633 fprintf(file, "\n");
11635 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11640 SetFilePermissions(filename, PERMS_PRIVATE);
11645 void SaveSetup_EditorCascade(void)
11647 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11651 InitUserDataDirectory();
11653 if (!(file = fopen(filename, MODE_WRITE)))
11655 Warn("cannot write editor cascade state file '%s'", filename);
11662 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11664 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11665 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11669 SetFilePermissions(filename, PERMS_PRIVATE);
11674 void SaveSetup(void)
11676 SaveSetup_Default();
11677 SaveSetup_AutoSetup();
11678 SaveSetup_ServerSetup();
11679 SaveSetup_EditorCascade();
11682 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11687 if (!(file = fopen(filename, MODE_WRITE)))
11689 Warn("cannot write game controller mappings file '%s'", filename);
11694 BEGIN_HASH_ITERATION(mappings_hash, itr)
11696 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11698 END_HASH_ITERATION(mappings_hash, itr)
11703 void SaveSetup_AddGameControllerMapping(char *mapping)
11705 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11706 SetupFileHash *mappings_hash = newSetupFileHash();
11708 InitUserDataDirectory();
11710 // load existing personal game controller mappings
11711 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11713 // add new mapping to personal game controller mappings
11714 addGameControllerMappingToHash(mappings_hash, mapping);
11716 // save updated personal game controller mappings
11717 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11719 freeSetupFileHash(mappings_hash);
11723 void LoadCustomElementDescriptions(void)
11725 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11726 SetupFileHash *setup_file_hash;
11729 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11731 if (element_info[i].custom_description != NULL)
11733 free(element_info[i].custom_description);
11734 element_info[i].custom_description = NULL;
11738 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11741 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11743 char *token = getStringCat2(element_info[i].token_name, ".name");
11744 char *value = getHashEntry(setup_file_hash, token);
11747 element_info[i].custom_description = getStringCopy(value);
11752 freeSetupFileHash(setup_file_hash);
11755 static int getElementFromToken(char *token)
11757 char *value = getHashEntry(element_token_hash, token);
11760 return atoi(value);
11762 Warn("unknown element token '%s'", token);
11764 return EL_UNDEFINED;
11767 void FreeGlobalAnimEventInfo(void)
11769 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11771 if (gaei->event_list == NULL)
11776 for (i = 0; i < gaei->num_event_lists; i++)
11778 checked_free(gaei->event_list[i]->event_value);
11779 checked_free(gaei->event_list[i]);
11782 checked_free(gaei->event_list);
11784 gaei->event_list = NULL;
11785 gaei->num_event_lists = 0;
11788 static int AddGlobalAnimEventList(void)
11790 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11791 int list_pos = gaei->num_event_lists++;
11793 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11794 sizeof(struct GlobalAnimEventListInfo *));
11796 gaei->event_list[list_pos] =
11797 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11799 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11801 gaeli->event_value = NULL;
11802 gaeli->num_event_values = 0;
11807 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11809 // do not add empty global animation events
11810 if (event_value == ANIM_EVENT_NONE)
11813 // if list position is undefined, create new list
11814 if (list_pos == ANIM_EVENT_UNDEFINED)
11815 list_pos = AddGlobalAnimEventList();
11817 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11818 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11819 int value_pos = gaeli->num_event_values++;
11821 gaeli->event_value = checked_realloc(gaeli->event_value,
11822 gaeli->num_event_values * sizeof(int *));
11824 gaeli->event_value[value_pos] = event_value;
11829 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11831 if (list_pos == ANIM_EVENT_UNDEFINED)
11832 return ANIM_EVENT_NONE;
11834 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11835 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11837 return gaeli->event_value[value_pos];
11840 int GetGlobalAnimEventValueCount(int list_pos)
11842 if (list_pos == ANIM_EVENT_UNDEFINED)
11845 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11846 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11848 return gaeli->num_event_values;
11851 // This function checks if a string <s> of the format "string1, string2, ..."
11852 // exactly contains a string <s_contained>.
11854 static boolean string_has_parameter(char *s, char *s_contained)
11858 if (s == NULL || s_contained == NULL)
11861 if (strlen(s_contained) > strlen(s))
11864 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11866 char next_char = s[strlen(s_contained)];
11868 // check if next character is delimiter or whitespace
11869 if (next_char == ',' || next_char == '\0' ||
11870 next_char == ' ' || next_char == '\t')
11874 // check if string contains another parameter string after a comma
11875 substring = strchr(s, ',');
11876 if (substring == NULL) // string does not contain a comma
11879 // advance string pointer to next character after the comma
11882 // skip potential whitespaces after the comma
11883 while (*substring == ' ' || *substring == '\t')
11886 return string_has_parameter(substring, s_contained);
11889 static int get_anim_parameter_value_ce(char *s)
11892 char *pattern_1 = "ce_change:custom_";
11893 char *pattern_2 = ".page_";
11894 int pattern_1_len = strlen(pattern_1);
11895 char *matching_char = strstr(s_ptr, pattern_1);
11896 int result = ANIM_EVENT_NONE;
11898 if (matching_char == NULL)
11899 return ANIM_EVENT_NONE;
11901 result = ANIM_EVENT_CE_CHANGE;
11903 s_ptr = matching_char + pattern_1_len;
11905 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11906 if (*s_ptr >= '0' && *s_ptr <= '9')
11908 int gic_ce_nr = (*s_ptr++ - '0');
11910 if (*s_ptr >= '0' && *s_ptr <= '9')
11912 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11914 if (*s_ptr >= '0' && *s_ptr <= '9')
11915 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11918 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11919 return ANIM_EVENT_NONE;
11921 // custom element stored as 0 to 255
11924 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11928 // invalid custom element number specified
11930 return ANIM_EVENT_NONE;
11933 // check for change page number ("page_X" or "page_XX") (optional)
11934 if (strPrefix(s_ptr, pattern_2))
11936 s_ptr += strlen(pattern_2);
11938 if (*s_ptr >= '0' && *s_ptr <= '9')
11940 int gic_page_nr = (*s_ptr++ - '0');
11942 if (*s_ptr >= '0' && *s_ptr <= '9')
11943 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11945 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11946 return ANIM_EVENT_NONE;
11948 // change page stored as 1 to 32 (0 means "all change pages")
11950 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11954 // invalid animation part number specified
11956 return ANIM_EVENT_NONE;
11960 // discard result if next character is neither delimiter nor whitespace
11961 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11962 *s_ptr == ' ' || *s_ptr == '\t'))
11963 return ANIM_EVENT_NONE;
11968 static int get_anim_parameter_value(char *s)
11970 int event_value[] =
11978 char *pattern_1[] =
11986 char *pattern_2 = ".part_";
11987 char *matching_char = NULL;
11989 int pattern_1_len = 0;
11990 int result = ANIM_EVENT_NONE;
11993 result = get_anim_parameter_value_ce(s);
11995 if (result != ANIM_EVENT_NONE)
11998 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12000 matching_char = strstr(s_ptr, pattern_1[i]);
12001 pattern_1_len = strlen(pattern_1[i]);
12002 result = event_value[i];
12004 if (matching_char != NULL)
12008 if (matching_char == NULL)
12009 return ANIM_EVENT_NONE;
12011 s_ptr = matching_char + pattern_1_len;
12013 // check for main animation number ("anim_X" or "anim_XX")
12014 if (*s_ptr >= '0' && *s_ptr <= '9')
12016 int gic_anim_nr = (*s_ptr++ - '0');
12018 if (*s_ptr >= '0' && *s_ptr <= '9')
12019 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12021 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12022 return ANIM_EVENT_NONE;
12024 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12028 // invalid main animation number specified
12030 return ANIM_EVENT_NONE;
12033 // check for animation part number ("part_X" or "part_XX") (optional)
12034 if (strPrefix(s_ptr, pattern_2))
12036 s_ptr += strlen(pattern_2);
12038 if (*s_ptr >= '0' && *s_ptr <= '9')
12040 int gic_part_nr = (*s_ptr++ - '0');
12042 if (*s_ptr >= '0' && *s_ptr <= '9')
12043 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12045 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12046 return ANIM_EVENT_NONE;
12048 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12052 // invalid animation part number specified
12054 return ANIM_EVENT_NONE;
12058 // discard result if next character is neither delimiter nor whitespace
12059 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12060 *s_ptr == ' ' || *s_ptr == '\t'))
12061 return ANIM_EVENT_NONE;
12066 static int get_anim_parameter_values(char *s)
12068 int list_pos = ANIM_EVENT_UNDEFINED;
12069 int event_value = ANIM_EVENT_DEFAULT;
12071 if (string_has_parameter(s, "any"))
12072 event_value |= ANIM_EVENT_ANY;
12074 if (string_has_parameter(s, "click:self") ||
12075 string_has_parameter(s, "click") ||
12076 string_has_parameter(s, "self"))
12077 event_value |= ANIM_EVENT_SELF;
12079 if (string_has_parameter(s, "unclick:any"))
12080 event_value |= ANIM_EVENT_UNCLICK_ANY;
12082 // if animation event found, add it to global animation event list
12083 if (event_value != ANIM_EVENT_NONE)
12084 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12088 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12089 event_value = get_anim_parameter_value(s);
12091 // if animation event found, add it to global animation event list
12092 if (event_value != ANIM_EVENT_NONE)
12093 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12095 // continue with next part of the string, starting with next comma
12096 s = strchr(s + 1, ',');
12102 static int get_anim_action_parameter_value(char *token)
12104 // check most common default case first to massively speed things up
12105 if (strEqual(token, ARG_UNDEFINED))
12106 return ANIM_EVENT_ACTION_NONE;
12108 int result = getImageIDFromToken(token);
12112 char *gfx_token = getStringCat2("gfx.", token);
12114 result = getImageIDFromToken(gfx_token);
12116 checked_free(gfx_token);
12121 Key key = getKeyFromX11KeyName(token);
12123 if (key != KSYM_UNDEFINED)
12124 result = -(int)key;
12131 result = get_hash_from_string(token); // unsigned int => int
12132 result = ABS(result); // may be negative now
12133 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12135 setHashEntry(anim_url_hash, int2str(result, 0), token);
12140 result = ANIM_EVENT_ACTION_NONE;
12145 int get_parameter_value(char *value_raw, char *suffix, int type)
12147 char *value = getStringToLower(value_raw);
12148 int result = 0; // probably a save default value
12150 if (strEqual(suffix, ".direction"))
12152 result = (strEqual(value, "left") ? MV_LEFT :
12153 strEqual(value, "right") ? MV_RIGHT :
12154 strEqual(value, "up") ? MV_UP :
12155 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12157 else if (strEqual(suffix, ".position"))
12159 result = (strEqual(value, "left") ? POS_LEFT :
12160 strEqual(value, "right") ? POS_RIGHT :
12161 strEqual(value, "top") ? POS_TOP :
12162 strEqual(value, "upper") ? POS_UPPER :
12163 strEqual(value, "middle") ? POS_MIDDLE :
12164 strEqual(value, "lower") ? POS_LOWER :
12165 strEqual(value, "bottom") ? POS_BOTTOM :
12166 strEqual(value, "any") ? POS_ANY :
12167 strEqual(value, "ce") ? POS_CE :
12168 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12169 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12171 else if (strEqual(suffix, ".align"))
12173 result = (strEqual(value, "left") ? ALIGN_LEFT :
12174 strEqual(value, "right") ? ALIGN_RIGHT :
12175 strEqual(value, "center") ? ALIGN_CENTER :
12176 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12178 else if (strEqual(suffix, ".valign"))
12180 result = (strEqual(value, "top") ? VALIGN_TOP :
12181 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12182 strEqual(value, "middle") ? VALIGN_MIDDLE :
12183 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12185 else if (strEqual(suffix, ".anim_mode"))
12187 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12188 string_has_parameter(value, "loop") ? ANIM_LOOP :
12189 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12190 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12191 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12192 string_has_parameter(value, "random") ? ANIM_RANDOM :
12193 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12194 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12195 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12196 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12197 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12198 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12199 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12200 string_has_parameter(value, "all") ? ANIM_ALL :
12201 string_has_parameter(value, "tiled") ? ANIM_TILED :
12202 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12205 if (string_has_parameter(value, "once"))
12206 result |= ANIM_ONCE;
12208 if (string_has_parameter(value, "reverse"))
12209 result |= ANIM_REVERSE;
12211 if (string_has_parameter(value, "opaque_player"))
12212 result |= ANIM_OPAQUE_PLAYER;
12214 if (string_has_parameter(value, "static_panel"))
12215 result |= ANIM_STATIC_PANEL;
12217 else if (strEqual(suffix, ".init_event") ||
12218 strEqual(suffix, ".anim_event"))
12220 result = get_anim_parameter_values(value);
12222 else if (strEqual(suffix, ".init_delay_action") ||
12223 strEqual(suffix, ".anim_delay_action") ||
12224 strEqual(suffix, ".post_delay_action") ||
12225 strEqual(suffix, ".init_event_action") ||
12226 strEqual(suffix, ".anim_event_action"))
12228 result = get_anim_action_parameter_value(value_raw);
12230 else if (strEqual(suffix, ".class"))
12232 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12233 get_hash_from_string(value));
12235 else if (strEqual(suffix, ".style"))
12237 result = STYLE_DEFAULT;
12239 if (string_has_parameter(value, "accurate_borders"))
12240 result |= STYLE_ACCURATE_BORDERS;
12242 if (string_has_parameter(value, "inner_corners"))
12243 result |= STYLE_INNER_CORNERS;
12245 if (string_has_parameter(value, "reverse"))
12246 result |= STYLE_REVERSE;
12248 if (string_has_parameter(value, "leftmost_position"))
12249 result |= STYLE_LEFTMOST_POSITION;
12251 if (string_has_parameter(value, "block_clicks"))
12252 result |= STYLE_BLOCK;
12254 if (string_has_parameter(value, "passthrough_clicks"))
12255 result |= STYLE_PASSTHROUGH;
12257 if (string_has_parameter(value, "multiple_actions"))
12258 result |= STYLE_MULTIPLE_ACTIONS;
12260 if (string_has_parameter(value, "consume_ce_event"))
12261 result |= STYLE_CONSUME_CE_EVENT;
12263 else if (strEqual(suffix, ".fade_mode"))
12265 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12266 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12267 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12268 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12269 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12270 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12271 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12272 FADE_MODE_DEFAULT);
12274 else if (strEqual(suffix, ".auto_delay_unit"))
12276 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12277 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12278 AUTO_DELAY_UNIT_DEFAULT);
12280 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12282 result = gfx.get_font_from_token_function(value);
12284 else // generic parameter of type integer or boolean
12286 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12287 type == TYPE_INTEGER ? get_integer_from_string(value) :
12288 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12289 ARG_UNDEFINED_VALUE);
12297 static int get_token_parameter_value(char *token, char *value_raw)
12301 if (token == NULL || value_raw == NULL)
12302 return ARG_UNDEFINED_VALUE;
12304 suffix = strrchr(token, '.');
12305 if (suffix == NULL)
12308 if (strEqual(suffix, ".element"))
12309 return getElementFromToken(value_raw);
12311 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12312 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12315 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12316 boolean ignore_defaults)
12320 for (i = 0; image_config_vars[i].token != NULL; i++)
12322 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12324 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12325 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12329 *image_config_vars[i].value =
12330 get_token_parameter_value(image_config_vars[i].token, value);
12334 void InitMenuDesignSettings_Static(void)
12336 // always start with reliable default values from static default config
12337 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12340 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12344 // the following initializes hierarchical values from static configuration
12346 // special case: initialize "ARG_DEFAULT" values in static default config
12347 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12348 titlescreen_initial_first_default.fade_mode =
12349 title_initial_first_default.fade_mode;
12350 titlescreen_initial_first_default.fade_delay =
12351 title_initial_first_default.fade_delay;
12352 titlescreen_initial_first_default.post_delay =
12353 title_initial_first_default.post_delay;
12354 titlescreen_initial_first_default.auto_delay =
12355 title_initial_first_default.auto_delay;
12356 titlescreen_initial_first_default.auto_delay_unit =
12357 title_initial_first_default.auto_delay_unit;
12358 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12359 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12360 titlescreen_first_default.post_delay = title_first_default.post_delay;
12361 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12362 titlescreen_first_default.auto_delay_unit =
12363 title_first_default.auto_delay_unit;
12364 titlemessage_initial_first_default.fade_mode =
12365 title_initial_first_default.fade_mode;
12366 titlemessage_initial_first_default.fade_delay =
12367 title_initial_first_default.fade_delay;
12368 titlemessage_initial_first_default.post_delay =
12369 title_initial_first_default.post_delay;
12370 titlemessage_initial_first_default.auto_delay =
12371 title_initial_first_default.auto_delay;
12372 titlemessage_initial_first_default.auto_delay_unit =
12373 title_initial_first_default.auto_delay_unit;
12374 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12375 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12376 titlemessage_first_default.post_delay = title_first_default.post_delay;
12377 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12378 titlemessage_first_default.auto_delay_unit =
12379 title_first_default.auto_delay_unit;
12381 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12382 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12383 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12384 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12385 titlescreen_initial_default.auto_delay_unit =
12386 title_initial_default.auto_delay_unit;
12387 titlescreen_default.fade_mode = title_default.fade_mode;
12388 titlescreen_default.fade_delay = title_default.fade_delay;
12389 titlescreen_default.post_delay = title_default.post_delay;
12390 titlescreen_default.auto_delay = title_default.auto_delay;
12391 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12392 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12393 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12394 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12395 titlemessage_initial_default.auto_delay_unit =
12396 title_initial_default.auto_delay_unit;
12397 titlemessage_default.fade_mode = title_default.fade_mode;
12398 titlemessage_default.fade_delay = title_default.fade_delay;
12399 titlemessage_default.post_delay = title_default.post_delay;
12400 titlemessage_default.auto_delay = title_default.auto_delay;
12401 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12403 // special case: initialize "ARG_DEFAULT" values in static default config
12404 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12405 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12407 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12408 titlescreen_first[i] = titlescreen_first_default;
12409 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12410 titlemessage_first[i] = titlemessage_first_default;
12412 titlescreen_initial[i] = titlescreen_initial_default;
12413 titlescreen[i] = titlescreen_default;
12414 titlemessage_initial[i] = titlemessage_initial_default;
12415 titlemessage[i] = titlemessage_default;
12418 // special case: initialize "ARG_DEFAULT" values in static default config
12419 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12420 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12422 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12425 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12426 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12427 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12430 // special case: initialize "ARG_DEFAULT" values in static default config
12431 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12432 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12434 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12435 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12436 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12438 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12441 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12445 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12449 struct XY *dst, *src;
12451 game_buttons_xy[] =
12453 { &game.button.save, &game.button.stop },
12454 { &game.button.pause2, &game.button.pause },
12455 { &game.button.load, &game.button.play },
12456 { &game.button.undo, &game.button.stop },
12457 { &game.button.redo, &game.button.play },
12463 // special case: initialize later added SETUP list size from LEVELS value
12464 if (menu.list_size[GAME_MODE_SETUP] == -1)
12465 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12467 // set default position for snapshot buttons to stop/pause/play buttons
12468 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12469 if ((*game_buttons_xy[i].dst).x == -1 &&
12470 (*game_buttons_xy[i].dst).y == -1)
12471 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12473 // --------------------------------------------------------------------------
12474 // dynamic viewports (including playfield margins, borders and alignments)
12475 // --------------------------------------------------------------------------
12477 // dynamic viewports currently only supported for landscape mode
12478 int display_width = MAX(video.display_width, video.display_height);
12479 int display_height = MIN(video.display_width, video.display_height);
12481 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12483 struct RectWithBorder *vp_window = &viewport.window[i];
12484 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12485 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12486 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12487 boolean dynamic_window_width = (vp_window->min_width != -1);
12488 boolean dynamic_window_height = (vp_window->min_height != -1);
12489 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12490 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12492 // adjust window size if min/max width/height is specified
12494 if (vp_window->min_width != -1)
12496 int window_width = display_width;
12498 // when using static window height, use aspect ratio of display
12499 if (vp_window->min_height == -1)
12500 window_width = vp_window->height * display_width / display_height;
12502 vp_window->width = MAX(vp_window->min_width, window_width);
12505 if (vp_window->min_height != -1)
12507 int window_height = display_height;
12509 // when using static window width, use aspect ratio of display
12510 if (vp_window->min_width == -1)
12511 window_height = vp_window->width * display_height / display_width;
12513 vp_window->height = MAX(vp_window->min_height, window_height);
12516 if (vp_window->max_width != -1)
12517 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12519 if (vp_window->max_height != -1)
12520 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12522 int playfield_width = vp_window->width;
12523 int playfield_height = vp_window->height;
12525 // adjust playfield size and position according to specified margins
12527 playfield_width -= vp_playfield->margin_left;
12528 playfield_width -= vp_playfield->margin_right;
12530 playfield_height -= vp_playfield->margin_top;
12531 playfield_height -= vp_playfield->margin_bottom;
12533 // adjust playfield size if min/max width/height is specified
12535 if (vp_playfield->min_width != -1)
12536 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12538 if (vp_playfield->min_height != -1)
12539 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12541 if (vp_playfield->max_width != -1)
12542 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12544 if (vp_playfield->max_height != -1)
12545 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12547 // adjust playfield position according to specified alignment
12549 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12550 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12551 else if (vp_playfield->align == ALIGN_CENTER)
12552 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12553 else if (vp_playfield->align == ALIGN_RIGHT)
12554 vp_playfield->x += playfield_width - vp_playfield->width;
12556 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12557 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12558 else if (vp_playfield->valign == VALIGN_MIDDLE)
12559 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12560 else if (vp_playfield->valign == VALIGN_BOTTOM)
12561 vp_playfield->y += playfield_height - vp_playfield->height;
12563 vp_playfield->x += vp_playfield->margin_left;
12564 vp_playfield->y += vp_playfield->margin_top;
12566 // adjust individual playfield borders if only default border is specified
12568 if (vp_playfield->border_left == -1)
12569 vp_playfield->border_left = vp_playfield->border_size;
12570 if (vp_playfield->border_right == -1)
12571 vp_playfield->border_right = vp_playfield->border_size;
12572 if (vp_playfield->border_top == -1)
12573 vp_playfield->border_top = vp_playfield->border_size;
12574 if (vp_playfield->border_bottom == -1)
12575 vp_playfield->border_bottom = vp_playfield->border_size;
12577 // set dynamic playfield borders if borders are specified as undefined
12578 // (but only if window size was dynamic and playfield size was static)
12580 if (dynamic_window_width && !dynamic_playfield_width)
12582 if (vp_playfield->border_left == -1)
12584 vp_playfield->border_left = (vp_playfield->x -
12585 vp_playfield->margin_left);
12586 vp_playfield->x -= vp_playfield->border_left;
12587 vp_playfield->width += vp_playfield->border_left;
12590 if (vp_playfield->border_right == -1)
12592 vp_playfield->border_right = (vp_window->width -
12594 vp_playfield->width -
12595 vp_playfield->margin_right);
12596 vp_playfield->width += vp_playfield->border_right;
12600 if (dynamic_window_height && !dynamic_playfield_height)
12602 if (vp_playfield->border_top == -1)
12604 vp_playfield->border_top = (vp_playfield->y -
12605 vp_playfield->margin_top);
12606 vp_playfield->y -= vp_playfield->border_top;
12607 vp_playfield->height += vp_playfield->border_top;
12610 if (vp_playfield->border_bottom == -1)
12612 vp_playfield->border_bottom = (vp_window->height -
12614 vp_playfield->height -
12615 vp_playfield->margin_bottom);
12616 vp_playfield->height += vp_playfield->border_bottom;
12620 // adjust playfield size to be a multiple of a defined alignment tile size
12622 int align_size = vp_playfield->align_size;
12623 int playfield_xtiles = vp_playfield->width / align_size;
12624 int playfield_ytiles = vp_playfield->height / align_size;
12625 int playfield_width_corrected = playfield_xtiles * align_size;
12626 int playfield_height_corrected = playfield_ytiles * align_size;
12627 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12628 i == GFX_SPECIAL_ARG_EDITOR);
12630 if (is_playfield_mode &&
12631 dynamic_playfield_width &&
12632 vp_playfield->width != playfield_width_corrected)
12634 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12636 vp_playfield->width = playfield_width_corrected;
12638 if (vp_playfield->align == ALIGN_LEFT)
12640 vp_playfield->border_left += playfield_xdiff;
12642 else if (vp_playfield->align == ALIGN_RIGHT)
12644 vp_playfield->border_right += playfield_xdiff;
12646 else if (vp_playfield->align == ALIGN_CENTER)
12648 int border_left_diff = playfield_xdiff / 2;
12649 int border_right_diff = playfield_xdiff - border_left_diff;
12651 vp_playfield->border_left += border_left_diff;
12652 vp_playfield->border_right += border_right_diff;
12656 if (is_playfield_mode &&
12657 dynamic_playfield_height &&
12658 vp_playfield->height != playfield_height_corrected)
12660 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12662 vp_playfield->height = playfield_height_corrected;
12664 if (vp_playfield->valign == VALIGN_TOP)
12666 vp_playfield->border_top += playfield_ydiff;
12668 else if (vp_playfield->align == VALIGN_BOTTOM)
12670 vp_playfield->border_right += playfield_ydiff;
12672 else if (vp_playfield->align == VALIGN_MIDDLE)
12674 int border_top_diff = playfield_ydiff / 2;
12675 int border_bottom_diff = playfield_ydiff - border_top_diff;
12677 vp_playfield->border_top += border_top_diff;
12678 vp_playfield->border_bottom += border_bottom_diff;
12682 // adjust door positions according to specified alignment
12684 for (j = 0; j < 2; j++)
12686 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12688 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12689 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12690 else if (vp_door->align == ALIGN_CENTER)
12691 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12692 else if (vp_door->align == ALIGN_RIGHT)
12693 vp_door->x += vp_window->width - vp_door->width;
12695 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12696 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12697 else if (vp_door->valign == VALIGN_MIDDLE)
12698 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12699 else if (vp_door->valign == VALIGN_BOTTOM)
12700 vp_door->y += vp_window->height - vp_door->height;
12705 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12709 struct XYTileSize *dst, *src;
12712 editor_buttons_xy[] =
12715 &editor.button.element_left, &editor.palette.element_left,
12716 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12719 &editor.button.element_middle, &editor.palette.element_middle,
12720 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12723 &editor.button.element_right, &editor.palette.element_right,
12724 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12731 // set default position for element buttons to element graphics
12732 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12734 if ((*editor_buttons_xy[i].dst).x == -1 &&
12735 (*editor_buttons_xy[i].dst).y == -1)
12737 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12739 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12741 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12745 // adjust editor palette rows and columns if specified to be dynamic
12747 if (editor.palette.cols == -1)
12749 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12750 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12751 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12753 editor.palette.cols = (vp_width - sc_width) / bt_width;
12755 if (editor.palette.x == -1)
12757 int palette_width = editor.palette.cols * bt_width + sc_width;
12759 editor.palette.x = (vp_width - palette_width) / 2;
12763 if (editor.palette.rows == -1)
12765 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12766 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12767 int tx_height = getFontHeight(FONT_TEXT_2);
12769 editor.palette.rows = (vp_height - tx_height) / bt_height;
12771 if (editor.palette.y == -1)
12773 int palette_height = editor.palette.rows * bt_height + tx_height;
12775 editor.palette.y = (vp_height - palette_height) / 2;
12780 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12781 boolean initialize)
12783 // special case: check if network and preview player positions are redefined,
12784 // to compare this later against the main menu level preview being redefined
12785 struct TokenIntPtrInfo menu_config_players[] =
12787 { "main.network_players.x", &menu.main.network_players.redefined },
12788 { "main.network_players.y", &menu.main.network_players.redefined },
12789 { "main.preview_players.x", &menu.main.preview_players.redefined },
12790 { "main.preview_players.y", &menu.main.preview_players.redefined },
12791 { "preview.x", &preview.redefined },
12792 { "preview.y", &preview.redefined }
12798 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12799 *menu_config_players[i].value = FALSE;
12803 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12804 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12805 *menu_config_players[i].value = TRUE;
12809 static void InitMenuDesignSettings_PreviewPlayers(void)
12811 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12814 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12816 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12819 static void LoadMenuDesignSettingsFromFilename(char *filename)
12821 static struct TitleFadingInfo tfi;
12822 static struct TitleMessageInfo tmi;
12823 static struct TokenInfo title_tokens[] =
12825 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12826 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12827 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12828 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12829 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12833 static struct TokenInfo titlemessage_tokens[] =
12835 { TYPE_INTEGER, &tmi.x, ".x" },
12836 { TYPE_INTEGER, &tmi.y, ".y" },
12837 { TYPE_INTEGER, &tmi.width, ".width" },
12838 { TYPE_INTEGER, &tmi.height, ".height" },
12839 { TYPE_INTEGER, &tmi.chars, ".chars" },
12840 { TYPE_INTEGER, &tmi.lines, ".lines" },
12841 { TYPE_INTEGER, &tmi.align, ".align" },
12842 { TYPE_INTEGER, &tmi.valign, ".valign" },
12843 { TYPE_INTEGER, &tmi.font, ".font" },
12844 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12845 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12846 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12847 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12848 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12849 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12850 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12851 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12852 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12858 struct TitleFadingInfo *info;
12863 // initialize first titles from "enter screen" definitions, if defined
12864 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12865 { &title_first_default, "menu.enter_screen.TITLE" },
12867 // initialize title screens from "next screen" definitions, if defined
12868 { &title_initial_default, "menu.next_screen.TITLE" },
12869 { &title_default, "menu.next_screen.TITLE" },
12875 struct TitleMessageInfo *array;
12878 titlemessage_arrays[] =
12880 // initialize first titles from "enter screen" definitions, if defined
12881 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12882 { titlescreen_first, "menu.enter_screen.TITLE" },
12883 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12884 { titlemessage_first, "menu.enter_screen.TITLE" },
12886 // initialize titles from "next screen" definitions, if defined
12887 { titlescreen_initial, "menu.next_screen.TITLE" },
12888 { titlescreen, "menu.next_screen.TITLE" },
12889 { titlemessage_initial, "menu.next_screen.TITLE" },
12890 { titlemessage, "menu.next_screen.TITLE" },
12892 // overwrite titles with title definitions, if defined
12893 { titlescreen_initial_first, "[title_initial]" },
12894 { titlescreen_first, "[title]" },
12895 { titlemessage_initial_first, "[title_initial]" },
12896 { titlemessage_first, "[title]" },
12898 { titlescreen_initial, "[title_initial]" },
12899 { titlescreen, "[title]" },
12900 { titlemessage_initial, "[title_initial]" },
12901 { titlemessage, "[title]" },
12903 // overwrite titles with title screen/message definitions, if defined
12904 { titlescreen_initial_first, "[titlescreen_initial]" },
12905 { titlescreen_first, "[titlescreen]" },
12906 { titlemessage_initial_first, "[titlemessage_initial]" },
12907 { titlemessage_first, "[titlemessage]" },
12909 { titlescreen_initial, "[titlescreen_initial]" },
12910 { titlescreen, "[titlescreen]" },
12911 { titlemessage_initial, "[titlemessage_initial]" },
12912 { titlemessage, "[titlemessage]" },
12916 SetupFileHash *setup_file_hash;
12919 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12922 // the following initializes hierarchical values from dynamic configuration
12924 // special case: initialize with default values that may be overwritten
12925 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12926 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12928 struct TokenIntPtrInfo menu_config[] =
12930 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12931 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12932 { "menu.list_size", &menu.list_size[i] }
12935 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12937 char *token = menu_config[j].token;
12938 char *value = getHashEntry(setup_file_hash, token);
12941 *menu_config[j].value = get_integer_from_string(value);
12945 // special case: initialize with default values that may be overwritten
12946 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12947 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12949 struct TokenIntPtrInfo menu_config[] =
12951 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12952 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12953 { "menu.list_size.INFO", &menu.list_size_info[i] },
12954 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12955 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12958 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12960 char *token = menu_config[j].token;
12961 char *value = getHashEntry(setup_file_hash, token);
12964 *menu_config[j].value = get_integer_from_string(value);
12968 // special case: initialize with default values that may be overwritten
12969 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12970 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12972 struct TokenIntPtrInfo menu_config[] =
12974 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12975 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12978 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12980 char *token = menu_config[j].token;
12981 char *value = getHashEntry(setup_file_hash, token);
12984 *menu_config[j].value = get_integer_from_string(value);
12988 // special case: initialize with default values that may be overwritten
12989 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12990 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12992 struct TokenIntPtrInfo menu_config[] =
12994 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12995 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12996 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12997 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12998 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12999 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13000 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13001 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13002 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13003 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13006 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13008 char *token = menu_config[j].token;
13009 char *value = getHashEntry(setup_file_hash, token);
13012 *menu_config[j].value = get_integer_from_string(value);
13016 // special case: initialize with default values that may be overwritten
13017 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13018 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13020 struct TokenIntPtrInfo menu_config[] =
13022 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13023 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13024 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13025 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13026 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13027 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13028 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13029 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13030 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13033 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13035 char *token = menu_config[j].token;
13036 char *value = getHashEntry(setup_file_hash, token);
13039 *menu_config[j].value = get_token_parameter_value(token, value);
13043 // special case: initialize with default values that may be overwritten
13044 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13045 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13049 char *token_prefix;
13050 struct RectWithBorder *struct_ptr;
13054 { "viewport.window", &viewport.window[i] },
13055 { "viewport.playfield", &viewport.playfield[i] },
13056 { "viewport.door_1", &viewport.door_1[i] },
13057 { "viewport.door_2", &viewport.door_2[i] }
13060 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13062 struct TokenIntPtrInfo vp_config[] =
13064 { ".x", &vp_struct[j].struct_ptr->x },
13065 { ".y", &vp_struct[j].struct_ptr->y },
13066 { ".width", &vp_struct[j].struct_ptr->width },
13067 { ".height", &vp_struct[j].struct_ptr->height },
13068 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13069 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13070 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13071 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13072 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13073 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13074 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13075 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13076 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13077 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13078 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13079 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13080 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13081 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13082 { ".align", &vp_struct[j].struct_ptr->align },
13083 { ".valign", &vp_struct[j].struct_ptr->valign }
13086 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13088 char *token = getStringCat2(vp_struct[j].token_prefix,
13089 vp_config[k].token);
13090 char *value = getHashEntry(setup_file_hash, token);
13093 *vp_config[k].value = get_token_parameter_value(token, value);
13100 // special case: initialize with default values that may be overwritten
13101 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13102 for (i = 0; title_info[i].info != NULL; i++)
13104 struct TitleFadingInfo *info = title_info[i].info;
13105 char *base_token = title_info[i].text;
13107 for (j = 0; title_tokens[j].type != -1; j++)
13109 char *token = getStringCat2(base_token, title_tokens[j].text);
13110 char *value = getHashEntry(setup_file_hash, token);
13114 int parameter_value = get_token_parameter_value(token, value);
13118 *(int *)title_tokens[j].value = (int)parameter_value;
13127 // special case: initialize with default values that may be overwritten
13128 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13129 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13131 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13132 char *base_token = titlemessage_arrays[i].text;
13134 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13136 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13137 char *value = getHashEntry(setup_file_hash, token);
13141 int parameter_value = get_token_parameter_value(token, value);
13143 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13147 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13148 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13150 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13160 // read (and overwrite with) values that may be specified in config file
13161 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13163 // special case: check if network and preview player positions are redefined
13164 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13166 freeSetupFileHash(setup_file_hash);
13169 void LoadMenuDesignSettings(void)
13171 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13173 InitMenuDesignSettings_Static();
13174 InitMenuDesignSettings_SpecialPreProcessing();
13175 InitMenuDesignSettings_PreviewPlayers();
13177 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13179 // first look for special settings configured in level series config
13180 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13182 if (fileExists(filename_base))
13183 LoadMenuDesignSettingsFromFilename(filename_base);
13186 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13188 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13189 LoadMenuDesignSettingsFromFilename(filename_local);
13191 InitMenuDesignSettings_SpecialPostProcessing();
13194 void LoadMenuDesignSettings_AfterGraphics(void)
13196 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13199 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13200 boolean ignore_defaults)
13204 for (i = 0; sound_config_vars[i].token != NULL; i++)
13206 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13208 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13209 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13213 *sound_config_vars[i].value =
13214 get_token_parameter_value(sound_config_vars[i].token, value);
13218 void InitSoundSettings_Static(void)
13220 // always start with reliable default values from static default config
13221 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13224 static void LoadSoundSettingsFromFilename(char *filename)
13226 SetupFileHash *setup_file_hash;
13228 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13231 // read (and overwrite with) values that may be specified in config file
13232 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13234 freeSetupFileHash(setup_file_hash);
13237 void LoadSoundSettings(void)
13239 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13241 InitSoundSettings_Static();
13243 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13245 // first look for special settings configured in level series config
13246 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13248 if (fileExists(filename_base))
13249 LoadSoundSettingsFromFilename(filename_base);
13252 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13254 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13255 LoadSoundSettingsFromFilename(filename_local);
13258 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13260 char *filename = getEditorSetupFilename();
13261 SetupFileList *setup_file_list, *list;
13262 SetupFileHash *element_hash;
13263 int num_unknown_tokens = 0;
13266 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13269 element_hash = newSetupFileHash();
13271 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13272 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13274 // determined size may be larger than needed (due to unknown elements)
13276 for (list = setup_file_list; list != NULL; list = list->next)
13279 // add space for up to 3 more elements for padding that may be needed
13280 *num_elements += 3;
13282 // free memory for old list of elements, if needed
13283 checked_free(*elements);
13285 // allocate memory for new list of elements
13286 *elements = checked_malloc(*num_elements * sizeof(int));
13289 for (list = setup_file_list; list != NULL; list = list->next)
13291 char *value = getHashEntry(element_hash, list->token);
13293 if (value == NULL) // try to find obsolete token mapping
13295 char *mapped_token = get_mapped_token(list->token);
13297 if (mapped_token != NULL)
13299 value = getHashEntry(element_hash, mapped_token);
13301 free(mapped_token);
13307 (*elements)[(*num_elements)++] = atoi(value);
13311 if (num_unknown_tokens == 0)
13314 Warn("unknown token(s) found in config file:");
13315 Warn("- config file: '%s'", filename);
13317 num_unknown_tokens++;
13320 Warn("- token: '%s'", list->token);
13324 if (num_unknown_tokens > 0)
13327 while (*num_elements % 4) // pad with empty elements, if needed
13328 (*elements)[(*num_elements)++] = EL_EMPTY;
13330 freeSetupFileList(setup_file_list);
13331 freeSetupFileHash(element_hash);
13334 for (i = 0; i < *num_elements; i++)
13335 Debug("editor", "element '%s' [%d]\n",
13336 element_info[(*elements)[i]].token_name, (*elements)[i]);
13340 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13343 SetupFileHash *setup_file_hash = NULL;
13344 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13345 char *filename_music, *filename_prefix, *filename_info;
13351 token_to_value_ptr[] =
13353 { "title_header", &tmp_music_file_info.title_header },
13354 { "artist_header", &tmp_music_file_info.artist_header },
13355 { "album_header", &tmp_music_file_info.album_header },
13356 { "year_header", &tmp_music_file_info.year_header },
13357 { "played_header", &tmp_music_file_info.played_header },
13359 { "title", &tmp_music_file_info.title },
13360 { "artist", &tmp_music_file_info.artist },
13361 { "album", &tmp_music_file_info.album },
13362 { "year", &tmp_music_file_info.year },
13363 { "played", &tmp_music_file_info.played },
13369 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13370 getCustomMusicFilename(basename));
13372 if (filename_music == NULL)
13375 // ---------- try to replace file extension ----------
13377 filename_prefix = getStringCopy(filename_music);
13378 if (strrchr(filename_prefix, '.') != NULL)
13379 *strrchr(filename_prefix, '.') = '\0';
13380 filename_info = getStringCat2(filename_prefix, ".txt");
13382 if (fileExists(filename_info))
13383 setup_file_hash = loadSetupFileHash(filename_info);
13385 free(filename_prefix);
13386 free(filename_info);
13388 if (setup_file_hash == NULL)
13390 // ---------- try to add file extension ----------
13392 filename_prefix = getStringCopy(filename_music);
13393 filename_info = getStringCat2(filename_prefix, ".txt");
13395 if (fileExists(filename_info))
13396 setup_file_hash = loadSetupFileHash(filename_info);
13398 free(filename_prefix);
13399 free(filename_info);
13402 if (setup_file_hash == NULL)
13405 // ---------- music file info found ----------
13407 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13409 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13411 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13413 *token_to_value_ptr[i].value_ptr =
13414 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13417 tmp_music_file_info.basename = getStringCopy(basename);
13418 tmp_music_file_info.music = music;
13419 tmp_music_file_info.is_sound = is_sound;
13421 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13422 *new_music_file_info = tmp_music_file_info;
13424 return new_music_file_info;
13427 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13429 return get_music_file_info_ext(basename, music, FALSE);
13432 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13434 return get_music_file_info_ext(basename, sound, TRUE);
13437 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13438 char *basename, boolean is_sound)
13440 for (; list != NULL; list = list->next)
13441 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13447 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13449 return music_info_listed_ext(list, basename, FALSE);
13452 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13454 return music_info_listed_ext(list, basename, TRUE);
13457 void LoadMusicInfo(void)
13459 int num_music_noconf = getMusicListSize_NoConf();
13460 int num_music = getMusicListSize();
13461 int num_sounds = getSoundListSize();
13462 struct FileInfo *music, *sound;
13463 struct MusicFileInfo *next, **new;
13467 while (music_file_info != NULL)
13469 next = music_file_info->next;
13471 checked_free(music_file_info->basename);
13473 checked_free(music_file_info->title_header);
13474 checked_free(music_file_info->artist_header);
13475 checked_free(music_file_info->album_header);
13476 checked_free(music_file_info->year_header);
13477 checked_free(music_file_info->played_header);
13479 checked_free(music_file_info->title);
13480 checked_free(music_file_info->artist);
13481 checked_free(music_file_info->album);
13482 checked_free(music_file_info->year);
13483 checked_free(music_file_info->played);
13485 free(music_file_info);
13487 music_file_info = next;
13490 new = &music_file_info;
13492 // get (configured or unconfigured) music file info for all levels
13493 for (i = leveldir_current->first_level;
13494 i <= leveldir_current->last_level; i++)
13498 if (levelset.music[i] != MUS_UNDEFINED)
13500 // get music file info for configured level music
13501 music_nr = levelset.music[i];
13503 else if (num_music_noconf > 0)
13505 // get music file info for unconfigured level music
13506 int level_pos = i - leveldir_current->first_level;
13508 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13515 char *basename = getMusicInfoEntryFilename(music_nr);
13517 if (basename == NULL)
13520 if (!music_info_listed(music_file_info, basename))
13522 *new = get_music_file_info(basename, music_nr);
13525 new = &(*new)->next;
13529 // get music file info for all remaining configured music files
13530 for (i = 0; i < num_music; i++)
13532 music = getMusicListEntry(i);
13534 if (music->filename == NULL)
13537 if (strEqual(music->filename, UNDEFINED_FILENAME))
13540 // a configured file may be not recognized as music
13541 if (!FileIsMusic(music->filename))
13544 if (!music_info_listed(music_file_info, music->filename))
13546 *new = get_music_file_info(music->filename, i);
13549 new = &(*new)->next;
13553 // get sound file info for all configured sound files
13554 for (i = 0; i < num_sounds; i++)
13556 sound = getSoundListEntry(i);
13558 if (sound->filename == NULL)
13561 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13564 // a configured file may be not recognized as sound
13565 if (!FileIsSound(sound->filename))
13568 if (!sound_info_listed(music_file_info, sound->filename))
13570 *new = get_sound_file_info(sound->filename, i);
13572 new = &(*new)->next;
13576 // add pointers to previous list nodes
13578 struct MusicFileInfo *node = music_file_info;
13580 while (node != NULL)
13583 node->next->prev = node;
13589 static void add_helpanim_entry(int element, int action, int direction,
13590 int delay, int *num_list_entries)
13592 struct HelpAnimInfo *new_list_entry;
13593 (*num_list_entries)++;
13596 checked_realloc(helpanim_info,
13597 *num_list_entries * sizeof(struct HelpAnimInfo));
13598 new_list_entry = &helpanim_info[*num_list_entries - 1];
13600 new_list_entry->element = element;
13601 new_list_entry->action = action;
13602 new_list_entry->direction = direction;
13603 new_list_entry->delay = delay;
13606 static void print_unknown_token(char *filename, char *token, int token_nr)
13611 Warn("unknown token(s) found in config file:");
13612 Warn("- config file: '%s'", filename);
13615 Warn("- token: '%s'", token);
13618 static void print_unknown_token_end(int token_nr)
13624 void LoadHelpAnimInfo(void)
13626 char *filename = getHelpAnimFilename();
13627 SetupFileList *setup_file_list = NULL, *list;
13628 SetupFileHash *element_hash, *action_hash, *direction_hash;
13629 int num_list_entries = 0;
13630 int num_unknown_tokens = 0;
13633 if (fileExists(filename))
13634 setup_file_list = loadSetupFileList(filename);
13636 if (setup_file_list == NULL)
13638 // use reliable default values from static configuration
13639 SetupFileList *insert_ptr;
13641 insert_ptr = setup_file_list =
13642 newSetupFileList(helpanim_config[0].token,
13643 helpanim_config[0].value);
13645 for (i = 1; helpanim_config[i].token; i++)
13646 insert_ptr = addListEntry(insert_ptr,
13647 helpanim_config[i].token,
13648 helpanim_config[i].value);
13651 element_hash = newSetupFileHash();
13652 action_hash = newSetupFileHash();
13653 direction_hash = newSetupFileHash();
13655 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13656 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13658 for (i = 0; i < NUM_ACTIONS; i++)
13659 setHashEntry(action_hash, element_action_info[i].suffix,
13660 i_to_a(element_action_info[i].value));
13662 // do not store direction index (bit) here, but direction value!
13663 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13664 setHashEntry(direction_hash, element_direction_info[i].suffix,
13665 i_to_a(1 << element_direction_info[i].value));
13667 for (list = setup_file_list; list != NULL; list = list->next)
13669 char *element_token, *action_token, *direction_token;
13670 char *element_value, *action_value, *direction_value;
13671 int delay = atoi(list->value);
13673 if (strEqual(list->token, "end"))
13675 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13680 /* first try to break element into element/action/direction parts;
13681 if this does not work, also accept combined "element[.act][.dir]"
13682 elements (like "dynamite.active"), which are unique elements */
13684 if (strchr(list->token, '.') == NULL) // token contains no '.'
13686 element_value = getHashEntry(element_hash, list->token);
13687 if (element_value != NULL) // element found
13688 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13689 &num_list_entries);
13692 // no further suffixes found -- this is not an element
13693 print_unknown_token(filename, list->token, num_unknown_tokens++);
13699 // token has format "<prefix>.<something>"
13701 action_token = strchr(list->token, '.'); // suffix may be action ...
13702 direction_token = action_token; // ... or direction
13704 element_token = getStringCopy(list->token);
13705 *strchr(element_token, '.') = '\0';
13707 element_value = getHashEntry(element_hash, element_token);
13709 if (element_value == NULL) // this is no element
13711 element_value = getHashEntry(element_hash, list->token);
13712 if (element_value != NULL) // combined element found
13713 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13714 &num_list_entries);
13716 print_unknown_token(filename, list->token, num_unknown_tokens++);
13718 free(element_token);
13723 action_value = getHashEntry(action_hash, action_token);
13725 if (action_value != NULL) // action found
13727 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13728 &num_list_entries);
13730 free(element_token);
13735 direction_value = getHashEntry(direction_hash, direction_token);
13737 if (direction_value != NULL) // direction found
13739 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13740 &num_list_entries);
13742 free(element_token);
13747 if (strchr(action_token + 1, '.') == NULL)
13749 // no further suffixes found -- this is not an action nor direction
13751 element_value = getHashEntry(element_hash, list->token);
13752 if (element_value != NULL) // combined element found
13753 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13754 &num_list_entries);
13756 print_unknown_token(filename, list->token, num_unknown_tokens++);
13758 free(element_token);
13763 // token has format "<prefix>.<suffix>.<something>"
13765 direction_token = strchr(action_token + 1, '.');
13767 action_token = getStringCopy(action_token);
13768 *strchr(action_token + 1, '.') = '\0';
13770 action_value = getHashEntry(action_hash, action_token);
13772 if (action_value == NULL) // this is no action
13774 element_value = getHashEntry(element_hash, list->token);
13775 if (element_value != NULL) // combined element found
13776 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13777 &num_list_entries);
13779 print_unknown_token(filename, list->token, num_unknown_tokens++);
13781 free(element_token);
13782 free(action_token);
13787 direction_value = getHashEntry(direction_hash, direction_token);
13789 if (direction_value != NULL) // direction found
13791 add_helpanim_entry(atoi(element_value), atoi(action_value),
13792 atoi(direction_value), delay, &num_list_entries);
13794 free(element_token);
13795 free(action_token);
13800 // this is no direction
13802 element_value = getHashEntry(element_hash, list->token);
13803 if (element_value != NULL) // combined element found
13804 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13805 &num_list_entries);
13807 print_unknown_token(filename, list->token, num_unknown_tokens++);
13809 free(element_token);
13810 free(action_token);
13813 print_unknown_token_end(num_unknown_tokens);
13815 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13816 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13818 freeSetupFileList(setup_file_list);
13819 freeSetupFileHash(element_hash);
13820 freeSetupFileHash(action_hash);
13821 freeSetupFileHash(direction_hash);
13824 for (i = 0; i < num_list_entries; i++)
13825 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13826 EL_NAME(helpanim_info[i].element),
13827 helpanim_info[i].element,
13828 helpanim_info[i].action,
13829 helpanim_info[i].direction,
13830 helpanim_info[i].delay);
13834 void LoadHelpTextInfo(void)
13836 char *filename = getHelpTextFilename();
13839 if (helptext_info != NULL)
13841 freeSetupFileHash(helptext_info);
13842 helptext_info = NULL;
13845 if (fileExists(filename))
13846 helptext_info = loadSetupFileHash(filename);
13848 if (helptext_info == NULL)
13850 // use reliable default values from static configuration
13851 helptext_info = newSetupFileHash();
13853 for (i = 0; helptext_config[i].token; i++)
13854 setHashEntry(helptext_info,
13855 helptext_config[i].token,
13856 helptext_config[i].value);
13860 BEGIN_HASH_ITERATION(helptext_info, itr)
13862 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13863 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13865 END_HASH_ITERATION(hash, itr)
13870 // ----------------------------------------------------------------------------
13872 // ----------------------------------------------------------------------------
13874 #define MAX_NUM_CONVERT_LEVELS 1000
13876 void ConvertLevels(void)
13878 static LevelDirTree *convert_leveldir = NULL;
13879 static int convert_level_nr = -1;
13880 static int num_levels_handled = 0;
13881 static int num_levels_converted = 0;
13882 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13885 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13886 global.convert_leveldir);
13888 if (convert_leveldir == NULL)
13889 Fail("no such level identifier: '%s'", global.convert_leveldir);
13891 leveldir_current = convert_leveldir;
13893 if (global.convert_level_nr != -1)
13895 convert_leveldir->first_level = global.convert_level_nr;
13896 convert_leveldir->last_level = global.convert_level_nr;
13899 convert_level_nr = convert_leveldir->first_level;
13901 PrintLine("=", 79);
13902 Print("Converting levels\n");
13903 PrintLine("-", 79);
13904 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13905 Print("Level series name: '%s'\n", convert_leveldir->name);
13906 Print("Level series author: '%s'\n", convert_leveldir->author);
13907 Print("Number of levels: %d\n", convert_leveldir->levels);
13908 PrintLine("=", 79);
13911 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13912 levels_failed[i] = FALSE;
13914 while (convert_level_nr <= convert_leveldir->last_level)
13916 char *level_filename;
13919 level_nr = convert_level_nr++;
13921 Print("Level %03d: ", level_nr);
13923 LoadLevel(level_nr);
13924 if (level.no_level_file || level.no_valid_file)
13926 Print("(no level)\n");
13930 Print("converting level ... ");
13933 // special case: conversion of some EMC levels as requested by ACME
13934 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13937 level_filename = getDefaultLevelFilename(level_nr);
13938 new_level = !fileExists(level_filename);
13942 SaveLevel(level_nr);
13944 num_levels_converted++;
13946 Print("converted.\n");
13950 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13951 levels_failed[level_nr] = TRUE;
13953 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13956 num_levels_handled++;
13960 PrintLine("=", 79);
13961 Print("Number of levels handled: %d\n", num_levels_handled);
13962 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13963 (num_levels_handled ?
13964 num_levels_converted * 100 / num_levels_handled : 0));
13965 PrintLine("-", 79);
13966 Print("Summary (for automatic parsing by scripts):\n");
13967 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13968 convert_leveldir->identifier, num_levels_converted,
13969 num_levels_handled,
13970 (num_levels_handled ?
13971 num_levels_converted * 100 / num_levels_handled : 0));
13973 if (num_levels_handled != num_levels_converted)
13975 Print(", FAILED:");
13976 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13977 if (levels_failed[i])
13982 PrintLine("=", 79);
13984 CloseAllAndExit(0);
13988 // ----------------------------------------------------------------------------
13989 // create and save images for use in level sketches (raw BMP format)
13990 // ----------------------------------------------------------------------------
13992 void CreateLevelSketchImages(void)
13998 InitElementPropertiesGfxElement();
14000 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14001 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14003 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14005 int element = getMappedElement(i);
14006 char basename1[16];
14007 char basename2[16];
14011 sprintf(basename1, "%04d.bmp", i);
14012 sprintf(basename2, "%04ds.bmp", i);
14014 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14015 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14017 DrawSizedElement(0, 0, element, TILESIZE);
14018 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14020 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14021 Fail("cannot save level sketch image file '%s'", filename1);
14023 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14024 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14026 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14027 Fail("cannot save level sketch image file '%s'", filename2);
14032 // create corresponding SQL statements (for normal and small images)
14035 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14036 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14039 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14040 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14042 // optional: create content for forum level sketch demonstration post
14044 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14047 FreeBitmap(bitmap1);
14048 FreeBitmap(bitmap2);
14051 fprintf(stderr, "\n");
14053 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14055 CloseAllAndExit(0);
14059 // ----------------------------------------------------------------------------
14060 // create and save images for element collecting animations (raw BMP format)
14061 // ----------------------------------------------------------------------------
14063 static boolean createCollectImage(int element)
14065 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14068 void CreateCollectElementImages(void)
14072 int anim_frames = num_steps - 1;
14073 int tile_size = TILESIZE;
14074 int anim_width = tile_size * anim_frames;
14075 int anim_height = tile_size;
14076 int num_collect_images = 0;
14077 int pos_collect_images = 0;
14079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14080 if (createCollectImage(i))
14081 num_collect_images++;
14083 Info("Creating %d element collecting animation images ...",
14084 num_collect_images);
14086 int dst_width = anim_width * 2;
14087 int dst_height = anim_height * num_collect_images / 2;
14088 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14089 char *basename_bmp = "RocksCollect.bmp";
14090 char *basename_png = "RocksCollect.png";
14091 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14092 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14093 int len_filename_bmp = strlen(filename_bmp);
14094 int len_filename_png = strlen(filename_png);
14095 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14096 char cmd_convert[max_command_len];
14098 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14102 // force using RGBA surface for destination bitmap
14103 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14104 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14106 dst_bitmap->surface =
14107 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14109 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14111 if (!createCollectImage(i))
14114 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14115 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14116 int graphic = el2img(i);
14117 char *token_name = element_info[i].token_name;
14118 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14119 Bitmap *src_bitmap;
14122 Info("- creating collecting image for '%s' ...", token_name);
14124 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14126 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14127 tile_size, tile_size, 0, 0);
14129 // force using RGBA surface for temporary bitmap (using transparent black)
14130 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14131 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14133 tmp_bitmap->surface =
14134 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14136 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14138 for (j = 0; j < anim_frames; j++)
14140 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14141 int frame_size = frame_size_final * num_steps;
14142 int offset = (tile_size - frame_size_final) / 2;
14143 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14145 while (frame_size > frame_size_final)
14149 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14151 FreeBitmap(frame_bitmap);
14153 frame_bitmap = half_bitmap;
14156 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14157 frame_size_final, frame_size_final,
14158 dst_x + j * tile_size + offset, dst_y + offset);
14160 FreeBitmap(frame_bitmap);
14163 tmp_bitmap->surface_masked = NULL;
14165 FreeBitmap(tmp_bitmap);
14167 pos_collect_images++;
14170 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14171 Fail("cannot save element collecting image file '%s'", filename_bmp);
14173 FreeBitmap(dst_bitmap);
14175 Info("Converting image file from BMP to PNG ...");
14177 if (system(cmd_convert) != 0)
14178 Fail("converting image file failed");
14180 unlink(filename_bmp);
14184 CloseAllAndExit(0);
14188 // ----------------------------------------------------------------------------
14189 // create and save images for custom and group elements (raw BMP format)
14190 // ----------------------------------------------------------------------------
14192 void CreateCustomElementImages(char *directory)
14194 char *src_basename = "RocksCE-template.ilbm";
14195 char *dst_basename = "RocksCE.bmp";
14196 char *src_filename = getPath2(directory, src_basename);
14197 char *dst_filename = getPath2(directory, dst_basename);
14198 Bitmap *src_bitmap;
14200 int yoffset_ce = 0;
14201 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14204 InitVideoDefaults();
14206 ReCreateBitmap(&backbuffer, video.width, video.height);
14208 src_bitmap = LoadImage(src_filename);
14210 bitmap = CreateBitmap(TILEX * 16 * 2,
14211 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14214 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14221 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14222 TILEX * x, TILEY * y + yoffset_ce);
14224 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14226 TILEX * x + TILEX * 16,
14227 TILEY * y + yoffset_ce);
14229 for (j = 2; j >= 0; j--)
14233 BlitBitmap(src_bitmap, bitmap,
14234 TILEX + c * 7, 0, 6, 10,
14235 TILEX * x + 6 + j * 7,
14236 TILEY * y + 11 + yoffset_ce);
14238 BlitBitmap(src_bitmap, bitmap,
14239 TILEX + c * 8, TILEY, 6, 10,
14240 TILEX * 16 + TILEX * x + 6 + j * 8,
14241 TILEY * y + 10 + yoffset_ce);
14247 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14254 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14255 TILEX * x, TILEY * y + yoffset_ge);
14257 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14259 TILEX * x + TILEX * 16,
14260 TILEY * y + yoffset_ge);
14262 for (j = 1; j >= 0; j--)
14266 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14267 TILEX * x + 6 + j * 10,
14268 TILEY * y + 11 + yoffset_ge);
14270 BlitBitmap(src_bitmap, bitmap,
14271 TILEX + c * 8, TILEY + 12, 6, 10,
14272 TILEX * 16 + TILEX * x + 10 + j * 8,
14273 TILEY * y + 10 + yoffset_ge);
14279 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14280 Fail("cannot save CE graphics file '%s'", dst_filename);
14282 FreeBitmap(bitmap);
14284 CloseAllAndExit(0);